[
  {
    "path": ".gitignore",
    "content": "\n# built\n*.app\n*.a\n\n# osx\n.DS_Store\n\n# win\n*.exe\n*.dll\n\n# xcode\n*.pbxuser\n*.perspectivev3\n*.mode1v3\nxcuserdata\nbuild\n\n# codeblocks\n*.layout\n*.depend\n*.cbTemp\n\n# example project files\n\npdExample/Makefile\npdExample/*.xcodeproj\npdExample/config.make\npdExample/*xcconfig\npdExample/*.plist\npdExample/of.entitlements\n\npdMultiExample/Makefile\npdMultiExample/*.xcodeproj\npdMultiExample/config.make\npdMultiExample/*xcconfig\npdMultiExample/*.plist\npdMultiExample/of.entitlements\n\npdExampleIOS/Makefile\npdExampleIOS/*.xcodeproj\npdExampleIOS/config.make\npdExampleIOS/*xcconfig\npdExampleIOS/*.plist\npdExampleIOS/*.pch\npdExampleIOS/bin/data/Default*.png\npdExampleIOS/bin/data/Icon*.png\npdExampleIOS/mediaAssets/\npdExampleIOS/of.entitlements\n\npitchShifter/Makefile\npitchShifter/*.xcodeproj\npitchShifter/config.make\npitchShifter/*xcconfig\npitchShifter/*.plist\npitchShifter/of.entitlements\n"
  },
  {
    "path": "CHANGES.txt",
    "content": "TBA\n\n* fixed pdMultiExample not using newer ofSoundBuffer audioIn and audioOut\n  functions (reported by Theo Watson)\n\n1.10.1: 2024 Apr 10\n\n* fixed endian detection issues on Linux by defining HAVE_ENDIAN_H\n\n* defined _DARWIN_UNLIMITED_SELECT to increased max number of file descriptors\n  on macOS\n\n1.10.0: 2023 Nov 16\n\n* updated to libpd 0.14.0 (pd 0.54-1)\n  note: some files have been added or removed, so regenerate your projects\n\n* updated for OF 0.12\n  warning: the ProjectGenerator may not set the correct C flags for Xcode\n           projects, see the readme on how to manually set them\n\n* added ofSoundBuffer audioIn() and audioOut() functions (suggested by Nejrup)\n* use ofSetBackgroundColor instead of ofSetBackground in pdExample\n  (Jonathan Frank)\n* updated thumbnail to pd vanilla screenshot\n* various formatting updates and typo fixes\n\n* removed references to Codeblocks in readme build requirements\n\n1.9.1: 2022 Dec 16\n\n* updated to libpd 0.13.2 (pd 0.53-1)\n\n1.9.0: 2022 Nov 05\n\n* updated to libpd 0.13.0 (pd 0.53-0)\n  note: some files have been added or removed, so regenerate your projects\n\n* added support for multiple ofxPd instances when compiling with PDINSTANCE\n  note: projects which use libpd_*_instance functions must be updated to use\n        separate ofxPd instances\n\n1.8.1: 2021 Jan 08\n\n* updated to libpd 0.12.1 for pd~ support\n\n1.8.0: 2020 Dec 30\n\n* updated to libpd 0.12.0 (pd 0.51-4)\n  note: some files have been added, so regenerate your projects\n\n* added prelim support for Visual Studio (not fully tested, 64 bit only)\n* added info about PDINSTANCE and PDTHREADS defines to readme\n* added PDINSTANCE and PDTHREADS defines addon_config.mk (uncomment to use)\n* added info about NSMicrophoneUsageDescription to readme\n\n* readded [pd~] sources as they build on windows again\n* fixed pdMultiExample not building with PDINSTANCE and PDTHREADS defines\n  (reported by Antoine Rousseau)\n* append ADDONS_CFLAGS to avoid problem with OF ProjectGenerator on Android\n* include ofFileUtils.h before ofxPd.h to conflict between boost & libpd's\n  s_ define (Antoine Rousseau)\n\n* clarifications & screenshots for MinGW build instructions (moebiussurfing)\n* clarify windows libpd.dll instructions\n* update pdMultiExample\n\n1.7.1: 2018 Sep 13\n\n* updated to libpd 0.11.0 (pd 0.48-2)\n\n1.7.0: 2018 Jul 24\n\n* updated for OF 0.10.0\n* updated to latest libpd pre-0.11 (pd 0.48-1),\n  note: some files have been removed, so regenerate your projects\n\n* fixed macOS 10.13 SDK dispatch.h error\n* fixed compile error due to leftover Poco/Mutex include (reported by Dan Moore)\n* fixed iOS duplicate symbols link error by setting -fcommon (reported by ukelady)\n\n1.6.1: 2017 Mar 17\n\n* updated to latest libpd 0.10 (pd 0.47-1)\n* updated readme section on externs\n\n* fixed bad C++11 preprocessor check (reported by Mateus Knelsen)\n\n1.6.0: 2016 May 31\n\n  some libpd source files changed, update your projects\n\n* added HAVE_LIBDL define for dynamic external loading on desktop\n\n* updated to libpd 0.9.0 & pd version 0.47-0:\n  - new clone object\n  - expr is now BSD licensed\n  - HAVE_ALLOCA_H no longer required\n* updated for OF 0.9.0\n\n* removed Poco mutex as we can now use the PdBase std::mutex with C++11\n\n1.5.3: 2015 Nov 30\n\n* updated to libpd 0.8.4\n* ofxPd now reinits itself if the buffer size or number of channels changes in\n  audioIn()/audioOut()\n* pdMultiExample updates\n\n1.5.2: 2015 Jun 16\n\n* updated latest libpd which includes big fix for ringbuffer race condition\n\n1.5.1: 2015 Apr 28\n\n* updated to libpd & pd version 0.46-6 (includes new bob~ external)\n\n* fixed examples not building correctly due to remaining polling interface code\n* fixed build issue due to missing expr~ and d_fft_fftsg.c sources \n\n* old polling interface removed since it's no longer part of PdBase,\n  use queued=true in init() & receiveMessages()/receiveMidi()\n\n1.5.0: 2015 Feb 22\n\n  some libpd source files changed, update your projects\n\n* updated to latest libpd & pd version 0.46-4\n* included extra externals are now loaded by default (expr, bonk~, fiddle~, etc)\n* updated addons_config.mk to work with upcoming OF 0.9.0 project generator\n* renamed examples and removed example project files (use the project generator)\n\n1.4.0: 2013 Aug 13\n\n* added PitchShifter example\n\n* general updates for OF 0.8.0:\n  * moved libpd source into libs/libpd to match ofxAddonsTemplate\n  * split example into seperate desktop & ios projects (easier to generate)\n  * regenerated project files\n* updated to latest libpd\n\n* built-in pd externals now work (bonk~, choice, fiddle~, loop~, lrshift~,\n  pique~, sigmund~, stdout)\n\n1.3.0: 2012 Nov 7\n\n* added ability to open a new patch instance with an existing patch object\n\n* updated to latest libpd\n* changed logging to use \"Pd\" logging module\n\n* fixed Windows hang on exit due to stuck mutex lock in ofxPd::clear()\n\n* removed [expr~] due to licensing, should be left up to individual developers\n\n1.2.0: 2012 Jul 30\n\n* added support for PdBase event polling in ofxPd\n\n1.1.0: 2012 Mar 22\n\n* updated to latest libpd\n* List::asFloat() & List::asSymbol() are now List::getFloat() & \n  List::getSymbol()\n\n* bugfixes for Visual Studio 2010 (thanks Laurence Muller)\n\n1.0.0: 2012 Jan 18\n\n  streamlined api\n\n* added List building & sending\n* wrapped Pd receiver and types within a \"pd\" namespace\n* reworked api to better match libpd Java api\n  - channel is now first arg in midi functions\n  - port is now first arg in raw midi byte functions\n  - pgm num range is now 1-128 to match [pgmin]/[pgmout] (was 0-127)\n  - destination names are now set when finishing compound messages\n  - renamed \"sysRT\" functions to \"sysRt\"\n  - renamed \"getArrayLen\" to \"getArraySize\"\n  - renamed \"*Source\" functions to \"subscribe\" functions\n  - renamed \"*subscribe\" functions to \"receive/ignore\" functions\n  - renamed \"ofxPdListener\" class to \"PdReceiver\"\n  - renamed \"*Listener\" functions to \"*Receiver\" functions\n  - renamed Receiver functions (bangReceived, floatReceived, etc) to match\n        Java Receiver (receiveBang, receiveFloat, etc)\n  - split midi receiving into PdMidiReceiver base class\n  - renamed \"dspOn/dspOff\" to \"start/stop\"\n  - added computeAudio function\n  - receivers now receive from all subscribed sources by default\n  - renamed ofxPdListener and ofxPdTypes source files to PdReceiver and PdTypes\n  - renamed \"getArraySize\" function to \"arraySize\"\n  - renamed \"setMaxMsgLength\" function to \"setMaxMessageLen\"\n  - renamed \"getMaxMsgLength\" function to \"maxMessgeLen\"\n  - renamed \"getBlockSize\" function to \"blockSize\"\n  - renamed \"sendMsg\" & \"finishMsg\" functions to \"sendMessage\" & finishMessage\"\n  - renamed midi funcs/objects to match java api (ie Note->NoteOn,\n    Ctl->ControlChange, etc)\n  - renamed \"receivedPrint\" function to \"print\"\n* added receiveMidi/ignoreMidi functions to filter midi channel events for\n  PdMidiReceivers\n* added isInited function\n\n* separated base libpd wrapper into PdBase class, now extended by ofxPd\n  - added setReceiver and setMidiReceiver functions\n  - added audio processing (processRaw, processShort, etc) functions\n  - Pd class source files (PdTypes, PdReceiver, etc) have been moved to\n    src/pd/cpp\n* now checks if HAVE_UNISTD_H & USEAPI_DUMMY are defined before defining\n* PdBase instance calls now wrapped by a PdContext singleton (global instance\n  until multiple context support is added to libpd)\n\n0.3.0: 2011 Sep 22\n\n* added max message length setting\n\n* updated for OF 0062\n* updated to newest libpd\n* merged win codeblocks branch\n\n* cleaned Xcode projects, removed OF 0062 project\n\n0.2.0: 2011 Aug 7\n\n* fleshed out entire libpd api:\n  - array access\n  - Patch object\n  - stream interface and type structs\n  - receiving control functions\n  - path handling in addToSearchPath()\n  - ticksPerBuffer added to init()\n  - etc\n* sound in and out working\n* added support for Linux and Codeblocks project (thanks Damian Stewart)\n* added iOS example project\n* added libpd source update script\n* added pd external libs tutorial to readme\n\n* now using BSD license\n* updated for OF 007\n* simplified some api names to match corresponding pd names (midi)\n* midi channels are now numbered 1-16\n* now thread safe on all platforms (thanks Damian Stewart)\n* reorganized examples into AppCore class\n* swapped num in and out channel arg order in init() to match ofSoundStream\n* moved libpd sources to src folder\n* updated example:\n  - search path abs test\n  - realtime scope array\n  - keyboard piano\n  - delay\n\n* bugfix 0062 hangs on exit\n* bugfix soundstream hang on exit\n* bugfix listener bugs and print handling\n\n* removed need for libpd static lib\n* removed 'pd' function name prefix\n\n0.1.0: 2011 Jan 24\n\n  initial version\n\n* added Xcode OF 007 beta project file\n* added libpd as a static lib\n* added message sending functions\n* added message receiving functions\n* audio out working\n* added event listeners (thanks Marek Bereza)\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "Copyright (c) 2011-2023 Dan Wilcox <danomatika@gmail.com>\nAll rights reserved.\n\nThe following terms (the \"Standard Improved BSD License\") apply to all files\nassociated with the software unless explicitly disclaimed in individual files:\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n1. Redistributions of source code must retain the above copyright\n   notice, this list of conditions and the following disclaimer.\n2. Redistributions in binary form must reproduce the above  \n   copyright notice, this list of conditions and the following \n   disclaimer in the documentation and/or other materials provided\n   with the distribution.\n3. The name of the author may not be used to endorse or promote\n   products derived from this software without specific prior \n   written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE AUTHOR \"AS IS\" AND ANY\nEXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A\nPARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR\nBE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\nEXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\nTO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,   \nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\nON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\nLIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING\nIN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF\nTHE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "README.md",
    "content": "ofxPd\n=====\n<p align=\"center\">\n<img src=\"https://raw.github.com/danomatika/ofxPd/master/ofxaddons_thumbnail.png\"/>\n</p>\n\nCopyright (c) [Dan Wilcox](danomatika.com) 2011-2023\n\nBSD Simplified License.\n\nFor information on usage and redistribution, and for a DISCLAIMER OF ALL\nWARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.\n\nSee https://github.com/danomatika/ofxPd for documentation as well as the [OF forum post on ofxPd](http://forum.openframeworks.cc/t/ofxpd/6492)\n\nThis project has been supported by the CMU [Frank-Ratchye STUDIO for Creative Inquiry](http://studioforcreativeinquiry.org), the DU [Emergent Digital Practices Program](https://www.du.edu/ahss/edp/), and my time at the [ZKM | Hertz-Lab](https://zkm.de/en/about-the-zkm/organisation/hertz-lab).\n\nDescription\n-----------\n\nofxPd is an Open Frameworks addon for running an instance of the Pure Data audio environment within an openFrameworks application. Audio, messages, and [MIDI](http://en.wikipedia.org/wiki/Musical_Instrument_Digital_Interface) events can be passed to and from pure data patches and the library is thread safe.\n\n[Pure Data](http://pure-data.info) is a graphical patching environment for audio and multimedia (note: the gui and graphics features are not within the scope of this addon) \n\n[openFrameworks](http://www.openframeworks.cc) is a cross platform open source toolkit for creative coding in C++\n\nBuild Requirements\n------------------\n\nTo use ofxPd, first you need to download and install Open Frameworks. Development is against the latest version of Open Frameworks on github. Checkout a tag if you need an earlier, stable version.\n\n[OF github repository](https://github.com/openframeworks/openFrameworks)\n\nOn macOS, you will need to install Xcode.\n\nOn Linux, you can use the Makefile.\n\nOn Windows, Qt Creator/Msys2 are recommended as the libpd sources do not currently build in Visual Studio. However, you *can* use a libpd.dll built by MinGW in a Visual Studio project. See the \"Using ofxPd with Visual Studio\" section below.\n\nInstallation\n------------\n\nPlace ofxPd within a folder in the apps folder of the OF dir tree:\n\n    openframeworks/addons/ofxPd\n\nThe easiest way to do this is via cloning with git:\n\n    cd openframeworks/addons/\n    git clone git://github.com/danomatika/ofxPd.git\n\n### Which version to use?\n\nThe master branch of ofxPd will work with the current stable version of openFrameworks and can be considered *relatively* stable.\n\nPrevious versions are tagged using [Semantic Versioning](http://semver.org) with the updates to newer versions of openFrameworks and libpd noted in the changelog, CHANGES.txt. You can select the tag in the Github \"Current Branch\" menu or clone and check it out using git.\n\nIf you want to use ofxPd with a previous version of openFrameworks, checkout the corresponding version tag after cloning:\n\n    git clone git://github.com/danomatika/ofxPd.git\n    cd ofxPd\n    git checkout 1.5.3\n\nRunning the Example Projects\n----------------------------\n\nThe example projects are in the `pdExample` & `pdExampleIOS` folders.\n\nProject files for the examples are not included so you will need to generate the project files for your operating system and development environment using the OF ProjectGenerator which is included with the openFrameworks distribution.\n\nTo (re)generate project files for an *existing* project:\n\n* Click the \"Import\" button in the ProjectGenerator\n* Navigate to the project's parent folder ie. \"ofxPd\", select the base folder for the example project ie. \"pdExample\", and click the Open button\n* Click the \"Update\" button\n\nIf everything went Ok, you should now be able to open the generated project and build/run the example.\n\n### Notes for iOS Projects\n\n* As of iOS 10+, you will need to manually add a NSMicrophoneUsageDescription string to the project's info plist or the app will crash immediately:\n  1. Click on the blue project at the top of the Xcode file tree (ie. something like \"pdExampleIOS\"\n  2. Click the \"Info\" tab in the top/middle\n  3. Under \"Custom iOS Target Properties\", hover over the last key in the list and click the \"+\" button\n  4. Add the following:\n    - Key: NSMicrophoneUsageDescription\n    - Type: string\n    - Value: a description string for the app like, ie. \"This app needs to use the microphone for bla bla...\"\n* If you use the OF release zips from openFrameworks.cc, you need the iOS zip *not* the macOS zip\n* Make sure that \"iOS (Xcode)\" is selected in the PG's \"Platforms\" box\n\npdMultiExample & Multiple Instance Support\n------------------------------------------\n\nThis special example demonstrates ofxPd's multiple-instance support, where you can run multiple separate instances of libpd concurrently.\n\nTo build and run this example, the following C *and* C++ flags must be set: `-DPDINSTANCE` & `-DPDTHREADS`. If these are not set, the example will exit early with an error.\n\n### Makefile\n\nFor Makefile builds, these are set in `pdMultiExample/config.make`.\n\n### Project Generator\n\nFor project files which are created with the Project Generator (ie. Xcode), uncomment the line in `addons_config.mk` under the `common` target, save, then (re)generate the project with the PG.\n\n### Xcode\n\nThe defines can be added manually to the Xcode projects build settings: `Other C Flags` & `Other C++ Flags`.\n\nPitchShifter\n------------\n\nPitchShifter is a simple example application which uses an OF GUI to control a pitch shifter within a PD patch. Like the basic example, you will have to generate the project files using the ProjectGenerator.\n\nHow to Create a New ofxPd Project\n---------------------------------\n\n_Note: These instructions are for manually creating a new project from an existing ofxPd project and it's project files (aka Xcode, C::B, etc). You do not need to follow these steps if you use the ProjecGenerator app in which case you *may* need to add the C Flags as the PG currently seems to have a problem doing this. See the IDE specific instructions on how to do this._\n\nTo develop your own project based on ofxPd, either generate a new project with the ProjectGenerator or generate one of the examples, copy, and rename it. You probably want to put it in your apps folder, for example, after copying:\n\n    openFrameworks/addons/ofxPd/pdExample/ => openFrameworks/apps/myApps/pdExample/\n\nIt must be 3 levels down in the openFrameworks folder structure.\n\nThen after renaming:\n\n    openFrameworks/apps/myApps/myPdProject/\n\n### For Xcode:\n\nRename the project in Xcode (do not rename the .xcodeproj file in Finder!): Xcode Menu->Project->Rename\n\nAdding ofxPd to an Existing Project\n-----------------------------------\n\n_Note: These instructions are for manually add ofxPd to an existing project. You do not need to follow these steps if you use the ProjecGenerator app in which case you *may* need to add the C flags as the PG currently seems to have a problem doing this. See the IDE specific instructions on how to do this._\n\nIf you want to add ofxPd to another project, you need to make sure you include the src folder:\n\n    openFrameworks/addons/ofxPd/src\n\nYou will also need to include some additional C flags for building the libpd source:\n\n\t-DPD -DUSEAPI_DUMMY -DPD_INTERNAL -DHAVE_UNISTD_H -DHAVE_ALLOCA_H -DLIBPD_EXTRA\n\n_Note: **-DLIBPD_EXTRA** is optional if you do not need/use the externals in `libpd/pure-data/extra`_\n\nIf you want to build ofxPd with the libpd experimental libpd multi-instance support (ie. for pdMultiExample), add these C flags as well:\n\n    -DPDINSTANCE -DPDTHREADS\n\n### For Xcode:\n\nAdditional C flags are needed per-platform:\n* macOS: `-DHAVE_LIBDL -DHAVE_MACHINE_ENDIAN_H -D_DARWIN_C_SOURCE`\n* iOS: `-fcommon -DHAVE_MACHINE_ENDIAN_H -D_DARWIN_C_SOURCE`\n\n* Create a new group \"ofxPd\" \n* Drag these directories from ofxPd into this new group: ofxPd/src\n* Add a search path to: `../../../addons/ofxPd/libs/libpd/pure-data/src` under Targets->YourApp->Build->Header Search Paths (make sure \"All\" is selected)\n* Under Targets->YourApp->Build->**Other C Flags** (make sure \"All\" is selected), add\n\t<pre>-DPD -DUSEAPI_DUMMY -DPD_INTERNAL -DHAVE_UNISTD_H -DHAVE_ALLOCA_H -DLIBPD_EXTRA</pre>\n    and the additional C flags noted above\n  * _Note: Make sure you use Other **C** Flags! Other **C++** Flags will **not** work since libpd is written in C._\n* Under **Other C++ Flags**, add\n    <pre>-DHAVE_UNISTD_H=1</pre>\n\n### For Linux (Makefiles):\n\nEdit addons.make in your project folder and add the following line to the end of the file:\n\t<pre>ofxPd</pre>\n\nUsing ofxPd with Visual Studio\n------------------------------\n\n### Visual Studio only\n\nAs of spring 2020, the libpd sources should build directly in new versions of Visual Studio. Also, ofxPd projects use the included 64 bit copy of `libwinpthread-1.dll` used for the Msys2/MinGW build, so everything should be included.\n\n_Note: This has not been directly confirmed with OF & ofxPd. (At least no one has told me.) If you mainly use VS, try this first before building the libpd.dll via Msys2/MinGW._\n\n### Build libpd.dll with MinGW & use with Visual Studio\n\nIn order to use libpd with ofxPd in a Visual Studio project, you need a libpd.lib and libpd.dll libraries built using MinGW (Minimal GNU for Windows) which provides a Unix command shell and compiler.\n\nYou can check if there is a pre-compiled libpd for ofxPd available here:\n\n<http://docs.danomatika.com/releases/ofxPd/>\n\nIf so, skip to the \"Adding libpd\" section, otherwise follow the steps below to set up a build environment and build libpd.\n\n#### Building libpd with Msys2/MinGW\n\nThe steps for 64 bit are basically:\n\n1. Set up Msys2/MinGW: see https://github.com/libpd/libpd#windows\n  * _Make sure to follow all steps in the Msys2 setup instructions, ie. updating packages after install_\n2. Open an Msys2 shell (64 bit)\n3. Build libpd: `make`\n4. Install libpd to a temp folder: `make install prefix=build/libpd`\n\n#### Adding libpd to a Visual Studio project\n\nReplace the libpd source code in ofxPd with the libpd headers and library files:\n\n1. Delete the ofxPd `ofxPd/libs/libpd` folder\n2. Copy `libpd/build/libpd` into `ofxPd/libs`\n\nTo set up a VS project using ofxPd, you need to link to the libpd.lib import library and place the runtime libraries for libpd in your project's `bin` folder.\n\nAdd libpd.lib to link stage of the Visual Studio project:\n\n![VS Linker properties](doc/windows_vs_linker.png)\n\n* Set \"x64\" target\n* Project -> Properties\n* Make sure Active configuration & platform are set (you will need to do this for both Debug & Release builds)\n* Configuration Properties -> Linker -> Input\n* Additional Dependencies -> click on right hand drop down, choose Edit...\n* Add the path libpd.lib: `$(OF_ROOT)\\addons\\ofxPd\\libs\\libpd\\lib\\libpd.lib`\n\nAdd the runtime libraries to the project's `bin` folder:\n\n* `libpd/build/libpd/lib/libpd.dll`\n* `libpd/libs/mingw64/libwinpthread-1.dll`\n\n_Note: You will need to re-add libpd.lib to the VS link stage whenever you regenerate the project with the OF ProjectGenerator._\n\n![VS project layout](doc/windows_vs_pdExample.png)\n\nFor 32 bit:\n\n* Open an Msys2 shell (32 bit)\n* Build libpd using `make`\n* Set the \"Win32\" target in your VS project before setting the libpd.lib path\n* Copy pthread from the \"mingw32\" folder: `libpd/libs/mingw32/libwinpthread-1.dll`\n\n_Screenshots provided by @moebiussurfing._\n\n### Contributing a libpd Build for Windows\n\nIf you have successfully built a new version of libpd for Windows, please consider contributing a copy for others to use.\n\nMake a zip file with the following layout from your ofxPd directory:\n\n~~~\nbin/pd.dll\nbin/libwinpthread-1.dll\nlibs/libpd/lib/pd.dll\nlibs/libpd/lib/libpd.lib\nlibs/libpd/lib/libpd.def\nlibs/libpd/include/ <-- libpd headers\n~~~\n\nName the zip using the following format: \"libpd-VER-ARCH-VS####.zip\". For example,\n\n\"libpd-0.12-prerelease-x64-VS2017.zip\" is a 64 bit build of libpd 0.12 (pre-release) using Visual Studio 2017.\n\nCreate an issue on the ofxPd Github repo about your new build and we can add it to the [release builds link](http://docs.danomatika.com/releases/ofxPd/).\n\nNotes\n-----\n\n### Audio Interfacing & Debugging Audio Issues\n\nlibpd as utilized in ofxPd does not handle any of the audio interfacing itself, but is called via the ofSoundStream system within openFrameworks. If you have any issues with the sound interface / audio apis themselves, please log an issue to the [openFrameworks Github repo](https://github.com/openframeworks/openFrameworks).\n\n### Sample Rate\n\nThe sample rate is set to 44100 when initializing ofxPd in the examples. If your sample rate is higher, the playback pitch will be higher. Make sure the sample rate is the same as your system audio sample rate to hear the correct pitch.\n\nFor example: The default sample rate on macOS is 96000. Running the app at 44100 results in double the playback pitch while initializing ofxPd at 96000 gives the correct pitch.\n\n### Running App in the Background on iOS\n\nIf you're using ofxPd to build an audio app on iOS, you probably want the app to keep running while in the background (aka switching between other apps or going to the home screen). You can enable this in Xcode by clicking on the Project in the project tree, selecting the \"Capabilities\" tab, and turning on the \"Background Modes\" switch, then checking \"Audio, Airplay and Picture in Picture\". Next, Set \"Application does not run in background\" to NO in the \"Info\" tab.\n\n### Disabling Automatic Screen Locking on iOS\n\nYou may be building an audio app for iOS that you want to run without the automatic screen locking mechanism closing it. You can disable the screen lock timer by adding the following to your ofApp setup() function:\n\n    [[UIApplication sharedApplication] setIdleTimerDisabled:YES];\n\nBugs & Errors\n-------------\n\n### OF 0.12.0 and Xcode: 'ext.h' file not found\n\n_Note: This issue is fixed in the OF 0.12.1 ProjectGenerator nightly builds._\n\nThe OF 0.12.0 ProjectGenerator seems to have an issue with setting the correct C flags. Check that the required flags specified in the \"Adding ofxPd to an Existing Project\" section are set in the Xcode project:\n\n1. Click on the project in the left-hand Project Navigator\n2. Under Targets->YourApp->Build->**Other C Flags** (make sure \"All\" is selected)\n3. If the entry is empty or is missing the required flags, set them\n\n### iOS app crashes immediately with something about \"Microphone Description\"\n\nAs of iOS version 10+, apps need to ask the user in order to use the camera, microphone, or location data. If you run an iOS project that uses microphone input (ie. pdExampleIOS), a text description is required or the app will be terminated by the OS.\n\nSince the OF Project Generator (currently) doesn't know how to automatically add these description strings to the Xcode project, you will need to do it manually every time you (re)generate an iOS project.\n\nSee the steps listed in the \"Running the Example Projects\" section.\n\n### Xcode: Expected value in expression dispatch.h\n\nThe macOS 10.13 SDK now requires the HAVE_UNISTD_H to define a value. This has been fixed in ofxPd's `addons_config.mk` file. You can either use the OF ProjectGenerator to regenerate the Xcode project or add the following to your project's \"Other C++ Flags\":\n\n~~~\n-DHAVE_UNISTD_H=1\n~~~\n\n### Pitch is off on the iPhone 6S\n\nThe iPhone 6S hardware seems to prefer a sample rate of 48000 and calling ofSoundStreamSetup() with 44100 will not change that in versions of OF 0.8.4 and previous. This means ofxPd will be running at 44100 but the audio stream is actually 48000, resulting in a higher pitch coming out of your patches and a lower pitch going in.\n\nThe fix is to follow Apple's method of setting the *preferred* sample rate, then grabbing what the *actual* sample rate is afterwards. You can then use this real value in ofSoundStreamSetup() and ofxPd::init(). The pdExampleIOS has been updated to show how to do this. Hopefully, this functionality will be added to OF in the future.\n\n### File \"tr1/memory\" not found in Xcode\n\nYou just upgraded to macOS 10.9 and Xcode 5 right? The default compiler is now LLVM and you need to rebuild your Xcode project files so OF will build correctly. Use the ProjectGenerator in the OF 0.8.0 download to regenerate the project:\n\n* Choose the _parent folder_ of your project folder\n* Set the name of the project\n* Add ofxPd as an add-on\n* Hit generate\n\nAlso note, currently the PG doesn't seem to set the C Flags correctly, so you might have to add them manually. See \"Adding ofxPd to an Existing Project\" on how to do this.\n\n### Unknown type `t_float`, etc\n\nThe compiler doesn't recognize the internal Pd types because it's missing the C Flags needed to build libpd. See the section for your IDE in \"Adding ofxPd to an Existing Project\" on how to do this. \n\n### Undefined basic_ostream in XCode\n\nIf you get the following [linker error](http://www.openframeworks.cc/forum/viewtopic.php?f=8&t=5344&p=26537&hilit=Undefined+symbol#p26537) in XCode:\n\n    Undefined symbols: \"std::basic_ostream<char, std::char_traits<char> ...\n\nyou need to change the Base SDK to 10.6: Project > Edit Project Settings\n\n### RtAudio Hang on Exit in 0062\n\nRtAudio will hang on app exit in OF 0062. The only way to fix this is to make a small edit to the OF 0062 core by editing `lib/openFrameworks/sound/ofSoundStream.cpp` and commenting line 143 so close() is not called.\n\n### \"verbose\" redefinition in Win Codeblocks\n\nCurrently, there is a static function in the videoinput lib on Win that conflicts with a #define in the Pure Data sources. The easy fix, until the OF core is updated, is to comment out line 115 in `libs\\videoInput\\include\\videoInput.h`.\n\nNote: This change hasn't been tested while using the ofVideoGrabber yet ... there is a slight chance it may cause a crash, be warned.\n\n### \"undefined reference to SetDllDirectory\" in Win Codeblocks\n\nNewer versions of libpd check the windows version, so this needs to be set via a define in your Codeblocks project. If you have an old ofxPd project, this is not set, hence the error. See \"Adding ofxPd to an Existing Project\" above, and add the `WINVER=0x502` define to your project.\n\t\nAdding Pure Data external libraries to ofxPd\n--------------------------------------------\n\nofxPd only includes the standard set of Pure Data objects as found in the \"Vanilla\" version of Pd. If you wish to include an external library from Pd-extended, etc you need to include the source files in your project and call the library setup function after initializing ofxPd in order to load the lib.\n\n### Adding external source files\n\nThe source files for externals included with Pd-extended can be found in the Pure Data Git repositories: <http://git.puredata.info/cgit>. Other externals may be found elsewhere, including on GitHub.\n\nFor example, if we want to include the [zexy external](http://git.puredata.info/cgit/svn2git/libraries/zexy.git/) in your project, first download the sources files from the Git repository (make sure you have git installed):\n\n    git clone https://git.puredata.info/cgit/svn2git/libraries/zexy.git\n\nOnce cloned, the zexy sources are in `zexy/src/`. Copy the .h and .c files into your project folder. In my case I create an externals folder in the src folder of my project, something like `myProject/src/externals/zexy`. Then add these files to your project.\n\nNote: Some libraries may require external libraries of their own and/or special compile time definitions. Make sure you read the build documentation on the external and include these with your project. \n\n### Calling the external setup function\n\nIn order for ofxPd to use an external library, the library has to register itself on startup. This is accomplished by calling the library's setup function which is named after the library followed by a \"\\_setup\" suffix: \"library_setup()\". The zexy setup function is simply \"zexy_setup()\". Call this setup function after initializing ofxPd in your app's setup() function:\n~~~\nif(!pd.init(numOutChannels, numInChannels, sampleRate, ticksPerBuffer)) {\n\tOF_EXIT_APP(1);\n}\n\n// load libs\nzexy_setup();\n\n...\n~~~\n\nIf all goes well, you should see some sort of print from the library as it initializes:\n~~~\n[zexy] part of zexy-2.2.3 (compiled: Aug  7 2011)\n\tCopyright (l) 1999-2008 IOhannes m zmölnig, forum::für::umläute & IEM\n[&&~] part of zexy-2.2.3 (compiled: Aug  7 2011)\n\tCopyright (l) 1999-2008 IOhannes m zmölnig, forum::für::umläute & IEM\n[.] part of zexy-2.2.3 (compiled: Aug  7 2011)\n\tCopyright (l) 1999-2008 IOhannes m zmölnig, forum::für::umläute & IEM\n...\n~~~\n\nFor C++ and some C libraries, this is all your need. The project should compile and the external load fine. However, some pure C libraries are not written with explicit C++ support in mind and, for arcane reasons best not delved into here, the C++ compiler will not be able to find the library's setup function. This is the case with zexy and the compiler error looks like this:\n~~~\n'zexy_setup' was not declared in this scope\n~~~\n\nIn order for the C++ compiler to find the function, we need to add our own declaration. This can be done in your app .cpp file, a project header file, etc. In order to keep things organized, I create an \"Externals.h\" header file and place it in `myProject/src/externals`. Here I declare the \"zexy_setup()\" function using a special syntax:\n~~~\n#pragma once\n\nextern \"C\" {\n\tvoid zexy_setup();\n}\n~~~\n\nThe `extern \"C\"` keywords tell the compiler to look for a pure C function, not a C++ function. Make sure to include the \"Externals.h\" header file where you include \"ofxPd.h\". Add a setup function declaration for any other externals that need it here.\n\n### External library licensing on iOS\n\nApple's iOS and App Store policies forbid dynamically linking libraries. As such, you cannot include any GPL licensed externals as the GPL expressly requires dynamic linking. Submitting an app using a GPL library is in violation of the GPL and will most likely result in your app being rejected from distribution in the App Store.\n\nGPL patches, however, are not in violation of GPL distribution policies and can be included. They are not compiled into an application binary and can be replaced by the user.\n\nDeveloping ofxPd\n----------------\n\nYou can help develop ofxPd on GitHub: [https://github.com/danomatika/ofxPd](https://github.com/danomatika/ofxPd)\n\nCreate an account, clone or fork the repo, then request a push/merge.\n\nIf you find any bugs or suggestions please log them to GitHub as well.\n"
  },
  {
    "path": "addon_config.mk",
    "content": "# All variables and this file are optional, if they are not present the PG and the\n# makefiles will try to parse the correct values from the file system.\n#\n# Variables that specify exclusions can use % as a wildcard to specify that anything in\n# that position will match. A partial path can also be specified to, for example, exclude\n# a whole folder from the parsed paths from the file system\n#\n# Variables can be specified using = or +=\n# = will clear the contents of that variable both specified from the file or the ones parsed\n# from the file system\n# += will add the values to the previous ones in the file or the ones parsed from the file \n# system\n# \n# The PG can be used to detect errors in this file, just create a new project with this addon \n# and the PG will write to the console the kind of error and in which line it is\n\nmeta:\n\tADDON_NAME = ofxPd\n\tADDON_DESCRIPTION = Addon for running the Pure Data audio engine in an OF app\n\tADDON_AUTHOR = Dan Wilcox\n\tADDON_TAGS = \"sound\" \"audio\" \"computer music\"\n\tADDON_URL = http://github.com/danomatika/ofxPd\n\ncommon:\n\t# required for libpd\n\tADDON_CFLAGS = -DPD -DUSEAPI_DUMMY -DPD_INTERNAL -DHAVE_UNISTD_H -DHAVE_ALLOCA_H -DLIBPD_EXTRA\n\t# uncomment this for multiple instance support, ie. for pdMultiExample\n\t#ADDON_CFLAGS += -DPDINSTANCE -DPDTHREADS\n\t# this is included directly in pd~.c, don't build twice\n\tADDON_SOURCES_EXCLUDE = libs/libpd/pure-data/extra/pd~/binarymsg.c\n\nlinux64:\n\tADDON_LIBS_EXCLUDE = libs/libpd/libs\n\t# support dynamic loading\n\tADDON_CFLAGS += -DHAVE_LIBDL -DHAVE_ENDIAN_H\n\nlinux:\n\tADDON_LIBS_EXCLUDE = libs/libpd/libs\n\t# support dynamic loading\n\tADDON_CFLAGS += -DHAVE_LIBDL -DHAVE_ENDIAN_H\n\nlinuxarmv6l:\n\tADDON_LIBS_EXCLUDE = libs/libpd/libs\n\t# support dynamic loading\n\tADDON_CFLAGS += -DHAVE_LIBDL -DHAVE_ENDIAN_H\n\nlinuxarmv7l:\n\tADDON_LIBS_EXCLUDE = libs/libpd/libs\n\t# support dynamic loading\n\tADDON_CFLAGS += -DHAVE_LIBDL -DHAVE_ENDIAN_H\n\nmsys2:\n\t# support dynamic loading\n\tADDON_CFLAGS += -DHAVE_LIBDL\n\t# this assumes 64 bit builds only at this point...\n\tADDON_DLLS_TO_COPY = libs/libpd/libs/mingw64/libwinpthread-1.dll\n\nvs:\n\t# support dynamic loading\n\tADDON_CFLAGS += -DHAVE_LIBDL\n\t# this assumes 64 bit builds only at this point...\n\tADDON_DLLS_TO_COPY = libs/libpd/libs/mingw64/libwinpthread-1.dll\n\nandroid/armeabi:\n\tADDON_LIBS_EXCLUDE = libs/libpd/libs\n\t# support dynamic loading\n\tADDON_CFLAGS += -DHAVE_LIBDL -DHAVE_ENDIAN_H\n\nandroid/armeabi-v7a:\n\tADDON_LIBS_EXCLUDE = libs/libpd/libs\n\t# support dynamic loading\n\tADDON_CFLAGS += -DHAVE_LIBDL -DHAVE_ENDIAN_H\n\nosx:\n\tADDON_LIBS_EXCLUDE = libs/libpd/libs\n\t# support dynamic loading\n\tADDON_CFLAGS += -DHAVE_LIBDL -DHAVE_MACHINE_ENDIAN_H -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT\n\t# fix dispatch.h error with macOS SDK 10.13+\n\tADDON_CPPFLAGS += -DHAVE_UNISTD_H=1\n\nios:\n\tADDON_LIBS_EXCLUDE = libs/libpd/libs\n\t# set No common blocks option to avoid duplicate symbols link error\n\tADDON_CFLAGS += -fcommon -DHAVE_MACHINE_ENDIAN_H -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT\n\t# fix dispatch.h error with macOS SDK 10.13+\n\tADDON_CPPFLAGS += -DHAVE_UNISTD_H=1\n"
  },
  {
    "path": "doc/interface_sketch.txt",
    "content": "\nTYPES\n\n// message primitives\nBang(string recvName)\nFloat(string recvName, float value)\nSymbol(string recvName, string symbol)\n\n// compound message\nclass List {\n\n\tpublic:\n\n\t\tunsigned int len();\t// number of items\n\t\tstring types();\t\t// OSC style type string ie \"fssfffs\"\n\n\t\t// check type\n\t\tbool isFloat(int index=-1)\n\t\tbool isSymbol(int index=-1)\n\n\t\t// get as type\n\t\tfloat asFloat(int index=-1)\n\t\tstring asSymbol(int index=-1)\n\n\tprotected:\n\n\t\tList(string dest, int length, t_atom *items)\n\n\t\taddFloat(Float& f);\n\t\taddSymbol(Symbol& s); \n\n\tprivate:\n\n\t\tstring types;\n\t\tunsigned int length;\n\t\tt_atom *items;\n};\n\n// compound messages\nStartList(string recvName)\nStartMsg(string recvName, string symName)\n\n// midi\nNote(int pitch, int velocity, int channel=0)\nControlChange(int controller, int value, int channel=0)\nProgramChange(int program, int channel=0)\nPitchBend(int value, int channel=0)\nAftertouch(int value, int channel=0)\nPolyAftertouch(int pitch, int value, int channel=0)\n\n// compound raw midi\nStartMidi(int port=0)\nStartSysex(int port=0)\nStartSysexRealtime(int port=0)\n\n// finish any compound message\nFinish()\n\n\n\nSENDING\n\n// MESSAGES\n\n// single messages\npd.sendBang(\"test\");\npd.sendFloat(\"test\", 100);\npd.sendSymbol(\"test\", \"symbol1\");\npd << Bang(\"test\") << Float(\"test\", 100) << Symbol(\"test\", \"symbol1\");\n\n// compound messages\n\n// send a list\n//\n// [ 100 292.99 c string (\n// |\n// [ s test (\n//\npd.startList(\"test\");\npd.addFloat(100);\npd.addFloat(292.99);\npd.addSymbol(\"c\");\npd.addSymbol(\"string\");\npd.finish();\npd << StartMessage(\"test\") << 100 << 292.99 << 'c' << \"string\" << Finish();\n\n// send a typed message\n//\n// [; pd dsp 1 (\n//\npd.startMsg(\"pd\", \"dsp\");\npd.addFloat(1);\npd.finish();\npd << StartMsg(\"pd\", \"dsp\") << 1 << Finish();\n\n// MIDI\n\n// midi channels should be 1-16? ... same as patching\n\n// note\npd.sendNote(60);\npd.sendNote(60, 100);\npd.sendNote(60, 100, 0);\npd << Note(60) << Note(60, 100) << Note(60, 100, 0);\n\n// controls\npd << ControlChange(7, 100) << ControlChange(7, 100, 0);\t// 7 - Volume\npd << ProgamChange(40) << ProgramChange(40, 0);\t// 40 - Violin\npd << PitchBend(8000) << PitchBend(8000, 0);\t// value -8192 to 8192\npd << Aftertouch(100) << Aftertouch(100, 0);\npd << PolyAftertouch(60, 100) << PolyAftertouch(60, 100, 0);\n\n// raw midi\n// Noteon chan 0, Middle C, velocity\npd << StartMidi() << 0x90 << 0x3c << 0x40 << FinishBytes(); \npd << StartSysex() << 0xF0 << 0x7D << 0xF7 << FinishBytes();\npd << StartSysexRealtime() << 0xF0 << 0x7D << 0xF7 << FinishBytes();\n\n\n\nRECEIVING\n\npublic ofxPdListener {\n\n\tpublic:\n\n\t\tvoid printReceived(const std::string& message) {}\n\n\t\tvoid bangReceived(string dest) {}\n\t\tvoid floatReceived(string dest, float value) {}\n\t\tvoid symbolReceived(string dest, string symbol) {}\n\t\tvoid listReceived(string dest, const List& list) {}\n\t\tvoid messageReceived(string dest, string msg, const List& list) {}\n\t\t\n\t\tvoid noteReceived(int channel, int pitch, int velocity) {}\n\t\tvoid controlChangeReceived(int channel, int controller, int val) {}\n\t\tvoid programChangeReceived(int channel, int program) {}\n\t\tvoid pitchbendReceived(int channel, int val) {}\n\t\tvoid aftertouchReceived(int channel, int val) {}\n\t\tvoid polyAftertouchReceived(int channel, int pitch, int val) {}\n\t\t\n\t\tvoid midiByteReceived(int port, int byte) {}\n\t\tvoid sysexReceived(int port, int byte) {}\n\t\tvoid sysexRealtimeReceived(int port, int byte) {}\n};\n\npd.addListener(listener);\t// add listener to global space\npd.addListener(listener, \"source1\");\n\npd.addSource(\"source1\");\t\t// add \"test\" source\n\npd.subscribe(listener, \"source1\");\t// subscribe listener to \"test\",\n\t\t\t\t\t\t\t\t// adds both if not already added,\n\t\t\t\t\t\t\t\t// takes listener out of global space\n\npd.addSource(\"source2\");\npd.subscribe(listener, \"source2\");\n\n\npd.unsubscribe(listener, \"source2\");\n\npd.unsubscribe(listener, \"source1\");\t// unsubscribe listener to \"test'\n\n\npd.unsubscribe(listener);\t// unsubscribes listener from all sources, global\npd.removeListener(listener);\t\t// removes listener from global space\n\n\nofxPdMsg msg;\nmsg.makeBang();\npd.sendMsg(msg);\t\t// bang, empty message\n\nmsg.makeFloat(float);\npd.sendMsg(msg);\t\t// float\nmsg.clear();\n\nmsg.\n\nList list;\nlist << 100 << 292.99 << 'c' << \"string\";\npd.sendList(\"test\", list);\npd.sendMsg(\"test\", \"pd\", list);\npd.clear();\n\nofxPdBundle bundle;\nbundle.add(msg);\n\nofxPdMidiMsg midi;\nmsp << StartMidi() << 0x90 << 0x3c << 0x40 << FinishBytes(); \npd.sendMidi(midi);\n\n"
  },
  {
    "path": "doc/logo/libpd.pd",
    "content": "#N canvas 0 22 450 300 10;\n#X obj 137 96 inlet;\n#X obj 142 154 outlet;\n#X obj 277 141 outlet~;\n#X obj 273 94 inlet~;\n"
  },
  {
    "path": "doc/logo/logo.pd",
    "content": "#N canvas 0 22 460 266 12;\n#X obj 278 125 libpd;\n#X obj 154 125 openFrameworks;\n#X connect 0 0 1 1;\n#X connect 1 1 0 0;\n#X coords 0 -1 1 1 271 71 1 100 100;\n"
  },
  {
    "path": "doc/logo/openFrameworks.pd",
    "content": "#N canvas 0 22 450 300 10;\n#X obj 80 100 outlet;\n#X obj 81 36 inlet;\n#X obj 218 33 inlet;\n#X obj 217 98 outlet;\n"
  },
  {
    "path": "libs/libpd/cpp/LICENSE.txt",
    "content": "Copyright (c) 2012-2022 Dan Wilcox <danomatika@gmail.com>\nAll rights reserved.\n\nThe following terms (the \"Standard Improved BSD License\") apply to all\nfiles associated with the software unless explicitly disclaimed in\nindividual files:\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n1. Redistributions of source code must retain the above copyright\n   notice, this list of conditions and the following disclaimer.\n2. Redistributions in binary form must reproduce the above\n   copyright notice, this list of conditions and the following\n   disclaimer in the documentation and/or other materials provided\n   with the distribution.\n3. The name of the author may not be used to endorse or promote\n   products derived from this software without specific prior\n   written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE AUTHOR \"AS IS\" AND ANY\nEXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A\nPARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR\nBE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\nEXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\nTO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\nON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\nLIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING\nIN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF\nTHE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "libs/libpd/cpp/PdBase.hpp",
    "content": "/*\n * Copyright (c) 2012-2022 Dan Wilcox <danomatika@gmail.com>\n *\n * BSD Simplified License.\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.\n *\n * See https://github.com/libpd/libpd for documentation\n *\n * This file was originally written for the ofxPd openFrameworks addon:\n * https://github.com/danomatika/ofxPd\n *\n */\n#pragma once\n\n#include \"z_libpd.h\"\n#include \"z_queued.h\"\n#include \"z_print_util.h\"\n\n#include <map>  \n\n#include \"PdTypes.hpp\"\n#include \"PdReceiver.hpp\"\n#include \"PdMidiReceiver.hpp\"\n\n// needed for libpd audio passing\n#ifndef USEAPI_DUMMY\n    #define USEAPI_DUMMY\n#endif\n\n#ifndef HAVE_UNISTD_H\n    #define HAVE_UNISTD_H\n#endif\n\n#ifdef PDINSTANCE\n    #define PDBASE_SETINSTANCE libpd_set_instance(instance);\n#else\n    #define PDBASE_SETINSTANCE\n#endif\n\ntypedef struct _atom t_atom;\n\nnamespace pd {\n\n/// a Pure Data instance\n///\n/// use this class directly or extend it and any of its virtual functions\n///\n/// by default, each PdBase instance refers to the single main libpd\n/// instance so it is recommended to use only one PdBase at a time\n///\n/// as of version 0.13, libpd supports multiple instances if compiled with\n/// PDINSTANCE defined, in which case each PdBase instance can act separately\n/// with it's own PdReceiver and PdMidiReceiver\n///\nclass PdBase {\n\npublic:\n\n    PdBase() {\n        bMsgInProgress = false;\n        maxMsgLen = 32;\n        curMsgLen = 0;\n        msgType = MSG;\n        midiPort = 0;\n        receiver = NULL;\n        midiReceiver = NULL;\n        bInited = false;\n        bQueued = false;\n        libpd_init();\n        #ifdef PDINSTANCE\n            instance = libpd_new_instance();\n        #endif\n        libpd_set_instancedata(this, NULL);\n    }\n\n    virtual ~PdBase() {\n        PDBASE_SETINSTANCE\n        libpd_set_instancedata(NULL, NULL);\n        #ifdef PDINSTANCE\n            libpd_set_instance(instance);\n            libpd_free_instance(instance);\n        #endif\n    }\n\n/// \\section Initializing Pd\n\n    /// initialize resources and set up the audio processing\n    ///\n    /// set the audio latency by setting the libpd ticks per buffer:\n    /// ticks per buffer * lib pd block size (always 64)\n    ///\n    /// ie 4 ticks per buffer * 64 = buffer len of 512\n    ///\n    /// you can call this again after loading patches & setting receivers\n    /// in order to update the audio settings\n    ///\n    /// the lower the number of ticks, the faster the audio processing\n    /// if you experience audio dropouts (audible clicks), increase the\n    /// ticks per buffer\n    ///\n    /// set queued = true to use the built in ringbuffers for message and\n    /// midi event passing, you will then need to call receiveMessages() and\n    /// receiveMidi() in order to pass messages from the ringbuffers to your\n    /// PdReceiver and PdMidiReceiver implementations\n    ///\n    /// the queued ringbuffers are useful when you need to receive events\n    /// on a gui thread and don't want to use locking\n    ///\n    /// return true if inited successfully\n    ///\n    /// note: must be called before processing\n    ///\n    virtual bool init(const int numInChannels, const int numOutChannels,\n                      const int sampleRate, bool queued=false) {\n        PDBASE_SETINSTANCE\n\n        // attach callbacks\n        bQueued = queued;\n        if(queued) {\n            libpd_queued_init();\n\n            libpd_set_queued_printhook(libpd_print_concatenator);\n            libpd_set_concatenated_printhook(_print);\n\n            libpd_set_queued_banghook(_bang);\n            libpd_set_queued_floathook(_float);\n            libpd_set_queued_symbolhook(_symbol);\n            libpd_set_queued_listhook(_list);\n            libpd_set_queued_messagehook(_message);\n\n            libpd_set_queued_noteonhook(_noteon);\n            libpd_set_queued_controlchangehook(_controlchange);\n            libpd_set_queued_programchangehook(_programchange);\n            libpd_set_queued_pitchbendhook(_pitchbend);\n            libpd_set_queued_aftertouchhook(_aftertouch);\n            libpd_set_queued_polyaftertouchhook(_polyaftertouch);\n            libpd_set_queued_midibytehook(_midibyte);\n        }\n        else {\n            libpd_set_printhook(libpd_print_concatenator);\n            libpd_set_concatenated_printhook(_print);\n\n            libpd_set_banghook(_bang);\n            libpd_set_floathook(_float);\n            libpd_set_symbolhook(_symbol);\n            libpd_set_listhook(_list);\n            libpd_set_messagehook(_message);\n\n            libpd_set_noteonhook(_noteon);\n            libpd_set_controlchangehook(_controlchange);\n            libpd_set_programchangehook(_programchange);\n            libpd_set_pitchbendhook(_pitchbend);\n            libpd_set_aftertouchhook(_aftertouch);\n            libpd_set_polyaftertouchhook(_polyaftertouch);\n            libpd_set_midibytehook(_midibyte);\n        }\n\n        // init audio\n        if(libpd_init_audio(numInChannels,\n                            numOutChannels,\n                            sampleRate) != 0) {\n            return false;\n        }\n        bInited = true;\n\n        return bInited;\n    }\n\n    /// clear resources\n    virtual void clear() {\n        PDBASE_SETINSTANCE\n\n        // detach callbacks\n        if(bInited) {\n            computeAudio(false);\n            if(bQueued) {\n                libpd_set_queued_printhook(NULL);\n                libpd_set_concatenated_printhook(NULL);\n\n                libpd_set_queued_banghook(NULL);\n                libpd_set_queued_floathook(NULL);\n                libpd_set_queued_symbolhook(NULL);\n                libpd_set_queued_listhook(NULL);\n                libpd_set_queued_messagehook(NULL);\n\n                libpd_set_queued_noteonhook(NULL);\n                libpd_set_queued_controlchangehook(NULL);\n                libpd_set_queued_programchangehook(NULL);\n                libpd_set_queued_pitchbendhook(NULL);\n                libpd_set_queued_aftertouchhook(NULL);\n                libpd_set_queued_polyaftertouchhook(NULL);\n                libpd_set_queued_midibytehook(NULL);\n\n                libpd_queued_release();\n            }\n            else {\n                libpd_set_printhook(NULL);\n                libpd_set_concatenated_printhook(NULL);\n\n                libpd_set_banghook(NULL);\n                libpd_set_floathook(NULL);\n                libpd_set_symbolhook(NULL);\n                libpd_set_listhook(NULL);\n                libpd_set_messagehook(NULL);\n\n                libpd_set_noteonhook(NULL);\n                libpd_set_controlchangehook(NULL);\n                libpd_set_programchangehook(NULL);\n                libpd_set_pitchbendhook(NULL);\n                libpd_set_aftertouchhook(NULL);\n                libpd_set_polyaftertouchhook(NULL);\n                libpd_set_midibytehook(NULL);\n            }\n        }\n        bInited = false;\n        bQueued = false;\n\n        bMsgInProgress = false;\n        curMsgLen = 0;\n        msgType = MSG;\n        midiPort = 0;\n\n        unsubscribeAll();\n    }\n\n/// \\section Adding Search Paths\n\n    /// add to the pd search path\n    /// takes an absolute or relative path (in data folder)\n    ///\n    /// note: fails silently if path not found\n    ///\n    virtual void addToSearchPath(const std::string &path) {\n        PDBASE_SETINSTANCE\n        libpd_add_to_search_path(path.c_str());\n    }\n\n    /// clear the current pd search path\n    virtual void clearSearchPath() {\n        PDBASE_SETINSTANCE\n        libpd_clear_search_path();\n    }\n\n/// \\section Opening Patches\n\n    /// open a patch file (aka somefile.pd) at a specified parent dir path\n    /// returns a pd::Patch object\n    ///\n    /// use pd::Patch::isValid() to check if a patch was opened successfully:\n    ///\n    ///     pd::Patch p1 = pd.openPatch(\"somefile.pd\", \"/some/dir/path/\");\n    ///     if(!p1.isValid()) {\n    ///         std::cout << \"aww ... p1 couldn't be opened\" << std::endl;\n    ///     }\n    virtual pd::Patch openPatch(const std::string &patch,\n                                const std::string &path) {\n        PDBASE_SETINSTANCE\n        // [; pd open file folder(\n        void *handle = libpd_openfile(patch.c_str(), path.c_str());\n        if(handle == NULL) {\n            return Patch(); // return empty Patch\n        }\n        int dollarZero = libpd_getdollarzero(handle);\n        return Patch(handle, dollarZero, patch, path);\n    }\n\n    /// open a patch file using the filename and path of an existing patch\n    ///\n    /// set the filename within the patch object or use a previously opened\n    /// object\n    ///\n    ///     // open an instance of \"somefile.pd\"\n    ///     pd::Patch p2(\"somefile.pd\", \"/some/path\"); // set file and path\n    ///     pd.openPatch(p2);\n    ///\n    ///     // open a new instance of \"somefile.pd\"\n    ///     pd::Patch p3 = pd.openPatch(p2);\n    ///\n    ///     // p2 and p3 refer to 2 different instances of \"somefile.pd\"\n    ///\n    virtual pd::Patch openPatch(pd::Patch &patch) {\n        return openPatch(patch.filename(), patch.path());\n    }\n\n    /// close a patch file\n    /// takes only the patch's basename (filename without extension)\n    virtual void closePatch(const std::string &patch) {\n        PDBASE_SETINSTANCE\n        // [; pd-name menuclose 1(\n        std::string patchname = (std::string)\"pd-\" + patch;\n        libpd_start_message(1);\n        libpd_add_float(1);\n        libpd_finish_message(patchname.c_str(), \"menuclose\");\n    }\n\n    /// close a patch file, takes a patch object\n    /// note: clears the given Patch object\n    virtual void closePatch(pd::Patch &patch) {\n        if(!patch.isValid()) {\n            return;\n        }\n        PDBASE_SETINSTANCE\n        libpd_closefile(patch.handle());\n        patch.clear();\n    }\n\n/// \\section Audio Processing\n///\n/// one of these must be called for audio dsp and message io to occur\n///\n/// inBuffer must be an array of the right size and never null\n/// use inBuffer = new type[0] if no input is desired\n///\n/// outBuffer must be an array of size outBufferSize from openAudio call\n///\n/// note: raw does not interlace the buffers\n///\n\n    /// process float buffers for a given number of ticks\n    /// returns false on error\n    bool processFloat(int ticks, const float *inBuffer, float *outBuffer) {\n        PDBASE_SETINSTANCE\n        return libpd_process_float(ticks, inBuffer, outBuffer) == 0;\n    }\n\n    /// process short buffers for a given number of ticks\n    /// returns false on error\n    bool processShort(int ticks, const short *inBuffer, short *outBuffer) {\n        PDBASE_SETINSTANCE\n        return libpd_process_short(ticks, inBuffer, outBuffer) == 0;\n    }\n\n    /// process double buffers for a given number of ticks\n    /// returns false on error\n    bool processDouble(int ticks, const double *inBuffer, double *outBuffer) {\n        PDBASE_SETINSTANCE\n        return libpd_process_double(ticks, inBuffer, outBuffer) == 0;\n    }\n\n    /// process one pd tick, writes raw float data to/from buffers\n    /// returns false on error\n    bool processRaw(const float *inBuffer, float *outBuffer) {\n        PDBASE_SETINSTANCE\n        return libpd_process_raw(inBuffer, outBuffer) == 0;\n    }\n\n    /// process one pd tick, writes raw short data to/from buffers\n    /// returns false on error\n    bool processRawShort(const short *inBuffer, short *outBuffer) {\n        PDBASE_SETINSTANCE\n        return libpd_process_raw_short(inBuffer, outBuffer) == 0;\n    }\n\n    /// process one pd tick, writes raw double data to/from buffers\n    /// returns false on error\n    bool processRawDouble(const double *inBuffer, double *outBuffer) {\n        PDBASE_SETINSTANCE\n        return libpd_process_raw_double(inBuffer, outBuffer) == 0;\n    }\n\n/// \\section Audio Processing Control\n\n    /// start/stop audio processing\n    ///\n    /// in general, once started, you won't need to turn off audio\n    ///\n    /// shortcut for [; pd dsp 1( & [; pd dsp 0(\n    ///\n    virtual void computeAudio(bool state) {\n        PDBASE_SETINSTANCE\n        // [; pd dsp $1(\n        libpd_start_message(1);\n        libpd_add_float((float) state);\n        libpd_finish_message(\"pd\", \"dsp\");\n    }\n\n/// \\section Message Receiving\n\n\n    /// subscribe to messages sent by a pd send source\n    ///\n    /// aka this like a virtual pd receive object\n    ///\n    ///     [r source]\n    ///     |\n    ///\n    virtual void subscribe(const std::string &source) {\n        if(exists(source)) {\n            std::cerr << \"Pd: unsubscribe: ignoring duplicate source\"\n                      << std::endl;\n            return;\n        }\n        PDBASE_SETINSTANCE\n        void *pointer = libpd_bind(source.c_str());\n        if(pointer != NULL) {\n            sources.insert(std::pair<std::string,void*>(source, pointer));\n        }\n    }\n\n    /// unsubscribe from messages sent by a pd send source\n    virtual void unsubscribe(const std::string &source) {\n        std::map<std::string,void*>::iterator iter;\n        iter = sources.find(source);\n        if(iter == sources.end()) {\n            std::cerr << \"Pd: unsubscribe: ignoring unknown source\"\n                      << std::endl;\n            return;\n        }\n        PDBASE_SETINSTANCE\n        libpd_unbind(iter->second);\n        sources.erase(iter);\n    }\n\n    /// is a pd send source subscribed?\n    virtual bool exists(const std::string &source) {\n        PDBASE_SETINSTANCE\n        if(sources.find(source) != sources.end()) {\n            return true;\n        }\n        return false;\n    }\n\n    //// receivers will be unsubscribed from *all* pd send sources\n    virtual void unsubscribeAll() {\n        PDBASE_SETINSTANCE\n        std::map<std::string,void*>::iterator iter;\n        for(iter = sources.begin(); iter != sources.end(); ++iter) {\n            libpd_unbind(iter->second);\n        }\n        sources.clear();\n    }\n\n/// \\section Receiving from the Message Queues\n///\n/// process the internal message queue if using the ringbuffer\n///\n/// internally, libpd will use a ringbuffer to pass messages & midi without\n/// needing to require locking if you call init() with queued = true\n///\n/// call these in a loop somewhere in order to receive waiting messages\n/// or midi data which are then sent to your PdReceiver & PdMidiReceiver\n///\n/// *do not* use if inited with queued = false\n\n    /// process waiting messages\n    virtual void receiveMessages() {\n        PDBASE_SETINSTANCE\n        libpd_queued_receive_pd_messages();\n    }\n\n    /// process waiting midi messages\n    virtual void receiveMidi() {\n        PDBASE_SETINSTANCE\n        libpd_queued_receive_midi_messages();\n    }\n\n/// \\section Event Receiving via Callbacks\n\n    /// set the incoming event receiver, disables the event queue\n    ///\n    /// automatically receives from all currently subscribed sources\n    ///\n    /// set this to NULL to disable callback receiving and re-enable the\n    /// event queue\n    ///\n    void setReceiver(pd::PdReceiver *receiver) {\n        this->receiver = receiver;\n    }\n\n/// \\section Midi Receiving via Callbacks\n\n    /// set the incoming midi event receiver, disables the midi queue\n    ///\n    /// automatically receives from all midi channels\n    ///\n    /// set this to NULL to disable midi events and re-enable the midi queue\n    ///\n    void setMidiReceiver(pd::PdMidiReceiver *midiReceiver) {\n        this->midiReceiver = midiReceiver;\n    }\n\n/// \\section Send Functions\n\n    /// send a bang message\n    virtual void sendBang(const std::string &dest) {\n        PDBASE_SETINSTANCE\n        libpd_bang(dest.c_str());\n    }\n\n    /// send a float\n    virtual void sendFloat(const std::string &dest, float value) {\n        PDBASE_SETINSTANCE\n        libpd_float(dest.c_str(), value);\n    }\n\n    /// send a symbol\n    virtual void sendSymbol(const std::string &dest,\n                            const std::string &symbol) {\n        PDBASE_SETINSTANCE\n        libpd_symbol(dest.c_str(), symbol.c_str());\n    }\n\n/// \\section Sending Compound Messages\n///\n///     pd.startMessage();\n///     pd.addSymbol(\"hello\");\n///     pd.addFloat(1.23);\n///     pd.finishList(\"test\"); // \"test\" is the receiver name in pd\n///\n/// sends [list hello 1.23( -> [r test],\n/// you will need to use the [list trim] object on the receiving end\n///\n/// finishMsg sends a typed message -> [; test msg1 hello 1.23(\n///\n///     pd.startMessage();\n///     pd.addSymbol(\"hello\");\n///     pd.addFloat(1.23);\n///     pd.finishMessage(\"test\", \"msg1\");\n///\n\n    /// start a compound list or message\n    virtual void startMessage() {\n        if(bMsgInProgress) {\n            std::cerr << \"Pd: cannot start message, message in progress\"\n                      << std::endl;\n            return;\n        }\n        PDBASE_SETINSTANCE\n        if(libpd_start_message(maxMsgLen) == 0) {\n            bMsgInProgress = true;\n            msgType = MSG;\n        }\n    }\n\n    /// add a float to the current compound list or message\n    virtual void addFloat(const float num) {\n        if(!bMsgInProgress) {\n            std::cerr << \"Pd: cannot add float, message not in progress\"\n                      << std::endl;\n            return;\n        }\n        if(msgType != MSG) {\n            std::cerr << \"Pd: cannot add float, midi byte stream in progress\"\n                      << std::endl;\n            return;\n        }\n        if(curMsgLen+1 >= maxMsgLen) {\n            std::cerr << \"Pd: cannot add float, max message len of \"\n                      << maxMsgLen << \" reached\" << std::endl;\n            return;\n        }\n        PDBASE_SETINSTANCE\n        libpd_add_float(num);\n        curMsgLen++;\n    }\n\n    /// add a symbol to the current compound list or message\n    virtual void addSymbol(const std::string &symbol) {\n        if(!bMsgInProgress) {\n            std::cerr << \"Pd: cannot add symbol, message not in progress\"\n                      << std::endl;\n            return;\n        }\n        if(msgType != MSG) {\n            std::cerr << \"Pd: cannot add symbol, midi byte stream in progress\"\n                      << std::endl;\n            return;\n        }\n        if(curMsgLen+1 >= maxMsgLen) {\n            std::cerr << \"Pd: cannot add symbol, max message len of \"\n                      << maxMsgLen << \" reached\" << std::endl;\n            return;\n        }\n        PDBASE_SETINSTANCE\n        libpd_add_symbol(symbol.c_str());\n        curMsgLen++;\n    }\n\n    /// finish and send as a list\n    virtual void finishList(const std::string &dest) {\n        if(!bMsgInProgress) {\n            std::cerr << \"Pd: cannot finish list, \"\n                      << \"message not in progress\" << std::endl;\n            return;\n        }\n        if(msgType != MSG) {\n            std::cerr << \"Pd: cannot finish list, \"\n                      << \"midi byte stream in progress\" << std::endl;\n            return;\n        }\n        PDBASE_SETINSTANCE\n        libpd_finish_list(dest.c_str());\n        bMsgInProgress = false;\n        curMsgLen = 0;\n    }\n\n    /// finish and send as a list with a specific message name\n    virtual void finishMessage(const std::string &dest,\n                               const std::string &msg) {\n        if(!bMsgInProgress) {\n            std::cerr << \"Pd: cannot finish message, \"\n                      << \"message not in progress\" << std::endl;\n            return;\n        }\n        if(msgType != MSG) {\n            std::cerr << \"Pd: cannot finish message, \"\n                      << \"midi byte stream in progress\" << std::endl;\n            return;\n        }\n        PDBASE_SETINSTANCE\n        libpd_finish_message(dest.c_str(), msg.c_str());\n        bMsgInProgress = false;\n        curMsgLen = 0;\n    }\n\n    /// send a list using the pd::List type\n    ///\n    ///     pd::List list;\n    ///     list.addSymbol(\"hello\");\n    ///     list.addFloat(1.23);\n    ///     pd.sendList(\"test\", list);\n    ///\n    /// sends [list hello 1.23( -> [r test]\n    ///\n    /// stream operators work as well:\n    ///\n    ///     list << \"hello\" << 1.23;\n    ///     pd.sendList(\"test\", list);\n    ///\n    virtual void sendList(const std::string &dest, const pd::List &list) {\n        if(bMsgInProgress) {\n            std::cerr << \"Pd: cannot send list, message in progress\"\n                      << std::endl;\n            return;\n        }\n        PDBASE_SETINSTANCE\n        libpd_start_message(list.len());\n        bMsgInProgress = true;\n        // step through list\n        for(int i = 0; i < (int)list.len(); ++i) {\n            if(list.isFloat(i))\n                addFloat(list.getFloat(i));\n            else if(list.isSymbol(i))\n                addSymbol(list.getSymbol(i));\n        }\n        finishList(dest);\n    }\n\n    /// send a message using the pd::List type\n    ///\n    ///     pd::List list;\n    ///     list.addSymbol(\"hello\");\n    ///     list.addFloat(1.23);\n    ///     pd.sendMessage(\"test\", \"msg1\", list);\n    ///\n    /// sends a typed message -> [; test msg1 hello 1.23(\n    ///\n    /// stream operators work as well:\n    ///\n    //      list << \"hello\" << 1.23;\n    ///     pd.sendMessage(\"test\", \"msg1\", list);\n    ///\n    virtual void sendMessage(const std::string &dest,\n                             const std::string &msg,\n                             const pd::List &list = pd::List()) {\n        if(bMsgInProgress) {\n            std::cerr << \"Pd: cannot send message, message in progress\"\n                      << std::endl;\n            return;\n        }\n        PDBASE_SETINSTANCE\n        libpd_start_message(list.len());\n        bMsgInProgress = true;\n        // step through list\n        for(int i = 0; i < (int)list.len(); ++i) {\n            if(list.isFloat(i))\n                addFloat(list.getFloat(i));\n            else if(list.isSymbol(i))\n                addSymbol(list.getSymbol(i));\n        }\n        finishMessage(dest, msg);\n    }\n\n/// \\section Sending MIDI\n///\n/// any out of range messages will be silently ignored\n///\n/// number ranges:\n/// * channel             0 - 15 * dev# (dev #0: 0-15, dev #1: 16-31, etc)\n/// * pitch               0 - 127\n/// * velocity            0 - 127\n/// * controller value    0 - 127\n/// * program value       0 - 127\n/// * bend value          -8192 - 8191\n/// * touch value         0 - 127\n///\n\n    /// send a MIDI note on\n    ///\n    /// pd does not use note off MIDI messages, so send a note on with vel = 0\n    ///\n    virtual void sendNoteOn(const int channel,\n                            const int pitch,\n                            const int velocity=64) {\n        PDBASE_SETINSTANCE\n        libpd_noteon(channel, pitch, velocity);\n    }\n\n    /// send a MIDI control change\n    virtual void sendControlChange(const int channel,\n                                   const int controller,\n                                   const int value) {\n        PDBASE_SETINSTANCE\n        libpd_controlchange(channel, controller, value);\n    }\n\n    /// send a MIDI program change\n    virtual void sendProgramChange(const int channel, const int value) {\n        PDBASE_SETINSTANCE\n        libpd_programchange(channel, value);\n    }\n\n    /// send a MIDI pitch bend\n    ///\n    /// in pd: [bendin] takes 0 - 16383 while [bendout] returns -8192 - 8192\n    ///\n    virtual void sendPitchBend(const int channel, const int value) {\n        PDBASE_SETINSTANCE\n        libpd_pitchbend(channel, value);\n    }\n\n    /// send a MIDI aftertouch\n    virtual void sendAftertouch(const int channel, const int value) {\n        PDBASE_SETINSTANCE\n        libpd_aftertouch(channel, value);\n    }\n\n    /// send a MIDI poly aftertouch\n    virtual void sendPolyAftertouch(const int channel,\n                                    const int pitch,\n                                    const int value) {\n        PDBASE_SETINSTANCE\n        libpd_polyaftertouch(channel, pitch, value);\n    }\n\n    /// send a raw MIDI byte\n    ///\n    /// value is a raw midi byte value 0 - 255\n    /// port is the raw portmidi port #, similar to a channel\n    ///\n    /// for some reason, [midiin], [sysexin] & [realtimein] add 2 to the\n    /// port num, so sending to port 1 in PdBase returns port 3 in pd\n    ///\n    /// however, [midiout], [sysexout], & [realtimeout] do not add to the\n    /// port num, so sending port 1 to [midiout] returns port 1 in PdBase\n    ///\n    virtual void sendMidiByte(const int port, const int value) {\n        PDBASE_SETINSTANCE\n        libpd_midibyte(port, value);\n    }\n\n    /// send a raw MIDI sysex byte\n    virtual void sendSysex(const int port, const int value) {\n        PDBASE_SETINSTANCE\n        libpd_sysex(port, value);\n    }\n\n    /// send a raw MIDI realtime byte\n    virtual void sendSysRealTime(const int port, const int value) {\n        PDBASE_SETINSTANCE\n        libpd_sysrealtime(port, value);\n    }\n\n/// \\section Stream Interface\n///\n/// single messages\n///\n///     pd << Bang(\"test\"); /// \"test\" is the receiver name in pd\n///     pd << Float(\"test\", 100);\n///     pd << Symbol(\"test\", \"a symbol\");\n///\n\n    /// send a bang message\n    PdBase& operator<<(const pd::Bang &var) {\n        if(bMsgInProgress) {\n            std::cerr << \"Pd: cannot send Bang, message in progress\"\n                      << std::endl;\n            return *this;\n        }\n        sendBang(var.dest.c_str());\n        return *this;\n    }\n\n    /// send a float message\n    PdBase& operator<<(const pd::Float &var) {\n        if(bMsgInProgress) {\n            std::cerr << \"Pd: cannot send Float, message in progress\"\n                      << std::endl;\n            return *this;\n        }\n        sendFloat(var.dest.c_str(), var.num);\n        return *this;\n    }\n\n    /// send a symbol message\n    PdBase& operator<<(const pd::Symbol &var) {\n        if(bMsgInProgress) {\n            std::cerr << \"Pd: cannot send Symbol, message in progress\"\n                      << std::endl;\n            return *this;\n        }\n        sendSymbol(var.dest.c_str(), var.symbol.c_str());\n        return *this;\n    }\n\n/// \\section Stream Interface for Compound Messages\n///\n/// pd << pd::StartMessage() << 100 << 1.2 << \"a symbol\" << pd::FinishList(\"test\");\n///\n\n    /// start a compound message\n    PdBase& operator<<(const pd::StartMessage &) {\n        startMessage();\n        return *this;\n    }\n\n    /// finish a compound message and send it as a list\n    PdBase& operator<<(const pd::FinishList &var) {\n        finishList(var.dest);\n        return *this;\n    }\n\n    /// finish a compound message and send it as a message\n    PdBase& operator<<(const pd::FinishMessage &var) {\n        finishMessage(var.dest, var.msg);\n        return *this;\n    }\n\n    // add a boolean as a float to the compound message\n    PdBase& operator<<(const bool var) {\n        addFloat((float) var);\n        return *this;\n    }\n\n    // add an integer as a float to the compound message\n    PdBase& operator<<(const int var) {\n        switch(msgType) {\n            case MSG:\n                addFloat((float) var);\n                break;\n            case MIDI:\n                sendMidiByte(midiPort, var);\n                break;\n            case SYSEX:\n                sendSysex(midiPort, var);\n                break;\n            case SYSRT:\n                sendSysRealTime(midiPort, var);\n                break;\n        }\n        return *this;\n    }\n\n    // add a float to the compound message\n    PdBase& operator<<(const float var) {\n        addFloat((float) var);\n        return *this;\n    }\n\n    // add a double as a float to the compound message\n    PdBase& operator<<(const double var) {\n        addFloat((float) var);\n        return *this;\n    }\n\n    // add a character as a symbol to the compound message\n    PdBase& operator<<(const char var) {\n        std::string s;\n        s = var;\n        addSymbol(s);\n        return *this;\n    }\n\n    // add a C-string char buffer as a symbol to the compound message\n    PdBase& operator<<(const char *var) {\n        addSymbol((std::string)var);\n        return *this;\n    }\n\n    // add a string as a symbol to the compound message\n    PdBase& operator<<(const std::string &var) {\n        addSymbol(var);\n        return *this;\n    }\n\n/// \\section Stream Interface for MIDI\n///\n/// pd << pd::NoteOn(64) << NoteOn(64, 60) << pd::NoteOn(64, 60, 1);\n/// pd << pd::ControlChange(100, 64) << pd::ProgramChange(100, 1);\n/// pd << pd::Aftertouch(127, 1) << pd::PolyAftertouch(64, 127, 1);\n/// pd << pd::PitchBend(2000, 1);\n///\n\n    /// send a MIDI note on\n    PdBase& operator<<(const pd::NoteOn &var) {\n        sendNoteOn(var.channel, var.pitch, var.velocity);\n        return *this;\n    }\n\n    /// send a MIDI control change\n    PdBase& operator<<(const pd::ControlChange &var) {\n        sendControlChange(var.channel, var.controller, var.value);\n        return *this;\n    }\n\n    /// send a MIDI program change\n    PdBase& operator<<(const pd::ProgramChange &var) {\n        sendProgramChange(var.channel, var.value);\n        return *this;\n    }\n\n    /// send a MIDI pitch bend\n    PdBase& operator<<(const pd::PitchBend &var) {\n        sendPitchBend(var.channel, var.value);\n        return *this;\n    }\n\n    /// send a MIDI aftertouch\n    PdBase& operator<<(const pd::Aftertouch &var) {\n        sendAftertouch(var.channel, var.value);\n        return *this;\n    }\n\n    /// send a MIDI poly aftertouch\n    PdBase& operator<<(const pd::PolyAftertouch &var) {\n        sendPolyAftertouch(var.channel, var.pitch, var.value);\n        return *this;\n    }\n\n/// \\section Stream Interface for Raw Bytes\n///\n/// pd << pd::StartMidi() << 0xEF << 0x45 << pd::Finish();\n/// pd << pd::StartSysex() << 0xE7 << 0x45 << 0x56 << 0x17 << pd::Finish();\n///\n\n    /// start a raw byte MIDI message\n    PdBase& operator<<(const pd::StartMidi &var) {\n        if(bMsgInProgress) {\n            std::cerr << \"Pd: cannot start MidiByte stream, \"\n                      << \"message in progress\" << std::endl;\n            return *this;\n        }\n        bMsgInProgress = true;\n        msgType = MIDI;\n        midiPort = var.port;\n        return *this;\n    }\n\n    /// start a raw byte MIDI sysex message\n    PdBase& operator<<(const pd::StartSysex &var) {\n        if(bMsgInProgress) {\n            std::cerr << \"Pd: cannot start Sysex stream, \"\n                      << \"message in progress\" << std::endl;\n            return *this;\n        }\n        bMsgInProgress = true;\n        msgType = SYSEX;\n        midiPort = var.port;\n        return *this;\n    }\n\n    /// start a raw byte MIDI realtime message\n    PdBase& operator<<(const pd::StartSysRealTime &var) {\n        if(bMsgInProgress) {\n            std::cerr << \"Pd: cannot start SysRealRime stream, \"\n                      << \"message in progress\" << std::endl;\n            return *this;\n        }\n        bMsgInProgress = true;\n        msgType = SYSRT;\n        midiPort = var.port;\n        return *this;\n    }\n\n    /// finish and send a raw byte MIDI message\n    PdBase& operator<<(const pd::Finish &) {\n        if(!bMsgInProgress) {\n            std::cerr << \"Pd: cannot finish midi byte stream, \"\n                      << \"stream not in progress\" << std::endl;\n            return *this;\n        }\n        if(msgType == MSG) {\n            std::cerr << \"Pd: cannot finish midi byte stream, \"\n                      << \"message in progress\" << std::endl;\n            return *this;\n        }\n        bMsgInProgress = false;\n        curMsgLen = 0;\n        return *this;\n    }\n\n    /// is a message or byte stream currently in progress?\n    bool isMessageInProgress() {\n        return bMsgInProgress;\n    }\n\n/// \\section Array Access\n\n    /// get the size of a pd array\n    /// returns 0 if array not found\n    int arraySize(const std::string &name) {\n        PDBASE_SETINSTANCE\n        int len = libpd_arraysize(name.c_str());\n        if(len < 0) {\n            std::cerr << \"Pd: cannot get size of unknown array \\\"\"\n                      << name << \"\\\"\" << std::endl;\n            return 0;\n        }\n        return len;\n    }\n\n    /// (re)size a pd array\n    /// sizes <= 0 are clipped to 1\n    /// returns true on success, false on failure\n    bool resizeArray(const std::string &name, long size) {\n        PDBASE_SETINSTANCE\n        int ret = libpd_resize_array(name.c_str(), size);\n        if(ret < 0) {\n            std::cerr << \"Pd: cannot resize unknown array \\\"\" << name << \"\\\"\"\n                      << std::endl;\n            return false;\n        }\n        return true;\n    }\n\n    /// read from a pd array\n    ///\n    /// resizes given vector to readLen, checks readLen and offset\n    ///\n    /// returns true on success, false on failure\n    ///\n    /// calling without setting readLen and offset reads the whole array:\n    ///\n    /// std::vector<float> array1;\n    /// readArray(\"array1\", array1);\n    ///\n    virtual bool readArray(const std::string &name,\n                           std::vector<float> &dest,\n                           int readLen=-1, int offset=0) {\n        PDBASE_SETINSTANCE\n        int len = libpd_arraysize(name.c_str());\n        if(len < 0) {\n            std::cerr << \"Pd: cannot read unknown array \\\"\" << name << \"\\\"\"\n                      << std::endl;\n            return false;\n        }\n        // full array len?\n        if(readLen < 0) {\n            readLen = len;\n        }\n        // check read len\n        else if(readLen > len) {\n            std::cerr << \"Pd: given read len \" << readLen << \" > len \"\n                      << len << \" of array \\\"\" << name << \"\\\"\" << std::endl;\n            return false;\n        }\n        // check offset\n        if(offset + readLen > len) {\n            std::cerr << \"Pd: given read len and offset > len \" << readLen\n                      << \" of array \\\"\" << name << \"\\\"\" << std::endl;\n            return false;\n        }\n        // resize if necessary\n        if(dest.size() != (std::size_t)readLen) {\n            dest.resize(readLen, 0);\n        }\n        if(libpd_read_array(&dest[0], name.c_str(), offset, readLen) < 0) {\n            std::cerr << \"Pd: libpd_read_array failed for array \\\"\"\n                      << name << \"\\\"\" << std::endl;\n            return false;\n        }\n        return true;\n    }\n\n    /// write to a pd array\n    ///\n    /// calling without setting writeLen and offset writes the whole array:\n    ///\n    /// writeArray(\"array1\", array1);\n    ///\n    virtual bool writeArray(const std::string &name,\n                            std::vector<float> &source,\n                            int writeLen=-1, int offset=0) {\n        PDBASE_SETINSTANCE\n        int len = libpd_arraysize(name.c_str());\n        if(len < 0) {\n            std::cerr << \"Pd: cannot write to unknown array \\\"\" << name << \"\\\"\"\n                      << std::endl;\n            return false;\n        }\n\n        // full array len?\n        if(writeLen < 0) {\n            writeLen = len;\n        }\n\n        // check write len\n        else if(writeLen > len) {\n            std::cerr << \"Pd: given write len \" << writeLen << \" > len \" << len\n                      << \" of array \\\"\" << name << \"\\\"\" << std::endl;\n            return false;\n        }\n\n        // check offset\n        if(offset+writeLen > len) {\n            std::cerr << \"Pd: given write len and offset > len \" << writeLen\n                      << \" of array \\\"\" << name << \"\\\"\" << std::endl;\n            return false;\n        }\n\n        if(libpd_write_array(name.c_str(), offset,\n                             &source[0], writeLen) < 0) {\n            std::cerr << \"Pd: libpd_write_array failed for array \\\"\"\n                      << name << \"\\\"\" << std::endl;\n            return false;\n        }\n        return true;\n    }\n\n    /// clear array and set to a specific value\n    virtual void clearArray(const std::string &name, int value=0) {\n        PDBASE_SETINSTANCE\n        int len = libpd_arraysize(name.c_str());\n        if(len < 0) {\n            std::cerr << \"Pd: cannot clear unknown array \\\"\"\n                      << name << \"\\\"\" << std::endl;\n            return;\n        }\n        std::vector<float> array;\n        array.resize(len, value);\n        if(libpd_write_array(name.c_str(), 0, &array[0], len) < 0) {\n            std::cerr << \"Pd: libpd_write_array failed while clearing array \\\"\"\n                      << name << \"\\\"\" << std::endl;\n        }\n    }\n\n/// \\section Utils\n\n    /// has the global pd instance been initialized?\n    bool isInited() {\n        return bInited;\n    }\n\n    /// is the global pd instance using the ringbuffer queue\n    /// for message padding?\n    bool isQueued() {\n        return bQueued;\n    }\n\n    /// get the blocksize of pd (sample length per channel)\n    static int blockSize() {\n        return libpd_blocksize();\n    }\n\n    /// set the max length of messages and lists, default: 32\n    void setMaxMessageLen(unsigned int len) {\n        maxMsgLen = len;\n    }\n\n    /// get the max length of messages and lists\n    unsigned int maxMessageLen() {\n        return maxMsgLen;\n    }\n\n    /// get the pd instance pointer\n    /// returns main instance when libpd is not compiled with PDINSTANCE\n    t_pdinstance *instancePtr() {\n        #ifdef PDINSTANCE\n            return instance;\n        #endif\n        return libpd_main_instance();\n    }\n\n    /// get the number of pd instances, including the main instance\n    /// returns number or 1 when libpd is not compiled with PDINSTANCE\n    static int numInstances() {\n        return libpd_num_instances();\n    }\n\nprotected:\n\n    /// compound message status\n    enum MsgType {\n        MSG,\n        MIDI,\n        SYSEX,\n        SYSRT\n    };\n\n/// \\section Variables\n\n    bool bMsgInProgress;    ///< is a compound message being constructed?\n    int maxMsgLen;          ///< maximum allowed message length\n    int curMsgLen;          ///< the length of the current message\n\n    /// compound message status\n    PdBase::MsgType msgType;\n\n    int midiPort;   ///< target midi port\n\n    std::map<std::string,void*> sources; ///< subscribed sources\n\n    pd::PdReceiver *receiver;            ///< the message receiver\n    pd::PdMidiReceiver *midiReceiver;    ///< the midi receiver\n\n#ifdef PDINSTANCE\n    t_pdinstance *instance; ///< instance pointer\n#endif\n\nprotected:\n\n    bool bInited; ///< is this pd instance inited?\n    bool bQueued; ///< is this instance using the libpd_queued ringbuffer?\n\n    // libpd static callback functions\n    static void _print(const char *s) {\n        PdBase *base = (PdBase *)libpd_get_instancedata();\n        if(base->receiver) {\n            base->receiver->print((std::string)s);\n        }\n    }\n\n    static void _bang(const char *source) {\n        PdBase *base = (PdBase *)libpd_get_instancedata();\n        if(base->receiver) {\n            base->receiver->receiveBang((std::string)source);\n        }\n    }\n\n    static void _float(const char *source, float value) {\n        PdBase *base = (PdBase *)libpd_get_instancedata();\n        if(base->receiver) {\n            base->receiver->receiveFloat((std::string)source, value);\n        }\n    }\n\n    static void _symbol(const char *source, const char *symbol) {\n        PdBase *base = (PdBase *)libpd_get_instancedata();\n        if(base->receiver) {\n            base->receiver->receiveSymbol((std::string)source,\n                                          (std::string)symbol);\n        }\n    }\n\n    static void _list(const char *source, int argc, t_atom *argv) {\n        PdBase *base = (PdBase *)libpd_get_instancedata();\n        pd::List list;\n        for(int i = 0; i < argc; i++) {\n            t_atom a = argv[i];\n            if(a.a_type == A_FLOAT) {\n                float f = a.a_w.w_float;\n                list.addFloat(f);\n            }\n            else if(a.a_type == A_SYMBOL) {\n                const char *s = a.a_w.w_symbol->s_name;\n                list.addSymbol((std::string)s);\n            }\n        }\n        if(base->receiver) {\n            base->receiver->receiveList((std::string)source, list);\n        }\n    }\n\n    static void _message(const char *source, const char *symbol,\n                         int argc, t_atom *argv) {\n        PdBase *base = (PdBase *)libpd_get_instancedata();\n        pd::List list;\n        for(int i = 0; i < argc; i++) {\n            t_atom a = argv[i];\n            if(a.a_type == A_FLOAT) {\n                float f = a.a_w.w_float;\n                list.addFloat(f);\n            }\n            else if(a.a_type == A_SYMBOL) {\n                const char *s = a.a_w.w_symbol->s_name;\n                list.addSymbol((std::string)s);\n            }\n        }\n        if(base->receiver) {\n            base->receiver->receiveMessage((std::string)source,\n                                           (std::string)symbol, list);\n        }\n    }\n\n    static void _noteon(int channel, int pitch, int velocity) {\n        PdBase *base = (PdBase *)libpd_get_instancedata();\n        if(base->midiReceiver) {\n            base->midiReceiver->receiveNoteOn(channel, pitch, velocity);\n        }\n    }\n\n    static void _controlchange(int channel, int controller, int value) {\n        PdBase *base = (PdBase *)libpd_get_instancedata();\n        if(base->midiReceiver) {\n            base->midiReceiver->receiveControlChange(channel,\n                                                     controller,\n                                                     value);\n        }\n    }\n\n    static void _programchange(int channel, int value) {\n        PdBase *base = (PdBase *)libpd_get_instancedata();\n        if(base->midiReceiver) {\n            base->midiReceiver->receiveProgramChange(channel, value);\n        }\n    }\n\n    static void _pitchbend(int channel, int value) {\n        PdBase *base = (PdBase *)libpd_get_instancedata();\n        if(base->midiReceiver) {\n            base->midiReceiver->receivePitchBend(channel, value);\n        }\n    }\n\n    static void _aftertouch(int channel, int value) {\n        PdBase *base = (PdBase *)libpd_get_instancedata();\n        if(base->midiReceiver) {\n            base->midiReceiver->receiveAftertouch(channel, value);\n        }\n    }\n\n    static void _polyaftertouch(int channel, int pitch, int value) {\n        PdBase *base = (PdBase *)libpd_get_instancedata();\n        if(base->midiReceiver) {\n            base->midiReceiver->receivePolyAftertouch(channel,\n                                                      pitch,\n                                                      value);\n        }\n    }\n\n    static void _midibyte(int port, int byte) {\n        PdBase *base = (PdBase *)libpd_get_instancedata();\n        if(base->midiReceiver) {\n            base->midiReceiver->receiveMidiByte(port, byte);\n        }\n    }\n};\n\n} // namespace\n"
  },
  {
    "path": "libs/libpd/cpp/PdMidiReceiver.hpp",
    "content": "/*\n * Copyright (c) 2012-2022 Dan Wilcox <danomatika@gmail.com>\n *\n * BSD Simplified License.\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.\n *\n * See https://github.com/libpd/libpd for documentation\n *\n * This file was originally written for the ofxPd openFrameworks addon:\n * https://github.com/danomatika/ofxPd\n *\n */\n#pragma once\n\nnamespace pd {\n\n/// a pd MIDI receiver base class\nclass PdMidiReceiver {\n\npublic:\n\n    virtual ~PdMidiReceiver() {}\n\n    /// receive a MIDI note on\n    virtual void receiveNoteOn(const int /*channel*/,\n                               const int /*pitch*/,\n                               const int /*velocity*/) {}\n\n    /// receive a MIDI control change\n    virtual void receiveControlChange(const int /*channel*/,\n                                      const int /*controller*/,\n                                      const int /*value*/) {}\n\n    /// receive a MIDI program change,\n    /// note: pgm value is 1-128\n    virtual void receiveProgramChange(const int /*channel*/, const int /*value*/) {}\n\n    /// receive a MIDI pitch bend\n    virtual void receivePitchBend(const int /*channel*/, const int /*value*/) {}\n\n    /// receive a MIDI aftertouch message\n    virtual void receiveAftertouch(const int /*channel*/, const int /*value*/) {}\n\n    /// receive a MIDI poly aftertouch message\n    virtual void receivePolyAftertouch(const int /*channel*/,\n                                       const int /*pitch*/,\n                                       const int /*value*/) {}\n\n    /// receive a raw MIDI byte (sysex, realtime, etc)\n    virtual void receiveMidiByte(const int /*port*/, const int /*byte*/) {}\n};\n\n} // namespace\n"
  },
  {
    "path": "libs/libpd/cpp/PdReceiver.hpp",
    "content": "/*\n * Copyright (c) 2012-2022 Dan Wilcox <danomatika@gmail.com>\n *\n * BSD Simplified License.\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.\n *\n * See https://github.com/libpd/libpd for documentation\n *\n * This file was originally written for the ofxPd openFrameworks addon:\n * https://github.com/danomatika/ofxPd\n *\n */\n#pragma once\n\n#include \"PdTypes.hpp\"\n\nnamespace pd {\n\n/// a pd message receiver base class\nclass PdReceiver {\n\npublic:\n\n    virtual ~PdReceiver() {}\n\n    /// receive a print\n    virtual void print(const std::string &/*message*/) {}\n\n    /// receive a bang\n    virtual void receiveBang(const std::string &/*dest*/) {}\n\n    /// receive a float\n    virtual void receiveFloat(const std::string &/*dest*/, float /*num*/) {}\n\n    /// receive a symbol\n    virtual void receiveSymbol(const std::string &/*dest*/,\n                               const std::string &/*symbol*/) {}\n\n    /// receive a list\n    virtual void receiveList(const std::string &/*dest*/, const pd::List &/*list*/) {}\n\n    /// receive a named message ie. sent from a message box like:\n    /// [; dest msg arg1 arg2 arg3(\n    virtual void receiveMessage(const std::string &/*dest*/,\n                                const std::string &/*msg*/,\n                                const pd::List &/*list*/) {}\n};\n\n} // namespace\n"
  },
  {
    "path": "libs/libpd/cpp/PdTypes.hpp",
    "content": "/*\n * Copyright (c) 2012-2022 Dan Wilcox <danomatika@gmail.com>\n *\n * BSD Simplified License.\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.\n *\n * See https://github.com/libpd/libpd for documentation\n *\n * This file was originally written for the ofxPd openFrameworks addon:\n * https://github.com/danomatika/ofxPd\n *\n */\n#pragma once\n\n#include <string>\n#include <vector>\n#include <iostream>\n#include <sstream>\n\nnamespace pd {\n\n/// \\section Pd Patch\n\n/// a pd patch\n///\n/// if you use the copy constructor/operator, keep in mind the libpd void*\n/// pointer patch handle is copied and problems can arise if one object is used\n/// to close a patch that other copies may be referring to\nclass Patch {\n\npublic:\n\n    Patch() :\n        _handle(NULL), _dollarZero(0), _dollarZeroStr(\"0\"),\n        _filename(\"\"), _path(\"\") {}\n\n    Patch(const std::string &filename, const std::string &path) :\n        _handle(NULL), _dollarZero(0), _dollarZeroStr(\"0\"),\n        _filename(filename), _path(path) {}\n\n    Patch(void *handle, int dollarZero,\n          const std::string &filename,\n          const std::string &path) :\n        _handle(handle), _dollarZero(dollarZero),\n        _filename(filename), _path(path) {\n            std::stringstream itoa;\n            itoa << dollarZero;\n            _dollarZeroStr = itoa.str();\n        }\n\n    /// get the raw pointer to the patch instance\n    void* handle() const {return _handle;}\n\n    /// get the unqiue instance $0 ID\n    int dollarZero() const {return _dollarZero;}\n\n    /// get the patch filename\n    std::string filename() const {return _filename;}\n\n    /// get the parent dir path for the file\n    std::string path() const {return _path;}\n\n    /// get the unique instance $0 ID as a string\n    std::string dollarZeroStr() const {return _dollarZeroStr;}\n\n    /// is the patch pointer valid?\n    bool isValid() const {return _handle != NULL;}\n\n    /// clear patch pointer and dollar zero (does not close patch!)\n    ///\n    /// note: does not clear filename and path so the object can be reused\n    //        for opening multiple instances\n    void clear()  {\n        _handle = NULL;\n        _dollarZero = 0;\n        _dollarZeroStr = \"0\";\n    }\n\n    /// copy constructor\n    Patch(const Patch &from) {\n        _handle = from._handle;\n        _dollarZero = from._dollarZero;\n        _dollarZeroStr = from._dollarZeroStr;\n        _filename = from._filename;\n        _path = from._path;\n    }\n\n    /// copy operator\n    void operator=(const Patch &from) {\n        _handle = from._handle;\n        _dollarZero = from._dollarZero;\n        _dollarZeroStr = from._dollarZeroStr;\n        _filename = from._filename;\n        _path = from._path;\n    }\n\n    /// print info to ostream\n    friend std::ostream& operator<<(std::ostream &os, const Patch &from) {\n        return os << \"Patch: \\\"\" << from.filename() << \"\\\" $0: \"\n                  << from.dollarZeroStr() << \" valid: \" << from.isValid();\n    }\n\nprivate:\n\n    void *_handle;              ///< patch handle pointer\n    int _dollarZero;            ///< the unique $0 patch ID\n    std::string _dollarZeroStr; ///< $0 as a string\n\n    std::string _filename;      ///< filename\n    std::string _path;          ///< full path to parent folder\n};\n\n/// \\section Pd stream interface message objects\n\n/// bang event\nstruct Bang {\n\n    const std::string dest; ///< dest receiver name\n\n    explicit Bang(const std::string &dest) : dest(dest) {}\n};\n\n/// float value\nstruct Float {\n\n    const std::string dest; ///< dest receiver name\n    const float num;        ///< the float value\n\n    Float(const std::string &dest, const float num) :\n        dest(dest), num(num) {}\n};\n\n/// symbol value\nstruct Symbol {\n\n    const std::string dest;   ///< dest receiver name\n    const std::string symbol; ///< the symbol value\n\n    Symbol(const std::string &dest, const std::string &symbol) :\n        dest(dest), symbol(symbol) {}\n};\n\n/// a compound message containing floats and symbols\nclass List {\n\npublic:\n\n    List() {}\n\n/// \\section Read\n\n    /// check if index is a float type\n    bool isFloat(const unsigned int index) const {\n        if(index < objects.size())\n            if(objects[index].type == List::FLOAT)\n                return true;\n        return false;\n    }\n\n    /// check if index is a symbol type\n    bool isSymbol(const unsigned int index) const {\n        if(index < objects.size())\n            if(objects[index].type == List::SYMBOL)\n                return true;\n        return false;\n    }\n\n    /// get index as a float\n    float getFloat(const unsigned int index) const {\n        if(!isFloat(index)) {\n            std::cerr << \"Pd: List object \" << index << \" is not a float\"\n                      << std::endl;\n            return 0;\n        }\n        return objects[index].value;\n    }\n\n    /// get index as a symbol\n    std::string getSymbol(const unsigned int index) const {\n        if(!isSymbol(index)) {\n            std::cerr << \"Pd: List object \" << index << \" is not a symbol\"\n                      << std::endl;\n            return \"\";\n        }\n        return objects[index].symbol;\n    }\n\n/// \\section Write\n///\n/// add elements to the list\n///\n/// List list;\n/// list.addSymbol(\"hello\");\n/// list.addFloat(1.23);\n///\n\n    /// add a float to the list\n    void addFloat(const float num) {\n        MsgObject o;\n        o.type = List::FLOAT;\n        o.value = num;\n        objects.push_back(o);\n        typeString += 'f';\n    }\n\n    /// add a symbol to the list\n    void addSymbol(const std::string &symbol) {\n        MsgObject o;\n        o.type = List::SYMBOL;\n        o.symbol = symbol;\n        objects.push_back(o);\n        typeString += 's';\n    }\n\n/// \\section Write Stream Interface\n///\n/// list << \"hello\" << 1.23;\n///\n\n    /// add a float to the message\n    List& operator<<(const bool var) {\n        addFloat((float) var);\n        return *this;\n    }\n\n    /// add a float to the message\n    List& operator<<(const int var) {\n        addFloat((float) var);\n        return *this;\n    }\n\n    /// add a float to the message\n    List& operator<<(const float var) {\n        addFloat((float) var);\n        return *this;\n    }\n\n    /// add a float to the message\n    List& operator<<(const double var) {\n        addFloat((float) var);\n        return *this;\n    }\n\n    /// add a symbol to the message\n    List& operator<<(const char var) {\n        std::string s;\n        s = var;\n        addSymbol(s);\n        return *this;\n    }\n\n    /// add a symbol to the message\n    List& operator<<(const char *var) {\n        addSymbol((std::string) var);\n        return *this;\n    }\n\n    /// add a symbol to the message\n    List& operator<<(const std::string &var) {\n        addSymbol((std::string) var);\n        return *this;\n    }\n\n/// \\section Util\n\n    /// return number of items\n    unsigned int len() const {return (unsigned int) objects.size();}\n\n    /// return OSC style type string ie \"fsfs\"\n    const std::string& types() const {return typeString;}\n\n    /// clear all objects\n    void clear() {\n        typeString = \"\";\n        objects.clear();\n    }\n\n    /// get list as a string\n    std::string toString() const {\n        std::string line;\n        std::stringstream itoa;\n        for(int i = 0; i < (int)objects.size(); ++i) {\n            if(isFloat(i)) {\n                itoa << getFloat(i);\n                line += itoa.str();\n                itoa.str(\"\");\n            }\n            else {\n                line += getSymbol(i);\n            }\n            if(i < (int)objects.size()-1) {\n                line += \" \";\n            }\n        }\n        return line;\n    }\n\n    /// print to ostream\n    friend std::ostream& operator<<(std::ostream &os, const List &from) {\n        return os << from.toString();\n    }\n\nprivate:\n\n    std::string typeString; ///< OSC style type string\n\n    // object type\n    enum MsgType {\n        FLOAT,\n        SYMBOL\n    };\n\n    // object wrapper\n    struct MsgObject {\n        MsgType type;\n        float value;\n        std::string symbol;\n    };\n\n    std::vector<MsgObject> objects; ///< list objects\n};\n\n/// start a compound message\nstruct StartMessage {\n    explicit StartMessage() {}\n};\n\n/// finish a compound message as a list\nstruct FinishList {\n\n    const std::string dest; ///< dest receiver name\n\n    explicit FinishList(const std::string &dest) : dest(dest) {}\n};\n\n/// finish a compound message as a typed message\nstruct FinishMessage {\n\n    const std::string dest; ///< dest receiver name\n    const std::string msg;  ///< target msg at the dest\n\n    FinishMessage(const std::string &dest, const std::string &msg) :\n            dest(dest), msg(msg) {}\n};\n\n/// \\section Pd stream interface midi objects\n/// ref: http://www.gweep.net/~prefect/eng/reference/protocol/midispec.html\n\n/// send a note on event (set vel = 0 for noteoff)\nstruct NoteOn {\n\n    const int channel;  ///< channel (0 - 15 * dev#)\n    const int pitch;    ///< pitch (0 - 127)\n    const int velocity; ///< velocity (0 - 127)\n\n    NoteOn(const int channel, const int pitch, const int velocity=64) :\n        channel(channel), pitch(pitch), velocity(velocity) {}\n};\n\n/// change a control value aka send a CC message\nstruct ControlChange {\n\n    const int channel;    ///< channel (0 - 15 * dev#)\n    const int controller; ///< controller (0 - 127)\n    const int value;      ///< value (0 - 127)\n\n    ControlChange(const int channel, const int controller, const int value) :\n        channel(channel), controller(controller), value(value) {}\n};\n\n/// change a program value (ie an instrument)\nstruct ProgramChange {\n\n    const int channel; ///< channel (0 - 15 * dev#)\n    const int value;   ///< value (0 - 127)\n\n    ProgramChange(const int channel, const int value) :\n        channel(channel), value(value) {}\n};\n\n/// change the pitch bend value\nstruct PitchBend {\n\n    const int channel; ///< channel (0 - 15 * dev#)\n    const int value;   ///< value (-8192 - 8192)\n\n    PitchBend(const int channel, const int value) :\n        channel(channel), value(value) {}\n};\n\n/// change an aftertouch value\nstruct Aftertouch {\n\n    const int channel; ///< channel (0 - 15 * dev#)\n    const int value;   ///< value (0 - 127)\n\n    Aftertouch(const int channel, const int value) :\n        channel(channel), value(value) {}\n};\n\n/// change a poly aftertouch value\nstruct PolyAftertouch {\n\n    const int channel; ///< channel (0 - 15 * dev#)\n    const int pitch;   ///< pitch (0 - 127)\n    const int value;   ///< value (0 - 127)\n\n    PolyAftertouch(const int channel, const int pitch, const int value) :\n        channel(channel), pitch(pitch), value(value) {}\n};\n\n/// a raw midi byte\nstruct MidiByte {\n\n    const int port; ///< raw portmidi port\n                    ///< see http://en.wikipedia.org/wiki/PortMidi\n    const unsigned char byte; ///< the raw midi byte value\n\n    MidiByte(const int port, unsigned char byte) : port(port), byte(byte) {}\n};\n\n/// start a raw midi byte stream\nstruct StartMidi {\n\n    const int port; ///< raw portmidi port\n\n    explicit StartMidi(const int port=0) : port(port) {}\n};\n\n/// start a raw sysex byte stream\nstruct StartSysex {\n\n    const int port; ///< raw portmidi port\n\n    explicit StartSysex(const int port=0) : port(port) {}\n};\n\n/// start a sys realtime byte stream\nstruct StartSysRealTime {\n\n    const int port; ///< raw portmidi port\n\n    explicit StartSysRealTime(const int port=0) : port(port) {}\n};\n\n/// finish a midi byte stream\nstruct Finish {\n    explicit Finish() {}\n};\n\n} // namespace\n"
  },
  {
    "path": "libs/libpd/libpd_wrapper/s_libpdmidi.c",
    "content": "/* Copyright (c) 1997-2010 Miller Puckette and others.\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n#include \"m_pd.h\"\n#include \"z_libpd.h\"\n#include \"z_hooks.h\"\n\n#define CLAMP(x, low, high) ((x > high) ? high : ((x < low) ? low : x))\n#define CLAMP4BIT(x) CLAMP(x, 0, 0x0f)\n#define CLAMP7BIT(x) CLAMP(x, 0, 0x7f)\n#define CLAMP8BIT(x) CLAMP(x, 0, 0xff)\n#define CLAMP12BIT(x) CLAMP(x, 0, 0x0fff)\n#define CLAMP14BIT(x) CLAMP(x, 0, 0x3fff)\n\n#define CHANNEL ((CLAMP12BIT(port) << 4) | CLAMP4BIT(channel))\n\nvoid outmidi_noteon(int port, int channel, int pitch, int velo) {\n  t_libpdimp *imp = LIBPDSTUFF;\n  if (imp && imp->i_hooks.h_noteonhook)\n    imp->i_hooks.h_noteonhook(CHANNEL, CLAMP7BIT(pitch), CLAMP7BIT(velo));\n}\n\nvoid outmidi_controlchange(int port, int channel, int ctl, int value) {\n  t_libpdimp *imp = LIBPDSTUFF;\n  if (imp && imp->i_hooks.h_controlchangehook)\n    imp->i_hooks.h_controlchangehook(CHANNEL, CLAMP7BIT(ctl), CLAMP7BIT(value));\n}\n\nvoid outmidi_programchange(int port, int channel, int value) {\n  t_libpdimp *imp = LIBPDSTUFF;\n  if (imp && imp->i_hooks.h_programchangehook)\n    imp->i_hooks.h_programchangehook(CHANNEL, CLAMP7BIT(value));\n}\n\nvoid outmidi_pitchbend(int port, int channel, int value) {\n  t_libpdimp *imp = LIBPDSTUFF;\n  if (imp && imp->i_hooks.h_pitchbendhook)\n    imp->i_hooks.h_pitchbendhook(CHANNEL, CLAMP14BIT(value) - 8192); // remove offset\n}\n\nvoid outmidi_aftertouch(int port, int channel, int value) {\n  t_libpdimp *imp = LIBPDSTUFF;\n  if (imp && imp->i_hooks.h_aftertouchhook)\n    imp->i_hooks.h_aftertouchhook(CHANNEL, CLAMP7BIT(value));\n}\n\nvoid outmidi_polyaftertouch(int port, int channel, int pitch, int value) {\n  t_libpdimp *imp = LIBPDSTUFF;\n  if (imp && imp->i_hooks.h_polyaftertouchhook)\n    imp->i_hooks.h_polyaftertouchhook(CHANNEL, CLAMP7BIT(pitch), CLAMP7BIT(value));\n}\n\nvoid outmidi_byte(int port, int value) {\n  t_libpdimp *imp = LIBPDSTUFF;\n  if (imp && imp->i_hooks.h_midibytehook)\n    imp->i_hooks.h_midibytehook(CLAMP12BIT(port), CLAMP8BIT(value));\n}\n\n/* tell Pd GUI that our list of MIDI APIs is empty */\n#include <string.h>\nvoid sys_get_midi_apis(char *buf) {strcpy(buf, \"{}\");}\n\n// the rest is not relevant to libpd\nvoid sys_listmididevs(void) {}\nvoid sys_get_midi_params(int *pnmidiindev, int *pmidiindev,\n    int *pnmidioutdev, int *pmidioutdev) {*pnmidiindev = *pnmidioutdev = 0;}\nvoid sys_open_midi(int nmidiindev, int *midiindev,\n    int nmidioutdev, int *midioutdev, int enable) {}\nvoid sys_close_midi() {}\nvoid sys_reopen_midi(void) {}\nvoid sys_initmidiqueue(void) {}\nvoid sys_pollmidiqueue(void) {}\nvoid sys_setmiditimediff(double inbuftime, double outbuftime) {}\nvoid glob_midi_setapi(void *dummy, t_floatarg f) {}\nvoid glob_midi_properties(t_pd *dummy, t_floatarg flongform) {}\nvoid glob_midi_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv) {}\nint sys_mididevnametonumber(int output, const char *name) { return 0; }\nvoid sys_mididevnumbertoname(int output, int devno, char *name, int namesize) {}\nvoid sys_set_midi_api(int api) {}\nvoid sys_gui_midipreferences(void) {}\nint sys_midiapi;\n"
  },
  {
    "path": "libs/libpd/libpd_wrapper/util/ringbuffer.c",
    "content": "/*\n *  Copyright (c) 2012 Peter Brinkmann (peter.brinkmann@gmail.com)\n *\n *  For information on usage and redistribution, and for a DISCLAIMER OF ALL\n *  WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.\n *\n * See https://github.com/libpd/libpd/wiki for documentation\n *\n */\n\n#include \"ringbuffer.h\"\n\n#include <stdarg.h>\n#include <stdlib.h>\n#include <string.h>\n\n#if __STDC_VERSION__ >= 201112L // use stdatomic if C11 is available\n  #include <stdatomic.h>\n  #define SYNC_FETCH(ptr) atomic_fetch_or((_Atomic int *)ptr, 0)\n  #define SYNC_COMPARE_AND_SWAP(ptr, oldval, newval) \\\n          atomic_compare_exchange_strong((_Atomic int *)ptr, &oldval, newval)\n#else // use platform specfics\n  #ifdef __APPLE__ // apple atomics\n    #include <libkern/OSAtomic.h>\n    #define SYNC_FETCH(ptr) OSAtomicOr32Barrier(0, (volatile uint32_t *)ptr)\n    #define SYNC_COMPARE_AND_SWAP(ptr, oldval, newval) \\\n            OSAtomicCompareAndSwap32Barrier(oldval, newval, ptr)\n  #elif defined(_WIN32) || defined(_WIN64) // win api atomics\n    #include <windows.h>\n    #define SYNC_FETCH(ptr) InterlockedOr(ptr, 0)\n    #define SYNC_COMPARE_AND_SWAP(ptr, oldval, newval) \\\n            InterlockedCompareExchange(ptr, newval, oldval)\n  #else // gcc atomics\n    #define SYNC_FETCH(ptr) __sync_fetch_and_or(ptr, 0)\n    #define SYNC_COMPARE_AND_SWAP(ptr, oldval, newval) \\\n            __sync_val_compare_and_swap(ptr, oldval, newval)\n  #endif\n#endif\n\nring_buffer *rb_create(int size) {\n  if (size & 0xff) return NULL;  // size must be a multiple of 256\n  ring_buffer *buffer = malloc(sizeof(ring_buffer));\n  if (!buffer) return NULL;\n  buffer->buf_ptr = calloc(size, sizeof(char));\n  if (!buffer->buf_ptr) {\n    free(buffer);\n    return NULL;\n  }\n  buffer->size = size;\n  buffer->write_idx = 0;\n  buffer->read_idx = 0;\n  return buffer;\n}\n\nvoid rb_free(ring_buffer *buffer) {\n  free(buffer->buf_ptr);\n  free(buffer);\n}\n\nint rb_available_to_write(ring_buffer *buffer) {\n  if (buffer) {\n    // note: the largest possible result is buffer->size - 1 because\n    // we adopt the convention that read_idx == write_idx means that the\n    // buffer is empty\n    int read_idx = SYNC_FETCH(&(buffer->read_idx));\n    int write_idx = SYNC_FETCH(&(buffer->write_idx));\n    return (buffer->size + read_idx - write_idx - 1) % buffer->size;\n  } else {\n    return 0;\n  }\n}\n\nint rb_available_to_read(ring_buffer *buffer) {\n  if (buffer) {\n    int read_idx = SYNC_FETCH(&(buffer->read_idx));\n    int write_idx = SYNC_FETCH(&(buffer->write_idx));\n    return (buffer->size + write_idx - read_idx) % buffer->size;\n  } else {\n    return 0;\n  }\n}\n\nint rb_write_to_buffer(ring_buffer *buffer, int n, ...) {\n  if (!buffer) return -1;\n  int write_idx = buffer->write_idx;  // no need for sync in writer thread\n  int available = rb_available_to_write(buffer);\n  va_list args;\n  va_start(args, n);\n  int i;\n  for (i = 0; i < n; ++i) {\n    const char* src = va_arg(args, const char*);\n    int len = va_arg(args, int);\n    available -= len;\n    if (len < 0 || available < 0) return -1;\n    if (write_idx + len <= buffer->size) {\n      memcpy(buffer->buf_ptr + write_idx, src, len);\n    } else {\n      int d = buffer->size - write_idx;\n      memcpy(buffer->buf_ptr + write_idx, src, d);\n      memcpy(buffer->buf_ptr, src + d, len - d);\n    }\n    write_idx = (write_idx + len) % buffer->size;\n  }\n  va_end(args);\n  SYNC_COMPARE_AND_SWAP(&(buffer->write_idx), buffer->write_idx,\n      write_idx);  // includes memory barrier\n  return 0;\n}\n\nint rb_write_value_to_buffer(ring_buffer *buffer, int value, int n) {\n  if (!buffer) return -1;\n  int write_idx = buffer->write_idx;  // No need for sync in writer thread.\n  int available = rb_available_to_write(buffer);\n  available -= n;\n  if (n < 0 || available < 0) return -1;\n  if (write_idx + n <= buffer->size) {\n    memset(buffer->buf_ptr + write_idx, value, n);\n  } else {\n    int d = buffer->size - write_idx;\n    memset(buffer->buf_ptr + write_idx, value, d);\n    memset(buffer->buf_ptr, value, n - d);\n  }\n  write_idx = (write_idx + n) % buffer->size;\n  SYNC_COMPARE_AND_SWAP(&(buffer->write_idx), buffer->write_idx,\n    write_idx);  // includes memory barrier\n  return 0;\n}\n\nint rb_read_from_buffer(ring_buffer *buffer, char *dest, int len) {\n  if (len == 0) return 0;\n  if (!buffer || len < 0 || len > rb_available_to_read(buffer)) return -1;\n  // note that rb_available_to_read also serves as a memory barrier, and so any\n  // writes to buffer->buf_ptr that precede the update of buffer->write_idx are\n  // visible to us now\n  int read_idx = buffer->read_idx;  // no need for sync in reader thread\n  if (read_idx + len <= buffer->size) {\n    memcpy(dest, buffer->buf_ptr + read_idx, len);\n  } else {\n    int d = buffer->size - read_idx;\n    memcpy(dest, buffer->buf_ptr + read_idx, d);\n    memcpy(dest + d, buffer->buf_ptr, len - d);\n  }\n  SYNC_COMPARE_AND_SWAP(&(buffer->read_idx), buffer->read_idx,\n       (read_idx + len) % buffer->size);  // includes memory barrier\n  return 0;\n}\n\n// simply reset the indices\nvoid rb_clear_buffer(ring_buffer *buffer) {\n  if (buffer) {\n  SYNC_COMPARE_AND_SWAP(&(buffer->read_idx), buffer->read_idx, 0);\n  SYNC_COMPARE_AND_SWAP(&(buffer->write_idx), buffer->write_idx, 0);\n  }\n}\n"
  },
  {
    "path": "libs/libpd/libpd_wrapper/util/ringbuffer.h",
    "content": "/*\n *  Copyright (c) 2012 Peter Brinkmann (peter.brinkmann@gmail.com)\n *\n *  For information on usage and redistribution, and for a DISCLAIMER OF ALL\n *  WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.\n *\n * See https://github.com/libpd/libpd/wiki for documentation\n *\n */\n\n#ifndef __Z_RING_BUFFER_H__\n#define __Z_RING_BUFFER_H__\n\n/// simple lock-free ring buffer implementation for one writer thread\n/// and one consumer thread\ntypedef struct ring_buffer {\n    int size;\n    char *buf_ptr;\n    int write_idx;\n    int read_idx;\n} ring_buffer;\n\n/// create a ring buffer, size must be a multiple of 256\n/// returns NULL on failure\nring_buffer *rb_create(int size);\n\n/// free a ring buffer\nvoid rb_free(ring_buffer *buffer);\n\n/// get the number of bytes that can currently be written\n/// this is safe to call from any thread\nint rb_available_to_write(ring_buffer *buffer);\n\n/// get the number of bytes that can currently be read\n/// this is safe to called from any thread\nint rb_available_to_read(ring_buffer *buffer);\n\n/// write bytes from n sources to the ring buffer (if the ring buffer has\n/// enough space), varargs are pairs of type (const char*, int) giving a pointer\n/// to a buffer and the number of bytes to be copied\n/// note: call this from a single writer thread only\n/// returns 0 on success\nint rb_write_to_buffer(ring_buffer *buffer, int n, ...);\n\n/// writes single byte value n times to the ring buffer (if the ring buffer has\n/// enough space)\n/// note: call this from a single writer thread only\n/// returns 0 on success\nint rb_write_value_to_buffer(ring_buffer *buffer, int value, int n);\n\n/// read given number of bytes from the ring buffer to dest (if the ring\n/// buffer has enough data)\n/// note: call this from a single reader thread only\n/// returns 0 on success\nint rb_read_from_buffer(ring_buffer *buffer, char *dest, int len);\n\n/// clears the contents of the ring buffer\n/// this is safe to call from any thread\nvoid rb_clear_buffer(ring_buffer *buffer);\n\n#endif\n"
  },
  {
    "path": "libs/libpd/libpd_wrapper/util/z_print_util.c",
    "content": "/*\n * Copyright (c) 2013 Dan Wilcox (danomatika@gmail.com) &\n *                    Peter Brinkmann (peter.brinkmann@gmail.com)\n * Copyright (c) 2022 libpd team\n *\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.\n *\n * See https://github.com/libpd/libpd/wiki for documentation\n *\n */\n\n#include \"z_print_util.h\"\n\n#include <stdlib.h>\n#include <string.h>\n\n#include \"../z_hooks.h\"\n\n#define PRINT_LINE_SIZE 2048\n\ntypedef struct _print_util {\n  t_libpd_printhook concat_printhook;\n  char concat_buf[PRINT_LINE_SIZE]; /* line buffer */\n  int concat_len;                   /* current line len */\n} print_util;\n\nvoid libpd_set_concatenated_printhook(const t_libpd_printhook hook) {\n  t_libpdimp *imp = LIBPDSTUFF;\n  if (hook) {\n    if (!imp->i_print_util) {\n      imp->i_print_util = calloc(1, sizeof(print_util));\n    }\n    ((print_util *)imp->i_print_util)->concat_printhook = hook;\n  }\n  else {\n    if (imp->i_print_util) {\n      free(imp->i_print_util);\n      imp->i_print_util = NULL;\n    }\n  }\n}\n\nvoid libpd_print_concatenator(const char *s) {\n  print_util *util = (print_util *)LIBPDSTUFF->i_print_util;\n  if (!util) return;\n\n  util->concat_buf[util->concat_len] = '\\0';\n\n  int len = (int)strlen(s);\n  while (util->concat_len + len >= PRINT_LINE_SIZE) {\n    int d = PRINT_LINE_SIZE - 1 - util->concat_len;\n    strncat(util->concat_buf, s, d);\n    util->concat_printhook(util->concat_buf);\n    s += d;\n    len -= d;\n    util->concat_len = 0;\n    util->concat_buf[0] = '\\0';\n  }\n\n  strncat(util->concat_buf, s, len);\n  util->concat_len += len;\n\n  if (util->concat_len > 0 && util->concat_buf[util->concat_len - 1] == '\\n') {\n    util->concat_buf[util->concat_len - 1] = '\\0';\n    util->concat_printhook(util->concat_buf);\n    util->concat_len = 0;\n  }\n}\n"
  },
  {
    "path": "libs/libpd/libpd_wrapper/util/z_print_util.h",
    "content": "/*\n * Copyright (c) 2013 Dan Wilcox (danomatika@gmail.com) &\n *                    Peter Brinkmann (peter.brinkmann@gmail.com)\n * Copyright (c) 2022 libpd team\n *\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.\n *\n * See https://github.com/libpd/libpd/wiki for documentation\n *\n */\n\n#ifndef __Z_PRINT_UTIL_H__\n#define __Z_PRINT_UTIL_H__\n\n#include \"z_libpd.h\"\n\n#ifdef __cplusplus\nextern \"C\"\n{\n#endif\n\n/// assign the pointer to your print line handler\n/// concatenates print messages into single lines before returning them to the\n/// print hook:\n///   ex: line \"hello 123\\n\" is received in 1 part -> \"hello 123\"\n/// for comparison, the default behavior may receive messages in chunks:\n///   ex: line \"hello 123\" could be sent in 3 parts -> \"hello\", \" \", \"123\\n\"\n/// call with NULL pointer to free internal buffer\n/// note: do not call before libpd_init()\nEXTERN void libpd_set_concatenated_printhook(const t_libpd_printhook hook);\n\n/// assign this function pointer to libpd_printhook or libpd_queued_printhook,\n/// depending on whether you're using queued messages, to intercept and\n/// concatenate print messages:\n///     libpd_set_printhook(libpd_print_concatenator);\n///     libpd_set_concatenated_printhook(your_print_handler);\n/// note: the char pointer argument is only good for the duration of the print\n///       callback; if you intend to use the argument after the callback has\n///       returned, you need to make a defensive copy\nEXTERN void libpd_print_concatenator(const char *s);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "libs/libpd/libpd_wrapper/util/z_queued.c",
    "content": "/*\n * Copyright (c) 2012 Peter Brinkmann (peter.brinkmann@gmail.com)\n * Copyright (c) 2022 libpd team\n *\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.\n *\n * See https://github.com/libpd/libpd/wiki for documentation\n *\n */\n\n#include \"z_queued.h\"\n\n#include <stdlib.h>\n#include <string.h>\n\n#include \"../z_hooks.h\"\n#include \"ringbuffer.h\"\n\n#define BUFFER_SIZE 16384\n\ntypedef struct _queued_stuff {\n  t_libpdhooks hooks;\n  t_libpd_printhook printhook;\n  ring_buffer *pd_receive_buffer;\n  ring_buffer *midi_receive_buffer;\n  char temp_buffer[BUFFER_SIZE];\n} queued_stuff;\n\n#define QUEUEDSTUFF ((queued_stuff *)(LIBPDSTUFF->i_queued))\n\ntypedef struct _pd_params {\n  enum {\n    LIBPD_PRINT, LIBPD_BANG, LIBPD_FLOAT,\n    LIBPD_SYMBOL, LIBPD_LIST, LIBPD_MESSAGE,\n  } type;\n  const char *src;\n  t_float x;\n  const char *sym;\n  int argc;\n} pd_params;\n\ntypedef struct _midi_params {\n  enum {\n    LIBPD_NOTEON, LIBPD_CONTROLCHANGE, LIBPD_PROGRAMCHANGE, LIBPD_PITCHBEND,\n    LIBPD_AFTERTOUCH, LIBPD_POLYAFTERTOUCH, LIBPD_MIDIBYTE\n  } type;\n  int midi1;\n  int midi2;\n  int midi3;\n} midi_params;\n\n#define S_PD_PARAMS sizeof(pd_params)\n#define S_MIDI_PARAMS sizeof(midi_params)\n#define S_ATOM sizeof(t_atom)\n\nstatic void receive_print(pd_params *p, char **buffer) {\n  if (QUEUEDSTUFF->printhook) {\n    QUEUEDSTUFF->printhook(*buffer);\n  }\n  *buffer += p->argc;\n}\n\nstatic void receive_bang(pd_params *p, char **buffer) {\n  if (QUEUEDSTUFF->hooks.h_banghook) {\n    QUEUEDSTUFF->hooks.h_banghook(p->src);\n  }\n}\n\nstatic void receive_float(pd_params *p, char **buffer) {\n  if (QUEUEDSTUFF->hooks.h_floathook) {\n    QUEUEDSTUFF->hooks.h_floathook(p->src, (float)p->x);\n  }\n  else if (QUEUEDSTUFF->hooks.h_doublehook) {\n    QUEUEDSTUFF->hooks.h_doublehook(p->src, (double)p->x);\n  }\n}\n\nstatic void receive_symbol(pd_params *p, char **buffer) {\n  if (QUEUEDSTUFF->hooks.h_symbolhook) {\n    QUEUEDSTUFF->hooks.h_symbolhook(p->src, p->sym);\n  }\n}\n\nstatic void receive_list(pd_params *p, char **buffer) {\n  if (QUEUEDSTUFF->hooks.h_listhook) {\n    QUEUEDSTUFF->hooks.h_listhook(p->src, p->argc, (t_atom *) *buffer);\n  }\n  *buffer += p->argc * S_ATOM;\n}\n\nstatic void receive_message(pd_params *p, char **buffer) {\n  if (QUEUEDSTUFF->hooks.h_messagehook) {\n    QUEUEDSTUFF->hooks.h_messagehook(p->src, p->sym, p->argc, (t_atom *) *buffer);\n  }\n  *buffer += p->argc * S_ATOM;\n}\n\n#define LIBPD_WORD_ALIGN 8\n\nstatic void internal_printhook(const char *s) {\n  static char padding[LIBPD_WORD_ALIGN];\n  queued_stuff *queued = QUEUEDSTUFF;\n  int len = (int) strlen(s) + 1; // remember terminating null char\n  int rest = len % LIBPD_WORD_ALIGN;\n  if (rest) rest = LIBPD_WORD_ALIGN - rest;\n  int total = len + rest;\n  if (rb_available_to_write(queued->pd_receive_buffer) >= S_PD_PARAMS + total) {\n    pd_params p = {LIBPD_PRINT, NULL, 0.0f, NULL, total};\n    rb_write_to_buffer(queued->pd_receive_buffer, 3,\n        (const char *)&p, S_PD_PARAMS, s, len, padding, rest);\n  }\n}\n\nstatic void internal_banghook(const char *src) {\n  queued_stuff *queued = QUEUEDSTUFF;\n  if (rb_available_to_write(queued->pd_receive_buffer) >= S_PD_PARAMS) {\n    pd_params p = {LIBPD_BANG, src, 0.0f, NULL, 0};\n    rb_write_to_buffer(queued->pd_receive_buffer, 1, (const char *)&p, S_PD_PARAMS);\n  }\n}\n\nstatic void internal_floathook(const char *src, float x) {\n  queued_stuff *queued = QUEUEDSTUFF;\n  if (rb_available_to_write(queued->pd_receive_buffer) >= S_PD_PARAMS) {\n    pd_params p = {LIBPD_FLOAT, src, x, NULL, 0};\n    rb_write_to_buffer(queued->pd_receive_buffer, 1, (const char *)&p, S_PD_PARAMS);\n  }\n}\n\nstatic void internal_doublehook(const char *src, double x) {\n  queued_stuff *queued = QUEUEDSTUFF;\n  if (rb_available_to_write(queued->pd_receive_buffer) >= S_PD_PARAMS) {\n    pd_params p = {LIBPD_FLOAT, src, (t_float)x, NULL, 0};\n    rb_write_to_buffer(queued->pd_receive_buffer, 1, (const char *)&p, S_PD_PARAMS);\n  }\n}\n\nstatic void internal_symbolhook(const char *src, const char *sym) {\n  queued_stuff *queued = QUEUEDSTUFF;\n  if (rb_available_to_write(queued->pd_receive_buffer) >= S_PD_PARAMS) {\n    pd_params p = {LIBPD_SYMBOL, src, 0.0f, sym, 0};\n    rb_write_to_buffer(queued->pd_receive_buffer, 1, (const char *)&p, S_PD_PARAMS);\n  }\n}\n\nstatic void internal_listhook(const char *src, int argc, t_atom *argv) {\n  queued_stuff *queued = QUEUEDSTUFF;\n  int n = argc * S_ATOM;\n  if (rb_available_to_write(queued->pd_receive_buffer) >= S_PD_PARAMS + n) {\n    pd_params p = {LIBPD_LIST, src, 0.0f, NULL, argc};\n    rb_write_to_buffer(queued->pd_receive_buffer, 2,\n        (const char *)&p, S_PD_PARAMS, (const char *)argv, n);\n  }\n}\n\nstatic void internal_messagehook(const char *src, const char* sym,\n  int argc, t_atom *argv) {\n  queued_stuff *queued = QUEUEDSTUFF;\n  int n = argc * S_ATOM;\n  if (rb_available_to_write(queued->pd_receive_buffer) >= S_PD_PARAMS + n) {\n    pd_params p = {LIBPD_MESSAGE, src, 0.0f, sym, argc};\n    rb_write_to_buffer(queued->pd_receive_buffer, 2,\n        (const char *)&p, S_PD_PARAMS, (const char *)argv, n);\n  }\n}\n\nstatic void receive_noteon(midi_params *p, char **buffer) {\n  if (QUEUEDSTUFF->hooks.h_noteonhook) {\n    QUEUEDSTUFF->hooks.h_noteonhook(p->midi1, p->midi2, p->midi3);\n  }\n}\n\nstatic void receive_controlchange(midi_params *p, char **buffer) {\n  if (QUEUEDSTUFF->hooks.h_controlchangehook) {\n    QUEUEDSTUFF->hooks.h_controlchangehook(p->midi1, p->midi2, p->midi3);\n  }\n}\n\nstatic void receive_programchange(midi_params *p, char **buffer) {\n  if (QUEUEDSTUFF->hooks.h_programchangehook) {\n    QUEUEDSTUFF->hooks.h_programchangehook(p->midi1, p->midi2);\n  }\n}\n\nstatic void receive_pitchbend(midi_params *p, char **buffer) {\n  if (QUEUEDSTUFF->hooks.h_pitchbendhook) {\n    QUEUEDSTUFF->hooks.h_pitchbendhook(p->midi1, p->midi2);\n  }\n}\n\nstatic void receive_aftertouch(midi_params *p, char **buffer) {\n  if (QUEUEDSTUFF->hooks.h_aftertouchhook) {\n    QUEUEDSTUFF->hooks.h_aftertouchhook(p->midi1, p->midi2);\n  }\n}\n\nstatic void receive_polyaftertouch(midi_params *p, char **buffer) {\n  if (QUEUEDSTUFF->hooks.h_polyaftertouchhook) {\n    QUEUEDSTUFF->hooks.h_polyaftertouchhook(p->midi1, p->midi2, p->midi3);\n  }\n}\n\nstatic void receive_midibyte(midi_params *p, char **buffer) {\n  if (QUEUEDSTUFF->hooks.h_midibytehook) {\n    QUEUEDSTUFF->hooks.h_midibytehook(p->midi1, p->midi2);\n  }\n}\n\nstatic void internal_noteonhook(int channel, int pitch, int velocity) {\n  queued_stuff *queued = QUEUEDSTUFF;\n  if (rb_available_to_write(queued->midi_receive_buffer) >= S_MIDI_PARAMS) {\n    midi_params p = {LIBPD_NOTEON, channel, pitch, velocity};\n    rb_write_to_buffer(queued->midi_receive_buffer, 1, (const char *)&p, S_MIDI_PARAMS);\n  }\n}\n\nstatic void internal_controlchangehook(int channel, int controller, int value) {\n  queued_stuff *queued = QUEUEDSTUFF;\n  if (rb_available_to_write(queued->midi_receive_buffer) >= S_MIDI_PARAMS) {\n    midi_params p = {LIBPD_CONTROLCHANGE, channel, controller, value};\n    rb_write_to_buffer(queued->midi_receive_buffer, 1, (const char *)&p, S_MIDI_PARAMS);\n  }\n}\n\nstatic void internal_programchangehook(int channel, int value) {\n  queued_stuff *queued = QUEUEDSTUFF;\n  if (rb_available_to_write(queued->midi_receive_buffer) >= S_MIDI_PARAMS) {\n    midi_params p = {LIBPD_PROGRAMCHANGE, channel, value, 0};\n    rb_write_to_buffer(queued->midi_receive_buffer, 1, (const char *)&p, S_MIDI_PARAMS);\n  }\n}\n\nstatic void internal_pitchbendhook(int channel, int value) {\n  queued_stuff *queued = QUEUEDSTUFF;\n  if (rb_available_to_write(queued->midi_receive_buffer) >= S_MIDI_PARAMS) {\n    midi_params p = {LIBPD_PITCHBEND, channel, value, 0};\n    rb_write_to_buffer(queued->midi_receive_buffer, 1, (const char *)&p, S_MIDI_PARAMS);\n  }\n}\n\nstatic void internal_aftertouchhook(int channel, int value) {\n  queued_stuff *queued = QUEUEDSTUFF;\n  if (rb_available_to_write(queued->midi_receive_buffer) >= S_MIDI_PARAMS) {\n    midi_params p = {LIBPD_AFTERTOUCH, channel, value, 0};\n    rb_write_to_buffer(queued->midi_receive_buffer, 1, (const char *)&p, S_MIDI_PARAMS);\n  }\n}\n\nstatic void internal_polyaftertouchhook(int channel, int pitch, int value) {\n  queued_stuff *queued = QUEUEDSTUFF;\n  if (rb_available_to_write(queued->midi_receive_buffer) >= S_MIDI_PARAMS) {\n    midi_params p = {LIBPD_POLYAFTERTOUCH, channel, pitch, value};\n    rb_write_to_buffer(queued->midi_receive_buffer, 1, (const char *)&p, S_MIDI_PARAMS);\n  }\n}\n\nstatic void internal_midibytehook(int port, int byte) {\n  queued_stuff *queued = QUEUEDSTUFF;\n  if (rb_available_to_write(queued->midi_receive_buffer) >= S_MIDI_PARAMS) {\n    midi_params p = {LIBPD_MIDIBYTE, port, byte, 0};\n    rb_write_to_buffer(queued->midi_receive_buffer, 1, (const char *)&p, S_MIDI_PARAMS);\n  }\n}\n\nvoid libpd_set_queued_printhook(const t_libpd_printhook hook) {\n  QUEUEDSTUFF->printhook = hook;\n}\n\nvoid libpd_set_queued_banghook(const t_libpd_banghook hook) {\n  QUEUEDSTUFF->hooks.h_banghook = hook;\n}\n\nvoid libpd_set_queued_floathook(const t_libpd_floathook hook) {\n  QUEUEDSTUFF->hooks.h_floathook = hook;\n  QUEUEDSTUFF->hooks.h_doublehook = NULL;\n}\n\nvoid libpd_set_queued_doublehook(const t_libpd_doublehook hook) {\n  QUEUEDSTUFF->hooks.h_floathook = NULL;\n  QUEUEDSTUFF->hooks.h_doublehook = hook;\n}\n\nvoid libpd_set_queued_symbolhook(const t_libpd_symbolhook hook) {\n  QUEUEDSTUFF->hooks.h_symbolhook = hook;\n}\n\nvoid libpd_set_queued_listhook(const t_libpd_listhook hook) {\n  QUEUEDSTUFF->hooks.h_listhook = hook;\n}\n\nvoid libpd_set_queued_messagehook(const t_libpd_messagehook hook) {\n  QUEUEDSTUFF->hooks.h_messagehook = hook;\n}\n\nvoid libpd_set_queued_noteonhook(const t_libpd_noteonhook hook) {\n  QUEUEDSTUFF->hooks.h_noteonhook = hook;\n}\n\nvoid libpd_set_queued_controlchangehook(const t_libpd_controlchangehook hook) {\n  QUEUEDSTUFF->hooks.h_controlchangehook = hook;\n}\n\nvoid libpd_set_queued_programchangehook(const t_libpd_programchangehook hook) {\n  QUEUEDSTUFF->hooks.h_programchangehook = hook;\n}\n\nvoid libpd_set_queued_pitchbendhook(const t_libpd_pitchbendhook hook) {\n  QUEUEDSTUFF->hooks.h_pitchbendhook = hook;\n}\n\nvoid libpd_set_queued_aftertouchhook(const t_libpd_aftertouchhook hook) {\n  QUEUEDSTUFF->hooks.h_aftertouchhook = hook;\n}\n\nvoid libpd_set_queued_polyaftertouchhook(const t_libpd_polyaftertouchhook hook) {\n  QUEUEDSTUFF->hooks.h_polyaftertouchhook = hook;\n}\n\nvoid libpd_set_queued_midibytehook(const t_libpd_midibytehook hook) {\n  QUEUEDSTUFF->hooks.h_midibytehook = hook;\n}\n\nstatic void queued_stuff_free(void *p) {\n  queued_stuff *queued = (queued_stuff *)p;\n  if (queued->pd_receive_buffer) rb_free(queued->pd_receive_buffer);\n  if (queued->midi_receive_buffer) rb_free(queued->midi_receive_buffer);\n}\n\nint libpd_queued_init() {\n  int ret = libpd_init();\n\n  libpd_set_printhook(internal_printhook);\n  libpd_set_banghook(internal_banghook);\n  libpd_set_doublehook(internal_doublehook);\n  libpd_set_symbolhook(internal_symbolhook);\n  libpd_set_listhook(internal_listhook);\n  libpd_set_messagehook(internal_messagehook);\n\n  libpd_set_noteonhook(internal_noteonhook);\n  libpd_set_controlchangehook(internal_controlchangehook);\n  libpd_set_programchangehook(internal_programchangehook);\n  libpd_set_pitchbendhook(internal_pitchbendhook);\n  libpd_set_aftertouchhook(internal_aftertouchhook);\n  libpd_set_polyaftertouchhook(internal_polyaftertouchhook);\n  libpd_set_midibytehook(internal_midibytehook);\n\n  t_libpdimp *imp = LIBPDSTUFF;\n  if (!imp->i_queued) {\n    queued_stuff *queued = (queued_stuff *)calloc(1, sizeof(queued_stuff));\n    if(!queued) goto cleanup;\n    queued->pd_receive_buffer = rb_create(BUFFER_SIZE);\n    if (!queued->pd_receive_buffer) goto cleanup;\n    queued->midi_receive_buffer = rb_create(BUFFER_SIZE);\n    if (!queued->midi_receive_buffer) goto cleanup;\n    imp->i_queued = (void *)queued;\n    imp->i_queued_freehook = queued_stuff_free;\n  }\n  return ret;\ncleanup:\n  libpd_queued_release();\n  return -2;\n}\n\nvoid libpd_queued_release() {\n  t_libpdimp *imp = LIBPDSTUFF;\n  if (imp->i_queued) {\n    queued_stuff_free(imp->i_queued);\n    imp->i_queued = NULL;\n    imp->i_queued_freehook = NULL;\n  }\n}\n\nvoid libpd_queued_receive_pd_messages() {\n  queued_stuff *queued = QUEUEDSTUFF;\n  size_t available = rb_available_to_read(queued->pd_receive_buffer);\n  if (!available) return;\n  rb_read_from_buffer(queued->pd_receive_buffer, queued->temp_buffer, (int)available);\n  char *end = queued->temp_buffer + available;\n  char *buffer = queued->temp_buffer;\n  while (buffer < end) {\n    pd_params *p = (pd_params *)buffer;\n    buffer += S_PD_PARAMS;\n    switch (p->type) {\n      case LIBPD_PRINT: {\n        receive_print(p, &buffer);\n        break;\n      }\n      case LIBPD_BANG: {\n        receive_bang(p, &buffer);\n        break;\n      }\n      case LIBPD_FLOAT: {\n        receive_float(p, &buffer);\n        break;\n      }\n      case LIBPD_SYMBOL: {\n        receive_symbol(p, &buffer);\n        break;\n      }\n      case LIBPD_LIST: {\n        receive_list(p, &buffer);\n        break;\n      }\n      case LIBPD_MESSAGE: {\n        receive_message(p, &buffer);\n        break;\n      }\n      default:\n        break;\n    }\n  }\n}\n\nvoid libpd_queued_receive_midi_messages() {\n  queued_stuff *queued = QUEUEDSTUFF;\n  size_t available = rb_available_to_read(queued->midi_receive_buffer);\n  if (!available) return;\n  rb_read_from_buffer(queued->midi_receive_buffer, queued->temp_buffer, (int)available);\n  char *end = queued->temp_buffer + available;\n  char *buffer = queued->temp_buffer;\n  while (buffer < end) {\n    midi_params *p = (midi_params *)buffer;\n    buffer += S_MIDI_PARAMS;\n    switch (p->type) {\n      case LIBPD_NOTEON: {\n        receive_noteon(p, &buffer);\n        break;\n      }\n      case LIBPD_CONTROLCHANGE: {\n        receive_controlchange(p, &buffer);\n        break;\n      }\n      case LIBPD_PROGRAMCHANGE: {\n        receive_programchange(p, &buffer);\n        break;\n      }\n      case LIBPD_PITCHBEND: {\n        receive_pitchbend(p, &buffer);\n        break;\n      }\n      case LIBPD_AFTERTOUCH: {\n        receive_aftertouch(p, &buffer);\n        break;\n      }\n      case LIBPD_POLYAFTERTOUCH: {\n        receive_polyaftertouch(p, &buffer);\n        break;\n      }\n      case LIBPD_MIDIBYTE: {\n        receive_midibyte(p, &buffer);\n        break;\n      }\n      default:\n        break;\n    }\n  }\n}\n"
  },
  {
    "path": "libs/libpd/libpd_wrapper/util/z_queued.h",
    "content": "/*\n * Copyright (c) 2012 Peter Brinkmann (peter.brinkmann@gmail.com)\n * Copyright (c) 2022 libpd team\n *\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.\n *\n * See https://github.com/libpd/libpd/wiki for documentation\n *\n */\n\n#ifndef __Z_QUEUED_H__\n#define __Z_QUEUED_H__\n\n#include \"z_libpd.h\"\n\n#ifdef __cplusplus\nextern \"C\"\n{\n#endif\n\n/// set the queued print receiver hook, NULL by default\n/// note: do not call this before libpd_queued_init() or while DSP is running\nEXTERN void libpd_set_queued_printhook(const t_libpd_printhook hook);\n\n/// set the queued bang receiver hook, NULL by default\n/// note: do not call this before libpd_queued_init() or while DSP is running\nEXTERN void libpd_set_queued_banghook(const t_libpd_banghook hook);\n\n/// set the queued float receiver hook, NULL by default\n/// note: do not call this before libpd_queued_init() or while DSP is running\n/// note: you can either have a queued float receiver hook, or a queued\n///       double receiver hook (see below), but not both.\n///       calling this, will automatically unset the queued double receiver\n///       hook\nEXTERN void libpd_set_queued_floathook(const t_libpd_floathook hook);\n\n/// set the queued double receiver hook, NULL by default\n/// note: do not call this before libpd_queued_init() or while DSP is running\n/// note: you can either have a queued double receiver hook, or a queued\n///       float receiver hook (see above), but not both.\n///       calling this, will automatically unset the queued float receiver\n///       hook\nEXTERN void libpd_set_queued_doublehook(const t_libpd_doublehook hook);\n\n/// set the queued symbol receiver hook, NULL by default\n/// note: do not call this before libpd_queued_init() or while DSP is running\nEXTERN void libpd_set_queued_symbolhook(const t_libpd_symbolhook hook);\n\n/// set the queued list receiver hook, NULL by default\n/// note: do not call this before libpd_queued_init() or while DSP is running\nEXTERN void libpd_set_queued_listhook(const t_libpd_listhook hook);\n\n/// set the queued typed message receiver hook, NULL by default\n/// note: do not call this before libpd_queued_init() or while DSP is running\nEXTERN void libpd_set_queued_messagehook(const t_libpd_messagehook hook);\n\n/// set the queued MIDI note on hook, NULL by default\n/// note: do not call this before libpd_queued_init() or while DSP is running\nEXTERN void libpd_set_queued_noteonhook(const t_libpd_noteonhook hook);\n\n/// set the queued MIDI control change hook, NULL by default\n/// note: do not call this before libpd_queued_init() or while DSP is running\nEXTERN void libpd_set_queued_controlchangehook(const t_libpd_controlchangehook hook);\n\n/// set the queued MIDI program change hook, NULL by default\n/// note: do not call this before libpd_queued_init() or while DSP is running\nEXTERN void libpd_set_queued_programchangehook(const t_libpd_programchangehook hook);\n\n/// set the queued MIDI pitch bend hook, NULL by default\n/// note: do not call this before libpd_queued_init() or while DSP is running\nEXTERN void libpd_set_queued_pitchbendhook(const t_libpd_pitchbendhook hook);\n\n/// set the queued MIDI after touch hook, NULL by default\n/// note: do not call this before libpd_queued_init() or while DSP is running\nEXTERN void libpd_set_queued_aftertouchhook(const t_libpd_aftertouchhook hook);\n\n/// set the queued MIDI poly after touch hook, NULL by default\n/// note: do not call this before libpd_queued_init() or while DSP is running\nEXTERN void libpd_set_queued_polyaftertouchhook(const t_libpd_polyaftertouchhook hook);\n\n/// set the queued raw MIDI byte hook, NULL by default\n/// note: do not call this before libpd_queued_init() or while DSP is running\nEXTERN void libpd_set_queued_midibytehook(const t_libpd_midibytehook hook);\n\n/// initialize libpd and the queued ringbuffers, safe to call more than once\n/// returns 0 on success, -1 if libpd was already initialized, or -2 if ring\n/// buffer allocation failed\n///\n/// with a single instance, use in place of libpd_init()\n///\n/// with multiple instances, call once for each instance *before* setting hooks:\n///    t_pdinstance *pd1 = libpd_new_instance();\n///    libpd_set_instance(pd1);\n///    libpd_queued_init();\n///    libpd_set_queued_printhook(pdprint);\n///    ...\n///\nEXTERN int libpd_queued_init();\n\n/// free the queued ringbuffers\n/// with multiple instances, call before freeing each instance:\n///     libpd_set_instance(pd1);\n///     libpd_queued_release();\n///     libpd_free_instance(pd1);\nEXTERN void libpd_queued_release();\n\n/// process and dispatch received messages in message ringbuffer\nEXTERN void libpd_queued_receive_pd_messages();\n\n/// process and dispatch receive midi messages in MIDI message ringbuffer\nEXTERN void libpd_queued_receive_midi_messages();\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "libs/libpd/libpd_wrapper/x_libpdreceive.c",
    "content": "/*\n * Copyright (c) 2010 Peter Brinkmann (peter.brinkmann@gmail.com)\n * Copyright (c) 2012-2021 libpd team\n *\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.\n *\n * See https://github.com/libpd/libpd/wiki for documentation\n *\n */\n\n#include \"x_libpdreceive.h\"\n#include \"z_libpd.h\"\n#include \"z_hooks.h\"\n\nstatic t_class *libpdrec_class;\n\ntypedef struct _libpdrec {\n  t_object x_obj;\n  t_symbol *x_sym;\n  t_libpdhooks *x_hooks;\n} t_libpdrec;\n\nstatic void libpdrecbang(t_libpdrec *x) {\n  if (x->x_hooks->h_banghook)\n    (*x->x_hooks->h_banghook)(x->x_sym->s_name);\n}\n\nstatic void libpdrecfloat(t_libpdrec *x, t_float f) {\n  if (x->x_hooks->h_floathook)\n    (*x->x_hooks->h_floathook)(x->x_sym->s_name, f);\n  else if (x->x_hooks->h_doublehook)\n    (*x->x_hooks->h_doublehook)(x->x_sym->s_name, f);\n}\n\nstatic void libpdrecsymbol(t_libpdrec *x, t_symbol *s) {\n  if (x->x_hooks->h_symbolhook)\n    (*x->x_hooks->h_symbolhook)(x->x_sym->s_name, s->s_name);\n}\n\nstatic void libpdrecpointer(t_libpdrec *x, t_gpointer *gp) {\n  // just ignore pointers for now...\n}\n\nstatic void libpdreclist(t_libpdrec *x, t_symbol *s, int argc, t_atom *argv) {\n  if (x->x_hooks->h_listhook)\n    (*x->x_hooks->h_listhook)(x->x_sym->s_name, argc, argv);\n}\n\nstatic void libpdrecanything(t_libpdrec *x, t_symbol *s,\n                int argc, t_atom *argv) {\n  if (x->x_hooks->h_messagehook)\n    (*x->x_hooks->h_messagehook)(x->x_sym->s_name, s->s_name, argc, argv);\n}\n\nstatic void libpdreceive_free(t_libpdrec *x) {\n  pd_unbind(&x->x_obj.ob_pd, x->x_sym);\n}\n\nstatic void *libpdreceive_donew(t_symbol *s) {\n  t_libpdrec *x;\n  x = (t_libpdrec *)pd_new(libpdrec_class);\n  x->x_sym = s;\n  x->x_hooks = &LIBPDSTUFF->i_hooks;\n  pd_bind(&x->x_obj.ob_pd, s);\n  return x;\n}\n\n// this is exposed in the libpd API so must set the lock\nvoid *libpdreceive_new(t_symbol *s) {\n  t_libpdrec *x;\n  sys_lock();\n  x = (t_libpdrec *)libpdreceive_donew(s);\n  sys_unlock();\n  return x;\n}\n\nvoid libpdreceive_setup(void) {\n  sys_lock();\n  libpdrec_class = class_new(gensym(\"libpd_receive\"),\n       (t_newmethod)libpdreceive_donew, (t_method)libpdreceive_free,\n       sizeof(t_libpdrec), CLASS_DEFAULT, A_DEFSYM, 0);\n  class_addbang(libpdrec_class, libpdrecbang);\n  class_addfloat(libpdrec_class, libpdrecfloat);\n  class_addsymbol(libpdrec_class, libpdrecsymbol);\n  class_addpointer(libpdrec_class, libpdrecpointer);\n  class_addlist(libpdrec_class, libpdreclist);\n  class_addanything(libpdrec_class, libpdrecanything);\n  sys_unlock();\n}\n"
  },
  {
    "path": "libs/libpd/libpd_wrapper/x_libpdreceive.h",
    "content": "/*\n * Copyright (c) 2010 Peter Brinkmann (peter.brinkmann@gmail.com)\n * Copyright (c) 2012-2021 libpd team\n *\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.\n *\n * See https://github.com/libpd/libpd/wiki for documentation\n *\n */\n\n#ifndef __X_LIBPDREC_H__\n#define __X_LIBPDREC_H__\n\n#include \"m_pd.h\"\n\n// internal \"virtual\" libpd receive object which forwards events to hooks\n// do *not* include this file in a user-facing header\n\n// set up the libpd source receiver class\nvoid libpdreceive_setup(void);\n\n// create a new libpd source receiver with a given name symbol\nvoid *libpdreceive_new(t_symbol *);\n\n#endif\n"
  },
  {
    "path": "libs/libpd/libpd_wrapper/z_hooks.c",
    "content": "/*\n * Copyright (c) 2013 Dan Wilcox (danomatika@gmail.com)\n * Copyright (c) 2013-2021 libpd team\n *\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.\n *\n * See https://github.com/libpd/libpd/wiki for documentation\n *\n */\n\n#include \"z_hooks.h\"\n#include <stdlib.h>\n\n/* instance */\n\nt_libpdimp libpd_mainimp = {NULL};\n\nt_libpdimp* libpdimp_new(void) {\n  t_libpdimp *imp = calloc(1, sizeof(t_libpdimp));\n  return imp;\n}\n\nvoid libpdimp_free(t_libpdimp *imp) {\n  if (imp == &libpd_mainimp) return;\n  if (imp->i_queued) imp->i_queued_freehook(imp->i_queued);\n  if (imp->i_print_util) free(imp->i_print_util);\n  if (imp->i_data && imp->i_data_freehook) imp->i_data_freehook(imp->i_data);\n  free(imp);\n}\n"
  },
  {
    "path": "libs/libpd/libpd_wrapper/z_hooks.h",
    "content": "/*\n * Copyright (c) 2013 Dan Wilcox (danomatika@gmail.com)\n * Copyright (c) 2013-2021 libpd team\n *\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.\n *\n * See https://github.com/libpd/libpd/wiki for documentation\n *\n */\n\n#ifndef __Z_HOOKS_H__\n#define __Z_HOOKS_H__\n\n#include \"z_libpd.h\"\n#include \"s_stuff.h\"\n\n// internal hooks, etc\n// do *not* include this file in a user-facing header\n\n/* hooks */\n\ntypedef struct _libpdhooks {\n\n  // messages\n  // no h_printhook as libpd_set_printhook() sets internal STUFF->st_printhook\n  t_libpd_banghook h_banghook;\n  t_libpd_floathook h_floathook;\n  t_libpd_doublehook h_doublehook;\n  t_libpd_symbolhook h_symbolhook;\n  t_libpd_listhook h_listhook;\n  t_libpd_messagehook h_messagehook;\n\n  // MIDI\n  t_libpd_noteonhook h_noteonhook;\n  t_libpd_controlchangehook h_controlchangehook;\n  t_libpd_programchangehook h_programchangehook;\n  t_libpd_pitchbendhook h_pitchbendhook;\n  t_libpd_aftertouchhook h_aftertouchhook;\n  t_libpd_polyaftertouchhook h_polyaftertouchhook;\n  t_libpd_midibytehook h_midibytehook;\n} t_libpdhooks;\n\n/* instance */\n\n/// libpd per-instance implementation data\ntypedef struct _libpdimp {\n  t_libpdhooks i_hooks; /* event hooks */\n  void *i_queued;       /* queued data, default NULL */\n  void *i_print_util;   /* print util data, default NULL */\n  void *i_data;         /* user data, default NULL */\n  t_libpd_freehook i_queued_freehook; /* i_queued free, default NULL */\n  t_libpd_freehook i_data_freehook;   /* i_data free, default NULL */\n} t_libpdimp;\n\n/// main instance implementation data, always valid\nextern t_libpdimp libpd_mainimp;\n\n/// alloc new instance implementation data\nt_libpdimp* libpdimp_new(void);\n\n/// free instance implementation data\n/// does nothing if imp is libpd_mainimp\nvoid libpdimp_free(t_libpdimp *imp);\n\n/// get current instance implementation data\n#ifdef PDINSTANCE\n  #define LIBPDSTUFF ((t_libpdimp *)(STUFF->st_impdata))\n#else\n  #define LIBPDSTUFF ((t_libpdimp *)&libpd_mainimp)\n#endif\n\n#endif\n"
  },
  {
    "path": "libs/libpd/libpd_wrapper/z_libpd.c",
    "content": "/*\n * Copyright (c) 2010 Peter Brinkmann (peter.brinkmann@gmail.com)\n * Copyright (c) 2012-2021 libpd team\n *\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.\n *\n * See https://github.com/libpd/libpd/wiki for documentation\n *\n */\n\n#include <signal.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <limits.h>\n#ifndef LIBPD_NO_NUMERIC\n# include <locale.h>\n#endif\n#include \"z_libpd.h\"\n#include \"x_libpdreceive.h\"\n#include \"z_hooks.h\"\n#include \"m_imp.h\"\n#include \"g_all_guis.h\"\n\n// pd_init() doesn't call socket_init() which is needed on windows for\n// libpd_start_gui() to work\n#if (defined(_WIN32) || defined(_WIN64)) && PD_MINOR_VERSION > 50\n# include \"s_net.h\"\n# define SOCKET_INIT socket_init();\n#else\n# define SOCKET_INIT\n#endif\n\n#if PD_MINOR_VERSION < 46\n# define HAVE_SCHED_TICK_ARG\n#endif\n\n#ifdef HAVE_SCHED_TICK_ARG\n# define SCHED_TICK(x) sched_tick(x)\n#else\n# define SCHED_TICK(x) sched_tick()\n#endif\n\n// forward declares\nvoid pd_init(void);\nint sys_startgui(const char *libdir);\nvoid sys_stopgui(void);\nint sys_pollgui(void);\n\n// (optional) setup functions for built-in \"extra\" externals\n#ifdef LIBPD_EXTRA\n  void bob_tilde_setup(void);\n  void bonk_tilde_setup(void);\n  void choice_setup(void);\n  void fiddle_tilde_setup(void);\n  void loop_tilde_setup(void);\n  void lrshift_tilde_setup(void);\n  void pd_tilde_setup(void);\n  void pique_setup(void);\n  void sigmund_tilde_setup(void);\n  void stdout_setup(void);\n#endif\n\nstatic PERTHREAD t_atom *s_argv = NULL;\nstatic PERTHREAD t_atom *s_curr = NULL;\nstatic PERTHREAD int s_argm = 0;\nstatic PERTHREAD int s_argc = 0;\n\nstatic void *get_object(const char *s) {\n  t_pd *x = gensym(s)->s_thing;\n  return x;\n}\n\n// note: could we use pd_this instead?\nstatic int s_initialized = 0;\n\n// this is called instead of sys_main() to start things\nint libpd_init(void) {\n  if (s_initialized) return -1; // only allow init once (for now)\n  s_initialized = 1;\n  signal(SIGFPE, SIG_IGN);\n  libpd_start_message(32); // allocate array for message assembly\n  sys_externalschedlib = 0;\n  sys_printtostderr = 0;\n  sys_usestdpath = 0; // don't use pd_extrapath, only sys_searchpath\n  sys_debuglevel = 0;\n  sys_noloadbang = 0;\n  sys_hipriority = 0;\n  sys_nmidiin = 0;\n  sys_nmidiout = 0;\n#ifdef HAVE_SCHED_TICK_ARG\n  sys_time = 0;\n#endif\n  pd_init();\n  STUFF->st_soundin = NULL;\n  STUFF->st_soundout = NULL;\n  STUFF->st_schedblocksize = DEFDACBLKSIZE;\n  STUFF->st_impdata = &libpd_mainimp;\n  sys_init_fdpoll();\n  libpdreceive_setup();\n  STUFF->st_searchpath = NULL;\n  sys_libdir = gensym(\"\");\n  SOCKET_INIT\n  post(\"pd %d.%d.%d%s\", PD_MAJOR_VERSION, PD_MINOR_VERSION,\n    PD_BUGFIX_VERSION, PD_TEST_VERSION);\n#ifdef LIBPD_EXTRA\n  bob_tilde_setup();\n  bonk_tilde_setup();\n  choice_setup();\n  fiddle_tilde_setup();\n  loop_tilde_setup();\n  lrshift_tilde_setup();\n  pd_tilde_setup();\n  pique_setup();\n  sigmund_tilde_setup();\n  stdout_setup();\n#endif\n#ifndef LIBPD_NO_NUMERIC\n  setlocale(LC_NUMERIC, \"C\");\n#endif\n  return 0;\n}\n\nvoid libpd_clear_search_path(void) {\n  sys_lock();\n  namelist_free(STUFF->st_searchpath);\n  STUFF->st_searchpath = NULL;\n  sys_unlock();\n}\n\nvoid libpd_add_to_search_path(const char *path) {\n  sys_lock();\n  STUFF->st_searchpath = namelist_append(STUFF->st_searchpath, path, 0);\n  sys_unlock();\n}\n\nvoid *libpd_openfile(const char *name, const char *dir) {\n  void *retval;\n  sys_lock();\n  pd_globallock();\n  retval = (void *)glob_evalfile(NULL, gensym(name), gensym(dir));\n  pd_globalunlock();\n  sys_unlock();\n  return retval;\n}\n\nvoid libpd_closefile(void *p) {\n  sys_lock();\n  pd_free((t_pd *)p);\n  sys_unlock();\n}\n\nint libpd_getdollarzero(void *p) {\n  sys_lock();\n  pd_pushsym((t_pd *)p);\n  int dzero = canvas_getdollarzero();\n  pd_popsym((t_pd *)p);\n  sys_unlock();\n  return dzero;\n}\n\nint libpd_blocksize(void) {\n  return DEFDACBLKSIZE;\n}\n\nint libpd_init_audio(int inChannels, int outChannels, int sampleRate) {\n  t_audiosettings as;\n  as.a_indevvec[0] = as.a_outdevvec[0] = DEFAULTAUDIODEV;\n  as.a_nindev = as.a_noutdev = as.a_nchindev = as.a_nchoutdev = 1;\n  as.a_chindevvec[0] = inChannels;\n  as.a_choutdevvec[0] = outChannels;\n  as.a_srate = sampleRate;\n  as.a_blocksize = DEFDACBLKSIZE;\n  as.a_callback = 0;\n  as.a_advance = -1;\n  as.a_api = API_DUMMY;\n  sys_lock();\n  sys_set_audio_settings(&as);\n  sched_set_using_audio(SCHED_AUDIO_CALLBACK);\n  sys_reopen_audio();\n  sys_unlock();\n  return 0;\n}\n\nstatic const t_sample sample_to_short = SHRT_MAX,\n                      short_to_sample = 1.0 / (t_sample) SHRT_MAX;\n\n#define PROCESS(_x, _y) \\\n  int i, j, k; \\\n  t_sample *p0, *p1; \\\n  sys_lock(); \\\n  sys_pollgui(); \\\n  for (i = 0; i < ticks; i++) { \\\n    for (j = 0, p0 = STUFF->st_soundin; j < DEFDACBLKSIZE; j++, p0++) { \\\n      for (k = 0, p1 = p0; k < STUFF->st_inchannels; k++, p1 += DEFDACBLKSIZE) \\\n        { \\\n        *p1 = *inBuffer++ _x; \\\n      } \\\n    } \\\n    memset(STUFF->st_soundout, 0, \\\n        STUFF->st_outchannels*DEFDACBLKSIZE*sizeof(t_sample)); \\\n    SCHED_TICK(pd_this->pd_systime + STUFF->st_time_per_dsp_tick); \\\n    for (j = 0, p0 = STUFF->st_soundout; j < DEFDACBLKSIZE; j++, p0++) { \\\n      for (k = 0, p1 = p0; k < STUFF->st_outchannels; k++, p1 += DEFDACBLKSIZE) \\\n        { \\\n        *outBuffer++ = *p1 _y; \\\n      } \\\n    } \\\n  } \\\n  sys_unlock(); \\\n  return 0;\n\nint libpd_process_short(const int ticks, const short *inBuffer, short *outBuffer) {\n  PROCESS(* short_to_sample, * sample_to_short)\n}\n\nint libpd_process_float(const int ticks, const float *inBuffer, float *outBuffer) {\n  PROCESS(,)\n}\n\nint libpd_process_double(const int ticks, const double *inBuffer, double *outBuffer) {\n  PROCESS(,)\n}\n\n#define PROCESS_RAW(_x, _y) \\\n  size_t n_in = STUFF->st_inchannels * DEFDACBLKSIZE; \\\n  size_t n_out = STUFF->st_outchannels * DEFDACBLKSIZE; \\\n  t_sample *p; \\\n  size_t i; \\\n  sys_lock(); \\\n  sys_pollgui(); \\\n  for (p = STUFF->st_soundin, i = 0; i < n_in; i++) { \\\n    *p++ = *inBuffer++ _x; \\\n  } \\\n  memset(STUFF->st_soundout, 0, n_out * sizeof(t_sample)); \\\n  SCHED_TICK(pd_this->pd_systime + STUFF->st_time_per_dsp_tick); \\\n  for (p = STUFF->st_soundout, i = 0; i < n_out; i++) { \\\n    *outBuffer++ = *p++ _y; \\\n  } \\\n  sys_unlock(); \\\n  return 0;\n\nint libpd_process_raw(const float *inBuffer, float *outBuffer) {\n  PROCESS_RAW(,)\n}\n\nint libpd_process_raw_short(const short *inBuffer, short *outBuffer) {\n  PROCESS_RAW(* short_to_sample, * sample_to_short)\n}\n\nint libpd_process_raw_double(const double *inBuffer, double *outBuffer) {\n  PROCESS_RAW(,)\n}\n\n#define GETARRAY \\\n  t_garray *garray = (t_garray *) pd_findbyclass(gensym(name), garray_class); \\\n  if (!garray) {sys_unlock(); return -1;} \\\n\nint libpd_arraysize(const char *name) {\n  int retval;\n  sys_lock();\n  GETARRAY\n  retval = garray_npoints(garray);\n  sys_unlock();\n  return retval;\n}\n\nint libpd_resize_array(const char *name, long size) {\n  sys_lock();\n  GETARRAY\n  garray_resize_long(garray, size);\n  sys_unlock();\n  return 0;\n}\n\n#define MEMCPY(_x, _y) \\\n  GETARRAY \\\n  if (n < 0 || offset < 0 || offset + n > garray_npoints(garray)) return -2; \\\n  t_word *vec = ((t_word *) garray_vec(garray)) + offset; \\\n  int i; \\\n  for (i = 0; i < n; i++) _x = _y;\n\nint libpd_read_array(float *dest, const char *name, int offset, int n) {\n  sys_lock();\n  MEMCPY(*dest++, (vec++)->w_float)\n  sys_unlock();\n  return 0;\n}\n\nint libpd_write_array(const char *name, int offset, const float *src, int n) {\n  sys_lock();\n  MEMCPY((vec++)->w_float, *src++)\n  sys_unlock();\n  return 0;\n}\n\nint libpd_read_array_double(double *dest, const char *name, int offset, int n) {\n  sys_lock();\n  MEMCPY(*dest++, (vec++)->w_float)\n  sys_unlock();\n  return 0;\n}\n\nint libpd_write_array_double(const char *name, int offset, const double *src, int n) {\n  sys_lock();\n  MEMCPY((vec++)->w_float, *src++)\n  sys_unlock();\n  return 0;\n}\n\nint libpd_bang(const char *recv) {\n  void *obj;\n  sys_lock();\n  obj = get_object(recv);\n  if (obj == NULL)\n  {\n    sys_unlock();\n    return -1;\n  }\n  pd_bang(obj);\n  sys_unlock();\n  return 0;\n}\n\nstatic int libpd_dofloat(const char *recv, t_float x) {\n  void *obj;\n  sys_lock();\n  obj = get_object(recv);\n  if (obj == NULL)\n  {\n    sys_unlock();\n    return -1;\n  }\n  pd_float(obj, x);\n  sys_unlock();\n  return 0;\n}\n\nint libpd_float(const char *recv, float x) {\n  return libpd_dofloat(recv, x);\n}\n\nint libpd_double(const char *recv, double x) {\n  return libpd_dofloat(recv, x);\n}\n\nint libpd_symbol(const char *recv, const char *symbol) {\n  void *obj;\n  sys_lock();\n  obj = get_object(recv);\n  if (obj == NULL)\n  {\n    sys_unlock();\n    return -1;\n  }\n  pd_symbol(obj, gensym(symbol));\n  sys_unlock();\n  return 0;\n}\n\nint libpd_start_message(int maxlen) {\n  if (maxlen > s_argm) {\n    t_atom *v = realloc(s_argv, maxlen * sizeof(t_atom));\n    if (v) {\n      s_argv = v;\n      s_argm = maxlen;\n    } else {\n      return -1;\n    }\n  }\n  s_argc = 0;\n  s_curr = s_argv;\n  return 0;\n}\n\n#define ADD_ARG(f) f(s_curr, x); s_curr++; s_argc++;\n\nvoid libpd_add_float(float x) {\n  ADD_ARG(SETFLOAT);\n}\n\nvoid libpd_add_double(double x) {\n  ADD_ARG(SETFLOAT);\n}\n\nvoid libpd_add_symbol(const char *symbol) {\n  t_symbol *x;\n  sys_lock();\n  x = gensym(symbol);\n  sys_unlock();\n  ADD_ARG(SETSYMBOL);\n}\n\nint libpd_finish_list(const char *recv) {\n  return libpd_list(recv, s_argc, s_argv);\n}\n\nint libpd_finish_message(const char *recv, const char *msg) {\n  return libpd_message(recv, msg, s_argc, s_argv);\n}\n\nvoid libpd_set_float(t_atom *a, float x) {\n  SETFLOAT(a, x);\n}\n\nvoid libpd_set_double(t_atom *v, double x) {\n  SETFLOAT(v, x);\n}\n\nvoid libpd_set_symbol(t_atom *a, const char *symbol) {\n  SETSYMBOL(a, gensym(symbol));\n}\n\nint libpd_list(const char *recv, int argc, t_atom *argv) {\n  t_pd *obj;\n  sys_lock();\n  obj = get_object(recv);\n  if (obj == NULL)\n  {\n    sys_unlock();\n    return -1;\n  }\n  pd_list(obj, &s_list, argc, argv);\n  sys_unlock();\n  return 0;\n}\n\nint libpd_message(const char *recv, const char *msg, int argc, t_atom *argv) {\n  t_pd *obj;\n  sys_lock();\n  obj = get_object(recv);\n  if (obj == NULL)\n  {\n    sys_unlock();\n    return -1;\n  }\n  pd_typedmess(obj, gensym(msg), argc, argv);\n  sys_unlock();\n  return 0;\n}\n\nvoid *libpd_bind(const char *recv) {\n  t_symbol *x;\n  sys_lock();\n  x = gensym(recv);\n  sys_unlock();\n  return libpdreceive_new(x);\n}\n\nvoid libpd_unbind(void *p) {\n  sys_lock();\n  pd_free((t_pd *)p);\n  sys_unlock();\n}\n\nint libpd_exists(const char *recv) {\n  int retval;\n  sys_lock();\n  retval = (get_object(recv) != NULL);\n  sys_unlock();\n  return retval;\n}\n\n// when setting hooks, use mainimp if pd is not yet inited\n#define IMP (s_initialized ? LIBPDSTUFF : &libpd_mainimp)\n\nvoid libpd_set_printhook(const t_libpd_printhook hook) {\n  if (!s_initialized) // set default hook\n    sys_printhook = (t_printhook)hook;\n  else // set instance hook\n    STUFF->st_printhook = (t_printhook)hook;\n}\n\nvoid libpd_set_banghook(const t_libpd_banghook hook) {\n  IMP->i_hooks.h_banghook = hook;\n}\n\nvoid libpd_set_floathook(const t_libpd_floathook hook) {\n  IMP->i_hooks.h_floathook = hook;\n  IMP->i_hooks.h_doublehook = NULL;\n}\n\nvoid libpd_set_doublehook(const t_libpd_doublehook hook) {\n  IMP->i_hooks.h_floathook = NULL;\n  IMP->i_hooks.h_doublehook = hook;\n}\n\nvoid libpd_set_symbolhook(const t_libpd_symbolhook hook) {\n  IMP->i_hooks.h_symbolhook = hook;\n}\n\nvoid libpd_set_listhook(const t_libpd_listhook hook) {\n  IMP->i_hooks.h_listhook = hook;\n}\n\nvoid libpd_set_messagehook(const t_libpd_messagehook hook) {\n  IMP->i_hooks.h_messagehook = hook;\n}\n\nint libpd_is_float(t_atom *a) {\n  return (a)->a_type == A_FLOAT;\n}\n\nint libpd_is_symbol(t_atom *a) {\n  return (a)->a_type == A_SYMBOL;\n}\n\nfloat libpd_get_float(t_atom *a) {\n  return (a)->a_w.w_float;\n}\n\ndouble libpd_get_double(t_atom *a) {\n  return (a)->a_w.w_float;\n}\n\nconst char *libpd_get_symbol(t_atom *a) {\n  return (a)->a_w.w_symbol->s_name;\n}\n\nt_atom *libpd_next_atom(t_atom *a) {\n  return a + 1;\n}\n\n#define CHECK_CHANNEL if (channel < 0) return -1;\n#define CHECK_PORT if (port < 0 || port > 0x0fff) return -1;\n#define CHECK_RANGE_7BIT(v) if (v < 0 || v > 0x7f) return -1;\n#define CHECK_RANGE_8BIT(v) if (v < 0 || v > 0xff) return -1;\n#define PORT (channel >> 4)\n#define CHANNEL (channel & 0x0f)\n\nint libpd_noteon(int channel, int pitch, int velocity) {\n  CHECK_CHANNEL\n  CHECK_RANGE_7BIT(pitch)\n  CHECK_RANGE_7BIT(velocity)\n  sys_lock();\n  inmidi_noteon(PORT, CHANNEL, pitch, velocity);\n  sys_unlock();\n  return 0;\n}\n\nint libpd_controlchange(int channel, int controller, int value) {\n  CHECK_CHANNEL\n  CHECK_RANGE_7BIT(controller)\n  CHECK_RANGE_7BIT(value)\n  sys_lock();\n  inmidi_controlchange(PORT, CHANNEL, controller, value);\n  sys_unlock();\n  return 0;\n}\n\nint libpd_programchange(int channel, int value) {\n  CHECK_CHANNEL\n  CHECK_RANGE_7BIT(value)\n  sys_lock();\n  inmidi_programchange(PORT, CHANNEL, value);\n  sys_unlock();\n  return 0;\n}\n\n// note: for consistency with Pd, we center the output of [pitchin] at 8192\nint libpd_pitchbend(int channel, int value) {\n  CHECK_CHANNEL\n  if (value < -8192 || value > 8191) return -1;\n  sys_lock();\n  inmidi_pitchbend(PORT, CHANNEL, value + 8192);\n  sys_unlock();\n  return 0;\n}\n\nint libpd_aftertouch(int channel, int value) {\n  CHECK_CHANNEL\n  CHECK_RANGE_7BIT(value)\n  sys_lock();\n  inmidi_aftertouch(PORT, CHANNEL, value);\n  sys_unlock();\n  return 0;\n}\n\nint libpd_polyaftertouch(int channel, int pitch, int value) {\n  CHECK_CHANNEL\n  CHECK_RANGE_7BIT(pitch)\n  CHECK_RANGE_7BIT(value)\n  sys_lock();\n  inmidi_polyaftertouch(PORT, CHANNEL, pitch, value);\n  sys_unlock();\n  return 0;\n}\n\nint libpd_midibyte(int port, int byte) {\n  CHECK_PORT\n  CHECK_RANGE_8BIT(byte)\n  sys_lock();\n  inmidi_byte(port, byte);\n  sys_unlock();\n  return 0;\n}\n\nint libpd_sysex(int port, int byte) {\n  CHECK_PORT\n  CHECK_RANGE_8BIT(byte)\n  sys_lock();\n  inmidi_sysex(port, byte);\n  sys_unlock();\n  return 0;\n}\n\nint libpd_sysrealtime(int port, int byte) {\n  CHECK_PORT\n  CHECK_RANGE_8BIT(byte)\n  sys_lock();\n  inmidi_realtimein(port, byte);\n  sys_unlock();\n  return 0;\n}\n\nvoid libpd_set_noteonhook(const t_libpd_noteonhook hook) {\n  IMP->i_hooks.h_noteonhook = hook;\n}\n\nvoid libpd_set_controlchangehook(const t_libpd_controlchangehook hook) {\n  IMP->i_hooks.h_controlchangehook = hook;\n}\n\nvoid libpd_set_programchangehook(const t_libpd_programchangehook hook) {\n  IMP->i_hooks.h_programchangehook = hook;\n}\n\nvoid libpd_set_pitchbendhook(const t_libpd_pitchbendhook hook) {\n  IMP->i_hooks.h_pitchbendhook = hook;\n}\n\nvoid libpd_set_aftertouchhook(const t_libpd_aftertouchhook hook) {\n  IMP->i_hooks.h_aftertouchhook = hook;\n}\n\nvoid libpd_set_polyaftertouchhook(const t_libpd_polyaftertouchhook hook) {\n  IMP->i_hooks.h_polyaftertouchhook = hook;\n}\n\nvoid libpd_set_midibytehook(const t_libpd_midibytehook hook) {\n  IMP->i_hooks.h_midibytehook = hook;\n}\n\nint libpd_start_gui(const char *path) {\n  int retval;\n  sys_lock();\n  retval = sys_startgui(path);\n  sys_unlock();\n  return retval;\n}\n\nvoid libpd_stop_gui(void) {\n  sys_lock();\n  sys_stopgui();\n  sys_unlock();\n}\n\nint libpd_poll_gui(void) {\n  int retval;\n  sys_lock();\n  retval = sys_pollgui();\n  sys_unlock();\n  return (retval);\n}\n\nt_pdinstance *libpd_new_instance(void) {\n#ifdef PDINSTANCE\n  t_pdinstance *pd = pdinstance_new();\n  pd->pd_stuff->st_impdata = libpdimp_new();\n  return pd;\n#else\n  return NULL;\n#endif\n}\n\nvoid libpd_set_instance(t_pdinstance *pd) {\n#ifdef PDINSTANCE\n  pd_setinstance(pd);\n#endif\n}\n\nvoid libpd_free_instance(t_pdinstance *pd) {\n#ifdef PDINSTANCE\n  if (pd == &pd_maininstance) return;\n  libpdimp_free(pd->pd_stuff->st_impdata);\n  pdinstance_free(pd);\n#endif\n}\n\nt_pdinstance *libpd_this_instance(void) {\n  return pd_this;\n}\n\nt_pdinstance *libpd_main_instance(void) {\n  return &pd_maininstance;\n}\n\nint libpd_num_instances(void) {\n#ifdef PDINSTANCE\n  return pd_ninstances;\n#else\n  return 1;\n#endif\n}\n\nvoid libpd_set_instancedata(void *data, t_libpd_freehook freehook) {\n  LIBPDSTUFF->i_data = data;\n  LIBPDSTUFF->i_data_freehook = freehook;\n}\n\nvoid* libpd_get_instancedata() {\n  return LIBPDSTUFF->i_data;\n}\n\nvoid libpd_set_verbose(int verbose) {\n  if (verbose < 0) verbose = 0;\n  sys_verbose = verbose;\n}\n\nint libpd_get_verbose(void) {\n  return sys_verbose;\n}\n\n// dummy routines needed because we don't use s_file.c\nvoid glob_loadpreferences(t_pd *dummy, t_symbol *s) {}\nvoid glob_savepreferences(t_pd *dummy, t_symbol *s) {}\nvoid glob_forgetpreferences(t_pd *dummy) {}\nvoid sys_loadpreferences(const char *filename, int startingup) {}\nint sys_oktoloadfiles(int done) {return 1;}\nvoid sys_savepreferences(const char *filename) {} // used in s_path.c\n"
  },
  {
    "path": "libs/libpd/libpd_wrapper/z_libpd.h",
    "content": "/*\n * Copyright (c) 2010 Peter Brinkmann (peter.brinkmann@gmail.com)\n * Copyright (c) 2012-2021 libpd team\n *\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.\n *\n * See https://github.com/libpd/libpd/wiki for documentation\n *\n */\n\n#ifndef __Z_LIBPD_H__\n#define __Z_LIBPD_H__\n\n#ifdef __cplusplus\nextern \"C\"\n{\n#endif\n\n#include \"m_pd.h\"\n\n/* initializing pd */\n\n/// initialize libpd; it is safe to call this more than once\n/// returns 0 on success or -1 if libpd was already initialized\n/// note: sets SIGFPE handler to keep bad pd patches from crashing due to divide\n///       by 0, set any custom handling after calling this function\nEXTERN int libpd_init(void);\n\n/// clear the libpd search path for abstractions and externals\n/// note: this is called by libpd_init()\nEXTERN void libpd_clear_search_path(void);\n\n/// add a path to the libpd search paths\n/// relative paths are relative to the current working directory\n/// unlike desktop pd, *no* search paths are set by default (ie. extra)\nEXTERN void libpd_add_to_search_path(const char *path);\n\n/* opening patches */\n\n/// open a patch by filename and parent dir path\n/// returns an opaque patch handle pointer or NULL on failure\nEXTERN void *libpd_openfile(const char *name, const char *dir);\n\n/// close a patch by patch handle pointer\nEXTERN void libpd_closefile(void *p);\n\n/// get the $0 id of the patch handle pointer\n/// returns $0 value or 0 if the patch is non-existent\nEXTERN int libpd_getdollarzero(void *p);\n\n/* audio processing */\n\n/// return pd's fixed block size: the number of sample frames per 1 pd tick\nEXTERN int libpd_blocksize(void);\n\n/// initialize audio rendering\n/// returns 0 on success\nEXTERN int libpd_init_audio(int inChannels, int outChannels, int sampleRate);\n\n/// process interleaved float samples from inBuffer -> libpd -> outBuffer\n/// buffer sizes are based on # of ticks and channels where:\n///     size = ticks * libpd_blocksize() * (in/out)channels\n/// returns 0 on success\nEXTERN int libpd_process_float(const int ticks,\n    const float *inBuffer, float *outBuffer);\n\n/// process interleaved short samples from inBuffer -> libpd -> outBuffer\n/// buffer sizes are based on # of ticks and channels where:\n///     size = ticks * libpd_blocksize() * (in/out)channels\n/// float samples are converted to short by multiplying by 32767 and casting,\n/// so any values received from pd patches beyond -1 to 1 will result in garbage\n/// note: for efficiency, does *not* clip input\n/// returns 0 on success\nEXTERN int libpd_process_short(const int ticks,\n    const short *inBuffer, short *outBuffer);\n\n/// process interleaved double samples from inBuffer -> libpd -> outBuffer\n/// buffer sizes are based on # of ticks and channels where:\n///     size = ticks * libpd_blocksize() * (in/out)channels\n/// note: only full-precision when compiled with PD_FLOATSIZE=64\n/// returns 0 on success\nEXTERN int libpd_process_double(const int ticks,\n    const double *inBuffer, double *outBuffer);\n\n/// process non-interleaved float samples from inBuffer -> libpd -> outBuffer\n/// copies buffer contents to/from libpd without striping\n/// buffer sizes are based on a single tick and # of channels where:\n///     size = libpd_blocksize() * (in/out)channels\n/// returns 0 on success\nEXTERN int libpd_process_raw(const float *inBuffer, float *outBuffer);\n\n/// process non-interleaved short samples from inBuffer -> libpd -> outBuffer\n/// copies buffer contents to/from libpd without striping\n/// buffer sizes are based on a single tick and # of channels where:\n///     size = libpd_blocksize() * (in/out)channels\n/// float samples are converted to short by multiplying by 32767 and casting,\n/// so any values received from pd patches beyond -1 to 1 will result in garbage\n/// note: for efficiency, does *not* clip input\n/// returns 0 on success\nEXTERN int libpd_process_raw_short(const short *inBuffer, short *outBuffer);\n\n/// process non-interleaved double samples from inBuffer -> libpd -> outBuffer\n/// copies buffer contents to/from libpd without striping\n/// buffer sizes are based on a single tick and # of channels where:\n///     size = libpd_blocksize() * (in/out)channels\n/// note: only full-precision when compiled with PD_FLOATSIZE=64\n/// returns 0 on success\nEXTERN int libpd_process_raw_double(const double *inBuffer, double *outBuffer);\n\n/* array access */\n\n/// get the size of an array by name\n/// returns size or negative error code if non-existent\nEXTERN int libpd_arraysize(const char *name);\n\n/// (re)size an array by name; sizes <= 0 are clipped to 1\n/// returns 0 on success or negative error code if non-existent\nEXTERN int libpd_resize_array(const char *name, long size);\n\n/// read n values from named src array and write into dest starting at an offset\n/// note: performs no bounds checking on dest\n/// returns 0 on success or a negative error code if the array is non-existent\n/// or offset + n exceeds range of array\nEXTERN int libpd_read_array(float *dest, const char *name, int offset, int n);\n\n/// read n values from src and write into named dest array starting at an offset\n/// note: performs no bounds checking on src\n/// returns 0 on success or a negative error code if the array is non-existent\n/// or offset + n exceeds range of array\nEXTERN int libpd_write_array(const char *name, int offset,\n    const float *src, int n);\n\n/// read n values from named src array and write into dest starting at an offset\n/// note: performs no bounds checking on dest\n/// note: only full-precision when compiled with PD_FLOATSIZE=64\n/// returns 0 on success or a negative error code if the array is non-existent\n/// or offset + n exceeds range of array\n/// double-precision variant of libpd_read_array()\nEXTERN int libpd_read_array_double(double *dest, const char *src,\n    int offset, int n);\n\n/// read n values from src and write into named dest array starting at an offset\n/// note: performs no bounds checking on src\n/// note: only full-precision when compiled with PD_FLOATSIZE=64\n/// returns 0 on success or a negative error code if the array is non-existent\n/// or offset + n exceeds range of array\n/// double-precision variant of libpd_write_array()\nEXTERN int libpd_write_array_double(const char *dest, int offset,\n    const double *src, int n);\n\n/* sending messages to pd */\n\n/// send a bang to a destination receiver\n/// ex: libpd_bang(\"foo\") will send a bang to [s foo] on the next tick\n/// returns 0 on success or -1 if receiver name is non-existent\nEXTERN int libpd_bang(const char *recv);\n\n/// send a float to a destination receiver\n/// ex: libpd_float(\"foo\", 1) will send a 1.0 to [s foo] on the next tick\n/// returns 0 on success or -1 if receiver name is non-existent\nEXTERN int libpd_float(const char *recv, float x);\n\n/// send a double to a destination receiver\n/// ex: libpd_double(\"foo\", 1.1) will send a 1.1 to [s foo] on the next tick\n/// note: only full-precision when compiled with PD_FLOATSIZE=64\n/// returns 0 on success or -1 if receiver name is non-existent\nEXTERN int libpd_double(const char *recv, double x);\n\n/// send a symbol to a destination receiver\n/// ex: libpd_symbol(\"foo\", \"bar\") will send \"bar\" to [s foo] on the next tick\n/// returns 0 on success or -1 if receiver name is non-existent\nEXTERN int libpd_symbol(const char *recv, const char *symbol);\n\n/* sending compound messages: sequenced function calls */\n\n/// start composition of a new list or typed message of up to max element length\n/// messages can be of a smaller length as max length is only an upper bound\n/// note: no cleanup is required for unfinished messages\n/// returns 0 on success or nonzero if the length is too large\nEXTERN int libpd_start_message(int maxlen);\n\n/// add a float to the current message in progress\nEXTERN void libpd_add_float(float x);\n\n/// add a double to the current message in progress\n/// note: only full-precision when compiled with PD_FLOATSIZE=64\nEXTERN void libpd_add_double(double x);\n\n/// add a symbol to the current message in progress\nEXTERN void libpd_add_symbol(const char *symbol);\n\n/// finish current message and send as a list to a destination receiver\n/// returns 0 on success or -1 if receiver name is non-existent\n/// ex: send [list 1 2 bar( to [s foo] on the next tick with:\n///     libpd_start_message(3);\n///     libpd_add_float(1);\n///     libpd_add_float(2);\n///     libpd_add_symbol(\"bar\");\n///     libpd_finish_list(\"foo\");\nEXTERN int libpd_finish_list(const char *recv);\n\n/// finish current message and send as a typed message to a destination receiver\n/// note: typed message handling currently only supports up to 4 elements\n///       internally, additional elements may be ignored\n/// returns 0 on success or -1 if receiver name is non-existent\n/// ex: send [; pd dsp 1( on the next tick with:\n///     libpd_start_message(1);\n///     libpd_add_float(1);\n///     libpd_finish_message(\"pd\", \"dsp\");\nEXTERN int libpd_finish_message(const char *recv, const char *msg);\n\n/* sending compound messages: atom array */\n\n/// write a float value to the given atom\nEXTERN void libpd_set_float(t_atom *a, float x);\n\n/// write a double value to the given atom\n/// note: only full-precision when compiled with PD_FLOATSIZE=64\nEXTERN void libpd_set_double(t_atom *v, double x);\n\n/// write a symbol value to the given atom\nEXTERN void libpd_set_symbol(t_atom *a, const char *symbol);\n\n/// send an atom array of a given length as a list to a destination receiver\n/// returns 0 on success or -1 if receiver name is non-existent\n/// ex: send [list 1 2 bar( to [r foo] on the next tick with:\n///     t_atom v[3];\n///     libpd_set_float(v, 1);\n///     libpd_set_float(v + 1, 2);\n///     libpd_set_symbol(v + 2, \"bar\");\n///     libpd_list(\"foo\", 3, v);\nEXTERN int libpd_list(const char *recv, int argc, t_atom *argv);\n\n/// send a atom array of a given length as a typed message to a destination\n/// receiver, returns 0 on success or -1 if receiver name is non-existent\n/// ex: send [; pd dsp 1( on the next tick with:\n///     t_atom v[1];\n///     libpd_set_float(v, 1);\n///     libpd_message(\"pd\", \"dsp\", 1, v);\nEXTERN int libpd_message(const char *recv, const char *msg,\n    int argc, t_atom *argv);\n\n/* receiving messages from pd */\n\n/// subscribe to messages sent to a source receiver\n/// ex: libpd_bind(\"foo\") adds a \"virtual\" [r foo] which forwards messages to\n///     the libpd message hooks\n/// returns an opaque receiver pointer or NULL on failure\nEXTERN void *libpd_bind(const char *recv);\n\n/// unsubscribe and free a source receiver object created by libpd_bind()\nEXTERN void libpd_unbind(void *p);\n\n/// check if a source receiver object exists with a given name\n/// returns 1 if the receiver exists, otherwise 0\nEXTERN int libpd_exists(const char *recv);\n\n/// print receive hook signature, s is the string to be printed\n/// note: default behavior returns individual words and spaces:\n///     line \"hello 123\" is received in 4 parts -> \"hello\", \" \", \"123\\n\"\ntypedef void (*t_libpd_printhook)(const char *s);\n\n/// bang receive hook signature, recv is the source receiver name\ntypedef void (*t_libpd_banghook)(const char *recv);\n\n/// float receive hook signature, recv is the source receiver name\ntypedef void (*t_libpd_floathook)(const char *recv, float x);\n\n/// double receive hook signature, recv is the source receiver name\n/// note: only full-precision when compiled with PD_FLOATSIZE=64\ntypedef void (*t_libpd_doublehook)(const char *recv, double x);\n\n/// symbol receive hook signature, recv is the source receiver name\ntypedef void (*t_libpd_symbolhook)(const char *recv, const char *symbol);\n\n/// list receive hook signature, recv is the source receiver name\n/// argc is the list length and vector argv contains the list elements\n/// which can be accessed using the atom accessor functions, ex:\n///     int i;\n///     for (i = 0; i < argc; i++) {\n///       t_atom *a = &argv[n];\n///       if (libpd_is_float(a)) {\n///         float x = libpd_get_float(a);\n///         // do something with float x\n///       } else if (libpd_is_symbol(a)) {\n///         char *s = libpd_get_symbol(a);\n///         // do something with c string s\n///       }\n///     }\n/// note: check for both float and symbol types as atom may also be a pointer\ntypedef void (*t_libpd_listhook)(const char *recv, int argc, t_atom *argv);\n\n/// typed message hook signature, recv is the source receiver name and msg is\n/// the typed message name: a message like [; foo bar 1 2 a b( will trigger a\n/// function call like libpd_messagehook(\"foo\", \"bar\", 4, argv)\n/// argc is the list length and vector argv contains the\n/// list elements which can be accessed using the atom accessor functions, ex:\n///     int i;\n///     for (i = 0; i < argc; i++) {\n///       t_atom *a = &argv[n];\n///       if (libpd_is_float(a)) {\n///         float x = libpd_get_float(a);\n///         // do something with float x\n///       } else if (libpd_is_symbol(a)) {\n///         char *s = libpd_get_symbol(a);\n///         // do something with c string s\n///       }\n///     }\n/// note: check for both float and symbol types as atom may also be a pointer\ntypedef void (*t_libpd_messagehook)(const char *recv, const char *msg,\n    int argc, t_atom *argv);\n\n/// set the print receiver hook, prints to stdout by default\n/// note: do not call this while DSP is running\nEXTERN void libpd_set_printhook(const t_libpd_printhook hook);\n\n/// set the bang receiver hook, NULL by default\n/// note: do not call this while DSP is running\nEXTERN void libpd_set_banghook(const t_libpd_banghook hook);\n\n/// set the float receiver hook, NULL by default\n/// note: avoid calling this while DSP is running\n/// note: you can either have a float receiver hook, or a double receiver\n///       hook (see below), but not both.\n///       calling this, will automatically unset the double receiver hook\n/// note: only full-precision when compiled with PD_FLOATSIZE=64\nEXTERN void libpd_set_floathook(const t_libpd_floathook hook);\n\n/// set the double receiver hook, NULL by default\n/// note: avoid calling this while DSP is running\n/// note: you can either have a double receiver hook, or a float receiver\n///       hook (see above), but not both.\n///       calling this, will automatically unset the float receiver hook\n/// note: only full-precision when compiled with PD_FLOATSIZE=64\nEXTERN void libpd_set_doublehook(const t_libpd_doublehook hook);\n\n/// set the symbol receiver hook, NULL by default\n/// note: do not call this while DSP is running\nEXTERN void libpd_set_symbolhook(const t_libpd_symbolhook hook);\n\n/// set the list receiver hook, NULL by default\n/// note: do not call this while DSP is running\nEXTERN void libpd_set_listhook(const t_libpd_listhook hook);\n\n/// set the message receiver hook, NULL by default\n/// note: do not call this while DSP is running\nEXTERN void libpd_set_messagehook(const t_libpd_messagehook hook);\n\n/// check if an atom is a float type: 0 or 1\n/// note: no NULL check is performed\nEXTERN int libpd_is_float(t_atom *a);\n\n/// check if an atom is a symbol type: 0 or 1\n/// note: no NULL check is performed\nEXTERN int libpd_is_symbol(t_atom *a);\n\n/// get the float value of an atom\n/// note: no NULL or type checks are performed\nEXTERN float libpd_get_float(t_atom *a);\n\n/// returns the double value of an atom\n/// note: no NULL or type checks are performed\n/// note: only full-precision when compiled with PD_FLOATSIZE=64\nEXTERN double libpd_get_double(t_atom *a);\n\n/// returns the symbol value of an atom\n/// note: no NULL or type checks are performed\nEXTERN const char *libpd_get_symbol(t_atom *a);\n\n/// increment to the next atom in an atom vector\n/// returns next atom or NULL, assuming the atom vector is NULL-terminated\nEXTERN t_atom *libpd_next_atom(t_atom *a);\n\n/* sending MIDI messages to pd */\n\n/// send a MIDI note on message to [notein] objects\n/// channel is 0-indexed, pitch is 0-127, and velocity is 0-127\n/// channels encode MIDI ports via: libpd_channel = pd_channel + 16 * pd_port\n/// note: there is no note off message, send a note on with velocity = 0 instead\n/// returns 0 on success or -1 if an argument is out of range\nEXTERN int libpd_noteon(int channel, int pitch, int velocity);\n\n/// send a MIDI control change message to [ctlin] objects\n/// channel is 0-indexed, controller is 0-127, and value is 0-127\n/// channels encode MIDI ports via: libpd_channel = pd_channel + 16 * pd_port\n/// returns 0 on success or -1 if an argument is out of range\nEXTERN int libpd_controlchange(int channel, int controller, int value);\n\n/// send a MIDI program change message to [pgmin] objects\n/// channel is 0-indexed and value is 0-127\n/// channels encode MIDI ports via: libpd_channel = pd_channel + 16 * pd_port\n/// returns 0 on success or -1 if an argument is out of range\nEXTERN int libpd_programchange(int channel, int value);\n\n/// send a MIDI pitch bend message to [bendin] objects\n/// channel is 0-indexed and value is -8192-8192\n/// channels encode MIDI ports via: libpd_channel = pd_channel + 16 * pd_port\n/// note: [bendin] outputs 0-16383 while [bendout] accepts -8192-8192\n/// returns 0 on success or -1 if an argument is out of range\nEXTERN int libpd_pitchbend(int channel, int value);\n\n/// send a MIDI after touch message to [touchin] objects\n/// channel is 0-indexed and value is 0-127\n/// channels encode MIDI ports via: libpd_channel = pd_channel + 16 * pd_port\n/// returns 0 on success or -1 if an argument is out of range\nEXTERN int libpd_aftertouch(int channel, int value);\n\n/// send a MIDI poly after touch message to [polytouchin] objects\n/// channel is 0-indexed, pitch is 0-127, and value is 0-127\n/// channels encode MIDI ports via: libpd_channel = pd_channel + 16 * pd_port\n/// returns 0 on success or -1 if an argument is out of range\nEXTERN int libpd_polyaftertouch(int channel, int pitch, int value);\n\n/// send a raw MIDI byte to [midiin] objects\n/// port is 0-indexed and byte is 0-256\n/// returns 0 on success or -1 if an argument is out of range\nEXTERN int libpd_midibyte(int port, int byte);\n\n/// send a raw MIDI byte to [sysexin] objects\n/// port is 0-indexed and byte is 0-256\n/// returns 0 on success or -1 if an argument is out of range\nEXTERN int libpd_sysex(int port, int byte);\n\n/// send a raw MIDI byte to [realtimein] objects\n/// port is 0-indexed and byte is 0-256\n/// returns 0 on success or -1 if an argument is out of range\nEXTERN int libpd_sysrealtime(int port, int byte);\n\n/* receiving MIDI messages from pd */\n\n/// MIDI note on receive hook signature\n/// channel is 0-indexed, pitch is 0-127, and value is 0-127\n/// channels encode MIDI ports via: libpd_channel = pd_channel + 16 * pd_port\n/// note: there is no note off message, note on w/ velocity = 0 is used instead\n/// note: out of range values from pd are clamped\ntypedef void (*t_libpd_noteonhook)(int channel, int pitch, int velocity);\n\n/// MIDI control change receive hook signature\n/// channel is 0-indexed, controller is 0-127, and value is 0-127\n/// channels encode MIDI ports via: libpd_channel = pd_channel + 16 * pd_port\n/// note: out of range values from pd are clamped\ntypedef void (*t_libpd_controlchangehook)(int channel,\n    int controller, int value);\n\n/// MIDI program change receive hook signature\n/// channel is 0-indexed and value is 0-127\n/// channels encode MIDI ports via: libpd_channel = pd_channel + 16 * pd_port\n/// note: out of range values from pd are clamped\ntypedef void (*t_libpd_programchangehook)(int channel, int value);\n\n/// MIDI pitch bend receive hook signature\n/// channel is 0-indexed and value is -8192-8192\n/// channels encode MIDI ports via: libpd_channel = pd_channel + 16 * pd_port\n/// note: [bendin] outputs 0-16383 while [bendout] accepts -8192-8192\n/// note: out of range values from pd are clamped\ntypedef void (*t_libpd_pitchbendhook)(int channel, int value);\n\n/// MIDI after touch receive hook signature\n/// channel is 0-indexed and value is 0-127\n/// channels encode MIDI ports via: libpd_channel = pd_channel + 16 * pd_port\n/// note: out of range values from pd are clamped\ntypedef void (*t_libpd_aftertouchhook)(int channel, int value);\n\n/// MIDI poly after touch receive hook signature\n/// channel is 0-indexed, pitch is 0-127, and value is 0-127\n/// channels encode MIDI ports via: libpd_channel = pd_channel + 16 * pd_port\n/// note: out of range values from pd are clamped\ntypedef void (*t_libpd_polyaftertouchhook)(int channel, int pitch, int value);\n\n/// raw MIDI byte receive hook signature\n/// port is 0-indexed and byte is 0-256\n/// note: out of range values from pd are clamped\ntypedef void (*t_libpd_midibytehook)(int port, int byte);\n\n/// set the MIDI note on hook to receive from [noteout] objects, NULL by default\n/// note: do not call this while DSP is running\nEXTERN void libpd_set_noteonhook(const t_libpd_noteonhook hook);\n\n/// set the MIDI control change hook to receive from [ctlout] objects,\n/// NULL by default\n/// note: do not call this while DSP is running\nEXTERN void libpd_set_controlchangehook(const t_libpd_controlchangehook hook);\n\n/// set the MIDI program change hook to receive from [pgmout] objects,\n/// NULL by default\n/// note: do not call this while DSP is running\nEXTERN void libpd_set_programchangehook(const t_libpd_programchangehook hook);\n\n/// set the MIDI pitch bend hook to receive from [bendout] objects,\n/// NULL by default\n/// note: do not call this while DSP is running\nEXTERN void libpd_set_pitchbendhook(const t_libpd_pitchbendhook hook);\n\n/// set the MIDI after touch hook to receive from [touchout] objects,\n/// NULL by default\n/// note: do not call this while DSP is running\nEXTERN void libpd_set_aftertouchhook(const t_libpd_aftertouchhook hook);\n\n/// set the MIDI poly after touch hook to receive from [polytouchout] objects,\n/// NULL by default\n/// note: do not call this while DSP is running\nEXTERN void libpd_set_polyaftertouchhook(const t_libpd_polyaftertouchhook hook);\n\n/// set the raw MIDI byte hook to receive from [midiout] objects,\n/// NULL by default\n/// note: do not call this while DSP is running\nEXTERN void libpd_set_midibytehook(const t_libpd_midibytehook hook);\n\n/* GUI */\n\n/// open the current patches within a pd vanilla GUI\n/// requires the path to pd's main folder that contains bin/, tcl/, etc\n/// for a macOS .app bundle: /path/to/Pd-#.#-#.app/Contents/Resources\n/// returns 0 on success\nEXTERN int libpd_start_gui(const char *path);\n\n/// stop the pd vanilla GUI\nEXTERN void libpd_stop_gui(void);\n\n/// manually update and handle any GUI messages\n/// this is called automatically when using a libpd_process function,\n/// note: this also facilitates network message processing, etc so it can be\n///       useful to call repeatedly when idle for more throughput\n/// returns 1 if the poll found something, in which case it might be desirable\n/// to poll again, up to some reasonable limit\nEXTERN int libpd_poll_gui(void);\n\n/* multiple instances */\n\n/// create a new pd instance and set as current\n/// note: use this in place of pdinstance_new()\n/// returns new instance or NULL when libpd is not compiled with PDINSTANCE\nEXTERN t_pdinstance *libpd_new_instance(void);\n\n/// set the current pd instance\n/// subsequent libpd calls will affect this instance only\n/// note: use this in place of pd_setinstance()\n/// does nothing when libpd is not compiled with PDINSTANCE\nEXTERN void libpd_set_instance(t_pdinstance *pd);\n\n/// free a pd instance and set main instance as current\n/// note: use this in place of pdinstance_free()\n/// does nothing when libpd is not compiled with PDINSTANCE\nEXTERN void libpd_free_instance(t_pdinstance *pd);\n\n/// get the current pd instance\nEXTERN t_pdinstance *libpd_this_instance(void);\n\n/// get the main pd instance, always valid\nEXTERN t_pdinstance *libpd_main_instance(void);\n\n/// get the number of pd instances, including the main instance\n/// returns number or 1 when libpd is not compiled with PDINSTANCE\nEXTERN int libpd_num_instances(void);\n\n/// per-instance data free hook signature\ntypedef void (*t_libpd_freehook)(void *data);\n\n/// set per-instance user data and optional free hook\n/// note: if non-NULL, freehook is called by libpd_free_instance()\nEXTERN void libpd_set_instancedata(void *data, t_libpd_freehook freehook);\n\n/// get per-instance user data\nEXTERN void* libpd_get_instancedata(void);\n\n/* log level */\n\n/// set verbose print state: 0 or 1\nEXTERN void libpd_set_verbose(int verbose);\n\n/// get the verbose print state: 0 or 1\nEXTERN int libpd_get_verbose(void);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "libs/libpd/pure-data/extra/README.txt",
    "content": "This is the README file for the \"extras\" library, which contains Pd objects\nwhich are too specialized or otherwise non-canonical for inclusion into Pd\nproper.   This library is part of the regular (\"vanilla\") Pd distribution.  It\nis all open source; see LICENSE.txt in the Pd distribution for details.\n\ncontents:\n\ngenerally useful externs:\n[sigmund~] - pitch and sinusoidal peak analysis\n[bonk~] - percussion detector\n[lrshift~] - left or right shift an audio vector (probably should be standard)\n\nabstractions:\n[hilbert~] - Hilbert transform for SSB modulation\n[complex-mod~] - ring modulation for complex (real+imaginary) audio signals\n[rev1~], etc. - reverberators\n[output~] - stereo output abstraction for convenience\n\nexterns aimed at particular tasks:\n[pd~] - embed one Pd inside another one\n[stdout] - send messages to standard out (useful in pd~ sub-process)\n[bob~] - Moog ladder filter simulation using a Runge-Kutte ODE solver\n[choice] - find the best fit of a vector to a set of example vectors\n[loop~] - sample looper\n\nobsolete:\n[pique] - fft-based peak finder (use [sigmund~] instead)\n[fiddle~] - pitch tracker (use [sigmund~] instead)\n\nThe [sigmund~], [bonk~], and [fiddle~] objects have also been compiled for Max/MSP\nby Ted Apel; see his website for details.\n"
  },
  {
    "path": "libs/libpd/pure-data/extra/bob~/README.txt",
    "content": "The bob~ object.  BSD licensed; Copyright notice is in bob~ source code.\n\nImitates a Moog resonant filter by Runge-Kutte numerical integration of\na differential equation approximately describing the dynamics of the circuit.\n\nUseful references:\n\nTim Stilson\nAnalyzing the Moog VCF with Considerations for Digital Implementation\nhttps://ccrma.stanford.edu/~stilti/papers/moogvcf.ps.gz\n\n(sections 1 and 2 are a reasonably good introduction but the model they use\nis highly idealized.)\n\nTimothy E. Stinchcombe\nAnalysis of the Moog Transistor Ladder and Derivative Filters\n\n(long, but a very thorough description of how the filter works including\nits nonlinearities)\n\nAntti Huovilainen\nNon-linear digital implementation of the moog ladder filter\n\n(comes close to giving a differential equation for a reasonably realistic\nmodel of the filter).\n\nTh differential equations are:\n\ny1' = k * (S(x - r * y4) - S(y1))\ny2' = k * (S(y1) - S(y2))\ny3' = k * (S(y2) - S(y3))\ny4' = k * (S(y3) - S(y4))\n\nwhere k controls the cutoff frequency, r is feedback (<= 4 for\nstability), and S(x) is a saturation function.\n"
  },
  {
    "path": "libs/libpd/pure-data/extra/bob~/bob~.c",
    "content": "/* bob~ - use a differential equation solver to imitate an analogue circuit */\n\n/* copyright 2015 Miller Puckette - BSD license */\n\n#include \"m_pd.h\"\n#include <math.h>\n#define DIM 4\n#define FLOAT double\n\n/* if CALCERROR is defined we compute an error estaimate to verify\nthe filter, outputting it from a second outlet on demand.  This\ndoubles the computation time, so it's only compiled in for testing. */\n\n/* #define CALCERROR */\n\ntypedef struct _params\n{\n    FLOAT p_input;\n    FLOAT p_cutoff;\n    FLOAT p_resonance;\n    FLOAT p_saturation;\n    FLOAT p_derivativeswere[DIM];\n} t_params;\n\n    /* imitate the (tanh) clipping function of a transistor pair.  We\n    hope/assume the C compiler is smart enough to inline this so use\n    a function instead of a #define. */\n#if 0\nstatic FLOAT clip(FLOAT value, FLOAT saturation, FLOAT saturationinverse)\n{\n    return (saturation * tanh(value * saturationinverse));\n}\n#else\n    /* cheaper way - to 4th order, tanh is x - x*x*x/3; this cubic's\n    plateaus are at +/- 1 so clip to 1 and evaluate the cubic.\n    This is pretty coarse - for instance if you clip a sinusoid this way you\n    can sometimes hear the discontinuity in 4th derivative at the clip point */\nstatic FLOAT clip(FLOAT value, FLOAT saturation, FLOAT saturationinverse)\n{\n    float v2 = (value*saturationinverse > 1 ? 1 :\n        (value*saturationinverse < -1 ? -1:\n            value*saturationinverse));\n    return (saturation * (v2 - (1./3.) * v2 * v2 * v2));\n}\n#endif\n\nstatic void calc_derivatives(FLOAT *dstate, FLOAT *state, t_params *params)\n{\n    FLOAT k = ((float)(2*3.14159)) * params->p_cutoff;\n    FLOAT sat = params->p_saturation, satinv = 1./sat;\n    FLOAT satstate0 = clip(state[0], sat, satinv);\n    FLOAT satstate1 = clip(state[1], sat, satinv);\n    FLOAT satstate2 = clip(state[2], sat, satinv);\n    dstate[0] = k *\n        (clip(params->p_input - params->p_resonance * state[3], sat, satinv)\n            - satstate0);\n    dstate[1] = k * (satstate0 - satstate1);\n    dstate[2] = k * (satstate1 - satstate2);\n    dstate[3] = k * (satstate2 - clip(state[3], sat, satinv));\n}\n\nstatic void solver_euler(FLOAT *state, FLOAT *errorestimate, \n    FLOAT stepsize, t_params *params)\n{\n    FLOAT cumerror = 0;\n    int i;\n    FLOAT derivatives[DIM];\n    calc_derivatives(derivatives, state, params);\n    *errorestimate = 0;\n    for (i = 0; i < DIM; i++)\n    {\n        state[i] += stepsize * derivatives[i];\n        *errorestimate += (derivatives[i] > params->p_derivativeswere[i] ?\n            derivatives[i] - params->p_derivativeswere[i] :\n            params->p_derivativeswere[i] - derivatives[i]);\n    }\n    for (i = 0; i < DIM; i++)\n        params->p_derivativeswere[i] = derivatives[i];\n}\n\nstatic void solver_rungekutte(FLOAT *state, FLOAT *errorestimate, \n    FLOAT stepsize, t_params *params)\n{\n    FLOAT cumerror = 0;\n    int i;\n    FLOAT deriv1[DIM], deriv2[DIM], deriv3[DIM], deriv4[DIM], tempstate[DIM];\n    FLOAT oldstate[DIM], backstate[DIM];\n#if CALCERROR\n    for (i = 0; i < DIM; i++)\n        oldstate[i] = state[i];\n#endif\n    *errorestimate = 0;\n    calc_derivatives(deriv1, state, params);\n    for (i = 0; i < DIM; i++)\n        tempstate[i] = state[i] + 0.5 * stepsize * deriv1[i];\n    calc_derivatives(deriv2, tempstate, params);\n    for (i = 0; i < DIM; i++)\n        tempstate[i] = state[i] + 0.5 * stepsize * deriv2[i];\n    calc_derivatives(deriv3, tempstate, params);\n    for (i = 0; i < DIM; i++)\n        tempstate[i] = state[i] + stepsize * deriv3[i];\n    calc_derivatives(deriv4, tempstate, params);\n    for (i = 0; i < DIM; i++)\n        state[i] += (1./6.) * stepsize * \n            (deriv1[i] + 2 * deriv2[i] + 2 * deriv3[i] + deriv4[i]);\n#if CALCERROR\n    calc_derivatives(deriv1, state, params);\n    for (i = 0; i < DIM; i++)\n        tempstate[i] = state[i] - 0.5 * stepsize * deriv1[i];\n    calc_derivatives(deriv2, tempstate, params);\n    for (i = 0; i < DIM; i++)\n        tempstate[i] = state[i] - 0.5 * stepsize * deriv2[i];\n    calc_derivatives(deriv3, tempstate, params);\n    for (i = 0; i < DIM; i++)\n        tempstate[i] = state[i] - stepsize * deriv3[i];\n    calc_derivatives(deriv4, tempstate, params);\n    for (i = 0; i < DIM; i++)\n    {\n        backstate[i] = state[i ]- (1./6.) * stepsize * \n            (deriv1[i] + 2 * deriv2[i] + 2 * deriv3[i] + deriv4[i]);\n        *errorestimate += (backstate[i] > oldstate[i] ?\n            backstate[i] - oldstate[i] : oldstate[i] - backstate[i]);\n    }\n#endif\n}\n\ntypedef struct _bob\n{\n    t_object x_obj;\n    t_float x_f;\n    t_outlet *x_out1;    /* signal output */\n#ifdef CALCERROR\n    t_outlet *x_out2;    /* error estimate */\n    FLOAT x_cumerror;\n#endif\n    t_params x_params;\n    FLOAT x_state[DIM];\n    FLOAT x_sr;\n    int x_oversample;\n    int x_errorcount;\n} t_bob;\n\nstatic t_class *bob_class;\n\nstatic void bob_saturation(t_bob *x, t_float saturation)\n{\n    if (saturation <= 1e-3)\n        saturation = 1e-3;\n    x->x_params.p_saturation = saturation;\n}\n\nstatic void bob_oversample(t_bob *x, t_float oversample)\n{\n    if (oversample <= 1)\n        oversample = 1;\n    x->x_oversample = oversample;\n}\n\nstatic void bob_clear(t_bob *x)\n{\n    int i;\n    for (i = 0; i < DIM; i++)\n        x->x_state[i] = x->x_params.p_derivativeswere[i] = 0;\n}\n\nstatic void bob_error(t_bob *x)\n{\n#ifdef CALCERROR\n    outlet_float(x->x_out2,\n        (x->x_errorcount ? x->x_cumerror/x->x_errorcount : 0));\n    x->x_cumerror = 0;\n    x->x_errorcount = 0;\n#else\n    post(\"error estimate unavailable (not compiled in)\");\n#endif\n}\n\nstatic void bob_print(t_bob *x)\n{\n    int i;\n    for (i = 0; i < DIM; i++)\n        post(\"state %d: %f\", i, x->x_state[i]);\n    post(\"saturation %f\", x->x_params.p_saturation);\n    post(\"oversample %d\", x->x_oversample);\n}\n\nstatic void *bob_new( void)\n{\n    t_bob *x = (t_bob *)pd_new(bob_class);\n    x->x_out1 = outlet_new(&x->x_obj, gensym(\"signal\"));\n    inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);\n    inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);\n    x->x_f = 0;\n    bob_clear(x);\n    bob_saturation(x, 3); \n    bob_oversample(x, 2);\n#ifdef CALCERROR\n    x->x_cumerror = 0;\n    x->x_errorcount = 0;\n    x->x_out2 = outlet_new(&x->x_obj, gensym(\"float\"));\n#endif\n    return (x);\n}\n\nstatic t_int *bob_perform(t_int *w)\n{\n    t_bob *x = (t_bob *)(w[1]);\n    t_float *in1 = (t_float *)(w[2]);\n    t_float *cutoffin = (t_float *)(w[3]);\n    t_float *resonancein = (t_float *)(w[4]);\n    t_float *out = (t_float *)(w[5]);\n        /* bug fix: output is last state variable, not first */\n    FLOAT *outstate =\n        (pd_compatibilitylevel > 51? &x->x_state[3] : &x->x_state[0]);\n    int n = (int)(w[6]), i, j;\n    FLOAT stepsize = 1./(x->x_oversample * x->x_sr);\n    FLOAT errorestimate;\n    for (i = 0; i < n; i++)\n    {\n        x->x_params.p_input = *in1++;\n        x->x_params.p_cutoff = *cutoffin++;\n        if ((x->x_params.p_resonance = *resonancein++) < 0)\n            x->x_params.p_resonance = 0;\n        for (j = 0; j < x->x_oversample; j++)\n            solver_rungekutte(x->x_state, &errorestimate,\n                stepsize, &x->x_params);\n        *out++ = *outstate;\n#if CALCERROR\n        x->x_cumerror += errorestimate;\n        x->x_errorcount++;\n#endif\n    }\n    return (w+7);\n}\n\nstatic void bob_dsp(t_bob *x, t_signal **sp)\n{\n    x->x_sr = sp[0]->s_sr;\n    dsp_add(bob_perform, 6, x, sp[0]->s_vec, sp[1]->s_vec,\n        sp[2]->s_vec, sp[3]->s_vec, (t_int)sp[0]->s_n);\n}\n\nvoid bob_tilde_setup(void)\n{\n    int i;\n    bob_class = class_new(gensym(\"bob~\"),\n        (t_newmethod)bob_new, 0, sizeof(t_bob), 0, 0);\n    class_addmethod(bob_class, (t_method)bob_saturation, gensym(\"saturation\"),\n        A_FLOAT, 0);\n    class_addmethod(bob_class, (t_method)bob_oversample, gensym(\"oversample\"),\n        A_FLOAT, 0);\n    class_addmethod(bob_class, (t_method)bob_clear, gensym(\"clear\"), 0);\n    class_addmethod(bob_class, (t_method)bob_print, gensym(\"print\"), 0);\n    class_addmethod(bob_class, (t_method)bob_error, gensym(\"error\"), 0);\n\n    class_addmethod(bob_class, (t_method)bob_dsp, gensym(\"dsp\"), A_CANT, 0);\n    CLASS_MAINSIGNALIN(bob_class, t_bob, x_f);\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/extra/bonk~/bonk~.c",
    "content": "/*\n ###########################################################################\n # bonk~ - a Max/MSP external\n # by miller puckette and ted apel\n # http://crca.ucsd.edu/~msp/\n # Max/MSP port by barry threw\n # http://www.barrythrew.com\n # me@barrythrew.com\n # San Francisco, CA\n # (c) 2008\n # for Kesumo - http://www.kesumo.com\n ###########################################################################\n // bonk~ detects attacks in an audio signal\n ###########################################################################\n This software is copyrighted by Miller Puckette and others.  The following\n terms (the \"Standard Improved BSD License\") apply to all files associated with\n the software unless explicitly disclaimed in individual files:\n \n Redistribution and use in source and binary forms, with or without\n modification, are permitted provided that the following conditions are\n met:\n \n 1. Redistributions of source code must retain the above copyright\n notice, this list of conditions and the following disclaimer.\n 2. Redistributions in binary form must reproduce the above  \n copyright notice, this list of conditions and the following \n disclaimer in the documentation and/or other materials provided\n with the distribution.\n 3. The name of the author may not be used to endorse or promote\n products derived from this software without specific prior \n written permission.\n \n THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY\n EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,\n THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A\n PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR\n BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,   \n DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING\n IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF\n THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\ndolist:\ndecay and other times in msec \n*/\n\n#include <math.h>\n#include <stdio.h>\n#include <string.h>\n\n/* These pragmas are only used for MSVC, not MinGW or Cygwin <hans@at.or.at> */\n#ifdef _MSC_VER\n#pragma warning (disable: 4305 4244)\n#endif\n \n#ifdef MSP\n#include \"ext.h\"\n#include \"z_dsp.h\"\n#include \"math.h\"\n#include \"ext_support.h\"\n#include \"ext_proto.h\"\n#include \"ext_obex.h\"\n\ntypedef double t_floatarg;      /* from m_pd.h */\ntypedef float t_float;               /* from m_pd.h */\n#define flog log\n#define fexp exp\n#define fsqrt sqrt\n#define t_resizebytes(a, b, c) t_resizebytes((char *)(a), (b), (c))\n\nvoid *bonk_class;\n#define getbytes t_getbytes\n#define freebytes t_freebytes\n#endif /* MSP */\n\n#ifdef PD\n#include \"m_pd.h\"\nstatic t_class *bonk_class;\n#endif\n\n#ifdef _WIN32\n# include <malloc.h> /* MSVC or mingw on windows */\n#elif defined(__linux__) || defined(__APPLE__)\n# include <alloca.h> /* linux, mac, mingw, cygwin */\n#else\n# include <stdlib.h> /* BSDs for example */\n#endif\n\n/* ------------------------ bonk~ ----------------------------- */\n\n#define DEFNPOINTS 256\n#define MAXCHANNELS 8\n#define MINPOINTS 64\n#define DEFPERIOD 128\n#define DEFNFILTERS 11\n#define DEFHALFTONES 6\n#define DEFOVERLAP 1\n#define DEFFIRSTBIN 1\n#define DEFMINBANDWIDTH 1.5\n#define DEFHITHRESH 5\n#define DEFLOTHRESH 2.5\n#define DEFMASKTIME 4\n#define DEFMASKDECAY 0.7\n#define DEFDEBOUNCEDECAY 0\n#define DEFMINVEL 7\n#define DEFATTACKBINS 1\n#define MAXATTACKWAIT 4\n\ntypedef struct _filterkernel\n{\n    int k_filterpoints;\n    int k_hoppoints;\n    int k_skippoints;\n    int k_nhops;\n    t_float k_centerfreq;        /* center frequency, bins */\n    t_float k_bandwidth;         /* bandwidth, bins */\n    t_float *k_stuff;\n} t_filterkernel;\n\ntypedef struct _filterbank\n{\n    int b_nfilters;             /* number of filters in bank */\n    int b_npoints;              /* input vector size */\n    t_float b_halftones;        /* filter bandwidth in halftones */\n    t_float b_overlap;          /* overlap; default 1 for 1/2-power pts */\n    t_float b_firstbin;         /* freq of first filter in bins, default 1 */\n    t_float b_minbandwidth;     /* minimum bandwidth, default 1.5 */\n    t_filterkernel *b_vec;      /* filter kernels */\n    int b_refcount;             /* number of bonk~ objects using this */\n    struct _filterbank *b_next; /* next in linked list */\n} t_filterbank;\n\n#if 0   /* this is the design for 1.0: */\nstatic t_filterkernel bonk_filterkernels[] =\n    {{256, 2, .01562}, {256, 4, .01562}, {256, 6, .01562}, {180, 6, .02222},\n    {128, 6, .01803}, {90, 6, .02222}, {64, 6, .02362}, {46, 6, .02773},\n    {32, 6, .03227}, {22, 6, .03932}, {16, 6, .04489}};\n#endif\n\n#if 0\n    /* here's the 1.1 rev: */\nstatic t_filterkernel bonk_filterkernels[] =\n    {{256, 1, .01562, 0}, {256, 3, .01562, 0}, {256, 5, .01562, 0},\n    {212, 6, .01886, 0}, {150, 6, .01885, 0}, {106, 6, .02179, 0},\n    {76, 6, .0236, 0}, {54, 6, .02634, 0}, {38, 6, .03047, 0},\n    {26, 6, .03667, 0}, {18, 6, .04458, 0}};\n\n#define NFILTERS \\\n    ((int)(sizeof(bonk_filterkernels) / sizeof(bonk_filterkernels[0])))\n\n#endif\n\n#if 0\n    /* and 1.2 */\n#define NFILTERS 11\nstatic t_filterkernel bonk_filterkernels[NFILTERS];\n#endif\n\n   /* and 1.3 */\n#define MAXNFILTERS 200\n#define MASKHIST 8\n\nstatic t_filterbank *bonk_filterbanklist;\n\ntypedef struct _hist\n{\n    t_float h_power;\n    t_float h_before;\n    t_float h_outpower;\n    int h_countup;\n    t_float h_mask[MASKHIST];\n} t_hist;\n\ntypedef struct template\n{\n    t_float t_amp[MAXNFILTERS];\n} t_template;\n\ntypedef struct _insig\n{\n    t_hist g_hist[MAXNFILTERS];    /* history for each filter */\n#ifdef PD\n    t_outlet *g_outlet;         /* outlet for raw data */\n#endif\n#ifdef MSP\n    void *g_outlet;             /* outlet for raw data */\n#endif\n    t_float *g_inbuf;           /* buffered input samples */\n    t_sample *g_invec;           /* new input samples */\n} t_insig;\n\ntypedef struct _bonk\n{\n#ifdef PD\n    t_object x_obj;\n    t_outlet *x_cookedout;\n    t_clock *x_clock;\n    t_canvas *x_canvas;     /* ptr to current canvas --fbar */\n#endif /* PD */\n#ifdef MSP\n    t_pxobject x_obj;\n    void *obex;\n    void *x_cookedout;\n    void *x_clock;\n#endif /* MSP */\n    /* parameters */\n    int x_npoints;          /* number of points in input buffer */\n    int x_period;           /* number of input samples between analyses */\n    int x_nfilters;         /* number of filters requested */\n    t_float x_halftones;    /* nominal halftones between filters */\n    t_float x_overlap;\n    t_float x_firstbin;\n    t_float x_minbandwidth;\n    t_float x_hithresh;     /* threshold for total growth to trigger */\n    t_float x_lothresh;     /* threshold for total growth to re-arm */\n    t_float x_minvel;       /* minimum velocity we output */\n    t_float x_maskdecay;\n    int x_masktime;\n    int x_useloudness;      /* use loudness spectra instead of power */\n    t_float x_debouncedecay;\n    t_float x_debouncevel;\n    double x_learndebounce; /* debounce time (in \"learn\" mode only) */\n    int x_attackbins;       /* number of bins to wait for attack */\n\n    t_filterbank *x_filterbank;\n    t_hist x_hist[MAXNFILTERS];\n    t_template *x_template;\n    t_insig *x_insig;                   \n    int x_ninsig;\n    int x_ntemplate;\n    int x_infill;\n    int x_countdown;\n    int x_willattack;\n    int x_attacked;\n    int x_debug;\n    int x_learn;\n    int x_learncount;           /* countup for \"learn\" mode */\n    int x_spew;                 /* if true, always generate output! */\n    int x_maskphase;            /* phase, 0 to MASKHIST-1, for mask history */\n    t_float x_sr;               /* current sample rate in Hz. */\n    int x_hit;                  /* next \"tick\" called because of a hit, not a poll */\n} t_bonk;\n\n#ifdef MSP\nstatic void *bonk_new(t_symbol *s, long ac, t_atom *av);\nstatic void bonk_tick(t_bonk *x);\nstatic void bonk_doit(t_bonk *x);\nstatic void bonk_perform_generic(t_bonk *x, int n);\nstatic t_int *bonk_perform(t_int *w);\nstatic void bonk_perform64(t_bonk *x, t_object *dsp64, double **ins,\n                           long numins, double **outs, long numouts,\n                           long sampleframes, long flags, void *userparam);\nstatic void bonk_dsp(t_bonk *x, t_signal **sp);\nstatic void bonk_dsp64(t_bonk *x, t_object *dsp64, short *count,\n                       double samplerate, long maxvectorsize, long flags);\nvoid bonk_assist(t_bonk *x, void *b, long m, long a, char *s);\nstatic void bonk_free(t_bonk *x);\nvoid bonk_setup(void);\nint main();\n\nstatic void bonk_thresh(t_bonk *x, t_floatarg f1, t_floatarg f2);\nstatic void bonk_print(t_bonk *x, t_floatarg f);\nstatic void bonk_bang(t_bonk *x);\n\nstatic void bonk_write(t_bonk *x, t_symbol *s);\nstatic void bonk_dowrite(t_bonk *x, t_symbol *s);\nstatic void bonk_writefile(t_bonk *x, char *filename, short path);\n\nstatic void bonk_read(t_bonk *x, t_symbol *s);\nstatic void bonk_doread(t_bonk *x, t_symbol *s);\nstatic void bonk_openfile(t_bonk *x, char *filename, short path);\n\nvoid bonk_minvel_set(t_bonk *x, void *attr, long ac, t_atom *av);\nvoid bonk_lothresh_set(t_bonk *x, void *attr, long ac, t_atom *av);\nvoid bonk_hithresh_set(t_bonk *x, void *attr, long ac, t_atom *av);\nvoid bonk_masktime_set(t_bonk *x, void *attr, long ac, t_atom *av);\nvoid bonk_maskdecay_set(t_bonk *x, void *attr, long ac, t_atom *av);\nvoid bonk_debouncedecay_set(t_bonk *x, void *attr, long ac, t_atom *av);\nvoid bonk_debug_set(t_bonk *x, void *attr, long ac, t_atom *av);\nvoid bonk_spew_set(t_bonk *x, void *attr, long ac, t_atom *av);\nvoid bonk_useloudness_set(t_bonk *x, void *attr, long ac, t_atom *av);\nvoid bonk_attackbins_set(t_bonk *x, void *attr, long ac, t_atom *av);\nvoid bonk_learn_set(t_bonk *x, void *attr, long ac, t_atom *av);\n\nt_float qrsqrt(t_float f);\ndouble clock_getsystime();\ndouble clock_gettimesince(double prevsystime);\nchar *strcpy(char *s1, const char *s2);\n#define SETFLOAT A_SETFLOAT\n#endif\n\nstatic void bonk_tick(t_bonk *x);\n\n#define HALFWIDTH 0.75  /* half peak bandwidth at half power point in bins */\n#define SLIDE 0.25    /* relative slide between filter subwindows */\n\nstatic t_filterbank *bonk_newfilterbank(int npoints, int nfilters,\n    t_float halftones, t_float overlap, t_float firstbin, t_float minbandwidth)\n{\n    int i, j;\n    t_float cf, bw, h, relspace;\n    t_filterbank *b = (t_filterbank *)getbytes(sizeof(*b));\n    b->b_npoints = npoints;\n    b->b_nfilters = nfilters;\n    b->b_halftones = halftones;\n    b->b_overlap = overlap;\n    b->b_firstbin = firstbin;\n    b->b_minbandwidth = minbandwidth;\n    b->b_refcount = 0;\n    b->b_next = bonk_filterbanklist;\n    bonk_filterbanklist = b;\n    b->b_vec = (t_filterkernel *)getbytes(nfilters * sizeof(*b->b_vec));\n    \n    h = exp((log(2.)/12.)*halftones);  /* specced interval between filters */\n    relspace = (h - 1)/(h + 1);        /* nominal spacing-per-f for fbank */\n    \n    if (minbandwidth < 2*HALFWIDTH)\n        minbandwidth = 2*HALFWIDTH;\n    if (firstbin < minbandwidth/(2*HALFWIDTH))\n        firstbin = minbandwidth/(2*HALFWIDTH);\n    cf = firstbin;\n    bw = cf * relspace * overlap;\n    if (bw < (0.5*minbandwidth))\n        bw = (0.5*minbandwidth);\n    for (i = 0; i < nfilters; i++)\n    {\n        t_float *fp, newcf, newbw;\n        t_float normalizer = 0;\n        int filterpoints, skippoints, hoppoints, nhops;\n        \n        filterpoints = npoints * HALFWIDTH/bw;\n        if (cf > npoints/2)\n        {\n            post(\"bonk~: only using %d filters (ran past Nyquist)\", i+1);\n            break;\n        }\n        if (filterpoints < 4)\n        {\n            post(\"bonk~: only using %d filters (kernels got too short)\", i+1);\n            break;\n        }\n        else if (filterpoints > npoints)\n            filterpoints = npoints;\n        \n        hoppoints = SLIDE * npoints * HALFWIDTH/bw;\n        \n        nhops = 1. + (npoints-filterpoints)/(t_float)hoppoints;\n        skippoints = 0.5 * (npoints-filterpoints - (nhops-1) * hoppoints);\n        \n        b->b_vec[i].k_stuff =\n            (t_float *)getbytes(2 * sizeof(t_float) * filterpoints);\n        b->b_vec[i].k_filterpoints = filterpoints;\n        b->b_vec[i].k_nhops = nhops;\n        b->b_vec[i].k_hoppoints = hoppoints;\n        b->b_vec[i].k_skippoints = skippoints;\n        b->b_vec[i].k_centerfreq = cf;\n        b->b_vec[i].k_bandwidth = bw;\n        \n        for (fp = b->b_vec[i].k_stuff, j = 0; j < filterpoints; j++, fp+= 2)\n        {\n            t_float phase = j * cf * (2*3.141592653589793 / npoints);\n            t_float wphase = j * (2*3.141592653589793 / filterpoints);\n            t_float window = sin(0.5*wphase);\n            fp[0] = window * cos(phase);\n            fp[1] = window * sin(phase);\n            normalizer += window;\n        }\n        normalizer = 1/(normalizer * sqrt(nhops));\n        for (fp = b->b_vec[i].k_stuff, j = 0;\n             j < filterpoints; j++, fp+= 2)\n            fp[0] *= normalizer, fp[1] *= normalizer;\n#if 0\n        post(\"i %d  cf %.2f  bw %.2f  nhops %d, hop %d, skip %d, npoints %d\",\n             i, cf, bw, nhops, hoppoints, skippoints, filterpoints);\n#endif\n        newcf = (cf + bw/overlap)/(1 - relspace);\n        newbw = newcf * overlap * relspace;\n        if (newbw < 0.5*minbandwidth)\n        {\n            newbw = 0.5*minbandwidth;\n            newcf = cf + minbandwidth / overlap;\n        }\n        cf = newcf;\n        bw = newbw;\n    }\n    for (; i < nfilters; i++)\n        b->b_vec[i].k_stuff = 0, b->b_vec[i].k_filterpoints = 0;\n    return (b);\n}\n\nstatic void bonk_freefilterbank(t_filterbank *b)\n{\n    t_filterbank *b2, *b3;\n    int i;\n    if (bonk_filterbanklist == b)\n        bonk_filterbanklist = b->b_next;\n    else for (b2 = bonk_filterbanklist; (b3 = b2->b_next); b2 = b3)\n        if (b3 == b)\n    {\n        b2->b_next = b3->b_next;\n        break;\n    }\n    for (i = 0; i < b->b_nfilters; i++)\n        if (b->b_vec[i].k_stuff)\n            freebytes(b->b_vec[i].k_stuff,\n                b->b_vec[i].k_filterpoints * sizeof(t_float));\n    freebytes(b->b_vec, b->b_nfilters * sizeof(*b->b_vec));\n    freebytes(b, sizeof(*b));\n}\n\nstatic void bonk_donew(t_bonk *x, int npoints, int period, int nsig, \n    int nfilters, t_float halftones, t_float overlap, t_float firstbin,\n    t_float minbandwidth, t_float samplerate)\n{\n    int i, j;\n    t_hist *h;\n    t_float *fp;\n    t_insig *g;\n    t_filterbank *fb;\n    for (j = 0, g = x->x_insig; j < nsig; j++, g++)\n    {\n        for (i = 0, h = g->g_hist; i--; h++)\n        {\n            h->h_power = h->h_before = 0, h->h_countup = 0;\n            for (j = 0; j < MASKHIST; j++)\n                h->h_mask[j] = 0;\n        }\n            /* we ought to check for failure to allocate memory here */\n        g->g_inbuf = (t_float *)getbytes(npoints * sizeof(t_float));\n        for (i = npoints, fp = g->g_inbuf; i--; fp++) *fp = 0;\n    }\n    if (!period) period = npoints/2;\n    x->x_npoints = npoints;\n    x->x_period = period;\n    x->x_ninsig = nsig;\n    x->x_nfilters = (nfilters > MAXNFILTERS ? MAXNFILTERS : nfilters);\n    x->x_halftones = halftones;\n    x->x_template = (t_template *)getbytes(0);\n    x->x_ntemplate = 0;\n    x->x_infill = 0;\n    x->x_countdown = 0;\n    x->x_willattack = 0;\n    x->x_attacked = 0;\n    x->x_maskphase = 0;\n    x->x_debug = 0;\n    x->x_hithresh = DEFHITHRESH;\n    x->x_lothresh = DEFLOTHRESH;\n    x->x_masktime = DEFMASKTIME;\n    x->x_maskdecay = DEFMASKDECAY;\n    x->x_learn = 0;\n    x->x_learndebounce = clock_getsystime();\n    x->x_learncount = 0;\n    x->x_debouncedecay = DEFDEBOUNCEDECAY;\n    x->x_minvel = DEFMINVEL;\n    x->x_useloudness = 0;\n    x->x_debouncevel = 0;\n    x->x_attackbins = DEFATTACKBINS;\n    x->x_sr = samplerate;\n    x->x_filterbank = 0;\n    x->x_hit = 0;\n    for (fb = bonk_filterbanklist; fb; fb = fb->b_next)\n        if (fb->b_nfilters == x->x_nfilters &&\n            fb->b_halftones == x->x_halftones &&\n            fb->b_firstbin == firstbin &&\n            fb->b_overlap == overlap &&\n            fb->b_npoints == x->x_npoints &&\n            fb->b_minbandwidth == minbandwidth)\n    {\n        fb->b_refcount++;\n        x->x_filterbank = fb;\n        break;\n    }\n    if (!x->x_filterbank)\n        x->x_filterbank = bonk_newfilterbank(npoints, nfilters, \n            halftones, overlap, firstbin, minbandwidth),\n                x->x_filterbank->b_refcount++;\n}\n\nstatic void bonk_tick(t_bonk *x)\n{\n    t_atom at[MAXNFILTERS], *ap, at2[3];\n    int i, j, k, n;\n    t_hist *h;\n    t_float *pp, vel = 0., temperature = 0.;\n    t_float *fp;\n    t_template *tp;\n    int nfit, ninsig = x->x_ninsig, ntemplate = x->x_ntemplate, nfilters = x->x_nfilters;\n    t_insig *gp;\n#ifdef _MSC_VER\n    t_float powerout[MAXNFILTERS*MAXCHANNELS];\n#else\n    t_float *powerout = alloca(x->x_nfilters * x->x_ninsig * sizeof(*powerout));\n#endif\n    \n    for (i = ninsig, pp = powerout, gp = x->x_insig; i--; gp++)\n    {\n        for (j = 0, h = gp->g_hist; j < nfilters; j++, h++, pp++)\n        {\n            t_float power = h->h_outpower;\n            t_float intensity = *pp = (power > 0. ? 100. * qrsqrt(qrsqrt(power)) : 0.);\n            vel += intensity;\n            temperature += intensity * (t_float)j;\n        }\n    }\n    if (vel > 0) temperature /= vel;\n    else temperature = 0;\n    vel *= 0.5 / ninsig;        /* fudge factor */\n    if (x->x_hit)\n    {\n        /* if hit nonzero it's a clock callback.  if in \"learn\" mode update the\n         template list; in any event match the hit to known templates. */\n        \n        if (vel < x->x_debouncevel)\n        {\n            if (x->x_debug)\n                post(\"bounce cancelled: vel %f debounce %f\",\n                     vel, x->x_debouncevel);\n            return;\n        }\n        if (vel < x->x_minvel)\n        {\n            if (x->x_debug)\n                post(\"low velocity cancelled: vel %f, minvel %f\",\n                     vel, x->x_minvel);\n            return;\n        }\n        x->x_debouncevel = vel;\n        if (x->x_learn)\n        {\n            double lasttime = x->x_learndebounce;\n            double msec = clock_gettimesince(lasttime);\n            if ((!ntemplate) || (msec > 200))\n            {\n                int countup = x->x_learncount;\n                /* normalize to 100  */\n                t_float norm;\n                for (i = nfilters * ninsig, norm = 0, pp = powerout; i--; pp++)\n                    norm += *pp * *pp;\n                if (norm < 1.0e-15) norm = 1.0e-15;\n                norm = 100. * qrsqrt(norm);\n                /* check if this is the first strike for a new template */\n                if (!countup)\n                {\n                    int oldn = ntemplate;\n                    x->x_ntemplate = ntemplate = oldn + ninsig;\n                    x->x_template = (t_template *)t_resizebytes(x->x_template,\n                        oldn * sizeof(x->x_template[0]),\n                            ntemplate * sizeof(x->x_template[0]));\n                    for (i = ninsig, pp = powerout; i--; oldn++)\n                        for (j = nfilters, fp = x->x_template[oldn].t_amp; j--;\n                             pp++, fp++)\n                                *fp = *pp * norm;\n                }\n                else\n                {\n                    int oldn = ntemplate - ninsig;\n                    if (oldn < 0) post(\"bonk_tick bug\");\n                    for (i = ninsig, pp = powerout; i--; oldn++)\n                    {\n                        for (j = nfilters, fp = x->x_template[oldn].t_amp; j--;\n                             pp++, fp++)\n                            *fp = (countup * *fp + *pp * norm)\n                            /(countup + 1.0);\n                    }\n                }\n                countup++;\n                if (countup == x->x_learn) countup = 0;\n                x->x_learncount = countup;\n            }\n            else return;\n        }\n        x->x_learndebounce = clock_getsystime();\n        if (ntemplate)\n        {\n            t_float bestfit = -1e30;\n            int templatecount;\n            nfit = -1;\n            for (i = 0, templatecount = 0, tp = x->x_template; \n                 templatecount < ntemplate; i++)\n            {\n                t_float dotprod = 0;\n                for (k = 0, pp = powerout;\n                     k < ninsig && templatecount < ntemplate;\n                     k++, tp++, templatecount++)\n                {\n                    for (j = nfilters, fp = tp->t_amp;\n                         j--; fp++, pp++)\n                    {\n                        if (*fp < 0 || *pp < 0) post(\"bonk_tick bug 2\");\n                        dotprod += *fp * *pp;\n                    }\n                }\n                if (dotprod > bestfit)\n                {\n                    bestfit = dotprod;\n                    nfit = i;\n                }\n            }\n            if (nfit < 0) post(\"bonk_tick bug\");\n        }\n        else nfit = 0;\n    }\n    else nfit = -1;     /* hit is zero; this is the \"bang\" method. */\n    \n    x->x_attacked = 1;\n    if (x->x_debug)\n        post(\"bonk out: number %d, vel %f, temperature %f\",\n            nfit, vel, temperature);\n    \n    SETFLOAT(at2, nfit);\n    SETFLOAT(at2+1, vel);\n    SETFLOAT(at2+2, temperature);\n    outlet_list(x->x_cookedout, 0, 3, at2);\n    \n    for (n = 0, gp = x->x_insig + (ninsig-1),\n        pp = powerout + nfilters * (ninsig-1); n < ninsig;\n            n++, gp--, pp -= nfilters)\n    {\n        t_float *pp2;\n        for (i = 0, ap = at, pp2 = pp; i < nfilters;\n            i++, ap++, pp2++)\n        {\n            ap->a_type = A_FLOAT;\n            ap->a_w.w_float = *pp2;\n        }\n        outlet_list(gp->g_outlet, 0, nfilters, at);\n    }\n}\n\nstatic void bonk_doit(t_bonk *x)\n{\n    int i, j, ch, n;\n    t_filterkernel *k;\n    t_hist *h;\n    t_float growth = 0, *fp1, *fp3, *fp4, hithresh, lothresh;\n    int ninsig = x->x_ninsig, nfilters = x->x_nfilters,\n        maskphase = x->x_maskphase, nextphase, oldmaskphase;\n    t_insig *gp;\n    nextphase = maskphase + 1;\n    if (nextphase >= MASKHIST)\n        nextphase = 0;\n    x->x_maskphase = nextphase;\n    oldmaskphase = nextphase - x->x_attackbins;\n    if (oldmaskphase < 0)\n        oldmaskphase += MASKHIST;\n    if (x->x_useloudness)\n        hithresh = qrsqrt(qrsqrt(x->x_hithresh)),\n        lothresh = qrsqrt(qrsqrt(x->x_lothresh));\n    else hithresh = x->x_hithresh, lothresh = x->x_lothresh;\n    for (ch = 0, gp = x->x_insig; ch < ninsig; ch++, gp++)\n    {\n        for (i = 0, k = x->x_filterbank->b_vec, h = gp->g_hist;\n             i < nfilters; i++, k++, h++)\n        {\n            t_float power = 0, maskpow = h->h_mask[maskphase];\n            t_float *inbuf= gp->g_inbuf + k->k_skippoints;\n            int countup = h->h_countup;\n            int filterpoints = k->k_filterpoints;\n            /* if the user asked for more filters that fit under the\n             Nyquist frequency, some filters won't actually be filled in\n             so we skip running them. */\n            if  (!filterpoints)\n            {\n                h->h_countup = 0;\n                h->h_mask[nextphase] = 0;\n                h->h_power = 0;\n                continue;\n            }\n            /* run the filter repeatedly, sliding it forward by hoppoints,\n             for nhop times */\n            for (fp1 = inbuf, n = 0;\n                 n < k->k_nhops; fp1 += k->k_hoppoints, n++)\n            {\n                t_float rsum = 0, isum = 0;\n                for (fp3 = fp1, fp4 = k->k_stuff, j = filterpoints; j--;)\n                {\n                    t_float g = *fp3++;\n                    rsum += g * *fp4++;\n                    isum += g * *fp4++;\n                }\n                power += rsum * rsum + isum * isum;\n            }\n            if (!x->x_willattack) \n                h->h_before = maskpow;\n            \n            if (power > h->h_mask[oldmaskphase])\n            {\n                if (x->x_useloudness)\n                    growth += qrsqrt(qrsqrt(\n                        power/(h->h_mask[oldmaskphase] + 1.0e-15))) - 1.;\n                else growth += power/(h->h_mask[oldmaskphase] + 1.0e-15) - 1.;\n            }\n            if (!x->x_willattack && countup >= x->x_masktime)\n                maskpow *= x->x_maskdecay;\n            \n            if (power > maskpow)\n            {\n                maskpow = power;\n                countup = 0;\n            }\n            countup++;\n            h->h_countup = countup;\n            h->h_mask[nextphase] = maskpow;\n            h->h_power = power;\n        }\n    }\n    if (x->x_willattack)\n    {\n        if (x->x_willattack > MAXATTACKWAIT || growth < x->x_lothresh)\n        {\n            /* if haven't yet, and if not in spew mode, report a hit */\n            if (!x->x_spew && !x->x_attacked)\n            {\n                for (ch = 0, gp = x->x_insig; ch < ninsig; ch++, gp++)\n                    for (i = nfilters, h = gp->g_hist; i--; h++)\n                        h->h_outpower = h->h_mask[nextphase];\n                x->x_hit = 1;\n                clock_delay(x->x_clock, 0);\n            }\n        }\n        if (growth < x->x_lothresh)\n            x->x_willattack = 0;\n        else x->x_willattack++;\n    }\n    else if (growth > x->x_hithresh)\n    {\n        if (x->x_debug) post(\"attack: growth = %f\", growth);\n        x->x_willattack = 1;\n        x->x_attacked = 0;\n        for (ch = 0, gp = x->x_insig; ch < ninsig; ch++, gp++)\n            for (i = nfilters, h = gp->g_hist; i--; h++)\n                h->h_mask[nextphase] = h->h_power, h->h_countup = 0;\n    }\n    \n    /* if in \"spew\" mode just always output */\n    if (x->x_spew)\n    {\n        for (ch = 0, gp = x->x_insig; ch < ninsig; ch++, gp++)\n            for (i = nfilters, h = gp->g_hist; i--; h++)\n                h->h_outpower = h->h_power;\n        x->x_hit = 0;\n        clock_delay(x->x_clock, 0);\n    }\n    x->x_debouncevel *= x->x_debouncedecay;\n}\n\nstatic void bonk_perform_generic(t_bonk *x, int n) {\n    int onset = 0;\n    if (x->x_countdown >= n)\n        x->x_countdown -= n;\n    else\n    {\n        int i, j, ninsig = x->x_ninsig;\n        t_insig *gp;\n        if (x->x_countdown > 0)\n        {\n            n -= x->x_countdown;\n            onset += x->x_countdown;\n            x->x_countdown = 0;\n        }\n        while (n > 0)\n        {\n            int infill = x->x_infill;\n            int m = (n < (x->x_npoints - infill) ?\n                     n : (x->x_npoints - infill));\n            for (i = 0, gp = x->x_insig; i < ninsig; i++, gp++)\n            {\n                t_float *fp = gp->g_inbuf + infill;\n                t_sample *in1 = gp->g_invec + onset;\n                for (j = 0; j < m; j++)\n                    *fp++ = *in1++;\n            }\n            infill += m;\n            x->x_infill = infill;\n            if (infill == x->x_npoints)\n            {\n                bonk_doit(x);\n                \n                /* shift or clear the input buffer and update counters */\n                if (x->x_period > x->x_npoints)\n                    x->x_countdown = x->x_period - x->x_npoints;\n                else x->x_countdown = 0;\n                if (x->x_period < x->x_npoints)\n                {\n                    int overlap = x->x_npoints - x->x_period;\n                    t_float *fp1, *fp2;\n                    for (n = 0, gp = x->x_insig; n < ninsig; n++, gp++)\n                        for (i = overlap, fp1 = gp->g_inbuf,\n                             fp2 = fp1 + x->x_period; i--;)\n                            *fp1++ = *fp2++;\n                    x->x_infill = overlap;\n                }\n                else x->x_infill = 0;\n            }\n            n -= m;\n            onset += m;\n        }\n    }\n}\n\nstatic t_int *bonk_perform(t_int *w)\n{\n    t_bonk *x = (t_bonk *)(w[1]);\n    int n = (int)(w[2]);\n    bonk_perform_generic(x, n);\n    return (w+3);\n}\n\nstatic void bonk_dsp(t_bonk *x, t_signal **sp)\n{\n    int i, n = sp[0]->s_n, ninsig = x->x_ninsig;\n    t_insig *gp;\n    \n    x->x_sr = sp[0]->s_sr;\n    \n    for (i = 0, gp = x->x_insig; i < ninsig; i++, gp++)\n        gp->g_invec = (*(sp++))->s_vec;\n    \n    dsp_add(bonk_perform, 2, x, (t_int)n);\n}\n\nstatic void bonk_thresh(t_bonk *x, t_floatarg f1, t_floatarg f2)\n{\n    if (f1 > f2)\n        post(\"bonk: warning: low threshold greater than hi threshold\");\n    x->x_lothresh = (f1 <= 0 ? 0.0001 : f1);\n    x->x_hithresh = (f2 <= 0 ? 0.0001 : f2);\n}\n\n#ifdef PD\nstatic void bonk_mask(t_bonk *x, t_floatarg f1, t_floatarg f2)\n{\n    int ticks = f1;\n    if (ticks < 0) ticks = 0;\n    if (f2 < 0) f2 = 0;\n    else if (f2 > 1) f2 = 1;\n    x->x_masktime = ticks;\n    x->x_maskdecay = f2;\n}\n\nstatic void bonk_debounce(t_bonk *x, t_floatarg f1)\n{\n    if (f1 < 0) f1 = 0;\n    else if (f1 > 1) f1 = 1;\n    x->x_debouncedecay = f1;\n}\n\nstatic void bonk_minvel(t_bonk *x, t_floatarg f)\n{\n    if (f < 0) f = 0; \n    x->x_minvel = f;\n}\n\nstatic void bonk_debug(t_bonk *x, t_floatarg f)\n{\n    x->x_debug = (f != 0);\n}\n\nstatic void bonk_spew(t_bonk *x, t_floatarg f)\n{\n    x->x_spew = (f != 0);\n}\n\nstatic void bonk_useloudness(t_bonk *x, t_floatarg f)\n{\n    x->x_useloudness = (f != 0);\n}\n\nstatic void bonk_attackbins(t_bonk *x, t_floatarg f)\n{\n    if (f < 1)\n        f = 1;\n    else if (f > MASKHIST)\n        f = MASKHIST;\n    x->x_attackbins = f;\n}\n\nstatic void bonk_learn(t_bonk *x, t_floatarg f)\n{\n    int n = f;\n    if (n < 0) n = 0;\n    if (n)\n    {\n        x->x_template = (t_template *)t_resizebytes(x->x_template,\n            x->x_ntemplate * sizeof(x->x_template[0]), 0);\n        x->x_ntemplate = 0;\n    }\n    x->x_learn = n;\n    x->x_learncount = 0;\n}\n#endif\n\nstatic void bonk_print(t_bonk *x, t_floatarg f)\n{\n    int i;\n    post(\"thresh %f %f\", x->x_lothresh, x->x_hithresh);\n    post(\"mask %d %f\", x->x_masktime, x->x_maskdecay);\n    post(\"attack-frames %d\", x->x_attackbins);\n    post(\"debounce %f\", x->x_debouncedecay);\n    post(\"minvel %f\", x->x_minvel);\n    post(\"spew %d\", x->x_spew);\n    post(\"useloudness %d\", x->x_useloudness);\n    \n#if 0       /* LATER rewrite without hard-coded 11 filters */\n    if (x->x_ntemplate)\n    {\n        post(\"templates:\");\n        for (i = 0; i < x->x_ntemplate; i++)\n            post(\n\"%2d %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f\",\n                i,\n                x->x_template[i].t_amp[0],\n                x->x_template[i].t_amp[1],\n                x->x_template[i].t_amp[2],\n                x->x_template[i].t_amp[3],\n                x->x_template[i].t_amp[4],\n                x->x_template[i].t_amp[5],\n                x->x_template[i].t_amp[6],\n                x->x_template[i].t_amp[7],\n                x->x_template[i].t_amp[8],\n                x->x_template[i].t_amp[9],\n                x->x_template[i].t_amp[10]);\n    }\n    else post(\"no templates\");\n#endif\n    post(\"number of templates %d\", x->x_ntemplate);\n    if (x->x_learn) post(\"learn mode\");\n    if (f != 0)\n    {\n        int j, ninsig = x->x_ninsig;\n        t_insig *gp;\n        for (j = 0, gp = x->x_insig; j < ninsig; j++, gp++)\n        {\n            t_hist *h;\n            if (ninsig > 1) post(\"input %d:\", j+1);\n            for (i = x->x_nfilters, h = gp->g_hist; i--; h++)\n                post(\"pow %f mask %f before %f count %d\",\n                     h->h_power, h->h_mask[x->x_maskphase],\n                     h->h_before, h->h_countup);\n        }\n        post(\"bin size %.2f Hz ... filters:\",\n             x->x_sr/x->x_npoints);\n        for (j = 0; j < x->x_nfilters; j++)\n            post(\"\\\n    %2d  cf %.2f(%.2f bins) bw %.2f(%.2f) nhops %d hop %d skip %d npoints %d\",\n                 j, \n                 x->x_filterbank->b_vec[j].k_centerfreq*x->x_sr/x->x_npoints,\n                 x->x_filterbank->b_vec[j].k_centerfreq,\n                 x->x_filterbank->b_vec[j].k_bandwidth*x->x_sr/x->x_npoints,\n                 x->x_filterbank->b_vec[j].k_bandwidth,\n                 x->x_filterbank->b_vec[j].k_nhops,\n                 x->x_filterbank->b_vec[j].k_hoppoints,\n                 x->x_filterbank->b_vec[j].k_skippoints,\n                 x->x_filterbank->b_vec[j].k_filterpoints);\n    }\n    if (x->x_debug) post(\"debug mode\");\n}\n\nstatic void bonk_forget(t_bonk *x)\n{\n    int ntemplate = x->x_ntemplate, newn = ntemplate - x->x_ninsig;\n    if (newn < 0) newn = 0;\n    x->x_template = (t_template *)t_resizebytes(x->x_template,\n        x->x_ntemplate * sizeof(x->x_template[0]),\n            newn * sizeof(x->x_template[0]));\n    x->x_ntemplate = newn;\n    x->x_learncount = 0;\n}\n\nstatic void bonk_bang(t_bonk *x)\n{\n    int i, ch;\n    t_insig *gp;\n    x->x_hit = 0;\n    for (ch = 0, gp = x->x_insig; ch < x->x_ninsig; ch++, gp++)\n    {\n        t_hist *h;\n        for (i = 0, h = gp->g_hist; i < x->x_nfilters; i++, h++)\n            h->h_outpower = h->h_power;\n    }\n    bonk_tick(x);\n}\n\n#ifdef PD\nstatic void bonk_read(t_bonk *x, t_symbol *s)\n{\n    float vec[MAXNFILTERS];\n    int i, ntemplate = 0, remaining;\n    float *fp;\n    t_float *fp2;\n\n    /* fbar: canvas_open code taken from g_array.c */\n    FILE *fd;\n    char buf[MAXPDSTRING], *bufptr;\n    int filedesc;\n\n    if ((filedesc = canvas_open(x->x_canvas,\n            s->s_name, \"\", buf, &bufptr, MAXPDSTRING, 0)) < 0 \n                || !(fd = fdopen(filedesc, \"r\")))\n    {\n        post(\"%s: open failed\", s->s_name);\n        return;\n    }\n    x->x_template = (t_template *)t_resizebytes(x->x_template, \n        x->x_ntemplate * sizeof(t_template), 0);\n    while (1)\n    {\n        for (i = x->x_nfilters, fp = vec; i--; fp++)\n            if (fscanf(fd, \"%f\", fp) < 1) goto nomore;\n        x->x_template = (t_template *)t_resizebytes(x->x_template,\n            ntemplate * sizeof(t_template),\n                (ntemplate + 1) * sizeof(t_template));\n        for (i = x->x_nfilters, fp = vec,\n             fp2 = x->x_template[ntemplate].t_amp; i--;)\n            *fp2++ = *fp++;\n        ntemplate++;\n    }\nnomore:\n    if ((remaining = (ntemplate % x->x_ninsig)))\n    {\n        post(\"bonk_read: %d templates not a multiple of %d; dropping extras\");\n        x->x_template = (t_template *)t_resizebytes(x->x_template,\n            ntemplate * sizeof(t_template),\n                (ntemplate - remaining) * sizeof(t_template));\n        ntemplate = ntemplate - remaining;\n    }\n    post(\"bonk: read %d templates\\n\", ntemplate);\n    x->x_ntemplate = ntemplate;\n    fclose(fd);\n}\n#endif\n\n#ifdef MSP\nstatic void bonk_perform64(t_bonk *x, t_object *dsp64, double **ins,\n                           long numins, double **outs, long numouts,\n                           long sampleframes, long flags, void *userparam)\n{\n    int n = sampleframes;\n\n    int i = sampleframes, ninsig = x->x_ninsig;\n    t_insig *gp;\n\n    for (i = 0, gp = x->x_insig; i < ninsig; i++, gp++)\n        gp->g_invec = ins[0];\n\n    bonk_perform_generic(x, n);\n}\n\nstatic void bonk_dsp64(t_bonk *x, t_object *dsp64, short *count,\n                       double samplerate, long maxvectorsize, long flags)\n{\n\n    x->x_sr = samplerate;\n    object_method(dsp64, gensym(\"dsp_add64\"), x, bonk_perform64, 0, NULL);\n}\n\n\nstatic void bonk_read(t_bonk *x, t_symbol *s)\n{\n    defer(x, (method)bonk_doread, s, 0, NULL);\n}\n\nstatic void bonk_doread(t_bonk *x, t_symbol *s)\n{\n    t_fourcc filetype = 'TEXT', outtype;\n    char filename[512];\n    short path;\n    \n    if (s == gensym(\"\")) {\n        if (open_dialog(filename, &path, &outtype, &filetype, 1))\n            return;\n    } else {\n        strcpy(filename, s->s_name);\n        if (locatefile_extended(filename, &path, &outtype, &filetype, 1)) {\n            object_error((t_object *) x, \"%s: not found\", s->s_name);\n            return;\n        }\n    }\n    // we have a file\n    bonk_openfile(x, filename, path);\n}\n\nstatic void bonk_openfile(t_bonk *x, char *filename, short path) {\n    t_float vec[MAXNFILTERS];\n    int i, ntemplate = 0, remaining;\n    t_float *fp, *fp2;\n    \n    t_filehandle fh;\n    char **texthandle;\n    char *tokptr;\n    \n    t_ptr_size size;\n\n    if (path_opensysfile(filename, path, &fh, READ_PERM)) {\n        object_error((t_object *) x, \"error opening %s\", filename);\n        return;\n    }\n    \n    sysfile_geteof(fh, &size); \n    texthandle = sysmem_newhandleclear(size + 1);\n    sysfile_readtextfile(fh, texthandle, 0, TEXT_LB_NATIVE);\n    sysfile_close(fh);\n    \n    x->x_template = (t_template *)t_resizebytes(x->x_template, \n                                                x->x_ntemplate * sizeof(t_template), 0);\n    \n    tokptr = strtok(*texthandle, \" \\n\");\n    \n    while(tokptr != NULL)\n    {\n        for (i = x->x_nfilters, fp = vec; i--; fp++) {\n            if (sscanf(tokptr, \"%f\", fp) < 1) \n                goto nomore;\n            tokptr = strtok(NULL, \" \\n\");\n        }\n        x->x_template = (t_template *)t_resizebytes(x->x_template,\n                                                    ntemplate * sizeof(t_template),\n                                                    (ntemplate + 1) * sizeof(t_template));\n        for (i = x->x_nfilters, fp = vec,\n             fp2 = x->x_template[ntemplate].t_amp; i--;)\n            *fp2++ = *fp++;\n        ntemplate++;\n    }\nnomore:\n    if ((remaining = (ntemplate % x->x_ninsig)))\n    {\n        post(\"bonk_read: %d templates not a multiple of %d; dropping extras\");\n        x->x_template = (t_template *)t_resizebytes(x->x_template,\n                                                    ntemplate * sizeof(t_template),\n                                                    (ntemplate - remaining) * sizeof(t_template));\n        ntemplate = ntemplate - remaining;\n    }\n    \n    sysmem_freehandle(texthandle);\n    post(\"bonk: read %d templates\\n\", ntemplate);\n    x->x_ntemplate = ntemplate;\n}\n#endif\n\n#ifdef PD\nstatic void bonk_write(t_bonk *x, t_symbol *s)\n{\n    FILE *fd;\n    char buf[MAXPDSTRING]; /* fbar */\n    int i, ntemplate = x->x_ntemplate;\n    t_template *tp = x->x_template;\n    t_float *fp;\n    \n    /* fbar: canvas-code as in g_array.c */\n    canvas_makefilename(x->x_canvas, s->s_name,\n        buf, MAXPDSTRING);\n    sys_bashfilename(buf, buf);\n\n    if (!(fd = fopen(buf, \"w\")))\n    {\n        post(\"%s: couldn't create\", s->s_name);\n        return;\n    }\n    for (; ntemplate--; tp++)\n    {\n        for (i = x->x_nfilters, fp = tp->t_amp; i--; fp++)\n            fprintf(fd, \"%6.2f \", *fp);\n        fprintf(fd, \"\\n\");\n    }\n    post(\"bonk: wrote %d templates\\n\", x->x_ntemplate);\n    fclose(fd);\n}\n#endif\n\n#ifdef MSP\nstatic void bonk_write(t_bonk *x, t_symbol *s)\n{\n    defer(x, (method)bonk_dowrite, s, 0, NULL);\n}\n\nstatic void bonk_dowrite(t_bonk *x, t_symbol *s)\n{\n    t_fourcc filetype = 'TEXT', outtype;\n    char filename[MAX_FILENAME_CHARS];\n    short path;\n    \n    if (s == gensym(\"\")) {\n        sprintf(filename, \"bonk_template.txt\");\n        saveas_promptset(\"Save template as...\");   \n        if (saveasdialog_extended(filename, &path, &outtype, &filetype, 0))\n            return;\n    } else {\n        strcpy(filename, s->s_name);\n        path = path_getdefault();\n    }\n    bonk_writefile(x, filename, path);\n}\n\nvoid bonk_writefile(t_bonk *x, char *filename, short path)\n{\n    int i, ntemplate = x->x_ntemplate;\n    t_template *tp = x->x_template;\n    t_float *fp;\n    long err;\n    t_ptr_size buflen;\n    \n    t_filehandle fh;\n    \n    char buf[20];\n    \n    err = path_createsysfile(filename, path, 'TEXT', &fh); \n    \n    if (err)\n        return;\n    \n    for (; ntemplate--; tp++)\n    {\n        for (i = x->x_nfilters, fp = tp->t_amp; i--; fp++) {\n            snprintf(buf, 20, \"%6.2f \", *fp);\n            buflen = strlen(buf);\n            sysfile_write(fh, &buflen, buf);\n        }\n        buflen = 1;\n        sysfile_write(fh, &buflen, \"\\n\");\n    }\n        \n    sysfile_close(fh);\n}\n#endif\n\nstatic void bonk_free(t_bonk *x)\n{\n    \n    int i, ninsig = x->x_ninsig;\n    t_insig *gp = x->x_insig;\n#ifdef MSP\n    dsp_free((t_pxobject *)x);\n#endif\n    for (i = 0, gp = x->x_insig; i < ninsig; i++, gp++)\n        freebytes(gp->g_inbuf, x->x_npoints * sizeof(t_float));\n    freebytes(x->x_insig, ninsig * sizeof(*x->x_insig));\n    clock_free(x->x_clock);\n    if (!--(x->x_filterbank->b_refcount))\n        bonk_freefilterbank(x->x_filterbank);\n    freebytes(x->x_template, x->x_ntemplate * sizeof(x->x_template[0]));\n}\n\n/* -------------------------- Pd glue ------------------------- */\n#ifdef PD\n\nstatic void *bonk_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_bonk *x = (t_bonk *)pd_new(bonk_class);\n    int nsig = 1, period = DEFPERIOD, npts = DEFNPOINTS,\n        nfilters = DEFNFILTERS, j;\n    t_float halftones = DEFHALFTONES, overlap = DEFOVERLAP,\n        firstbin = DEFFIRSTBIN, minbandwidth = DEFMINBANDWIDTH;\n    t_insig *g;\n\n    x->x_canvas = canvas_getcurrent(); /* fbar: bind current canvas to x */\n    if (argc > 0 && argv[0].a_type == A_FLOAT)\n    {\n            /* old style args for compatibility */\n        period = atom_getfloatarg(0, argc, argv);\n        nsig = atom_getfloatarg(1, argc, argv);\n    }\n    else while (argc > 0)\n    {\n        t_symbol *firstarg = atom_getsymbolarg(0, argc, argv);\n        if (!strcmp(firstarg->s_name, \"-npts\") && argc > 1)\n        {\n            npts = atom_getfloatarg(1, argc, argv);\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(firstarg->s_name, \"-hop\") && argc > 1)\n        {\n            period = atom_getfloatarg(1, argc, argv);\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(firstarg->s_name, \"-nsigs\") && argc > 1)\n        {\n            nsig = atom_getfloatarg(1, argc, argv);\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(firstarg->s_name, \"-nfilters\") && argc > 1)\n        {\n            nfilters = atom_getfloatarg(1, argc, argv);\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(firstarg->s_name, \"-halftones\") && argc > 1)\n        {\n            halftones = atom_getfloatarg(1, argc, argv);\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(firstarg->s_name, \"-overlap\") && argc > 1)\n        {\n            overlap = atom_getfloatarg(1, argc, argv);\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(firstarg->s_name, \"-firstbin\") && argc > 1)\n        {\n            firstbin = atom_getfloatarg(1, argc, argv);\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(firstarg->s_name, \"-minbandwidth\") && argc > 1)\n        {\n            minbandwidth = atom_getfloatarg(1, argc, argv);\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(firstarg->s_name, \"-spew\") && argc > 1)\n        {\n            x->x_spew = (atom_getfloatarg(1, argc, argv) != 0);\n            argc -= 2; argv += 2;\n        }\n        else\n        {\n            pd_error(x,\n\"usage is: bonk [-npts #] [-hop #] [-nsigs #] [-nfilters #] [-halftones #]\"); \n            post(\n\"... [-overlap #] [-firstbin #] [-spew #]\");\n            argc = 0;\n        }\n    }\n\n    x->x_npoints = (npts >= MINPOINTS ? npts : DEFNPOINTS);\n    x->x_period = (period >= 1 ? period : npts/2);\n    x->x_nfilters = (nfilters >= 1 ? nfilters : DEFNFILTERS);\n    if (halftones < 0.01)\n        halftones = DEFHALFTONES;\n    else if (halftones > 12)\n        halftones = 12;\n    if (nsig < 1)\n        nsig = 1;\n    else if (nsig > MAXCHANNELS)\n        nsig = MAXCHANNELS;\n    if (firstbin < 0.5)\n        firstbin = 0.5;\n    if (overlap < 1)\n        overlap = 1;\n\n    x->x_clock = clock_new(x, (t_method)bonk_tick);\n    x->x_insig = (t_insig *)getbytes(nsig * sizeof(*x->x_insig));\n    for (j = 0, g = x->x_insig; j < nsig; j++, g++)\n    {\n        g->g_outlet = outlet_new(&x->x_obj, gensym(\"list\"));\n        if (j)\n            inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);\n    }\n    x->x_cookedout = outlet_new(&x->x_obj, gensym(\"list\"));\n    bonk_donew(x, npts, period, nsig, nfilters, halftones, overlap,\n        firstbin, minbandwidth, sys_getsr());\n    return (x);\n}\n\nvoid bonk_tilde_setup(void)\n{\n    bonk_class = class_new(gensym(\"bonk~\"), (t_newmethod)bonk_new,\n        (t_method)bonk_free, sizeof(t_bonk), 0, A_GIMME, 0);\n    class_addmethod(bonk_class, nullfn, gensym(\"signal\"), 0);\n    class_addmethod(bonk_class, (t_method)bonk_dsp, gensym(\"dsp\"), A_CANT, 0);\n    class_addbang(bonk_class, bonk_bang);\n    class_addmethod(bonk_class, (t_method)bonk_learn,\n        gensym(\"learn\"), A_FLOAT, 0);\n    class_addmethod(bonk_class, (t_method)bonk_forget, gensym(\"forget\"), 0);\n    class_addmethod(bonk_class, (t_method)bonk_thresh,\n        gensym(\"thresh\"), A_FLOAT, A_FLOAT, 0);\n    class_addmethod(bonk_class, (t_method)bonk_mask,\n        gensym(\"mask\"), A_FLOAT, A_FLOAT, 0);\n    class_addmethod(bonk_class, (t_method)bonk_debounce,\n        gensym(\"debounce\"), A_FLOAT, 0);\n    class_addmethod(bonk_class, (t_method)bonk_minvel,\n        gensym(\"minvel\"), A_FLOAT, 0);\n    class_addmethod(bonk_class, (t_method)bonk_print,\n        gensym(\"print\"), A_DEFFLOAT, 0);\n    class_addmethod(bonk_class, (t_method)bonk_debug,\n        gensym(\"debug\"), A_DEFFLOAT, 0);\n    class_addmethod(bonk_class, (t_method)bonk_spew,\n        gensym(\"spew\"), A_DEFFLOAT, 0);\n    class_addmethod(bonk_class, (t_method)bonk_useloudness,\n        gensym(\"useloudness\"), A_DEFFLOAT, 0);\n    class_addmethod(bonk_class, (t_method)bonk_attackbins,\n        gensym(\"attack-bins\"), A_DEFFLOAT, 0);\n    class_addmethod(bonk_class, (t_method)bonk_attackbins,\n        gensym(\"attack-frames\"), A_DEFFLOAT, 0);\n    class_addmethod(bonk_class, (t_method)bonk_read,\n        gensym(\"read\"), A_SYMBOL, 0);\n    class_addmethod(bonk_class, (t_method)bonk_write,\n        gensym(\"write\"), A_SYMBOL, 0);\n    post(\"bonk version 1.5\");\n}\n#endif\n\n/* -------------------------- MSP glue ------------------------- */\n#ifdef MSP\n\nint main()\n{       \n        t_class *c;\n        t_object *attr;\n        long attrflags = 0;\n        t_symbol *sym_long = gensym(\"long\"), *sym_float32 = gensym(\"float32\");\n        \n        c = class_new(\"bonk~\", (method)bonk_new, (method)bonk_free, sizeof(t_bonk), (method)0L, A_GIMME, 0);\n        \n        class_obexoffset_set(c, calcoffset(t_bonk, obex));\n        \n        attr = attr_offset_new(\"npoints\", sym_long, attrflags, (method)0L, (method)0L, calcoffset(t_bonk, x_npoints));\n        class_addattr(c, attr);\n        \n        attr = attr_offset_new(\"hop\", sym_long, attrflags, (method)0L, (method)0L, calcoffset(t_bonk, x_period));\n        class_addattr(c, attr);\n        \n        attr = attr_offset_new(\"nfilters\", sym_long, attrflags, (method)0L, (method)0L, calcoffset(t_bonk, x_nfilters));\n        class_addattr(c, attr);\n        \n        attr = attr_offset_new(\"halftones\", sym_float32, attrflags, (method)0L, (method)0L, calcoffset(t_bonk, x_halftones));\n        class_addattr(c, attr);\n        \n        attr = attr_offset_new(\"overlap\", sym_float32, attrflags, (method)0L, (method)0L, calcoffset(t_bonk, x_overlap));\n        class_addattr(c, attr);\n        \n        attr = attr_offset_new(\"firstbin\", sym_float32, attrflags, (method)0L, (method)0L, calcoffset(t_bonk, x_firstbin));\n        class_addattr(c, attr);\n        \n        attr = attr_offset_new(\"minbandwidth\", sym_float32, attrflags, (method)0L, (method)0L, calcoffset(t_bonk, x_minbandwidth));\n        class_addattr(c, attr);\n        \n        attr = attr_offset_new(\"minvel\", sym_float32, attrflags, (method)0L, (method)bonk_minvel_set, calcoffset(t_bonk, x_minvel));\n        class_addattr(c, attr);\n        \n        attr = attr_offset_new(\"lothresh\", sym_float32, attrflags, (method)0L, (method)bonk_lothresh_set, calcoffset(t_bonk, x_lothresh));\n        class_addattr(c, attr);\n        \n        attr = attr_offset_new(\"hithresh\", sym_float32, attrflags, (method)0L, (method)bonk_hithresh_set, calcoffset(t_bonk, x_hithresh));\n        class_addattr(c, attr);\n        \n        attr = attr_offset_new(\"masktime\", sym_long, attrflags, (method)0L, (method)bonk_masktime_set, calcoffset(t_bonk, x_masktime));\n        class_addattr(c, attr);\n        \n        attr = attr_offset_new(\"maskdecay\", sym_float32, attrflags, (method)0L, (method)bonk_maskdecay_set, calcoffset(t_bonk, x_maskdecay));\n        class_addattr(c, attr);\n    \n        attr = attr_offset_new(\"debouncedecay\", sym_float32, attrflags, (method)0L, (method)bonk_debouncedecay_set, calcoffset(t_bonk, x_debouncedecay));\n        class_addattr(c, attr);\n        \n        attr = attr_offset_new(\"debug\", sym_long, attrflags, (method)0L, (method)bonk_debug_set, calcoffset(t_bonk, x_debug));\n        class_addattr(c, attr);\n        \n        attr = attr_offset_new(\"spew\", sym_long, attrflags, (method)0L, (method)bonk_spew_set, calcoffset(t_bonk, x_spew));\n        class_addattr(c, attr);\n        \n        attr = attr_offset_new(\"useloudness\", sym_long, attrflags, (method)0L, (method)bonk_useloudness_set, calcoffset(t_bonk, x_useloudness));\n        class_addattr(c, attr);\n\n        attr = attr_offset_new(\"attackframes\", sym_long, attrflags, (method)0L, (method)bonk_attackbins_set, calcoffset(t_bonk, x_attackbins));\n        class_addattr(c, attr);\n        \n        attr = attr_offset_new(\"learn\", sym_long, attrflags, (method)0L, (method)bonk_learn_set, calcoffset(t_bonk, x_learn));\n        class_addattr(c, attr);\n    \n        class_addmethod(c, (method)bonk_dsp, \"dsp\", A_CANT, 0);\n        class_addmethod(c, (method)bonk_dsp64, \"dsp64\", A_CANT, 0);\n        class_addmethod(c, (method)bonk_bang, \"bang\", A_CANT, 0);\n        class_addmethod(c, (method)bonk_forget, \"forget\", 0);\n        class_addmethod(c, (method)bonk_thresh, \"thresh\", A_FLOAT, A_FLOAT, 0);\n        class_addmethod(c, (method)bonk_print, \"print\", A_DEFFLOAT, 0);\n        class_addmethod(c, (method)bonk_read, \"read\", A_DEFSYM, 0);\n        class_addmethod(c, (method)bonk_write, \"write\", A_DEFSYM, 0);\n        class_addmethod(c, (method)bonk_assist, \"assist\", A_CANT, 0);\n        \n        class_addmethod(c, (method)object_obex_dumpout, \"dumpout\", A_CANT, 0);\n        class_addmethod(c, (method)object_obex_quickref, \"quickref\", A_CANT, 0);\n        \n        class_dspinit(c);\n    \n        class_register(CLASS_BOX, c);\n        bonk_class = c;\n        \n        post(\"bonk~ v1.5\");\n        return (0);\n}\n\nstatic void *bonk_new(t_symbol *s, long ac, t_atom *av)\n{\n    short j;\n    t_bonk *x;\n        \n    if ((x = (t_bonk *)object_alloc(bonk_class))) {\n        \n        t_insig *g;\n\n        x->x_npoints = DEFNPOINTS;\n        x->x_period = DEFPERIOD;\n        x->x_nfilters = DEFNFILTERS;\n        x->x_halftones = DEFHALFTONES;\n        x->x_firstbin = DEFFIRSTBIN;\n        x->x_minbandwidth = DEFMINBANDWIDTH;\n        x->x_overlap = DEFOVERLAP;\n        x->x_ninsig = 1;\n\n        x->x_hithresh = DEFHITHRESH;\n        x->x_lothresh = DEFLOTHRESH;\n        x->x_masktime = DEFMASKTIME;\n        x->x_maskdecay = DEFMASKDECAY;\n        x->x_debouncedecay = DEFDEBOUNCEDECAY;\n        x->x_minvel = DEFMINVEL;\n        x->x_attackbins = DEFATTACKBINS;\n        \n        if (!x->x_period) x->x_period = x->x_npoints/2;\n        x->x_template = (t_template *)getbytes(0);\n        x->x_ntemplate = 0;\n        x->x_infill = 0;\n        x->x_countdown = 0;\n        x->x_willattack = 0;\n        x->x_attacked = 0;\n        x->x_maskphase = 0;\n        x->x_debug = 0;\n        x->x_learn = 0;\n        x->x_learndebounce = clock_getsystime();\n        x->x_learncount = 0;\n        x->x_useloudness = 0;\n        x->x_debouncevel = 0;\n        x->x_sr = sys_getsr();\n        \n        if (ac) {\n            switch (av[0].a_type) {\n                case A_LONG:\n                    x->x_ninsig = av[0].a_w.w_long;\n                    break;\n            }\n        }\n\n        if (x->x_ninsig < 1) x->x_ninsig = 1;\n        if (x->x_ninsig > MAXCHANNELS) x->x_ninsig = MAXCHANNELS;\n        \n        attr_args_process(x, ac, av);   \n\n        x->x_insig = (t_insig *)getbytes(x->x_ninsig * sizeof(*x->x_insig));\n\n        dsp_setup((t_pxobject *)x, x->x_ninsig);\n\n        object_obex_store(x, gensym(\"dumpout\"), outlet_new(x, NULL));\n\n        x->x_cookedout = listout((t_object *)x);\n\n        for (j = 0, g = x->x_insig + x->x_ninsig-1; j < x->x_ninsig; j++, g--) {\n                g->g_outlet = listout((t_object *)x);\n        }\n\n        x->x_clock = clock_new(x, (method)bonk_tick);\n\n        bonk_donew(x, x->x_npoints, x->x_period, x->x_ninsig, x->x_nfilters,\n            x->x_halftones, x->x_overlap, x->x_firstbin, x->x_minbandwidth,\n                sys_getsr());\n    }\n    return (x);\n}\n\n/* Attribute setters. */\nvoid bonk_minvel_set(t_bonk *x, void *attr, long ac, t_atom *av)\n{\n    if (ac && av) {\n        t_float f = atom_getfloat(av);\n        if (f < 0) f = 0; \n        x->x_minvel = f;\n    }\n}\n\nvoid bonk_lothresh_set(t_bonk *x, void *attr, long ac, t_atom *av)\n{\n    if (ac && av) {\n        t_float f = atom_getfloat(av);\n        if (f > x->x_hithresh)\n            post(\"bonk: warning: low threshold greater than hi threshold\");\n        x->x_lothresh = (f <= 0 ? 0.0001 : f);\n    }\n}\n    \nvoid bonk_hithresh_set(t_bonk *x, void *attr, long ac, t_atom *av)\n{\n    if (ac && av) {\n        t_float f = atom_getfloat(av);\n        if (f < x->x_lothresh)\n            post(\"bonk: warning: low threshold greater than hi threshold\");\n        x->x_hithresh = (f <= 0 ? 0.0001 : f);\n    }\n}\n\nvoid bonk_masktime_set(t_bonk *x, void *attr, long ac, t_atom *av)\n{\n    if (ac && av) {\n        int n = atom_getlong(av);\n        x->x_masktime = (n < 0) ? 0 : n;\n    }\n}\n\nvoid bonk_maskdecay_set(t_bonk *x, void *attr, long ac, t_atom *av)\n{\n    if (ac && av) {\n        t_float f = atom_getfloat(av);\n        f = (f < 0) ? 0 : f;\n        f = (f > 1) ? 1 : f;\n        x->x_maskdecay = f;\n    }\n}\n\nvoid bonk_debouncedecay_set(t_bonk *x, void *attr, long ac, t_atom *av)\n{\n    if (ac && av) {\n        t_float f = atom_getfloat(av);\n        f = (f < 0) ? 0 : f;\n        f = (f > 1) ? 1 : f;\n        x->x_debouncedecay = f;\n    }\n}\n\nvoid bonk_debug_set(t_bonk *x, void *attr, long ac, t_atom *av)\n{\n    if (ac && av) {\n        int n = atom_getlong(av);\n        x->x_debug = (n != 0);\n    }\n}\n\nvoid bonk_spew_set(t_bonk *x, void *attr, long ac, t_atom *av)\n{\n    if (ac && av) {\n        int n = atom_getlong(av);\n        x->x_spew = (n != 0);\n    }\n}\n\nvoid bonk_useloudness_set(t_bonk *x, void *attr, long ac, t_atom *av)\n{\n    if (ac && av) {\n        int n = atom_getlong(av);\n        x->x_useloudness = (n != 0);\n    }\n}\n\nvoid bonk_attackbins_set(t_bonk *x, void *attr, long ac, t_atom *av)\n{\n    if (ac && av) {\n        int n = atom_getlong(av);\n        n = (n < 1) ? 1 : n;\n        n = (n > MASKHIST) ? MASKHIST : n;\n        x->x_attackbins = n;\n    }\n}\n\nvoid bonk_learn_set(t_bonk *x, void *attr, long ac, t_atom *av)\n{\n    if (ac && av) {\n        int n = atom_getlong(av);\n        if (n != 0) {\n            x->x_template = (t_template *)t_resizebytes(x->x_template,\n                x->x_ntemplate * sizeof(x->x_template[0]), 0);\n            x->x_ntemplate = 0;\n        }\n        x->x_learn = n;\n        x->x_learncount = 0;\n    }\n}\n/* end attr setters */\n\nvoid bonk_assist(t_bonk *x, void *b, long m, long a, char *s)\n{\n}\n\n    /* get current system time */\ndouble clock_getsystime()\n{\n    return gettime();\n}\n\n    /* elapsed time in milliseconds since the given system time */\ndouble clock_gettimesince(double prevsystime)\n{\n    return ((gettime() - prevsystime));\n}\n\nt_float qrsqrt(t_float f)\n{\n    return 1/sqrt(f);\n}\n#endif /* MSP */\n"
  },
  {
    "path": "libs/libpd/pure-data/extra/bonk~/templates.txt",
    "content": " 10.47   9.65  14.95  23.77  28.32  38.84  53.21  41.20  31.25  21.70  16.48 \n  6.52  13.93  27.82  58.05  24.11  35.26  35.98  37.78  22.54  13.56  10.75 \n 30.45  28.86  29.42  21.94  29.92  35.70  38.49  32.01  28.19  27.38  22.10 \n 66.77  46.27  28.82  25.95  22.84  20.61  20.33  14.18   6.86   8.92   7.37 \n"
  },
  {
    "path": "libs/libpd/pure-data/extra/choice/choice.c",
    "content": "/* choice -- match incoming list against a collection of stored templates. */\n\n/*  Copyright 1999 Miller Puckette.\nPermission is granted to use this software for any purpose provided you\nkeep this copyright notice intact.\n\nTHE AUTHOR AND HIS EMPLOYERS MAKE NO WARRANTY, EXPRESS OR IMPLIED,\nIN CONNECTION WITH THIS SOFTWARE.\n \nThis file is downloadable from http://www.crca.ucsd.edu/~msp .\n*/\n\n#include \"m_pd.h\"\n#include <math.h>\nstatic t_class *choice_class;\n#define DIMENSION 10\n\ntypedef struct _elem\n{\n    t_float e_age;\n    t_float e_weight[DIMENSION];\n} t_elem;\n\ntypedef struct _choice\n{\n    t_object x_obj;\n    t_elem *x_vec;\n    int x_n;\n    int x_nonrepeat;\n} t_choice;\n\nstatic void *choice_new(t_float fnonrepeat)\n{\n    t_choice *x = (t_choice *)pd_new(choice_class);\n    outlet_new(&x->x_obj, gensym(\"float\"));\n    x->x_vec = (t_elem *)getbytes(0);\n    x->x_n = 0;\n    x->x_nonrepeat = (fnonrepeat != 0);\n    return (x);\n}\n\nstatic void choice_clear(t_choice *x)\n{\n    x->x_vec = (t_elem *)resizebytes(x->x_vec, x->x_n * sizeof(t_elem), 0);\n    x->x_n = 0;\n}\n\nstatic void choice_print(t_choice *x)\n{\n    int j;\n    for (j = 0; j < x->x_n; j++)\n    {\n        t_elem *e = x->x_vec + j;\n        t_float *w = e->e_weight;\n        post(\"%2d age %2d \\\nw %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f\",\n            j, (int)(e->e_age), w[0], w[1], w[2], w[3], w[4], w[5],\n                w[6], w[7], w[8], w[9]);\n    }\n}\n\nstatic void choice_add(t_choice *x, t_symbol *s, int argc, t_atom *argv)\n{\n    int oldn = x->x_n, newn = oldn + 1, i;\n    t_elem *e;\n    t_float sum, normal;\n    x->x_vec = (t_elem *)resizebytes(x->x_vec, oldn * sizeof(t_elem),\n        newn * sizeof(t_elem));\n    x->x_n = newn;\n    e = x->x_vec + oldn;\n    e->e_age = 2;\n    \n    for (i = 0, sum = 0; i < DIMENSION; i++)\n    {\n        t_float f = atom_getfloatarg(i, argc, argv);\n        e->e_weight[i] = f;\n        sum += f*f;\n    }\n    normal = (t_float)(sum > 0 ? 1./sqrt(sum) : 1);\n    for (i = 0; i < DIMENSION; i++)\n        e->e_weight[i] *= normal;\n}\n\nstatic void choice_list(t_choice *x, t_symbol *s, int argc, t_atom *argv)\n{\n    int i, j;\n    t_float bestsum = 0;\n    int bestindex = -1;\n    t_float invec[DIMENSION];\n    for (i = 0; i < DIMENSION; i++)\n        invec[i] = atom_getfloatarg(i, argc, argv);\n    for (j = 0; j < x->x_n; j++)\n    {\n        t_elem *e = x->x_vec + j;\n        t_float sum;\n        for (i = 0, sum = 0; i < DIMENSION; i++)\n            sum += e->e_weight[i] * invec[i];\n        if (x->x_nonrepeat) sum *= (t_float)(log(e->e_age));\n        if (sum > bestsum)\n        {\n            bestsum = sum;\n            sum = 1;\n            bestindex = j;\n        }\n    }\n    if (bestindex >= 0)\n    {\n        for (j = 0; j < x->x_n; j++)\n            x->x_vec[j].e_age += 1.;\n        x->x_vec[bestindex].e_age = 1;\n    }\n    outlet_float(x->x_obj.ob_outlet, (t_float)bestindex);\n}\n\nstatic void choice_free(t_choice *x)\n{\n    freebytes(x->x_vec, x->x_n * sizeof(t_elem));\n}\n\nvoid choice_setup(void)\n{\n    choice_class = class_new(gensym(\"choice\"), (t_newmethod)choice_new,\n        (t_method)choice_free, sizeof(t_choice), 0, A_DEFFLOAT, 0);\n    class_addmethod(choice_class, (t_method)choice_add, gensym(\"add\"), A_GIMME, 0);\n    class_addmethod(choice_class, (t_method)choice_clear, gensym(\"clear\"), 0);\n    class_addmethod(choice_class, (t_method)choice_print, gensym(\"print\"), 0);\n    class_addlist(choice_class, choice_list);\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/extra/fiddle~/fiddle~.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette and Ted Apel.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/*\n * Fiddle is a pitch tracker hardwired to have hop size (\"H\") equal to\n * half its window size (\"N\").\n *\n * This version should compile for Max \"0.26,\" JMAX, Pd, or Max/MSP.\n *\n * The \"lastanalysis\" field holds the shifted FT of the previous H\n * samples.  The buffer contains in effect points 1/2,  3/2, ..., (N-1)/2\n * of the DTFT of a real vector of length N, half of whose points are zero,\n * i.e.,  only the first H points are used.  Put another way, we get the\n * the odd-numbered points of the FFT of the H points, zero padded to 4*H in\n * length. The integer points 0, 1, ..., H-1\n * are found by interpolating these others,  using the fact that the\n * half-integer points are band-limited (they only have positive frequencies.)\n * To facilitate the interpolation the \"lastanalysis\" buffer contains\n * FILTSIZE extra points (1/2-FILTSIZE, ...,  -1/2) at the beginning and\n * FILTSIZE again at the end ((N+1)/2, ..., FILTSIZE+(N-1)/2).  The buffer\n * therefore has N+4*FILTSIZE floating-point numbers in it.\n *\n * after doing this I found out that you can just do a real FFT\n * of the H new points, zero-padded to contain N points, and using a similar\n * but simpler interpolation scheme you can still get 2N points of the DTFT\n * of the N points.  Jean Laroche is a big fat hen.\n *\n */\n\n\n/* These pragmas are only used for MSVC, not MinGW or Cygwin <hans@at.or.at> */\n#ifdef _MSC_VER\n#pragma warning (disable: 4305 4244)\n#endif\n\n/* this #ifdef does nothing, but its there... */\n#ifdef _WIN32\n#define flog log\n#define fexp exp\n#define fsqrt sqrt\n#else\n#define flog log\n#define fexp exp\n#define fsqrt sqrt\n#endif\n\nchar fiddle_version[] = \"fiddle version 1.1 TEST4\";\n\n#ifdef JMAX\n#include \"fts.h\"\n#include <stdio.h>\n#include <stdlib.h>\ntypedef float t_float;\ntypedef float t_floatarg;\ntypedef fts_symbol_t t_symbol;\n\nstatic void *getbytes(size_t nbytes)\n{\n    void *ret;\n    if (nbytes < 1) nbytes = 1;\n    ret = (void *)malloc(nbytes);\n    return (ret);\n}\n\nstatic void *resizebytes(void *old, size_t oldsize, size_t newsize)\n{\n    void *ret;\n    if (newsize < 1) newsize = 1;\n    ret = (void *)realloc((char *)old, newsize);\n    return (ret);\n}\n\nstatic void freebytes(void *fatso, size_t nbytes)\n{\n    free(fatso);\n}\n\n#define CLASSNAME \"fiddle\"\n\n#define OUTLETpower 5\n#define OUTLETmicropitch1 4\n#define OUTLETmicropitch2 3\n#define OUTLETmicropitch3 2\n#define OUTLETattack 1\n#define OUTLETpitch 0\n\nstatic fts_symbol_t *dsp_symbol = 0;\n#define error post\n\n#endif /* FTS */\n\n#ifdef MAX26\n#define t_floatarg double\n#include \"m_extern.h\"\n#include \"d_graph.h\"\n#include \"d_ugen.h\"\n#endif /* MAX26 */\n\n#ifdef PD\n#include \"m_pd.h\"\n#endif /* PD */\n\n#ifdef MSP\n#define flog log\n#define fexp exp\n#define fsqrt sqrt\n#endif /* MSP */\n\n#ifdef MSP\n#include \"ext.h\"\n#include \"z_dsp.h\"\n#include \"fft_mayer.proto.h\"\ntypedef float t_float;\ntypedef double t_floatarg;\n#endif /* MSP */\n\n#include <math.h>\n\n\n#define MINBIN 3\n#define DEFAMPLO 40\n#define DEFAMPHI 50\n#define DEFATTACKTIME 100\n#define DEFATTACKTHRESH 10\n#define DEFVIBTIME 50\n#define DEFVIBDEPTH 0.5\n#define GLISS 0.7f\n#define DBFUDGE 30.8f\n#define MINFREQINBINS 5     /* minimum frequency in bins for reliable output */\n\n#define MAXNPITCH 3\n#define MAXHIST 3                   /* find N hottest peaks in histogram */\n\n#define MAXPOINTS 8192\n#define MINPOINTS 128\n#define DEFAULTPOINTS 1024\n\n#define HISTORY 20\n#define MAXPEAK 100             /* maximum number of peaks */\n#define DEFNPEAK 20             /* default number of peaks */\n\n#define MAXNPEAK (MAXLOWPEAK + MAXSTRONGPEAK)\n#define MINBW (0.03f)                   /* consider BW >= 0.03 FFT bins */\n\n#define BINPEROCT 48                    /* bins per octave */\n#define BPERO_OVER_LOG2 69.24936196f    /* BINSPEROCT/log(2) */\n#define FACTORTOBINS (t_float)(4/0.0145453)       /* 4 / (pow(2.,1/48.) - 1) */\n#define BINGUARD 10                     /* extra bins to throw in front */\n#define PARTIALDEVIANCE 0.023f          /* acceptable partial detuning in % */\n#define LOGTODB 4.34294481903f          /* 20/log(10) */\n\n#define KNOCKTHRESH 10.f     /* don't know how to describe this */\n\n\nstatic t_float sigfiddle_partialonset[] =\n{\n0,\n48,\n76.0782000346154967102,\n96,\n111.45254855459339269887,\n124.07820003461549671089,\n134.75303625876499715823,\n144,\n152.15640006923099342109,\n159.45254855459339269887,\n166.05271769459026829915,\n172.07820003461549671088,\n177.62110647077242370064,\n182.75303625876499715892,\n187.53074858920888940907,\n192,\n};\n\n#define NPARTIALONSET ((int)(sizeof(sigfiddle_partialonset)/sizeof(t_float)))\n\nstatic int sigfiddle_intpartialonset[] =\n{\n0,\n48,\n76,\n96,\n111,\n124,\n135,\n144,\n152,\n159,\n166,\n172,\n178,\n183,\n188,\n192,\n};\n\n/* these coefficients, which come from the \"upsamp\" subdirectory,\nare a filter kernel for upsampling by a factor of two, assuming\nthe sound to be upsampled has no energy above half the Nyquist, i.e.,\nthat it's already 2x oversampled compared to the theoretically possible\nsample rate.  I got these by trial and error. */\n\n#define FILT1 ((t_float)(.5 * 1.227054))\n#define FILT2 ((t_float)(.5 * -0.302385))\n#define FILT3 ((t_float)(.5 * 0.095326))\n#define FILT4 ((t_float)(.5 * -0.022748))\n#define FILT5 ((t_float)(.5 * 0.002533))\n#define FILTSIZE 5\n\ntypedef struct peakout      /* a peak for output */\n{\n    t_float po_freq;                /* frequency in hz */\n    t_float po_amp;                 /* amplitude */\n} t_peakout;\n\ntypedef struct peak         /* a peak for analysis */\n{\n    t_float p_freq;                 /* frequency in bins */\n    t_float p_width;                /* peak width in bins */\n    t_float p_pow;                  /* peak power */\n    t_float p_loudness;             /* 4th root of power */\n    t_float *p_fp;                  /* pointer back to spectrum */\n} t_peak;\n\ntypedef struct histopeak\n{\n    t_float h_pitch;                /* estimated pitch */\n    t_float h_value;                /* value of peak */\n    t_float h_loud;                 /* combined strength of found partials */\n    int h_index;                    /* index of bin holding peak */\n    int h_used;                     /* true if an x_hist entry points here */\n} t_histopeak;\n\ntypedef struct pitchhist            /* struct for keeping history by pitch */\n{\n    t_float h_pitch;                /* pitch to output */\n    t_float h_amps[HISTORY];        /* past amplitudes */\n    t_float h_pitches[HISTORY];     /* past pitches */\n    t_float h_noted;                /* last pitch output */\n    int h_age;                      /* number of frames pitch has been there */\n    t_histopeak *h_wherefrom;       /* new histogram peak to incorporate */\n    void *h_outlet;\n} t_pitchhist;\n\ntypedef struct sigfiddle                    /* instance struct */\n{\n#ifdef JMAX\n    fts_object_t x_h;               /* object header */\n    fts_alarm_t x_clock;            /* callback for timeouts */\n#endif\n#ifdef MAX26\n    t_head x_h;                     /* header for tilde objects */\n    t_sig *x_io[IN1+OUT0];          /* number of signal inputs and outputs */\n    void *x_clock;                  /* a \"clock\" object */\n#endif\n#ifdef PD\n    t_object x_ob;                  /* object header */\n    t_clock *x_clock;               /* callback for timeouts */\n#endif\n#ifdef MSP\n        t_pxobject x_obj;\n        void *x_clock;\n        long x_downsample;              /* downsample feature because of\n                                         MSP's large sig vector sizes */\n#endif\n    t_float *x_inbuf;               /* buffer to analyze, npoints/2 elems */\n    t_float *x_lastanalysis;        /* FT of last buffer (see main comment) */\n    t_float *x_spiral;              /* 1/4-wave complex exponential */\n    t_peakout *x_peakbuf;           /* spectral peaks for output */\n    int x_npeakout;                 /* number of spectral peaks to output */\n    int x_npeakanal;                /* number of spectral peaks to analyze */\n    int x_phase;                    /* number of points since last output */\n    int x_histphase;                /* phase into amplitude history vector */\n    int x_hop;                      /* period of output, npoints/2 */\n    t_float x_sr;                   /* sample rate */\n    t_pitchhist x_hist[MAXNPITCH];  /* history of current pitches */\n    int x_nprint;                   /* how many periods to print */\n    int x_npitch;                   /* number of simultaneous pitches */\n    t_float x_dbs[HISTORY];         /* DB history, indexed by \"histphase\" */\n    t_float x_peaked;               /* peak since last attack */\n    int x_dbage;                    /* number of bins DB has met threshold */\n    int x_auto;                     /* true if generating continuous output */\n/* parameters */\n    t_float x_amplo;\n    t_float x_amphi;\n    int x_attacktime;\n    int x_attackbins;\n    t_float x_attackthresh;\n    int x_vibtime;\n    int x_vibbins;\n    t_float x_vibdepth;\n    t_float x_npartial;\n/* outlets & clock */\n    void *x_envout;\n    int x_attackvalue;\n    void *x_attackout;\n    void *x_noteout;\n    void *x_peakout;\n} t_sigfiddle;\n\n#if CHECKER\nt_float fiddle_checker[1024];\n#endif\n\n#ifdef MSP\n/* Mac compiler requires prototypes for everything */\n\nint sigfiddle_ilog2(int n);\nt_float fiddle_mtof(t_float f);\nt_float fiddle_ftom(t_float f);\nvoid sigfiddle_doit(t_sigfiddle *x);\nvoid sigfiddle_debug(t_sigfiddle *x);\nvoid sigfiddle_print(t_sigfiddle *x);\nvoid sigfiddle_assist(t_sigfiddle *x, void *b, long m, long a, char *s);\nvoid sigfiddle_amprange(t_sigfiddle *x, double amplo,  double amphi);\nvoid sigfiddle_reattack(t_sigfiddle *x, t_floatarg attacktime, t_floatarg\nattackthresh);\nvoid sigfiddle_vibrato(t_sigfiddle *x, t_floatarg vibtime, t_floatarg\nvibdepth);\nvoid sigfiddle_npartial(t_sigfiddle *x, double npartial);\nvoid sigfiddle_auto(t_sigfiddle *x, t_floatarg f);\nvoid sigfiddle_setnpoints(t_sigfiddle *x, t_floatarg f);\nint sigfiddle_doinit(t_sigfiddle *x, long npoints, long npitch, long\nnpeakanal, long npeakout);\nstatic t_int *fiddle_perform(t_int *w);\nvoid sigfiddle_dsp(t_sigfiddle *x, t_signal **sp);\nvoid sigfiddle_tick(t_sigfiddle *x);\nvoid sigfiddle_bang(t_sigfiddle *x);\nvoid sigfiddle_ff(t_sigfiddle *x);\nvoid *sigfiddle_new(long npoints, long npitch,\n    long npeakanal, long npeakout);\nvoid msp_fft(t_float *buf, long np, long inv);\nt_float msp_ffttemp[MAXPOINTS*2];\nint errno;\n#endif\n\nint sigfiddle_ilog2(int n)\n{\n    int ret = -1;\n    while (n)\n    {\n        n >>= 1;\n        ret++;\n    }\n    return (ret);\n}\n\nt_float fiddle_mtof(t_float f)\n{\n        return (8.17579891564 * exp(.0577622650 * f));\n}\n\nt_float fiddle_ftom(t_float f)\n{\n        return (17.3123405046 * log(.12231220585 * f));\n}\n#define ftom fiddle_ftom\n#define mtof fiddle_mtof\n\nvoid sigfiddle_doit(t_sigfiddle *x)\n{\n#ifdef MSP\n        /* prevents interrupt-level stack overflow crash with Netscape. */\n    static t_float spect1[4*MAXPOINTS];\n    static t_float spect2[MAXPOINTS + 4*FILTSIZE];\n#else\n    t_float spect1[4*MAXPOINTS];\n    t_float spect2[MAXPOINTS + 4*FILTSIZE];\n#endif\n#if CHECKER\n    t_float checker3[4*MAXPOINTS];\n#endif\n\n    t_peak peaklist[MAXPEAK + 1], *pk1;\n    t_peakout *pk2;\n    t_histopeak histvec[MAXHIST], *hp1;\n    int i, j, k, hop = x->x_hop, n = 2*hop, npeak, npitch,\n        logn = sigfiddle_ilog2(n), newphase, oldphase;\n    t_float *fp, *fp1, *fp2, *fp3, total_power, total_loudness, total_db;\n    t_float maxbin = BINPEROCT * (logn-2),  *histogram = spect2 + BINGUARD;\n    t_pitchhist *phist;\n    t_float hzperbin = x->x_sr / (2.0f * n);\n    int npeakout = x->x_npeakout, npeakanal = x->x_npeakanal;\n    int npeaktot = (npeakout > npeakanal ? npeakout : npeakanal);\n\n    oldphase = x->x_histphase;\n    newphase = x->x_histphase + 1;\n    if (newphase == HISTORY) newphase = 0;\n    x->x_histphase = newphase;\n\n        /*\n         * multiply the H points by a 1/4-wave complex exponential,\n         * and take FFT of the result.\n         */\n    for (i = 0, fp1 = x->x_inbuf, fp2 = x->x_spiral, fp3 = spect1;\n        i < hop; i++, fp1++, fp2 += 2, fp3 += 2)\n            fp3[0] = fp1[0] * fp2[0], fp3[1] = fp1[0] * fp2[1];\n\n#ifdef MAX26\n    fft(spect1, hop, 0);\n#endif\n#ifdef PD\n    pd_fft(spect1, hop, 0);\n#endif\n#ifdef JMAX\n    fts_cfft_inplc((complex *)spect1, hop);\n#endif\n#ifdef MSP\n        msp_fft(spect1,hop,0);\n#endif\n        /*\n         * now redistribute the points to get in effect the odd-numbered\n         * points of the FFT of the H points, zero padded to 4*H in length.\n         */\n    for (i = 0, fp1 = spect1, fp2 = spect2 + (2*FILTSIZE);\n        i < (hop>>1); i++, fp1 += 2, fp2 += 4)\n            fp2[0] = fp1[0], fp2[1] = fp1[1];\n    for (i = 0, fp1 = spect1 + n - 2, fp2 = spect2 + (2*FILTSIZE+2);\n        i < (hop>>1); i++, fp1 -= 2, fp2 += 4)\n            fp2[0] = fp1[0], fp2[1] = -fp1[1];\n    for (i = 0, fp1 = spect2 + (2*FILTSIZE), fp2 = spect2 + (2*FILTSIZE-2);\n        i<FILTSIZE; i++, fp1+=2, fp2-=2)\n            fp2[0] = fp1[0],  fp2[1] = -fp1[1];\n    for (i = 0, fp1 = spect2 + (2*FILTSIZE+n-2), fp2 = spect2 + (2*FILTSIZE+n);\n        i<FILTSIZE; i++, fp1-=2, fp2+=2)\n            fp2[0] = fp1[0],  fp2[1] = -fp1[1];\n#if 0\n    {\n        fp = spect2 + 2*FILTSIZE;\n        post(\"x1 re %12.4f %12.4f %12.4f %12.4f %12.4f\",\n            fp[0], fp[2], fp[4], fp[6], fp[8]);\n        post(\"x1 im %12.4f %12.4f %12.4f %12.4f %12.4f\",\n            fp[1], fp[3], fp[5], fp[7], fp[9]);\n    }\n#endif\n        /* spect2 is now prepared; now combine spect2 and lastanalysis into\n         * spect1.  Odd-numbered points of spect1 are the points of \"last\"\n         * plus (-i, i, -i, ...) times spect1.  Even-numbered points are\n         * the interpolated points of \"last\" plus (1, -1, 1, ...) times the\n         * interpolated points of spect1.\n         *\n         * To interpolate, take FILT1 exp(-pi/4) times\n         * the previous point,  FILT2*exp(-3*pi/4) times 3 bins before,\n         * etc,  and FILT1 exp(pi/4), FILT2 exp(3pi/4), etc., to weight\n         * the +1, +3, etc., points.\n         *\n         * In this calculation,  we take (1, i, -1, -i, 1) times the\n         * -9, -7, ..., -1 points, and (i, -1, -i, 1, i) times the 1, 3,..., 9\n         * points of the OLD spectrum, alternately adding and subtracting\n         * the new spectrum to the old; then we multiply the whole thing\n         * by exp(-i pi/4).\n         */\n    for (i = 0, fp1 = spect1, fp2 = x->x_lastanalysis + 2*FILTSIZE,\n        fp3 = spect2 + 2*FILTSIZE;\n            i < (hop>>1); i++)\n    {\n        t_float re,  im;\n\n        re= FILT1 * ( fp2[ -2] -fp2[ 1]  +fp3[ -2] -fp3[ 1]) +\n            FILT2 * ( fp2[ -3] -fp2[ 2]  +fp3[ -3] -fp3[ 2]) +\n            FILT3 * (-fp2[ -6] +fp2[ 5]  -fp3[ -6] +fp3[ 5]) +\n            FILT4 * (-fp2[ -7] +fp2[ 6]  -fp3[ -7] +fp3[ 6]) +\n            FILT5 * ( fp2[-10] -fp2[ 9]  +fp3[-10] -fp3[ 9]);\n\n        im= FILT1 * ( fp2[ -1] +fp2[ 0]  +fp3[ -1] +fp3[ 0]) +\n            FILT2 * (-fp2[ -4] -fp2[ 3]  -fp3[ -4] -fp3[ 3]) +\n            FILT3 * (-fp2[ -5] -fp2[ 4]  -fp3[ -5] -fp3[ 4]) +\n            FILT4 * ( fp2[ -8] +fp2[ 7]  +fp3[ -8] +fp3[ 7]) +\n            FILT5 * ( fp2[ -9] +fp2[ 8]  +fp3[ -9] +fp3[ 8]);\n\n        fp1[0] = 0.7071f * (re + im);\n        fp1[1] = 0.7071f * (im - re);\n        fp1[4] = fp2[0] + fp3[1];\n        fp1[5] = fp2[1] - fp3[0];\n        \n        fp1 += 8, fp2 += 2, fp3 += 2;\n        re= FILT1 * ( fp2[ -2] -fp2[ 1]  -fp3[ -2] +fp3[ 1]) +\n            FILT2 * ( fp2[ -3] -fp2[ 2]  -fp3[ -3] +fp3[ 2]) +\n            FILT3 * (-fp2[ -6] +fp2[ 5]  +fp3[ -6] -fp3[ 5]) +\n            FILT4 * (-fp2[ -7] +fp2[ 6]  +fp3[ -7] -fp3[ 6]) +\n            FILT5 * ( fp2[-10] -fp2[ 9]  -fp3[-10] +fp3[ 9]);\n\n        im= FILT1 * ( fp2[ -1] +fp2[ 0]  -fp3[ -1] -fp3[ 0]) +\n            FILT2 * (-fp2[ -4] -fp2[ 3]  +fp3[ -4] +fp3[ 3]) +\n            FILT3 * (-fp2[ -5] -fp2[ 4]  +fp3[ -5] +fp3[ 4]) +\n            FILT4 * ( fp2[ -8] +fp2[ 7]  -fp3[ -8] -fp3[ 7]) +\n            FILT5 * ( fp2[ -9] +fp2[ 8]  -fp3[ -9] -fp3[ 8]);\n\n        fp1[0] = 0.7071f * (re + im);\n        fp1[1] = 0.7071f * (im - re);\n        fp1[4] = fp2[0] - fp3[1];\n        fp1[5] = fp2[1] + fp3[0];\n        \n        fp1 += 8, fp2 += 2, fp3 += 2;\n    }\n#if 0\n    if (x->x_nprint)\n    {\n        for (i = 0,  fp = spect1; i < 16; i++,  fp+= 4)\n            post(\"spect %d %f %f --> %f\", i, fp[0], fp[1],\n                sqrt(fp[0] * fp[0] + fp[1] * fp[1]));\n    }\n#endif\n         /* copy new spectrum out */\n    for (i = 0, fp1 = spect2, fp2 = x->x_lastanalysis;\n            i < n + 4*FILTSIZE; i++) *fp2++ = *fp1++;\n\n    for (i = 0; i < MINBIN; i++) spect1[4*i + 2] = spect1[4*i + 3] = 0;\n        /* starting at bin MINBIN, compute hanning windowed power spectrum */\n    for (i = MINBIN, fp1 = spect1+4*MINBIN, total_power = 0;\n        i < n-2; i++,  fp1 += 4)\n    {\n        t_float re = fp1[0] - 0.5 * (fp1[-8] + fp1[8]);\n        t_float im = fp1[1] - 0.5 * (fp1[-7] + fp1[9]);\n        fp1[3] = (total_power += (fp1[2] = re * re + im * im));\n    }\n\n    if (total_power > 1e-9f)\n    {\n        total_db = (100.f - DBFUDGE) + LOGTODB * log(total_power/n);\n        total_loudness = fsqrt(fsqrt(total_power));\n        if (total_db < 0) total_db = 0;\n    }\n    else total_db = total_loudness = 0;\n        /*  store new db in history vector */\n    x->x_dbs[newphase] = total_db;\n    if (total_db < x->x_amplo) goto nopow;\n#if 1\n    if (x->x_nprint) post(\"power %f\", total_power);\n#endif\n\n#if CHECKER\n        /* verify that our FFT resampling thing is putting out good results */\n    for (i = 0; i < hop; i++)\n    {\n        checker3[2*i] = fiddle_checker[i];\n        checker3[2*i + 1] = 0;\n        checker3[n + 2*i] = fiddle_checker[i] = x->x_inbuf[i];\n        checker3[n + 2*i + 1] = 0;\n    }\n    for (i = 2*n; i < 4*n; i++) checker3[i] = 0;\n    fft(checker3, 2*n, 0);\n    if (x->x_nprint)\n    {\n        for (i = 0,  fp = checker3; i < 16; i++,  fp += 2)\n            post(\"spect %d %f %f --> %f\", i, fp[0], fp[1],\n                sqrt(fp[0] * fp[0] + fp[1] * fp[1]));\n    }\n\n#endif\n    npeak = 0;\n\n         /* search for peaks */\n    for (i = MINBIN, fp = spect1+4*MINBIN, pk1 = peaklist;\n        i < n-2 && npeak < npeaktot; i++, fp += 4)\n    {\n        t_float height = fp[2], h1 = fp[-2], h2 = fp[6];\n        t_float totalfreq, pfreq, f1, f2, m, var, stdev;\n        \n        if (height < h1 || height < h2 ||\n            h1 < 0.00001f*total_power || h2 < 0.00001f*total_power)\n                continue;\n\n            /* use an informal phase vocoder to estimate the frequency.\n            Do this for the two adjacent bins too. */\n        pfreq= ((fp[-8] - fp[8]) * (2.0f * fp[0] - fp[8] - fp[-8]) +\n                (fp[-7] - fp[9]) * (2.0f * fp[1] - fp[9] - fp[-7])) /\n                    (2.0f * height);\n        f1=    ((fp[-12] - fp[4]) * (2.0f * fp[-4] - fp[4] - fp[-12]) +\n                (fp[-11] - fp[5]) * (2.0f * fp[-3] - fp[5] - fp[-11])) /\n                    (2.0f * h1) - 1;\n        f2=    ((fp[-4] - fp[12]) * (2.0f * fp[4] - fp[12] - fp[-4]) +\n                (fp[-3] - fp[13]) * (2.0f * fp[5] - fp[13] - fp[-3])) /\n                    (2.0f * h2) + 1;\n\n            /* get sample mean and variance of the three */\n        m = 0.333333f * (pfreq + f1 + f2);\n        var = 0.5f * ((pfreq-m)*(pfreq-m) + (f1-m)*(f1-m) + (f2-m)*(f2-m));\n\n        totalfreq = i + m;\n        if (var * total_power > KNOCKTHRESH * height || var < 1e-30)\n        {\n#if 0\n            if (x->x_nprint)\n                post(\"cancel: %.2f hz, index %.1f, power %.5f, stdev=%.2f\",\n                    totalfreq * hzperbin, BPERO_OVER_LOG2 * log(totalfreq) - 96,\n                     height, sqrt(var));\n#endif\n            continue;\n        }\n        stdev = fsqrt(var);\n        if (totalfreq < 4)\n        {\n            if (x->x_nprint) post(\"oops: was %d,  freq %f, m %f, stdev %f h %f\",\n                i,  totalfreq, m, stdev, height);\n            totalfreq = 4;\n        }\n        pk1->p_width = stdev;\n\n        pk1->p_pow = height;\n        pk1->p_loudness = fsqrt(fsqrt(height));\n        pk1->p_fp = fp;\n        pk1->p_freq = totalfreq;\n        npeak++;\n#if 1\n        if (x->x_nprint)\n        {\n            post(\"peak: %.2f hz. index %.1f, power %.5f, stdev=%.2f\",\n                pk1->p_freq * hzperbin,\n                BPERO_OVER_LOG2 * log(pk1->p_freq) - 96,\n                 height, stdev);\n        }\n#endif\n        pk1++;\n    }\n\n            /* prepare the raw peaks for output */\n    for (i = 0, pk1 = peaklist, pk2 = x->x_peakbuf; i < npeak;\n        i++, pk1++, pk2++)\n    {\n        t_float loudness = pk1->p_loudness;\n        if (i >= npeakout) break;\n        pk2->po_freq = hzperbin * pk1->p_freq;\n        pk2->po_amp = (2. / (t_float)n) * (loudness * loudness);\n    }\n    for (; i < npeakout; i++, pk2++) pk2->po_amp = pk2->po_freq = 0;\n\n        /* now, working back into spect2, make a sort of \"liklihood\"\n         * spectrum.  Proceeding in 48ths of an octave,  from 2 to\n         * n/2 (in bins), the likelihood of each pitch range is contributed\n         * to by every peak in peaklist that's an integer multiple of it\n         * in frequency.\n         */\n\n    if (npeak > npeakanal) npeak = npeakanal; /* max # peaks to analyze */\n    for (i = 0, fp1 = histogram; i < maxbin; i++) *fp1++ = 0;\n    for (i = 0, pk1 = peaklist; i < npeak; i++, pk1++)\n    {\n        t_float pit = BPERO_OVER_LOG2 * flog(pk1->p_freq) - 96.0;\n        t_float binbandwidth = FACTORTOBINS * pk1->p_width/pk1->p_freq;\n        t_float putbandwidth = (binbandwidth < 2 ? 2 : binbandwidth);\n        t_float weightbandwidth = (binbandwidth < 1.0 ? 1.0 : binbandwidth);\n        /* t_float weightamp = 1.0f + 3.0f * pk1->p_pow / pow; */\n        t_float weightamp = 4. * pk1->p_loudness / total_loudness;\n        for (j = 0, fp2 = sigfiddle_partialonset; j < NPARTIALONSET; j++, fp2++)\n        {\n            t_float bin = pit - *fp2;\n            if (bin < maxbin)\n            {\n                t_float para, pphase, score = 30.0 * weightamp /\n                    ((j+x->x_npartial) * weightbandwidth);\n                int firstbin = bin + 0.5f - 0.5f * putbandwidth;\n                int lastbin = bin + 0.5f + 0.5f * putbandwidth;\n                int ibw = lastbin - firstbin;\n                if (firstbin < -BINGUARD) break;\n                para = 1.0f / (putbandwidth * putbandwidth);\n                for (k = 0, fp3 = histogram + firstbin,\n                    pphase = firstbin-bin; k <= ibw;\n                        k++, fp3++,  pphase += 1.0f)\n                {\n                    *fp3 += score * (1.0f - para * pphase * pphase);\n                }\n            }\n        }\n    }\n#if 1\n    if (x->x_nprint)\n    {\n        for (i = 0; i < 6*5; i++)\n        {\n            t_float fhz = hzperbin * exp ((8*i + 96) * (1./BPERO_OVER_LOG2));\n            if (!(i % 6)) post(\"-- bin %d pitch %f freq %f----\", 8*i,\n                ftom(fhz), fhz);;\n            post(\"%3d %3d %3d %3d %3d %3d %3d %3d\",\n                (int)(histogram[8*i]),\n                (int)(histogram[8*i+1]),\n                (int)(histogram[8*i+2]),\n                (int)(histogram[8*i+3]),\n                (int)(histogram[8*i+4]),\n                (int)(histogram[8*i+5]),\n                (int)(histogram[8*i+6]),\n                (int)(histogram[8*i+7]));\n        }\n    }\n\n#endif\n\n        /*\n         * Next we find up to NPITCH strongest peaks in the histogram.\n         * if a peak is related to a stronger one via an interval in\n         * the sigfiddle_partialonset array,  we suppress it.\n         */\n\n    for (npitch = 0; npitch < x->x_npitch; npitch++)\n    {\n        int indx;\n        t_float best;\n        if (npitch)\n        {\n            for (best = 0, indx = -1, j=1; j < maxbin-1; j++)\n            {\n                if (histogram[j] > best && histogram[j] > histogram[j-1] &&\n                    histogram[j] > histogram[j+1])\n                {\n                    for (k = 0; k < npitch; k++)\n                        if (histvec[k].h_index == j)\n                            goto peaknogood;\n                    for (k = 0; k < NPARTIALONSET; k++)\n                    {\n                        if (j - sigfiddle_intpartialonset[k] < 0) break;\n                        if (histogram[j - sigfiddle_intpartialonset[k]]\n                            > histogram[j]) goto peaknogood;\n                    }\n                    for (k = 0; k < NPARTIALONSET; k++)\n                    {\n                        if (j + sigfiddle_intpartialonset[k] >= maxbin) break;\n                        if (histogram[j + sigfiddle_intpartialonset[k]]\n                            > histogram[j]) goto peaknogood;\n                    }\n                    indx = j;\n                    best = histogram[j];\n                }\n            peaknogood: ;\n            }\n        }\n        else\n        {\n            for (best = 0, indx = -1, j=0; j < maxbin; j++)\n                if (histogram[j] > best)\n                    indx = j,  best = histogram[j];\n        }\n        if (indx < 0) break;\n        histvec[npitch].h_value = best;\n        histvec[npitch].h_index = indx;\n    }\n#if 1\n    if (x->x_nprint)\n    {\n        for (i = 0; i < npitch; i++)\n        {\n            post(\"index %d freq %f --> value %f\", histvec[i].h_index,\n                exp((1./BPERO_OVER_LOG2) * (histvec[i].h_index + 96)),\n                histvec[i].h_value);\n            post(\"next %f , prev %f\",\n                exp((1./BPERO_OVER_LOG2) * (histvec[i].h_index + 97)),\n                exp((1./BPERO_OVER_LOG2) * (histvec[i].h_index + 95)) );\n        }\n    }\n#endif\n\n        /* for each histogram peak, we now search back through the\n         * FFT peaks.  A peak is a pitch if either there are several\n         * harmonics that match it,  or else if (a) the fundamental is\n         * present,  and (b) the sum of the powers of the contributing peaks\n         * is at least 1/100 of the total power.\n         *\n         * A peak is a contributor if its frequency is within 25 cents of\n         * a partial from 1 to 16.\n         *\n         * Finally, we have to be at least 5 bins in frequency, which\n         * corresponds to 2-1/5 periods fitting in the analysis window.\n         */\n\n    for (i = 0; i < npitch; i++)\n    {\n        t_float cumpow = 0, cumstrength = 0, freqnum = 0, freqden = 0;\n        int npartials = 0,  nbelow8 = 0;\n            /* guessed-at frequency in bins */\n        t_float putfreq = fexp((1.0 / BPERO_OVER_LOG2) *\n            (histvec[i].h_index + 96.0f));\n        for (j = 0; j < npeak; j++)\n        {\n            t_float fpnum = peaklist[j].p_freq/putfreq;\n            int pnum = fpnum + 0.5f;\n            t_float fipnum = pnum;\n            t_float deviation;\n            if (pnum > 16 || pnum < 1) continue;\n            deviation = 1.0f - fpnum/fipnum;\n            if (deviation > -PARTIALDEVIANCE && deviation < PARTIALDEVIANCE)\n            {\n                /*\n                 * we figure this is a partial since it's within 1/4 of\n                 * a halftone of a multiple of the putative frequency.\n                 */\n\n                t_float stdev, weight;\n                npartials++;\n                if (pnum < 8) nbelow8++;\n                cumpow += peaklist[j].p_pow;\n                cumstrength += fsqrt(fsqrt(peaklist[j].p_pow));\n                stdev = (peaklist[j].p_width > MINBW ?\n                    peaklist[j].p_width : MINBW);\n                weight = 1.0f / ((stdev*fipnum) * (stdev*fipnum));\n                freqden += weight;\n                freqnum += weight * peaklist[j].p_freq/fipnum;          \n#if 1\n                if (x->x_nprint)\n                {\n                    post(\"peak %d partial %d f=%f w=%f\",\n                        j, pnum, peaklist[j].p_freq/fipnum, weight);\n                }\n#endif\n            }\n#if 1\n            else if (x->x_nprint) post(\"peak %d partial %d dev %f\",\n                        j, pnum, deviation);\n#endif\n        }\n        if ((nbelow8 < 4 || npartials < 7) && cumpow < 0.01f * total_power)\n            histvec[i].h_value = 0;\n        else\n        {\n            t_float pitchpow = (cumstrength * cumstrength) *\n                (cumstrength * cumstrength);\n            t_float freqinbins = freqnum/freqden;\n                /* check for minimum output frequency */\n\n            if (freqinbins < MINFREQINBINS)\n                histvec[i].h_value = 0;\n            else\n            {\n                    /* we passed all tests... save the values we got */\n                histvec[i].h_pitch = ftom(hzperbin * freqnum/freqden);\n                histvec[i].h_loud = (100.0f -DBFUDGE) +\n                    (LOGTODB) * log(pitchpow/n);\n            }\n        }\n    }\n#if 1\n    if (x->x_nprint)\n    {\n        for (i = 0; i < npitch; i++)\n        {\n            if (histvec[i].h_value > 0)\n                post(\"index %d pit %f loud %f\", histvec[i].h_index,\n                histvec[i].h_pitch, histvec[i].h_loud);\n            else post(\"-- cancelled --\");\n        }\n    }\n#endif\n\n        /* now try to find continuous pitch tracks that match the new\n         * pitches.  First mark each peak unmatched.\n         */\n    for (i = 0, hp1 = histvec; i < npitch; i++, hp1++)\n        hp1->h_used = 0;\n\n        /* for each old pitch, try to match a new one to it. */\n    for (i = 0, phist = x->x_hist; i < x->x_npitch; i++,  phist++)\n    {\n        t_float thispitch = phist->h_pitches[oldphase];\n        phist->h_pitch = 0;         /* no output, thanks */\n        phist->h_wherefrom = 0;\n        if (thispitch == 0.0f) continue;\n        for (j = 0, hp1 = histvec; j < npitch; j++, hp1++)\n            if ((hp1->h_value > 0) && hp1->h_pitch > thispitch - GLISS\n                && hp1->h_pitch < thispitch + GLISS)\n        {\n            phist->h_wherefrom = hp1;\n            hp1->h_used = 1;\n        }\n    }\n    for (i = 0, hp1 = histvec; i < npitch; i++, hp1++)\n        if ((hp1->h_value > 0) && !hp1->h_used)\n    {\n        for (j = 0, phist = x->x_hist; j < x->x_npitch; j++,  phist++)\n            if (!phist->h_wherefrom)\n        {\n            phist->h_wherefrom = hp1;\n            phist->h_age = 0;\n            phist->h_noted = 0;\n            hp1->h_used = 1;\n            goto happy;\n        }\n        break;\n    happy: ;\n    }\n        /* copy the pitch info into the history vector */\n    for (i = 0, phist = x->x_hist; i < x->x_npitch; i++,  phist++)\n    {\n        if (phist->h_wherefrom)\n        {\n            phist->h_amps[newphase] = phist->h_wherefrom->h_loud;\n            phist->h_pitches[newphase] =\n                phist->h_wherefrom->h_pitch;\n            (phist->h_age)++;\n        }\n        else\n        {\n            phist->h_age = 0;\n            phist->h_amps[newphase] = phist->h_pitches[newphase] = 0;\n        }\n    }\n#if 1\n    if (x->x_nprint)\n    {\n        post(\"vibrato %d %f\", x->x_vibbins, x->x_vibdepth);\n        for (i = 0, phist = x->x_hist; i < x->x_npitch; i++,  phist++)\n        {\n            post(\"noted %f, age %d\", phist->h_noted,  phist->h_age);\n#ifndef I860\n            post(\"values %f %f %f %f %f\",\n                phist->h_pitches[newphase],\n                phist->h_pitches[(newphase + HISTORY-1)%HISTORY],\n                phist->h_pitches[(newphase + HISTORY-2)%HISTORY],\n                phist->h_pitches[(newphase + HISTORY-3)%HISTORY],\n                phist->h_pitches[(newphase + HISTORY-4)%HISTORY]);\n#endif\n        }\n    }\n#endif\n        /* look for envelope attacks */\n\n    x->x_attackvalue = 0;\n\n    if (x->x_peaked)\n    {\n        if (total_db > x->x_amphi)\n        {\n            int binlook = newphase - x->x_attackbins;\n            if (binlook < 0) binlook += HISTORY;\n            if (total_db > x->x_dbs[binlook] + x->x_attackthresh)\n            {\n                x->x_attackvalue = 1;\n                x->x_peaked = 0;\n            }\n        }\n    }\n    else\n    {\n        int binlook = newphase - x->x_attackbins;\n        if (binlook < 0) binlook += HISTORY;\n        if (x->x_dbs[binlook] > x->x_amphi && x->x_dbs[binlook] > total_db)\n            x->x_peaked = 1;\n    }\n\n        /* for each current frequency track, test for a new note using a\n         * stability criterion.  Later perhaps we should also do as in\n         * pitch~ and check for unstable notes a posteriori when\n         * there's a new attack with no note found since the last onset;\n         * but what's an attack &/or onset when we're polyphonic?\n         */\n\n    for (i = 0, phist = x->x_hist; i < x->x_npitch; i++,  phist++)\n    {\n            /*\n             * if we've found a pitch but we've now strayed from it turn\n             * it off.\n             */\n        if (phist->h_noted)\n        {\n            if (phist->h_pitches[newphase] > phist->h_noted + x->x_vibdepth\n                || phist->h_pitches[newphase] < phist->h_noted - x->x_vibdepth)\n                    phist->h_noted = 0;\n        }\n        else\n        {\n            if (phist->h_wherefrom && phist->h_age >= x->x_vibbins)\n            {\n                t_float centroid = 0;\n                int not = 0;\n                for (j = 0, k = newphase; j < x->x_vibbins; j++)\n                {\n                    centroid += phist->h_pitches[k];\n                    k--;\n                    if (k < 0) k = HISTORY-1;\n                }\n                centroid /= x->x_vibbins;\n                for (j = 0, k = newphase; j < x->x_vibbins; j++)\n                {\n                        /* calculate deviation from norm */\n                    t_float dev = centroid - phist->h_pitches[k];\n                    k--;\n                    if (k < 0) k = HISTORY-1;\n                    if (dev > x->x_vibdepth ||\n                        -dev > x->x_vibdepth) not = 1;\n                }\n                if (!not)\n                {\n                    phist->h_pitch = phist->h_noted = centroid;\n                }\n            }\n        }\n    }\n    return;\n\nnopow:\n    for (i = 0; i < x->x_npitch; i++)\n    {\n        x->x_hist[i].h_pitch = x->x_hist[i].h_noted =\n            x->x_hist[i].h_pitches[newphase] =\n            x->x_hist[i].h_amps[newphase] = 0;\n        x->x_hist[i].h_age = 0;\n    }\n    x->x_peaked = 1;\n    x->x_dbage = 0;\n}\n\nvoid sigfiddle_debug(t_sigfiddle *x)\n{\n    x->x_nprint = 1;\n}\n\nvoid sigfiddle_print(t_sigfiddle *x)\n{\n    post(\"npoints %d,\",  2 * x->x_hop);\n    post(\"amp-range %f %f,\",  x->x_amplo, x->x_amphi);\n    post(\"reattack %d %f,\",  x->x_attacktime, x->x_attackthresh);\n    post(\"vibrato %d %f\",  x->x_vibtime, x->x_vibdepth);\n    post(\"npartial %f\",  x->x_npartial);\n    post(\"auto %d\",  x->x_auto);\n}\n\nvoid sigfiddle_amprange(t_sigfiddle *x, t_floatarg amplo, t_floatarg amphi)\n{\n    if (amplo < 0) amplo = 0;\n    if (amphi < amplo) amphi = amplo + 1;\n    x->x_amplo = amplo;\n    x->x_amphi = amphi;\n}\n\nvoid sigfiddle_reattack(t_sigfiddle *x,\n    t_floatarg attacktime, t_floatarg attackthresh)\n{\n    if (attacktime < 0) attacktime = 0;\n    if (attackthresh <= 0) attackthresh = 1000;\n    x->x_attacktime = attacktime;\n    x->x_attackthresh = attackthresh;\n    x->x_attackbins = (x->x_sr * 0.001 * attacktime) / x->x_hop;\n    if (x->x_attackbins >= HISTORY) x->x_attackbins = HISTORY - 1;\n}\n\nvoid sigfiddle_vibrato(t_sigfiddle *x, t_floatarg vibtime, t_floatarg vibdepth)\n{\n    if (vibtime < 0) vibtime = 0;\n    if (vibdepth <= 0) vibdepth = 1000;\n    x->x_vibtime = vibtime;\n    x->x_vibdepth = vibdepth;\n    x->x_vibbins = (x->x_sr * 0.001  * vibtime) / x->x_hop;\n    if (x->x_vibbins >= HISTORY) x->x_vibbins = HISTORY - 1;\n    if (x->x_vibbins < 1) x->x_vibbins = 1;\n}\n\nvoid sigfiddle_npartial(t_sigfiddle *x, t_floatarg npartial)\n{\n    if (npartial < 0.1) npartial = 0.1;\n    x->x_npartial = npartial;\n}\n\nvoid sigfiddle_auto(t_sigfiddle *x, t_floatarg f)\n{\n    x->x_auto = (f != 0);\n}\n\nstatic void sigfiddle_freebird(t_sigfiddle *x)\n{\n    if (x->x_inbuf)\n    {\n        freebytes(x->x_inbuf, sizeof(t_float) * x->x_hop);\n        x->x_inbuf = 0;\n    }\n    if (x->x_lastanalysis)\n    {\n        freebytes(x->x_lastanalysis,\n            sizeof(t_float) * (2 * x->x_hop + 4 * FILTSIZE));\n        x->x_lastanalysis = 0;\n    }\n    if (x->x_spiral)\n    {\n        freebytes(x->x_spiral, sizeof(t_float) * 2 * x->x_hop);\n        x->x_spiral = 0;\n    }\n    x->x_hop = 0;\n}\n\nint sigfiddle_setnpoints(t_sigfiddle *x, t_floatarg fnpoints)\n{\n    int i, npoints = fnpoints;\n    sigfiddle_freebird(x);\n    if (npoints < MINPOINTS || npoints > MAXPOINTS)\n    {\n        pd_error(0, \"fiddle~: npoints out of range; using %d\",\n            npoints = DEFAULTPOINTS);\n    }\n    if (npoints != (1 << sigfiddle_ilog2(npoints)))\n    {\n        pd_error(0, \"fiddle~: npoints not a power of 2; using %d\",\n            npoints = (1 << sigfiddle_ilog2(npoints)));\n    }\n    x->x_hop = npoints >> 1;\n    if (!(x->x_inbuf = (t_float *)getbytes(sizeof(t_float) * x->x_hop)))\n        goto fail;\n    if (!(x->x_lastanalysis = (t_float *)getbytes(\n        sizeof(t_float) * (2 * x->x_hop + 4 * FILTSIZE))))\n            goto fail;\n    if (!(x->x_spiral = (t_float *)getbytes(sizeof(t_float) * 2 * x->x_hop)))\n        goto fail;\n    for (i = 0; i < x->x_hop; i++)\n        x->x_inbuf[i] = 0;\n    for (i = 0; i < npoints + 4 * FILTSIZE; i++)\n        x->x_lastanalysis[i] = 0;\n    for (i = 0; i < x->x_hop; i++)\n        x->x_spiral[2*i] =    cos((3.14159*i)/(npoints)),\n        x->x_spiral[2*i+1] = -sin((3.14159*i)/(npoints));\n    x->x_phase = 0;\n    return (1);\nfail:\n    sigfiddle_freebird(x);\n    return (0);\n}\n\nint sigfiddle_doinit(t_sigfiddle *x, long npoints, long npitch,\n    long npeakanal, long npeakout)\n{\n    t_float *buf1, *buf2,  *buf3;\n    t_peakout *buf4;\n    int i;\n\n    if (!npeakanal && !npeakout) npeakanal = DEFNPEAK, npeakout = 0;\n    if (npeakanal < 0) npeakanal = 0;\n    else if (npeakanal > MAXPEAK) npeakanal = MAXPEAK;\n    if (npeakout < 0) npeakout = 0;\n    else if (npeakout > MAXPEAK) npeakout = MAXPEAK;\n    if (npitch <= 0) npitch = 0;\n    else if (npitch > MAXNPITCH) npitch = MAXNPITCH;\n    if (npeakanal && !npitch) npitch = 1;\n    if (!npoints)\n        npoints = DEFAULTPOINTS;\n    if (!sigfiddle_setnpoints(x, npoints))\n    {\n        pd_error(0, \"fiddle~: out of memory\");\n        return (0);\n    }\n    if (!(buf4 = (t_peakout *)getbytes(sizeof(*buf4) * npeakout)))\n    {\n        sigfiddle_freebird(x);\n        pd_error(0, \"fiddle~: out of memory\");\n        return (0);\n    }\n    for (i = 0; i < npeakout; i++)\n        buf4[i].po_freq = buf4[i].po_amp = 0;\n    x->x_peakbuf = buf4;\n\n    x->x_npeakout = (int)npeakout;\n    x->x_npeakanal = (int)npeakanal;\n    x->x_phase = 0;\n    x->x_histphase = 0;\n    x->x_sr = 44100;            /* this and the next are filled in later */\n    for (i = 0; i < MAXNPITCH; i++)\n    {\n        int j;\n        x->x_hist[i].h_pitch = x->x_hist[i].h_noted = 0;\n        x->x_hist[i].h_age = 0;\n        x->x_hist[i].h_wherefrom = 0;\n        x->x_hist[i].h_outlet = 0;\n        for (j = 0; j < HISTORY; j++)\n            x->x_hist[i].h_amps[j] = x->x_hist[i].h_pitches[j] = 0;\n    }\n    x->x_nprint = 0;\n    x->x_npitch = (int)npitch;\n    for (i = 0; i < HISTORY; i++) x->x_dbs[i] = 0;\n    x->x_dbage = 0;\n    x->x_peaked = 0;\n    x->x_auto = 1;\n    x->x_amplo = DEFAMPLO;\n    x->x_amphi = DEFAMPHI;\n    x->x_attacktime = DEFATTACKTIME;\n    x->x_attackbins = 1;                /* real value calculated afterward */\n    x->x_attackthresh = DEFATTACKTHRESH;\n    x->x_vibtime = DEFVIBTIME;\n    x->x_vibbins = 1;                   /* real value calculated afterward */\n    x->x_vibdepth = DEFVIBDEPTH;\n    x->x_npartial = 7;\n    x->x_attackvalue = 0;\n    return (1);\n}\n\n    /* formalities for JMAX */\n\n#ifdef JMAX\n\nvoid sigfiddle_debug13(fts_object_t *o, int winlet, fts_symbol_t s, int ac, const fts_atom_t *at)\n{\n  t_sigfiddle *x = (t_sigfiddle *)o;\n  sigfiddle_debug(x);\n}\n\nvoid sigfiddle_print13(fts_object_t *o, int winlet, fts_symbol_t s,\n    int ac, const fts_atom_t *at)\n{\n  t_sigfiddle *x = (t_sigfiddle *)o;\n  sigfiddle_print(x);\n}\n\nvoid sigfiddle_amprange13(fts_object_t *o, int winlet, fts_symbol_t s,\n    int ac, const fts_atom_t *at)\n{\n    t_sigfiddle *x = (t_sigfiddle *)o;\n    t_float lo =  (t_float) fts_get_float_arg(ac, at, 0, 0);\n    t_float hi =  (t_float) fts_get_float_arg(ac, at, 1, 0);\n    sigfiddle_amprange(x, lo, hi);\n}\n\nvoid sigfiddle_reattack13(fts_object_t *o, int winlet, fts_symbol_t s,\n    int ac, const fts_atom_t *at)\n{\n    t_sigfiddle *x = (t_sigfiddle *)o;\n    long msec =  fts_get_float_arg(ac, at, 0, 0);\n    t_float db =  (t_float) fts_get_float_arg(ac, at, 1, 0);\n    sigfiddle_reattack(x, msec, db);\n}\n\nvoid sigfiddle_vibrato13(fts_object_t *o, int winlet, fts_symbol_t s,\n    int ac, const fts_atom_t *at)\n{\n    t_sigfiddle *x = (t_sigfiddle *)o;\n    long msec =  fts_get_float_arg(ac, at, 0, 0);\n    t_float halftones =  (t_float) fts_get_float_arg(ac, at, 1, 0);\n    sigfiddle_vibrato(x, msec, halftones);\n}\n\nvoid sigfiddle_npartial13(fts_object_t *o, int winlet, fts_symbol_t s,\n    int ac, const fts_atom_t *at)\n{\n    t_sigfiddle *x = (t_sigfiddle *)o;\n    t_float npartial =  (t_float) fts_get_float_arg(ac, at, 0, 0);\n    sigfiddle_npartial(x, npartial);\n}\n\n\nvoid ftl_sigfiddle(fts_word_t *a)\n{\n    t_sigfiddle *x = (t_sigfiddle *)fts_word_get_long(a);\n    t_float *in = (t_float *)fts_word_get_long(a + 1);\n    long n_tick = fts_word_get_long(a + 2);\n\n    int count;\n    t_float *fp,  *fp2;\n    for (count = 0, fp = x->x_inbuf + x->x_phase;\n            count < n_tick; count++) *fp++ = *in++;\n    if (fp == x->x_inbuf + x->x_hop)\n    {\n        sigfiddle_doit(x);\n        x->x_phase = 0;\n        fts_alarm_set_delay(&x->x_clock, 0L);        /* output bang */\n        fts_alarm_arm(&x->x_clock);\n\n        if (x->x_nprint) x->x_nprint--;\n    }\n    else x->x_phase += n_tick;\n}\n\nvoid sigfiddle_put(fts_object_t *o, int winlet, fts_symbol_t *s, int ac, const fts_atom_t *at)\n{\n    t_sigfiddle *x = (t_sigfiddle *)o;\n    fts_dsp_descr_t *dsp = (fts_dsp_descr_t *)fts_get_long_arg(ac, at, 0, 0);\n    fts_atom_t a[3];\n\n    x->x_sr = fts_dsp_get_input_srate(dsp, 0);\n    sigfiddle_reattack(x, x->x_attacktime, x->x_attackthresh);\n    sigfiddle_vibrato(x, x->x_vibtime, x->x_vibdepth);\n\n    fts_set_long(a, (long)x);\n    fts_set_symbol(a+1, fts_dsp_get_input_name(dsp, 0));\n    fts_set_long(a+2, fts_dsp_get_input_size(dsp, 0));\n    dsp_add_funcall(dsp_symbol, 3, a);\n}\n\nvoid sigfiddle_tick(fts_alarm_t *alarm, void *p)\n{\n    fts_object_t *o = (fts_object_t *)p;\n    t_sigfiddle *x = (t_sigfiddle *)p;\n\n    int i;\n    t_pitchhist *ph;\n    fts_outlet_float(o, OUTLETpower, x->x_dbs[x->x_histphase]);\n    for (i = 0,  ph = x->x_hist; i < x->x_npitch; i++,  ph++)\n    {\n        fts_atom_t at[2];\n        fts_set_float(at, ph->h_pitches[x->x_histphase]);\n        fts_set_float(at+1, ph->h_amps[x->x_histphase]);\n        fts_outlet_list(o, OUTLETmicropitch3 - i, 2, at);\n    }\n    if (x->x_attackvalue) fts_outlet_bang(o, OUTLETattack);\n    for (i = 0,  ph = x->x_hist; i < x->x_npitch; i++,  ph++)\n        if (ph->h_pitch) fts_outlet_float(o, OUTLETpitch, ph->h_pitch);\n}\n\nstatic void sigfiddle_delete(fts_object_t *o, int winlet, fts_symbol_t *s, int ac,\n const fts_atom_t *at)\n{\n  t_sigfiddle *x = (t_sigfiddle *)o;\n\n  fts_free(x->x_inbuf);\n  fts_free(x->x_lastanalysis);\n  fts_free(x->x_spiral);\n  dsp_list_remove(o);\n}\n\nstatic void sigfiddle_init(fts_object_t *o, int winlet, fts_symbol_t *s, int ac, const fts_atom_t *at)\n{\n    t_sigfiddle *x = (t_sigfiddle *)o;\n    t_float *buf1, *buf2,  *buf3;\n    int i, hop;\n    long npoints    = fts_get_long_arg(ac, at, 1, 0);\n    long npitch    = fts_get_long_arg(ac, at, 2, 0);\n    long npeakanal    = fts_get_long_arg(ac, at, 3, 0);\n    long npeakout    = fts_get_long_arg(ac, at, 4, 0);\n\n    if (!sigfiddle_doinit(x, npoints, npitch, npeakanal, npeakout))\n    {\n        post(\"fiddle~: initialization failed\");\n        return;\n    }\n    hop = npoints>>1;\n    if (fts_fft_declaresize(hop) != fts_Success)\n        post(\"fiddle~: bad FFT size\");\n\n    fts_alarm_init(&(x->x_clock), 0, sigfiddle_tick, x);\n    dsp_list_insert(o);\n}\n\nstatic fts_status_t sigfiddle_instantiate(fts_class_t *cl, int ac,\n    const fts_atom_t *at)\n{\n  int i;\n  fts_type_t a[5];\n\n  fts_class_init(cl, sizeof(t_sigfiddle), 1, 6, 0);  /* 1 inlet + 6 outlets */\n\n  /* the system methods */\n\n  a[0] = fts_Symbol;\n  a[1] = fts_Long | fts_OptArg;\n  a[2] = fts_Long | fts_OptArg;\n  fts_method_define(cl, fts_SystemInlet, fts_s_init, sigfiddle_init, 3, a);\n\n  fts_method_define(cl, fts_SystemInlet, fts_s_delete, sigfiddle_delete, 0, a);\n  a[0] = fts_Object;\n  fts_method_define(cl, fts_SystemInlet, fts_s_put, sigfiddle_put, 1, a);\n\n  /* class' own methods */\n  fts_method_define(cl, 0, fts_new_symbol(\"print\"), sigfiddle_print13, 0, a);\n  fts_method_define(cl, 0, fts_new_symbol(\"debug\"), sigfiddle_debug13, 0, a);\n  fts_method_define(cl, 0, fts_new_symbol(\"amp-range\"), sigfiddle_amprange13,\n        0, a);\n  fts_method_define(cl, 0, fts_new_symbol(\"reattack\"), sigfiddle_reattack13,\n        0, a);\n  fts_method_define(cl, 0, fts_new_symbol(\"vibrato\"), sigfiddle_vibrato13,\n        0, a);\n  fts_method_define(cl, 0, fts_new_symbol(\"npartial\"), sigfiddle_npartial13,\n        0, a);\n\n  /* classes signal inlets */\n  dsp_sig_inlet(cl, 0);                    /* declare signal input #0 */\n\n  /* classes outlets */\n  a[0] = fts_Float;\n  fts_outlet_type_define(cl, OUTLETpitch, fts_s_float, 1, a); /* declare outlet #0 */\n  fts_outlet_type_define(cl, OUTLETattack, fts_s_bang, 0, a); /* declare outlet #1 */\n  a[0] = fts_VarArgs;\n  fts_outlet_type_define(cl, OUTLETmicropitch1, fts_s_list, 1, a); /* declare outlet #2 */\n  fts_outlet_type_define(cl, OUTLETmicropitch2, fts_s_list, 1, a); /* declare outlet #3 */\n  fts_outlet_type_define(cl, OUTLETmicropitch3, fts_s_list, 1, a); /* declare outlet #4 */\n  a[0] = fts_Float;\n  fts_outlet_type_define(cl, OUTLETpower, fts_s_float, 1, a); /* declare outlet #5 */\n\n  dsp_symbol = fts_new_symbol(\"fiddle\");\n  dsp_declare_function(dsp_symbol, ftl_sigfiddle);\n\n  /* DSP properties  */\n\n  fts_class_put_prop(cl, fts_s_dsp_is_sink, fts_true);\n\n  return(fts_Success);\n}\n\nvoid fiddle_config(void)\n{\n  sys_log(fiddle_version);\n  fts_metaclass_create(fts_new_symbol(CLASSNAME), sigfiddle_instantiate, fts_always_equiv);\n}\n\nfts_module_t fiddle_module =\n  {\"fiddle\", \"sonic meat fiddle\", fiddle_config, 0};\n\n#endif  /* JMAX */\n\n#ifdef PD\n\nstatic t_int *fiddle_perform(t_int *w)\n{\n    t_float *in = (t_float *)(w[1]);\n    t_sigfiddle *x = (t_sigfiddle *)(w[2]);\n    int n = (int)(w[3]);\n    int count;\n    t_float *fp;\n    if (!x->x_hop)\n        goto nono;\n    for (count = 0, fp = x->x_inbuf + x->x_phase; count < n; count++)\n        *fp++ = *in++;\n    if (fp == x->x_inbuf + x->x_hop)\n    {\n        sigfiddle_doit(x);\n        x->x_phase = 0;\n        if (x->x_auto) clock_delay(x->x_clock, 0L);\n        if (x->x_nprint) x->x_nprint--;\n    }\n    else x->x_phase += n;\nnono:\n    return (w+4);\n}\n\nvoid sigfiddle_dsp(t_sigfiddle *x, t_signal **sp)\n{\n    x->x_sr = sp[0]->s_sr;\n    sigfiddle_reattack(x, x->x_attacktime, x->x_attackthresh);\n    sigfiddle_vibrato(x, x->x_vibtime, x->x_vibdepth);\n    dsp_add(fiddle_perform, 3, sp[0]->s_vec, x, (t_int)sp[0]->s_n);\n}\n\n    /* This is the callback function for the clock, but also acts as\n    the \"bang\" method; you can leave \"auto\" on to get this called\n    automatically (the default) or turn auto off and bang it yourself. */\n\nvoid sigfiddle_bang(t_sigfiddle *x)\n{\n    int i;\n    t_pitchhist *ph;\n    if (x->x_npeakout)\n    {\n        int npeakout = x->x_npeakout;\n        t_peakout *po;\n        for (i = 0, po = x->x_peakbuf; i < npeakout; i++, po++)\n        {\n            t_atom at[3];\n            SETFLOAT(at, i+1);\n            SETFLOAT(at+1, po->po_freq);\n            SETFLOAT(at+2, po->po_amp);\n            outlet_list(x->x_peakout, 0, 3, at);\n        }\n    }\n    outlet_float(x->x_envout, x->x_dbs[x->x_histphase]);\n    for (i = 0,  ph = x->x_hist; i < x->x_npitch; i++,  ph++)\n    {\n        t_atom at[2];\n        SETFLOAT(at, ph->h_pitches[x->x_histphase]);\n        SETFLOAT(at+1, ph->h_amps[x->x_histphase]);\n        outlet_list(ph->h_outlet, 0, 2, at);\n    }\n    if (x->x_attackvalue) outlet_bang(x->x_attackout);\n    for (i = 0,  ph = x->x_hist; i < x->x_npitch; i++,  ph++)\n        if (ph->h_pitch) outlet_float(x->x_noteout, ph->h_pitch);\n}\n\nvoid sigfiddle_ff(t_sigfiddle *x)               /* cleanup on free */\n{\n    if (x->x_inbuf)\n    {\n        freebytes(x->x_inbuf, sizeof(t_float) * x->x_hop);\n        freebytes(x->x_lastanalysis, sizeof(t_float) * (2*x->x_hop + 4 * FILTSIZE));\n        freebytes(x->x_spiral, sizeof(t_float) * 2*x->x_hop);\n        freebytes(x->x_peakbuf, sizeof(*x->x_peakbuf) * x->x_npeakout);\n        clock_free(x->x_clock);\n    }\n}\n\nstatic t_class *sigfiddle_class;\n\nvoid *sigfiddle_new(t_floatarg npoints, t_floatarg npitch,\n    t_floatarg fnpeakanal, t_floatarg fnpeakout)\n{\n    t_sigfiddle *x = (t_sigfiddle *)pd_new(sigfiddle_class);\n    int i;\n    int npeakanal = fnpeakanal, npeakout = fnpeakout;\n\n\n    if (!sigfiddle_doinit(x, npoints, npitch,\n        npeakanal, npeakout))\n    {\n        x->x_inbuf = 0;     /* prevent the free routine from cleaning up */\n        pd_free(&x->x_ob.ob_pd);\n        return (0);\n    }\n    x->x_noteout = outlet_new(&x->x_ob, gensym(\"float\"));\n    x->x_attackout = outlet_new(&x->x_ob, gensym(\"bang\"));\n    for (i = 0; i < x->x_npitch; i++)\n        x->x_hist[i].h_outlet = outlet_new(&x->x_ob, gensym(\"list\"));\n    x->x_envout = outlet_new(&x->x_ob, gensym(\"float\"));\n    if (x->x_npeakout)\n        x->x_peakout = outlet_new(&x->x_ob, gensym(\"list\"));\n    else x->x_peakout = 0;\n    x->x_clock = clock_new(&x->x_ob.ob_pd, (t_method)sigfiddle_bang);\n    return (x);\n}\n\nvoid fiddle_tilde_setup(void)\n{\n    sigfiddle_class = class_new(gensym(\"fiddle~\"), (t_newmethod)sigfiddle_new,\n        (t_method)sigfiddle_ff, sizeof(t_sigfiddle), 0,\n            A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0);\n    class_addmethod(sigfiddle_class, (t_method)sigfiddle_dsp,\n        gensym(\"dsp\"), 0);\n    class_addmethod(sigfiddle_class, (t_method)sigfiddle_debug,\n        gensym(\"debug\"), 0);\n    class_addmethod(sigfiddle_class, (t_method)sigfiddle_setnpoints,\n        gensym(\"npoints\"), A_FLOAT, 0);\n    class_addmethod(sigfiddle_class, (t_method)sigfiddle_amprange,\n        gensym(\"amp-range\"), A_FLOAT, A_FLOAT, 0);\n    class_addmethod(sigfiddle_class, (t_method)sigfiddle_reattack,\n        gensym(\"reattack\"), A_FLOAT, A_FLOAT, 0);\n    class_addmethod(sigfiddle_class, (t_method)sigfiddle_vibrato,\n        gensym(\"vibrato\"), A_FLOAT, A_FLOAT, 0);\n    class_addmethod(sigfiddle_class, (t_method)sigfiddle_npartial,\n        gensym(\"npartial\"), A_FLOAT, 0);\n    class_addmethod(sigfiddle_class, (t_method)sigfiddle_auto,\n        gensym(\"auto\"), A_FLOAT, 0);\n    class_addmethod(sigfiddle_class, (t_method)sigfiddle_print,\n        gensym(\"print\"), 0);\n    class_addmethod(sigfiddle_class, nullfn, gensym(\"signal\"), 0);\n    class_addbang(sigfiddle_class, sigfiddle_bang);\n    class_addcreator((t_newmethod)sigfiddle_new, gensym(\"fiddle\"),\n        A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0);\n    post(fiddle_version);\n}\n\nvoid fiddle_setup(void)\n{\n    fiddle_tilde_setup();\n}\n#endif /* PD */\n\n#ifdef MAX26\n\nvoid cu_fiddle(t_float *in1, t_sigfiddle *x, int n)\n{\n    int count;\n    t_float *fp,  *fp2;\n    for (count = 0, fp = x->x_inbuf + x->x_phase;\n            count < n; count++) *fp++ = *in1++;\n    if (fp == x->x_inbuf + x->x_hop)\n    {\n        sigfiddle_doit(x);\n        x->x_phase = 0;\n        if (x->x_auto) clock_delay(x->x_clock, 0L);\n        if (x->x_nprint) x->x_nprint--;\n    }\n    else x->x_phase += n;\n}\n\nvoid sigfiddle_put(t_sigfiddle *x, long whether)\n{\n    if (whether)\n    {\n        u_stdout(x);\n        x->x_sr = x->x_io[0]->s_sr;\n        sigfiddle_reattack(x, x->x_attacktime, x->x_attackthresh);\n        sigfiddle_vibrato(x, x->x_vibtime, x->x_vibdepth);\n        dspchain_addc(cu_fiddle, 3,\n            x->x_io[0]->s_shit, x, x->x_io[0]->s_n);\n    }\n}\n\nvoid sigfiddle_tick(t_sigfiddle *x)     /* callback function for the clock */\n{\n    int i;\n    t_pitchhist *ph;\n    outlet_float(x->x_envout, x->x_dbs[x->x_histphase]);\n    for (i = 0,  ph = x->x_hist; i < x->x_npitch; i++,  ph++)\n    {\n        t_atom at[2];\n        SETFLOAT(at, ph->h_pitches[x->x_histphase]);\n        SETFLOAT(at+1, ph->h_amps[x->x_histphase]);\n        outlet_list(ph->h_outlet, NIL, 2, at);\n    }\n    if (x->x_attackvalue) outlet_bang(x->x_attackout);\n    for (i = 0,  ph = x->x_hist; i < x->x_npitch; i++,  ph++)\n        if (ph->h_pitch) outlet_float(x->x_noteout, ph->h_pitch);\n}\n\nvoid sigfiddle_ff(t_sigfiddle *x)               /* cleanup on free */\n{\n    if (x->x_inbuf)\n    {\n        freebytes(x->x_inbuf, sizeof(t_float) * x->x_hop);\n        freebytes(x->x_lastanalysis, sizeof(t_float) * (2*x->x_hop + 4 * FILTSIZE));\n        freebytes(x->x_spiral, sizeof(t_float) * 2*x->x_hop);\n        clock_free(x->x_clock);\n        u_clean(x);\n    }\n}\n\nt_externclass *sigfiddle_class;\n\nvoid *sigfiddle_new(long npoints, long npitch,\n    long npeakanal, long npeakout)\n{\n    t_sigfiddle *x = (t_sigfiddle *)obj_new(&sigfiddle_class, 0);\n    int i;\n\n    if (!sigfiddle_doinit(x, npoints, npitch, npeakanal, npeakout))\n    {\n        x->x_inbuf = 0;     /* prevent the free routine from cleaning up */\n        obj_free(x);\n        return (0);\n    }\n    u_setup(x, IN1, OUT0);\n    x->x_envout = outlet_new(x, gensym(\"float\"));\n    for (i = 0; i < x->x_npitch; i++)\n        x->x_hist[i].h_outlet = outlet_new(x, gensym(\"list\"));\n    x->x_attackout = outlet_new(x, gensym(\"bang\"));\n    x->x_noteout = outlet_new(x, gensym(\"float\"));\n    x->x_clock = clock_new(x, sigfiddle_tick);\n    return (x);\n}\n\nvoid fiddle_setup()\n{\n    c_extern(&sigfiddle_class, sigfiddle_new, sigfiddle_ff,\n        gensym(\"fiddle\"), sizeof(t_sigfiddle), 0, A_DEFLONG, A_DEFLONG,\n            A_DEFLONG, A_DEFLONG, 0);\n    c_addmess(sigfiddle_put, gensym(\"put\"), A_CANT, 0);\n    c_addmess(sigfiddle_debug, gensym(\"debug\"), 0);\n    c_addmess(sigfiddle_amprange, gensym(\"amp-range\"), A_FLOAT, A_FLOAT, 0);\n    c_addmess(sigfiddle_reattack, gensym(\"reattack\"), A_FLOAT, A_FLOAT, 0);\n    c_addmess(sigfiddle_vibrato, gensym(\"vibrato\"), A_LONG, A_FLOAT, 0);\n    c_addmess(sigfiddle_npartial, gensym(\"npartial\"), A_FLOAT, 0);\n    c_addmess(sigfiddle_print, gensym(\"print\"), 0);\n    u_inletmethod(0);   /* one signal input */\n#ifdef MAX\n    post(fiddle_version);\n#endif\n}\n\n#endif /* MAX26 */\n\n/************* Beginning of MSP Code ******************************/\n\n#ifdef MSP\n\nstatic t_int *fiddle_perform(t_int *w)\n{\n    t_float *in = (t_float *)(w[1]);\n    t_sigfiddle *x = (t_sigfiddle *)(w[2]);\n    int n = (int)(w[3]);\n    int count,inc = x->x_downsample;\n    t_float *fp;\n\n    if (x->x_obj.z_disabled)\n        goto skip;\n    for (count = 0, fp = x->x_inbuf + x->x_phase; count < n; count+=inc) {\n        *fp++ = *in;\n        in += inc;\n    }\n    if (fp == x->x_inbuf + x->x_hop)\n    {\n                sigfiddle_doit(x);\n                x->x_phase = 0;\n                if (x->x_auto) clock_delay(x->x_clock, 0L);\n                if (x->x_nprint) x->x_nprint--;\n    }\n    else x->x_phase += n;\nskip:\n    return (w+4);\n}\n\nvoid sigfiddle_dsp(t_sigfiddle *x, t_signal **sp)\n{\n     if (sp[0]->s_n > x->x_hop) {\n        x->x_downsample = sp[0]->s_n / x->x_hop;\n        post(\"* warning: fiddle~: will downsample input by %ld\",x->x_downsample);\n        x->x_sr = sp[0]->s_sr / x->x_downsample;\n    } else {\n        x->x_downsample = 1;\n                x->x_sr = sp[0]->s_sr;\n        }\n        sigfiddle_reattack(x, x->x_attacktime, x->x_attackthresh);\n    sigfiddle_vibrato(x, x->x_vibtime, x->x_vibdepth);\n    dsp_add(fiddle_perform, 3, sp[0]->s_vec, x, (t_int)sp[0]->s_n);\n}\n\nvoid sigfiddle_tick(t_sigfiddle *x)     /* callback function for the clock MSP*/\n{\n    int i;\n    t_pitchhist *ph;\n    if (x->x_npeakout)\n    {\n        int npeakout = x->x_npeakout;\n        t_peakout *po;\n        for (i = 0, po = x->x_peakbuf; i < npeakout; i++, po++)\n        {\n                t_atom at[3];\n                SETINT(at, i+1);\n                SETFLOAT(at+1, po->po_freq);\n                SETFLOAT(at+2, po->po_amp);\n                outlet_list(x->x_peakout, 0, 3, at);\n                }\n    }\n    outlet_float(x->x_envout, x->x_dbs[x->x_histphase]);\n    for (i = 0,  ph = x->x_hist; i < x->x_npitch; i++,  ph++)\n    {\n        t_atom at[2];\n        SETFLOAT(at, ph->h_pitches[x->x_histphase]);\n        SETFLOAT(at+1, ph->h_amps[x->x_histphase]);\n        outlet_list(ph->h_outlet, 0, 2, at);\n    }\n    if (x->x_attackvalue) outlet_bang(x->x_attackout);\n    for (i = 0,  ph = x->x_hist; i < x->x_npitch; i++,  ph++)\n        if (ph->h_pitch) outlet_float(x->x_noteout, ph->h_pitch);\n}\n\nvoid sigfiddle_bang(t_sigfiddle *x)             \n{\n    int i;\n    t_pitchhist *ph;\n    if (x->x_npeakout)\n    {\n        int npeakout = x->x_npeakout;\n        t_peakout *po;\n        for (i = 0, po = x->x_peakbuf; i < npeakout; i++, po++)\n        {\n            t_atom at[3];\n            SETLONG(at, i+1);\n            SETFLOAT(at+1, po->po_freq);\n            SETFLOAT(at+2, po->po_amp);\n            outlet_list(x->x_peakout, 0, 3, at);\n        }\n    }\n    outlet_float(x->x_envout, x->x_dbs[x->x_histphase]);\n    for (i = 0,  ph = x->x_hist; i < x->x_npitch; i++,  ph++)\n    {\n        t_atom at[2];\n        SETFLOAT(at, ph->h_pitches[x->x_histphase]);\n        SETFLOAT(at+1, ph->h_amps[x->x_histphase]);\n        outlet_list(ph->h_outlet, 0, 2, at);\n    }\n    if (x->x_attackvalue) outlet_bang(x->x_attackout);\n    for (i = 0,  ph = x->x_hist; i < x->x_npitch; i++,  ph++)\n        if (ph->h_pitch) outlet_float(x->x_noteout, ph->h_pitch);\n}\n\n\nvoid sigfiddle_ff(t_sigfiddle *x)               /* cleanup on free  MSP  */\n{\n\n    if (x->x_inbuf)\n    {\n        t_freebytes(x->x_inbuf, sizeof(t_float) * x->x_hop);\n        t_freebytes(x->x_lastanalysis, sizeof(t_float) * (2*x->x_hop + 4 *\nFILTSIZE));\n        t_freebytes(x->x_spiral, sizeof(t_float) * 2*x->x_hop);\n        t_freebytes(x->x_peakbuf, sizeof(*x->x_peakbuf) * x->x_npeakout);\n    }\n     dsp_free((t_pxobject *)x);\n}\n\nvoid *sigfiddle_class;\n\nvoid *sigfiddle_new(long npoints, long npitch,\n    long npeakanal, long npeakout)\n{\n    t_sigfiddle *x = (t_sigfiddle *)newobject(sigfiddle_class);\n    int i;\n\n    if (!sigfiddle_doinit(x, npoints, npitch, npeakanal, npeakout))\n    {\n        x->x_inbuf = 0;     /* prevent the free routine from cleaning up */\n        return (0);\n    }\n    dsp_setup((t_pxobject *)x,1);\n\n    x->x_clock = clock_new(x, (method)sigfiddle_tick);\n     if (x->x_npeakout)\n        x->x_peakout = listout((t_object *)x);\n    else x->x_peakout = 0;\n    x->x_envout = floatout((t_object *)x);\n    for (i = 0; i < x->x_npitch; i++)\n                x->x_hist[i].h_outlet = listout((t_object *)x);\n        x->x_attackout = bangout((t_object *)x);\n        x->x_noteout = floatout((t_object *)x);\n          return (x);\n\n\n}\n\nvoid main()\n{\n        setup(&sigfiddle_class, sigfiddle_new, (method)sigfiddle_ff,\n                (short)sizeof(t_sigfiddle), 0L, A_DEFLONG, A_DEFLONG,\nA_DEFLONG, A_DEFLONG, 0);\n        addmess((method)sigfiddle_dsp,          \"dsp\",\n        A_CANT, 0);\n    addmess((method)sigfiddle_debug,    \"debug\",                0);\n    addmess((method)sigfiddle_setnpoints, \"npoints\",    A_FLOAT, 0);\n    addmess((method)sigfiddle_amprange, \"amp-range\",    A_FLOAT, A_FLOAT, 0);\n    addmess((method)sigfiddle_reattack, \"reattack\",     A_FLOAT, A_FLOAT, 0);\n    addmess((method)sigfiddle_vibrato,  \"vibrato\",              A_FLOAT,\nA_FLOAT, 0);\n    addmess((method)sigfiddle_npartial, \"npartial\",     A_FLOAT, 0);\n    addmess((method)sigfiddle_auto,             \"auto\",\n        A_FLOAT, 0);\n    addmess((method)sigfiddle_print,    \"print\",                0);\n        addmess((method)sigfiddle_assist,       \"assist\",\n        A_CANT, 0);\n        addbang((method)sigfiddle_bang);\n    dsp_initclass();\n    rescopy('STR#',3748);\n    post(fiddle_version);\n}\n\nvoid sigfiddle_assist(t_sigfiddle *x, void *b, long m, long a, char *s)\n{\n        assist_string(3748,m,a,1,2,s);\n}\n\nvoid msp_fft(t_float *buf, long np, long inv)\n{\n        t_float *src,*real,*rp,*imag,*ip;\n        long i;\n\n        /*\n        // because this fft algorithm uses separate real and imaginary\n        // buffers\n        // we must split the real and imaginary parts into two buffers,\n        // then do the opposite on output\n        // a more ambitious person would either do an in-place conversion\n        // or rewrite the fft algorithm\n        */\n    \n        real = rp = msp_ffttemp;\n        imag = ip = real + MAXPOINTS;\n        src = buf;\n        for (i = 0; i < np; i++) {\n                *rp++ = *src++;\n                *ip++ = *src++;\n        }\n        if (inv)\n                ifft(np,real,imag);\n        else\n                fft(np,real,imag);\n        rp = real;\n        ip = imag;\n        src = buf;\n        for (i = 0; i < np; i++) {\n                *src++ = *rp++;\n                *src++ = *ip++;\n        }\n}\n\n#endif /* MSP */\n"
  },
  {
    "path": "libs/libpd/pure-data/extra/loop~/loop~.c",
    "content": "/* loop~ -- loop generator for sampling */\n\n/*  Copyright 1997-1999 Miller Puckette.\nPermission is granted to use this software for any purpose provided you\nkeep this copyright notice intact.\n\nTHE AUTHOR AND HIS EMPLOYERS MAKE NO WARRANTY, EXPRESS OR IMPLIED,\nIN CONNECTION WITH THIS SOFTWARE.\n\nThis file is downloadable from http://www.crca.ucsd.edu/~msp .\n\n*/\n\n#ifdef PD\n#include \"m_pd.h\"\n#else\n#define t_sample float\n#define t_float float\n#endif\n\n\n\ntypedef struct _loopctl\n{\n    double l_phase;\n    t_sample l_invwindow;\n    t_sample l_window;\n    int l_resync;\n} t_loopctl;\n\nstatic void loopctl_run(t_loopctl *x, t_sample *transposein,\n        t_sample *windowin, t_sample *rawout, t_sample *windowout, int n)\n{\n    t_sample window, invwindow;\n    double phase = x->l_phase;\n    if (x->l_resync)\n    {\n        window = *windowin;\n        if (window < 0)\n        {\n            if (window > -1)\n                window = -1;\n            invwindow = -1/window;\n        }\n        else\n        {\n            if (window < 1)\n                window = 1;\n            invwindow = 1/window;\n        }\n        x->l_resync = 0;\n    }\n    else\n    {\n        window = x->l_window;\n        phase = x->l_phase;\n        invwindow = x->l_invwindow;\n    }\n    while (n--)\n    {\n        double phaseinc = invwindow * *transposein++;\n        double newphase;\n        t_sample nwind = *windowin++;\n        if (phaseinc >= 1 || phaseinc < 0)\n            phaseinc = 0;\n        newphase = phase + phaseinc;\n        if (newphase >= 1)\n        {\n            window = nwind;\n            if (window < 0)\n            {\n                if (window > -1)\n                    window = -1;\n                invwindow = -1/window;\n            }\n            else\n            {\n                if (window < 1)\n                    window = 1;\n                invwindow = 1/window;\n            }\n            newphase -= 1.;\n        }\n        phase = newphase;\n        *rawout++ = (t_sample)phase;\n        *windowout++ = window;\n    }\n    x->l_invwindow = invwindow;\n    x->l_window = window;\n    x->l_phase = phase;\n}\n\nstatic void loopctl_init(t_loopctl *x)\n{\n    x->l_window = 1;\n    x->l_invwindow = 1;\n    x->l_phase = 0;\n}\n\nstatic void loopctl_set(t_loopctl *x, t_float val)\n{\n    if (val < 0 || val > 1)\n        val = 0;\n    x->l_phase = val;\n    x->l_resync = 1;\n}\n\n#ifdef PD\n\ntypedef struct _loop\n{\n    t_object x_obj;\n    t_float x_f;\n    t_loopctl x_loopctl;\n} t_loop;\n\nstatic t_class *loop_class;\n\nstatic void *loop_new(void)\n{\n    t_loop *x = (t_loop *)pd_new(loop_class);\n    loopctl_init(&x->x_loopctl);\n    inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);\n    outlet_new(&x->x_obj, gensym(\"signal\"));\n    outlet_new(&x->x_obj, gensym(\"signal\"));\n    return (x);\n}\n\nstatic t_int *loop_perform(t_int *w)\n{\n    t_loopctl *ctl = (t_loopctl *)(w[1]);\n    t_sample *in1 = (t_sample *)(w[2]);\n    t_sample *in2 = (t_sample *)(w[3]);\n    t_sample *out1 = (t_sample *)(w[4]);\n    t_sample *out2 = (t_sample *)(w[5]);\n    int n = (int)(w[6]);\n    loopctl_run(ctl, in1, in2, out1, out2, n);\n    return (w+7);\n}\n\nstatic void loop_dsp(t_loop *x, t_signal **sp)\n{\n    dsp_add(loop_perform, 6,\n        &x->x_loopctl, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec,\n            (t_int)sp[0]->s_n);\n}\n\nstatic void loop_set(t_loop *x, t_floatarg val)\n{\n    loopctl_set(&x->x_loopctl, val);\n}\n\nstatic void loop_bang(t_loop *x)\n{\n    loopctl_set(&x->x_loopctl, 0);\n}\n\nvoid loop_tilde_setup(void)\n{\n    loop_class = class_new(gensym(\"loop~\"), (t_newmethod)loop_new, 0,\n        sizeof(t_loop), 0, 0);\n    class_addmethod(loop_class, (t_method)loop_dsp, gensym(\"dsp\"), A_CANT, 0);\n    CLASS_MAINSIGNALIN(loop_class, t_loop, x_f);\n    class_addmethod(loop_class, (t_method)loop_set, gensym(\"set\"),\n        A_DEFFLOAT, 0);\n    class_addbang(loop_class, loop_bang);\n}\n\n#endif /* PD */\n"
  },
  {
    "path": "libs/libpd/pure-data/extra/lrshift~/lrshift~.c",
    "content": "#include \"m_pd.h\"\n\n/* ------------------------ lrshift~ ----------------------------- */\n\nstatic t_class *lrshift_tilde_class;\n\ntypedef struct _lrshift_tilde\n{\n    t_object x_obj;\n    int x_n;\n    t_float x_f;\n} t_lrshift_tilde;\n\nstatic t_int *leftshift_perform(t_int *w)\n{\n    t_sample *in = (t_sample *)(w[1]);\n    t_sample *out= (t_sample *)(w[2]);\n    int n = (int)(w[3]);\n    int shift = (int)(w[4]);\n    in += shift;\n    n -= shift;\n    while (n--)\n        *out++ = *in++;\n    while (shift--)\n        *out++ = 0;\n    return (w+5);\n}\n\nstatic t_int *rightshift_perform(t_int *w)\n{\n    t_sample *in = (t_sample *)(w[1]);\n    t_sample *out= (t_sample *)(w[2]);\n    int n = (int)(w[3]);\n    int shift = (int)(w[4]);\n    n -= shift;\n    in -= shift;\n    while (n--)\n        *--out = *--in;\n    while (shift--)\n        *--out = 0;\n    return (w+5);\n}\n\nstatic void lrshift_tilde_dsp(t_lrshift_tilde *x, t_signal **sp)\n{\n    int n = sp[0]->s_length, i;\n    int shift = x->x_n;\n    if (shift > n)\n        shift = n;\n    if (shift < -n)\n        shift = -n;\n    signal_setmultiout(&sp[1], sp[0]->s_nchans);\n    for (i = 0; i < sp[0]->s_nchans; i++)\n    {\n        if (shift < 0)\n            dsp_add(rightshift_perform, 4,  sp[0]->s_vec + i*n + n,\n                sp[1]->s_vec + i*n + n, (t_int)n, (t_int)(-shift));\n        else dsp_add(leftshift_perform, 4, sp[0]->s_vec + i*n,\n            sp[1]->s_vec + i*n, (t_int)n, (t_int)shift);\n    }\n}\n\nstatic void *lrshift_tilde_new(t_floatarg f)\n{\n    t_lrshift_tilde *x = (t_lrshift_tilde *)pd_new(lrshift_tilde_class);\n    x->x_n = f;\n    x->x_f = 0;\n    outlet_new(&x->x_obj, gensym(\"signal\"));\n    return (x);\n}\n\nvoid lrshift_tilde_setup(void)\n{\n    lrshift_tilde_class = class_new(gensym(\"lrshift~\"),\n        (t_newmethod)lrshift_tilde_new, 0, sizeof(t_lrshift_tilde),\n            CLASS_MULTICHANNEL, A_DEFFLOAT, 0);\n    CLASS_MAINSIGNALIN(lrshift_tilde_class, t_lrshift_tilde, x_f);\n    class_addmethod(lrshift_tilde_class, (t_method)lrshift_tilde_dsp,\n        gensym(\"dsp\"), 0);\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/extra/pd~/binarymsg.c",
    "content": "#ifdef MAX\n#define A_PDFLOAT 1\n#define A_PDSYMBOL 2\n#define A_PDSEMI 4\n#else\n#define A_PDFLOAT A_FLOAT\n#define A_PDSYMBOL A_SYMBOL\n#define A_PDSEMI A_SEMI\n#endif\n\nstatic void pd_tilde_putfloat(float f, FILE *fd)\n{\n    putc(A_PDFLOAT, fd);\n    fwrite(&f, sizeof(f), 1, fd);\n}\n\nstatic void pd_tilde_putsymbol(t_symbol *s, FILE *fd)\n{\n    const char *sp = s->s_name;\n    putc(A_PDSYMBOL, fd);\n    do\n        putc(*sp, fd);\n    while (*sp++);\n}\n\nstatic void pd_tilde_putsemi(FILE *fd)\n{\n    putc(A_PDSEMI, fd);\n}\n\nstatic int pd_tilde_getatom(t_atom *ap, FILE *fd)\n{\n    char buf[MAXPDSTRING];\n    while (1)\n    {\n        int type = getc(fd), fill;\n        float f;\n        switch (type)\n        {\n        case EOF:\n            return (0);\n        case A_PDSEMI:\n            SETSEMI(ap);\n            return (1);\n        case A_PDFLOAT:\n            if (fread(&f, sizeof(f), 1, fd) >= 1)\n            {\n                SETFLOAT(ap, f);\n                return (1);\n            }\n            else return (0);\n        case A_PDSYMBOL:\n            for (fill = 0; fill < MAXPDSTRING; fill++)\n            {\n                int c = getc(fd);\n                if (c == EOF)\n                    return (0);\n                else buf[fill] = c;\n                if (!c)\n                {\n                    SETSYMBOL(ap, gensym(buf));\n                    return (1);\n                }\n            }\n            return (0);\n        }\n    }\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/extra/pd~/notes.txt",
    "content": "pd -schedlib `pwd`/pdsched\n\ndolist:\npd~ to delay starting subproc until asked\nfigure out about setting nchannels from command line\nfix maximum nchannels in and out\n\n\n"
  },
  {
    "path": "libs/libpd/pure-data/extra/pd~/pdsched.c",
    "content": "/* Copyright 2008 Miller Puckette.  Berkeley license; see the\nfile LICENSE.txt in this distribution. */\n\n/* A plug-in scheduler that turns Pd into a filter that inputs and\noutputs audio and messages. */\n\n/* todo: \n    fix schedlib code to use extent2\n    figure out about  if (sys_externalschedlib) { return; } in s_audio.c\n    make buffer size ynamically growable\n\n*/    \n#include \"m_pd.h\"\n#include \"s_stuff.h\"\n#include \"m_imp.h\"\n#include <stdio.h>\n#ifdef _WIN32\n#include <io.h>\n#include <fcntl.h>\n#endif\n\n#include \"binarymsg.c\"\n\n#if PD_WATCHDOG\nvoid glob_watchdog(t_pd *dummy);\n\nstatic void pollwatchdog( void)\n{\n    static int sched_diddsp, sched_nextpingtime;\n    sched_diddsp++;\n    if (!sys_havegui() && sys_hipriority &&\n        (sched_diddsp - sched_nextpingtime > 0))\n    {\n        glob_watchdog(0);\n            /* ping every 2 seconds */\n        sched_nextpingtime = sched_diddsp +\n            2 * (int)(STUFF->st_dacsr /(double)STUFF->st_schedblocksize);\n    }\n}\n#else /* ! PD_WATCHDOG */\nstatic void pollwatchdog( void)\n{\n    /* dummy */\n}\n#endif /* PD_WATCHDOG */\n\nstatic t_class *pd_ambinary_class;\n#define BUFSIZE 65536\nstatic char *ascii_inbuf;\n\nstatic int readasciimessage(t_binbuf *b)\n{\n    int fill = 0, c;\n\n    binbuf_clear(b);\n    while ((c = getchar()) != EOF)\n    {\n        if (c == ';')\n        {\n            binbuf_text(b, ascii_inbuf, fill);\n            return (1);\n        }\n        else if (fill < BUFSIZE)\n            ascii_inbuf[fill++] = c;\n        else if (fill == BUFSIZE)\n            fprintf(stderr, \"pd-extern: input buffer overflow\\n\");\n    }\n    return (0);\n}\n\nstatic int readbinmessage(t_binbuf *b)\n{\n    binbuf_clear(b);\n    while (1)\n    {\n        t_atom at;\n        if (!pd_tilde_getatom(&at, stdin))\n        {\n            fprintf(stderr, \"pd-extern: EOF on input\\n\");\n            return (0);\n        }\n        else if (at.a_type == A_SEMI)\n            return (1);\n        else binbuf_add(b, 1, &at);\n    }\n}\n\nint pd_extern_sched(char *flags)\n{\n    int i, j, chin, chout, fill = 0, c, useascii = 0;\n    t_binbuf *b = binbuf_new();\n    t_audiosettings as;\n\n    sys_get_audio_settings(&as);\n    as.a_api = API_NONE;\n    sys_set_audio_settings(&as);\n\n    chin = (as.a_nindev < 1 ? 0 : as.a_chindevvec[0]);\n    chout = (as.a_noutdev < 1 ? 0 : as.a_choutdevvec[0]);\n    if (!flags || flags[0] != 'a')\n    {\n            /* signal to stdout object to do binary by attaching an object\n            to an obscure symbol name */\n        pd_ambinary_class = class_new(gensym(\"pd~\"), 0, 0, sizeof(t_pd),\n            CLASS_PD, 0);\n        pd_bind(&pd_ambinary_class, gensym(\"#pd_binary_stdio\"));\n            /* On Windows, set stdin and out to \"binary\" mode */\n#ifdef _WIN32\n        setmode(fileno(stdout),O_BINARY);\n        setmode(fileno(stdin),O_BINARY);\n#endif\n    }\n    else\n    {\n        if (!(ascii_inbuf = getbytes(BUFSIZE)))\n            return (1);\n        useascii = 1;\n    }\n    /* fprintf(stderr, \"Pd plug-in scheduler called, chans %d %d, sr %d\\n\",\n        chin, chout, (int)rate); */\n    sys_setchsr(chin, chout, as.a_srate);\n    while (useascii ? readasciimessage(b) : readbinmessage(b) )\n    {\n        t_atom *ap = binbuf_getvec(b);\n        int n = binbuf_getnatom(b);\n        if (n > 0 && ap[0].a_type == A_FLOAT)\n        {\n            /* a list -- take it as incoming signals. */\n            int chan, nchan = n/DEFDACBLKSIZE;\n            t_sample *fp;\n            for (i = chan = 0, fp = STUFF->st_soundin; chan < nchan; chan++)\n                for (j = 0; j < DEFDACBLKSIZE; j++)\n                    *fp++ = atom_getfloat(ap++);\n            for (; chan < chin; chan++)\n                for (j = 0; j < DEFDACBLKSIZE; j++)\n                    *fp++ = 0;\n            sched_tick();\n            sys_pollgui();\n            pollwatchdog();\n            if (useascii)\n                printf(\";\\n\");\n            else putchar(A_SEMI);\n            for (i = chout*DEFDACBLKSIZE, fp = STUFF->st_soundout; i--;\n                fp++)\n            {\n                if (useascii)\n                    printf(\"%g\\n\", *fp);\n                else pd_tilde_putfloat(*fp, stdout);\n                *fp = 0;\n            }\n            if (useascii)\n                printf(\";\\n\");\n            else putchar(A_SEMI);\n            fflush(stdout);\n        }\n        else if (n > 1 && ap[0].a_type == A_SYMBOL)\n        {\n            t_pd *whom = ap[0].a_w.w_symbol->s_thing;\n            if (!whom)\n                pd_error(0, \"%s: no such object\", ap[0].a_w.w_symbol->s_name);\n            else if (ap[1].a_type == A_SYMBOL)\n                typedmess(whom, ap[1].a_w.w_symbol, n-2, ap+2);\n            else pd_list(whom, 0, n-1, ap+1);\n        }\n    }\n    binbuf_free(b);\n    return (0);\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/extra/pd~/pd~.c",
    "content": "/*\n  pd~.c - embed a Pd process within Pd or Max.\n\n  Copyright 2008 Miller Puckette\n  BSD license; see README.txt in this distribution for details.\n*/\n\n/* this is here so that we don't have to do anything in the MACOS tool\nchain to define MSP.  I'm sure it's easy but life's too short to waste\nlearning XCode.  */\n#if !defined(PD) && !defined(MSP)\n#define MSP\n#endif\n\n#ifdef _WIN32\n#include <io.h>\n#include <fcntl.h>\n#include <process.h>\n#include <windows.h>\ntypedef int socklen_t;\n#else\n#include <string.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <errno.h>\n#include <ctype.h>\n#include <sys/wait.h>\n#include <fcntl.h>\n#include <signal.h>\n#endif\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <stdio.h>\n\n#ifdef _MSC_VER\n#pragma warning (disable: 4305 4244)\n#define snprintf _snprintf\n#define stat _stat\n#endif\n\n#ifdef _WIN32\n# define PIPE(p) _pipe(p, 65536, O_BINARY | O_NOINHERIT)\n#else\n# define PIPE(p) pipe(p)\n#endif\n\n\n#ifdef MSP\n#include \"ext.h\"\n#include \"z_dsp.h\"\n#include \"math.h\"\n#include \"ext_mess.h\"\n#include \"ext_support.h\"\n#include \"ext_proto.h\"\n#include \"ext_obex.h\"\n\ntypedef float t_float;\ntypedef float t_pdsample;\n#define PD_FLOATSIZE 32\n#define w_symbol w_sym\n#define A_SYMBOL A_SYM\n#define PDERROR error(\nvoid *pd_tilde_class;\n#define MAXPDSTRING 4096\n#define DEFDACBLKSIZE 64\n\ntypedef struct _binbuf\n{\n    int b_n;\n    t_atom *b_vec;\n} t_binbuf;\n#define SETSEMI(atom) ((atom)->a_type = A_SEMI, (atom)->a_w.w_long = 0)\n#define SETCOMMA(atom) ((atom)->a_type = A_COMMA, (atom)->a_w.w_long = 0)\n#define SETFLOAT(atom, f) ((atom)->a_type = A_FLOAT, (atom)->a_w.w_float = (f))\n#define SETSYMBOL(atom, s) ((atom)->a_type = A_SYMBOL, \\\n    (atom)->a_w.w_symbol = (s))\n\nvoid critical_enter(int z);\nvoid critical_exit(int z);\n\n#else   /* not MSP  */\n#define t_pdsample t_sample\n#endif /* MSP */\n\n#ifdef PD\n#include \"m_pd.h\"\n#include \"s_stuff.h\"\nstatic t_class *pd_tilde_class;\n#define PDERROR pd_error(x,\n#endif\n\n#if defined(__x86_64__) || defined(_M_X64)\n# define ARCHEXT \"amd64\"\n#elif defined(__i386__) || defined(_M_IX86)\n# define ARCHEXT \"i386\"\n#elif defined(__arm__)\n# define ARCHEXT \"arm\"\n#elif defined(__aarch64__)\n# define ARCHEXT \"arm64\"\n#elif defined(__ppc__)\n# define ARCHEXT \"ppc\"\n#endif\n\n#ifdef ARCHEXT\n#define ARCHDLLEXT(prefix) prefix ARCHEXT ,\n#else\n#define ARCHDLLEXT(prefix)\n#endif\n\n\nstatic const char *pd_tilde_dllextent[] = {\n#if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__GNU__)\n    ARCHDLLEXT(\".l_\")\n    \".pd_linux\",\n#elif defined(__APPLE__)\n    ARCHDLLEXT(\".d_\")\n    \".d_fat\",\n    \".pd_darwin\",\n#elif defined(_WIN32) || defined(__CYGWIN__)\n    ARCHDLLEXT(\".m_\")\n#endif\n        /* and some generic extensions */\n#if defined(_WIN32) || defined(__CYGWIN__)\n    \".dll\",\n#else\n    \".so\",\n#endif\n    0};\n\n\nstatic const char**get_dllextent()\n{\n#if PD\n    const char**dllextent = sys_get_dllextensions();\n    if(dllextent && *dllextent)\n        return dllextent;\n#endif\n    return pd_tilde_dllextent;\n}\n\n\n#include \"binarymsg.c\"\n\n/* ------------------------ pd_tilde~ ----------------------------- */\n\n#define MSGBUFSIZE 65536\n\ntypedef struct _pd_tilde\n{\n#ifdef PD\n    t_object x_obj;\n    t_clock *x_clock;\n    t_outlet *x_outlet1;        /* for messages back from subproc */\n    t_canvas *x_canvas;\n#endif /* PD */\n#ifdef MSP\n    t_pxobject x_obj;\n    void *x_outlet1;\n    void *x_clock;\n    int x_clockisset;\n    t_pdsample *x_sampbuf;\n#endif /* MSP */\n    FILE *x_infd;\n    FILE *x_outfd;\n    t_binbuf *x_binbuf;\n    int x_childpid;\n    int x_ninsig;\n    int x_noutsig;\n    int x_fifo;\n    int x_binary;\n    t_float x_sr;\n    t_symbol *x_pddir;\n    t_symbol *x_schedlibdir;\n    t_pdsample **x_insig;\n    t_pdsample **x_outsig;\n    int x_blksize;\n} t_pd_tilde;\n\n#ifdef MSP\nstatic void *pd_tilde_new(t_symbol *s, long ac, t_atom *av);\nstatic void pd_tilde_dsp64(t_pd_tilde *x, t_object *dsp64, short *count,\n    double samplerate, long maxvectorsize, long flags);\nvoid pd_tilde_assist(t_pd_tilde *x, void *b, long m, long a, char *s);\nstatic void pd_tilde_free(t_pd_tilde *x);\nvoid pd_tilde_setup(void);\nvoid ext_main( void *r);\nvoid pd_tilde_minvel_set(t_pd_tilde *x, void *attr, long ac, t_atom *av);\n\n#define binbuf_new pdbinbuf_new\n#undef binbuf_free\n#define binbuf_free pdbinbuf_free\n#define binbuf_clear pdbinbuf_clear\n#define binbuf_resize pdbinbuf_resize\n#define binbuf_add pdbinbuf_add\n#define binbuf_text pdbinbuf_text\n#define binbuf_getnatom pdbinbuf_getnatom\n#define binbuf_getvec pdbinbuf_getvec\n\nt_binbuf *binbuf_new(void)\n{\n    t_binbuf *x = (t_binbuf *)t_getbytes(sizeof(*x));\n    x->b_n = 0;\n    x->b_vec = t_getbytes(0);\n    return (x);\n}\n\nvoid binbuf_free(t_binbuf *x)\n{\n    t_freebytes(x->b_vec, x->b_n * sizeof(*x->b_vec));\n    t_freebytes(x,  sizeof(*x));\n}\n\nvoid binbuf_clear(t_binbuf *x)\n{\n    x->b_vec = t_resizebytes((char *)x->b_vec, x->b_n * sizeof(*x->b_vec), 0);\n    x->b_n = 0;\n}\n\nint binbuf_resize(t_binbuf *x, int newsize)\n{\n    t_atom *new = (t_atom *)t_resizebytes((char *)x->b_vec,\n        x->b_n * sizeof(*x->b_vec), newsize * sizeof(*x->b_vec));\n    if (new)\n        x->b_vec = new, x->b_n = newsize;\n    return (new != 0);\n}\n\nvoid binbuf_add(t_binbuf *x, int argc, const t_atom *argv)\n{\n    int previoussize = x->b_n;\n    int newsize = previoussize + argc, i;\n    t_atom *ap;\n\n    if (!binbuf_resize(x, newsize))\n    {\n        PDERROR \"binbuf_addmessage: out of space\");\n        return;\n    }\n    for (ap = x->b_vec + previoussize, i = argc; i--; ap++)\n        *ap = *(argv++);\n}\n\n/* convert text to a binbuf */\nvoid binbuf_text(t_binbuf *x, const char *text, size_t size)\n{\n    char buf[MAXPDSTRING+1], *bufp, *ebuf = buf+MAXPDSTRING;\n    const char *textp = text, *etext = text+size;\n    t_atom *ap;\n    int nalloc = 16, natom = 0;\n    binbuf_clear(x);\n    if (!binbuf_resize(x, nalloc)) return;\n    ap = x->b_vec;\n    while (1)\n    {\n\n        /* skip leading space */\n        while ((textp != etext) && (*textp == ' ' || *textp == '\\n'\n            || *textp == '\\r' || *textp == '\\t'))\n                textp++;\n        if (textp == etext) break;\n        if (*textp == ';') SETSEMI(ap), textp++;\n        else if (*textp == ',') SETCOMMA(ap), textp++;\n        else\n        {\n            /* it's an atom other than a comma or semi */\n            char c;\n            int floatstate = 0, slash = 0, lastslash = 0, dollar = 0;\n            bufp = buf;\n            do\n            {\n                c = *bufp = *textp++;\n                lastslash = slash;\n                slash = (c == '\\\\');\n\n                if (floatstate >= 0)\n                {\n                    int digit = (c >= '0' && c <= '9'),\n                    dot = (c == '.'), minus = (c == '-'),\n                    plusminus = (minus || (c == '+')),\n                    expon = (c == 'e' || c == 'E');\n                    if (floatstate == 0)    /* beginning */\n                    {\n                        if (minus) floatstate = 1;\n                        else if (digit) floatstate = 2;\n                        else if (dot) floatstate = 3;\n                        else floatstate = -1;\n                    }\n                    else if (floatstate == 1)   /* got minus */\n                    {\n                        if (digit) floatstate = 2;\n                        else if (dot) floatstate = 3;\n                        else floatstate = -1;\n                    }\n                    else if (floatstate == 2)   /* got digits */\n                    {\n                        if (dot) floatstate = 4;\n                        else if (expon) floatstate = 6;\n                        else if (!digit) floatstate = -1;\n                    }\n                    else if (floatstate == 3)   /* got '.' without digits */\n                    {\n                        if (digit) floatstate = 5;\n                        else floatstate = -1;\n                    }\n                    else if (floatstate == 4)   /* got '.' after digits */\n                    {\n                        if (digit) floatstate = 5;\n                        else if (expon) floatstate = 6;\n                        else floatstate = -1;\n                    }\n                    else if (floatstate == 5)   /* got digits after . */\n                    {\n                        if (expon) floatstate = 6;\n                        else if (!digit) floatstate = -1;\n                    }\n                    else if (floatstate == 6)   /* got 'e' */\n                    {\n                        if (plusminus) floatstate = 7;\n                        else if (digit) floatstate = 8;\n                        else floatstate = -1;\n                    }\n                    else if (floatstate == 7)   /* got plus or minus */\n                    {\n                        if (digit) floatstate = 8;\n                        else floatstate = -1;\n                    }\n                    else if (floatstate == 8)   /* got digits */\n                    {\n                        if (!digit) floatstate = -1;\n                    }\n                }\n                if (!lastslash && c == '$' && (textp != etext &&\n                    textp[0] >= '0' && textp[0] <= '9'))\n                        dollar = 1;\n                if (!slash) bufp++;\n                else if (lastslash)\n                {\n                    bufp++;\n                    slash = 0;\n                }\n            }\n            while (textp != etext && bufp != ebuf &&\n                (slash || (*textp != ' ' && *textp != '\\n' && *textp != '\\r'\n                    && *textp != '\\t' &&*textp != ',' && *textp != ';')));\n            *bufp = 0;\n            if (floatstate == 2 || floatstate == 4 || floatstate == 5 ||\n                floatstate == 8)\n                SETFLOAT(ap, atof(buf));\n            /* LATER try to figure out how to mix \"$\" and \"\\$\" correctly;\n             here, the backslashes were already stripped so we assume all\n             \"$\" chars are real dollars.  In fact, we only know at least one\n             was. */\n            else if (dollar)\n            {\n                if (buf[0] != '$')\n                    dollar = 0;\n                for (bufp = buf+1; *bufp; bufp++)\n                    if (*bufp < '0' || *bufp > '9')\n                        dollar = 0;\n                    SETFLOAT(ap, 0);\n            }\n            else SETSYMBOL(ap, gensym(buf));\n        }\n        ap++;\n        natom++;\n        if (natom == nalloc)\n        {\n            if (!binbuf_resize(x, nalloc*2)) break;\n            nalloc = nalloc * 2;\n            ap = x->b_vec + natom;\n        }\n        if (textp == etext) break;\n    }\n    /* reallocate the vector to exactly the right size */\n    binbuf_resize(x, natom);\n}\n\nint binbuf_getnatom(const t_binbuf *x)\n{\n    return (x->b_n);\n}\n\nt_atom *binbuf_getvec(const t_binbuf *x)\n{\n    return (x->b_vec);\n}\nvoid sys_bashfilename(char *tmpbuf, char *outbuf)\n{\n    strcpy(outbuf, tmpbuf);\n}\n\n#if 0  /* stuff for debugging */\nstatic FILE *barf_fd;\n\nvoid barf(const char *s)\n{\n    if (!barf_fd)\n        barf_fd = fopen(\"/tmp/foo.txt\", \"w\");\n    if (barf_fd)\n    {\n        fprintf(barf_fd, \"%s\\n\", s);\n        fflush(barf_fd);\n    }\n}\n\nvoid barf1(const char *s, int n1)\n{\n    char buf2[1000];\n    sprintf(buf2, s, n1);\n    barf(buf2);\n}\n\n\nvoid barf2(const char *s, int n1, int n2)\n{\n    char buf2[1000];\n    sprintf(buf2, s, n1, n2);\n    barf(buf2);\n}\n\n#ifdef getc     /* if debugging is on, copy all getc() output to /tmp/foo.txt */\n#undef getc\n#endif\n\nint barf_getc(FILE *fd)\n{\n    int poodle = getc(fd);\n#if 0\n    if (poodle < 0)\n        barf(\"barf_getc oops\");\n    else\n    {\n        if (!barf_fd)\n            barf_fd = fopen(\"/tmp/foo.txt\", \"w\");\n        if (barf_fd)\n            putc(poodle, barf_fd);\n    }\n#endif\n    return (poodle);\n}\n\n#define getc barf_getc\n\n#endif /* debugging */\n\n#endif /* MAX */\n\nstatic void pd_tilde_close(t_pd_tilde *x)\n{\n#ifdef _WIN32\n    int termstat;\n#endif\n    FILE *infd = x->x_infd, *outfd = x->x_outfd;\n    x->x_infd = x->x_outfd = 0;\n    if (outfd)\n        fclose(outfd);\n    if (infd)\n        fclose(infd);\n    if (x->x_childpid > 0)\n#ifdef _WIN32\n        _cwait(&termstat, x->x_childpid, WAIT_CHILD);\n#else\n        waitpid(x->x_childpid, 0, 0);\n#endif\n    binbuf_clear(x->x_binbuf);\n    x->x_infd = x->x_outfd = 0;\n    x->x_childpid = -1;\n}\n\nstatic int pd_tilde_readmessages(t_pd_tilde *x, FILE *infd)\n{\n    t_atom at;\n    if (x->x_binary)\n    {\n        int nonempty = 0;\n        while (1)\n        {\n            if (!pd_tilde_getatom(&at, infd))\n                return 0;\n            if (!nonempty && at.a_type == A_SEMI)\n                break;\n            nonempty = (at.a_type != A_SEMI);\n            binbuf_add(x->x_binbuf, 1, &at);\n        }\n    }\n    else    /* ASCII */\n    {\n        t_binbuf *tmpb = binbuf_new();\n        while (1)\n        {\n            char msgbuf[MAXPDSTRING];\n            int c, infill = 0, n;\n            t_atom *vec;\n            while (isspace((c = getc(infd))) && c != EOF)\n                ;\n            if (c == EOF)\n                return 0;\n            do\n                msgbuf[infill++] = c;\n            while (!isspace((c = getc(infd))) && c != ';' && c != EOF\n                && infill < MAXPDSTRING-1) ;\n            if (c == ';' && infill < MAXPDSTRING-1)\n                msgbuf[infill++] = c;\n            binbuf_text(tmpb, msgbuf, infill);\n            n = binbuf_getnatom(tmpb);\n            vec = binbuf_getvec(tmpb);\n            binbuf_add(x->x_binbuf, n, vec);\n            if (!n)\n            {\n                post(\"bug: pd~\");\n                break;  /* shouldn't happen */\n            }\n            if (vec[0].a_type == A_SEMI)\n                break;\n        }\n        binbuf_free(tmpb);\n    }\n#ifdef MAX\n        /* in Max, since control and DSP may be asynchronous, we might\n        already be set.  If so, re-setting can just delay things further. */\n    if (!x->x_clockisset)\n    {\n        x->x_clockisset = 1;\n        clock_delay(x->x_clock, 0);\n    }\n#else\n    clock_delay(x->x_clock, 0);\n#endif\n    return (1);\n}\n\n#define FIXEDARG 13\n#define MAXARG 100\n#ifdef _WIN32\n#define EXTENT \".exe\"\n#else\n#define EXTENT \"\"\n#endif\n\n    /* only call this if we're not already running (x->x_infd = 0, etc.) */\nstatic void pd_tilde_dostart(t_pd_tilde *x, const char *pddir,\n    const char *schedlibdir, const char *patchdir_c, int argc, t_atom *argv,\n    int ninsig, int noutsig, int fifo, t_float samplerate)\n{\n    int i, pid, pipe1[2], pipe2[2];\n    FILE *infd, *outfd;\n    char cmdbuf[MAXPDSTRING], pdexecbuf[MAXPDSTRING], schedbuf[MAXPDSTRING],\n        tmpbuf[MAXPDSTRING], patchdir[MAXPDSTRING];\n    char *execargv[FIXEDARG+MAXARG+1], ninsigstr[20], noutsigstr[20],\n        sampleratestr[40];\n    const char**dllextent;\n    struct stat statbuf;\n    x->x_childpid = -1;\n    if (argc > MAXARG)\n    {\n        post(\"pd~: args truncated to %d items\", MAXARG);\n        argc = MAXARG;\n    }\n    sprintf(ninsigstr, \"%d\", ninsig);\n    sprintf(noutsigstr, \"%d\", noutsig);\n    sprintf(sampleratestr, \"%f\", (float)samplerate);\n    snprintf(tmpbuf, MAXPDSTRING, \"%s/bin/pd\" EXTENT, pddir);\n    sys_bashfilename(tmpbuf, pdexecbuf);\n    if (stat(pdexecbuf, &statbuf) < 0)\n    {\n        snprintf(tmpbuf, MAXPDSTRING, \"%s/../../../bin/pd\" EXTENT, pddir);\n        sys_bashfilename(tmpbuf, pdexecbuf);\n        if (stat(pdexecbuf, &statbuf) < 0)\n        {\n            snprintf(tmpbuf, MAXPDSTRING, \"%s/pd\" EXTENT, pddir);\n            sys_bashfilename(tmpbuf, pdexecbuf);\n            if (stat(pdexecbuf, &statbuf) < 0)\n            {\n                PDERROR \"pd~: can't stat %s\", pdexecbuf);\n                goto fail1;\n            }\n        }\n    }\n        /* check that the scheduler dynamic linkable exists w either suffix */\n    for(dllextent=get_dllextent(); *dllextent; dllextent++)\n    {\n      snprintf(tmpbuf, MAXPDSTRING, \"%s/pdsched%s\", schedlibdir, *dllextent);\n      sys_bashfilename(tmpbuf, schedbuf);\n      if (stat(schedbuf, &statbuf) >= 0)\n        goto gotone;\n    }\n    PDERROR \"pd~: can't stat %s\", schedbuf);\n    goto fail1;\n\ngotone:\n        /* but the sub-process wants the scheduler name without the suffix */\n    snprintf(tmpbuf, MAXPDSTRING, \"%s/pdsched\", schedlibdir);\n    sys_bashfilename(tmpbuf, schedbuf);\n    /* was: snprintf(cmdbuf, MAXPDSTRING,\n\"'%s' -schedlib '%s'/pdsched -path '%s' -inchannels %d -outchannels %d -r \\\n%g %s\\n\",\n        pdexecbuf, schedlibdir, patchdir, ninsig, noutsig, samplerate, pdargs);\n        */\n    snprintf(cmdbuf, MAXPDSTRING, \"%s\", pdexecbuf);\n#ifdef _WIN32\n    /* _spawnv wants the command without quotes as in cmdbuf above;\n        but in the argument vector paths must be quoted if they contain\n        whitespace */\n    if (strchr(pdexecbuf, ' ') && *pdexecbuf != '\"' && *pdexecbuf != '\\'')\n    {\n        if (snprintf(tmpbuf, MAXPDSTRING, \"\\\"%s\\\"\", pdexecbuf) >= 0)\n            snprintf(pdexecbuf, MAXPDSTRING, \"%s\", tmpbuf);\n    }\n    if (strchr(schedbuf, ' ') && *schedbuf != '\"' && *schedbuf != '\\'')\n    {\n        if (snprintf(tmpbuf, MAXPDSTRING, \"\\\"%s\\\"\", schedbuf) >= 0)\n            snprintf(schedbuf, MAXPDSTRING, \"%s\", tmpbuf);\n    }\n    if (strchr(patchdir_c, ' ') && *patchdir_c != '\"' && *patchdir_c != '\\'')\n        snprintf(patchdir, MAXPDSTRING, \"\\\"%s\\\"\", patchdir_c);\n    else\n#endif /* _WIN32 */\n        snprintf(patchdir, MAXPDSTRING, \"%s\", patchdir_c);\n\n    execargv[0] = pdexecbuf;\n    execargv[1] = \"-schedlib\";\n    execargv[2] = schedbuf;\n    execargv[3] = \"-extraflags\";\n    execargv[4] = (x->x_binary ? \"b\" : \"a\");\n    execargv[5] = \"-path\";\n    execargv[6] = patchdir;\n    execargv[7] = \"-inchannels\";\n    execargv[8] = ninsigstr;\n    execargv[9] = \"-outchannels\";\n    execargv[10] = noutsigstr;\n    execargv[11] = \"-r\";\n    execargv[12] = sampleratestr;\n\n        /* convert atom arguments to strings (temporarily allocating space) */\n    for (i = 0; i < argc; i++)\n    {\n#ifdef PD\n        if (argv[i].a_type == A_SYMBOL)\n            snprintf(tmpbuf, MAXPDSTRING, \"%s\", argv[i].a_w.w_symbol->s_name);\n        else if (argv[i].a_type == A_FLOAT)\n            sprintf(tmpbuf,  \"%f\", (float)argv[i].a_w.w_float);\n#endif\n#ifdef MSP\n            /* because Mac pathnames sometimes have an evil preceding\n            colon character, we test for and silently eat them */\n        if (argv[i].a_type == A_SYM)\n            strncpy(tmpbuf, (*argv[i].a_w.w_sym->s_name == ':'?\n                argv[i].a_w.w_sym->s_name+1 : argv[i].a_w.w_sym->s_name),\n                MAXPDSTRING-3);\n        else if (argv[i].a_type == A_LONG)\n            sprintf(tmpbuf, \"%ld\", (long)argv[i].a_w.w_long);\n        else if (argv[i].a_type == A_FLOAT)\n            sprintf(tmpbuf,  \"%f\", (float)argv[i].a_w.w_float);\n#endif\n#ifdef _WIN32\n            /* and now, for Windows (whether Max or Pd), spaces need quotes */\n        if (strchr(tmpbuf, ' ') && *tmpbuf != '\"' && *tmpbuf != '\\'')\n        {\n            char nutherbuf[MAXPDSTRING];\n            snprintf(nutherbuf, MAXPDSTRING, \"\\\"%s\\\"\", tmpbuf);\n            snprintf(tmpbuf, MAXPDSTRING, \"%s\", nutherbuf);\n        }\n#endif /* _WIN32 */\n        execargv[FIXEDARG+i] = malloc(strlen(tmpbuf) + 1);\n        strcpy(execargv[FIXEDARG+i], tmpbuf);\n    }\n    execargv[argc+FIXEDARG] = 0;\n#if 0\n    for (i = 0; i < argc+FIXEDARG; i++)\n        post(\"arg %d = %s\", i, execargv[i]);\n#endif\n    if (PIPE(pipe1) < 0)\n    {\n        PDERROR \"pd~: can't create pipe\");\n        goto fail1;\n    }\n    if (PIPE(pipe2) < 0)\n    {\n        PDERROR \"pd~: can't create pipe\");\n        goto fail2;\n    }\n#ifdef _WIN32\n    {\n        int stdinwas = _dup(0), stdoutwas = _dup(1);\n        if (pipe2[1] == 0)\n            pipe2[1] = _dup(pipe2[1]);\n        if (pipe1[0] != 0)\n            _dup2(pipe1[0], 0);\n        if (pipe2[1] != 1)\n            _dup2(pipe2[1], 1);\n        pid = _spawnv(P_NOWAIT, cmdbuf, (const char * const *)execargv);\n        if (pid < 0)\n        {\n            post(\"%s: couldn't start subprocess (%s)\\n\", execargv[0],\n                strerror(errno));\n            goto fail1;\n        }\n        _dup2(stdinwas, 0);\n        _dup2(stdoutwas, 1);\n        _close(stdinwas);\n        _close(stdoutwas);\n    }\n#else /* _WIN32 */\n    if ((pid = fork()) < 0)\n    {\n        PDERROR \"pd~: can't fork\");\n        goto fail3;\n    }\n    else if (pid == 0)\n    {\n        /* child process */\n            /* the first dup2 below would bash pipe2[1] if it happens to be\n                zero so in that case renumber it */\n        if (pipe2[1] == 0)\n            pipe2[1] = dup(pipe2[1]);\n        if (pipe1[0] != 0)\n        {\n            dup2(pipe1[0], 0);\n            close(pipe1[0]);\n        }\n        if (pipe2[1] != 1)\n        {\n            dup2(pipe2[1], 1);\n            close(pipe2[1]);\n        }\n        if (pipe1[1] >= 2)\n            close(pipe1[1]);\n        if (pipe2[0] >= 2)\n            close(pipe2[0]);\n        execv(cmdbuf, execargv);\n        _exit(1);\n    }\n    for (i=FIXEDARG; execargv[i]; i++)\n        free(execargv[i]);\n\n#endif /* _WIN32 */\n        /* done with fork/exec or spawn; parent continues here */\n    close(pipe1[0]);\n    close(pipe2[1]);\n#ifndef _WIN32      /* this was done in windows via the O_NOINHERIT flag */\n    fcntl(pipe1[1],  F_SETFD, FD_CLOEXEC);\n    fcntl(pipe2[0],  F_SETFD, FD_CLOEXEC);\n#endif\n    outfd = fdopen(pipe1[1], \"w\");\n    infd = fdopen(pipe2[0], \"r\");\n    x->x_childpid = pid;\n    for (i = 0; i < fifo; i++)\n        if (x->x_binary)\n    {\n        pd_tilde_putsemi(outfd);\n        pd_tilde_putfloat(0, outfd);\n        pd_tilde_putsemi(outfd);\n    }\n    else fprintf(outfd, \"%s\", \";\\n0;\\n\");\n\n    fflush(outfd);\n    binbuf_clear(x->x_binbuf);\n    pd_tilde_readmessages(x, infd);\n    x->x_outfd = outfd;\n    x->x_infd = infd;\n    return;\n#ifndef _WIN32\nfail3:\n    close(pipe2[0]);\n    close(pipe2[1]);\n    if (x->x_childpid > 0)\n    waitpid(x->x_childpid, 0, 0);\n#endif\nfail2:\n    close(pipe1[0]);\n    close(pipe1[1]);\nfail1:\n    x->x_infd = x->x_outfd = 0;\n    x->x_childpid = -1;\n    post(\"pd~ startup failed\");\n    return;\n}\n\n/* #define TOSSIN */\n#ifdef TOSSIN\n#include <sys/select.h>\n\nvoid pd_empty(int infd)\n{\n    char buf[10000];\n    fd_set readset, writeset, exceptset;\n    FD_ZERO(&writeset);\n    FD_ZERO(&readset);\n    FD_ZERO(&exceptset);\n    FD_SET(infd, &readset);\n    if (select(infd+1, &readset, &writeset, &exceptset, 0) >= 0 &&\n        FD_ISSET(infd, &readset))\n            read(infd, buf, 10000);\n}\n#endif\n\nstatic int nperfed = 0;\n\nstatic void pd_tilde_doperf(t_pd_tilde *x)\n{\n    int n = x->x_blksize, i, j, nsigs, numbuffill = 0, c;\n    char numbuf[80];\n    if (n > DEFDACBLKSIZE)\n        n = DEFDACBLKSIZE;\n#ifdef MAX\n    critical_enter(0);\n#endif\n    if (!x->x_infd)\n        goto zeroit;\n    if (x->x_binary)\n    {\n        pd_tilde_putsemi(x->x_outfd);\n        if (!x->x_ninsig)\n            pd_tilde_putfloat(0, x->x_outfd);\n        else for (i = 0; i < x->x_ninsig; i++)\n        {\n            t_pdsample *fp = x->x_insig[i];\n            for (j = 0; j < n; j++)\n                pd_tilde_putfloat(*fp++, x->x_outfd);\n            for (; j < DEFDACBLKSIZE; j++)\n                pd_tilde_putfloat(0, x->x_outfd);\n        }\n        pd_tilde_putsemi(x->x_outfd);\n    }\n    else\n    {\n        fprintf(x->x_outfd, \";\\n\");\n        if (!x->x_ninsig)\n            fprintf(x->x_outfd, \"0\\n\");\n        else for (i = 0; i < x->x_ninsig; i++)\n        {\n            t_pdsample *fp = x->x_insig[i];\n            for (j = 0; j < n; j++)\n                fprintf(x->x_outfd, \"%g\\n\", *fp++);\n            for (; j < DEFDACBLKSIZE; j++)\n                fprintf(x->x_outfd, \"0\\n\");\n        }\n        fprintf(x->x_outfd, \";\\n\");\n    }\n    fflush(x->x_outfd);\n    nsigs = j = 0;\n    if (x->x_binary)\n    {\n        while (1)\n        {\n            t_atom at;\n            if (!pd_tilde_getatom(&at, x->x_infd))\n            {\n                if (errno)\n                    PDERROR \"pd~: %s\", strerror(errno));\n                else PDERROR \"pd~: subprocess exited\");\n                pd_tilde_close(x);\n                goto zeroit;\n            }\n            if (at.a_type == A_SEMI)\n                break;\n            else if (at.a_type == A_FLOAT)\n            {\n                if (nsigs < x->x_noutsig)\n                    x->x_outsig[nsigs][j] = at.a_w.w_float;\n                if (++j >= DEFDACBLKSIZE)\n                    j = 0, nsigs++;\n            }\n            else PDERROR \"pd~: subprocess returned malformed audio\");\n        }\n    }\n    else\n    {\n        while (1)\n        {\n            while (1)\n            {\n                c = getc(x->x_infd);\n                if (c == EOF)\n                {\n                    if (errno)\n                        PDERROR \"pd~: %s\", strerror(errno));\n                    else PDERROR \"pd~: subprocess exited\");\n                    pd_tilde_close(x);\n                    goto zeroit;\n                }\n                else if (!isspace(c) && c != ';')\n                {\n                    if (numbuffill < (80-1))\n                        numbuf[numbuffill++] = c;\n                }\n                else\n                {\n                    t_pdsample z;\n                    if (numbuffill)\n                    {\n                        numbuf[numbuffill] = 0;\n#if PD_FLOATSIZE == 32\n                        if (sscanf(numbuf, \"%f\", &z) < 1)\n#else\n                        if (sscanf(numbuf, \"%lf\", &z) < 1)\n#endif\n                            continue;\n                        if (nsigs < x->x_noutsig)\n                            x->x_outsig[nsigs][j] = z;\n                        if (++j >= DEFDACBLKSIZE)\n                            j = 0, nsigs++;\n                    }\n                    numbuffill = 0;\n                    break;\n                }\n            }\n            /* message terminated */\n            if (c == ';')\n                break;\n        }\n    }\n    if (nsigs < x->x_noutsig)\n        post(\"pd~: short audio signals (sigs %d, fragment %d)\", nsigs, j);\n    for (; nsigs < x->x_noutsig; nsigs++, j = 0)\n    {\n        for (; j < x->x_blksize; j++)\n            x->x_outsig[nsigs][j] = 0;\n    }\n    if (!pd_tilde_readmessages(x, x->x_infd))\n    {\n        if (errno)\n            PDERROR \"pd~: %s\", strerror(errno));\n        else PDERROR \"pd~: subprocess exited\");\n        pd_tilde_close(x);\n    }\n#ifdef MAX\n    critical_exit(0);\n#endif\n    return;\nzeroit:\n#ifdef MAX\n    critical_exit(0);\n#endif\n    for (i = 0; i < x->x_noutsig; i++)\n    {\n        for (j = 0; j < x->x_blksize; j++)\n            x->x_outsig[i][j] = 0;\n    }\n}\n\nstatic void pd_tilde_pdtilde(t_pd_tilde *x, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    t_symbol *sel = ((argc > 0 && argv->a_type == A_SYMBOL) ?\n        argv->a_w.w_symbol : gensym(\"?\")), *schedlibdir;\n    const char *patchdir;\n\n    if (sel == gensym(\"start\"))\n    {\n        char pdargstring[MAXPDSTRING];\n#ifdef MAX\n    critical_enter(0);\n#endif\n        if (x->x_infd)\n            pd_tilde_close(x);\n#ifdef MAX\n    critical_exit(0);\n#endif\n        pdargstring[0] = 0;\n        argc--; argv++;\n#ifdef PD\n        patchdir = canvas_getdir(x->x_canvas)->s_name;\n#endif\n#ifdef MSP\n        patchdir = \".\";\n#endif\n        schedlibdir = x->x_schedlibdir;\n        if (schedlibdir == gensym(\".\") && x->x_pddir != gensym(\".\"))\n        {\n            const char *pds = x->x_pddir->s_name;\n            char scheddirstring[MAXPDSTRING];\n            int l = strlen(pds);\n            if (l >= 4 && (!strcmp(pds+l-3, \"bin\") || !strcmp(pds+l-4, \"bin/\")))\n                snprintf(scheddirstring, MAXPDSTRING, \"%s/../extra/pd~\", pds);\n            else snprintf(scheddirstring, MAXPDSTRING, \"%s/extra/pd~\", pds);\n            schedlibdir = gensym(scheddirstring);\n        }\n        pd_tilde_dostart(x, x->x_pddir->s_name, schedlibdir->s_name,\n            patchdir, argc, argv, x->x_ninsig, x->x_noutsig, x->x_fifo,\n                x->x_sr);\n    }\n    else if (sel == gensym(\"stop\"))\n    {\n#ifdef MAX\n    critical_enter(0);\n#endif\n        if (x->x_infd)\n            pd_tilde_close(x);\n#ifdef MAX\n    critical_exit(0);\n#endif\n    }\n    else if (sel == gensym(\"pddir\"))\n    {\n        if ((argc > 1) && argv[1].a_type == A_SYMBOL)\n        {\n            t_symbol *sym = argv[1].a_w.w_symbol;\n#ifdef MSP\n            if (sym->s_name[0] == ':')\n                sym = gensym(s->s_name+1);\n#endif\n            x->x_pddir = sym;\n        }\n        else PDERROR \"pd~ pddir: needs symbol argument\");\n    }\n    else PDERROR \"pd~: unknown control message: %s\", sel->s_name);\n}\n\nstatic void pd_tilde_free(t_pd_tilde *x)\n{\n    pd_tilde_close(x);\n    clock_free(x->x_clock);\n    t_freebytes(x->x_insig, x->x_ninsig * sizeof(*x->x_insig));\n    t_freebytes(x->x_outsig, x->x_noutsig * sizeof(*x->x_outsig));\n#ifdef MSP\n    if (x->x_sampbuf)\n        free(x->x_sampbuf);\n    dsp_free((t_pxobject *)x);\n#endif\n}\n\n/* -------------------------- Pd glue ------------------------- */\n#ifdef PD\n\nstatic t_int *pd_tilde_perform(t_int *w)\n{\n    t_pd_tilde *x = (t_pd_tilde *)(w[1]);\n    pd_tilde_doperf(x);\n    return (w+2);\n}\n\nstatic void pd_tilde_dsp(t_pd_tilde *x, t_signal **sp)\n{\n    int i;\n    t_pdsample **g;\n\n    x->x_blksize = (x->x_ninsig || x->x_noutsig ? sp[0]->s_n : 1);\n    for (i = 0, g = x->x_insig; i < x->x_ninsig; i++, g++)\n        *g = (*(sp++))->s_vec;\n        /* if there were no input signals Pd still provided us with one,\n        which we ignore: */\n    if (!x->x_ninsig)\n        sp++;\n    for (i = 0, g = x->x_outsig; i < x->x_noutsig; i++, g++)\n        *g = (*(sp++))->s_vec;\n    dsp_add(pd_tilde_perform, 1, x);\n}\n\nstatic void pd_tilde_tick(t_pd_tilde *x)\n{\n    int messstart = 0, i, n;\n    t_atom *vec;\n    /* binbuf_print(b); */\n    n = binbuf_getnatom(x->x_binbuf);\n    vec = binbuf_getvec(x->x_binbuf);\n    for (i = 0; i < n; i++)\n    {\n        if (vec[i].a_type == A_SEMI)\n        {\n            if (i > messstart && vec[messstart].a_type == A_SYMBOL)\n                outlet_anything(x->x_outlet1, vec[messstart].a_w.w_symbol,\n                    i-(messstart+1), vec+(messstart+1));\n            else if (i > messstart)\n                outlet_list(x->x_outlet1, 0, i-messstart, vec+messstart);\n            messstart = i+1;\n        }\n    }\n    binbuf_clear(x->x_binbuf);\n}\n\nstatic void pd_tilde_anything(t_pd_tilde *x, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    char msgbuf[MAXPDSTRING];\n    if (!x->x_outfd)\n        return;\n    if (x->x_binary)\n    {\n        pd_tilde_putsymbol(s, x->x_outfd);\n        for (; argc--; argv++)\n        {\n            if (argv->a_type == A_FLOAT)\n                pd_tilde_putfloat(argv->a_w.w_float, x->x_outfd);\n            else if (argv->a_type == A_SYMBOL)\n                pd_tilde_putsymbol(argv->a_w.w_symbol, x->x_outfd);\n        }\n        putc(A_SEMI, x->x_outfd);\n    }\n    else\n    {\n        fprintf(x->x_outfd, \"%s \", s->s_name);\n        while (argc--)\n        {\n            atom_string(argv++, msgbuf, MAXPDSTRING);\n            fprintf(x->x_outfd, \"%s \", msgbuf);\n        }\n        fprintf(x->x_outfd, \";\\n\");\n    }\n}\n\nstatic void *pd_tilde_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_pd_tilde *x = (t_pd_tilde *)pd_new(pd_tilde_class);\n    int ninsig = 2, noutsig = 2, j, fifo = 5, binary = 1;\n    t_float sr = sys_getsr();\n    t_pdsample **g;\n    t_symbol *pddir = sys_libdir,\n        *scheddir = gensym(class_gethelpdir(pd_tilde_class));\n    while (argc > 0)\n    {\n        t_symbol *firstarg = atom_getsymbolarg(0, argc, argv);\n        if (!strcmp(firstarg->s_name, \"-sr\") && argc > 1)\n        {\n            sr = atom_getfloatarg(1, argc, argv);\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(firstarg->s_name, \"-ninsig\") && argc > 1)\n        {\n            ninsig = atom_getfloatarg(1, argc, argv);\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(firstarg->s_name, \"-noutsig\") && argc > 1)\n        {\n            noutsig = atom_getfloatarg(1, argc, argv);\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(firstarg->s_name, \"-fifo\") && argc > 1)\n        {\n            fifo = atom_getfloatarg(1, argc, argv);\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(firstarg->s_name, \"-pddir\") && argc > 1)\n        {\n            pddir = atom_getsymbolarg(1, argc, argv);\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(firstarg->s_name, \"-scheddir\") && argc > 1)\n        {\n            scheddir = atom_getsymbolarg(1, argc, argv);\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(firstarg->s_name, \"-ascii\"))\n        {\n            binary = 0;\n            argc--; argv++;\n        }\n        else break;\n    }\n\n    if (argc)\n    {\n        pd_error(x,\n\"usage: pd~ [-sr #] [-ninsig #] [-noutsig #] [-fifo #] [-pddir <>]\");\n        post(\n\"... [-scheddir <>]\");\n    }\n\n    x->x_clock = clock_new(x, (t_method)pd_tilde_tick);\n    x->x_insig = (t_pdsample **)t_getbytes(ninsig * sizeof(*x->x_insig));\n    x->x_outsig = (t_pdsample **)t_getbytes(noutsig * sizeof(*x->x_outsig));\n    x->x_ninsig = ninsig;\n    x->x_noutsig = noutsig;\n    x->x_blksize = DEFDACBLKSIZE;\n    x->x_fifo = fifo;\n    x->x_sr = sr;\n    x->x_pddir = pddir;\n    x->x_schedlibdir = scheddir;\n    x->x_infd = 0;\n    x->x_outfd = 0;\n    x->x_childpid = -1;\n    x->x_canvas = canvas_getcurrent();\n    x->x_binbuf = binbuf_new();\n    x->x_binary = binary;\n    for (j = 1, g = x->x_insig; j < ninsig; j++, g++)\n        inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);\n    x->x_outlet1 = outlet_new(&x->x_obj, 0);\n    for (j = 0, g = x->x_outsig; j < noutsig; j++, g++)\n        outlet_new(&x->x_obj, &s_signal);\n#ifndef _WIN32\n    signal(SIGPIPE, SIG_IGN);\n#endif\n    return (x);\n}\n\nvoid pd_tilde_setup(void)\n{\n    pd_tilde_class = class_new(gensym(\"pd~\"), (t_newmethod)pd_tilde_new,\n        (t_method)pd_tilde_free, sizeof(t_pd_tilde), 0, A_GIMME, 0);\n    class_addmethod(pd_tilde_class, nullfn, gensym(\"signal\"), 0);\n    class_addmethod(pd_tilde_class, (t_method)pd_tilde_dsp, gensym(\"dsp\"),\n        A_CANT, 0);\n    class_addmethod(pd_tilde_class, (t_method)pd_tilde_pdtilde, gensym(\"pd~\"),\n        A_GIMME, 0);\n    class_addanything(pd_tilde_class, pd_tilde_anything);\n    post(\"pd~ version 0.54\");\n}\n#endif\n\n/* -------------------------- Max/MSP glue ------------------------- */\n#ifdef MSP\n\nstatic void pd_tilde_perf64(t_pd_tilde *x, t_object *dsp64,\n    double **ins, long numins, double **outs, long numouts,\n    long sampleframes, long flags, void *userparam)\n{\n    int i, j, nin = (numins < x->x_ninsig ? numins : x->x_ninsig),\n        nout = (numouts < x->x_noutsig ? numouts : x->x_noutsig);\n\n    if (sampleframes > DEFDACBLKSIZE)\n        sampleframes = DEFDACBLKSIZE;   /* LATER iterate through them */\n    x->x_blksize = sampleframes;\n\n    for (i = 0; i < nin; i++)\n        for (j = 0; j < sampleframes; j++)\n            x->x_insig[i][j] = ins[i][j];\n     for (; i < x->x_ninsig; i++)\n        for (j = 0; j < sampleframes; j++)\n            x->x_insig[i][j] = 0;\n    pd_tilde_doperf(x);\n    for (i = 0; i < nout; i++)\n        for (j = 0; j < sampleframes; j++)\n            outs[i][j] = x->x_outsig[i][j];\n    for (; i < numouts; i++)\n        for (j = 0; j < sampleframes; j++)\n            outs[i][j] = 0;\n    nperfed++;\n}\n\nstatic void pd_tilde_dsp64(t_pd_tilde *x, t_object *dsp64,\n    short *count, double samplerate, long maxvectorsize, long flags)\n{\n    int i;\n    t_pdsample *sp;\n\n    if (x->x_sampbuf)\n        free(x->x_sampbuf);\n    x->x_sampbuf = malloc((x->x_ninsig + x->x_noutsig) *\n        DEFDACBLKSIZE * sizeof(t_pdsample));\n    for (i = 0, sp = x->x_sampbuf; i < x->x_ninsig; i++)\n        x->x_insig[i] = sp, sp += DEFDACBLKSIZE;\n    for (i = 0; i < x->x_noutsig; i++)\n        x->x_outsig[i] = sp, sp += DEFDACBLKSIZE;\n    object_method(dsp64, gensym(\"dsp_add64\"), x, pd_tilde_perf64, 0, 0);\n}\n\nstatic void pd_tilde_tick(t_pd_tilde *x)\n{\n    int messstart = 0, i, n = 0;\n    t_atom *vec;\n    t_binbuf *b;\n\n    critical_enter(0);\n    b = x->x_binbuf;\n    x->x_binbuf = binbuf_new();\n    critical_exit(0);\n    n = binbuf_getnatom(b);\n    vec = binbuf_getvec(b);\n    for (i = 0; i < n; i++)\n    {\n        if (vec[i].a_type == A_SEMI)\n        {\n            if (i > messstart)\n            {\n                if (vec[messstart].a_type == A_SYM)\n                    outlet_anything(x->x_outlet1, vec[messstart].a_w.w_sym,\n                        i-messstart-1, vec+(messstart+1));\n                else if (vec[messstart].a_type == A_FLOAT && i == messstart+1)\n                    outlet_float(x->x_outlet1, vec[messstart].a_w.w_float);\n                else if (vec[messstart].a_type == A_LONG && i == messstart+1)\n                    outlet_int(x->x_outlet1, vec[messstart].a_w.w_long);\n                else outlet_list(x->x_outlet1, gensym(\"list\"),\n                    i-messstart, vec+(messstart));\n            }\n            messstart = i+1;\n        }\n    }\n    binbuf_free(b);\n    x->x_clockisset = 0;\n}\n\nstatic void pd_tilde_anything(t_pd_tilde *x, t_symbol *s, long ac, t_atom *av)\n{\n    if (!x->x_outfd)\n        return;\n    if (x->x_binary)\n    {\n        critical_enter(0);\n        pd_tilde_putsymbol(s, x->x_outfd);\n        while (ac--)\n        {\n            switch (av->a_type)\n            {\n            case A_FLOAT:\n                pd_tilde_putfloat(av->a_w.w_float, x->x_outfd);\n                break;\n            case A_LONG:\n                pd_tilde_putfloat(av->a_w.w_long, x->x_outfd);\n                break;\n            case A_SYM:\n                pd_tilde_putsymbol(av->a_w.w_sym, x->x_outfd);\n                break;\n            }\n            av++;\n        }\n        pd_tilde_putsemi(x->x_outfd);\n        critical_exit(0);\n    }\n    else\n    {\n        char msgbuf[MAXPDSTRING], *sp, *ep = msgbuf+MAXPDSTRING;\n        msgbuf[0] = 0;\n        strncpy(msgbuf, s->s_name, MAXPDSTRING);\n        msgbuf[MAXPDSTRING-1] = 0;\n        sp = msgbuf + strlen(msgbuf);\n        while (ac--)\n        {\n            if (sp < ep-1)\n                sp[0] = ' ', sp[1] = 0, sp++;\n            if (sp < ep - 80)\n            {\n                if (av->a_type == A_SYM && strlen(av->a_w.w_sym->s_name)\n                    < ep - sp-20)\n                        strcpy(sp, av->a_w.w_sym->s_name);\n                else if (av->a_type == A_LONG)\n                    sprintf(sp, \"%ld\" , (long)av->a_w.w_long);\n                else if (av->a_type == A_FLOAT)\n                    sprintf(sp, \"%g\" , (t_float)av->a_w.w_float);\n            }\n            sp += strlen(sp);\n            av++;\n        }\n        critical_enter(0);\n        fprintf(x->x_outfd, \"%s;\\n\", msgbuf);\n        critical_exit(0);\n    }\n}\n\nvoid ext_main( void *r)\n{\n    t_class *c;\n\n    c = class_new(\"pd~\", (method)pd_tilde_new, (method)pd_tilde_free,\n        sizeof(t_pd_tilde), (method)0L, A_GIMME, 0);\n\n    class_addmethod(c, (method)pd_tilde_dsp64, \"dsp64\", A_CANT, 0);\n    class_addmethod(c, (method)pd_tilde_assist, \"assist\", A_CANT, 0);\n    class_addmethod(c, (method)pd_tilde_pdtilde, \"pd~\", A_GIMME, 0);\n    class_addmethod(c, (method)pd_tilde_anything, \"anything\", A_GIMME, 0);\n    class_dspinit(c);\n\n    class_register(CLASS_BOX, c);\n    pd_tilde_class = c;\n    post(\"pd~ version 0.54\");\n}\n\nstatic void *pd_tilde_new(t_symbol *s, long ac, t_atom *av)\n{\n    int ninsig = 2, noutsig = 2, fifo = 5, binary = 1, j;\n    t_float sr = sys_getsr();\n    t_symbol *pddir = gensym(\".\"), *scheddir = gensym(\".\");\n    t_pd_tilde *x;\n\n    if ((x = (t_pd_tilde *)object_alloc(pd_tilde_class)))\n    {\n        while (ac > 0 && av[0].a_type == A_SYM)\n        {\n            const char *flag = av[0].a_w.w_sym->s_name;\n            if (!strcmp(flag, \"-sr\") && ac > 1)\n            {\n                sr = (av[1].a_type == A_FLOAT ? av[1].a_w.w_float :\n                    (av[1].a_type == A_LONG ? av[1].a_w.w_long : 0));\n                ac -= 2; av += 2;\n            }\n            else if (!strcmp(flag, \"-ninsig\") && ac > 1)\n            {\n                ninsig = (av[1].a_type == A_FLOAT ? av[1].a_w.w_float :\n                    (av[1].a_type == A_LONG ? av[1].a_w.w_long : 0));\n                ac -= 2; av += 2;\n            }\n            else if (!strcmp(flag, \"-noutsig\") && ac > 1)\n            {\n                noutsig = (av[1].a_type == A_FLOAT ? av[1].a_w.w_float :\n                    (av[1].a_type == A_LONG ? av[1].a_w.w_long : 0));\n                ac -= 2; av += 2;\n            }\n            else if (!strcmp(flag, \"-fifo\") && ac > 1)\n            {\n                fifo = (av[1].a_type == A_FLOAT ? av[1].a_w.w_float :\n                    (av[1].a_type == A_LONG ? av[1].a_w.w_long : 0));\n                ac -= 2; av += 2;\n            }\n            else if (!strcmp(flag, \"-pddir\") && ac > 1)\n            {\n                pddir = (av[1].a_type == A_SYM ? av[1].a_w.w_sym : gensym(\".\"));\n                ac -= 2; av += 2;\n            }\n            else if (!strcmp(flag, \"-scheddir\") && ac > 1)\n            {\n                scheddir =\n                    (av[1].a_type == A_SYM ? av[1].a_w.w_sym : gensym(\".\"));\n                ac -= 2; av += 2;\n            }\n            else if (!strcmp(flag, \"-ascii\"))\n            {\n                binary = 0;\n                ac--; av++;\n            }\n            else break;\n        }\n        if (ac)\n            post(\"pd~: warning: ignoring extra arguments\");\n        dsp_setup((t_pxobject *)x, ninsig);\n        x->x_outlet1 = outlet_new(&x->x_obj, 0);\n        for (j = 0; j < noutsig; j++)\n            outlet_new((t_pxobject *)x, \"signal\");\n        x->x_clock = clock_new(x, (method)pd_tilde_tick);\n        x->x_insig = (t_pdsample **)t_getbytes(ninsig * sizeof(*x->x_insig));\n        x->x_outsig = (t_pdsample **)t_getbytes(noutsig * sizeof(*x->x_outsig));\n        x->x_ninsig = ninsig;\n        x->x_noutsig = noutsig;\n        x->x_fifo = fifo;\n        x->x_sr = sr;\n        x->x_pddir = pddir;\n        x->x_schedlibdir = scheddir;\n        x->x_infd = 0;\n        x->x_outfd = 0;\n        x->x_childpid = -1;\n        x->x_binbuf = binbuf_new();\n        x->x_clockisset = 0;\n        x->x_binary = binary;\n        x->x_sampbuf = 0;\n    }\n    return (x);\n}\n\nvoid pd_tilde_assist(t_pd_tilde *x, void *b, long m, long a, char *s)\n{\n}\n\n#endif /* MSP */\n"
  },
  {
    "path": "libs/libpd/pure-data/extra/pique/pique.c",
    "content": "/* Copyright (c) 1999 Miller Puckette.  The\ncontents of this file are free for any use, but BOTH THE AUTHOR AND UCSD\nDISCLAIM ALL WARRANTIES related to it.  Although not written in Java, this\nstill should not be used to control any machinery containing a sharp blade or\ncombustible materiel, or as part of any life support system or weapon. */\n\n#include \"m_pd.h\"\n#include <math.h>\n#include <stdio.h>\n/* These pragmas are only used for MSVC, not MinGW or Cygwin <hans@at.or.at> */\n#ifdef _MSC_VER\n#pragma warning( disable : 4244 )\n#pragma warning( disable : 4305 )\n#endif\n\nstatic t_class *pique_class;\n\ntypedef struct _pique\n{\n    t_object x_obj;\n    int x_n;\n    t_float x_errthresh;\n    t_float *x_freq;\n    t_float *x_amp;\n    t_float *x_ampre;\n    t_float *x_ampim;\n} t_pique;\n\nstatic void *pique_new(t_floatarg f)\n{\n    int n = f;\n    t_pique *x = (t_pique *)pd_new(pique_class);\n    if (n < 1) n = 100;\n    x->x_n = n;\n    x->x_errthresh = 0;\n    x->x_freq = t_getbytes(n * sizeof(*x->x_freq));\n    x->x_amp = t_getbytes(n * sizeof(*x->x_amp));\n    x->x_ampre = t_getbytes(n * sizeof(*x->x_ampre));\n    x->x_ampim = t_getbytes(n * sizeof(*x->x_ampim));\n    outlet_new(&x->x_obj, &s_list);\n    return (x);\n}\n\nstatic t_float hanning(t_float pidetune, t_float sinpidetune)\n{\n    t_float pi = 3.141592653589793;\n    if (pidetune < 0.01 && pidetune > -0.01) return (1);\n    else if (pidetune > 3.14 && pidetune < 3.143) return (0.5);\n    else if (pidetune < -3.14 && pidetune > -3.143) return (0.5);\n    else return (sinpidetune/pidetune - 0.5 *\n        (sinpidetune/(pidetune+pi) + sinpidetune/(pidetune-pi)));\n}\n\nstatic t_float peakerror(t_word *fpreal, t_word *fpimag, t_float pidetune,\n    t_float norm, t_float peakreal, t_float peakimag)\n{\n    t_float sinpidetune = sin(pidetune);\n    t_float cospidetune = cos(pidetune);\n    t_float windowshould = hanning(pidetune, sinpidetune);\n    t_float realshould = windowshould * (\n        peakreal * cospidetune + peakimag * sinpidetune);\n    t_float imagshould =  windowshould * (\n        peakimag * cospidetune - peakreal * sinpidetune);\n    t_float realgot = norm * (fpreal[0].w_float -\n        0.5 * (fpreal[1].w_float + fpreal[-1].w_float));\n    t_float imaggot = norm * (fpimag[0].w_float -\n        0.5 * (fpimag[1].w_float + fpimag[-1].w_float));\n    t_float realdev = realshould - realgot, imagdev = imagshould - imaggot;\n    \n    /* post(\"real %f->%f; imag %f->%f\", realshould, realgot,\n        imagshould, imaggot); */\n    return (realdev * realdev + imagdev * imagdev);\n}\n\nstatic void pique_doit(int npts, t_word *fpreal, t_word *fpimag,\n    int npeak, int *nfound, t_float *fpfreq, t_float *fpamp,\n        t_float *fpampre, t_float *fpampim, t_float errthresh)\n{\n    t_float srate = sys_getsr();      /* not sure how to get this correctly */\n    t_float oneovern = 1.0/ (t_float)npts;\n    t_float fperbin = srate * oneovern;\n    t_float pow1, pow2 = 0, pow3 = 0, pow4 = 0, pow5 = 0;\n    t_float re1, re2 = 0, re3 = fpreal->w_float;\n    t_float im1, im2 = 0, im3 = 0, powthresh, relativeerror;\n    int count, peakcount = 0, n2 = (npts >> 1);\n    t_float *fp1, *fp2;\n    t_word *wp1, *wp2;\n    for (count = n2, wp1 = fpreal, wp2 = fpimag, powthresh = 0;\n        count--; wp1++, wp2++)\n            powthresh += (wp1->w_float) * (wp1->w_float) +\n                (wp2->w_float) * (wp2->w_float) ; \n    powthresh *= 0.00001;\n    for (count = 1; count < n2; count++)\n    {\n        t_float windreal, windimag, pi = 3.141592653589793;\n        t_float detune, pidetune, sinpidetune, cospidetune,\n            ampcorrect, freqout, ampout, ampoutreal, ampoutimag;\n        t_float rpeak, rpeaknext, rpeakprev;\n        t_float ipeak, ipeaknext, ipeakprev;\n        t_float errleft, errright;\n        fpreal++;\n        fpimag++;\n        re1 = re2;\n        re2 = re3;\n        re3 = fpreal->w_float;\n        im1 = im2;\n        im2 = im3;\n        im3 = fpimag->w_float;\n        if (count < 2) continue;\n        pow1 = pow2;\n        pow2 = pow3;\n        pow3 = pow4;\n        pow4 = pow5;\n            /* get Hanning-windowed spectrum by convolution */\n        windreal = re2 - 0.5 * (re1 + re3);\n        windimag = im2 - 0.5 * (im1 + im3);\n        pow5 = windreal * windreal + windimag * windimag;\n        /* if (count < 30) post(\"power %f\", pow5); */\n        if (count < 5) continue;\n            /* check for a peak.  The actual bin is count-3. */\n        if (pow3 <= pow2 || pow3 <= pow4 || pow3 <= pow1 || pow3 <= pow5\n            || pow3 < powthresh)\n                continue;\n            /* go back for the raw FFT values around the peak. */\n        rpeak = fpreal[-3].w_float;\n        rpeaknext = fpreal[-2].w_float;\n        rpeakprev = fpreal[-4].w_float;\n        ipeak = fpimag[-3].w_float;\n        ipeaknext = fpimag[-2].w_float;\n        ipeakprev = fpimag[-4].w_float;\n            /* recalculate Hanning-windowed spectrum by convolution */\n        windreal = rpeak - 0.5 * (rpeaknext + rpeakprev);\n        windimag = ipeak - 0.5 * (ipeaknext + ipeakprev);\n        \n        detune = ((rpeakprev - rpeaknext) *\n            (2.0 * rpeak - rpeakprev - rpeaknext) +\n                (ipeakprev - ipeaknext) *\n                    (2.0 * ipeak - ipeakprev - ipeaknext)) /\n                        (4.0 * pow3);\n        /* if (count < 30) post(\"detune %f\", detune); */\n        if (detune > 0.7 || detune < -0.7) continue;\n            /* the frequency is the sum of the bin frequency and detuning */ \n        freqout = fperbin * ((t_float)(count-3) + detune);\n        pidetune = pi * detune;\n        sinpidetune = sin(pidetune);\n        cospidetune = cos(pidetune);\n        ampcorrect = 1.0 / hanning(pidetune, sinpidetune);\n                /* Multiply by 2 to get real-sinusoid peak amplitude \n                and divide by N to normalize FFT */\n        ampcorrect *= 2. * oneovern;\n            /* amplitude is peak height, corrected for Hanning window shape */\n\n        ampout = ampcorrect * sqrt(pow3);\n        ampoutreal = ampcorrect *\n            (windreal * cospidetune - windimag * sinpidetune);\n        ampoutimag = ampcorrect *\n            (windreal * sinpidetune + windimag * cospidetune);\n        if (errthresh > 0)\n        {\n            /* post(\"peak %f %f\", freqout, ampout); */\n            errleft = peakerror(fpreal-4, fpimag-4, pidetune+pi,\n                2. * oneovern, ampoutreal, ampoutimag);\n            errright = peakerror(fpreal-2, fpimag-2, pidetune-pi,\n                2. * oneovern,  ampoutreal, ampoutimag);\n            relativeerror = (errleft + errright)/(ampout * ampout);\n            if (relativeerror > errthresh) continue;\n        }\n        /* post(\"power %f, error %f, relative %f\",\n            pow3, errleft + errright, relativeerror); */\n        *fpfreq++ = freqout;\n        *fpamp++ = ampout;\n        *fpampre++ = ampoutreal;\n        *fpampim++ = ampoutimag;\n        if (++peakcount == npeak) break;\n    }\n    *nfound = peakcount;\n}\n\nstatic void pique_list(t_pique *x, t_symbol *s, int argc, t_atom *argv)\n{\n    int npts = atom_getfloatarg(0, argc, argv);\n    t_symbol *symreal = atom_getsymbolarg(1, argc, argv);\n    t_symbol *symimag = atom_getsymbolarg(2, argc, argv);\n    int npeak = atom_getfloatarg(3, argc, argv);\n    int n;\n    t_garray *a;\n    t_word *fpreal, *fpimag;\n    if (npts < 8 || npeak < 1) pd_error(0, \"pique: bad npoints or npeak\");\n    if (npeak > x->x_n) npeak = x->x_n;\n    if (!(a = (t_garray *)pd_findbyclass(symreal, garray_class)) ||\n        !garray_getfloatwords(a, &n, &fpreal) ||\n            n < npts)\n                pd_error(0, \"%s: missing or bad array\", symreal->s_name);\n    else if (!(a = (t_garray *)pd_findbyclass(symimag, garray_class)) ||\n        !garray_getfloatwords(a, &n, &fpimag) ||\n            n < npts)\n                pd_error(0, \"%s: missing or bad array\", symimag->s_name);\n    else\n    {\n        int nfound, i;\n        t_float *fpfreq = x->x_freq;\n        t_float *fpamp = x->x_amp;\n        t_float *fpampre = x->x_ampre;\n        t_float *fpampim = x->x_ampim;\n        pique_doit(npts, fpreal, fpimag, npeak,\n            &nfound, fpfreq, fpamp, fpampre, fpampim, x->x_errthresh);\n        for (i = 0; i < nfound; i++, fpamp++, fpfreq++, fpampre++, fpampim++)\n        {\n            t_atom at[5];\n            SETFLOAT(at, (t_float)i);\n            SETFLOAT(at+1, *fpfreq);\n            SETFLOAT(at+2, *fpamp);\n            SETFLOAT(at+3, *fpampre);\n            SETFLOAT(at+4, *fpampim);\n            outlet_list(x->x_obj.ob_outlet, &s_list, 5, at);   \n        }\n    }\n}\n\nstatic void pique_errthresh(t_pique *x, t_floatarg f)\n{\n    x->x_errthresh = f;\n}\n\nstatic void pique_free(t_pique *x)\n{\n    int n = x->x_n;\n    t_freebytes(x->x_freq, n * sizeof(*x->x_freq));\n    t_freebytes(x->x_amp, n * sizeof(*x->x_amp));\n    t_freebytes(x->x_ampre, n * sizeof(*x->x_ampre));\n    t_freebytes(x->x_ampim, n * sizeof(*x->x_ampim));\n}\n\nvoid pique_setup(void)\n{\n    pique_class = class_new(gensym(\"pique\"), (t_newmethod)pique_new,\n        (t_method)pique_free, sizeof(t_pique),0, A_DEFFLOAT, 0);\n    class_addlist(pique_class, pique_list);\n    class_addmethod(pique_class, (t_method)pique_errthresh,\n        gensym(\"errthresh\"), A_FLOAT, 0);\n    post(\"pique 0.1 for PD version 23\");\n}\n\n"
  },
  {
    "path": "libs/libpd/pure-data/extra/sigmund~/sigmund~.c",
    "content": "/* Copyright (c) 2005 Miller Puckette.  BSD licensed.  No warranties. */\n\n/*\n    fix parameter settings\n    not to report pitch if evidence too scanty?\n    note-on detection triggered by falling envelope (a posteriori)\n    reentrancy bug setting loud flag (other parameters too?)\n    tweaked freqs still not stable enough\n    implement block (\"-b\") mode\n*/\n\n#ifdef PD\n#include \"m_pd.h\"\n#endif\n#ifdef MSP\n#include \"ext.h\"\n#include \"z_dsp.h\"\n#include \"ext_support.h\"\n#include \"ext_proto.h\"\n#include \"ext_obex.h\"\ntypedef double t_floatarg;\ntypedef float t_float;\n#define t_resizebytes(a, b, c) t_resizebytes((char *)(a), (b), (c))\n#endif\n\n/* From here to the next \"#ifdef PD\" or \"#ifdef Max\" should be extractable\nand usable in other contexts.  The one external requirement is a real\nsingle-precision FFT, invoked as in the Mayer one: */\n\n/* this routine is passed a buffer of npoints values, and returns the\nN/2+1 real parts of the DFT (frequency zero through Nyquist), followed\nby the N/2-1 imaginary points, in order of decreasing frequency.  Pd 0.41,\nfor example, defines this in the file d_fft_mayer.c or d_fft_fftsg.c. */\n\n#include <math.h>\n#include <stdio.h>\n#include <string.h>\n#ifdef _WIN32\n# include <malloc.h> /* MSVC or mingw on windows */\n#elif defined(__linux__) || defined(__APPLE__)\n# include <alloca.h> /* linux, mac, mingw, cygwin */\n#endif\n#include <stdlib.h>\n#ifdef _MSC_VER\n#pragma warning( disable : 4244 )\n#pragma warning( disable : 4305 )\n#endif\n\n#ifndef HAVE_ALLOCA     /* can work without alloca() but we never need it */\n#define HAVE_ALLOCA 1\n#endif\n\n/* limit stack allocation to ~400kB (enough for 16384 points).\n * usually the stack size is at least 1 MB */\n#define ALLOCA_MAXBYTES 400000\n\n#if HAVE_ALLOCA\n#define BUF_ALLOCA(n) ((n) < ALLOCA_MAXBYTES ?  \\\n        alloca(n) : getbytes(n))\n#define BUF_FREEA(x, n) ( \\\n    (((n) < ALLOCA_MAXBYTES) || (freebytes((x), (n)), 0)))\n#else\n#define BUF_ALLOCA(n) (getbytes(n))\n#define BUF_FREEA(x, n) (freebytes((x), (n)))\n#endif\n\ntypedef struct peak\n{\n    t_float p_freq;\n    t_float p_amp;\n    t_float p_ampreal;\n    t_float p_ampimag;\n    t_float p_pit;\n    t_float p_db;\n    t_float p_salience;\n    t_float p_tmp;\n} t_peak;\n\ntypedef struct _pitchpt\n{\n    t_float p_weight;\n    t_float p_loudness;\n    t_float p_evenness;\n    t_float p_unused;\n} t_pitchpt;\n\n\n/********************** service routines **************************/\n\n/* these three are adapted from elsewhere in Pd but included here for\n   completeness */\nstatic unsigned int sigmund_ilog2(int n)\n{\n    int ret = -1;\n    while (n)\n    {\n        n >>= 1;\n        ret++;\n    }\n    return (ret);\n}\n\nstatic t_float sigmund_ftom(t_float f)\n{\n    return (f > 0 ? 17.3123405046 * log(.12231220585 * f) : -1500);\n}\n\n#define LOGTEN 2.302585092994\nstatic t_float sigmund_powtodb(t_float f)\n{\n    if (f <= 0) return (0);\n    else\n    {\n        t_float val = 100 + 10./LOGTEN * log(f);\n        return (val < 0 ? 0 : val);\n    }\n}\n\n/* parameters for von Hann window (change these to get Hamming if desired) */\n#define W_ALPHA 0.5\n#define W_BETA 0.5\n#define NEGBINS 4   /* number of bins of negative frequency we'll need */\n\n#define PI 3.141592653589793\n#define LOG2  0.693147180559945\n#define LOG10 2.302585092994046\n\nstatic t_float sinx(t_float theta, t_float sintheta)\n{\n    if (theta > -0.003 && theta < 0.003)\n        return (1);\n    else return (sintheta/theta);\n}\n\nstatic t_float window_hann_mag(t_float pidetune, t_float sinpidetune)\n{\n    return (W_ALPHA * sinx(pidetune, sinpidetune)\n        - 0.5 * W_BETA *\n            (sinx(pidetune+PI, sinpidetune) + sinx(pidetune-PI, sinpidetune)));\n}\n\nstatic t_float window_mag(t_float pidetune, t_float cospidetune)\n{\n    return (sinx(pidetune + (PI/2), cospidetune)\n        + sinx(pidetune - (PI/2), -cospidetune));\n}\n\n/*********** Routines to analyze a window into sinusoidal peaks *************/\n\nstatic int sigmund_cmp_freq(const void *p1, const void *p2)\n{\n    if ((*(t_peak **)p1)->p_freq > (*(t_peak **)p2)->p_freq)\n        return (1);\n    else if ((*(t_peak **)p1)->p_freq < (*(t_peak **)p2)->p_freq)\n        return (-1);\n    else return (0);\n}\n\nstatic void sigmund_tweak(int npts, t_float *ftreal, t_float *ftimag,\n    int npeak, t_peak *peaks, t_float fperbin, int loud)\n{\n    t_peak **peakptrs = (t_peak **)alloca(sizeof (*peakptrs) * (npeak+1));\n    t_peak negpeak;\n    int peaki, j, k;\n    t_float ampreal[3], ampimag[3];\n    t_float binperf = 1./fperbin;\n    t_float phaseperbin = (npts-0.5)/npts, oneovern = 1./npts;\n    if (npeak < 1)\n        return;\n    for (peaki = 0; peaki < npeak; peaki++)\n        peakptrs[peaki+1] = &peaks[peaki];\n    qsort(peakptrs+1, npeak, sizeof (*peakptrs), sigmund_cmp_freq);\n    peakptrs[0] = &negpeak;\n    negpeak.p_ampreal = peakptrs[1]->p_ampreal;\n    negpeak.p_ampimag = -peakptrs[1]->p_ampimag;\n    negpeak.p_freq = -peakptrs[1]->p_freq;\n    for (peaki = 1; peaki <= npeak; peaki++)\n    {\n        int cbin = peakptrs[peaki]->p_freq*binperf + 0.5;\n        int nsub = (peaki == npeak ? 1:2);\n        t_float windreal, windimag, windpower, detune, pidetune, sinpidetune,\n            cospidetune, ampcorrect, ampout, ampoutreal, ampoutimag, freqout;\n        /* post(\"3 nsub %d amp %f freq %f\", nsub,\n            peakptrs[peaki]->p_amp, peakptrs[peaki]->p_freq); */\n        if (cbin < 0 || cbin > 2*npts - 3)\n            continue;\n        for (j = 0; j < 3; j++)\n            ampreal[j] = ftreal[cbin+2*j-2], ampimag[j] = ftimag[cbin+2*j-2];\n        /* post(\"a %f %f\", ampreal[1], ampimag[1]); */\n        for (j = 0; j < nsub; j++)\n        {\n            t_peak *neighbor = peakptrs[(peaki-1) + 2*j];\n            t_float neighborreal = npts * neighbor->p_ampreal;\n            t_float neighborimag = npts * neighbor->p_ampimag;\n            for (k = 0; k < 3; k++)\n            {\n                t_float freqdiff = (0.5*PI) * ((cbin + 2*k-2)\n                    -binperf * neighbor->p_freq);\n                t_float sx = sinx(freqdiff, sin(freqdiff));\n                t_float phasere = cos(freqdiff * phaseperbin);\n                t_float phaseim = sin(freqdiff * phaseperbin);\n                ampreal[k] -=\n                    sx * (phasere * neighborreal - phaseim * neighborimag);\n                ampimag[k] -=\n                    sx * (phaseim * neighborreal + phasere * neighborimag);\n            }       \n            /* post(\"b %f %f\", ampreal[1], ampimag[1]); */\n        }\n\n        windreal = W_ALPHA * ampreal[1] -\n            (0.5 * W_BETA) * (ampreal[0] + ampreal[2]);\n        windimag = W_ALPHA * ampimag[1] -\n            (0.5 * W_BETA) * (ampimag[0] + ampimag[2]);\n        windpower = windreal * windreal + windimag * windimag;\n        detune = (\n            W_BETA*(ampreal[0] - ampreal[2]) * \n                (2.0*W_ALPHA * ampreal[1] - W_BETA * (ampreal[0] + ampreal[2]))\n                    +\n            W_BETA*(ampimag[0] - ampimag[2]) *\n                (2.0*W_ALPHA * ampimag[1] - W_BETA * (ampimag[0] + ampimag[2]))\n                        ) / (4.0 * windpower);\n        if (detune > 0.5)\n            detune = 0.5;\n        else if (detune < -0.5)\n            detune = -0.5;\n        /* if (loud > 0)\n            post(\"tweak: windpower %f, bin %d, detune %f\",\n                windpower, cbin, detune); */\n        pidetune = PI * detune;\n        sinpidetune = sin(pidetune);\n        cospidetune = cos(pidetune);\n\n        ampcorrect = 1.0 / window_hann_mag(pidetune, sinpidetune);\n\n        ampout = oneovern * ampcorrect *sqrt(windpower);\n        ampoutreal = oneovern * ampcorrect *\n            (windreal * cospidetune - windimag * sinpidetune);\n        ampoutimag = oneovern * ampcorrect *\n            (windreal * sinpidetune + windimag * cospidetune);\n        freqout = (cbin + 2*detune) * fperbin;\n        /* if (loud > 1)\n            post(\"amp %f, freq %f\", ampout, freqout); */\n        \n        peakptrs[peaki]->p_freq = freqout;\n        peakptrs[peaki]->p_amp = ampout;\n        peakptrs[peaki]->p_ampreal = ampoutreal;\n        peakptrs[peaki]->p_ampimag = ampoutimag;\n    }\n}\n\nstatic void sigmund_remask(int maxbin, int bestindex, t_float powmask, \n    t_float maxpower, t_float *maskbuf)\n{\n    int bin;\n    int bin1 = (bestindex > 52 ? bestindex-50:2);\n    int bin2 = (maxbin < bestindex + 50 ? bestindex + 50 : maxbin);\n    for (bin = bin1; bin < bin2; bin++)\n    {\n        t_float bindiff = bin - bestindex;\n        t_float mymask;\n        mymask = powmask/ (1. + bindiff * bindiff * bindiff * bindiff);\n        if (bindiff < 2 && bindiff > -2)\n            mymask = 2*maxpower;\n        if (mymask > maskbuf[bin])\n            maskbuf[bin] = mymask;\n    } \n}\n\n#define PEAKMASKFACTOR 1.\n#define PEAKTHRESHFACTOR 0.6\n\nstatic void sigmund_getrawpeaks(int npts, t_float *insamps,\n    int npeak, t_peak *peakv, int *nfound, t_float *power, t_float srate,\n    int loud, t_float hifreq)\n{\n    t_float oneovern = 1.0/ (t_float)npts;\n    t_float fperbin = 0.5 * srate * oneovern, totalpower = 0;\n    int npts2 = 2*npts, i, bin, bufsize = sizeof (t_float ) *\n        (2*NEGBINS + 6*npts);\n    int peakcount = 0;\n    t_float *fp1, *fp2;\n    t_float *rawreal, *rawimag, *maskbuf, *powbuf;\n    t_float *bigbuf = (t_float *)BUF_ALLOCA(bufsize);\n    int maxbin = hifreq/fperbin;\n    if (maxbin > npts - NEGBINS)\n        maxbin = npts - NEGBINS;\n    /* if (loud) post(\"tweak %d\", tweak); */\n    maskbuf = bigbuf + npts2;\n    powbuf = maskbuf + npts;\n    rawreal = powbuf + npts+NEGBINS;\n    rawimag = rawreal+npts+NEGBINS;\n    for (i = 0; i < npts; i++)\n        maskbuf[i] = 0;\n\n    for (i = 0; i < npts; i++)\n        bigbuf[i] = insamps[i];\n    for (i = npts; i < 2*npts; i++)\n        bigbuf[i] = 0;\n    mayer_realfft(npts2, bigbuf);\n    for (i = 0; i < npts; i++)\n        rawreal[i] = bigbuf[i];\n    for (i = 1; i < npts-1; i++)\n        rawimag[i] = bigbuf[npts2-i];\n    rawreal[-1] = rawreal[1];\n    rawreal[-2] = rawreal[2];\n    rawreal[-3] = rawreal[3];\n    rawreal[-4] = rawreal[4];\n    rawimag[0] = rawimag[npts-1] = 0;\n    rawimag[-1] = -rawimag[1];\n    rawimag[-2] = -rawimag[2];\n    rawimag[-3] = -rawimag[3];\n    rawimag[-4] = -rawimag[4];\n#if 1\n    for (i = 0, fp1 = rawreal, fp2 = rawimag; i < maxbin; i++, fp1++, fp2++)\n    {\n        t_float x1 = fp1[1] - fp1[-1], x2 = fp2[1] - fp2[-1],\n            p = powbuf[i] = x1*x1+x2*x2;\n        if (i >= 2)\n           totalpower += p;\n    }\n    powbuf[maxbin] = powbuf[maxbin+1] = 0;\n    *power = 0.5 * totalpower *oneovern * oneovern;\n#endif\n    for (peakcount = 0; peakcount < npeak; peakcount++)\n    {\n        t_float pow1, maxpower = 0, windreal, windimag, windpower,\n            detune, pidetune, sinpidetune, cospidetune, ampcorrect, ampout,\n            ampoutreal, ampoutimag, freqout, powmask;\n        int bestindex = -1;\n\n        for (bin = 2, fp1 = rawreal+2, fp2 = rawimag+2;\n            bin < maxbin; bin++, fp1++, fp2++)\n        {\n            pow1 = powbuf[bin];\n            if (pow1 > maxpower && pow1 > maskbuf[bin])\n            {\n                t_float thresh = PEAKTHRESHFACTOR *\n                    (powbuf[bin-2]+powbuf[bin+2]);\n                if (pow1 > thresh)\n                    maxpower = pow1, bestindex = bin;\n            }\n        }\n\n        if (totalpower <= 0 || maxpower < 1e-10*totalpower || bestindex < 0)\n            break;\n        fp1 = rawreal+bestindex;\n        fp2 = rawimag+bestindex;\n        powmask = maxpower * PEAKMASKFACTOR;\n        /* if (loud > 2)\n            post(\"maxpower %f, powmask %f, param1 %f\",\n                maxpower, powmask, param1); */\n        sigmund_remask(maxbin, bestindex, powmask, maxpower, maskbuf);\n        \n        /* if (loud > 1)\n            post(\"best index %d, total power %f\", bestindex, totalpower); */\n\n        windreal = fp1[1] - fp1[-1];\n        windimag = fp2[1] - fp2[-1];\n        windpower = windreal * windreal + windimag * windimag;\n        detune = ((fp1[1] * fp1[1] - fp1[-1]*fp1[-1]) \n            + (fp2[1] * fp2[1] - fp2[-1]*fp2[-1])) / (2 * windpower);\n\n        if (detune > 0.5)\n            detune = 0.5;\n        else if (detune < -0.5)\n            detune = -0.5;\n        /* if (loud > 1)\n            post(\"windpower %f, index %d, detune %f\",\n                windpower, bestindex, detune); */\n        pidetune = PI * detune;\n        sinpidetune = sin(pidetune);\n        cospidetune = cos(pidetune);\n        ampcorrect = 1.0 / window_mag(pidetune, cospidetune);\n\n        ampout = ampcorrect *sqrt(windpower);\n        ampoutreal = ampcorrect *\n            (windreal * cospidetune - windimag * sinpidetune);\n        ampoutimag = ampcorrect *\n            (windreal * sinpidetune + windimag * cospidetune);\n\n            /* the frequency is the sum of the bin frequency and detuning */\n\n        peakv[peakcount].p_freq = (freqout = (bestindex + 2*detune)) * fperbin;\n        peakv[peakcount].p_amp = oneovern * ampout;\n        peakv[peakcount].p_ampreal = oneovern * ampoutreal;\n        peakv[peakcount].p_ampimag = oneovern * ampoutimag;\n    }\n    sigmund_tweak(npts, rawreal, rawimag, peakcount, peakv, fperbin, loud);\n    sigmund_tweak(npts, rawreal, rawimag, peakcount, peakv, fperbin, loud);\n    for (i = 0; i < peakcount; i++)\n    {\n        peakv[i].p_pit = sigmund_ftom(peakv[i].p_freq);\n        peakv[i].p_db = sigmund_powtodb(peakv[i].p_amp);\n    }\n    *nfound = peakcount;\n    BUF_FREEA(bigbuf, bufsize);\n}\n\n/*************** Routines for finding fundamental pitch *************/\n\n#define PITCHNPEAK 12\n#define NHARMONICS 16\n#define STEPSPEROCTAVE 48\n#define PITCHSTEPS(npts) (STEPSPEROCTAVE * sigmund_ilog2((npts)))\n\nstatic void sigmund_getpitch(int npeak, t_peak *peakv, t_float *freqp,\n    t_float *qualityp, t_float *evennessp,\n    t_float npts, t_float srate, t_float *harmonicweights, t_float amppowerlaw,\n    t_float qualitythreshold, t_pitchpt *ppt, int compat, int verbose)\n{\n    t_float fperbin = 0.5 * srate / npts, fbestbin;\n    int npit = PITCHSTEPS(npts), i, j, k, nsalient, bestbin;\n    t_float bestweight, sumamp, sumloudness, sumfreq, sumweight, freq = 0;\n    t_peak *bigpeaks[PITCHNPEAK];\n\n    if (npeak < 1)\n        goto done;\n    for (i = 0; i < npit; i++)\n        ppt[i].p_weight = ppt[i].p_loudness = ppt[i].p_evenness = 0;\n    for (i = 0; i < npeak; i++)\n    {\n        peakv[i].p_tmp = 0;\n        peakv[i].p_salience = peakv[i].p_db;\n    }\n    for (nsalient = 0; nsalient < PITCHNPEAK; nsalient++)\n    {\n        t_peak *bestpeak = 0;\n        t_float bestsalience = -1e20;\n        for (j = 0; j < npeak; j++)\n            if (peakv[j].p_tmp == 0 && peakv[j].p_salience > bestsalience)\n        {\n            bestsalience = peakv[j].p_salience;\n            bestpeak = &peakv[j];\n        }\n        if (!bestpeak)\n            break;\n        bigpeaks[nsalient] = bestpeak;\n        bestpeak->p_tmp = 1;\n        /* post(\"peak f=%f a=%f\", bestpeak.p_freq, bestpeak.p_amp); */\n    }\n    sumloudness = 0;\n    for (i = 0; i < nsalient; i++)\n    {\n        t_peak *thispeak = bigpeaks[i];\n        t_float weightindex = (STEPSPEROCTAVE/LOG2) *\n            log(thispeak->p_freq/(2.*fperbin));\n        t_float loudness = pow(thispeak->p_amp, amppowerlaw);\n        /* post(\"index %f, uncertainty %f\", weightindex, pitchuncertainty); */\n        for (j = 0; j < NHARMONICS; j++)\n        {\n            int loindex = weightindex -\n                (STEPSPEROCTAVE/LOG2) * log(j + 1.) - 0.5;\n            int hiindex = loindex + 2;\n            if (hiindex < 0)\n                break;\n            if (hiindex >= npit)\n                continue;\n            if (loindex < 0)\n                loindex = 0;\n            for (k = loindex; k <= hiindex; k++)\n            {\n                ppt[k].p_weight += loudness * harmonicweights[j];\n                ppt[k].p_loudness += loudness;\n                if (j&1)\n                    ppt[k].p_evenness += loudness;\n            }\n        }\n        sumloudness += loudness;\n    }\n    bestbin = -1;\n    bestweight = -1e20;\n    for (i = 0; i < npit; i++)\n        if (ppt[i].p_weight > bestweight)\n            bestweight = ppt[i].p_weight, bestbin = i;\n\n    if (bestbin < 0 || bestbin >= npit - 1 || sumloudness <= 0 ||\n        ppt[bestbin].p_loudness <= 0 ||\n        (compat && bestweight < sumloudness * 0.4) ||\n        (!compat && ppt[bestbin].p_loudness < sumloudness * qualitythreshold))\n    {\n        *qualityp = 0;\n        *evennessp = 0;\n        goto done;\n    }\n\n    *qualityp = ppt[bestbin].p_loudness / sumloudness;\n    *evennessp = ppt[bestbin].p_evenness / ppt[bestbin].p_loudness;\n\n        /* first guess by parabolic peak fitting */\n    fbestbin = bestbin + (ppt[bestbin+1].p_weight\n        - ppt[bestbin-1].p_weight) /\n            (ppt[bestbin+1].p_weight +  ppt[bestbin].p_weight +\n                ppt[bestbin-1].p_weight);\n\n    freq = 2*fperbin * exp((LOG2/STEPSPEROCTAVE)*fbestbin);\n    for (sumamp = sumweight = sumfreq = 0, i = 0; i < nsalient; i++)\n    {\n        t_peak *thispeak = bigpeaks[i];\n        t_float thisamp = thispeak->p_amp;\n        t_float thisfreq = thispeak->p_freq;\n        t_float harmonic = thisfreq/freq;\n        t_float intpart = (int)(0.5 + harmonic);\n        t_float inharm = harmonic - intpart;\n#if 0\n        if (loud)\n            post(\"freq %f intpart %f inharm %f\", freq, intpart, inharm);\n#endif\n        if (intpart >= 1 && intpart <= 16 &&\n            inharm < 0.015 * intpart && inharm > - (0.015 * intpart))\n        {\n            t_float weight = thisamp * intpart;\n            sumweight += weight;\n            sumfreq += weight*thisfreq/intpart;\n#if 0\n            if (loud)\n                post(\"weight %f freq %f\", weight, thisfreq);\n#endif\n        }\n    }\n    if (sumweight > 0)\n        freq = sumfreq / sumweight;\ndone:\n    if (!(freq >= 0 || freq <= 0))\n    {\n        /* post(\"freq nan cancelled\"); */\n        freq = 0;\n    }\n    *freqp = freq;\n}\n\n/*************** gather peak lists into sinusoidal tracks *************/\n\nstatic void sigmund_peaktrack(int ninpeak, t_peak *inpeakv, \n    int noutpeak, t_peak *outpeakv, float maxerror, int loud)\n{\n    int incnt, outcnt;\n    for (outcnt = 0; outcnt < noutpeak; outcnt++)\n        outpeakv[outcnt].p_tmp = -1;\n        \n        /* first pass. Match each \"in\" peak with the closest previous\n        \"out\" peak, but no two to the same one. */\n    for (incnt = 0; incnt < ninpeak; incnt++)\n    {\n        t_float besterror = 1e20;\n        int bestcnt = -1;\n        inpeakv[incnt].p_tmp = -1;\n        for (outcnt = 0; outcnt < noutpeak; outcnt++)\n        {\n            t_float thiserror;\n            if (outpeakv[outcnt].p_amp == 0)\n                continue;\n            thiserror = inpeakv[incnt].p_freq - outpeakv[outcnt].p_freq;\n            if (thiserror < 0)\n                thiserror = -thiserror;\n            if (thiserror < besterror)\n            {\n                besterror = thiserror;\n                bestcnt = outcnt;\n            }\n        }\n        if (bestcnt >= 0 && besterror < maxerror && outpeakv[bestcnt].p_tmp < 0)\n        {\n            outpeakv[bestcnt] = inpeakv[incnt];\n            inpeakv[incnt].p_tmp = 0;\n            outpeakv[bestcnt].p_tmp = 0;\n        }\n    }\n        /* second pass.  Unmatched \"in\" peaks assigned to free \"out\"\n        peaks */\n    for (incnt = 0; incnt < ninpeak; incnt++)\n        if (inpeakv[incnt].p_tmp < 0)\n    {\n        for (outcnt = 0; outcnt < noutpeak; outcnt++)\n            if (outpeakv[outcnt].p_tmp < 0)\n        {\n            outpeakv[outcnt] = inpeakv[incnt];\n            inpeakv[incnt].p_tmp = 0;\n            outpeakv[outcnt].p_tmp = 1;\n            break;\n        }\n    }\n    for (outcnt = 0; outcnt < noutpeak; outcnt++)\n        if (outpeakv[outcnt].p_tmp == -1)\n            outpeakv[outcnt].p_amp = 0;\n}\n\n/**************** parse continuous pitch into note starts ***************/\n\n#define NHISTPOINT 100\n\ntypedef struct _histpoint\n{\n    t_float h_freq;\n    t_float h_power;\n} t_histpoint;\n\ntypedef struct _notefinder\n{\n    t_float n_age;\n    t_float n_hifreq;\n    t_float n_lofreq;\n    int n_peaked;\n    t_histpoint n_hist[NHISTPOINT];\n    int n_histphase;\n} t_notefinder;\n\n\nstatic void notefinder_init(t_notefinder *x)\n{\n    int i;\n    x->n_peaked = x->n_age = 0;\n    x->n_hifreq = x->n_lofreq = 0;\n    x->n_histphase = 0;\n    for (i = 0; i < NHISTPOINT; i++)\n        x->n_hist[i].h_freq =x->n_hist[i].h_power = 0;\n}\n\nstatic void notefinder_doit(t_notefinder *x, t_float freq, t_float power,\n    t_float *note, t_float vibrato, int stableperiod, t_float powerthresh,\n        t_float growththresh, int loud)\n{\n        /* calculate frequency ratio between allowable vibrato extremes\n        (equal to twice the vibrato deviation from center) */\n    t_float vibmultiple = exp((2*LOG2/12) * vibrato);\n    int oldhistphase, i, k;\n    if (stableperiod > NHISTPOINT - 1)\n        stableperiod = NHISTPOINT - 1;\n    else if (stableperiod < 1)\n        stableperiod = 1;\n    if (++x->n_histphase == NHISTPOINT)\n        x->n_histphase = 0;\n    x->n_hist[x->n_histphase].h_freq = freq;\n    x->n_hist[x->n_histphase].h_power = power;\n    x->n_age++;\n    *note = 0;\n#if 0\n    if (loud)\n    {\n        post(\"stable %d, age %d, vibmultiple %f, powerthresh %f, hifreq %f\",\n            stableperiod, (int)x->n_age ,vibmultiple, powerthresh, x->n_hifreq);\n        post(\"histfreq %f %f %f %f\",\n            x->n_hist[x->n_histphase].h_freq,\n            x->n_hist[(x->n_histphase+NHISTPOINT-1)%NHISTPOINT].h_freq,\n            x->n_hist[(x->n_histphase+NHISTPOINT-2)%NHISTPOINT].h_freq,\n            x->n_hist[(x->n_histphase+NHISTPOINT-3)%NHISTPOINT].h_freq);\n        post(\"power %f %f %f %f\",\n            x->n_hist[x->n_histphase].h_power,\n            x->n_hist[(x->n_histphase+NHISTPOINT-1)%NHISTPOINT].h_power,\n            x->n_hist[(x->n_histphase+NHISTPOINT-2)%NHISTPOINT].h_power,\n            x->n_hist[(x->n_histphase+NHISTPOINT-3)%NHISTPOINT].h_power);\n        for (i = 0, k = x->n_histphase; i < stableperiod; i++)\n        {\n            post(\"pit %5.1f  pow %f\", sigmund_ftom(x->n_hist[k].h_freq),\n                x->n_hist[k].h_power);\n            if (--k < 0)\n                k = NHISTPOINT - 1;\n        }\n    }\n#endif\n       /* look for shorter notes than \"stableperiod\" in length.\n       The amplitude must rise and then fall while the pitch holds\n       steady. */\n    if (x->n_hifreq <= 0 && x->n_age > stableperiod)\n    {\n        t_float maxpow = 0, freqatmaxpow = 0,\n            localhifreq = -1e20, locallofreq = 1e20;\n        int startphase = x->n_histphase - stableperiod + 1;\n        if (startphase < 0)\n            startphase += NHISTPOINT;\n        for (i = 0, k = startphase; i < stableperiod; i++)\n        {\n            if (x->n_hist[k].h_freq <= 0)\n                break;\n            if (x->n_hist[k].h_power > maxpow)\n                maxpow = x->n_hist[k].h_power,\n                    freqatmaxpow = x->n_hist[k].h_freq;\n            if (x->n_hist[k].h_freq > localhifreq)\n                localhifreq = x->n_hist[k].h_freq;\n            if (x->n_hist[k].h_freq < locallofreq)\n                locallofreq = x->n_hist[k].h_freq;\n            if (localhifreq > locallofreq * vibmultiple)\n                break;\n            if (maxpow > power * growththresh &&\n                maxpow > x->n_hist[startphase].h_power * growththresh &&\n                    localhifreq < vibmultiple * locallofreq\n                        && freqatmaxpow > 0 && maxpow > powerthresh)\n            {\n                x->n_hifreq = x->n_lofreq = *note = freqatmaxpow;\n                x->n_age = 0;\n                x->n_peaked = 0;\n                /* post(\"got short note\"); */\n                return;\n            }\n            if (++k >= NHISTPOINT)\n                k = 0;\n        }\n        \n    }\n    if (x->n_hifreq > 0)\n    {\n            /* test if we're within \"vibrato\" range, and if so update range */\n        if (freq * vibmultiple >= x->n_hifreq &&\n            x->n_lofreq * vibmultiple >= freq)\n        {\n            if (freq > x->n_hifreq)\n                x->n_hifreq = freq;\n            if (freq < x->n_lofreq)\n                x->n_lofreq = freq;\n        }\n        else if (x->n_hifreq > 0 && x->n_age > stableperiod)\n        {\n                /* if we've been out of range at least 1/2 the\n                last \"stableperiod+1\" analyses, clear the note */\n            int nbad = 0;\n            for (i = 0, k = x->n_histphase; i < stableperiod + 1; i++)\n            {\n                if (--k < 0)\n                    k = NHISTPOINT - 1;\n                if (x->n_hist[k].h_freq * vibmultiple <= x->n_hifreq ||\n                    x->n_lofreq * vibmultiple <= x->n_hist[k].h_freq)\n                        nbad++;\n            }\n            if (2 * nbad >= stableperiod + 1)\n            {\n                x->n_hifreq = x->n_lofreq = 0;\n                x->n_age = 0;\n            }\n        }\n    }\n\n    oldhistphase = x->n_histphase - stableperiod;\n    if (oldhistphase < 0)\n        oldhistphase += NHISTPOINT;\n        \n        /* look for envelope attacks */\n\n    if (x->n_hifreq > 0 && x->n_peaked)\n    {\n        if (freq > 0 && power > powerthresh &&\n            power > x->n_hist[oldhistphase].h_power *\n                exp((LOG10*0.1)*growththresh))\n        {\n                /* clear it and fall through for new stable-note test */\n            x->n_peaked = 0;\n            x->n_hifreq = x->n_lofreq = 0;\n            x->n_age = 0;\n        }\n    }\n    else if (!x->n_peaked)\n    {\n        if (x->n_hist[oldhistphase].h_power > powerthresh &&\n            x->n_hist[oldhistphase].h_power > power)\n                x->n_peaked = 1;\n    }\n\n        /* test for a new note using a stability criterion. */\n\n    if (freq >= 0 &&\n        (x->n_hifreq <= 0 || freq > x->n_hifreq || freq < x->n_lofreq))\n    {\n        t_float testfhi = freq, testflo = freq,\n            maxpow = x->n_hist[x->n_histphase].h_power;\n        for (i = 0, k = x->n_histphase; i < stableperiod-1; i++)\n        {\n            if (--k < 0)\n                k = NHISTPOINT - 1;\n            if (x->n_hist[k].h_freq > testfhi)\n                testfhi = x->n_hist[k].h_freq;\n            if (x->n_hist[k].h_freq < testflo)\n                testflo = x->n_hist[k].h_freq;\n            if (x->n_hist[k].h_power > maxpow)\n                maxpow = x->n_hist[k].h_power;\n        }\n#if 0\n        if (loud)\n            post(\"freq %.2g testfhi %.2g  testflo %.2g maxpow %.2g\",\n                freq, testfhi, testflo, maxpow);\n#endif\n        if (testflo > 0 && testfhi <= vibmultiple * testflo\n            && maxpow > powerthresh)\n        {\n                /* report new note */\n            t_float sumf = 0, sumw = 0, thisw;\n            for (i = 0, k = x->n_histphase; i < stableperiod; i++)\n            {\n                thisw = x->n_hist[k].h_power;\n                sumw += thisw;\n                sumf += thisw*x->n_hist[k].h_freq;\n                if (--k < 0)\n                    k = NHISTPOINT - 1;\n            }\n            x->n_hifreq = x->n_lofreq = *note = (sumw > 0 ? sumf/sumw : 0);\n#if 0\n                /* debugging printout */\n            for (i = 0; i < stableperiod; i++)\n            {\n                int k3 = x->n_histphase - i;\n                if (k3 < 0)\n                    k3 += NHISTPOINT;\n                startpost(\"%5.1f \", sigmund_ftom(x->n_hist[k3].h_freq));\n            }\n            post(\"\");\n#endif\n            x->n_age = 0;\n            x->n_peaked = 0;\n            return;\n        }\n    }\n    *note = 0;\n    return;\n}\n\n/**************** object structure for Pd and Max. *********************/ \n\n/* From here onward, the code is specific to eithr Pd, Max, or both.  If\nneither \"PD 'nor \"MSP\" is defined, none of this is compiled, so that the\nwhole file can be included in other, non-PD and non-Max projects.  */\n\n#if (defined(PD) || defined (MSP))\n\n#define NHIST 100\n\n#define MODE_STREAM 1\n#define MODE_BLOCK 2        /* unimplemented */\n#define MODE_TABLE 3\n\n#define NPOINTS_DEF 1024\n#define NPOINTS_MIN 128\n#define NPOINTS_MAX 4194304\n\n#define HOP_DEF 512\n#define NPEAK_DEF 20\n\n#define VIBRATO_DEF 1\n#define STABLETIME_DEF 50\n#define MINPOWER_DEF 50\n#define GROWTH_DEF 7\n#define AMPPOWERLAW_DEF 0.5\n#define NHARMONICS_DEF 6\n#define QUALITY_DEF 0.4\n\n#define OUT_PITCH 0\n#define OUT_ENV 1\n#define OUT_NOTE 2\n#define OUT_PEAKS 3\n#define OUT_TRACKS 4\n#define OUT_SPECTRUM 5\n#define OUT_QUALITY 6\n#define OUT_EVENNESS 7\n\ntypedef struct _varout\n{\n#ifdef PD\n    t_outlet *v_outlet;\n#endif /* PD */\n#ifdef MSP\n    void *v_outlet;\n#endif /* MSP */\n    int v_what;\n} t_varout;\n\ntypedef struct _sigmund\n{\n#ifdef PD\n    t_object x_obj;\n    t_clock *x_clock;\n    t_float x_f;        /* for main signal inlet */\n#endif /* PD */\n#ifdef MSP\n    t_pxobject x_obj;\n    void *obex;\n    void *x_clock;\n    t_sample *x_inbuf2; /* extra input buffer to eat clock/DSP jitter */\n#endif /* MSP */\n    t_varout *x_varoutv;\n    int x_nvarout;\n    t_float x_sr;       /* sample rate */\n    int x_mode;         /* MODE_STREAM, etc. */\n    int x_npts;         /* number of points in analysis window */\n    int x_npeak;        /* number of peaks to find */\n    int x_loud;         /* debug level */\n    t_sample *x_inbuf;  /* input buffer */\n    t_pitchpt *x_pitchbuf;  /* input buffer */\n    int x_infill;       /* number of points filled */\n    int x_countdown;    /* countdown to start filling buffer */\n    int x_hop;          /* samples between analyses */\n    t_float x_maxfreq;    /* highest-frequency peak to report */\n    t_float x_vibrato;    /* vibrato depth in half tones */\n    t_float x_stabletime; /* period of stability needed for note */\n    t_float x_growth;     /* growth to set off a new note */\n    t_float x_minpower;   /* minimum power, in DB, for a note */\n    t_float x_hweights[NHARMONICS];  /* harmonic weights for pitch detection */\n    t_float x_nharmonics; /* harmonic droppoff point to calculate hweights */\n    t_float x_octavebias; /* octave-up or down bias to calculate hweights */\n    t_float x_amppowerlaw; /* power to raise amplitudes to get pitch weight */\n    t_float x_quality;    /* required quality to report pitch */\n    t_float x_param1;     /* three parameters for temporary use */\n    t_float x_param2;\n    t_float x_param3;\n    t_notefinder x_notefinder;  /* note parsing state */\n    t_peak *x_trackv;           /* peak tracking state */\n    int x_ntrack;               /* number of peaks tracked */\n    unsigned int x_dopitch:1;   /* which things to calculate */\n    unsigned int x_donote:1;\n    unsigned int x_dotracks:1;\n} t_sigmund;\n\nstatic void sigmund_nharmonics(t_sigmund *x, t_floatarg nharmonics,\n    t_floatarg octavebias);\n\nstatic void sigmund_preinit(t_sigmund *x)\n{\n    x->x_npts = NPOINTS_DEF;\n    sigmund_nharmonics(x, NHARMONICS_DEF, 0);\n    x->x_amppowerlaw = AMPPOWERLAW_DEF;\n    x->x_quality = QUALITY_DEF;\n    x->x_param1 = 0;\n    x->x_param2 = 0;\n    x->x_param3 = 0;\n    x->x_hop = HOP_DEF;\n    x->x_mode = MODE_STREAM;\n    x->x_npeak = NPEAK_DEF;\n    x->x_vibrato = VIBRATO_DEF;\n    x->x_stabletime = STABLETIME_DEF;\n    x->x_growth = GROWTH_DEF;\n    x->x_minpower = MINPOWER_DEF;\n    x->x_maxfreq = 1000000;\n    x->x_loud = 0;\n    x->x_sr = 1;\n    x->x_nvarout = 0;\n    x->x_varoutv = (t_varout *)getbytes(0);\n    x->x_trackv = 0;\n    x->x_ntrack = 0;\n    x->x_dopitch = x->x_donote = x->x_dotracks = 0;\n    x->x_inbuf = 0;\n    x->x_pitchbuf = (t_pitchpt *)getbytes(PITCHSTEPS(x->x_npts));\n#ifdef MSP\n    x->x_inbuf2 = 0;\n#endif\n}\n\nstatic void sigmund_npts(t_sigmund *x, t_floatarg f)\n{\n    int nwas = x->x_npts, npts = f;\n        /* check parameter ranges */\n    if (npts < NPOINTS_MIN)\n        post(\"sigmund~: minimum points %d\", NPOINTS_MIN),\n            npts = NPOINTS_MIN;\n    if (npts > NPOINTS_MAX)\n        post(\"sigmund~: maximum points %d\", NPOINTS_MAX),\n            npts = NPOINTS_MAX;\n\n    if (npts != (1 << sigmund_ilog2(npts)))\n        post(\"sigmund~: adjusting analysis size to %d points\",\n            (npts = (1 << sigmund_ilog2(npts))));\n    if (npts != nwas)\n        x->x_countdown = x->x_infill  = 0;\n    if (x->x_mode == MODE_STREAM)\n    {\n        if (x->x_inbuf)\n        {\n            x->x_inbuf = (t_sample *)t_resizebytes(x->x_inbuf,\n                sizeof(*x->x_inbuf) * nwas, sizeof(*x->x_inbuf) * npts);\n#ifdef MSP\n            x->x_inbuf2 = (t_sample *)t_resizebytes(x->x_inbuf2,\n                sizeof(*x->x_inbuf2) * nwas, sizeof(*x->x_inbuf2) * npts);\n#endif\n        }\n        else\n        {\n            x->x_inbuf = (t_sample *)getbytes(sizeof(*x->x_inbuf) * npts);\n            memset((char *)(x->x_inbuf), 0, sizeof(*x->x_inbuf) * npts);\n#ifdef MSP\n            x->x_inbuf2 = (t_sample *)getbytes(sizeof(*x->x_inbuf2) * npts);\n            memset((char *)(x->x_inbuf2), 0, sizeof(*x->x_inbuf2) * npts);\n#endif\n        }\n    }\n    else x->x_inbuf = 0;\n    x->x_pitchbuf = (t_pitchpt *)resizebytes(x->x_pitchbuf,\n        PITCHSTEPS(nwas) * sizeof(t_pitchpt),\n            PITCHSTEPS(npts) * sizeof(t_pitchpt));\n    x->x_npts = npts;\n}\n\nstatic void sigmund_hop(t_sigmund *x, t_floatarg f)\n{\n    int hop = f;\n    if (hop < 0)\n    {\n        pd_error(0, \"sigmund~: ignoring negative hopsize %d\", hop);\n        return;\n    }\n    x->x_hop = hop;\n    if (0 == hop) return;\n        /* check parameter ranges */\n    if (x->x_hop != (1 << sigmund_ilog2(x->x_hop)))\n        post(\"sigmund~: adjusting analysis size to %d points\",\n            (x->x_hop = (1 << sigmund_ilog2(x->x_hop))));\n}\n\nstatic void sigmund_npeak(t_sigmund *x, t_floatarg f)\n{\n    if (f < 1)\n        f = 1;\n    x->x_npeak = f;\n}\n\nstatic void sigmund_maxfreq(t_sigmund *x, t_floatarg f)\n{\n    x->x_maxfreq = f;\n}\n\nstatic void sigmund_vibrato(t_sigmund *x, t_floatarg f)\n{\n    if (f < 0)\n        f = 0;\n    x->x_vibrato = f;\n}\n\nstatic void sigmund_stabletime(t_sigmund *x, t_floatarg f)\n{\n    if (f < 0)\n        f = 0;\n    x->x_stabletime = f;\n}\n\nstatic void sigmund_growth(t_sigmund *x, t_floatarg f)\n{\n    if (f < 0)\n        f = 0;\n    x->x_growth = f;\n}\n\nstatic void sigmund_minpower(t_sigmund *x, t_floatarg f)\n{\n    if (f < 0)\n        f = 0;\n    x->x_minpower = f;\n}\n\nstatic void sigmund_nharmonics(t_sigmund *x, t_floatarg nharmonics,\n    t_floatarg octavebias)\n{\n    int i;\n    t_float evenbias, oddbias;\n    if (nharmonics < 0)\n        nharmonics = 0;\n    if (octavebias < -100)\n        octavebias = -100;\n    else if (octavebias > 100)\n        octavebias = 100;\n    evenbias = (octavebias > 0 ? 1 : 1 + octavebias/100);\n    oddbias = (octavebias < 0 ? 1 : 1 - octavebias/100);\n    for (i = 0; i < NHARMONICS; i++)\n        x->x_hweights[i] = (nharmonics <= 0 ? (i==0) :\n            (nharmonics / (nharmonics + i)) * ((i & 1) ? oddbias : evenbias));\n    x->x_nharmonics = nharmonics;\n    x->x_octavebias = octavebias;\n}\n\nstatic void sigmund_amppowerlaw(t_sigmund *x, t_floatarg f)\n{\n    if (f <= 0.01)\n       f = 0.01;\n    else if (f > 10)\n        f = 10;\n    x->x_amppowerlaw = f;\n}\n\nstatic void sigmund_quality(t_sigmund *x, t_floatarg f)\n{\n    if (f <= 0)\n        f = 0;\n    else if (f > 1)\n        f = 1;\n    x->x_quality = f;\n}\n\nstatic void sigmund_outspectrum(t_sigmund *x, t_outlet *outlet,\n    t_float basepit)\n{\n        /* \"&0xffff\" below silences spurious compiler warning */\n    int nsteps = PITCHSTEPS(x->x_npts) & 0xffff, i;\n    t_atom *at = (t_atom *)alloca(sizeof(t_atom) * (nsteps + 1));\n    SETFLOAT(at, basepit);\n    for (i = 0; i < nsteps; i++)\n        SETFLOAT(at + (i+1), x->x_pitchbuf[i].p_weight);\n    outlet_list(outlet, 0, nsteps+1, at);\n}\n\nstatic void sigmund_doit(t_sigmund *x, int npts, t_float *arraypoints,\n    int debug, t_float srate)\n{\n    t_peak *peakv = (t_peak *)alloca(sizeof(t_peak) * x->x_npeak);\n    int nfound, i, cnt;\n    t_float freq = 0, quality = 0, evenness = 0, power, note = 0;\n    sigmund_getrawpeaks(npts, arraypoints, x->x_npeak, peakv,\n        &nfound, &power, srate, debug, x->x_maxfreq);\n    if (x->x_dopitch)\n        sigmund_getpitch(nfound, peakv, &freq, &quality, &evenness, npts,\n        srate, x->x_hweights, x->x_amppowerlaw, x->x_quality,\n        x->x_pitchbuf, (pd_compatibilitylevel < 54), debug);\n    if (x->x_donote)\n        notefinder_doit(&x->x_notefinder, freq, power, &note, x->x_vibrato, \n            1 + x->x_stabletime * 0.001 * srate / (t_float)x->x_hop,\n                exp(LOG10*0.1*(x->x_minpower - 100)), x->x_growth, debug);\n    if (x->x_dotracks)\n        sigmund_peaktrack(nfound, peakv, x->x_ntrack, x->x_trackv, \n            2* srate / npts, debug);\n    for (cnt = x->x_nvarout; cnt--;)\n    {\n        t_varout *v = &x->x_varoutv[cnt];\n        switch (v->v_what)\n        {\n        case OUT_PITCH:\n            outlet_float(v->v_outlet, sigmund_ftom(freq));\n            break;\n        case OUT_EVENNESS:\n            outlet_float(v->v_outlet, evenness);\n            break;\n        case OUT_QUALITY:\n            outlet_float(v->v_outlet, quality);\n            break;\n        case OUT_SPECTRUM:\n            sigmund_outspectrum(x, v->v_outlet,\n                sigmund_ftom(2 * srate / npts));\n            break;\n        case OUT_ENV:\n            outlet_float(v->v_outlet, sigmund_powtodb(power));\n            break;\n        case OUT_NOTE:\n            if (note > 0)\n                outlet_float(v->v_outlet, sigmund_ftom(note));\n            break;\n        case OUT_PEAKS:\n            for (i = 0; i < nfound; i++)\n            {\n                t_atom at[5];\n                SETFLOAT(at, (t_float)i);\n                SETFLOAT(at+1, peakv[i].p_freq);\n                SETFLOAT(at+2, 2*peakv[i].p_amp);\n                SETFLOAT(at+3, 2*peakv[i].p_ampreal);\n                SETFLOAT(at+4, 2*peakv[i].p_ampimag);\n                outlet_list(v->v_outlet, 0, 5, at);   \n            }\n            break;\n        case OUT_TRACKS:\n            for (i = 0; i < x->x_ntrack; i++)\n            {\n                t_atom at[4];\n                SETFLOAT(at, (t_float)i);\n                SETFLOAT(at+1, x->x_trackv[i].p_freq);\n                SETFLOAT(at+2, 2*x->x_trackv[i].p_amp);\n                SETFLOAT(at+3, x->x_trackv[i].p_tmp);\n                outlet_list(v->v_outlet, 0, 4, at);   \n            }\n            break;\n        }\n    }\n}\n\nstatic t_int *sigmund_perform(t_int *w);\nstatic void sigmund_dsp(t_sigmund *x, t_signal **sp)\n{\n    if (x->x_mode == MODE_STREAM)\n    {\n        if (x->x_hop % sp[0]->s_n)\n            post(\"sigmund~: adjusting hop size to %d\",\n                (x->x_hop = sp[0]->s_n * (x->x_hop / sp[0]->s_n)));\n        if (x->x_infill % sp[0]->s_n) {\n            if (x->x_inbuf) {\n                int i;\n                t_sample*inbuf = x->x_inbuf;\n                for(i=0; i<x->x_npts; i++)\n                    *inbuf++ = 0.;\n            }\n            x->x_infill = 0;\n        }\n        x->x_sr = sp[0]->s_sr;\n        dsp_add(sigmund_perform, 3, x, sp[0]->s_vec, (t_int)sp[0]->s_n);\n    }\n}\n\nstatic void sigmund_print(t_sigmund *x)\n{\n    int i;\n    post(\"sigmund~ version 0.08 settings:\");\n    post(\"npts %d\", (int)x->x_npts);\n    post(\"hop %d\", (int)x->x_hop);\n    post(\"npeak %d\", (int)x->x_npeak);\n    post(\"maxfreq %g\", x->x_maxfreq);\n    post(\"vibrato %g\", x->x_vibrato);\n    post(\"stabletime %g\", x->x_stabletime);\n    post(\"growth %g\", x->x_growth);\n    post(\"minpower %g\", x->x_minpower);\n    post(\"amppowerlaw %g\", x->x_amppowerlaw);\n    post(\"quality %g\", x->x_quality);\n    if (x->x_nharmonics >= 0)\n        post(\"nharmonics %f %f\", x->x_nharmonics, x->x_octavebias),\n        post(\"resulting harmonic weights:\");\n    else post(\"harmonic weights specified individually:\");\n\n    post(\"%5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f\",\n      x->x_hweights[0], x->x_hweights[1],\n      x->x_hweights[2], x->x_hweights[3],\n      x->x_hweights[4], x->x_hweights[5],\n      x->x_hweights[6], x->x_hweights[7]);\n    post(\"%5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f\",\n     x->x_hweights[8], x->x_hweights[9],\n     x->x_hweights[10],  x->x_hweights[11],\n     x->x_hweights[12], x->x_hweights[13],\n     x->x_hweights[14], x->x_hweights[15]);\n    if (x->x_sr > 0)\n        post(\"minimum possible pitch at sample rate %f is %f\",\n            x->x_sr, sigmund_ftom(2 * x->x_sr / x->x_npts));\n    x->x_loud = 1;\n}\n\nstatic void sigmund_free(t_sigmund *x)\n{\n    if (x->x_inbuf)\n    {\n        freebytes(x->x_inbuf, x->x_npts * sizeof(*x->x_inbuf));\n#ifdef MSP\n        freebytes(x->x_inbuf2, x->x_npts * sizeof(*x->x_inbuf2));\n#endif\n    }\n    freebytes(x->x_pitchbuf, PITCHSTEPS(x->x_npts) * sizeof(*x->x_inbuf));\n    if (x->x_trackv)\n        freebytes(x->x_trackv, x->x_ntrack * sizeof(*x->x_trackv));\n    freebytes(x->x_varoutv, x->x_nvarout * sizeof(t_varout));\n    clock_free(x->x_clock);\n}\n\n#endif /* PD or MSP */\n/*************************** Glue for Pd ************************/\n#ifdef PD\n\nstatic t_class *sigmund_class;\n\nstatic void sigmund_tick(t_sigmund *x);\nstatic void sigmund_clear(t_sigmund *x);\nstatic void sigmund_npts(t_sigmund *x, t_floatarg f);\nstatic void sigmund_hop(t_sigmund *x, t_floatarg f);\nstatic void sigmund_npeak(t_sigmund *x, t_floatarg f);\nstatic void sigmund_maxfreq(t_sigmund *x, t_floatarg f);\nstatic void sigmund_vibrato(t_sigmund *x, t_floatarg f);\nstatic void sigmund_stabletime(t_sigmund *x, t_floatarg f);\nstatic void sigmund_growth(t_sigmund *x, t_floatarg f);\nstatic void sigmund_minpower(t_sigmund *x, t_floatarg f);\nstatic void sigmund_amppowerlaw(t_sigmund *x, t_floatarg f);\nstatic void sigmund_quality(t_sigmund *x, t_floatarg f);\n\nstatic void sigmund_tick(t_sigmund *x)\n{\n    if (x->x_infill == x->x_npts)\n    {\n        sigmund_doit(x, x->x_npts, x->x_inbuf, x->x_loud, x->x_sr);\n        if (x->x_hop >= x->x_npts)\n        {\n            x->x_infill = 0;\n            x->x_countdown = x->x_hop - x->x_npts;\n        }\n        else\n        {\n            memmove(x->x_inbuf, x->x_inbuf + x->x_hop,\n                (x->x_infill = x->x_npts - x->x_hop) * sizeof(*x->x_inbuf));\n            x->x_countdown = 0;\n        }\n        if (x->x_loud)\n            x->x_loud--;\n    }\n}\n\nstatic t_int *sigmund_perform(t_int *w)\n{\n    t_sigmund *x = (t_sigmund *)(w[1]);\n    t_sample *in = (t_sample *)(w[2]);\n    int n = (int)(w[3]);\n\n    if (x->x_hop % n)\n        return (w+4);\n    if (x->x_countdown > 0)\n        x->x_countdown -= n;\n    else if (x->x_infill != x->x_npts)\n    {\n        int j;\n        t_float *fp = x->x_inbuf + x->x_infill;\n        for (j = 0; j < n; j++)\n            *fp++ = *in++;\n        x->x_infill += n;\n        if (x->x_infill == x->x_npts)\n            clock_delay(x->x_clock, 0);\n    }\n    return (w+4);\n}\n\nstatic void *sigmund_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_sigmund *x = (t_sigmund *)pd_new(sigmund_class);\n    sigmund_preinit(x);\n\n    while (argc > 0)\n    {\n        t_symbol *firstarg = atom_getsymbolarg(0, argc, argv);\n        if (!strcmp(firstarg->s_name, \"-t\"))\n        {\n            x->x_mode = MODE_TABLE;\n            argc--, argv++;\n        }\n        else if (!strcmp(firstarg->s_name, \"-s\"))\n        {\n            x->x_mode = MODE_STREAM;\n            argc--, argv++;\n        }\n#if 0\n        else if (!strcmp(firstarg->s_name, \"-b\"))\n        {\n            x->x_mode = MODE_BLOCK;\n            argc--, argv++;\n        }\n#endif\n        else if (!strcmp(firstarg->s_name, \"-npts\") && argc > 1)\n        {\n            x->x_npts = atom_getfloatarg(1, argc, argv);\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(firstarg->s_name, \"-hop\") && argc > 1)\n        {\n            sigmund_hop(x, atom_getfloatarg(1, argc, argv));\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(firstarg->s_name, \"-npeak\") && argc > 1)\n        {\n            sigmund_npeak(x, atom_getfloatarg(1, argc, argv));\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(firstarg->s_name, \"-maxfreq\") && argc > 1)\n        {\n            sigmund_maxfreq(x, atom_getfloatarg(1, argc, argv));\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(firstarg->s_name, \"-vibrato\") && argc > 1)\n        {\n            sigmund_vibrato(x, atom_getfloatarg(1, argc, argv));\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(firstarg->s_name, \"-stabletime\") && argc > 1)\n        {\n            sigmund_stabletime(x, atom_getfloatarg(1, argc, argv));\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(firstarg->s_name, \"-growth\") && argc > 1)\n        {\n            sigmund_growth(x, atom_getfloatarg(1, argc, argv));\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(firstarg->s_name, \"-minpower\") && argc > 1)\n        {\n            sigmund_minpower(x, atom_getfloatarg(1, argc, argv));\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(firstarg->s_name, \"-nharmonics\") && argc > 2)\n        {\n            sigmund_nharmonics(x, atom_getfloatarg(1, argc, argv),\n                atom_getfloatarg(2, argc, argv));\n            argc -= 3; argv += 3;\n        }\n        else if (!strcmp(firstarg->s_name, \"-amppowerlaw\") && argc > 1)\n        {\n            sigmund_amppowerlaw(x, atom_getfloatarg(1, argc, argv));\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(firstarg->s_name, \"-quality\") && argc > 1)\n        {\n            sigmund_quality(x, atom_getfloatarg(1, argc, argv));\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(firstarg->s_name, \"pitch\"))\n        {\n            int n2 = x->x_nvarout+1;\n            x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv,\n                x->x_nvarout*sizeof(t_varout), n2*sizeof(t_varout));\n            x->x_varoutv[x->x_nvarout].v_outlet =\n                outlet_new(&x->x_obj, &s_float);\n            x->x_varoutv[x->x_nvarout].v_what = OUT_PITCH;\n            x->x_nvarout = n2;\n            x->x_dopitch = 1;\n            argc--, argv++;\n        }\n        else if (!strcmp(firstarg->s_name, \"quality\"))\n        {\n            int n2 = x->x_nvarout+1;\n            x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv,\n                x->x_nvarout*sizeof(t_varout), n2*sizeof(t_varout));\n            x->x_varoutv[x->x_nvarout].v_outlet =\n                outlet_new(&x->x_obj, &s_float);\n            x->x_varoutv[x->x_nvarout].v_what = OUT_QUALITY;\n            x->x_nvarout = n2;\n            x->x_dopitch = 1;\n            argc--, argv++;\n        }\n        else if (!strcmp(firstarg->s_name, \"evenness\"))\n        {\n            int n2 = x->x_nvarout+1;\n            x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv,\n                x->x_nvarout*sizeof(t_varout), n2*sizeof(t_varout));\n            x->x_varoutv[x->x_nvarout].v_outlet =\n                outlet_new(&x->x_obj, &s_float);\n            x->x_varoutv[x->x_nvarout].v_what = OUT_EVENNESS;\n            x->x_nvarout = n2;\n            x->x_dopitch = 1;\n            argc--, argv++;\n        }\n        else if (!strcmp(firstarg->s_name, \"spectrum\"))\n        {\n            int n2 = x->x_nvarout+1;\n            x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv,\n                x->x_nvarout*sizeof(t_varout), n2*sizeof(t_varout));\n            x->x_varoutv[x->x_nvarout].v_outlet =\n                outlet_new(&x->x_obj, &s_float);\n            x->x_varoutv[x->x_nvarout].v_what = OUT_SPECTRUM;\n            x->x_nvarout = n2;\n            x->x_dopitch = 1;\n            argc--, argv++;\n        }\n        else if (!strcmp(firstarg->s_name, \"env\"))\n        {\n            int n2 = x->x_nvarout+1;\n            x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv,\n                x->x_nvarout*sizeof(t_varout), n2*sizeof(t_varout));\n            x->x_varoutv[x->x_nvarout].v_outlet =\n                outlet_new(&x->x_obj, &s_float);\n            x->x_varoutv[x->x_nvarout].v_what = OUT_ENV;\n            x->x_nvarout = n2;\n            argc--, argv++;\n        }\n        else if (!strcmp(firstarg->s_name, \"note\")\n            || !strcmp(firstarg->s_name, \"notes\"))\n        {\n            int n2 = x->x_nvarout+1;\n            x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv,\n                x->x_nvarout*sizeof(t_varout), n2*sizeof(t_varout));\n            x->x_varoutv[x->x_nvarout].v_outlet =\n                outlet_new(&x->x_obj, &s_float);\n            x->x_varoutv[x->x_nvarout].v_what = OUT_NOTE;\n            x->x_nvarout = n2;\n            x->x_dopitch = x->x_donote = 1;\n            argc--, argv++;\n        }\n        else if (!strcmp(firstarg->s_name, \"peaks\"))\n        {\n            int n2 = x->x_nvarout+1;\n            x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv,\n                x->x_nvarout*sizeof(t_varout), n2*sizeof(t_varout));\n            x->x_varoutv[x->x_nvarout].v_outlet =\n                outlet_new(&x->x_obj, &s_list);\n            x->x_varoutv[x->x_nvarout].v_what = OUT_PEAKS;\n            x->x_nvarout = n2;\n            argc--, argv++;\n        }\n        else if (!strcmp(firstarg->s_name, \"tracks\"))\n        {\n            int n2 = x->x_nvarout+1;\n            x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv,\n                x->x_nvarout*sizeof(t_varout), n2*sizeof(t_varout));\n            x->x_varoutv[x->x_nvarout].v_outlet =\n                outlet_new(&x->x_obj, &s_list);\n            x->x_varoutv[x->x_nvarout].v_what = OUT_TRACKS;\n            x->x_nvarout = n2;\n            x->x_dotracks = 1;\n            argc--, argv++;\n        }\n        else\n        {\n            pd_error(x, \"sigmund~: %s: unknown flag or argument missing\",\n                firstarg->s_name);\n            argc--, argv++;\n        }\n    }\n    if (!x->x_nvarout)\n    {\n        x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv,\n            0, 2*sizeof(t_varout));\n        x->x_varoutv[0].v_outlet = outlet_new(&x->x_obj, &s_float);\n        x->x_varoutv[0].v_what = OUT_PITCH;\n        x->x_varoutv[1].v_outlet = outlet_new(&x->x_obj, &s_float);\n        x->x_varoutv[1].v_what = OUT_ENV;\n        x->x_nvarout = 2;\n        x->x_dopitch = 1;\n    }\n    if (x->x_dotracks)\n    {\n        x->x_ntrack = x->x_npeak;\n        x->x_trackv = (t_peak *)getbytes(x->x_ntrack * sizeof(*x->x_trackv));\n    }\n    x->x_clock = clock_new(&x->x_obj.ob_pd, (t_method)sigmund_tick);\n    \n    x->x_infill = 0;\n    x->x_countdown = 0;\n    sigmund_npts(x, x->x_npts);\n    notefinder_init(&x->x_notefinder);\n    sigmund_clear(x);\n    return (x);\n}\n\nstatic void sigmund_list(t_sigmund *x, t_symbol *s, int argc, t_atom *argv)\n{\n    t_symbol *syminput = atom_getsymbolarg(0, argc, argv);\n    int npts = atom_getfloatarg(1, argc, argv);\n    int onset = atom_getfloatarg(2, argc, argv);\n    t_float srate = atom_getfloatarg(3, argc, argv);\n    int loud = atom_getfloatarg(4, argc, argv);\n    int arraysize, totstorage, nfound, i, bufsize;\n    t_garray *a;\n    t_float *arraypoints, pit;\n    t_word *wordarray = 0;\n    if (argc < 4)\n    {\n        post(\n\"sigmund~: array-name, npts, array-onset, samplerate, [optional debug flag]\");\n        return;\n    }\n    if (npts < 64 || npts != (1 << ilog2(npts))) \n    {\n        pd_error(0, \"sigmund~: bad npoints\");\n        return;\n    }\n    if (onset < 0)\n    {\n        pd_error(0, \"sigmund~: negative onset\");\n        return;\n    }\n    if (srate <= 0)\n    {\n        pd_error(0, \"sigmund~: bad samplerate\");\n        return;\n    }\n    bufsize = sizeof(t_float)*npts;\n    arraypoints = (t_float *)getbytes(bufsize);\n    if (!(a = (t_garray *)pd_findbyclass(syminput, garray_class)) ||\n        !garray_getfloatwords(a, &arraysize, &wordarray) ||\n            arraysize < onset + npts)\n    {\n        pd_error(0, \"sigmund~: '%s' array missing or too small\", syminput->s_name);\n        goto cleanup;\n    }\n    if (arraysize < npts)\n    {\n        pd_error(0, \"sigmund~: too few points in array\");\n        goto cleanup;\n    }\n    for (i = 0; i < npts; i++)\n        arraypoints[i] = wordarray[i+onset].w_float;\n    sigmund_doit(x, npts, arraypoints, loud, srate);\ncleanup:\n    freebytes(arraypoints, bufsize);\n}\n\nstatic void sigmund_clear(t_sigmund *x)\n{\n    if (x->x_trackv)\n        memset(x->x_trackv, 0, x->x_ntrack * sizeof(*x->x_trackv));\n    x->x_infill = x->x_countdown = 0;\n}\n\nstatic void sigmund_harmonicweights(t_sigmund *x, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    int i;\n    for (i = 0; i < NHARMONICS; i++)\n        x->x_hweights[i] = atom_getfloatarg(i, argc, argv);\n    x->x_nharmonics = x->x_octavebias = -1;\n}\n\n    /* these are for testing; their meanings vary... */\nstatic void sigmund_param1(t_sigmund *x, t_floatarg f)\n{\n    x->x_param1 = f;\n}\n\nstatic void sigmund_param2(t_sigmund *x, t_floatarg f)\n{\n    x->x_param2 = f;\n}\n\nstatic void sigmund_param3(t_sigmund *x, t_floatarg f)\n{\n    x->x_param3 = f;\n}\n\nstatic void sigmund_printnext(t_sigmund *x, t_float f)\n{\n    x->x_loud = f;\n}\n\nvoid sigmund_tilde_setup(void)\n{\n    sigmund_class = class_new(gensym(\"sigmund~\"), (t_newmethod)sigmund_new,\n        (t_method)sigmund_free, sizeof(t_sigmund), 0, A_GIMME, 0);\n    class_addlist(sigmund_class, sigmund_list);\n    class_addmethod(sigmund_class, (t_method)sigmund_dsp, gensym(\"dsp\"),\n        A_CANT, 0);\n    CLASS_MAINSIGNALIN(sigmund_class, t_sigmund, x_f);\n    class_addmethod(sigmund_class, (t_method)sigmund_param1,\n        gensym(\"param1\"), A_FLOAT, 0);\n    class_addmethod(sigmund_class, (t_method)sigmund_param2,\n        gensym(\"param2\"), A_FLOAT, 0);\n    class_addmethod(sigmund_class, (t_method)sigmund_param3,\n        gensym(\"param3\"), A_FLOAT, 0);\n    class_addmethod(sigmund_class, (t_method)sigmund_npts,\n        gensym(\"npts\"), A_FLOAT, 0);\n    class_addmethod(sigmund_class, (t_method)sigmund_hop,\n        gensym(\"hop\"), A_FLOAT, 0);\n    class_addmethod(sigmund_class, (t_method)sigmund_maxfreq,\n        gensym(\"maxfreq\"), A_FLOAT, 0);\n    class_addmethod(sigmund_class, (t_method)sigmund_npeak,\n        gensym(\"npeak\"), A_FLOAT, 0);\n    class_addmethod(sigmund_class, (t_method)sigmund_vibrato,\n        gensym(\"vibrato\"), A_FLOAT, 0);\n    class_addmethod(sigmund_class, (t_method)sigmund_stabletime,\n        gensym(\"stabletime\"), A_FLOAT, 0);\n    class_addmethod(sigmund_class, (t_method)sigmund_growth,\n        gensym(\"growth\"), A_FLOAT, 0);\n    class_addmethod(sigmund_class, (t_method)sigmund_minpower,\n        gensym(\"minpower\"), A_FLOAT, 0);\n    class_addmethod(sigmund_class, (t_method)sigmund_nharmonics,\n        gensym(\"nharmonics\"), A_FLOAT, A_DEFFLOAT, 0);\n    class_addmethod(sigmund_class, (t_method)sigmund_harmonicweights,\n        gensym(\"harmonicweights\"), A_GIMME, 0);\n    class_addmethod(sigmund_class, (t_method)sigmund_amppowerlaw,\n        gensym(\"amppowerlaw\"), A_FLOAT, 0);\n    class_addmethod(sigmund_class, (t_method)sigmund_quality,\n        gensym(\"quality\"), A_FLOAT, 0);\n    class_addmethod(sigmund_class, (t_method)sigmund_clear,\n        gensym(\"clear\"), 0);\n    class_addmethod(sigmund_class, (t_method)sigmund_print,\n        gensym(\"print\"), 0);\n    class_addmethod(sigmund_class, (t_method)sigmund_printnext,\n        gensym(\"printnext\"), A_FLOAT, 0);\n}\n\n#endif /* PD */\n\n/************************ Max/MSP glue **********************************/\n\n/* -------------------------- MSP glue ------------------------- */\n#ifdef MSP\nstatic void *sigmund_class;\n\n/* Max/MSP has laxer sync between DSP and \"tick\"s - so in the perf routine we\nkeep a circular buffer that is rectified into inbuf only when the tick comes. */\n\nstatic void sigmund_tick(t_sigmund *x)\n{\n    int i, j, npts = x->x_npts;\n    if (!x->x_inbuf)\n        return;\n    for (i = x->x_infill, j = 0; i < npts; i++, j++)\n        x->x_inbuf[j] = x->x_inbuf2[i];\n    for (i = 0; j < npts; i++, j++)\n        x->x_inbuf[j] = x->x_inbuf2[i];\n    sigmund_doit(x, x->x_npts, x->x_inbuf, x->x_loud, x->x_sr);\n    x->x_loud = 0;\n}\n\nstatic t_int *sigmund_perform(t_int *w)\n{\n    t_sigmund *x = (t_sigmund *)(w[1]);\n    t_float *in = (t_float *)(w[2]);\n    int n = (int)(w[3]), j;\n    int infill = x->x_infill;\n    t_float *fp = x->x_inbuf2 + infill;\n\n    if (x->x_obj.z_disabled) /* return if in muted MSP subpatch -Rd */\n        return (w+4);\n\n    if (infill < 0 || infill >= x->x_npts)\n        infill = 0;\n        /* for some reason this sometimes happens: */\n    if (!x->x_inbuf2)\n        return (w+4);\n    for (j = 0; j < n; j++)\n    {\n         *fp++ = *in++;\n         if (++infill == x->x_npts)\n            infill = 0, fp = x->x_inbuf2;\n    }\n    x->x_infill = infill;\n    if (x->x_countdown <= 0)\n    {\n        x->x_countdown = x->x_hop;\n        clock_delay(x->x_clock, 0);\n    }\n    x->x_countdown -= n;\n    return (w+4);\n}\n\nstatic void *sigmund_new(t_symbol *s, long ac, t_atom *av)\n{\n    t_sigmund *x;\n    t_varout *g;\n    int i, j;\n    if (!(x = (t_sigmund *)object_alloc(sigmund_class)))\n        return (0);\n    sigmund_preinit(x);\n    attr_args_process(x, ac, av);   \n    dsp_setup((t_pxobject *)x, 1);\n    object_obex_store(x, gensym(\"dumpout\"), outlet_new(x, NULL));\n    \n    for (i = 0; i < ac; i++)\n        if (av[i].a_type == A_SYM)\n    {\n        char *s = av[i].a_w.w_sym->s_name;\n        if (!strcmp(s, \"pitch\"))\n        {\n            int n2 = x->x_nvarout+1;\n            x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv,\n                x->x_nvarout*sizeof(t_varout), n2*sizeof(t_varout));\n            x->x_varoutv[x->x_nvarout].v_what = OUT_PITCH;\n            x->x_nvarout = n2;\n            x->x_dopitch = 1;\n        }\n        else if (!strcmp(s, \"spectrum\"))\n        {\n            int n2 = x->x_nvarout+1;\n            x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv,\n                x->x_nvarout*sizeof(t_varout), n2*sizeof(t_varout));\n            x->x_varoutv[x->x_nvarout].v_what = OUT_SPECTRUM;\n            x->x_nvarout = n2;\n            x->x_dopitch = 1;\n        }\n        else if (!strcmp(s, \"quality\"))\n        {\n            int n2 = x->x_nvarout+1;\n            x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv,\n                x->x_nvarout*sizeof(t_varout), n2*sizeof(t_varout));\n            x->x_varoutv[x->x_nvarout].v_what = OUT_QUALITY;\n            x->x_nvarout = n2;\n            x->x_dopitch = 1;\n        }\n        else if (!strcmp(s, \"evenness\"))\n        {\n            int n2 = x->x_nvarout+1;\n            x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv,\n                x->x_nvarout*sizeof(t_varout), n2*sizeof(t_varout));\n            x->x_varoutv[x->x_nvarout].v_what = OUT_EVENNESS;\n            x->x_nvarout = n2;\n            x->x_dopitch = 1;\n        }\n        else if (!strcmp(s, \"env\"))\n        {\n            int n2 = x->x_nvarout+1;\n            x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv,\n                x->x_nvarout*sizeof(t_varout), n2*sizeof(t_varout));\n            x->x_varoutv[x->x_nvarout].v_what = OUT_ENV;\n            x->x_nvarout = n2;\n        }\n        else if (!strcmp(s, \"note\") || !strcmp(s, \"notes\"))\n        {\n            int n2 = x->x_nvarout+1;\n            x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv,\n                x->x_nvarout*sizeof(t_varout), n2*sizeof(t_varout));\n            x->x_varoutv[x->x_nvarout].v_what = OUT_NOTE;\n            x->x_nvarout = n2;\n            x->x_dopitch = x->x_donote = 1;\n        }\n        else if (!strcmp(s, \"peaks\"))\n        {\n            int n2 = x->x_nvarout+1;\n            x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv,\n                x->x_nvarout*sizeof(t_varout), n2*sizeof(t_varout));\n            x->x_varoutv[x->x_nvarout].v_what = OUT_PEAKS;\n            x->x_nvarout = n2;\n        }\n        else if (!strcmp(s, \"tracks\"))\n        {\n            int n2 = x->x_nvarout+1;\n            x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv,\n                x->x_nvarout*sizeof(t_varout), n2*sizeof(t_varout));\n            x->x_varoutv[x->x_nvarout].v_what = OUT_TRACKS;\n            x->x_nvarout = n2;\n            x->x_dotracks = 1;\n        }\n        else if (s[0] != '@')\n            post(\"sigmund~: ignoring unknown argument '%s'\" ,s);\n    }\n    if (!x->x_nvarout)\n    {\n        x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv,\n            0, 2*sizeof(t_varout));\n        x->x_varoutv[0].v_what = OUT_PITCH;\n        x->x_varoutv[1].v_what = OUT_ENV;\n        x->x_nvarout = 2;\n        x->x_dopitch = 1;\n    }\n    for (j = 0, g = x->x_varoutv + x->x_nvarout-1; j < x->x_nvarout; j++, g--)\n        g->v_outlet = ((g->v_what == OUT_PITCH  || g->v_what == OUT_ENV ||\n            g->v_what == OUT_NOTE) ?\n                floatout((t_object *)x) : listout((t_object *)x));\n    if (x->x_dotracks)\n    {\n        x->x_ntrack = x->x_npeak;\n        x->x_trackv = (t_peak *)getbytes(x->x_ntrack * sizeof(*x->x_trackv));\n    }\n    x->x_clock = clock_new(x, (method)sigmund_tick);\n    x->x_infill = 0;\n    x->x_countdown = 0;\n    sigmund_npts(x, x->x_npts);\n    notefinder_init(&x->x_notefinder);\n    return (x);\n}\n\n/* Attribute setters. */\nvoid sigmund_npts_set(t_sigmund *x, void *attr, long ac, t_atom *av)\n{\n    if (ac && av)\n        sigmund_npts(x, atom_getfloat(av));\n}\n\nvoid sigmund_hop_set(t_sigmund *x, void *attr, long ac, t_atom *av)\n{\n    if (ac && av)\n        sigmund_hop(x, atom_getfloat(av));\n}\n\nvoid sigmund_npeak_set(t_sigmund *x, void *attr, long ac, t_atom *av)\n{\n    if (ac && av)\n        sigmund_npeak(x, atom_getfloat(av));\n}\n\nvoid sigmund_maxfreq_set(t_sigmund *x, void *attr, long ac, t_atom *av)\n{\n    if (ac && av)\n        sigmund_maxfreq(x, atom_getfloat(av));\n}\n\nvoid sigmund_vibrato_set(t_sigmund *x, void *attr, long ac, t_atom *av)\n{\n    if (ac && av)\n        sigmund_vibrato(x, atom_getfloat(av));\n}\n\nvoid sigmund_stabletime_set(t_sigmund *x, void *attr, long ac, t_atom *av)\n{\n    if (ac && av)\n        sigmund_stabletime(x, atom_getfloat(av));\n}\n\nvoid sigmund_growth_set(t_sigmund *x, void *attr, long ac, t_atom *av)\n{\n    if (ac && av)\n        sigmund_growth(x, atom_getfloat(av));\n}\n\nvoid sigmund_minpower_set(t_sigmund *x, void *attr, long ac, t_atom *av)\n{\n    if (ac && av)\n        sigmund_minpower(x, atom_getfloat(av));\n}\n\n/* end attr setters */\n\nvoid sigmund_assist(t_sigmund *x, void *b, long m, long a, char *s)\n{\n}\n\nint main()\n{       \n    t_class *c;\n    long attrflags = 0;\n    t_symbol *sym_long = gensym(\"long\"), *sym_float32 = gensym(\"float32\");\n    \n    c = class_new(\"sigmund~\", (method)sigmund_new,\n        (method)sigmund_free, sizeof(t_sigmund), (method)0L, A_GIMME, 0);\n    \n    class_obexoffset_set(c, calcoffset(t_sigmund, obex));\n    \n    class_addattr(c, attr_offset_new(\"npts\", sym_long, attrflags,\n        (method)0L, (method)sigmund_npts_set,\n            calcoffset(t_sigmund, x_npts)));\n    class_addattr(c ,attr_offset_new(\"hop\", sym_long, attrflags,\n        (method)0L, (method)sigmund_hop_set,\n            calcoffset(t_sigmund, x_hop)));\n    class_addattr(c ,attr_offset_new(\"maxfreq\", sym_float32, attrflags,\n        (method)0L, (method)sigmund_maxfreq_set,\n            calcoffset(t_sigmund, x_maxfreq)));\n    class_addattr(c ,attr_offset_new(\"npeak\", sym_long, attrflags,\n        (method)0L, (method)sigmund_npeak_set,\n            calcoffset(t_sigmund, x_npeak)));\n    class_addattr(c ,attr_offset_new(\"vibrato\", sym_float32, attrflags,\n        (method)0L, (method)sigmund_vibrato_set,\n            calcoffset(t_sigmund, x_vibrato)));\n    class_addattr(c ,attr_offset_new(\"stabletime\", sym_float32, attrflags,\n        (method)0L, (method)sigmund_stabletime_set,\n            calcoffset(t_sigmund, x_stabletime)));\n    class_addattr(c ,attr_offset_new(\"growth\", sym_float32, attrflags,\n        (method)0L, (method)sigmund_growth_set,\n            calcoffset(t_sigmund, x_growth)));\n    class_addattr(c ,attr_offset_new(\"minpower\", sym_float32, attrflags,\n        (method)0L, (method)sigmund_minpower_set,\n            calcoffset(t_sigmund, x_minpower)));\n\n    class_addmethod(c, (method)sigmund_dsp, \"dsp\", A_CANT, 0);\n    class_addmethod(c, (method)sigmund_print, \"print\", 0);\n    class_addmethod(c, (method)sigmund_print, \"printnext\", A_DEFFLOAT, 0);\n    class_addmethod(c, (method)sigmund_assist, \"assist\", A_CANT, 0);\n    \n    class_addmethod(c, (method)object_obex_dumpout, \"dumpout\", A_CANT, 0);\n    class_addmethod(c, (method)object_obex_quickref, \"quickref\", A_CANT, 0);\n    \n    class_dspinit(c);\n\n    class_register(CLASS_BOX, c);\n    sigmund_class = c;\n    \n    post(\"sigmund~ version 0.08\");\n    return (0);\n}\n\n\n#endif /* MSP */\n\n\n"
  },
  {
    "path": "libs/libpd/pure-data/extra/stdout/stdout.c",
    "content": "/* stdout -- write messages to standard output.\n\n  Copyright 2008 Miller Puckette\n  BSD license; see README.txt in this distribution for details.\n*/\n\n#include \"m_pd.h\"\n#include <stdio.h>\n#include <string.h>\nstatic t_class *stdout_class;\n\n#define MODE_DEFAULT 0  /* default, FUDI style */\n#define MODE_CR 1       /* newline-terminate messages and omit semicolons */\n#define MODE_BIN 2      /* binary messages supplied bytewise from patch */\n#define MODE_PDTILDE 3  /* binary atoms for subprocess of pd~ object */\n\ntypedef struct _stdout\n{\n    t_object x_obj;\n    int x_mode; /* 0=FUDI; 1=printf (no terminating semicolon); -1=binary */\n    int x_flush; /* fflush() stdout after each message */\n} t_stdout;\n\nstatic void *stdout_new(t_symbol*s, int argc, t_atom*argv)\n{\n    t_stdout *x = (t_stdout *)pd_new(stdout_class);\n        /* for some reason in MS windows we have to flush standard out here -\n        otherwise these outputs get out of order from the ones from pdsched.c\n        over in ../pd~.  I'm guessing mingw (with its different C runtime\n        support) will handle this correctly and so am only ifdeffing this on\n        Microsoft C compiler.  It's an efficiency hit, possibly a serious\n        one. */\n#ifdef _MSC_VER\n    x->x_flush = 1;\n#endif\n\n    while(argc--)\n    {\n        s = atom_getsymbol(argv++);\n        if (gensym(\"-cr\") == s)\n        {\n                /* No-semicolon mode */\n            x->x_mode = MODE_CR;\n        }\n        else if ((gensym(\"-b\") == s) || (gensym(\"-binary\") == s))\n        {\n                /* Binary mode:\n                   no extra characters (semicolons, CR,...) is appended\n                 */\n            x->x_mode = MODE_BIN;\n        }\n        else if ((gensym(\"-f\") == s) || (gensym(\"-flush\") == s))\n        {\n            x->x_flush = 1;\n        }\n        else if ((gensym(\"-nf\") == s) || (gensym(\"-noflush\") == s))\n        {\n            x->x_flush = 0;\n        }\n        else if (gensym(\"\") != s)\n        {\n                /* unknown mode; ignore it */\n        }\n    }\n    if (gensym(\"#pd_binary_stdio\")->s_thing)\n        x->x_mode = MODE_PDTILDE;\n    return (x);\n}\n\nstatic void stdout_binary(t_stdout *x, int argc, t_atom *argv)\n{\n#define BUFSIZE 65535\n    char buf[BUFSIZE];\n    int i;\n    if (argc>BUFSIZE)\n        argc = BUFSIZE;\n    for (i=0; i<argc; i++)\n        ((unsigned char *)buf)[i] = atom_getfloatarg(i, argc, argv);\n    buf[i>=BUFSIZE?(BUFSIZE-1):i] = 0;\n    fwrite(buf, 1, argc, stdout);\n\n    if (x->x_flush || !argc)\n        fflush(stdout);\n}\n\nstatic void pd_tilde_putfloat(float f, FILE *fd)\n{\n    putc(A_FLOAT, fd);\n    fwrite(&f, sizeof(f), 1, fd);\n}\n\nstatic void pd_tilde_putsymbol(t_symbol *s, FILE *fd)\n{\n    const char *sp = s->s_name;\n    putc(A_SYMBOL, fd);\n    do\n        putc(*sp, fd);\n    while (*sp++);\n}\n\nstatic void stdout_anything(t_stdout *x, t_symbol *s, int argc, t_atom *argv)\n{\n    char msgbuf[MAXPDSTRING], *sp, *ep = msgbuf+MAXPDSTRING;\n    if (x->x_mode == MODE_BIN)\n    {\n        if ((gensym(\"list\") == s) || (gensym(\"float\") == s) ||\n            (gensym(\"bang\") == s))\n                stdout_binary(x, argc, argv);\n        else\n            pd_error(x,\n \"stdout: only 'list' messages allowed in binary mode (got '%s')\",\n                s->s_name);\n        return;\n    }\n    else if (x->x_mode == MODE_PDTILDE)\n    {\n        pd_tilde_putsymbol(s, stdout);\n        for (; argc--; argv++)\n        {\n            if (argv->a_type == A_FLOAT)\n                pd_tilde_putfloat(argv->a_w.w_float, stdout);\n            else if (argv->a_type == A_SYMBOL)\n                pd_tilde_putsymbol(argv->a_w.w_symbol, stdout);\n        }\n        putc(A_SEMI, stdout);\n        if (x->x_flush)\n            fflush(stdout);\n        return;\n    }\n    msgbuf[0] = 0;\n    strncpy(msgbuf, s->s_name, MAXPDSTRING);\n    msgbuf[MAXPDSTRING-1] = 0;\n    sp = msgbuf + strlen(msgbuf);\n    while (argc--)\n    {\n        if (sp < ep-1)\n            sp[0] = ' ', sp[1] = 0, sp++;\n        atom_string(argv++, sp, (unsigned int)(ep-sp));\n        sp += strlen(sp);\n    }\n    switch(x->x_mode) {\n    case MODE_CR:\n        printf(\"%s\\n\", msgbuf);\n        break;\n    default:\n        printf(\"%s;\\n\", msgbuf);\n    }\n    if (x->x_flush)\n        fflush(stdout);\n}\n\nstatic void stdout_free(t_stdout *x)\n{\n    fflush(stdout);\n}\n\nvoid stdout_setup(void)\n{\n    stdout_class = class_new(gensym(\"stdout\"), (t_newmethod)stdout_new,\n        (t_method)stdout_free, sizeof(t_stdout), 0, A_GIMME, 0);\n    class_addanything(stdout_class, stdout_anything);\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/d_arithmetic.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/*  arithmetic binops (+, -, *, /).\nIf no creation argument is given, there are two signal inlets for vector/vector\noperation; otherwise it's vector/scalar and the second inlet takes a float\nto reset the value.\n*/\n\n#include \"m_pd.h\"\n#include <math.h> /* needed for log~ */\n\n/* -------------- convenience routines for multichannel binops ----- */\n\n    /* add a binary operation (such as \"+\") to the DSP chain, in\n    which inputs may be of different sizes (but the output should have\n    the same size as the larger input).  Whichever is shorter gets re-used\n    as many times as necessary to match the longer one; so one can add signals\n    with different numbers of channels. Two functions are passed, a general\n    one and another that is called if the block size is a multiple of 8. */\n\nstatic void dsp_add_multi(t_sample *vec1, int n1, t_sample *vec2,\n    int n2, t_sample *outvec, t_perfroutine func, t_perfroutine func8)\n{\n    int i;\n    if (n1 > n2)\n        for (i = (n1+n2-1)/n2; i--; )\n    {\n        t_int blocksize = (n2 < n1 - i*n2 ?\n            n2 : n1 - i*n2);\n        dsp_add(((blocksize & 7) || !func8 ? func : func8), 4,\n            vec1 + i * n2, vec2, outvec + i * n2, blocksize);\n    }\n    else for (i = (n1+n2-1)/n1; i--; )\n    {\n        t_int blocksize = (n1 < n2 - i*n1 ?\n            n1 : n2 - i*n1);\n        dsp_add(((blocksize & 7) || !func8 ? func : func8), 4,\n            vec1, vec2 + i*n1, outvec + i*n1, blocksize);\n    }\n}\n\n    /* more generally, add a binary operation to the DSP chain, that\n    may be vector or scalar in either of its two inputs.  The caller\n    supplies three versions: vector-vector, vector-scalar, and scalar-vector.\n    In the scalar-vector case the \"perf\" routine gets the vector argument\n    first (on the DSP chain) - this way, if the operation is commutative\n    the same function can be supplied for perf_vs and perf_vs_reverse.\n    If perf_vv8, etc, are nonzero, they are called if the vector size is a\n    multiple of 8.  */\nstatic void any_binop_dsp(t_signal **sp,\n    t_perfroutine perf_vv, t_perfroutine perf_vv8,\n    t_perfroutine perf_vs, t_perfroutine perf_vs8,\n    t_perfroutine perf_vs_reverse, t_perfroutine perf_vs8_reverse)\n{\n    int bign0 = sp[0]->s_length * sp[0]->s_nchans,\n        bign1 = sp[1]->s_length * sp[1]->s_nchans, outchans;\n    if (bign1 > bign0)\n        outchans = sp[1]->s_nchans;\n    else if (bign0 > 1)\n        outchans = sp[0]->s_nchans;\n    else outchans = 1;\n    signal_setmultiout(&sp[2], outchans);\n    if (bign0 > 1) /* first input is a vector */\n    {\n        if (bign1 > 1)\n                    /* general case: both inputs are vectors */\n            dsp_add_multi(sp[0]->s_vec, bign0, sp[1]->s_vec, bign1,\n                sp[2]->s_vec, perf_vv, perf_vv8);\n                    /* add a scalar to a vector */\n        else dsp_add(((bign0 & 7) || !perf_vs8 ? perf_vs : perf_vs8),\n            4, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, (t_int)bign0);\n    }\n    else /* first input is scalar */\n    {\n        if (bign1 > 1)\n                /* second input is a vector: use reverse scalar version */\n            dsp_add(((bign0 & 7) || !perf_vs8_reverse ?\n                perf_vs_reverse : perf_vs8_reverse),\n                    4, sp[1]->s_vec, sp[0]->s_vec, sp[2]->s_vec, (t_int)bign1);\n        else\n        {\n                /* operate on two scalars to make a vector, needing two ops */\n            dsp_add(perf_vs, 4,\n                sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, (t_int)1);\n            dsp_add_scalarcopy(sp[2]->s_vec, sp[2]->s_vec,\n                (t_int)sp[2]->s_length);\n        }\n    }\n}\n\n    /* vector-scalar version (as in \"+~ 1\") - here we don't deal with\n    scalar left inputs - the class may not be declared CLASS_NOPROMOTELEFT. */\nstatic void any_binop_scalar_dsp(t_signal **sp, t_sample *g,\n    t_perfroutine perf, t_perfroutine perf8)\n{\n    t_int bign = sp[0]->s_length * sp[0]->s_nchans;\n    signal_setmultiout(&sp[1], sp[0]->s_nchans);\n    dsp_add(((bign & 7) || !perf8 ? perf : perf8),\n        4, sp[0]->s_vec, g, sp[1]->s_vec, bign);\n}\n\n\n/* ----------------------------- plus ----------------------------- */\nstatic t_class *plus_class, *scalarplus_class;\n\ntypedef struct _plus\n{\n    t_object x_obj;\n    t_float x_f;\n} t_plus;\n\ntypedef struct _scalarplus\n{\n    t_object x_obj;\n    t_float x_f;\n    t_float x_g;            /* inlet value */\n} t_scalarplus;\n\nstatic void *plus_new(t_symbol *s, int argc, t_atom *argv)\n{\n    if (argc > 1)\n        post(\"+~: extra arguments ignored\");\n    if (argc)   /* argument implies we'll do a scalar add as in \"+~ 1\" */\n    {\n        t_scalarplus *x = (t_scalarplus *)pd_new(scalarplus_class);\n        floatinlet_new(&x->x_obj, &x->x_g);\n        x->x_g = atom_getfloatarg(0, argc, argv);\n        outlet_new(&x->x_obj, &s_signal);\n        x->x_f = 0;\n        return (x);\n    }\n    else\n    {\n        t_plus *x = (t_plus *)pd_new(plus_class);\n        inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);\n        outlet_new(&x->x_obj, &s_signal);\n        x->x_f = 0;\n        return (x);\n    }\n}\n\nt_int *scalarplus_perform(t_int *w)\n{\n    t_sample *in = (t_sample *)(w[1]);\n    t_float f = *(t_float *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    while (n--) *out++ = *in++ + f;\n    return (w+5);\n}\n\nt_int *scalarplus_perf8(t_int *w)\n{\n    t_sample *in = (t_sample *)(w[1]);\n    t_float g = *(t_float *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    for (; n; n -= 8, in += 8, out += 8)\n    {\n        t_sample f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3];\n        t_sample f4 = in[4], f5 = in[5], f6 = in[6], f7 = in[7];\n\n        out[0] = f0 + g; out[1] = f1 + g; out[2] = f2 + g; out[3] = f3 + g;\n        out[4] = f4 + g; out[5] = f5 + g; out[6] = f6 + g; out[7] = f7 + g;\n    }\n    return (w+5);\n}\n\nstatic void plus_dsp(t_plus *x, t_signal **sp)\n{\n    any_binop_dsp(sp, plus_perform, plus_perf8,\n        scalarplus_perform, scalarplus_perf8,\n        scalarplus_perform, scalarplus_perf8);\n}\n\nstatic void scalarplus_dsp(t_scalarplus *x, t_signal **sp)\n{\n    any_binop_scalar_dsp(sp, &x->x_g, scalarplus_perform, scalarplus_perf8);\n}\n\nstatic void plus_setup(void)\n{\n    plus_class = class_new(gensym(\"+~\"), (t_newmethod)plus_new, 0,\n        sizeof(t_plus),\n            CLASS_MULTICHANNEL | CLASS_NOPROMOTESIG | CLASS_NOPROMOTELEFT,\n                A_GIMME, 0);\n    class_addmethod(plus_class, (t_method)plus_dsp, gensym(\"dsp\"), A_CANT, 0);\n    CLASS_MAINSIGNALIN(plus_class, t_plus, x_f);\n    class_sethelpsymbol(plus_class, gensym(\"binops-tilde\"));\n    scalarplus_class = class_new(gensym(\"+~\"), 0, 0,\n        sizeof(t_scalarplus), CLASS_MULTICHANNEL, 0);\n    CLASS_MAINSIGNALIN(scalarplus_class, t_scalarplus, x_f);\n    class_addmethod(scalarplus_class, (t_method)scalarplus_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_sethelpsymbol(scalarplus_class, gensym(\"binops-tilde\"));\n}\n\n/* ----------------------------- minus ----------------------------- */\nstatic t_class *minus_class, *scalarminus_class;\n\ntypedef struct _minus\n{\n    t_object x_obj;\n    t_float x_f;\n} t_minus;\n\ntypedef struct _scalarminus\n{\n    t_object x_obj;\n    t_float x_f;\n    t_float x_g;\n} t_scalarminus;\n\nstatic void *minus_new(t_symbol *s, int argc, t_atom *argv)\n{\n    if (argc > 1) post(\"-~: extra arguments ignored\");\n    if (argc)\n    {\n        t_scalarminus *x = (t_scalarminus *)pd_new(scalarminus_class);\n        floatinlet_new(&x->x_obj, &x->x_g);\n        x->x_g = atom_getfloatarg(0, argc, argv);\n        outlet_new(&x->x_obj, &s_signal);\n        x->x_f = 0;\n        return (x);\n    }\n    else\n    {\n        t_minus *x = (t_minus *)pd_new(minus_class);\n        inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);\n        outlet_new(&x->x_obj, &s_signal);\n        x->x_f = 0;\n        return (x);\n    }\n}\n\nt_int *minus_perform(t_int *w)\n{\n    t_sample *in1 = (t_sample *)(w[1]);\n    t_sample *in2 = (t_sample *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    while (n--) *out++ = *in1++ - *in2++;\n    return (w+5);\n}\n\nt_int *minus_perf8(t_int *w)\n{\n    t_sample *in1 = (t_sample *)(w[1]);\n    t_sample *in2 = (t_sample *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    for (; n; n -= 8, in1 += 8, in2 += 8, out += 8)\n    {\n        t_sample f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3];\n        t_sample f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7];\n\n        t_sample g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3];\n        t_sample g4 = in2[4], g5 = in2[5], g6 = in2[6], g7 = in2[7];\n\n        out[0] = f0 - g0; out[1] = f1 - g1; out[2] = f2 - g2; out[3] = f3 - g3;\n        out[4] = f4 - g4; out[5] = f5 - g5; out[6] = f6 - g6; out[7] = f7 - g7;\n    }\n    return (w+5);\n}\n\nt_int *scalarminus_perform(t_int *w)\n{\n    t_sample *in = (t_sample *)(w[1]);\n    t_float f = *(t_float *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    while (n--) *out++ = *in++ - f;\n    return (w+5);\n}\n\nt_int *scalarminus_perf8(t_int *w)\n{\n    t_sample *in = (t_sample *)(w[1]);\n    t_float g = *(t_float *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    for (; n; n -= 8, in += 8, out += 8)\n    {\n        t_sample f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3];\n        t_sample f4 = in[4], f5 = in[5], f6 = in[6], f7 = in[7];\n\n        out[0] = f0 - g; out[1] = f1 - g; out[2] = f2 - g; out[3] = f3 - g;\n        out[4] = f4 - g; out[5] = f5 - g; out[6] = f6 - g; out[7] = f7 - g;\n    }\n    return (w+5);\n}\n\nt_int *reversescalarminus_perform(t_int *w)\n{\n    t_sample *in = (t_sample *)(w[1]);\n    t_float f = *(t_float *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    while (n--) *out++ = f - *in++;\n    return (w+5);\n}\n\nt_int *reversescalarminus_perf8(t_int *w)\n{\n    t_sample *in = (t_sample *)(w[1]);\n    t_float g = *(t_float *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    for (; n; n -= 8, in += 8, out += 8)\n    {\n        t_sample f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3];\n        t_sample f4 = in[4], f5 = in[5], f6 = in[6], f7 = in[7];\n\n        out[0] = g - f0; out[1] = g - f1; out[2] = g - f2; out[3] = g - f3;\n        out[4] = g - f4; out[5] = g - f5; out[6] = g - f6; out[7] = g - f7;\n    }\n    return (w+5);\n}\n\nstatic void minus_dsp(t_minus *x, t_signal **sp)\n{\n    any_binop_dsp(sp, minus_perform, minus_perf8,\n        scalarminus_perform, scalarminus_perf8,\n        reversescalarminus_perform, reversescalarminus_perf8);\n}\n\nstatic void scalarminus_dsp(t_scalarminus *x, t_signal **sp)\n{\n    any_binop_scalar_dsp(sp, &x->x_g, scalarminus_perform, scalarminus_perf8);\n}\n\nstatic void minus_setup(void)\n{\n    minus_class = class_new(gensym(\"-~\"), (t_newmethod)minus_new, 0,\n        sizeof(t_minus),\n            CLASS_MULTICHANNEL | CLASS_NOPROMOTESIG | CLASS_NOPROMOTELEFT,\n                A_GIMME, 0);\n    CLASS_MAINSIGNALIN(minus_class, t_minus, x_f);\n    class_addmethod(minus_class, (t_method)minus_dsp, gensym(\"dsp\"), A_CANT, 0);\n    class_sethelpsymbol(minus_class, gensym(\"binops-tilde\"));\n    scalarminus_class = class_new(gensym(\"-~\"), 0, 0,\n        sizeof(t_scalarminus), CLASS_MULTICHANNEL, 0);\n    CLASS_MAINSIGNALIN(scalarminus_class, t_scalarminus, x_f);\n    class_addmethod(scalarminus_class, (t_method)scalarminus_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_sethelpsymbol(scalarminus_class, gensym(\"binops-tilde\"));\n}\n\n/* ----------------------------- times ----------------------------- */\n\nstatic t_class *times_class, *scalartimes_class;\n\ntypedef struct _times\n{\n    t_object x_obj;\n    t_float x_f;\n} t_times;\n\ntypedef struct _scalartimes\n{\n    t_object x_obj;\n    t_float x_f;\n    t_float x_g;\n} t_scalartimes;\n\nstatic void *times_new(t_symbol *s, int argc, t_atom *argv)\n{\n    if (argc > 1) post(\"*~: extra arguments ignored\");\n    if (argc)\n    {\n        t_scalartimes *x = (t_scalartimes *)pd_new(scalartimes_class);\n        floatinlet_new(&x->x_obj, &x->x_g);\n        x->x_g = atom_getfloatarg(0, argc, argv);\n        outlet_new(&x->x_obj, &s_signal);\n        x->x_f = 0;\n        return (x);\n    }\n    else\n    {\n        t_times *x = (t_times *)pd_new(times_class);\n        inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);\n        outlet_new(&x->x_obj, &s_signal);\n        x->x_f = 0;\n        return (x);\n    }\n}\n\nt_int *times_perform(t_int *w)\n{\n    t_sample *in1 = (t_sample *)(w[1]);\n    t_sample *in2 = (t_sample *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    while (n--) *out++ = *in1++ * *in2++;\n    return (w+5);\n}\n\nt_int *times_perf8(t_int *w)\n{\n    t_sample *in1 = (t_sample *)(w[1]);\n    t_sample *in2 = (t_sample *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    for (; n; n -= 8, in1 += 8, in2 += 8, out += 8)\n    {\n        t_sample f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3];\n        t_sample f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7];\n\n        t_sample g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3];\n        t_sample g4 = in2[4], g5 = in2[5], g6 = in2[6], g7 = in2[7];\n\n        out[0] = f0 * g0; out[1] = f1 * g1; out[2] = f2 * g2; out[3] = f3 * g3;\n        out[4] = f4 * g4; out[5] = f5 * g5; out[6] = f6 * g6; out[7] = f7 * g7;\n    }\n    return (w+5);\n}\n\nt_int *scalartimes_perform(t_int *w)\n{\n    t_sample *in = (t_sample *)(w[1]);\n    t_float f = *(t_float *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    while (n--) *out++ = *in++ * f;\n    return (w+5);\n}\n\nt_int *scalartimes_perf8(t_int *w)\n{\n    t_sample *in = (t_sample *)(w[1]);\n    t_float g = *(t_float *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    for (; n; n -= 8, in += 8, out += 8)\n    {\n        t_sample f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3];\n        t_sample f4 = in[4], f5 = in[5], f6 = in[6], f7 = in[7];\n\n        out[0] = f0 * g; out[1] = f1 * g; out[2] = f2 * g; out[3] = f3 * g;\n        out[4] = f4 * g; out[5] = f5 * g; out[6] = f6 * g; out[7] = f7 * g;\n    }\n    return (w+5);\n}\n\nstatic void times_dsp(t_times *x, t_signal **sp)\n{\n    any_binop_dsp(sp, times_perform, times_perf8,\n        scalartimes_perform, scalartimes_perf8,\n        scalartimes_perform, scalartimes_perf8);\n}\n\nstatic void scalartimes_dsp(t_scalartimes *x, t_signal **sp)\n{\n    any_binop_scalar_dsp(sp, &x->x_g, scalartimes_perform, scalartimes_perf8);\n}\n\nstatic void times_setup(void)\n{\n    times_class = class_new(gensym(\"*~\"), (t_newmethod)times_new, 0,\n        sizeof(t_times),\n            CLASS_MULTICHANNEL | CLASS_NOPROMOTESIG | CLASS_NOPROMOTELEFT,\n                A_GIMME, 0);\n    CLASS_MAINSIGNALIN(times_class, t_times, x_f);\n    class_addmethod(times_class, (t_method)times_dsp, gensym(\"dsp\"), A_CANT, 0);\n    class_sethelpsymbol(times_class, gensym(\"binops-tilde\"));\n    scalartimes_class = class_new(gensym(\"*~\"), 0, 0,\n        sizeof(t_scalartimes), CLASS_MULTICHANNEL, 0);\n    CLASS_MAINSIGNALIN(scalartimes_class, t_scalartimes, x_f);\n    class_addmethod(scalartimes_class, (t_method)scalartimes_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_sethelpsymbol(scalartimes_class, gensym(\"binops-tilde\"));\n}\n\n/* ----------------------------- over ----------------------------- */\nstatic t_class *over_class, *scalarover_class;\n\ntypedef struct _over\n{\n    t_object x_obj;\n    t_float x_f;\n} t_over;\n\ntypedef struct _scalarover\n{\n    t_object x_obj;\n    t_float x_f;\n    t_float x_g;\n} t_scalarover;\n\nstatic void *over_new(t_symbol *s, int argc, t_atom *argv)\n{\n    if (argc > 1) post(\"/~: extra arguments ignored\");\n    if (argc)\n    {\n        t_scalarover *x = (t_scalarover *)pd_new(scalarover_class);\n        floatinlet_new(&x->x_obj, &x->x_g);\n        x->x_g = atom_getfloatarg(0, argc, argv);\n        outlet_new(&x->x_obj, &s_signal);\n        x->x_f = 0;\n        return (x);\n    }\n    else\n    {\n        t_over *x = (t_over *)pd_new(over_class);\n        inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);\n        outlet_new(&x->x_obj, &s_signal);\n        x->x_f = 0;\n        return (x);\n    }\n}\n\nt_int *over_perform(t_int *w)\n{\n    t_sample *in1 = (t_sample *)(w[1]);\n    t_sample *in2 = (t_sample *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    while (n--)\n    {\n        t_sample f = *in1++, g = *in2++;\n        *out++ = (g ? f / g : 0);\n    }\n    return (w+5);\n}\n\nt_int *over_perf8(t_int *w)\n{\n    t_sample *in1 = (t_sample *)(w[1]);\n    t_sample *in2 = (t_sample *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    for (; n; n -= 8, in1 += 8, in2 += 8, out += 8)\n    {\n        t_sample f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3];\n        t_sample f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7];\n\n        t_sample g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3];\n        t_sample g4 = in2[4], g5 = in2[5], g6 = in2[6], g7 = in2[7];\n\n        out[0] = (g0? f0 / g0 : 0);\n        out[1] = (g1? f1 / g1 : 0);\n        out[2] = (g2? f2 / g2 : 0);\n        out[3] = (g3? f3 / g3 : 0);\n        out[4] = (g4? f4 / g4 : 0);\n        out[5] = (g5? f5 / g5 : 0);\n        out[6] = (g6? f6 / g6 : 0);\n        out[7] = (g7? f7 / g7 : 0);\n    }\n    return (w+5);\n}\n\nt_int *scalarover_perform(t_int *w)\n{\n    t_sample *in = (t_sample *)(w[1]);\n    t_float f = *(t_float *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    if(f) f = 1./f;\n    while (n--) *out++ = *in++ * f;\n    return (w+5);\n}\n\nt_int *scalarover_perf8(t_int *w)\n{\n    t_sample *in = (t_sample *)(w[1]);\n    t_float g = *(t_float *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    if (g) g = 1.f / g;\n    for (; n; n -= 8, in += 8, out += 8)\n    {\n        t_sample f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3];\n        t_sample f4 = in[4], f5 = in[5], f6 = in[6], f7 = in[7];\n\n        out[0] = f0 * g; out[1] = f1 * g; out[2] = f2 * g; out[3] = f3 * g;\n        out[4] = f4 * g; out[5] = f5 * g; out[6] = f6 * g; out[7] = f7 * g;\n    }\n    return (w+5);\n}\n\nt_int *reversescalarover_perform(t_int *w)\n{\n    t_sample *in = (t_sample *)(w[1]);\n    t_float f = *(t_float *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    while (n--)\n    {\n        t_sample g = *in++;\n        *out++ = (g != 0 ? f/g : 0);\n    }\n    return (w+5);\n}\n\nt_int *reversescalarover_perf8(t_int *w)\n{\n    t_sample *in = (t_sample *)(w[1]);\n    t_float g = *(t_float *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    for (; n; n -= 8, in += 8, out += 8)\n    {\n        t_sample f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3];\n        t_sample f4 = in[4], f5 = in[5], f6 = in[6], f7 = in[7];\n\n        out[0] = (f0 != 0 ? g/f0 : 0); out[1] = (f1 != 0 ? g/f1 : 0);\n        out[2] = (f2 != 0 ? g/f2 : 0); out[3] = (f3 != 0 ? g/f3 : 0);\n        out[4] = (f4 != 0 ? g/f4 : 0); out[5] = (f5 != 0 ? g/f5 : 0);\n        out[6] = (f6 != 0 ? g/f6 : 0); out[7] = (f7 != 0 ? g/f7 : 0);\n    }\n    return (w+5);\n}\n\n\nstatic void over_dsp(t_over *x, t_signal **sp)\n{\n    any_binop_dsp(sp, over_perform, over_perf8,\n        scalarover_perform, scalarover_perf8,\n        reversescalarover_perform, reversescalarover_perf8);\n}\n\nstatic void scalarover_dsp(t_scalarover *x, t_signal **sp)\n{\n    any_binop_scalar_dsp(sp, &x->x_g, scalarover_perform, scalarover_perf8);\n}\n\nstatic void over_setup(void)\n{\n    over_class = class_new(gensym(\"/~\"), (t_newmethod)over_new, 0,\n        sizeof(t_over),\n            CLASS_MULTICHANNEL | CLASS_NOPROMOTESIG | CLASS_NOPROMOTELEFT,\n                A_GIMME, 0);\n    CLASS_MAINSIGNALIN(over_class, t_over, x_f);\n    class_addmethod(over_class, (t_method)over_dsp, gensym(\"dsp\"), A_CANT, 0);\n    class_sethelpsymbol(over_class, gensym(\"binops-tilde\"));\n    scalarover_class = class_new(gensym(\"/~\"), 0, 0,\n        sizeof(t_scalarover), CLASS_MULTICHANNEL, 0);\n    CLASS_MAINSIGNALIN(scalarover_class, t_scalarover, x_f);\n    class_addmethod(scalarover_class, (t_method)scalarover_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_sethelpsymbol(scalarover_class, gensym(\"binops-tilde\"));\n}\n\n/* ----------------------------- max ----------------------------- */\nstatic t_class *max_class, *scalarmax_class;\n\ntypedef struct _max\n{\n    t_object x_obj;\n    t_float x_f;\n} t_max;\n\ntypedef struct _scalarmax\n{\n    t_object x_obj;\n    t_float x_f;\n    t_float x_g;\n} t_scalarmax;\n\nstatic void *max_new(t_symbol *s, int argc, t_atom *argv)\n{\n    if (argc > 1) post(\"max~: extra arguments ignored\");\n    if (argc)\n    {\n        t_scalarmax *x = (t_scalarmax *)pd_new(scalarmax_class);\n        floatinlet_new(&x->x_obj, &x->x_g);\n        x->x_g = atom_getfloatarg(0, argc, argv);\n        outlet_new(&x->x_obj, &s_signal);\n        x->x_f = 0;\n        return (x);\n    }\n    else\n    {\n        t_max *x = (t_max *)pd_new(max_class);\n        inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);\n        outlet_new(&x->x_obj, &s_signal);\n        x->x_f = 0;\n        return (x);\n    }\n}\n\nt_int *max_perform(t_int *w)\n{\n    t_sample *in1 = (t_sample *)(w[1]);\n    t_sample *in2 = (t_sample *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    while (n--)\n    {\n        t_sample f = *in1++, g = *in2++;\n        *out++ = (f > g ? f : g);\n    }\n    return (w+5);\n}\n\nt_int *max_perf8(t_int *w)\n{\n    t_sample *in1 = (t_sample *)(w[1]);\n    t_sample *in2 = (t_sample *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    for (; n; n -= 8, in1 += 8, in2 += 8, out += 8)\n    {\n        t_sample f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3];\n        t_sample f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7];\n\n        t_sample g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3];\n        t_sample g4 = in2[4], g5 = in2[5], g6 = in2[6], g7 = in2[7];\n\n        out[0] = (f0 > g0 ? f0 : g0); out[1] = (f1 > g1 ? f1 : g1);\n        out[2] = (f2 > g2 ? f2 : g2); out[3] = (f3 > g3 ? f3 : g3);\n        out[4] = (f4 > g4 ? f4 : g4); out[5] = (f5 > g5 ? f5 : g5);\n        out[6] = (f6 > g6 ? f6 : g6); out[7] = (f7 > g7 ? f7 : g7);\n    }\n    return (w+5);\n}\n\nt_int *scalarmax_perform(t_int *w)\n{\n    t_sample *in = (t_sample *)(w[1]);\n    t_float f = *(t_float *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    while (n--)\n    {\n        t_sample g = *in++;\n        *out++ = (f > g ? f : g);\n    }\n    return (w+5);\n}\n\nt_int *scalarmax_perf8(t_int *w)\n{\n    t_sample *in = (t_sample *)(w[1]);\n    t_float g = *(t_float *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    for (; n; n -= 8, in += 8, out += 8)\n    {\n        t_sample f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3];\n        t_sample f4 = in[4], f5 = in[5], f6 = in[6], f7 = in[7];\n\n        out[0] = (f0 > g ? f0 : g); out[1] = (f1 > g ? f1 : g);\n        out[2] = (f2 > g ? f2 : g); out[3] = (f3 > g ? f3 : g);\n        out[4] = (f4 > g ? f4 : g); out[5] = (f5 > g ? f5 : g);\n        out[6] = (f6 > g ? f6 : g); out[7] = (f7 > g ? f7 : g);\n    }\n    return (w+5);\n}\n\nstatic void max_dsp(t_max *x, t_signal **sp)\n{\n    any_binop_dsp(sp, max_perform, max_perf8,\n        scalarmax_perform, scalarmax_perf8,\n        scalarmax_perform, scalarmax_perf8);\n}\n\nstatic void scalarmax_dsp(t_scalarmax *x, t_signal **sp)\n{\n    any_binop_scalar_dsp(sp, &x->x_g, scalarmax_perform, scalarmax_perf8);\n}\n\nstatic void max_setup(void)\n{\n    max_class = class_new(gensym(\"max~\"), (t_newmethod)max_new, 0,\n        sizeof(t_max),\n            CLASS_MULTICHANNEL | CLASS_NOPROMOTESIG | CLASS_NOPROMOTELEFT,\n                A_GIMME, 0);\n    CLASS_MAINSIGNALIN(max_class, t_max, x_f);\n    class_addmethod(max_class, (t_method)max_dsp, gensym(\"dsp\"), A_CANT, 0);\n    class_sethelpsymbol(max_class, gensym(\"binops-tilde\"));\n    scalarmax_class = class_new(gensym(\"max~\"), 0, 0,\n        sizeof(t_scalarmax), CLASS_MULTICHANNEL, 0);\n    CLASS_MAINSIGNALIN(scalarmax_class, t_scalarmax, x_f);\n    class_addmethod(scalarmax_class, (t_method)scalarmax_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_sethelpsymbol(scalarmax_class, gensym(\"binops-tilde\"));\n}\n\n/* ----------------------------- min ----------------------------- */\nstatic t_class *min_class, *scalarmin_class;\n\ntypedef struct _min\n{\n    t_object x_obj;\n    t_float x_f;\n} t_min;\n\ntypedef struct _scalarmin\n{\n    t_object x_obj;\n    t_float x_g;\n    t_float x_f;\n} t_scalarmin;\n\nstatic void *min_new(t_symbol *s, int argc, t_atom *argv)\n{\n    if (argc > 1) post(\"min~: extra arguments ignored\");\n    if (argc)\n    {\n        t_scalarmin *x = (t_scalarmin *)pd_new(scalarmin_class);\n        floatinlet_new(&x->x_obj, &x->x_g);\n        x->x_g = atom_getfloatarg(0, argc, argv);\n        outlet_new(&x->x_obj, &s_signal);\n        x->x_f = 0;\n        return (x);\n    }\n    else\n    {\n        t_min *x = (t_min *)pd_new(min_class);\n        inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);\n        outlet_new(&x->x_obj, &s_signal);\n        x->x_f = 0;\n        return (x);\n    }\n}\n\nt_int *min_perform(t_int *w)\n{\n    t_sample *in1 = (t_sample *)(w[1]);\n    t_sample *in2 = (t_sample *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    while (n--)\n    {\n        t_sample f = *in1++, g = *in2++;\n        *out++ = (f < g ? f : g);\n    }\n    return (w+5);\n}\n\nt_int *min_perf8(t_int *w)\n{\n    t_sample *in1 = (t_sample *)(w[1]);\n    t_sample *in2 = (t_sample *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    for (; n; n -= 8, in1 += 8, in2 += 8, out += 8)\n    {\n        t_sample f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3];\n        t_sample f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7];\n\n        t_sample g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3];\n        t_sample g4 = in2[4], g5 = in2[5], g6 = in2[6], g7 = in2[7];\n\n        out[0] = (f0 < g0 ? f0 : g0); out[1] = (f1 < g1 ? f1 : g1);\n        out[2] = (f2 < g2 ? f2 : g2); out[3] = (f3 < g3 ? f3 : g3);\n        out[4] = (f4 < g4 ? f4 : g4); out[5] = (f5 < g5 ? f5 : g5);\n        out[6] = (f6 < g6 ? f6 : g6); out[7] = (f7 < g7 ? f7 : g7);\n    }\n    return (w+5);\n}\n\nt_int *scalarmin_perform(t_int *w)\n{\n    t_sample *in = (t_sample *)(w[1]);\n    t_float f = *(t_float *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    while (n--)\n    {\n        t_sample g = *in++;\n        *out++ = (f < g ? f : g);\n    }\n    return (w+5);\n}\n\nt_int *scalarmin_perf8(t_int *w)\n{\n    t_sample *in = (t_sample *)(w[1]);\n    t_float g = *(t_float *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    for (; n; n -= 8, in += 8, out += 8)\n    {\n        t_sample f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3];\n        t_sample f4 = in[4], f5 = in[5], f6 = in[6], f7 = in[7];\n\n        out[0] = (f0 < g ? f0 : g); out[1] = (f1 < g ? f1 : g);\n        out[2] = (f2 < g ? f2 : g); out[3] = (f3 < g ? f3 : g);\n        out[4] = (f4 < g ? f4 : g); out[5] = (f5 < g ? f5 : g);\n        out[6] = (f6 < g ? f6 : g); out[7] = (f7 < g ? f7 : g);\n    }\n    return (w+5);\n}\n\nstatic void min_dsp(t_min *x, t_signal **sp)\n{\n    any_binop_dsp(sp, min_perform, min_perf8,\n        scalarmin_perform, scalarmin_perf8,\n        scalarmin_perform, scalarmin_perf8);\n}\n\nstatic void scalarmin_dsp(t_scalarmin *x, t_signal **sp)\n{\n    any_binop_scalar_dsp(sp, &x->x_g, scalarmin_perform, scalarmin_perf8);\n}\n\nstatic void min_setup(void)\n{\n    min_class = class_new(gensym(\"min~\"), (t_newmethod)min_new, 0,\n        sizeof(t_min),\n            CLASS_MULTICHANNEL | CLASS_NOPROMOTESIG | CLASS_NOPROMOTELEFT,\n                A_GIMME, 0);\n    CLASS_MAINSIGNALIN(min_class, t_min, x_f);\n    class_addmethod(min_class, (t_method)min_dsp, gensym(\"dsp\"), A_CANT, 0);\n    class_sethelpsymbol(min_class, gensym(\"binops-tilde\"));\n    scalarmin_class = class_new(gensym(\"min~\"), 0, 0,\n        sizeof(t_scalarmin), CLASS_MULTICHANNEL, 0);\n    CLASS_MAINSIGNALIN(scalarmin_class, t_scalarmin, x_f);\n    class_addmethod(scalarmin_class, (t_method)scalarmin_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_sethelpsymbol(scalarmin_class, gensym(\"binops-tilde\"));\n}\n\n/* ----------------------------- log ----------------------------- */\nstatic t_class *log_tilde_class, *scalarlog_tilde_class;\n\ntypedef struct _log_tilde\n{\n    t_object x_obj;\n    t_float x_f;\n} t_log_tilde;\n\ntypedef struct _scalarlog_tilde\n{\n    t_object x_obj;\n    t_float x_f;\n    t_float x_g;\n} t_scalarlog_tilde;\n\nstatic void *log_tilde_new(t_symbol *s, int argc, t_atom *argv)\n{\n    if (argc > 1) post(\"log~: extra arguments ignored\");\n    if (argc)\n    {\n        t_scalarlog_tilde *x =\n            (t_scalarlog_tilde *)pd_new(scalarlog_tilde_class);\n        floatinlet_new(&x->x_obj, &x->x_g);\n        x->x_g = atom_getfloatarg(0, argc, argv);\n        outlet_new(&x->x_obj, &s_signal);\n        x->x_f = 0;\n        return (x);\n    }\n    else\n    {\n        t_log_tilde *x = (t_log_tilde *)pd_new(log_tilde_class);\n        inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);\n        outlet_new(&x->x_obj, &s_signal);\n        x->x_f = 0;\n        return (x);\n    }\n}\n\nt_int *log_tilde_perform(t_int *w)\n{\n    t_sample *in1 = (t_sample *)(w[1]);\n    t_sample *in2 = (t_sample *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    while (n--)\n    {\n        t_sample f = *in1++, g = *in2++;\n        if (f <= 0)\n            *out = -1000;   /* rather than blow up, output a number << 0 */\n        else if (g == 1 || g <= 0)\n            *out = log(f);\n        else *out = log(f)/log(g);\n        out++;\n    }\n    return (w+5);\n}\n\nt_int *log_tilde_perform_scalar(t_int *w)\n{\n    t_sample *in1 = (t_sample *)(w[1]);\n    t_sample base = *(t_sample *)(w[2]),\n        multiplier = ((base > 0 && base != 1) ? 1./log(base) : 1);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    while (n--)\n    {\n        t_sample f = *in1++;\n        if (f <= 0)\n            *out = -1000;   /* rather than blow up, output a number << 0 */\n        else *out = log(f) * multiplier;\n        out++;\n    }\n    return (w+5);\n}\n\n    /* nobody sane will ever ask for log(scalar) to a signal base but ok... */\nt_int *log_tilde_perform_reversescalar(t_int *w)\n{\n    t_sample *in1 = (t_sample *)(w[1]);\n    t_sample scalarin = *(t_sample *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    while (n--)\n    {\n        t_sample base = *in1++;\n        if (base <= 1)\n            *out = -1000;   /* rather than blow up, output a number << 0 */\n        else if (scalarin <- 0)\n            *out = -1000;\n        else *out = log(scalarin) / log(base);\n        out++;\n    }\n    return (w+5);\n}\n\nstatic void log_tilde_dsp(t_log_tilde *x, t_signal **sp)\n{\n    any_binop_dsp(sp, log_tilde_perform, log_tilde_perform,\n        log_tilde_perform_scalar, log_tilde_perform_scalar,\n        log_tilde_perform_reversescalar, log_tilde_perform_reversescalar);\n}\n\nstatic void scalarlog_tilde_dsp(t_scalarlog_tilde *x, t_signal **sp)\n{\n    any_binop_scalar_dsp(sp, &x->x_g, log_tilde_perform_scalar,\n        log_tilde_perform_scalar);\n}\n\nstatic void log_tilde_setup(void)\n{\n    log_tilde_class = class_new(gensym(\"log~\"), (t_newmethod)log_tilde_new, 0,\n        sizeof(t_log_tilde),\n            CLASS_MULTICHANNEL | CLASS_NOPROMOTESIG | CLASS_NOPROMOTELEFT,\n                A_GIMME, 0);\n    CLASS_MAINSIGNALIN(log_tilde_class, t_log_tilde, x_f);\n    class_addmethod(log_tilde_class, (t_method)log_tilde_dsp, gensym(\"dsp\"), A_CANT, 0);\n    class_sethelpsymbol(log_tilde_class, gensym(\"binops-tilde\"));\n    scalarlog_tilde_class = class_new(gensym(\"log~\"), 0, 0,\n        sizeof(t_scalarlog_tilde), CLASS_MULTICHANNEL, 0);\n    CLASS_MAINSIGNALIN(scalarlog_tilde_class, t_scalarlog_tilde, x_f);\n    class_addmethod(scalarlog_tilde_class, (t_method)scalarlog_tilde_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_sethelpsymbol(scalarlog_tilde_class, gensym(\"binops-tilde\"));\n}\n\n/* ----------------------------- pow ----------------------------- */\nstatic t_class *pow_tilde_class, *scalarpow_tilde_class;\n\ntypedef struct _pow_tilde\n{\n    t_object x_obj;\n    t_float x_f;\n} t_pow_tilde;\n\ntypedef struct _scalarpow_tilde\n{\n    t_object x_obj;\n    t_float x_f;\n    t_float x_g;\n} t_scalarpow_tilde;\n\nstatic void *pow_tilde_new(t_symbol *s, int argc, t_atom *argv)\n{\n    if (argc > 1) post(\"pow~: extra arguments ignored\");\n    if (argc)\n    {\n        t_scalarpow_tilde *x =\n            (t_scalarpow_tilde *)pd_new(scalarpow_tilde_class);\n        floatinlet_new(&x->x_obj, &x->x_g);\n        x->x_g = atom_getfloatarg(0, argc, argv);\n        outlet_new(&x->x_obj, &s_signal);\n        x->x_f = 0;\n        return (x);\n    }\n    else\n    {\n        t_pow_tilde *x = (t_pow_tilde *)pd_new(pow_tilde_class);\n        inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);\n        outlet_new(&x->x_obj, &s_signal);\n        x->x_f = 0;\n        return (x);\n    }\n}\n\nt_int *pow_tilde_perform(t_int *w)\n{\n    t_sample *in1 = (t_sample *)(w[1]);\n    t_sample *in2 = (t_sample *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    while (n--)\n    {\n        t_sample f = *in1++, g = *in2++;\n        *out++ = (f == 0 && g < 0) ||\n            (f < 0 && (g - (int)g) != 0) ?\n                0 : pow(f, g);\n    }\n    return (w+5);\n}\n\nt_int *pow_tilde_perform_scalar(t_int *w)\n{\n    t_sample *in1 = (t_sample *)(w[1]);\n    t_sample g = *(t_sample *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    while (n--)\n    {\n        t_sample f = *in1++;\n        *out++ = (f == 0 && g < 0) ||\n            (f < 0 && (g - (int)g) != 0) ?\n                0 : pow(f, g);\n    }\n    return (w+5);\n}\n\nt_int *pow_tilde_perform_reversescalar(t_int *w)\n{\n    t_sample *in1 = (t_sample *)(w[1]);\n    t_sample f = *(t_sample *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    while (n--)\n    {\n        t_sample g = *in1++;\n        *out++ = (f == 0 && g < 0) ||\n            (f < 0 && (g - (int)g) != 0) ?\n                0 : pow(f, g);\n    }\n    return (w+5);\n}\n\nstatic void pow_tilde_dsp(t_pow_tilde *x, t_signal **sp)\n{\n    any_binop_dsp(sp, pow_tilde_perform, pow_tilde_perform,\n        pow_tilde_perform_scalar, pow_tilde_perform_scalar,\n        pow_tilde_perform_reversescalar, pow_tilde_perform_reversescalar);\n}\n\nstatic void scalarpow_tilde_dsp(t_scalarpow_tilde *x, t_signal **sp)\n{\n    any_binop_scalar_dsp(sp, &x->x_g, pow_tilde_perform_scalar,\n        pow_tilde_perform_scalar);\n}\n\nstatic void pow_tilde_setup(void)\n{\n    pow_tilde_class = class_new(gensym(\"pow~\"), (t_newmethod)pow_tilde_new, 0,\n        sizeof(t_pow_tilde),\n            CLASS_MULTICHANNEL | CLASS_NOPROMOTESIG | CLASS_NOPROMOTELEFT,\n                A_GIMME, 0);\n    CLASS_MAINSIGNALIN(pow_tilde_class, t_pow_tilde, x_f);\n    class_addmethod(pow_tilde_class, (t_method)pow_tilde_dsp, gensym(\"dsp\"), A_CANT, 0);\n    class_sethelpsymbol(pow_tilde_class, gensym(\"binops-tilde\"));\n    scalarpow_tilde_class = class_new(gensym(\"pow~\"), 0, 0,\n        sizeof(t_scalarpow_tilde), CLASS_MULTICHANNEL, 0);\n    CLASS_MAINSIGNALIN(scalarpow_tilde_class, t_scalarpow_tilde, x_f);\n    class_addmethod(scalarpow_tilde_class, (t_method)scalarpow_tilde_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_sethelpsymbol(scalarpow_tilde_class, gensym(\"binops-tilde\"));\n}\n\n/* ----------------------- global setup routine ---------------- */\nvoid d_arithmetic_setup(void)\n{\n    plus_setup();\n    minus_setup();\n    times_setup();\n    over_setup();\n    max_setup();\n    min_setup();\n    log_tilde_setup();\n    pow_tilde_setup();\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/d_array.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette and others.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* reading and writing arrays */\n/* LATER consider adding methods to set gpointer */\n\n#include \"m_pd.h\"\n#include \"g_canvas.h\"\n\n    /* common struct for reading or writing to an array at DSP time. */\ntypedef struct _dsparray\n{\n    t_symbol *d_symbol;\n    t_gpointer d_gp;\n    int d_phase;    /* used for tabwrite~ and tabplay~ */\n    void *d_owner;  /* for pd_error() */\n} t_dsparray;\n\ntypedef struct _arrayvec\n{\n    int v_n;\n    t_dsparray *v_vec;\n} t_arrayvec;\n\n    /* LATER consider exporting this and using it for tabosc4~ too */\nstatic int dsparray_get_array(t_dsparray *d, int *npoints, t_word **vec,\n    int recover)\n{\n    t_garray *a;\n\n    if (gpointer_check(&d->d_gp, 0))\n    {\n        *vec = (t_word *)d->d_gp.gp_stub->gs_un.gs_array->a_vec;\n        *npoints = d->d_gp.gp_stub->gs_un.gs_array->a_n;\n        return 1;\n    }\n    else if (recover || d->d_gp.gp_stub)\n        /* when the pointer is invalid: if \"recover\" is true or if the array\n        has already been successfully acquired, then try re-acquiring it */\n    {\n        if (!(a = (t_garray *)pd_findbyclass(d->d_symbol, garray_class)))\n        {\n            if (d->d_owner && *d->d_symbol->s_name)\n                pd_error(d->d_owner, \"%s: no such array\", d->d_symbol->s_name);\n            gpointer_unset(&d->d_gp);\n            return 0;\n        }\n        else if (!garray_getfloatwords(a, npoints, vec))\n        {\n            if (d->d_owner)\n                pd_error(d->d_owner, \"%s: bad template\", d->d_symbol->s_name);\n            gpointer_unset(&d->d_gp);\n            return 0;\n        }\n        else\n        {\n            gpointer_setarray(&d->d_gp, garray_getarray(a), *vec);\n            return 1;\n        }\n    }\n    return 0;\n}\n\nstatic void arrayvec_testvec(t_arrayvec *v)\n{\n    int i, vecsize;\n    t_word *vec;\n    for (i = 0; i < v->v_n; i++)\n    {\n        if (*v->v_vec[i].d_symbol->s_name)\n            dsparray_get_array(&v->v_vec[i], &vecsize, &vec, 1);\n    }\n}\n\nstatic void arrayvec_set(t_arrayvec *v, int argc, t_atom *argv)\n{\n    int i;\n    for (i = 0; i < v->v_n && i < argc; i++)\n    {\n        gpointer_unset(&v->v_vec[i].d_gp); /* reset the pointer */\n        if (argv[i].a_type != A_SYMBOL)\n            pd_error(v->v_vec[i].d_owner,\n                \"expected symbolic array name, got number instead\"),\n                v->v_vec[i].d_symbol = &s_;\n        else\n        {\n            v->v_vec[i].d_phase = 0x7fffffff;\n            v->v_vec[i].d_symbol = argv[i].a_w.w_symbol;\n        }\n    }\n    if (pd_getdspstate())\n        arrayvec_testvec(v);\n}\n\nstatic void arrayvec_init(t_arrayvec *v, void *x, int rawargc, t_atom *rawargv)\n{\n    int i, argc;\n    t_atom a, *argv;\n    if (rawargc == 0)\n    {\n        argc = 1;\n        SETSYMBOL(&a, &s_);\n        argv = &a;\n    }\n    else argc = rawargc, argv=rawargv;\n\n    v->v_vec = (t_dsparray *)getbytes(argc * sizeof(*v->v_vec));\n    v->v_n = argc;\n    for (i = 0; i < v->v_n; i++)\n    {\n        v->v_vec[i].d_owner = x;\n        v->v_vec[i].d_phase = 0x7fffffff;\n        gpointer_init(&v->v_vec[i].d_gp);\n    }\n    arrayvec_set(v, argc, argv);\n}\n\nstatic void arrayvec_free(t_arrayvec *v)\n{\n    int i;\n    for (i = 0; i < v->v_n; i++)\n        gpointer_unset(&v->v_vec[i].d_gp);\n    freebytes(v->v_vec, v->v_n * sizeof(*v->v_vec));\n}\n\n/* ------------------------- tabwrite~ -------------------------- */\n\nstatic t_class *tabwrite_tilde_class;\n\ntypedef struct _tabwrite_tilde\n{\n    t_object x_obj;\n    t_arrayvec x_v;\n    t_float x_f;\n} t_tabwrite_tilde;\n\nstatic void *tabwrite_tilde_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_tabwrite_tilde *x = (t_tabwrite_tilde *)pd_new(tabwrite_tilde_class);\n    arrayvec_init(&x->x_v, x, argc, argv);\n    x->x_f = 0;\n    return (x);\n}\n\nstatic void tabwrite_tilde_redraw(t_symbol *arraysym)\n{\n    t_garray *a = (t_garray *)pd_findbyclass(arraysym, garray_class);\n    if (!a)\n        bug(\"tabwrite_tilde_redraw\");\n    else garray_redraw(a);\n}\n\nstatic t_int *tabwrite_tilde_perform(t_int *w)\n{\n    t_dsparray *d = (t_dsparray *)(w[1]);\n    t_sample *in = (t_sample *)(w[2]);\n    int n = (int)(w[3]), phase = d->d_phase, endphase;\n    t_word *buf;\n\n    if (!dsparray_get_array(d, &endphase, &buf, 0))\n        goto noop;\n\n    if (phase < endphase)\n    {\n        int nxfer = endphase - phase;\n        t_word *wp = buf + phase;\n        if (nxfer > n)\n            nxfer = n;\n        phase += nxfer;\n        while (nxfer--)\n        {\n            t_sample f = *in++;\n            if (PD_BIGORSMALL(f))\n                f = 0;\n            (wp++)->w_float = f;\n        }\n        if (phase >= endphase)\n        {\n            tabwrite_tilde_redraw(d->d_symbol);\n            phase = 0x7fffffff;\n        }\n        d->d_phase = phase;\n    }\n    else d->d_phase = 0x7fffffff;\nnoop:\n    return (w+4);\n}\n\nstatic void tabwrite_tilde_set(t_tabwrite_tilde *x, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    arrayvec_set(&x->x_v, argc, argv);\n}\n\nstatic void tabwrite_tilde_dsp(t_tabwrite_tilde *x, t_signal **sp)\n{\n    int i, nchans = (sp[0]->s_nchans < x->x_v.v_n ?\n        sp[0]->s_nchans : x->x_v.v_n);\n    arrayvec_testvec(&x->x_v);\n    for (i = 0; i < nchans; i++)\n        dsp_add(tabwrite_tilde_perform, 3, x->x_v.v_vec+i,\n            sp[0]->s_vec + i * sp[0]->s_length, (t_int)sp[0]->s_length);\n}\n\nstatic void tabwrite_tilde_start(t_tabwrite_tilde *x, t_floatarg f)\n{\n    int i;\n    for (i = 0; i < x->x_v.v_n; i++)\n        x->x_v.v_vec[i].d_phase = (f > 0 ? f : 0);\n}\n\nstatic void tabwrite_tilde_bang(t_tabwrite_tilde *x)\n{\n    tabwrite_tilde_start(x, 0);\n}\n\nstatic void tabwrite_tilde_stop(t_tabwrite_tilde *x)\n{\n    int i;\n    for (i = 0; i < x->x_v.v_n; i++)\n        if (x->x_v.v_vec[i].d_phase != 0x7fffffff)\n    {\n        tabwrite_tilde_redraw(x->x_v.v_vec[i].d_symbol);\n        x->x_v.v_vec[i].d_phase = 0x7fffffff;\n    }\n}\n\nstatic void tabwrite_tilde_free(t_tabwrite_tilde *x)\n{\n    arrayvec_free(&x->x_v);\n}\n\nstatic void tabwrite_tilde_setup(void)\n{\n    tabwrite_tilde_class = class_new(gensym(\"tabwrite~\"),\n        (t_newmethod)tabwrite_tilde_new, (t_method)tabwrite_tilde_free,\n        sizeof(t_tabwrite_tilde), CLASS_MULTICHANNEL, A_GIMME, 0);\n    CLASS_MAINSIGNALIN(tabwrite_tilde_class, t_tabwrite_tilde, x_f);\n    class_addmethod(tabwrite_tilde_class, (t_method)tabwrite_tilde_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_addmethod(tabwrite_tilde_class, (t_method)tabwrite_tilde_set,\n        gensym(\"set\"), A_GIMME, 0);\n    class_addmethod(tabwrite_tilde_class, (t_method)tabwrite_tilde_stop,\n        gensym(\"stop\"), 0);\n    class_addmethod(tabwrite_tilde_class, (t_method)tabwrite_tilde_start,\n        gensym(\"start\"), A_DEFFLOAT, 0);\n    class_addbang(tabwrite_tilde_class, tabwrite_tilde_bang);\n}\n\n/* ------------ tabplay~ - non-transposing sample playback --------------- */\n\nstatic t_class *tabplay_tilde_class;\n\ntypedef struct _tabplay_tilde\n{\n    t_object x_obj;\n    t_outlet *x_bangout;\n    int x_limit;\n    t_clock *x_clock;\n    t_arrayvec x_v;\n} t_tabplay_tilde;\n\nstatic void tabplay_tilde_tick(t_tabplay_tilde *x);\n\nstatic void *tabplay_tilde_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_tabplay_tilde *x = (t_tabplay_tilde *)pd_new(tabplay_tilde_class);\n    x->x_clock = clock_new(x, (t_method)tabplay_tilde_tick);\n    outlet_new(&x->x_obj, &s_signal);\n    x->x_bangout = outlet_new(&x->x_obj, &s_bang);\n    arrayvec_init(&x->x_v, x, argc, argv);\n    x->x_limit = 0;\n    return (x);\n}\n\nstatic t_int *tabplay_tilde_perform(t_int *w)\n{\n    t_tabplay_tilde *x = (t_tabplay_tilde *)(w[1]);\n    t_dsparray *d = (t_dsparray *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    t_word *wp;\n    int n = (int)(w[4]), phase = d->d_phase, endphase, nxfer, n3;\n    t_word *buf;\n\n    if (!dsparray_get_array(d, &endphase, &buf, 0) || phase >= endphase)\n        goto zero;\n    if (endphase > x->x_limit)\n        endphase = x->x_limit;\n    nxfer = endphase - phase;\n    wp = buf + phase;\n    if (nxfer > n)\n        nxfer = n;\n    n3 = n - nxfer;\n    phase += nxfer;\n    while (nxfer--)\n        *out++ = (wp++)->w_float;\n    if (phase >= endphase)\n    {\n        int i, playing = 0;\n        d->d_phase = 0x7fffffff;\n            /* set the clock when all channels have run out */\n        for (i = 0; i < x->x_v.v_n; i++)\n            if (x->x_v.v_vec[i].d_phase < 0x7fffffff)\n                playing = 1;\n        if (!playing)\n            clock_delay(x->x_clock, 0);\n        while (n3--)\n            *out++ = 0;\n    }\n    else d->d_phase = phase;\n\n    return (w+5);\nzero:\n    while (n--) *out++ = 0;\n    return (w+5);\n}\n\nstatic void tabplay_tilde_set(t_tabplay_tilde *x, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    arrayvec_set(&x->x_v, argc, argv);\n}\n\nstatic void tabplay_tilde_dsp(t_tabplay_tilde *x, t_signal **sp)\n{\n    int i;\n    signal_setmultiout(&sp[0], x->x_v.v_n);\n    arrayvec_testvec(&x->x_v);\n    for (i = 0; i < x->x_v.v_n; i++)\n        dsp_add(tabplay_tilde_perform, 4, x, &x->x_v.v_vec[i],\n            sp[0]->s_vec + i * sp[0]->s_length, (t_int)sp[0]->s_length);\n}\n\nstatic void tabplay_tilde_list(t_tabplay_tilde *x, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    long start = atom_getfloatarg(0, argc, argv);\n    long length = atom_getfloatarg(1, argc, argv);\n    int i;\n    if (start < 0) start = 0;\n    if (length <= 0)\n        x->x_limit = 0x7fffffff;\n    else\n        x->x_limit = (int)(start + length);\n    for (i = 0; i < x->x_v.v_n; i++)\n        x->x_v.v_vec[i].d_phase = (int)start;\n}\n\nstatic void tabplay_tilde_stop(t_tabplay_tilde *x)\n{\n    int i;\n    for (i = 0; i < x->x_v.v_n; i++)\n        x->x_v.v_vec[i].d_phase = 0x7fffffff;\n}\n\nstatic void tabplay_tilde_tick(t_tabplay_tilde *x)\n{\n    outlet_bang(x->x_bangout);\n}\n\nstatic void tabplay_tilde_free(t_tabplay_tilde *x)\n{\n    clock_free(x->x_clock);\n    arrayvec_free(&x->x_v);\n}\n\nstatic void tabplay_tilde_setup(void)\n{\n    tabplay_tilde_class = class_new(gensym(\"tabplay~\"),\n        (t_newmethod)tabplay_tilde_new, (t_method)tabplay_tilde_free,\n        sizeof(t_tabplay_tilde), CLASS_MULTICHANNEL, A_GIMME, 0);\n    class_addmethod(tabplay_tilde_class, (t_method)tabplay_tilde_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_addmethod(tabplay_tilde_class, (t_method)tabplay_tilde_stop,\n        gensym(\"stop\"), 0);\n    class_addmethod(tabplay_tilde_class, (t_method)tabplay_tilde_set,\n        gensym(\"set\"), A_GIMME, 0);\n    class_addlist(tabplay_tilde_class, tabplay_tilde_list);\n}\n\n/******************** tabread~ ***********************/\n\nstatic t_class *tabread_tilde_class;\n\ntypedef struct _tabread_tilde\n{\n    t_object x_obj;\n    t_arrayvec x_v;\n    t_float x_f;\n} t_tabread_tilde;\n\nstatic void *tabread_tilde_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_tabread_tilde *x = (t_tabread_tilde *)pd_new(tabread_tilde_class);\n    arrayvec_init(&x->x_v, x, argc, argv);\n    outlet_new(&x->x_obj, gensym(\"signal\"));\n    x->x_f = 0;\n    return (x);\n}\n\nstatic t_int *tabread_tilde_perform(t_int *w)\n{\n    t_dsparray *d = (t_dsparray *)(w[1]);\n    t_sample *in = (t_sample *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]), i, maxindex;\n    t_word *buf;\n\n    if (!dsparray_get_array(d, &maxindex, &buf, 0))\n        goto zero;\n    maxindex -= 1;\n\n    for (i = 0; i < n; i++)\n    {\n        int index = *in++;\n        if (index < 0)\n            index = 0;\n        else if (index > maxindex)\n            index = maxindex;\n        *out++ = buf[index].w_float;\n    }\n    return (w+5);\n zero:\n    while (n--) *out++ = 0;\n\n    return (w+5);\n}\n\nstatic void tabread_tilde_set(t_tabread_tilde *x, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    arrayvec_set(&x->x_v, argc, argv);\n}\n\nstatic void tabread_tilde_dsp(t_tabread_tilde *x, t_signal **sp)\n{\n    int i;\n    signal_setmultiout(&sp[1], x->x_v.v_n);\n    arrayvec_testvec(&x->x_v);\n    for (i = 0; i < x->x_v.v_n; i++)\n        dsp_add(tabread_tilde_perform, 4, &x->x_v.v_vec[i],\n            sp[0]->s_vec + (i%(sp[0]->s_nchans)) * sp[0]->s_length,\n                sp[1]->s_vec + i * sp[0]->s_length, (t_int)sp[0]->s_length);\n\n}\n\nstatic void tabread_tilde_free(t_tabread_tilde *x)\n{\n    arrayvec_free(&x->x_v);\n}\n\nstatic void tabread_tilde_setup(void)\n{\n    tabread_tilde_class = class_new(gensym(\"tabread~\"),\n        (t_newmethod)tabread_tilde_new, (t_method)tabread_tilde_free,\n        sizeof(t_tabread_tilde), CLASS_MULTICHANNEL, A_GIMME, 0);\n    CLASS_MAINSIGNALIN(tabread_tilde_class, t_tabread_tilde, x_f);\n    class_addmethod(tabread_tilde_class, (t_method)tabread_tilde_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_addmethod(tabread_tilde_class, (t_method)tabread_tilde_set,\n        gensym(\"set\"), A_GIMME, 0);\n}\n\n/******************** tabread4~ ***********************/\n\nstatic t_class *tabread4_tilde_class;\n\ntypedef struct _tabread4_tilde\n{\n    t_object x_obj;\n    t_arrayvec x_v;\n    t_float x_f;\n    t_float x_onset;\n} t_tabread4_tilde;\n\nstatic void *tabread4_tilde_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_tabread4_tilde *x = (t_tabread4_tilde *)pd_new(tabread4_tilde_class);\n    arrayvec_init(&x->x_v, x, argc, argv);\n    outlet_new(&x->x_obj, gensym(\"signal\"));\n    floatinlet_new(&x->x_obj, &x->x_onset);\n    x->x_f = x->x_onset = 0;\n    return (x);\n}\n\nstatic t_int *tabread4_tilde_perform(t_int *w)\n{\n    t_dsparray *d = (t_dsparray *)(w[1]);\n    double onset = *(t_float *)(w[2]);\n    t_sample *in = (t_sample *)(w[3]);\n    t_sample *out = (t_sample *)(w[4]);\n    int n = (int)(w[5]);\n    int maxindex, i;\n    t_word *buf, *wp;\n    const t_sample one_over_six = 1./6.;\n\n    if (!dsparray_get_array(d, &maxindex, &buf, 0))\n        goto zero;\n\n    maxindex -= 3;\n    if (maxindex < 0) goto zero;\n    if (!buf || maxindex < 1)\n        goto zero;\n\n    for (i = 0; i < n; i++)\n    {\n        double findex = *in++ + onset;\n        int index = findex;\n        t_sample frac,  a,  b,  c,  d, cminusb;\n        if (index < 1)\n            index = 1, frac = 0;\n        else if (index > maxindex)\n            index = maxindex, frac = 1;\n        else frac = findex - index;\n        wp = buf + index;\n        a = wp[-1].w_float;\n        b = wp[0].w_float;\n        c = wp[1].w_float;\n        d = wp[2].w_float;\n        cminusb = c-b;\n        *out++ = b + frac * (\n            cminusb - one_over_six * ((t_sample)1.-frac) * (\n                (d - a - (t_sample)3.0 * cminusb) * frac +\n                (d + a*(t_sample)2.0 - b*(t_sample)3.0)\n            )\n        );\n    }\n    return (w+6);\n zero:\n    while (n--)\n        *out++ = 0;\n\n    return (w+6);\n}\n\nstatic void tabread4_tilde_set(t_tabread4_tilde *x, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    arrayvec_set(&x->x_v, argc, argv);\n}\n\nstatic void tabread4_tilde_dsp(t_tabread4_tilde *x, t_signal **sp)\n{\n    int i;\n    signal_setmultiout(&sp[1], x->x_v.v_n);\n    arrayvec_testvec(&x->x_v);\n    for (i = 0; i < x->x_v.v_n; i++)\n        dsp_add(tabread4_tilde_perform, 5, &x->x_v.v_vec[i], &x->x_onset,\n            sp[0]->s_vec + (i%(sp[0]->s_nchans)) * sp[0]->s_length,\n                sp[1]->s_vec + i * sp[0]->s_length, (t_int)sp[0]->s_length);\n\n}\n\nstatic void tabread4_tilde_free(t_tabread4_tilde *x)\n{\n    arrayvec_free(&x->x_v);\n}\n\nstatic void tabread4_tilde_setup(void)\n{\n    tabread4_tilde_class = class_new(gensym(\"tabread4~\"),\n        (t_newmethod)tabread4_tilde_new, (t_method)tabread4_tilde_free,\n        sizeof(t_tabread4_tilde), CLASS_MULTICHANNEL, A_GIMME, 0);\n    CLASS_MAINSIGNALIN(tabread4_tilde_class, t_tabread4_tilde, x_f);\n    class_addmethod(tabread4_tilde_class, (t_method)tabread4_tilde_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_addmethod(tabread4_tilde_class, (t_method)tabread4_tilde_set,\n        gensym(\"set\"), A_GIMME, 0);\n}\n\n\n/* ------------------------ tabsend~ ------------------------- */\n\nstatic t_class *tabsend_class;\n\ntypedef struct _tabsend\n{\n    t_object x_obj;\n    t_arrayvec x_v;\n    int x_graphperiod;\n    t_float x_f;\n} t_tabsend;\n\nstatic void tabsend_tick(t_tabsend *x);\n\nstatic void *tabsend_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_tabsend *x = (t_tabsend *)pd_new(tabsend_class);\n    arrayvec_init(&x->x_v, x, argc, argv);\n    x->x_graphperiod = 1;\n    x->x_f = 0;\n    return (x);\n}\n\nstatic t_int *tabsend_perform(t_int *w)\n{\n    t_tabsend *x = (t_tabsend *)(w[1]);\n    t_dsparray *d = (t_dsparray *)(w[2]);\n    t_sample *in = (t_sample *)(w[3]);\n    int n = (int)w[4], maxindex;\n    t_word *dest;\n    int phase = d->d_phase;\n\n    if (!dsparray_get_array(d, &maxindex, &dest, 0))\n        goto bad;\n\n    if (n > maxindex)\n        n = maxindex;\n    while (n--)\n    {\n        t_sample f = *in++;\n        if (PD_BIGORSMALL(f))\n            f = 0;\n         (dest++)->w_float = f;\n    }\n    if (phase++ >= x->x_graphperiod)\n    {\n        tabwrite_tilde_redraw(d->d_symbol);\n        phase = 0;\n    }\n    d->d_phase = phase;\nbad:\n    return (w+5);\n}\n\nstatic void tabsend_set(t_tabsend *x, t_symbol *s, int argc, t_atom *argv)\n{\n    arrayvec_set(&x->x_v, argc, argv);\n}\n\nstatic void tabsend_dsp(t_tabsend *x, t_signal **sp)\n{\n    int i, nchans = (sp[0]->s_nchans < x->x_v.v_n ?\n        sp[0]->s_nchans : x->x_v.v_n);\n    int length = sp[0]->s_length;\n    int tickspersec = sp[0]->s_sr/length;\n    if (tickspersec < 1)\n        tickspersec = 1;\n    x->x_graphperiod = tickspersec;\n    arrayvec_testvec(&x->x_v);\n    for (i = 0; i < nchans; i++)\n        dsp_add(tabsend_perform, 4, x, x->x_v.v_vec+i,\n            sp[0]->s_vec + i * sp[0]->s_length, (t_int)sp[0]->s_length);\n}\n\nstatic void tabsend_free(t_tabsend *x)\n{\n    arrayvec_free(&x->x_v);\n}\n\nstatic void tabsend_setup(void)\n{\n    tabsend_class = class_new(gensym(\"tabsend~\"),\n        (t_newmethod)tabsend_new, (t_method)tabsend_free,\n        sizeof(t_tabsend), CLASS_MULTICHANNEL, A_GIMME, 0);\n    CLASS_MAINSIGNALIN(tabsend_class, t_tabsend, x_f);\n    class_addmethod(tabsend_class, (t_method)tabsend_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_addmethod(tabsend_class, (t_method)tabsend_set,\n        gensym(\"set\"), A_GIMME, 0);\n    class_sethelpsymbol(tabsend_class, gensym(\"tabsend-receive~\"));\n}\n\n/* ------------------------ tabreceive~ ------------------------- */\n\nstatic t_class *tabreceive_class;\n\ntypedef struct _tabreceive\n{\n    t_object x_obj;\n    t_arrayvec x_v;\n} t_tabreceive;\n\nstatic void *tabreceive_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_tabreceive *x = (t_tabreceive *)pd_new(tabreceive_class);\n    outlet_new(&x->x_obj, &s_signal);\n    arrayvec_init(&x->x_v, x, argc, argv);\n    return (x);\n}\n\nstatic t_int *tabreceive_perform(t_int *w)\n{\n    t_dsparray *d = (t_dsparray *)(w[1]);\n    t_sample *out = (t_sample *)(w[2]);\n    int n = (int)w[3], maxindex;\n    t_word *from;\n\n    if (dsparray_get_array(d, &maxindex, &from, 0))\n    {\n        t_int vecsize = maxindex;\n        if (vecsize > n)\n            vecsize = n;\n        while (vecsize--)\n            *out++ = (from++)->w_float;\n        vecsize = n - maxindex;\n        if (vecsize > 0)\n            while (vecsize--)\n                *out++ = 0;\n    }\n    else while (n--)\n            *out++ = 0;\n    return (w+4);\n}\n\nstatic void tabreceive_set(t_tabreceive *x, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    arrayvec_set(&x->x_v, argc, argv);\n}\n\nstatic void tabreceive_dsp(t_tabreceive *x, t_signal **sp)\n{\n    int i;\n    signal_setmultiout(&sp[0], x->x_v.v_n);\n    arrayvec_testvec(&x->x_v);\n    for (i = 0; i < x->x_v.v_n; i++)\n        dsp_add(tabreceive_perform, 3, &x->x_v.v_vec[i],\n            sp[0]->s_vec + i * sp[0]->s_length, (t_int)sp[0]->s_length);\n}\n\nstatic void tabreceive_free(t_tabreceive *x)\n{\n    arrayvec_free(&x->x_v);\n}\n\nstatic void tabreceive_setup(void)\n{\n    tabreceive_class = class_new(gensym(\"tabreceive~\"),\n        (t_newmethod)tabreceive_new, (t_method)tabreceive_free,\n        sizeof(t_tabreceive), CLASS_MULTICHANNEL, A_GIMME, 0);\n    class_addmethod(tabreceive_class, (t_method)tabreceive_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_addmethod(tabreceive_class, (t_method)tabreceive_set,\n        gensym(\"set\"), A_GIMME, 0);\n    class_sethelpsymbol(tabreceive_class, gensym(\"tabsend-receive~\"));\n}\n\n/* ---------- tabread: control, non-interpolating ------------------------ */\n\nstatic t_class *tabread_class;\n\ntypedef struct _tabread\n{\n    t_object x_obj;\n    t_symbol *x_arrayname;\n} t_tabread;\n\nstatic void tabread_float(t_tabread *x, t_float f)\n{\n    t_garray *a;\n    int npoints;\n    t_word *vec;\n\n    if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class)))\n        pd_error(x, \"%s: no such array\", x->x_arrayname->s_name);\n    else if (!garray_getfloatwords(a, &npoints, &vec))\n        pd_error(x, \"%s: bad template for tabread\", x->x_arrayname->s_name);\n    else\n    {\n        int n = f;\n        if (n < 0) n = 0;\n        else if (n >= npoints) n = npoints - 1;\n        outlet_float(x->x_obj.ob_outlet, (npoints ? vec[n].w_float : 0));\n    }\n}\n\nstatic void tabread_set(t_tabread *x, t_symbol *s)\n{\n    x->x_arrayname = s;\n}\n\nstatic void *tabread_new(t_symbol *s)\n{\n    t_tabread *x = (t_tabread *)pd_new(tabread_class);\n    x->x_arrayname = s;\n    outlet_new(&x->x_obj, &s_float);\n    return (x);\n}\n\nstatic void tabread_setup(void)\n{\n    tabread_class = class_new(gensym(\"tabread\"), (t_newmethod)tabread_new,\n        0, sizeof(t_tabread), 0, A_DEFSYM, 0);\n    class_addfloat(tabread_class, (t_method)tabread_float);\n    class_addmethod(tabread_class, (t_method)tabread_set, gensym(\"set\"),\n        A_SYMBOL, 0);\n}\n\n/* ---------- tabread4: control, 4-point interpolation --------------- */\n\nstatic t_class *tabread4_class;\n\ntypedef struct _tabread4\n{\n    t_object x_obj;\n    t_symbol *x_arrayname;\n} t_tabread4;\n\nstatic void tabread4_float(t_tabread4 *x, t_float f)\n{\n    t_garray *a;\n    int npoints;\n    t_word *vec;\n\n    if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class)))\n        pd_error(x, \"%s: no such array\", x->x_arrayname->s_name);\n    else if (!garray_getfloatwords(a, &npoints, &vec))\n        pd_error(x, \"%s: bad template for tabread4\", x->x_arrayname->s_name);\n    else if (npoints < 4)\n        outlet_float(x->x_obj.ob_outlet, 0);\n    else if (f <= 1)\n        outlet_float(x->x_obj.ob_outlet, vec[1].w_float);\n    else if (f >= npoints - 2)\n        outlet_float(x->x_obj.ob_outlet, vec[npoints - 2].w_float);\n    else\n    {\n        int n = f;\n        float a, b, c, d, cminusb, frac;\n        t_word *wp;\n        if (n >= npoints - 2)\n            n = npoints - 3;\n        wp = vec + n;\n        frac = f - n;\n        a = wp[-1].w_float;\n        b = wp[0].w_float;\n        c = wp[1].w_float;\n        d = wp[2].w_float;\n        cminusb = c-b;\n        outlet_float(x->x_obj.ob_outlet, b + frac * (\n            cminusb - 0.1666667f * (1.-frac) * (\n                (d - a - 3.0f * cminusb) * frac + (d + 2.0f*a - 3.0f*b))));\n    }\n}\n\nstatic void tabread4_set(t_tabread4 *x, t_symbol *s)\n{\n    x->x_arrayname = s;\n}\n\nstatic void *tabread4_new(t_symbol *s)\n{\n    t_tabread4 *x = (t_tabread4 *)pd_new(tabread4_class);\n    x->x_arrayname = s;\n    outlet_new(&x->x_obj, &s_float);\n    return (x);\n}\n\nstatic void tabread4_setup(void)\n{\n    tabread4_class = class_new(gensym(\"tabread4\"), (t_newmethod)tabread4_new,\n        0, sizeof(t_tabread4), 0, A_DEFSYM, 0);\n    class_addfloat(tabread4_class, (t_method)tabread4_float);\n    class_addmethod(tabread4_class, (t_method)tabread4_set, gensym(\"set\"),\n        A_SYMBOL, 0);\n}\n\n/* ------------------ tabwrite: control ------------------------ */\n\nstatic t_class *tabwrite_class;\n\ntypedef struct _tabwrite\n{\n    t_object x_obj;\n    t_symbol *x_arrayname;\n    t_float x_ft1;\n} t_tabwrite;\n\nstatic void tabwrite_float(t_tabwrite *x, t_float f)\n{\n    int vecsize;\n    t_garray *a;\n    t_word *vec;\n\n    if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class)))\n        pd_error(x, \"%s: no such array\", x->x_arrayname->s_name);\n    else if (!garray_getfloatwords(a, &vecsize, &vec))\n        pd_error(x, \"%s: bad template for tabwrite\", x->x_arrayname->s_name);\n    else\n    {\n        int n = x->x_ft1;\n        if (n < 0)\n            n = 0;\n        else if (n >= vecsize)\n            n = vecsize-1;\n        vec[n].w_float = f;\n        garray_redraw(a);\n    }\n}\n\nstatic void tabwrite_set(t_tabwrite *x, t_symbol *s)\n{\n    x->x_arrayname = s;\n}\n\nstatic void *tabwrite_new(t_symbol *s)\n{\n    t_tabwrite *x = (t_tabwrite *)pd_new(tabwrite_class);\n    x->x_ft1 = 0;\n    x->x_arrayname = s;\n    floatinlet_new(&x->x_obj, &x->x_ft1);\n    return (x);\n}\n\nvoid tabwrite_setup(void)\n{\n    tabwrite_class = class_new(gensym(\"tabwrite\"), (t_newmethod)tabwrite_new,\n        0, sizeof(t_tabwrite), 0, A_DEFSYM, 0);\n    class_addfloat(tabwrite_class, (t_method)tabwrite_float);\n    class_addmethod(tabwrite_class, (t_method)tabwrite_set, gensym(\"set\"),\n        A_SYMBOL, 0);\n}\n\n/* ------------------------ global setup routine ------------------------- */\n\nvoid d_array_setup(void)\n{\n    tabwrite_tilde_setup();\n    tabplay_tilde_setup();\n    tabread_tilde_setup();\n    tabread4_tilde_setup();\n    tabsend_setup();\n    tabreceive_setup();\n    tabread_setup();\n    tabread4_setup();\n    tabwrite_setup();\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/d_ctl.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/*  sig~ and line~ control-to-signal converters;\n    snapshot~ signal-to-control converter.\n*/\n\n#include \"m_pd.h\"\n#include \"math.h\"\n\n/* -------------------------- sig~ ------------------------------ */\nstatic t_class *sig_tilde_class;\n\ntypedef struct _sig\n{\n    t_object x_obj;\n    t_float x_f;\n} t_sig;\n\nstatic void sig_tilde_float(t_sig *x, t_float f)\n{\n    x->x_f = f;\n}\n\nstatic void sig_tilde_dsp(t_sig *x, t_signal **sp)\n{\n    dsp_add_scalarcopy(&x->x_f, sp[0]->s_vec, (t_int)sp[0]->s_n);\n}\n\nstatic void *sig_tilde_new(t_floatarg f)\n{\n    t_sig *x = (t_sig *)pd_new(sig_tilde_class);\n    x->x_f = f;\n    outlet_new(&x->x_obj, gensym(\"signal\"));\n    return (x);\n}\n\nstatic void sig_tilde_setup(void)\n{\n    sig_tilde_class = class_new(gensym(\"sig~\"), (t_newmethod)sig_tilde_new, 0,\n        sizeof(t_sig), 0, A_DEFFLOAT, 0);\n    class_addfloat(sig_tilde_class, (t_method)sig_tilde_float);\n    class_addmethod(sig_tilde_class, (t_method)sig_tilde_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n}\n\n/* -------------------------- line~ ------------------------------ */\nstatic t_class *line_tilde_class;\n\ntypedef struct _line\n{\n    t_object x_obj;\n    t_sample x_target; /* target value of ramp */\n    t_sample x_value; /* current value of ramp at block-borders */\n    t_sample x_biginc;\n    t_sample x_inc;\n    t_float x_1overn;\n    t_float x_dspticktomsec;\n    t_float x_inletvalue;\n    t_float x_inletwas;\n    int x_ticksleft;\n    int x_retarget;\n} t_line;\n\nstatic t_int *line_tilde_perform(t_int *w)\n{\n    t_line *x = (t_line *)(w[1]);\n    t_sample *out = (t_sample *)(w[2]);\n    int n = (int)(w[3]);\n    t_sample f = x->x_value;\n\n    if (PD_BIGORSMALL(f))\n            x->x_value = f = 0;\n    if (x->x_retarget)\n    {\n        int nticks = x->x_inletwas * x->x_dspticktomsec;\n        if (!nticks) nticks = 1;\n        x->x_ticksleft = nticks;\n        x->x_biginc = (x->x_target - x->x_value)/(t_float)nticks;\n        x->x_inc = x->x_1overn * x->x_biginc;\n        x->x_retarget = 0;\n    }\n    if (x->x_ticksleft)\n    {\n        t_sample f = x->x_value;\n        while (n--) *out++ = f, f += x->x_inc;\n        x->x_value += x->x_biginc;\n        x->x_ticksleft--;\n    }\n    else\n    {\n        t_sample g = x->x_value = x->x_target;\n        while (n--)\n            *out++ = g;\n    }\n    return (w+4);\n}\n\n/* TB: vectorized version */\nstatic t_int *line_tilde_perf8(t_int *w)\n{\n    t_line *x = (t_line *)(w[1]);\n    t_sample *out = (t_sample *)(w[2]);\n    int n = (int)(w[3]);\n    t_sample f = x->x_value;\n\n    if (PD_BIGORSMALL(f))\n        x->x_value = f = 0;\n    if (x->x_retarget)\n    {\n        int nticks = x->x_inletwas * x->x_dspticktomsec;\n        if (!nticks) nticks = 1;\n        x->x_ticksleft = nticks;\n        x->x_biginc = (x->x_target - x->x_value)/(t_sample)nticks;\n        x->x_inc = x->x_1overn * x->x_biginc;\n        x->x_retarget = 0;\n    }\n    if (x->x_ticksleft)\n    {\n        t_sample f = x->x_value;\n        while (n--) *out++ = f, f += x->x_inc;\n        x->x_value += x->x_biginc;\n        x->x_ticksleft--;\n    }\n    else\n    {\n        t_sample f = x->x_value = x->x_target;\n        for (; n; n -= 8, out += 8)\n        {\n            out[0] = f; out[1] = f; out[2] = f; out[3] = f;\n            out[4] = f; out[5] = f; out[6] = f; out[7] = f;\n        }\n    }\n    return (w+4);\n}\n\nstatic void line_tilde_float(t_line *x, t_float f)\n{\n    if (x->x_inletvalue <= 0)\n    {\n        x->x_target = x->x_value = f;\n        x->x_ticksleft = x->x_retarget = 0;\n    }\n    else\n    {\n        x->x_target = f;\n        x->x_retarget = 1;\n        x->x_inletwas = x->x_inletvalue;\n        x->x_inletvalue = 0;\n    }\n}\n\nstatic void line_tilde_stop(t_line *x)\n{\n    x->x_target = x->x_value;\n    x->x_ticksleft = x->x_retarget = 0;\n}\n\nstatic void line_tilde_dsp(t_line *x, t_signal **sp)\n{\n    if(sp[0]->s_n&7)\n        dsp_add(line_tilde_perform, 3, x, sp[0]->s_vec, (t_int)sp[0]->s_n);\n    else\n        dsp_add(line_tilde_perf8, 3, x, sp[0]->s_vec, (t_int)sp[0]->s_n);\n    x->x_1overn = 1./sp[0]->s_n;\n    x->x_dspticktomsec = sp[0]->s_sr / (1000 * sp[0]->s_n);\n}\n\nstatic void *line_tilde_new(void)\n{\n    t_line *x = (t_line *)pd_new(line_tilde_class);\n    outlet_new(&x->x_obj, gensym(\"signal\"));\n    floatinlet_new(&x->x_obj, &x->x_inletvalue);\n    x->x_ticksleft = x->x_retarget = 0;\n    x->x_value = x->x_target = x->x_inletvalue = x->x_inletwas = 0;\n    return (x);\n}\n\nstatic void line_tilde_setup(void)\n{\n    line_tilde_class = class_new(gensym(\"line~\"), line_tilde_new, 0,\n        sizeof(t_line), 0, 0);\n    class_addfloat(line_tilde_class, (t_method)line_tilde_float);\n    class_addmethod(line_tilde_class, (t_method)line_tilde_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_addmethod(line_tilde_class, (t_method)line_tilde_stop,\n        gensym(\"stop\"), 0);\n}\n\n/* -------------------------- vline~ ------------------------------ */\nstatic t_class *vline_tilde_class;\n#include \"s_stuff.h\"    /* for DEFDACBLKSIZE; this should be in m_pd.h */\ntypedef struct _vseg\n{\n    double s_targettime;\n    double s_starttime;\n    t_sample s_target;\n    struct _vseg *s_next;\n} t_vseg;\n\ntypedef struct _vline\n{\n    t_object x_obj;\n    double x_value;\n    double x_inc;\n    double x_referencetime;\n    double x_lastlogicaltime;\n    double x_nextblocktime;\n    double x_samppermsec;\n    double x_msecpersamp;\n    double x_targettime;\n    t_sample x_target;\n    t_float x_inlet1;\n    t_float x_inlet2;\n    t_vseg *x_list;\n} t_vline;\n\nstatic t_int *vline_tilde_perform(t_int *w)\n{\n    t_vline *x = (t_vline *)(w[1]);\n    t_sample *out = (t_sample *)(w[2]);\n    int n = (int)(w[3]), i;\n    double f = x->x_value;\n    double inc = x->x_inc;\n    double msecpersamp = x->x_msecpersamp;\n    double timenow, logicaltimenow = clock_gettimesince(x->x_referencetime);\n    t_vseg *s = x->x_list;\n    if (logicaltimenow != x->x_lastlogicaltime)\n    {\n        int sampstotime = (n > DEFDACBLKSIZE ? n : DEFDACBLKSIZE);\n        x->x_lastlogicaltime = logicaltimenow;\n        x->x_nextblocktime = logicaltimenow - sampstotime * msecpersamp;\n    }\n    timenow = x->x_nextblocktime;\n    x->x_nextblocktime = timenow + n * msecpersamp;\n    for (i = 0; i < n; i++)\n    {\n        double timenext = timenow + msecpersamp;\n    checknext:\n        if (s)\n        {\n            /* has starttime elapsed?  If so update value and increment */\n            if (s->s_starttime < timenext)\n            {\n                if (x->x_targettime <= timenext)\n                    f = x->x_target, inc = 0;\n                    /* if zero-length segment bash output value */\n                if (s->s_targettime <= s->s_starttime)\n                {\n                    f = s->s_target;\n                    inc = 0;\n                }\n                else\n                {\n                    double incpermsec = (s->s_target - f)/\n                        (s->s_targettime - s->s_starttime);\n                    f = f + incpermsec * (timenext - s->s_starttime);\n                    inc = incpermsec * msecpersamp;\n                }\n                x->x_inc = inc;\n                x->x_target = s->s_target;\n                x->x_targettime = s->s_targettime;\n                x->x_list = s->s_next;\n                t_freebytes(s, sizeof(*s));\n                s = x->x_list;\n                goto checknext;\n            }\n        }\n        if (x->x_targettime <= timenext)\n            f = x->x_target, inc = x->x_inc = 0, x->x_targettime = 1e20;\n        *out++ = f;\n        f = f + inc;\n        timenow = timenext;\n    }\n    x->x_value = f;\n    return (w+4);\n}\n\nstatic void vline_tilde_stop(t_vline *x)\n{\n    t_vseg *s1, *s2;\n    for (s1 = x->x_list; s1; s1 = s2)\n        s2 = s1->s_next, t_freebytes(s1, sizeof(*s1));\n    x->x_list = 0;\n    x->x_inc = 0;\n    x->x_inlet1 = x->x_inlet2 = 0;\n    x->x_target = x->x_value;\n    x->x_targettime = 1e20;\n}\n\nstatic void vline_tilde_float(t_vline *x, t_float f)\n{\n    double timenow = clock_gettimesince(x->x_referencetime);\n    t_float inlet1 = (x->x_inlet1 < 0 ? 0 : x->x_inlet1);\n    t_float inlet2 = x->x_inlet2;\n    double starttime = timenow + inlet2;\n    t_vseg *s1, *s2, *deletefrom = 0, *snew;\n    if (PD_BIGORSMALL(f))\n        f = 0;\n\n        /* negative delay input means stop and jump immediately to new value */\n    if (inlet2 < 0)\n    {\n        x->x_value = f;\n        vline_tilde_stop(x);\n        return;\n    }\n    snew = (t_vseg *)t_getbytes(sizeof(*snew));\n        /* check if we supplant the first item in the list.  We supplant\n        an item by having an earlier starttime, or an equal starttime unless\n        the equal one was instantaneous and the new one isn't (in which case\n        we'll do a jump-and-slide starting at that time.) */\n    if (!x->x_list || x->x_list->s_starttime > starttime ||\n        (x->x_list->s_starttime == starttime &&\n            (x->x_list->s_targettime > x->x_list->s_starttime || inlet1 <= 0)))\n    {\n        deletefrom = x->x_list;\n        x->x_list = snew;\n    }\n    else\n    {\n        for (s1 = x->x_list; (s2 = s1->s_next); s1 = s2)\n        {\n            if (s2->s_starttime > starttime ||\n                (s2->s_starttime == starttime &&\n                    (s2->s_targettime > s2->s_starttime || inlet1 <= 0)))\n            {\n                deletefrom = s2;\n                s1->s_next = snew;\n                goto didit;\n            }\n        }\n        s1->s_next = snew;\n        deletefrom = 0;\n    didit: ;\n    }\n    while (deletefrom)\n    {\n        s1 = deletefrom->s_next;\n        t_freebytes(deletefrom, sizeof(*deletefrom));\n        deletefrom = s1;\n    }\n    snew->s_next = 0;\n    snew->s_target = f;\n    snew->s_starttime = starttime;\n    snew->s_targettime = starttime + inlet1;\n    x->x_inlet1 = x->x_inlet2 = 0;\n}\n\nstatic void vline_tilde_dsp(t_vline *x, t_signal **sp)\n{\n    dsp_add(vline_tilde_perform, 3, x, sp[0]->s_vec, (t_int)sp[0]->s_n);\n    x->x_samppermsec = ((double)(sp[0]->s_sr)) / 1000;\n    x->x_msecpersamp = ((double)1000) / sp[0]->s_sr;\n}\n\nstatic void *vline_tilde_new(void)\n{\n    t_vline *x = (t_vline *)pd_new(vline_tilde_class);\n    outlet_new(&x->x_obj, gensym(\"signal\"));\n    floatinlet_new(&x->x_obj, &x->x_inlet1);\n    floatinlet_new(&x->x_obj, &x->x_inlet2);\n    x->x_inlet1 = x->x_inlet2 = 0;\n    x->x_value = x->x_inc = 0;\n    x->x_referencetime = x->x_lastlogicaltime = x->x_nextblocktime =\n        clock_getlogicaltime();\n    x->x_list = 0;\n    x->x_samppermsec = 0;\n    x->x_targettime = 1e20;\n    return (x);\n}\n\nstatic void vline_tilde_setup(void)\n{\n    vline_tilde_class = class_new(gensym(\"vline~\"), vline_tilde_new,\n        (t_method)vline_tilde_stop, sizeof(t_vline), 0, 0);\n    class_addfloat(vline_tilde_class, (t_method)vline_tilde_float);\n    class_addmethod(vline_tilde_class, (t_method)vline_tilde_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_addmethod(vline_tilde_class, (t_method)vline_tilde_stop,\n        gensym(\"stop\"), 0);\n}\n\n/* -------------------------- snapshot~ ------------------------------ */\nstatic t_class *snapshot_tilde_class;\n\ntypedef struct _snapshot\n{\n    t_object x_obj;\n    t_sample x_value;\n    t_float x_f;\n} t_snapshot;\n\nstatic void *snapshot_tilde_new(void)\n{\n    t_snapshot *x = (t_snapshot *)pd_new(snapshot_tilde_class);\n    x->x_value = 0;\n    outlet_new(&x->x_obj, &s_float);\n    x->x_f = 0;\n    return (x);\n}\n\nstatic t_int *snapshot_tilde_perform(t_int *w)\n{\n    t_sample *in = (t_sample *)(w[1]);\n    t_sample *out = (t_sample *)(w[2]);\n    *out = *in;\n    return (w+3);\n}\n\nstatic void snapshot_tilde_dsp(t_snapshot *x, t_signal **sp)\n{\n    dsp_add(snapshot_tilde_perform, 2, sp[0]->s_vec + (sp[0]->s_n-1),\n        &x->x_value);\n}\n\nstatic void snapshot_tilde_bang(t_snapshot *x)\n{\n    outlet_float(x->x_obj.ob_outlet, x->x_value);\n}\n\nstatic void snapshot_tilde_set(t_snapshot *x, t_floatarg f)\n{\n    x->x_value = f;\n}\n\nstatic void snapshot_tilde_setup(void)\n{\n    snapshot_tilde_class = class_new(gensym(\"snapshot~\"), snapshot_tilde_new, 0,\n        sizeof(t_snapshot), 0, 0);\n    CLASS_MAINSIGNALIN(snapshot_tilde_class, t_snapshot, x_f);\n    class_addmethod(snapshot_tilde_class, (t_method)snapshot_tilde_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_addmethod(snapshot_tilde_class, (t_method)snapshot_tilde_set,\n        gensym(\"set\"), A_DEFFLOAT, 0);\n    class_addbang(snapshot_tilde_class, snapshot_tilde_bang);\n}\n\n/* -------------------------- vsnapshot~ ------------------------------ */\nstatic t_class *vsnapshot_tilde_class;\n\ntypedef struct _vsnapshot\n{\n    t_object x_obj;\n    int x_n;\n    int x_gotone;\n    t_sample *x_vec;\n    t_float x_f;\n    t_float x_sampspermsec;\n    double x_time;\n} t_vsnapshot;\n\nstatic void *vsnapshot_tilde_new(void)\n{\n    t_vsnapshot *x = (t_vsnapshot *)pd_new(vsnapshot_tilde_class);\n    outlet_new(&x->x_obj, &s_float);\n    x->x_f = 0;\n    x->x_n = 0;\n    x->x_vec = 0;\n    x->x_gotone = 0;\n    return (x);\n}\n\nstatic t_int *vsnapshot_tilde_perform(t_int *w)\n{\n    t_sample *in = (t_sample *)(w[1]);\n    t_vsnapshot *x = (t_vsnapshot *)(w[2]);\n    t_sample *out = x->x_vec;\n    int n = x->x_n, i;\n    for (i = 0; i < n; i++)\n        out[i] = in[i];\n    x->x_time = clock_getlogicaltime();\n    x->x_gotone = 1;\n    return (w+3);\n}\n\nstatic void vsnapshot_tilde_dsp(t_vsnapshot *x, t_signal **sp)\n{\n    int n = sp[0]->s_n;\n    if (n != x->x_n)\n    {\n        if (x->x_vec)\n            t_freebytes(x->x_vec, x->x_n * sizeof(t_sample));\n        x->x_vec = (t_sample *)getbytes(n * sizeof(t_sample));\n        x->x_gotone = 0;\n        x->x_n = n;\n    }\n    x->x_sampspermsec = sp[0]->s_sr / 1000;\n    dsp_add(vsnapshot_tilde_perform, 2, sp[0]->s_vec, x);\n}\n\nstatic void vsnapshot_tilde_bang(t_vsnapshot *x)\n{\n    t_sample val;\n    if (x->x_gotone)\n    {\n        int indx = clock_gettimesince(x->x_time) * x->x_sampspermsec;\n        if (indx < 0)\n            indx = 0;\n        else if (indx >= x->x_n)\n            indx = x->x_n - 1;\n        val = x->x_vec[indx];\n    }\n    else val = 0;\n    outlet_float(x->x_obj.ob_outlet, val);\n}\n\nstatic void vsnapshot_tilde_ff(t_vsnapshot *x)\n{\n    if (x->x_vec)\n        t_freebytes(x->x_vec, x->x_n * sizeof(t_sample));\n}\n\nstatic void vsnapshot_tilde_setup(void)\n{\n    vsnapshot_tilde_class = class_new(gensym(\"vsnapshot~\"),\n        vsnapshot_tilde_new, (t_method)vsnapshot_tilde_ff,\n        sizeof(t_vsnapshot), 0, 0);\n    CLASS_MAINSIGNALIN(vsnapshot_tilde_class, t_vsnapshot, x_f);\n    class_addmethod(vsnapshot_tilde_class, (t_method)vsnapshot_tilde_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_addbang(vsnapshot_tilde_class, vsnapshot_tilde_bang);\n}\n\n\n/* ---------------- env~ - simple envelope follower. ----------------- */\n\n#define MAXOVERLAP 32\n#define INITVSTAKEN 64\n\ntypedef struct sigenv\n{\n    t_object x_obj;                 /* header */\n    void *x_outlet;                 /* a \"float\" outlet */\n    void *x_clock;                  /* a \"clock\" object */\n    t_sample *x_buf;                   /* a Hanning window */\n    int x_phase;                    /* number of points since last output */\n    int x_period;                   /* requested period of output */\n    int x_realperiod;               /* period rounded up to vecsize multiple */\n    int x_npoints;                  /* analysis window size in samples */\n    t_float x_result;                 /* result to output */\n    t_sample x_sumbuf[MAXOVERLAP];     /* summing buffer */\n    t_float x_f;\n    int x_allocforvs;               /* extra buffer for DSP vector size */\n} t_sigenv;\n\nt_class *env_tilde_class;\nstatic void env_tilde_tick(t_sigenv *x);\n\nstatic void *env_tilde_new(t_floatarg fnpoints, t_floatarg fperiod)\n{\n    int npoints = fnpoints;\n    int period = fperiod;\n    t_sigenv *x;\n    t_sample *buf;\n    int i;\n\n    if (npoints < 1) npoints = 1024;\n    if (period < 1) period = npoints/2;\n    if (period < npoints / MAXOVERLAP + 1)\n        period = npoints / MAXOVERLAP + 1;\n    if (!(buf = getbytes(sizeof(t_sample) * (npoints + INITVSTAKEN))))\n    {\n        pd_error(0, \"env: couldn't allocate buffer\");\n        return (0);\n    }\n    x = (t_sigenv *)pd_new(env_tilde_class);\n    x->x_buf = buf;\n    x->x_npoints = npoints;\n    x->x_phase = 0;\n    x->x_period = period;\n    for (i = 0; i < MAXOVERLAP; i++) x->x_sumbuf[i] = 0;\n    for (i = 0; i < npoints; i++)\n        buf[i] = (1. - cos((2 * 3.14159 * i) / npoints))/npoints;\n    for (; i < npoints+INITVSTAKEN; i++) buf[i] = 0;\n    x->x_clock = clock_new(x, (t_method)env_tilde_tick);\n    x->x_outlet = outlet_new(&x->x_obj, gensym(\"float\"));\n    x->x_f = 0;\n    x->x_allocforvs = INITVSTAKEN;\n    return (x);\n}\n\nstatic t_int *env_tilde_perform(t_int *w)\n{\n    t_sigenv *x = (t_sigenv *)(w[1]);\n    t_sample *in = (t_sample *)(w[2]);\n    int n = (int)(w[3]);\n    int count;\n    t_sample *sump;\n    in += n;\n    for (count = x->x_phase, sump = x->x_sumbuf;\n        count < x->x_npoints; count += x->x_realperiod, sump++)\n    {\n        t_sample *hp = x->x_buf + count;\n        t_sample *fp = in;\n        t_sample sum = *sump;\n        int i;\n\n        for (i = 0; i < n; i++)\n        {\n            fp--;\n            sum += *hp++ * (*fp * *fp);\n        }\n        *sump = sum;\n    }\n    sump[0] = 0;\n    x->x_phase -= n;\n    if (x->x_phase < 0)\n    {\n        x->x_result = x->x_sumbuf[0];\n        for (count = x->x_realperiod, sump = x->x_sumbuf;\n            count < x->x_npoints; count += x->x_realperiod, sump++)\n                sump[0] = sump[1];\n        sump[0] = 0;\n        x->x_phase = x->x_realperiod - n;\n        clock_delay(x->x_clock, 0L);\n    }\n    return (w+4);\n}\n\nstatic void env_tilde_dsp(t_sigenv *x, t_signal **sp)\n{\n    if (x->x_period % sp[0]->s_n) x->x_realperiod =\n        x->x_period + sp[0]->s_n - (x->x_period % sp[0]->s_n);\n    else x->x_realperiod = x->x_period;\n    if (sp[0]->s_n > x->x_allocforvs)\n    {\n        void *xx = resizebytes(x->x_buf,\n            (x->x_npoints + x->x_allocforvs) * sizeof(t_sample),\n            (x->x_npoints + sp[0]->s_n) * sizeof(t_sample));\n        if (!xx)\n        {\n            pd_error(0, \"env~: out of memory\");\n            return;\n        }\n        x->x_buf = (t_sample *)xx;\n        x->x_allocforvs = sp[0]->s_n;\n    }\n    dsp_add(env_tilde_perform, 3, x, sp[0]->s_vec, (t_int)sp[0]->s_n);\n}\n\nstatic void env_tilde_tick(t_sigenv *x) /* callback function for the clock */\n{\n    outlet_float(x->x_outlet, powtodb(x->x_result));\n}\n\nstatic void env_tilde_ff(t_sigenv *x)           /* cleanup on free */\n{\n    clock_free(x->x_clock);\n    freebytes(x->x_buf, (x->x_npoints + x->x_allocforvs) * sizeof(*x->x_buf));\n}\n\n\nvoid env_tilde_setup(void)\n{\n    env_tilde_class = class_new(gensym(\"env~\"), (t_newmethod)env_tilde_new,\n        (t_method)env_tilde_ff, sizeof(t_sigenv), 0, A_DEFFLOAT, A_DEFFLOAT, 0);\n    CLASS_MAINSIGNALIN(env_tilde_class, t_sigenv, x_f);\n    class_addmethod(env_tilde_class, (t_method)env_tilde_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n}\n\n/* --------------------- threshold~ ----------------------------- */\n\nstatic t_class *threshold_tilde_class;\n\ntypedef struct _threshold_tilde\n{\n    t_object x_obj;\n    t_outlet *x_outlet1;        /* bang out for high thresh */\n    t_outlet *x_outlet2;        /* bang out for low thresh */\n    t_clock *x_clock;           /* wakeup for message output */\n    t_float x_f;                  /* scalar inlet */\n    int x_state;                /* 1 = high, 0 = low */\n    t_float x_hithresh;           /* value of high threshold */\n    t_float x_lothresh;           /* value of low threshold */\n    t_float x_deadwait;           /* msec remaining in dead period */\n    t_float x_msecpertick;        /* msec per DSP tick */\n    t_float x_hideadtime;         /* hi dead time in msec */\n    t_float x_lodeadtime;         /* lo dead time in msec */\n} t_threshold_tilde;\n\nstatic void threshold_tilde_tick(t_threshold_tilde *x);\nstatic void threshold_tilde_set(t_threshold_tilde *x,\n    t_floatarg hithresh, t_floatarg hideadtime,\n    t_floatarg lothresh, t_floatarg lodeadtime);\n\nstatic t_threshold_tilde *threshold_tilde_new(t_floatarg hithresh,\n    t_floatarg hideadtime, t_floatarg lothresh, t_floatarg lodeadtime)\n{\n    t_threshold_tilde *x = (t_threshold_tilde *)\n        pd_new(threshold_tilde_class);\n    x->x_state = 0;             /* low state */\n    x->x_deadwait = 0;          /* no dead time */\n    x->x_clock = clock_new(x, (t_method)threshold_tilde_tick);\n    x->x_outlet1 = outlet_new(&x->x_obj, &s_bang);\n    x->x_outlet2 = outlet_new(&x->x_obj, &s_bang);\n    inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym(\"ft1\"));\n    x->x_msecpertick = 0.;\n    x->x_f = 0;\n    threshold_tilde_set(x, hithresh, hideadtime, lothresh, lodeadtime);\n    return (x);\n}\n\n    /* \"set\" message to specify thresholds and dead times */\nstatic void threshold_tilde_set(t_threshold_tilde *x,\n    t_floatarg hithresh, t_floatarg hideadtime,\n    t_floatarg lothresh, t_floatarg lodeadtime)\n{\n    if (lothresh > hithresh)\n        lothresh = hithresh;\n    x->x_hithresh = hithresh;\n    x->x_hideadtime = hideadtime;\n    x->x_lothresh = lothresh;\n    x->x_lodeadtime = lodeadtime;\n}\n\n    /* number in inlet sets state -- note incompatible with JMAX which used\n    \"int\" message for this, impossible here because of auto signal conversion */\nstatic void threshold_tilde_ft1(t_threshold_tilde *x, t_floatarg f)\n{\n    x->x_state = (f != 0);\n    x->x_deadwait = 0;\n}\n\nstatic void threshold_tilde_tick(t_threshold_tilde *x)\n{\n    if (x->x_state)\n        outlet_bang(x->x_outlet1);\n    else outlet_bang(x->x_outlet2);\n}\n\nstatic t_int *threshold_tilde_perform(t_int *w)\n{\n    t_sample *in1 = (t_sample *)(w[1]);\n    t_threshold_tilde *x = (t_threshold_tilde *)(w[2]);\n    int n = (int)w[3];\n    if (x->x_deadwait > 0)\n        x->x_deadwait -= x->x_msecpertick;\n    else if (x->x_state)\n    {\n            /* we're high; look for low sample */\n        for (; n--; in1++)\n        {\n            if (*in1 < x->x_lothresh)\n            {\n                clock_delay(x->x_clock, 0L);\n                x->x_state = 0;\n                x->x_deadwait = x->x_lodeadtime;\n                goto done;\n            }\n        }\n    }\n    else\n    {\n            /* we're low; look for high sample */\n        for (; n--; in1++)\n        {\n            if (*in1 >= x->x_hithresh)\n            {\n                clock_delay(x->x_clock, 0L);\n                x->x_state = 1;\n                x->x_deadwait = x->x_hideadtime;\n                goto done;\n            }\n        }\n    }\ndone:\n    return (w+4);\n}\n\nvoid threshold_tilde_dsp(t_threshold_tilde *x, t_signal **sp)\n{\n    x->x_msecpertick = 1000. * sp[0]->s_n / sp[0]->s_sr;\n    dsp_add(threshold_tilde_perform, 3, sp[0]->s_vec, x, (t_int)sp[0]->s_n);\n}\n\nstatic void threshold_tilde_ff(t_threshold_tilde *x)\n{\n    clock_free(x->x_clock);\n}\n\nstatic void threshold_tilde_setup(void)\n{\n    threshold_tilde_class = class_new(gensym(\"threshold~\"),\n        (t_newmethod)threshold_tilde_new, (t_method)threshold_tilde_ff,\n        sizeof(t_threshold_tilde), 0,\n            A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0);\n    CLASS_MAINSIGNALIN(threshold_tilde_class, t_threshold_tilde, x_f);\n    class_addmethod(threshold_tilde_class, (t_method)threshold_tilde_set,\n        gensym(\"set\"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);\n    class_addmethod(threshold_tilde_class, (t_method)threshold_tilde_ft1,\n        gensym(\"ft1\"), A_FLOAT, 0);\n    class_addmethod(threshold_tilde_class, (t_method)threshold_tilde_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n}\n\n/* ------------------------ global setup routine ------------------------- */\n\nvoid d_ctl_setup(void)\n{\n    sig_tilde_setup();\n    line_tilde_setup();\n    vline_tilde_setup();\n    snapshot_tilde_setup();\n    vsnapshot_tilde_setup();\n    env_tilde_setup();\n    threshold_tilde_setup();\n}\n\n"
  },
  {
    "path": "libs/libpd/pure-data/src/d_dac.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/*  The dac~ and adc~ routines.\n*/\n\n#include \"m_pd.h\"\n#include \"s_stuff.h\"\n#include <string.h>\n\n/* ----------------------------- dac~ --------------------------- */\nstatic t_class *dac_class;\n\ntypedef struct _dac\n{\n    t_object x_obj;\n    t_int x_n;\n    t_int *x_vec;\n    t_float x_f;\n} t_dac;\n\nstatic void *dac_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_dac *x = (t_dac *)pd_new(dac_class);\n    t_atom defarg[2];\n    int i;\n    if (!argc)\n    {\n        argv = defarg;\n        argc = 2;\n        SETFLOAT(&defarg[0], 1);\n        SETFLOAT(&defarg[1], 2);\n    }\n    x->x_n = argc;\n    x->x_vec = (t_int *)getbytes(argc * sizeof(*x->x_vec));\n    for (i = 0; i < argc; i++)\n        x->x_vec[i] = atom_getfloatarg(i, argc, argv);\n    for (i = 1; i < argc; i++)\n        inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);\n    x->x_f = 0;\n    return (x);\n}\n\nstatic void dac_dsp(t_dac *x, t_signal **sp)\n{\n    t_int i, j;\n    for (i = 0; i < x->x_n; i++)\n    {\n        int ch = (int)(x->x_vec[i] - 1);\n        if (sp[i]->s_length != DEFDACBLKSIZE)\n            pd_error(x,\n              \"dac~: input vector size (%d) doesn't match Pd vector size (%d)\",\n                    sp[i]->s_length, DEFDACBLKSIZE);\n        else for (j = 0; j < sp[i]->s_nchans; j++)\n        {\n            if (ch + j >= 0 && ch + j < sys_get_outchannels())\n                dsp_add(plus_perform, 4,\n                    STUFF->st_soundout + DEFDACBLKSIZE * (ch + j),\n                sp[i]->s_vec + j * sp[i]->s_length,\n                    STUFF->st_soundout + DEFDACBLKSIZE * (ch + j),\n                        (t_int)DEFDACBLKSIZE);\n        }\n    }\n}\n\nstatic void dac_set(t_dac *x, t_symbol *s, int argc, t_atom *argv)\n{\n    int i;\n    for (i = 0; i < argc && i < x->x_n; i++)\n        x->x_vec[i] = atom_getfloatarg(i, argc, argv);\n    canvas_update_dsp();\n}\n\nstatic void dac_free(t_dac *x)\n{\n    freebytes(x->x_vec, x->x_n * sizeof(*x->x_vec));\n}\n\nstatic void dac_setup(void)\n{\n    dac_class = class_new(gensym(\"dac~\"), (t_newmethod)dac_new,\n        (t_method)dac_free, sizeof(t_dac), CLASS_MULTICHANNEL, A_GIMME, 0);\n    CLASS_MAINSIGNALIN(dac_class, t_dac, x_f);\n    class_addmethod(dac_class, (t_method)dac_dsp, gensym(\"dsp\"), A_CANT, 0);\n    class_addmethod(dac_class, (t_method)dac_set, gensym(\"set\"), A_GIMME, 0);\n    class_sethelpsymbol(dac_class, gensym(\"adc~_dac~\"));\n}\n\n/* ----------------------------- adc~ --------------------------- */\nstatic t_class *adc_class;\n\ntypedef struct _adc\n{\n    t_object x_obj;\n    int x_n;\n    int *x_vec;\n    int x_multi;\n} t_adc;\n\nstatic void *adc_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_adc *x = (t_adc *)pd_new(adc_class);\n    t_atom defarg[2];\n    int i, firstchan;\n    if (!argc)\n    {\n        argv = defarg;\n        argc = 2;\n        SETFLOAT(&defarg[0], 1);\n        SETFLOAT(&defarg[1], 2);\n    }\n    if (argc > 0 && argv[0].a_type == A_SYMBOL &&\n        !strcmp(argv[0].a_w.w_symbol->s_name, \"-m\"))\n    {       /* multichannel version: -m [nchans] [start channel] */\n        x->x_multi = 1;\n        if ((x->x_n = atom_getfloatarg(1, argc, argv)) < 1)\n            x->x_n = 2;\n        if ((firstchan = atom_getfloatarg(2, argc, argv)) < 1)\n            firstchan = 1;\n        x->x_vec = (int *)getbytes(x->x_n * sizeof(*x->x_vec));\n        for (i = 0; i < x->x_n; i++)\n            x->x_vec[i] = firstchan+i;\n        outlet_new(&x->x_obj, &s_signal);\n    }\n    else\n    {\n        x->x_multi = 0;\n        x->x_n = argc;\n        x->x_vec = (int *)getbytes(argc * sizeof(*x->x_vec));\n        for (i = 0; i < argc; i++)\n            x->x_vec[i] = atom_getfloatarg(i, argc, argv);\n        for (i = 0; i < x->x_n; i++)\n            outlet_new(&x->x_obj, &s_signal);\n    }\n    return (x);\n}\n\nstatic void adc_dsp(t_adc *x, t_signal **sp)\n{\n    int i;\n    if (x->x_multi)\n        signal_setmultiout(sp, x->x_n);\n    else for (i = 0; i < x->x_n; i++)\n        signal_setmultiout(&sp[i], 1);\n    if (sp[0]->s_length != DEFDACBLKSIZE)\n    {\n        pd_error(0, \"adc~: local vector size %d doesn't match system (%d)\",\n            sp[0]->s_length, DEFDACBLKSIZE);\n        return;\n    }\n    for (i = 0; i < x->x_n; i++)\n    {\n        int ch = x->x_vec[i] - 1;\n        t_sample *out = (x->x_multi? sp[0]->s_vec + sp[0]->s_length * i:\n            sp[i]->s_vec);\n        if (ch >= 0 && ch < sys_get_inchannels())\n            dsp_add_copy(STUFF->st_soundin + DEFDACBLKSIZE*ch,\n                out, DEFDACBLKSIZE);\n        else dsp_add_zero(out, DEFDACBLKSIZE);\n    }\n}\n\nstatic void adc_set(t_adc *x, t_symbol *s, int argc, t_atom *argv)\n{\n    int i;\n    if (x->x_multi)\n    {\n        int nchannels = atom_getfloatarg(0, argc, argv),\n            startchannel = atom_getfloatarg(1, argc, argv);\n        if (nchannels < 1)\n            nchannels = 2;\n        if (startchannel < 1)\n            startchannel = 1;\n        x->x_vec = (int *)t_resizebytes(x->x_vec,\n            x->x_n * sizeof(*x->x_vec), nchannels * sizeof(*x->x_vec));\n        for (i = 0; i < nchannels; i++)\n            x->x_vec[i] = startchannel + i;\n        x->x_n = nchannels;\n    }\n    else for (i = 0; i < argc && i < x->x_n; i++)\n        x->x_vec[i] = atom_getfloatarg(i, argc, argv);\n    canvas_update_dsp();\n}\n\nstatic void adc_free(t_adc *x)\n{\n    freebytes(x->x_vec, x->x_n * sizeof(*x->x_vec));\n}\n\nstatic void adc_setup(void)\n{\n    adc_class = class_new(gensym(\"adc~\"), (t_newmethod)adc_new,\n        (t_method)adc_free, sizeof(t_adc), 0, A_GIMME, 0);\n    class_addmethod(adc_class, (t_method)adc_dsp, gensym(\"dsp\"), A_CANT, 0);\n    class_addmethod(adc_class, (t_method)adc_set, gensym(\"set\"), A_GIMME, 0);\n    class_sethelpsymbol(adc_class, gensym(\"adc~_dac~\"));\n}\n\nvoid d_dac_setup(void)\n{\n    dac_setup();\n    adc_setup();\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/d_delay.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/*  send~, delread~, throw~, catch~ */\n\n#include \"m_pd.h\"\n#include <string.h>\nextern int ugen_getsortno(void);\n\n/* ----------------------------- delwrite~ ----------------------------- */\nstatic t_class *sigdelwrite_class;\n\ntypedef struct delwritectl\n{\n    int c_n;\n    t_sample *c_vec;\n    int c_phase;\n} t_delwritectl;\n\ntypedef struct _sigdelwrite\n{\n    t_object x_obj;\n    t_symbol *x_sym;\n    t_float x_deltime;  /* delay in msec (added by Mathieu Bouchard) */\n    t_delwritectl x_cspace;\n    int x_sortno;   /* DSP sort number at which this was last put on chain */\n    int x_rsortno;  /* DSP sort # for first delread or write in chain */\n    int x_vecsize;  /* vector size for delread~ to use */\n    t_float x_sr;\n    t_float x_f;\n} t_sigdelwrite;\n\n#define XTRASAMPS 4\n#define SAMPBLK 4\n\nstatic void sigdelwrite_update(t_sigdelwrite *x) /* added by Mathieu Bouchard */\n{\n    int nsamps = x->x_deltime * x->x_sr * (t_float)(0.001f);\n    if (nsamps < 1) nsamps = 1;\n    nsamps += ((- nsamps) & (SAMPBLK - 1));\n    nsamps += x->x_vecsize;\n    if (x->x_cspace.c_n != nsamps)\n    {\n        x->x_cspace.c_vec = (t_sample *)resizebytes(x->x_cspace.c_vec,\n            (x->x_cspace.c_n + XTRASAMPS) * sizeof(t_sample),\n            (nsamps + XTRASAMPS) * sizeof(t_sample));\n        x->x_cspace.c_n = nsamps;\n        x->x_cspace.c_phase = XTRASAMPS;\n    #if 0\n        post(\"delay line resized to %d samples\", nsamps);\n    #endif\n    }\n}\n\nstatic void sigdelwrite_clear (t_sigdelwrite *x) /* added by Orm Finnendahl */\n{\n  if (x->x_cspace.c_n > 0)\n    memset(x->x_cspace.c_vec, 0, sizeof(t_sample)*(x->x_cspace.c_n + XTRASAMPS));\n}\n\n\n    /* routine to check that all delwrites/delreads/vds have same vecsize */\nstatic void sigdelwrite_check(t_sigdelwrite *x, int vecsize, t_float sr)\n{\n        /* the first object in the DSP chain sets the vecsize */\n    if (x->x_rsortno != ugen_getsortno())\n    {\n        x->x_vecsize = vecsize;\n        x->x_sr = sr;\n        x->x_rsortno = ugen_getsortno();\n    }\n#if 1\n    /* Subsequent objects are only allowed to increase the vector size/samplerate */\n    else\n    {\n        if (vecsize > x->x_vecsize)\n            x->x_vecsize = vecsize;\n        if (sr > x->x_sr)\n            x->x_sr = sr;\n    }\n#else\n    /*\n        LATER this should really check sample rate and blocking, once that is\n        supported.  Probably we don't actually care about vecsize.\n        For now just suppress this check. */\n    else if (vecsize != x->x_vecsize)\n        pd_error(x, \"delread/delwrite/vd vector size mismatch\");\n#endif\n}\n\nstatic void *sigdelwrite_new(t_symbol *s, t_floatarg msec)\n{\n    t_sigdelwrite *x = (t_sigdelwrite *)pd_new(sigdelwrite_class);\n    if (!*s->s_name) s = gensym(\"delwrite~\");\n    pd_bind(&x->x_obj.ob_pd, s);\n    x->x_sym = s;\n    x->x_deltime = msec;\n    x->x_cspace.c_n = 0;\n    x->x_cspace.c_vec = getbytes(XTRASAMPS * sizeof(t_sample));\n    x->x_sortno = 0;\n    x->x_vecsize = 0;\n    x->x_sr = 0;\n    x->x_f = 0;\n    return (x);\n}\n\nstatic t_int *sigdelwrite_perform(t_int *w)\n{\n    t_sample *in = (t_sample *)(w[1]);\n    t_delwritectl *c = (t_delwritectl *)(w[2]);\n    int n = (int)(w[3]);\n    int phase = c->c_phase, nsamps = c->c_n;\n    t_sample *vp = c->c_vec, *bp = vp + phase, *ep = vp + (c->c_n + XTRASAMPS);\n    phase += n;\n\n    while (n--)\n    {\n        t_sample f = *in++;\n        if (PD_BIGORSMALL(f))\n            f = 0;\n        *bp++ = f;\n        if (bp == ep)\n        {\n            vp[0] = ep[-4];\n            vp[1] = ep[-3];\n            vp[2] = ep[-2];\n            vp[3] = ep[-1];\n            bp = vp + XTRASAMPS;\n            phase -= nsamps;\n        }\n    }\n    c->c_phase = phase;\n    return (w+4);\n}\n\nstatic void sigdelwrite_dsp(t_sigdelwrite *x, t_signal **sp)\n{\n    dsp_add(sigdelwrite_perform, 3, sp[0]->s_vec, &x->x_cspace, (t_int)sp[0]->s_n);\n    x->x_sortno = ugen_getsortno();\n    sigdelwrite_check(x, sp[0]->s_n, sp[0]->s_sr);\n    sigdelwrite_update(x);\n}\n\nstatic void sigdelwrite_free(t_sigdelwrite *x)\n{\n    pd_unbind(&x->x_obj.ob_pd, x->x_sym);\n    freebytes(x->x_cspace.c_vec,\n        (x->x_cspace.c_n + XTRASAMPS) * sizeof(t_sample));\n}\n\nstatic void sigdelwrite_setup(void)\n{\n    sigdelwrite_class = class_new(gensym(\"delwrite~\"),\n        (t_newmethod)sigdelwrite_new, (t_method)sigdelwrite_free,\n        sizeof(t_sigdelwrite), 0, A_DEFSYM, A_DEFFLOAT, 0);\n    CLASS_MAINSIGNALIN(sigdelwrite_class, t_sigdelwrite, x_f);\n    class_addmethod(sigdelwrite_class, (t_method)sigdelwrite_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_addmethod(sigdelwrite_class, (t_method)sigdelwrite_clear,\n                    gensym(\"clear\"), 0);\n    class_sethelpsymbol(sigdelwrite_class, gensym(\"delay-tilde-objects\"));\n}\n\n/* ----------------------------- delread~ ----------------------------- */\nstatic t_class *sigdelread_class;\n\ntypedef struct _sigdelread\n{\n    t_object x_obj;\n    t_symbol *x_sym;\n    t_float x_deltime;  /* delay in msec */\n    int x_delsamps;     /* delay in samples */\n    t_float x_sr;       /* samples per msec */\n    t_float x_n;        /* vector size */\n    int x_zerodel;      /* 0 or vecsize depending on read/write order */\n} t_sigdelread;\n\nstatic void sigdelread_float(t_sigdelread *x, t_float f);\n\nstatic void *sigdelread_new(t_symbol *s, t_floatarg f)\n{\n    t_sigdelread *x = (t_sigdelread *)pd_new(sigdelread_class);\n    x->x_sym = s;\n    x->x_sr = 1;\n    x->x_n = 1;\n    x->x_zerodel = 0;\n    sigdelread_float(x, f);\n    outlet_new(&x->x_obj, &s_signal);\n    return (x);\n}\n\nstatic void sigdelread_float(t_sigdelread *x, t_float f)\n{\n    t_sigdelwrite *delwriter =\n        (t_sigdelwrite *)pd_findbyclass(x->x_sym, sigdelwrite_class);\n    x->x_deltime = f;\n    if (delwriter)\n    {\n        x->x_delsamps = (int)(0.5 + x->x_sr * x->x_deltime)\n            + x->x_n - x->x_zerodel;\n        if (x->x_delsamps < x->x_n) x->x_delsamps = x->x_n;\n        else if (x->x_delsamps > delwriter->x_cspace.c_n)\n            x->x_delsamps = delwriter->x_cspace.c_n;\n    }\n}\n\nstatic t_int *sigdelread_perform(t_int *w)\n{\n    t_sample *out = (t_sample *)(w[1]);\n    t_delwritectl *c = (t_delwritectl *)(w[2]);\n    int delsamps = *(int *)(w[3]);\n    int n = (int)(w[4]);\n    int phase = c->c_phase - delsamps, nsamps = c->c_n;\n    t_sample *vp = c->c_vec, *bp, *ep = vp + (c->c_n + XTRASAMPS);\n    if (phase < 0) phase += nsamps;\n    bp = vp + phase;\n\n    while (n--)\n    {\n        *out++ = *bp++;\n        if (bp == ep) bp -= nsamps;\n    }\n    return (w+5);\n}\n\nstatic void sigdelread_dsp(t_sigdelread *x, t_signal **sp)\n{\n    t_sigdelwrite *delwriter =\n        (t_sigdelwrite *)pd_findbyclass(x->x_sym, sigdelwrite_class);\n    x->x_sr = sp[0]->s_sr * 0.001;\n    x->x_n = sp[0]->s_n;\n    if (delwriter)\n    {\n        sigdelwrite_check(delwriter, sp[0]->s_n, sp[0]->s_sr);\n        sigdelwrite_update(delwriter);\n        x->x_zerodel = (delwriter->x_sortno == ugen_getsortno() ?\n            0 : delwriter->x_vecsize);\n        sigdelread_float(x, x->x_deltime);\n        dsp_add(sigdelread_perform, 4,\n            sp[0]->s_vec, &delwriter->x_cspace, &x->x_delsamps, (t_int)sp[0]->s_n);\n        /* check block size - but only if delwriter has been initialized */\n        if (delwriter->x_cspace.c_n > 0 && sp[0]->s_n > delwriter->x_cspace.c_n)\n            pd_error(x, \"delread~ %s: blocksize larger than delwrite~ buffer\", x->x_sym->s_name);\n    }\n    else if (*x->x_sym->s_name)\n        pd_error(x, \"delread~: %s: no such delwrite~\",x->x_sym->s_name);\n}\n\nstatic void sigdelread_setup(void)\n{\n    sigdelread_class = class_new(gensym(\"delread~\"),\n        (t_newmethod)sigdelread_new, 0,\n        sizeof(t_sigdelread), 0, A_DEFSYM, A_DEFFLOAT, 0);\n    class_addmethod(sigdelread_class, (t_method)sigdelread_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_addfloat(sigdelread_class, (t_method)sigdelread_float);\n    class_sethelpsymbol(sigdelread_class, gensym(\"delay-tilde-objects\"));\n}\n\n\n/* ----------------------------- vd~ / delread4~ ----------------------------- */\nstatic t_class *sigvd_class;\n\ntypedef struct _sigvd\n{\n    t_object x_obj;\n    t_symbol *x_sym;\n    t_float x_sr;       /* samples per msec */\n    int x_zerodel;      /* 0 or vecsize depending on read/write order */\n    t_float x_f;\n} t_sigvd;\n\nstatic void *sigvd_new(t_symbol *s)\n{\n    t_sigvd *x = (t_sigvd *)pd_new(sigvd_class);\n    x->x_sym = s;\n    x->x_sr = 1;\n    x->x_zerodel = 0;\n    outlet_new(&x->x_obj, &s_signal);\n    x->x_f = 0;\n    return (x);\n}\n\nstatic t_int *sigvd_perform(t_int *w)\n{\n    t_sample *in = (t_sample *)(w[1]);\n    t_sample *out = (t_sample *)(w[2]);\n    t_delwritectl *ctl = (t_delwritectl *)(w[3]);\n    t_sigvd *x = (t_sigvd *)(w[4]);\n    int n = (int)(w[5]);\n\n    int nsamps = ctl->c_n;\n    t_sample limit = nsamps - n;\n    t_sample fn = n-1;\n    t_sample *vp = ctl->c_vec, *bp, *wp = vp + ctl->c_phase;\n    t_sample zerodel = x->x_zerodel;\n    if (limit < 0) /* blocksize is larger than delread~ buffer size */\n    {\n        while (n--)\n            *out++ = 0;\n        return (w+6);\n    }\n    while (n--)\n    {\n        t_sample delsamps = x->x_sr * *in++ - zerodel, frac;\n        int idelsamps;\n        t_sample a, b, c, d, cminusb;\n        if (!(delsamps >= 1.00001f))    /* too small or NAN */\n            delsamps = 1.00001f;\n        if (delsamps > limit)           /* too big */\n            delsamps = limit;\n        delsamps += fn;\n        fn = fn - 1.0f;\n        idelsamps = delsamps;\n        frac = delsamps - (t_sample)idelsamps;\n        bp = wp - idelsamps;\n        if (bp < vp + XTRASAMPS) bp += nsamps;\n        d = bp[-3];\n        c = bp[-2];\n        b = bp[-1];\n        a = bp[0];\n        cminusb = c-b;\n        *out++ = b + frac * (\n            cminusb - 0.1666667f * (1.-frac) * (\n                (d - a - 3.0f * cminusb) * frac + (d + 2.0f*a - 3.0f*b)\n            )\n        );\n    }\n    return (w+6);\n}\n\nstatic void sigvd_dsp(t_sigvd *x, t_signal **sp)\n{\n    t_sigdelwrite *delwriter =\n        (t_sigdelwrite *)pd_findbyclass(x->x_sym, sigdelwrite_class);\n    x->x_sr = sp[0]->s_sr * 0.001;\n    if (delwriter)\n    {\n        sigdelwrite_check(delwriter, sp[0]->s_n, sp[0]->s_sr);\n        sigdelwrite_update(delwriter);\n        x->x_zerodel = (delwriter->x_sortno == ugen_getsortno() ?\n            0 : delwriter->x_vecsize);\n        dsp_add(sigvd_perform, 5,\n            sp[0]->s_vec, sp[1]->s_vec,\n                &delwriter->x_cspace, x, (t_int)sp[0]->s_n);\n        /* check block size - but only if delwriter has been initialized */\n        if (delwriter->x_cspace.c_n > 0 && sp[0]->s_n > delwriter->x_cspace.c_n)\n            pd_error(x, \"delread4~ %s: blocksize larger than delwrite~ buffer\", x->x_sym->s_name);\n    }\n    else if (*x->x_sym->s_name)\n        pd_error(x, \"delread4~: %s: no such delwrite~\",x->x_sym->s_name);\n}\n\nstatic void sigvd_setup(void)\n{\n    sigvd_class = class_new(gensym(\"delread4~\"), (t_newmethod)sigvd_new, 0,\n        sizeof(t_sigvd), 0, A_DEFSYM, 0);\n    class_addcreator((t_newmethod)sigvd_new, gensym(\"vd~\"), A_DEFSYM, 0);\n    class_addmethod(sigvd_class, (t_method)sigvd_dsp, gensym(\"dsp\"), A_CANT, 0);\n    CLASS_MAINSIGNALIN(sigvd_class, t_sigvd, x_f);\n    class_sethelpsymbol(sigvd_class, gensym(\"delay-tilde-objects\"));\n}\n\n/* ----------------------- global setup routine ---------------- */\n\nvoid d_delay_setup(void)\n{\n    sigdelwrite_setup();\n    sigdelread_setup();\n    sigvd_setup();\n}\n\n"
  },
  {
    "path": "libs/libpd/pure-data/src/d_fft.c",
    "content": "/* Copyright (c) 1997- Miller Puckette and others.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n#include \"m_pd.h\"\n\n/* This file interfaces to one of the Mayer, Ooura, or fftw FFT packages\nto implement the \"fft~\", etc, Pd objects.  If using Mayer, also compile\nd_fft_mayer.c; if ooura, use d_fft_fftsg.c instead; if fftw, use d_fft_fftw.c\nand also link in the fftw library.  You can only have one of these three\nlinked in.  The configure script can be used to select which one.\n*/\n\n/* ------------------ initialization and cleanup -------------------------- */\n\nvoid mayer_init( void);\nvoid mayer_term( void);\n\nstatic void fftclass_cleanup(t_class *c)\n{\n    mayer_term();\n}\n\n/* ---------------- utility functions for DSP chains ---------------------- */\n\n    /* swap two arrays */\nstatic t_int *sigfft_swap(t_int *w)\n{\n    t_sample *in1 = (t_sample *)(w[1]);\n    t_sample *in2 = (t_sample *)(w[2]);\n    int n = (int)w[3];\n    for (;n--; in1++, in2++)\n    {\n        t_sample f = *in1;\n        *in1 = *in2;\n        *in2 = f;\n    }\n    return (w+4);\n}\n\n    /* take array1 (supply a pointer to beginning) and copy it,\n    into decreasing addresses, into array 2 (supply a pointer one past the\n    end), and negate the sign. */\n\nstatic t_int *sigrfft_flip(t_int *w)\n{\n    t_sample *in = (t_sample *)(w[1]);\n    t_sample *out = (t_sample *)(w[2]);\n    int n = (int)w[3];\n    while (n--)\n        *(--out) = - *in++;\n    return (w+4);\n}\n\n/* ------------------------ fft~ and ifft~ -------------------------------- */\nstatic t_class *sigfft_class, *sigifft_class;\n\ntypedef struct fft\n{\n    t_object x_obj;\n    t_float x_f;\n} t_sigfft;\n\nstatic void *sigfft_new(void)\n{\n    t_sigfft *x = (t_sigfft *)pd_new(sigfft_class);\n    outlet_new(&x->x_obj, gensym(\"signal\"));\n    outlet_new(&x->x_obj, gensym(\"signal\"));\n    inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);\n    x->x_f = 0;\n    return (x);\n}\n\nstatic void *sigifft_new(void)\n{\n    t_sigfft *x = (t_sigfft *)pd_new(sigifft_class);\n    outlet_new(&x->x_obj, gensym(\"signal\"));\n    outlet_new(&x->x_obj, gensym(\"signal\"));\n    inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);\n    x->x_f = 0;\n    return (x);\n}\n\nstatic t_int *sigfft_perform(t_int *w)\n{\n    t_sample *in1 = (t_sample *)(w[1]);\n    t_sample *in2 = (t_sample *)(w[2]);\n    int n = (int)w[3];\n    mayer_fft(n, in1, in2);\n    return (w+4);\n}\n\nstatic t_int *sigifft_perform(t_int *w)\n{\n    t_sample *in1 = (t_sample *)(w[1]);\n    t_sample *in2 = (t_sample *)(w[2]);\n    int n = (int)w[3];\n    mayer_ifft(n, in1, in2);\n    return (w+4);\n}\n\nstatic void sigfft_dspx(t_sigfft *x, t_signal **sp, t_int *(*f)(t_int *w))\n{\n    int length = sp[0]->s_length, nchans = (sp[0]->s_nchans < sp[1]->s_nchans ?\n        sp[0]->s_nchans : sp[1]->s_nchans), ch;\n    if (sp[0]->s_nchans != sp[1]->s_nchans)\n        pd_error(x,\n            \"FFT inputs have different channel counts - ignoring extras\");\n    signal_setmultiout(&sp[2], nchans);\n    signal_setmultiout(&sp[3], nchans);\n    if (length < 4 || (length != (1 << ilog2(length))))\n    {\n        if (length < 4)\n            pd_error(x, \"fft: minimum 4 points\");\n        else\n            pd_error(x, \"fft: blocksize (%d) not a power of 2\", length);\n        dsp_add_zero(sp[2]->s_vec, length * nchans);\n        dsp_add_zero(sp[3]->s_vec, length * nchans);\n        return;\n    }\n    for (ch = 0; ch < nchans; ch++)\n    {\n        t_sample *in1 = sp[0]->s_vec + ch * length;\n        t_sample *in2 = sp[1]->s_vec + ch * length;\n        t_sample *out1 = sp[2]->s_vec + ch * length;\n        t_sample *out2 = sp[3]->s_vec + ch * length;\n        if (out1 == in2 && out2 == in1)\n            dsp_add(sigfft_swap, 3, out1, out2, (t_int)length);\n        else if (out1 == in2)\n        {\n            dsp_add(copy_perform, 3, in2, out2, (t_int)length);\n            dsp_add(copy_perform, 3, in1, out1, (t_int)length);\n        }\n        else\n        {\n            if (out1 != in1) dsp_add(copy_perform, 3, in1, out1, (t_int)length);\n            if (out2 != in2) dsp_add(copy_perform, 3, in2, out2, (t_int)length);\n        }\n        dsp_add(f, 3, out1, out2, (t_int)length);\n    }\n}\n\nstatic void sigfft_dsp(t_sigfft *x, t_signal **sp)\n{\n    sigfft_dspx(x, sp, sigfft_perform);\n}\n\nstatic void sigifft_dsp(t_sigfft *x, t_signal **sp)\n{\n    sigfft_dspx(x, sp, sigifft_perform);\n}\n\nstatic void sigfft_setup(void)\n{\n    sigfft_class = class_new(gensym(\"fft~\"), sigfft_new, 0,\n        sizeof(t_sigfft), CLASS_MULTICHANNEL, 0);\n    class_setfreefn(sigfft_class, fftclass_cleanup);\n    CLASS_MAINSIGNALIN(sigfft_class, t_sigfft, x_f);\n    class_addmethod(sigfft_class, (t_method)sigfft_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    mayer_init();\n\n    sigifft_class = class_new(gensym(\"ifft~\"), sigifft_new, 0,\n        sizeof(t_sigfft), CLASS_MULTICHANNEL, 0);\n    class_setfreefn(sigifft_class, fftclass_cleanup);\n    CLASS_MAINSIGNALIN(sigifft_class, t_sigfft, x_f);\n    class_addmethod(sigifft_class, (t_method)sigifft_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_sethelpsymbol(sigifft_class, gensym(\"fft~\"));\n    mayer_init();\n}\n\n/* ----------------------- rfft~ -------------------------------- */\n\nstatic t_class *sigrfft_class;\n\ntypedef struct rfft\n{\n    t_object x_obj;\n    t_float x_f;\n} t_sigrfft;\n\nstatic void *sigrfft_new(void)\n{\n    t_sigrfft *x = (t_sigrfft *)pd_new(sigrfft_class);\n    outlet_new(&x->x_obj, gensym(\"signal\"));\n    outlet_new(&x->x_obj, gensym(\"signal\"));\n    x->x_f = 0;\n    return (x);\n}\n\nstatic t_int *sigrfft_perform(t_int *w)\n{\n    t_sample *in = (t_sample *)(w[1]);\n    int n = (int)w[2];\n    mayer_realfft(n, in);\n    return (w+3);\n}\n\nstatic void sigrfft_dsp(t_sigrfft *x, t_signal **sp)\n{\n    int length = sp[0]->s_length, n2 = (length>>1), ch;\n    int nchans = sp[0]->s_nchans;\n    signal_setmultiout(&sp[1], nchans);\n    signal_setmultiout(&sp[2], nchans);\n    if (length < 4 || (length != (1 << ilog2(length))))\n    {\n        if (length < 4)\n            pd_error(x, \"fft: minimum 4 points\");\n        else\n            pd_error(x, \"fft: blocksize (%d) not a power of 2\", length);\n        dsp_add_zero(sp[1]->s_vec, length * nchans);\n        dsp_add_zero(sp[2]->s_vec, length * nchans);\n        return;\n    }\n    for (ch = 0; ch < nchans; ch++)\n    {\n        t_sample *in1 = sp[0]->s_vec + ch * length;\n        t_sample *out1 = sp[1]->s_vec + ch * length;\n        t_sample *out2 = sp[2]->s_vec + ch * length;\n        if (in1 != out1)\n            dsp_add(copy_perform, 3, in1, out1, (t_int)length);\n        dsp_add(sigrfft_perform, 2, out1, (t_int)length);\n        dsp_add(sigrfft_flip, 3, out1 + (n2+1), out2 + n2, (t_int)(n2-1));\n        dsp_add_zero(out1 + (n2+1), ((n2-1)&(~7)));\n        dsp_add_zero(out1 + (n2+1) + ((n2-1)&(~7)), ((n2-1)&7));\n        dsp_add_zero(out2 + n2, n2);\n        dsp_add_zero(out2, 1);\n    }\n}\n\nstatic void sigrfft_setup(void)\n{\n    sigrfft_class = class_new(gensym(\"rfft~\"), sigrfft_new, 0,\n        sizeof(t_sigrfft), CLASS_MULTICHANNEL, 0);\n    class_setfreefn(sigrfft_class, fftclass_cleanup);\n    CLASS_MAINSIGNALIN(sigrfft_class, t_sigrfft, x_f);\n    class_addmethod(sigrfft_class, (t_method)sigrfft_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_sethelpsymbol(sigrfft_class, gensym(\"fft~\"));\n    mayer_init();\n}\n\n/* ----------------------- rifft~ -------------------------------- */\n\nstatic t_class *sigrifft_class;\n\ntypedef struct rifft\n{\n    t_object x_obj;\n    t_float x_f;\n} t_sigrifft;\n\nstatic void *sigrifft_new(void)\n{\n    t_sigrifft *x = (t_sigrifft *)pd_new(sigrifft_class);\n    inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);\n    outlet_new(&x->x_obj, gensym(\"signal\"));\n    x->x_f = 0;\n    return (x);\n}\n\nstatic t_int *sigrifft_perform(t_int *w)\n{\n    t_sample *in = (t_sample *)(w[1]);\n    int n = (int)w[2];\n    mayer_realifft(n, in);\n    return (w+3);\n}\n\nstatic void sigrifft_dsp(t_sigrifft *x, t_signal **sp)\n{\n    int length = sp[0]->s_length, n2 = (length>>1),\n        nchans = (sp[0]->s_nchans < sp[1]->s_nchans ?\n            sp[0]->s_nchans : sp[1]->s_nchans), ch;\n    if (sp[0]->s_nchans != sp[1]->s_nchans)\n        pd_error(x,\n            \"rifft~ inputs have different channel counts - ignoring extras\");\n    signal_setmultiout(&sp[2], nchans);\n    if (length < 4 || (length != (1 << ilog2(length))))\n    {\n        if (length < 4)\n            pd_error(x, \"fft: minimum 4 points\");\n        else\n            pd_error(x, \"fft: blocksize (%d) not a power of 2\", length);\n        dsp_add_zero(sp[2]->s_vec, length * nchans);\n        return;\n    }\n    for (ch = 0; ch < nchans; ch++)\n    {\n        t_sample *in1 = sp[0]->s_vec + ch * length;\n        t_sample *in2 = sp[1]->s_vec + ch * length;\n        t_sample *out1 = sp[2]->s_vec + ch * length;\n        if (in2 == out1)\n        {\n            dsp_add(sigrfft_flip, 3, out1+1, out1 + length, (t_int)(n2-1));\n            dsp_add(copy_perform, 3, in1, out1, (t_int)(n2+1));\n        }\n        else\n        {\n            if (in1 != out1) dsp_add(copy_perform, 3, in1, out1, (t_int)(n2+1));\n            dsp_add(sigrfft_flip, 3, in2+1, out1 + length, (t_int)(n2-1));\n        }\n        dsp_add(sigrifft_perform, 2, out1, (t_int)length);\n    }\n}\n\nstatic void sigrifft_setup(void)\n{\n    sigrifft_class = class_new(gensym(\"rifft~\"), sigrifft_new, 0,\n        sizeof(t_sigrifft), CLASS_MULTICHANNEL, 0);\n    class_setfreefn(sigrifft_class, fftclass_cleanup);\n    CLASS_MAINSIGNALIN(sigrifft_class, t_sigrifft, x_f);\n    class_addmethod(sigrifft_class, (t_method)sigrifft_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_sethelpsymbol(sigrifft_class, gensym(\"fft~\"));\n    mayer_init();\n}\n\n/* ----------------------- framp~ -------------------------------- */\n\nstatic t_class *sigframp_class;\n\ntypedef struct framp\n{\n    t_object x_obj;\n    t_float x_f;\n} t_sigframp;\n\nstatic void *sigframp_new(void)\n{\n    t_sigframp *x = (t_sigframp *)pd_new(sigframp_class);\n    inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);\n    outlet_new(&x->x_obj, gensym(\"signal\"));\n    outlet_new(&x->x_obj, gensym(\"signal\"));\n        /* q8_rsqrt() triggers init_rsqrt() as a side-effect */\n    q8_rsqrt(-1.);\n    x->x_f = 0;\n    return (x);\n}\n\nstatic t_int *sigframp_perform(t_int *w)\n{\n    t_sample *inreal = (t_sample *)(w[1]);\n    t_sample *inimag = (t_sample *)(w[2]);\n    t_sample *outfreq = (t_sample *)(w[3]);\n    t_sample *outamp = (t_sample *)(w[4]);\n    t_sample lastreal = 0, currentreal = inreal[0], nextreal = inreal[1];\n    t_sample lastimag = 0, currentimag = inimag[0], nextimag = inimag[1];\n    int n = (int)w[5];\n    int m = n + 1;\n    t_sample fbin = 1, oneovern2 = 1.f/((t_sample)n * (t_sample)n);\n\n    inreal += 2;\n    inimag += 2;\n    *outamp++ = *outfreq++ = 0;\n    n -= 2;\n    while (n--)\n    {\n        t_sample re, im, pow, freq;\n        lastreal = currentreal;\n        currentreal = nextreal;\n        nextreal = *inreal++;\n        lastimag = currentimag;\n        currentimag = nextimag;\n        nextimag = *inimag++;\n        re = currentreal - 0.5f * (lastreal + nextreal);\n        im = currentimag - 0.5f * (lastimag + nextimag);\n        pow = re * re + im * im;\n        if (pow > 1e-19)\n        {\n            t_sample detune = ((lastreal - nextreal) * re +\n                    (lastimag - nextimag) * im) / (2.0f * pow);\n            if (detune > 2 || detune < -2) freq = pow = 0;\n            else freq = fbin + detune;\n        }\n        else freq = pow = 0;\n        *outfreq++ = freq;\n        *outamp++ = oneovern2 * pow;\n        fbin += 1.0f;\n    }\n    while (m--) *outamp++ = *outfreq++ = 0;\n    return (w+6);\n}\n\nt_int *sigsqrt_perform(t_int *w);\n\nstatic void sigframp_dsp(t_sigframp *x, t_signal **sp)\n{\n    int n = sp[0]->s_n, n2 = (n>>1);\n    if (n < 4)\n    {\n        pd_error(0, \"framp: minimum 4 points\");\n        return;\n    }\n    dsp_add(sigframp_perform, 5, sp[0]->s_vec, sp[1]->s_vec,\n        sp[2]->s_vec, sp[3]->s_vec, (t_int)n2);\n    dsp_add(sigsqrt_perform, 3, sp[3]->s_vec, sp[3]->s_vec, (t_int)n2);\n}\n\nstatic void sigframp_setup(void)\n{\n    sigframp_class = class_new(gensym(\"framp~\"), sigframp_new, 0,\n        sizeof(t_sigframp), 0, 0);\n    class_setfreefn(sigframp_class, fftclass_cleanup);\n    CLASS_MAINSIGNALIN(sigframp_class, t_sigframp, x_f);\n    class_addmethod(sigframp_class, (t_method)sigframp_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    mayer_init();\n}\n\n/* ------------------------ global setup routine ------------------------- */\n\nvoid d_fft_setup(void)\n{\n    sigfft_setup();\n    sigrfft_setup();\n    sigrifft_setup();\n    sigframp_setup();\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/d_fft_fftsg.c",
    "content": "/****************** begin Pd-specific prologue ***********************/\n\n/*\nThis is the file, \"fftsg.c\" from the OOURA FFT package.  The copyright notice\nfrom Ooura's README is:\n\nCopyright:\n    Copyright(C) 1996-2001 Takuya OOURA\n    email: ooura@mmm.t.u-tokyo.ac.jp\n    download: http://momonga.t.u-tokyo.ac.jp/~ooura/fft.html\n    You may use, copy, modify this code for any purpose and\n    without fee. You may distribute this ORIGINAL package.\n\nAfter the following prologue, the code is essentially Ooura's original, which I\nbelieve the above notice permits me to redistribute; the only change is to\nreplace 'double' with a macro, 'FFTFLT', so that it can optionally\nbe run in single precision for greater speed.  See Ooura's website\nfor another, more permissive-sounding copyright notice.  -MSP\n*/\n\n/* ---------- Pd interface to OOURA FFT; imitate Mayer API ---------- */\n#include \"m_pd.h\"\n#include \"m_imp.h\"\n\n#include \"m_private_utils.h\"\n\n#define FFTFLT double\nvoid cdft(int, int, FFTFLT *, int *, FFTFLT *);\nvoid rdft(int, int, FFTFLT *, int *, FFTFLT *);\n\nint ilog2(int n);\n\nstatic PERTHREAD int ooura_maxn;\nstatic PERTHREAD int *ooura_bitrev;\nstatic PERTHREAD int ooura_bitrevsize;\nstatic PERTHREAD FFTFLT *ooura_costab;\nstatic PERTHREAD FFTFLT *ooura_buffer;\n\nstatic int ooura_init( int n)\n{\n    n = (1 << ilog2(n));\n    if (n < 4)\n        return (0);\n    if (n > ooura_maxn)\n    {\n        if (ooura_maxn)\n        {\n            t_freebytes(ooura_bitrev, ooura_bitrevsize);\n            t_freebytes(ooura_costab, ooura_maxn * sizeof(FFTFLT) / 2);\n            t_freebytes(ooura_buffer, ooura_maxn * sizeof(FFTFLT));\n        }\n        ooura_bitrevsize = sizeof(int) * (2 + (1 << (ilog2(n)/2)));\n        ooura_bitrev = (int *)t_getbytes(ooura_bitrevsize);\n        ooura_bitrev[0] = 0;\n        if (!ooura_bitrev)\n        {\n            pd_error(0, \"out of memory allocating FFT buffer\");\n            ooura_maxn = 0;\n            return (0);\n        }\n        ooura_costab = (FFTFLT *)t_getbytes(n * sizeof(FFTFLT)/2);\n        if (!ooura_costab)\n        {\n            pd_error(0, \"out of memory allocating FFT buffer\");\n            t_freebytes(ooura_bitrev, ooura_bitrevsize);\n            ooura_maxn = 0;\n            return (0);\n        }\n        ooura_buffer = (FFTFLT *)t_getbytes(n * sizeof(FFTFLT));\n        if (!ooura_buffer)\n        {\n            pd_error(0, \"out of memory allocating FFT buffer\");\n            t_freebytes(ooura_bitrev, ooura_bitrevsize);\n            t_freebytes(ooura_costab, n * sizeof(FFTFLT) / 2);\n            ooura_maxn = 0;\n            return (0);\n        }\n        ooura_maxn = n;\n        ooura_bitrev[0] = 0;\n    }\n    return (1);\n}\n\nstatic void ooura_term( void)\n{\n    if (!ooura_maxn)\n        return;\n    t_freebytes(ooura_bitrev, ooura_bitrevsize);\n    t_freebytes(ooura_costab, ooura_maxn * sizeof(FFTFLT)/2);\n    t_freebytes(ooura_buffer, ooura_maxn * sizeof(FFTFLT));\n    ooura_maxn = 0;\n    ooura_bitrev = 0;\n    ooura_bitrevsize = 0;\n    ooura_costab = 0;\n}\n\n/* -------- initialization and cleanup -------- */\nstatic PERTHREAD int mayer_refcount = 0;\n\nvoid mayer_init( void)\n{\n    mayer_refcount++;\n}\n\nvoid mayer_term( void)\n{\n    if (--mayer_refcount == 0)  /* clean up */\n        ooura_term();\n}\n\n/* -------- public routines -------- */\nEXTERN void mayer_fht(t_sample *fz, int n)\n{\n    post(\"FHT: not yet implemented\");\n}\n\nEXTERN void mayer_dofft(t_sample *fz1, t_sample *fz2, int n, int sgn)\n{\n    FFTFLT *buf, *fp3;\n    int i;\n    t_sample *fp1, *fp2;\n    if (!ooura_init(2*n))\n        return;\n    buf = ooura_buffer;\n    for (i = 0, fp1 = fz1, fp2 = fz2, fp3 = buf; i < n; i++)\n    {\n        fp3[0] = *fp1++;\n        fp3[1] = *fp2++;\n        fp3 += 2;\n    }\n    cdft(2*n, sgn, buf, ooura_bitrev, ooura_costab);\n    for (i = 0, fp1 = fz1, fp2 = fz2, fp3 = buf; i < n; i++)\n    {\n        *fp1++ = fp3[0];\n        *fp2++ = fp3[1];\n        fp3 += 2;\n    }\n}\n\nEXTERN void mayer_fft(int n, t_sample *fz1, t_sample *fz2)\n{\n    mayer_dofft(fz1, fz2, n, -1);\n}\n\nEXTERN void mayer_ifft(int n, t_sample *fz1, t_sample *fz2)\n{\n    mayer_dofft(fz1, fz2, n, 1);\n}\n\nEXTERN void mayer_realfft(int n, t_sample *fz)\n{\n    FFTFLT *buf, *fp3;\n    int i, nover2 = n/2;\n    t_sample *fp1, *fp2;\n    if (!ooura_init(n))\n        return;\n    buf = ooura_buffer;\n    for (i = 0, fp1 = fz, fp3 = buf; i < n; i++, fp1++, fp3++)\n        buf[i] = fz[i];\n    rdft(n, 1, buf, ooura_bitrev, ooura_costab);\n    fz[0] = buf[0];\n    fz[nover2] = buf[1];\n    for (i = 1, fp1 = fz+1, fp2 = fz+(n-1), fp3 = buf+2; i < nover2;\n        i++, fp1++, fp2--, fp3 += 2)\n            *fp1 = fp3[0], *fp2 = fp3[1];\n}\n\nEXTERN void mayer_realifft(int n, t_sample *fz)\n{\n    FFTFLT *buf, *fp3;\n    int i, nover2 = n/2;\n    t_sample *fp1, *fp2;\n    if (!ooura_init(n))\n        return;\n    buf = ooura_buffer;\n    buf[0] = fz[0];\n    buf[1] = fz[nover2];\n    for (i = 1, fp1 = fz+1, fp2 = fz+(n-1), fp3 = buf+2; i < nover2;\n        i++, fp1++, fp2--, fp3 += 2)\n            fp3[0] = *fp1, fp3[1] = *fp2;\n    rdft(n, -1, buf, ooura_bitrev, ooura_costab);\n    for (i = 0, fp1 = fz, fp3 = buf; i < n; i++, fp1++, fp3++)\n        fz[i] = 2*buf[i];\n}\n\n    /* ancient ISPW-like version, used in fiddle~ and perhaps other externs\n    here and there. */\nvoid pd_fft(t_float *buf, int npoints, int inverse)\n{\n    FFTFLT *buf2 = (FFTFLT *)alloca(2 * npoints * sizeof(FFTFLT)), *bp2;\n    t_float *fp;\n    int i;\n    if (!ooura_init(2*npoints))\n        return;\n    for (i = 0, bp2 = buf2, fp = buf; i < 2 * npoints; i++, bp2++, fp++)\n        *bp2 = *fp;\n    cdft(2*npoints, (inverse ? 1 : -1), buf2, ooura_bitrev, ooura_costab);\n    for (i = 0, bp2 = buf2, fp = buf; i < 2 * npoints; i++, bp2++, fp++)\n        *fp = *bp2;\n}\n\n/****************** end Pd-specific prologue ***********************/\n/*\nFast Fourier/Cosine/Sine Transform\n    dimension   :one\n    data length :power of 2\n    decimation  :frequency\n    radix       :split-radix\n    data        :inplace\n    table       :use\nfunctions\n    cdft: Complex Discrete Fourier Transform\n    rdft: Real Discrete Fourier Transform\n    ddct: Discrete Cosine Transform\n    ddst: Discrete Sine Transform\n    dfct: Cosine Transform of RDFT (Real Symmetric DFT)\n    dfst: Sine Transform of RDFT (Real Anti-symmetric DFT)\nfunction prototypes\n    void cdft(int, int, FFTFLT *, int *, FFTFLT *);\n    void rdft(int, int, FFTFLT *, int *, FFTFLT *);\n    void ddct(int, int, FFTFLT *, int *, FFTFLT *);\n    void ddst(int, int, FFTFLT *, int *, FFTFLT *);\n    void dfct(int, FFTFLT *, FFTFLT *, int *, FFTFLT *);\n    void dfst(int, FFTFLT *, FFTFLT *, int *, FFTFLT *);\nmacro definitions\n    USE_CDFT_PTHREADS : default=not defined\n        CDFT_THREADS_BEGIN_N  : must be >= 512, default=8192\n        CDFT_4THREADS_BEGIN_N : must be >= 512, default=65536\n    USE_CDFT_WINTHREADS : default=not defined\n        CDFT_THREADS_BEGIN_N  : must be >= 512, default=32768\n        CDFT_4THREADS_BEGIN_N : must be >= 512, default=524288\n\n\n-------- Complex DFT (Discrete Fourier Transform) --------\n    [definition]\n        <case1>\n            X[k] = sum_j=0^n-1 x[j]*exp(2*pi*i*j*k/n), 0<=k<n\n        <case2>\n            X[k] = sum_j=0^n-1 x[j]*exp(-2*pi*i*j*k/n), 0<=k<n\n        (notes: sum_j=0^n-1 is a summation from j=0 to n-1)\n    [usage]\n        <case1>\n            ip[0] = 0; // first time only\n            cdft(2*n, 1, a, ip, w);\n        <case2>\n            ip[0] = 0; // first time only\n            cdft(2*n, -1, a, ip, w);\n    [parameters]\n        2*n            :data length (int)\n                        n >= 1, n = power of 2\n        a[0...2*n-1]   :input/output data (FFTFLT *)\n                        input data\n                            a[2*j] = Re(x[j]),\n                            a[2*j+1] = Im(x[j]), 0<=j<n\n                        output data\n                            a[2*k] = Re(X[k]),\n                            a[2*k+1] = Im(X[k]), 0<=k<n\n        ip[0...*]      :work area for bit reversal (int *)\n                        length of ip >= 2+sqrt(n)\n                        strictly,\n                        length of ip >=\n                            2+(1<<(int)(log(n+0.5)/log(2))/2).\n                        ip[0],ip[1] are pointers of the cos/sin table.\n        w[0...n/2-1]   :cos/sin table (FFTFLT *)\n                        w[],ip[] are initialized if ip[0] == 0.\n    [remark]\n        Inverse of\n            cdft(2*n, -1, a, ip, w);\n        is\n            cdft(2*n, 1, a, ip, w);\n            for (j = 0; j <= 2 * n - 1; j++) {\n                a[j] *= 1.0 / n;\n            }\n        .\n\n\n-------- Real DFT / Inverse of Real DFT --------\n    [definition]\n        <case1> RDFT\n            R[k] = sum_j=0^n-1 a[j]*cos(2*pi*j*k/n), 0<=k<=n/2\n            I[k] = sum_j=0^n-1 a[j]*sin(2*pi*j*k/n), 0<k<n/2\n        <case2> IRDFT (excluding scale)\n            a[k] = (R[0] + R[n/2]*cos(pi*k))/2 +\n                   sum_j=1^n/2-1 R[j]*cos(2*pi*j*k/n) +\n                   sum_j=1^n/2-1 I[j]*sin(2*pi*j*k/n), 0<=k<n\n    [usage]\n        <case1>\n            ip[0] = 0; // first time only\n            rdft(n, 1, a, ip, w);\n        <case2>\n            ip[0] = 0; // first time only\n            rdft(n, -1, a, ip, w);\n    [parameters]\n        n              :data length (int)\n                        n >= 2, n = power of 2\n        a[0...n-1]     :input/output data (FFTFLT *)\n                        <case1>\n                            output data\n                                a[2*k] = R[k], 0<=k<n/2\n                                a[2*k+1] = I[k], 0<k<n/2\n                                a[1] = R[n/2]\n                        <case2>\n                            input data\n                                a[2*j] = R[j], 0<=j<n/2\n                                a[2*j+1] = I[j], 0<j<n/2\n                                a[1] = R[n/2]\n        ip[0...*]      :work area for bit reversal (int *)\n                        length of ip >= 2+sqrt(n/2)\n                        strictly,\n                        length of ip >=\n                            2+(1<<(int)(log(n/2+0.5)/log(2))/2).\n                        ip[0],ip[1] are pointers of the cos/sin table.\n        w[0...n/2-1]   :cos/sin table (FFTFLT *)\n                        w[],ip[] are initialized if ip[0] == 0.\n    [remark]\n        Inverse of\n            rdft(n, 1, a, ip, w);\n        is\n            rdft(n, -1, a, ip, w);\n            for (j = 0; j <= n - 1; j++) {\n                a[j] *= 2.0 / n;\n            }\n        .\n\n\n-------- DCT (Discrete Cosine Transform) / Inverse of DCT --------\n    [definition]\n        <case1> IDCT (excluding scale)\n            C[k] = sum_j=0^n-1 a[j]*cos(pi*j*(k+1/2)/n), 0<=k<n\n        <case2> DCT\n            C[k] = sum_j=0^n-1 a[j]*cos(pi*(j+1/2)*k/n), 0<=k<n\n    [usage]\n        <case1>\n            ip[0] = 0; // first time only\n            ddct(n, 1, a, ip, w);\n        <case2>\n            ip[0] = 0; // first time only\n            ddct(n, -1, a, ip, w);\n    [parameters]\n        n              :data length (int)\n                        n >= 2, n = power of 2\n        a[0...n-1]     :input/output data (FFTFLT *)\n                        output data\n                            a[k] = C[k], 0<=k<n\n        ip[0...*]      :work area for bit reversal (int *)\n                        length of ip >= 2+sqrt(n/2)\n                        strictly,\n                        length of ip >=\n                            2+(1<<(int)(log(n/2+0.5)/log(2))/2).\n                        ip[0],ip[1] are pointers of the cos/sin table.\n        w[0...n*5/4-1] :cos/sin table (FFTFLT *)\n                        w[],ip[] are initialized if ip[0] == 0.\n    [remark]\n        Inverse of\n            ddct(n, -1, a, ip, w);\n        is\n            a[0] *= 0.5;\n            ddct(n, 1, a, ip, w);\n            for (j = 0; j <= n - 1; j++) {\n                a[j] *= 2.0 / n;\n            }\n        .\n\n\n-------- DST (Discrete Sine Transform) / Inverse of DST --------\n    [definition]\n        <case1> IDST (excluding scale)\n            S[k] = sum_j=1^n A[j]*sin(pi*j*(k+1/2)/n), 0<=k<n\n        <case2> DST\n            S[k] = sum_j=0^n-1 a[j]*sin(pi*(j+1/2)*k/n), 0<k<=n\n    [usage]\n        <case1>\n            ip[0] = 0; // first time only\n            ddst(n, 1, a, ip, w);\n        <case2>\n            ip[0] = 0; // first time only\n            ddst(n, -1, a, ip, w);\n    [parameters]\n        n              :data length (int)\n                        n >= 2, n = power of 2\n        a[0...n-1]     :input/output data (FFTFLT *)\n                        <case1>\n                            input data\n                                a[j] = A[j], 0<j<n\n                                a[0] = A[n]\n                            output data\n                                a[k] = S[k], 0<=k<n\n                        <case2>\n                            output data\n                                a[k] = S[k], 0<k<n\n                                a[0] = S[n]\n        ip[0...*]      :work area for bit reversal (int *)\n                        length of ip >= 2+sqrt(n/2)\n                        strictly,\n                        length of ip >=\n                            2+(1<<(int)(log(n/2+0.5)/log(2))/2).\n                        ip[0],ip[1] are pointers of the cos/sin table.\n        w[0...n*5/4-1] :cos/sin table (FFTFLT *)\n                        w[],ip[] are initialized if ip[0] == 0.\n    [remark]\n        Inverse of\n            ddst(n, -1, a, ip, w);\n        is\n            a[0] *= 0.5;\n            ddst(n, 1, a, ip, w);\n            for (j = 0; j <= n - 1; j++) {\n                a[j] *= 2.0 / n;\n            }\n        .\n\n\n-------- Cosine Transform of RDFT (Real Symmetric DFT) --------\n    [definition]\n        C[k] = sum_j=0^n a[j]*cos(pi*j*k/n), 0<=k<=n\n    [usage]\n        ip[0] = 0; // first time only\n        dfct(n, a, t, ip, w);\n    [parameters]\n        n              :data length - 1 (int)\n                        n >= 2, n = power of 2\n        a[0...n]       :input/output data (FFTFLT *)\n                        output data\n                            a[k] = C[k], 0<=k<=n\n        t[0...n/2]     :work area (FFTFLT *)\n        ip[0...*]      :work area for bit reversal (int *)\n                        length of ip >= 2+sqrt(n/4)\n                        strictly,\n                        length of ip >=\n                            2+(1<<(int)(log(n/4+0.5)/log(2))/2).\n                        ip[0],ip[1] are pointers of the cos/sin table.\n        w[0...n*5/8-1] :cos/sin table (FFTFLT *)\n                        w[],ip[] are initialized if ip[0] == 0.\n    [remark]\n        Inverse of\n            a[0] *= 0.5;\n            a[n] *= 0.5;\n            dfct(n, a, t, ip, w);\n        is\n            a[0] *= 0.5;\n            a[n] *= 0.5;\n            dfct(n, a, t, ip, w);\n            for (j = 0; j <= n; j++) {\n                a[j] *= 2.0 / n;\n            }\n        .\n\n\n-------- Sine Transform of RDFT (Real Anti-symmetric DFT) --------\n    [definition]\n        S[k] = sum_j=1^n-1 a[j]*sin(pi*j*k/n), 0<k<n\n    [usage]\n        ip[0] = 0; // first time only\n        dfst(n, a, t, ip, w);\n    [parameters]\n        n              :data length + 1 (int)\n                        n >= 2, n = power of 2\n        a[0...n-1]     :input/output data (FFTFLT *)\n                        output data\n                            a[k] = S[k], 0<k<n\n                        (a[0] is used for work area)\n        t[0...n/2-1]   :work area (FFTFLT *)\n        ip[0...*]      :work area for bit reversal (int *)\n                        length of ip >= 2+sqrt(n/4)\n                        strictly,\n                        length of ip >=\n                            2+(1<<(int)(log(n/4+0.5)/log(2))/2).\n                        ip[0],ip[1] are pointers of the cos/sin table.\n        w[0...n*5/8-1] :cos/sin table (FFTFLT *)\n                        w[],ip[] are initialized if ip[0] == 0.\n    [remark]\n        Inverse of\n            dfst(n, a, t, ip, w);\n        is\n            dfst(n, a, t, ip, w);\n            for (j = 1; j <= n - 1; j++) {\n                a[j] *= 2.0 / n;\n            }\n        .\n\n\nAppendix :\n    The cos/sin table is recalculated when the larger table required.\n    w[] and ip[] are compatible with all routines.\n*/\n\n\nvoid cdft(int n, int isgn, FFTFLT *a, int *ip, FFTFLT *w)\n{\n    void makewt(int nw, int *ip, FFTFLT *w);\n    void cftfsub(int n, FFTFLT *a, int *ip, int nw, FFTFLT *w);\n    void cftbsub(int n, FFTFLT *a, int *ip, int nw, FFTFLT *w);\n    int nw;\n\n    nw = ip[0];\n    if (n > (nw << 2)) {\n        nw = n >> 2;\n        makewt(nw, ip, w);\n    }\n    if (isgn >= 0) {\n        cftfsub(n, a, ip, nw, w);\n    } else {\n        cftbsub(n, a, ip, nw, w);\n    }\n}\n\n\nvoid rdft(int n, int isgn, FFTFLT *a, int *ip, FFTFLT *w)\n{\n    void makewt(int nw, int *ip, FFTFLT *w);\n    void makect(int nc, int *ip, FFTFLT *c);\n    void cftfsub(int n, FFTFLT *a, int *ip, int nw, FFTFLT *w);\n    void cftbsub(int n, FFTFLT *a, int *ip, int nw, FFTFLT *w);\n    void rftfsub(int n, FFTFLT *a, int nc, FFTFLT *c);\n    void rftbsub(int n, FFTFLT *a, int nc, FFTFLT *c);\n    int nw, nc;\n    FFTFLT xi;\n\n    nw = ip[0];\n    if (n > (nw << 2)) {\n        nw = n >> 2;\n        makewt(nw, ip, w);\n    }\n    nc = ip[1];\n    if (n > (nc << 2)) {\n        nc = n >> 2;\n        makect(nc, ip, w + nw);\n    }\n    if (isgn >= 0) {\n        if (n > 4) {\n            cftfsub(n, a, ip, nw, w);\n            rftfsub(n, a, nc, w + nw);\n        } else if (n == 4) {\n            cftfsub(n, a, ip, nw, w);\n        }\n        xi = a[0] - a[1];\n        a[0] += a[1];\n        a[1] = xi;\n    } else {\n        a[1] = 0.5 * (a[0] - a[1]);\n        a[0] -= a[1];\n        if (n > 4) {\n            rftbsub(n, a, nc, w + nw);\n            cftbsub(n, a, ip, nw, w);\n        } else if (n == 4) {\n            cftbsub(n, a, ip, nw, w);\n        }\n    }\n}\n\n\nvoid ddct(int n, int isgn, FFTFLT *a, int *ip, FFTFLT *w)\n{\n    void makewt(int nw, int *ip, FFTFLT *w);\n    void makect(int nc, int *ip, FFTFLT *c);\n    void cftfsub(int n, FFTFLT *a, int *ip, int nw, FFTFLT *w);\n    void cftbsub(int n, FFTFLT *a, int *ip, int nw, FFTFLT *w);\n    void rftfsub(int n, FFTFLT *a, int nc, FFTFLT *c);\n    void rftbsub(int n, FFTFLT *a, int nc, FFTFLT *c);\n    void dctsub(int n, FFTFLT *a, int nc, FFTFLT *c);\n    int j, nw, nc;\n    FFTFLT xr;\n\n    nw = ip[0];\n    if (n > (nw << 2)) {\n        nw = n >> 2;\n        makewt(nw, ip, w);\n    }\n    nc = ip[1];\n    if (n > nc) {\n        nc = n;\n        makect(nc, ip, w + nw);\n    }\n    if (isgn < 0) {\n        xr = a[n - 1];\n        for (j = n - 2; j >= 2; j -= 2) {\n            a[j + 1] = a[j] - a[j - 1];\n            a[j] += a[j - 1];\n        }\n        a[1] = a[0] - xr;\n        a[0] += xr;\n        if (n > 4) {\n            rftbsub(n, a, nc, w + nw);\n            cftbsub(n, a, ip, nw, w);\n        } else if (n == 4) {\n            cftbsub(n, a, ip, nw, w);\n        }\n    }\n    dctsub(n, a, nc, w + nw);\n    if (isgn >= 0) {\n        if (n > 4) {\n            cftfsub(n, a, ip, nw, w);\n            rftfsub(n, a, nc, w + nw);\n        } else if (n == 4) {\n            cftfsub(n, a, ip, nw, w);\n        }\n        xr = a[0] - a[1];\n        a[0] += a[1];\n        for (j = 2; j < n; j += 2) {\n            a[j - 1] = a[j] - a[j + 1];\n            a[j] += a[j + 1];\n        }\n        a[n - 1] = xr;\n    }\n}\n\n\nvoid ddst(int n, int isgn, FFTFLT *a, int *ip, FFTFLT *w)\n{\n    void makewt(int nw, int *ip, FFTFLT *w);\n    void makect(int nc, int *ip, FFTFLT *c);\n    void cftfsub(int n, FFTFLT *a, int *ip, int nw, FFTFLT *w);\n    void cftbsub(int n, FFTFLT *a, int *ip, int nw, FFTFLT *w);\n    void rftfsub(int n, FFTFLT *a, int nc, FFTFLT *c);\n    void rftbsub(int n, FFTFLT *a, int nc, FFTFLT *c);\n    void dstsub(int n, FFTFLT *a, int nc, FFTFLT *c);\n    int j, nw, nc;\n    FFTFLT xr;\n\n    nw = ip[0];\n    if (n > (nw << 2)) {\n        nw = n >> 2;\n        makewt(nw, ip, w);\n    }\n    nc = ip[1];\n    if (n > nc) {\n        nc = n;\n        makect(nc, ip, w + nw);\n    }\n    if (isgn < 0) {\n        xr = a[n - 1];\n        for (j = n - 2; j >= 2; j -= 2) {\n            a[j + 1] = -a[j] - a[j - 1];\n            a[j] -= a[j - 1];\n        }\n        a[1] = a[0] + xr;\n        a[0] -= xr;\n        if (n > 4) {\n            rftbsub(n, a, nc, w + nw);\n            cftbsub(n, a, ip, nw, w);\n        } else if (n == 4) {\n            cftbsub(n, a, ip, nw, w);\n        }\n    }\n    dstsub(n, a, nc, w + nw);\n    if (isgn >= 0) {\n        if (n > 4) {\n            cftfsub(n, a, ip, nw, w);\n            rftfsub(n, a, nc, w + nw);\n        } else if (n == 4) {\n            cftfsub(n, a, ip, nw, w);\n        }\n        xr = a[0] - a[1];\n        a[0] += a[1];\n        for (j = 2; j < n; j += 2) {\n            a[j - 1] = -a[j] - a[j + 1];\n            a[j] -= a[j + 1];\n        }\n        a[n - 1] = -xr;\n    }\n}\n\n\nvoid dfct(int n, FFTFLT *a, FFTFLT *t, int *ip, FFTFLT *w)\n{\n    void makewt(int nw, int *ip, FFTFLT *w);\n    void makect(int nc, int *ip, FFTFLT *c);\n    void cftfsub(int n, FFTFLT *a, int *ip, int nw, FFTFLT *w);\n    void rftfsub(int n, FFTFLT *a, int nc, FFTFLT *c);\n    void dctsub(int n, FFTFLT *a, int nc, FFTFLT *c);\n    int j, k, l, m, mh, nw, nc;\n    FFTFLT xr, xi, yr, yi;\n\n    nw = ip[0];\n    if (n > (nw << 3)) {\n        nw = n >> 3;\n        makewt(nw, ip, w);\n    }\n    nc = ip[1];\n    if (n > (nc << 1)) {\n        nc = n >> 1;\n        makect(nc, ip, w + nw);\n    }\n    m = n >> 1;\n    yi = a[m];\n    xi = a[0] + a[n];\n    a[0] -= a[n];\n    t[0] = xi - yi;\n    t[m] = xi + yi;\n    if (n > 2) {\n        mh = m >> 1;\n        for (j = 1; j < mh; j++) {\n            k = m - j;\n            xr = a[j] - a[n - j];\n            xi = a[j] + a[n - j];\n            yr = a[k] - a[n - k];\n            yi = a[k] + a[n - k];\n            a[j] = xr;\n            a[k] = yr;\n            t[j] = xi - yi;\n            t[k] = xi + yi;\n        }\n        t[mh] = a[mh] + a[n - mh];\n        a[mh] -= a[n - mh];\n        dctsub(m, a, nc, w + nw);\n        if (m > 4) {\n            cftfsub(m, a, ip, nw, w);\n            rftfsub(m, a, nc, w + nw);\n        } else if (m == 4) {\n            cftfsub(m, a, ip, nw, w);\n        }\n        a[n - 1] = a[0] - a[1];\n        a[1] = a[0] + a[1];\n        for (j = m - 2; j >= 2; j -= 2) {\n            a[2 * j + 1] = a[j] + a[j + 1];\n            a[2 * j - 1] = a[j] - a[j + 1];\n        }\n        l = 2;\n        m = mh;\n        while (m >= 2) {\n            dctsub(m, t, nc, w + nw);\n            if (m > 4) {\n                cftfsub(m, t, ip, nw, w);\n                rftfsub(m, t, nc, w + nw);\n            } else if (m == 4) {\n                cftfsub(m, t, ip, nw, w);\n            }\n            a[n - l] = t[0] - t[1];\n            a[l] = t[0] + t[1];\n            k = 0;\n            for (j = 2; j < m; j += 2) {\n                k += l << 2;\n                a[k - l] = t[j] - t[j + 1];\n                a[k + l] = t[j] + t[j + 1];\n            }\n            l <<= 1;\n            mh = m >> 1;\n            for (j = 0; j < mh; j++) {\n                k = m - j;\n                t[j] = t[m + k] - t[m + j];\n                t[k] = t[m + k] + t[m + j];\n            }\n            t[mh] = t[m + mh];\n            m = mh;\n        }\n        a[l] = t[0];\n        a[n] = t[2] - t[1];\n        a[0] = t[2] + t[1];\n    } else {\n        a[1] = a[0];\n        a[2] = t[0];\n        a[0] = t[1];\n    }\n}\n\n\nvoid dfst(int n, FFTFLT *a, FFTFLT *t, int *ip, FFTFLT *w)\n{\n    void makewt(int nw, int *ip, FFTFLT *w);\n    void makect(int nc, int *ip, FFTFLT *c);\n    void cftfsub(int n, FFTFLT *a, int *ip, int nw, FFTFLT *w);\n    void rftfsub(int n, FFTFLT *a, int nc, FFTFLT *c);\n    void dstsub(int n, FFTFLT *a, int nc, FFTFLT *c);\n    int j, k, l, m, mh, nw, nc;\n    FFTFLT xr, xi, yr, yi;\n\n    nw = ip[0];\n    if (n > (nw << 3)) {\n        nw = n >> 3;\n        makewt(nw, ip, w);\n    }\n    nc = ip[1];\n    if (n > (nc << 1)) {\n        nc = n >> 1;\n        makect(nc, ip, w + nw);\n    }\n    if (n > 2) {\n        m = n >> 1;\n        mh = m >> 1;\n        for (j = 1; j < mh; j++) {\n            k = m - j;\n            xr = a[j] + a[n - j];\n            xi = a[j] - a[n - j];\n            yr = a[k] + a[n - k];\n            yi = a[k] - a[n - k];\n            a[j] = xr;\n            a[k] = yr;\n            t[j] = xi + yi;\n            t[k] = xi - yi;\n        }\n        t[0] = a[mh] - a[n - mh];\n        a[mh] += a[n - mh];\n        a[0] = a[m];\n        dstsub(m, a, nc, w + nw);\n        if (m > 4) {\n            cftfsub(m, a, ip, nw, w);\n            rftfsub(m, a, nc, w + nw);\n        } else if (m == 4) {\n            cftfsub(m, a, ip, nw, w);\n        }\n        a[n - 1] = a[1] - a[0];\n        a[1] = a[0] + a[1];\n        for (j = m - 2; j >= 2; j -= 2) {\n            a[2 * j + 1] = a[j] - a[j + 1];\n            a[2 * j - 1] = -a[j] - a[j + 1];\n        }\n        l = 2;\n        m = mh;\n        while (m >= 2) {\n            dstsub(m, t, nc, w + nw);\n            if (m > 4) {\n                cftfsub(m, t, ip, nw, w);\n                rftfsub(m, t, nc, w + nw);\n            } else if (m == 4) {\n                cftfsub(m, t, ip, nw, w);\n            }\n            a[n - l] = t[1] - t[0];\n            a[l] = t[0] + t[1];\n            k = 0;\n            for (j = 2; j < m; j += 2) {\n                k += l << 2;\n                a[k - l] = -t[j] - t[j + 1];\n                a[k + l] = t[j] - t[j + 1];\n            }\n            l <<= 1;\n            mh = m >> 1;\n            for (j = 1; j < mh; j++) {\n                k = m - j;\n                t[j] = t[m + k] + t[m + j];\n                t[k] = t[m + k] - t[m + j];\n            }\n            t[0] = t[m + mh];\n            m = mh;\n        }\n        a[l] = t[0];\n    }\n    a[0] = 0;\n}\n\n\n/* -------- initializing routines -------- */\n\n\n#include <math.h>\n\nvoid makewt(int nw, int *ip, FFTFLT *w)\n{\n    void makeipt(int nw, int *ip);\n    int j, nwh, nw0, nw1;\n    FFTFLT delta, wn4r, wk1r, wk1i, wk3r, wk3i;\n\n    ip[0] = nw;\n    ip[1] = 1;\n    if (nw > 2) {\n        nwh = nw >> 1;\n        delta = atan(1.0) / nwh;\n        wn4r = cos(delta * nwh);\n        w[0] = 1;\n        w[1] = wn4r;\n        if (nwh == 4) {\n            w[2] = cos(delta * 2);\n            w[3] = sin(delta * 2);\n        } else if (nwh > 4) {\n            makeipt(nw, ip);\n            w[2] = 0.5 / cos(delta * 2);\n            w[3] = 0.5 / cos(delta * 6);\n            for (j = 4; j < nwh; j += 4) {\n                w[j] = cos(delta * j);\n                w[j + 1] = sin(delta * j);\n                w[j + 2] = cos(3 * delta * j);\n                w[j + 3] = -sin(3 * delta * j);\n            }\n        }\n        nw0 = 0;\n        while (nwh > 2) {\n            nw1 = nw0 + nwh;\n            nwh >>= 1;\n            w[nw1] = 1;\n            w[nw1 + 1] = wn4r;\n            if (nwh == 4) {\n                wk1r = w[nw0 + 4];\n                wk1i = w[nw0 + 5];\n                w[nw1 + 2] = wk1r;\n                w[nw1 + 3] = wk1i;\n            } else if (nwh > 4) {\n                wk1r = w[nw0 + 4];\n                wk3r = w[nw0 + 6];\n                w[nw1 + 2] = 0.5 / wk1r;\n                w[nw1 + 3] = 0.5 / wk3r;\n                for (j = 4; j < nwh; j += 4) {\n                    wk1r = w[nw0 + 2 * j];\n                    wk1i = w[nw0 + 2 * j + 1];\n                    wk3r = w[nw0 + 2 * j + 2];\n                    wk3i = w[nw0 + 2 * j + 3];\n                    w[nw1 + j] = wk1r;\n                    w[nw1 + j + 1] = wk1i;\n                    w[nw1 + j + 2] = wk3r;\n                    w[nw1 + j + 3] = wk3i;\n                }\n            }\n            nw0 = nw1;\n        }\n    }\n}\n\n\nvoid makeipt(int nw, int *ip)\n{\n    int j, l, m, m2, p, q;\n\n    ip[2] = 0;\n    ip[3] = 16;\n    m = 2;\n    for (l = nw; l > 32; l >>= 2) {\n        m2 = m << 1;\n        q = m2 << 3;\n        for (j = m; j < m2; j++) {\n            p = ip[j] << 2;\n            ip[m + j] = p;\n            ip[m2 + j] = p + q;\n        }\n        m = m2;\n    }\n}\n\n\nvoid makect(int nc, int *ip, FFTFLT *c)\n{\n    int j, nch;\n    FFTFLT delta;\n\n    ip[1] = nc;\n    if (nc > 1) {\n        nch = nc >> 1;\n        delta = atan(1.0) / nch;\n        c[0] = cos(delta * nch);\n        c[nch] = 0.5 * c[0];\n        for (j = 1; j < nch; j++) {\n            c[j] = 0.5 * cos(delta * j);\n            c[nc - j] = 0.5 * sin(delta * j);\n        }\n    }\n}\n\n\n/* -------- child routines -------- */\n\n\n#ifdef USE_CDFT_PTHREADS\n#define USE_CDFT_THREADS\n#ifndef CDFT_THREADS_BEGIN_N\n#define CDFT_THREADS_BEGIN_N 8192\n#endif\n#ifndef CDFT_4THREADS_BEGIN_N\n#define CDFT_4THREADS_BEGIN_N 65536\n#endif\n#include <pthread.h>\n#include <stdio.h>\n#include <stdlib.h>\n#define cdft_thread_t pthread_t\n#define cdft_thread_create(thp,func,argp) { \\\n    if (pthread_create(thp, NULL, func, (void *) argp) != 0) { \\\n        fprintf(stderr, \"cdft thread error\\n\"); \\\n        exit(1); \\\n    } \\\n}\n#define cdft_thread_wait(th) { \\\n    if (pthread_join(th, NULL) != 0) { \\\n        fprintf(stderr, \"cdft thread error\\n\"); \\\n        exit(1); \\\n    } \\\n}\n#endif /* USE_CDFT_PTHREADS */\n\n\n#ifdef USE_CDFT_WINTHREADS\n#define USE_CDFT_THREADS\n#ifndef CDFT_THREADS_BEGIN_N\n#define CDFT_THREADS_BEGIN_N 32768\n#endif\n#ifndef CDFT_4THREADS_BEGIN_N\n#define CDFT_4THREADS_BEGIN_N 524288\n#endif\n#include <windows.h>\n#include <stdio.h>\n#include <stdlib.h>\n#define cdft_thread_t HANDLE\n#define cdft_thread_create(thp,func,argp) { \\\n    DWORD thid; \\\n    *(thp) = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) func, (LPVOID) argp, 0, &thid); \\\n    if (*(thp) == 0) { \\\n        fprintf(stderr, \"cdft thread error\\n\"); \\\n        exit(1); \\\n    } \\\n}\n#define cdft_thread_wait(th) { \\\n    WaitForSingleObject(th, INFINITE); \\\n    CloseHandle(th); \\\n}\n#endif /* USE_CDFT_WINTHREADS */\n\n\nvoid cftfsub(int n, FFTFLT *a, int *ip, int nw, FFTFLT *w)\n{\n    void bitrv2(int n, int *ip, FFTFLT *a);\n    void bitrv216(FFTFLT *a);\n    void bitrv208(FFTFLT *a);\n    void cftf1st(int n, FFTFLT *a, FFTFLT *w);\n    void cftrec4(int n, FFTFLT *a, int nw, FFTFLT *w);\n    void cftleaf(int n, int isplt, FFTFLT *a, int nw, FFTFLT *w);\n    void cftfx41(int n, FFTFLT *a, int nw, FFTFLT *w);\n    void cftf161(FFTFLT *a, FFTFLT *w);\n    void cftf081(FFTFLT *a, FFTFLT *w);\n    void cftf040(FFTFLT *a);\n    void cftx020(FFTFLT *a);\n#ifdef USE_CDFT_THREADS\n    void cftrec4_th(int n, FFTFLT *a, int nw, FFTFLT *w);\n#endif /* USE_CDFT_THREADS */\n\n    if (n > 8) {\n        if (n > 32) {\n            cftf1st(n, a, &w[nw - (n >> 2)]);\n#ifdef USE_CDFT_THREADS\n            if (n > CDFT_THREADS_BEGIN_N) {\n                cftrec4_th(n, a, nw, w);\n            } else\n#endif /* USE_CDFT_THREADS */\n            if (n > 512) {\n                cftrec4(n, a, nw, w);\n            } else if (n > 128) {\n                cftleaf(n, 1, a, nw, w);\n            } else {\n                cftfx41(n, a, nw, w);\n            }\n            bitrv2(n, ip, a);\n        } else if (n == 32) {\n            cftf161(a, &w[nw - 8]);\n            bitrv216(a);\n        } else {\n            cftf081(a, w);\n            bitrv208(a);\n        }\n    } else if (n == 8) {\n        cftf040(a);\n    } else if (n == 4) {\n        cftx020(a);\n    }\n}\n\n\nvoid cftbsub(int n, FFTFLT *a, int *ip, int nw, FFTFLT *w)\n{\n    void bitrv2conj(int n, int *ip, FFTFLT *a);\n    void bitrv216neg(FFTFLT *a);\n    void bitrv208neg(FFTFLT *a);\n    void cftb1st(int n, FFTFLT *a, FFTFLT *w);\n    void cftrec4(int n, FFTFLT *a, int nw, FFTFLT *w);\n    void cftleaf(int n, int isplt, FFTFLT *a, int nw, FFTFLT *w);\n    void cftfx41(int n, FFTFLT *a, int nw, FFTFLT *w);\n    void cftf161(FFTFLT *a, FFTFLT *w);\n    void cftf081(FFTFLT *a, FFTFLT *w);\n    void cftb040(FFTFLT *a);\n    void cftx020(FFTFLT *a);\n#ifdef USE_CDFT_THREADS\n    void cftrec4_th(int n, FFTFLT *a, int nw, FFTFLT *w);\n#endif /* USE_CDFT_THREADS */\n\n    if (n > 8) {\n        if (n > 32) {\n            cftb1st(n, a, &w[nw - (n >> 2)]);\n#ifdef USE_CDFT_THREADS\n            if (n > CDFT_THREADS_BEGIN_N) {\n                cftrec4_th(n, a, nw, w);\n            } else\n#endif /* USE_CDFT_THREADS */\n            if (n > 512) {\n                cftrec4(n, a, nw, w);\n            } else if (n > 128) {\n                cftleaf(n, 1, a, nw, w);\n            } else {\n                cftfx41(n, a, nw, w);\n            }\n            bitrv2conj(n, ip, a);\n        } else if (n == 32) {\n            cftf161(a, &w[nw - 8]);\n            bitrv216neg(a);\n        } else {\n            cftf081(a, w);\n            bitrv208neg(a);\n        }\n    } else if (n == 8) {\n        cftb040(a);\n    } else if (n == 4) {\n        cftx020(a);\n    }\n}\n\n\nvoid bitrv2(int n, int *ip, FFTFLT *a)\n{\n    int j, j1, k, k1, l, m, nh, nm;\n    FFTFLT xr, xi, yr, yi;\n\n    m = 1;\n    for (l = n >> 2; l > 8; l >>= 2) {\n        m <<= 1;\n    }\n    nh = n >> 1;\n    nm = 4 * m;\n    if (l == 8) {\n        for (k = 0; k < m; k++) {\n            for (j = 0; j < k; j++) {\n                j1 = 4 * j + 2 * ip[m + k];\n                k1 = 4 * k + 2 * ip[m + j];\n                xr = a[j1];\n                xi = a[j1 + 1];\n                yr = a[k1];\n                yi = a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 += nm;\n                k1 += 2 * nm;\n                xr = a[j1];\n                xi = a[j1 + 1];\n                yr = a[k1];\n                yi = a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 += nm;\n                k1 -= nm;\n                xr = a[j1];\n                xi = a[j1 + 1];\n                yr = a[k1];\n                yi = a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 += nm;\n                k1 += 2 * nm;\n                xr = a[j1];\n                xi = a[j1 + 1];\n                yr = a[k1];\n                yi = a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 += nh;\n                k1 += 2;\n                xr = a[j1];\n                xi = a[j1 + 1];\n                yr = a[k1];\n                yi = a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 -= nm;\n                k1 -= 2 * nm;\n                xr = a[j1];\n                xi = a[j1 + 1];\n                yr = a[k1];\n                yi = a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 -= nm;\n                k1 += nm;\n                xr = a[j1];\n                xi = a[j1 + 1];\n                yr = a[k1];\n                yi = a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 -= nm;\n                k1 -= 2 * nm;\n                xr = a[j1];\n                xi = a[j1 + 1];\n                yr = a[k1];\n                yi = a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 += 2;\n                k1 += nh;\n                xr = a[j1];\n                xi = a[j1 + 1];\n                yr = a[k1];\n                yi = a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 += nm;\n                k1 += 2 * nm;\n                xr = a[j1];\n                xi = a[j1 + 1];\n                yr = a[k1];\n                yi = a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 += nm;\n                k1 -= nm;\n                xr = a[j1];\n                xi = a[j1 + 1];\n                yr = a[k1];\n                yi = a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 += nm;\n                k1 += 2 * nm;\n                xr = a[j1];\n                xi = a[j1 + 1];\n                yr = a[k1];\n                yi = a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 -= nh;\n                k1 -= 2;\n                xr = a[j1];\n                xi = a[j1 + 1];\n                yr = a[k1];\n                yi = a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 -= nm;\n                k1 -= 2 * nm;\n                xr = a[j1];\n                xi = a[j1 + 1];\n                yr = a[k1];\n                yi = a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 -= nm;\n                k1 += nm;\n                xr = a[j1];\n                xi = a[j1 + 1];\n                yr = a[k1];\n                yi = a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 -= nm;\n                k1 -= 2 * nm;\n                xr = a[j1];\n                xi = a[j1 + 1];\n                yr = a[k1];\n                yi = a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n            }\n            k1 = 4 * k + 2 * ip[m + k];\n            j1 = k1 + 2;\n            k1 += nh;\n            xr = a[j1];\n            xi = a[j1 + 1];\n            yr = a[k1];\n            yi = a[k1 + 1];\n            a[j1] = yr;\n            a[j1 + 1] = yi;\n            a[k1] = xr;\n            a[k1 + 1] = xi;\n            j1 += nm;\n            k1 += 2 * nm;\n            xr = a[j1];\n            xi = a[j1 + 1];\n            yr = a[k1];\n            yi = a[k1 + 1];\n            a[j1] = yr;\n            a[j1 + 1] = yi;\n            a[k1] = xr;\n            a[k1 + 1] = xi;\n            j1 += nm;\n            k1 -= nm;\n            xr = a[j1];\n            xi = a[j1 + 1];\n            yr = a[k1];\n            yi = a[k1 + 1];\n            a[j1] = yr;\n            a[j1 + 1] = yi;\n            a[k1] = xr;\n            a[k1 + 1] = xi;\n            j1 -= 2;\n            k1 -= nh;\n            xr = a[j1];\n            xi = a[j1 + 1];\n            yr = a[k1];\n            yi = a[k1 + 1];\n            a[j1] = yr;\n            a[j1 + 1] = yi;\n            a[k1] = xr;\n            a[k1 + 1] = xi;\n            j1 += nh + 2;\n            k1 += nh + 2;\n            xr = a[j1];\n            xi = a[j1 + 1];\n            yr = a[k1];\n            yi = a[k1 + 1];\n            a[j1] = yr;\n            a[j1 + 1] = yi;\n            a[k1] = xr;\n            a[k1 + 1] = xi;\n            j1 -= nh - nm;\n            k1 += 2 * nm - 2;\n            xr = a[j1];\n            xi = a[j1 + 1];\n            yr = a[k1];\n            yi = a[k1 + 1];\n            a[j1] = yr;\n            a[j1 + 1] = yi;\n            a[k1] = xr;\n            a[k1 + 1] = xi;\n        }\n    } else {\n        for (k = 0; k < m; k++) {\n            for (j = 0; j < k; j++) {\n                j1 = 4 * j + ip[m + k];\n                k1 = 4 * k + ip[m + j];\n                xr = a[j1];\n                xi = a[j1 + 1];\n                yr = a[k1];\n                yi = a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 += nm;\n                k1 += nm;\n                xr = a[j1];\n                xi = a[j1 + 1];\n                yr = a[k1];\n                yi = a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 += nh;\n                k1 += 2;\n                xr = a[j1];\n                xi = a[j1 + 1];\n                yr = a[k1];\n                yi = a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 -= nm;\n                k1 -= nm;\n                xr = a[j1];\n                xi = a[j1 + 1];\n                yr = a[k1];\n                yi = a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 += 2;\n                k1 += nh;\n                xr = a[j1];\n                xi = a[j1 + 1];\n                yr = a[k1];\n                yi = a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 += nm;\n                k1 += nm;\n                xr = a[j1];\n                xi = a[j1 + 1];\n                yr = a[k1];\n                yi = a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 -= nh;\n                k1 -= 2;\n                xr = a[j1];\n                xi = a[j1 + 1];\n                yr = a[k1];\n                yi = a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 -= nm;\n                k1 -= nm;\n                xr = a[j1];\n                xi = a[j1 + 1];\n                yr = a[k1];\n                yi = a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n            }\n            k1 = 4 * k + ip[m + k];\n            j1 = k1 + 2;\n            k1 += nh;\n            xr = a[j1];\n            xi = a[j1 + 1];\n            yr = a[k1];\n            yi = a[k1 + 1];\n            a[j1] = yr;\n            a[j1 + 1] = yi;\n            a[k1] = xr;\n            a[k1 + 1] = xi;\n            j1 += nm;\n            k1 += nm;\n            xr = a[j1];\n            xi = a[j1 + 1];\n            yr = a[k1];\n            yi = a[k1 + 1];\n            a[j1] = yr;\n            a[j1 + 1] = yi;\n            a[k1] = xr;\n            a[k1 + 1] = xi;\n        }\n    }\n}\n\n\nvoid bitrv2conj(int n, int *ip, FFTFLT *a)\n{\n    int j, j1, k, k1, l, m, nh, nm;\n    FFTFLT xr, xi, yr, yi;\n\n    m = 1;\n    for (l = n >> 2; l > 8; l >>= 2) {\n        m <<= 1;\n    }\n    nh = n >> 1;\n    nm = 4 * m;\n    if (l == 8) {\n        for (k = 0; k < m; k++) {\n            for (j = 0; j < k; j++) {\n                j1 = 4 * j + 2 * ip[m + k];\n                k1 = 4 * k + 2 * ip[m + j];\n                xr = a[j1];\n                xi = -a[j1 + 1];\n                yr = a[k1];\n                yi = -a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 += nm;\n                k1 += 2 * nm;\n                xr = a[j1];\n                xi = -a[j1 + 1];\n                yr = a[k1];\n                yi = -a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 += nm;\n                k1 -= nm;\n                xr = a[j1];\n                xi = -a[j1 + 1];\n                yr = a[k1];\n                yi = -a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 += nm;\n                k1 += 2 * nm;\n                xr = a[j1];\n                xi = -a[j1 + 1];\n                yr = a[k1];\n                yi = -a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 += nh;\n                k1 += 2;\n                xr = a[j1];\n                xi = -a[j1 + 1];\n                yr = a[k1];\n                yi = -a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 -= nm;\n                k1 -= 2 * nm;\n                xr = a[j1];\n                xi = -a[j1 + 1];\n                yr = a[k1];\n                yi = -a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 -= nm;\n                k1 += nm;\n                xr = a[j1];\n                xi = -a[j1 + 1];\n                yr = a[k1];\n                yi = -a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 -= nm;\n                k1 -= 2 * nm;\n                xr = a[j1];\n                xi = -a[j1 + 1];\n                yr = a[k1];\n                yi = -a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 += 2;\n                k1 += nh;\n                xr = a[j1];\n                xi = -a[j1 + 1];\n                yr = a[k1];\n                yi = -a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 += nm;\n                k1 += 2 * nm;\n                xr = a[j1];\n                xi = -a[j1 + 1];\n                yr = a[k1];\n                yi = -a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 += nm;\n                k1 -= nm;\n                xr = a[j1];\n                xi = -a[j1 + 1];\n                yr = a[k1];\n                yi = -a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 += nm;\n                k1 += 2 * nm;\n                xr = a[j1];\n                xi = -a[j1 + 1];\n                yr = a[k1];\n                yi = -a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 -= nh;\n                k1 -= 2;\n                xr = a[j1];\n                xi = -a[j1 + 1];\n                yr = a[k1];\n                yi = -a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 -= nm;\n                k1 -= 2 * nm;\n                xr = a[j1];\n                xi = -a[j1 + 1];\n                yr = a[k1];\n                yi = -a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 -= nm;\n                k1 += nm;\n                xr = a[j1];\n                xi = -a[j1 + 1];\n                yr = a[k1];\n                yi = -a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 -= nm;\n                k1 -= 2 * nm;\n                xr = a[j1];\n                xi = -a[j1 + 1];\n                yr = a[k1];\n                yi = -a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n            }\n            k1 = 4 * k + 2 * ip[m + k];\n            j1 = k1 + 2;\n            k1 += nh;\n            a[j1 - 1] = -a[j1 - 1];\n            xr = a[j1];\n            xi = -a[j1 + 1];\n            yr = a[k1];\n            yi = -a[k1 + 1];\n            a[j1] = yr;\n            a[j1 + 1] = yi;\n            a[k1] = xr;\n            a[k1 + 1] = xi;\n            a[k1 + 3] = -a[k1 + 3];\n            j1 += nm;\n            k1 += 2 * nm;\n            xr = a[j1];\n            xi = -a[j1 + 1];\n            yr = a[k1];\n            yi = -a[k1 + 1];\n            a[j1] = yr;\n            a[j1 + 1] = yi;\n            a[k1] = xr;\n            a[k1 + 1] = xi;\n            j1 += nm;\n            k1 -= nm;\n            xr = a[j1];\n            xi = -a[j1 + 1];\n            yr = a[k1];\n            yi = -a[k1 + 1];\n            a[j1] = yr;\n            a[j1 + 1] = yi;\n            a[k1] = xr;\n            a[k1 + 1] = xi;\n            j1 -= 2;\n            k1 -= nh;\n            xr = a[j1];\n            xi = -a[j1 + 1];\n            yr = a[k1];\n            yi = -a[k1 + 1];\n            a[j1] = yr;\n            a[j1 + 1] = yi;\n            a[k1] = xr;\n            a[k1 + 1] = xi;\n            j1 += nh + 2;\n            k1 += nh + 2;\n            xr = a[j1];\n            xi = -a[j1 + 1];\n            yr = a[k1];\n            yi = -a[k1 + 1];\n            a[j1] = yr;\n            a[j1 + 1] = yi;\n            a[k1] = xr;\n            a[k1 + 1] = xi;\n            j1 -= nh - nm;\n            k1 += 2 * nm - 2;\n            a[j1 - 1] = -a[j1 - 1];\n            xr = a[j1];\n            xi = -a[j1 + 1];\n            yr = a[k1];\n            yi = -a[k1 + 1];\n            a[j1] = yr;\n            a[j1 + 1] = yi;\n            a[k1] = xr;\n            a[k1 + 1] = xi;\n            a[k1 + 3] = -a[k1 + 3];\n        }\n    } else {\n        for (k = 0; k < m; k++) {\n            for (j = 0; j < k; j++) {\n                j1 = 4 * j + ip[m + k];\n                k1 = 4 * k + ip[m + j];\n                xr = a[j1];\n                xi = -a[j1 + 1];\n                yr = a[k1];\n                yi = -a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 += nm;\n                k1 += nm;\n                xr = a[j1];\n                xi = -a[j1 + 1];\n                yr = a[k1];\n                yi = -a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 += nh;\n                k1 += 2;\n                xr = a[j1];\n                xi = -a[j1 + 1];\n                yr = a[k1];\n                yi = -a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 -= nm;\n                k1 -= nm;\n                xr = a[j1];\n                xi = -a[j1 + 1];\n                yr = a[k1];\n                yi = -a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 += 2;\n                k1 += nh;\n                xr = a[j1];\n                xi = -a[j1 + 1];\n                yr = a[k1];\n                yi = -a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 += nm;\n                k1 += nm;\n                xr = a[j1];\n                xi = -a[j1 + 1];\n                yr = a[k1];\n                yi = -a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 -= nh;\n                k1 -= 2;\n                xr = a[j1];\n                xi = -a[j1 + 1];\n                yr = a[k1];\n                yi = -a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n                j1 -= nm;\n                k1 -= nm;\n                xr = a[j1];\n                xi = -a[j1 + 1];\n                yr = a[k1];\n                yi = -a[k1 + 1];\n                a[j1] = yr;\n                a[j1 + 1] = yi;\n                a[k1] = xr;\n                a[k1 + 1] = xi;\n            }\n            k1 = 4 * k + ip[m + k];\n            j1 = k1 + 2;\n            k1 += nh;\n            a[j1 - 1] = -a[j1 - 1];\n            xr = a[j1];\n            xi = -a[j1 + 1];\n            yr = a[k1];\n            yi = -a[k1 + 1];\n            a[j1] = yr;\n            a[j1 + 1] = yi;\n            a[k1] = xr;\n            a[k1 + 1] = xi;\n            a[k1 + 3] = -a[k1 + 3];\n            j1 += nm;\n            k1 += nm;\n            a[j1 - 1] = -a[j1 - 1];\n            xr = a[j1];\n            xi = -a[j1 + 1];\n            yr = a[k1];\n            yi = -a[k1 + 1];\n            a[j1] = yr;\n            a[j1 + 1] = yi;\n            a[k1] = xr;\n            a[k1 + 1] = xi;\n            a[k1 + 3] = -a[k1 + 3];\n        }\n    }\n}\n\n\nvoid bitrv216(FFTFLT *a)\n{\n    FFTFLT x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i,\n        x5r, x5i, x7r, x7i, x8r, x8i, x10r, x10i,\n        x11r, x11i, x12r, x12i, x13r, x13i, x14r, x14i;\n\n    x1r = a[2];\n    x1i = a[3];\n    x2r = a[4];\n    x2i = a[5];\n    x3r = a[6];\n    x3i = a[7];\n    x4r = a[8];\n    x4i = a[9];\n    x5r = a[10];\n    x5i = a[11];\n    x7r = a[14];\n    x7i = a[15];\n    x8r = a[16];\n    x8i = a[17];\n    x10r = a[20];\n    x10i = a[21];\n    x11r = a[22];\n    x11i = a[23];\n    x12r = a[24];\n    x12i = a[25];\n    x13r = a[26];\n    x13i = a[27];\n    x14r = a[28];\n    x14i = a[29];\n    a[2] = x8r;\n    a[3] = x8i;\n    a[4] = x4r;\n    a[5] = x4i;\n    a[6] = x12r;\n    a[7] = x12i;\n    a[8] = x2r;\n    a[9] = x2i;\n    a[10] = x10r;\n    a[11] = x10i;\n    a[14] = x14r;\n    a[15] = x14i;\n    a[16] = x1r;\n    a[17] = x1i;\n    a[20] = x5r;\n    a[21] = x5i;\n    a[22] = x13r;\n    a[23] = x13i;\n    a[24] = x3r;\n    a[25] = x3i;\n    a[26] = x11r;\n    a[27] = x11i;\n    a[28] = x7r;\n    a[29] = x7i;\n}\n\n\nvoid bitrv216neg(FFTFLT *a)\n{\n    FFTFLT x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i,\n        x5r, x5i, x6r, x6i, x7r, x7i, x8r, x8i,\n        x9r, x9i, x10r, x10i, x11r, x11i, x12r, x12i,\n        x13r, x13i, x14r, x14i, x15r, x15i;\n\n    x1r = a[2];\n    x1i = a[3];\n    x2r = a[4];\n    x2i = a[5];\n    x3r = a[6];\n    x3i = a[7];\n    x4r = a[8];\n    x4i = a[9];\n    x5r = a[10];\n    x5i = a[11];\n    x6r = a[12];\n    x6i = a[13];\n    x7r = a[14];\n    x7i = a[15];\n    x8r = a[16];\n    x8i = a[17];\n    x9r = a[18];\n    x9i = a[19];\n    x10r = a[20];\n    x10i = a[21];\n    x11r = a[22];\n    x11i = a[23];\n    x12r = a[24];\n    x12i = a[25];\n    x13r = a[26];\n    x13i = a[27];\n    x14r = a[28];\n    x14i = a[29];\n    x15r = a[30];\n    x15i = a[31];\n    a[2] = x15r;\n    a[3] = x15i;\n    a[4] = x7r;\n    a[5] = x7i;\n    a[6] = x11r;\n    a[7] = x11i;\n    a[8] = x3r;\n    a[9] = x3i;\n    a[10] = x13r;\n    a[11] = x13i;\n    a[12] = x5r;\n    a[13] = x5i;\n    a[14] = x9r;\n    a[15] = x9i;\n    a[16] = x1r;\n    a[17] = x1i;\n    a[18] = x14r;\n    a[19] = x14i;\n    a[20] = x6r;\n    a[21] = x6i;\n    a[22] = x10r;\n    a[23] = x10i;\n    a[24] = x2r;\n    a[25] = x2i;\n    a[26] = x12r;\n    a[27] = x12i;\n    a[28] = x4r;\n    a[29] = x4i;\n    a[30] = x8r;\n    a[31] = x8i;\n}\n\n\nvoid bitrv208(FFTFLT *a)\n{\n    FFTFLT x1r, x1i, x3r, x3i, x4r, x4i, x6r, x6i;\n\n    x1r = a[2];\n    x1i = a[3];\n    x3r = a[6];\n    x3i = a[7];\n    x4r = a[8];\n    x4i = a[9];\n    x6r = a[12];\n    x6i = a[13];\n    a[2] = x4r;\n    a[3] = x4i;\n    a[6] = x6r;\n    a[7] = x6i;\n    a[8] = x1r;\n    a[9] = x1i;\n    a[12] = x3r;\n    a[13] = x3i;\n}\n\n\nvoid bitrv208neg(FFTFLT *a)\n{\n    FFTFLT x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i,\n        x5r, x5i, x6r, x6i, x7r, x7i;\n\n    x1r = a[2];\n    x1i = a[3];\n    x2r = a[4];\n    x2i = a[5];\n    x3r = a[6];\n    x3i = a[7];\n    x4r = a[8];\n    x4i = a[9];\n    x5r = a[10];\n    x5i = a[11];\n    x6r = a[12];\n    x6i = a[13];\n    x7r = a[14];\n    x7i = a[15];\n    a[2] = x7r;\n    a[3] = x7i;\n    a[4] = x3r;\n    a[5] = x3i;\n    a[6] = x5r;\n    a[7] = x5i;\n    a[8] = x1r;\n    a[9] = x1i;\n    a[10] = x6r;\n    a[11] = x6i;\n    a[12] = x2r;\n    a[13] = x2i;\n    a[14] = x4r;\n    a[15] = x4i;\n}\n\n\nvoid cftf1st(int n, FFTFLT *a, FFTFLT *w)\n{\n    int j, j0, j1, j2, j3, k, m, mh;\n    FFTFLT wn4r, csc1, csc3, wk1r, wk1i, wk3r, wk3i,\n        wd1r, wd1i, wd3r, wd3i;\n    FFTFLT x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i,\n        y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i;\n\n    mh = n >> 3;\n    m = 2 * mh;\n    j1 = m;\n    j2 = j1 + m;\n    j3 = j2 + m;\n    x0r = a[0] + a[j2];\n    x0i = a[1] + a[j2 + 1];\n    x1r = a[0] - a[j2];\n    x1i = a[1] - a[j2 + 1];\n    x2r = a[j1] + a[j3];\n    x2i = a[j1 + 1] + a[j3 + 1];\n    x3r = a[j1] - a[j3];\n    x3i = a[j1 + 1] - a[j3 + 1];\n    a[0] = x0r + x2r;\n    a[1] = x0i + x2i;\n    a[j1] = x0r - x2r;\n    a[j1 + 1] = x0i - x2i;\n    a[j2] = x1r - x3i;\n    a[j2 + 1] = x1i + x3r;\n    a[j3] = x1r + x3i;\n    a[j3 + 1] = x1i - x3r;\n    wn4r = w[1];\n    csc1 = w[2];\n    csc3 = w[3];\n    wd1r = 1;\n    wd1i = 0;\n    wd3r = 1;\n    wd3i = 0;\n    k = 0;\n    for (j = 2; j < mh - 2; j += 4) {\n        k += 4;\n        wk1r = csc1 * (wd1r + w[k]);\n        wk1i = csc1 * (wd1i + w[k + 1]);\n        wk3r = csc3 * (wd3r + w[k + 2]);\n        wk3i = csc3 * (wd3i + w[k + 3]);\n        wd1r = w[k];\n        wd1i = w[k + 1];\n        wd3r = w[k + 2];\n        wd3i = w[k + 3];\n        j1 = j + m;\n        j2 = j1 + m;\n        j3 = j2 + m;\n        x0r = a[j] + a[j2];\n        x0i = a[j + 1] + a[j2 + 1];\n        x1r = a[j] - a[j2];\n        x1i = a[j + 1] - a[j2 + 1];\n        y0r = a[j + 2] + a[j2 + 2];\n        y0i = a[j + 3] + a[j2 + 3];\n        y1r = a[j + 2] - a[j2 + 2];\n        y1i = a[j + 3] - a[j2 + 3];\n        x2r = a[j1] + a[j3];\n        x2i = a[j1 + 1] + a[j3 + 1];\n        x3r = a[j1] - a[j3];\n        x3i = a[j1 + 1] - a[j3 + 1];\n        y2r = a[j1 + 2] + a[j3 + 2];\n        y2i = a[j1 + 3] + a[j3 + 3];\n        y3r = a[j1 + 2] - a[j3 + 2];\n        y3i = a[j1 + 3] - a[j3 + 3];\n        a[j] = x0r + x2r;\n        a[j + 1] = x0i + x2i;\n        a[j + 2] = y0r + y2r;\n        a[j + 3] = y0i + y2i;\n        a[j1] = x0r - x2r;\n        a[j1 + 1] = x0i - x2i;\n        a[j1 + 2] = y0r - y2r;\n        a[j1 + 3] = y0i - y2i;\n        x0r = x1r - x3i;\n        x0i = x1i + x3r;\n        a[j2] = wk1r * x0r - wk1i * x0i;\n        a[j2 + 1] = wk1r * x0i + wk1i * x0r;\n        x0r = y1r - y3i;\n        x0i = y1i + y3r;\n        a[j2 + 2] = wd1r * x0r - wd1i * x0i;\n        a[j2 + 3] = wd1r * x0i + wd1i * x0r;\n        x0r = x1r + x3i;\n        x0i = x1i - x3r;\n        a[j3] = wk3r * x0r + wk3i * x0i;\n        a[j3 + 1] = wk3r * x0i - wk3i * x0r;\n        x0r = y1r + y3i;\n        x0i = y1i - y3r;\n        a[j3 + 2] = wd3r * x0r + wd3i * x0i;\n        a[j3 + 3] = wd3r * x0i - wd3i * x0r;\n        j0 = m - j;\n        j1 = j0 + m;\n        j2 = j1 + m;\n        j3 = j2 + m;\n        x0r = a[j0] + a[j2];\n        x0i = a[j0 + 1] + a[j2 + 1];\n        x1r = a[j0] - a[j2];\n        x1i = a[j0 + 1] - a[j2 + 1];\n        y0r = a[j0 - 2] + a[j2 - 2];\n        y0i = a[j0 - 1] + a[j2 - 1];\n        y1r = a[j0 - 2] - a[j2 - 2];\n        y1i = a[j0 - 1] - a[j2 - 1];\n        x2r = a[j1] + a[j3];\n        x2i = a[j1 + 1] + a[j3 + 1];\n        x3r = a[j1] - a[j3];\n        x3i = a[j1 + 1] - a[j3 + 1];\n        y2r = a[j1 - 2] + a[j3 - 2];\n        y2i = a[j1 - 1] + a[j3 - 1];\n        y3r = a[j1 - 2] - a[j3 - 2];\n        y3i = a[j1 - 1] - a[j3 - 1];\n        a[j0] = x0r + x2r;\n        a[j0 + 1] = x0i + x2i;\n        a[j0 - 2] = y0r + y2r;\n        a[j0 - 1] = y0i + y2i;\n        a[j1] = x0r - x2r;\n        a[j1 + 1] = x0i - x2i;\n        a[j1 - 2] = y0r - y2r;\n        a[j1 - 1] = y0i - y2i;\n        x0r = x1r - x3i;\n        x0i = x1i + x3r;\n        a[j2] = wk1i * x0r - wk1r * x0i;\n        a[j2 + 1] = wk1i * x0i + wk1r * x0r;\n        x0r = y1r - y3i;\n        x0i = y1i + y3r;\n        a[j2 - 2] = wd1i * x0r - wd1r * x0i;\n        a[j2 - 1] = wd1i * x0i + wd1r * x0r;\n        x0r = x1r + x3i;\n        x0i = x1i - x3r;\n        a[j3] = wk3i * x0r + wk3r * x0i;\n        a[j3 + 1] = wk3i * x0i - wk3r * x0r;\n        x0r = y1r + y3i;\n        x0i = y1i - y3r;\n        a[j3 - 2] = wd3i * x0r + wd3r * x0i;\n        a[j3 - 1] = wd3i * x0i - wd3r * x0r;\n    }\n    wk1r = csc1 * (wd1r + wn4r);\n    wk1i = csc1 * (wd1i + wn4r);\n    wk3r = csc3 * (wd3r - wn4r);\n    wk3i = csc3 * (wd3i - wn4r);\n    j0 = mh;\n    j1 = j0 + m;\n    j2 = j1 + m;\n    j3 = j2 + m;\n    x0r = a[j0 - 2] + a[j2 - 2];\n    x0i = a[j0 - 1] + a[j2 - 1];\n    x1r = a[j0 - 2] - a[j2 - 2];\n    x1i = a[j0 - 1] - a[j2 - 1];\n    x2r = a[j1 - 2] + a[j3 - 2];\n    x2i = a[j1 - 1] + a[j3 - 1];\n    x3r = a[j1 - 2] - a[j3 - 2];\n    x3i = a[j1 - 1] - a[j3 - 1];\n    a[j0 - 2] = x0r + x2r;\n    a[j0 - 1] = x0i + x2i;\n    a[j1 - 2] = x0r - x2r;\n    a[j1 - 1] = x0i - x2i;\n    x0r = x1r - x3i;\n    x0i = x1i + x3r;\n    a[j2 - 2] = wk1r * x0r - wk1i * x0i;\n    a[j2 - 1] = wk1r * x0i + wk1i * x0r;\n    x0r = x1r + x3i;\n    x0i = x1i - x3r;\n    a[j3 - 2] = wk3r * x0r + wk3i * x0i;\n    a[j3 - 1] = wk3r * x0i - wk3i * x0r;\n    x0r = a[j0] + a[j2];\n    x0i = a[j0 + 1] + a[j2 + 1];\n    x1r = a[j0] - a[j2];\n    x1i = a[j0 + 1] - a[j2 + 1];\n    x2r = a[j1] + a[j3];\n    x2i = a[j1 + 1] + a[j3 + 1];\n    x3r = a[j1] - a[j3];\n    x3i = a[j1 + 1] - a[j3 + 1];\n    a[j0] = x0r + x2r;\n    a[j0 + 1] = x0i + x2i;\n    a[j1] = x0r - x2r;\n    a[j1 + 1] = x0i - x2i;\n    x0r = x1r - x3i;\n    x0i = x1i + x3r;\n    a[j2] = wn4r * (x0r - x0i);\n    a[j2 + 1] = wn4r * (x0i + x0r);\n    x0r = x1r + x3i;\n    x0i = x1i - x3r;\n    a[j3] = -wn4r * (x0r + x0i);\n    a[j3 + 1] = -wn4r * (x0i - x0r);\n    x0r = a[j0 + 2] + a[j2 + 2];\n    x0i = a[j0 + 3] + a[j2 + 3];\n    x1r = a[j0 + 2] - a[j2 + 2];\n    x1i = a[j0 + 3] - a[j2 + 3];\n    x2r = a[j1 + 2] + a[j3 + 2];\n    x2i = a[j1 + 3] + a[j3 + 3];\n    x3r = a[j1 + 2] - a[j3 + 2];\n    x3i = a[j1 + 3] - a[j3 + 3];\n    a[j0 + 2] = x0r + x2r;\n    a[j0 + 3] = x0i + x2i;\n    a[j1 + 2] = x0r - x2r;\n    a[j1 + 3] = x0i - x2i;\n    x0r = x1r - x3i;\n    x0i = x1i + x3r;\n    a[j2 + 2] = wk1i * x0r - wk1r * x0i;\n    a[j2 + 3] = wk1i * x0i + wk1r * x0r;\n    x0r = x1r + x3i;\n    x0i = x1i - x3r;\n    a[j3 + 2] = wk3i * x0r + wk3r * x0i;\n    a[j3 + 3] = wk3i * x0i - wk3r * x0r;\n}\n\n\nvoid cftb1st(int n, FFTFLT *a, FFTFLT *w)\n{\n    int j, j0, j1, j2, j3, k, m, mh;\n    FFTFLT wn4r, csc1, csc3, wk1r, wk1i, wk3r, wk3i,\n        wd1r, wd1i, wd3r, wd3i;\n    FFTFLT x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i,\n        y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i;\n\n    mh = n >> 3;\n    m = 2 * mh;\n    j1 = m;\n    j2 = j1 + m;\n    j3 = j2 + m;\n    x0r = a[0] + a[j2];\n    x0i = -a[1] - a[j2 + 1];\n    x1r = a[0] - a[j2];\n    x1i = -a[1] + a[j2 + 1];\n    x2r = a[j1] + a[j3];\n    x2i = a[j1 + 1] + a[j3 + 1];\n    x3r = a[j1] - a[j3];\n    x3i = a[j1 + 1] - a[j3 + 1];\n    a[0] = x0r + x2r;\n    a[1] = x0i - x2i;\n    a[j1] = x0r - x2r;\n    a[j1 + 1] = x0i + x2i;\n    a[j2] = x1r + x3i;\n    a[j2 + 1] = x1i + x3r;\n    a[j3] = x1r - x3i;\n    a[j3 + 1] = x1i - x3r;\n    wn4r = w[1];\n    csc1 = w[2];\n    csc3 = w[3];\n    wd1r = 1;\n    wd1i = 0;\n    wd3r = 1;\n    wd3i = 0;\n    k = 0;\n    for (j = 2; j < mh - 2; j += 4) {\n        k += 4;\n        wk1r = csc1 * (wd1r + w[k]);\n        wk1i = csc1 * (wd1i + w[k + 1]);\n        wk3r = csc3 * (wd3r + w[k + 2]);\n        wk3i = csc3 * (wd3i + w[k + 3]);\n        wd1r = w[k];\n        wd1i = w[k + 1];\n        wd3r = w[k + 2];\n        wd3i = w[k + 3];\n        j1 = j + m;\n        j2 = j1 + m;\n        j3 = j2 + m;\n        x0r = a[j] + a[j2];\n        x0i = -a[j + 1] - a[j2 + 1];\n        x1r = a[j] - a[j2];\n        x1i = -a[j + 1] + a[j2 + 1];\n        y0r = a[j + 2] + a[j2 + 2];\n        y0i = -a[j + 3] - a[j2 + 3];\n        y1r = a[j + 2] - a[j2 + 2];\n        y1i = -a[j + 3] + a[j2 + 3];\n        x2r = a[j1] + a[j3];\n        x2i = a[j1 + 1] + a[j3 + 1];\n        x3r = a[j1] - a[j3];\n        x3i = a[j1 + 1] - a[j3 + 1];\n        y2r = a[j1 + 2] + a[j3 + 2];\n        y2i = a[j1 + 3] + a[j3 + 3];\n        y3r = a[j1 + 2] - a[j3 + 2];\n        y3i = a[j1 + 3] - a[j3 + 3];\n        a[j] = x0r + x2r;\n        a[j + 1] = x0i - x2i;\n        a[j + 2] = y0r + y2r;\n        a[j + 3] = y0i - y2i;\n        a[j1] = x0r - x2r;\n        a[j1 + 1] = x0i + x2i;\n        a[j1 + 2] = y0r - y2r;\n        a[j1 + 3] = y0i + y2i;\n        x0r = x1r + x3i;\n        x0i = x1i + x3r;\n        a[j2] = wk1r * x0r - wk1i * x0i;\n        a[j2 + 1] = wk1r * x0i + wk1i * x0r;\n        x0r = y1r + y3i;\n        x0i = y1i + y3r;\n        a[j2 + 2] = wd1r * x0r - wd1i * x0i;\n        a[j2 + 3] = wd1r * x0i + wd1i * x0r;\n        x0r = x1r - x3i;\n        x0i = x1i - x3r;\n        a[j3] = wk3r * x0r + wk3i * x0i;\n        a[j3 + 1] = wk3r * x0i - wk3i * x0r;\n        x0r = y1r - y3i;\n        x0i = y1i - y3r;\n        a[j3 + 2] = wd3r * x0r + wd3i * x0i;\n        a[j3 + 3] = wd3r * x0i - wd3i * x0r;\n        j0 = m - j;\n        j1 = j0 + m;\n        j2 = j1 + m;\n        j3 = j2 + m;\n        x0r = a[j0] + a[j2];\n        x0i = -a[j0 + 1] - a[j2 + 1];\n        x1r = a[j0] - a[j2];\n        x1i = -a[j0 + 1] + a[j2 + 1];\n        y0r = a[j0 - 2] + a[j2 - 2];\n        y0i = -a[j0 - 1] - a[j2 - 1];\n        y1r = a[j0 - 2] - a[j2 - 2];\n        y1i = -a[j0 - 1] + a[j2 - 1];\n        x2r = a[j1] + a[j3];\n        x2i = a[j1 + 1] + a[j3 + 1];\n        x3r = a[j1] - a[j3];\n        x3i = a[j1 + 1] - a[j3 + 1];\n        y2r = a[j1 - 2] + a[j3 - 2];\n        y2i = a[j1 - 1] + a[j3 - 1];\n        y3r = a[j1 - 2] - a[j3 - 2];\n        y3i = a[j1 - 1] - a[j3 - 1];\n        a[j0] = x0r + x2r;\n        a[j0 + 1] = x0i - x2i;\n        a[j0 - 2] = y0r + y2r;\n        a[j0 - 1] = y0i - y2i;\n        a[j1] = x0r - x2r;\n        a[j1 + 1] = x0i + x2i;\n        a[j1 - 2] = y0r - y2r;\n        a[j1 - 1] = y0i + y2i;\n        x0r = x1r + x3i;\n        x0i = x1i + x3r;\n        a[j2] = wk1i * x0r - wk1r * x0i;\n        a[j2 + 1] = wk1i * x0i + wk1r * x0r;\n        x0r = y1r + y3i;\n        x0i = y1i + y3r;\n        a[j2 - 2] = wd1i * x0r - wd1r * x0i;\n        a[j2 - 1] = wd1i * x0i + wd1r * x0r;\n        x0r = x1r - x3i;\n        x0i = x1i - x3r;\n        a[j3] = wk3i * x0r + wk3r * x0i;\n        a[j3 + 1] = wk3i * x0i - wk3r * x0r;\n        x0r = y1r - y3i;\n        x0i = y1i - y3r;\n        a[j3 - 2] = wd3i * x0r + wd3r * x0i;\n        a[j3 - 1] = wd3i * x0i - wd3r * x0r;\n    }\n    wk1r = csc1 * (wd1r + wn4r);\n    wk1i = csc1 * (wd1i + wn4r);\n    wk3r = csc3 * (wd3r - wn4r);\n    wk3i = csc3 * (wd3i - wn4r);\n    j0 = mh;\n    j1 = j0 + m;\n    j2 = j1 + m;\n    j3 = j2 + m;\n    x0r = a[j0 - 2] + a[j2 - 2];\n    x0i = -a[j0 - 1] - a[j2 - 1];\n    x1r = a[j0 - 2] - a[j2 - 2];\n    x1i = -a[j0 - 1] + a[j2 - 1];\n    x2r = a[j1 - 2] + a[j3 - 2];\n    x2i = a[j1 - 1] + a[j3 - 1];\n    x3r = a[j1 - 2] - a[j3 - 2];\n    x3i = a[j1 - 1] - a[j3 - 1];\n    a[j0 - 2] = x0r + x2r;\n    a[j0 - 1] = x0i - x2i;\n    a[j1 - 2] = x0r - x2r;\n    a[j1 - 1] = x0i + x2i;\n    x0r = x1r + x3i;\n    x0i = x1i + x3r;\n    a[j2 - 2] = wk1r * x0r - wk1i * x0i;\n    a[j2 - 1] = wk1r * x0i + wk1i * x0r;\n    x0r = x1r - x3i;\n    x0i = x1i - x3r;\n    a[j3 - 2] = wk3r * x0r + wk3i * x0i;\n    a[j3 - 1] = wk3r * x0i - wk3i * x0r;\n    x0r = a[j0] + a[j2];\n    x0i = -a[j0 + 1] - a[j2 + 1];\n    x1r = a[j0] - a[j2];\n    x1i = -a[j0 + 1] + a[j2 + 1];\n    x2r = a[j1] + a[j3];\n    x2i = a[j1 + 1] + a[j3 + 1];\n    x3r = a[j1] - a[j3];\n    x3i = a[j1 + 1] - a[j3 + 1];\n    a[j0] = x0r + x2r;\n    a[j0 + 1] = x0i - x2i;\n    a[j1] = x0r - x2r;\n    a[j1 + 1] = x0i + x2i;\n    x0r = x1r + x3i;\n    x0i = x1i + x3r;\n    a[j2] = wn4r * (x0r - x0i);\n    a[j2 + 1] = wn4r * (x0i + x0r);\n    x0r = x1r - x3i;\n    x0i = x1i - x3r;\n    a[j3] = -wn4r * (x0r + x0i);\n    a[j3 + 1] = -wn4r * (x0i - x0r);\n    x0r = a[j0 + 2] + a[j2 + 2];\n    x0i = -a[j0 + 3] - a[j2 + 3];\n    x1r = a[j0 + 2] - a[j2 + 2];\n    x1i = -a[j0 + 3] + a[j2 + 3];\n    x2r = a[j1 + 2] + a[j3 + 2];\n    x2i = a[j1 + 3] + a[j3 + 3];\n    x3r = a[j1 + 2] - a[j3 + 2];\n    x3i = a[j1 + 3] - a[j3 + 3];\n    a[j0 + 2] = x0r + x2r;\n    a[j0 + 3] = x0i - x2i;\n    a[j1 + 2] = x0r - x2r;\n    a[j1 + 3] = x0i + x2i;\n    x0r = x1r + x3i;\n    x0i = x1i + x3r;\n    a[j2 + 2] = wk1i * x0r - wk1r * x0i;\n    a[j2 + 3] = wk1i * x0i + wk1r * x0r;\n    x0r = x1r - x3i;\n    x0i = x1i - x3r;\n    a[j3 + 2] = wk3i * x0r + wk3r * x0i;\n    a[j3 + 3] = wk3i * x0i - wk3r * x0r;\n}\n\n\n#ifdef USE_CDFT_THREADS\nstruct cdft_arg_st {\n    int n0;\n    int n;\n    FFTFLT *a;\n    int nw;\n    FFTFLT *w;\n};\ntypedef struct cdft_arg_st cdft_arg_t;\n\n\nvoid cftrec4_th(int n, FFTFLT *a, int nw, FFTFLT *w)\n{\n    void *cftrec1_th(void *p);\n    void *cftrec2_th(void *p);\n    int i, idiv4, m, nthread;\n    cdft_thread_t th[4];\n    cdft_arg_t ag[4];\n\n    nthread = 2;\n    idiv4 = 0;\n    m = n >> 1;\n    if (n > CDFT_4THREADS_BEGIN_N) {\n        nthread = 4;\n        idiv4 = 1;\n        m >>= 1;\n    }\n    for (i = 0; i < nthread; i++) {\n        ag[i].n0 = n;\n        ag[i].n = m;\n        ag[i].a = &a[i * m];\n        ag[i].nw = nw;\n        ag[i].w = w;\n        if (i != idiv4) {\n            cdft_thread_create(&th[i], cftrec1_th, &ag[i]);\n        } else {\n            cdft_thread_create(&th[i], cftrec2_th, &ag[i]);\n        }\n    }\n    for (i = 0; i < nthread; i++) {\n        cdft_thread_wait(th[i]);\n    }\n}\n\n\nvoid *cftrec1_th(void *p)\n{\n    int cfttree(int n, int j, int k, FFTFLT *a, int nw, FFTFLT *w);\n    void cftleaf(int n, int isplt, FFTFLT *a, int nw, FFTFLT *w);\n    void cftmdl1(int n, FFTFLT *a, FFTFLT *w);\n    int isplt, j, k, m, n, n0, nw;\n    FFTFLT *a, *w;\n\n    n0 = ((cdft_arg_t *) p)->n0;\n    n = ((cdft_arg_t *) p)->n;\n    a = ((cdft_arg_t *) p)->a;\n    nw = ((cdft_arg_t *) p)->nw;\n    w = ((cdft_arg_t *) p)->w;\n    m = n0;\n    while (m > 512) {\n        m >>= 2;\n        cftmdl1(m, &a[n - m], &w[nw - (m >> 1)]);\n    }\n    cftleaf(m, 1, &a[n - m], nw, w);\n    k = 0;\n    for (j = n - m; j > 0; j -= m) {\n        k++;\n        isplt = cfttree(m, j, k, a, nw, w);\n        cftleaf(m, isplt, &a[j - m], nw, w);\n    }\n    return (void *) 0;\n}\n\n\nvoid *cftrec2_th(void *p)\n{\n    int cfttree(int n, int j, int k, FFTFLT *a, int nw, FFTFLT *w);\n    void cftleaf(int n, int isplt, FFTFLT *a, int nw, FFTFLT *w);\n    void cftmdl2(int n, FFTFLT *a, FFTFLT *w);\n    int isplt, j, k, m, n, n0, nw;\n    FFTFLT *a, *w;\n\n    n0 = ((cdft_arg_t *) p)->n0;\n    n = ((cdft_arg_t *) p)->n;\n    a = ((cdft_arg_t *) p)->a;\n    nw = ((cdft_arg_t *) p)->nw;\n    w = ((cdft_arg_t *) p)->w;\n    k = 1;\n    m = n0;\n    while (m > 512) {\n        m >>= 2;\n        k <<= 2;\n        cftmdl2(m, &a[n - m], &w[nw - m]);\n    }\n    cftleaf(m, 0, &a[n - m], nw, w);\n    k >>= 1;\n    for (j = n - m; j > 0; j -= m) {\n        k++;\n        isplt = cfttree(m, j, k, a, nw, w);\n        cftleaf(m, isplt, &a[j - m], nw, w);\n    }\n    return (void *) 0;\n}\n#endif /* USE_CDFT_THREADS */\n\n\nvoid cftrec4(int n, FFTFLT *a, int nw, FFTFLT *w)\n{\n    int cfttree(int n, int j, int k, FFTFLT *a, int nw, FFTFLT *w);\n    void cftleaf(int n, int isplt, FFTFLT *a, int nw, FFTFLT *w);\n    void cftmdl1(int n, FFTFLT *a, FFTFLT *w);\n    int isplt, j, k, m;\n\n    m = n;\n    while (m > 512) {\n        m >>= 2;\n        cftmdl1(m, &a[n - m], &w[nw - (m >> 1)]);\n    }\n    cftleaf(m, 1, &a[n - m], nw, w);\n    k = 0;\n    for (j = n - m; j > 0; j -= m) {\n        k++;\n        isplt = cfttree(m, j, k, a, nw, w);\n        cftleaf(m, isplt, &a[j - m], nw, w);\n    }\n}\n\n\nint cfttree(int n, int j, int k, FFTFLT *a, int nw, FFTFLT *w)\n{\n    void cftmdl1(int n, FFTFLT *a, FFTFLT *w);\n    void cftmdl2(int n, FFTFLT *a, FFTFLT *w);\n    int i, isplt, m;\n\n    if ((k & 3) != 0) {\n        isplt = k & 1;\n        if (isplt != 0) {\n            cftmdl1(n, &a[j - n], &w[nw - (n >> 1)]);\n        } else {\n            cftmdl2(n, &a[j - n], &w[nw - n]);\n        }\n    } else {\n        m = n;\n        for (i = k; (i & 3) == 0; i >>= 2) {\n            m <<= 2;\n        }\n        isplt = i & 1;\n        if (isplt != 0) {\n            while (m > 128) {\n                cftmdl1(m, &a[j - m], &w[nw - (m >> 1)]);\n                m >>= 2;\n            }\n        } else {\n            while (m > 128) {\n                cftmdl2(m, &a[j - m], &w[nw - m]);\n                m >>= 2;\n            }\n        }\n    }\n    return isplt;\n}\n\n\nvoid cftleaf(int n, int isplt, FFTFLT *a, int nw, FFTFLT *w)\n{\n    void cftmdl1(int n, FFTFLT *a, FFTFLT *w);\n    void cftmdl2(int n, FFTFLT *a, FFTFLT *w);\n    void cftf161(FFTFLT *a, FFTFLT *w);\n    void cftf162(FFTFLT *a, FFTFLT *w);\n    void cftf081(FFTFLT *a, FFTFLT *w);\n    void cftf082(FFTFLT *a, FFTFLT *w);\n\n    if (n == 512) {\n        cftmdl1(128, a, &w[nw - 64]);\n        cftf161(a, &w[nw - 8]);\n        cftf162(&a[32], &w[nw - 32]);\n        cftf161(&a[64], &w[nw - 8]);\n        cftf161(&a[96], &w[nw - 8]);\n        cftmdl2(128, &a[128], &w[nw - 128]);\n        cftf161(&a[128], &w[nw - 8]);\n        cftf162(&a[160], &w[nw - 32]);\n        cftf161(&a[192], &w[nw - 8]);\n        cftf162(&a[224], &w[nw - 32]);\n        cftmdl1(128, &a[256], &w[nw - 64]);\n        cftf161(&a[256], &w[nw - 8]);\n        cftf162(&a[288], &w[nw - 32]);\n        cftf161(&a[320], &w[nw - 8]);\n        cftf161(&a[352], &w[nw - 8]);\n        if (isplt != 0) {\n            cftmdl1(128, &a[384], &w[nw - 64]);\n            cftf161(&a[480], &w[nw - 8]);\n        } else {\n            cftmdl2(128, &a[384], &w[nw - 128]);\n            cftf162(&a[480], &w[nw - 32]);\n        }\n        cftf161(&a[384], &w[nw - 8]);\n        cftf162(&a[416], &w[nw - 32]);\n        cftf161(&a[448], &w[nw - 8]);\n    } else {\n        cftmdl1(64, a, &w[nw - 32]);\n        cftf081(a, &w[nw - 8]);\n        cftf082(&a[16], &w[nw - 8]);\n        cftf081(&a[32], &w[nw - 8]);\n        cftf081(&a[48], &w[nw - 8]);\n        cftmdl2(64, &a[64], &w[nw - 64]);\n        cftf081(&a[64], &w[nw - 8]);\n        cftf082(&a[80], &w[nw - 8]);\n        cftf081(&a[96], &w[nw - 8]);\n        cftf082(&a[112], &w[nw - 8]);\n        cftmdl1(64, &a[128], &w[nw - 32]);\n        cftf081(&a[128], &w[nw - 8]);\n        cftf082(&a[144], &w[nw - 8]);\n        cftf081(&a[160], &w[nw - 8]);\n        cftf081(&a[176], &w[nw - 8]);\n        if (isplt != 0) {\n            cftmdl1(64, &a[192], &w[nw - 32]);\n            cftf081(&a[240], &w[nw - 8]);\n        } else {\n            cftmdl2(64, &a[192], &w[nw - 64]);\n            cftf082(&a[240], &w[nw - 8]);\n        }\n        cftf081(&a[192], &w[nw - 8]);\n        cftf082(&a[208], &w[nw - 8]);\n        cftf081(&a[224], &w[nw - 8]);\n    }\n}\n\n\nvoid cftmdl1(int n, FFTFLT *a, FFTFLT *w)\n{\n    int j, j0, j1, j2, j3, k, m, mh;\n    FFTFLT wn4r, wk1r, wk1i, wk3r, wk3i;\n    FFTFLT x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;\n\n    mh = n >> 3;\n    m = 2 * mh;\n    j1 = m;\n    j2 = j1 + m;\n    j3 = j2 + m;\n    x0r = a[0] + a[j2];\n    x0i = a[1] + a[j2 + 1];\n    x1r = a[0] - a[j2];\n    x1i = a[1] - a[j2 + 1];\n    x2r = a[j1] + a[j3];\n    x2i = a[j1 + 1] + a[j3 + 1];\n    x3r = a[j1] - a[j3];\n    x3i = a[j1 + 1] - a[j3 + 1];\n    a[0] = x0r + x2r;\n    a[1] = x0i + x2i;\n    a[j1] = x0r - x2r;\n    a[j1 + 1] = x0i - x2i;\n    a[j2] = x1r - x3i;\n    a[j2 + 1] = x1i + x3r;\n    a[j3] = x1r + x3i;\n    a[j3 + 1] = x1i - x3r;\n    wn4r = w[1];\n    k = 0;\n    for (j = 2; j < mh; j += 2) {\n        k += 4;\n        wk1r = w[k];\n        wk1i = w[k + 1];\n        wk3r = w[k + 2];\n        wk3i = w[k + 3];\n        j1 = j + m;\n        j2 = j1 + m;\n        j3 = j2 + m;\n        x0r = a[j] + a[j2];\n        x0i = a[j + 1] + a[j2 + 1];\n        x1r = a[j] - a[j2];\n        x1i = a[j + 1] - a[j2 + 1];\n        x2r = a[j1] + a[j3];\n        x2i = a[j1 + 1] + a[j3 + 1];\n        x3r = a[j1] - a[j3];\n        x3i = a[j1 + 1] - a[j3 + 1];\n        a[j] = x0r + x2r;\n        a[j + 1] = x0i + x2i;\n        a[j1] = x0r - x2r;\n        a[j1 + 1] = x0i - x2i;\n        x0r = x1r - x3i;\n        x0i = x1i + x3r;\n        a[j2] = wk1r * x0r - wk1i * x0i;\n        a[j2 + 1] = wk1r * x0i + wk1i * x0r;\n        x0r = x1r + x3i;\n        x0i = x1i - x3r;\n        a[j3] = wk3r * x0r + wk3i * x0i;\n        a[j3 + 1] = wk3r * x0i - wk3i * x0r;\n        j0 = m - j;\n        j1 = j0 + m;\n        j2 = j1 + m;\n        j3 = j2 + m;\n        x0r = a[j0] + a[j2];\n        x0i = a[j0 + 1] + a[j2 + 1];\n        x1r = a[j0] - a[j2];\n        x1i = a[j0 + 1] - a[j2 + 1];\n        x2r = a[j1] + a[j3];\n        x2i = a[j1 + 1] + a[j3 + 1];\n        x3r = a[j1] - a[j3];\n        x3i = a[j1 + 1] - a[j3 + 1];\n        a[j0] = x0r + x2r;\n        a[j0 + 1] = x0i + x2i;\n        a[j1] = x0r - x2r;\n        a[j1 + 1] = x0i - x2i;\n        x0r = x1r - x3i;\n        x0i = x1i + x3r;\n        a[j2] = wk1i * x0r - wk1r * x0i;\n        a[j2 + 1] = wk1i * x0i + wk1r * x0r;\n        x0r = x1r + x3i;\n        x0i = x1i - x3r;\n        a[j3] = wk3i * x0r + wk3r * x0i;\n        a[j3 + 1] = wk3i * x0i - wk3r * x0r;\n    }\n    j0 = mh;\n    j1 = j0 + m;\n    j2 = j1 + m;\n    j3 = j2 + m;\n    x0r = a[j0] + a[j2];\n    x0i = a[j0 + 1] + a[j2 + 1];\n    x1r = a[j0] - a[j2];\n    x1i = a[j0 + 1] - a[j2 + 1];\n    x2r = a[j1] + a[j3];\n    x2i = a[j1 + 1] + a[j3 + 1];\n    x3r = a[j1] - a[j3];\n    x3i = a[j1 + 1] - a[j3 + 1];\n    a[j0] = x0r + x2r;\n    a[j0 + 1] = x0i + x2i;\n    a[j1] = x0r - x2r;\n    a[j1 + 1] = x0i - x2i;\n    x0r = x1r - x3i;\n    x0i = x1i + x3r;\n    a[j2] = wn4r * (x0r - x0i);\n    a[j2 + 1] = wn4r * (x0i + x0r);\n    x0r = x1r + x3i;\n    x0i = x1i - x3r;\n    a[j3] = -wn4r * (x0r + x0i);\n    a[j3 + 1] = -wn4r * (x0i - x0r);\n}\n\n\nvoid cftmdl2(int n, FFTFLT *a, FFTFLT *w)\n{\n    int j, j0, j1, j2, j3, k, kr, m, mh;\n    FFTFLT wn4r, wk1r, wk1i, wk3r, wk3i, wd1r, wd1i, wd3r, wd3i;\n    FFTFLT x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, y0r, y0i, y2r, y2i;\n\n    mh = n >> 3;\n    m = 2 * mh;\n    wn4r = w[1];\n    j1 = m;\n    j2 = j1 + m;\n    j3 = j2 + m;\n    x0r = a[0] - a[j2 + 1];\n    x0i = a[1] + a[j2];\n    x1r = a[0] + a[j2 + 1];\n    x1i = a[1] - a[j2];\n    x2r = a[j1] - a[j3 + 1];\n    x2i = a[j1 + 1] + a[j3];\n    x3r = a[j1] + a[j3 + 1];\n    x3i = a[j1 + 1] - a[j3];\n    y0r = wn4r * (x2r - x2i);\n    y0i = wn4r * (x2i + x2r);\n    a[0] = x0r + y0r;\n    a[1] = x0i + y0i;\n    a[j1] = x0r - y0r;\n    a[j1 + 1] = x0i - y0i;\n    y0r = wn4r * (x3r - x3i);\n    y0i = wn4r * (x3i + x3r);\n    a[j2] = x1r - y0i;\n    a[j2 + 1] = x1i + y0r;\n    a[j3] = x1r + y0i;\n    a[j3 + 1] = x1i - y0r;\n    k = 0;\n    kr = 2 * m;\n    for (j = 2; j < mh; j += 2) {\n        k += 4;\n        wk1r = w[k];\n        wk1i = w[k + 1];\n        wk3r = w[k + 2];\n        wk3i = w[k + 3];\n        kr -= 4;\n        wd1i = w[kr];\n        wd1r = w[kr + 1];\n        wd3i = w[kr + 2];\n        wd3r = w[kr + 3];\n        j1 = j + m;\n        j2 = j1 + m;\n        j3 = j2 + m;\n        x0r = a[j] - a[j2 + 1];\n        x0i = a[j + 1] + a[j2];\n        x1r = a[j] + a[j2 + 1];\n        x1i = a[j + 1] - a[j2];\n        x2r = a[j1] - a[j3 + 1];\n        x2i = a[j1 + 1] + a[j3];\n        x3r = a[j1] + a[j3 + 1];\n        x3i = a[j1 + 1] - a[j3];\n        y0r = wk1r * x0r - wk1i * x0i;\n        y0i = wk1r * x0i + wk1i * x0r;\n        y2r = wd1r * x2r - wd1i * x2i;\n        y2i = wd1r * x2i + wd1i * x2r;\n        a[j] = y0r + y2r;\n        a[j + 1] = y0i + y2i;\n        a[j1] = y0r - y2r;\n        a[j1 + 1] = y0i - y2i;\n        y0r = wk3r * x1r + wk3i * x1i;\n        y0i = wk3r * x1i - wk3i * x1r;\n        y2r = wd3r * x3r + wd3i * x3i;\n        y2i = wd3r * x3i - wd3i * x3r;\n        a[j2] = y0r + y2r;\n        a[j2 + 1] = y0i + y2i;\n        a[j3] = y0r - y2r;\n        a[j3 + 1] = y0i - y2i;\n        j0 = m - j;\n        j1 = j0 + m;\n        j2 = j1 + m;\n        j3 = j2 + m;\n        x0r = a[j0] - a[j2 + 1];\n        x0i = a[j0 + 1] + a[j2];\n        x1r = a[j0] + a[j2 + 1];\n        x1i = a[j0 + 1] - a[j2];\n        x2r = a[j1] - a[j3 + 1];\n        x2i = a[j1 + 1] + a[j3];\n        x3r = a[j1] + a[j3 + 1];\n        x3i = a[j1 + 1] - a[j3];\n        y0r = wd1i * x0r - wd1r * x0i;\n        y0i = wd1i * x0i + wd1r * x0r;\n        y2r = wk1i * x2r - wk1r * x2i;\n        y2i = wk1i * x2i + wk1r * x2r;\n        a[j0] = y0r + y2r;\n        a[j0 + 1] = y0i + y2i;\n        a[j1] = y0r - y2r;\n        a[j1 + 1] = y0i - y2i;\n        y0r = wd3i * x1r + wd3r * x1i;\n        y0i = wd3i * x1i - wd3r * x1r;\n        y2r = wk3i * x3r + wk3r * x3i;\n        y2i = wk3i * x3i - wk3r * x3r;\n        a[j2] = y0r + y2r;\n        a[j2 + 1] = y0i + y2i;\n        a[j3] = y0r - y2r;\n        a[j3 + 1] = y0i - y2i;\n    }\n    wk1r = w[m];\n    wk1i = w[m + 1];\n    j0 = mh;\n    j1 = j0 + m;\n    j2 = j1 + m;\n    j3 = j2 + m;\n    x0r = a[j0] - a[j2 + 1];\n    x0i = a[j0 + 1] + a[j2];\n    x1r = a[j0] + a[j2 + 1];\n    x1i = a[j0 + 1] - a[j2];\n    x2r = a[j1] - a[j3 + 1];\n    x2i = a[j1 + 1] + a[j3];\n    x3r = a[j1] + a[j3 + 1];\n    x3i = a[j1 + 1] - a[j3];\n    y0r = wk1r * x0r - wk1i * x0i;\n    y0i = wk1r * x0i + wk1i * x0r;\n    y2r = wk1i * x2r - wk1r * x2i;\n    y2i = wk1i * x2i + wk1r * x2r;\n    a[j0] = y0r + y2r;\n    a[j0 + 1] = y0i + y2i;\n    a[j1] = y0r - y2r;\n    a[j1 + 1] = y0i - y2i;\n    y0r = wk1i * x1r - wk1r * x1i;\n    y0i = wk1i * x1i + wk1r * x1r;\n    y2r = wk1r * x3r - wk1i * x3i;\n    y2i = wk1r * x3i + wk1i * x3r;\n    a[j2] = y0r - y2r;\n    a[j2 + 1] = y0i - y2i;\n    a[j3] = y0r + y2r;\n    a[j3 + 1] = y0i + y2i;\n}\n\n\nvoid cftfx41(int n, FFTFLT *a, int nw, FFTFLT *w)\n{\n    void cftf161(FFTFLT *a, FFTFLT *w);\n    void cftf162(FFTFLT *a, FFTFLT *w);\n    void cftf081(FFTFLT *a, FFTFLT *w);\n    void cftf082(FFTFLT *a, FFTFLT *w);\n\n    if (n == 128) {\n        cftf161(a, &w[nw - 8]);\n        cftf162(&a[32], &w[nw - 32]);\n        cftf161(&a[64], &w[nw - 8]);\n        cftf161(&a[96], &w[nw - 8]);\n    } else {\n        cftf081(a, &w[nw - 8]);\n        cftf082(&a[16], &w[nw - 8]);\n        cftf081(&a[32], &w[nw - 8]);\n        cftf081(&a[48], &w[nw - 8]);\n    }\n}\n\n\nvoid cftf161(FFTFLT *a, FFTFLT *w)\n{\n    FFTFLT wn4r, wk1r, wk1i,\n        x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i,\n        y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i,\n        y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i,\n        y8r, y8i, y9r, y9i, y10r, y10i, y11r, y11i,\n        y12r, y12i, y13r, y13i, y14r, y14i, y15r, y15i;\n\n    wn4r = w[1];\n    wk1r = w[2];\n    wk1i = w[3];\n    x0r = a[0] + a[16];\n    x0i = a[1] + a[17];\n    x1r = a[0] - a[16];\n    x1i = a[1] - a[17];\n    x2r = a[8] + a[24];\n    x2i = a[9] + a[25];\n    x3r = a[8] - a[24];\n    x3i = a[9] - a[25];\n    y0r = x0r + x2r;\n    y0i = x0i + x2i;\n    y4r = x0r - x2r;\n    y4i = x0i - x2i;\n    y8r = x1r - x3i;\n    y8i = x1i + x3r;\n    y12r = x1r + x3i;\n    y12i = x1i - x3r;\n    x0r = a[2] + a[18];\n    x0i = a[3] + a[19];\n    x1r = a[2] - a[18];\n    x1i = a[3] - a[19];\n    x2r = a[10] + a[26];\n    x2i = a[11] + a[27];\n    x3r = a[10] - a[26];\n    x3i = a[11] - a[27];\n    y1r = x0r + x2r;\n    y1i = x0i + x2i;\n    y5r = x0r - x2r;\n    y5i = x0i - x2i;\n    x0r = x1r - x3i;\n    x0i = x1i + x3r;\n    y9r = wk1r * x0r - wk1i * x0i;\n    y9i = wk1r * x0i + wk1i * x0r;\n    x0r = x1r + x3i;\n    x0i = x1i - x3r;\n    y13r = wk1i * x0r - wk1r * x0i;\n    y13i = wk1i * x0i + wk1r * x0r;\n    x0r = a[4] + a[20];\n    x0i = a[5] + a[21];\n    x1r = a[4] - a[20];\n    x1i = a[5] - a[21];\n    x2r = a[12] + a[28];\n    x2i = a[13] + a[29];\n    x3r = a[12] - a[28];\n    x3i = a[13] - a[29];\n    y2r = x0r + x2r;\n    y2i = x0i + x2i;\n    y6r = x0r - x2r;\n    y6i = x0i - x2i;\n    x0r = x1r - x3i;\n    x0i = x1i + x3r;\n    y10r = wn4r * (x0r - x0i);\n    y10i = wn4r * (x0i + x0r);\n    x0r = x1r + x3i;\n    x0i = x1i - x3r;\n    y14r = wn4r * (x0r + x0i);\n    y14i = wn4r * (x0i - x0r);\n    x0r = a[6] + a[22];\n    x0i = a[7] + a[23];\n    x1r = a[6] - a[22];\n    x1i = a[7] - a[23];\n    x2r = a[14] + a[30];\n    x2i = a[15] + a[31];\n    x3r = a[14] - a[30];\n    x3i = a[15] - a[31];\n    y3r = x0r + x2r;\n    y3i = x0i + x2i;\n    y7r = x0r - x2r;\n    y7i = x0i - x2i;\n    x0r = x1r - x3i;\n    x0i = x1i + x3r;\n    y11r = wk1i * x0r - wk1r * x0i;\n    y11i = wk1i * x0i + wk1r * x0r;\n    x0r = x1r + x3i;\n    x0i = x1i - x3r;\n    y15r = wk1r * x0r - wk1i * x0i;\n    y15i = wk1r * x0i + wk1i * x0r;\n    x0r = y12r - y14r;\n    x0i = y12i - y14i;\n    x1r = y12r + y14r;\n    x1i = y12i + y14i;\n    x2r = y13r - y15r;\n    x2i = y13i - y15i;\n    x3r = y13r + y15r;\n    x3i = y13i + y15i;\n    a[24] = x0r + x2r;\n    a[25] = x0i + x2i;\n    a[26] = x0r - x2r;\n    a[27] = x0i - x2i;\n    a[28] = x1r - x3i;\n    a[29] = x1i + x3r;\n    a[30] = x1r + x3i;\n    a[31] = x1i - x3r;\n    x0r = y8r + y10r;\n    x0i = y8i + y10i;\n    x1r = y8r - y10r;\n    x1i = y8i - y10i;\n    x2r = y9r + y11r;\n    x2i = y9i + y11i;\n    x3r = y9r - y11r;\n    x3i = y9i - y11i;\n    a[16] = x0r + x2r;\n    a[17] = x0i + x2i;\n    a[18] = x0r - x2r;\n    a[19] = x0i - x2i;\n    a[20] = x1r - x3i;\n    a[21] = x1i + x3r;\n    a[22] = x1r + x3i;\n    a[23] = x1i - x3r;\n    x0r = y5r - y7i;\n    x0i = y5i + y7r;\n    x2r = wn4r * (x0r - x0i);\n    x2i = wn4r * (x0i + x0r);\n    x0r = y5r + y7i;\n    x0i = y5i - y7r;\n    x3r = wn4r * (x0r - x0i);\n    x3i = wn4r * (x0i + x0r);\n    x0r = y4r - y6i;\n    x0i = y4i + y6r;\n    x1r = y4r + y6i;\n    x1i = y4i - y6r;\n    a[8] = x0r + x2r;\n    a[9] = x0i + x2i;\n    a[10] = x0r - x2r;\n    a[11] = x0i - x2i;\n    a[12] = x1r - x3i;\n    a[13] = x1i + x3r;\n    a[14] = x1r + x3i;\n    a[15] = x1i - x3r;\n    x0r = y0r + y2r;\n    x0i = y0i + y2i;\n    x1r = y0r - y2r;\n    x1i = y0i - y2i;\n    x2r = y1r + y3r;\n    x2i = y1i + y3i;\n    x3r = y1r - y3r;\n    x3i = y1i - y3i;\n    a[0] = x0r + x2r;\n    a[1] = x0i + x2i;\n    a[2] = x0r - x2r;\n    a[3] = x0i - x2i;\n    a[4] = x1r - x3i;\n    a[5] = x1i + x3r;\n    a[6] = x1r + x3i;\n    a[7] = x1i - x3r;\n}\n\n\nvoid cftf162(FFTFLT *a, FFTFLT *w)\n{\n    FFTFLT wn4r, wk1r, wk1i, wk2r, wk2i, wk3r, wk3i,\n        x0r, x0i, x1r, x1i, x2r, x2i,\n        y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i,\n        y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i,\n        y8r, y8i, y9r, y9i, y10r, y10i, y11r, y11i,\n        y12r, y12i, y13r, y13i, y14r, y14i, y15r, y15i;\n\n    wn4r = w[1];\n    wk1r = w[4];\n    wk1i = w[5];\n    wk3r = w[6];\n    wk3i = -w[7];\n    wk2r = w[8];\n    wk2i = w[9];\n    x1r = a[0] - a[17];\n    x1i = a[1] + a[16];\n    x0r = a[8] - a[25];\n    x0i = a[9] + a[24];\n    x2r = wn4r * (x0r - x0i);\n    x2i = wn4r * (x0i + x0r);\n    y0r = x1r + x2r;\n    y0i = x1i + x2i;\n    y4r = x1r - x2r;\n    y4i = x1i - x2i;\n    x1r = a[0] + a[17];\n    x1i = a[1] - a[16];\n    x0r = a[8] + a[25];\n    x0i = a[9] - a[24];\n    x2r = wn4r * (x0r - x0i);\n    x2i = wn4r * (x0i + x0r);\n    y8r = x1r - x2i;\n    y8i = x1i + x2r;\n    y12r = x1r + x2i;\n    y12i = x1i - x2r;\n    x0r = a[2] - a[19];\n    x0i = a[3] + a[18];\n    x1r = wk1r * x0r - wk1i * x0i;\n    x1i = wk1r * x0i + wk1i * x0r;\n    x0r = a[10] - a[27];\n    x0i = a[11] + a[26];\n    x2r = wk3i * x0r - wk3r * x0i;\n    x2i = wk3i * x0i + wk3r * x0r;\n    y1r = x1r + x2r;\n    y1i = x1i + x2i;\n    y5r = x1r - x2r;\n    y5i = x1i - x2i;\n    x0r = a[2] + a[19];\n    x0i = a[3] - a[18];\n    x1r = wk3r * x0r - wk3i * x0i;\n    x1i = wk3r * x0i + wk3i * x0r;\n    x0r = a[10] + a[27];\n    x0i = a[11] - a[26];\n    x2r = wk1r * x0r + wk1i * x0i;\n    x2i = wk1r * x0i - wk1i * x0r;\n    y9r = x1r - x2r;\n    y9i = x1i - x2i;\n    y13r = x1r + x2r;\n    y13i = x1i + x2i;\n    x0r = a[4] - a[21];\n    x0i = a[5] + a[20];\n    x1r = wk2r * x0r - wk2i * x0i;\n    x1i = wk2r * x0i + wk2i * x0r;\n    x0r = a[12] - a[29];\n    x0i = a[13] + a[28];\n    x2r = wk2i * x0r - wk2r * x0i;\n    x2i = wk2i * x0i + wk2r * x0r;\n    y2r = x1r + x2r;\n    y2i = x1i + x2i;\n    y6r = x1r - x2r;\n    y6i = x1i - x2i;\n    x0r = a[4] + a[21];\n    x0i = a[5] - a[20];\n    x1r = wk2i * x0r - wk2r * x0i;\n    x1i = wk2i * x0i + wk2r * x0r;\n    x0r = a[12] + a[29];\n    x0i = a[13] - a[28];\n    x2r = wk2r * x0r - wk2i * x0i;\n    x2i = wk2r * x0i + wk2i * x0r;\n    y10r = x1r - x2r;\n    y10i = x1i - x2i;\n    y14r = x1r + x2r;\n    y14i = x1i + x2i;\n    x0r = a[6] - a[23];\n    x0i = a[7] + a[22];\n    x1r = wk3r * x0r - wk3i * x0i;\n    x1i = wk3r * x0i + wk3i * x0r;\n    x0r = a[14] - a[31];\n    x0i = a[15] + a[30];\n    x2r = wk1i * x0r - wk1r * x0i;\n    x2i = wk1i * x0i + wk1r * x0r;\n    y3r = x1r + x2r;\n    y3i = x1i + x2i;\n    y7r = x1r - x2r;\n    y7i = x1i - x2i;\n    x0r = a[6] + a[23];\n    x0i = a[7] - a[22];\n    x1r = wk1i * x0r + wk1r * x0i;\n    x1i = wk1i * x0i - wk1r * x0r;\n    x0r = a[14] + a[31];\n    x0i = a[15] - a[30];\n    x2r = wk3i * x0r - wk3r * x0i;\n    x2i = wk3i * x0i + wk3r * x0r;\n    y11r = x1r + x2r;\n    y11i = x1i + x2i;\n    y15r = x1r - x2r;\n    y15i = x1i - x2i;\n    x1r = y0r + y2r;\n    x1i = y0i + y2i;\n    x2r = y1r + y3r;\n    x2i = y1i + y3i;\n    a[0] = x1r + x2r;\n    a[1] = x1i + x2i;\n    a[2] = x1r - x2r;\n    a[3] = x1i - x2i;\n    x1r = y0r - y2r;\n    x1i = y0i - y2i;\n    x2r = y1r - y3r;\n    x2i = y1i - y3i;\n    a[4] = x1r - x2i;\n    a[5] = x1i + x2r;\n    a[6] = x1r + x2i;\n    a[7] = x1i - x2r;\n    x1r = y4r - y6i;\n    x1i = y4i + y6r;\n    x0r = y5r - y7i;\n    x0i = y5i + y7r;\n    x2r = wn4r * (x0r - x0i);\n    x2i = wn4r * (x0i + x0r);\n    a[8] = x1r + x2r;\n    a[9] = x1i + x2i;\n    a[10] = x1r - x2r;\n    a[11] = x1i - x2i;\n    x1r = y4r + y6i;\n    x1i = y4i - y6r;\n    x0r = y5r + y7i;\n    x0i = y5i - y7r;\n    x2r = wn4r * (x0r - x0i);\n    x2i = wn4r * (x0i + x0r);\n    a[12] = x1r - x2i;\n    a[13] = x1i + x2r;\n    a[14] = x1r + x2i;\n    a[15] = x1i - x2r;\n    x1r = y8r + y10r;\n    x1i = y8i + y10i;\n    x2r = y9r - y11r;\n    x2i = y9i - y11i;\n    a[16] = x1r + x2r;\n    a[17] = x1i + x2i;\n    a[18] = x1r - x2r;\n    a[19] = x1i - x2i;\n    x1r = y8r - y10r;\n    x1i = y8i - y10i;\n    x2r = y9r + y11r;\n    x2i = y9i + y11i;\n    a[20] = x1r - x2i;\n    a[21] = x1i + x2r;\n    a[22] = x1r + x2i;\n    a[23] = x1i - x2r;\n    x1r = y12r - y14i;\n    x1i = y12i + y14r;\n    x0r = y13r + y15i;\n    x0i = y13i - y15r;\n    x2r = wn4r * (x0r - x0i);\n    x2i = wn4r * (x0i + x0r);\n    a[24] = x1r + x2r;\n    a[25] = x1i + x2i;\n    a[26] = x1r - x2r;\n    a[27] = x1i - x2i;\n    x1r = y12r + y14i;\n    x1i = y12i - y14r;\n    x0r = y13r - y15i;\n    x0i = y13i + y15r;\n    x2r = wn4r * (x0r - x0i);\n    x2i = wn4r * (x0i + x0r);\n    a[28] = x1r - x2i;\n    a[29] = x1i + x2r;\n    a[30] = x1r + x2i;\n    a[31] = x1i - x2r;\n}\n\n\nvoid cftf081(FFTFLT *a, FFTFLT *w)\n{\n    FFTFLT wn4r, x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i,\n        y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i,\n        y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i;\n\n    wn4r = w[1];\n    x0r = a[0] + a[8];\n    x0i = a[1] + a[9];\n    x1r = a[0] - a[8];\n    x1i = a[1] - a[9];\n    x2r = a[4] + a[12];\n    x2i = a[5] + a[13];\n    x3r = a[4] - a[12];\n    x3i = a[5] - a[13];\n    y0r = x0r + x2r;\n    y0i = x0i + x2i;\n    y2r = x0r - x2r;\n    y2i = x0i - x2i;\n    y1r = x1r - x3i;\n    y1i = x1i + x3r;\n    y3r = x1r + x3i;\n    y3i = x1i - x3r;\n    x0r = a[2] + a[10];\n    x0i = a[3] + a[11];\n    x1r = a[2] - a[10];\n    x1i = a[3] - a[11];\n    x2r = a[6] + a[14];\n    x2i = a[7] + a[15];\n    x3r = a[6] - a[14];\n    x3i = a[7] - a[15];\n    y4r = x0r + x2r;\n    y4i = x0i + x2i;\n    y6r = x0r - x2r;\n    y6i = x0i - x2i;\n    x0r = x1r - x3i;\n    x0i = x1i + x3r;\n    x2r = x1r + x3i;\n    x2i = x1i - x3r;\n    y5r = wn4r * (x0r - x0i);\n    y5i = wn4r * (x0r + x0i);\n    y7r = wn4r * (x2r - x2i);\n    y7i = wn4r * (x2r + x2i);\n    a[8] = y1r + y5r;\n    a[9] = y1i + y5i;\n    a[10] = y1r - y5r;\n    a[11] = y1i - y5i;\n    a[12] = y3r - y7i;\n    a[13] = y3i + y7r;\n    a[14] = y3r + y7i;\n    a[15] = y3i - y7r;\n    a[0] = y0r + y4r;\n    a[1] = y0i + y4i;\n    a[2] = y0r - y4r;\n    a[3] = y0i - y4i;\n    a[4] = y2r - y6i;\n    a[5] = y2i + y6r;\n    a[6] = y2r + y6i;\n    a[7] = y2i - y6r;\n}\n\n\nvoid cftf082(FFTFLT *a, FFTFLT *w)\n{\n    FFTFLT wn4r, wk1r, wk1i, x0r, x0i, x1r, x1i,\n        y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i,\n        y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i;\n\n    wn4r = w[1];\n    wk1r = w[2];\n    wk1i = w[3];\n    y0r = a[0] - a[9];\n    y0i = a[1] + a[8];\n    y1r = a[0] + a[9];\n    y1i = a[1] - a[8];\n    x0r = a[4] - a[13];\n    x0i = a[5] + a[12];\n    y2r = wn4r * (x0r - x0i);\n    y2i = wn4r * (x0i + x0r);\n    x0r = a[4] + a[13];\n    x0i = a[5] - a[12];\n    y3r = wn4r * (x0r - x0i);\n    y3i = wn4r * (x0i + x0r);\n    x0r = a[2] - a[11];\n    x0i = a[3] + a[10];\n    y4r = wk1r * x0r - wk1i * x0i;\n    y4i = wk1r * x0i + wk1i * x0r;\n    x0r = a[2] + a[11];\n    x0i = a[3] - a[10];\n    y5r = wk1i * x0r - wk1r * x0i;\n    y5i = wk1i * x0i + wk1r * x0r;\n    x0r = a[6] - a[15];\n    x0i = a[7] + a[14];\n    y6r = wk1i * x0r - wk1r * x0i;\n    y6i = wk1i * x0i + wk1r * x0r;\n    x0r = a[6] + a[15];\n    x0i = a[7] - a[14];\n    y7r = wk1r * x0r - wk1i * x0i;\n    y7i = wk1r * x0i + wk1i * x0r;\n    x0r = y0r + y2r;\n    x0i = y0i + y2i;\n    x1r = y4r + y6r;\n    x1i = y4i + y6i;\n    a[0] = x0r + x1r;\n    a[1] = x0i + x1i;\n    a[2] = x0r - x1r;\n    a[3] = x0i - x1i;\n    x0r = y0r - y2r;\n    x0i = y0i - y2i;\n    x1r = y4r - y6r;\n    x1i = y4i - y6i;\n    a[4] = x0r - x1i;\n    a[5] = x0i + x1r;\n    a[6] = x0r + x1i;\n    a[7] = x0i - x1r;\n    x0r = y1r - y3i;\n    x0i = y1i + y3r;\n    x1r = y5r - y7r;\n    x1i = y5i - y7i;\n    a[8] = x0r + x1r;\n    a[9] = x0i + x1i;\n    a[10] = x0r - x1r;\n    a[11] = x0i - x1i;\n    x0r = y1r + y3i;\n    x0i = y1i - y3r;\n    x1r = y5r + y7r;\n    x1i = y5i + y7i;\n    a[12] = x0r - x1i;\n    a[13] = x0i + x1r;\n    a[14] = x0r + x1i;\n    a[15] = x0i - x1r;\n}\n\n\nvoid cftf040(FFTFLT *a)\n{\n    FFTFLT x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;\n\n    x0r = a[0] + a[4];\n    x0i = a[1] + a[5];\n    x1r = a[0] - a[4];\n    x1i = a[1] - a[5];\n    x2r = a[2] + a[6];\n    x2i = a[3] + a[7];\n    x3r = a[2] - a[6];\n    x3i = a[3] - a[7];\n    a[0] = x0r + x2r;\n    a[1] = x0i + x2i;\n    a[2] = x1r - x3i;\n    a[3] = x1i + x3r;\n    a[4] = x0r - x2r;\n    a[5] = x0i - x2i;\n    a[6] = x1r + x3i;\n    a[7] = x1i - x3r;\n}\n\n\nvoid cftb040(FFTFLT *a)\n{\n    FFTFLT x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;\n\n    x0r = a[0] + a[4];\n    x0i = a[1] + a[5];\n    x1r = a[0] - a[4];\n    x1i = a[1] - a[5];\n    x2r = a[2] + a[6];\n    x2i = a[3] + a[7];\n    x3r = a[2] - a[6];\n    x3i = a[3] - a[7];\n    a[0] = x0r + x2r;\n    a[1] = x0i + x2i;\n    a[2] = x1r + x3i;\n    a[3] = x1i - x3r;\n    a[4] = x0r - x2r;\n    a[5] = x0i - x2i;\n    a[6] = x1r - x3i;\n    a[7] = x1i + x3r;\n}\n\n\nvoid cftx020(FFTFLT *a)\n{\n    FFTFLT x0r, x0i;\n\n    x0r = a[0] - a[2];\n    x0i = a[1] - a[3];\n    a[0] += a[2];\n    a[1] += a[3];\n    a[2] = x0r;\n    a[3] = x0i;\n}\n\n\nvoid rftfsub(int n, FFTFLT *a, int nc, FFTFLT *c)\n{\n    int j, k, kk, ks, m;\n    FFTFLT wkr, wki, xr, xi, yr, yi;\n\n    m = n >> 1;\n    ks = 2 * nc / m;\n    kk = 0;\n    for (j = 2; j < m; j += 2) {\n        k = n - j;\n        kk += ks;\n        wkr = 0.5 - c[nc - kk];\n        wki = c[kk];\n        xr = a[j] - a[k];\n        xi = a[j + 1] + a[k + 1];\n        yr = wkr * xr - wki * xi;\n        yi = wkr * xi + wki * xr;\n        a[j] -= yr;\n        a[j + 1] -= yi;\n        a[k] += yr;\n        a[k + 1] -= yi;\n    }\n}\n\n\nvoid rftbsub(int n, FFTFLT *a, int nc, FFTFLT *c)\n{\n    int j, k, kk, ks, m;\n    FFTFLT wkr, wki, xr, xi, yr, yi;\n\n    m = n >> 1;\n    ks = 2 * nc / m;\n    kk = 0;\n    for (j = 2; j < m; j += 2) {\n        k = n - j;\n        kk += ks;\n        wkr = 0.5 - c[nc - kk];\n        wki = c[kk];\n        xr = a[j] - a[k];\n        xi = a[j + 1] + a[k + 1];\n        yr = wkr * xr + wki * xi;\n        yi = wkr * xi - wki * xr;\n        a[j] -= yr;\n        a[j + 1] -= yi;\n        a[k] += yr;\n        a[k + 1] -= yi;\n    }\n}\n\n\nvoid dctsub(int n, FFTFLT *a, int nc, FFTFLT *c)\n{\n    int j, k, kk, ks, m;\n    FFTFLT wkr, wki, xr;\n\n    m = n >> 1;\n    ks = nc / n;\n    kk = 0;\n    for (j = 1; j < m; j++) {\n        k = n - j;\n        kk += ks;\n        wkr = c[kk] - c[nc - kk];\n        wki = c[kk] + c[nc - kk];\n        xr = wki * a[j] - wkr * a[k];\n        a[j] = wkr * a[j] + wki * a[k];\n        a[k] = xr;\n    }\n    a[m] *= c[0];\n}\n\n\nvoid dstsub(int n, FFTFLT *a, int nc, FFTFLT *c)\n{\n    int j, k, kk, ks, m;\n    FFTFLT wkr, wki, xr;\n\n    m = n >> 1;\n    ks = nc / n;\n    kk = 0;\n    for (j = 1; j < m; j++) {\n        k = n - j;\n        kk += ks;\n        wkr = c[kk] - c[nc - kk];\n        wki = c[kk] + c[nc - kk];\n        xr = wki * a[k] - wkr * a[j];\n        a[k] = wkr * a[k] + wki * a[j];\n        a[j] = xr;\n    }\n    a[m] *= c[0];\n}\n\n"
  },
  {
    "path": "libs/libpd/pure-data/src/d_filter.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/*  \"filters\", both linear and nonlinear.\n*/\n#include \"m_pd.h\"\n\n/* ---------------- hip~ - 1-pole 1-zero hipass filter. ----------------- */\n\ntypedef struct hipctl\n{\n    t_sample c_x;\n    t_sample c_coef;\n} t_hipctl;\n\ntypedef struct sighip\n{\n    t_object x_obj;\n    t_float x_sr;\n    t_float x_hz;\n    t_hipctl x_cspace;\n    t_float x_f;\n} t_sighip;\n\nt_class *sighip_class;\nstatic void sighip_ft1(t_sighip *x, t_floatarg f);\n\nstatic void *sighip_new(t_floatarg f)\n{\n    t_sighip *x = (t_sighip *)pd_new(sighip_class);\n    inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym(\"float\"), gensym(\"ft1\"));\n    outlet_new(&x->x_obj, &s_signal);\n    x->x_sr = 44100;\n    x->x_cspace.c_x = 0;\n    sighip_ft1(x, f);\n    x->x_f = 0;\n    return (x);\n}\n\nstatic void sighip_ft1(t_sighip *x, t_floatarg f)\n{\n    if (f < 0) f = 0;\n    x->x_hz = f;\n    x->x_cspace.c_coef = 1 - f * (2 * 3.14159) / x->x_sr;\n    if (x->x_cspace.c_coef < 0)\n        x->x_cspace.c_coef = 0;\n    else if (x->x_cspace.c_coef > 1)\n        x->x_cspace.c_coef = 1;\n}\n\nstatic t_int *sighip_perform(t_int *w)\n{\n    t_sample *in = (t_sample *)(w[1]);\n    t_sample *out = (t_sample *)(w[2]);\n    t_hipctl *c = (t_hipctl *)(w[3]);\n    int n = (int)w[4];\n    int i;\n    t_sample last = c->c_x;\n    t_sample coef = c->c_coef;\n    if (coef < 1)\n    {\n        t_sample normal = 0.5*(1+coef);\n        for (i = 0; i < n; i++)\n        {\n            t_sample new = *in++ + coef * last;\n            *out++ = normal * (new - last);\n            last = new;\n        }\n        if (PD_BIGORSMALL(last))\n            last = 0;\n        c->c_x = last;\n    }\n    else\n    {\n        for (i = 0; i < n; i++)\n            *out++ = *in++;\n        c->c_x = 0;\n    }\n    return (w+5);\n}\n\nstatic t_int *sighip_perform_old(t_int *w)\n{\n    t_sample *in = (t_sample *)(w[1]);\n    t_sample *out = (t_sample *)(w[2]);\n    t_hipctl *c = (t_hipctl *)(w[3]);\n    int n = (int)w[4];\n    int i;\n    t_sample last = c->c_x;\n    t_sample coef = c->c_coef;\n    if (coef < 1)\n    {\n        for (i = 0; i < n; i++)\n        {\n            t_sample new = *in++ + coef * last;\n            *out++ = new - last;\n            last = new;\n        }\n        if (PD_BIGORSMALL(last))\n            last = 0;\n        c->c_x = last;\n    }\n    else\n    {\n        for (i = 0; i < n; i++)\n            *out++ = *in++;\n        c->c_x = 0;\n    }\n    return (w+5);\n}\n\nstatic void sighip_dsp(t_sighip *x, t_signal **sp)\n{\n    x->x_sr = sp[0]->s_sr;\n    sighip_ft1(x,  x->x_hz);\n    dsp_add((pd_compatibilitylevel > 43 ?\n        sighip_perform : sighip_perform_old),\n            4, sp[0]->s_vec, sp[1]->s_vec, &x->x_cspace, (t_int)sp[0]->s_n);\n}\n\nstatic void sighip_clear(t_sighip *x, t_floatarg q)\n{\n    x->x_cspace.c_x = 0;\n}\n\nvoid sighip_setup(void)\n{\n    sighip_class = class_new(gensym(\"hip~\"), (t_newmethod)sighip_new, 0,\n        sizeof(t_sighip), 0, A_DEFFLOAT, 0);\n    CLASS_MAINSIGNALIN(sighip_class, t_sighip, x_f);\n    class_addmethod(sighip_class, (t_method)sighip_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_addmethod(sighip_class, (t_method)sighip_ft1,\n        gensym(\"ft1\"), A_FLOAT, 0);\n    class_addmethod(sighip_class, (t_method)sighip_clear, gensym(\"clear\"), 0);\n}\n\n/* ---------------- lop~ - 1-pole lopass filter. ----------------- */\n\ntypedef struct siglop\n{\n    t_object x_obj;\n    t_float x_conversion;   /* frequency-to-coefficient conversion factor */\n    t_sample x_last;        /* last output */\n    t_float x_hz;           /* rolloff frequency in hz  (for scalar inlet) */\n    t_sample x_coef;        /* filter coefficient (for scalar inlet) */\n    t_float x_f;            /* value of first inlet if unconnected */\n} t_siglop;\n\nt_class *siglop_class;\n\nstatic void siglop_ft1(t_siglop *x, t_floatarg f);\n\nstatic void *siglop_new(t_floatarg f)\n{\n    t_siglop *x = (t_siglop *)pd_new(siglop_class);\n    signalinlet_new(&x->x_obj, f);\n    outlet_new(&x->x_obj, &s_signal);\n    x->x_conversion =  x->x_last = x->x_hz = x->x_coef = 0;\n    x->x_f = 0;\n    return (x);\n}\n\nstatic void siglop_clear(t_siglop *x, t_floatarg q)\n{\n    x->x_last = 0;\n}\n\nstatic t_int *siglop_perf_scalar(t_int *w)\n{\n    t_siglop *x = (t_siglop *)(w[1]);\n    t_sample *in1 = (t_sample *)(w[2]);\n    t_sample newhz = *(t_sample *)(w[3]);\n    t_sample *out = (t_sample *)(w[4]);\n    int i, n = (int)w[5];\n    t_sample last = x->x_last, coef, feedback;\n    if (newhz != x->x_hz)\n    {\n        x->x_hz = newhz;\n        coef = newhz * x->x_conversion;\n        if (coef > 1)\n            coef = 1;\n        else if (coef < 0)\n            coef = 0;\n        x->x_coef = coef;\n    }\n    else coef = x->x_coef;\n    feedback = 1.f - coef;\n    for (i = 0; i < n; i++)\n        last = *out++ = coef * *in1++ + feedback * last;\n    if (PD_BIGORSMALL(last))\n        last = 0;\n    x->x_last = last;\n    return (w+6);\n}\n\nstatic t_int *siglop_perf_vector(t_int *w)\n{\n    t_siglop *x = (t_siglop *)(w[1]);\n    t_sample *in1 = (t_sample *)(w[2]);\n    t_sample *in2 = (t_sample *)(w[3]);\n    t_sample *out = (t_sample *)(w[4]);\n    int i, n = (int)w[5];\n    t_sample last = x->x_last, coef;\n\n    for (i = 0; i < n; i++)\n    {\n        coef = *in2++ * x->x_conversion;\n        if (coef > 1)\n            coef = 1;\n        else if (coef < 0)\n            coef = 0;\n        last = *out++ = coef * *in1++ + (1.f - coef) * last;\n        /* this formulation seems to run slower, at least on Intel hardware:\n            *out++ = (last += coef * (*in1++ - last)); */\n    }\n    if (PD_BIGORSMALL(last))\n        last = 0;\n    x->x_last = last;\n    return (w+6);\n}\n\nstatic void siglop_dsp(t_siglop *x, t_signal **sp)\n{\n    x->x_conversion = (2*3.14159)/sp[0]->s_sr;\n    x->x_hz = x->x_coef = 0;    /* this will be updated at perf time */\n    dsp_add((sp[1]->s_n > 1 ? siglop_perf_vector : siglop_perf_scalar), 5,\n        x, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, (t_int)sp[0]->s_n);\n}\n\nvoid siglop_setup(void)\n{\n    siglop_class = class_new(gensym(\"lop~\"), (t_newmethod)siglop_new, 0,\n        sizeof(t_siglop), CLASS_NOPROMOTESIG, A_DEFFLOAT, 0);\n    CLASS_MAINSIGNALIN(siglop_class, t_siglop, x_f);\n    class_addmethod(siglop_class, (t_method)siglop_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_addmethod(siglop_class, (t_method)siglop_clear, gensym(\"clear\"), 0);\n}\n\n/* ---------------- bp~ - 2-pole bandpass filter. ----------------- */\n\ntypedef struct bpctl\n{\n    t_sample c_x1;\n    t_sample c_x2;\n    t_sample c_coef1;\n    t_sample c_coef2;\n    t_sample c_gain;\n} t_bpctl;\n\ntypedef struct sigbp\n{\n    t_object x_obj;\n    t_float x_sr;\n    t_float x_freq;\n    t_float x_q;\n    t_bpctl x_cspace;\n    t_float x_f;\n} t_sigbp;\n\nt_class *sigbp_class;\n\nstatic void sigbp_docoef(t_sigbp *x, t_floatarg f, t_floatarg q);\n\nstatic void *sigbp_new(t_floatarg f, t_floatarg q)\n{\n    t_sigbp *x = (t_sigbp *)pd_new(sigbp_class);\n    inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym(\"float\"), gensym(\"ft1\"));\n    inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym(\"float\"), gensym(\"ft2\"));\n    outlet_new(&x->x_obj, &s_signal);\n    x->x_sr = 44100;\n    x->x_cspace.c_x1 = 0;\n    x->x_cspace.c_x2 = 0;\n    sigbp_docoef(x, f, q);\n    x->x_f = 0;\n    return (x);\n}\n\nstatic t_float sigbp_qcos(t_float f)\n{\n    if (f >= -(0.5f*3.14159f) && f <= 0.5f*3.14159f)\n    {\n        t_float g = f*f;\n        return (((g*g*g * (-1.0f/720.0f) + g*g*(1.0f/24.0f)) - g*0.5) + 1);\n    }\n    else return (0);\n}\n\nstatic void sigbp_docoef(t_sigbp *x, t_floatarg f, t_floatarg q)\n{\n    t_float r, oneminusr, omega;\n    if (f < 0.001) f = 10;\n    if (q < 0) q = 0;\n    x->x_freq = f;\n    x->x_q = q;\n    omega = f * (2.0f * 3.14159f) / x->x_sr;\n    if (q < 0.001) oneminusr = 1.0f;\n    else oneminusr = omega/q;\n    if (oneminusr > 1.0f) oneminusr = 1.0f;\n    r = 1.0f - oneminusr;\n    x->x_cspace.c_coef1 = 2.0f * sigbp_qcos(omega) * r;\n    x->x_cspace.c_coef2 = - r * r;\n    x->x_cspace.c_gain = 2 * oneminusr * (oneminusr + r * omega);\n    /* post(\"r %f, omega %f, coef1 %f, coef2 %f\",\n        r, omega, x->x_cspace.c_coef1, x->x_cspace.c_coef2); */\n}\n\nstatic void sigbp_ft1(t_sigbp *x, t_floatarg f)\n{\n    sigbp_docoef(x, f, x->x_q);\n}\n\nstatic void sigbp_ft2(t_sigbp *x, t_floatarg q)\n{\n    sigbp_docoef(x, x->x_freq, q);\n}\n\nstatic void sigbp_clear(t_sigbp *x, t_floatarg q)\n{\n    x->x_cspace.c_x1 = x->x_cspace.c_x2 = 0;\n}\n\nstatic t_int *sigbp_perform(t_int *w)\n{\n    t_sample *in = (t_sample *)(w[1]);\n    t_sample *out = (t_sample *)(w[2]);\n    t_bpctl *c = (t_bpctl *)(w[3]);\n    int n = (int)w[4];\n    int i;\n    t_sample last = c->c_x1;\n    t_sample prev = c->c_x2;\n    t_sample coef1 = c->c_coef1;\n    t_sample coef2 = c->c_coef2;\n    t_sample gain = c->c_gain;\n    for (i = 0; i < n; i++)\n    {\n        t_sample output =  *in++ + coef1 * last + coef2 * prev;\n        *out++ = gain * output;\n        prev = last;\n        last = output;\n    }\n    if (PD_BIGORSMALL(last))\n        last = 0;\n    if (PD_BIGORSMALL(prev))\n        prev = 0;\n    c->c_x1 = last;\n    c->c_x2 = prev;\n    return (w+5);\n}\n\nstatic void sigbp_dsp(t_sigbp *x, t_signal **sp)\n{\n    x->x_sr = sp[0]->s_sr;\n    sigbp_docoef(x, x->x_freq, x->x_q);\n    dsp_add(sigbp_perform, 4,\n        sp[0]->s_vec, sp[1]->s_vec,\n            &x->x_cspace, (t_int)sp[0]->s_n);\n}\n\nvoid sigbp_setup(void)\n{\n    sigbp_class = class_new(gensym(\"bp~\"), (t_newmethod)sigbp_new, 0,\n        sizeof(t_sigbp), 0, A_DEFFLOAT, A_DEFFLOAT, 0);\n    CLASS_MAINSIGNALIN(sigbp_class, t_sigbp, x_f);\n    class_addmethod(sigbp_class, (t_method)sigbp_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_addmethod(sigbp_class, (t_method)sigbp_ft1,\n        gensym(\"ft1\"), A_FLOAT, 0);\n    class_addmethod(sigbp_class, (t_method)sigbp_ft2,\n        gensym(\"ft2\"), A_FLOAT, 0);\n    class_addmethod(sigbp_class, (t_method)sigbp_clear, gensym(\"clear\"), 0);\n}\n\n/* ---------------- biquad~ - raw biquad filter ----------------- */\n\ntypedef struct biquadctl\n{\n    t_sample c_x1;\n    t_sample c_x2;\n    t_sample c_fb1;\n    t_sample c_fb2;\n    t_sample c_ff1;\n    t_sample c_ff2;\n    t_sample c_ff3;\n} t_biquadctl;\n\ntypedef struct sigbiquad\n{\n    t_object x_obj;\n    t_float x_f;\n    t_biquadctl x_cspace;\n} t_sigbiquad;\n\nt_class *sigbiquad_class;\n\nstatic void sigbiquad_list(t_sigbiquad *x, t_symbol *s, int argc, t_atom *argv);\n\nstatic void *sigbiquad_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_sigbiquad *x = (t_sigbiquad *)pd_new(sigbiquad_class);\n    outlet_new(&x->x_obj, &s_signal);\n    x->x_cspace.c_x1 = x->x_cspace.c_x2 = 0;\n    sigbiquad_list(x, s, argc, argv);\n    x->x_f = 0;\n    return (x);\n}\n\nstatic t_int *sigbiquad_perform(t_int *w)\n{\n    t_sample *in = (t_sample *)(w[1]);\n    t_sample *out = (t_sample *)(w[2]);\n    t_biquadctl *c = (t_biquadctl *)(w[3]);\n    int n = (int)w[4];\n    int i;\n    t_sample last = c->c_x1;\n    t_sample prev = c->c_x2;\n    t_sample fb1 = c->c_fb1;\n    t_sample fb2 = c->c_fb2;\n    t_sample ff1 = c->c_ff1;\n    t_sample ff2 = c->c_ff2;\n    t_sample ff3 = c->c_ff3;\n    for (i = 0; i < n; i++)\n    {\n        t_sample output =  *in++ + fb1 * last + fb2 * prev;\n        if (PD_BIGORSMALL(output))\n            output = 0;\n        *out++ = ff1 * output + ff2 * last + ff3 * prev;\n        prev = last;\n        last = output;\n    }\n    c->c_x1 = last;\n    c->c_x2 = prev;\n    return (w+5);\n}\n\nstatic void sigbiquad_list(t_sigbiquad *x, t_symbol *s, int argc, t_atom *argv)\n{\n    t_float fb1 = atom_getfloatarg(0, argc, argv);\n    t_float fb2 = atom_getfloatarg(1, argc, argv);\n    t_float ff1 = atom_getfloatarg(2, argc, argv);\n    t_float ff2 = atom_getfloatarg(3, argc, argv);\n    t_float ff3 = atom_getfloatarg(4, argc, argv);\n    t_float discriminant = fb1 * fb1 + 4 * fb2;\n    t_biquadctl *c = &x->x_cspace;\n    if (discriminant < 0) /* imaginary roots -- resonant filter */\n    {\n            /* they're conjugates so we just check that the product\n            is less than one */\n        if (fb2 >= -1.0f) goto stable;\n    }\n    else    /* real roots */\n    {\n            /* check that the parabola 1 - fb1 x - fb2 x^2 has a\n                vertex between -1 and 1, and that it's nonnegative\n                at both ends, which implies both roots are in [1-,1]. */\n        if (fb1 <= 2.0f && fb1 >= -2.0f &&\n            1.0f - fb1 -fb2 >= 0 && 1.0f + fb1 - fb2 >= 0)\n                goto stable;\n    }\n        /* if unstable, just bash to zero */\n    fb1 = fb2 = ff1 = ff2 = ff3 = 0;\nstable:\n    c->c_fb1 = fb1;\n    c->c_fb2 = fb2;\n    c->c_ff1 = ff1;\n    c->c_ff2 = ff2;\n    c->c_ff3 = ff3;\n}\n\nstatic void sigbiquad_set(t_sigbiquad *x, t_symbol *s, int argc, t_atom *argv)\n{\n    t_biquadctl *c = &x->x_cspace;\n    c->c_x1 = atom_getfloatarg(0, argc, argv);\n    c->c_x2 = atom_getfloatarg(1, argc, argv);\n}\n\nstatic void sigbiquad_dsp(t_sigbiquad *x, t_signal **sp)\n{\n    dsp_add(sigbiquad_perform, 4,\n        sp[0]->s_vec, sp[1]->s_vec,\n            &x->x_cspace, (t_int)sp[0]->s_n);\n}\n\nvoid sigbiquad_setup(void)\n{\n    sigbiquad_class = class_new(gensym(\"biquad~\"), (t_newmethod)sigbiquad_new,\n        0, sizeof(t_sigbiquad), 0, A_GIMME, 0);\n    CLASS_MAINSIGNALIN(sigbiquad_class, t_sigbiquad, x_f);\n    class_addmethod(sigbiquad_class, (t_method)sigbiquad_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_addlist(sigbiquad_class, sigbiquad_list);\n    class_addmethod(sigbiquad_class, (t_method)sigbiquad_set, gensym(\"set\"),\n        A_GIMME, 0);\n    class_addmethod(sigbiquad_class, (t_method)sigbiquad_set, gensym(\"clear\"),\n        A_GIMME, 0);\n}\n\n/* ---------------- samphold~ - sample and hold  ----------------- */\n\ntypedef struct sigsamphold\n{\n    t_object x_obj;\n    t_float x_f;\n    t_sample x_lastin;\n    t_sample x_lastout;\n} t_sigsamphold;\n\nt_class *sigsamphold_class;\n\nstatic void *sigsamphold_new(void)\n{\n    t_sigsamphold *x = (t_sigsamphold *)pd_new(sigsamphold_class);\n    inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);\n    outlet_new(&x->x_obj, &s_signal);\n    x->x_lastin = 0;\n    x->x_lastout = 0;\n    x->x_f = 0;\n    return (x);\n}\n\nstatic t_int *sigsamphold_perform(t_int *w)\n{\n    t_sample *in1 = (t_sample *)(w[1]);\n    t_sample *in2 = (t_sample *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    t_sigsamphold *x = (t_sigsamphold *)(w[4]);\n    int n = (int)w[5];\n    int i;\n    t_sample lastin = x->x_lastin;\n    t_sample lastout = x->x_lastout;\n    for (i = 0; i < n; i++, in1++)\n    {\n        t_sample next = *in2++;\n        if (next < lastin) lastout = *in1;\n        *out++ = lastout;\n        lastin = next;\n    }\n    x->x_lastin = lastin;\n    x->x_lastout = lastout;\n    return (w+6);\n}\n\nstatic void sigsamphold_dsp(t_sigsamphold *x, t_signal **sp)\n{\n    dsp_add(sigsamphold_perform, 5,\n        sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec,\n            x, (t_int)sp[0]->s_n);\n}\n\nstatic void sigsamphold_reset(t_sigsamphold *x, t_symbol *s, int argc,\n    t_atom *argv)\n{\n    x->x_lastin = ((argc > 0 && (argv[0].a_type == A_FLOAT)) ?\n        argv[0].a_w.w_float : 1e20);\n}\n\nstatic void sigsamphold_set(t_sigsamphold *x, t_float f)\n{\n    x->x_lastout = f;\n}\n\nvoid sigsamphold_setup(void)\n{\n    sigsamphold_class = class_new(gensym(\"samphold~\"),\n        (t_newmethod)sigsamphold_new, 0, sizeof(t_sigsamphold), 0, 0);\n    CLASS_MAINSIGNALIN(sigsamphold_class, t_sigsamphold, x_f);\n    class_addmethod(sigsamphold_class, (t_method)sigsamphold_set,\n        gensym(\"set\"), A_DEFFLOAT, 0);\n    class_addmethod(sigsamphold_class, (t_method)sigsamphold_reset,\n        gensym(\"reset\"), A_GIMME, 0);\n    class_addmethod(sigsamphold_class, (t_method)sigsamphold_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n}\n\n/* ---------------- rpole~ - real one-pole filter (raw) ----------------- */\n\ntypedef struct sigrpole\n{\n    t_object x_obj;\n    t_float x_f;\n    t_sample x_last;\n} t_sigrpole;\n\nt_class *sigrpole_class;\n\nstatic void *sigrpole_new(t_float f)\n{\n    t_sigrpole *x = (t_sigrpole *)pd_new(sigrpole_class);\n    pd_float(\n        (t_pd *)inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal),\n            f);\n    outlet_new(&x->x_obj, &s_signal);\n    x->x_last = 0;\n    return (x);\n}\n\nstatic t_int *sigrpole_perform(t_int *w)\n{\n    t_sample *in1 = (t_sample *)(w[1]);\n    t_sample *in2 = (t_sample *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    t_sigrpole *x = (t_sigrpole *)(w[4]);\n    int n = (int)w[5];\n    int i;\n    t_sample last = x->x_last;\n    for (i = 0; i < n; i++)\n    {\n        t_sample next = *in1++;\n        t_sample coef = *in2++;\n        *out++ = last = coef * last + next;\n    }\n    if (PD_BIGORSMALL(last))\n        last = 0;\n    x->x_last = last;\n    return (w+6);\n}\n\nstatic void sigrpole_dsp(t_sigrpole *x, t_signal **sp)\n{\n    dsp_add(sigrpole_perform, 5,\n        sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec,\n            x, (t_int)sp[0]->s_n);\n}\n\nstatic void sigrpole_clear(t_sigrpole *x)\n{\n    x->x_last = 0;\n}\n\nstatic void sigrpole_set(t_sigrpole *x, t_float f)\n{\n    x->x_last = f;\n}\n\nvoid sigrpole_setup(void)\n{\n    sigrpole_class = class_new(gensym(\"rpole~\"),\n        (t_newmethod)sigrpole_new, 0, sizeof(t_sigrpole), 0, A_DEFFLOAT, 0);\n    CLASS_MAINSIGNALIN(sigrpole_class, t_sigrpole, x_f);\n    class_addmethod(sigrpole_class, (t_method)sigrpole_set,\n        gensym(\"set\"), A_DEFFLOAT, 0);\n    class_addmethod(sigrpole_class, (t_method)sigrpole_clear,\n        gensym(\"clear\"), 0);\n    class_addmethod(sigrpole_class, (t_method)sigrpole_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n}\n\n/* ---------------- rzero~ - real one-zero filter (raw) ----------------- */\n\ntypedef struct sigrzero\n{\n    t_object x_obj;\n    t_float x_f;\n    t_sample x_last;\n} t_sigrzero;\n\nt_class *sigrzero_class;\n\nstatic void *sigrzero_new(t_float f)\n{\n    t_sigrzero *x = (t_sigrzero *)pd_new(sigrzero_class);\n    pd_float(\n        (t_pd *)inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal),\n            f);\n    outlet_new(&x->x_obj, &s_signal);\n    x->x_last = 0;\n    return (x);\n}\n\nstatic t_int *sigrzero_perform(t_int *w)\n{\n    t_sample *in1 = (t_sample *)(w[1]);\n    t_sample *in2 = (t_sample *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    t_sigrzero *x = (t_sigrzero *)(w[4]);\n    int n = (int)w[5];\n    int i;\n    t_sample last = x->x_last;\n    for (i = 0; i < n; i++)\n    {\n        t_sample next = *in1++;\n        t_sample coef = *in2++;\n        *out++ = next - coef * last;\n        last = next;\n    }\n    x->x_last = last;\n    return (w+6);\n}\n\nstatic void sigrzero_dsp(t_sigrzero *x, t_signal **sp)\n{\n    dsp_add(sigrzero_perform, 5,\n        sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec,\n            x, (t_int)sp[0]->s_n);\n}\n\nstatic void sigrzero_clear(t_sigrzero *x)\n{\n    x->x_last = 0;\n}\n\nstatic void sigrzero_set(t_sigrzero *x, t_float f)\n{\n    x->x_last = f;\n}\n\nvoid sigrzero_setup(void)\n{\n    sigrzero_class = class_new(gensym(\"rzero~\"),\n        (t_newmethod)sigrzero_new, 0, sizeof(t_sigrzero), 0, A_DEFFLOAT, 0);\n    CLASS_MAINSIGNALIN(sigrzero_class, t_sigrzero, x_f);\n    class_addmethod(sigrzero_class, (t_method)sigrzero_set,\n        gensym(\"set\"), A_DEFFLOAT, 0);\n    class_addmethod(sigrzero_class, (t_method)sigrzero_clear,\n        gensym(\"clear\"), 0);\n    class_addmethod(sigrzero_class, (t_method)sigrzero_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n}\n\n/* ---------- rzero_rev~ - real, reverse one-zero filter (raw) ------------ */\n\ntypedef struct sigrzero_rev\n{\n    t_object x_obj;\n    t_float x_f;\n    t_sample x_last;\n} t_sigrzero_rev;\n\nt_class *sigrzero_rev_class;\n\nstatic void *sigrzero_rev_new(t_float f)\n{\n    t_sigrzero_rev *x = (t_sigrzero_rev *)pd_new(sigrzero_rev_class);\n    pd_float(\n        (t_pd *)inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal),\n            f);\n    outlet_new(&x->x_obj, &s_signal);\n    x->x_last = 0;\n    return (x);\n}\n\nstatic t_int *sigrzero_rev_perform(t_int *w)\n{\n    t_sample *in1 = (t_sample *)(w[1]);\n    t_sample *in2 = (t_sample *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    t_sigrzero_rev *x = (t_sigrzero_rev *)(w[4]);\n    int n = (int)w[5];\n    int i;\n    t_sample last = x->x_last;\n    for (i = 0; i < n; i++)\n    {\n        t_sample next = *in1++;\n        t_sample coef = *in2++;\n        *out++ = last - coef * next;\n        last = next;\n    }\n    x->x_last = last;\n    return (w+6);\n}\n\nstatic void sigrzero_rev_dsp(t_sigrzero_rev *x, t_signal **sp)\n{\n    dsp_add(sigrzero_rev_perform, 5,\n        sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec,\n            x, (t_int)sp[0]->s_n);\n}\n\nstatic void sigrzero_rev_clear(t_sigrzero_rev *x)\n{\n    x->x_last = 0;\n}\n\nstatic void sigrzero_rev_set(t_sigrzero_rev *x, t_float f)\n{\n    x->x_last = f;\n}\n\nvoid sigrzero_rev_setup(void)\n{\n    sigrzero_rev_class = class_new(gensym(\"rzero_rev~\"),\n        (t_newmethod)sigrzero_rev_new, 0, sizeof(t_sigrzero_rev),\n        0, A_DEFFLOAT, 0);\n    CLASS_MAINSIGNALIN(sigrzero_rev_class, t_sigrzero_rev, x_f);\n    class_addmethod(sigrzero_rev_class, (t_method)sigrzero_rev_set,\n        gensym(\"set\"), A_DEFFLOAT, 0);\n    class_addmethod(sigrzero_rev_class, (t_method)sigrzero_rev_clear,\n        gensym(\"clear\"), 0);\n    class_addmethod(sigrzero_rev_class, (t_method)sigrzero_rev_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n}\n\n/* -------------- cpole~ - complex one-pole filter (raw) --------------- */\n\ntypedef struct sigcpole\n{\n    t_object x_obj;\n    t_float x_f;\n    t_sample x_lastre;\n    t_sample x_lastim;\n} t_sigcpole;\n\nt_class *sigcpole_class;\n\nstatic void *sigcpole_new(t_float re, t_float im)\n{\n    t_sigcpole *x = (t_sigcpole *)pd_new(sigcpole_class);\n    inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);\n    pd_float(\n        (t_pd *)inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal),\n            re);\n    pd_float(\n        (t_pd *)inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal),\n            im);\n    outlet_new(&x->x_obj, &s_signal);\n    outlet_new(&x->x_obj, &s_signal);\n    x->x_lastre = x->x_lastim = 0;\n    x->x_f = 0;\n    return (x);\n}\n\nstatic t_int *sigcpole_perform(t_int *w)\n{\n    t_sample *inre1 = (t_sample *)(w[1]);\n    t_sample *inim1 = (t_sample *)(w[2]);\n    t_sample *inre2 = (t_sample *)(w[3]);\n    t_sample *inim2 = (t_sample *)(w[4]);\n    t_sample *outre = (t_sample *)(w[5]);\n    t_sample *outim = (t_sample *)(w[6]);\n    t_sigcpole *x = (t_sigcpole *)(w[7]);\n    int n = (int)w[8];\n    int i;\n    t_sample lastre = x->x_lastre;\n    t_sample lastim = x->x_lastim;\n    for (i = 0; i < n; i++)\n    {\n        t_sample nextre = *inre1++;\n        t_sample nextim = *inim1++;\n        t_sample coefre = *inre2++;\n        t_sample coefim = *inim2++;\n        t_sample tempre = *outre++ = nextre + lastre * coefre - lastim * coefim;\n        lastim = *outim++ = nextim + lastre * coefim + lastim * coefre;\n        lastre = tempre;\n    }\n    if (PD_BIGORSMALL(lastre))\n        lastre = 0;\n    if (PD_BIGORSMALL(lastim))\n        lastim = 0;\n    x->x_lastre = lastre;\n    x->x_lastim = lastim;\n    return (w+9);\n}\n\nstatic void sigcpole_dsp(t_sigcpole *x, t_signal **sp)\n{\n    dsp_add(sigcpole_perform, 8,\n        sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec,\n        sp[4]->s_vec, sp[5]->s_vec, x, (t_int)sp[0]->s_n);\n}\n\nstatic void sigcpole_clear(t_sigcpole *x)\n{\n    x->x_lastre = x->x_lastim = 0;\n}\n\nstatic void sigcpole_set(t_sigcpole *x, t_float re, t_float im)\n{\n    x->x_lastre = re;\n    x->x_lastim = im;\n}\n\nvoid sigcpole_setup(void)\n{\n    sigcpole_class = class_new(gensym(\"cpole~\"),\n        (t_newmethod)sigcpole_new, 0, sizeof(t_sigcpole), 0,\n            A_DEFFLOAT, A_DEFFLOAT, 0);\n    CLASS_MAINSIGNALIN(sigcpole_class, t_sigcpole, x_f);\n    class_addmethod(sigcpole_class, (t_method)sigcpole_set,\n        gensym(\"set\"), A_DEFFLOAT, A_DEFFLOAT, 0);\n    class_addmethod(sigcpole_class, (t_method)sigcpole_clear,\n        gensym(\"clear\"), 0);\n    class_addmethod(sigcpole_class, (t_method)sigcpole_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n}\n\n/* -------------- czero~ - complex one-zero filter (raw) --------------- */\n\ntypedef struct sigczero\n{\n    t_object x_obj;\n    t_float x_f;\n    t_sample x_lastre;\n    t_sample x_lastim;\n} t_sigczero;\n\nt_class *sigczero_class;\n\nstatic void *sigczero_new(t_float re, t_float im)\n{\n    t_sigczero *x = (t_sigczero *)pd_new(sigczero_class);\n    inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);\n    pd_float(\n        (t_pd *)inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal),\n            re);\n    pd_float(\n        (t_pd *)inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal),\n            im);\n    outlet_new(&x->x_obj, &s_signal);\n    outlet_new(&x->x_obj, &s_signal);\n    x->x_lastre = x->x_lastim = 0;\n    x->x_f = 0;\n    return (x);\n}\n\nstatic t_int *sigczero_perform(t_int *w)\n{\n    t_sample *inre1 = (t_sample *)(w[1]);\n    t_sample *inim1 = (t_sample *)(w[2]);\n    t_sample *inre2 = (t_sample *)(w[3]);\n    t_sample *inim2 = (t_sample *)(w[4]);\n    t_sample *outre = (t_sample *)(w[5]);\n    t_sample *outim = (t_sample *)(w[6]);\n    t_sigczero *x = (t_sigczero *)(w[7]);\n    int n = (int)w[8];\n    int i;\n    t_sample lastre = x->x_lastre;\n    t_sample lastim = x->x_lastim;\n    for (i = 0; i < n; i++)\n    {\n        t_sample nextre = *inre1++;\n        t_sample nextim = *inim1++;\n        t_sample coefre = *inre2++;\n        t_sample coefim = *inim2++;\n        *outre++ = nextre - lastre * coefre + lastim * coefim;\n        *outim++ = nextim - lastre * coefim - lastim * coefre;\n        lastre = nextre;\n        lastim = nextim;\n    }\n    x->x_lastre = lastre;\n    x->x_lastim = lastim;\n    return (w+9);\n}\n\nstatic void sigczero_dsp(t_sigczero *x, t_signal **sp)\n{\n    dsp_add(sigczero_perform, 8,\n        sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec,\n        sp[4]->s_vec, sp[5]->s_vec, x, (t_int)sp[0]->s_n);\n}\n\nstatic void sigczero_clear(t_sigczero *x)\n{\n    x->x_lastre = x->x_lastim = 0;\n}\n\nstatic void sigczero_set(t_sigczero *x, t_float re, t_float im)\n{\n    x->x_lastre = re;\n    x->x_lastim = im;\n}\n\nvoid sigczero_setup(void)\n{\n    sigczero_class = class_new(gensym(\"czero~\"),\n        (t_newmethod)sigczero_new, 0, sizeof(t_sigczero), 0,\n            A_DEFFLOAT, A_DEFFLOAT, 0);\n    CLASS_MAINSIGNALIN(sigczero_class, t_sigczero, x_f);\n    class_addmethod(sigczero_class, (t_method)sigczero_set,\n        gensym(\"set\"), A_DEFFLOAT, A_DEFFLOAT, 0);\n    class_addmethod(sigczero_class, (t_method)sigczero_clear,\n        gensym(\"clear\"), 0);\n    class_addmethod(sigczero_class, (t_method)sigczero_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n}\n\n/* ------ czero_rev~ - complex one-zero filter (raw, reverse form) ----- */\n\ntypedef struct sigczero_rev\n{\n    t_object x_obj;\n    t_float x_f;\n    t_sample x_lastre;\n    t_sample x_lastim;\n} t_sigczero_rev;\n\nt_class *sigczero_rev_class;\n\nstatic void *sigczero_rev_new(t_float re, t_float im)\n{\n    t_sigczero_rev *x = (t_sigczero_rev *)pd_new(sigczero_rev_class);\n    inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);\n    pd_float(\n        (t_pd *)inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal),\n            re);\n    pd_float(\n        (t_pd *)inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal),\n            im);\n    outlet_new(&x->x_obj, &s_signal);\n    outlet_new(&x->x_obj, &s_signal);\n    x->x_lastre = x->x_lastim = 0;\n    x->x_f = 0;\n    return (x);\n}\n\nstatic t_int *sigczero_rev_perform(t_int *w)\n{\n    t_sample *inre1 = (t_sample *)(w[1]);\n    t_sample *inim1 = (t_sample *)(w[2]);\n    t_sample *inre2 = (t_sample *)(w[3]);\n    t_sample *inim2 = (t_sample *)(w[4]);\n    t_sample *outre = (t_sample *)(w[5]);\n    t_sample *outim = (t_sample *)(w[6]);\n    t_sigczero_rev *x = (t_sigczero_rev *)(w[7]);\n    int n = (int)w[8];\n    int i;\n    t_sample lastre = x->x_lastre;\n    t_sample lastim = x->x_lastim;\n    for (i = 0; i < n; i++)\n    {\n        t_sample nextre = *inre1++;\n        t_sample nextim = *inim1++;\n        t_sample coefre = *inre2++;\n        t_sample coefim = *inim2++;\n            /* transfer function is (A bar) - Z^-1, for the same\n            frequency response as 1 - AZ^-1 from czero_tilde. */\n        *outre++ = lastre - nextre * coefre - nextim * coefim;\n        *outim++ = lastim - nextre * coefim + nextim * coefre;\n        lastre = nextre;\n        lastim = nextim;\n    }\n    x->x_lastre = lastre;\n    x->x_lastim = lastim;\n    return (w+9);\n}\n\nstatic void sigczero_rev_dsp(t_sigczero_rev *x, t_signal **sp)\n{\n    dsp_add(sigczero_rev_perform, 8,\n        sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec,\n        sp[4]->s_vec, sp[5]->s_vec, x, (t_int)sp[0]->s_n);\n}\n\nstatic void sigczero_rev_clear(t_sigczero_rev *x)\n{\n    x->x_lastre = x->x_lastim = 0;\n}\n\nstatic void sigczero_rev_set(t_sigczero_rev *x, t_float re, t_float im)\n{\n    x->x_lastre = re;\n    x->x_lastim = im;\n}\n\nvoid sigczero_rev_setup(void)\n{\n    sigczero_rev_class = class_new(gensym(\"czero_rev~\"),\n        (t_newmethod)sigczero_rev_new, 0, sizeof(t_sigczero_rev), 0,\n            A_DEFFLOAT, A_DEFFLOAT, 0);\n    CLASS_MAINSIGNALIN(sigczero_rev_class, t_sigczero_rev, x_f);\n    class_addmethod(sigczero_rev_class, (t_method)sigczero_rev_set,\n        gensym(\"set\"), A_DEFFLOAT, A_DEFFLOAT, 0);\n    class_addmethod(sigczero_rev_class, (t_method)sigczero_rev_clear,\n        gensym(\"clear\"), 0);\n    class_addmethod(sigczero_rev_class, (t_method)sigczero_rev_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n}\n\n/* ---------------- slop~ - slewing low-pass filter ----------------- */\n\ntypedef struct slop_tilde\n{\n    t_object x_obj;\n    t_sample x_f;\n    t_sample x_coef;\n    t_sample x_last;\n    t_sample x_sigin;\n    t_sample x_freqin;\n    t_sample x_poslimitin;\n    t_sample x_posfreqin;\n    t_sample x_neglimitin;\n    t_sample x_negfreqin;\n} t_slop_tilde;\n\nt_class *slop_tilde_class;\n\nstatic void *slop_tilde_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_slop_tilde *x = (t_slop_tilde *)pd_new(slop_tilde_class);\n    signalinlet_new(&x->x_obj, atom_getfloatarg(0, argc, argv));\n    signalinlet_new(&x->x_obj, atom_getfloatarg(1, argc, argv));\n    signalinlet_new(&x->x_obj, atom_getfloatarg(2, argc, argv));\n    signalinlet_new(&x->x_obj, atom_getfloatarg(3, argc, argv));\n    signalinlet_new(&x->x_obj, atom_getfloatarg(4, argc, argv));\n    outlet_new(&x->x_obj, &s_signal);\n    x->x_coef = 0;\n    return (x);\n}\n\nstatic void slop_tilde_set(t_slop_tilde *x, t_floatarg q)\n{\n    x->x_last = q;\n}\n\nstatic t_int *slop_tilde_perform(t_int *w)\n{\n    t_slop_tilde *x = (t_slop_tilde *)(w[1]);\n    t_sample *sigin = (t_sample *)(w[2]);\n    t_sample *freqin = (t_sample *)(w[3]);\n    t_sample *neglimit = (t_sample *)(w[4]);\n    t_sample *negfreqin = (t_sample *)(w[5]);\n    t_sample *poslimit = (t_sample *)(w[6]);\n    t_sample *posfreqin = (t_sample *)(w[7]);\n    t_sample coef = x->x_coef;\n    t_sample *out = (t_sample *)(w[8]);\n    int n = (int)w[9];\n    int i;\n    t_sample last = x->x_last;\n    for (i = 0; i < n; i++)\n    {\n        t_sample diff = *sigin++ - last;\n        t_sample inc = *freqin++ * coef, diffinc;\n        t_sample posinc = *posfreqin++ * coef;\n        t_sample neginc = *negfreqin++ * coef;\n        t_sample maxdiff = *poslimit++;\n        t_sample mindiff = *neglimit++;\n        if (inc < 0.f)\n            inc = 0.f;\n        else if (inc > 1.f)\n            inc = 1.f;\n        if (posinc < 0.f)\n            posinc = 0.f;\n        else if (posinc > 1.f)\n            posinc = 1.f;\n        if (neginc < 0.f)\n            neginc = 0.f;\n        else if (neginc > 1.f)\n            neginc = 1.f;\n        if (maxdiff < 0)\n            maxdiff = 0;\n        if (mindiff < 0)\n            mindiff = 0;\n        if (diff > maxdiff)\n            diffinc = posinc * (diff- maxdiff) + inc * maxdiff;\n        else if (diff < -mindiff)\n            diffinc = neginc * (diff + mindiff) - inc * mindiff;\n        else diffinc = inc * diff;\n        last = *out++ = last + diffinc;\n    }\n    if (PD_BIGORSMALL(last))\n        last = 0;\n    x->x_last = last;\n    return (w+10);\n}\n\nstatic void slop_tilde_dsp(t_slop_tilde *x, t_signal **sp)\n{\n    x->x_coef = (2 * 3.14159) / sp[0]->s_sr;\n    dsp_add(slop_tilde_perform, 9,\n        x, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec, sp[4]->s_vec,\n            sp[5]->s_vec, sp[6]->s_vec, (t_int)sp[0]->s_n);\n\n}\n\nvoid slop_tilde_setup(void)\n{\n    slop_tilde_class = class_new(gensym(\"slop~\"), (t_newmethod)slop_tilde_new, 0,\n        sizeof(t_slop_tilde), 0, A_GIMME, 0);\n    CLASS_MAINSIGNALIN(slop_tilde_class, t_slop_tilde, x_f);\n    class_addmethod(slop_tilde_class, (t_method)slop_tilde_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_addmethod(slop_tilde_class, (t_method)slop_tilde_set, gensym(\"set\"),\n        A_FLOAT, 0);\n}\n\n\n/* ------------------------ setup routine ------------------------- */\n\nvoid d_filter_setup(void)\n{\n    sighip_setup();\n    siglop_setup();\n    sigbp_setup();\n    sigbiquad_setup();\n    sigsamphold_setup();\n    sigrpole_setup();\n    sigrzero_setup();\n    sigrzero_rev_setup();\n    sigcpole_setup();\n    sigczero_setup();\n    sigczero_rev_setup();\n    slop_tilde_setup();\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/d_global.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/*  send~, receive~, throw~, catch~ */\n\n#include \"m_pd.h\"\n#include <string.h>\n\n/* ----------------------------- send~ ----------------------------- */\nstatic t_class *sigsend_class;\n\ntypedef struct _sigsend\n{\n    t_object x_obj;\n    t_symbol *x_sym;\n    t_canvas *x_canvas;\n    int x_length;\n    int x_nchans;\n    t_sample *x_vec;\n    t_float x_f;\n} t_sigsend;\n\nstatic void *sigsend_new(t_symbol *s, t_floatarg fnchans)\n{\n    t_sigsend *x = (t_sigsend *)pd_new(sigsend_class);\n    if (*s->s_name)\n        pd_bind(&x->x_obj.ob_pd, s);\n    x->x_sym = s;\n    if ((x->x_nchans = fnchans) < 1)\n        x->x_nchans = 1;\n    x->x_length = 1;\n    x->x_vec = (t_sample *)getbytes(x->x_nchans * sizeof(t_sample));\n    x->x_f = 0;\n    x->x_canvas = canvas_getcurrent();\n    return (x);\n}\n\nstatic t_int *sigsend_perform(t_int *w)\n{\n    t_sample *in = (t_sample *)(w[1]);\n    t_sample *out = (t_sample *)(w[2]);\n    int n = (int)(w[3]);\n    while (n--)\n    {\n        *out = (PD_BIGORSMALL(*in) ? 0 : *in);\n        out++;\n        in++;\n    }\n    return (w+4);\n}\n\nstatic void sigsend_channels(t_sigsend *x, t_float fnchans)\n{\n    x->x_nchans = fnchans >= 1 ? fnchans : 1;\n    x->x_length = 1; /* trigger update via sigsend_fixbuf */\n    canvas_update_dsp();\n}\n\nstatic void sigsend_fixbuf(t_sigsend *x, int length)\n{\n    if (x->x_length != length)\n    {\n        x->x_vec = (t_sample *)resizebytes(x->x_vec,\n            x->x_length * x->x_nchans * sizeof(t_sample),\n            length * x->x_nchans * sizeof(t_sample));\n        x->x_length = length;\n    }\n}\n\nstatic void sigsend_dsp(t_sigsend *x, t_signal **sp)\n{\n    int usenchans = (x->x_nchans < sp[0]->s_nchans ?\n        x->x_nchans : sp[0]->s_nchans);\n    sigsend_fixbuf(x, sp[0]->s_length);\n    dsp_add(sigsend_perform, 3, sp[0]->s_vec, x->x_vec,\n        x->x_length * usenchans);\n    if (x->x_nchans > usenchans)\n        memset(x->x_vec + usenchans * x->x_length, 0,\n            (x->x_nchans - usenchans) * x->x_length * sizeof(t_sample));\n}\n\nstatic void sigsend_free(t_sigsend *x)\n{\n    if (*x->x_sym->s_name)\n        pd_unbind(&x->x_obj.ob_pd, x->x_sym);\n    freebytes(x->x_vec, x->x_length * sizeof(t_sample));\n}\n\nstatic void sigsend_setup(void)\n{\n    sigsend_class = class_new(gensym(\"send~\"), (t_newmethod)sigsend_new,\n        (t_method)sigsend_free, sizeof(t_sigsend), CLASS_MULTICHANNEL,\n            A_DEFSYM, A_DEFFLOAT, 0);\n    class_addcreator((t_newmethod)sigsend_new, gensym(\"s~\"),\n        A_DEFSYM, A_DEFFLOAT, 0);\n    CLASS_MAINSIGNALIN(sigsend_class, t_sigsend, x_f);\n    class_addmethod(sigsend_class, (t_method)sigsend_channels,\n        gensym(\"channels\"), A_FLOAT, 0);\n    class_addmethod(sigsend_class, (t_method)sigsend_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_sethelpsymbol(sigsend_class, gensym(\"send-receive-tilde\"));\n}\n\n/* ----------------------------- receive~ ----------------------------- */\nstatic t_class *sigreceive_class;\n\ntypedef struct _sigreceive\n{\n    t_object x_obj;\n    t_symbol *x_sym;\n    t_sample *x_wherefrom;\n    int x_length;\n    int x_nchans;\n} t_sigreceive;\n\nstatic void *sigreceive_new(t_symbol *s)\n{\n    t_sigreceive *x = (t_sigreceive *)pd_new(sigreceive_class);\n    x->x_length = 0;             /* this is changed in dsp routine */\n    x->x_nchans = 1;\n    x->x_sym = s;\n    x->x_wherefrom = 0;\n    outlet_new(&x->x_obj, &s_signal);\n    return (x);\n}\n\nstatic t_int *sigreceive_perform(t_int *w)\n{\n    t_sigreceive *x = (t_sigreceive *)(w[1]);\n    t_sample *out = (t_sample *)(w[2]);\n    int n = (int)(w[3]);\n    t_sample *in = x->x_wherefrom;\n    if (in)\n    {\n        while (n--)\n            *out++ = *in++;\n    }\n    else\n    {\n        while (n--)\n            *out++ = 0;\n    }\n    return (w+4);\n}\n\n/* tb: vectorized receive function */\nstatic t_int *sigreceive_perf8(t_int *w)\n{\n    t_sigreceive *x = (t_sigreceive *)(w[1]);\n    t_sample *out = (t_sample *)(w[2]);\n    int n = (int)(w[3]);\n    t_sample *in = x->x_wherefrom;\n    if (in)\n    {\n        for (; n; n -= 8, in += 8, out += 8)\n        {\n            out[0] = in[0]; out[1] = in[1]; out[2] = in[2]; out[3] = in[3];\n            out[4] = in[4]; out[5] = in[5]; out[6] = in[6]; out[7] = in[7];\n        }\n    }\n    else\n    {\n        for (; n; n -= 8, in += 8, out += 8)\n        {\n            out[0] = 0; out[1] = 0; out[2] = 0; out[3] = 0;\n            out[4] = 0; out[5] = 0; out[6] = 0; out[7] = 0;\n        }\n    }\n    return (w+4);\n}\n\n    /* set receive symbol.  Also check our signal length (setting\n    x->x_length) and chase down the sender to verify length and nchans match */\nstatic void sigreceive_set(t_sigreceive *x, t_symbol *s)\n{\n    t_sigsend *sender = (t_sigsend *)pd_findbyclass((x->x_sym = s),\n        sigsend_class);\n    x->x_wherefrom = 0;\n    if (sender)\n    {\n        int length = canvas_getsignallength(sender->x_canvas),\n            dspstate = pd_getdspstate();\n        sigsend_fixbuf(sender, length);\n        if (!dspstate)\n            x->x_nchans = sender->x_nchans;\n        if (sender->x_nchans == x->x_nchans &&\n            length == x->x_length)\n                x->x_wherefrom = sender->x_vec;\n        else if (x->x_length)\n        {\n            if (dspstate && length == x->x_length)\n                pd_error(x,\n     \"receive~ (set %s) changed number of channels; restart DSP to fix\",\n                    s->s_name);\n            else pd_error(x,\n                \"receive~ %s: dimensions %dx%d don't match the send~ (%dx%d)\",\n                x->x_sym->s_name, x->x_nchans, x->x_length,\n                    sender->x_nchans, sender->x_length);\n        }\n    }\n    else if (*x->x_sym->s_name)\n        pd_error(x, \"receive~ %s: no matching send\", x->x_sym->s_name);\n}\n\nstatic void sigreceive_dsp(t_sigreceive *x, t_signal **sp)\n{\n    x->x_length = sp[0]->s_length;\n    sigreceive_set(x, x->x_sym);\n    signal_setmultiout(&sp[0], x->x_nchans);\n    if ((x->x_length * x->x_nchans) & 7)\n        dsp_add(sigreceive_perform, 3,\n            x, sp[0]->s_vec, (t_int)(x->x_length * x->x_nchans));\n    else dsp_add(sigreceive_perf8, 3,\n        x, sp[0]->s_vec, (t_int)(x->x_length * x->x_nchans));\n}\n\nstatic void sigreceive_setup(void)\n{\n    sigreceive_class = class_new(gensym(\"receive~\"),\n        (t_newmethod)sigreceive_new, 0,\n        sizeof(t_sigreceive), CLASS_MULTICHANNEL, A_DEFSYM, 0);\n    class_addcreator((t_newmethod)sigreceive_new, gensym(\"r~\"),\n        A_DEFSYM, A_DEFFLOAT, 0);\n    class_addmethod(sigreceive_class, (t_method)sigreceive_set, gensym(\"set\"),\n        A_SYMBOL, 0);\n    class_addmethod(sigreceive_class, (t_method)sigreceive_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_sethelpsymbol(sigreceive_class, gensym(\"send-receive-tilde\"));\n}\n\n/* ----------------------------- catch~ ----------------------------- */\nstatic t_class *sigcatch_class;\n\ntypedef struct _sigcatch\n{\n    t_object x_obj;\n    t_symbol *x_sym;\n    t_canvas *x_canvas;\n    int x_length;\n    int x_nchans;\n    t_sample *x_vec;\n} t_sigcatch;\n\nstatic void *sigcatch_new(t_symbol *s, t_floatarg fnchans)\n{\n    t_sigcatch *x = (t_sigcatch *)pd_new(sigcatch_class);\n    if (*s->s_name)\n        pd_bind(&x->x_obj.ob_pd, s);\n    x->x_sym = s;\n    x->x_canvas = canvas_getcurrent();\n    x->x_length = 1;     /* replaced later */\n    if ((x->x_nchans = fnchans) < 1)\n        x->x_nchans = 1;\n    x->x_vec = (t_sample *)getbytes(x->x_length * sizeof(t_sample));\n    outlet_new(&x->x_obj, &s_signal);\n    return (x);\n}\n\nstatic void sigcatch_channels(t_sigcatch *x, t_float fnchans)\n{\n    x->x_nchans = fnchans >= 1 ? fnchans : 1;\n    x->x_length = 1; /* trigger update via sigcatch_fixbuf */\n    canvas_update_dsp();\n}\n\nstatic void sigcatch_fixbuf(t_sigcatch *x, int length)\n{\n    if (x->x_length != length)\n    {\n        x->x_vec = (t_sample *)resizebytes(x->x_vec,\n            x->x_length * x->x_nchans * sizeof(t_sample),\n            length * x->x_nchans * sizeof(t_sample));\n        x->x_length = length;\n    }\n}\n\nstatic t_int *sigcatch_perform(t_int *w)\n{\n    t_sample *in = (t_sample *)(w[1]);\n    t_sample *out = (t_sample *)(w[2]);\n    int n = (int)(w[3]);\n    while (n--)\n    {\n        float f = *in;\n        *in++ = 0;\n        *out++ = (PD_BIGORSMALL(f) ? 0 : f);\n    }\n    return (w+4);\n}\n\nstatic void sigcatch_dsp(t_sigcatch *x, t_signal **sp)\n{\n    sigcatch_fixbuf(x, sp[0]->s_length);\n    signal_setmultiout(&sp[0], x->x_nchans);\n    dsp_add(sigcatch_perform, 3, x->x_vec, sp[0]->s_vec,\n        x->x_length * x->x_nchans);\n}\n\nstatic void sigcatch_free(t_sigcatch *x)\n{\n    if (*x->x_sym->s_name)\n        pd_unbind(&x->x_obj.ob_pd, x->x_sym);\n    freebytes(x->x_vec, x->x_length * sizeof(t_sample));\n}\n\nstatic void sigcatch_setup(void)\n{\n    sigcatch_class = class_new(gensym(\"catch~\"), (t_newmethod)sigcatch_new,\n        (t_method)sigcatch_free, sizeof(t_sigcatch),\n            CLASS_MULTICHANNEL, A_DEFSYM, A_DEFFLOAT, 0);\n    class_addmethod(sigcatch_class, (t_method)sigcatch_channels,\n        gensym(\"channels\"), A_FLOAT, 0);\n    class_addmethod(sigcatch_class, (t_method)sigcatch_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_sethelpsymbol(sigcatch_class, gensym(\"throw~-catch~\"));\n}\n\n/* ----------------------------- throw~ ----------------------------- */\nstatic t_class *sigthrow_class;\n\ntypedef struct _sigthrow\n{\n    t_object x_obj;\n    t_symbol *x_sym;\n    t_sample *x_whereto;\n    int x_length;\n    int x_nsamps;\n    t_float x_f;\n} t_sigthrow;\n\nstatic void *sigthrow_new(t_symbol *s)\n{\n    t_sigthrow *x = (t_sigthrow *)pd_new(sigthrow_class);\n    x->x_sym = s;\n    x->x_whereto  = 0;\n    x->x_length = 0;\n    x->x_f = 0;\n    return (x);\n}\n\nstatic t_int *sigthrow_perform(t_int *w)\n{\n    t_sigthrow *x = (t_sigthrow *)(w[1]);\n    t_sample *in = (t_sample *)(w[2]);\n    int n = (int)(w[3]);\n    t_sample *out = x->x_whereto;\n    if (out)\n    {\n        n = (n <= x->x_nsamps ? n : x->x_nsamps);\n        while (n--)\n            *out++ += *in++;\n    }\n    return (w+4);\n}\n\nstatic void sigthrow_set(t_sigthrow *x, t_symbol *s)\n{\n    t_sigcatch *catcher = (t_sigcatch *)pd_findbyclass((x->x_sym = s),\n        sigcatch_class);\n    if (catcher)\n    {\n        int length = canvas_getsignallength(catcher->x_canvas);\n        sigcatch_fixbuf(catcher, length);\n        if (x->x_length && length != x->x_length)\n        {\n            pd_error(x, \"throw~ %s: my vector size %d doesn't match catch~ (%d)\",\n                x->x_sym->s_name, x->x_length, length);\n            x->x_whereto = 0;\n        }\n        else\n        {\n            x->x_whereto = catcher->x_vec;\n            x->x_nsamps = catcher->x_length * catcher->x_nchans;\n        }\n    }\n    else x->x_whereto = 0;\n}\n\nstatic void sigthrow_dsp(t_sigthrow *x, t_signal **sp)\n{\n    x->x_length = sp[0]->s_n;\n    sigthrow_set(x, x->x_sym);\n    dsp_add(sigthrow_perform, 3,\n        x, sp[0]->s_vec, (t_int)(sp[0]->s_length * sp[0]->s_nchans));\n}\n\nstatic void sigthrow_setup(void)\n{\n    sigthrow_class = class_new(gensym(\"throw~\"), (t_newmethod)sigthrow_new, 0,\n        sizeof(t_sigthrow), CLASS_MULTICHANNEL, A_DEFSYM, 0);\n    class_addmethod(sigthrow_class, (t_method)sigthrow_set, gensym(\"set\"),\n        A_SYMBOL, 0);\n    CLASS_MAINSIGNALIN(sigthrow_class, t_sigthrow, x_f);\n    class_addmethod(sigthrow_class, (t_method)sigthrow_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_sethelpsymbol(sigthrow_class, gensym(\"throw~-catch~\"));\n}\n\n/* ----------------------- global setup routine ---------------- */\n\nvoid d_global_setup(void)\n{\n    sigsend_setup();\n    sigreceive_setup();\n    sigcatch_setup();\n    sigthrow_setup();\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/d_math.c",
    "content": "/* Copyright (c) 1997-2001 Miller Puckette and others.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/*  mathematical functions and other transfer functions, including tilde\n    versions of stuff from x_acoustics.c.\n*/\n\n#include \"m_pd.h\"\n#include <math.h>\n#include <limits.h>\n#define LOGTEN 2.302585092994046\n#define SIGTOTAL(s) ((t_int)((s)->s_length * (s)->s_nchans))\n\n/* ------------------------- clip~ -------------------------- */\nstatic t_class *clip_class;\n\ntypedef struct _clip\n{\n    t_object x_obj;\n    t_float x_f;\n    t_float x_lo;\n    t_float x_hi;\n} t_clip;\n\nstatic void *clip_new(t_floatarg lo, t_floatarg hi)\n{\n    t_clip *x = (t_clip *)pd_new(clip_class);\n    x->x_lo = lo;\n    x->x_hi = hi;\n    outlet_new(&x->x_obj, gensym(\"signal\"));\n    floatinlet_new(&x->x_obj, &x->x_lo);\n    floatinlet_new(&x->x_obj, &x->x_hi);\n    x->x_f = 0;\n    return (x);\n}\n\nstatic t_int *clip_perform(t_int *w)\n{\n    t_clip *x = (t_clip *)(w[1]);\n    t_sample *in = (t_sample *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    while (n--)\n    {\n        t_sample f = *in++;\n        if (f < x->x_lo) f = x->x_lo;\n        if (f > x->x_hi) f = x->x_hi;\n        *out++ = f;\n    }\n    return (w+5);\n}\n\nstatic void clip_dsp(t_clip *x, t_signal **sp)\n{\n    signal_setmultiout(&sp[1], sp[0]->s_nchans);\n    dsp_add(clip_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, SIGTOTAL(sp[0]));\n}\n\nstatic void clip_setup(void)\n{\n    clip_class = class_new(gensym(\"clip~\"), (t_newmethod)clip_new, 0,\n        sizeof(t_clip), CLASS_MULTICHANNEL, A_DEFFLOAT, A_DEFFLOAT, 0);\n    CLASS_MAINSIGNALIN(clip_class, t_clip, x_f);\n    class_addmethod(clip_class, (t_method)clip_dsp, gensym(\"dsp\"), A_CANT, 0);\n}\n\n/* sigrsqrt - reciprocal square root good to 8 mantissa bits  */\n\n#define DUMTAB1SIZE 256\n#define DUMTAB2SIZE 1024\n\n/* There could be a thread race condition here but it will only cause extra]\nmemory allocation. */\nstatic float *rsqrt_exptab, *rsqrt_mantissatab;\n\nstatic void init_rsqrt(void)\n{\n    int i;\n    if (!rsqrt_exptab)\n    {\n        rsqrt_exptab = (float *)getbytes(DUMTAB1SIZE*sizeof(float));\n        rsqrt_mantissatab = (float *)getbytes(DUMTAB2SIZE*sizeof(float));\n        for (i = 0; i < DUMTAB1SIZE; i++)\n        {\n            union {\n              float f;\n              long l;\n            } u;\n            int32_t l =\n                (i ? (i == DUMTAB1SIZE-1 ? DUMTAB1SIZE-2 : i) : 1)<< 23;\n            u.l = l;\n            rsqrt_exptab[i] = 1./sqrt(u.f);\n        }\n        for (i = 0; i < DUMTAB2SIZE; i++)\n        {\n            float f = 1 + (1./DUMTAB2SIZE) * i;\n            rsqrt_mantissatab[i] = 1./sqrt(f);\n        }\n    }\n}\n\n    /* these are used in externs like \"bonk\" */\n\nt_float q8_rsqrt(t_float f0)\n{\n    union {\n      float f;\n      long l;\n    } u;\n    init_rsqrt();\n    u.f = f0;\n    if (u.f < 0) return (0);\n    else return (t_float)(rsqrt_exptab[(u.l >> 23) & 0xff] *\n            rsqrt_mantissatab[(u.l >> 13) & 0x3ff]);\n}\n\nt_float q8_sqrt(t_float f0)\n{\n    union {\n      float f;\n      long l;\n    } u;\n    init_rsqrt();\n    u.f = f0;\n    if (u.f < 0) return (0);\n    else return (t_float)(u.f * rsqrt_exptab[(u.l >> 23) & 0xff] *\n            rsqrt_mantissatab[(u.l >> 13) & 0x3ff]);\n}\n\nt_float qsqrt(t_float f) {return (q8_sqrt(f)); }\nt_float qrsqrt(t_float f) {return (q8_rsqrt(f)); }\n\ntypedef struct sigrsqrt\n{\n    t_object x_obj;\n    t_float x_f;\n} t_sigrsqrt;\n\nstatic t_class *sigrsqrt_class;\n\nstatic void *sigrsqrt_new(void)\n{\n    t_sigrsqrt *x = (t_sigrsqrt *)pd_new(sigrsqrt_class);\n    init_rsqrt();\n    outlet_new(&x->x_obj, gensym(\"signal\"));\n    x->x_f = 0;\n    return (x);\n}\n\nstatic t_int *sigrsqrt_perform(t_int *w)\n{\n    t_sample *in = (t_sample *)w[1], *out = (t_sample *)w[2];\n    int n = (int)w[3];\n    while (n--)\n    {\n        t_sample f = *in++;\n        union {\n          float f;\n          long l;\n        } u;\n        u.f = f;\n        if (f < 0) *out++ = 0;\n        else\n        {\n            t_sample g = rsqrt_exptab[(u.l >> 23) & 0xff] *\n                rsqrt_mantissatab[(u.l >> 13) & 0x3ff];\n            *out++ = 1.5 * g - 0.5 * g * g * g * f;\n        }\n    }\n    return (w + 4);\n}\n\nstatic void sigrsqrt_dsp(t_sigrsqrt *x, t_signal **sp)\n{\n    signal_setmultiout(&sp[1], sp[0]->s_nchans);\n    dsp_add(sigrsqrt_perform, 3, sp[0]->s_vec, sp[1]->s_vec, SIGTOTAL(sp[0]));\n}\n\nvoid sigrsqrt_setup(void)\n{\n    sigrsqrt_class = class_new(gensym(\"rsqrt~\"), (t_newmethod)sigrsqrt_new, 0,\n        sizeof(t_sigrsqrt), CLASS_MULTICHANNEL, 0);\n            /* an old name for it: */\n    class_addcreator(sigrsqrt_new, gensym(\"q8_rsqrt~\"), 0);\n    CLASS_MAINSIGNALIN(sigrsqrt_class, t_sigrsqrt, x_f);\n    class_addmethod(sigrsqrt_class, (t_method)sigrsqrt_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n}\n\n\n/* sigsqrt -  square root good to 8 mantissa bits  */\n\ntypedef struct sigsqrt\n{\n    t_object x_obj;\n    t_float x_f;\n} t_sigsqrt;\n\nstatic t_class *sigsqrt_class;\n\nstatic void *sigsqrt_new(void)\n{\n    t_sigsqrt *x = (t_sigsqrt *)pd_new(sigsqrt_class);\n    init_rsqrt();\n    outlet_new(&x->x_obj, gensym(\"signal\"));\n    x->x_f = 0;\n    return (x);\n}\n\nt_int *sigsqrt_perform(t_int *w)    /* not static; also used in d_fft.c */\n{\n    t_sample *in = (t_sample *)w[1], *out = (t_sample *)w[2];\n    int n = (int)w[3];\n    while (n--)\n    {\n        t_sample f = *in++;\n        union {\n          float f;\n          long l;\n        } u;\n        u.f = f;\n        if (f < 0) *out++ = 0;\n        else\n        {\n            t_sample g = rsqrt_exptab[(u.l >> 23) & 0xff] *\n                rsqrt_mantissatab[(u.l >> 13) & 0x3ff];\n            *out++ = f * (1.5 * g - 0.5 * g * g * g * f);\n        }\n    }\n    return (w + 4);\n}\n\nstatic void sigsqrt_dsp(t_sigsqrt *x, t_signal **sp)\n{\n    signal_setmultiout(&sp[1], sp[0]->s_nchans);\n    dsp_add(sigsqrt_perform, 3, sp[0]->s_vec, sp[1]->s_vec, SIGTOTAL(sp[0]));\n}\n\nvoid sigsqrt_setup(void)\n{\n    sigsqrt_class = class_new(gensym(\"sqrt~\"), (t_newmethod)sigsqrt_new, 0,\n        sizeof(t_sigsqrt), CLASS_MULTICHANNEL, 0);\n    class_addcreator(sigsqrt_new, gensym(\"q8_sqrt~\"), 0);   /* old name */\n    CLASS_MAINSIGNALIN(sigsqrt_class, t_sigsqrt, x_f);\n    class_addmethod(sigsqrt_class, (t_method)sigsqrt_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n}\n\n/* ------------------------------ wrap~ -------------------------- */\n\ntypedef struct wrap\n{\n    t_object x_obj;\n    t_float x_f;\n} t_sigwrap;\n\nt_class *sigwrap_class;\n\nstatic void *sigwrap_new(void)\n{\n    t_sigwrap *x = (t_sigwrap *)pd_new(sigwrap_class);\n    outlet_new(&x->x_obj, gensym(\"signal\"));\n    x->x_f = 0;\n    return (x);\n}\n\nstatic t_int *sigwrap_perform(t_int *w)\n{\n    t_sample *in = (t_sample *)w[1], *out = (t_sample *)w[2];\n    int n = (int)w[3];\n    while (n--)\n    {\n        int k;\n        t_sample f = *in++;\n        f = (f>INT_MAX || f<INT_MIN)?0.:f;\n        k = (int)f;\n        if (k <= f) *out++ = f-k;\n        else *out++ = f - (k-1);\n    }\n    return (w + 4);\n}\n\n     /* old buggy version that sometimes output 1 instead of 0 */\nstatic t_int *sigwrap_old_perform(t_int *w)\n{\n    t_sample *in = (t_sample *)w[1], *out = (t_sample *)w[2];\n    int n = (int)w[3];\n    while (n--)\n    {\n        t_sample f = *in++;\n        int k = f;\n        if (f > 0) *out++ = f-k;\n        else *out++ = f - (k-1);\n    }\n    return (w + 4);\n}\n\nstatic void sigwrap_dsp(t_sigwrap *x, t_signal **sp)\n{\n    signal_setmultiout(&sp[1], sp[0]->s_nchans);\n    dsp_add((pd_compatibilitylevel < 48 ?\n        sigwrap_old_perform : sigwrap_perform),\n            3, sp[0]->s_vec, sp[1]->s_vec, SIGTOTAL(sp[0]));\n}\n\nvoid sigwrap_setup(void)\n{\n    sigwrap_class = class_new(gensym(\"wrap~\"), (t_newmethod)sigwrap_new, 0,\n        sizeof(t_sigwrap), CLASS_MULTICHANNEL, 0);\n    CLASS_MAINSIGNALIN(sigwrap_class, t_sigwrap, x_f);\n    class_addmethod(sigwrap_class, (t_method)sigwrap_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n}\n\n/* ------------------------------ mtof~ -------------------------- */\n\ntypedef struct mtof_tilde\n{\n    t_object x_obj;\n    t_float x_f;\n} t_mtof_tilde;\n\nt_class *mtof_tilde_class;\n\nstatic void *mtof_tilde_new(void)\n{\n    t_mtof_tilde *x = (t_mtof_tilde *)pd_new(mtof_tilde_class);\n    outlet_new(&x->x_obj, gensym(\"signal\"));\n    x->x_f = 0;\n    return (x);\n}\n\nstatic t_int *mtof_tilde_perform(t_int *w)\n{\n    t_sample *in = (t_sample *)w[1], *out = (t_sample *)w[2];\n    int n = (int)w[3];\n    for (; n--; in++, out++)\n    {\n        t_sample f = *in;\n        if (f <= -1500) *out = 0;\n        else\n        {\n            if (f > 1499) f = 1499;\n            *out = 8.17579891564 * exp(.0577622650 * f);\n        }\n    }\n    return (w + 4);\n}\n\nstatic void mtof_tilde_dsp(t_mtof_tilde *x, t_signal **sp)\n{\n    signal_setmultiout(&sp[1], sp[0]->s_nchans);\n    dsp_add(mtof_tilde_perform, 3, sp[0]->s_vec, sp[1]->s_vec, SIGTOTAL(sp[0]));\n}\n\nvoid mtof_tilde_setup(void)\n{\n    mtof_tilde_class = class_new(gensym(\"mtof~\"), (t_newmethod)mtof_tilde_new, 0,\n        sizeof(t_mtof_tilde), CLASS_MULTICHANNEL, 0);\n    CLASS_MAINSIGNALIN(mtof_tilde_class, t_mtof_tilde, x_f);\n    class_addmethod(mtof_tilde_class, (t_method)mtof_tilde_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n}\n\n/* ------------------------------ ftom~ -------------------------- */\n\ntypedef struct ftom_tilde\n{\n    t_object x_obj;\n    t_float x_f;\n} t_ftom_tilde;\n\nt_class *ftom_tilde_class;\n\nstatic void *ftom_tilde_new(void)\n{\n    t_ftom_tilde *x = (t_ftom_tilde *)pd_new(ftom_tilde_class);\n    outlet_new(&x->x_obj, gensym(\"signal\"));\n    x->x_f = 0;\n    return (x);\n}\n\nstatic t_int *ftom_tilde_perform(t_int *w)\n{\n    t_sample *in = (t_sample *)w[1], *out = (t_sample *)w[2];\n    int n = (int)w[3];\n    for (; n--; in++, out++)\n    {\n        t_sample f = *in;\n        *out = (f > 0 ? 17.3123405046 * log(.12231220585 * f) : -1500);\n    }\n    return (w + 4);\n}\n\nstatic void ftom_tilde_dsp(t_ftom_tilde *x, t_signal **sp)\n{\n    signal_setmultiout(&sp[1], sp[0]->s_nchans);\n    dsp_add(ftom_tilde_perform, 3, sp[0]->s_vec, sp[1]->s_vec, SIGTOTAL(sp[0]));\n}\n\nvoid ftom_tilde_setup(void)\n{\n    ftom_tilde_class = class_new(gensym(\"ftom~\"), (t_newmethod)ftom_tilde_new, 0,\n        sizeof(t_ftom_tilde), CLASS_MULTICHANNEL, 0);\n    CLASS_MAINSIGNALIN(ftom_tilde_class, t_ftom_tilde, x_f);\n    class_addmethod(ftom_tilde_class, (t_method)ftom_tilde_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n}\n\n/* ------------------------------ dbtorms~ -------------------------- */\n\ntypedef struct dbtorms_tilde\n{\n    t_object x_obj;\n    t_float x_f;\n} t_dbtorms_tilde;\n\nt_class *dbtorms_tilde_class;\n\nstatic void *dbtorms_tilde_new(void)\n{\n    t_dbtorms_tilde *x = (t_dbtorms_tilde *)pd_new(dbtorms_tilde_class);\n    outlet_new(&x->x_obj, gensym(\"signal\"));\n    x->x_f = 0;\n    return (x);\n}\n\nstatic t_int *dbtorms_tilde_perform(t_int *w)\n{\n    t_sample *in = (t_sample *)w[1], *out = (t_sample *)w[2];\n    int n = (int)w[3];\n    for (; n--; in++, out++)\n    {\n        t_sample f = *in;\n        if (f <= 0) *out = 0;\n        else\n        {\n            if (f > 485)\n                f = 485;\n            *out = exp((LOGTEN * 0.05) * (f-100.));\n        }\n    }\n    return (w + 4);\n}\n\nstatic void dbtorms_tilde_dsp(t_dbtorms_tilde *x, t_signal **sp)\n{\n    signal_setmultiout(&sp[1], sp[0]->s_nchans);\n    dsp_add(dbtorms_tilde_perform, 3, sp[0]->s_vec, sp[1]->s_vec,\n        SIGTOTAL(sp[0]));\n}\n\nvoid dbtorms_tilde_setup(void)\n{\n    dbtorms_tilde_class = class_new(gensym(\"dbtorms~\"),\n        (t_newmethod)dbtorms_tilde_new, 0,\n            sizeof(t_dbtorms_tilde), CLASS_MULTICHANNEL, 0);\n    CLASS_MAINSIGNALIN(dbtorms_tilde_class, t_dbtorms_tilde, x_f);\n    class_addmethod(dbtorms_tilde_class, (t_method)dbtorms_tilde_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n}\n\n/* ------------------------------ rmstodb~ -------------------------- */\n\ntypedef struct rmstodb_tilde\n{\n    t_object x_obj;\n    t_float x_f;\n} t_rmstodb_tilde;\n\nt_class *rmstodb_tilde_class;\n\nstatic void *rmstodb_tilde_new(void)\n{\n    t_rmstodb_tilde *x = (t_rmstodb_tilde *)pd_new(rmstodb_tilde_class);\n    outlet_new(&x->x_obj, gensym(\"signal\"));\n    x->x_f = 0;\n    return (x);\n}\n\nstatic t_int *rmstodb_tilde_perform(t_int *w)\n{\n    t_sample *in = (t_sample *)w[1], *out = (t_sample *)w[2];\n    int n = (int)w[3];\n    for (; n--; in++, out++)\n    {\n        t_sample f = *in;\n        if (f <= 0) *out = 0;\n        else\n        {\n            t_sample g = 100 + 20./LOGTEN * log(f);\n            *out = (g < 0 ? 0 : g);\n        }\n    }\n    return (w + 4);\n}\n\nstatic void rmstodb_tilde_dsp(t_rmstodb_tilde *x, t_signal **sp)\n{\n    signal_setmultiout(&sp[1], sp[0]->s_nchans);\n    dsp_add(rmstodb_tilde_perform, 3, sp[0]->s_vec, sp[1]->s_vec,\n        SIGTOTAL(sp[0]));\n}\n\nvoid rmstodb_tilde_setup(void)\n{\n    rmstodb_tilde_class = class_new(gensym(\"rmstodb~\"),\n        (t_newmethod)rmstodb_tilde_new, 0, sizeof(t_rmstodb_tilde),\n            CLASS_MULTICHANNEL, 0);\n    CLASS_MAINSIGNALIN(rmstodb_tilde_class, t_rmstodb_tilde, x_f);\n    class_addmethod(rmstodb_tilde_class, (t_method)rmstodb_tilde_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n}\n\n/* ------------------------------ dbtopow~ -------------------------- */\n\ntypedef struct dbtopow_tilde\n{\n    t_object x_obj;\n    t_float x_f;\n} t_dbtopow_tilde;\n\nt_class *dbtopow_tilde_class;\n\nstatic void *dbtopow_tilde_new(void)\n{\n    t_dbtopow_tilde *x = (t_dbtopow_tilde *)pd_new(dbtopow_tilde_class);\n    outlet_new(&x->x_obj, gensym(\"signal\"));\n    x->x_f = 0;\n    return (x);\n}\n\nstatic t_int *dbtopow_tilde_perform(t_int *w)\n{\n    t_sample *in = (t_sample *)w[1], *out = (t_sample *)w[2];\n    int n = (int)w[3];\n    for (; n--; in++, out++)\n    {\n        t_sample f = *in;\n        if (f <= 0) *out = 0;\n        else\n        {\n            if (f > 870)\n                f = 870;\n            *out = exp((LOGTEN * 0.1) * (f-100.));\n        }\n    }\n    return (w + 4);\n}\n\nstatic void dbtopow_tilde_dsp(t_dbtopow_tilde *x, t_signal **sp)\n{\n    signal_setmultiout(&sp[1], sp[0]->s_nchans);\n    dsp_add(dbtopow_tilde_perform, 3, sp[0]->s_vec, sp[1]->s_vec,\n        SIGTOTAL(sp[0]));\n}\n\nvoid dbtopow_tilde_setup(void)\n{\n    dbtopow_tilde_class = class_new(gensym(\"dbtopow~\"), (t_newmethod)dbtopow_tilde_new, 0,\n        sizeof(t_dbtopow_tilde), CLASS_MULTICHANNEL, 0);\n    CLASS_MAINSIGNALIN(dbtopow_tilde_class, t_dbtopow_tilde, x_f);\n    class_addmethod(dbtopow_tilde_class, (t_method)dbtopow_tilde_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n}\n\n/* ------------------------------ powtodb~ -------------------------- */\n\ntypedef struct powtodb_tilde\n{\n    t_object x_obj;\n    t_float x_f;\n} t_powtodb_tilde;\n\nt_class *powtodb_tilde_class;\n\nstatic void *powtodb_tilde_new(void)\n{\n    t_powtodb_tilde *x = (t_powtodb_tilde *)pd_new(powtodb_tilde_class);\n    outlet_new(&x->x_obj, gensym(\"signal\"));\n    x->x_f = 0;\n    return (x);\n}\n\nstatic t_int *powtodb_tilde_perform(t_int *w)\n{\n    t_sample *in = (t_sample *)w[1], *out = (t_sample *)w[2];\n    int n = (int)w[3];\n    for (; n--; in++, out++)\n    {\n        t_sample f = *in;\n        if (f <= 0) *out = 0;\n        else\n        {\n            t_sample g = 100 + 10./LOGTEN * log(f);\n            *out = (g < 0 ? 0 : g);\n        }\n    }\n    return (w + 4);\n}\n\nstatic void powtodb_tilde_dsp(t_powtodb_tilde *x, t_signal **sp)\n{\n    signal_setmultiout(&sp[1], sp[0]->s_nchans);\n    dsp_add(powtodb_tilde_perform, 3, sp[0]->s_vec, sp[1]->s_vec,\n        SIGTOTAL(sp[0]));\n}\n\nvoid powtodb_tilde_setup(void)\n{\n    powtodb_tilde_class = class_new(gensym(\"powtodb~\"),\n        (t_newmethod)powtodb_tilde_new, 0,\n            sizeof(t_powtodb_tilde), CLASS_MULTICHANNEL, 0);\n    CLASS_MAINSIGNALIN(powtodb_tilde_class, t_powtodb_tilde, x_f);\n    class_addmethod(powtodb_tilde_class, (t_method)powtodb_tilde_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n}\n\n/* ----------------------------- exp~ ----------------------------- */\nstatic t_class *exp_tilde_class;\n\ntypedef struct _exp_tilde\n{\n    t_object x_obj;\n    t_float x_f;\n} t_exp_tilde;\n\nstatic void *exp_tilde_new(void)\n{\n    t_exp_tilde *x = (t_exp_tilde *)pd_new(exp_tilde_class);\n    outlet_new(&x->x_obj, &s_signal);\n    return (x);\n}\n\nt_int *exp_tilde_perform(t_int *w)\n{\n    t_sample *in1 = (t_sample *)(w[1]);\n    t_sample *out = (t_sample *)(w[2]);\n    int n = (int)(w[3]);\n    while (n--)\n        *out++ = exp(*in1++);\n    return (w+4);\n}\n\nstatic void exp_tilde_dsp(t_exp_tilde *x, t_signal **sp)\n{\n    signal_setmultiout(&sp[1], sp[0]->s_nchans);\n    dsp_add(exp_tilde_perform, 3,\n        sp[0]->s_vec, sp[1]->s_vec, SIGTOTAL(sp[0]));\n}\n\nstatic void exp_tilde_setup(void)\n{\n    exp_tilde_class = class_new(gensym(\"exp~\"), (t_newmethod)exp_tilde_new, 0,\n        sizeof(t_exp_tilde), CLASS_MULTICHANNEL, 0);\n    CLASS_MAINSIGNALIN(exp_tilde_class, t_exp_tilde, x_f);\n    class_addmethod(exp_tilde_class, (t_method)exp_tilde_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n}\n\n/* ----------------------------- abs~ ----------------------------- */\nstatic t_class *abs_tilde_class;\n\ntypedef struct _abs_tilde\n{\n    t_object x_obj;\n    t_float x_f;\n} t_abs_tilde;\n\nstatic void *abs_tilde_new(void)\n{\n    t_abs_tilde *x = (t_abs_tilde *)pd_new(abs_tilde_class);\n    outlet_new(&x->x_obj, &s_signal);\n    return (x);\n}\n\nt_int *abs_tilde_perform(t_int *w)\n{\n    t_sample *in1 = (t_sample *)(w[1]);\n    t_sample *out = (t_sample *)(w[2]);\n    int n = (int)(w[3]);\n    while (n--)\n    {\n        t_sample f = *in1++;\n        *out++ = (f >= 0 ? f : -f);\n    }\n    return (w+4);\n}\n\nstatic void abs_tilde_dsp(t_abs_tilde *x, t_signal **sp)\n{\n    signal_setmultiout(&sp[1], sp[0]->s_nchans);\n    dsp_add(abs_tilde_perform, 3,\n        sp[0]->s_vec, sp[1]->s_vec, SIGTOTAL(sp[0]));\n}\n\nstatic void abs_tilde_setup(void)\n{\n    abs_tilde_class = class_new(gensym(\"abs~\"), (t_newmethod)abs_tilde_new, 0,\n        sizeof(t_abs_tilde), CLASS_MULTICHANNEL, 0);\n    CLASS_MAINSIGNALIN(abs_tilde_class, t_abs_tilde, x_f);\n    class_addmethod(abs_tilde_class, (t_method)abs_tilde_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n}\n\n/* ------------------------ global setup routine ------------------------- */\n\nvoid d_math_setup(void)\n{\n    dbtorms_tilde_setup();\n    rmstodb_tilde_setup();\n    dbtopow_tilde_setup();\n    powtodb_tilde_setup();\n    mtof_tilde_setup();\n    ftom_tilde_setup();\n    sigrsqrt_setup();\n    sigsqrt_setup();\n    sigwrap_setup();\n    exp_tilde_setup();\n    abs_tilde_setup();\n    clip_setup();\n    t_symbol *s1 = gensym(\"acoustics-tilde.pd\");\n    class_sethelpsymbol(mtof_tilde_class, s1);\n    class_sethelpsymbol(ftom_tilde_class, s1);\n    class_sethelpsymbol(dbtorms_tilde_class, s1);\n    class_sethelpsymbol(rmstodb_tilde_class, s1);\n    class_sethelpsymbol(dbtopow_tilde_class, s1);\n    class_sethelpsymbol(powtodb_tilde_class, s1);\n    t_symbol *s2 = gensym(\"unops-tilde.pd\");\n    class_sethelpsymbol(sigrsqrt_class, s2);\n    class_sethelpsymbol(sigsqrt_class, s2);\n    class_sethelpsymbol(sigwrap_class, s2);\n    class_sethelpsymbol(exp_tilde_class, s2);\n    class_sethelpsymbol(abs_tilde_class, s2);\n}\n\n"
  },
  {
    "path": "libs/libpd/pure-data/src/d_misc.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/*  miscellaneous: print~; more to come.\n*/\n\n#include \"m_pd.h\"\n#include <string.h>\n\n/* ------------------------- print~ -------------------------- */\nstatic t_class *print_class;\n\ntypedef struct _print\n{\n    t_object x_obj;\n    t_float x_f;\n    t_symbol *x_sym;\n    int x_count;\n} t_print;\n\nstatic t_int *print_perform(t_int *w)\n{\n    t_print *x = (t_print *)(w[1]);\n    t_sample *in = (t_sample *)(w[2]);\n    int n = (int)(w[3]);\n    if (x->x_count)\n    {\n        int i=0;\n        startpost(\"%s:\", x->x_sym->s_name);\n        for(i=0; i<n; i++) {\n          if(i%8==0)endpost();\n          startpost(\"%.4g  \", in[i]);\n        }\n        endpost();\n        x->x_count--;\n    }\n    return (w+4);\n}\n\nstatic void print_dsp(t_print *x, t_signal **sp)\n{\n    dsp_add(print_perform, 3, x, sp[0]->s_vec, (t_int)sp[0]->s_n);\n}\n\nstatic void print_float(t_print *x, t_float f)\n{\n    if (f < 0) f = 0;\n    x->x_count = f;\n}\n\nstatic void print_bang(t_print *x)\n{\n    x->x_count = 1;\n}\n\nstatic void *print_new(t_symbol *s)\n{\n    t_print *x = (t_print *)pd_new(print_class);\n    x->x_sym = (s->s_name[0]? s : gensym(\"print~\"));\n    x->x_count = 0;\n    x->x_f = 0;\n    return (x);\n}\n\nstatic void print_setup(void)\n{\n    print_class = class_new(gensym(\"print~\"), (t_newmethod)print_new, 0,\n        sizeof(t_print), 0, A_DEFSYM, 0);\n    CLASS_MAINSIGNALIN(print_class, t_print, x_f);\n    class_addmethod(print_class, (t_method)print_dsp, gensym(\"dsp\"), A_CANT, 0);\n    class_addbang(print_class, print_bang);\n    class_addfloat(print_class, print_float);\n}\n\n/* ------------------------ bang~ -------------------------- */\n\nstatic t_class *bang_tilde_class;\n\ntypedef struct _bang\n{\n    t_object x_obj;\n    t_clock *x_clock;\n} t_bang;\n\nstatic t_int *bang_tilde_perform(t_int *w)\n{\n    t_bang *x = (t_bang *)(w[1]);\n    clock_delay(x->x_clock, 0);\n    return (w+2);\n}\n\nstatic void bang_tilde_dsp(t_bang *x, t_signal **sp)\n{\n    dsp_add(bang_tilde_perform, 1, x);\n}\n\nstatic void bang_tilde_tick(t_bang *x)\n{\n    outlet_bang(x->x_obj.ob_outlet);\n}\n\nstatic void bang_tilde_free(t_bang *x)\n{\n    clock_free(x->x_clock);\n}\n\nstatic void *bang_tilde_new(t_symbol *s)\n{\n    t_bang *x = (t_bang *)pd_new(bang_tilde_class);\n    x->x_clock = clock_new(x, (t_method)bang_tilde_tick);\n    outlet_new(&x->x_obj, &s_bang);\n    return (x);\n}\n\nstatic void bang_tilde_setup(void)\n{\n    bang_tilde_class = class_new(gensym(\"bang~\"), (t_newmethod)bang_tilde_new,\n        (t_method)bang_tilde_free, sizeof(t_bang), 0, 0);\n    class_addmethod(bang_tilde_class, (t_method)bang_tilde_dsp,\n        gensym(\"dsp\"), 0);\n}\n\n\n/* ------------------------ snake_in~ -------------------------- */\n\nstatic t_class *snake_in_tilde_class;\n\ntypedef struct _snake_in\n{\n    t_object x_obj;\n    t_sample x_f;\n    int x_nchans;\n} t_snake_in;\n\nstatic void snake_in_tilde_dsp(t_snake_in *x, t_signal **sp)\n{\n    int i;\n        /* create an n-channel output signal. sp has n+1 elements. */\n    signal_setmultiout(&sp[x->x_nchans], x->x_nchans);\n        /* add n copy operations to the DSP chain, one from each input */\n    for (i = 0; i < x->x_nchans; i++)\n         dsp_add_copy(sp[i]->s_vec,\n            sp[x->x_nchans]->s_vec + i * sp[0]->s_length, sp[0]->s_length);\n}\n\nstatic void *snake_in_tilde_new(t_floatarg fnchans)\n{\n    t_snake_in *x = (t_snake_in *)pd_new(snake_in_tilde_class);\n    int i;\n    if ((x->x_nchans = fnchans) <= 0)\n        x->x_nchans = 2;\n    for (i = 1; i < x->x_nchans; i++)\n        inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);\n    outlet_new(&x->x_obj, &s_signal);\n    return (x);\n}\n\n/* ------------------------ snake_out~ -------------------------- */\n\nstatic t_class *snake_out_tilde_class;\n\ntypedef struct _snake_out\n{\n    t_object x_obj;\n    t_sample x_f;\n    int x_nchans;\n} t_snake_out;\n\nstatic void snake_out_tilde_dsp(t_snake_out *x, t_signal **sp)\n{\n    int i, usenchans = (x->x_nchans < sp[0]->s_nchans ?\n        x->x_nchans : sp[0]->s_nchans);\n        /* create n one-channel output signals and add a copy operation\n        for each one tothe DSP chain */\n    for (i = 0; i < x->x_nchans; i++)\n    {\n        signal_setmultiout(&sp[i+1], 1);\n        if (i < usenchans)\n            dsp_add_copy(sp[0]->s_vec + i * sp[0]->s_length,\n                sp[i+1]->s_vec, sp[0]->s_length);\n        else dsp_add_zero(sp[i+1]->s_vec, sp[0]->s_length);\n    }\n}\n\nstatic void *snake_out_tilde_new(t_floatarg fnchans)\n{\n    t_snake_out *x = (t_snake_out *)pd_new(snake_out_tilde_class);\n    int i;\n    if ((x->x_nchans = fnchans) <= 0)\n        x->x_nchans = 2;\n    for (i = 0; i < x->x_nchans; i++)\n        outlet_new(&x->x_obj, &s_signal);\n    return (x);\n}\n\nstatic void *snake_tilde_new(t_symbol *s, int argc, t_atom *argv)\n{\n    if (!argc || argv[0].a_type != A_SYMBOL)\n        pd_this->pd_newest =\n            snake_in_tilde_new(atom_getfloatarg(0, argc, argv));\n    else\n    {\n        const char *str = argv[0].a_w.w_symbol->s_name;\n        if (!strcmp(str, \"in\"))\n            pd_this->pd_newest =\n                snake_in_tilde_new(atom_getfloatarg(1, argc, argv));\n        else if (!strcmp(str, \"out\"))\n            pd_this->pd_newest =\n                snake_out_tilde_new(atom_getfloatarg(1, argc, argv));\n        else\n        {\n            pd_error(0, \"list %s: unknown function\", str);\n            pd_this->pd_newest = 0;\n        }\n    }\n    return (pd_this->pd_newest);\n}\n\nstatic void snake_tilde_setup(void)\n{\n    snake_in_tilde_class = class_new(gensym(\"snake_in~\"),\n        (t_newmethod)snake_in_tilde_new, 0, sizeof(t_snake_in),\n            CLASS_MULTICHANNEL, A_DEFFLOAT, 0);\n    CLASS_MAINSIGNALIN(snake_in_tilde_class, t_snake_in, x_f);\n    class_addmethod(snake_in_tilde_class, (t_method)snake_in_tilde_dsp,\n        gensym(\"dsp\"), 0);\n    class_sethelpsymbol(snake_in_tilde_class, gensym(\"snake-tilde\"));\n\n    snake_out_tilde_class = class_new(gensym(\"snake_out~\"),\n        (t_newmethod)snake_out_tilde_new, 0, sizeof(t_snake_out),\n            CLASS_MULTICHANNEL, A_DEFFLOAT, 0);\n    CLASS_MAINSIGNALIN(snake_out_tilde_class, t_snake_out, x_f);\n    class_addmethod(snake_out_tilde_class, (t_method)snake_out_tilde_dsp,\n        gensym(\"dsp\"), 0);\n    class_sethelpsymbol(snake_out_tilde_class, gensym(\"snake-tilde\"));\n\n    class_addcreator((t_newmethod)snake_tilde_new, gensym(\"snake~\"),\n        A_GIMME, 0);\n}\n\n/* ------------------------ global setup routine ------------------------- */\n\nvoid d_misc_setup(void)\n{\n    print_setup();\n    bang_tilde_setup();\n    snake_tilde_setup();\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/d_osc.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* sinusoidal oscillator and table lookup; see also tabosc4~ in d_array.c.\n*/\n\n#include \"m_pd.h\"\n#include \"math.h\"\n\n#define BIGFLOAT 1.0e+19\n#define UNITBIT32 1572864.  /* 3*2^19; bit 32 has place value 1 */\n\n#include \"m_private_utils.h\"\n\n#if BYTE_ORDER == LITTLE_ENDIAN\n# define HIOFFSET 1\n# define LOWOFFSET 0\n#else\n# define HIOFFSET 0    /* word offset to find MSB */\n# define LOWOFFSET 1    /* word offset to find LSB */\n#endif\n\nunion tabfudge\n{\n    double tf_d;\n    int32_t tf_i[2];\n};\n\n/* -------------------------- phasor~ ------------------------------ */\nstatic t_class *phasor_class;\n\n#if 1   /* in the style of R. Hoeldrich (ICMC 1995 Banff) */\n\ntypedef struct _phasor\n{\n    t_object x_obj;\n    double x_phase;\n    t_float x_conv;\n    t_float x_f;\t\t\t\t\t\t// scalar frequency\n} t_phasor;\n\nstatic void *phasor_new(t_floatarg f)\n{\n    t_phasor *x = (t_phasor *)pd_new(phasor_class);\n    x->x_f = f;\n    inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym(\"ft1\"));\n    x->x_phase = 0;\n    x->x_conv = 0;\n    outlet_new(&x->x_obj, gensym(\"signal\"));\n    return (x);\n}\n\nstatic t_int *phasor_perform(t_int *w)\n{\n    t_phasor *x = (t_phasor *)(w[1]);\n    t_sample *in = (t_float *)(w[2]);\n    t_sample *out = (t_float *)(w[3]);\n    int n = (int)(w[4]);\n    double dphase = x->x_phase + (double)UNITBIT32;\n    union tabfudge tf;\n    int normhipart;\n    t_float conv = x->x_conv;\n\n    tf.tf_d = UNITBIT32;\n    normhipart = tf.tf_i[HIOFFSET];\n    tf.tf_d = dphase;\n\n    while (n--)\n    {\n        tf.tf_i[HIOFFSET] = normhipart;\n        dphase += *in++ * conv;\n        *out++ = tf.tf_d - UNITBIT32;\n        tf.tf_d = dphase;\n    }\n    tf.tf_i[HIOFFSET] = normhipart;\n    x->x_phase = tf.tf_d - UNITBIT32;\n    return (w+5);\n}\n\nstatic void phasor_dsp(t_phasor *x, t_signal **sp)\n{\n    x->x_conv = 1./sp[0]->s_sr;\n    dsp_add(phasor_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, (t_int)sp[0]->s_n);\n}\n\nstatic void phasor_ft1(t_phasor *x, t_float f)\n{\n    x->x_phase = (double)f;\n}\n\nstatic void phasor_setup(void)\n{\n    phasor_class = class_new(gensym(\"phasor~\"), (t_newmethod)phasor_new, 0,\n        sizeof(t_phasor), 0, A_DEFFLOAT, 0);\n    CLASS_MAINSIGNALIN(phasor_class, t_phasor, x_f);\n    class_addmethod(phasor_class, (t_method)phasor_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_addmethod(phasor_class, (t_method)phasor_ft1,\n        gensym(\"ft1\"), A_FLOAT, 0);\n}\n\n#endif  /* Hoeldrich version */\n\n/* ------------------------ cos~ ----------------------------- */\n\nfloat *cos_table;\n\nstatic t_class *cos_class;\n\ntypedef struct _cos\n{\n    t_object x_obj;\n    t_float x_f;\t\t\t// scalar frequency\n} t_cos;\n\nstatic void *cos_new(t_floatarg f)\n{\n    t_cos *x = (t_cos *)pd_new(cos_class);\n    outlet_new(&x->x_obj, gensym(\"signal\"));\n    x->x_f = f;\n    return (x);\n}\n\nstatic t_int *cos_perform(t_int *w)\n{\n    t_sample *in = (t_sample *)(w[1]);\n    t_sample *out = (t_sample *)(w[2]);\n    int n = (int)(w[3]);\n    float *tab = cos_table, *addr;\n    t_float f1, f2, frac;\n    double dphase;\n    int normhipart;\n    union tabfudge tf;\n\n    tf.tf_d = UNITBIT32;\n    normhipart = tf.tf_i[HIOFFSET];\n\n#if 0           /* this is the readable version of the code. */\n    while (n--)\n    {\n        dphase = (double)(*in++ * (float)(COSTABSIZE)) + UNITBIT32;\n        tf.tf_d = dphase;\n        addr = tab + (tf.tf_i[HIOFFSET] & (COSTABSIZE-1));\n        tf.tf_i[HIOFFSET] = normhipart;\n        frac = tf.tf_d - UNITBIT32;\n        f1 = addr[0];\n        f2 = addr[1];\n        *out++ = f1 + frac * (f2 - f1);\n    }\n#endif\n#if 1           /* this is the same, unwrapped by hand. */\n        dphase = (double)(*in++ * (float)(COSTABSIZE)) + UNITBIT32;\n        tf.tf_d = dphase;\n        addr = tab + (tf.tf_i[HIOFFSET] & (COSTABSIZE-1));\n        tf.tf_i[HIOFFSET] = normhipart;\n    while (--n)\n    {\n        dphase = (double)(*in++ * (float)(COSTABSIZE)) + UNITBIT32;\n            frac = tf.tf_d - UNITBIT32;\n        tf.tf_d = dphase;\n            f1 = addr[0];\n            f2 = addr[1];\n        addr = tab + (tf.tf_i[HIOFFSET] & (COSTABSIZE-1));\n            *out++ = f1 + frac * (f2 - f1);\n        tf.tf_i[HIOFFSET] = normhipart;\n    }\n            frac = tf.tf_d - UNITBIT32;\n            f1 = addr[0];\n            f2 = addr[1];\n            *out++ = f1 + frac * (f2 - f1);\n#endif\n    return (w+4);\n}\n\nstatic void cos_dsp(t_cos *x, t_signal **sp)\n{\n    signal_setmultiout(&sp[1], sp[0]->s_nchans);\n    dsp_add(cos_perform, 3, sp[0]->s_vec, sp[1]->s_vec,\n        (t_int)(sp[0]->s_length * sp[0]->s_nchans));\n}\n\nstatic void cos_maketable(void)\n{\n    int i;\n    float *fp, phase, phsinc = (2. * 3.14159) / COSTABSIZE;\n    union tabfudge tf;\n\n    if (cos_table) return;\n    cos_table = (float *)getbytes(sizeof(float) * (COSTABSIZE+1));\n    for (i = COSTABSIZE + 1, fp = cos_table, phase = 0; i--;\n        fp++, phase += phsinc)\n            *fp = cos(phase);\n\n        /* here we check at startup whether the byte alignment\n            is as we declared it.  If not, the code has to be\n            recompiled the other way. */\n    tf.tf_d = UNITBIT32 + 0.5;\n    if ((unsigned)tf.tf_i[LOWOFFSET] != 0x80000000)\n        bug(\"cos~: unexpected machine alignment\");\n}\n\nstatic void cos_cleanup(t_class *c)\n{\n    freebytes(cos_table, sizeof(float) * (COSTABSIZE+1));\n    cos_table = 0;\n}\n\nstatic void cos_setup(void)\n{\n    cos_class = class_new(gensym(\"cos~\"), (t_newmethod)cos_new, 0,\n        sizeof(t_cos), CLASS_MULTICHANNEL, A_DEFFLOAT, 0);\n    class_setfreefn(cos_class, cos_cleanup);\n    CLASS_MAINSIGNALIN(cos_class, t_cos, x_f);\n    class_addmethod(cos_class, (t_method)cos_dsp, gensym(\"dsp\"), A_CANT, 0);\n    cos_maketable();\n}\n\n/* ------------------------ osc~ ----------------------------- */\n\nstatic t_class *osc_class, *scalarosc_class;\n\ntypedef struct _osc\n{\n    t_object x_obj;\n    double x_phase;\n    t_float x_conv;\n    t_float x_f;\t\t\t\t\t\t// scalar frequency\n} t_osc;\n\nstatic void *osc_new(t_floatarg f)\n{\n    t_osc *x = (t_osc *)pd_new(osc_class);\n    x->x_f = f;\n    outlet_new(&x->x_obj, gensym(\"signal\"));\n    inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym(\"ft1\"));\n    x->x_phase = 0;\n    x->x_conv = 0;\n    return (x);\n}\n\nstatic t_int *osc_perform(t_int *w)\n{\n    t_osc *x = (t_osc *)(w[1]);\n    t_sample *in = (t_sample *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    float *tab = cos_table, *addr;\n    t_float f1, f2, frac;\n    double dphase = x->x_phase + UNITBIT32;\n    int normhipart;\n    union tabfudge tf;\n    float conv = x->x_conv;\n\n    tf.tf_d = UNITBIT32;\n    normhipart = tf.tf_i[HIOFFSET];\n#if 0\n    while (n--)\n    {\n        tf.tf_d = dphase;\n        dphase += *in++ * conv;\n        addr = tab + (tf.tf_i[HIOFFSET] & (COSTABSIZE-1));\n        tf.tf_i[HIOFFSET] = normhipart;\n        frac = tf.tf_d - UNITBIT32;\n        f1 = addr[0];\n        f2 = addr[1];\n        *out++ = f1 + frac * (f2 - f1);\n    }\n#endif\n#if 1\n        tf.tf_d = dphase;\n        dphase += *in++ * conv;\n        addr = tab + (tf.tf_i[HIOFFSET] & (COSTABSIZE-1));\n        tf.tf_i[HIOFFSET] = normhipart;\n        frac = tf.tf_d - UNITBIT32;\n    while (--n)\n    {\n        tf.tf_d = dphase;\n            f1 = addr[0];\n        dphase += *in++ * conv;\n            f2 = addr[1];\n        addr = tab + (tf.tf_i[HIOFFSET] & (COSTABSIZE-1));\n        tf.tf_i[HIOFFSET] = normhipart;\n            *out++ = f1 + frac * (f2 - f1);\n        frac = tf.tf_d - UNITBIT32;\n    }\n            f1 = addr[0];\n            f2 = addr[1];\n            *out++ = f1 + frac * (f2 - f1);\n#endif\n\n    tf.tf_d = UNITBIT32 * COSTABSIZE;\n    normhipart = tf.tf_i[HIOFFSET];\n    tf.tf_d = dphase + (UNITBIT32 * COSTABSIZE - UNITBIT32);\n    tf.tf_i[HIOFFSET] = normhipart;\n    x->x_phase = tf.tf_d - UNITBIT32 * COSTABSIZE;\n    return (w+5);\n}\n\nstatic void osc_dsp(t_osc *x, t_signal **sp)\n{\n    x->x_conv = COSTABSIZE/sp[0]->s_sr;\n    dsp_add(osc_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, (t_int)sp[0]->s_n);\n}\n\nstatic void osc_ft1(t_osc *x, t_float f)\n{\n    x->x_phase = COSTABSIZE * f;\n}\n\nstatic void osc_setup(void)\n{\n    osc_class = class_new(gensym(\"osc~\"), (t_newmethod)osc_new, 0,\n        sizeof(t_osc), 0, A_DEFFLOAT, 0);\n    CLASS_MAINSIGNALIN(osc_class, t_osc, x_f);\n    class_addmethod(osc_class, (t_method)osc_dsp, gensym(\"dsp\"), A_CANT, 0);\n    class_addmethod(osc_class, (t_method)osc_ft1, gensym(\"ft1\"), A_FLOAT, 0);\n\n    cos_maketable();\n}\n\n/* ---- vcf~ - resonant filter with audio-rate center frequency input ----- */\n\ntypedef struct vcfctl\n{\n    t_float c_re;\n    t_float c_im;\n    t_float c_q;\n    t_float c_isr;\n} t_vcfctl;\n\ntypedef struct sigvcf\n{\n    t_object x_obj;\n    t_vcfctl x_cspace;\n    t_float x_f;\n} t_sigvcf;\n\nt_class *sigvcf_class;\n\nstatic void *sigvcf_new(t_floatarg q)\n{\n    t_sigvcf *x = (t_sigvcf *)pd_new(sigvcf_class);\n    inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);\n    inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym(\"float\"), gensym(\"ft1\"));\n    outlet_new(&x->x_obj, gensym(\"signal\"));\n    outlet_new(&x->x_obj, gensym(\"signal\"));\n    x->x_cspace.c_re = 0;\n    x->x_cspace.c_im = 0;\n    x->x_cspace.c_q = q;\n    x->x_cspace.c_isr = 0;\n    x->x_f = 0;\n    return (x);\n}\n\nstatic void sigvcf_ft1(t_sigvcf *x, t_float f)\n{\n    if(f < 0.) f = 0.;\n    if(f > BIGFLOAT) f = BIGFLOAT;\n    x->x_cspace.c_q = f;\n}\n\nstatic t_int *sigvcf_perform(t_int *w)\n{\n    t_sample *in1 = (t_sample *)(w[1]);\n    t_sample *in2 = (t_sample *)(w[2]);\n    t_sample *out1 = (t_sample *)(w[3]);\n    t_sample *out2 = (t_sample *)(w[4]);\n    t_vcfctl *c = (t_vcfctl *)(w[5]);\n    int n = (int)w[6];\n    int i;\n    t_float re = c->c_re, re2;\n    t_float im = c->c_im;\n    t_float q = c->c_q;\n    t_float isr = c->c_isr;\n    t_float qinv = (q > 0? 1.0f/q : 0);\n    t_float ampcorrect = 2. - 2. / (q + 2.);\n    t_float coefr, coefi;\n    float *tab = cos_table, *addr, f1, f2, frac;\n    double dphase;\n    int normhipart, tabindex;\n    union tabfudge tf;\n\n    tf.tf_d = UNITBIT32;\n    normhipart = tf.tf_i[HIOFFSET];\n\n    for (i = 0; i < n; i++)\n    {\n        float cf, cfindx, r, oneminusr;\n        cf = *in2++ * isr;\n        if (cf < 0) cf = 0;\n        cfindx = cf * (float)(COSTABSIZE/6.28318f);\n        r = (qinv > 0 ? 1 - cf * qinv : 0);\n        if (r < 0) r = 0;\n        oneminusr = 1.0f - r;\n        dphase = ((double)(cfindx)) + UNITBIT32;\n        tf.tf_d = dphase;\n        tabindex = tf.tf_i[HIOFFSET] & (COSTABSIZE-1);\n        addr = tab + tabindex;\n        tf.tf_i[HIOFFSET] = normhipart;\n        frac = tf.tf_d - UNITBIT32;\n        f1 = addr[0];\n        f2 = addr[1];\n        coefr = r * (f1 + frac * (f2 - f1));\n\n        addr = tab + ((tabindex - (COSTABSIZE/4)) & (COSTABSIZE-1));\n        f1 = addr[0];\n        f2 = addr[1];\n        coefi = r * (f1 + frac * (f2 - f1));\n\n        f1 = *in1++;\n        re2 = re;\n        *out1++ = re = ampcorrect * oneminusr * f1\n            + coefr * re2 - coefi * im;\n        *out2++ = im = coefi * re2 + coefr * im;\n    }\n    if (PD_BIGORSMALL(re))\n        re = 0;\n    if (PD_BIGORSMALL(im))\n        im = 0;\n    c->c_re = re;\n    c->c_im = im;\n    return (w+7);\n}\n\nstatic void sigvcf_dsp(t_sigvcf *x, t_signal **sp)\n{\n    x->x_cspace.c_isr = 6.28318f/sp[0]->s_sr;\n    dsp_add(sigvcf_perform, 6,\n        sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec,\n            &x->x_cspace, (t_int)sp[0]->s_n);\n}\n\nstatic\nvoid sigvcf_setup(void)\n{\n    sigvcf_class = class_new(gensym(\"vcf~\"), (t_newmethod)sigvcf_new, 0,\n        sizeof(t_sigvcf), 0, A_DEFFLOAT, 0);\n    CLASS_MAINSIGNALIN(sigvcf_class, t_sigvcf, x_f);\n    class_addmethod(sigvcf_class, (t_method)sigvcf_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_addmethod(sigvcf_class, (t_method)sigvcf_ft1,\n        gensym(\"ft1\"), A_FLOAT, 0);\n}\n\n/* -------------------------- noise~ ------------------------------ */\nstatic t_class *noise_class;\n\ntypedef struct _noise\n{\n    t_object x_obj;\n    int x_val;\n} t_noise;\n\nstatic void *noise_new(void)\n{\n    t_noise *x = (t_noise *)pd_new(noise_class);\n        /* seed each instance differently.  Once in a blue moon two threads\n        could grab the same seed value.  We can live with that. */\n    static int init = 307;\n    x->x_val = (init *= 1319);\n    outlet_new(&x->x_obj, gensym(\"signal\"));\n    return (x);\n}\n\nstatic t_int *noise_perform(t_int *w)\n{\n    t_sample *out = (t_sample *)(w[1]);\n    int *vp = (int *)(w[2]);\n    int n = (int)(w[3]);\n    int val = *vp;\n    while (n--)\n    {\n        *out++ = ((t_sample)((val & 0x7fffffff) - 0x40000000)) *\n            (t_sample)(1.0 / 0x40000000);\n        val = val * 435898247 + 382842987;\n    }\n    *vp = val;\n    return (w+4);\n}\n\nstatic void noise_dsp(t_noise *x, t_signal **sp)\n{\n    dsp_add(noise_perform, 3, sp[0]->s_vec, &x->x_val, (t_int)sp[0]->s_n);\n}\n\nstatic void noise_float(t_noise *x, t_float f)\n{\n    /* set the seed */\n    x->x_val = (int)f;\n}\n\nstatic void noise_setup(void)\n{\n    noise_class = class_new(gensym(\"noise~\"), (t_newmethod)noise_new, 0,\n        sizeof(t_noise), 0, A_DEFFLOAT, 0);\n    class_addmethod(noise_class, (t_method)noise_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_addmethod(noise_class, (t_method)noise_float,\n        gensym(\"seed\"), A_FLOAT, 0);\n}\n\n\n/******************** tabosc4~ ***********************/\n\nstatic t_class *tabosc4_tilde_class;\n\ntypedef struct _tabosc4_tilde\n{\n    t_object x_obj;\n    t_float x_fnpoints;\n    t_float x_finvnpoints;\n    t_word *x_vec;\n    t_symbol *x_arrayname;\n    t_float x_f;\n    double x_phase;\n    t_float x_conv;\n} t_tabosc4_tilde;\n\nstatic void *tabosc4_tilde_new(t_symbol *s)\n{\n    t_tabosc4_tilde *x = (t_tabosc4_tilde *)pd_new(tabosc4_tilde_class);\n    x->x_arrayname = s;\n    x->x_vec = 0;\n    x->x_fnpoints = 512.;\n    x->x_finvnpoints = (1./512.);\n    outlet_new(&x->x_obj, gensym(\"signal\"));\n    inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym(\"ft1\"));\n    x->x_f = 0;\n    return (x);\n}\n\nstatic t_int *tabosc4_tilde_perform(t_int *w)\n{\n    t_tabosc4_tilde *x = (t_tabosc4_tilde *)(w[1]);\n    t_sample *in = (t_sample *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    int normhipart;\n    union tabfudge tf;\n    t_float fnpoints = x->x_fnpoints;\n    int mask = fnpoints - 1;\n    t_float conv = fnpoints * x->x_conv;\n    t_word *tab = x->x_vec, *addr;\n    double dphase = fnpoints * x->x_phase + UNITBIT32;\n\n    if (!tab) goto zero;\n    tf.tf_d = UNITBIT32;\n    normhipart = tf.tf_i[HIOFFSET];\n\n#if 1\n    while (n--)\n    {\n        t_sample frac,  a,  b,  c,  d, cminusb;\n        tf.tf_d = dphase;\n        dphase += *in++ * conv;\n        addr = tab + (tf.tf_i[HIOFFSET] & mask);\n        tf.tf_i[HIOFFSET] = normhipart;\n        frac = tf.tf_d - UNITBIT32;\n        a = addr[0].w_float;\n        b = addr[1].w_float;\n        c = addr[2].w_float;\n        d = addr[3].w_float;\n        cminusb = c-b;\n        *out++ = b + frac * (\n            cminusb - 0.1666667f * (1.-frac) * (\n                (d - a - 3.0f * cminusb) * frac + (d + 2.0f*a - 3.0f*b)\n            )\n        );\n    }\n#endif\n\n    tf.tf_d = UNITBIT32 * fnpoints;\n    normhipart = tf.tf_i[HIOFFSET];\n    tf.tf_d = dphase + (UNITBIT32 * fnpoints - UNITBIT32);\n    tf.tf_i[HIOFFSET] = normhipart;\n    x->x_phase = (tf.tf_d - UNITBIT32 * fnpoints)  * x->x_finvnpoints;\n    return (w+5);\n zero:\n    while (n--) *out++ = 0;\n\n    return (w+5);\n}\n\nstatic void tabosc4_tilde_set(t_tabosc4_tilde *x, t_symbol *s)\n{\n    t_garray *a;\n    int npoints, pointsinarray;\n\n    x->x_arrayname = s;\n    if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class)))\n    {\n        if (*s->s_name)\n            pd_error(x, \"tabosc4~: %s: no such array\", x->x_arrayname->s_name);\n        x->x_vec = 0;\n    }\n    else if (!garray_getfloatwords(a, &pointsinarray, &x->x_vec))\n    {\n        pd_error(x, \"%s: bad template for tabosc4~\", x->x_arrayname->s_name);\n        x->x_vec = 0;\n    }\n    else if ((npoints = pointsinarray - 3) != (1 << ilog2(pointsinarray - 3)))\n    {\n        pd_error(x, \"%s: number of points (%d) not a power of 2 plus three\",\n            x->x_arrayname->s_name, pointsinarray);\n        x->x_vec = 0;\n    }\n    else\n    {\n        x->x_fnpoints = npoints;\n        x->x_finvnpoints = 1./npoints;\n        garray_usedindsp(a);\n    }\n}\n\nstatic void tabosc4_tilde_ft1(t_tabosc4_tilde *x, t_float f)\n{\n    x->x_phase = f;\n}\n\nstatic void tabosc4_tilde_dsp(t_tabosc4_tilde *x, t_signal **sp)\n{\n    x->x_conv = 1. / sp[0]->s_sr;\n    tabosc4_tilde_set(x, x->x_arrayname);\n\n    dsp_add(tabosc4_tilde_perform, 4, x,\n        sp[0]->s_vec, sp[1]->s_vec, (t_int)sp[0]->s_n);\n}\n\nstatic void tabosc4_tilde_setup(void)\n{\n    tabosc4_tilde_class = class_new(gensym(\"tabosc4~\"),\n        (t_newmethod)tabosc4_tilde_new, 0,\n        sizeof(t_tabosc4_tilde), 0, A_DEFSYM, 0);\n    CLASS_MAINSIGNALIN(tabosc4_tilde_class, t_tabosc4_tilde, x_f);\n    class_addmethod(tabosc4_tilde_class, (t_method)tabosc4_tilde_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_addmethod(tabosc4_tilde_class, (t_method)tabosc4_tilde_set,\n        gensym(\"set\"), A_SYMBOL, 0);\n    class_addmethod(tabosc4_tilde_class, (t_method)tabosc4_tilde_ft1,\n        gensym(\"ft1\"), A_FLOAT, 0);\n}\n\n/* ----------------------- global setup routine ---------------- */\nvoid d_osc_setup(void)\n{\n    phasor_setup();\n    cos_setup();\n    osc_setup();\n    sigvcf_setup();\n    noise_setup();\n    tabosc4_tilde_setup();\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/d_resample.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n\n#include \"m_pd.h\"\n\n/* --------------------- up/down-sampling --------------------- */\nt_int *downsampling_perform_0(t_int *w)\n{\n  t_sample *in  = (t_sample *)(w[1]); /* original signal     */\n  t_sample *out = (t_sample *)(w[2]); /* downsampled signal  */\n  int down     = (int)(w[3]);       /* downsampling factor */\n  int parent   = (int)(w[4]);       /* original vectorsize */\n\n  int n=parent/down;\n\n  while(n--){\n    *out++=*in;\n    in+=down;\n  }\n\n  return (w+5);\n}\n\nt_int *upsampling_perform_0(t_int *w)\n{\n  t_sample *in  = (t_sample *)(w[1]); /* original signal     */\n  t_sample *out = (t_sample *)(w[2]); /* upsampled signal    */\n  int up       = (int)(w[3]);       /* upsampling factor   */\n  int parent   = (int)(w[4]);       /* original vectorsize */\n\n  int n=parent*up;\n  t_sample *dummy = out;\n\n  while(n--)*out++=0;\n\n  n = parent;\n  out = dummy;\n  while(n--){\n    *out=*in++;\n    out+=up;\n  }\n\n  return (w+5);\n}\n\nt_int *upsampling_perform_hold(t_int *w)\n{\n  t_sample *in  = (t_sample *)(w[1]); /* original signal     */\n  t_sample *out = (t_sample *)(w[2]); /* upsampled signal    */\n  int up       = (int)(w[3]);       /* upsampling factor   */\n  int parent   = (int)(w[4]);       /* original vectorsize */\n  int i=up;\n\n  int n=parent;\n  t_sample *dum_out = out;\n  t_sample *dum_in  = in;\n\n  while (i--) {\n    n = parent;\n    out = dum_out+i;\n    in  = dum_in;\n    while(n--){\n      *out=*in++;\n      out+=up;\n    }\n  }\n  return (w+5);\n}\n\nt_int *upsampling_perform_linear(t_int *w)\n{\n  t_resample *x= (t_resample *)(w[1]);\n  t_sample *in  = (t_sample *)(w[2]); /* original signal     */\n  t_sample *out = (t_sample *)(w[3]); /* upsampled signal    */\n  int up       = (int)(w[4]);       /* upsampling factor   */\n  int parent   = (int)(w[5]);       /* original vectorsize */\n  int length   = parent*up;\n  int n;\n  t_sample *fp;\n  t_sample a=*x->buffer, b=*in;\n\n\n  for (n=0; n<length; n++) {\n    t_sample findex = (t_sample)(n+1)/up;\n    int     index  = findex;\n    t_sample frac=findex - index;\n    if (frac==0.)frac=1.;\n    *out++ = frac * b + (1.-frac) * a;\n    fp = in+index;\n    b=*fp;\n    a=(index)?*(fp-1):a;\n  }\n\n  *x->buffer = a;\n  return (w+6);\n}\n\n/* ----------------------- public -------------------------------- */\n\n/* utils */\n\nvoid resample_init(t_resample *x)\n{\n  x->downsample=x->upsample=1;\n\n  x->s_n = x->coefsize = x->bufsize = 0;\n  x->s_vec = x->coeffs = x->buffer  = 0;\n}\n\nvoid resample_free(t_resample *x)\n{\n  if (x->s_n) t_freebytes(x->s_vec, x->s_n*sizeof(*x->s_vec));\n  if (x->coefsize) t_freebytes(x->coeffs, x->coefsize*sizeof(*x->coeffs));\n  if (x->bufsize) t_freebytes(x->buffer, x->bufsize*sizeof(*x->buffer));\n\n  x->s_n = x->coefsize = x->bufsize = 0;\n  x->s_vec = x->coeffs = x->buffer  = 0;\n}\n\n\n/* dsp-adding */\n\nvoid resample_dsp(t_resample *x,\n                  t_sample* in,  int insize,\n                  t_sample* out, int outsize,\n                  int method)\n{\n  if (insize == outsize){\n    bug(\"nothing to be done\");\n    return;\n  }\n\n  if (insize > outsize) { /* downsampling */\n    if (insize % outsize) {\n      pd_error(0, \"bad downsampling factor\");\n      return;\n    }\n    switch (method) {\n    default: /* always zero padding */\n      dsp_add(downsampling_perform_0, 4, in, out, (t_int)(insize/outsize), (t_int)insize);\n    }\n\n\n  } else { /* upsampling */\n    if (outsize % insize) {\n      pd_error(0, \"bad upsampling factor\");\n      return;\n    }\n    switch (method) {\n    case 1: /* sample and hold */\n      dsp_add(upsampling_perform_hold, 4, in, out, (t_int)(outsize/insize), (t_int)insize);\n      break;\n    case 2: /* linear interpolation */\n      if (x->bufsize != 1) {\n        t_freebytes(x->buffer, x->bufsize*sizeof(*x->buffer));\n        x->bufsize = 1;\n        x->buffer = t_getbytes(x->bufsize*sizeof(*x->buffer));\n      }\n      dsp_add(upsampling_perform_linear, 5, x, in, out, (t_int)(outsize/insize), (t_int)insize);\n      break;\n    default: /* zero padding */\n      dsp_add(upsampling_perform_0, 4, in, out, (t_int)(outsize/insize), (t_int)insize);\n    }\n  }\n}\n\nvoid resamplefrom_dsp(t_resample *x,\n                           t_sample *in,\n                           int insize, int outsize, int method)\n{\n  if (insize==outsize) {\n   t_freebytes(x->s_vec, x->s_n * sizeof(*x->s_vec));\n    x->s_n = 0;\n    x->s_vec = in;\n    return;\n  }\n\n  if (x->s_n != outsize) {\n    t_sample *buf=x->s_vec;\n    t_freebytes(buf, x->s_n * sizeof(*buf));\n    buf = (t_sample *)t_getbytes(outsize * sizeof(*buf));\n    x->s_vec = buf;\n    x->s_n   = outsize;\n  }\n\n  resample_dsp(x, in, insize, x->s_vec, x->s_n, method);\n  return;\n}\n\nvoid resampleto_dsp(t_resample *x,\n                         t_sample *out,\n                         int insize, int outsize, int method)\n{\n  if (insize==outsize) {\n    if (x->s_n)t_freebytes(x->s_vec, x->s_n * sizeof(*x->s_vec));\n    x->s_n = 0;\n    x->s_vec = out;\n    return;\n  }\n\n  if (x->s_n != insize) {\n    t_sample *buf=x->s_vec;\n    t_freebytes(buf, x->s_n * sizeof(*buf));\n    buf = (t_sample *)t_getbytes(insize * sizeof(*buf));\n    x->s_vec = buf;\n    x->s_n   = insize;\n  }\n\n  resample_dsp(x, x->s_vec, x->s_n, out, outsize, method);\n\n  return;\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/d_soundfile.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette. Updated 2019 Dan Wilcox.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* this file contains, first, a collection of soundfile access routines, a\nsort of soundfile library.  Second, the \"soundfiler\" object is defined which\nuses the routines to read or write soundfiles, synchronously, from garrays.\nThese operations are not to be done in \"real time\" as they may have to wait\nfor disk accesses (even the write routine.)  Finally, the realtime objects\nreadsf~ and writesf~ are defined which confine disk operations to a separate\nthread so that they can be used in real time.  The readsf~ and writesf~\nobjects use Posix-like threads. */\n\n#include \"d_soundfile.h\"\n#ifdef _WIN32\n#include <io.h>\n#endif\n#include <fcntl.h>\n#include <stdio.h>\n#include <pthread.h>\n\n/* Supported sample formats: LPCM (16 or 24 bit int) & 32 bit float */\n\n#define MAXSFCHANS 64\n\n/* GLIBC large file support */\n#ifdef _LARGEFILE64_SOURCE\n#define open open64\n#endif\n\n/* MSVC uses different naming for these */\n#ifdef _MSC_VER\n#define O_CREAT  _O_CREAT\n#define O_TRUNC  _O_TRUNC\n#define O_WRONLY _O_WRONLY\n#endif\n\n#define SCALE (1. / (1024. * 1024. * 1024. * 2.))\n\n    /* float sample conversion wrapper */\ntypedef union _floatuint\n{\n  float f;\n  uint32_t ui;\n} t_floatuint;\n\n/* ----- soundfile ----- */\n\nvoid soundfile_clear(t_soundfile *sf)\n{\n    memset(sf, 0, sizeof(t_soundfile));\n    sf->sf_fd = -1;\n    sf->sf_type = NULL;\n    sf->sf_bytelimit = SFMAXBYTES;\n}\n\nvoid soundfile_copy(t_soundfile *dst, const t_soundfile *src)\n{\n    memcpy((char *)dst, (char *)src, sizeof(t_soundfile));\n}\n\nint soundfile_needsbyteswap(const t_soundfile *sf)\n{\n    return sf->sf_bigendian != sys_isbigendian();\n}\n\nconst char* soundfile_strerror(int errnum)\n{\n    switch (errnum)\n    {\n        case SOUNDFILE_ERRUNKNOWN:\n            return \"unknown header format\";\n        case SOUNDFILE_ERRMALFORMED:\n            return \"bad header format\";\n        case SOUNDFILE_ERRVERSION:\n            return \"unsupported header format version\";\n        case SOUNDFILE_ERRSAMPLEFMT:\n            return \"unsupported sample format\";\n        default: /* C/POSIX error */\n            return strerror(errnum);\n    }\n}\n\n    /** output soundfile format info as a list */\nstatic void outlet_soundfileinfo(t_outlet *out, t_soundfile *sf)\n{\n    t_atom info_list[5];\n    SETFLOAT((t_atom *)info_list, (t_float)sf->sf_samplerate);\n    SETFLOAT((t_atom *)info_list+1,\n        (t_float)(sf->sf_headersize < 0 ? 0 : sf->sf_headersize));\n    SETFLOAT((t_atom *)info_list+2, (t_float)sf->sf_nchannels);\n    SETFLOAT((t_atom *)info_list+3, (t_float)sf->sf_bytespersample);\n    SETSYMBOL((t_atom *)info_list+4, gensym((sf->sf_bigendian ? \"b\" : \"l\")));\n    outlet_list(out, &s_list, 5, (t_atom *)info_list);\n}\n\n    /* post soundfile error, try to print type name */\nstatic void object_sferror(const void *x, const char *header,\n    const char *filename, int errnum, const t_soundfile *sf)\n{\n    if (sf && sf->sf_type)\n        pd_error(x, \"%s %s: %s: %s\", header, sf->sf_type->t_name, filename,\n            soundfile_strerror(errnum));\n    else\n        pd_error(x, \"%s: %s: %s\", header, filename, soundfile_strerror(errnum));\n}\n\n/* ----- soundfile type ----- */\n\n#define SFMAXTYPES 4\n\n/* should these globals be PERTHREAD? */\n\n    /** supported type implementations */\nstatic t_soundfile_type *sf_types[SFMAXTYPES] = {0};\n\n    /** number of types */\nstatic size_t sf_numtypes = 0;\n\n    /** min required header size, largest among the current types */\nstatic size_t sf_minheadersize = 0;\n\n    /** printable type argument list,\n       dash prepended and separated by spaces */\nstatic char sf_typeargs[MAXPDSTRING] = {0};\n\n    /* built-in type implementations */\nvoid soundfile_wave_setup(void);\nvoid soundfile_aiff_setup(void);\nvoid soundfile_caf_setup(void);\nvoid soundfile_next_setup(void);\n\n    /** set up built-in types */\nvoid soundfile_type_setup(void)\n{\n    soundfile_wave_setup(); /* default first */\n    soundfile_aiff_setup();\n    soundfile_caf_setup();\n    soundfile_next_setup();\n}\n\nint soundfile_addtype(const t_soundfile_type *type)\n{\n    int i;\n    if (sf_numtypes == SFMAXTYPES)\n    {\n        pd_error(0, \"soundfile: max number of type implementations reached\");\n        return 0;\n    }\n    sf_types[sf_numtypes] = (t_soundfile_type *)type;\n    sf_numtypes++;\n    if (type->t_minheadersize > sf_minheadersize)\n        sf_minheadersize = type->t_minheadersize;\n    strcat(sf_typeargs, (sf_numtypes > 1 ? \" -\" : \"-\"));\n    strcat(sf_typeargs, type->t_name);\n    return 1;\n}\n\n    /** return type list head pointer */\nstatic t_soundfile_type **soundfile_firsttype(void)\n{\n    return &sf_types[0];\n}\n\n    /** return next type list pointer or NULL if at the end */\nstatic t_soundfile_type **soundfile_nexttype(t_soundfile_type **t)\n{\n    return (t == &sf_types[sf_numtypes-1] ? NULL : ++t);\n}\n\n    /** find type by name, returns NULL if not found */\nstatic t_soundfile_type *soundfile_findtype(const char *name)\n{\n    t_soundfile_type **t = soundfile_firsttype();\n    while (t)\n    {\n        if (!strcmp(name, (*t)->t_name))\n            break;\n        t = soundfile_nexttype(t);\n    }\n    return (t ? *t : NULL);\n}\n\n/* ----- ASCII ----- */\n\n    /** compound ascii read/write args  */\ntypedef struct _asciiargs\n{\n    ssize_t aa_onsetframe;  /* start frame */\n    ssize_t aa_nframes;     /* nframes to read/write */\n    int aa_nchannels;       /* number of channels to read/write */\n    t_word **aa_vectors;    /* vectors to read into/write out of */\n    t_garray **aa_garrays;  /* read: arrays to resize & read into */\n    int aa_resize;          /* read: resize when reading? */\n    size_t aa_maxsize;      /* read: max size to read/resize to */\n    t_sample aa_normfactor; /* write: normalization factor */\n} t_asciiargs;\n\nstatic int ascii_hasextension(const char *filename, size_t size)\n{\n    int len = strnlen(filename, size);\n    if (len >= 5 && !strncmp(filename + (len - 4), \".txt\", 4))\n        return 1;\n    return 0;\n}\n\nstatic int ascii_addextension(char *filename, size_t size)\n{\n    size_t len = strnlen(filename, size);\n    if (len + 4 >= size)\n        return 0;\n    strcpy(filename + len, \".txt\");\n    return 1;\n}\n\n/* ----- read write ----- */\n\nssize_t fd_read(int fd, off_t offset, void *dst, size_t size)\n{\n    if (lseek(fd, offset, SEEK_SET) != offset)\n        return -1;\n    return read(fd, dst, size);\n}\n\nssize_t fd_write(int fd, off_t offset, const void *src, size_t size)\n{\n    if (lseek(fd, offset, SEEK_SET) != offset)\n        return -1;\n    return write(fd, src, size);\n}\n\n/* ----- byte swappers ----- */\n\nint sys_isbigendian(void)\n{\n    unsigned short s = 1;\n    unsigned char c = *(char *)(&s);\n    return (c == 0);\n}\n\nuint64_t swap8(uint64_t n, int doit)\n{\n    if (doit)\n        return (((n >> 56) & 0x00000000000000ffULL) |\n                ((n >> 40) & 0x000000000000ff00ULL) |\n                ((n >> 24) & 0x0000000000ff0000ULL) |\n                ((n >>  8) & 0x00000000ff000000ULL) |\n                ((n <<  8) & 0x000000ff00000000ULL) |\n                ((n << 24) & 0x0000ff0000000000ULL) |\n                ((n << 40) & 0x00ff000000000000ULL) |\n                ((n << 56) & 0xff00000000000000ULL));\n    return n;\n}\n\nint64_t swap8s(int64_t n, int doit)\n{\n    if (doit)\n    {\n        n = ((n <<  8) & 0xff00ff00ff00ff00ULL) |\n            ((n >>  8) & 0x00ff00ff00ff00ffULL);\n        n = ((n << 16) & 0xffff0000ffff0000ULL) |\n            ((n >> 16) & 0x0000ffff0000ffffULL );\n        return (n << 32) | ((n >> 32) & 0xffffffffULL);\n    }\n    return n;\n}\n\nuint32_t swap4(uint32_t n, int doit)\n{\n    if (doit)\n        return (((n & 0x0000ff) << 24) | ((n & 0x0000ff00) <<  8) |\n                ((n & 0xff0000) >>  8) | ((n & 0xff000000) >> 24));\n    return n;\n}\n\nint32_t swap4s(int32_t n, int doit)\n{\n    if (doit)\n    {\n        n = ((n << 8) & 0xff00ff00) | ((n >> 8) & 0xff00ff);\n        return (n << 16) | ((n >> 16) & 0xffff);\n    }\n    return n;\n}\n\nuint16_t swap2(uint16_t n, int doit)\n{\n    if (doit)\n        return (((n & 0x00ff) << 8) | ((n & 0xff00) >> 8));\n    return n;\n}\n\nvoid swapstring4(char *foo, int doit)\n{\n    if (doit)\n    {\n        char a = foo[0], b = foo[1], c = foo[2], d = foo[3];\n        foo[0] = d; foo[1] = c; foo[2] = b; foo[3] = a;\n    }\n}\n\nvoid swapstring8(char *foo, int doit)\n{\n    if (doit)\n    {\n        char a = foo[0], b = foo[1], c = foo[2], d = foo[3],\n        e = foo[4], f = foo[5], g = foo[6], h = foo[7];\n        foo[0] = h; foo[1] = g; foo[2] = f; foo[3] = e;\n        foo[4] = d; foo[5] = c; foo[6] = b; foo[7] = a;\n    }\n}\n\n/* ----------------------- soundfile access routines ----------------------- */\n\n    /** This routine opens a file, looks for a supported file format\n        header, seeks to end of it, and fills in the soundfile header info\n        values. Only 2- and 3-byte fixed-point samples and 4-byte floating point\n        samples are supported.  If sf->sf_headersize is nonzero, the caller\n        should supply the number of channels, endinanness, and bytes per sample;\n        the header is ignored.  If sf->sf_type is non-NULL, the given type\n        implementation is used. Otherwise, the routine tries to read the header\n        and fill in the properties. Fills sf struct on success, closes fd on\n        failure. */\nint open_soundfile_via_fd(int fd, t_soundfile *sf, size_t skipframes)\n{\n    off_t offset;\n    errno = 0;\n    if (sf->sf_headersize >= 0) /* header detection overridden */\n    {\n            /* interpret data size from file size */\n        ssize_t bytelimit = lseek(fd, 0, SEEK_END);\n        if (bytelimit < 0)\n            goto badheader;\n        if (bytelimit > SFMAXBYTES || bytelimit < 0)\n            bytelimit = SFMAXBYTES;\n        sf->sf_bytelimit = bytelimit;\n        sf->sf_fd = fd;\n    }\n    else\n    {\n        char buf[SFHDRBUFSIZE];\n        ssize_t bytesread = read(fd, buf, sf_minheadersize);\n\n        if (!sf->sf_type)\n        {\n                /* check header for type */\n            t_soundfile_type **t = soundfile_firsttype();\n            while (t)\n            {\n                if ((*t)->t_isheaderfn(buf, bytesread))\n                    break;\n                t = soundfile_nexttype(t);\n            }\n            if (!t) /* not recognized */\n            {\n                errno = SOUNDFILE_ERRUNKNOWN;\n                goto badheader;\n            }\n            sf->sf_type = *t;\n        }\n        else\n        {\n                /* check header using given type */\n            if (!sf->sf_type->t_isheaderfn(buf, bytesread))\n            {\n                errno = SOUNDFILE_ERRUNKNOWN;\n                goto badheader;\n            }\n        }\n        sf->sf_fd = fd;\n\n            /* rewind and read header */\n        if (lseek(sf->sf_fd, 0, SEEK_SET) < 0)\n            goto badheader;\n        if (!sf->sf_type->t_readheaderfn(sf))\n            goto badheader;\n    }\n\n        /* seek past header and any sample frames to skip */\n    offset = sf->sf_headersize + (skipframes * sf->sf_bytesperframe);\n    if (lseek(sf->sf_fd, offset, 0) < offset)\n        goto badheader;\n    sf->sf_bytelimit -= skipframes * sf->sf_bytesperframe;\n    if (sf->sf_bytelimit < 0)\n        sf->sf_bytelimit = 0;\n\n        /* copy sample format back to caller */\n    return fd;\n\nbadheader:\n        /* the header wasn't recognized.  We're threadable here so let's not\n        print out the error... */\n    if (!errno)\n        errno = SOUNDFILE_ERRMALFORMED;\n    sf->sf_fd = -1;\n    if (fd >= 0)\n        sys_close(fd);\n    return -1;\n}\n\n    /** open a soundfile, using open_via_path().  This is used by readsf~ in\n        a not-perfectly-threadsafe way.  LATER replace with a thread-hardened\n        version of open_soundfile_via_canvas().\n        returns number of frames in the soundfile */\nint open_soundfile_via_path(const char *dirname, const char *filename,\n    t_soundfile *sf, size_t skipframes)\n{\n    char buf[MAXPDSTRING], *dummy;\n    int fd, sf_fd;\n    fd = open_via_path(dirname, filename, \"\", buf, &dummy, MAXPDSTRING, 1);\n    if (fd < 0)\n        return -1;\n    sf_fd = open_soundfile_via_fd(fd, sf, skipframes);\n    return sf_fd;\n}\n\n    /** open a soundfile, using open_via_canvas().  This is used by readsf~ in\n        a not-perfectly-threadsafe way.  LATER replace with a thread-hardened\n        version of open_soundfile_via_canvas().\n        returns number of frames in the soundfile */\nint open_soundfile_via_canvas(t_canvas *canvas, const char *filename,\n    t_soundfile *sf, size_t skipframes)\n{\n    char buf[MAXPDSTRING], *dummy;\n    int fd, sf_fd;\n    fd = canvas_open(canvas, filename, \"\", buf, &dummy, MAXPDSTRING, 1);\n    if (fd < 0)\n        return -1;\n    sf_fd = open_soundfile_via_fd(fd, sf, skipframes);\n    return sf_fd;\n}\n\nstatic void soundfile_xferin_sample(const t_soundfile *sf, int nvecs,\n    t_sample **vecs, size_t framesread, unsigned char *buf, size_t nframes)\n{\n    int nchannels = (sf->sf_nchannels < nvecs ? sf->sf_nchannels : nvecs), i;\n    size_t j;\n    unsigned char *sp, *sp2;\n    t_sample *fp;\n    for (i = 0, sp = buf; i < nchannels; i++, sp += sf->sf_bytespersample)\n    {\n        if (sf->sf_bytespersample == 2)\n        {\n            if (sf->sf_bigendian)\n            {\n                for (j = 0, sp2 = sp, fp = vecs[i] + framesread;\n                    j < nframes; j++, sp2 += sf->sf_bytesperframe, fp++)\n                        *fp = SCALE * ((sp2[0] << 24) | (sp2[1] << 16));\n            }\n            else\n            {\n                for (j = 0, sp2 = sp, fp = vecs[i] + framesread;\n                    j < nframes; j++, sp2 += sf->sf_bytesperframe, fp++)\n                        *fp = SCALE * ((sp2[1] << 24) | (sp2[0] << 16));\n            }\n        }\n        else if (sf->sf_bytespersample == 3)\n        {\n            if (sf->sf_bigendian)\n            {\n                for (j = 0, sp2 = sp, fp = vecs[i] + framesread;\n                    j < nframes; j++, sp2 += sf->sf_bytesperframe, fp++)\n                        *fp = SCALE * ((sp2[0] << 24) | (sp2[1] << 16) |\n                                       (sp2[2] << 8));\n            }\n            else\n            {\n                for (j = 0, sp2 = sp, fp = vecs[i] + framesread;\n                    j < nframes; j++, sp2 += sf->sf_bytesperframe, fp++)\n                        *fp = SCALE * ((sp2[2] << 24) | (sp2[1] << 16) |\n                                       (sp2[0] << 8));\n            }\n        }\n        else if (sf->sf_bytespersample == 4)\n        {\n            t_floatuint alias;\n            if (sf->sf_bigendian)\n            {\n                for (j = 0, sp2 = sp, fp = vecs[i] + framesread;\n                    j < nframes; j++, sp2 += sf->sf_bytesperframe, fp++)\n                {\n                    alias.ui = ((sp2[0] << 24) | (sp2[1] << 16) |\n                                (sp2[2] << 8)  |  sp2[3]);\n                    *fp = (t_sample)alias.f;\n                }\n            }\n            else\n            {\n                for (j = 0, sp2 = sp, fp = vecs[i] + framesread;\n                    j < nframes; j++, sp2 += sf->sf_bytesperframe, fp++)\n                {\n                    alias.ui = ((sp2[3] << 24) | (sp2[2] << 16) |\n                                (sp2[1] << 8)  |  sp2[0]);\n                    *fp = (t_sample)alias.f;\n                }\n            }\n        }\n    }\n        /* zero out other outputs */\n    for (i = sf->sf_nchannels; i < nvecs; i++)\n        for (j = nframes, fp = vecs[i]; j--;)\n            *fp++ = 0;\n}\n\nstatic void soundfile_xferin_words(const t_soundfile *sf, int nvecs,\n    t_word **vecs, size_t framesread, unsigned char *buf, size_t nframes)\n{\n    unsigned char *sp, *sp2;\n    t_word *wp;\n    int nchannels = (sf->sf_nchannels < nvecs ? sf->sf_nchannels : nvecs), i;\n    size_t j;\n    for (i = 0, sp = buf; i < nchannels; i++, sp += sf->sf_bytespersample)\n    {\n        if (sf->sf_bytespersample == 2)\n        {\n            if (sf->sf_bigendian)\n            {\n                for (j = 0, sp2 = sp, wp = vecs[i] + framesread;\n                    j < nframes; j++, sp2 += sf->sf_bytesperframe, wp++)\n                        wp->w_float = SCALE * ((sp2[0] << 24) | (sp2[1] << 16));\n            }\n            else\n            {\n                for (j = 0, sp2 = sp, wp = vecs[i] + framesread;\n                    j < nframes; j++, sp2 += sf->sf_bytesperframe, wp++)\n                        wp->w_float = SCALE * ((sp2[1] << 24) | (sp2[0] << 16));\n            }\n        }\n        else if (sf->sf_bytespersample == 3)\n        {\n            if (sf->sf_bigendian)\n            {\n                for (j = 0, sp2 = sp, wp = vecs[i] + framesread;\n                    j < nframes; j++, sp2 += sf->sf_bytesperframe, wp++)\n                        wp->w_float = SCALE * ((sp2[0] << 24) | (sp2[1] << 16) |\n                                               (sp2[2] << 8));\n            }\n            else\n            {\n                for (j = 0, sp2 = sp, wp = vecs[i] + framesread;\n                    j < nframes; j++, sp2 += sf->sf_bytesperframe, wp++)\n                        wp->w_float = SCALE * ((sp2[2] << 24) | (sp2[1] << 16) |\n                                               (sp2[0] << 8));\n            }\n        }\n        else if (sf->sf_bytespersample == 4)\n        {\n            t_floatuint alias;\n            if (sf->sf_bigendian)\n            {\n                for (j = 0, sp2 = sp, wp = vecs[i] + framesread;\n                    j < nframes; j++, sp2 += sf->sf_bytesperframe, wp++)\n                {\n                    alias.ui = ((sp2[0] << 24) | (sp2[1] << 16) |\n                                (sp2[2] << 8)  |  sp2[3]);\n                    wp->w_float = (t_float)alias.f;\n                }\n            }\n            else\n            {\n                for (j = 0, sp2 = sp, wp = vecs[i] + framesread;\n                    j < nframes; j++, sp2 += sf->sf_bytesperframe, wp++)\n                {\n                    alias.ui = ((sp2[3] << 24) | (sp2[2] << 16) |\n                                (sp2[1] << 8)  |  sp2[0]);\n                    wp->w_float = (t_float)alias.f;\n                }\n            }\n        }\n    }\n        /* zero out other outputs */\n    for (i = sf->sf_nchannels; i < nvecs; i++)\n        for (j = nframes, wp = vecs[i]; j--;)\n            (wp++)->w_float = 0;\n}\n\n    /* soundfiler_write ...\n\n       usage: write [flags] filename table ...\n       flags:\n         -nframes <frames>\n         -skip <frames>\n         -bytes <bytespersample>\n         -rate / -r <samplerate>\n         -normalize\n         -wave\n         -aiff\n         -caf\n         -next / -nextstep\n         -ascii\n         -big\n         -little\n    */\n\n    /** parsed write arguments */\ntypedef struct _soundfiler_writeargs\n{\n    t_symbol *wa_filesym;             /* file path symbol */\n    t_soundfile_type *wa_type;        /* type implementation */\n    int wa_samplerate;                /* sample rate */\n    int wa_bytespersample;            /* number of bytes per sample */\n    int wa_bigendian;                 /* is sample data bigendian? */\n    size_t wa_nframes;                /* number of sample frames to write */\n    size_t wa_onsetframes;            /* sample frame onset when writing */\n    int wa_normalize;                 /* normalize samples? */\n    int wa_ascii;                     /* write ascii? */\n} t_soundfiler_writeargs;\n\n\n/* the routine which actually does the work should LATER also be called\nfrom garray_write16. */\n\n    /** Parse arguments for writing.  The \"obj\" argument is only for flagging\n        errors.  For streaming to a file the \"normalize\" and \"nframes\"\n        arguments shouldn't be set but the calling routine flags this. */\nstatic int soundfiler_parsewriteargs(void *obj, int *p_argc, t_atom **p_argv,\n    t_soundfiler_writeargs *wa)\n{\n    int argc = *p_argc;\n    t_atom *argv = *p_argv;\n    int samplerate = -1, bytespersample = 2, bigendian = 0, endianness = -1;\n    size_t nframes = SFMAXFRAMES, onsetframes = 0;\n    int normalize = 0, ascii = 0;\n    t_symbol *filesym;\n    t_soundfile_type *type = NULL;\n\n    while (argc > 0 && argv->a_type == A_SYMBOL &&\n        *argv->a_w.w_symbol->s_name == '-')\n    {\n        const char *flag = argv->a_w.w_symbol->s_name + 1;\n        if (!strcmp(flag, \"skip\"))\n        {\n            if (argc < 2 || argv[1].a_type != A_FLOAT ||\n                (argv[1].a_w.w_float) < 0)\n                    return -1;\n            onsetframes = argv[1].a_w.w_float;\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(flag, \"nframes\"))\n        {\n            if (argc < 2 || argv[1].a_type != A_FLOAT ||\n                argv[1].a_w.w_float < 0)\n                    return -1;\n            nframes = argv[1].a_w.w_float;\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(flag, \"bytes\"))\n        {\n            if (argc < 2 || argv[1].a_type != A_FLOAT ||\n                ((bytespersample = argv[1].a_w.w_float) < 2) ||\n                    bytespersample > 4)\n                        return -1;\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(flag, \"normalize\"))\n        {\n            normalize = 1;\n            argc -= 1; argv += 1;\n        }\n        else if (!strcmp(flag, \"big\"))\n        {\n            endianness = 1;\n            argc -= 1; argv += 1;\n        }\n        else if (!strcmp(flag, \"little\"))\n        {\n            endianness = 0;\n            argc -= 1; argv += 1;\n        }\n        else if (!strcmp(flag, \"rate\") || !strcmp(flag, \"r\"))\n        {\n            if (argc < 2 || argv[1].a_type != A_FLOAT ||\n                ((samplerate = argv[1].a_w.w_float) <= 0))\n                    return -1;\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(flag, \"ascii\"))\n        {\n            ascii = 1;\n            argc -= 1; argv += 1;\n        }\n        else if (!strcmp(flag, \"nextstep\"))\n        {\n                /* handle old \"-nextstep\" alias */\n            type = soundfile_findtype(\"next\");\n            argc -= 1; argv += 1;\n        }\n        else\n        {\n                /* check for type by name */\n            if (!(type = soundfile_findtype(flag)))\n                return -1; /* unknown flag */\n            ascii = 0; /* replaced */\n            argc -= 1; argv += 1;\n        }\n    }\n    if (!argc || argv->a_type != A_SYMBOL)\n        return -1;\n    filesym = argv->a_w.w_symbol;\n\n        /* deduce from filename extension? */\n    if (!type)\n    {\n        t_soundfile_type **t = soundfile_firsttype();\n        while (t)\n        {\n            if ((*t)->t_hasextensionfn(filesym->s_name, MAXPDSTRING))\n                break;\n            t = soundfile_nexttype(t);\n        }\n        if (!t)\n        {\n            if (!ascii)\n                ascii = ascii_hasextension(filesym->s_name, MAXPDSTRING);\n            t = soundfile_firsttype(); /* default if unknown */\n        }\n        type = *t;\n    }\n\n        /* check requested endianness */\n    bigendian = type->t_endiannessfn(endianness);\n    if (endianness != -1 && endianness != bigendian)\n    {\n        post(\"%s: forced to %s endian\", type->t_name,\n            (bigendian ? \"big\" : \"little\"));\n    }\n\n        /* return to caller */\n    argc--; argv++;\n    *p_argc = argc;\n    *p_argv = argv;\n    wa->wa_filesym = filesym;\n    wa->wa_type = type;\n    wa->wa_samplerate = samplerate;\n    wa->wa_bytespersample = bytespersample;\n    wa->wa_bigendian = bigendian;\n    wa->wa_nframes = nframes;\n    wa->wa_onsetframes = onsetframes;\n    wa->wa_normalize = normalize;\n    wa->wa_ascii = ascii;\n    return 0;\n}\n\n    /** sets sf fd & headerisze on success and returns fd or -1 on failure */\nstatic int create_soundfile(t_canvas *canvas, const char *filename,\n    t_soundfile *sf, size_t nframes)\n{\n    char filenamebuf[MAXPDSTRING], pathbuf[MAXPDSTRING];\n    ssize_t headersize = -1;\n    int fd;\n\n        /* create file */\n    strncpy(filenamebuf, filename, MAXPDSTRING);\n    if (!sf->sf_type->t_hasextensionfn(filenamebuf, MAXPDSTRING-10))\n        if (!sf->sf_type->t_addextensionfn(filenamebuf, MAXPDSTRING-10))\n            return -1;\n    filenamebuf[MAXPDSTRING-10] = 0; /* FIXME: what is the 10 for? */\n    canvas_makefilename(canvas, filenamebuf, pathbuf, MAXPDSTRING);\n    if ((fd = sys_open(pathbuf, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0)\n        return -1;\n    sf->sf_fd = fd;\n\n        /* write header */\n    headersize = sf->sf_type->t_writeheaderfn(sf, nframes);\n    if (headersize < 0)\n        goto badcreate;\n    sf->sf_headersize = headersize;\n    return fd;\n\nbadcreate:\n    sf->sf_fd = -1;\n    if (fd >= 0)\n        sys_close(fd);\n    return -1;\n}\n\nstatic void soundfile_finishwrite(void *obj, const char *filename,\n    t_soundfile *sf, size_t nframes, size_t frameswritten)\n{\n    if (frameswritten >= nframes) return;\n    if (nframes < SFMAXFRAMES)\n        pd_error(obj, \"soundfiler write: %ld out of %ld frames written\",\n            (long)frameswritten, (long)nframes);\n    if (sf->sf_type->t_updateheaderfn(sf, frameswritten))\n        return;\n    object_sferror(obj, \"soundfiler write\", filename, errno, sf);\n}\n\nstatic void soundfile_xferout_sample(const t_soundfile *sf,\n    t_sample **vecs, unsigned char *buf, size_t nframes, size_t onsetframes,\n    t_sample normalfactor)\n{\n    int i;\n    size_t j;\n    unsigned char *sp, *sp2;\n    t_sample *fp;\n    for (i = 0, sp = buf; i < sf->sf_nchannels; i++,\n        sp += sf->sf_bytespersample)\n    {\n        if (sf->sf_bytespersample == 2)\n        {\n            t_sample ff = normalfactor * 32768.;\n            if (sf->sf_bigendian)\n            {\n                for (j = 0, sp2 = sp, fp = vecs[i] + onsetframes;\n                    j < nframes; j++, sp2 += sf->sf_bytesperframe, fp++)\n                {\n                    int xx = 32768. + (*fp * ff);\n                    xx -= 32768;\n                    if (xx < -32767)\n                        xx = -32767;\n                    if (xx > 32767)\n                        xx = 32767;\n                    sp2[0] = (xx >> 8);\n                    sp2[1] = xx;\n                }\n            }\n            else\n            {\n                for (j = 0, sp2 = sp, fp = vecs[i] + onsetframes;\n                    j < nframes; j++, sp2 += sf->sf_bytesperframe, fp++)\n                {\n                    int xx = 32768. + (*fp * ff);\n                    xx -= 32768;\n                    if (xx < -32767)\n                        xx = -32767;\n                    if (xx > 32767)\n                        xx = 32767;\n                    sp2[1] = (xx >> 8);\n                    sp2[0] = xx;\n                }\n            }\n        }\n        else if (sf->sf_bytespersample == 3)\n        {\n            t_sample ff = normalfactor * 8388608.;\n            if (sf->sf_bigendian)\n            {\n                for (j = 0, sp2 = sp, fp = vecs[i] + onsetframes;\n                    j < nframes; j++, sp2 += sf->sf_bytesperframe, fp++)\n                {\n                    int xx = 8388608. + (*fp * ff);\n                    xx -= 8388608;\n                    if (xx < -8388607)\n                        xx = -8388607;\n                    if (xx > 8388607)\n                        xx = 8388607;\n                    sp2[0] = (xx >> 16);\n                    sp2[1] = (xx >> 8);\n                    sp2[2] = xx;\n                }\n            }\n            else\n            {\n                for (j = 0, sp2 = sp, fp = vecs[i] + onsetframes;\n                    j < nframes; j++, sp2 += sf->sf_bytesperframe, fp++)\n                {\n                    int xx = 8388608. + (*fp * ff);\n                    xx -= 8388608;\n                    if (xx < -8388607)\n                        xx = -8388607;\n                    if (xx > 8388607)\n                        xx = 8388607;\n                    sp2[2] = (xx >> 16);\n                    sp2[1] = (xx >> 8);\n                    sp2[0] = xx;\n                }\n            }\n        }\n        else if (sf->sf_bytespersample == 4)\n        {\n            t_floatuint f2;\n            if (sf->sf_bigendian)\n            {\n                for (j = 0, sp2 = sp, fp = vecs[i] + onsetframes;\n                    j < nframes; j++, sp2 += sf->sf_bytesperframe, fp++)\n                {\n                    f2.f = *fp * normalfactor;\n                    sp2[0] = (f2.ui >> 24); sp2[1] = (f2.ui >> 16);\n                    sp2[2] = (f2.ui >> 8);  sp2[3] = f2.ui;\n                }\n            }\n            else\n            {\n                for (j = 0, sp2 = sp, fp = vecs[i] + onsetframes;\n                    j < nframes; j++, sp2 += sf->sf_bytesperframe, fp++)\n                {\n                    f2.f = *fp * normalfactor;\n                    sp2[3] = (f2.ui >> 24); sp2[2] = (f2.ui >> 16);\n                    sp2[1] = (f2.ui >> 8);  sp2[0] = f2.ui;\n                }\n            }\n        }\n    }\n}\n\nstatic void soundfile_xferout_words(const t_soundfile *sf, t_word **vecs,\n    unsigned char *buf, size_t nframes, size_t onsetframes,\n    t_sample normalfactor)\n{\n    int i;\n    size_t j;\n    unsigned char *sp, *sp2;\n    t_word *wp;\n    for (i = 0, sp = buf; i < sf->sf_nchannels;\n         i++, sp += sf->sf_bytespersample)\n    {\n        if (sf->sf_bytespersample == 2)\n        {\n            t_sample ff = normalfactor * 32768.;\n            if (sf->sf_bigendian)\n            {\n                for (j = 0, sp2 = sp, wp = vecs[i] + onsetframes;\n                    j < nframes; j++, sp2 += sf->sf_bytesperframe, wp++)\n                {\n                    int xx = 32768. + (wp->w_float * ff);\n                    xx -= 32768;\n                    if (xx < -32767)\n                        xx = -32767;\n                    if (xx > 32767)\n                        xx = 32767;\n                    sp2[0] = (xx >> 8);\n                    sp2[1] = xx;\n                }\n            }\n            else\n            {\n                for (j = 0, sp2 = sp, wp = vecs[i] + onsetframes;\n                    j < nframes; j++, sp2 += sf->sf_bytesperframe, wp++)\n                {\n                    int xx = 32768. + (wp->w_float * ff);\n                    xx -= 32768;\n                    if (xx < -32767)\n                        xx = -32767;\n                    if (xx > 32767)\n                        xx = 32767;\n                    sp2[1] = (xx >> 8);\n                    sp2[0] = xx;\n                }\n            }\n        }\n        else if (sf->sf_bytespersample == 3)\n        {\n            t_sample ff = normalfactor * 8388608.;\n            if (sf->sf_bigendian)\n            {\n                for (j = 0, sp2 = sp, wp = vecs[i] + onsetframes;\n                    j < nframes; j++, sp2 += sf->sf_bytesperframe, wp++)\n                {\n                    int xx = 8388608. + (wp->w_float * ff);\n                    xx -= 8388608;\n                    if (xx < -8388607)\n                        xx = -8388607;\n                    if (xx > 8388607)\n                        xx = 8388607;\n                    sp2[0] = (xx >> 16);\n                    sp2[1] = (xx >> 8);\n                    sp2[2] = xx;\n                }\n            }\n            else\n            {\n                for (j = 0, sp2 = sp, wp = vecs[i] + onsetframes;\n                    j < nframes; j++, sp2 += sf->sf_bytesperframe, wp++)\n                {\n                    int xx = 8388608. + (wp->w_float * ff);\n                    xx -= 8388608;\n                    if (xx < -8388607)\n                        xx = -8388607;\n                    if (xx > 8388607)\n                        xx = 8388607;\n                    sp2[2] = (xx >> 16);\n                    sp2[1] = (xx >> 8);\n                    sp2[0] = xx;\n                }\n            }\n        }\n        else if (sf->sf_bytespersample == 4)\n        {\n            t_floatuint f2;\n            if (sf->sf_bigendian)\n            {\n                for (j = 0, sp2 = sp, wp = vecs[i] + onsetframes;\n                    j < nframes; j++, sp2 += sf->sf_bytesperframe, wp++)\n                {\n                    f2.f = wp->w_float * normalfactor;\n                    sp2[0] = (f2.ui >> 24); sp2[1] = (f2.ui >> 16);\n                    sp2[2] = (f2.ui >> 8);  sp2[3] = f2.ui;\n                }\n            }\n            else\n            {\n                for (j = 0, sp2 = sp, wp = vecs[i] + onsetframes;\n                    j < nframes; j++, sp2 += sf->sf_bytesperframe, wp++)\n                {\n                    f2.f = wp->w_float * normalfactor;\n                    sp2[3] = (f2.ui >> 24); sp2[2] = (f2.ui >> 16);\n                    sp2[1] = (f2.ui >> 8);  sp2[0] = f2.ui;\n                }\n            }\n        }\n    }\n}\n\n/* ----- soundfiler - reads and writes soundfiles to/from \"garrays\" ----- */\n\n#define SAMPBUFSIZE 1024\n\nstatic t_class *soundfiler_class;\n\ntypedef struct _soundfiler\n{\n    t_object x_obj;\n    t_outlet *x_out2;\n    t_canvas *x_canvas;\n} t_soundfiler;\n\nstatic t_soundfiler *soundfiler_new(void)\n{\n    t_soundfiler *x = (t_soundfiler *)pd_new(soundfiler_class);\n    x->x_canvas = canvas_getcurrent();\n    outlet_new(&x->x_obj, &s_float);\n    x->x_out2 = outlet_new(&x->x_obj, &s_float);\n    return x;\n}\n\nstatic int soundfiler_readascii(t_soundfiler *x, const char *filename,\n    t_asciiargs *a)\n{\n    t_binbuf *b = binbuf_new();\n    int i, j, vecsize;\n    ssize_t framesinfile, nframes = a->aa_nframes;\n    t_atom *atoms, *ap;\n    if (binbuf_read_via_canvas(b, filename, x->x_canvas, 0))\n    {\n        nframes = 0;\n        goto done;\n    }\n    atoms = binbuf_getvec(b);\n    framesinfile = binbuf_getnatom(b) / a->aa_nchannels;\n#ifdef DEBUG_SOUNDFILE\n    post(\"ascii: read 1 natoms %d frames %d onset %d channels %d\",\n        binbuf_getnatom(b), nframes, a->aa_onsetframe, a->aa_nchannels);\n#endif\n    if (framesinfile < 1)\n    {\n        pd_error(x, \"soundfiler read: %s: empty or very short ascii file\",\n            filename);\n        nframes = 0;\n        goto done;\n    }\n    if (a->aa_resize)\n    {\n        if ((size_t)framesinfile > a->aa_maxsize)\n        {\n            pd_error(x, \"soundfiler read: truncated to %ld elements\",\n                (long)a->aa_maxsize);\n            framesinfile = a->aa_maxsize;\n        }\n        nframes = framesinfile - a->aa_onsetframe;\n        for (i = 0; i < a->aa_nchannels; i++)\n        {\n            garray_resize_long(a->aa_garrays[i], nframes);\n            garray_setsaveit(a->aa_garrays[i], 0);\n            if (!garray_getfloatwords(a->aa_garrays[i], &vecsize,\n                &a->aa_vectors[i]) || (vecsize != nframes))\n            {\n                pd_error(x, \"soundfiler read: resize failed\");\n                nframes = 0;\n                goto done;\n            }\n        }\n    }\n    else if (nframes > framesinfile)\n        nframes = framesinfile - a->aa_onsetframe;\n#ifdef DEBUG_SOUNDFILE\n    post(\"ascii: read 2 frames %d\", nframes);\n#endif\n    if (a->aa_onsetframe > 0)\n        atoms += a->aa_onsetframe * a->aa_nchannels;\n    for (j = 0, ap = atoms; j < nframes; j++)\n        for (i = 0; i < a->aa_nchannels; i++)\n            a->aa_vectors[i][j].w_float = atom_getfloat(ap++);\n        /* zero out remaining elements of vectors */\n    for (i = 0; i < a->aa_nchannels; i++)\n    {\n        if (garray_getfloatwords(a->aa_garrays[i], &vecsize, &a->aa_vectors[i]))\n            for (j = nframes; j < vecsize; j++)\n                a->aa_vectors[i][j].w_float = 0;\n    }\n    for (i = 0; i < a->aa_nchannels; i++)\n        garray_redraw(a->aa_garrays[i]);\n#ifdef DEBUG_SOUNDFILE\n    post(\"ascii: read 3\");\n#endif\ndone:\n    binbuf_free(b);\n    return nframes;\n}\n\n    /* soundfiler_read ...\n\n       usage: read [flags] filename [tablename] ...\n       flags:\n           -skip <frames> ... frames to skip in file\n           -raw <headersize channels bytespersample endian>\n           -resize\n           -maxsize <maxsize>\n           -wave\n           -aiff\n           -caf\n           -next\n           -ascii\n    */\n\nstatic void soundfiler_read(t_soundfiler *x, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    t_soundfile sf = {0};\n    int fd = -1, resize = 0, ascii = 0, raw = 0, i;\n    size_t skipframes = 0, finalsize = 0, maxsize = SFMAXFRAMES,\n           framesread = 0, bufframes, j;\n    ssize_t nframes, framesinfile;\n    char endianness;\n    const char *filename;\n    t_garray *garrays[MAXSFCHANS];\n    t_word *vecs[MAXSFCHANS];\n    char sampbuf[SAMPBUFSIZE];\n\n    soundfile_clear(&sf);\n    sf.sf_headersize = -1;\n    while (argc > 0 && argv->a_type == A_SYMBOL &&\n        *argv->a_w.w_symbol->s_name == '-')\n    {\n        const char *flag = argv->a_w.w_symbol->s_name + 1;\n        if (!strcmp(flag, \"skip\"))\n        {\n            if (argc < 2 || argv[1].a_type != A_FLOAT ||\n                (argv[1].a_w.w_float) < 0)\n                    goto usage;\n            skipframes = argv[1].a_w.w_float;\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(flag, \"ascii\"))\n        {\n            if (sf.sf_headersize >= 0)\n                post(\"'-ascii' overridden by '-raw'\");\n            ascii = 1;\n            argc--; argv++;\n        }\n        else if (!strcmp(flag, \"raw\"))\n        {\n            if (ascii)\n                post(\"'-ascii' overridden by '-raw'\");\n            else if (sf.sf_type)\n                post(\"'-%s' overridden by '-raw'\", sf.sf_type->t_name);\n            if (argc < 5 ||\n                argv[1].a_type != A_FLOAT ||\n                ((sf.sf_headersize = argv[1].a_w.w_float) < 0) ||\n                argv[2].a_type != A_FLOAT ||\n                ((sf.sf_nchannels = argv[2].a_w.w_float) < 1) ||\n                (sf.sf_nchannels > MAXSFCHANS) ||\n                argv[3].a_type != A_FLOAT ||\n                ((sf.sf_bytespersample = argv[3].a_w.w_float) < 2) ||\n                    (sf.sf_bytespersample > 4) ||\n                argv[4].a_type != A_SYMBOL ||\n                    ((endianness = argv[4].a_w.w_symbol->s_name[0]) != 'b'\n                    && endianness != 'l' && endianness != 'n'))\n                        goto usage;\n            if (endianness == 'b')\n                sf.sf_bigendian = 1;\n            else if (endianness == 'l')\n                sf.sf_bigendian = 0;\n            else\n                sf.sf_bigendian = sys_isbigendian();\n            sf.sf_samplerate = sys_getsr();\n            sf.sf_bytesperframe = sf.sf_nchannels * sf.sf_bytespersample;\n            raw = 1;\n            argc -= 5; argv += 5;\n        }\n        else if (!strcmp(flag, \"resize\"))\n        {\n            resize = 1;\n            argc -= 1; argv += 1;\n        }\n        else if (!strcmp(flag, \"maxsize\"))\n        {\n            ssize_t tmp;\n            if (argc < 2 || argv[1].a_type != A_FLOAT ||\n                ((tmp = (argv[1].a_w.w_float > SFMAXFRAMES ?\n                SFMAXFRAMES : argv[1].a_w.w_float)) < 0))\n                    goto usage;\n            maxsize = (size_t)tmp;\n            resize = 1;     /* maxsize implies resize */\n            argc -= 2; argv += 2;\n        }\n        else\n        {\n                /* check for type by name */\n            if (!(sf.sf_type = soundfile_findtype(flag)))\n                goto usage; /* unknown flag */\n            ascii = 0; /* replaced */\n            if (sf.sf_headersize >= 0)\n                post(\"'-%s' overridden by '-raw'\", sf.sf_type->t_name);\n            argc -= 1; argv += 1;\n        }\n    }\n    if (sf.sf_headersize >= 0)\n    {\n        ascii = 0;\n        sf.sf_type = NULL;\n    }\n    if (argc < 1 ||                           /* no filename or tables */\n        argc > MAXSFCHANS + 1 ||              /* too many tables */\n        argv[0].a_type != A_SYMBOL)           /* bad filename */\n            goto usage;\n    filename = argv[0].a_w.w_symbol->s_name;\n    argc--; argv++;\n\n        /* check for implicit ascii */\n    if (!ascii && !sf.sf_type && sf.sf_headersize < 0)\n        ascii = ascii_hasextension(filename, MAXPDSTRING);\n\n    for (i = 0; i < argc; i++)\n    {\n        int vecsize;\n        if (argv[i].a_type != A_SYMBOL)\n            goto usage;\n        if (!(garrays[i] =\n            (t_garray *)pd_findbyclass(argv[i].a_w.w_symbol, garray_class)))\n        {\n            pd_error(x, \"soundfiler read: %s: no such table\",\n                argv[i].a_w.w_symbol->s_name);\n            goto done;\n        }\n        else if (!garray_getfloatwords(garrays[i], &vecsize,\n                &vecs[i]))\n            pd_error(x, \"soundfiler read: %s: bad template for tabwrite\",\n                argv[i].a_w.w_symbol->s_name);\n        if (finalsize && finalsize != (size_t)vecsize && !resize)\n        {\n            post(\"arrays have different lengths, resizing...\");\n            resize = 1;\n        }\n        finalsize = vecsize;\n    }\n    if (ascii)\n    {\n        t_asciiargs a =\n            {skipframes, finalsize, argc, vecs, garrays, resize, maxsize, 0};\n        if (!argc)\n        {\n            pd_error(x, \"soundfiler read: \"\n                        \"'-ascii' requires at least one table\");\n            goto done;\n        }\n        if ((framesread = soundfiler_readascii(x, filename, &a)) == 0)\n            goto done;\n            /* fill in for info outlet */\n        sf.sf_samplerate = sys_getsr();\n        sf.sf_headersize = 0;\n        sf.sf_nchannels = argc;\n        sf.sf_bytespersample = 4;\n        sf.sf_bigendian = sys_isbigendian();\n        goto done;\n    }\n\n    fd = open_soundfile_via_canvas(x->x_canvas, filename, &sf, skipframes);\n    if (fd < 0)\n    {\n        object_sferror(x, \"soundfiler read\", filename, errno, &sf);\n        goto done;\n    }\n    framesinfile = sf.sf_bytelimit / sf.sf_bytesperframe;\n\n    if (resize)\n    {\n            /* figure out what to resize to using header info */\n        if ((size_t)framesinfile > maxsize)\n        {\n            pd_error(x, \"soundfiler read: truncated to %ld elements\",\n                (long)maxsize);\n            framesinfile = maxsize;\n        }\n        finalsize = framesinfile;\n        for (i = 0; i < argc; i++)\n        {\n            int vecsize;\n            garray_resize_long(garrays[i], finalsize);\n                /* for sanity's sake let's clear the save-in-patch flag here */\n            garray_setsaveit(garrays[i], 0);\n            if (!garray_getfloatwords(garrays[i], &vecsize, &vecs[i])\n                /* if the resize failed, garray_resize reported the error */\n                || (vecsize != framesinfile))\n            {\n                pd_error(x, \"soundfiler read: resize failed\");\n                goto done;\n            }\n        }\n    }\n\n    if (!finalsize) finalsize = SFMAXFRAMES;\n    if (framesinfile >= 0 && finalsize > (size_t)framesinfile)\n        finalsize = framesinfile;\n\n        /* no tablenames, try to use header info instead of reading */\n    if (argc == 0 &&\n        !(raw ||                     /* read if raw */\n          finalsize == SFMAXFRAMES)) /* read if unknown size */\n    {\n        framesread = finalsize;\n#ifdef DEBUG_SOUNDFILE\n    post(\"skipped reading frames\");\n#endif\n        goto done;\n    }\n\n        /* read */\n#ifdef DEBUG_SOUNDFILE\n    post(\"reading frames\");\n#endif\n    bufframes = SAMPBUFSIZE / sf.sf_bytesperframe;\n    for (framesread = 0; framesread < finalsize;)\n    {\n        size_t thisread = finalsize - framesread;\n        thisread = (thisread > bufframes ? bufframes : thisread);\n        nframes = read(sf.sf_fd, sampbuf,\n            thisread * sf.sf_bytesperframe) / sf.sf_bytesperframe;\n        if (nframes <= 0) break;\n        soundfile_xferin_words(&sf, argc, vecs, framesread,\n            (unsigned char *)sampbuf, nframes);\n        framesread += nframes;\n    }\n        /* warn if a file's bad size field is gobbling memory */\n    if (resize && framesread < (size_t)finalsize)\n    {\n        post(\"warning: soundfile %s header promised \\\n%ld points but file was truncated to %ld\",\n            filename, (long)finalsize, (long)framesread);\n    }\n        /* zero out remaining elements of vectors */\n    for (i = 0; i < argc; i++)\n    {\n        int vecsize;\n        if (garray_getfloatwords(garrays[i], &vecsize, &vecs[i]))\n            for (j = framesread; j < (size_t)vecsize; j++)\n                vecs[i][j].w_float = 0;\n    }\n        /* zero out vectors in excess of number of channels */\n    for (i = sf.sf_nchannels; i < argc; i++)\n    {\n        int vecsize;\n        t_word *foo;\n        if (garray_getfloatwords(garrays[i], &vecsize, &foo))\n            for (j = 0; j < (size_t)vecsize; j++)\n                foo[j].w_float = 0;\n    }\n        /* do all graphics updates */\n    for (i = 0; i < argc; i++)\n        garray_redraw(garrays[i]);\n    goto done;\nusage:\n    pd_error(x, \"usage: read [flags] filename [tablename]...\");\n    post(\"flags: -skip <n> -resize -maxsize <n> %s -ascii ...\", sf_typeargs);\n    post(\"-raw <headerbytes> <channels> <bytespersample> \"\n         \"<endian (b, l, or n)>\");\ndone:\n    sf.sf_fd = -1;\n    if (fd >= 0)\n        sys_close(fd);\n    outlet_soundfileinfo(x->x_out2, &sf);\n    outlet_float(x->x_obj.ob_outlet, (t_float)framesread);\n}\n\n    /** write to an ascii text file, channels (nvecs) are interleaved,\n        assumes all vectors are at least nframes in length\n        adapted from garray_savecontentsto()\n        returns frames written on success or 0 on error */\nint soundfiler_writeascii(t_soundfiler *x, const char *filename, t_asciiargs *a)\n{\n    char path[MAXPDSTRING];\n    t_binbuf *b = binbuf_new();\n    int i, j, frameswritten = 0, ret = 1;\n#ifdef DEBUG_SOUNDFILE\n    post(\"ascii write: frames %d onset %d channels %d\",\n        a->aa_nframes, a->aa_onsetframe, a->aa_nchannels);\n#endif\n    canvas_makefilename(x->x_canvas, filename, path, MAXPDSTRING);\n    if (a->aa_nframes > 200000)\n        post(\"warning: writing %d table points to ascii file!\");\n    for (i = a->aa_onsetframe; frameswritten < a->aa_nframes; ++i)\n    {\n        for (j = 0; j < a->aa_nchannels; ++j)\n            binbuf_addv(b, \"f\", a->aa_vectors[j][i].w_float * a->aa_normfactor);\n        frameswritten++;\n    }\n    binbuf_addv(b, \";\");\n    ret = binbuf_write(b, path, \"\", 1); /* convert semis to cr */\n    binbuf_free(b);\n    return (ret == 0 ? frameswritten : 0);\n}\n\n    /** this is broken out from soundfiler_write below so garray_write can\n        call it too... not done yet though. */\nsize_t soundfiler_dowrite(void *obj, t_canvas *canvas,\n    int argc, t_atom *argv, t_soundfile *sf)\n{\n    t_soundfiler_writeargs wa = {0};\n    int fd = -1, i;\n    size_t bufframes, frameswritten = 0, j;\n    t_garray *garrays[MAXSFCHANS];\n    t_word *vectors[MAXSFCHANS];\n    char sampbuf[SAMPBUFSIZE];\n    t_sample normfactor = 1, biggest = 0;\n\n    soundfile_clear(sf);\n    if (soundfiler_parsewriteargs(obj, &argc, &argv, &wa))\n        goto usage;\n    sf->sf_type = (wa.wa_ascii ? NULL : wa.wa_type);\n    sf->sf_nchannels = argc;\n    sf->sf_samplerate = wa.wa_samplerate;\n    sf->sf_bytespersample = wa.wa_bytespersample;\n    sf->sf_bigendian = wa.wa_bigendian;\n    sf->sf_bytesperframe = argc * wa.wa_bytespersample;\n    if (sf->sf_nchannels < 1 || sf->sf_nchannels > MAXSFCHANS)\n        goto usage;\n    if (sf->sf_samplerate <= 0)\n        sf->sf_samplerate = sys_getsr();\n    for (i = 0; i < sf->sf_nchannels; i++)\n    {\n        int vecsize;\n        if (argv[i].a_type != A_SYMBOL)\n            goto usage;\n        if (!(garrays[i] =\n            (t_garray *)pd_findbyclass(argv[i].a_w.w_symbol, garray_class)))\n        {\n            pd_error(obj, \"soundfiler write: %s: no such table\",\n                argv[i].a_w.w_symbol->s_name);\n            goto fail;\n        }\n        else if (!garray_getfloatwords(garrays[i], &vecsize, &vectors[i]))\n            pd_error(obj, \"soundfiler write: %s: bad template for tabwrite\",\n                argv[i].a_w.w_symbol->s_name);\n        if (wa.wa_nframes > vecsize - wa.wa_onsetframes)\n            wa.wa_nframes = vecsize - wa.wa_onsetframes;\n    }\n    if (wa.wa_nframes <= 0)\n    {\n        pd_error(obj, \"soundfiler write: no samples at onset %ld\",\n            (long)wa.wa_onsetframes);\n        goto fail;\n    }\n\n        /* find biggest sample for normalizing */\n    for (i = 0; i < sf->sf_nchannels; i++)\n    {\n        for (j = wa.wa_onsetframes; j < wa.wa_nframes + wa.wa_onsetframes; j++)\n        {\n            if (vectors[i][j].w_float > biggest)\n                biggest = vectors[i][j].w_float;\n            else if (-vectors[i][j].w_float > biggest)\n                biggest = -vectors[i][j].w_float;\n        }\n    }\n\n        /* write to ascii text file? */\n    if (wa.wa_ascii)\n    {\n        char filenamebuf[MAXPDSTRING];\n        t_asciiargs a =\n            {wa.wa_onsetframes, wa.wa_nframes, sf->sf_nchannels, vectors, 0,\n                0, 0, 0};\n        strcpy(filenamebuf, wa.wa_filesym->s_name);\n        if (!ascii_hasextension(filenamebuf, MAXPDSTRING))\n            ascii_addextension(filenamebuf, MAXPDSTRING);\n        if (wa.wa_normalize)\n            a.aa_normfactor = (biggest > 0 ? 32767./(32768. * biggest) : 1);\n        else\n            a.aa_normfactor = 1;\n        if ((frameswritten = soundfiler_writeascii(obj, filenamebuf, &a)) == 0)\n        {\n            pd_error(obj, \"soundfiler write: writing ascii failed\");\n            goto fail;\n        }\n            /* fill in for info outlet */\n        sf->sf_headersize = 0;\n        sf->sf_bytespersample = 4;\n        sf->sf_bigendian = sys_isbigendian();\n        return frameswritten;\n    }\n\n        /* create file and detect if int samples should be normalized */\n    if ((fd = create_soundfile(canvas, wa.wa_filesym->s_name,\n        sf, wa.wa_nframes)) < 0)\n    {\n        object_sferror(obj, \"soundfiler write\",\n            wa.wa_filesym->s_name, errno, sf);\n        goto fail;\n    }\n    if (!wa.wa_normalize)\n    {\n        if (sf->sf_bytespersample != 4 && biggest > 1)\n        {\n            post(\"%s: reducing max amplitude %f to 1\",\n                wa.wa_filesym->s_name, biggest);\n            wa.wa_normalize = 1;\n        }\n        else post(\"%s: biggest amplitude = %f\",\n            wa.wa_filesym->s_name, biggest);\n    }\n    if (wa.wa_normalize)\n        normfactor = (biggest > 0 ? 32767./(32768. * biggest) : 1);\n\n        /* write samples */\n    bufframes = SAMPBUFSIZE / sf->sf_bytesperframe;\n    for (frameswritten = 0; frameswritten < wa.wa_nframes;)\n    {\n        size_t thiswrite = wa.wa_nframes - frameswritten,\n               datasize;\n        ssize_t byteswritten;\n        thiswrite = (thiswrite > bufframes ? bufframes : thiswrite);\n        datasize = sf->sf_bytesperframe * thiswrite;\n        soundfile_xferout_words(sf, vectors, (unsigned char *)sampbuf,\n            thiswrite, wa.wa_onsetframes, normfactor);\n        byteswritten = write(sf->sf_fd, sampbuf, datasize);\n        if (byteswritten < 0 || (size_t)byteswritten < datasize)\n        {\n            object_sferror(obj, \"soundfiler write\",\n                wa.wa_filesym->s_name, errno, sf);\n            if (byteswritten > 0)\n                frameswritten += byteswritten / sf->sf_bytesperframe;\n            break;\n        }\n        frameswritten += thiswrite;\n        wa.wa_onsetframes += thiswrite;\n    }\n        /* update header frame size */\n    if (fd >= 0)\n    {\n        soundfile_finishwrite(obj, wa.wa_filesym->s_name, sf,\n                wa.wa_nframes, frameswritten);\n        sys_close(fd);\n    }\n    sf->sf_fd = -1;\n    return frameswritten;\nusage:\n    pd_error(obj, \"usage: write [flags] filename tablename...\");\n    post(\"flags: -skip <n> -nframes <n> -bytes <n> %s ...\", sf_typeargs);\n    post(\"-ascii -big -little -normalize\");\n    post(\"(defaults to a 16 bit wave file)\");\nfail:\n    soundfile_clear(sf); /* clear any bad data */\n    if (fd >= 0)\n        sys_close(fd);\n    return 0;\n}\n\nstatic void soundfiler_write(t_soundfiler *x, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    size_t frameswritten;\n    t_soundfile sf = {0};\n    frameswritten = soundfiler_dowrite(x, x->x_canvas, argc, argv, &sf);\n    outlet_soundfileinfo(x->x_out2, &sf);\n    outlet_float(x->x_obj.ob_outlet, (t_float)frameswritten);\n}\n\nstatic void soundfiler_setup(void)\n{\n    soundfiler_class = class_new(gensym(\"soundfiler\"),\n        (t_newmethod)soundfiler_new, 0,\n        sizeof(t_soundfiler), 0, 0);\n    class_addmethod(soundfiler_class, (t_method)soundfiler_read,\n        gensym(\"read\"), A_GIMME, 0);\n    class_addmethod(soundfiler_class, (t_method)soundfiler_write,\n        gensym(\"write\"), A_GIMME, 0);\n}\n\n/* ------------------------- readsf object ------------------------- */\n\n/* READSF uses the Posix threads package; for the moment we're Linux\nonly although this should be portable to the other platforms.\n\nEach instance of readsf~ owns a \"child\" thread for doing the Posix file reading.\nThe parent thread signals the child each time:\n    (1) a file wants opening or closing;\n    (2) we've eaten another 1/16 of the shared buffer (so that the\n        child thread should check if it's time to read some more.)\nThe child signals the parent whenever a read has completed.  Signaling\nis done by setting \"conditions\" and putting data in mutex-controlled common\nareas.\n*/\n\n#define MAXVECSIZE 128\n\n#define READSIZE 65536\n#define WRITESIZE 65536\n#define DEFBUFPERCHAN 262144\n#define MINBUFSIZE (4 * READSIZE)\n#define MAXBUFSIZE 16777216     /* arbitrary; just don't want to hang malloc */\n\n    /* read/write thread request type */\ntypedef enum _soundfile_request\n{\n    REQUEST_NOTHING = 0,\n    REQUEST_OPEN    = 1,\n    REQUEST_CLOSE   = 2,\n    REQUEST_QUIT    = 3,\n    REQUEST_BUSY    = 4\n} t_soundfile_request;\n\n    /* read/write thread state */\ntypedef enum _soundfile_state\n{\n    STATE_IDLE    = 0,\n    STATE_STARTUP = 1,\n    STATE_STREAM  = 2\n} t_soundfile_state;\n\nstatic t_class *readsf_class;\n\ntypedef struct _readsf\n{\n    t_object x_obj;\n    t_canvas *x_canvas;\n    t_clock *x_clock;\n    char *x_buf;                      /**< soundfile buffer */\n    int x_bufsize;                    /**< buffer size in bytes */\n    int x_noutlets;                   /**< number of audio outlets */\n    t_sample *(x_outvec[MAXSFCHANS]); /**< audio vectors */\n    int x_vecsize;                    /**< vector size for transfers */\n    t_outlet *x_bangout;              /**< bang-on-done outlet */\n    t_soundfile_state x_state;        /**< opened, running, or idle */\n    t_float x_insamplerate;           /**< input signal sample rate, if known */\n        /* parameters to communicate with subthread */\n    t_soundfile_request x_requestcode; /**< pending request to I/O thread */\n    const char *x_filename;   /**< file to open (string permanently allocated) */\n    int x_fileerror;          /**< slot for \"errno\" return */\n    t_soundfile x_sf;         /**< soundfile fd, type, and format info */\n    size_t x_onsetframes;     /**< number of sample frames to skip */\n    int x_fifosize;           /**< buffer size appropriately rounded down */\n    int x_fifohead;           /**< index of next byte to get from file */\n    int x_fifotail;           /**< index of next byte the ugen will read */\n    int x_eof;                /**< true if fifohead has stopped changing */\n    int x_sigcountdown;       /**< counter for signaling child for more data */\n    int x_sigperiod;          /**< number of ticks per signal */\n    size_t x_frameswritten;   /**< writesf~ only; frames written */\n    t_float x_f;              /**< writesf~ only; scalar for signal inlet */\n    pthread_mutex_t x_mutex;\n    pthread_cond_t x_requestcondition;\n    pthread_cond_t x_answercondition;\n    pthread_t x_childthread;\n#ifdef PDINSTANCE\n    t_pdinstance *x_pd_this;  /**< pointer to the owner pd instance */\n#endif\n} t_readsf;\n\n/* ----- the child thread which performs file I/O ----- */\n\n    /** thread state debug prints to stderr */\n//#define DEBUG_SOUNDFILE_THREADS\n\n#if 1\n#define sfread_cond_wait pthread_cond_wait\n#define sfread_cond_signal pthread_cond_signal\n#else\n#include <sys/time.h>    /* debugging version... */\n#include <sys/types.h>\nstatic void readsf_fakewait(pthread_mutex_t *b)\n{\n    struct timeval timeout;\n    timeout.tv_sec = 0;\n    timeout.tv_usec = 1000000;\n    pthread_mutex_unlock(b);\n    select(0, 0, 0, 0, &timeout);\n    pthread_mutex_lock(b);\n}\n\n#define sfread_cond_wait(a,b) readsf_fakewait(b)\n#define sfread_cond_signal(a)\n#endif\n\nstatic void *readsf_child_main(void *zz)\n{\n    t_readsf *x = zz;\n    t_soundfile sf = {0};\n    soundfile_clear(&sf);\n#ifdef PDINSTANCE\n    pd_this = x->x_pd_this;\n#endif\n#ifdef DEBUG_SOUNDFILE_THREADS\n    fprintf(stderr, \"readsf~: 1\\n\");\n#endif\n    pthread_mutex_lock(&x->x_mutex);\n    while (1)\n    {\n        int fifohead;\n        char *buf;\n#ifdef DEBUG_SOUNDFILE_THREADS\n        fprintf(stderr, \"readsf~: 0\\n\");\n#endif\n        if (x->x_requestcode == REQUEST_NOTHING)\n        {\n#ifdef DEBUG_SOUNDFILE_THREADS\n            fprintf(stderr, \"readsf~: wait 2\\n\");\n#endif\n            sfread_cond_signal(&x->x_answercondition);\n            sfread_cond_wait(&x->x_requestcondition, &x->x_mutex);\n#ifdef DEBUG_SOUNDFILE_THREADS\n            fprintf(stderr, \"readsf~: 3\\n\");\n#endif\n        }\n        else if (x->x_requestcode == REQUEST_OPEN)\n        {\n            ssize_t bytesread;\n            size_t wantbytes;\n\n                /* copy file stuff out of the data structure so we can\n                relinquish the mutex while we're in open_soundfile_via_path() */\n            size_t onsetframes = x->x_onsetframes;\n            const char *filename = x->x_filename;\n            const char *dirname = canvas_getdir(x->x_canvas)->s_name;\n\n#ifdef DEBUG_SOUNDFILE_THREADS\n            fprintf(stderr, \"readsf~: 4\\n\");\n#endif\n                /* alter the request code so that an ensuing \"open\" will get\n                noticed. */\n            x->x_requestcode = REQUEST_BUSY;\n            x->x_fileerror = 0;\n\n                /* if there's already a file open, close it */\n            if (sf.sf_fd >= 0)\n            {\n                pthread_mutex_unlock(&x->x_mutex);\n                sys_close(sf.sf_fd);\n                sf.sf_fd = -1;\n                pthread_mutex_lock(&x->x_mutex);\n                x->x_sf.sf_fd = -1;\n                if (x->x_requestcode != REQUEST_BUSY)\n                    goto lost;\n            }\n                /* cache sf *after* closing as x->sf's type\n                    may have changed in readsf_open() */\n            soundfile_copy(&sf, &x->x_sf);\n\n                /* open the soundfile with the mutex unlocked */\n            pthread_mutex_unlock(&x->x_mutex);\n            open_soundfile_via_path(dirname, filename, &sf, onsetframes);\n            pthread_mutex_lock(&x->x_mutex);\n\n#ifdef DEBUG_SOUNDFILE_THREADS\n            fprintf(stderr, \"readsf~: 5\\n\");\n#endif\n            if (sf.sf_fd < 0)\n            {\n                x->x_fileerror = errno;\n                x->x_eof = 1;\n#ifdef DEBUG_SOUNDFILE_THREADS\n                fprintf(stderr, \"readsf~: open failed %s %s\\n\",\n                    filename, dirname);\n#endif\n                goto lost;\n            }\n                /* copy back into the instance structure. */\n            soundfile_copy(&x->x_sf, &sf);\n                /* check if another request has been made; if so, field it */\n            if (x->x_requestcode != REQUEST_BUSY)\n                goto lost;\n#ifdef DEBUG_SOUNDFILE_THREADS\n            fprintf(stderr, \"readsf~: 6\\n\");\n#endif\n            x->x_fifohead = 0;\n                    /* set fifosize from bufsize.  fifosize must be a\n                    multiple of the number of bytes eaten for each DSP\n                    tick.  We pessimistically assume MAXVECSIZE samples\n                    per tick since that could change.  There could be a\n                    problem here if the vector size increases while a\n                    soundfile is being played...  */\n            x->x_fifosize = x->x_bufsize - (x->x_bufsize %\n                (sf.sf_bytesperframe * MAXVECSIZE));\n                    /* arrange for the \"request\" condition to be signaled 16\n                    times per buffer */\n#ifdef DEBUG_SOUNDFILE_THREADS\n            fprintf(stderr, \"readsf~: fifosize %d\\n\", x->x_fifosize);\n#endif\n            x->x_sigcountdown = x->x_sigperiod = (x->x_fifosize /\n                (16 * sf.sf_bytesperframe * x->x_vecsize));\n                /* in a loop, wait for the fifo to get hungry and feed it */\n\n            while (x->x_requestcode == REQUEST_BUSY)\n            {\n                int fifosize = x->x_fifosize;\n#ifdef DEBUG_SOUNDFILE_THREADS\n                fprintf(stderr, \"readsf~: 77\\n\");\n#endif\n                if (x->x_eof)\n                    break;\n                if (x->x_fifohead >= x->x_fifotail)\n                {\n                        /* if the head is >= the tail, we can immediately read\n                        to the end of the fifo.  Unless, that is, we would\n                        read all the way to the end of the buffer and the\n                        \"tail\" is zero; this would fill the buffer completely\n                        which isn't allowed because you can't tell a completely\n                        full buffer from an empty one. */\n                    if (x->x_fifotail || (fifosize - x->x_fifohead > READSIZE))\n                    {\n                        wantbytes = fifosize - x->x_fifohead;\n                        if (wantbytes > READSIZE)\n                            wantbytes = READSIZE;\n                        if (sf.sf_bytelimit >= 0 && wantbytes > (size_t)sf.sf_bytelimit)\n                            wantbytes = sf.sf_bytelimit;\n#ifdef DEBUG_SOUNDFILE_THREADS\n                        fprintf(stderr, \"readsf~: head %d, tail %d, size %ld\\n\",\n                            x->x_fifohead, x->x_fifotail, wantbytes);\n#endif\n                    }\n                    else\n                    {\n#ifdef DEBUG_SOUNDFILE_THREADS\n                        fprintf(stderr, \"readsf~: wait 7a...\\n\");\n#endif\n                        sfread_cond_signal(&x->x_answercondition);\n#ifdef DEBUG_SOUNDFILE_THREADS\n                        fprintf(stderr, \"readsf~: signaled...\\n\");\n#endif\n                        sfread_cond_wait(&x->x_requestcondition, &x->x_mutex);\n#ifdef DEBUG_SOUNDFILE_THREADS\n                        fprintf(stderr, \"readsf~: 7a ... done\\n\");\n#endif\n                        continue;\n                    }\n                }\n                else\n                {\n                        /* otherwise check if there are at least READSIZE\n                        bytes to read.  If not, wait and loop back. */\n                    wantbytes =  x->x_fifotail - x->x_fifohead - 1;\n                    if (wantbytes < READSIZE)\n                    {\n#ifdef DEBUG_SOUNDFILE_THREADS\n                        fprintf(stderr, \"readsf~: wait 7...\\n\");\n#endif\n                        sfread_cond_signal(&x->x_answercondition);\n                        sfread_cond_wait(&x->x_requestcondition, &x->x_mutex);\n#ifdef DEBUG_SOUNDFILE_THREADS\n                        fprintf(stderr, \"readsf~: 7 ... done\\n\");\n#endif\n                        continue;\n                    }\n                    else wantbytes = READSIZE;\n                    if (sf.sf_bytelimit >= 0 && wantbytes > (size_t)sf.sf_bytelimit)\n                        wantbytes = sf.sf_bytelimit;\n                }\n#ifdef DEBUG_SOUNDFILE_THREADS\n                fprintf(stderr, \"readsf~: 8\\n\");\n#endif\n                buf = x->x_buf;\n                fifohead = x->x_fifohead;\n                pthread_mutex_unlock(&x->x_mutex);\n                bytesread = read(sf.sf_fd, buf + fifohead, wantbytes);\n                pthread_mutex_lock(&x->x_mutex);\n                if (x->x_requestcode != REQUEST_BUSY)\n                    break;\n                if (bytesread < 0)\n                {\n#ifdef DEBUG_SOUNDFILE_THREADS\n                    fprintf(stderr, \"readsf~: fileerror %d\\n\", errno);\n#endif\n                    x->x_fileerror = errno;\n                    break;\n                }\n                else if (bytesread == 0)\n                {\n                    x->x_eof = 1;\n                    break;\n                }\n                else\n                {\n                    x->x_fifohead += bytesread;\n                    sf.sf_bytelimit -= bytesread;\n                    if (x->x_fifohead == fifosize)\n                        x->x_fifohead = 0;\n                    if (sf.sf_bytelimit <= 0)\n                    {\n                        x->x_eof = 1;\n                        break;\n                    }\n                }\n#ifdef DEBUG_SOUNDFILE_THREADS\n                fprintf(stderr, \"readsf~: after, head %d tail %d\\n\",\n                    x->x_fifohead, x->x_fifotail);\n#endif\n                    /* signal parent in case it's waiting for data */\n                sfread_cond_signal(&x->x_answercondition);\n            }\n\n        lost:\n            if (x->x_requestcode == REQUEST_BUSY)\n                x->x_requestcode = REQUEST_NOTHING;\n#ifdef DEBUG_SOUNDFILE_THREADS\n                fprintf(stderr, \"readsf~: lost\\n\");\n#endif\n                /* fell out of read loop: close file if necessary,\n                set EOF and signal once more */\n            if (sf.sf_fd >= 0)\n            {\n                    /* only set EOF if there is no pending \"open\" request!\n                    Otherwise, we might accidentally set EOF after it has been\n                    unset in readsf_open() and the stream would fail silently. */\n                if (x->x_requestcode != REQUEST_OPEN)\n                    x->x_eof = 1;\n                x->x_sf.sf_fd = -1;\n                    /* use cached sf */\n                pthread_mutex_unlock(&x->x_mutex);\n                sys_close(sf.sf_fd);\n                sf.sf_fd = -1;\n                pthread_mutex_lock(&x->x_mutex);\n            }\n            sfread_cond_signal(&x->x_answercondition);\n        }\n        else if (x->x_requestcode == REQUEST_CLOSE)\n        {\n            if (sf.sf_fd >= 0)\n            {\n                x->x_sf.sf_fd = -1;\n                    /* use cached sf */\n                pthread_mutex_unlock(&x->x_mutex);\n                sys_close(sf.sf_fd);\n                sf.sf_fd = -1;\n                pthread_mutex_lock(&x->x_mutex);\n            }\n            if (x->x_requestcode == REQUEST_CLOSE)\n                x->x_requestcode = REQUEST_NOTHING;\n            sfread_cond_signal(&x->x_answercondition);\n        }\n        else if (x->x_requestcode == REQUEST_QUIT)\n        {\n            if (sf.sf_fd >= 0)\n            {\n                x->x_sf.sf_fd = -1;\n                    /* use cached sf */\n                pthread_mutex_unlock(&x->x_mutex);\n                sys_close(sf.sf_fd);\n                sf.sf_fd = -1;\n                pthread_mutex_lock(&x->x_mutex);\n            }\n            x->x_requestcode = REQUEST_NOTHING;\n            sfread_cond_signal(&x->x_answercondition);\n            break;\n        }\n        else\n        {\n#ifdef DEBUG_SOUNDFILE_THREADS\n            fprintf(stderr, \"readsf~: 13\\n\");\n#endif\n        }\n    }\n#ifdef DEBUG_SOUNDFILE_THREADS\n    fprintf(stderr, \"readsf~: thread exit\\n\");\n#endif\n    pthread_mutex_unlock(&x->x_mutex);\n    return 0;\n}\n\n/* ----- the object proper runs in the calling (parent) thread ----- */\n\nstatic void readsf_tick(t_readsf *x);\n\nstatic void *readsf_new(t_floatarg fnchannels, t_floatarg fbufsize)\n{\n    t_readsf *x;\n    int nchannels = fnchannels, bufsize = fbufsize, i;\n    char *buf;\n\n    if (nchannels < 1)\n        nchannels = 1;\n    else if (nchannels > MAXSFCHANS)\n        nchannels = MAXSFCHANS;\n    if (bufsize <= 0) bufsize = DEFBUFPERCHAN * nchannels;\n    else if (bufsize < MINBUFSIZE)\n        bufsize = MINBUFSIZE;\n    else if (bufsize > MAXBUFSIZE)\n        bufsize = MAXBUFSIZE;\n    buf = getbytes(bufsize);\n    if (!buf) return 0;\n\n    x = (t_readsf *)pd_new(readsf_class);\n\n    for (i = 0; i < nchannels; i++)\n        outlet_new(&x->x_obj, gensym(\"signal\"));\n    x->x_noutlets = nchannels;\n    x->x_bangout = outlet_new(&x->x_obj, &s_bang);\n    pthread_mutex_init(&x->x_mutex, 0);\n    pthread_cond_init(&x->x_requestcondition, 0);\n    pthread_cond_init(&x->x_answercondition, 0);\n    x->x_vecsize = MAXVECSIZE;\n    x->x_state = STATE_IDLE;\n    x->x_clock = clock_new(x, (t_method)readsf_tick);\n    x->x_canvas = canvas_getcurrent();\n    soundfile_clear(&x->x_sf);\n    x->x_sf.sf_bytespersample = 2;\n    x->x_sf.sf_nchannels = 1;\n    x->x_sf.sf_bytesperframe = 2;\n    x->x_buf = buf;\n    x->x_bufsize = bufsize;\n    x->x_fifosize = x->x_fifohead = x->x_fifotail = x->x_requestcode = 0;\n#ifdef PDINSTANCE\n    x->x_pd_this = pd_this;\n#endif\n    pthread_create(&x->x_childthread, 0, readsf_child_main, x);\n    return x;\n}\n\nstatic void readsf_tick(t_readsf *x)\n{\n    outlet_bang(x->x_bangout);\n}\n\nstatic t_int *readsf_perform(t_int *w)\n{\n    t_readsf *x = (t_readsf *)(w[1]);\n    int vecsize = x->x_vecsize, noutlets = x->x_noutlets, i;\n    size_t j;\n    t_sample *fp;\n    if (x->x_state == STATE_STREAM)\n    {\n        int wantbytes;\n        t_soundfile sf = {0};\n        pthread_mutex_lock(&x->x_mutex);\n            /* copy with mutex locked! */\n        soundfile_copy(&sf, &x->x_sf);\n        wantbytes = vecsize * sf.sf_bytesperframe;\n        while (!x->x_eof && x->x_fifohead >= x->x_fifotail &&\n                x->x_fifohead < x->x_fifotail + wantbytes-1)\n        {\n#ifdef DEBUG_SOUNDFILE_THREADS\n            fprintf(stderr, \"readsf~: wait...\\n\");\n#endif\n            sfread_cond_signal(&x->x_requestcondition);\n            sfread_cond_wait(&x->x_answercondition, &x->x_mutex);\n                /* resync local variables -- bug fix thanks to Shahrokh */\n            vecsize = x->x_vecsize;\n            soundfile_copy(&sf, &x->x_sf);\n            wantbytes = vecsize * sf.sf_bytesperframe;\n#ifdef DEBUG_SOUNDFILE_THREADS\n            fprintf(stderr, \"readsf~: ... done\\n\");\n#endif\n        }\n        if (x->x_eof && x->x_fifohead >= x->x_fifotail &&\n            x->x_fifohead < x->x_fifotail + wantbytes-1)\n        {\n            int xfersize;\n            if (x->x_fileerror)\n                object_sferror(x, \"readsf~\", x->x_filename,\n                    x->x_fileerror, &x->x_sf);\n                /* if there's a partial buffer left, copy it out */\n            xfersize = (x->x_fifohead - x->x_fifotail + 1) /\n                       sf.sf_bytesperframe;\n            if (xfersize)\n            {\n                soundfile_xferin_sample(&sf, noutlets, x->x_outvec, 0,\n                    (unsigned char *)(x->x_buf + x->x_fifotail), xfersize);\n                vecsize -= xfersize;\n            }\n            pthread_mutex_unlock(&x->x_mutex);\n                /* send bang and zero out the (rest of the) output */\n            clock_delay(x->x_clock, 0);\n            x->x_state = STATE_IDLE;\n            for (i = 0; i < noutlets; i++)\n                for (j = vecsize, fp = x->x_outvec[i] + xfersize; j--;)\n                    *fp++ = 0;\n            return w + 2;\n        }\n\n        soundfile_xferin_sample(&sf, noutlets, x->x_outvec, 0,\n            (unsigned char *)(x->x_buf + x->x_fifotail), vecsize);\n\n        x->x_fifotail += wantbytes;\n        if (x->x_fifotail >= x->x_fifosize)\n            x->x_fifotail = 0;\n        if ((--x->x_sigcountdown) <= 0)\n        {\n            sfread_cond_signal(&x->x_requestcondition);\n            x->x_sigcountdown = x->x_sigperiod;\n        }\n        pthread_mutex_unlock(&x->x_mutex);\n    }\n    else\n    {\n        for (i = 0; i < noutlets; i++)\n            for (j = vecsize, fp = x->x_outvec[i]; j--;)\n                *fp++ = 0;\n    }\n    return w + 2;\n}\n\n    /** start making output.  If we're in the \"startup\" state change\n        to the \"running\" state. */\nstatic void readsf_start(t_readsf *x)\n{\n    if (x->x_state == STATE_STARTUP)\n        x->x_state = STATE_STREAM;\n    else pd_error(x, \"readsf~: start requested with no prior 'open'\");\n}\n\n    /** LATER rethink whether you need the mutex just to set a variable? */\nstatic void readsf_stop(t_readsf *x)\n{\n    pthread_mutex_lock(&x->x_mutex);\n    x->x_state = STATE_IDLE;\n    x->x_requestcode = REQUEST_CLOSE;\n    sfread_cond_signal(&x->x_requestcondition);\n    pthread_mutex_unlock(&x->x_mutex);\n}\n\nstatic void readsf_float(t_readsf *x, t_floatarg f)\n{\n    if (f != 0)\n        readsf_start(x);\n    else readsf_stop(x);\n}\n\n    /** open method.  Called as:\n        open [flags] filename [onsetframes headersize channels bytes endianness]\n        (if headersize is zero, header is taken to be automatically detected;\n        thus, use the special \"-1\" to mean a truly headerless file.)\n        if type implementation is set, pass this to open unless headersize is -1 */\nstatic void readsf_open(t_readsf *x, t_symbol *s, int argc, t_atom *argv)\n{\n    t_symbol *filesym, *endian;\n    t_float onsetframes, headersize, nchannels, bytespersample;\n    t_soundfile_type *type = NULL;\n\n    while (argc > 0 && argv->a_type == A_SYMBOL &&\n        *argv->a_w.w_symbol->s_name == '-')\n    {\n            /* check for type by name */\n        const char *flag = argv->a_w.w_symbol->s_name + 1;\n        if (!(type = soundfile_findtype(flag)))\n            goto usage; /* unknown flag */\n        argc -= 1; argv += 1;\n    }\n    filesym = atom_getsymbolarg(0, argc, argv);\n    onsetframes = atom_getfloatarg(1, argc, argv);\n    headersize = atom_getfloatarg(2, argc, argv);\n    nchannels = atom_getfloatarg(3, argc, argv);\n    bytespersample = atom_getfloatarg(4, argc, argv);\n    endian = atom_getsymbolarg(5, argc, argv);\n    if (!*filesym->s_name)\n        return; /* no filename */\n\n    pthread_mutex_lock(&x->x_mutex);\n    soundfile_clear(&x->x_sf);\n    x->x_requestcode = REQUEST_OPEN;\n    x->x_filename = filesym->s_name;\n    x->x_fifotail = 0;\n    x->x_fifohead = 0;\n    if (*endian->s_name == 'b')\n         x->x_sf.sf_bigendian = 1;\n    else if (*endian->s_name == 'l')\n         x->x_sf.sf_bigendian = 0;\n    else if (*endian->s_name)\n        pd_error(x, \"readsf~ open: endianness neither 'b' nor 'l'\");\n    else x->x_sf.sf_bigendian = sys_isbigendian();\n    x->x_onsetframes = (onsetframes > 0 ? onsetframes : 0);\n    x->x_sf.sf_headersize = (headersize > 0 ? headersize :\n        (headersize == 0 ? -1 : 0));\n    x->x_sf.sf_nchannels = (nchannels >= 1 ? nchannels : 1);\n    x->x_sf.sf_bytespersample = (bytespersample > 2 ? bytespersample : 2);\n    x->x_sf.sf_bytesperframe = x->x_sf.sf_nchannels * x->x_sf.sf_bytespersample;\n    if (type && x->x_sf.sf_headersize >= 0)\n    {\n        post(\"'-%s' overridden by headersize\", type->t_name);\n        x->x_sf.sf_type = NULL;\n    }\n    else\n        x->x_sf.sf_type = type;\n    x->x_eof = 0;\n    x->x_fileerror = 0;\n    x->x_state = STATE_STARTUP;\n    sfread_cond_signal(&x->x_requestcondition);\n    pthread_mutex_unlock(&x->x_mutex);\n    return;\nusage:\n    pd_error(x, \"usage: open [flags] filename [onset] [headersize]...\");\n    pd_error(0, \"[nchannels] [bytespersample] [endian (b or l)]\");\n    post(\"flags: %s\", sf_typeargs);\n}\n\nstatic void readsf_dsp(t_readsf *x, t_signal **sp)\n{\n    int i, noutlets = x->x_noutlets;\n    pthread_mutex_lock(&x->x_mutex);\n    x->x_vecsize = sp[0]->s_n;\n    x->x_sigperiod = x->x_fifosize / (x->x_sf.sf_bytesperframe * x->x_vecsize);\n    for (i = 0; i < noutlets; i++)\n        x->x_outvec[i] = sp[i]->s_vec;\n    pthread_mutex_unlock(&x->x_mutex);\n    dsp_add(readsf_perform, 1, x);\n}\n\nstatic void readsf_print(t_readsf *x)\n{\n    post(\"state %d\", x->x_state);\n    post(\"fifo head %d\", x->x_fifohead);\n    post(\"fifo tail %d\", x->x_fifotail);\n    post(\"fifo size %d\", x->x_fifosize);\n    post(\"fd %d\", x->x_sf.sf_fd);\n    post(\"eof %d\", x->x_eof);\n}\n\n    /** request QUIT and wait for acknowledge */\nstatic void readsf_free(t_readsf *x)\n{\n    void *threadrtn;\n    pthread_mutex_lock(&x->x_mutex);\n    x->x_requestcode = REQUEST_QUIT;\n    sfread_cond_signal(&x->x_requestcondition);\n    while (x->x_requestcode != REQUEST_NOTHING)\n    {\n        sfread_cond_signal(&x->x_requestcondition);\n        sfread_cond_wait(&x->x_answercondition, &x->x_mutex);\n    }\n    pthread_mutex_unlock(&x->x_mutex);\n    if (pthread_join(x->x_childthread, &threadrtn))\n        pd_error(x, \"readsf_free: join failed\");\n\n    pthread_cond_destroy(&x->x_requestcondition);\n    pthread_cond_destroy(&x->x_answercondition);\n    pthread_mutex_destroy(&x->x_mutex);\n    freebytes(x->x_buf, x->x_bufsize);\n    clock_free(x->x_clock);\n}\n\nstatic void readsf_setup(void)\n{\n    readsf_class = class_new(gensym(\"readsf~\"),\n        (t_newmethod)readsf_new, (t_method)readsf_free,\n        sizeof(t_readsf), 0, A_DEFFLOAT, A_DEFFLOAT, 0);\n    class_addfloat(readsf_class, (t_method)readsf_float);\n    class_addmethod(readsf_class, (t_method)readsf_start, gensym(\"start\"), 0);\n    class_addmethod(readsf_class, (t_method)readsf_stop, gensym(\"stop\"), 0);\n    class_addmethod(readsf_class, (t_method)readsf_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_addmethod(readsf_class, (t_method)readsf_open,\n        gensym(\"open\"), A_GIMME, 0);\n    class_addmethod(readsf_class, (t_method)readsf_print, gensym(\"print\"), 0);\n}\n\n/* ------------------------- writesf ------------------------- */\n\nstatic t_class *writesf_class;\n\ntypedef t_readsf t_writesf; /* just re-use the structure */\n\n/* ----- the child thread which performs file I/O ----- */\n\nstatic void *writesf_child_main(void *zz)\n{\n    t_writesf *x = zz;\n    t_soundfile sf = {0};\n    soundfile_clear(&sf);\n#ifdef PDINSTANCE\n    pd_this = x->x_pd_this;\n#endif\n#ifdef DEBUG_SOUNDFILE_THREADS\n    fprintf(stderr, \"writesf~: 1\\n\");\n#endif\n    pthread_mutex_lock(&x->x_mutex);\n    while (1)\n    {\n#ifdef DEBUG_SOUNDFILE_THREADS\n        fprintf(stderr, \"writesf~: 0\\n\");\n#endif\n        if (x->x_requestcode == REQUEST_NOTHING)\n        {\n#ifdef DEBUG_SOUNDFILE_THREADS\n            fprintf(stderr, \"writesf~: wait 2\\n\");\n#endif\n            sfread_cond_signal(&x->x_answercondition);\n            sfread_cond_wait(&x->x_requestcondition, &x->x_mutex);\n#ifdef DEBUG_SOUNDFILE_THREADS\n            fprintf(stderr, \"writesf~: 3\\n\");\n#endif\n        }\n        else if (x->x_requestcode == REQUEST_OPEN)\n        {\n            ssize_t byteswritten;\n            size_t writebytes;\n\n                /* copy file stuff out of the data structure so we can\n                relinquish the mutex while we're in open_soundfile_via_path() */\n            const char *filename = x->x_filename;\n            t_canvas *canvas = x->x_canvas;\n            soundfile_copy(&sf, &x->x_sf);\n\n                /* alter the request code so that an ensuing \"open\" will get\n                noticed. */\n#ifdef DEBUG_SOUNDFILE_THREADS\n            fprintf(stderr, \"writesf~: 4\\n\");\n#endif\n            x->x_requestcode = REQUEST_BUSY;\n            x->x_fileerror = 0;\n\n                /* if there's already a file open, close it.  This\n                should never happen since writesf_open() calls stop if\n                needed and then waits until we're idle. */\n            if (sf.sf_fd >= 0)\n            {\n                size_t frameswritten = x->x_frameswritten;\n\n                pthread_mutex_unlock(&x->x_mutex);\n                soundfile_finishwrite(x, filename, &sf,\n                    SFMAXFRAMES, frameswritten);\n                sys_close(sf.sf_fd);\n                sf.sf_fd = -1;\n                pthread_mutex_lock(&x->x_mutex);\n                x->x_sf.sf_fd = -1;\n#ifdef DEBUG_SOUNDFILE_THREADS\n                fprintf(stderr, \"writesf~: bug? ditched %ld\\n\", frameswritten);\n#endif\n                if (x->x_requestcode != REQUEST_BUSY)\n                    continue;\n            }\n                /* cache sf *after* closing as x->sf's type\n                    may have changed in writesf_open() */\n            soundfile_copy(&sf, &x->x_sf);\n\n                /* open the soundfile with the mutex unlocked */\n            pthread_mutex_unlock(&x->x_mutex);\n            create_soundfile(canvas, filename, &sf, 0);\n            pthread_mutex_lock(&x->x_mutex);\n\n#ifdef DEBUG_SOUNDFILE_THREADS\n            fprintf(stderr, \"writesf~: 5\\n\");\n#endif\n\n            if (sf.sf_fd < 0)\n            {\n                x->x_sf.sf_fd = -1;\n                x->x_eof = 1;\n                x->x_fileerror = errno;\n#ifdef DEBUG_SOUNDFILE_THREADS\n                fprintf(stderr, \"writesf~: open failed %s\\n\", filename);\n#endif\n                goto bail;\n            }\n                /* check if another request has been made; if so, field it */\n            if (x->x_requestcode != REQUEST_BUSY)\n                continue;\n#ifdef DEBUG_SOUNDFILE_THREADS\n            fprintf(stderr, \"writesf~: 6\\n\");\n#endif\n                /* copy back into the instance structure. */\n            soundfile_copy(&x->x_sf, &sf);\n            x->x_fifotail = 0;\n            x->x_frameswritten = 0;\n                /* in a loop, wait for the fifo to have data and write it\n                    to disk */\n            while (x->x_requestcode == REQUEST_BUSY ||\n                (x->x_requestcode == REQUEST_CLOSE &&\n                    x->x_fifohead != x->x_fifotail))\n            {\n                int fifosize = x->x_fifosize, fifotail;\n                char *buf = x->x_buf;\n#ifdef DEBUG_SOUNDFILE_THREADS\n                fprintf(stderr, \"writesf~: 77\\n\");\n#endif\n                    /* if the head is < the tail, we can immediately write\n                    from tail to end of fifo to disk; otherwise we hold off\n                    writing until there are at least WRITESIZE bytes in the\n                    buffer */\n                if (x->x_fifohead < x->x_fifotail ||\n                    x->x_fifohead >= x->x_fifotail + WRITESIZE\n                    || (x->x_requestcode == REQUEST_CLOSE &&\n                        x->x_fifohead != x->x_fifotail))\n                {\n                    writebytes = (x->x_fifohead < x->x_fifotail ?\n                        fifosize : x->x_fifohead) - x->x_fifotail;\n                    if (writebytes > READSIZE)\n                        writebytes = READSIZE;\n                }\n                else\n                {\n#ifdef DEBUG_SOUNDFILE_THREADS\n                    fprintf(stderr, \"writesf~: wait 7a...\\n\");\n#endif\n                    sfread_cond_signal(&x->x_answercondition);\n#ifdef DEBUG_SOUNDFILE_THREADS\n                    fprintf(stderr, \"writesf~: signaled...\\n\");\n#endif\n                    sfread_cond_wait(&x->x_requestcondition,\n                        &x->x_mutex);\n#ifdef DEBUG_SOUNDFILE_THREADS\n                    fprintf(stderr, \"writesf~: 7a ... done\\n\");\n#endif\n                    continue;\n                }\n#ifdef DEBUG_SOUNDFILE_THREADS\n                fprintf(stderr, \"writesf~: 8\\n\");\n#endif\n                fifotail = x->x_fifotail;\n                soundfile_copy(&sf, &x->x_sf);\n                pthread_mutex_unlock(&x->x_mutex);\n                byteswritten = write(sf.sf_fd, buf + fifotail, writebytes);\n                pthread_mutex_lock(&x->x_mutex);\n                if (x->x_requestcode != REQUEST_BUSY &&\n                    x->x_requestcode != REQUEST_CLOSE)\n                        break;\n                if (byteswritten < 0 || (size_t)byteswritten < writebytes)\n                {\n#ifdef DEBUG_SOUNDFILE_THREADS\n                    fprintf(stderr, \"writesf~: fileerror %d\\n\", errno);\n#endif\n                    x->x_fileerror = errno;\n                    goto bail;\n                }\n                else\n                {\n                    x->x_fifotail += byteswritten;\n                    if (x->x_fifotail == fifosize)\n                        x->x_fifotail = 0;\n                }\n                x->x_frameswritten += byteswritten / sf.sf_bytesperframe;\n#ifdef DEBUG_SOUNDFILE_THREADS\n                fprintf(stderr, \"writesf~: after head %d tail %d written %ld\\n\",\n                    x->x_fifohead, x->x_fifotail, x->x_frameswritten);\n#endif\n                    /* signal parent in case it's waiting for data */\n                sfread_cond_signal(&x->x_answercondition);\n                continue;\n\n             bail:\n                 if (x->x_requestcode == REQUEST_BUSY)\n                     x->x_requestcode = REQUEST_NOTHING;\n                     /* hit an error; close file if necessary,\n                     set EOF and signal once more */\n                 if (sf.sf_fd >= 0)\n                 {\n                     pthread_mutex_unlock(&x->x_mutex);\n                     sys_close(sf.sf_fd);\n                     sf.sf_fd = -1;\n                     pthread_mutex_lock(&x->x_mutex);\n                     x->x_eof = 1;\n                     x->x_sf.sf_fd = -1;\n                 }\n                 sfread_cond_signal(&x->x_answercondition);\n            }\n        }\n        else if (x->x_requestcode == REQUEST_CLOSE ||\n            x->x_requestcode == REQUEST_QUIT)\n        {\n            int quit = (x->x_requestcode == REQUEST_QUIT);\n            if (sf.sf_fd >= 0)\n            {\n                const char *filename = x->x_filename;\n                size_t frameswritten = x->x_frameswritten;\n                soundfile_copy(&sf, &x->x_sf);\n                pthread_mutex_unlock(&x->x_mutex);\n                soundfile_finishwrite(x, filename, &sf,\n                    SFMAXFRAMES, frameswritten);\n                sys_close(sf.sf_fd);\n                sf.sf_fd = -1;\n                pthread_mutex_lock(&x->x_mutex);\n                x->x_sf.sf_fd = -1;\n            }\n            x->x_requestcode = REQUEST_NOTHING;\n            sfread_cond_signal(&x->x_answercondition);\n            if (quit)\n                break;\n        }\n        else\n        {\n#ifdef DEBUG_SOUNDFILE_THREADS\n            fprintf(stderr, \"writesf~: 13\\n\");\n#endif\n        }\n    }\n#ifdef DEBUG_SOUNDFILE_THREADS\n    fprintf(stderr, \"writesf~: thread exit\\n\");\n#endif\n    pthread_mutex_unlock(&x->x_mutex);\n    return 0;\n}\n\n/* ----- the object proper runs in the calling (parent) thread ----- */\n\nstatic void writesf_tick(t_writesf *x);\n\nstatic void *writesf_new(t_floatarg fnchannels, t_floatarg fbufsize)\n{\n    t_writesf *x;\n    int nchannels = fnchannels, bufsize = fbufsize, i;\n    char *buf;\n\n    if (nchannels < 1)\n        nchannels = 1;\n    else if (nchannels > MAXSFCHANS)\n        nchannels = MAXSFCHANS;\n    if (bufsize <= 0) bufsize = DEFBUFPERCHAN * nchannels;\n    else if (bufsize < MINBUFSIZE)\n        bufsize = MINBUFSIZE;\n    else if (bufsize > MAXBUFSIZE)\n        bufsize = MAXBUFSIZE;\n    buf = getbytes(bufsize);\n    if (!buf) return 0;\n\n    x = (t_writesf *)pd_new(writesf_class);\n\n    for (i = 1; i < nchannels; i++)\n        inlet_new(&x->x_obj,  &x->x_obj.ob_pd, &s_signal, &s_signal);\n\n    x->x_f = 0;\n    pthread_mutex_init(&x->x_mutex, 0);\n    pthread_cond_init(&x->x_requestcondition, 0);\n    pthread_cond_init(&x->x_answercondition, 0);\n    x->x_vecsize = MAXVECSIZE;\n    x->x_insamplerate = 0;\n    x->x_state = STATE_IDLE;\n    x->x_clock = 0;     /* no callback needed here */\n    x->x_canvas = canvas_getcurrent();\n    soundfile_clear(&x->x_sf);\n    x->x_sf.sf_nchannels = nchannels;\n    x->x_sf.sf_bytespersample = 2;\n    x->x_sf.sf_bytesperframe = nchannels * 2;\n    x->x_buf = buf;\n    x->x_bufsize = bufsize;\n    x->x_fifosize = x->x_fifohead = x->x_fifotail = x->x_requestcode = 0;\n#ifdef PDINSTANCE\n    x->x_pd_this = pd_this;\n#endif\n    pthread_create(&x->x_childthread, 0, writesf_child_main, x);\n    return x;\n}\n\nstatic t_int *writesf_perform(t_int *w)\n{\n    t_writesf *x = (t_writesf *)(w[1]);\n    if (x->x_state == STATE_STREAM)\n    {\n        size_t roominfifo;\n        size_t wantbytes;\n        int vecsize = x->x_vecsize;\n        t_soundfile sf = {0};\n        pthread_mutex_lock(&x->x_mutex);\n            /* copy with mutex locked! */\n        soundfile_copy(&sf, &x->x_sf);\n        wantbytes = vecsize * sf.sf_bytesperframe;\n        roominfifo = x->x_fifotail - x->x_fifohead;\n        if (roominfifo <= 0)\n            roominfifo += x->x_fifosize;\n        while (!x->x_eof && roominfifo < wantbytes + 1)\n        {\n            fprintf(stderr, \"writesf waiting for disk write..\\n\");\n            fprintf(stderr, \"(head %d, tail %d, room %d, want %ld)\\n\",\n                (int)x->x_fifohead, (int)x->x_fifotail,\n                (int)roominfifo, (long)wantbytes);\n            sfread_cond_signal(&x->x_requestcondition);\n            sfread_cond_wait(&x->x_answercondition, &x->x_mutex);\n            fprintf(stderr, \"... done waiting.\\n\");\n            roominfifo = x->x_fifotail - x->x_fifohead;\n            if (roominfifo <= 0)\n                roominfifo += x->x_fifosize;\n        }\n        if (x->x_eof)\n        {\n            if (x->x_fileerror)\n                object_sferror(x, \"writesf~\", x->x_filename,\n                    x->x_fileerror, &x->x_sf);\n            x->x_state = STATE_IDLE;\n            sfread_cond_signal(&x->x_requestcondition);\n            pthread_mutex_unlock(&x->x_mutex);\n            return w + 2;\n        }\n\n        soundfile_xferout_sample(&sf, x->x_outvec,\n            (unsigned char *)(x->x_buf + x->x_fifohead), vecsize, 0, 1.);\n\n        x->x_fifohead += wantbytes;\n        if (x->x_fifohead >= x->x_fifosize)\n            x->x_fifohead = 0;\n        if ((--x->x_sigcountdown) <= 0)\n        {\n#ifdef DEBUG_SOUNDFILE_THREADS\n            fprintf(stderr, \"writesf~: signal 1\\n\");\n#endif\n            sfread_cond_signal(&x->x_requestcondition);\n            x->x_sigcountdown = x->x_sigperiod;\n        }\n        pthread_mutex_unlock(&x->x_mutex);\n    }\n    return w + 2;\n}\n\n    /** start making output.  If we're in the \"startup\" state change\n        to the \"running\" state. */\nstatic void writesf_start(t_writesf *x)\n{\n    if (x->x_state == STATE_STARTUP)\n        x->x_state = STATE_STREAM;\n    else\n        pd_error(x, \"writesf~: start requested with no prior 'open'\");\n}\n\n    /** LATER rethink whether you need the mutex just to set a variable? */\nstatic void writesf_stop(t_writesf *x)\n{\n    pthread_mutex_lock(&x->x_mutex);\n    x->x_state = STATE_IDLE;\n    x->x_requestcode = REQUEST_CLOSE;\n#ifdef DEBUG_SOUNDFILE_THREADS\n    fprintf(stderr, \"writesf~: signal 2\\n\");\n#endif\n    sfread_cond_signal(&x->x_requestcondition);\n    pthread_mutex_unlock(&x->x_mutex);\n}\n\n    /** open method.  Called as: open [flags] filename with args as in\n        soundfiler_parsewriteargs(). */\nstatic void writesf_open(t_writesf *x, t_symbol *s, int argc, t_atom *argv)\n{\n    t_soundfiler_writeargs wa = {0};\n    if (x->x_state != STATE_IDLE)\n        writesf_stop(x);\n    if (soundfiler_parsewriteargs(x, &argc, &argv, &wa) || wa.wa_ascii)\n    {\n        pd_error(x, \"usage: open [flags] filename...\");\n        post(\"flags: -bytes <n> %s -big -little -rate <n>\", sf_typeargs);\n        return;\n    }\n    if (wa.wa_normalize || wa.wa_onsetframes || (wa.wa_nframes != SFMAXFRAMES))\n        pd_error(x, \"writesf~ open: normalize/onset/nframes argument ignored\");\n    if (argc)\n        pd_error(x, \"writesf~ open: extra argument(s) ignored\");\n    pthread_mutex_lock(&x->x_mutex);\n        /* make sure that the child thread has finished writing */\n    while (x->x_requestcode != REQUEST_NOTHING)\n    {\n        sfread_cond_signal(&x->x_requestcondition);\n        sfread_cond_wait(&x->x_answercondition, &x->x_mutex);\n    }\n    x->x_filename = wa.wa_filesym->s_name;\n    x->x_sf.sf_type = wa.wa_type;\n    if (wa.wa_samplerate > 0)\n        x->x_sf.sf_samplerate = wa.wa_samplerate;\n    else if (x->x_insamplerate > 0)\n        x->x_sf.sf_samplerate = x->x_insamplerate;\n    else x->x_sf.sf_samplerate = sys_getsr();\n    x->x_sf.sf_bytespersample =\n        (wa.wa_bytespersample > 2 ? wa.wa_bytespersample : 2);\n    x->x_sf.sf_bigendian = wa.wa_bigendian;\n    x->x_sf.sf_bytesperframe = x->x_sf.sf_nchannels * x->x_sf.sf_bytespersample;\n    x->x_frameswritten = 0;\n    x->x_requestcode = REQUEST_OPEN;\n    x->x_fifotail = 0;\n    x->x_fifohead = 0;\n    x->x_eof = 0;\n    x->x_fileerror = 0;\n    x->x_state = STATE_STARTUP;\n        /* set fifosize from bufsize.  fifosize must be a\n        multiple of the number of bytes eaten for each DSP\n        tick.  */\n    x->x_fifosize = x->x_bufsize - (x->x_bufsize %\n        (x->x_sf.sf_bytesperframe * MAXVECSIZE));\n        /* arrange for the \"request\" condition to be signaled 16\n            times per buffer */\n    x->x_sigcountdown = x->x_sigperiod = (x->x_fifosize /\n            (16 * (x->x_sf.sf_bytesperframe * x->x_vecsize)));\n    sfread_cond_signal(&x->x_requestcondition);\n    pthread_mutex_unlock(&x->x_mutex);\n}\n\nstatic void writesf_dsp(t_writesf *x, t_signal **sp)\n{\n    int i, ninlets = x->x_sf.sf_nchannels;\n    pthread_mutex_lock(&x->x_mutex);\n    x->x_vecsize = sp[0]->s_n;\n    x->x_sigperiod = (x->x_fifosize /\n            (16 * x->x_sf.sf_bytesperframe * x->x_vecsize));\n    for (i = 0; i < ninlets; i++)\n        x->x_outvec[i] = sp[i]->s_vec;\n    x->x_insamplerate = sp[0]->s_sr;\n    pthread_mutex_unlock(&x->x_mutex);\n    dsp_add(writesf_perform, 1, x);\n}\n\nstatic void writesf_print(t_writesf *x)\n{\n    post(\"state %d\", x->x_state);\n    post(\"fifo head %d\", x->x_fifohead);\n    post(\"fifo tail %d\", x->x_fifotail);\n    post(\"fifo size %d\", x->x_fifosize);\n    post(\"fd %d\", x->x_sf.sf_fd);\n    post(\"eof %d\", x->x_eof);\n}\n\n    /** request QUIT and wait for acknowledge */\nstatic void writesf_free(t_writesf *x)\n{\n    void *threadrtn;\n    pthread_mutex_lock(&x->x_mutex);\n    x->x_requestcode = REQUEST_QUIT;\n#ifdef DEBUG_SOUNDFILE_THREADS\n    fprintf(stderr, \"writesf~: stopping thread...\\n\");\n#endif\n    sfread_cond_signal(&x->x_requestcondition);\n    while (x->x_requestcode != REQUEST_NOTHING)\n    {\n#ifdef DEBUG_SOUNDFILE_THREADS\n        fprintf(stderr, \"writesf~: signaling...\\n\");\n#endif\n        sfread_cond_signal(&x->x_requestcondition);\n        sfread_cond_wait(&x->x_answercondition, &x->x_mutex);\n    }\n    pthread_mutex_unlock(&x->x_mutex);\n    if (pthread_join(x->x_childthread, &threadrtn))\n        pd_error(x, \"writesf_free: join failed\");\n#ifdef DEBUG_SOUNDFILE_THREADS\n    fprintf(stderr, \"writesf~: ... done\\n\");\n#endif\n\n    pthread_cond_destroy(&x->x_requestcondition);\n    pthread_cond_destroy(&x->x_answercondition);\n    pthread_mutex_destroy(&x->x_mutex);\n    freebytes(x->x_buf, x->x_bufsize);\n}\n\nstatic void writesf_setup(void)\n{\n    writesf_class = class_new(gensym(\"writesf~\"),\n        (t_newmethod)writesf_new, (t_method)writesf_free,\n        sizeof(t_writesf), 0, A_DEFFLOAT, A_DEFFLOAT, 0);\n    class_addmethod(writesf_class, (t_method)writesf_start, gensym(\"start\"), 0);\n    class_addmethod(writesf_class, (t_method)writesf_stop, gensym(\"stop\"), 0);\n    class_addmethod(writesf_class, (t_method)writesf_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_addmethod(writesf_class, (t_method)writesf_open,\n        gensym(\"open\"), A_GIMME, 0);\n    class_addmethod(writesf_class, (t_method)writesf_print, gensym(\"print\"), 0);\n    CLASS_MAINSIGNALIN(writesf_class, t_writesf, x_f);\n}\n\n/* ------------------------- global setup routine ------------------------ */\n\nvoid d_soundfile_setup(void)\n{\n    soundfile_type_setup();\n    soundfiler_setup();\n    readsf_setup();\n    writesf_setup();\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/d_soundfile.h",
    "content": "/* Copyright (c) 2019 Dan Wilcox.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* soundfile formats and helper functions */\n\n#pragma once\n\n#include \"m_pd.h\"\n#ifdef HAVE_UNISTD_H\n#include <unistd.h>\n#endif\n#ifdef _WIN32\n#include <io.h>\n#endif\n#include <string.h>\n#include <limits.h>\n#include <errno.h>\n\n/* GLIBC large file support */\n#ifdef _LARGEFILE64_SOURCE\n#define lseek lseek64\n#define off_t __off64_t\n#endif\n\n/* MSVC doesn't define or uses different naming for these Posix types */\n#ifdef _MSC_VER\n#include <BaseTsd.h>\ntypedef SSIZE_T ssize_t;\n#define off_t ssize_t\n/* choose appropriate size if SSIZE_MAX is not defined */\n#ifndef SSIZE_MAX\n#ifdef _WIN64\n#define SSIZE_MAX _I64_MAX\n#else /* _WIN32 */\n#define SSIZE_MAX INT_MAX\n#endif\n#endif /* SSIZE_MAX */\n#endif /* _MSC_VER */\n\n    /** should be large enough for all file type min sizes */\n#define SFHDRBUFSIZE 128\n\n#define SFMAXFRAMES SIZE_MAX  /**< default max sample frames, unsigned */\n#define SFMAXBYTES  SSIZE_MAX /**< default max sample bytes, signed */\n\n    /** sound file read/write debug posts */\n//#define DEBUG_SOUNDFILE\n\n/* ----- soundfile ----- */\n\n    /** soundfile file descriptor, backend type, and format info\n        note: headersize and bytelimit are signed as they are used for < 0\n              comparisons, hopefully ssize_t is large enough\n        \"headersize\" can also be thought of as the audio data byte offset */\ntypedef struct _soundfile\n{\n    int sf_fd;             /**< file descriptor, >= 0 : open, -1 : closed */\n    struct _soundfile_type *sf_type; /**< type implementation             */\n    /* format info */\n    int sf_samplerate;     /**< read: file sr, write: pd sr               */\n    int sf_nchannels;      /**< number of channels                        */\n    int sf_bytespersample; /**< bit rate, 2: 16 bit, 3: 24 bit, 4: 32 bit */\n    ssize_t sf_headersize; /**< header size in bytes, -1 for unknown size */\n    int sf_bigendian;      /**< sample endianness, 1 : big or 0 : little  */\n    int sf_bytesperframe;  /**< number of bytes per sample frame          */\n    ssize_t sf_bytelimit;  /**< number of sound data bytes to read/write  */\n} t_soundfile;\n\n    /** clear soundfile struct to defaults, does not close or free */\nvoid soundfile_clear(t_soundfile *sf);\n\n    /** copy src soundfile info into dst */\nvoid soundfile_copy(t_soundfile *dst, const t_soundfile *src);\n\n    /** returns 1 if bytes need to be swapped due to endianness, otherwise 0 */\nint soundfile_needsbyteswap(const t_soundfile *sf);\n\n    /** generic soundfile errors */\ntypedef enum _soundfile_errno\n{\n    SOUNDFILE_ERRUNKNOWN   = -1000, /* unknown header */\n    SOUNDFILE_ERRMALFORMED = -1001, /* bad header */\n    SOUNDFILE_ERRVERSION   = -1002, /* header ok, unsupported version */\n    SOUNDFILE_ERRSAMPLEFMT = -1003  /* header ok, unsupported sample format */\n} t_soundfile_errno;\n\n    /** returns a soundfile error string, otherwise calls C strerror */\nconst char* soundfile_strerror(int errnum);\n\n/* ----- soundfile type ----- */\n\n    /** returns 1 if buffer is the beginning of a supported file header,\n        size will be at least minheadersize\n        this may be called in a background thread */\ntypedef int (*t_soundfile_isheaderfn)(const char *buf, size_t size);\n\n    /** read format info from soundfile header,\n        returns 1 on success or 0 on error\n        note: set sf_bytelimit = sound data size, optionally set errno\n        this may be called in a background thread */\ntypedef int (*t_soundfile_readheaderfn)(t_soundfile *sf);\n\n    /** write header to beginning of an open file from an info struct\n        returns header bytes written or < 0 on error\n        note: optionally set errno\n        this may be called in a background thread */\ntypedef int (*t_soundfile_writeheaderfn)(t_soundfile *sf, size_t nframes);\n\n    /** update file header data size, returns 1 on success or 0 on error\n        this may be called in a background thread */\ntypedef int (*t_soundfile_updateheaderfn)(t_soundfile *sf, size_t nframes);\n\n    /** returns 1 if the filename has a supported file extension, otherwise 0\n        this may be called in a background thread */\ntypedef int (*t_soundfile_hasextensionfn)(const char *filename, size_t size);\n\n    /** appends the default file extension, returns 1 on success\n        this may be called in a background thread */\ntypedef int (*t_soundfile_addextensionfn)(char *filename, size_t size);\n\n    /** returns the type's preferred sample endianness based on the\n        requested endianness (0 little, 1 big, -1 unspecified)\n        returns 1 for big endian, 0 for little endian */\ntypedef int (*t_soundfile_endiannessfn)(int endianness);\n\n    /* type implementation for a single file format */\ntypedef struct _soundfile_type\n{\n    char *t_name;           /**< type name, unique & w/o white spaces       */\n    size_t t_minheadersize; /**< minimum valid header size                  */\n    t_soundfile_isheaderfn t_isheaderfn;         /**< must be non-NULL      */\n    t_soundfile_readheaderfn t_readheaderfn;     /**< must be non-NULL      */\n    t_soundfile_writeheaderfn t_writeheaderfn;   /**< must be non-NULL      */\n    t_soundfile_updateheaderfn t_updateheaderfn; /**< must be non-NULL      */\n    t_soundfile_hasextensionfn t_hasextensionfn; /**< must be non-NULL      */\n    t_soundfile_addextensionfn t_addextensionfn; /**< must be non-NULL      */\n    t_soundfile_endiannessfn t_endiannessfn;     /**< must be non-NULL      */\n} t_soundfile_type;\n\n    /** add a new type implementation\n        returns 1 on success or 0 if max types has been reached */\nint soundfile_addtype(const t_soundfile_type *t);\n\n/* ----- read/write helpers ----- */\n\n    /** seek to offset in file fd and read size bytes into dst,\n        returns bytes written on success or -1 on failure */\nssize_t fd_read(int fd, off_t offset, void *dst, size_t size);\n\n    /** seek to offset in file fd and write size bytes from dst,\n        returns number of bytes written on success or -1 if seek or write\n        failed */\nssize_t fd_write(int fd, off_t offset, const void *src, size_t size);\n\n/* ----- byte swappers ----- */\n\n    /** returns 1 if system is bigendian */\nint sys_isbigendian(void);\n\n    /** swap 8 bytes and return if doit = 1, otherwise return n */\nuint64_t swap8(uint64_t n, int doit);\n\n    /** swap a 64 bit signed int and return if doit = 1, otherwise return n */\nint64_t swap8s(int64_t n, int doit);\n\n    /** swap 4 bytes and return if doit = 1, otherwise return n */\nuint32_t swap4(uint32_t n, int doit);\n\n    /** swap a 32 bit signed int and return if doit = 1, otherwise return n */\nint32_t swap4s(int32_t n, int doit);\n\n    /** swap 2 bytes and return if doit = 1, otherwise return n */\nuint16_t swap2(uint16_t n, int doit);\n\n    /** swap a 4 byte string in place if doit = 1, otherwise do nothing */\nvoid swapstring4(char *foo, int doit);\n\n    /** swap an 8 byte string in place if doit = 1, otherwise do nothing */\nvoid swapstring8(char *foo, int doit);\n"
  },
  {
    "path": "libs/libpd/pure-data/src/d_soundfile_aiff.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette. Updated 2020 Dan Wilcox.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* ref: http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/AIFF/AIFF.html */\n\n#include \"d_soundfile.h\"\n#include <math.h>\n\n/* AIFF (Audio Interchange File Format) and AIFF-C (AIFF Compressed)\n\n  * RIFF variant with sections split into data \"chunks\"\n  * chunk sizes do not include the chunk id or size (- 8)\n  * the file header's size is the total size of all subsequent chunks (- 8)\n  * chunk data is big endian\n  * sound data is big endian, unless AIFF-C \"sowt\" compression type\n  * the sound data chunk can be omitted if common chunk sample frames is 0\n  * chunks start on even byte offsets\n  * chunk string values are pascal strings:\n    - first byte is string length, limited to 255 chars\n    - total length must be even, pad with a '\\0' byte, max 256\n    - padding is not added to string length byte value\n  * AIFF-C compression 4 char types & name (pascal) strings:\n    - NONE \"not compressed\" big endian\n    - sowt \"not compressed\" little endian\n    - fl32 \"32-bit floating point\" big endian\n    - FL32 \"Float 32\" big endian\n    - the rest are not relevant to Pd...\n  * limited to ~2 GB files as sizes are signed 32 bit ints\n\n  this implementation:\n\n  * supports AIFF and AIFF-C\n  * implicitly writes AIFF-C header for 32 bit float (see below)\n  * implements chunks: common, data, version (AIFF-C)\n  * ignores chunks: marker, instrument, comment, name, author, copyright,\n                    annotation, audio recording, MIDI data, application, ID3\n  * assumes common chunk is always before sound data chunk\n  * ignores any chunks after finding the sound data chunk\n  * assumes there is always a sound data chunk\n  * does not block align sound data\n  * sample format: 16 and 24 bit lpcm, 32 bit float, no 32 bit lpcm\n\n  Pd versions < 0.51 did *not* read or write AIFF files with a 32 bit float\n  sample format.\n\n*/\n\n    /* explicit byte sizes, sizeof(struct) may return alignment-padded values */\n#define AIFFCHUNKSIZE  8 /**< chunk header only */\n#define AIFFHEADSIZE  12 /**< chunk header and file format only */\n#define AIFFVERSIZE   12 /**< chunk header and data */\n#define AIFFCOMMSIZE  26 /**< chunk header and data */\n#define AIFFDATASIZE  16 /**< chunk header, offset, and block size data */\n\n#define AIFFCVER1    0xA2805140 /**< 2726318400 decimal */\n#define AIFFMAXBYTES 0x7fffffff /**< max signed 32 bit size */\n\n     /* compression string defines */\n#define AIFF_NONE_STR \"not compressed\"\n#define AIFF_FL32_STR \"32-bit floating point\"\n\n#define AIFF_NONE_LEN 16 /**< 1 len byte + 15 bytes + 1 \\0 pad byte */\n#define AIFF_FL32_LEN 22 /**< 1 len byte + 22 bytes, no pad byte */\n\n    /** basic chunk header, 8 bytes */\ntypedef struct _chunk\n{\n    char c_id[4];                    /**< chunk id                     */\n    int32_t c_size;                  /**< chunk data length            */\n} t_chunk;\n\n    /** file head container chunk, 12 bytes */\ntypedef struct _head\n{\n    char h_id[4];                    /**< chunk id \"FORM\"              */\n    int32_t h_size;                  /**< chunk data length            */\n    char h_formtype[4];              /**< format: \"AIFF\" or \"AIFC\"     */\n} t_head;\n\n    /** common chunk, 26 (AIFF) or 30+ (AIFC) bytes\n        note: sample frames is split to avoid struct alignment padding */\ntypedef struct _commchunk\n{\n    char cc_id[4];                   /**< chunk id \"COMM\"              */\n    int32_t cc_size;                 /**< chunk data length            */\n    uint16_t cc_nchannels;           /**< number of channels           */\n    uint8_t cc_nframes[4];           /**< # of sample frames, uint32_t */\n    uint16_t cc_bitspersample;       /**< bits per sample              */\n    uint8_t cc_samplerate[10];       /**< sample rate, 80-bit float!   */\n    /* AIFF-C */\n    char cc_comptype[4];             /**< compression type             */\n    char cc_compname[256];           /**< compression name, pascal str */\n} t_commchunk;\n\n    /** sound data chunk, min 16 bytes before data */\ntypedef struct _datachunk\n{\n    char dc_id[4];                  /**< chunk id \"SSND\"               */\n    int32_t dc_size;                /**< chunk data length             */\n    uint32_t dc_offset;             /**< additional offset in bytes    */\n    uint32_t dc_block;              /**< block size                    */\n} t_datachunk;\n\n    /** AIFF-C format version chunk, 12 bytes */\ntypedef struct _verchunk\n{\n    char vc_id[4];                  /**< chunk id \"FVER\"               */\n    int32_t vc_size;                /**< chunk data length, 4 bytes    */\n    uint32_t vc_timestamp;          /**< AIFF-C version timestamp      */\n} t_verchunk;\n\n/* ----- helpers ----- */\n\n    /** returns 1 if format requires AIFF-C */\nstatic int aiff_isaiffc(const t_soundfile *sf)\n{\n    return (!sf->sf_bigendian || sf->sf_bytespersample == 4);\n}\n\n    /** pascal string to c string, max size 256, returns *total* pstring size */\nstatic int aiff_getpstring(const char* pstring, char *cstring)\n{\n    uint8_t len = (uint8_t)pstring[0];\n    memcpy(cstring, pstring + 1, len);\n    cstring[len] = '\\0';\n    len++; /* length byte */\n    if (len & 1) len++; /* pad byte */\n    return len;\n}\n\n    /** c string to pascal string, max size 256, returns *total* pstring size */\nstatic int aiff_setpstring(char *pstring, const char *cstring)\n{\n    uint8_t len = strlen(cstring);\n    pstring[0] = len;\n    memcpy(pstring + 1, cstring, len);\n    len++; /* length byte */\n    if (len & 1)\n    {\n        pstring[len] = '\\0'; /* pad byte */\n        len++;\n    }\n    return len;\n}\n\n    /** read byte buffer to unsigned 32 bit int */\nstatic uint32_t aiff_get4(const uint8_t *src, int swap)\n{\n    uint32_t uinttmp = 0;\n    memcpy(&uinttmp, src, 4);\n    return swap4(uinttmp, swap);\n}\n\n    /** write unsigned 32 bit int to byte buffer */\nstatic void aiff_set4(uint8_t* dst, uint32_t ui, int swap)\n{\n    ui = swap4(ui, swap);\n    memcpy(dst, &ui, 4);\n}\n\n    /** read sample rate from comm chunk 80-bit AIFF-compatible number */\nstatic double aiff_getsamplerate(const uint8_t *src, int swap)\n{\n    unsigned char temp[10], *p = temp, exponent;\n    unsigned long mantissa, last = 0;\n\n    memcpy(temp, src, 10);\n    swapstring4((char *)p + 2, swap);\n\n    mantissa = (uint32_t) *((uint32_t *)(p + 2));\n    exponent = 30 - *(p + 1);\n    while (exponent--)\n    {\n        last = mantissa;\n        mantissa >>= 1;\n    }\n    if (last & 0x00000001) mantissa++;\n    return mantissa;\n}\n\n    /** write a sample rate to comm chunk 80-bit AIFF-compatible number */\nstatic void aiff_setsamplerate(uint8_t *dst, double sr)\n{\n    int exponent;\n    double mantissa = frexp(sr, &exponent);\n    unsigned long fixmantissa = ldexp(mantissa, 32);\n    dst[0] = (exponent + 16382) >> 8;\n    dst[1] = exponent + 16382;\n    dst[2] = fixmantissa >> 24;\n    dst[3] = fixmantissa >> 16;\n    dst[4] = fixmantissa >> 8;\n    dst[5] = fixmantissa;\n    dst[6] = dst[7] = dst[8] = dst[9] = 0;\n}\n\n    /** read first chunk, returns filled chunk and offset on success or -1 */\nstatic off_t aiff_firstchunk(const t_soundfile *sf, t_chunk *chunk)\n{\n    if (fd_read(sf->sf_fd, AIFFHEADSIZE, (char *)chunk,\n        AIFFCHUNKSIZE) < AIFFCHUNKSIZE)\n        return -1;\n    return AIFFHEADSIZE;\n}\n\n    /** read next chunk, chunk should be filled when calling\n        returns fills chunk offset on success or -1 */\nstatic off_t aiff_nextchunk(const t_soundfile *sf, off_t offset, t_chunk *chunk)\n{\n    int32_t chunksize = swap4s(chunk->c_size, !sys_isbigendian());\n    off_t seekto = offset + AIFFCHUNKSIZE + chunksize;\n    if (seekto & 1) /* pad up to even number of bytes */\n        seekto++;\n    if (fd_read(sf->sf_fd, seekto, (char *)chunk,\n        AIFFCHUNKSIZE) < AIFFCHUNKSIZE)\n        return -1;\n    return seekto;\n}\n\n#ifdef DEBUG_SOUNDFILE\n\n    /** post chunk info for debugging */\nstatic void aiff_postchunk(const t_chunk *chunk, int swap)\n{\n    post(\"%.4s %d\", chunk->c_id, swap4s(chunk->c_size, swap));\n}\n\n    /** post head info for debugging */\nstatic void aiff_posthead(const t_head *head, int swap)\n{\n    aiff_postchunk((const t_chunk *)head, swap);\n    post(\"  %.4s\", head->h_formtype);\n}\n\n    /** post comm info for debugging */\nstatic void aiff_postcomm(const t_commchunk *comm, int isaiffc, int swap)\n{\n    aiff_postchunk((const t_chunk *)comm, swap);\n    post(\"  channels %u\", swap2(comm->cc_nchannels, swap));\n    post(\"  frames %u\", aiff_get4(comm->cc_nframes, swap));\n    post(\"  bits per sample %u\", swap2(comm->cc_bitspersample, swap));\n    post(\"  sample rate %g\", aiff_getsamplerate(comm->cc_samplerate, swap));\n    if (isaiffc)\n    {\n            /* handle pascal string */\n        char name[256];\n        aiff_getpstring(comm->cc_compname, name);\n        post(\"  comp type %.4s\", comm->cc_comptype);\n        post(\"  comp name \\\"%s\\\"\", name);\n    }\n}\n\n    /** post sata info for debugging */\nstatic void aiff_postdata(const t_datachunk *data, int swap)\n{\n    aiff_postchunk((const t_chunk *)data, swap);\n    post(\"  offset %u\", swap4(data->dc_offset, swap));\n    post(\"  block size %u\", swap4(data->dc_block, swap));\n}\n\n#endif /* DEBUG_SOUNDFILE */\n\n/* ------------------------- AIFF ------------------------- */\n\nstatic int aiff_isheader(const char *buf, size_t size)\n{\n    if (size < 4) return 0;\n    return !strncmp(buf, \"FORM\", 4);\n}\n\n    /** loop through chunks to find comm and data */\nstatic int aiff_readheader(t_soundfile *sf)\n{\n    int nchannels = 1, bytespersample = 2, samplerate = 44100, bigendian = 1,\n        swap = !sys_isbigendian(), isaiffc = 0, commfound = 0;\n    off_t headersize = AIFFHEADSIZE;\n    size_t bytelimit = AIFFMAXBYTES;\n    union\n    {\n        char b_c[SFHDRBUFSIZE];\n        t_head b_head;\n        t_chunk b_chunk;\n        t_commchunk b_commchunk;\n        t_verchunk b_verchunk;\n        t_datachunk b_datachunk;\n    } buf = {0};\n    t_head *head = &buf.b_head;\n    t_chunk *chunk = &buf.b_chunk;\n\n        /* file header */\n    if (fd_read(sf->sf_fd, 0, buf.b_c, headersize) < headersize)\n        return 0;\n    if (strncmp(head->h_formtype, \"AIFF\", 4))\n    {\n        if (strncmp(head->h_formtype, \"AIFC\", 4))\n            return 0;\n        isaiffc = 1;\n    }\n#ifdef DEBUG_SOUNDFILE\n    aiff_posthead(head, swap);\n#endif\n\n        /* read chunks in loop until we find the sound data chunk */\n    if ((headersize = aiff_firstchunk(sf, chunk)) == -1)\n        return 0;\n    while (1)\n    {\n        int32_t chunksize = swap4s(chunk->c_size, swap);\n        /* post(\"chunk %.4s seek %d\", chunk->c_id, seekto); */\n        if (!strncmp(chunk->c_id, \"FVER\", 4))\n        {\n                /* AIFF-C format version chunk */\n            if (!isaiffc)\n            {\n                errno = SOUNDFILE_ERRMALFORMED;\n                return 0;\n            }\n            if (fd_read(sf->sf_fd, headersize + AIFFCHUNKSIZE,\n                    buf.b_c + AIFFCHUNKSIZE, chunksize) < chunksize)\n                return 0;\n            if (swap4(buf.b_verchunk.vc_timestamp, swap) != AIFFCVER1)\n            {\n                errno = SOUNDFILE_ERRVERSION;\n                return 0;\n            }\n        }\n        else if (!strncmp(chunk->c_id, \"COMM\", 4))\n        {\n                /* common chunk */\n            int bitspersample, isfloat = 0;\n            t_commchunk *comm = &buf.b_commchunk;\n            if (fd_read(sf->sf_fd, headersize + AIFFCHUNKSIZE,\n                    buf.b_c + AIFFCHUNKSIZE, chunksize) < chunksize)\n                return 0;\n#ifdef DEBUG_SOUNDFILE\n            aiff_postcomm(comm, isaiffc, swap);\n#endif\n            nchannels = swap2(comm->cc_nchannels, swap);\n            bitspersample = swap2(comm->cc_bitspersample, swap);\n            switch (bitspersample)\n            {\n                case 16: bytespersample = 2; break;\n                case 24: bytespersample = 3; break;\n                case 32: bytespersample = 4; break;\n                default:\n                {\n                    errno = SOUNDFILE_ERRSAMPLEFMT;\n                    return 0;\n                }\n            }\n            samplerate = aiff_getsamplerate(comm->cc_samplerate, swap);\n            if (isaiffc)\n            {\n                if (!strncmp(comm->cc_comptype, \"NONE\", 4))\n                {\n                        /* big endian */\n                }\n                else if (!strncmp(comm->cc_comptype, \"sowt\", 4))\n                {\n                        /* little endian */\n                    bigendian = 0;\n                }\n                else if (!strncmp(comm->cc_comptype, \"fl32\", 4) ||\n                         !strncmp(comm->cc_comptype, \"FL32\", 4))\n                {\n                    if (bytespersample != 4)\n                    {\n                        errno = SOUNDFILE_ERRMALFORMED;\n                        return 0;\n                    }\n                    isfloat = 1;\n                }\n                else\n                {\n                    errno = SOUNDFILE_ERRSAMPLEFMT;\n                    return 0;\n                }\n            }\n            if (bytespersample == 4 && !isfloat)\n            {\n                    /* 32 bit int */\n                errno = SOUNDFILE_ERRSAMPLEFMT;\n                return 0;\n            }\n            commfound = 1;\n        }\n        else if (!strncmp(chunk->c_id, \"SSND\", 4))\n        {\n                /* uncompressed sound data chunk */\n#ifdef DEBUG_SOUNDFILE\n            t_datachunk *data = &buf.b_datachunk;\n            if (fd_read(sf->sf_fd, headersize + AIFFCHUNKSIZE,\n                    buf.b_c + AIFFCHUNKSIZE, 8) < 8)\n                return 0;\n            aiff_postdata(data, swap);\n#endif\n            bytelimit = chunksize - 8; /* - offset and block */\n            headersize += AIFFDATASIZE;\n            break;\n        }\n        else if (!strncmp(chunk->c_id, \"CSND\", 4))\n        {\n                /* AIFF-C compressed sound data chunk */\n            errno = SOUNDFILE_ERRSAMPLEFMT;\n            return 0;\n        }\n#ifdef DEBUG_SOUNDFILE\n        else\n        {\n                /* everything else */\n            aiff_postchunk(chunk, swap);\n        }\n#endif\n        if ((headersize = aiff_nextchunk(sf, headersize, chunk)) == -1)\n            return 0;\n    }\n    if (!commfound)\n    {\n        errno = SOUNDFILE_ERRMALFORMED;\n        return 0;\n    }\n\n        /* interpret data size from file size? this is not supported by the\n            AIFF spec, but let's do it just in case */\n    if (bytelimit == AIFFMAXBYTES)\n    {\n        bytelimit = lseek(sf->sf_fd, 0, SEEK_END) - headersize;\n            bytelimit = AIFFMAXBYTES;\n    }\n\n        /* copy sample format back to caller */\n    sf->sf_samplerate = samplerate;\n    sf->sf_nchannels = nchannels;\n    sf->sf_bytespersample = bytespersample;\n    sf->sf_headersize = headersize;\n    sf->sf_bytelimit = bytelimit;\n    sf->sf_bigendian = bigendian;\n    sf->sf_bytesperframe = nchannels * bytespersample;\n\n    return 1;\n}\n\n    /** write basic header with order: head [ver] comm data */\nstatic int aiff_writeheader(t_soundfile *sf, size_t nframes)\n{\n    int isaiffc = aiff_isaiffc(sf), swap = !sys_isbigendian();\n    size_t commsize = AIFFCOMMSIZE, datasize = nframes * sf->sf_bytesperframe;\n    off_t headersize = 0;\n    ssize_t byteswritten = 0;\n    char buf[SFHDRBUFSIZE] = {0};\n    t_head head = {\"FORM\", 0, \"AIFF\"};\n    t_commchunk comm = {\n        \"COMM\", swap4s(18, swap),\n        swap2(sf->sf_nchannels, swap),          /* channels         */\n        {0},                                      /* sample frames    */\n        swap2(sf->sf_bytespersample / 8, swap), /* bits per sample  */\n        {0},                                      /* sample rate      */\n        {0}, {0}                                    /* comp info        */\n    };\n    t_datachunk data = {\"SSND\", swap4s(8, swap), 0, 0};\n\n        /* file header */\n    if (isaiffc)\n        strncpy(head.h_formtype, \"AIFC\", 4);\n    memcpy(buf + headersize, &head, AIFFHEADSIZE);\n    headersize += AIFFHEADSIZE;\n\n        /* format ver chunk */\n    if (isaiffc)\n    {\n        t_verchunk ver = {\n            \"FVER\", swap4s(4, swap),\n            swap4(AIFFCVER1, swap) /* version timestamp */\n        };\n        memcpy(buf + headersize, &ver, AIFFVERSIZE);\n        headersize += AIFFVERSIZE;\n    }\n\n        /* comm chunk */\n    comm.cc_nchannels = swap2(sf->sf_nchannels, swap);\n    aiff_set4(comm.cc_nframes, nframes, swap);\n    comm.cc_bitspersample = swap2(8 * sf->sf_bytespersample, swap);\n    aiff_setsamplerate(comm.cc_samplerate, sf->sf_samplerate);\n    if (isaiffc)\n    {\n            /* AIFF-C compression info */\n        if (sf->sf_bytespersample == 4)\n        {\n            strncpy(comm.cc_comptype, \"fl32\", 4);\n            commsize += 4 + aiff_setpstring(comm.cc_compname, AIFF_FL32_STR);\n        }\n        else\n        {\n            strncpy(comm.cc_comptype, (sf->sf_bigendian ? \"NONE\" : \"sowt\"), 4);\n            commsize += 4 + aiff_setpstring(comm.cc_compname, AIFF_NONE_STR);\n        }\n    }\n    comm.cc_size = swap4(commsize - AIFFCHUNKSIZE, swap);\n    memcpy(buf + headersize, &comm, commsize);\n    headersize += commsize;\n\n        /* data chunk (+ offset & block) */\n    data.dc_size = swap4((uint32_t)(datasize + 8), swap);\n    memcpy(buf + headersize, &data, AIFFDATASIZE);\n    headersize += AIFFDATASIZE;\n\n        /* update file header chunk size (- chunk header) */\n    head.h_size = swap4s((int32_t)(headersize + datasize - 8), swap);\n    memcpy(buf + 4, &head.h_size, 4);\n\n#ifdef DEBUG_SOUNDFILE\n    aiff_posthead(&head, swap);\n    aiff_postcomm(&comm, isaiffc, swap);\n    aiff_postdata(&data, swap);\n#endif\n\n    byteswritten = fd_write(sf->sf_fd, 0, buf, headersize);\n    return (byteswritten < headersize ? -1 : byteswritten);\n}\n\n    /** assumes chunk order:\n        * AIFF  : head comm data\n        * AIFF-C: head version comm data, comm chunk size variable due to str */\nstatic int aiff_updateheader(t_soundfile *sf, size_t nframes)\n{\n    int isaiffc = aiff_isaiffc(sf), swap = !sys_isbigendian();\n    size_t datasize = nframes * sf->sf_bytesperframe,\n           headersize = AIFFHEADSIZE, commsize = AIFFCOMMSIZE;\n    uint32_t uinttmp;\n    int32_t inttmp;\n\n    if (isaiffc)\n    {\n            /* AIFF-C compression info */\n        if (sf->sf_bytespersample == 4)\n            commsize += 4 + AIFF_FL32_LEN;\n        else\n            commsize += 4 + AIFF_NONE_LEN;\n        headersize += AIFFVERSIZE;\n    }\n\n        /* num frames */\n    uinttmp = swap4((uint32_t)nframes, swap);\n    if (fd_write(sf->sf_fd, headersize + 10, &uinttmp, 4) < 4)\n        return 0;\n    headersize += commsize;\n\n        /* data chunk size (+ offset & block) */\n    inttmp = swap4s((int32_t)datasize + 8, swap);\n    if (fd_write(sf->sf_fd, headersize + 4, &inttmp, 4) < 4)\n        return 0;\n    headersize += AIFFDATASIZE;\n\n        /* file header chunk size (- chunk header) */\n    inttmp = swap4s((int32_t)(headersize + datasize - 8), swap);\n    if (fd_write(sf->sf_fd, 4, &inttmp, 4) < 4)\n        return 0;\n\n#ifdef DEBUG_SOUNDFILE\n    post(\"FORM %d\", headersize + datasize - 8);\n    post(\"  %s\", (aiff_isaiffc(sf) ? \"AIFC\" : \"AIFF\"));\n    post(\"COMM %d\", commsize);\n    post(\"  frames %d\", nframes);\n    post(\"SSND %d\", datasize + 8);\n#endif\n\n    return 1;\n}\n\nstatic int aiff_hasextension(const char *filename, size_t size)\n{\n    int len = strnlen(filename, size);\n    if (len >= 5 &&\n        (!strncmp(filename + (len - 4), \".aif\", 4) ||\n         !strncmp(filename + (len - 4), \".AIF\", 4)))\n        return 1;\n    if (len >= 6 &&\n        (!strncmp(filename + (len - 5), \".aiff\", 5) ||\n         !strncmp(filename + (len - 5), \".aifc\", 5) ||\n         !strncmp(filename + (len - 5), \".AIFF\", 5) ||\n         !strncmp(filename + (len - 5), \".AIFC\", 5)))\n        return 1;\n    return 0;\n}\n\nstatic int aiff_addextension(char *filename, size_t size)\n{\n    int len = strnlen(filename, size);\n    if (len + 4 >= size)\n        return 0;\n    strcpy(filename + len, \".aif\");\n    return 1;\n}\n\n    /* default to big endian unless overridden */\nstatic int aiff_endianness(int endianness)\n{\n    if (endianness == 0)\n        return 0;\n    return 1;\n}\n\n/* ------------------------- setup routine ------------------------ */\n\nstatic const t_soundfile_type aiff = {\n    \"aiff\",\n    AIFFHEADSIZE + AIFFCOMMSIZE + AIFFDATASIZE,\n    aiff_isheader,\n    aiff_readheader,\n    aiff_writeheader,\n    aiff_updateheader,\n    aiff_hasextension,\n    aiff_addextension,\n    aiff_endianness\n};\n\nvoid soundfile_aiff_setup( void)\n{\n    soundfile_addtype(&aiff);\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/d_soundfile_caf.c",
    "content": "/* Copyright (c) 2020 Dan Wilcox.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* ref: https://developer.apple.com/library/archive/documentation/MusicAudio/Reference/CAFSpec/CAF_spec/CAF_spec.html */\n\n#include \"d_soundfile.h\"\n\n#ifndef _MSC_VER\n#include <inttypes.h>\n#endif\n\n/* CAF (Core Audio Format)\n\n  * RIFF variant with sections split into data \"chunks\"\n  * chunk sizes do not include the chunk id or size (- 12)\n  * chunk data is big-endian\n  * sound data can be big or little endian (set in desc fmtflags)\n  * header is immediately followed by description chunk\n  * data chunk:\n     - size > 0: can be anywhere after the description chunk\n     - size -1: last chunk in the file,\n                size = file size - (data chunk pos + 12)\n\n  this implementation:\n\n  * supports CAF version 1 only\n  * implements chunks: description, data\n  * ignores chunks: packet table, magic cookie, strings, marker, region,\n                    instrument, MIDI, overview, peak, edit comments,\n                    information, unique material identifier, user-defined\n  * ignores any chunks after finding the data chunk\n  * sample format: 16 and 24 bit lpcm, 32 bit float, no 32 bit lpcm\n\n  Pd versions < 0.51 did *not* read or write CAF files.\n\n*/\n\n    /* explicit byte sizes, sizeof(struct) can return alignment padded values */\n#define CAFHEADSIZE     8 /**< chunk header only */\n#define CAFCHUNKSIZE   12 /**< chunk header and format */\n#define CAFDESCSIZE    44 /**< chunk header and data */\n#define CAFDATASIZE    16 /**< chunk header and edit count data */\n\n#define CAFMAXBYTES SFMAXBYTES /** max signed 64 bit size */\n\n#define CAF_UNKNOWN_SIZE 0xffffffffffffffff /** aka -1 */\n\n    /** Apple-style description format flags */\nenum {\n    kCAFLinearPCMFormatFlagIsFloat        = (1L << 0),\n    kCAFLinearPCMFormatFlagIsLittleEndian = (1L << 1)\n};\n\n    /** basic chunk header, 12 bytes\n        note: size is split to avoid struct alignment padding */\ntypedef struct _chunk {\n    char c_id[4];                /**< chunk id                        */\n    uint8_t c_size[8];           /**< chunk data length, int64_t      */\n} t_chunk;\n\n    /** file head container chunk, 8 bytes */\ntypedef struct _head {\n    char h_id[4];                /**< file id \"caff\"                  */\n    uint16_t h_version;          /**< file version, probably 1        */\n    uint16_t h_flags;            /**< format flags, 0 for CAF v1      */\n} t_head;\n\n    /** description chunk, 44 bytes\n        note: size and samplerate are split to avoid struct alignment padding */\ntypedef struct _descchunk {\n    char ds_id[4];               /**< chunk id \"desc\"                 */\n    uint8_t ds_size[8];          /**< chunk data length, int64_t      */\n    uint8_t ds_samplerate[8];    /**< sample rate, double             */\n    char ds_fmtid[4];            /**< format id, 4 letter char code   */\n    uint32_t ds_fmtflags;        /**< format flags, set 0 for \"none\"  */\n    uint32_t ds_bytesperpacket;  /**< bytes per packet                */\n    uint32_t ds_framesperpacket; /**< uncompressed: 1 pckt = 1 frame  */\n    uint32_t ds_nchannels;       /**< number of channels              */\n    uint32_t ds_bitsperchannel;  /**< bits per chan in 1 sample frame */\n} t_descchunk;\n\n    /** data chunk, min 16 bytes\n        note: size is split to avoid struct alignment padding */\ntypedef struct _datachunk {\n    char dc_id[4];               /**< chunk id \"data\"                 */\n    uint8_t dc_size[8];          /**< chunk data length, int64_t      */\n    uint32_t dc_editcount;       /**< edit count, set 0 for none      */\n} t_datachunk;\n\n/* ----- helpers ----- */\n\nstatic int64_t caf_getchunksize(const t_chunk *chunk, int swap)\n{\n    int64_t size = 0;\n    memcpy(&size, chunk->c_size, 8);\n    return swap8s(size, swap);\n}\n\nstatic void caf_setchunksize(t_chunk *chunk, int64_t size, int swap)\n{\n    size = swap8s(size, swap);\n    memcpy(chunk->c_size, &size, 8);\n}\n\nstatic double caf_getsamplerate(const t_descchunk *desc, int swap)\n{\n    double sr = 0;\n    memcpy(&sr, desc->ds_samplerate, 8);\n    swapstring8((char *)&sr, swap);\n    return sr;\n}\n\nstatic void caf_setsamplerate(t_descchunk *desc, double sr, int swap)\n{\n    memcpy(desc->ds_samplerate, &sr, 8);\n    swapstring8((char *)desc->ds_samplerate, swap);\n}\n\n    /** read first chunk, returns filled chunk and offset on success or -1 */\nstatic off_t caf_firstchunk(const t_soundfile *sf, t_chunk *chunk)\n{\n    if (fd_read(sf->sf_fd, CAFHEADSIZE, (char *)chunk,\n        CAFCHUNKSIZE) < CAFCHUNKSIZE)\n        return -1;\n    return CAFHEADSIZE;\n}\n\n    /** read next chunk, chunk should be filled when calling\n        returns fills chunk offset on success or -1 */\nstatic off_t caf_nextchunk(const t_soundfile *sf, off_t offset, t_chunk *chunk)\n{\n    int64_t chunksize = caf_getchunksize(chunk, !sys_isbigendian());\n    off_t seekto = offset + CAFCHUNKSIZE + chunksize;\n    if (seekto & 1) /* pad up to even number of bytes */\n        seekto++;\n    if (fd_read(sf->sf_fd, seekto, (char *)chunk,\n        CAFCHUNKSIZE) < CAFCHUNKSIZE)\n        return -1;\n    return seekto;\n}\n\n#ifdef DEBUG_SOUNDFILE\n\n    /** post head info for debugging */\nstatic void caf_posthead(const t_head *head, int swap)\n{\n    post(\"%.4s\", head->h_id);\n    post(\"  version %d\", swap2(head->h_version, swap));\n    post(\"  flags %d\", swap2(head->h_flags, swap));\n}\n\n    /** post chunk info for debugging */\nstatic void caf_postchunk(const t_chunk *chunk, int swap)\n{\n    post(\"%.4s %\" PRId64, chunk->c_id, caf_getchunksize(chunk, swap));\n}\n\n    /** post desc info for debugging */\nstatic void caf_postdesc(const t_descchunk *desc, int swap)\n{\n    uint32_t fmtflags = swap4(desc->ds_fmtflags, swap);\n    caf_postchunk((t_chunk *)desc, swap);\n    post(\"  sample rate %g\", caf_getsamplerate(desc, swap));\n    post(\"  fmt id %.4s\", desc->ds_fmtid);\n    post(\"  fmt flags %d (%s %s)\", fmtflags,\n        (fmtflags & kCAFLinearPCMFormatFlagIsLittleEndian ? \"little\" : \"big\"),\n        (fmtflags & kCAFLinearPCMFormatFlagIsFloat ? \"float\" : \"int\"));\n    post(\"  bytes per packet %d\", swap4(desc->ds_bytesperpacket, swap));\n    post(\"  frames per packet %d\", swap4(desc->ds_framesperpacket, swap));\n    post(\"  channels %d\", swap4(desc->ds_nchannels, swap));\n    post(\"  bits per channel %d\", swap4(desc->ds_bitsperchannel, swap));\n}\n\n    /** post data header info for debugging, currently edit count only */\nstatic void caf_postdata(const t_datachunk *data, int swap)\n{\n    caf_postchunk((t_chunk *)data, swap);\n    post(\"  edit count %d\", swap4(data->dc_editcount, swap));\n}\n\n#endif /* DEBUG_SOUNDFILE */\n\n/* ------------------------- CAF -------------------------- */\n\nstatic int caf_isheader(const char *buf, size_t size)\n{\n    if (size < 4) return 0;\n    return !strncmp(buf, \"caff\", 4);\n}\n\nstatic int caf_readheader(t_soundfile *sf)\n{\n    int nchannels = 1, bytespersample = 2, samplerate = 44100, bigendian = 1,\n        fmtflags, swap = !sys_isbigendian();\n    off_t headersize = CAFHEADSIZE + CAFDESCSIZE;\n    ssize_t bytelimit = CAFMAXBYTES;\n    union\n    {\n        char b_c[SFHDRBUFSIZE];\n        t_head b_head;\n        t_chunk b_chunk;\n        t_descchunk b_descchunk;\n        t_datachunk b_datachunk;\n    } buf = {0};\n    t_head *head = &buf.b_head;\n    t_chunk *chunk = &buf.b_chunk;\n    t_descchunk *desc = &buf.b_descchunk;\n\n        /* file header */\n    if (fd_read(sf->sf_fd, 0, buf.b_c, headersize) < headersize)\n        return 0;\n    if (strncmp(head->h_id, \"caff\", 4))\n        return 0;\n    if (swap2(head->h_version, swap) != 1)\n    {\n        errno = SOUNDFILE_ERRVERSION;\n        return 0;\n    }\n    if (swap2(head->h_flags, swap) != 0)\n    {\n            /* current spec says these should be empty */\n        errno = SOUNDFILE_ERRVERSION;\n        return 0;\n    }\n#ifdef DEBUG_SOUNDFILE\n    caf_posthead(head, swap);\n#endif\n\n        /* copy the first chunk header to beginning of buffer,\n           use memmove as src & dst are the same */\n    memmove(buf.b_c, buf.b_c + CAFHEADSIZE, CAFDESCSIZE);\n    headersize = CAFHEADSIZE;\n\n        /* first chunk must be description */\n    if (strncmp(desc->ds_id, \"desc\", 4))\n        return 0;\n#ifdef DEBUG_SOUNDFILE\n    caf_postdesc(desc, swap);\n#endif\n    if (strncmp(desc->ds_fmtid, \"lpcm\", 4))\n    {\n        errno = SOUNDFILE_ERRSAMPLEFMT;\n        return 0;\n    }\n    nchannels = swap4(desc->ds_nchannels, swap);\n    fmtflags = swap4(desc->ds_fmtflags, swap);\n    bytespersample = swap4(desc->ds_bitsperchannel, swap) / 8;\n    switch (bytespersample)\n    {\n        case 2: case 3: case 4: break;\n        default:\n            errno = SOUNDFILE_ERRSAMPLEFMT;\n            return 0;\n    }\n    if (bytespersample == 4 && !(fmtflags & kCAFLinearPCMFormatFlagIsFloat))\n    {\n        errno = SOUNDFILE_ERRSAMPLEFMT;\n        return 0;\n    }\n    bigendian = !(fmtflags & kCAFLinearPCMFormatFlagIsLittleEndian);\n    samplerate = caf_getsamplerate(desc, swap);\n\n        /* read chunks in loop until we find the sound data chunk */\n    if ((headersize = caf_nextchunk(sf, headersize, chunk)) == -1)\n        return 0;\n    while (1)\n    {\n        int64_t chunksize = caf_getchunksize(chunk, swap);\n        off_t seekto = headersize + CAFCHUNKSIZE + chunksize, seekout;\n        if (seekto & 1) /* pad up to even number of bytes */\n            seekto++;\n        /* post(\"chunk %.4s seek %d\", chunk->c_id, seekto); */\n        if (!strncmp(chunk->c_id, \"data\", 4))\n        {\n                /* sound data chunk */\n#ifdef DEBUG_SOUNDFILE\n            t_datachunk *data = &buf.b_datachunk;\n            if (fd_read(sf->sf_fd, headersize + CAFCHUNKSIZE,\n                    buf.b_c + CAFCHUNKSIZE, 4) < 4)\n                return 0;\n            caf_postdata(data, swap);\n#endif\n            headersize += CAFDATASIZE;\n            if (chunksize == CAF_UNKNOWN_SIZE)\n            {\n                    /* interpret data size from file size */\n                bytelimit = lseek(sf->sf_fd, 0, SEEK_END) - headersize;\n                if (bytelimit > CAFMAXBYTES || bytelimit < 0)\n                    bytelimit = CAFMAXBYTES;\n            }\n            else\n                bytelimit = chunksize - 4; /* - edit count */\n            break;\n        }\n#ifdef DEBUG_SOUNDFILE\n        else\n        {\n                /* everything else */\n            caf_postchunk(chunk, swap);\n        }\n#endif\n        if ((headersize = caf_nextchunk(sf, headersize, chunk)) == -1)\n            return 0;\n    }\n\n        /* copy sample format back to caller */\n    sf->sf_samplerate = samplerate;\n    sf->sf_nchannels = nchannels;\n    sf->sf_bytespersample = bytespersample;\n    sf->sf_headersize = headersize;\n    sf->sf_bytelimit = bytelimit;\n    sf->sf_bigendian = bigendian;\n    sf->sf_bytesperframe = nchannels * bytespersample;\n\n    return 1;\n}\n\nstatic int caf_writeheader(t_soundfile *sf, size_t nframes)\n{\n    int swap = !sys_isbigendian();\n    size_t datasize =\n        (nframes > 0 ? nframes * sf->sf_bytesperframe : CAF_UNKNOWN_SIZE);\n    off_t headersize = 0;\n    ssize_t byteswritten = 0;\n    uint32_t uinttmp = 0;\n    char buf[SFHDRBUFSIZE] = {0};\n    t_head head = {\"caff\", swap2(1, swap), 0};\n    t_descchunk desc = {\"desc\", {0}, {0}};\n    t_datachunk data = {\"data\", {0}, 0};\n\n        /* file header */\n    memcpy(buf + headersize, &head, CAFHEADSIZE);\n    headersize += CAFHEADSIZE;\n\n        /* description chunk */\n    caf_setchunksize((t_chunk *)&desc, CAFDESCSIZE - CAFCHUNKSIZE, swap);\n    caf_setsamplerate(&desc, sf->sf_samplerate, swap);\n    strncpy(desc.ds_fmtid, \"lpcm\", 4);\n    if (sf->sf_bytespersample == 4)\n        uinttmp |= kCAFLinearPCMFormatFlagIsFloat;\n    if (!sf->sf_bigendian)\n        uinttmp |= kCAFLinearPCMFormatFlagIsLittleEndian;\n    desc.ds_fmtflags = swap4(uinttmp, swap);\n    desc.ds_bytesperpacket = swap4(sf->sf_bytesperframe, swap);\n    desc.ds_framesperpacket = swap4(1, swap);\n    desc.ds_nchannels = swap4(sf->sf_nchannels, swap);\n    desc.ds_bitsperchannel = swap4(sf->sf_bytespersample * 8, swap);\n    memcpy(buf + headersize, &desc, CAFDESCSIZE);\n    headersize += CAFDESCSIZE;\n\n        /* data chunk (+ edit count) */\n    caf_setchunksize((t_chunk *)&data, datasize + 4, swap);\n    memcpy(buf + headersize, &data, CAFDATASIZE);\n    headersize += CAFDATASIZE;\n\n#ifdef DEBUG_SOUNDFILE\n    caf_posthead(&head, swap);\n    caf_postdesc(&desc, swap);\n    caf_postdata(&data, swap);\n#endif\n\n    byteswritten = fd_write(sf->sf_fd, 0, buf, headersize);\n    return (byteswritten < headersize ? -1 : byteswritten);\n}\n\n    /** assumes chunk order: head desc data */\nstatic int caf_updateheader(t_soundfile *sf, size_t nframes)\n{\n    int swap = !sys_isbigendian();\n    int64_t datasize = swap8s((nframes * sf->sf_bytesperframe) + 4, swap);\n\n        /* data chunk size (+ edit count) */\n    if (fd_write(sf->sf_fd, CAFHEADSIZE + CAFDESCSIZE + 4, &datasize, 8) < 8)\n        return 0;\n\n#ifdef DEBUG_SOUNDFILE\n    post(\"caff\");\n    post(\"data %\" PRId64, swap8s(datasize, swap));\n#endif\n\n    return 1;\n}\n\nstatic int caf_hasextension(const char *filename, size_t size)\n{\n    int len = strnlen(filename, size);\n    if (len >= 5 &&\n        (!strncmp(filename + (len - 4), \".caf\", 4) ||\n         !strncmp(filename + (len - 4), \".CAF\", 4)))\n        return 1;\n    return 0;\n}\n\nstatic int caf_addextension(char *filename, size_t size)\n{\n    int len = strnlen(filename, size);\n    if (len + 4 >= size)\n        return 0;\n    strcpy(filename + len, \".caf\");\n    return 1;\n}\n\n    /* default to big endian if not specified */\nstatic int caf_endianness(int endianness)\n{\n    if (endianness == -1)\n        return 1;\n    return endianness;\n}\n\n/* ------------------------- setup routine ------------------------ */\n\nstatic const t_soundfile_type caf = {\n    \"caf\",\n    CAFHEADSIZE + CAFDESCSIZE + CAFDATASIZE,\n    caf_isheader,\n    caf_readheader,\n    caf_writeheader,\n    caf_updateheader,\n    caf_hasextension,\n    caf_addextension,\n    caf_endianness\n};\n\nvoid soundfile_caf_setup( void)\n{\n    soundfile_addtype(&caf);\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/d_soundfile_next.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette. Updated 2019 Dan Wilcox.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* refs: http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/AU/AU.html\n         https://pubs.opengroup.org/external/auformat.html\n         http://sox.sourceforge.net/AudioFormats-11.html#ss11.2\n         http://soundfile.sapp.org/doc/NextFormat */\n\n#include \"d_soundfile.h\"\n\n/* NeXTStep/Sun sound\n\n  * simple header followed by sound data\n  * header and sound data can be big or little endian, depending on id:\n    - \".snd\" big endian\n    - \"dns.\" little endian\n  * sound data length can be set to an \"unknown size\", in which case the\n    actual length is file size - sound data offset\n  * info string after 24 byte header is optional\n    - null terminated\n    - string len is sound data len - 24, min len is 4\n  * can be headerless, in which case is 8 bit u-law at 8000 Hz\n  * limited to ~4 GB files as sizes are unsigned 32 bit ints\n\n  this implementation:\n\n    * does not support headerless files\n    * supports big and little endian, system endianness used by default\n    * sets a default info string: \"Pd \"\n    * tries to set sound data length, otherwise falls back to \"unknown size\"\n    * sample format: 16 and 24 bit lpcm, 32 bit float, no 32 bit lpcm\n\n    Pd versions < 0.51 did *not* write the actual data chunk size when updating\n    the header, but set \"unknown size\" instead.\n\n*/\n\n    /* explicit byte sizes, sizeof(struct) may return alignment padded values */\n#define NEXTHEADSIZE 28 /**< min valid header size + info string */\n\n#define NEXTMAXBYTES 0xffffffff /**< max unsigned 32 bit size */\n\n#define NEXT_FORMAT_LINEAR_16 3 /**< 16 bit int */\n#define NEXT_FORMAT_LINEAR_24 4 /**< 24 bit int */\n#define NEXT_FORMAT_FLOAT     6 /**< 32 bit float */\n\n#define NEXT_UNKNOWN_SIZE 0xffffffff /** aka -1 */\n\ntypedef struct _nextstep\n{\n    char ns_id[4];          /**< magic number; \".snd\" (big endian) or \"dns.\" */\n    uint32_t ns_onset;      /**< offset to sound data in bytes               */\n    uint32_t ns_length;     /**< length of sound data in bytes               */\n    uint32_t ns_format;     /**< sound data format code; see below           */\n    uint32_t ns_samplerate; /**< sample rate                                 */\n    uint32_t ns_nchannels;  /**< number of channels                          */\n    char ns_info[4];        /**< info string, minimum 4 bytes up to onset    */\n} t_nextstep;\n\n/* ----- helpers ----- */\n\n    /** returns 1 if big endian, 0 if little endian, or -1 for bad header */\nstatic int next_isbigendian(const t_nextstep *next)\n{\n    if (!strncmp(next->ns_id, \".snd\", 4))\n        return 1;\n    else if(!strncmp(next->ns_id, \"dns.\", 4))\n        return 0;\n    return -1;\n}\n\n#ifdef DEBUG_SOUNDFILE\n\n    /** post head info for debugging */\nstatic void next_posthead(const t_nextstep *next, int swap)\n{\n    uint32_t onset = swap4(next->ns_onset, swap),\n             format = swap4(next->ns_format, swap),\n             datasize = swap4(next->ns_length, swap);\n    post(\"next %.4s (%s)\", next->ns_id,\n        (next_isbigendian(next) ? \"big\" : \"little\"));\n    post(\"  data onset %d\", onset);\n    if (datasize == NEXT_UNKNOWN_SIZE)\n        post(\"  data length -1\");\n    else\n        post(\"  data length %d\", datasize);\n    switch (format)\n    {\n        case NEXT_FORMAT_LINEAR_16:\n            post(\"  format %d (16 bit int)\", format);\n            break;\n        case NEXT_FORMAT_LINEAR_24:\n            post(\"  format %d (24 bit int)\", format);\n            break;\n        case NEXT_FORMAT_FLOAT:\n            post(\"  format %d (32 bit float)\", format);\n            break;\n        default:\n            post(\"  format %d (unsupported)\", format);\n            break;\n    }\n    post(\"  sample rate %d\", swap4(next->ns_samplerate, swap));\n    post(\"  channels %d\", swap4(next->ns_nchannels, swap));\n    post(\"  info \\\"%.4s%s\\\"\",\n        next->ns_info, (onset > NEXTHEADSIZE ? \"...\" : \"\"));\n}\n\n#endif /* DEBUG_SOUNDFILE */\n\n/* ------------------------- NEXT ------------------------- */\n\nstatic int next_isheader(const char *buf, size_t size)\n{\n    if (size < 4) return 0;\n    if (!strncmp(buf, \".snd\", 4) || !strncmp(buf, \"dns.\", 4))\n        return 1;\n    return 0;\n}\n\nstatic int next_readheader(t_soundfile *sf)\n{\n    int format, bytespersample, bigendian = 1, swap = 0;\n    off_t headersize = NEXTHEADSIZE;\n    size_t bytelimit = NEXTMAXBYTES;\n    union\n    {\n        char b_c[SFHDRBUFSIZE];\n        t_nextstep b_nextstep;\n    } buf = {0};\n    t_nextstep *next = &buf.b_nextstep;\n\n    if (fd_read(sf->sf_fd, 0, buf.b_c, headersize) < headersize)\n        return 0;\n\n    bigendian = next_isbigendian(next);\n    if (bigendian < 0)\n        return 0;\n    swap = (bigendian != sys_isbigendian());\n\n    headersize = swap4(next->ns_onset, swap);\n    if (headersize < NEXTHEADSIZE - 4) /* min valid header w/o info string */\n        return 0;\n\n    bytelimit = swap4(next->ns_length, swap);\n    if (bytelimit == NEXT_UNKNOWN_SIZE)\n    {\n            /* interpret data size from file size */\n        bytelimit = lseek(sf->sf_fd, 0, SEEK_END) - headersize;\n        if (bytelimit > NEXTMAXBYTES || bytelimit < 0)\n            bytelimit = NEXTMAXBYTES;\n    }\n\n    format = swap4(next->ns_format, swap);\n    switch (format)\n    {\n        case NEXT_FORMAT_LINEAR_16: bytespersample = 2; break;\n        case NEXT_FORMAT_LINEAR_24: bytespersample = 3; break;\n        case NEXT_FORMAT_FLOAT:     bytespersample = 4; break;\n        default:\n            errno = SOUNDFILE_ERRSAMPLEFMT;\n            return 0;\n    }\n\n#ifdef DEBUG_SOUNDFILE\n    next_posthead(next, swap);\n#endif\n\n        /* copy sample format back to caller */\n    sf->sf_samplerate = swap4(next->ns_samplerate, swap);\n    sf->sf_nchannels = swap4(next->ns_nchannels, swap);\n    sf->sf_bytespersample = bytespersample;\n    sf->sf_headersize = headersize;\n    sf->sf_bytelimit = bytelimit;\n    sf->sf_bigendian = bigendian;\n    sf->sf_bytesperframe = sf->sf_nchannels * bytespersample;\n\n    return 1;\n}\n\nstatic int next_writeheader(t_soundfile *sf, size_t nframes)\n{\n    int swap = soundfile_needsbyteswap(sf);\n    size_t datasize =\n        (nframes > 0 ? nframes * sf->sf_bytesperframe : NEXT_UNKNOWN_SIZE);\n    off_t headersize = NEXTHEADSIZE;\n    ssize_t byteswritten = 0;\n    t_nextstep next = {\n        \".snd\",                                   /* id          */\n        swap4((uint32_t)headersize, swap),        /* data onset  */\n        swap4((uint32_t)datasize, swap),          /* data length */\n        0,                                        /* format      */\n        swap4((uint32_t)sf->sf_samplerate, swap), /* sample rate */\n        swap4((uint32_t)sf->sf_nchannels, swap),  /* channels    */\n        \"Pd \"                                     /* info string */\n    };\n\n    if (!sf->sf_bigendian)\n        swapstring4(next.ns_id, 1);\n    switch (sf->sf_bytespersample)\n    {\n        case 2:\n            next.ns_format = swap4(NEXT_FORMAT_LINEAR_16, swap);\n            break;\n        case 3:\n            next.ns_format = swap4(NEXT_FORMAT_LINEAR_24, swap);\n            break;\n        case 4:\n            next.ns_format = swap4(NEXT_FORMAT_FLOAT, swap);\n            break;\n        default: /* unsupported format */\n            return 0;\n    }\n\n#ifdef DEBUG_SOUNDFILE\n    next_posthead(&next, swap);\n#endif\n\n    byteswritten = fd_write(sf->sf_fd, 0, &next, headersize);\n    return (byteswritten < headersize ? -1 : byteswritten);\n}\n\n    /** the data length is limited to 4 bytes, so if the size is too large,\n        do it the lazy way: just set the size field to \"unknown size\" */\nstatic int next_updateheader(t_soundfile *sf, size_t nframes)\n{\n    int swap = soundfile_needsbyteswap(sf);\n    size_t datasize = nframes * sf->sf_bytesperframe;\n\n    if (datasize > NEXTMAXBYTES)\n        datasize = NEXT_UNKNOWN_SIZE;\n    datasize = swap4(datasize, swap);\n    if (fd_write(sf->sf_fd, 8, &datasize, 4) < 4)\n        return 0;\n\n#ifdef DEBUG_SOUNDFILE\n    datasize = swap4(datasize, swap);\n    post(\"next %.4s (%s)\", (sf->sf_bigendian ? \".snd\" : \"dns.\"),\n        (sf->sf_bigendian ? \"big\" : \"little\"));\n    if (datasize == NEXT_UNKNOWN_SIZE)\n        post(\"  data length -1\");\n    else\n        post(\"  data length %d\", datasize);\n#endif\n\n    return 1;\n}\n\nstatic int next_hasextension(const char *filename, size_t size)\n{\n    int len = strnlen(filename, size);\n    if (len >= 4 &&\n        (!strncmp(filename + (len - 3), \".au\", 3) ||\n         !strncmp(filename + (len - 3), \".AU\", 3)))\n        return 1;\n    if (len >= 5 &&\n        (!strncmp(filename + (len - 4), \".snd\", 4) ||\n         !strncmp(filename + (len - 4), \".SND\", 4)))\n        return 1;\n    return 0;\n}\n\nstatic int next_addextension(char *filename, size_t size)\n{\n    int len = strnlen(filename, size);\n    if (len + 4 >= size)\n        return 0;\n    strcpy(filename + len, \".snd\");\n    return 1;\n}\n\n    /* machine native if not specified */\nstatic int next_endianness(int endianness)\n{\n    if (endianness == -1)\n        return sys_isbigendian();\n    return endianness;\n}\n\n/* ------------------------- setup routine ------------------------ */\n\nt_soundfile_type next = {\n    \"next\",\n    NEXTHEADSIZE - 4, /* - info string */\n    next_isheader,\n    next_readheader,\n    next_writeheader,\n    next_updateheader,\n    next_hasextension,\n    next_addextension,\n    next_endianness\n};\n\nvoid soundfile_next_setup( void)\n{\n    soundfile_addtype(&next);\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/d_soundfile_wave.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette. Updated 2019 Dan Wilcox.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* ref: http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html */\n\n#include \"d_soundfile.h\"\n\n/* WAVE (Waveform Audio File Format)\n\n  * RIFF variant with sections split into data \"chunks\"\n  * chunk sizes do not include the chunk id or size (- 8)\n  * chunk and sound data are little endian\n  * format and sound data chunks are required\n  * format tags:\n    - PCM   linear PCM\n    - FLOAT 32-bit float\n    - EXT   extended format -> described in subformat fields\n    - the rest are not relevant to Pd...\n  * extended format must be used when:\n    - PCM data has more than 16 bits per sample\n    - number of channels is more than 2\n    - mapping of channels to speakers is required (not relevant to Pd)\n  * a fact chunk is required when the format is non-PCM\n  * if sound data length is odd, a 0 pad byte should appended\n  * limited to ~4 GB files as sizes are unsigned 32 bit ints\n  * there are variants with 64-bit sizes (W64 and RF64) as well as extension\n    formats which can split sound data across multiple files (BWF)\n\n  this implementation:\n\n  * supports basic and extended format chunks (WAVE Rev. 3)\n  * implicitly writes extended format for 32 bit float (see below)\n  * implements chunks: format, fact, sound data\n  * ignores chunks: info, cset, cue, playlist, associated data, instrument,\n                    sample, display, junk, pad, time code, digitization time\n  * assumes format chunk is always before sound data chunk\n  * assumes there is only 1 sound data chunk\n  * does not support 64-bit variants or BWF file-splitting\n  * sample format: 16 and 24 bit lpcm, 32 bit float, no 32 bit lpcm\n\n  Pd versions < 0.51 did *not* read or write extended format explicitly, but\n  ignored the format chunk format tag and interpreted the sample type based on\n  the bits per sample: 2 : int 16, 3 : int 24, 4 : float 32. This means files\n  created by newer Pd versions are currently more or less compatible with older\n  Pd versions. This may change if 32 bit int support is introduced.\n\n*/\n\n    /* explicit byte sizes, sizeof(struct) may return alignment-padded values */\n#define WAVECHUNKSIZE   8 /**< chunk header only */\n#define WAVEHEADSIZE   12 /**< chunk header and file format only */\n#define WAVEFORMATSIZE 24 /**< chunk header and data */\n#define WAVEFACTSIZE   12 /**< chunk header and data */\n\n#define WAVEMAXBYTES 0xffffffff /**< max unsigned 32 bit size */\n\n#define WAVE_FORMAT_PCM   0x0001 /**< 16 or 24 bit int */\n#define WAVE_FORMAT_FLOAT 0x0003 /**< 32 bit float */\n#define WAVE_FORMAT_EXT   0xfffe /**< extended, see format chunk subformat */\n\n/** extended format header and data length */\n#define WAVE_EXT_SIZE 24\n\n/** 14 byte extended subformat GUID */\n#define WAVE_EXT_GUID \"\\x00\\x00\\x00\\x00\\x10\\x00\\x80\\x00\\x00\\xAA\\x00\\x38\\x9B\\x71\"\n\n    /** basic chunk header, 8 bytes */\ntypedef struct _chunk\n{\n    char c_id[4];                  /**< data chunk id                   */\n    uint32_t c_size;               /**< length of data chunk            */\n} t_chunk;\n\n    /** file head container chunk, 12 bytes */\ntypedef struct _head\n{\n    char h_id[4];                   /**< chunk id \"RIFF\"                */\n    uint32_t h_size;                /**< chunk data length              */\n    char h_formtype[4];             /**< format: \"WAVE\"                 */\n} t_head;\n\n    /** format chunk, 24 (basic) or 48 (extended) */\ntypedef struct _formatchunk\n{\n    char fc_id[4];                   /**< chunk id \"fmt \"               */\n    uint32_t fc_size;                /**< chunk data length             */\n    uint16_t fc_fmttag;              /**< format tag                    */\n    uint16_t fc_nchannels;           /**< number of channels            */\n    uint32_t fc_samplerate;          /**< sample rate in hz             */\n    uint32_t fc_bytespersecond;      /**< average bytes per second      */\n    uint16_t fc_blockalign;          /**< number of bytes per frame     */\n    uint16_t fc_bitspersample;       /**< number of bits in a sample    */\n    /* extended format */\n    uint16_t fc_extsize;             /**< extended format info length   */\n    uint16_t fc_validbitspersample;  /**< number of valid bits          */\n    uint32_t fc_channelmask;         /**< speaker pos channel mask      */\n    char fc_subformat[16];           /**< format tag is bytes 0 & 1     */\n} t_formatchunk;\n\n    /** fact chunk, 12 bytes */\ntypedef struct _factchunk\n{\n    char fc_id[4];                   /**< chunk id \"fact\"               */\n    uint32_t fc_size;                /**< chunk data length             */\n    uint32_t fc_samplelength;        /**< number of samples per channel */\n} t_factchunk;\n\n/* ----- helpers ----- */\n\n    /** returns 1 if format requires extended format and fact chunk */\nstatic int wave_isextended(const t_soundfile *sf)\n{\n    return sf->sf_bytespersample == 4;\n}\n\n    /** read first chunk, returns filled chunk and offset on success or -1 */\nstatic off_t wave_firstchunk(const t_soundfile *sf, t_chunk *chunk)\n{\n    if (fd_read(sf->sf_fd, WAVEHEADSIZE, (char *)chunk,\n        WAVECHUNKSIZE) < WAVECHUNKSIZE)\n        return -1;\n    return WAVEHEADSIZE;\n}\n\n    /** read next chunk, chunk should be filled when calling\n        returns fills chunk offset on success or -1 */\nstatic off_t wave_nextchunk(const t_soundfile *sf, off_t offset, t_chunk *chunk)\n{\n    uint32_t chunksize = swap4(chunk->c_size, sys_isbigendian());\n    off_t seekto = offset + WAVECHUNKSIZE + chunksize;\n    if (seekto & 1) /* pad up to even number of bytes */\n        seekto++;\n    if (fd_read(sf->sf_fd, seekto, (char *)chunk,\n        WAVECHUNKSIZE) < WAVECHUNKSIZE)\n        return -1;\n    return seekto;\n}\n\n#ifdef DEBUG_SOUNDFILE\n\n    /** post chunk info for debugging */\nstatic void wave_postchunk(const t_chunk *chunk, int swap)\n{\n    post(\"%.4s %d\", chunk->c_id, swap4s(chunk->c_size, swap));\n}\n\n    /** post head info for debugging */\nstatic void wave_posthead(const t_head *head, int swap)\n{\n    wave_postchunk((const t_chunk *)head, swap);\n    post(\"  %.4s\", head->h_formtype);\n}\n\n    /** post format info for debugging */\nstatic void wave_postformat(const t_formatchunk *format, int swap)\n{\n    uint16_t formattag = swap2(format->fc_fmttag, swap);\n    wave_postchunk((const t_chunk *)format, swap);\n    switch (formattag)\n    {\n        case WAVE_FORMAT_PCM:\n            post(\"  format %d (PCM)\", formattag);\n            break;\n        case WAVE_FORMAT_FLOAT:\n            post(\"  format %d (FLOAT)\", formattag);\n            break;\n        case WAVE_FORMAT_EXT:\n            post(\"  format %d (EXT)\", formattag);\n            break;\n        default:\n            post(\"  format %d (unsupported)\", formattag);\n            break;\n    }\n    post(\"  channels %d\", swap2(format->fc_nchannels, swap));\n    post(\"  sample rate %d\", swap4(format->fc_samplerate, swap));\n    post(\"  bytes per sec %d\", swap4(format->fc_bytespersecond, swap));\n    post(\"  block align %d\", swap2(format->fc_blockalign, swap));\n    post(\"  bits per sample %u\", swap2(format->fc_bitspersample, swap));\n    if (formattag == WAVE_FORMAT_EXT)\n    {\n        formattag = swap2(*(uint16_t *)&format->fc_subformat, swap);\n        post(\"  ext size %d\", swap2(format->fc_extsize, swap));\n        post(\"  ext valid bits per sample %d\",\n            swap2(format->fc_validbitspersample, swap));\n        post(\"  ext channel mask 0x%04x\", swap4(format->fc_channelmask, swap));\n        switch (formattag)\n        {\n            case WAVE_FORMAT_PCM:\n                post(\"  ext format %d (PCM)\", formattag);\n                break;\n            case WAVE_FORMAT_FLOAT:\n                post(\"  ext format %d (FLOAT)\", formattag);\n                break;\n            default:\n                post(\"  ext format %d (unsupported)\", formattag);\n                break;\n        }\n    }\n}\n\n    /** post fact info for debugging */\nstatic void wave_postfact(const t_factchunk *fact, int swap)\n{\n    wave_postchunk((const t_chunk *)fact, swap);\n    post(\"  sample length %d\", swap4(fact->fc_samplelength, swap));\n}\n\n#endif /* DEBUG_SOUNDFILE */\n\n/* ------------------------- WAVE ------------------------- */\n\nstatic int wave_isheader(const char *buf, size_t size)\n{\n    if (size < 4) return 0;\n    return !strncmp(buf, \"RIFF\", 4);\n}\n\nstatic int wave_readheader(t_soundfile *sf)\n{\n    int nchannels = 1, bytespersample = 2, samplerate = 44100, bigendian = 0,\n        swap = (bigendian != sys_isbigendian()), formatfound = 1;\n    off_t headersize = WAVEHEADSIZE;\n    size_t bytelimit = WAVEMAXBYTES;\n    union\n    {\n        char b_c[SFHDRBUFSIZE];\n        t_head b_head;\n        t_chunk b_chunk;\n        t_formatchunk b_formatchunk;\n        t_factchunk b_factchunk;\n    } buf = {0};\n    t_chunk *chunk = &buf.b_chunk;\n\n        /* file header */\n    if (fd_read(sf->sf_fd, 0, buf.b_c, headersize) < headersize)\n        return 0;\n    if (strncmp(buf.b_c + 8, \"WAVE\", 4))\n        return 0;\n#ifdef DEBUG_SOUNDFILE\n        wave_posthead(&buf.b_head, swap);\n#endif\n\n        /* read chunks in loop until we find the sound data chunk */\n    if ((headersize = wave_firstchunk(sf, chunk)) == -1)\n        return 0;\n    while (1)\n    {\n        uint32_t chunksize = swap4(chunk->c_size, swap);\n        /* post(\"chunk %.4s seek %d\", chunk->c_id, seekto); */\n        if (!strncmp(chunk->c_id, \"fmt \", 4))\n        {\n                /* format chunk */\n            int formattag;\n            t_formatchunk *format = &buf.b_formatchunk;\n            if (fd_read(sf->sf_fd, headersize + 8,\n                    buf.b_c + 8, chunksize) < chunksize)\n                return 0;\n#ifdef DEBUG_SOUNDFILE\n            wave_postformat(format, swap);\n#endif\n            nchannels = swap2(format->fc_nchannels, swap);\n            samplerate = swap4(format->fc_samplerate, swap);\n            formattag = swap2(format->fc_fmttag, swap);\n            if (formattag == WAVE_FORMAT_EXT && chunksize == WAVEFORMATSIZE)\n            {\n                errno = SOUNDFILE_ERRSAMPLEFMT;\n                return 0;\n            }\n            switch (formattag)\n            {\n                case WAVE_FORMAT_PCM: case WAVE_FORMAT_FLOAT: break;\n                case WAVE_FORMAT_EXT:\n                    formattag = swap2(*(uint16_t *)&format->fc_subformat, swap);\n                    if (formattag == WAVE_FORMAT_PCM ||\n                        formattag == WAVE_FORMAT_FLOAT)\n                            break;\n                default:\n                {\n                    errno = SOUNDFILE_ERRSAMPLEFMT;\n                    return 0;\n                }\n            }\n            bytespersample = swap2(format->fc_bitspersample, swap) / 8;\n            switch (bytespersample)\n            {\n                case 2: case 3: break;\n                case 4:\n                    if (formattag == WAVE_FORMAT_FLOAT) /* 32 bit int? */\n                        break;\n                default:\n                    errno = SOUNDFILE_ERRSAMPLEFMT;\n                    return 0;\n            }\n\n            formatfound = 1;\n        }\n        else if(!strncmp(chunk->c_id, \"data\", 4))\n        {\n                /* sound data chunk */\n            bytelimit = swap4(chunk->c_size, swap);\n            headersize += WAVECHUNKSIZE;\n#ifdef DEBUG_SOUNDFILE\n            wave_postchunk(chunk, swap);\n#endif\n            break;\n        }\n#ifdef DEBUG_SOUNDFILE\n        else if (!strncmp(chunk->c_id, \"fact\", 4))\n        {\n                /* extended format fact chunk */\n            wave_postfact(&buf.b_factchunk, swap);\n        }\n        else\n        {\n                /* everything else */\n            wave_postchunk(chunk, swap);\n        }\n#endif\n        if ((headersize = wave_nextchunk(sf, headersize, chunk)) == -1)\n            return 0;\n    }\n    if (!formatfound)\n    {\n        errno = SOUNDFILE_ERRMALFORMED;\n        return 0;\n    }\n\n        /* interpret data size from file size? */\n    if (bytelimit == WAVEMAXBYTES)\n    {\n        bytelimit = lseek(sf->sf_fd, 0, SEEK_END) - headersize;\n        if (bytelimit > WAVEMAXBYTES || bytelimit < 0)\n            bytelimit = WAVEMAXBYTES;\n    } else if (bytelimit & 1) {\n            /* the actual data chunk size is always even */\n        bytelimit++;\n    }\n\n        /* copy sample format back to caller */\n    sf->sf_samplerate = samplerate;\n    sf->sf_nchannels = nchannels;\n    sf->sf_bytespersample = bytespersample;\n    sf->sf_headersize = headersize;\n    sf->sf_bytelimit = bytelimit;\n    sf->sf_bigendian = bigendian;\n    sf->sf_bytesperframe = nchannels * bytespersample;\n\n    return 1;\n}\n\nstatic int wave_writeheader(t_soundfile *sf, size_t nframes)\n{\n    int isextended = wave_isextended(sf), swap = soundfile_needsbyteswap(sf);\n    size_t formatsize = WAVEFORMATSIZE,\n           datasize = nframes * sf->sf_bytesperframe;\n    off_t headersize = 0;\n    ssize_t byteswritten = 0;\n    char buf[SFHDRBUFSIZE] = {0};\n    t_head head = {\"RIFF\", 0, \"WAVE\"};\n    t_formatchunk format = {\n        \"fmt \", swap4(16, swap),\n        WAVE_FORMAT_PCM,                                  /* format tag      */\n        swap2((uint16_t)sf->sf_nchannels, swap),          /* channels        */\n        swap4((uint32_t)sf->sf_samplerate, swap),         /* sample rate     */\n        swap4((uint32_t)(sf->sf_samplerate *              /* bytes per sec   */\n                         sf->sf_bytesperframe), swap),\n        swap2((uint16_t)sf->sf_bytesperframe, swap),      /* block align     */\n        swap2((uint16_t)sf->sf_bytespersample * 8, swap), /* bits per sample */\n        0                                                 /* extended format */\n    };\n    t_chunk data = {\"data\", swap4((uint32_t)datasize, swap)};\n\n        /* file header */\n    memcpy(buf + headersize, &head, WAVEHEADSIZE);\n    headersize += WAVEHEADSIZE;\n\n        /* format chunk */\n    if (sf->sf_bytespersample == 4)\n        format.fc_fmttag = swap2(WAVE_FORMAT_FLOAT, swap);\n    if (isextended)\n    {\n        format.fc_extsize = swap2(WAVE_EXT_SIZE - 2, swap); /* - fmttag */\n        format.fc_validbitspersample = format.fc_bitspersample;\n        format.fc_channelmask = 0; /* no specific speaker positions */\n        memcpy(format.fc_subformat, &format.fc_fmttag, 2); /* move tag */\n        memcpy(format.fc_subformat + 2, WAVE_EXT_GUID, 14);\n        format.fc_fmttag = swap2(WAVE_FORMAT_EXT, swap);\n        formatsize += WAVE_EXT_SIZE;\n    }\n    format.fc_size = swap4(formatsize - 8, swap);\n    memcpy(buf + headersize, &format, formatsize);\n    headersize += formatsize;\n\n        /* fact chunk */\n    if (isextended)\n    {\n        t_factchunk fact = {\n            \"fact\", swap4(4, swap),\n            swap4((uint32_t)(sf->sf_nchannels * nframes), swap)\n        };\n        memcpy(buf + headersize, &fact, WAVEFACTSIZE);\n        headersize += WAVEFACTSIZE;\n    }\n\n        /* data chunk */\n    if (datasize & 1)\n    {\n            /* add pad byte */\n        data.c_size = swap4((uint32_t)datasize + 1, swap);\n    }\n    memcpy(buf + headersize, &data, WAVECHUNKSIZE);\n    headersize += WAVECHUNKSIZE;\n\n        /* update file header chunk size (- chunk header) */\n    head.h_size = swap4s((int32_t)(datasize + headersize - 8), swap);\n    memcpy(buf + 4, &head.h_size, 4);\n\n#ifdef DEBUG_SOUNDFILE\n    wave_posthead(&head, swap);\n    wave_postformat(&format, swap);\n    wave_postchunk(&data, swap);\n#endif\n\n    byteswritten = fd_write(sf->sf_fd, 0, buf, headersize);\n    return (byteswritten < headersize ? -1 : byteswritten);\n}\n\n    /** assumes chunk order:\n        * basic:    head format data\n        * extended: head format+ext fact data */\nstatic int wave_updateheader(t_soundfile *sf, size_t nframes)\n{\n    int isextended = wave_isextended(sf), swap = soundfile_needsbyteswap(sf);\n    size_t datasize = nframes * sf->sf_bytesperframe,\n           headersize = WAVEHEADSIZE + WAVEFORMATSIZE;\n    int padbyte = (datasize & 1);\n    uint32_t uinttmp;\n\n    if (isextended)\n    {\n        headersize += WAVE_EXT_SIZE;\n\n            /* fact chunk sample length */\n        uinttmp = swap4((uint32_t)(nframes * sf->sf_nchannels), swap);\n        if (fd_write(sf->sf_fd, headersize + 8, &uinttmp, 4) < 4)\n            return 0;\n        headersize += WAVEFACTSIZE;\n    }\n\n        /* sound data chunk size */\n    datasize += padbyte;\n    uinttmp = swap4((uint32_t)datasize, swap);\n    if (fd_write(sf->sf_fd, headersize + 4, &uinttmp, 4) < 4)\n        return 0;\n    headersize += WAVECHUNKSIZE;\n\n        /* add pad byte */\n    if (padbyte)\n    {\n        uinttmp = 0;\n        if (fd_write(sf->sf_fd, headersize + datasize - 1, &uinttmp, 1) < 1)\n            return 0;\n    }\n\n        /* file header chunk size (- chunk header) */\n    uinttmp = swap4((uint32_t)(headersize + datasize - 8), swap);\n    if (fd_write(sf->sf_fd, 4, &uinttmp, 4) < 4)\n        return 0;\n\n#ifdef DEBUG_SOUNDFILE\n        post(\"RIFF %d\", headersize + datasize - 8);\n        post(\"  WAVE\");\n        post(\"data %d\", datasize);\n#endif\n\n    return 1;\n}\n\nstatic int wave_hasextension(const char *filename, size_t size)\n{\n    int len = strnlen(filename, size);\n    if (len >= 5 &&\n        (!strncmp(filename + (len - 4), \".wav\", 4) ||\n         !strncmp(filename + (len - 4), \".WAV\", 4)))\n        return 1;\n    if (len >= 6 &&\n        (!strncmp(filename + (len - 5), \".wave\", 5) ||\n         !strncmp(filename + (len - 5), \".WAVE\", 5)))\n        return 1;\n    return 0;\n}\n\nstatic int wave_addextension(char *filename, size_t size)\n{\n    int len = strnlen(filename, size);\n    if (len + 4 >= size)\n        return 0;\n    strcpy(filename + len, \".wav\");\n    return 1;\n}\n\n    /* force little endian */\nstatic int wave_endianness(int endianness)\n{\n    return 0;\n}\n\n/* ------------------------- setup routine ------------------------ */\n\nt_soundfile_type wave = {\n    \"wave\",\n    WAVEHEADSIZE + WAVEFORMATSIZE + WAVECHUNKSIZE,\n    wave_isheader,\n    wave_readheader,\n    wave_writeheader,\n    wave_updateheader,\n    wave_hasextension,\n    wave_addextension,\n    wave_endianness\n};\n\nvoid soundfile_wave_setup( void)\n{\n    soundfile_addtype(&wave);\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/d_ugen.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/*  These routines build a copy of the DSP portion of a graph, which is\n    then sorted into a linear list of DSP operations which are added to\n    the DSP duty cycle called by the scheduler.  Once that's been done,\n    we delete the copy.  The DSP objects are represented by \"ugenbox\"\n    structures which are parallel to the DSP objects in the graph and\n    have vectors of siginlets and sigoutlets which record their\n    interconnections.\n*/\n\n#include \"m_pd.h\"\n#include \"m_imp.h\"\n#include \"g_canvas.h\"\n#include <stdarg.h>\n#define DEFDACBLKSIZE 64    /* from s_stuff.h - LATER make this dynamic */\n\nextern t_class *vinlet_class, *voutlet_class, *canvas_class, *text_class;\n\nEXTERN_STRUCT _vinlet;\nEXTERN_STRUCT _voutlet;\n\nvoid vinlet_dspprolog(struct _vinlet *x, t_signal **parentsigs,\n    int myvecsize, int phase, int period, int frequency,\n    int downsample, int upsample,  int reblock, int switched);\nvoid voutlet_dspprolog(struct _voutlet *x, t_signal **parentsigs,\n    int myvecsize, int phase, int period, int frequency,\n    int downsample, int upsample, int reblock, int switched);\nvoid voutlet_dspepilog(struct _voutlet *x, t_signal **parentsigs,\n    int myvecsize, int phase, int period, int frequency,\n    int downsample, int upsample, int reblock, int switched);\n\nstruct _instanceugen\n{\n    t_int *u_dspchain;         /* DSP chain */\n    int u_dspchainsize;        /* number of elements in DSP chain */\n    t_signal *u_signals;       /* list of signals used by DSP chain */\n    int u_sortno;              /* number of DSP sortings so far */\n        /* list of signals which can be reused, sorted by buffer size */\n    t_signal *u_freelist[MAXLOGSIG+1];\n        /* list of reusable \"borrowed\" signals (which don't own sample buffers) */\n    t_signal *u_freeborrowed;\n    int u_phase;\n    int u_loud;\n    struct _dspcontext *u_context;\n};\n\n#define THIS (pd_this->pd_ugen)\n\nvoid d_ugen_newpdinstance(void)\n{\n    THIS = getbytes(sizeof(*THIS));\n    THIS->u_dspchain = 0;\n    THIS->u_dspchainsize = 0;\n    THIS->u_signals = 0;\n}\n\nvoid d_ugen_freepdinstance(void)\n{\n    freebytes(THIS, sizeof(*THIS));\n}\n\nt_int *zero_perform(t_int *w)   /* zero out a vector */\n{\n    t_sample *out = (t_sample *)(w[1]);\n    int n = (int)(w[2]);\n    while (n--) *out++ = 0;\n    return (w+3);\n}\n\nt_int *zero_perf8(t_int *w)\n{\n    t_sample *out = (t_sample *)(w[1]);\n    int n = (int)(w[2]);\n\n    for (; n; n -= 8, out += 8)\n    {\n        out[0] = 0;\n        out[1] = 0;\n        out[2] = 0;\n        out[3] = 0;\n        out[4] = 0;\n        out[5] = 0;\n        out[6] = 0;\n        out[7] = 0;\n    }\n    return (w+3);\n}\n\nvoid dsp_add_zero(t_sample *out, int n)\n{\n    if (n&7)\n        dsp_add(zero_perform, 2, out, (t_int)n);\n    else\n        dsp_add(zero_perf8, 2, out, (t_int)n);\n}\n\n/* ---------------------------- block~ ----------------------------- */\n\n/* The \"block~ object maintains the containing canvas's DSP computation,\ncalling it at a super- or sub-multiple of the containing canvas's\ncalling frequency.  The block~'s creation arguments specify block size\nand overlap.  Block~ does no \"dsp\" computation in its own right, but it\nadds prolog and epilog code before and after the canvas's unit generators.\n\nA subcanvas need not have a block~ at all; if there's none, its\nugens are simply put on the list without any prolog or epilog code.\n\nBlock~ may be invoked as switch~, in which case it also acts to switch the\nsubcanvas on and off.  The overall order of scheduling for a subcanvas\nis thus,\n\n    inlet and outlet prologue code (1)\n    block prologue (2)\n    the objects in the subcanvas, including inlets and outlets\n    block epilogue (2)\n    outlet epilogue code (2)\n\nwhere (1) means, \"if reblocked\" and  (2) means, \"if reblocked or switched\".\n\nIf we're reblocked, the inlet prolog and outlet epilog code takes care of\noverlapping and buffering to deal with vector size changes.  If we're switched\nbut not reblocked, the inlet prolog is not needed, and the output epilog is\nONLY run when the block is switched off; in this case the epilog code simply\ncopies zeros to all signal outlets.\n*/\n\nt_class *block_class;\n\ntypedef struct _block\n{\n    t_object x_obj;\n    int x_calcsize;     /* number of samples actually to compute */\n    int x_overlap;\n    int x_phase;        /* from 0 to period-1; when zero we run the block */\n    int x_period;       /* submultiple of containing canvas */\n    int x_frequency;    /* supermultiple of comtaining canvas */\n    int x_count;        /* number of times parent block has called us */\n    int x_chainonset;   /* beginning of code in DSP chain */\n    int x_blocklength;  /* length of dspchain for this block */\n    int x_epiloglength; /* length of epilog */\n    char x_switched;    /* true if we're acting as a a switch */\n    char x_switchon;    /* true if we're switched on */\n    char x_reblock;     /* true if inlets and outlets are reblocking */\n    int x_upsample;     /* upsampling-factor */\n    int x_downsample;   /* downsampling-factor */\n    int x_return;       /* stop right after this block (for one-shots) */\n} t_block;\n\nstatic void block_set(t_block *x, t_floatarg fvecsize, t_floatarg foverlap,\n    t_floatarg fupsample);\n\nstatic void *block_new(t_floatarg fvecsize, t_floatarg foverlap,\n                       t_floatarg fupsample)\n{\n    t_block *x = (t_block *)pd_new(block_class);\n    x->x_phase = 0;\n    x->x_period = 1;\n    x->x_frequency = 1;\n    x->x_switched = 0;\n    x->x_switchon = 1;\n    block_set(x, fvecsize, foverlap, fupsample);\n    return (x);\n}\n\nstatic void block_set(t_block *x, t_floatarg fcalcsize, t_floatarg foverlap,\n    t_floatarg fupsample)\n{\n    int upsample, downsample;\n    int calcsize = fcalcsize;\n    int overlap = foverlap;\n    int dspstate = canvas_suspend_dsp();\n    if (overlap < 1)\n        overlap = 1;\n    if (calcsize < 0)\n        calcsize = 0;    /* this means we'll get it from parent later. */\n\n    if (fupsample <= 0)\n        upsample = downsample = 1;\n    else if (fupsample >= 1) {\n        upsample = fupsample;\n        downsample   = 1;\n    }\n    else\n    {\n        downsample = 1.0 / fupsample;\n        upsample   = 1;\n    }\n    if (overlap != (1 << ilog2(overlap)))\n    {\n        pd_error(x, \"block~: overlap not a power of 2\");\n        overlap = 1;\n    }\n    if (downsample != (1 << ilog2(downsample)))\n    {\n        pd_error(x, \"block~: downsampling not a power of 2\");\n        downsample = 1;\n    }\n    if (upsample != (1 << ilog2(upsample)))\n    {\n        pd_error(x, \"block~: upsampling not a power of 2\");\n        upsample = 1;\n    }\n\n    x->x_calcsize = calcsize;\n    x->x_overlap = overlap;\n    x->x_upsample = upsample;\n    x->x_downsample = downsample;\n    canvas_resume_dsp(dspstate);\n}\n\nt_float canvas_getsr(t_canvas *x)\n{\n    t_float srate = sys_getsr();\n    t_canvas *canvas;\n    t_gobj *g;\n    for (canvas = x; canvas; canvas = canvas->gl_owner)\n    {\n        for (g = canvas->gl_list; g; g = g->g_next)\n        {\n            if (g->g_pd == block_class)\n            {\n                srate *= ((t_float)(((t_block *)g)->x_upsample)) /\n                    ((t_float)(((t_block *)g)->x_downsample));\n                break;\n            }\n        }\n    }\n    return (srate);\n}\n\nint canvas_getsignallength(t_canvas *x)\n{\n    t_canvas *canvas;\n    t_gobj *g;\n    for (canvas = x; canvas; canvas = canvas->gl_owner)\n        for (g = canvas->gl_list; g; g = g->g_next)\n            if (g->g_pd == block_class &&\n                ((t_block *)g)->x_calcsize)\n                    return (((t_block *)g)->x_calcsize);\n    return (DEFDACBLKSIZE);\n}\n\nstatic void *switch_new(t_floatarg fvecsize, t_floatarg foverlap,\n                        t_floatarg fupsample)\n{\n    t_block *x = (t_block *)(block_new(fvecsize, foverlap, fupsample));\n    x->x_switched = 1;\n    x->x_switchon = 0;\n    return (x);\n}\n\nstatic void block_float(t_block *x, t_floatarg f)\n{\n    if (x->x_switched)\n        x->x_switchon = (f != 0);\n}\n\nstatic void block_bang(t_block *x)\n{\n    if (x->x_switched && !x->x_switchon && THIS->u_dspchain)\n    {\n        t_int *ip;\n        x->x_return = 1;\n        for (ip = THIS->u_dspchain + x->x_chainonset; ip; )\n            ip = (*(t_perfroutine)(*ip))(ip);\n        x->x_return = 0;\n    }\n    else if (!x->x_switched)\n        pd_error(x, \"[block~]: bang has no effect\");\n    else if (x->x_switched)\n    {\n        if (x->x_switchon)\n            pd_error(x, \"[switch~]: bang has no effect at on-state\");\n        if (!THIS->u_dspchain)\n            pd_error(x, \"[switch~]: bang has no effect if DSP is off\");\n    }\n}\n\n\n#define PROLOGCALL 2\n#define EPILOGCALL 2\n\nstatic t_int *block_prolog(t_int *w)\n{\n    t_block *x = (t_block *)w[1];\n    int phase = x->x_phase;\n        /* if we're switched off, jump past the epilog code */\n    if (!x->x_switchon)\n        return (w + x->x_blocklength);\n    if (phase)\n    {\n        phase++;\n        if (phase == x->x_period) phase = 0;\n        x->x_phase = phase;\n        return (w + x->x_blocklength);  /* skip block; jump past epilog */\n    }\n    else\n    {\n        x->x_count = x->x_frequency;\n        x->x_phase = (x->x_period > 1 ? 1 : 0);\n        return (w + PROLOGCALL);        /* beginning of block is next ugen */\n    }\n}\n\nstatic t_int *block_epilog(t_int *w)\n{\n    t_block *x = (t_block *)w[1];\n    int count = x->x_count - 1;\n    if (x->x_return)\n        return (0);\n    if (!x->x_reblock)\n        return (w + x->x_epiloglength + EPILOGCALL);\n    if (count)\n    {\n        x->x_count = count;\n        return (w - (x->x_blocklength -\n            (PROLOGCALL + EPILOGCALL)));   /* go to ugen after prolog */\n    }\n    else return (w + EPILOGCALL);\n}\n\nstatic void block_dsp(t_block *x, t_signal **sp)\n{\n    /* do nothing here */\n}\n\nvoid block_tilde_setup(void)\n{\n    block_class = class_new(gensym(\"block~\"), (t_newmethod)block_new, 0,\n            sizeof(t_block), 0, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0);\n    class_addcreator((t_newmethod)switch_new, gensym(\"switch~\"),\n        A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0);\n    class_addmethod(block_class, (t_method)block_set, gensym(\"set\"),\n        A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0);\n    class_addmethod(block_class, (t_method)block_dsp, gensym(\"dsp\"), A_CANT, 0);\n    class_addfloat(block_class, block_float);\n    class_addbang(block_class, block_bang);\n}\n\n/* ------------------ DSP call list ----------------------- */\n\nstatic t_int dsp_done(t_int *w)\n{\n    return (0);\n}\n\nvoid dsp_add(t_perfroutine f, int n, ...)\n{\n    int newsize = THIS->u_dspchainsize + n+1, i;\n    va_list ap;\n\n    THIS->u_dspchain = t_resizebytes(THIS->u_dspchain,\n        THIS->u_dspchainsize * sizeof (t_int), newsize * sizeof (t_int));\n    THIS->u_dspchain[THIS->u_dspchainsize-1] = (t_int)f;\n    if (THIS->u_loud)\n        post(\"add to chain: %lx\",\n            THIS->u_dspchain[THIS->u_dspchainsize-1]);\n    va_start(ap, n);\n    for (i = 0; i < n; i++)\n    {\n        THIS->u_dspchain[THIS->u_dspchainsize + i] = va_arg(ap, t_int);\n        if (THIS->u_loud)\n            post(\"add to chain: %lx\",\n                THIS->u_dspchain[THIS->u_dspchainsize + i]);\n    }\n    va_end(ap);\n    THIS->u_dspchain[newsize-1] = (t_int)dsp_done;\n    THIS->u_dspchainsize = newsize;\n}\n\n    /* at Guenter's suggestion, here's a vectorized version */\nvoid dsp_addv(t_perfroutine f, int n, t_int *vec)\n{\n    int newsize = THIS->u_dspchainsize + n+1, i;\n\n    THIS->u_dspchain = t_resizebytes(THIS->u_dspchain,\n        THIS->u_dspchainsize * sizeof (t_int), newsize * sizeof (t_int));\n    THIS->u_dspchain[THIS->u_dspchainsize-1] = (t_int)f;\n    for (i = 0; i < n; i++)\n        THIS->u_dspchain[THIS->u_dspchainsize + i] = vec[i];\n    THIS->u_dspchain[newsize-1] = (t_int)dsp_done;\n    THIS->u_dspchainsize = newsize;\n}\n\nvoid dsp_tick(void)\n{\n    if (THIS->u_dspchain)\n    {\n        t_int *ip;\n        for (ip = THIS->u_dspchain; ip; ) ip = (*(t_perfroutine)(*ip))(ip);\n        THIS->u_phase++;\n    }\n}\n\n/* ---------------- signals ---------------------------- */\n\nint ilog2(int n)\n{\n    int r = -1;\n    if (n <= 0) return(0);\n    while (n)\n    {\n        r++;\n        n >>= 1;\n    }\n    return (r);\n}\n\n\n    /* call this when DSP is stopped to free all the signals */\nstatic void signal_cleanup(void)\n{\n    t_signal *sig;\n    int i;\n    while ((sig = THIS->u_signals))\n    {\n        THIS->u_signals = sig->s_nextused;\n        if (!sig->s_isborrowed && !sig->s_isscalar)\n            t_freebytes(sig->s_vec, sig->s_nalloc * sizeof (*sig->s_vec));\n        t_freebytes(sig, sizeof *sig);\n    }\n    for (i = 0; i <= MAXLOGSIG; i++)\n        THIS->u_freelist[i] = 0;\n    THIS->u_freeborrowed = 0;\n}\n\nstatic void signal_dereference(t_signal *s)\n{\n    if (THIS->u_loud) post(\"dereference %lx: %d\", s, s->s_refcount);\n    if (s->s_refcount <= 0)\n        bug(\"signal_dereference\");\n    s->s_refcount--;\n    if (!s->s_refcount)\n        signal_makereusable(s);\n}\n\n\n    /* mark the signal \"reusable.\" */\nvoid signal_makereusable(t_signal *sig)\n{\n    int logn = ilog2(sig->s_nalloc);\n#if 0\n    t_signal *s5;\n    for (s5 = THIS->u_freeborrowed; s5; s5 = s5->s_nextfree)\n    {\n        if (s5 == sig)\n        {\n            bug(\"signal_free 3\");\n            return;\n        }\n    }\n    for (s5 = THIS->u_freelist[logn]; s5; s5 = s5->s_nextfree)\n    {\n        if (s5 == sig)\n        {\n            bug(\"signal_free 4\");\n            return;\n        }\n    }\n#endif\n    if (THIS->u_loud) post(\"free %lx: %d\", sig, sig->s_isborrowed);\n    if (sig->s_isborrowed || sig->s_isscalar)\n    {\n        if (sig->s_isborrowed)\n        {\n            /* if the signal is borrowed, decrement the borrowed-from signal's\n                reference count, possibly marking it reusable too */\n            t_signal *s2 = sig->s_borrowedfrom;\n            if ((s2 == sig) || !s2)\n                bug(\"signal_free\");\n            signal_dereference(s2);\n        }\n        sig->s_nextfree = THIS->u_freeborrowed;\n        THIS->u_freeborrowed = sig;\n    }\n    else\n    {\n            /* if it's a real signal (not borrowed), put it on the free list\n                so we can reuse it. */\n        if (THIS->u_freelist[logn] == sig) bug(\"signal_free 2\");\n        sig->s_nextfree = THIS->u_freelist[logn];\n        THIS->u_freelist[logn] = sig;\n    }\n}\n\n    /* pop an audio signal from free list or create a new one.\n    if \"scalarp\" is nonzero, it's a pointer to a scalar owned by the\n    tilde object; in this case we neither allocate nor free it.\n    Otherwise, if \"length\" is zero, return a \"borrowed\"\n    signal whose buffer and size will be obtained later via\n    signal_setborrowed(). */\n\nt_signal *signal_new(int length, int nchans, t_float sr, t_sample *scalarptr)\n{\n    int allocsize = 0;\n    t_signal *ret, **whichlist;\n    if (sr < 1)\n        bug(\"signal_new\");\n    if (length && !scalarptr)\n    {\n            /* figure out which free list to use, depending on size of vector */\n        int logn = ilog2(length*nchans);\n            /* round up to a power of two */\n        if ((1<<logn) < length*nchans)\n            logn++;\n        allocsize = 1<<logn;\n        if (logn > MAXLOGSIG)\n            bug(\"signal buffer too large\");\n        whichlist = THIS->u_freelist + logn;\n    }\n    else /* scalar or borrowed signal */\n        whichlist = &THIS->u_freeborrowed;\n\n        /* try to reclaim one from the free list */\n    if ((ret = *whichlist))\n        *whichlist = ret->s_nextfree;\n    else\n    {\n                 /* LATER figure out what to do if we ran out of space */\n        ret = (t_signal *)t_getbytes(sizeof *ret);\n        if (allocsize)\n            ret->s_vec = (t_sample *)getbytes(allocsize * sizeof (*ret->s_vec));\n        ret->s_nextused = THIS->u_signals;\n        THIS->u_signals = ret;\n    }\n    if (scalarptr)\n    {\n        ret->s_vec = scalarptr;\n        ret->s_isborrowed = 0;\n        ret->s_isscalar = 1;\n    }\n    else if (length)\n        ret->s_isborrowed = ret->s_isscalar = 0;\n    else\n    {\n        ret->s_vec = 0;\n        ret->s_isborrowed = 1;\n        ret->s_isscalar = 0;\n    }\n    ret->s_length = length;\n    ret->s_nchans = nchans;\n    ret->s_nalloc = allocsize;\n    ret->s_sr = sr;\n    ret->s_overlap = 0;\n    ret->s_refcount = 0;\n    ret->s_borrowedfrom = 0;\n    if (THIS->u_loud) post(\"new %lx: %lx\", ret, ret->s_vec);\n    return (ret);\n}\n\nt_signal *signal_newlike(const t_signal *sig)\n{\n    t_signal *s = signal_new(sig->s_length, sig->s_nchans, sig->s_sr, 0);\n    s->s_overlap = sig->s_overlap;\n    return s;\n}\n\nvoid signal_setborrowed(t_signal *sig, t_signal *sig2)\n{\n    if (!sig->s_isborrowed || sig->s_borrowedfrom)\n        bug(\"signal_setborrowed\");\n    if (sig == sig2)\n        bug(\"signal_setborrowed 2\");\n    sig->s_borrowedfrom = sig2;\n    sig->s_vec = sig2->s_vec;\n    sig->s_length = sig2->s_length;\n    sig->s_nchans = sig2->s_nchans;\n    sig->s_sr = sig2->s_sr;\n    sig->s_overlap = sig2->s_overlap;\n    sig->s_nalloc = sig2->s_nalloc;\n    sig2->s_refcount++;\n    if (THIS->u_loud) post(\"set borrowed %lx: from %lx vec %lx\",\n        sig, sig2, sig->s_vec);\n}\n\n    /* only use this in the context of dsp routines to set number of channels\n    on output signal - we assume it's currently a pointer to the null signal */\nvoid signal_setmultiout(t_signal **sig, int nchans)\n{\n    int overlap = (*sig)->s_overlap;\n    *sig = signal_new((*sig)->s_length, nchans, (*sig)->s_sr, 0);\n    (*sig)->s_overlap = overlap;\n}\n\nstatic int signal_compatible(t_signal *s1, t_signal *s2)\n{\n    return (s1->s_length == s2->s_length && s1->s_nchans == s2->s_nchans\n        && s1->s_sr == s2->s_sr && s1->s_overlap == s2->s_overlap);\n}\n\n/* ------------------ ugen (\"unit generator\") sorting ----------------- */\n\ntypedef struct _ugenbox\n{\n    struct _siginlet *u_in;\n    int u_nin;\n    struct _sigoutlet *u_out;\n    int u_nout;\n    int u_phase;\n    struct _ugenbox *u_next;\n    t_object *u_obj;\n    int u_done;\n} t_ugenbox;\n\ntypedef struct _siginlet\n{\n    int i_nconnect;\n    int i_ngot;\n    t_signal *i_signal;\n} t_siginlet;\n\ntypedef struct _sigoutconnect\n{\n    t_ugenbox *oc_who;\n    int oc_inno;\n    struct _sigoutconnect *oc_next;\n} t_sigoutconnect;\n\ntypedef struct _sigoutlet\n{\n    int o_nconnect;\n    int o_nsent;\n    t_signal *o_signal;\n    t_sigoutconnect *o_connections;\n} t_sigoutlet;\n\n\nstruct _dspcontext\n{\n    struct _ugenbox *dc_ugenlist;\n    struct _dspcontext *dc_parentcontext;\n    int dc_ninlets;\n    int dc_noutlets;\n    t_signal **dc_iosigs;\n    t_signal dc_nullsignal;   /* non-signal showing sample rate and length */\n    unsigned int dc_toplevel:1;     /* true if \"iosigs\" is invalid. */\n    unsigned int dc_reblock:1;      /* true if we have to reblock in/outlets */\n    unsigned int dc_switched:1;     /* true if we're switched */\n    unsigned int dc_warnedmulti:1;  /* already warned about bad multi input */\n};\n#define DC_LENGTH(x) ((x)->dc_nullsignal.s_length)\n#define DC_SR(x) ((x)->dc_nullsignal.s_sr)\n#define DC_OVERLAP(x) ((x)->dc_nullsignal.s_overlap)\n\n#define t_dspcontext struct _dspcontext\n\n    /* get a new signal for the current context - used by clone~ object */\nt_signal *signal_newfromcontext(int borrowed, int nchans)\n{\n    t_signal *s = signal_new((borrowed? 0 : DC_LENGTH(THIS->u_context)), nchans,\n        DC_SR(THIS->u_context), 0);\n    s->s_overlap = DC_OVERLAP(THIS->u_context);\n    return s;\n}\n\nvoid ugen_stop(void)\n{\n#if 0   /* test to make sure we aren't leaving signal garbage */\n    {\n        t_signal *sig;\n        int done = 0, count = 0;\n            /* report any signals still in use */\n        for (sig = THIS->u_signals; sig; sig = sig->s_nextused)\n        {\n            if (sig->s_refcount)\n                post(\"signal %lx refcount %d\", sig, sig->s_refcount), done = 1;\n            count++;\n        }\n        if (!done)\n            post(\"all %d signals freed correctly\", count);\n    }\n#endif\n    if (THIS->u_dspchain)\n    {\n        freebytes(THIS->u_dspchain,\n            THIS->u_dspchainsize * sizeof (t_int));\n        THIS->u_dspchain = 0;\n    }\n    signal_cleanup();\n\n}\n\nvoid ugen_start(void)\n{\n    ugen_stop();\n    THIS->u_sortno++;\n    /*  THIS->u_loud = 1;  -- enable this for volumes of debugging output */\n    THIS->u_dspchain = (t_int *)getbytes(sizeof(*THIS->u_dspchain));\n    THIS->u_dspchain[0] = (t_int)dsp_done;\n    THIS->u_dspchainsize = 1;\n    if (THIS->u_context) bug(\"ugen_start\");\n}\n\nint ugen_getsortno(void)\n{\n    return (THIS->u_sortno);\n}\n\n#if 0\nvoid glob_ugen_printstate(void *dummy, t_symbol *s, int argc, t_atom *argv)\n{\n    int i, count;\n    t_signal *sig;\n    for (count = 0, sig = THIS->u_signals; sig;\n        count++, sig = sig->s_nextused)\n            ;\n    post(\"used signals %d\", count);\n    for (i = 0; i < MAXLOGSIG; i++)\n    {\n        for (count = 0, sig = THIS->u_freelist[i]; sig;\n            count++, sig = sig->s_nextfree)\n                ;\n        if (count)\n            post(\"size %d: free %d\", (1 << i), count);\n    }\n    for (count = 0, sig = THIS->u_freeborrowed; sig;\n        count++, sig = sig->s_nextfree)\n            ;\n    post(\"free borrowed %d\", count);\n\n    THIS->u_loud = argc;\n}\n#endif\n\n    /* start building the graph for a canvas */\nt_dspcontext *ugen_start_graph(int toplevel, t_signal **sp,\n    int ninlets, int noutlets)\n{\n    t_dspcontext *dc = (t_dspcontext *)getbytes(sizeof(*dc));\n\n    if (THIS->u_loud) post(\"ugen_start_graph...\");\n\n    /* protect against invalid numsignals.  This might happen if we have\n    an abstraction with inlet~/outlet~  opened as a toplevel patch */\n    if (toplevel)\n        ninlets = noutlets = 0;\n\n    dc->dc_ugenlist = 0;\n    dc->dc_toplevel = toplevel;\n    dc->dc_iosigs = sp;\n    dc->dc_ninlets = ninlets;\n    dc->dc_noutlets = noutlets;\n    dc->dc_warnedmulti = 0;\n    dc->dc_parentcontext = THIS->u_context;\n    THIS->u_context = dc;\n    return (dc);\n}\n\n    /* first the canvas calls this to create all the boxes... */\nvoid ugen_add(t_dspcontext *dc, t_object *obj)\n{\n    t_ugenbox *x = (t_ugenbox *)getbytes(sizeof *x);\n    int i;\n    t_sigoutlet *uout;\n    t_siginlet *uin;\n\n    x->u_next = dc->dc_ugenlist;\n    dc->dc_ugenlist = x;\n    x->u_obj = obj;\n    x->u_nin = obj_nsiginlets(obj);\n    x->u_in = getbytes(x->u_nin * sizeof (*x->u_in));\n    for (uin = x->u_in, i = x->u_nin; i--; uin++)\n        uin->i_nconnect = 0;\n    x->u_nout = obj_nsigoutlets(obj);\n    x->u_out = getbytes(x->u_nout * sizeof (*x->u_out));\n    for (uout = x->u_out, i = x->u_nout; i--; uout++)\n        uout->o_connections = 0, uout->o_nconnect = 0;\n}\n\n    /* and then this to make all the connections. */\nvoid ugen_connect(t_dspcontext *dc, t_object *x1, int outno, t_object *x2,\n    int inno)\n{\n    t_ugenbox *u1, *u2;\n    t_sigoutlet *uout;\n    t_siginlet *uin;\n    t_sigoutconnect *oc;\n    int sigoutno = obj_sigoutletindex(x1, outno);\n    int siginno = obj_siginletindex(x2, inno);\n    if (THIS->u_loud)\n        post(\"%s -> %s: %d->%d\",\n            class_getname(x1->ob_pd),\n                class_getname(x2->ob_pd), outno, inno);\n    for (u1 = dc->dc_ugenlist; u1 && u1->u_obj != x1; u1 = u1->u_next);\n    for (u2 = dc->dc_ugenlist; u2 && u2->u_obj != x2; u2 = u2->u_next);\n    if (!u1 || !u2 || siginno < 0 || !u2->u_nin)\n    {\n        if (!u1)\n            pd_error(0, \"object with signal outlets but no DSP method?\");\n                /* check if it's a \"text\" (i.e., object wasn't created) -\n                if so fail silently */\n        else if (!(x2 && (pd_class(&x2->ob_pd) == text_class)))\n            pd_error(u1->u_obj,\n                \"audio signal outlet connected to nonsignal inlet (ignored)\");\n        return;\n    }\n    if (sigoutno < 0 || sigoutno >= u1->u_nout || siginno >= u2->u_nin)\n    {\n        bug(\"ugen_connect %s %s %d %d (%d %d)\",\n            class_getname(x1->ob_pd),\n            class_getname(x2->ob_pd), sigoutno, siginno, u1->u_nout,\n                u2->u_nin);\n    }\n    uout = u1->u_out + sigoutno;\n    uin = u2->u_in + siginno;\n\n        /* add a new connection to the outlet's list */\n    oc = (t_sigoutconnect *)getbytes(sizeof *oc);\n    oc->oc_next = uout->o_connections;\n    uout->o_connections = oc;\n    oc->oc_who = u2;\n    oc->oc_inno = siginno;\n        /* update inlet and outlet counts  */\n    uout->o_nconnect++;\n    uin->i_nconnect++;\n}\n\n    /* get the index of a ugenbox or -1 if it's not on the list */\nstatic int ugen_index(t_dspcontext *dc, t_ugenbox *x)\n{\n    int ret;\n    t_ugenbox *u;\n    for (u = dc->dc_ugenlist, ret = 0; u; u = u->u_next, ret++)\n        if (u == x) return (ret);\n    return (-1);\n}\nextern t_class *clone_class;\n\nstatic const t_sample ugen_scalarzero;  /* zero for scalar-to-vector copying */\n\nextern int class_getdspflags(const t_class *c);\n\n    /* put a ugenbox on the chain, recursively putting any others on that\n    this one might uncover. */\nstatic void ugen_doit(t_dspcontext *dc, t_ugenbox *u)\n{\n    t_sigoutlet *uout;\n    t_siginlet *uin;\n    t_sigoutconnect *oc;\n    t_class *class = pd_class(&u->u_obj->ob_pd);\n    t_signal *freelater = 0, *stmp;\n    int flags = class_getdspflags(class);\n    int i, n;\n        /* suppress creating new signals for the outputs of signal\n        inlets for non-reblocked canvases -- those will be borrowed. */\n    int nonewsigs = ((class == vinlet_class) && !dc->dc_reblock);\n        /* when we encounter a subcanvas or outlet~ object, suppress freeing\n        the input signals as they may be \"borrowed\" for the super or sub\n        patch; except blocked or switched outlet~s. */\n    int nofreesigs = (class == canvas_class || class == clone_class ||\n        ((class == voutlet_class) &&  !(dc->dc_reblock || dc->dc_switched)));\n    t_signal **insig, **outsig, **sig, *s1, *s2, *s3;\n    t_ugenbox *u2;\n\n        /* if CLASS_MULTICHANNEL isn't set, check that all input signals\n        are one-channel, and if not, just return without doing anything. */\n\n    for (i = 0, uin = u->u_in; i < u->u_nin; i++, uin++)\n        if (uin->i_nconnect && uin->i_signal->s_nchans != 1 &&\n            !(flags & CLASS_MULTICHANNEL))\n    {\n        pd_error(u->u_obj, \"object %s can't take multichannel inputs\",\n            class_getname(u->u_obj->ob_pd));\n        dc->dc_warnedmulti = 1;\n        return;\n    }\n\n    if (THIS->u_loud) post(\"doit %s %d %d\", class_getname(class), nofreesigs,\n        nonewsigs);\n\n        /* Fill in unconnected inlets.  Normally we create a signal for it and\n        add a scalar-to-vector copy to the DSP chain to fill it in from the\n        inlet's scalar source.  But if the relevant NOPROMOTE flag is set,\n        create a scalar \"signal\" that just points to the preexisting location\n        of the scalar to be promoted so that the object can use the scalar\n        input directly. */\n\n    for (i = 0, uin = u->u_in; i < u->u_nin; i++, uin++)\n    {\n        if (!uin->i_nconnect)\n        {\n            t_float *scalar = obj_findsignalscalar(u->u_obj, i);\n                /* if the object can deal with it, we generate a signal that\n                consists only of a pointer to a scalar. */\n            if ((i && (flags & CLASS_NOPROMOTESIG)) ||\n                (!i && (flags & CLASS_NOPROMOTELEFT)))\n                    uin->i_signal = signal_new(1, 1, DC_SR(dc), scalar);\n            else\n            {       /* otherwise we have to add a call to the DSP chain to\n                    promote the scalar input to a vector. */\n                uin->i_signal = signal_new(DC_LENGTH(dc), 1, DC_SR(dc), 0);\n                dsp_add_scalarcopy(scalar, uin->i_signal->s_vec,\n                    uin->i_signal->s_n);\n            }\n            uin->i_signal->s_overlap = DC_OVERLAP(dc);\n            uin->i_signal->s_refcount = 1;\n        }\n    }\n        /* allocate space for signals to send to the object's DSP routine. */\n    insig = (t_signal **)getbytes((u->u_nin + u->u_nout) * sizeof(t_signal *));\n    outsig = insig + u->u_nin;\n    for (sig = insig, uin = u->u_in, i = u->u_nin; i--; sig++, uin++)\n    {\n        *sig = uin->i_signal;\n        if (1)\n        {\n                /* if scalar or borrowed, put on free-after-dsp-call list -\n                we can't reuse them yet because the \"dsp\" call below might\n                then reclaim and bash them while someone else still needs to\n                see the s_vec/s_length/s_nchans fields.  Otherwise, the signal\n                owns its s_vec array and, to maximize in-place reuse of s_vec\n                arrays, we mark it free now so that we can get it back when\n                creating output signals below. */\n\n            if (nofreesigs || (*sig)->s_isscalar || (*sig)->s_isborrowed)\n            {\n                if ((*sig)->s_refcount > 1)\n                    (*sig)->s_refcount--;\n                else (*sig)->s_nextfree = freelater, freelater = *sig;\n            }\n            else signal_dereference(*sig);\n        }\n    }\n        /* Create output signals.  These may re-inhabit space that was freed\n        in the previous step (so tilde objects must be able to compute\n        in place.)\n        We delay creating signals for subcanvases and outlet~ objects;\n        instead we create \"borrowed\" ones so that we can track the refcount.\n        The subcanvas/outlet~ later fixes the borrowed signal to show\n        where the output data actually is, to avoid having to copy it.\n        Otherwise, in case the CLASS_MULTICHANNEL flag is set for the object,\n        we pass \"null\" signal and expect the DSP routine to replace it..\n        In any other case, we just allocate a new output vector. */\n    for (sig = outsig, uout = u->u_out, i = u->u_nout; i--; sig++, uout++)\n    {\n        if (nonewsigs)\n            *sig = signal_new(0, 1, DC_SR(dc), 0);\n        else if (flags & CLASS_MULTICHANNEL)\n            *sig = &dc->dc_nullsignal;\n        else *sig = signal_new(DC_LENGTH(dc), 1, DC_SR(dc), 0);\n        (*sig)->s_overlap = DC_OVERLAP(dc);\n\n    }\n        /* now call the DSP scheduling routine for the ugen.  This\n        routine must fill in \"borrowed\" signal outputs in case it's either\n        a subcanvas or a signal inlet. */\n    mess1(&u->u_obj->ob_pd, gensym(\"dsp\"), insig);\n\n    for (sig = outsig, uout = u->u_out, i = u->u_nout; i--; sig++, uout++)\n    {\n        uout->o_signal = *sig;\n        (*sig)->s_refcount = uout->o_nconnect;\n\n            /* if any output signals aren't connected to anyone, free them\n            now; otherwise they'll either get freed when the reference count\n            goes back to zero, or even later as explained above. */\n\n        if (!(*sig)->s_refcount)\n            signal_makereusable(*sig);\n    }\n    if (THIS->u_loud)\n    {\n        if (u->u_nin + u->u_nout == 0) post(\"put %s %d\",\n            class_getname(u->u_obj->ob_pd), ugen_index(dc, u));\n        else if (u->u_nin + u->u_nout == 1) post(\"put %s %d (%lx)\",\n            class_getname(u->u_obj->ob_pd), ugen_index(dc, u), insig[0]);\n        else if (u->u_nin + u->u_nout == 2) post(\"put %s %d (%lx %lx)\",\n            class_getname(u->u_obj->ob_pd), ugen_index(dc, u),\n                insig[0], insig[1]);\n        else post(\"put %s %d (%lx %lx %lx ...)\",\n            class_getname(u->u_obj->ob_pd), ugen_index(dc, u),\n                insig[0], insig[1], insig[2]);\n    }\n        /* now we can act on delayed-free */\n    while (freelater)\n    {\n        if (THIS->u_loud)\n            post(\"delayed deref %lx\", freelater);\n        stmp = freelater->s_nextfree;\n        signal_dereference(freelater);\n        freelater = stmp;\n    }\n        /* pass it on and add anyone whose last inlet was filled */\n    for (uout = u->u_out, i = u->u_nout; i--; uout++)\n    {\n        s1 = uout->o_signal;\n        for (oc = uout->o_connections; oc; oc = oc->oc_next)\n        {\n            u2 = oc->oc_who;\n            uin = &u2->u_in[oc->oc_inno];\n                /* if there's already someone here, sum the two */\n            if ((s2 = uin->i_signal))\n            {\n                s1->s_refcount--;\n                s2->s_refcount--;\n                s3 = signal_newlike(s1);\n                if (s1->s_nchans != s2->s_nchans ||\n                    s1->s_length != s2->s_length)\n                {\n                    pd_error(u->u_obj,\n                        \"%s: incompatible signal inputs (%dx%d vs. %dx%d)\",\n                        class_getname(u->u_obj->ob_pd),\n                            s1->s_nchans, s1->s_length,\n                            s2->s_nchans, s2->s_length);\n                    dsp_add_copy(s1->s_vec, s3->s_vec,\n                        s1->s_length * s1->s_nchans);\n                }\n                else\n                {\n                    if (s1->s_sr != s2->s_sr)\n                        bug(\"signals sample rate mismatch\");\n                    dsp_add_plus(s1->s_vec, s2->s_vec, s3->s_vec,\n                        s1->s_length * s1->s_nchans);\n                }\n                uin->i_signal = s3;\n                s3->s_refcount = 1;\n                if (!s1->s_refcount) signal_makereusable(s1);\n                if (!s2->s_refcount) signal_makereusable(s2);\n            }\n            else uin->i_signal = s1;\n            uin->i_ngot++;\n                /* if we didn't fill this inlet don't bother yet */\n            if (uin->i_ngot < uin->i_nconnect)\n                goto notyet;\n                /* if there's more than one, check them all */\n            if (u2->u_nin > 1)\n            {\n                for (uin = u2->u_in, n = u2->u_nin; n--; uin++)\n                    if (uin->i_ngot < uin->i_nconnect) goto notyet;\n            }\n                /* so now we can schedule the ugen.  */\n            ugen_doit(dc, u2);\n        notyet: ;\n        }\n    }\n    t_freebytes(insig,(u->u_nin + u->u_nout) * sizeof(t_signal *));\n    u->u_done = 1;\n}\n\n    /* once the DSP graph is built, we call this routine to sort it.\n    This routine also deletes the graph; later we might want to leave the\n    graph around, in case the user is editing the DSP network, to save having\n    to recreate it all the time.  But not today.  */\n\nvoid ugen_done_graph(t_dspcontext *dc)\n{\n    t_ugenbox *u;\n    t_sigoutlet *uout;\n    t_siginlet *uin;\n    t_sigoutconnect *oc, *oc2;\n    int i, n;\n    t_block *blk;\n    t_dspcontext *parent_context = dc->dc_parentcontext;\n    t_float parent_srate;\n    int parent_vecsize, parent_overlap;\n    int period, frequency, phase, calcsize;\n    t_float srate;\n    int chainblockbegin;    /* DSP chain onset before block prolog code */\n    int chainblockend;      /* and after block epilog code */\n    int chainafterall;      /* and after signal outlet epilog */\n    int reblock = 0, switched;\n    int downsample = 1, upsample = 1;\n    int totaloverlap;\n\n        /* debugging printout */\n    if (THIS->u_loud)\n    {\n        post(\"ugen_done_graph...\");\n        for (u = dc->dc_ugenlist; u; u = u->u_next)\n        {\n            post(\"ugen: %s\", class_getname(u->u_obj->ob_pd));\n            for (uout = u->u_out, i = 0; i < u->u_nout; uout++, i++)\n                for (oc = uout->o_connections; oc; oc = oc->oc_next)\n            {\n                post(\"... out %d to %s, index %d, inlet %d\", i,\n                    class_getname(oc->oc_who->u_obj->ob_pd),\n                        ugen_index(dc, oc->oc_who), oc->oc_inno);\n            }\n        }\n    }\n\n        /* search for an object of class \"block~\" */\n    for (u = dc->dc_ugenlist, blk = 0; u; u = u->u_next)\n    {\n        t_pd *zz = &u->u_obj->ob_pd;\n        if (pd_class(zz) == block_class)\n        {\n            if (blk)\n                pd_error(blk, \"conflicting block~ and/or switch~ objects in same window\");\n            else blk = (t_block *)zz;\n        }\n    }\n\n        /* figure out block size, calling frequency, sample rate */\n    if (parent_context)\n    {\n        parent_srate = DC_SR(parent_context);\n        parent_vecsize = DC_LENGTH(parent_context);\n        parent_overlap = DC_OVERLAP(parent_context);\n    }\n    else\n    {\n        parent_srate = sys_getsr();\n        parent_vecsize = sys_getblksize();\n        parent_overlap = 1;\n    }\n    totaloverlap = parent_overlap;\n    if (blk)\n    {\n        int realoverlap;\n        calcsize = blk->x_calcsize;\n        if (calcsize == 0)\n            calcsize = parent_vecsize;\n        realoverlap = blk->x_overlap;\n        if (realoverlap > calcsize) realoverlap = calcsize;\n        downsample = blk->x_downsample;\n        upsample   = blk->x_upsample;\n        if (downsample > parent_vecsize)\n            downsample = parent_vecsize;\n        period = (calcsize * downsample)/\n            (parent_vecsize * realoverlap * upsample);\n        frequency = (parent_vecsize * realoverlap * upsample)/\n            (calcsize * downsample);\n        phase = blk->x_phase;\n        srate = parent_srate * realoverlap * upsample / downsample;\n        if (period < 1) period = 1;\n        if (frequency < 1) frequency = 1;\n        blk->x_frequency = frequency;\n        blk->x_period = period;\n        blk->x_phase = THIS->u_phase & (period - 1);\n        if (! parent_context || (realoverlap != 1) ||\n            (calcsize != parent_vecsize) ||\n                (downsample != 1) || (upsample != 1))\n                    reblock = 1;\n        switched = blk->x_switched;\n\n        totaloverlap = parent_overlap * realoverlap;\n    }\n    else\n    {\n        srate = parent_srate;\n        calcsize = parent_vecsize;\n        downsample = upsample = 1;\n        period = frequency = 1;\n        phase = 0;\n        if (!parent_context) reblock = 1;\n        switched = 0;\n    }\n    dc->dc_reblock = reblock;\n    dc->dc_switched = switched;\n    dc->dc_nullsignal.s_sr = srate;\n    dc->dc_nullsignal.s_length = calcsize;\n    dc->dc_nullsignal.s_overlap = totaloverlap;\n    dc->dc_nullsignal.s_nchans = -1;    /* fake so we can sanity check */\n\n        /* if we're reblocking or switched, we now have to create output\n        signals to fill in for the \"borrowed\" ones we have now.  This\n        is also possibly true even if we're not blocked/switched, in\n        the case that there was a signal loop.  But we don't know this\n        yet.  */\n\n    if (dc->dc_iosigs && (switched || reblock))\n    {\n        t_signal **sigp;\n        for (i = 0, sigp = dc->dc_iosigs + dc->dc_ninlets; i < dc->dc_noutlets;\n            i++, sigp++)\n        {\n            if ((*sigp)->s_isborrowed && !(*sigp)->s_borrowedfrom)\n            {\n                t_signal *s = signal_new(parent_vecsize, 1, parent_srate, 0);\n                s->s_overlap = totaloverlap;\n                signal_setborrowed(*sigp, s);\n\n                if (THIS->u_loud) post(\"set %lx->%lx\", *sigp,\n                    (*sigp)->s_borrowedfrom);\n            }\n        }\n    }\n\n    if (THIS->u_loud)\n        post(\"reblock %d, switched %d\", reblock, switched);\n\n        /* schedule prologs for inlets and outlets.  If the \"reblock\" flag\n        is set, an inlet will put code on the DSP chain to copy its input\n        into an internal buffer here, before any unit generators' DSP code\n        gets scheduled.  If we don't \"reblock\", inlets will need to get\n        pointers to their corresponding inlets/outlets on the box we're inside,\n        if any.  Outlets will also need pointers, unless we're switched, in\n        which case outlet epilog code will kick in. */\n\n    for (u = dc->dc_ugenlist; u; u = u->u_next)\n    {\n        t_pd *zz = &u->u_obj->ob_pd;\n        t_signal **outsigs = dc->dc_iosigs;\n        if (outsigs) outsigs += dc->dc_ninlets;\n\n        if (pd_class(zz) == vinlet_class)\n            vinlet_dspprolog((struct _vinlet *)zz,\n                dc->dc_iosigs, calcsize, THIS->u_phase, period,\n                    frequency, downsample, upsample, reblock, switched);\n        else if (pd_class(zz) == voutlet_class)\n            voutlet_dspprolog((struct _voutlet *)zz,\n                outsigs, calcsize, THIS->u_phase, period, frequency,\n                    downsample, upsample, reblock, switched);\n    }\n    chainblockbegin = THIS->u_dspchainsize;\n\n    if (blk && (reblock || switched))   /* add the block DSP prolog */\n    {\n        dsp_add(block_prolog, 1, blk);\n        blk->x_chainonset = THIS->u_dspchainsize - 1;\n    }\n        /* Initialize for sorting */\n    for (u = dc->dc_ugenlist; u; u = u->u_next)\n    {\n        u->u_done = 0;\n        for (uout = u->u_out, i = u->u_nout; i--; uout++)\n            uout->o_nsent = 0;\n        for (uin = u->u_in, i = u->u_nin; i--; uin++)\n            uin->i_ngot = 0, uin->i_signal = 0;\n   }\n\n        /* Do the sort */\n\n    for (u = dc->dc_ugenlist; u; u = u->u_next)\n    {\n            /* check that we have no connected signal inlets */\n        if (u->u_done) continue;\n        for (uin = u->u_in, i = u->u_nin; i--; uin++)\n            if (uin->i_nconnect) goto next;\n\n        ugen_doit(dc, u);\n    next: ;\n    }\n\n        /* check for a DSP loop, which is evidenced here by the presence\n        of ugens not yet scheduled. */\n\n    for (u = dc->dc_ugenlist; u; u = u->u_next)\n        if (!u->u_done)\n    {\n        t_signal **sigp;\n        if (!dc->dc_warnedmulti)\n            pd_error(u->u_obj,\n                \"DSP loop detected (some tilde objects weren't scheduled)\");\n                /* this might imply that we have unfilled \"borrowed\" outputs\n                which we'd better fill in now. */\n        for (i = 0, sigp = dc->dc_iosigs + dc->dc_ninlets; i < dc->dc_noutlets;\n            i++, sigp++)\n        {\n            if ((*sigp)->s_isborrowed && !(*sigp)->s_borrowedfrom)\n            {\n                t_signal *s3 = signal_new(parent_vecsize, 1, parent_srate, 0);\n                s3->s_overlap = totaloverlap;\n                signal_setborrowed(*sigp, s3);\n                dsp_add_zero(s3->s_vec, s3->s_n);\n                if (THIS->u_loud)\n                    post(\"oops, belatedly set %lx->%lx\", *sigp,\n                        (*sigp)->s_borrowedfrom);\n            }\n        }\n        break;   /* don't need to keep looking. */\n    }\n\n    if (blk && (reblock || switched))    /* add block DSP epilog */\n        dsp_add(block_epilog, 1, blk);\n    chainblockend = THIS->u_dspchainsize;\n\n        /* add epilogs for outlets.  */\n\n    for (u = dc->dc_ugenlist; u; u = u->u_next)\n    {\n        t_pd *zz = &u->u_obj->ob_pd;\n        if (pd_class(zz) == voutlet_class)\n        {\n            t_signal **iosigs = dc->dc_iosigs;\n            if (iosigs) iosigs += dc->dc_ninlets;\n            voutlet_dspepilog((struct _voutlet *)zz,\n                iosigs, calcsize, THIS->u_phase, period, frequency,\n                    downsample, upsample, reblock, switched);\n        }\n    }\n\n    chainafterall = THIS->u_dspchainsize;\n    if (blk)\n    {\n        blk->x_blocklength = chainblockend - chainblockbegin;\n        blk->x_epiloglength = chainafterall - chainblockend;\n        blk->x_reblock = reblock;\n    }\n\n    if (THIS->u_loud)\n    {\n        t_int *ip;\n        if (!dc->dc_parentcontext)\n            for (i = THIS->u_dspchainsize, ip = THIS->u_dspchain;\n                i--; ip++)\n                    post(\"chain %lx\", *ip);\n        post(\"... ugen_done_graph done.\");\n    }\n        /* now delete everything. */\n    while (dc->dc_ugenlist)\n    {\n        for (uout = dc->dc_ugenlist->u_out, n = dc->dc_ugenlist->u_nout;\n            n--; uout++)\n        {\n            oc = uout->o_connections;\n            while (oc)\n            {\n                oc2 = oc->oc_next;\n                freebytes(oc, sizeof *oc);\n                oc = oc2;\n            }\n        }\n        freebytes(dc->dc_ugenlist->u_out, dc->dc_ugenlist->u_nout *\n            sizeof (*dc->dc_ugenlist->u_out));\n        freebytes(dc->dc_ugenlist->u_in, dc->dc_ugenlist->u_nin *\n            sizeof(*dc->dc_ugenlist->u_in));\n        u = dc->dc_ugenlist;\n        dc->dc_ugenlist = u->u_next;\n        freebytes(u, sizeof *u);\n    }\n    if (THIS->u_context == dc)\n        THIS->u_context = dc->dc_parentcontext;\n    else bug(\"THIS->u_context\");\n    freebytes(dc, sizeof(*dc));\n}\n\nstatic t_signal *ugen_getiosig(int index, int inout)\n{\n    if (!THIS->u_context) bug(\"ugen_getiosig\");\n    if (THIS->u_context->dc_toplevel) return (0);\n    if (inout) index += THIS->u_context->dc_ninlets;\n    return (THIS->u_context->dc_iosigs[index]);\n}\n\n/* -------------- DSP basics, copying and adding signals --------------- */\n\nt_int *plus_perform(t_int *w)\n{\n    t_sample *in1 = (t_sample *)(w[1]);\n    t_sample *in2 = (t_sample *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    while (n--) *out++ = *in1++ + *in2++;\n    return (w+5);\n}\n\nt_int *plus_perf8(t_int *w)\n{\n    t_sample *in1 = (t_sample *)(w[1]);\n    t_sample *in2 = (t_sample *)(w[2]);\n    t_sample *out = (t_sample *)(w[3]);\n    int n = (int)(w[4]);\n    for (; n; n -= 8, in1 += 8, in2 += 8, out += 8)\n    {\n        t_sample f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3];\n        t_sample f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7];\n\n        t_sample g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3];\n        t_sample g4 = in2[4], g5 = in2[5], g6 = in2[6], g7 = in2[7];\n\n        out[0] = f0 + g0; out[1] = f1 + g1; out[2] = f2 + g2; out[3] = f3 + g3;\n        out[4] = f4 + g4; out[5] = f5 + g5; out[6] = f6 + g6; out[7] = f7 + g7;\n    }\n    return (w+5);\n}\n\nvoid dsp_add_plus(t_sample *in1, t_sample *in2, t_sample *out, int n)\n{\n    if (n&7)\n        dsp_add(plus_perform, 4, in1, in2, out, (t_int)n);\n    else\n        dsp_add(plus_perf8, 4, in1, in2, out, (t_int)n);\n}\n\nt_int *copy_perform(t_int *w)\n{\n    t_sample *in1 = (t_sample *)(w[1]);\n    t_sample *out = (t_sample *)(w[2]);\n    int n = (int)(w[3]);\n    while (n--) *out++ = *in1++;\n    return (w+4);\n}\n\nt_int *copy_perf8(t_int *w)\n{\n    t_sample *in1 = (t_sample *)(w[1]);\n    t_sample *out = (t_sample *)(w[2]);\n    int n = (int)(w[3]);\n\n    for (; n; n -= 8, in1 += 8, out += 8)\n    {\n        t_sample f0 = in1[0];\n        t_sample f1 = in1[1];\n        t_sample f2 = in1[2];\n        t_sample f3 = in1[3];\n        t_sample f4 = in1[4];\n        t_sample f5 = in1[5];\n        t_sample f6 = in1[6];\n        t_sample f7 = in1[7];\n\n        out[0] = f0;\n        out[1] = f1;\n        out[2] = f2;\n        out[3] = f3;\n        out[4] = f4;\n        out[5] = f5;\n        out[6] = f6;\n        out[7] = f7;\n    }\n    return (w+4);\n}\n\nvoid dsp_add_copy(t_sample *in, t_sample *out, int n)\n{\n    if (n&7)\n        dsp_add(copy_perform, 3, in, out, (t_int)n);\n    else\n        dsp_add(copy_perf8, 3, in, out, (t_int)n);\n}\n\nt_int *scalarcopy_perform(t_int *w)\n{\n    t_float f = *(t_float *)(w[1]);\n    t_sample *out = (t_sample *)(w[2]);\n    int n = (int)(w[3]);\n    while (n--)\n        *out++ = f;\n    return (w+4);\n}\n\nt_int *scalarcopy_perf8(t_int *w)\n{\n    t_float f = *(t_float *)(w[1]);\n    t_sample *out = (t_sample *)(w[2]);\n    int n = (int)(w[3]);\n\n    for (; n; n -= 8, out += 8)\n    {\n        out[0] = f;\n        out[1] = f;\n        out[2] = f;\n        out[3] = f;\n        out[4] = f;\n        out[5] = f;\n        out[6] = f;\n        out[7] = f;\n    }\n    return (w+4);\n}\n\nvoid dsp_add_scalarcopy(t_float *in, t_sample *out, int n)\n{\n    if (n&7)\n        dsp_add(scalarcopy_perform, 3, in, out, (t_int)n);\n    else\n        dsp_add(scalarcopy_perf8, 3, in, out, (t_int)n);\n}\n\n/* ------------------------ samplerate~~ -------------------------- */\n\nstatic t_class *samplerate_tilde_class;\n\ntypedef struct _samplerate\n{\n    t_object x_obj;\n    t_float x_sr;\n    t_canvas *x_canvas;\n} t_samplerate;\n\nstatic void samplerate_tilde_bang(t_samplerate *x)\n{\n    outlet_float(x->x_obj.ob_outlet, canvas_getsr(x->x_canvas));\n}\n\nstatic void *samplerate_tilde_new(t_symbol *s)\n{\n    t_samplerate *x = (t_samplerate *)pd_new(samplerate_tilde_class);\n    outlet_new(&x->x_obj, &s_float);\n    x->x_canvas = canvas_getcurrent();\n    return (x);\n}\n\nstatic void samplerate_tilde_setup(void)\n{\n    samplerate_tilde_class = class_new(gensym(\"samplerate~\"),\n        (t_newmethod)samplerate_tilde_new, 0, sizeof(t_samplerate), 0, 0);\n    class_addbang(samplerate_tilde_class, samplerate_tilde_bang);\n}\n\n/* -------------------- setup routine -------------------------- */\n\nvoid d_ugen_setup(void)\n{\n    block_tilde_setup();\n    samplerate_tilde_setup();\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/g_all_guis.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution. */\n\n/* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */\n/* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */\n\n\n#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n#include <ctype.h>\n#include \"m_pd.h\"\n#include \"s_stuff.h\"\n\n#include \"g_all_guis.h\"\n\n#include \"m_private_utils.h\"\n\n#ifdef _WIN32\n#include <io.h>\n#else\n#include <unistd.h>\n#endif\n\n\ntypedef struct _iemgui_private {\n    int p_prevX, p_prevY;\n    t_iemgui_drawfunctions p_widget;\n    int p_binbuf_valid;\n} t_iemgui_private;\n\n/*  #define GGEE_HSLIDER_COMPATIBLE  */\n\n/* helpers */\nstatic int srl_is_valid(const t_symbol* s)\n{\n    return (!!s && s != &s_);\n}\n\n\n/*------------------ global variables -------------------------*/\n\nint iemgui_color_hex[]=\n{\n    16579836, 10526880, 4210752, 16572640, 16572608,\n    16579784, 14220504, 14220540, 14476540, 16308476,\n    14737632, 8158332, 2105376, 16525352, 16559172,\n    15263784, 1370132, 2684148, 3952892, 16003312,\n    12369084, 6316128, 0, 9177096, 5779456,\n    7874580, 2641940, 17488, 5256, 5767248\n};\n\nint iemgui_vu_db2i[]=\n{\n    1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n    1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n    1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n    1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n    2, 2, 2, 2, 2, 2, 2, 2, 2, 2,\n    2, 2, 2, 2, 2, 2, 2, 2, 2, 2,\n    2, 2, 2, 2, 2, 2, 2, 2, 2, 2,\n    2, 2, 2, 2, 2, 2, 2, 2, 2, 2,\n    3, 3, 3, 3, 3, 3, 3, 3, 3, 3,\n    4, 4, 4, 4, 4, 4, 4, 4, 4, 4,\n    5, 5, 5, 5, 5, 5, 5, 5, 5, 5,\n    6, 6, 6, 6, 6, 6, 6, 6, 6, 6,\n    7, 7, 7, 7, 7, 7, 7, 7, 7, 7,\n    8, 8, 8, 8, 8, 8, 8, 8, 8, 8,\n    9, 9, 9, 9, 9,10,10,10,10,10,\n    11,11,11,11,11,12,12,12,12,12,\n    13,13,13,13,14,14,14,14,15,15,\n    15,15,16,16,16,16,17,17,17,18,\n    18,18,19,19,19,20,20,20,21,21,\n    22,22,23,23,24,24,25,26,27,28,\n    29,30,31,32,33,33,34,34,35,35,\n    36,36,37,37,37,38,38,38,39,39,\n    39,39,39,39,40,40\n};\n\nint iemgui_vu_col[]=\n{\n    0,17,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,\n    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\n};\n\nchar *iemgui_vu_scale_str[]=\n{\n    \"\",\n    \"<-99\",\n    \"\",\n    \"\",\n    \"\",\n    \"-50\",\n    \"\",\n    \"\",\n    \"\",\n    \"-30\",\n    \"\",\n    \"\",\n    \"\",\n    \"-20\",\n    \"\",\n    \"\",\n    \"\",\n    \"-12\",\n    \"\",\n    \"\",\n    \"\",\n    \"-6\",\n    \"\",\n    \"\",\n    \"\",\n    \"-2\",\n    \"\",\n    \"\",\n    \"\",\n    \"-0dB\",\n    \"\",\n    \"\",\n    \"\",\n    \"+2\",\n    \"\",\n    \"\",\n    \"\",\n    \"+6\",\n    \"\",\n    \"\",\n    \"\",\n    \">+12\",\n    \"\",\n    \"\",\n    \"\",\n    \"\",\n    \"\",\n};\n\n\n/*------------------ global functions -------------------------*/\n\nint iemgui_clip_size(int size)\n{\n    if(size < IEM_GUI_MINSIZE)\n        size = IEM_GUI_MINSIZE;\n    return(size);\n}\n\nint iemgui_modulo_color(int col)\n{\n    while(col >= IEM_GUI_MAX_COLOR)\n        col -= IEM_GUI_MAX_COLOR;\n    while(col < 0)\n        col += IEM_GUI_MAX_COLOR;\n    return(col);\n}\n\nt_symbol *iemgui_dollar2raute(t_symbol *s)\n{\n    const char *s1;\n    char buf[MAXPDSTRING+1], *s2;\n    if (strlen(s->s_name) >= MAXPDSTRING)\n        return (s);\n    for (s1 = s->s_name, s2 = buf; ; s1++, s2++)\n    {\n        if (*s1 == '$')\n            *s2 = '#';\n        else if (!(*s2 = *s1))\n            break;\n    }\n    return(gensym(buf));\n}\n\nt_symbol *iemgui_raute2dollar(t_symbol *s)\n{\n    const char *s1;\n    char buf[MAXPDSTRING+1], *s2;\n    if (strlen(s->s_name) >= MAXPDSTRING)\n        return (s);\n    for (s1 = s->s_name, s2 = buf; ; s1++, s2++)\n    {\n        if (*s1 == '#')\n            *s2 = '$';\n        else if (!(*s2 = *s1))\n            break;\n    }\n    return(gensym(buf));\n}\n\nvoid iemgui_verify_snd_ne_rcv(t_iemgui *iemgui)\n{\n    iemgui->x_fsf.x_put_in2out = 1;\n    if(iemgui->x_fsf.x_snd_able && iemgui->x_fsf.x_rcv_able)\n    {\n        if(!strcmp(iemgui->x_snd->s_name, iemgui->x_rcv->s_name))\n            iemgui->x_fsf.x_put_in2out = 0;\n    }\n}\n\nt_symbol *iemgui_new_dogetname(t_iemgui *iemgui, int indx, t_atom *argv)\n{\n    if (IS_A_SYMBOL(argv, indx)) {\n        t_symbol*name=atom_getsymbolarg(indx, 100000, argv);\n        if(gensym(\"empty\") == name)\n            return 0;\n        return name;\n    }\n    else if (IS_A_FLOAT(argv, indx))\n    {\n        char str[80];\n        sprintf(str, \"%d\", (int)atom_getfloatarg(indx, 100000, argv));\n        return (gensym(str));\n    }\n    else return 0;\n}\n\nvoid iemgui_new_getnames(t_iemgui *iemgui, int indx, t_atom *argv)\n{\n    if (argv)\n    {\n        iemgui->x_snd = iemgui_new_dogetname(iemgui, indx, argv);\n        iemgui->x_rcv = iemgui_new_dogetname(iemgui, indx+1, argv);\n        if(IS_A_FLOAT(argv, indx+2))\n        {\n            char str[80];\n            atom_string(argv+indx+2, str, sizeof(str));\n            iemgui->x_lab = gensym(str);\n        } else {\n            iemgui->x_lab = iemgui_new_dogetname(iemgui, indx+2, argv);\n        }\n        iemgui->x_private->p_binbuf_valid = 1;\n    }\n    else\n    {\n        iemgui->x_snd = iemgui->x_rcv = iemgui->x_lab = 0;\n        iemgui->x_private->p_binbuf_valid = 0;\n    }\n    /* in the object's constructor, we can't access the raw values yet: */\n    iemgui->x_snd_unexpanded = iemgui->x_rcv_unexpanded = iemgui->x_lab_unexpanded = 0;\n    iemgui->x_binbufindex = indx;\n    iemgui->x_labelbindex = indx + 3;\n}\n\n    /* initialize a single symbol in unexpanded form.  We reach into the\n    binbuf to grab them; if there's nothing there, set it to the\n    fallback; if still nothing, set to NULL. */\nstatic void iemgui_init_sym2dollararg(t_iemgui *iemgui, t_symbol **symp,\n    int indx, t_symbol *fallback)\n{\n    t_binbuf *b = iemgui->x_obj.ob_binbuf;\n\n    if ((!*symp) && iemgui->x_private->p_binbuf_valid && (binbuf_getnatom(b) > indx))\n    {\n        t_atom *a = binbuf_getvec(b) + indx;\n        char astring[80];\n        const char *buf = astring;\n        if(A_SYMBOL == a->a_type)\n        {\n            buf = atom_getsymbol(a)->s_name;\n        } else {\n            atom_string(a, astring, sizeof(astring));\n        }\n        if(strcmp(buf, \"empty\"))\n            *symp = gensym(buf);\n    }\n    if (!*symp)\n        *symp = fallback;\n}\n\n    /* get the unexpanded versions of the symbols;\n       initialize them if necessary. */\nvoid iemgui_all_sym2dollararg(t_iemgui *iemgui, t_symbol **srlsym)\n{\n    iemgui_init_sym2dollararg(iemgui, &iemgui->x_snd_unexpanded,\n        iemgui->x_binbufindex+1, iemgui->x_snd);\n    iemgui_init_sym2dollararg(iemgui, &iemgui->x_rcv_unexpanded,\n        iemgui->x_binbufindex+2, iemgui->x_rcv);\n    iemgui_init_sym2dollararg(iemgui, &iemgui->x_lab_unexpanded,\n        iemgui->x_labelbindex, iemgui->x_lab);\n    srlsym[0] = iemgui->x_snd_unexpanded;\n    srlsym[1] = iemgui->x_rcv_unexpanded;\n    srlsym[2] = iemgui->x_lab_unexpanded;\n}\n\n    /* helper for iemgui_all_dollararg2sym */\nstatic t_symbol*do_all_dollarg2sym(t_iemgui*iemgui, t_symbol**s, size_t index)\n{\n    t_symbol*org = s[index];\n    if(org) {\n        s[index] = canvas_realizedollar(iemgui->x_glist, org);\n    }\n    return org;\n}\n    /* convert symbols in \"$\" form to the expanded symbols */\nvoid iemgui_all_dollararg2sym(t_iemgui *iemgui, t_symbol **srlsym)\n{\n        /* save unexpanded ones for later */\n    iemgui->x_snd_unexpanded = do_all_dollarg2sym(iemgui, srlsym, 0);\n    iemgui->x_rcv_unexpanded = do_all_dollarg2sym(iemgui, srlsym, 1);\n    iemgui->x_lab_unexpanded = do_all_dollarg2sym(iemgui, srlsym, 2);\n}\n\n\n\nstatic t_symbol* color2symbol(int col) {\n    const int  compat = (pd_compatibilitylevel < 48) ? 1 : 0;\n\n    char colname[MAXPDSTRING];\n    colname[0] = colname[MAXPDSTRING-1] = 0;\n\n    if (compat)\n    {\n            /* compatibility with Pd<=0.47: saves colors as numbers with limited resolution */\n        int col2 = -1 - (((0xfc0000 & col) >> 6)|((0xfc00 & col) >> 4)|((0xfc & col) >> 2));\n        snprintf(colname, MAXPDSTRING-1, \"%d\", col2);\n    } else {\n        snprintf(colname, MAXPDSTRING-1, \"#%06x\", col);\n    }\n    return gensym(colname);\n}\n\nstatic void iemgui_all_col2save(t_iemgui *iemgui, t_symbol**bflcol)\n{\n    bflcol[0] = color2symbol(iemgui->x_bcol);\n    bflcol[1] = color2symbol(iemgui->x_fcol);\n    bflcol[2] = color2symbol(iemgui->x_lcol);\n}\n\nstatic int iemgui_getcolorarg(int index, int argc, t_atom*argv)\n{\n    if(index < 0 || index >= argc)\n        return 0;\n    if(IS_A_FLOAT(argv,index))\n        return atom_getfloatarg(index, argc, argv);\n    if(IS_A_SYMBOL(argv,index))\n    {\n        t_symbol*s=atom_getsymbolarg(index, argc, argv);\n        if ('#' == s->s_name[0])\n        {\n            int col = (int)strtol(s->s_name+1, 0, 16);\n            return col & 0xFFFFFF;\n        }\n    }\n    return 0;\n}\n\nstatic int colfromatomload(t_atom*colatom)\n{\n    int color;\n        /* old-fashioned color argument, either a number or symbol\n        evaluating to an integer */\n    if (colatom->a_type == A_FLOAT)\n        color = atom_getfloat(colatom);\n    else if (colatom->a_type == A_SYMBOL &&\n        (isdigit(colatom->a_w.w_symbol->s_name[0]) ||\n            colatom->a_w.w_symbol->s_name[0] == '-'))\n                color = atoi(colatom->a_w.w_symbol->s_name);\n\n        /* symbolic color */\n    else return (iemgui_getcolorarg(0, 1, colatom));\n    if (color < 0)\n    {\n        color = -1 - color;\n        color = ((color & 0x3f000) << 6)|((color & 0xfc0) << 4)|\n            ((color & 0x3f) << 2);\n    }\n    else\n    {\n        color = iemgui_modulo_color(color);\n        color = iemgui_color_hex[color];\n    }\n    return (color);\n}\n\nvoid iemgui_all_loadcolors(t_iemgui *iemgui, t_atom*bcol, t_atom*fcol, t_atom*lcol)\n{\n    if(bcol)iemgui->x_bcol = colfromatomload(bcol);\n    if(fcol)iemgui->x_fcol = colfromatomload(fcol);\n    if(lcol)iemgui->x_lcol = colfromatomload(lcol);\n}\n\nint iemgui_compatible_colorarg(int index, int argc, t_atom* argv)\n{\n    if (index < 0 || index >= argc)\n        return 0;\n    if(IS_A_FLOAT(argv,index))\n        {\n            int col=atom_getfloatarg(index, argc, argv);\n            if(col >= 0)\n            {\n                int idx = iemgui_modulo_color(col);\n                return(iemgui_color_hex[(idx)]);\n            }\n            else\n               return((-1 -col)&0xffffff);\n        }\n    return iemgui_getcolorarg(index, argc, argv);\n}\n\nvoid iemgui_send(void *x, t_iemgui *iemgui, t_symbol *s)\n{\n    int sndable=1, oldsndrcvable=0;\n\n    if(iemgui->x_fsf.x_rcv_able)\n        oldsndrcvable |= IEM_GUI_OLD_RCV_FLAG;\n    if(iemgui->x_fsf.x_snd_able)\n        oldsndrcvable |= IEM_GUI_OLD_SND_FLAG;\n\n    if(s && gensym(\"empty\") == s)\n        s = 0;\n\n    if(s) {\n        iemgui->x_snd_unexpanded = s;\n        iemgui->x_snd = canvas_realizedollar(iemgui->x_glist, s);\n    } else {\n        iemgui->x_snd_unexpanded = &s_;\n        iemgui->x_snd = 0;\n        sndable = 0;\n    }\n    iemgui->x_fsf.x_snd_able = sndable;\n    iemgui_verify_snd_ne_rcv(iemgui);\n    if(glist_isvisible(iemgui->x_glist) && gobj_shouldvis((t_gobj *)x, iemgui->x_glist))\n        (*iemgui->x_draw)(x, iemgui->x_glist, IEM_GUI_DRAW_MODE_IO + oldsndrcvable);\n}\n\nvoid iemgui_receive(void *x, t_iemgui *iemgui, t_symbol *s)\n{\n    int oldsndrcvable=0;\n\n    if(iemgui->x_fsf.x_rcv_able)\n        oldsndrcvable |= IEM_GUI_OLD_RCV_FLAG;\n    if(iemgui->x_fsf.x_snd_able)\n        oldsndrcvable |= IEM_GUI_OLD_SND_FLAG;\n\n    if(s && gensym(\"empty\") == s)\n        s = 0;\n\n    if(s) {\n        iemgui->x_rcv_unexpanded = s;\n        s = canvas_realizedollar(iemgui->x_glist, s);\n    } else {\n        iemgui->x_rcv_unexpanded = &s_;\n    }\n    if(s)\n    {\n        if(!iemgui->x_rcv || strcmp(s->s_name, iemgui->x_rcv->s_name))\n        {\n            if(iemgui->x_fsf.x_rcv_able)\n                pd_unbind(&iemgui->x_obj.ob_pd, iemgui->x_rcv);\n            iemgui->x_rcv = s;\n            pd_bind(&iemgui->x_obj.ob_pd, iemgui->x_rcv);\n        }\n    }\n    else if(iemgui->x_fsf.x_rcv_able)\n    {\n        pd_unbind(&iemgui->x_obj.ob_pd, iemgui->x_rcv);\n        iemgui->x_rcv = s;\n    }\n    iemgui->x_fsf.x_rcv_able = (s!=0);\n    iemgui_verify_snd_ne_rcv(iemgui);\n    if(glist_isvisible(iemgui->x_glist) && gobj_shouldvis((t_gobj *)x, iemgui->x_glist))\n        (*iemgui->x_draw)(x, iemgui->x_glist, IEM_GUI_DRAW_MODE_IO + oldsndrcvable);\n}\n\nstatic void iemgui_dolabelpos(t_object*obj, t_iemgui*iemgui) {\n    int zoom = glist_getzoom(iemgui->x_glist);\n    int x0 = text_xpix((t_object *)obj, iemgui->x_glist);\n    int y0 = text_ypix((t_object *)obj, iemgui->x_glist);\n    int dx = iemgui->x_ldx, dy = iemgui->x_ldy;\n    char tag[128];\n    sprintf(tag, \"%pLABEL\", obj);\n    if(gensym(\"\") == iemgui->x_lab) {\n        /* put empty labels where they don't create scrollbars */\n        dx = 0;\n        dy = 7;\n    }\n    pdgui_vmess(0, \"crs ii\",\n        glist_getcanvas(iemgui->x_glist), \"coords\", tag,\n        x0  + dx*zoom, y0 + dy*zoom);\n}\nvoid iemgui_dolabel(void *x, t_iemgui *iemgui, t_symbol *s, int senditup)\n{\n    t_symbol *empty = gensym(\"\");\n    t_symbol *old = iemgui->x_lab;\n    s = s?canvas_realizedollar(iemgui->x_glist, s):0;\n    if (!(s && s->s_name && s->s_name[0] && strcmp(s->s_name, \"empty\")))\n        s = empty;\n    iemgui->x_lab = s;\n\n    if(senditup < 0) {\n        senditup = (glist_isvisible(iemgui->x_glist) && iemgui->x_lab != old);\n    }\n\n    if(senditup)\n    {\n        const char*label = s->s_name;\n        int have_label = (s != empty);\n        char tag[128];\n        sprintf(tag, \"%pLABEL\", x);\n        pdgui_vmess(\"pdtk_text_set\", \"cs s\",\n            glist_getcanvas(iemgui->x_glist), tag,\n            have_label?s->s_name:\"\");\n        iemgui_dolabelpos(x, iemgui);\n    }\n}\nvoid iemgui_label(void *x, t_iemgui *iemgui, t_symbol *s)\n{\n    iemgui->x_lab_unexpanded = s;\n    iemgui_dolabel(x, iemgui, s, -1);\n}\n\n\nvoid iemgui_label_pos(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av)\n{\n    iemgui->x_ldx = (int)atom_getfloatarg(0, ac, av);\n    iemgui->x_ldy = (int)atom_getfloatarg(1, ac, av);\n    if(glist_isvisible(iemgui->x_glist))\n        iemgui_dolabelpos(x, iemgui);\n}\n\nvoid iemgui_label_font(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av)\n{\n    int zoom = glist_getzoom(iemgui->x_glist);\n    int f = (int)atom_getfloatarg(0, ac, av);\n\n    if(f == 1) strcpy(iemgui->x_font, \"helvetica\");\n    else if(f == 2) strcpy(iemgui->x_font, \"times\");\n    else\n    {\n        f = 0;\n        strcpy(iemgui->x_font, sys_font);\n    }\n    iemgui->x_fsf.x_font_style = f;\n    f = (int)atom_getfloatarg(1, ac, av);\n    if(f < 4)\n        f = 4;\n    iemgui->x_fontsize = f;\n    if(glist_isvisible(iemgui->x_glist))\n    {\n        char tag[128];\n        t_atom fontatoms[3];\n        sprintf(tag, \"%pLABEL\", x);\n        SETSYMBOL(fontatoms+0, gensym(iemgui->x_font));\n        SETFLOAT (fontatoms+1, -iemgui->x_fontsize*zoom);\n        SETSYMBOL(fontatoms+2, gensym(sys_fontweight));\n        pdgui_vmess(0, \"crs rA\",\n            glist_getcanvas(iemgui->x_glist), \"itemconfigure\", tag,\n            \"-font\", 3, fontatoms);\n    }\n}\n\nstatic void iemgui_do_drawmove(void *x, t_iemgui*iemgui)\n{\n    if(glist_isvisible(iemgui->x_glist))\n    {\n        int xpos = text_xpix(&iemgui->x_obj, iemgui->x_glist);\n        int ypos = text_ypix(&iemgui->x_obj, iemgui->x_glist);\n        (*iemgui->x_draw)(x, iemgui->x_glist, IEM_GUI_DRAW_MODE_MOVE);\n        iemgui->x_private->p_prevX = xpos;\n        iemgui->x_private->p_prevY = ypos;\n        canvas_fixlinesfor(iemgui->x_glist, (t_text*)x);\n    }\n}\n\nvoid iemgui_size(void *x, t_iemgui *iemgui)\n{\n    if(glist_isvisible(iemgui->x_glist))\n    {\n        (*iemgui->x_draw)(x, iemgui->x_glist, IEM_GUI_DRAW_MODE_CONFIG);\n        (*iemgui->x_draw)(x, iemgui->x_glist, IEM_GUI_DRAW_MODE_IO);\n        canvas_fixlinesfor(iemgui->x_glist, (t_text*)x);\n    }\n}\n\nvoid iemgui_delta(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av)\n{\n    int zoom = glist_getzoom(iemgui->x_glist);\n    iemgui->x_obj.te_xpix += (int)atom_getfloatarg(0, ac, av);\n    iemgui->x_obj.te_ypix += (int)atom_getfloatarg(1, ac, av);\n    iemgui_do_drawmove(x, iemgui);\n}\n\nvoid iemgui_pos(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av)\n{\n    int zoom = glist_getzoom(iemgui->x_glist);\n    iemgui->x_obj.te_xpix = (int)atom_getfloatarg(0, ac, av);\n    iemgui->x_obj.te_ypix = (int)atom_getfloatarg(1, ac, av);\n    iemgui_do_drawmove(x, iemgui);\n}\n\nvoid iemgui_color(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av)\n{\n    if (ac >= 1)\n        iemgui->x_bcol = iemgui_compatible_colorarg(0, ac, av);\n    if (ac == 2 && pd_compatibilitylevel < 47)\n            /* old versions of Pd updated foreground and label color\n            if only two args; now we do it more coherently. */\n        iemgui->x_lcol = iemgui_compatible_colorarg(1, ac, av);\n    else if (ac >= 2)\n        iemgui->x_fcol = iemgui_compatible_colorarg(1, ac, av);\n    if (ac >= 3)\n        iemgui->x_lcol = iemgui_compatible_colorarg(2, ac, av);\n    if(glist_isvisible(iemgui->x_glist))\n        (*iemgui->x_draw)(x, iemgui->x_glist, IEM_GUI_DRAW_MODE_CONFIG);\n}\n\nvoid iemgui_displace(t_gobj *z, t_glist *glist, int dx, int dy)\n{\n    t_iemgui *x = (t_iemgui *)z;\n\n    x->x_obj.te_xpix += dx;\n    x->x_obj.te_ypix += dy;\n    iemgui_do_drawmove(x, x);\n}\n\nvoid iemgui_select(t_gobj *z, t_glist *glist, int selected)\n{\n    t_iemgui *x = (t_iemgui *)z;\n\n    x->x_fsf.x_selected = selected;\n    if(glist_isvisible(x->x_glist))\n        (*x->x_draw)((void *)z, glist, IEM_GUI_DRAW_MODE_SELECT);\n}\n\nvoid iemgui_delete(t_gobj *z, t_glist *glist)\n{\n    canvas_deletelinesfor(glist, (t_text *)z);\n}\n\nvoid iemgui_vis(t_gobj *z, t_glist *glist, int vis)\n{\n    t_iemgui *x = (t_iemgui *)z;\n\n    (*x->x_draw)((void *)z, glist, IEM_GUI_DRAW_MODE_ERASE);\n    if (vis)\n        (*x->x_draw)((void *)z, glist, IEM_GUI_DRAW_MODE_NEW);\n    else\n    {\n        sys_unqueuegui(z);\n    }\n    x->x_private->p_prevX = text_xpix(&x->x_obj, glist);\n    x->x_private->p_prevY = text_ypix(&x->x_obj, glist);\n}\n\n/* store saveable symbols (with spaces and dollars escaped) into srl[3] */\nvoid iemgui_save(t_iemgui *iemgui, t_symbol **srl, t_symbol**bflcol)\n{\n    int i;\n    srl[0] = iemgui->x_snd;\n    srl[1] = iemgui->x_rcv;\n    srl[2] = iemgui->x_lab;\n    iemgui_all_sym2dollararg(iemgui, srl);\n    for(i=0; i<3; i++)\n    {\n        if(!srl[i] || !srl[i]->s_name || !srl[i]->s_name[0])\n            srl[i]=gensym(\"empty\");\n    }\n    iemgui_all_col2save(iemgui, bflcol);\n}\n\n    /* inform GUIs that glist's zoom is about to change.  The glist will\n    take care of x,y locations but we have to adjust width and height */\nvoid iemgui_zoom(t_iemgui *iemgui, t_floatarg zoom)\n{\n    int oldzoom = iemgui->x_glist->gl_zoom;\n    if (oldzoom < 1)\n        oldzoom = 1;\n    iemgui->x_w = (int)(iemgui->x_w)/oldzoom*(int)zoom;\n    iemgui->x_h = (int)(iemgui->x_h)/oldzoom*(int)zoom;\n}\n\n    /* when creating a new GUI from menu onto a zoomed canvas, pretend to\n    change the canvas's zoom so we'll get properly sized */\nvoid iemgui_newzoom(t_iemgui *iemgui)\n{\n    if (iemgui->x_glist->gl_zoom != 1)\n    {\n        int newzoom = iemgui->x_glist->gl_zoom;\n        iemgui->x_glist->gl_zoom = 1;\n        iemgui_zoom(iemgui, (t_floatarg)newzoom);\n        iemgui->x_glist->gl_zoom = newzoom;\n    }\n}\n\nvoid iemgui_properties(t_iemgui *iemgui, t_symbol **srl)\n{\n    char label[MAXPDSTRING];\n    int i;\n    srl[0] = iemgui->x_snd;\n    srl[1] = iemgui->x_rcv;\n    srl[2] = iemgui->x_lab;\n\n    iemgui_all_sym2dollararg(iemgui, srl);\n\n    for(i=0; i<3; i++) {\n        if(srl[i])\n            srl[i] = gensym(pdgui_strnescape(label, sizeof(label), srl[i]->s_name, strlen(srl[i]->s_name)));\n    }\n}\n\nvoid iemgui_new_dialog(void*x, t_iemgui*iemgui,\n                       const char*objname,\n                       t_float width,  t_float width_min,\n                       t_float height, t_float height_min,\n                       t_float range_min, t_float range_max,\n                       int schedule,\n                       int mode, /* lin0_log1 */\n                       const char* label_mode0,\n                       const char* label_mode1,\n                       int canloadbang, int steady, int number)\n{\n    char objname_[MAXPDSTRING];\n    t_symbol *srl[3];\n    iemgui_properties(iemgui, srl);\n    sprintf(objname_, \"|%s|\", objname);\n\n    pdgui_stub_vnew(&iemgui->x_obj.ob_pd, \"pdtk_iemgui_dialog\", x,\n        \"r s ffs ffs sfsfs i iss ii si sss ii ii kkk\",\n        objname_,\n        \"\",\n        width, width_min, \"\",\n        height, height_min, \"\",\n        \"\", range_min, \"\", range_max, \"\",\n        schedule,\n        mode, label_mode0, label_mode1,\n        canloadbang?iemgui->x_isa.x_loadinit:-1, steady,\n        \"\", number,\n        srl[0]?srl[0]->s_name:\"\", srl[1]?srl[1]->s_name:\"\", srl[2]?srl[2]->s_name:\"\",\n        iemgui->x_ldx, iemgui->x_ldy,\n        iemgui->x_fsf.x_font_style, iemgui->x_fontsize,\n        iemgui->x_bcol, iemgui->x_fcol, iemgui->x_lcol);\n}\n\nint iemgui_dialog(t_iemgui *iemgui, t_symbol **srl, int argc, t_atom *argv)\n{\n    char str[144];\n    int init = (int)atom_getfloatarg(5, argc, argv);\n    int ldx = (int)atom_getfloatarg(10, argc, argv);\n    int ldy = (int)atom_getfloatarg(11, argc, argv);\n    int f = (int)atom_getfloatarg(12, argc, argv);\n    int fs = (int)atom_getfloatarg(13, argc, argv);\n    int bcol = (int)iemgui_getcolorarg(14, argc, argv);\n    int fcol = (int)iemgui_getcolorarg(15, argc, argv);\n    int lcol = (int)iemgui_getcolorarg(16, argc, argv);\n    int rcv_changed=0, oldsndrcvable=0;\n    int i;\n\n    if(iemgui->x_fsf.x_rcv_able)\n        oldsndrcvable |= IEM_GUI_OLD_RCV_FLAG;\n    if(iemgui->x_fsf.x_snd_able)\n        oldsndrcvable |= IEM_GUI_OLD_SND_FLAG;\n    if(IS_A_SYMBOL(argv,7))\n        srl[0] = atom_getsymbolarg(7, argc, argv);\n    else if(IS_A_FLOAT(argv,7))\n    {\n        srl[0] = gensym(\"empty\");\n    }\n    if(IS_A_SYMBOL(argv,8))\n        srl[1] = atom_getsymbolarg(8, argc, argv);\n    else if(IS_A_FLOAT(argv,8))\n    {\n        srl[1] = gensym(\"empty\");\n    }\n    if(IS_A_SYMBOL(argv,9))\n        srl[2] = atom_getsymbolarg(9, argc, argv);\n    else if(IS_A_FLOAT(argv,9))\n    {\n        sprintf(str, \"%g\", atom_getfloatarg(9, argc, argv));\n        srl[2] = gensym(str);\n    }\n    if(init != 0) init = 1;\n    iemgui->x_isa.x_loadinit = init;\n    for(i=0; i<3; i++)\n        if(!srl_is_valid(srl[i]) || (!strcmp(srl[i]->s_name, \"empty\"))) srl[i] = &s_;\n\n        /* expand dollargs\n         * after this, srl holds the $-expanded versions of the labels\n         * and iemgui->x_(snd|rcv|lab)_unexpanded hold the unexpanded versions\n         */\n    iemgui_all_dollararg2sym(iemgui, srl);\n\n        /* check if the receiver changed */\n    if(0\n       || (!srl_is_valid(iemgui->x_rcv) && srl_is_valid(srl[1])) /* there was none, but now there is */\n       || ( srl_is_valid(iemgui->x_rcv) && !(srl_is_valid(srl[1]))) /* there was one, but now there is */\n       || ( srl_is_valid(iemgui->x_rcv) && srl_is_valid(srl[1]) && iemgui->x_rcv != srl[1])) /* both are valid, but changed */\n        rcv_changed = 1;\n\n        /* if the receiver changed (and was previously set), unbind it */\n    if(rcv_changed && srl_is_valid(iemgui->x_rcv))\n        pd_unbind(&iemgui->x_obj.ob_pd, iemgui->x_rcv);\n\n    iemgui->x_snd = srl[0];\n    iemgui->x_fsf.x_snd_able = srl_is_valid(srl[0]);\n    iemgui->x_rcv = srl[1];\n    iemgui->x_fsf.x_rcv_able = srl_is_valid(srl[1]);\n    iemgui->x_lab = srl[2];\n    iemgui->x_lcol = lcol & 0xffffff;\n    iemgui->x_fcol = fcol & 0xffffff;\n    iemgui->x_bcol = bcol & 0xffffff;\n    iemgui->x_ldx = ldx;\n    iemgui->x_ldy = ldy;\n    if(f == 1) strcpy(iemgui->x_font, \"helvetica\");\n    else if(f == 2) strcpy(iemgui->x_font, \"times\");\n    else\n    {\n        f = 0;\n        strcpy(iemgui->x_font, sys_font);\n    }\n    iemgui->x_fsf.x_font_style = f;\n    if(fs < 4)\n        fs = 4;\n    iemgui->x_fontsize = fs;\n\n        /* if the receiver changed (and is now set), bind it */\n    if(rcv_changed && srl_is_valid(iemgui->x_rcv))\n        pd_bind(&iemgui->x_obj.ob_pd, iemgui->x_rcv);\n\n    iemgui_verify_snd_ne_rcv(iemgui);\n    canvas_dirty(iemgui->x_glist, 1);\n    return(oldsndrcvable);\n}\n\nvoid iemgui_setdialogatoms(t_iemgui *iemgui, int argc, t_atom*argv)\n{\n#define SETCOLOR(a, col) do {char color[MAXPDSTRING]; snprintf(color, MAXPDSTRING-1, \"#%06x\", 0xffffff & col); color[MAXPDSTRING-1] = 0; SETSYMBOL(a, gensym(color));} while(0)\n    t_float zoom = iemgui->x_glist->gl_zoom;\n    t_symbol *srl[3];\n    int for_undo = 1;\n    int i;\n    for(i=0; i<argc; i++)\n        SETFLOAT(argv+i, -1); /* initialize */\n\n    if(for_undo) {\n        static t_symbol*s_empty = 0;\n        if(!s_empty)s_empty = gensym(\"empty\");\n        srl[0] = iemgui->x_snd_unexpanded;\n        srl[1] = iemgui->x_rcv_unexpanded;\n        srl[2] = iemgui->x_lab_unexpanded;\n        /* just in case one of the labels is NULL, set it to something valid */\n        for(i=0; i<3; i++)\n            if (!srl[i])\n                srl[i]=s_empty;\n    } else {\n        iemgui_properties(iemgui, srl);\n    }\n\n    if(argc> 0) SETFLOAT (argv+ 0, iemgui->x_w/zoom);\n    if(argc> 1) SETFLOAT (argv+ 1, iemgui->x_h/zoom);\n    if(argc> 5) SETFLOAT (argv+ 5, iemgui->x_isa.x_loadinit);\n    if(argc> 6) SETFLOAT (argv+ 6, 1); /* num */\n    if(argc> 7) SETSYMBOL(argv+ 7, srl[0]);\n    if(argc> 8) SETSYMBOL(argv+ 8, srl[1]);\n    if(argc> 9) SETSYMBOL(argv+ 9, srl[2]);\n    if(argc>10) SETFLOAT (argv+10, iemgui->x_ldx);\n    if(argc>11) SETFLOAT (argv+11, iemgui->x_ldy);\n    if(argc>12) SETFLOAT (argv+12, iemgui->x_fsf.x_font_style);\n    if(argc>13) SETFLOAT (argv+13, iemgui->x_fontsize);\n    if(argc>14) SETCOLOR (argv+14, iemgui->x_bcol);\n    if(argc>15) SETCOLOR (argv+15, iemgui->x_fcol);\n    if(argc>16) SETCOLOR (argv+16, iemgui->x_lcol);\n}\n\n\n/* pre-0.46 the flags were 1 for 'loadinit' and 1<<20 for 'scale'.\nStarting in 0.46, take either 1<<20 or 1<<1 for 'scale' and save to both\nbits (so that old versions can read files we write).  In the future (2015?)\nwe can stop writing the annoying  1<<20 bit. */\n#define LOADINIT 1\n#define SCALE 2\n#define SCALEBIS (1<<20)\n\nvoid iem_inttosymargs(t_iem_init_symargs *symargp, int n)\n{\n    memset(symargp, 0, sizeof(*symargp));\n    symargp->x_loadinit = ((n & LOADINIT) != 0);\n    symargp->x_scale = ((n & SCALE) || (n & SCALEBIS)) ;\n    symargp->x_flashed = 0;\n    symargp->x_locked = 0;\n}\n\nint iem_symargstoint(t_iem_init_symargs *symargp)\n{\n    return ((symargp->x_loadinit ? LOADINIT : 0) |\n        (symargp->x_scale ? (SCALE | SCALEBIS) : 0));\n}\n\nvoid iem_inttofstyle(t_iem_fstyle_flags *fstylep, int n)\n{\n    memset(fstylep, 0, sizeof(*fstylep));\n    fstylep->x_font_style = (n >> 0);\n    fstylep->x_shiftdown = 0;\n    fstylep->x_selected = 0;\n    fstylep->x_finemoved = 0;\n    fstylep->x_put_in2out = 0;\n    fstylep->x_change = 0;\n    fstylep->x_thick = 0;\n    fstylep->x_lin0_log1 = 0;\n    fstylep->x_steady = 0;\n}\n\nint iem_fstyletoint(t_iem_fstyle_flags *fstylep)\n{\n    return ((fstylep->x_font_style << 0) & 63);\n}\n\nstatic void iemgui_draw_new(t_iemgui*x, t_glist*glist) {;}\nstatic void iemgui_draw_config(t_iemgui*x, t_glist*glist) {;}\nstatic void iemgui_draw_update(t_iemgui*x, t_glist*glist) {;}\nstatic void iemgui_draw_select(t_iemgui*x, t_glist*glist) {;}\nstatic void iemgui_draw_iolets(t_iemgui*x, t_glist*glist, int old_snd_rcv_flags)\n{\n    const int zoom = x->x_glist->gl_zoom;\n    int xpos = text_xpix(&x->x_obj, glist);\n    int ypos = text_ypix(&x->x_obj, glist);\n    int iow = IOWIDTH * zoom, ioh = IEM_GUI_IOHEIGHT * zoom;\n    t_canvas *canvas = glist_getcanvas(glist);\n    char tag_object[128], tag_label[128], tag[128];\n    char *tags[] = {tag_object, tag};\n\n    (void)old_snd_rcv_flags;\n\n    sprintf(tag_object, \"%pOBJ\", x);\n    sprintf(tag_label, \"%pLABEL\", x);\n\n    /* re-create outlet */\n    sprintf(tag, \"%pOUT%d\", x, 0);\n    pdgui_vmess(0, \"crs\", canvas, \"delete\", tag);\n    if(!x->x_fsf.x_snd_able) {\n        pdgui_vmess(0, \"crr iiii rs rS\",\n            canvas, \"create\", \"rectangle\",\n            xpos, ypos + x->x_h + zoom - ioh, xpos + iow, ypos + x->x_h,\n            \"-fill\", \"black\",\n            \"-tags\", 2, tags);\n        /* keep label above outlet */\n        pdgui_vmess(0, \"crss\", canvas, \"lower\", tag, tag_label);\n    }\n\n    /* re-create inlet */\n    sprintf(tag, \"%pIN%d\", x, 0);\n    pdgui_vmess(0, \"crs\", canvas, \"delete\", tag);\n    if(!x->x_fsf.x_rcv_able) {\n        pdgui_vmess(0, \"crr iiii rs rS\",\n            canvas, \"create\", \"rectangle\",\n            xpos, ypos, xpos + iow, ypos - zoom + ioh,\n            \"-fill\", \"black\",\n            \"-tags\", 2, tags);\n        /* keep label above inlet */\n        pdgui_vmess(0, \"crss\", canvas, \"lower\", tag, tag_label);\n    }\n}\n\nstatic void iemgui_draw_erase(t_iemgui* x, t_glist* glist)\n{\n    t_canvas *canvas = glist_getcanvas(glist);\n    char tag_object[128];\n    sprintf(tag_object, \"%pOBJ\", x);\n\n    pdgui_vmess(0, \"crs\", canvas, \"delete\", tag_object);\n}\n\nstatic void iemgui_draw_move(t_iemgui *x, t_glist *glist)\n{\n    t_canvas *canvas = glist_getcanvas(glist);\n    int dx = text_xpix(&x->x_obj, glist) - x->x_private->p_prevX;\n    int dy = text_ypix(&x->x_obj, glist) - x->x_private->p_prevY;\n\n    char tag_object[128];\n    sprintf(tag_object, \"%pOBJ\", x);\n\n    pdgui_vmess(0, \"crs ii\", canvas, \"move\", tag_object, dx, dy);\n}\n\nstatic void iemgui_draw(t_iemgui *x, t_glist *glist, int mode)\n{\n#define DRAW_FUN(fun, x, glist) {                                       \\\n        t_iemdrawfunptr do_draw = x->x_private->p_widget.draw_##fun;    \\\n        if (!do_draw) do_draw = (t_iemdrawfunptr)iemgui_draw_##fun;     \\\n        do_draw(x, glist);                                              \\\n    }\n\n    switch(mode) {\n    case (IEM_GUI_DRAW_MODE_UPDATE):\n    {\n        t_iemdrawfunptr draw_update = x->x_private->p_widget.draw_update;\n        if(!draw_update)\n            draw_update = (t_iemdrawfunptr)iemgui_draw_update;\n        sys_queuegui(x, x->x_glist, (t_guicallbackfn)draw_update);\n    }\n        break;\n    case (IEM_GUI_DRAW_MODE_MOVE):\n        DRAW_FUN(move, x, glist);\n        break;\n    case (IEM_GUI_DRAW_MODE_NEW):\n        DRAW_FUN(new, x, glist);\n        break;\n    case (IEM_GUI_DRAW_MODE_SELECT):\n        DRAW_FUN(select, x, glist);\n        break;\n    case (IEM_GUI_DRAW_MODE_ERASE):\n        DRAW_FUN(erase, x, glist);\n        break;\n    case (IEM_GUI_DRAW_MODE_CONFIG):\n        DRAW_FUN(config, x, glist);\n        break;\n    default:\n        if(x->x_private->p_widget.draw_iolets)\n            x->x_private->p_widget.draw_iolets(x, glist, mode - IEM_GUI_DRAW_MODE_IO);\n        else\n            iemgui_draw_iolets(x, glist, mode - IEM_GUI_DRAW_MODE_IO);\n    }\n}\n\nvoid iemgui_setdrawfunctions(t_iemgui *iemgui, t_iemgui_drawfunctions *w)\n{\n#define SET_DRAW(x, fun) \\\n    x->x_private->p_widget.draw_##fun = w->draw_##fun\n\n    SET_DRAW(iemgui, new);\n    SET_DRAW(iemgui, config);\n    SET_DRAW(iemgui, iolets);\n    SET_DRAW(iemgui, update);\n    SET_DRAW(iemgui, select);\n    SET_DRAW(iemgui, erase);\n    SET_DRAW(iemgui, move);\n}\n\n\n\nt_iemgui *iemgui_new(t_class*cls)\n{\n    t_iemgui *x = (t_iemgui *)pd_new(cls);\n    t_glist *cnv = canvas_getcurrent();\n    int fs = cnv->gl_font;\n    x->x_glist = cnv;\n    x->x_private = (t_iemgui_private*)getbytes(sizeof(*x->x_private));\n    x->x_draw = (t_iemfunptr)iemgui_draw;\n\n    x->x_fontsize = (fs<4)?4:fs;\n/*\n    int fs = x->x_gui.x_fontsize;\n    x->x_gui.x_fontsize = (fs < 4)?4:fs;\n*/\n    iem_inttosymargs(&x->x_isa, 0);\n    iem_inttofstyle(&x->x_fsf, 0);\n\n    x->x_bcol = 0xFCFCFC;\n    x->x_fcol = 0x00;\n    x->x_lcol = 0x00;\n\n    return x;\n}\n\n\n#if 1\n/* LEGACY (for binary compatibility with existing externals)\n * DO NOT USE\n */\n\n/* g_all_guis.h */\n/* *********** */\nint iemgui_clip_font(int size)\n{\n    if(size < IEM_FONT_MINSIZE)\n        size = IEM_FONT_MINSIZE;\n    return(size);\n}\n\nvoid iemgui_all_dollar2raute(t_symbol **srlsym)\n{\n    srlsym[0] = iemgui_dollar2raute(srlsym[0]);\n    srlsym[1] = iemgui_dollar2raute(srlsym[1]);\n    srlsym[2] = iemgui_dollar2raute(srlsym[2]);\n}\n\nvoid iemgui_all_raute2dollar(t_symbol **srlsym)\n{\n    srlsym[0] = iemgui_raute2dollar(srlsym[0]);\n    srlsym[1] = iemgui_raute2dollar(srlsym[1]);\n    srlsym[2] = iemgui_raute2dollar(srlsym[2]);\n}\n\n\n\n/* g_canvas.h */\n/* ********** */\n\nt_symbol *iemgui_put_in_braces(t_symbol *s)\n{\n    const char *s1;\n    char buf[MAXPDSTRING+1], *s2;\n    int i = 0;\n    if (strlen(s->s_name) >= MAXPDSTRING)\n        return (s);\n    for (s1 = s->s_name, s2 = buf; ; s1++, s2++, i++)\n    {\n        if (i == 0)\n        {\n            *s2 = '{';\n            s2++;\n        }\n        if (!(*s2 = *s1))\n        {\n            *s2 = '}';\n            s2++;\n            *s2 = '\\0';\n            break;\n        }\n    }\n    return(gensym(buf));\n}\n\n\n/* no header */\n/* ********* */\nvoid iemgui_all_put_in_braces(t_symbol **srlsym)\n{\n    srlsym[0] = iemgui_put_in_braces(srlsym[0]);\n    srlsym[1] = iemgui_put_in_braces(srlsym[1]);\n    srlsym[2] = iemgui_put_in_braces(srlsym[2]);\n}\n\n    /* for compatibility with pre-0.47 unofficial IEM GUIS like \"knob\". */\nvoid iemgui_all_colfromload(t_iemgui *iemgui, int *bflcol)\n{\n    static int warned = 0;\n    if (!warned)\n    {\n        post(\"warning: external GUI object uses obsolete Pd function %s()\", __FUNCTION__);\n        warned = 1;\n    }\n    if(bflcol[0] < 0)\n    {\n        bflcol[0] = -1 - bflcol[0];\n        iemgui->x_bcol = ((bflcol[0] & 0x3f000) << 6)|((bflcol[0] & 0xfc0) << 4)|\n            ((bflcol[0] & 0x3f) << 2);\n    }\n    else\n    {\n        bflcol[0] = iemgui_modulo_color(bflcol[0]);\n        iemgui->x_bcol = iemgui_color_hex[bflcol[0]];\n    }\n    if(bflcol[1] < 0)\n    {\n        bflcol[1] = -1 - bflcol[1];\n        iemgui->x_fcol = ((bflcol[1] & 0x3f000) << 6)|((bflcol[1] & 0xfc0) << 4)|\n            ((bflcol[1] & 0x3f) << 2);\n    }\n    else\n    {\n        bflcol[1] = iemgui_modulo_color(bflcol[1]);\n        iemgui->x_fcol = iemgui_color_hex[bflcol[1]];\n    }\n    if(bflcol[2] < 0)\n    {\n        bflcol[2] = -1 - bflcol[2];\n        iemgui->x_lcol = ((bflcol[2] & 0x3f000) << 6)|((bflcol[2] & 0xfc0) << 4)|\n            ((bflcol[2] & 0x3f) << 2);\n    }\n    else\n    {\n        bflcol[2] = iemgui_modulo_color(bflcol[2]);\n        iemgui->x_lcol = iemgui_color_hex[bflcol[2]];\n    }\n}\n#endif\n"
  },
  {
    "path": "libs/libpd/pure-data/src/g_all_guis.h",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution. */\n/* g_7_guis.h written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */\n\n#ifndef __g_all_guis_h_\n\n#include \"g_canvas.h\"\n\n#define IEM_GUI_COLNR_WHITE          0\n#define IEM_GUI_COLNR_ML_GREY        1\n#define IEM_GUI_COLNR_D_GREY         2\n#define IEM_GUI_COLNR_L_RED          3\n#define IEM_GUI_COLNR_L_ORANGE       4\n#define IEM_GUI_COLNR_L_YELLOW       5\n#define IEM_GUI_COLNR_L_GREEN        6\n#define IEM_GUI_COLNR_L_CYAN         7\n#define IEM_GUI_COLNR_L_BLUE         8\n#define IEM_GUI_COLNR_L_MAGENTA      9\n\n#define IEM_GUI_COLNR_LL_GREY        10\n#define IEM_GUI_COLNR_M_GREY         11\n#define IEM_GUI_COLNR_DD_GREY        12\n#define IEM_GUI_COLNR_RED            13\n#define IEM_GUI_COLNR_ORANGE         14\n#define IEM_GUI_COLNR_YELLOW         15\n#define IEM_GUI_COLNR_GREEN          16\n#define IEM_GUI_COLNR_CYAN           17\n#define IEM_GUI_COLNR_BLUE           18\n#define IEM_GUI_COLNR_MAGENTA        19\n\n#define IEM_GUI_COLNR_L_GREY         20\n#define IEM_GUI_COLNR_MD_GREY        21\n#define IEM_GUI_COLNR_BLACK          22\n#define IEM_GUI_COLNR_D_RED          23\n#define IEM_GUI_COLNR_D_ORANGE       24\n#define IEM_GUI_COLNR_D_YELLOW       25\n#define IEM_GUI_COLNR_D_GREEN        26\n#define IEM_GUI_COLNR_D_CYAN         27\n#define IEM_GUI_COLNR_D_BLUE         28\n#define IEM_GUI_COLNR_D_MAGENTA      29\n\n#define IEM_GUI_COLOR_SELECTED       0x0000FF\n#define IEM_GUI_COLOR_NORMAL         0x000000\n#define IEM_GUI_COLOR_EDITED         0xFF0000\n\n#define IEM_GUI_MAX_COLOR            30\n\n//#define IEM_GUI_DEFAULTSIZE 15\n/* the \"+3+2\" = \"+TMARGIN+BMARGIN\" from g_rtext.c */\n#define IEM_GUI_DEFAULTSIZE (sys_zoomfontheight(canvas_getcurrent()->gl_font, 1, 0) + 2 + 3)\n#define IEM_GUI_DEFAULTSIZE_SCALE IEM_GUI_DEFAULTSIZE/15.\n#define IEM_GUI_MINSIZE     8\n#define IEM_GUI_MAXSIZE     1000\n#define IEM_SL_DEFAULTSIZE  128\n#define IEM_SL_MINSIZE      2\n#define IEM_FONT_MINSIZE    4\n\n#define IEM_BNG_DEFAULTHOLDFLASHTIME  250\n#define IEM_BNG_DEFAULTBREAKFLASHTIME 50\n#define IEM_BNG_MINHOLDFLASHTIME      50\n#define IEM_BNG_MINBREAKFLASHTIME     10\n\n#define IEM_VU_DEFAULTSIZE 4\n#define IEM_VU_LARGESMALL  2\n#define IEM_VU_MINSIZE     2\n#define IEM_VU_MAXSIZE     25\n#define IEM_VU_STEPS       40\n\n#define IEM_VU_MINDB  -99.9\n#define IEM_VU_MAXDB  12.0\n#define IEM_VU_OFFSET 100.0\n\n#define IEM_RADIO_MAX 128\n\n#define IEM_SYM_UNIQUE_SND  256\n#define IEM_SYM_UNIQUE_RCV  512\n#define IEM_SYM_UNIQUE_LAB  1024\n#define IEM_SYM_UNIQUE_ALL  1792\n#define IEM_FONT_STYLE_ALL  255\n\n#define IEM_MAX_SYM_LEN 127\n\n#define IEM_GUI_DRAW_MODE_UPDATE 0\n#define IEM_GUI_DRAW_MODE_MOVE   1\n#define IEM_GUI_DRAW_MODE_NEW    2\n#define IEM_GUI_DRAW_MODE_SELECT 3\n#define IEM_GUI_DRAW_MODE_ERASE  4\n#define IEM_GUI_DRAW_MODE_CONFIG 5\n#define IEM_GUI_DRAW_MODE_IO     6\n\n#define IEM_GUI_IOHEIGHT IHEIGHT\n\n#define IS_A_POINTER(atom,index) ((atom+index)->a_type == A_POINTER)\n#define IS_A_FLOAT(atom,index) ((atom+index)->a_type == A_FLOAT)\n#define IS_A_SYMBOL(atom,index) ((atom+index)->a_type == A_SYMBOL)\n#define IS_A_DOLLAR(atom,index) ((atom+index)->a_type == A_DOLLAR)\n#define IS_A_DOLLSYM(atom,index) ((atom+index)->a_type == A_DOLLSYM)\n\n#define IEM_FSTYLE_FLAGS_ALL 0x007fffff\n#define IEM_INIT_ARGS_ALL    0x01ffffff\n\n#define IEM_GUI_OLD_SND_FLAG 1\n#define IEM_GUI_OLD_RCV_FLAG 2\n\n#define IEMGUI_MAX_NUM_LEN 32\n\ntypedef enum {\n    horizontal = 0,\n    vertical = 1,\n} t_iem_orientation;\n\n#define IEMGUI_ZOOM(x) ((x)->x_gui.x_glist->gl_zoom)\n\ntypedef struct _iem_fstyle_flags\n{\n    unsigned int x_font_style:6;\n    unsigned int x_rcv_able:1;\n    unsigned int x_snd_able:1;\n    unsigned int x_lab_is_unique:1;\n    unsigned int x_rcv_is_unique:1;\n    unsigned int x_snd_is_unique:1;\n    unsigned int x_lab_arg_tail_len:6;\n    unsigned int x_lab_is_arg_num:6;\n    unsigned int x_shiftdown:1;\n    unsigned int x_selected:1;\n    unsigned int x_finemoved:1;\n    unsigned int x_put_in2out:1;\n    unsigned int x_change:1;\n    unsigned int x_thick:1;\n    unsigned int x_lin0_log1:1;\n    unsigned int x_steady:1;\n} t_iem_fstyle_flags;\n\ntypedef struct _iem_init_symargs\n{\n    unsigned int x_loadinit:1;\n    unsigned int x_rcv_arg_tail_len:6;\n    unsigned int x_snd_arg_tail_len:6;\n    unsigned int x_rcv_is_arg_num:6;\n    unsigned int x_snd_is_arg_num:6;\n    unsigned int x_scale:1;\n    unsigned int x_flashed:1;\n    unsigned int x_locked:1;\n} t_iem_init_symargs;\n\ntypedef void (*t_iemfunptr)(void *x, t_glist *glist, int mode);\n\ntypedef void (*t_iemdrawfunptr)(void *x, t_glist *glist);\ntypedef struct _iemgui_drawfunctions {\n    t_iemdrawfunptr draw_new; /* create all widgets */\n    t_iemdrawfunptr draw_config; /* reconfigure (draw, but don't create) all widgets (except iolets) */\n    t_iemfunptr     draw_iolets;  /* reconfigure (draw, but don't create) all iolets (0 uses default iolets function) */\n    t_iemdrawfunptr draw_update; /* update the changeable part of the iemgui (e.g. the number in a numbox) */\n    t_iemdrawfunptr draw_select; /* highlight object when it's selected */\n    t_iemdrawfunptr draw_erase; /* destroy all widgets; (0 uses default erase function) */\n    t_iemdrawfunptr draw_move; /* move all widgets; (0 uses default move function) */\n} t_iemgui_drawfunctions;\ntypedef struct _iemgui\n{\n    t_object           x_obj;\n    t_glist            *x_glist;\n    t_iemfunptr        x_draw;\n    int                x_h;\n    int                x_w;\n    struct _iemgui_private *x_private;\n    int                x_ldx;\n    int                x_ldy;\n    char               x_font[MAXPDSTRING]; /* font names can be long! */\n    t_iem_fstyle_flags x_fsf;\n    int                x_fontsize;\n    t_iem_init_symargs x_isa;\n    int                x_fcol;\n    int                x_bcol;\n    int                x_lcol;\n    /* send/receive/label as used ($args expanded) */\n    t_symbol           *x_snd;              /* send symbol */\n    t_symbol           *x_rcv;              /* receive */\n    t_symbol           *x_lab;              /* label */\n    /* same, with $args unexpanded */\n    t_symbol           *x_snd_unexpanded;   /* NULL=uninitialized; gensym(\"\")=empty */\n    t_symbol           *x_rcv_unexpanded;\n    t_symbol           *x_lab_unexpanded;\n    int                x_binbufindex;       /* where in binbuf to find these */\n    int                x_labelbindex;       /* where in binbuf to find label */\n} t_iemgui;\n\ntypedef struct _bng\n{\n    t_iemgui x_gui;\n    int      x_flashed;\n    int      x_flashtime_break;\n    int      x_flashtime_hold;\n    t_clock  *x_clock_hld;\n    t_clock  *x_clock_brk;\n    t_clock  *x_clock_lck;\n    double x_lastflashtime;\n} t_bng;\n\ntypedef struct _slider\n{\n    t_iemgui x_gui;\n    int      x_pos;\n    int      x_val;\n    int      x_lin0_log1;\n    int      x_steady;\n    double   x_min;\n    double   x_max;\n    double   x_k;\n    t_float  x_fval;\n    t_iem_orientation x_orientation;\n} t_slider;\n\ntypedef struct _radio\n{\n    t_iemgui x_gui;\n    int      x_on;\n    int      x_on_old;  /* LATER delete this; it's used for old version */\n    int      x_change;\n    int      x_number;\n    int      x_drawn;\n    t_float  x_fval;\n    t_iem_orientation x_orientation;\n    int      x_compat; /* old version */\n} t_radio;\n\ntypedef struct _toggle\n{\n    t_iemgui x_gui;\n    t_float    x_on;\n    t_float    x_nonzero;\n} t_toggle;\n\ntypedef struct _my_canvas\n{\n    t_iemgui x_gui;\n    t_atom   x_at[3];\n    int      x_vis_w;\n    int      x_vis_h;\n} t_my_canvas;\n\n\ntypedef struct _vu\n{\n    t_iemgui x_gui;\n    int      x_led_size;\n    int      x_peak;\n    int      x_rms;\n    t_float  x_fp;\n    t_float  x_fr;\n    int      x_scale;\n    void     *x_out_rms;\n    void     *x_out_peak;\n    unsigned int x_updaterms:1;\n    unsigned int x_updatepeak:1;\n} t_vu;\n\ntypedef struct _my_numbox\n{\n    t_iemgui x_gui;\n    t_clock  *x_clock_reset;\n    t_clock  *x_clock_wait;\n    t_float  x_val;\n    double   x_min;\n    double   x_max;\n    double   x_k;\n    int      x_lin0_log1;\n    char     x_buf[IEMGUI_MAX_NUM_LEN];\n    int      x_numwidth;\n    int      x_log_height;\n} t_my_numbox;\n\nextern int iemgui_color_hex[];\nextern int iemgui_vu_db2i[];\nextern int iemgui_vu_col[];\nextern char *iemgui_vu_scale_str[];\n\nEXTERN int iemgui_clip_size(int size);\nEXTERN int iemgui_clip_font(int size);\nEXTERN t_symbol *iemgui_dollararg2sym(t_symbol *s, int nth_arg, int tail_len, int pargc, t_atom *pargv);\nEXTERN void iemgui_verify_snd_ne_rcv(t_iemgui *iemgui);\nEXTERN void iemgui_all_sym2dollararg(t_iemgui *iemgui, t_symbol **srlsym);\nEXTERN t_symbol *iemgui_new_dogetname(t_iemgui *iemgui, int indx, t_atom *argv);\nEXTERN void iemgui_new_getnames(t_iemgui *iemgui, int indx, t_atom *argv);\nEXTERN void iemgui_all_dollararg2sym(t_iemgui *iemgui, t_symbol **srlsym);\nEXTERN void iemgui_all_loadcolors(t_iemgui *iemgui, t_atom*bcol, t_atom*fcol, t_atom*lcol);\nEXTERN void iemgui_all_dollar2raute(t_symbol **srlsym);\nEXTERN void iemgui_all_raute2dollar(t_symbol **srlsym);\nEXTERN void iemgui_send(void *x, t_iemgui *iemgui, t_symbol *s);\nEXTERN void iemgui_receive(void *x, t_iemgui *iemgui, t_symbol *s);\nEXTERN void iemgui_label(void *x, t_iemgui *iemgui, t_symbol *s);\nEXTERN void iemgui_label_pos(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av);\nEXTERN void iemgui_label_font(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av);\nEXTERN void iemgui_size(void *x, t_iemgui *iemgui);\nEXTERN void iemgui_delta(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av);\nEXTERN void iemgui_pos(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av);\nEXTERN void iemgui_color(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av);\nEXTERN void iemgui_displace(t_gobj *z, t_glist *glist, int dx, int dy);\nEXTERN void iemgui_select(t_gobj *z, t_glist *glist, int selected);\nEXTERN void iemgui_delete(t_gobj *z, t_glist *glist);\nEXTERN void iemgui_vis(t_gobj *z, t_glist *glist, int vis);\nEXTERN void iemgui_save(t_iemgui *iemgui, t_symbol **srl, t_symbol **bflcol);\nEXTERN void iemgui_zoom(t_iemgui *iemgui, t_floatarg zoom);\nEXTERN void iemgui_newzoom(t_iemgui *iemgui);\nEXTERN void iemgui_properties(t_iemgui *iemgui, t_symbol **srl);\nEXTERN int iemgui_dialog(t_iemgui *iemgui, t_symbol **srl, int argc, t_atom *argv);\nEXTERN void iemgui_setdialogatoms(t_iemgui *iemgui, int argc, t_atom*argv);\n\nEXTERN int canvas_getdollarzero(void);\n\nEXTERN void iem_inttosymargs(t_iem_init_symargs *symargp, int n);\nEXTERN int iem_symargstoint(t_iem_init_symargs *symargp);\nEXTERN void iem_inttofstyle(t_iem_fstyle_flags *fstylep, int n);\nEXTERN int iem_fstyletoint(t_iem_fstyle_flags *fstylep);\nEXTERN void iemgui_setdrawfunctions(t_iemgui *iemgui, t_iemgui_drawfunctions *w);\n\n#define IEMGUI_SETDRAWFUNCTIONS(x, prefix)                     \\\n    {                                                            \\\n        t_iemgui_drawfunctions w;                              \\\n        w.draw_new    = (t_iemdrawfunptr)prefix##_draw_new;      \\\n        w.draw_config = (t_iemdrawfunptr)prefix##_draw_config;   \\\n        w.draw_iolets = (t_iemfunptr)prefix##_draw_io;       \\\n        w.draw_update = (t_iemdrawfunptr)prefix##_draw_update;   \\\n        w.draw_select = (t_iemdrawfunptr)prefix##_draw_select;   \\\n        w.draw_erase = 0;                                        \\\n        w.draw_move  = 0;                                        \\\n        iemgui_setdrawfunctions(&x->x_gui, &w);                        \\\n    }\n\n\n/* wrapper around pd_new() for classes that start with a t_iemgui\n * initializes the iemgui struct\n */\nt_iemgui* iemgui_new(t_class*cls);\n\n/* these are deliberately not exported for now */\n\n/* update the label (both internally and on the GUI)\n * senditup=0 never-to-gui; senditup=1 always-to-gui; senditup<0 autodetect\n */\nvoid iemgui_dolabel(void *x, t_iemgui *iemgui, t_symbol *s, int senditup);\n\nvoid iemgui_new_dialog(void*x, t_iemgui*iemgui,\n                       const char*objname,\n                       t_float width,  t_float width_min,\n                       t_float height, t_float height_min,\n                       t_float range_min, t_float range_max, int range_checkmode,\n                       int mode, /* lin0_log1 */\n                       const char* mode_label0, const char* mode_label1,\n                       int canloadbang, int steady, int number);\n\n\n#define __g_all_guis_h_\n#endif /* __g_all_guis_h_ */\n"
  },
  {
    "path": "libs/libpd/pure-data/src/g_array.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n#include <string.h>\n#include <stdio.h>      /* for read/write to files */\n#include \"m_pd.h\"\n#include \"g_canvas.h\"\n#include <math.h>\n\n/* jsarlo { */\n#define ARRAYPAGESIZE 1000  /* this should match the page size in u_main.tk */\n/* } jsarlo */\n\n/* --------- \"pure\" arrays with scalars for elements. --------------- */\n\n/* Pure arrays have no a priori graphical capabilities.\nThey are instantiated by \"garrays\" below or can be elements of other\nscalars (g_scalar.c); their graphical behavior is defined accordingly. */\n\nt_array *array_new(t_symbol *templatesym, t_gpointer *parent)\n{\n    t_array *x = (t_array *)getbytes(sizeof (*x));\n    t_template *template;\n    template = template_findbyname(templatesym);\n    x->a_templatesym = templatesym;\n    x->a_n = 1;\n    x->a_elemsize = sizeof(t_word) * template->t_n;\n    x->a_vec = (char *)getbytes(x->a_elemsize);\n        /* note here we blithely copy a gpointer instead of \"setting\" a\n        new one; this gpointer isn't accounted for and needn't be since\n        we'll be deleted before the thing pointed to gets deleted anyway;\n        see array_free. */\n    x->a_gp = *parent;\n    x->a_stub = gstub_new(0, x);\n    word_init((t_word *)(x->a_vec), template, parent);\n    return (x);\n}\n\n/* jsarlo { */\nstatic void garray_arrayviewlist_close(t_garray *x);\n/* } jsarlo */\n\nvoid array_resize(t_array *x, int n)\n{\n    int elemsize, oldn;\n    char *tmp;\n    t_template *template = template_findbyname(x->a_templatesym);\n    if (n < 1)\n        n = 1;\n    oldn = x->a_n;\n    elemsize = sizeof(t_word) * template->t_n;\n\n    tmp = (char *)resizebytes(x->a_vec, oldn * elemsize, n * elemsize);\n    if (!tmp)\n        return;\n    x->a_vec = tmp;\n    x->a_n = n;\n    if (n > oldn)\n    {\n        char *cp = x->a_vec + elemsize * oldn;\n        int i = n - oldn;\n        for (; i--; cp += elemsize)\n        {\n            t_word *wp = (t_word *)cp;\n            word_init(wp, template, &x->a_gp);\n        }\n    }\n    x->a_valid = ++glist_valid;\n}\n\nvoid array_resize_and_redraw(t_array *array, t_glist *glist, int n)\n{\n    t_array *a2 = array;\n    int vis = glist_isvisible(glist);\n    while (a2->a_gp.gp_stub->gs_which == GP_ARRAY)\n        a2 = a2->a_gp.gp_stub->gs_un.gs_array;\n    if (vis)\n        gobj_vis(&a2->a_gp.gp_un.gp_scalar->sc_gobj, glist, 0);\n    array_resize(array, n);\n    if (vis)\n        gobj_vis(&a2->a_gp.gp_un.gp_scalar->sc_gobj, glist, 1);\n}\n\nvoid word_free(t_word *wp, t_template *template);\n\nvoid array_free(t_array *x)\n{\n    int i;\n    t_template *scalartemplate = template_findbyname(x->a_templatesym);\n    gstub_cutoff(x->a_stub);\n    for (i = 0; i < x->a_n; i++)\n    {\n        t_word *wp = (t_word *)(x->a_vec + x->a_elemsize * i);\n        word_free(wp, scalartemplate);\n    }\n    freebytes(x->a_vec, x->a_elemsize * x->a_n);\n    freebytes(x, sizeof *x);\n}\n\n/* --------------------- graphical arrays (garrays) ------------------- */\n\nt_class *garray_class;\n\nstruct _garray\n{\n    t_gobj x_gobj;\n    t_scalar *x_scalar;     /* scalar \"containing\" the array */\n    t_glist *x_glist;       /* containing glist */\n    t_symbol *x_name;       /* unexpanded name (possibly with leading '$') */\n    t_symbol *x_realname;   /* expanded name (symbol we're bound to) */\n    unsigned int  x_usedindsp:1;    /* 1 if some DSP routine is using this */\n    unsigned int  x_saveit:1;       /* we should save this with parent */\n    unsigned int  x_savesize:1;     /* save size too */\n    unsigned int  x_listviewing:1;  /* list view window is open */\n    unsigned int  x_hidename:1;     /* don't print name above graph */\n    unsigned int  x_edit:1;         /* we can edit the array */\n};\n\nstatic t_pd *garray_arraytemplatecanvas;  /* written at setup w/ global lock */\nstatic const char garray_arraytemplatefile[] = \"\\\ncanvas 0 0 458 153 10;\\n\\\n#X obj 43 31 struct float-array array z float float style\\n\\\nfloat linewidth float color float v;\\n\\\n#X obj 43 70 plot -v v z color linewidth 0 0 1 style;\\n\\\n\";\nstatic const char garray_floattemplatefile[] = \"\\\ncanvas 0 0 458 153 10;\\n\\\n#X obj 39 26 struct float float y;\\n\\\n\";\n\n/* create invisible, built-in canvases to supply templates for floats\nand float-arrays. */\n\nvoid garray_init(void)\n{\n    t_binbuf *b;\n    b = binbuf_new();\n\n    glob_setfilename(0, gensym(\"_float_template\"), gensym(\".\"));\n    binbuf_text(b, garray_floattemplatefile, strlen(garray_floattemplatefile));\n    binbuf_eval(b, &pd_canvasmaker, 0, 0);\n    vmess(s__X.s_thing, gensym(\"pop\"), \"i\", 0);\n\n    glob_setfilename(0, gensym(\"_float_array_template\"), gensym(\".\"));\n    binbuf_text(b, garray_arraytemplatefile, strlen(garray_arraytemplatefile));\n    binbuf_eval(b, &pd_canvasmaker, 0, 0);\n    garray_arraytemplatecanvas = s__X.s_thing;\n    vmess(s__X.s_thing, gensym(\"pop\"), \"i\", 0);\n\n    glob_setfilename(0, &s_, &s_);\n    binbuf_free(b);\n}\n\n/* create a new scalar attached to a symbol.  Used to make floating-point\narrays (the scalar will be of type \"float-array\").  Currently this is\nalways called by graph_array() below; but when we make a more general way\nto save and create arrays this might get called more directly. */\n\nstatic t_garray *graph_scalar(t_glist *gl, t_symbol *s, t_symbol *templatesym,\n    int saveit, int savesize)\n{\n    t_garray *x;\n    if (!template_findbyname(templatesym))\n        return (0);\n    x = (t_garray *)pd_new(garray_class);\n    x->x_scalar = scalar_new(gl, templatesym);\n    x->x_name = s;\n    x->x_realname = canvas_realizedollar(gl, s);\n    pd_bind(&x->x_gobj.g_pd, x->x_realname);\n    x->x_usedindsp = 0;\n        /* when invoked this way, saving implies saving size too */\n    x->x_saveit = saveit;\n    x->x_savesize = savesize;\n    x->x_listviewing = 0;\n    x->x_edit = 1;\n    glist_add(gl, &x->x_gobj);\n    x->x_glist = gl;\n    return (x);\n}\n\n    /* get a garray's \"array\" structure. */\nt_array *garray_getarray(t_garray *x)\n{\n    int zonset, ztype;\n    t_symbol *zarraytype;\n    t_scalar *sc = x->x_scalar;\n    t_symbol *templatesym = sc->sc_template;\n    t_template *template = template_findbyname(templatesym);\n    if (!template)\n    {\n        pd_error(0, \"array: couldn't find template %s\", templatesym->s_name);\n        return (0);\n    }\n    if (!template_find_field(template, gensym(\"z\"),\n        &zonset, &ztype, &zarraytype))\n    {\n        pd_error(0, \"array: template %s has no 'z' field\", templatesym->s_name);\n        return (0);\n    }\n    if (ztype != DT_ARRAY)\n    {\n        pd_error(0, \"array: template %s, 'z' field is not an array\",\n            templatesym->s_name);\n        return (0);\n    }\n    return (sc->sc_vec[zonset].w_array);\n}\n\n    /* get the \"array\" structure and furthermore check it's float */\nstatic t_array *garray_getarray_floatonly(t_garray *x,\n    int *yonsetp, int *elemsizep)\n{\n    t_array *a = garray_getarray(x);\n    int yonset, type;\n    t_symbol *arraytype;\n    t_template *template = template_findbyname(a->a_templatesym);\n    if (!template_find_field(template, gensym(\"y\"), &yonset,\n        &type, &arraytype) || type != DT_FLOAT)\n            return (0);\n    *yonsetp = yonset;\n    *elemsizep = a->a_elemsize;\n    return (a);\n}\n\n    /* get the array's name.  Return nonzero if it should be hidden */\nint garray_getname(t_garray *x, t_symbol **namep)\n{\n    *namep = x->x_name;\n    return (x->x_hidename);\n}\n\n    /* get a garray's containing glist */\nt_glist *garray_getglist(t_garray *x)\n{\n    return (x->x_glist);\n}\n\n    /* get a garray's associated scalar */\nt_scalar *garray_getscalar(t_garray *x)\n{\n    return (x->x_scalar);\n}\n\n        /* if there is one garray in a graph, reset the graph's coordinates\n            to fit a new size and style for the garray */\nstatic void garray_fittograph(t_garray *x, int n, int style)\n{\n    t_array *array = garray_getarray(x);\n    t_glist *gl = x->x_glist;\n    if (gl->gl_list == &x->x_gobj && !x->x_gobj.g_next)\n    {\n        vmess(&gl->gl_pd, gensym(\"bounds\"), \"ffff\",\n            0., gl->gl_y1, (double)\n                (style == PLOTSTYLE_POINTS || n == 1 ? n : n-1),\n                    gl->gl_y2);\n\n            /* hack - if the xlabels seem to want to be from 0 to table size-1,\n            update the second label */\n        if (gl->gl_nxlabels == 2 && !strcmp(gl->gl_xlabel[0]->s_name, \"0\"))\n        {\n            t_atom a;\n            SETFLOAT(&a, n-1);\n            gl->gl_xlabel[1] = atom_gensym(&a);\n            glist_redraw(gl);\n        }\n            /* close any dialogs that might have the wrong info now... */\n        pdgui_stub_deleteforkey(gl);\n    }\n}\n\n/* handle \"array\" message to glists; call graph_scalar above with\nan appropriate template; then set size and flags.  This is called\nfrom the menu and in the file format for patches.  LATER replace this\nby a more coherent (and general) invocation. */\n\nt_garray *graph_array(t_glist *gl, t_symbol *s, t_symbol *templateargsym,\n    t_floatarg fsize, t_floatarg fflags)\n{\n    int n = fsize, zonset, ztype, saveit, savesize;\n    t_symbol *zarraytype, *asym = gensym(\"#A\");\n    t_garray *x;\n    t_template *template, *ztemplate;\n    t_symbol *templatesym;\n    int flags = fflags;\n    int filestyle = ((flags & GRAPH_ARRAY_PLOTSTYLE) >> 1);\n    int style = (filestyle == 0 ? PLOTSTYLE_POLY :\n        (filestyle == 1 ? PLOTSTYLE_POINTS : filestyle));\n    if (templateargsym != &s_float)\n    {\n        pd_error(0, \"array %s: only 'float' type understood\", templateargsym->s_name);\n        return (0);\n    }\n    templatesym = gensym(\"pd-float-array\");\n    template = template_findbyname(templatesym);\n    if (!template)\n    {\n        pd_error(0, \"array: couldn't find template %s\", templatesym->s_name);\n        return (0);\n    }\n    if (!template_find_field(template, gensym(\"z\"),\n        &zonset, &ztype, &zarraytype))\n    {\n        pd_error(0, \"array: template %s has no 'z' field\", templatesym->s_name);\n        return (0);\n    }\n    if (ztype != DT_ARRAY)\n    {\n        pd_error(0, \"array: template %s, 'z' field is not an array\",\n            templatesym->s_name);\n        return (0);\n    }\n    if (!(ztemplate = template_findbyname(zarraytype)))\n    {\n        pd_error(0, \"array: no template of type %s\", zarraytype->s_name);\n        return (0);\n    }\n    saveit = ((flags & GRAPH_ARRAY_SAVE) != 0);\n    savesize = ((flags & GRAPH_ARRAY_SAVESIZE) != 0);\n    x = graph_scalar(gl, s, templatesym, saveit, savesize);\n    x->x_hidename = ((flags & 8) >> 3);\n\n    if (n <= 0)\n        n = 100;\n    array_resize(x->x_scalar->sc_vec[zonset].w_array, n);\n\n    template_setfloat(template, gensym(\"style\"), x->x_scalar->sc_vec,\n        style, 1);\n    template_setfloat(template, gensym(\"linewidth\"), x->x_scalar->sc_vec,\n        ((style == PLOTSTYLE_POINTS) ? 2 : 1), 1);\n    template_setfloat(template, gensym(\"v\"), x->x_scalar->sc_vec, 1, 1);\n\n           /* bashily unbind #A -- this would create garbage if #A were\n           multiply bound but we believe in this context it's at most\n           bound to whichever textobj or array was created most recently */\n    asym->s_thing = 0;\n        /* and now bind #A to us to receive following messages in the\n        saved file or copy buffer */\n    pd_bind(&x->x_gobj.g_pd, asym);\n    garray_fittograph(x, n, style);\n    canvas_update_dsp();\n    return (x);\n}\n\n    /* called from array menu item to create a new one */\nvoid canvas_menuarray(t_glist *canvas)\n{\n    t_glist *x = (t_glist *)canvas;\n    int gcount;\n    char arraybuf[80];\n    for (gcount = 1; gcount < 1000; gcount++)\n    {\n        sprintf(arraybuf, \"array%d\", gcount);\n        if (!pd_findbyclass(gensym(arraybuf), garray_class))\n            break;\n    }\n    pdgui_stub_vnew(&x->gl_pd,\n        \"pdtk_array_dialog\", x, \"siii\",\n        arraybuf, 100, 3, 1);\n}\n\n    /* called from graph_dialog to set properties */\nvoid garray_properties(t_garray *x)\n{\n    t_array *a = garray_getarray(x);\n    t_scalar *sc = x->x_scalar;\n    int style = template_getfloat(template_findbyname(sc->sc_template),\n        gensym(\"style\"), x->x_scalar->sc_vec, 1);\n    int filestyle = (style == 0 ? PLOTSTYLE_POLY :\n        (style == 1 ? PLOTSTYLE_POINTS : style));\n\n    if (!a)\n        return;\n    pdgui_stub_deleteforkey(x);\n    pdgui_stub_vnew(&x->x_gobj.g_pd,\n        \"pdtk_array_dialog\", x,\n        \"siii\",\n        x->x_name->s_name,\n        a->a_n, x->x_saveit + 2 * filestyle, 0);\n}\n\n    /* this is called back from the dialog window to create a garray.\n    The otherflag requests that we find an existing graph to put it in. */\nvoid glist_arraydialog(t_glist *parent, t_symbol *name, t_floatarg size,\n    t_floatarg fflags, t_floatarg otherflag)\n{\n    t_glist *gl;\n    t_garray *a;\n    int flags = fflags;\n    if (size < 1)\n        size = 1;\n    if (otherflag == 0 || (!(gl = glist_findgraph(parent))))\n        gl = glist_addglist(parent, &s_, 0, 1,\n            size, -1, 0, 0, 0, 0);\n    a = graph_array(gl, name, &s_float, size, flags);\n    canvas_dirty(parent, 1);\n}\n\n    /* this is called from the properties dialog window for an existing array */\nvoid garray_arraydialog(t_garray *x, t_symbol *name, t_floatarg fsize,\n    t_floatarg fflags, t_floatarg deleteit)\n{\n    int flags = fflags;\n    int saveit = ((flags & 1) != 0);\n    int filestyle = ((flags & 6) >> 1);\n    int style = (filestyle == 0 ? PLOTSTYLE_POLY :\n        (filestyle == 1 ? PLOTSTYLE_POINTS : filestyle));\n    t_float stylewas = template_getfloat(\n        template_findbyname(x->x_scalar->sc_template),\n            gensym(\"style\"), x->x_scalar->sc_vec, 1);\n    if (deleteit != 0)\n    {\n        int wasused = x->x_usedindsp;\n        glist_delete(x->x_glist, &x->x_gobj);\n        if (wasused)\n            canvas_update_dsp();\n    }\n    else\n    {\n        long size;\n        t_array *a = garray_getarray(x);\n        t_template *scalartemplate;\n        if (!a)\n        {\n            pd_error(x, \"can't find array\\n\");\n            return;\n        }\n        if (!(scalartemplate = template_findbyname(x->x_scalar->sc_template)))\n        {\n            pd_error(0, \"array: no template of type %s\",\n                x->x_scalar->sc_template->s_name);\n            return;\n        }\n        if (name != x->x_name)\n        {\n            /* jsarlo { */\n            if (x->x_listviewing)\n            {\n              garray_arrayviewlist_close(x);\n            }\n            /* } jsarlo */\n            x->x_name = name;\n            pd_unbind(&x->x_gobj.g_pd, x->x_realname);\n            x->x_realname = canvas_realizedollar(x->x_glist, name);\n            pd_bind(&x->x_gobj.g_pd, x->x_realname);\n                /* redraw the whole glist, just so the name change shows up */\n            if (x->x_glist->gl_havewindow)\n                canvas_redraw(x->x_glist);\n            else if (glist_isvisible(x->x_glist->gl_owner))\n            {\n                gobj_vis(&x->x_glist->gl_gobj, x->x_glist->gl_owner, 0);\n                gobj_vis(&x->x_glist->gl_gobj, x->x_glist->gl_owner, 1);\n            }\n            canvas_update_dsp();\n        }\n        size = fsize;\n        if (size < 1)\n            size = 1;\n        if (size != a->a_n)\n            garray_resize_long(x, size);\n        else if (style != stylewas)\n            garray_fittograph(x, (int)size, style);\n        template_setfloat(scalartemplate, gensym(\"style\"),\n            x->x_scalar->sc_vec, (t_float)style, 0);\n        template_setfloat(scalartemplate, gensym(\"linewidth\"), x->x_scalar->sc_vec,\n            ((style == PLOTSTYLE_POINTS) ? 2 : 1), 0);\n\n        garray_setsaveit(x, (saveit != 0));\n        garray_redraw(x);\n        canvas_dirty(x->x_glist, 1);\n    }\n}\n\n/* jsarlo { */\nstatic void garray_arrayviewlist_fillpage(t_garray *x,\n                                   t_float fPage,\n                                   t_float fTopItem)\n{\n    int i, size=0, topItem=(int)fTopItem;\n    int pagesize=ARRAYPAGESIZE, page=(int)fPage, maxpage;\n    int offset, length;\n    t_word *data=0;\n\n    if(!garray_getfloatwords(x, &size, &data)) {\n        pd_error(x, \"error in %s()\", __FUNCTION__);\n        return;\n    }\n\n        /* make sure the requested page is within range */\n    maxpage = (size - 1) / pagesize;\n    if(page > maxpage)\n        page = maxpage;\n    if(page < 0)\n        page = 0;\n\n    pdgui_vmess(\"::dialog_array::listview_setpage\", \"s iii\",\n        x->x_realname->s_name,\n        page, maxpage+1, pagesize);\n\n    offset = page*pagesize;\n    length = ((offset+pagesize) > size)?size-offset:pagesize;\n\n    pdgui_vmess(\"::dialog_array::listview_setdata\", \"siw\",\n             x->x_realname->s_name,\n             offset,\n             length, data + offset);\n\n    pdgui_vmess(\"::dialog_array::listview_focus\", \"si\",\n             x->x_realname->s_name,\n             topItem);\n}\n\nstatic void garray_arrayviewlist_new(t_garray *x)\n{\n    int size=0;\n    t_word*data=0;\n\n    if(!garray_getfloatwords(x, &size, &data)) {\n        pd_error(x, \"error in %s()\", __FUNCTION__);\n        return;\n    }\n    x->x_listviewing = 1;\n\n    pdgui_stub_vnew(&x->x_gobj.g_pd,\n        \"pdtk_array_listview_new\", x,\n        \"si\",\n        x->x_realname->s_name, 0);\n\n    garray_arrayviewlist_fillpage(x, 0, 0);\n}\n\nstatic void garray_arrayviewlist_close(t_garray *x)\n{\n    x->x_listviewing = 0;\n    pdgui_vmess(\"pdtk_array_listview_closeWindow\", \"s\",\n             x->x_realname->s_name);\n}\n/* } jsarlo */\n\nstatic void garray_free(t_garray *x)\n{\n    t_pd *x2;\n        sys_unqueuegui(&x->x_gobj);\n    /* jsarlo { */\n    if (x->x_listviewing)\n    {\n        garray_arrayviewlist_close(x);\n    }\n    /* } jsarlo */\n    pdgui_stub_deleteforkey(x);\n    pd_unbind(&x->x_gobj.g_pd, x->x_realname);\n        /* just in case we're still bound to #A from loading... */\n    while ((x2 = pd_findbyclass(gensym(\"#A\"), garray_class)))\n        pd_unbind(x2, gensym(\"#A\"));\n    pd_free(&x->x_scalar->sc_gobj.g_pd);\n}\n\n/* ------------- code used by both array and plot widget functions ---- */\n\nvoid array_redraw(t_array *a, t_glist *glist)\n{\n    while (a->a_gp.gp_stub->gs_which == GP_ARRAY)\n        a = a->a_gp.gp_stub->gs_un.gs_array;\n    scalar_redraw(a->a_gp.gp_un.gp_scalar, glist);\n}\n\n    /* routine to get screen coordinates of a point in an array */\nvoid array_getcoordinate(t_glist *glist,\n    char *elem, int xonset, int yonset, int wonset, int indx,\n    t_float basex, t_float basey, t_float xinc,\n    t_fielddesc *xfielddesc, t_fielddesc *yfielddesc, t_fielddesc *wfielddesc,\n    t_float *xp, t_float *yp, t_float *wp)\n{\n    t_float xval, yval, ypix, wpix;\n    if (xonset >= 0)\n        xval = *(t_float *)(elem + xonset);\n    else xval = indx * xinc;\n    if (yonset >= 0)\n        yval = *(t_float *)(elem + yonset);\n    else yval = 0;\n    ypix = glist_ytopixels(glist, basey +\n        fielddesc_cvttocoord(yfielddesc, yval));\n    if (wonset >= 0)\n    {\n            /* found \"w\" field which controls linewidth. */\n        t_float wval = *(t_float *)(elem + wonset);\n        wpix = glist_ytopixels(glist, basey +\n            fielddesc_cvttocoord(yfielddesc, yval) +\n                fielddesc_cvttocoord(wfielddesc, wval)) - ypix;\n        if (wpix < 0)\n            wpix = -wpix;\n    }\n    else wpix = 1;\n    *xp = glist_xtopixels(glist, basex +\n        fielddesc_cvttocoord(xfielddesc, xval));\n    *yp = ypix;\n    *wp = wpix;\n}\n\nstatic void array_getrect(t_array *array, t_glist *glist,\n    int *xp1, int *yp1, int *xp2, int *yp2)\n{\n    t_float x1 = 0x7fffffff, y1 = 0x7fffffff, x2 = -0x7fffffff, y2 = -0x7fffffff;\n    t_canvas *elemtemplatecanvas;\n    t_template *elemtemplate;\n    int elemsize, yonset, wonset, xonset, i;\n\n    if (!array_getfields(array->a_templatesym, &elemtemplatecanvas,\n        &elemtemplate, &elemsize, 0, 0, 0, &xonset, &yonset, &wonset))\n    {\n        int incr;\n            /* if it has more than 2000 points, just check 300 of them. */\n        if (array->a_n < 2000)\n            incr = 1;\n        else incr = array->a_n / 300;\n        for (i = 0; i < array->a_n; i += incr)\n        {\n            t_float pxpix, pypix, pwpix;\n            array_getcoordinate(glist, (char *)(array->a_vec) +\n                i * elemsize,\n                xonset, yonset, wonset, i, 0, 0, 1,\n                0, 0, 0,\n                &pxpix, &pypix, &pwpix);\n            if (pwpix < 2)\n                pwpix = 2;\n            if (pxpix < x1)\n                x1 = pxpix;\n            if (pxpix > x2)\n                x2 = pxpix;\n            if (pypix - pwpix < y1)\n                y1 = pypix - pwpix;\n            if (pypix + pwpix > y2)\n                y2 = pypix + pwpix;\n        }\n    }\n    *xp1 = x1;\n    *yp1 = y1;\n    *xp2 = x2;\n    *yp2 = y2;\n}\n\n/* -------------------- widget behavior for garray ------------ */\n\nstatic void garray_getrect(t_gobj *z, t_glist *glist,\n    int *xp1, int *yp1, int *xp2, int *yp2)\n{\n    t_garray *x = (t_garray *)z;\n    gobj_getrect(&x->x_scalar->sc_gobj, glist, xp1, yp1, xp2, yp2);\n}\n\nstatic void garray_displace(t_gobj *z, t_glist *glist, int dx, int dy)\n{\n    /* refuse */\n}\n\nstatic void garray_select(t_gobj *z, t_glist *glist, int state)\n{\n    t_garray *x = (t_garray *)z;\n    /* fill in later */\n}\n\nstatic void garray_activate(t_gobj *z, t_glist *glist, int state)\n{\n}\n\nstatic void garray_delete(t_gobj *z, t_glist *glist)\n{\n    /* nothing to do */\n}\n\nstatic void garray_vis(t_gobj *z, t_glist *glist, int vis)\n{\n    t_garray *x = (t_garray *)z;\n    gobj_vis(&x->x_scalar->sc_gobj, glist, vis);\n}\n\nstatic int garray_click(t_gobj *z, t_glist *glist,\n    int xpix, int ypix, int shift, int alt, int dbl, int doit)\n{\n    t_garray *x = (t_garray *)z;\n    if (x->x_edit)\n        return (gobj_click(&x->x_scalar->sc_gobj, glist,\n            xpix, ypix, shift, alt, dbl, doit));\n    else\n        return (0);\n}\n\n#define ARRAYWRITECHUNKSIZE 1000\n\nvoid garray_savecontentsto(t_garray *x, t_binbuf *b)\n{\n    t_array *array = garray_getarray(x);\n    if (x->x_savesize)\n        binbuf_addv(b, \"ssi;\", gensym(\"#A\"), gensym(\"resize\"), array->a_n);\n    if (x->x_saveit)\n    {\n        int n = array->a_n, n2 = 0;\n        if (n > 200000)\n            post(\"warning: I'm saving an array with %d points!\\n\", n);\n        while (n2 < n)\n        {\n            int chunk = n - n2, i;\n            if (chunk > ARRAYWRITECHUNKSIZE)\n                chunk = ARRAYWRITECHUNKSIZE;\n            binbuf_addv(b, \"si\", gensym(\"#A\"), n2);\n            for (i = 0; i < chunk; i++)\n                binbuf_addv(b, \"f\", ((t_word *)(array->a_vec))[n2+i].w_float);\n            binbuf_addv(b, \";\");\n            n2 += chunk;\n        }\n    }\n}\n\nstatic void garray_save(t_gobj *z, t_binbuf *b)\n{\n    int style, filestyle;\n    t_garray *x = (t_garray *)z;\n    t_array *array = garray_getarray(x);\n    t_template *scalartemplate;\n    if (x->x_scalar->sc_template != gensym(\"pd-float-array\"))\n    {\n            /* LATER \"save\" the scalar as such */\n        pd_error(x, \"can't save arrays of type %s yet\",\n            x->x_scalar->sc_template->s_name);\n        return;\n    }\n    if (!(scalartemplate = template_findbyname(x->x_scalar->sc_template)))\n    {\n        pd_error(0, \"array: no template of type %s\",\n            x->x_scalar->sc_template->s_name);\n        return;\n    }\n    style = template_getfloat(scalartemplate, gensym(\"style\"),\n            x->x_scalar->sc_vec, 0);\n    filestyle = (style == PLOTSTYLE_POINTS ? 1 :\n        (style == PLOTSTYLE_POLY ? 0 : style));\n    binbuf_addv(b, \"sssisi;\", gensym(\"#X\"), gensym(\"array\"),\n        x->x_name, array->a_n, &s_float,\n            x->x_saveit + 2 * filestyle + 8*x->x_hidename);\n    garray_savecontentsto(x, b);\n}\n\nconst t_widgetbehavior garray_widgetbehavior =\n{\n    garray_getrect,\n    garray_displace,\n    garray_select,\n    garray_activate,\n    garray_delete,\n    garray_vis,\n    garray_click,\n};\n\n/* ----------------------- public functions -------------------- */\n\nvoid garray_usedindsp(t_garray *x)\n{\n    x->x_usedindsp = 1;\n}\n\nstatic void garray_doredraw(t_gobj *client, t_glist *glist)\n{\n    t_garray *x = (t_garray *)client;\n    if (glist_isvisible(x->x_glist) && gobj_shouldvis(client, glist))\n    {\n        garray_vis(&x->x_gobj, x->x_glist, 0);\n        garray_vis(&x->x_gobj, x->x_glist, 1);\n    }\n}\n\nvoid garray_redraw(t_garray *x)\n{\n    if (glist_isvisible(x->x_glist))\n        sys_queuegui(&x->x_gobj, x->x_glist, garray_doredraw);\n    /* jsarlo { */\n    /* this happens in garray_vis() when array is visible for\n       performance reasons */\n    else\n    {\n      if (x->x_listviewing)\n          pdgui_vmess(\"pdtk_array_listview_fillpage\", \"s\",\n                 x->x_realname->s_name);\n    }\n    /* } jsarlo */\n}\n\n   /* This functiopn gets the template of an array; if we can't figure\n   out what template an array's elements belong to we're in grave trouble\n   when it's time to free or resize it.  */\nt_template *garray_template(t_garray *x)\n{\n    t_array *array = garray_getarray(x);\n    t_template *template =\n        (array ? template_findbyname(array->a_templatesym) : 0);\n    if (!template)\n        bug(\"garray_template\");\n    return (template);\n}\n\nint garray_npoints(t_garray *x) /* get the length */\n{\n    t_array *array = garray_getarray(x);\n    return (array->a_n);\n}\n\nchar *garray_vec(t_garray *x) /* get the contents */\n{\n    t_array *array = garray_getarray(x);\n    return ((char *)(array->a_vec));\n}\n\n    /* routine that checks if we're just an array of floats and if\n    so returns the goods */\n\nint garray_getfloatwords(t_garray *x, int *size, t_word **vec)\n{\n    int yonset, elemsize;\n    t_array *a = garray_getarray_floatonly(x, &yonset, &elemsize);\n    if (!a)\n    {\n        pd_error(0, \"%s: needs floating-point 'y' field\", x->x_realname->s_name);\n        return (0);\n    }\n    else if (elemsize != sizeof(t_word))\n    {\n        pd_error(0, \"%s: has more than one field\", x->x_realname->s_name);\n        return (0);\n    }\n    *size = garray_npoints(x);\n    *vec =  (t_word *)garray_vec(x);\n    return (1);\n}\n    /* older, non-64-bit safe version, supplied for older externs */\n\nint garray_getfloatarray(t_garray *x, int *size, t_float **vec)\n{\n    if (sizeof(t_word) != sizeof(t_float))\n    {\n        t_symbol *patchname;\n        if (x->x_glist->gl_owner)\n            patchname = x->x_glist->gl_owner->gl_name;\n        else\n            patchname = x->x_glist->gl_name;\n        pd_error(0, \"an operation on the array '%s' in the patch '%s'\",\n              x->x_name->s_name, patchname->s_name);\n        pd_error(0, \"failed since it uses garray_getfloatarray while running 64-bit\");\n    }\n    return (garray_getfloatwords(x, size, (t_word **)vec));\n}\n\n    /* set the \"saveit\" flag */\nvoid garray_setsaveit(t_garray *x, int saveit)\n{\n    if (x->x_saveit && !saveit)\n        post(\"warning: array %s: clearing save-in-patch flag\",\n            x->x_name->s_name);\n    x->x_saveit = saveit;\n}\n\n/*------------------- Pd messages ------------------------ */\nstatic void garray_const(t_garray *x, t_floatarg g)\n{\n    int yonset, i, elemsize;\n    t_array *array = garray_getarray_floatonly(x, &yonset, &elemsize);\n    if (!array)\n        pd_error(0, \"%s: needs floating-point 'y' field\", x->x_realname->s_name);\n    else for (i = 0; i < array->a_n; i++)\n        *((t_float *)((char *)array->a_vec\n            + elemsize * i) + yonset) = g;\n    garray_redraw(x);\n}\n\n    /* sum of Fourier components; called from routines below */\nstatic void garray_dofo(t_garray *x, long npoints, t_float dcval,\n    int nsin, t_float *vsin, int sineflag)\n{\n    double phase, phaseincr, fj;\n    int yonset, i, j, elemsize;\n    t_array *array = garray_getarray_floatonly(x, &yonset, &elemsize);\n    if (!array)\n    {\n        pd_error(0, \"%s: needs floating-point 'y' field\", x->x_realname->s_name);\n        return;\n    }\n    if (npoints == 0)\n        npoints = 512;  /* dunno what a good default would be... */\n    if (npoints != (1 << ilog2((int)npoints)))\n        post(\"%s: rounding to %d points\", array->a_templatesym->s_name,\n            (npoints = (1<<ilog2((int)npoints))));\n    garray_resize_long(x, npoints + 3);\n    phaseincr = 2. * 3.14159 / npoints;\n    for (i = 0, phase = -phaseincr; i < array->a_n; i++, phase += phaseincr)\n    {\n        double sum = dcval;\n        if (sineflag)\n            for (j = 0, fj = phase; j < nsin; j++, fj += phase)\n                sum += vsin[j] * sin(fj);\n        else\n            for (j = 0, fj = 0; j < nsin; j++, fj += phase)\n                sum += vsin[j] * cos(fj);\n        *((t_float *)((array->a_vec + elemsize * i)) + yonset)\n            = sum;\n    }\n    garray_redraw(x);\n}\n\nstatic void garray_sinesum(t_garray *x, t_symbol *s, int argc, t_atom *argv)\n{\n    t_float *svec;\n    long npoints;\n    int i;\n    if (argc < 2)\n    {\n        pd_error(0, \"sinesum: %s: need number of points and partial strengths\",\n            x->x_realname->s_name);\n        return;\n    }\n\n    npoints = atom_getfloatarg(0, argc, argv);\n    argv++, argc--;\n\n    svec = (t_float *)t_getbytes(sizeof(t_float) * argc);\n    if (!svec) return;\n\n    for (i = 0; i < argc; i++)\n        svec[i] = atom_getfloatarg(i, argc, argv);\n    garray_dofo(x, npoints, 0, argc, svec, 1);\n    t_freebytes(svec, sizeof(t_float) * argc);\n}\n\nstatic void garray_cosinesum(t_garray *x, t_symbol *s, int argc, t_atom *argv)\n{\n    t_float *svec;\n    long npoints;\n    int i;\n    if (argc < 2)\n    {\n        pd_error(0, \"sinesum: %s: need number of points and partial strengths\",\n            x->x_realname->s_name);\n        return;\n    }\n\n    npoints = atom_getfloatarg(0, argc, argv);\n    argv++, argc--;\n\n    svec = (t_float *)t_getbytes(sizeof(t_float) * argc);\n    if (!svec) return;\n\n    for (i = 0; i < argc; i++)\n        svec[i] = atom_getfloatarg(i, argc, argv);\n    garray_dofo(x, npoints, 0, argc, svec, 0);\n    t_freebytes(svec, sizeof(t_float) * argc);\n}\n\nstatic void garray_normalize(t_garray *x, t_float f)\n{\n    int i;\n    double maxv, renormer;\n    int yonset, elemsize;\n    t_array *array = garray_getarray_floatonly(x, &yonset, &elemsize);\n    if (!array)\n    {\n        pd_error(0, \"%s: needs floating-point 'y' field\", x->x_realname->s_name);\n        return;\n    }\n\n    if (f <= 0)\n        f = 1;\n\n    for (i = 0, maxv = 0; i < array->a_n; i++)\n    {\n        double v = *((t_float *)(array->a_vec + elemsize * i)\n            + yonset);\n        if (v > maxv)\n            maxv = v;\n        if (-v > maxv)\n            maxv = -v;\n    }\n    if (maxv > 0)\n    {\n        renormer = f / maxv;\n        for (i = 0; i < array->a_n; i++)\n            *((t_float *)(array->a_vec + elemsize * i) + yonset)\n                *= renormer;\n    }\n    garray_redraw(x);\n}\n\n    /* list -- the first value is an index; subsequent values are put in\n    the \"y\" slot of the array.  This generalizes Max's \"table\", sort of. */\nstatic void garray_list(t_garray *x, t_symbol *s, int argc, t_atom *argv)\n{\n    int i;\n    int yonset, elemsize;\n    t_array *array = garray_getarray_floatonly(x, &yonset, &elemsize);\n    if (!array)\n    {\n        pd_error(0, \"%s: needs floating-point 'y' field\", x->x_realname->s_name);\n        return;\n    }\n    if (argc < 2) return;\n    else\n    {\n        int firstindex = atom_getfloat(argv);\n        argc--;\n        argv++;\n            /* drop negative x values */\n        if (firstindex < 0)\n        {\n            argc += firstindex;\n            argv -= firstindex;\n            firstindex = 0;\n            if (argc <= 0) return;\n        }\n        if (argc + firstindex > array->a_n)\n        {\n            argc = array->a_n - firstindex;\n            if (argc <= 0) return;\n        }\n        for (i = 0; i < argc; i++)\n            *((t_float *)(array->a_vec + elemsize * (i + firstindex)) + yonset)\n                = atom_getfloat(argv + i);\n    }\n    garray_redraw(x);\n}\n\n    /* forward a \"bounds\" message to the owning graph */\nstatic void garray_bounds(t_garray *x, t_floatarg x1, t_floatarg y1,\n    t_floatarg x2, t_floatarg y2)\n{\n    vmess(&x->x_glist->gl_pd, gensym(\"bounds\"), \"ffff\", x1, y1, x2, y2);\n}\n\n    /* same for \"xticks\", etc */\nstatic void garray_xticks(t_garray *x,\n    t_floatarg point, t_floatarg inc, t_floatarg f)\n{\n    vmess(&x->x_glist->gl_pd, gensym(\"xticks\"), \"fff\", point, inc, f);\n}\n\nstatic void garray_yticks(t_garray *x,\n    t_floatarg point, t_floatarg inc, t_floatarg f)\n{\n    vmess(&x->x_glist->gl_pd, gensym(\"yticks\"), \"fff\", point, inc, f);\n}\n\nstatic void garray_xlabel(t_garray *x, t_symbol *s, int argc, t_atom *argv)\n{\n    typedmess(&x->x_glist->gl_pd, s, argc, argv);\n}\n\nstatic void garray_ylabel(t_garray *x, t_symbol *s, int argc, t_atom *argv)\n{\n    typedmess(&x->x_glist->gl_pd, s, argc, argv);\n}\n\nstatic void garray_style(t_garray *x, t_floatarg fstyle)\n{\n    int stylewas, style = fstyle;\n    t_template *scalartemplate;\n    if (!(scalartemplate = template_findbyname(x->x_scalar->sc_template)))\n    {\n        pd_error(0, \"array: no template of type %s\",\n            x->x_scalar->sc_template->s_name);\n        return;\n    }\n    stylewas = template_getfloat(\n        scalartemplate, gensym(\"style\"), x->x_scalar->sc_vec, 1);\n    if (style != stylewas)\n    {\n        t_array *a = garray_getarray(x);\n        if (!a)\n        {\n            pd_error(x, \"can't find array\\n\");\n            return;\n        }\n        if (style == PLOTSTYLE_POINTS || stylewas == PLOTSTYLE_POINTS)\n            garray_fittograph(x, a->a_n, style);\n        template_setfloat(scalartemplate, gensym(\"style\"),\n            x->x_scalar->sc_vec, (t_float)style, 0);\n    #if 1\n        template_setfloat(scalartemplate, gensym(\"linewidth\"), x->x_scalar->sc_vec,\n            ((style == PLOTSTYLE_POINTS) ? 2 : 1), 1);\n    #endif\n        garray_redraw(x);\n    }\n}\n\nstatic void garray_width(t_garray *x, t_floatarg width)\n{\n    t_float widthwas;\n    t_template *scalartemplate;\n    if (!(scalartemplate = template_findbyname(x->x_scalar->sc_template)))\n    {\n        pd_error(0, \"array: no template of type %s\",\n            x->x_scalar->sc_template->s_name);\n        return;\n    }\n    widthwas = template_getfloat(\n        scalartemplate, gensym(\"linewidth\"), x->x_scalar->sc_vec, 1);\n    if (width < 1) width = 1;\n    if (width != widthwas)\n    {\n        template_setfloat(scalartemplate, gensym(\"linewidth\"),\n            x->x_scalar->sc_vec, width, 0);\n        garray_redraw(x);\n    }\n}\n\nstatic void garray_color(t_garray *x, t_floatarg color)\n{\n    t_float colorwas;\n    t_template *scalartemplate;\n    if (!(scalartemplate = template_findbyname(x->x_scalar->sc_template)))\n    {\n        pd_error(0, \"array: no template of type %s\",\n            x->x_scalar->sc_template->s_name);\n        return;\n    }\n    colorwas = template_getfloat(\n        scalartemplate, gensym(\"color\"), x->x_scalar->sc_vec, 1);\n    if (color != colorwas)\n    {\n        template_setfloat(scalartemplate, gensym(\"color\"),\n            x->x_scalar->sc_vec, color, 0);\n        garray_redraw(x);\n    }\n}\n\nstatic void garray_vis_msg(t_garray *x, t_floatarg fvis)\n{\n    int viswas, vis = fvis != 0;\n    t_template *scalartemplate;\n    if (!(scalartemplate = template_findbyname(x->x_scalar->sc_template)))\n    {\n        pd_error(0, \"array: no template of type %s\",\n            x->x_scalar->sc_template->s_name);\n        return;\n    }\n    viswas = template_getfloat(\n        scalartemplate, gensym(\"v\"), x->x_scalar->sc_vec, 1);\n    if (vis != viswas)\n    {\n        template_setfloat(scalartemplate, gensym(\"v\"),\n            x->x_scalar->sc_vec, vis, 0);\n        garray_redraw(x);\n    }\n}\n\n    /* change the name of a garray. */\nstatic void garray_rename(t_garray *x, t_symbol *s)\n{\n    /* jsarlo { */\n    if (x->x_listviewing)\n    {\n        garray_arrayviewlist_close(x);\n    }\n    /* } jsarlo */\n    pd_unbind(&x->x_gobj.g_pd, x->x_realname);\n    pd_bind(&x->x_gobj.g_pd, x->x_realname = x->x_name = s);\n    garray_redraw(x);\n}\n\nstatic void garray_read(t_garray *x, t_symbol *filename)\n{\n    int nelem, filedesc, i;\n    FILE *fd;\n    char buf[MAXPDSTRING], *bufptr;\n    int yonset, elemsize;\n    t_array *array = garray_getarray_floatonly(x, &yonset, &elemsize);\n    if (!array)\n    {\n        pd_error(0, \"%s: needs floating-point 'y' field\", x->x_realname->s_name);\n        return;\n    }\n    nelem = array->a_n;\n    if ((filedesc = canvas_open(glist_getcanvas(x->x_glist),\n            filename->s_name, \"\", buf, &bufptr, MAXPDSTRING, 0)) < 0\n                || !(fd = fdopen(filedesc, \"r\")))\n    {\n        pd_error(0, \"%s: can't open\", filename->s_name);\n        return;\n    }\n    for (i = 0; i < nelem; i++)\n    {\n        double f;\n        if (!fscanf(fd, \"%lf\", &f))\n        {\n            post(\"%s: read %d elements into table of size %d\",\n                filename->s_name, i, nelem);\n            break;\n        }\n        else *((t_float *)(array->a_vec + elemsize * i) + yonset) = f;\n    }\n    while (i < nelem)\n        *((t_float *)(array->a_vec +\n            elemsize * i) + yonset) = 0, i++;\n    fclose(fd);\n    garray_redraw(x);\n}\n\nstatic void garray_write(t_garray *x, t_symbol *filename)\n{\n    FILE *fd;\n    char buf[MAXPDSTRING];\n    int yonset, elemsize, i;\n    t_array *array = garray_getarray_floatonly(x, &yonset, &elemsize);\n    if (!array)\n    {\n        pd_error(0, \"%s: needs floating-point 'y' field\", x->x_realname->s_name);\n        return;\n    }\n    canvas_makefilename(glist_getcanvas(x->x_glist), filename->s_name,\n        buf, MAXPDSTRING);\n    if (!(fd = sys_fopen(buf, \"w\")))\n    {\n        pd_error(0, \"%s: can't create\", buf);\n        return;\n    }\n    for (i = 0; i < array->a_n; i++)\n    {\n        if (fprintf(fd, \"%g\\n\",\n            *(t_float *)(((array->a_vec + sizeof(t_word) * i)) + yonset)) < 1)\n        {\n            post(\"%s: write error\", filename->s_name);\n            break;\n        }\n    }\n    fclose(fd);\n}\n\nvoid garray_resize_long(t_garray *x, long n)\n{\n    t_array *array = garray_getarray(x);\n    if (n < 1)\n        n = 1;\n    if (n == array->a_n)\n        return;\n    garray_fittograph(x, (int)n, template_getfloat(\n        template_findbyname(x->x_scalar->sc_template),\n            gensym(\"style\"), x->x_scalar->sc_vec, 1));\n    array_resize_and_redraw(array, x->x_glist, (int)n);\n    if (x->x_usedindsp)\n        canvas_update_dsp();\n}\n\n    /* float version to use as Pd method */\nvoid garray_resize(t_garray *x, t_floatarg f)\n{\n    garray_resize_long(x, f);\n}\n\n/* ignore zoom for now */\nstatic void garray_zoom(t_garray *x, t_floatarg f)\n{\n}\n\nstatic void garray_edit(t_garray *x, t_floatarg f)\n{\n    x->x_edit = (int)f;\n}\n\nstatic void garray_print(t_garray *x)\n{\n    t_array *array = garray_getarray(x);\n    post(\"garray %s: template %s, length %d\",\n        x->x_realname->s_name, array->a_templatesym->s_name, array->a_n);\n}\n\nvoid g_array_setup(void)\n{\n    garray_class = class_new(gensym(\"array\"), 0, (t_method)garray_free,\n        sizeof(t_garray), CLASS_GOBJ, 0);\n    class_setwidget(garray_class, &garray_widgetbehavior);\n    class_addmethod(garray_class, (t_method)garray_const, gensym(\"const\"),\n        A_DEFFLOAT, A_NULL);\n    class_addlist(garray_class, garray_list);\n    class_addmethod(garray_class, (t_method)garray_bounds, gensym(\"bounds\"),\n        A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);\n    class_addmethod(garray_class, (t_method)garray_xticks, gensym(\"xticks\"),\n        A_FLOAT, A_FLOAT, A_FLOAT, 0);\n    class_addmethod(garray_class, (t_method)garray_xlabel, gensym(\"xlabel\"),\n        A_GIMME, 0);\n    class_addmethod(garray_class, (t_method)garray_yticks, gensym(\"yticks\"),\n        A_FLOAT, A_FLOAT, A_FLOAT, 0);\n    class_addmethod(garray_class, (t_method)garray_ylabel, gensym(\"ylabel\"),\n        A_GIMME, 0);\n    class_addmethod(garray_class, (t_method)garray_style, gensym(\"style\"),\n        A_FLOAT, 0);\n    class_addmethod(garray_class, (t_method)garray_width, gensym(\"width\"),\n        A_FLOAT, 0);\n    class_addmethod(garray_class, (t_method)garray_color, gensym(\"color\"),\n        A_FLOAT, 0);\n    class_addmethod(garray_class, (t_method)garray_vis_msg, gensym(\"vis\"),\n        A_FLOAT, 0);\n    class_addmethod(garray_class, (t_method)garray_rename, gensym(\"rename\"),\n        A_SYMBOL, 0);\n    class_addmethod(garray_class, (t_method)garray_read, gensym(\"read\"),\n        A_SYMBOL, A_NULL);\n    class_addmethod(garray_class, (t_method)garray_write, gensym(\"write\"),\n        A_SYMBOL, A_NULL);\n    class_addmethod(garray_class, (t_method)garray_resize, gensym(\"resize\"),\n        A_FLOAT, A_NULL);\n    class_addmethod(garray_class, (t_method)garray_zoom, gensym(\"zoom\"),\n        A_FLOAT, 0);\n    class_addmethod(garray_class, (t_method)garray_edit, gensym(\"edit\"),\n        A_FLOAT, 0);\n    class_addmethod(garray_class, (t_method)garray_print, gensym(\"print\"),\n        A_NULL);\n    class_addmethod(garray_class, (t_method)garray_sinesum, gensym(\"sinesum\"),\n        A_GIMME, 0);\n    class_addmethod(garray_class, (t_method)garray_cosinesum,\n        gensym(\"cosinesum\"), A_GIMME, 0);\n    class_addmethod(garray_class, (t_method)garray_normalize,\n        gensym(\"normalize\"), A_DEFFLOAT, 0);\n    class_addmethod(garray_class, (t_method)garray_arraydialog,\n        gensym(\"arraydialog\"), A_SYMBOL, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);\n/* jsarlo { */\n    class_addmethod(garray_class, (t_method)garray_arrayviewlist_new,\n        gensym(\"arrayviewlistnew\"), A_NULL);\n    class_addmethod(garray_class, (t_method)garray_arrayviewlist_fillpage,\n        gensym(\"arrayviewlistfillpage\"), A_FLOAT, A_DEFFLOAT, A_NULL);\n    class_addmethod(garray_class, (t_method)garray_arrayviewlist_close,\n      gensym(\"arrayviewclose\"), A_NULL);\n/* } jsarlo */\n    class_setsavefn(garray_class, garray_save);\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/g_bang.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution. */\n\n/* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */\n/* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */\n\n#include <string.h>\n#include <stdio.h>\n#include \"m_pd.h\"\n\n#include \"g_all_guis.h\"\n#include \"m_private_utils.h\"\n\n/* --------------- bng     gui-bang ------------------------- */\n\nt_widgetbehavior bng_widgetbehavior;\nstatic t_class *bng_class;\n\n/*  widget helper functions  */\n#define bng_draw_io 0\nstatic void bng_draw_config(t_bng* x, t_glist* glist)\n{\n    const int zoom = IEMGUI_ZOOM(x);\n    t_iemgui *iemgui = &x->x_gui;\n    t_canvas *canvas = glist_getcanvas(glist);\n    int xpos = text_xpix(&x->x_gui.x_obj, glist);\n    int ypos = text_ypix(&x->x_gui.x_obj, glist);\n    int iow = IOWIDTH * zoom, ioh = IEM_GUI_IOHEIGHT * zoom;\n    int inset = zoom;\n    char tag[128];\n    t_atom fontatoms[3];\n    SETSYMBOL(fontatoms+0, gensym(iemgui->x_font));\n    SETFLOAT (fontatoms+1, -iemgui->x_fontsize*zoom);\n    SETSYMBOL(fontatoms+2, gensym(sys_fontweight));\n\n    sprintf(tag, \"%pBASE\", x);\n    pdgui_vmess(0, \"crs iiii\", canvas, \"coords\", tag,\n        xpos, ypos, xpos + x->x_gui.x_w, ypos + x->x_gui.x_h);\n    pdgui_vmess(0, \"crs ri rk\", canvas, \"itemconfigure\", tag,\n        \"-width\", zoom, \"-fill\", x->x_gui.x_bcol);\n\n    sprintf(tag, \"%pBUT\", x);\n    pdgui_vmess(0, \"crs iiii\", canvas, \"coords\", tag,\n        xpos + inset, ypos + inset,\n        xpos + x->x_gui.x_w - inset, ypos + x->x_gui.x_h - inset);\n    pdgui_vmess(0, \"crs ri rk\", canvas, \"itemconfigure\", tag,\n        \"-width\", zoom, \"-fill\", (x->x_flashed ? x->x_gui.x_fcol : x->x_gui.x_bcol));\n\n    sprintf(tag, \"%pLABEL\", x);\n    pdgui_vmess(0, \"crs ii\", canvas, \"coords\", tag,\n        xpos + x->x_gui.x_ldx * zoom, ypos + x->x_gui.x_ldy * zoom);\n    pdgui_vmess(0, \"crs rA rk\", canvas, \"itemconfigure\", tag,\n        \"-font\", 3, fontatoms,\n        \"-fill\", (x->x_gui.x_fsf.x_selected ? IEM_GUI_COLOR_SELECTED : x->x_gui.x_lcol));\n    iemgui_dolabel(x, &x->x_gui, x->x_gui.x_lab, 1);\n}\n\nstatic void bng_draw_new(t_bng *x, t_glist *glist)\n{\n    t_canvas *canvas = glist_getcanvas(glist);\n    char tag[128], tag_object[128];\n    char*tags[] = {tag_object, tag, \"label\", \"text\"};\n    sprintf(tag_object, \"%pOBJ\", x);\n\n    sprintf(tag, \"%pBASE\", x);\n    pdgui_vmess(0, \"crr iiii rS\", canvas, \"create\", \"rectangle\",\n        0, 0, 0, 0, \"-tags\", 2, tags);\n\n    sprintf(tag, \"%pBUT\", x);\n    pdgui_vmess(0, \"crr iiii rS\", canvas, \"create\", \"oval\",\n        0, 0, 0, 0, \"-tags\", 2, tags);\n\n    sprintf(tag, \"%pLABEL\", x);\n    pdgui_vmess(0, \"crr ii rs rS\", canvas, \"create\", \"text\",\n        0, 0, \"-anchor\", \"w\", \"-tags\", 4, tags);\n\n    bng_draw_config(x, glist);\n    (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_IO);\n}\n\nstatic void bng_draw_select(t_bng* x, t_glist* glist)\n{\n    t_canvas *canvas = glist_getcanvas(glist);\n    int col = IEM_GUI_COLOR_NORMAL, lcol = x->x_gui.x_lcol;\n    char tag[128];\n\n    if(x->x_gui.x_fsf.x_selected)\n        col = lcol = IEM_GUI_COLOR_SELECTED;\n\n    sprintf(tag, \"%pBASE\", x);\n    pdgui_vmess(0, \"crs rk\", canvas, \"itemconfigure\", tag, \"-outline\", col);\n    sprintf(tag, \"%pBUT\", x);\n    pdgui_vmess(0, \"crs rk\", canvas, \"itemconfigure\", tag, \"-outline\", col);\n    sprintf(tag, \"%pLABEL\", x);\n    pdgui_vmess(0, \"crs rk\", canvas, \"itemconfigure\", tag, \"-fill\", lcol);\n}\n\nstatic void bng_draw_update(t_bng *x, t_glist *glist)\n{\n    if(glist_isvisible(glist))\n    {\n        char tag[128];\n        sprintf(tag, \"%pBUT\", x);\n        pdgui_vmess(0, \"crs rk\", glist_getcanvas(glist), \"itemconfigure\", tag,\n            \"-fill\", (x->x_flashed ? x->x_gui.x_fcol : x->x_gui.x_bcol));\n    }\n}\n\n/* ------------------------ bng widgetbehaviour----------------------------- */\n\nstatic void bng_getrect(t_gobj *z, t_glist *glist,\n                        int *xp1, int *yp1, int *xp2, int *yp2)\n{\n    t_bng *x = (t_bng *)z;\n\n    *xp1 = text_xpix(&x->x_gui.x_obj, glist);\n    *yp1 = text_ypix(&x->x_gui.x_obj, glist);\n    *xp2 = *xp1 + x->x_gui.x_w;\n    *yp2 = *yp1 + x->x_gui.x_h;\n}\n\nstatic void bng_save(t_gobj *z, t_binbuf *b)\n{\n    t_bng *x = (t_bng *)z;\n    t_symbol *bflcol[3];\n    t_symbol *srl[3];\n\n    iemgui_save(&x->x_gui, srl, bflcol);\n    binbuf_addv(b, \"ssiisiiiisssiiiisss\", gensym(\"#X\"),gensym(\"obj\"),\n                (int)x->x_gui.x_obj.te_xpix, (int)x->x_gui.x_obj.te_ypix,\n                gensym(\"bng\"), x->x_gui.x_w/IEMGUI_ZOOM(x),\n                x->x_flashtime_hold, x->x_flashtime_break,\n                iem_symargstoint(&x->x_gui.x_isa),\n                srl[0], srl[1], srl[2],\n                x->x_gui.x_ldx, x->x_gui.x_ldy,\n                iem_fstyletoint(&x->x_gui.x_fsf), x->x_gui.x_fontsize,\n                bflcol[0], bflcol[1], bflcol[2]);\n    binbuf_addv(b, \";\");\n}\n\nvoid bng_check_minmax(t_bng *x, int ftbreak, int fthold)\n{\n    if(ftbreak > fthold)\n    {\n        int h;\n\n        h = ftbreak;\n        ftbreak = fthold;\n        fthold = h;\n    }\n    if(ftbreak < IEM_BNG_MINBREAKFLASHTIME)\n        ftbreak = IEM_BNG_MINBREAKFLASHTIME;\n    if(fthold < IEM_BNG_MINHOLDFLASHTIME)\n        fthold = IEM_BNG_MINHOLDFLASHTIME;\n    x->x_flashtime_break = ftbreak;\n    x->x_flashtime_hold = fthold;\n}\n\nstatic void bng_properties(t_gobj *z, t_glist *owner)\n{\n    t_bng *x = (t_bng *)z;\n    iemgui_new_dialog(x, &x->x_gui, \"bang\",\n                      x->x_gui.x_w/IEMGUI_ZOOM(x), IEM_GUI_MINSIZE,\n                      0, 0,\n                      x->x_flashtime_break, x->x_flashtime_hold,\n                      2,\n                      -1, \"\", \"\",\n                      1, -1, -1);\n}\n\nstatic void bng_set(t_bng *x)\n{\n    int holdtime = x->x_flashtime_hold;\n    int sincelast = clock_gettimesince(x->x_lastflashtime);\n    x->x_lastflashtime = clock_getsystime();\n    if (sincelast < x->x_flashtime_hold*2)\n        holdtime = sincelast/2;\n    if (holdtime < x->x_flashtime_break)\n        holdtime = x->x_flashtime_break;\n    x->x_flashed = 1;\n    (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);\n    clock_delay(x->x_clock_hld, holdtime);\n}\n\nstatic void bng_bout1(t_bng *x) /* wird nur mehr gesendet, wenn snd != rcv*/\n{\n    if(!x->x_gui.x_fsf.x_put_in2out)\n    {\n        x->x_gui.x_isa.x_locked = 1;\n        clock_delay(x->x_clock_lck, 2);\n    }\n    outlet_bang(x->x_gui.x_obj.ob_outlet);\n    if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing && x->x_gui.x_fsf.x_put_in2out)\n        pd_bang(x->x_gui.x_snd->s_thing);\n}\n\nstatic void bng_bout2(t_bng *x) /* wird immer gesendet, wenn moeglich*/\n{\n    if(!x->x_gui.x_fsf.x_put_in2out)\n    {\n        x->x_gui.x_isa.x_locked = 1;\n        clock_delay(x->x_clock_lck, 2);\n    }\n    outlet_bang(x->x_gui.x_obj.ob_outlet);\n    if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)\n        pd_bang(x->x_gui.x_snd->s_thing);\n}\n\nstatic void bng_bang(t_bng *x) /* wird nur mehr gesendet, wenn snd != rcv*/\n{\n    if(!x->x_gui.x_isa.x_locked)\n    {\n        bng_set(x);\n        bng_bout1(x);\n    }\n}\n\nstatic void bng_bang2(t_bng *x) /* wird immer gesendet, wenn moeglich*/\n{\n    if(!x->x_gui.x_isa.x_locked)\n    {\n        bng_set(x);\n        bng_bout2(x);\n    }\n}\n\nstatic void bng_dialog(t_bng *x, t_symbol *s, int argc, t_atom *argv)\n{\n    t_symbol *srl[3];\n    int a = (int)atom_getfloatarg(0, argc, argv);\n    int fthold = (int)atom_getfloatarg(2, argc, argv);\n    int ftbreak = (int)atom_getfloatarg(3, argc, argv);\n    int sr_flags;\n\n    t_atom undo[18];\n    iemgui_setdialogatoms(&x->x_gui, 18, undo);\n    SETFLOAT (undo+1, 0);\n    SETFLOAT (undo+2, x->x_flashtime_break);\n    SETFLOAT (undo+3, x->x_flashtime_hold);\n    pd_undo_set_objectstate(x->x_gui.x_glist, (t_pd*)x, gensym(\"dialog\"),\n                            18, undo,\n                            argc, argv);\n\n    sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv);\n    x->x_gui.x_w = iemgui_clip_size(a) * IEMGUI_ZOOM(x);\n    x->x_gui.x_h = x->x_gui.x_w;\n    bng_check_minmax(x, ftbreak, fthold);\n\n    iemgui_size((void *)x, &x->x_gui);\n}\n\nstatic void bng_click(t_bng *x, t_floatarg xpos, t_floatarg ypos, t_floatarg shift, t_floatarg ctrl, t_floatarg alt)\n{\n    bng_set(x);\n    bng_bout2(x);\n}\n\nstatic int bng_newclick(t_gobj *z, struct _glist *glist, int xpix, int ypix, int shift, int alt, int dbl, int doit)\n{\n    if(doit)\n        bng_click((t_bng *)z, (t_floatarg)xpix, (t_floatarg)ypix, (t_floatarg)shift, 0, (t_floatarg)alt);\n    return (1);\n}\n\nstatic void bng_float(t_bng *x, t_floatarg f)\n{bng_bang2(x);}\n\nstatic void bng_symbol(t_bng *x, t_symbol *s)\n{bng_bang2(x);}\n\nstatic void bng_pointer(t_bng *x, t_gpointer *gp)\n{bng_bang2(x);}\n\nstatic void bng_list(t_bng *x, t_symbol *s, int ac, t_atom *av)\n{bng_bang2(x);}\n\nstatic void bng_anything(t_bng *x, t_symbol *s, int argc, t_atom *argv)\n{bng_bang2(x);}\n\nstatic void bng_loadbang(t_bng *x, t_floatarg action)\n{\n    if (action == LB_LOAD && x->x_gui.x_isa.x_loadinit)\n    {\n        bng_set(x);\n        bng_bout2(x);\n    }\n}\n\nstatic void bng_size(t_bng *x, t_symbol *s, int ac, t_atom *av)\n{\n    x->x_gui.x_w = iemgui_clip_size((int)atom_getfloatarg(0, ac, av)) * IEMGUI_ZOOM(x);\n    x->x_gui.x_h = x->x_gui.x_w;\n    iemgui_size((void *)x, &x->x_gui);\n}\n\nstatic void bng_delta(t_bng *x, t_symbol *s, int ac, t_atom *av)\n{iemgui_delta((void *)x, &x->x_gui, s, ac, av);}\n\nstatic void bng_pos(t_bng *x, t_symbol *s, int ac, t_atom *av)\n{iemgui_pos((void *)x, &x->x_gui, s, ac, av);}\n\nstatic void bng_flashtime(t_bng *x, t_symbol *s, int ac, t_atom *av)\n{\n    bng_check_minmax(x, (int)atom_getfloatarg(0, ac, av),\n                        (int)atom_getfloatarg(1, ac, av));\n}\n\nstatic void bng_color(t_bng *x, t_symbol *s, int ac, t_atom *av)\n{iemgui_color((void *)x, &x->x_gui, s, ac, av);}\n\nstatic void bng_send(t_bng *x, t_symbol *s)\n{iemgui_send(x, &x->x_gui, s);}\n\nstatic void bng_receive(t_bng *x, t_symbol *s)\n{iemgui_receive(x, &x->x_gui, s);}\n\nstatic void bng_label(t_bng *x, t_symbol *s)\n{iemgui_label((void *)x, &x->x_gui, s);}\n\nstatic void bng_label_pos(t_bng *x, t_symbol *s, int ac, t_atom *av)\n{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);}\n\nstatic void bng_label_font(t_bng *x, t_symbol *s, int ac, t_atom *av)\n{iemgui_label_font((void *)x, &x->x_gui, s, ac, av);}\n\nstatic void bng_init(t_bng *x, t_floatarg f)\n{\n    x->x_gui.x_isa.x_loadinit = (f == 0.0) ? 0 : 1;\n}\n\nstatic void bng_tick_hld(t_bng *x)\n{\n    x->x_flashed = 0;\n    (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);\n}\n\nstatic void bng_tick_lck(t_bng *x)\n{\n    x->x_gui.x_isa.x_locked = 0;\n}\n\nstatic void *bng_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_bng *x = (t_bng *)iemgui_new(bng_class);\n    int a = IEM_GUI_DEFAULTSIZE;\n    int ldx = 0, ldy = -8 * IEM_GUI_DEFAULTSIZE_SCALE;\n    int fs = x->x_gui.x_fontsize;\n    int ftbreak = IEM_BNG_DEFAULTBREAKFLASHTIME,\n        fthold = IEM_BNG_DEFAULTHOLDFLASHTIME;\n\n    IEMGUI_SETDRAWFUNCTIONS(x, bng);\n\n    if((argc == 14)&&IS_A_FLOAT(argv,0)\n       &&IS_A_FLOAT(argv,1)&&IS_A_FLOAT(argv,2)\n       &&IS_A_FLOAT(argv,3)\n       &&(IS_A_SYMBOL(argv,4)||IS_A_FLOAT(argv,4))\n       &&(IS_A_SYMBOL(argv,5)||IS_A_FLOAT(argv,5))\n       &&(IS_A_SYMBOL(argv,6)||IS_A_FLOAT(argv,6))\n       &&IS_A_FLOAT(argv,7)&&IS_A_FLOAT(argv,8)\n       &&IS_A_FLOAT(argv,9)&&IS_A_FLOAT(argv,10))\n    {\n        a = (int)atom_getfloatarg(0, argc, argv);\n        fthold = (int)atom_getfloatarg(1, argc, argv);\n        ftbreak = (int)atom_getfloatarg(2, argc, argv);\n        iem_inttosymargs(&x->x_gui.x_isa, atom_getfloatarg(3, argc, argv));\n        iemgui_new_getnames(&x->x_gui, 4, argv);\n        ldx = (int)atom_getfloatarg(7, argc, argv);\n        ldy = (int)atom_getfloatarg(8, argc, argv);\n        iem_inttofstyle(&x->x_gui.x_fsf, atom_getfloatarg(9, argc, argv));\n        fs = (int)atom_getfloatarg(10, argc, argv);\n        iemgui_all_loadcolors(&x->x_gui, argv+11, argv+12, argv+13);\n    }\n    else iemgui_new_getnames(&x->x_gui, 4, 0);\n\n    x->x_gui.x_fsf.x_snd_able = (0 != x->x_gui.x_snd);\n    x->x_gui.x_fsf.x_rcv_able = (0 != x->x_gui.x_rcv);\n    x->x_flashed = 0;\n    if(x->x_gui.x_fsf.x_font_style == 1) strcpy(x->x_gui.x_font, \"helvetica\");\n    else if(x->x_gui.x_fsf.x_font_style == 2) strcpy(x->x_gui.x_font, \"times\");\n    else { x->x_gui.x_fsf.x_font_style = 0;\n        strcpy(x->x_gui.x_font, sys_font); }\n\n    if (x->x_gui.x_fsf.x_rcv_able)\n        pd_bind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv);\n    x->x_gui.x_ldx = ldx;\n    x->x_gui.x_ldy = ldy;\n\n    x->x_gui.x_fontsize = (fs < 4)?4:fs;\n    x->x_gui.x_w = iemgui_clip_size(a);\n    x->x_gui.x_h = x->x_gui.x_w;\n    bng_check_minmax(x, ftbreak, fthold);\n    x->x_gui.x_isa.x_locked = 0;\n    iemgui_verify_snd_ne_rcv(&x->x_gui);\n    x->x_lastflashtime = clock_getsystime();\n    x->x_clock_hld = clock_new(x, (t_method)bng_tick_hld);\n    x->x_clock_lck = clock_new(x, (t_method)bng_tick_lck);\n    iemgui_newzoom(&x->x_gui);\n    outlet_new(&x->x_gui.x_obj, &s_bang);\n    return (x);\n}\n\nstatic void bng_free(t_bng *x)\n{\n    if(x->x_gui.x_fsf.x_rcv_able)\n        pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv);\n    clock_free(x->x_clock_lck);\n    clock_free(x->x_clock_hld);\n    pdgui_stub_deleteforkey(x);\n}\n\nvoid g_bang_setup(void)\n{\n    bng_class = class_new(gensym(\"bng\"), (t_newmethod)bng_new,\n        (t_method)bng_free, sizeof(t_bng), 0, A_GIMME, 0);\n    class_addbang(bng_class, bng_bang);\n    class_addfloat(bng_class, bng_float);\n    class_addsymbol(bng_class, bng_symbol);\n    class_addpointer(bng_class, bng_pointer);\n    class_addlist(bng_class, bng_list);\n    class_addanything(bng_class, bng_anything);\n    class_addmethod(bng_class, (t_method)bng_click,\n        gensym(\"click\"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);\n    class_addmethod(bng_class, (t_method)bng_dialog,\n        gensym(\"dialog\"), A_GIMME, 0);\n    class_addmethod(bng_class, (t_method)bng_loadbang,\n        gensym(\"loadbang\"), A_DEFFLOAT, 0);\n    class_addmethod(bng_class, (t_method)bng_size,\n        gensym(\"size\"), A_GIMME, 0);\n    class_addmethod(bng_class, (t_method)bng_delta,\n        gensym(\"delta\"), A_GIMME, 0);\n    class_addmethod(bng_class, (t_method)bng_pos,\n        gensym(\"pos\"), A_GIMME, 0);\n    class_addmethod(bng_class, (t_method)bng_flashtime,\n        gensym(\"flashtime\"), A_GIMME, 0);\n    class_addmethod(bng_class, (t_method)bng_color,\n        gensym(\"color\"), A_GIMME, 0);\n    class_addmethod(bng_class, (t_method)bng_send,\n        gensym(\"send\"), A_DEFSYM, 0);\n    class_addmethod(bng_class, (t_method)bng_receive,\n        gensym(\"receive\"), A_DEFSYM, 0);\n    class_addmethod(bng_class, (t_method)bng_label,\n        gensym(\"label\"), A_DEFSYM, 0);\n    class_addmethod(bng_class, (t_method)bng_label_pos,\n        gensym(\"label_pos\"), A_GIMME, 0);\n    class_addmethod(bng_class, (t_method)bng_label_font,\n        gensym(\"label_font\"), A_GIMME, 0);\n    class_addmethod(bng_class, (t_method)bng_init,\n        gensym(\"init\"), A_FLOAT, 0);\n    class_addmethod(bng_class, (t_method)iemgui_zoom,\n        gensym(\"zoom\"), A_CANT, 0);\n    bng_widgetbehavior.w_getrectfn = bng_getrect;\n    bng_widgetbehavior.w_displacefn = iemgui_displace;\n    bng_widgetbehavior.w_selectfn = iemgui_select;\n    bng_widgetbehavior.w_activatefn = NULL;\n    bng_widgetbehavior.w_deletefn = iemgui_delete;\n    bng_widgetbehavior.w_visfn = iemgui_vis;\n    bng_widgetbehavior.w_clickfn = bng_newclick;\n    class_setwidget(bng_class, &bng_widgetbehavior);\n    class_setsavefn(bng_class, bng_save);\n    class_setpropertiesfn(bng_class, bng_properties);\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/g_canvas.c",
    "content": "/* Copyright (c) 1997-2001 Miller Puckette and others.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* this file defines the \"glist\" class, also known as \"canvas\" (the two used\nto be different but are now unified except for some fossilized names.) */\n\n#include <stdlib.h>\n#include <stdio.h>\n#include \"m_pd.h\"\n#include \"m_imp.h\"\n#include \"s_stuff.h\"\n#include \"g_canvas.h\"\n#include \"s_utf8.h\"\n#include <string.h>\n#include \"g_undo.h\"\n\n#ifdef _WIN32\n#include <io.h>\n#endif\n#include \"m_private_utils.h\"\n\n    /* LATER consider adding font size to this struct (see glist_getfont()) */\nstruct _canvasenvironment\n{\n    t_symbol *ce_dir;      /* directory patch lives in */\n    int ce_argc;           /* number of \"$\" arguments */\n    t_atom *ce_argv;       /* array of \"$\" arguments */\n    int ce_dollarzero;     /* value of \"$0\" */\n    t_namelist *ce_path;   /* search path */\n};\ntypedef struct _canvas_private\n{\n    t_undo undo;\n} t_canvas_private;\n\n#define GLIST_DEFCANVASWIDTH 450\n#define GLIST_DEFCANVASHEIGHT 300\n\n/* ---------------------- variables --------------------------- */\n\nt_class *canvas_class;\nt_canvas *canvas_whichfind;         /* last canvas we did a find in */\n\n/* ------------------ forward function declarations --------------- */\nstatic void canvas_start_dsp(void);\nstatic void canvas_stop_dsp(void);\nstatic void canvas_drawlines(t_canvas *x);\nstatic void canvas_dosetbounds(t_canvas *x, int x1, int y1, int x2, int y2);\nvoid canvas_reflecttitle(t_canvas *x);\nstatic void canvas_addtolist(t_canvas *x);\nstatic void canvas_takeofflist(t_canvas *x);\nstatic void canvas_pop(t_canvas *x, t_floatarg fvis);\nstatic void canvas_bind(t_canvas *x);\nstatic void canvas_unbind(t_canvas *x);\nvoid canvas_declare(t_canvas *x, t_symbol *s, int argc, t_atom *argv);\n\n/* ---------------- generic widget behavior ------------------------- */\n\nvoid gobj_getrect(t_gobj *x, t_glist *glist, int *x1, int *y1,\n    int *x2, int *y2)\n{\n    if (x->g_pd->c_wb && x->g_pd->c_wb->w_getrectfn)\n        (*x->g_pd->c_wb->w_getrectfn)(x, glist, x1, y1, x2, y2);\n    else *x1 = *y1 = 0, *x2 = *y2 = 10;\n}\n\nvoid gobj_displace(t_gobj *x, t_glist *glist, int dx, int dy)\n{\n    if (x->g_pd->c_wb && x->g_pd->c_wb->w_displacefn)\n        (*x->g_pd->c_wb->w_displacefn)(x, glist, dx, dy);\n}\n\n    /* here we add an extra check whether we're mapped, because some\n       editing moves are carried out on invisible windows (notably, re-creating\n       abstractions when one is saved).  Should any other widget functions also\n       be doing this?  */\nvoid gobj_select(t_gobj *x, t_glist *glist, int state)\n{\n    if (glist->gl_mapped && x->g_pd->c_wb && x->g_pd->c_wb->w_selectfn)\n        (*x->g_pd->c_wb->w_selectfn)(x, glist, state);\n}\n\nvoid gobj_activate(t_gobj *x, t_glist *glist, int state)\n{\n    if (x->g_pd->c_wb && x->g_pd->c_wb->w_activatefn)\n        (*x->g_pd->c_wb->w_activatefn)(x, glist, state);\n}\n\nvoid gobj_delete(t_gobj *x, t_glist *glist)\n{\n    if (x->g_pd->c_wb && x->g_pd->c_wb->w_deletefn)\n        (*x->g_pd->c_wb->w_deletefn)(x, glist);\n}\n\nint gobj_shouldvis(t_gobj *x, struct _glist *glist)\n{\n    t_object *ob;\n    int has_parent = !glist->gl_havewindow && glist->gl_isgraph\n        && glist->gl_owner && !glist->gl_isclone;\n        /* if our parent is a graph, and if that graph itself isn't\n           visible, then we aren't either. */\n    if (has_parent && !gobj_shouldvis(&glist->gl_gobj, glist->gl_owner))\n            return (0);\n        /* if we're graphing-on-parent and the object falls outside the\n           graph rectangle, don't draw it. */\n    if (has_parent && glist->gl_goprect)\n    {\n        int x1, y1, x2, y2, gx1, gy1, gx2, gy2, m;\n            /* for some reason the bounds check on arrays and scalars\n               don't seem to apply here.  Perhaps this was in order to allow\n               arrays to reach outside their containers?  I no longer understand\n               this. */\n        if (pd_class(&x->g_pd) == scalar_class\n            || pd_class(&x->g_pd) == garray_class)\n                return (1);\n        gobj_getrect(&glist->gl_gobj, glist->gl_owner, &x1, &y1, &x2, &y2);\n        if (x1 > x2)\n            m = x1, x1 = x2, x2 = m;\n        if (y1 > y2)\n            m = y1, y1 = y2, y2 = m;\n        gobj_getrect(x, glist, &gx1, &gy1, &gx2, &gy2);\n#if 0\n        post(\"graph %d %d %d %d, %s %d %d %d %d\",\n             x1, x2, y1, y2, class_gethelpname(x->g_pd), gx1, gx2, gy1, gy2);\n#endif\n        if (gx1 < x1 || gx1 > x2 || gx2 < x1 || gx2 > x2 ||\n            gy1 < y1 || gy1 > y2 || gy2 < y1 || gy2 > y2)\n                return (0);\n    }\n    if ((ob = pd_checkobject(&x->g_pd)))\n    {\n            /* return true if the text box should be drawn.  We don't show text\n               boxes inside graphs---except comments, if we're doing the new\n               (goprect) style. */\n        return (glist->gl_havewindow ||\n            (ob->te_pd != canvas_class &&\n                ob->te_pd->c_wb != &text_widgetbehavior) ||\n            (ob->te_pd == canvas_class && (((t_glist *)ob)->gl_isgraph)) ||\n            (glist->gl_goprect && (ob->te_type == T_TEXT)));\n    }\n    else return (1);\n}\n\nvoid gobj_vis(t_gobj *x, struct _glist *glist, int flag)\n{\n    if (x->g_pd->c_wb && x->g_pd->c_wb->w_visfn && gobj_shouldvis(x, glist))\n        (*x->g_pd->c_wb->w_visfn)(x, glist, flag);\n}\n\nint gobj_click(t_gobj *x, struct _glist *glist,\n    int xpix, int ypix, int shift, int alt, int dbl, int doit)\n{\n    if (x->g_pd->c_wb && x->g_pd->c_wb->w_clickfn)\n        return ((*x->g_pd->c_wb->w_clickfn)(x,\n            glist, xpix, ypix, shift, alt, dbl, doit));\n    else return (0);\n}\n\n    /* maintain the list of visible toplevels for the GUI's \"windows\" menu */\nvoid canvas_updatewindowlist(void)\n{\n            /* not if we're in a reload */\n    if (!THISGUI->i_reloadingabstraction)\n        pdgui_vmess(\"::pd_menus::update_window_menu\", 0);\n}\n\n    /* add a glist the list of \"root\" canvases (toplevels without parents.) */\nstatic void canvas_addtolist(t_canvas *x)\n{\n    x->gl_next = pd_this->pd_canvaslist;\n    pd_this->pd_canvaslist = x;\n}\n\nstatic void canvas_takeofflist(t_canvas *x)\n{\n        /* take it off the window list */\n    if (x == pd_this->pd_canvaslist) pd_this->pd_canvaslist = x->gl_next;\n    else\n    {\n        t_canvas *z;\n        for (z = pd_this->pd_canvaslist; z->gl_next != x; z = z->gl_next)\n            if (!z->gl_next) return;\n        z->gl_next = x->gl_next;\n    }\n}\n\n    /* turn message tracing on and off globally */\n\nvoid obj_dosettracing(t_object *ob, int onoff);\nstatic void canvas_dosettracing(t_canvas *x, int onoff)\n{\n    t_gobj *y;\n    for (y = x->gl_list; y; y = y->g_next)\n    {\n        t_object *ob;\n        if (pd_class(&y->g_pd) == canvas_class)\n            canvas_dosettracing((t_canvas *)y, onoff);\n        if ((ob = pd_checkobject(&y->g_pd)))\n            obj_dosettracing(ob, onoff);\n    }\n}\n\nvoid canvas_settracing(int onoff)\n{\n    t_glist *gl;\n    for (gl = pd_this->pd_canvaslist; gl; gl = gl->gl_next)\n        canvas_dosettracing(gl, onoff);\n}\n\n/* --------- functions to handle the canvas environment ----------- */\n\nvoid canvas_setargs(int argc, const t_atom *argv)\n{\n        /* if there's an old one lying around free it here.  This\n        happens if an abstraction is loaded but never gets as far\n        as calling canvas_new(). */\n    if (THISGUI->i_newargv)\n        freebytes(THISGUI->i_newargv, THISGUI->i_newargc * sizeof(t_atom));\n    THISGUI->i_newargc = argc;\n    THISGUI->i_newargv = copybytes(argv, argc * sizeof(t_atom));\n}\n\nvoid glob_setfilename(void *dummy, t_symbol *filesym, t_symbol *dirsym)\n{\n    THISGUI->i_newfilename = filesym;\n    THISGUI->i_newdirectory = dirsym;\n}\n\nvoid glob_menunew(void *dummy, t_symbol *filesym, t_symbol *dirsym)\n{\n    glob_setfilename(dummy, filesym, dirsym);\n    canvas_new(0, 0, 0, 0);\n    canvas_pop((t_canvas *)s__X.s_thing, 1);\n}\n\nt_canvas *canvas_getcurrent(void)\n{\n    return ((t_canvas *)pd_findbyclass(&s__X, canvas_class));\n}\n\nvoid canvas_setcurrent(t_canvas *x)\n{\n    pd_pushsym(&x->gl_pd);\n}\n\nvoid canvas_unsetcurrent(t_canvas *x)\n{\n    pd_popsym(&x->gl_pd);\n}\n\nt_canvasenvironment *canvas_getenv(const t_canvas *x)\n{\n    if (!x) bug(\"canvas_getenv\");\n    while (!x->gl_env)\n        if (!(x = x->gl_owner))\n            bug(\"t_canvasenvironment\");\n    return (x->gl_env);\n}\n\nint canvas_getdollarzero(void)\n{\n    t_canvas *x = canvas_getcurrent();\n    t_canvasenvironment *env = (x ? canvas_getenv(x) : 0);\n    if (env)\n        return (env->ce_dollarzero);\n    else return (0);\n}\n\nvoid canvas_getargs(int *argcp, t_atom **argvp)\n{\n    t_canvas *x = canvas_getcurrent();\n    t_canvasenvironment *e = canvas_getenv(x);\n    *argcp = e->ce_argc;\n    *argvp = e->ce_argv;\n}\n\nt_symbol *canvas_realizedollar(t_canvas *x, t_symbol *s)\n{\n    t_symbol *ret;\n    const char *name = s->s_name;\n    if (strchr(name, '$'))\n    {\n        t_canvasenvironment *env = canvas_getenv(x);\n        canvas_setcurrent(x);\n        ret = binbuf_realizedollsym(s, env->ce_argc, env->ce_argv, 1);\n        canvas_unsetcurrent(x);\n    }\n    else ret = s;\n    return (ret);\n}\n\nt_symbol *canvas_getcurrentdir(void)\n{\n    t_canvasenvironment *e = canvas_getenv(canvas_getcurrent());\n    return (e->ce_dir);\n}\n\nt_symbol *canvas_getdir(const t_canvas *x)\n{\n    t_canvasenvironment *e = canvas_getenv(x);\n    return (e->ce_dir);\n}\n\nvoid canvas_makefilename(const t_canvas *x, const char *file, char *result, int resultsize)\n{\n    const char *dir = canvas_getenv(x)->ce_dir->s_name;\n    if (file[0] == '/' || (file[0] && file[1] == ':') || !*dir)\n    {\n        strncpy(result, file, resultsize);\n        result[resultsize-1] = 0;\n    }\n    else\n    {\n        int nleft;\n        strncpy(result, dir, resultsize);\n        result[resultsize-1] = 0;\n        nleft = resultsize - (int)strlen(result) - 1;\n        if (nleft <= 0) return;\n        strcat(result, \"/\");\n        strncat(result, file, nleft);\n        result[resultsize-1] = 0;\n    }\n}\n\nvoid canvas_rename(t_canvas *x, t_symbol *s, t_symbol *dir)\n{\n    canvas_unbind(x);\n    x->gl_name = s;\n    canvas_bind(x);\n    if (dir && dir != &s_)\n    {\n        t_canvasenvironment *e = canvas_getenv(x);\n        e->ce_dir = dir;\n    }\n    if (x->gl_havewindow)\n        canvas_reflecttitle(x);\n}\n\n/* --------------- traversing the set of lines in a canvas ----------- */\n\nint canvas_getindex(t_canvas *x, t_gobj *y)\n{\n    t_gobj *y2;\n    int indexno;\n    for (indexno = 0, y2 = x->gl_list; y2 && y2 != y; y2 = y2->g_next)\n        indexno++;\n    return (indexno);\n}\n\nvoid linetraverser_start(t_linetraverser *t, t_canvas *x)\n{\n    t->tr_ob = 0;\n    t->tr_x = x;\n    t->tr_nextoc = 0;\n    t->tr_nextoutno = t->tr_nout = 0;\n}\n\nt_outconnect *linetraverser_next(t_linetraverser *t)\n{\n    t_outconnect *rval = t->tr_nextoc;\n    int outno;\n    while (!rval)\n    {\n        outno = t->tr_nextoutno;\n        while (outno == t->tr_nout)\n        {\n            t_gobj *y;\n            t_object *ob = 0;\n            if (!t->tr_ob) y = t->tr_x->gl_list;\n            else y = t->tr_ob->ob_g.g_next;\n            for (; y; y = y->g_next)\n                if ((ob = pd_checkobject(&y->g_pd))) break;\n            if (!ob) return (0);\n            t->tr_ob = ob;\n            t->tr_nout = obj_noutlets(ob);\n            outno = 0;\n            if (glist_isvisible(t->tr_x))\n                gobj_getrect(y, t->tr_x,\n                    &t->tr_x11, &t->tr_y11, &t->tr_x12, &t->tr_y12);\n            else t->tr_x11 = t->tr_y11 = t->tr_x12 = t->tr_y12 = 0;\n        }\n        t->tr_nextoutno = outno + 1;\n        rval = obj_starttraverseoutlet(t->tr_ob, &t->tr_outlet, outno);\n        t->tr_outno = outno;\n    }\n    t->tr_nextoc = obj_nexttraverseoutlet(rval, &t->tr_ob2,\n        &t->tr_inlet, &t->tr_inno);\n    t->tr_nin = obj_ninlets(t->tr_ob2);\n    if (!t->tr_nin) bug(\"drawline\");\n    if (glist_isvisible(t->tr_x))\n    {\n        int inplus = (t->tr_nin == 1 ? 1 : t->tr_nin - 1);\n        int outplus = (t->tr_nout == 1 ? 1 : t->tr_nout - 1);\n        int iow = IOWIDTH * t->tr_x->gl_zoom;\n        int iom = IOMIDDLE * t->tr_x->gl_zoom;\n        gobj_getrect(&t->tr_ob2->ob_g, t->tr_x,\n            &t->tr_x21, &t->tr_y21, &t->tr_x22, &t->tr_y22);\n        t->tr_lx1 = t->tr_x11 +\n            ((t->tr_x12 - t->tr_x11 - iow) * t->tr_outno) /\n                outplus + iom;\n        t->tr_ly1 = t->tr_y12;\n        t->tr_lx2 = t->tr_x21 +\n            ((t->tr_x22 - t->tr_x21 - iow) * t->tr_inno)/inplus +\n                iom;\n        t->tr_ly2 = t->tr_y21;\n    }\n    else\n    {\n        t->tr_x21 = t->tr_y21 = t->tr_x22 = t->tr_y22 = 0;\n        t->tr_lx1 = t->tr_ly1 = t->tr_lx2 = t->tr_ly2 = 0;\n    }\n\n    return (rval);\n}\n\nvoid linetraverser_skipobject(t_linetraverser *t)\n{\n    t->tr_nextoc = 0;\n    t->tr_nextoutno = t->tr_nout;\n}\n\n/* -------------------- the canvas object -------------------------- */\nint glist_valid = 10000;\n\nvoid glist_init(t_glist *x)\n{\n        /* zero out everyone except \"pd\" field */\n    memset(((char *)x) + sizeof(x->gl_pd), 0, sizeof(*x) - sizeof(x->gl_pd));\n    x->gl_stub = gstub_new(x, 0);\n    x->gl_valid = ++glist_valid;\n    x->gl_xlabel = (t_symbol **)t_getbytes(0);\n    x->gl_ylabel = (t_symbol **)t_getbytes(0);\n    x->gl_privatedata = getbytes(sizeof(t_canvas_private));\n}\n\n    /* make a new glist.  It will either be a \"root\" canvas or else\n    it appears as a \"text\" object in another window (canvas_getcurrent()\n    tells us which.) */\nt_canvas *canvas_new(void *dummy, t_symbol *sel, int argc, t_atom *argv)\n{\n    t_canvas *x = (t_canvas *)pd_new(canvas_class);\n    t_canvas *owner = canvas_getcurrent();\n    t_symbol *s = &s_;\n    int vis = 0, width = GLIST_DEFCANVASWIDTH, height = GLIST_DEFCANVASHEIGHT;\n    int xloc = GLIST_DEFCANVASXLOC, yloc = GLIST_DEFCANVASYLOC;\n    int font = (owner ? owner->gl_font : sys_defaultfont);\n    glist_init(x);\n    x->gl_obj.te_type = T_OBJECT;\n    if (!owner)\n        canvas_addtolist(x);\n    /* post(\"canvas %p, owner %p\", x, owner); */\n\n    if (argc == 5)  /* toplevel: x, y, w, h, font */\n    {\n        xloc = atom_getfloatarg(0, argc, argv);\n        yloc = atom_getfloatarg(1, argc, argv);\n        width = atom_getfloatarg(2, argc, argv);\n        height = atom_getfloatarg(3, argc, argv);\n        font = atom_getfloatarg(4, argc, argv);\n    }\n    else if (argc == 6)  /* subwindow: x, y, w, h, name, vis */\n    {\n        xloc = atom_getfloatarg(0, argc, argv);\n        yloc = atom_getfloatarg(1, argc, argv);\n        width = atom_getfloatarg(2, argc, argv);\n        height = atom_getfloatarg(3, argc, argv);\n        s = atom_getsymbolarg(4, argc, argv);\n        vis = atom_getfloatarg(5, argc, argv);\n    }\n\n        /* (otherwise assume we're being created from the menu.) */\n    if (THISGUI->i_newdirectory &&\n        THISGUI->i_newdirectory->s_name[0])\n    {\n        t_canvasenvironment *env = x->gl_env =\n            (t_canvasenvironment *)getbytes(sizeof(*x->gl_env));\n        if (!THISGUI->i_newargv)\n            THISGUI->i_newargv = getbytes(0);\n        env->ce_dir = THISGUI->i_newdirectory;\n        env->ce_argc = THISGUI->i_newargc;\n        env->ce_argv = THISGUI->i_newargv;\n        env->ce_dollarzero = THISGUI->i_dollarzero++;\n        env->ce_path = 0;\n        THISGUI->i_newdirectory = &s_;\n        THISGUI->i_newargc = 0;\n        THISGUI->i_newargv = 0;\n    }\n    else x->gl_env = 0;\n\n        /* initialize private data, like the undo-queue */\n    canvas_undo_init(x);\n\n    x->gl_x1 = 0;\n    x->gl_y1 = 0;\n    x->gl_x2 = 1;\n    x->gl_y2 = 1;\n    canvas_dosetbounds(x, xloc, yloc, xloc + width, yloc + height);\n    x->gl_owner = owner;\n    x->gl_isclone = 0;\n    x->gl_name = (*s->s_name ? s :\n        (THISGUI->i_newfilename ? THISGUI->i_newfilename : gensym(\"Pd\")));\n    canvas_bind(x);\n    x->gl_loading = 1;\n    x->gl_goprect = 0;      /* no GOP rectangle unless it's turned on later */\n        /* cancel \"vis\" flag if we're a subpatch of an\n         abstraction inside another patch.  A separate mechanism prevents\n         the toplevel abstraction from showing up. */\n    if (vis && gensym(\"#X\")->s_thing &&\n        ((*gensym(\"#X\")->s_thing) == canvas_class))\n    {\n        t_canvas *zzz = (t_canvas *)(gensym(\"#X\")->s_thing);\n        while (zzz && !zzz->gl_env)\n            zzz = zzz->gl_owner;\n        if (zzz && canvas_isabstraction(zzz) && zzz->gl_owner)\n            vis = 0;\n    }\n    x->gl_willvis = vis;\n    x->gl_edit = !UNTITLED_STRNCMP(x->gl_name->s_name);\n    x->gl_font = sys_nearestfontsize(font);\n    x->gl_zoom = (owner ? owner->gl_zoom : 1);\n    pd_pushsym(&x->gl_pd);\n    return(x);\n}\n\nvoid canvas_setgraph(t_glist *x, int flag, int nogoprect);\n\nstatic void canvas_coords(t_glist *x, t_symbol *s, int argc, t_atom *argv)\n{\n          /* FIXME: this is a stopgap - we should always be using\n            glist_getzoom() and never gl_zoom in rest of code. */\n    x->gl_zoom = glist_getzoom(x);\n    x->gl_x1 = atom_getfloatarg(0, argc, argv);\n    x->gl_y1 = atom_getfloatarg(1, argc, argv);\n    x->gl_x2 = atom_getfloatarg(2, argc, argv);\n    x->gl_y2 = atom_getfloatarg(3, argc, argv);\n    x->gl_pixwidth = atom_getfloatarg(4, argc, argv);\n    x->gl_pixheight = atom_getfloatarg(5, argc, argv);\n    if (argc <= 7)\n        canvas_setgraph(x, atom_getfloatarg(6, argc, argv), 1);\n    else\n    {\n        x->gl_xmargin = atom_getfloatarg(7, argc, argv);\n        x->gl_ymargin = atom_getfloatarg(8, argc, argv);\n        canvas_setgraph(x, atom_getfloatarg(6, argc, argv), 0);\n    }\n}\n\n\n    /* make a new glist and add it to this glist.  It will appear as\n    a \"graph\", not a text object.  */\nt_glist *glist_addglist(t_glist *g, t_symbol *sym,\n    t_float x1, t_float y1, t_float x2, t_float y2,\n    t_float px1, t_float py1, t_float px2, t_float py2)\n{\n    static int gcount = 0;  /* it's OK if two threads get the same value */\n    int zz;\n    int menu = 0;\n    const char *str;\n    t_glist *x = (t_glist *)pd_new(canvas_class);\n    glist_init(x);\n    x->gl_obj.te_type = T_OBJECT;\n    if (!*sym->s_name)\n    {\n        char buf[40];\n        sprintf(buf, \"graph%d\", ++gcount);\n        sym = gensym(buf);\n        menu = 1;\n    }\n    else if (!strncmp((str = sym->s_name), \"graph\", 5)\n        && (zz = atoi(str + 5)) > gcount)\n            gcount = zz;\n        /* in 0.34 and earlier, the pixel rectangle and the y bounds were\n        reversed; this would behave the same, except that the dialog window\n        would be confusing.  The \"correct\" way is to have \"py1\" be the value\n        that is higher on the screen. */\n    if (py2 < py1)\n    {\n        t_float zz;\n        zz = y2;\n        y2 = y1;\n        y1 = zz;\n        zz = py2;\n        py2 = py1;\n        py1 = zz;\n    }\n    if (x1 == x2 || y1 == y2)\n        x1 = 0, x2 = 100, y1 = 1, y2 = -1;\n    if (px1 >= px2 || py1 >= py2)\n        px1 = 100, py1 = 20, px2 = 100 + GLIST_DEFGRAPHWIDTH,\n            py2 = 20 + GLIST_DEFGRAPHHEIGHT;\n    x->gl_name = sym;\n    x->gl_x1 = x1;\n    x->gl_x2 = x2;\n    x->gl_y1 = y1;\n    x->gl_y2 = y2;\n    x->gl_obj.te_xpix = px1;\n    x->gl_obj.te_ypix = py1;\n    x->gl_pixwidth = px2 - px1;\n    x->gl_pixheight = py2 - py1;\n    x->gl_font =  (canvas_getcurrent() ?\n        canvas_getcurrent()->gl_font : sys_defaultfont);\n    x->gl_zoom = g->gl_zoom;\n    x->gl_screenx1 = GLIST_DEFCANVASXLOC;\n    x->gl_screeny1 = GLIST_DEFCANVASYLOC;\n    x->gl_screenx2 = GLIST_DEFCANVASWIDTH;\n    x->gl_screeny2 = GLIST_DEFCANVASHEIGHT;\n    x->gl_owner = g;\n    canvas_bind(x);\n    x->gl_isgraph = 1;\n    x->gl_goprect = 0;\n    x->gl_obj.te_binbuf = binbuf_new();\n        /* initialize private data, like the undo-queue */\n    canvas_undo_init(x);\n\n    binbuf_addv(x->gl_obj.te_binbuf, \"s\", gensym(\"graph\"));\n    if (!menu)\n        pd_pushsym(&x->gl_pd);\n    glist_add(g, &x->gl_gobj);\n    return (x);\n}\n\n    /* call glist_addglist from a Pd message */\nvoid glist_glist(t_glist *g, t_symbol *s, int argc, t_atom *argv)\n{\n    t_symbol *sym = atom_getsymbolarg(0, argc, argv);\n    t_float x1 = atom_getfloatarg(1, argc, argv);\n    t_float y1 = atom_getfloatarg(2, argc, argv);\n    t_float x2 = atom_getfloatarg(3, argc, argv);\n    t_float y2 = atom_getfloatarg(4, argc, argv);\n    t_float px1 = atom_getfloatarg(5, argc, argv);\n    t_float py1 = atom_getfloatarg(6, argc, argv);\n    t_float px2 = atom_getfloatarg(7, argc, argv);\n    t_float py2 = atom_getfloatarg(8, argc, argv);\n    glist_addglist(g, sym, x1, y1, x2, y2, px1, py1, px2, py2);\n    if (!canvas_undo_get(glist_getcanvas(g))->u_doing)\n        canvas_undo_add(glist_getcanvas(g), UNDO_CREATE, \"create\",\n            (void *)canvas_undo_set_create(glist_getcanvas(g)));\n}\n\n    /* return true if the glist should appear as a graph on parent;\n    otherwise it appears as a text box. */\nint glist_isgraph(t_glist *x)\n{\n  return (x->gl_isgraph|(x->gl_hidetext<<1));\n}\n\n    /* This is sent from the GUI to inform a toplevel that its window has been\n    moved or resized. */\nstatic void canvas_setbounds(t_canvas *x, t_float left, t_float top,\n                             t_float right, t_float bottom)\n{\n    canvas_dosetbounds(x, (int)left, (int)top, (int)right, (int)bottom);\n}\n\n/* this is the internal version using ints */\nstatic void canvas_dosetbounds(t_canvas *x, int x1, int y1, int x2, int y2)\n{\n    int heightwas = y2 - y1;\n    int heightchange = y2 - y1 - (x->gl_screeny2 - x->gl_screeny1);\n    if (x->gl_screenx1 == x1 && x->gl_screeny1 == y1 &&\n        x->gl_screenx2 == x2 && x->gl_screeny2 == y2)\n            return;\n    x->gl_screenx1 = x1;\n    x->gl_screeny1 = y1;\n    x->gl_screenx2 = x2;\n    x->gl_screeny2 = y2;\n    if (!glist_isgraph(x) && (x->gl_y2 < x->gl_y1))\n    {\n            /* if it's flipped so that y grows upward,\n            fix so that zero is bottom edge and redraw.  This is\n            only appropriate if we're a regular \"text\" object on the\n            parent. */\n        t_float diff = x->gl_y1 - x->gl_y2;\n        t_gobj *y;\n        x->gl_y1 = heightwas * diff/x->gl_zoom;\n        x->gl_y2 = x->gl_y1 - diff;\n            /* and move text objects accordingly; they should stick\n            to the bottom, not the top. */\n        for (y = x->gl_list; y; y = y->g_next)\n            if (pd_checkobject(&y->g_pd))\n                gobj_displace(y, x, 0, heightchange/x->gl_zoom);\n        canvas_redraw(x);\n    }\n}\n\nt_symbol *canvas_makebindsym(t_symbol *s)\n{\n    char buf[MAXPDSTRING];\n    snprintf(buf, MAXPDSTRING-1, \"pd-%s\", s->s_name);\n    buf[MAXPDSTRING-1] = 0;\n    return (gensym(buf));\n}\n\n    /* functions to bind and unbind canvases to symbol \"pd-blah\".  As\n    discussed on Pd dev list there should be a way to defeat this for\n    abstractions.  (Claude Heiland et al. Aug 9 2013) */\nstatic void canvas_bind(t_canvas *x)\n{\n    if (strcmp(x->gl_name->s_name, \"Pd\"))\n        pd_bind(&x->gl_pd, canvas_makebindsym(x->gl_name));\n}\n\nstatic void canvas_unbind(t_canvas *x)\n{\n    if (strcmp(x->gl_name->s_name, \"Pd\"))\n        pd_unbind(&x->gl_pd, canvas_makebindsym(x->gl_name));\n}\n\nvoid canvas_reflecttitle(t_canvas *x)\n{\n    char namebuf[MAXPDSTRING];\n    t_canvasenvironment *env = canvas_getenv(x);\n    if (!x->gl_havewindow)\n    {\n        bug(\"canvas_reflecttitle\");\n        return;\n    }\n    if (env->ce_argc)\n    {\n        int i;\n        strcpy(namebuf, \" (\");\n        for (i = 0; i < env->ce_argc; i++)\n        {\n            if (strlen(namebuf) > MAXPDSTRING/2 - 5)\n                break;\n            if (i != 0)\n                strcat(namebuf, \" \");\n            atom_string(&env->ce_argv[i], namebuf + strlen(namebuf),\n                MAXPDSTRING/2);\n        }\n        strcat(namebuf, \")\");\n    }\n    else namebuf[0] = 0;\n    if (x->gl_edit)\n    {\n        strncat(namebuf, \" [edit]\", MAXPDSTRING-strlen(namebuf)-1);\n        namebuf[MAXPDSTRING-1] = 0;\n    }\n    pdgui_vmess(\"pdtk_canvas_reflecttitle\", \"^ sss i\",\n        x,\n        canvas_getdir(x)->s_name, x->gl_name->s_name, namebuf,\n        x->gl_dirty);\n}\n\n    /* mark a glist dirty or clean */\nvoid canvas_dirty(t_canvas *x, t_floatarg f)\n{\n    t_canvas *x2 = canvas_getrootfor(x);\n    unsigned int n = f;\n    if (THISGUI->i_reloadingabstraction)\n        return;\n    if (n != x2->gl_dirty)\n    {\n        x2->gl_dirty = n;\n        if (x2->gl_havewindow)\n            canvas_reflecttitle(x2);\n    }\n    if(!n)\n        canvas_undo_cleardirty(x);\n}\n\nvoid canvas_drawredrect(t_canvas *x, int doit)\n{\n    if (doit)\n    {\n        int x1 = x->gl_zoom * x->gl_xmargin,\n            x2 = x1 + x->gl_zoom * x->gl_pixwidth,\n            y1 = x->gl_zoom * x->gl_ymargin,\n            y2 = y1 + x->gl_zoom * x->gl_pixheight;\n        pdgui_vmess(0, \"crr iiiiiiiiii rr ri rr rr\",\n            glist_getcanvas(x), \"create\", \"line\",\n            x1,y1, x1,y2, x2,y2, x2,y1, x1,y1,\n            \"-fill\", \"#ff8080\",\n            \"-width\", x->gl_zoom,\n            \"-capstyle\", \"projecting\",\n            \"-tags\", \"GOP\"); /* better: \"-tags\", 1, &\"GOP\" */\n    }\n    else\n        pdgui_vmess(0, \"crs\", glist_getcanvas(x), \"delete\", \"GOP\");\n}\n\n    /* the window becomes \"mapped\" (visible and not miniaturized) or\n    \"unmapped\" (either miniaturized or just plain gone.)  This should be\n    called from the GUI after the fact to \"notify\" us that we're mapped. */\nvoid canvas_map(t_canvas *x, t_floatarg f)\n{\n    int flag = (f != 0);\n    t_gobj *y;\n    if (flag)\n    {\n        if (!glist_isvisible(x))\n        {\n            t_selection *sel;\n            if (!x->gl_havewindow)\n            {\n                bug(\"canvas_map\");\n                canvas_vis(x, 1);\n            }\n            for (y = x->gl_list; y; y = y->g_next)\n                gobj_vis(y, x, 1);\n            x->gl_mapped = 1;\n            for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next)\n                gobj_select(sel->sel_what, x, 1);\n            canvas_drawlines(x);\n            if (x->gl_isgraph && x->gl_goprect)\n                canvas_drawredrect(x, 1);\n            pdgui_vmess(\"pdtk_canvas_getscroll\", \"c\", x);\n        }\n    }\n    else\n    {\n        if (glist_isvisible(x))\n        {\n            if (!x->gl_havewindow)\n            {\n                bug(\"canvas_map\");\n                return;\n            }\n                /* just clear out the whole canvas */\n            pdgui_vmess(0, \"crs\", x, \"delete\", \"all\");\n            x->gl_mapped = 0;\n        }\n    }\n}\n\nvoid canvas_redraw(t_canvas *x)\n{\n    if (glist_isvisible(x))\n    {\n        canvas_map(x, 0);\n        canvas_map(x, 1);\n    }\n}\n\n\n    /* we call this on a non-toplevel glist to \"open\" it into its\n    own window. */\nvoid glist_menu_open(t_glist *x)\n{\n    if (glist_isvisible(x) && !glist_istoplevel(x))\n    {\n        t_glist *gl2 = x->gl_owner;\n        if (!gl2)\n            bug(\"glist_menu_open\");  /* shouldn't happen but not dangerous */\n        else\n        {\n                /* erase ourself in parent window */\n            gobj_vis(&x->gl_gobj, gl2, 0);\n                    /* get rid of our editor (and subeditors) */\n            if (x->gl_editor)\n                canvas_destroy_editor(x);\n            x->gl_havewindow = 1;\n                    /* redraw ourself in parent window (blanked out this time) */\n            gobj_vis(&x->gl_gobj, gl2, 1);\n        }\n    }\n    canvas_vis(x, 1);\n}\n\nint glist_isvisible(t_glist *x)\n{\n    return ((!x->gl_loading) && glist_getcanvas(x)->gl_mapped);\n}\n\nint glist_istoplevel(t_glist *x)\n{\n        /* we consider a graph \"toplevel\" if it has its own window\n        or if it appears as a box in its parent window so that we\n        don't draw the actual contents there. */\n    return (x->gl_havewindow || !x->gl_isgraph);\n}\n\nint glist_getfont(t_glist *x)\n{\n    while (!x->gl_env)\n        if (!(x = x->gl_owner))\n            bug(\"t_canvasenvironment\");\n    return (x->gl_font);\n}\n\nint glist_getzoom(t_glist *x)\n{\n    t_glist *gl2 = x;\n    while (!glist_istoplevel(gl2) && gl2->gl_owner)\n        gl2 = gl2->gl_owner;\n    return (gl2->gl_zoom);\n}\n\nint glist_fontwidth(t_glist *x)\n{\n    return (sys_zoomfontwidth(glist_getfont(x), glist_getzoom(x), 0));\n}\n\nint glist_fontheight(t_glist *x)\n{\n    return (sys_zoomfontheight(glist_getfont(x), glist_getzoom(x), 0));\n}\n\nvoid canvas_free(t_canvas *x)\n{\n    t_gobj *y;\n    t_canvas_private*private = x->gl_privatedata;\n    int dspstate = canvas_suspend_dsp();\n    canvas_noundo(x);\n    if (canvas_whichfind == x)\n        canvas_whichfind = 0;\n    glist_noselect(x);\n    while ((y = x->gl_list))\n        glist_delete(x, y);\n    if (x == glist_getcanvas(x))\n        canvas_vis(x, 0);\n    if (x->gl_editor)\n        canvas_destroy_editor(x);   /* bug workaround; should already be gone*/\n    canvas_unbind(x);\n\n    if (x->gl_env)\n    {\n        freebytes(x->gl_env->ce_argv, x->gl_env->ce_argc * sizeof(t_atom));\n        freebytes(x->gl_env, sizeof(*x->gl_env));\n    }\n    canvas_undo_free(x);\n    freebytes(private, sizeof(*private));\n    canvas_resume_dsp(dspstate);\n    freebytes(x->gl_xlabel, x->gl_nxlabels * sizeof(*(x->gl_xlabel)));\n    freebytes(x->gl_ylabel, x->gl_nylabels * sizeof(*(x->gl_ylabel)));\n    gstub_cutoff(x->gl_stub);\n    pdgui_stub_deleteforkey(x);        /* probably unnecessary */\n    if (!x->gl_owner && !x->gl_isclone)\n        canvas_takeofflist(x);\n}\n\n/* ----------------- lines ---------- */\n\nstatic void canvas_drawlines(t_canvas *x)\n{\n    t_linetraverser t;\n    t_outconnect *oc;\n    {\n        char tag[128];\n        const char*tags[2] = {tag, \"cord\"};\n        linetraverser_start(&t, x);\n        while ((oc = linetraverser_next(&t)))\n        {\n            sprintf(tag, \"l%p\", oc);\n            pdgui_vmess(0, \"crr iiii ri rS\",\n                glist_getcanvas(x), \"create\", \"line\",\n                t.tr_lx1,t.tr_ly1, t.tr_lx2,t.tr_ly2,\n                \"-width\", (outlet_getsymbol(t.tr_outlet) == &s_signal ? 2:1) * x->gl_zoom,\n                \"-tags\", 2, tags);\n        }\n    }\n}\nvoid canvas_fixlinesfor(t_canvas *x, t_text *text)\n{\n    t_linetraverser t;\n    t_outconnect *oc;\n\n    linetraverser_start(&t, x);\n    while ((oc = linetraverser_next(&t)))\n    {\n        if (t.tr_ob == text || t.tr_ob2 == text)\n        {\n            char tag[128];\n            sprintf(tag, \"l%p\", oc);\n            pdgui_vmess(0, \"crs iiii\",\n                glist_getcanvas(x), \"coords\", tag,\n                t.tr_lx1,t.tr_ly1, t.tr_lx2,t.tr_ly2);\n        }\n    }\n}\n\nstatic void _canvas_delete_line(t_canvas*x, t_outconnect *oc)\n{\n    char tag[128];\n    if (!glist_isvisible(x))\n        return;\n    sprintf(tag, \"l%p\", oc);\n    pdgui_vmess(0, \"crs\", glist_getcanvas(x), \"delete\", tag);\n}\n\n    /* kill all lines for the object */\nvoid canvas_deletelinesfor(t_canvas *x, t_text *text)\n{\n    t_linetraverser t;\n    t_outconnect *oc;\n    linetraverser_start(&t, x);\n    while ((oc = linetraverser_next(&t)))\n    {\n        if (t.tr_ob == text || t.tr_ob2 == text)\n        {\n            _canvas_delete_line(x, oc);\n            obj_disconnect(t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno);\n        }\n    }\n}\n\n    /* kill all lines for one inlet or outlet */\nvoid canvas_deletelinesforio(t_canvas *x, t_text *text,\n    t_inlet *inp, t_outlet *outp)\n{\n    t_linetraverser t;\n    t_outconnect *oc;\n    linetraverser_start(&t, x);\n    while ((oc = linetraverser_next(&t)))\n    {\n        if ((t.tr_ob == text && t.tr_outlet == outp) ||\n            (t.tr_ob2 == text && t.tr_inlet == inp))\n        {\n            _canvas_delete_line(x, oc);\n            obj_disconnect(t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno);\n        }\n    }\n}\n\ntypedef void (*t_zoomfn)(void *x, t_floatarg arg1);\n\nstatic void canvas_pop(t_canvas *x, t_floatarg fvis)\n{\n    if (glist_istoplevel(x) && (sys_zoom_open == 2))\n    {\n        t_zoomfn zoommethod = (t_zoomfn)zgetfn(&x->gl_pd, gensym(\"zoom\"));\n        if (zoommethod)\n            (*zoommethod)(&x->gl_pd, (t_floatarg)2);\n    }\n    if (fvis != 0)\n        canvas_vis(x, 1);\n    pd_popsym(&x->gl_pd);\n    canvas_resortinlets(x);\n    canvas_resortoutlets(x);\n    x->gl_loading = 0;\n}\n\nvoid canvas_objfor(t_glist *gl, t_text *x, int argc, t_atom *argv);\n\n\nvoid canvas_restore(t_canvas *x, t_symbol *s, int argc, t_atom *argv)\n{\n    t_pd *z;\n    if (argc > 3)\n    {\n        t_atom *ap=argv+3;\n        if (ap->a_type == A_SYMBOL)\n        {\n            t_canvasenvironment *e = canvas_getenv(canvas_getcurrent());\n            canvas_rename(x, binbuf_realizedollsym(ap->a_w.w_symbol,\n                e->ce_argc, e->ce_argv, 1), 0);\n        }\n    }\n    canvas_pop(x, x->gl_willvis);\n\n    if (!(z = gensym(\"#X\")->s_thing)) pd_error(0, \"canvas_restore: out of context\");\n    else if (*z != canvas_class) pd_error(0, \"canvas_restore: wasn't a canvas\");\n    else\n    {\n        t_canvas *x2 = (t_canvas *)z;\n        x->gl_owner = x2;\n        canvas_objfor(x2, &x->gl_obj, argc, argv);\n    }\n}\n\nstatic void canvas_loadbangabstractions(t_canvas *x)\n{\n    t_gobj *y;\n    t_symbol *s = gensym(\"loadbang\");\n    for (y = x->gl_list; y; y = y->g_next)\n        if (pd_class(&y->g_pd) == canvas_class)\n        {\n            if (canvas_isabstraction((t_canvas *)y))\n                canvas_loadbang((t_canvas *)y);\n            else\n                canvas_loadbangabstractions((t_canvas *)y);\n        }\n        else if ((pd_class(&y->g_pd) == clone_class) &&\n            zgetfn(&y->g_pd, s))\n        {\n            pd_vmess(&y->g_pd, s, \"f\", (t_floatarg)LB_LOAD);\n        }\n}\n\nvoid canvas_loadbangsubpatches(t_canvas *x)\n{\n    t_gobj *y;\n    t_symbol *s = gensym(\"loadbang\");\n    for (y = x->gl_list; y; y = y->g_next)\n        if (pd_class(&y->g_pd) == canvas_class)\n        {\n            if (!canvas_isabstraction((t_canvas *)y))\n                canvas_loadbangsubpatches((t_canvas *)y);\n        }\n    for (y = x->gl_list; y; y = y->g_next)\n        if ((pd_class(&y->g_pd) != canvas_class) &&\n            (pd_class(&y->g_pd) != clone_class) &&\n            zgetfn(&y->g_pd, s))\n        {\n            pd_vmess(&y->g_pd, s, \"f\", (t_floatarg)LB_LOAD);\n        }\n}\n\nvoid canvas_loadbang(t_canvas *x)\n{\n    canvas_loadbangabstractions(x);\n    canvas_loadbangsubpatches(x);\n}\n\n/* JMZ/MSP:\n * initbang is emitted after a canvas is read from a file, but before the\n   parent canvas is finished loading.  This is apparently used so that\n   abstractions can create inlets/outlets as a function of creation arguments.\n   This practice is quite ugly but there's no other way to do it so far.\n */\nvoid canvas_initbang(t_canvas *x)\n{\n    t_gobj *y;\n    t_symbol *s = gensym(\"loadbang\");\n    /* run \"initbang\" for all subpatches, but NOT for the child abstractions */\n    for (y = x->gl_list; y; y = y->g_next)\n        if (pd_class(&y->g_pd) == canvas_class &&\n            !canvas_isabstraction((t_canvas *)y))\n                canvas_initbang((t_canvas *)y);\n    /* call the initbang()-method for objects that have one */\n    for (y = x->gl_list; y; y = y->g_next)\n        if ((pd_class(&y->g_pd) != canvas_class) && zgetfn(&y->g_pd, s))\n            pd_vmess(&y->g_pd, s, \"f\", (t_floatarg)LB_INIT);\n}\n\n/* JMZ:\n * closebang is emitted before the canvas is destroyed\n * and BEFORE subpatches/abstractions in this canvas are destroyed\n */\nvoid canvas_closebang(t_canvas *x)\n{\n    t_gobj *y;\n    t_symbol *s = gensym(\"loadbang\");\n\n    /* call the closebang()-method for objects that have one\n     * but NOT for subpatches/abstractions: these are called separately\n     * from g_graph:glist_delete()\n     */\n    for (y = x->gl_list; y; y = y->g_next)\n        if ((pd_class(&y->g_pd) != canvas_class) && zgetfn(&y->g_pd, s))\n            pd_vmess(&y->g_pd, s, \"f\", (t_floatarg)LB_CLOSE);\n}\n\n/* no longer used by 'pd-gui', but kept here for backwards compatibility.  The\n * new method calls canvas_setbounds() directly. */\nstatic void canvas_relocate(t_canvas *x, t_symbol *canvasgeom,\n    t_symbol *topgeom)\n{\n    int cxpix, cypix, cw, ch, txpix, typix, tw, th;\n    if (sscanf(canvasgeom->s_name, \"%dx%d+%d+%d\", &cw, &ch, &cxpix, &cypix)\n        < 4 ||\n        sscanf(topgeom->s_name, \"%dx%d+%d+%d\", &tw, &th, &txpix, &typix) < 4)\n        bug(\"canvas_relocate\");\n            /* for some reason this is initially called with cw=ch=1 so\n            we just suppress that here. */\n    if (cw > 5 && ch > 5)\n        canvas_dosetbounds(x, txpix, typix,\n            txpix + cw, typix + ch);\n}\n\nvoid canvas_popabstraction(t_canvas *x)\n{\n    pd_this->pd_newest = &x->gl_pd;\n    gensym(\"#A\")->s_thing = 0;\n    pd_bind(pd_this->pd_newest, gensym(\"#A\"));\n    pd_popsym(&x->gl_pd);\n    x->gl_loading = 0;\n    canvas_resortinlets(x);\n    canvas_resortoutlets(x);\n}\n\nvoid canvas_logerror(t_object *y)\n{\n#ifdef LATER\n    canvas_vis(x, 1);\n    if (!glist_isselected(x, &y->ob_g))\n        glist_select(x, &y->ob_g);\n#endif\n}\n\n/* -------------------------- subcanvases ---------------------- */\n\nextern void canvas_obj(t_glist *gl, t_symbol *s, int argc, t_atom *argv);\nstatic void *subcanvas_new(t_symbol *s)\n{\n    t_atom a[6];\n    t_canvas *x, *z = canvas_getcurrent();\n\n    if (!*s->s_name) s = gensym(\"/SUBPATCH/\");\n    SETFLOAT(a+0, GLIST_DEFCANVASXLOC);\n    SETFLOAT(a+1, GLIST_DEFCANVASYLOC);\n    SETFLOAT(a+2, GLIST_DEFCANVASWIDTH);\n    SETFLOAT(a+3, GLIST_DEFCANVASHEIGHT);\n    SETSYMBOL(a+4, s);\n    SETFLOAT(a+5, 1);\n    x = canvas_new(0, 0, 6, a);\n\n        /* check if subpatch is supposed to be connected (on the 1st inlet) */\n    if(z && z->gl_editor && z->gl_editor->e_connectbuf)\n    {\n        t_atom*argv = binbuf_getvec(z->gl_editor->e_connectbuf);\n        int argc = binbuf_getnatom(z->gl_editor->e_connectbuf);\n        t_symbol *sob = 0;\n        if ((argc == 7)\n            && atom_getsymbolarg(0, argc, argv) == gensym(\"#X\")\n            && atom_getsymbolarg(1, argc, argv) == gensym(\"connect\"))\n        {\n            int index2 = canvas_getindex(z, &x->gl_gobj);\n            if (((int)atom_getfloat(argv+5) == 0)\n                && (int)atom_getfloat(argv+4) == index2)\n                {\n                    int index1 = (int)atom_getfloat(argv+2);\n                    int outno = (int)atom_getfloat(argv+3);\n                    t_gobj*outobj=z->gl_list;\n                        /* get handle to object */\n                    while(index1-->0 && outobj)\n                        outobj=outobj->g_next;\n                    if(outobj && pd_checkobject(&outobj->g_pd))\n                    {\n                        if (obj_issignaloutlet(pd_checkobject(&outobj->g_pd), outno))\n                            sob = gensym(\"inlet~\");\n                        else\n                            sob = gensym(\"inlet\");\n                    }\n                }\n        }\n        if(sob)\n        {\n                /* JMZ: weirdo hardcoded numbers, taken from\n                 * glist_getnextxy(): 40\n                 * and canvas_howputnew(): -3\n                 */\n            SETFLOAT(a+0, 37);\n            SETFLOAT(a+1, 37);\n            SETSYMBOL(a+2, sob);\n            canvas_obj(x, gensym(\"obj\"), 3, a);\n\n                /* select the newly created inlet to continue autopatching */\n            canvas_create_editor(x);\n            glist_noselect(x);\n            glist_select(x, x->gl_list);\n        }\n    }\n    x->gl_owner = z;\n    canvas_pop(x, 1);\n    return (x);\n}\n\nstatic void canvas_click(t_canvas *x,\n    t_floatarg xpos, t_floatarg ypos,\n        t_floatarg shift, t_floatarg ctrl, t_floatarg alt)\n{\n    canvas_vis(x, 1);\n}\n\n\n    /* find out from subcanvas contents how much to fatten the box */\nvoid canvas_fattensub(t_canvas *x,\n    int *xp1, int *yp1, int *xp2, int *yp2)\n{\n    t_gobj *y;\n    *xp2 += 50;     /* fake for now */\n    *yp2 += 50;\n}\n\nstatic void canvas_rename_method(t_canvas *x, t_symbol *s, int ac, t_atom *av)\n{\n    if (ac && av->a_type == A_SYMBOL)\n        canvas_rename(x, av->a_w.w_symbol, 0);\n    else if (ac && av->a_type == A_DOLLSYM)\n    {\n        t_canvasenvironment *e = canvas_getenv(x);\n        canvas_setcurrent(x);\n        canvas_rename(x, binbuf_realizedollsym(av->a_w.w_symbol,\n            e->ce_argc, e->ce_argv, 1), 0);\n        canvas_unsetcurrent(x);\n    }\n    else canvas_rename(x, gensym(\"Pd\"), 0);\n}\n\n\n    /* return true if the \"canvas\" object is an abstraction (so we don't\n    save its contents, for example.)  */\nint canvas_isabstraction(const t_canvas *x)\n{\n    return (x->gl_env != 0);\n}\n\n    /* return true if the \"canvas\" object should be treated as a text\n    object.  This is true for abstractions but also for \"table\"s... */\n/* JMZ: add a flag to gop-abstractions to hide the title */\nint canvas_showtext(const t_canvas *x)\n{\n    t_atom *argv = (x->gl_obj.te_binbuf? binbuf_getvec(x->gl_obj.te_binbuf):0);\n    int argc = (x->gl_obj.te_binbuf? binbuf_getnatom(x->gl_obj.te_binbuf) : 0);\n    int isarray = (argc && argv[0].a_type == A_SYMBOL &&\n        argv[0].a_w.w_symbol == gensym(\"graph\"));\n    if(x->gl_hidetext)\n      return 0;\n    else\n      return (!isarray);\n}\n\n    /* get the document containing this canvas */\nt_canvas *canvas_getrootfor(t_canvas *x)\n{\n    if ((!x->gl_owner) || canvas_isabstraction(x))\n        return (x);\n    else return (canvas_getrootfor(x->gl_owner));\n}\n\nt_undo* canvas_undo_get(t_canvas *x)\n{\n    t_canvas_private*private = x?(x->gl_privatedata):0;\n    if(private)\n        return &(private->undo);\n    return 0;\n}\n\n/* ------------------------- DSP chain handling ------------------------- */\n\nEXTERN_STRUCT _dspcontext;\n#define t_dspcontext struct _dspcontext\n\nvoid ugen_start(void);\nvoid ugen_stop(void);\n\nt_dspcontext *ugen_start_graph(int toplevel, t_signal **sp,\n    int ninlets, int noutlets);\nvoid ugen_add(t_dspcontext *dc, t_object *x);\nvoid ugen_connect(t_dspcontext *dc, t_object *x1, int outno,\n    t_object *x2, int inno);\nvoid ugen_done_graph(t_dspcontext *dc);\n\n    /* schedule one canvas for DSP.  This is called below for all \"root\"\n    canvases, but is also called from the \"dsp\" method for sub-\n    canvases, which are treated almost like any other tilde object.  */\n\nvoid canvas_dodsp(t_canvas *x, int toplevel, t_signal **sp)\n{\n    t_linetraverser t;\n    t_outconnect *oc;\n    t_gobj *y;\n    t_object *ob;\n    t_symbol *dspsym = gensym(\"dsp\");\n    t_dspcontext *dc;\n#if 0\n    {\n        int i, n = obj_nsiginlets(&x->gl_obj) + obj_nsigoutlets(&x->gl_obj);\n        post(\"signals for %x (toplevel %d):\", x, toplevel);\n        for (i = 0; i < n; i++)\n        {\n            if (sp[i])\n                post(\"nchans %d, length %d\", sp[i]->s_nchans,  sp[i]->s_length);\n            else post(\"(null)\");\n        }\n    }\n#endif\n        /* create a new \"DSP graph\" object to use in sorting this canvas.\n        If we aren't toplevel, there are already other dspcontexts around. */\n    dc = ugen_start_graph(toplevel, sp,\n        obj_nsiginlets(&x->gl_obj),\n        obj_nsigoutlets(&x->gl_obj));\n\n        /* find all the \"dsp\" boxes and add them to the graph */\n\n    for (y = x->gl_list; y; y = y->g_next)\n        if ((ob = pd_checkobject(&y->g_pd)) && zgetfn(&y->g_pd, dspsym))\n            ugen_add(dc, ob);\n\n        /* ... and all dsp interconnections */\n    linetraverser_start(&t, x);\n    while ((oc = linetraverser_next(&t)))\n        if (obj_issignaloutlet(t.tr_ob, t.tr_outno))\n            ugen_connect(dc, t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno);\n\n        /* finally, sort them and add them to the DSP chain */\n    ugen_done_graph(dc);\n}\n\nstatic void canvas_dsp(t_canvas *x, t_signal **sp)\n{\n    canvas_dodsp(x, 0, sp);\n}\n\nint canvas_dspstate;    /* for back compatibility with externs - don't use */\n\n    /* this routine starts DSP for all root canvases. */\nstatic void canvas_start_dsp(void)\n{\n    t_canvas *x;\n    if (THISGUI->i_dspstate) ugen_stop();\n    else pdgui_vmess(\"pdtk_pd_dsp\", \"s\", \"ON\");\n    ugen_start();\n\n    for (x = pd_getcanvaslist(); x; x = x->gl_next)\n        canvas_dodsp(x, 1, 0);\n\n    canvas_dspstate = THISGUI->i_dspstate = 1;\n    if (gensym(\"pd-dsp-started\")->s_thing)\n        pd_bang(gensym(\"pd-dsp-started\")->s_thing);\n}\n\nstatic void canvas_stop_dsp(void)\n{\n    if (THISGUI->i_dspstate)\n    {\n        ugen_stop();\n        pdgui_vmess(\"pdtk_pd_dsp\", \"s\", \"OFF\");\n        canvas_dspstate = THISGUI->i_dspstate = 0;\n        if (gensym(\"pd-dsp-stopped\")->s_thing)\n            pd_bang(gensym(\"pd-dsp-stopped\")->s_thing);\n    }\n}\n\n    /* DSP can be suspended before, and resumed after, operations which\n    might affect the DSP chain.  For example, we suspend before loading and\n    resume afterward, so that DSP doesn't get resorted for every DSP object\n    int the patch. */\n\nint canvas_suspend_dsp(void)\n{\n    int rval = THISGUI->i_dspstate;\n    if (rval) canvas_stop_dsp();\n    return (rval);\n}\n\nvoid canvas_resume_dsp(int oldstate)\n{\n    if (oldstate) canvas_start_dsp();\n}\n\n    /* this is equivalent to suspending and resuming in one step. */\nvoid canvas_update_dsp(void)\n{\n    if (THISGUI->i_dspstate)\n    {\n        canvas_stop_dsp();\n        canvas_start_dsp();\n    }\n}\n\n/* the \"dsp\" message to pd starts and stops DSP computation, and, if\nappropriate, also opens and closes the audio device.  On exclusive-access\nAPIs such as ALSA, MMIO, and ASIO (I think) it's appropriate to close the\naudio devices when not using them; but jack behaves better if audio I/O\nsimply keeps running.  This is wasteful of CPU cycles but we do it anyway\nand can perhaps regard this is a design flaw in jack that we're working around\nhere.  The function audio_shouldkeepopen() is provided by s_audio.c to tell\nus that we should elide the step of closing audio when DSP is turned off.*/\n\nvoid glob_dsp(void *dummy, t_symbol *s, int argc, t_atom *argv)\n{\n    int newstate;\n    if (argc)\n    {\n        newstate = atom_getfloatarg(0, argc, argv);\n        if (newstate && !THISGUI->i_dspstate)\n        {\n            sys_set_audio_state(1);\n            canvas_start_dsp();\n        }\n        else if (!newstate && THISGUI->i_dspstate)\n        {\n            canvas_stop_dsp();\n            if (!audio_shouldkeepopen())\n                sys_set_audio_state(0);\n        }\n    }\n    else post(\"dsp state %d\", THISGUI->i_dspstate);\n}\n\n/******************* redrawing  data *********************/\n\nstatic int template_usestemplate(t_symbol *usersym, t_template *used)\n{\n    int i;\n    t_template *user = template_findbyname(usersym);\n    if (!user)\n        return (0);\n    if (user == used)\n        return (1);\n    for (i = 0; i < user->t_n; i++)\n        if (user->t_vec[i].ds_type == DT_ARRAY &&\n            template_usestemplate(user->t_vec[i].ds_arraytemplate, used))\n                return (1);\n    return (0);\n}\n\n    /* redraw all \"scalars\" that depend on a template, e.g., if a drawing\n     command is changed. Action = 0 to redraw, 1 to draw only, 2 to erase. */\nstatic void glist_doredrawfortemplate(t_glist *gl,\n    t_template *template, int action)\n{\n    t_gobj *g;\n    int vis = glist_isvisible(gl);\n    for (g = gl->gl_list; g; g = g->g_next)\n    {\n        if (vis && g->g_pd == scalar_class &&\n            template_usestemplate(((t_scalar *)g)->sc_template, template))\n        {\n            if (action == 1)\n            {\n                if (glist_isvisible(gl))\n                    gobj_vis(g, gl, 1);\n            }\n            else if (action == 2)\n            {\n                if (glist_isvisible(gl))\n                    gobj_vis(g, gl, 0);\n            }\n            else scalar_redraw((t_scalar *)g, gl);\n        }\n        else if (g->g_pd == canvas_class)\n            glist_doredrawfortemplate((t_glist *)g, template, action);\n    }\n}\n\n    /* public interface for above. */\nvoid canvas_redrawallfortemplate(t_template *template, int action)\n{\n    t_canvas *x;\n        /* find all root canvases */\n    for (x = pd_getcanvaslist(); x; x = x->gl_next)\n        glist_doredrawfortemplate(x, template, action);\n}\n\n    /* find the template defined by a canvas, and redraw all affected scalars */\nvoid canvas_redrawallfortemplatecanvas(t_canvas *x, int action)\n{\n    t_template *template = template_findbyname(canvas_makebindsym(x->gl_name));\n    if (template)\n        canvas_redrawallfortemplate(template, action);\n}\n\n/* ------------------------------- declare ------------------------ */\n\n/* put \"declare\" objects in a patch to tell it about the environment in\nwhich objects should be created in this canvas.  This includes directories to\nsearch (\"-path\", \"-stdpath\") and object libraries to load\n(\"-lib\" and \"-stdlib\").  These must be set before the patch containing\nthe \"declare\" object is filled in with its contents; so when the patch is\nsaved,  we throw early messages to the canvas to set the environment\nbefore any objects are created in it. */\n\nstatic t_class *declare_class;\n\ntypedef struct _declare\n{\n    t_object x_obj;\n    t_canvas *x_canvas;\n    int x_useme;\n} t_declare;\n\nstatic void *declare_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_declare *x = (t_declare *)pd_new(declare_class);\n    x->x_useme = 1;\n    x->x_canvas = canvas_getcurrent();\n        /* LATER update environment and/or load libraries */\n    if (!x->x_canvas->gl_loading)\n    {\n        /* the object is created by the user (not by loading a patch),\n         * so update canvas's properties on the fly */\n        canvas_declare(x->x_canvas, s, argc, argv);\n    }\n    return (x);\n}\n\nstatic void declare_free(t_declare *x)\n{\n    x->x_useme = 0;\n        /* LATER update environment */\n}\n\nvoid canvas_savedeclarationsto(t_canvas *x, t_binbuf *b)\n{\n    t_gobj *y;\n\n    for (y = x->gl_list; y; y = y->g_next)\n    {\n        if (pd_class(&y->g_pd) == declare_class)\n        {\n            binbuf_addv(b, \"s\", gensym(\"#X\"));\n            binbuf_addbinbuf(b, ((t_declare *)y)->x_obj.te_binbuf);\n            binbuf_addv(b, \";\");\n        }\n            /* before 0.47 we also allowed abstractions to write out to the\n            parent's declarations; now we only allow non-abstraction subpatches\n            to do so. */\n        else if (pd_checkglist(&y->g_pd) &&\n            (pd_compatibilitylevel < 47 || !canvas_isabstraction((t_canvas *)y)))\n                canvas_savedeclarationsto((t_canvas *)y, b);\n    }\n}\n\nstatic void canvas_completepath(const char *from, char *to, int bufsize,\n    t_canvas *x)\n{\n    if (sys_isabsolutepath(from))\n    {\n        to[0] = '\\0';\n    }\n    else if (x)\n    {\n        /* append canvas dir */\n        const char *dir = canvas_getdir(x)->s_name;\n        int dirlen = (int)strlen(dir);\n        strncpy(to, dir, bufsize-dirlen);\n        to[bufsize-dirlen-1] = '\\0';\n        strcat(to, \"/\");\n    }\n     else\n    {   /* append Pd lib dir */\n        strncpy(to, sys_libdir->s_name, bufsize-10);\n        to[bufsize-9] = '\\0';\n        strcat(to, \"/extra/\");\n    }\n    strncat(to, from, bufsize-strlen(to));\n    to[bufsize-1] = '\\0';\n}\n\n/* maybe we should rename check_exists() to sys_access() and move it to s_path */\n#ifdef _WIN32\nstatic int check_exists(const char*path)\n{\n    char pathbuf[MAXPDSTRING];\n    wchar_t ucs2path[MAXPDSTRING];\n    sys_bashfilename(path, pathbuf);\n    u8_utf8toucs2(ucs2path, MAXPDSTRING, pathbuf, MAXPDSTRING-1);\n    return (0 ==  _waccess(ucs2path, 0));\n}\n#else\n#include <unistd.h>\nstatic int check_exists(const char*path)\n{\n    char pathbuf[MAXPDSTRING];\n    sys_bashfilename(path, pathbuf);\n    return (0 == access(pathbuf, 0));\n}\n#endif\n\nstatic void canvas_path(t_canvas *x, t_canvasenvironment *e, const char *path)\n{\n    t_namelist *nl;\n    char strbuf[MAXPDSTRING];\n    if (sys_isabsolutepath(path))\n    {\n        e->ce_path = namelist_append(e->ce_path, path, 0);\n        return;\n    }\n\n        /* explicit relative path, starts with ./ or ../ */\n    if ((strncmp(\"./\", path, 2) == 0) || (strncmp(\"../\", path, 3) == 0))\n    {\n        e->ce_path = namelist_append(e->ce_path, path, 0);\n        return;\n    }\n\n        /* check if path is a subdir of the canvas-path */\n    canvas_completepath(path, strbuf, MAXPDSTRING, x);\n    if (check_exists(strbuf))\n    {\n        e->ce_path = namelist_append(e->ce_path, path, 0);\n        return;\n    }\n\n        /* check whether the given subdir is in one of the user search-paths */\n    for (nl=STUFF->st_searchpath; nl; nl=nl->nl_next)\n    {\n        snprintf(strbuf, MAXPDSTRING-1, \"%s/%s/\", nl->nl_string, path);\n        strbuf[MAXPDSTRING-1]=0;\n        if (check_exists(strbuf))\n        {\n            e->ce_path = namelist_append(e->ce_path, strbuf, 0);\n            return;\n        }\n    }\n\n        /* check whether the given subdir is in one of the standard-paths */\n    for (nl=STUFF->st_staticpath; nl; nl=nl->nl_next)\n    {\n        snprintf(strbuf, MAXPDSTRING-1, \"%s/%s/\", nl->nl_string, path);\n        strbuf[MAXPDSTRING-1]=0;\n        if (check_exists(strbuf))\n        {\n            e->ce_path = namelist_append(e->ce_path, strbuf, 0);\n            return;\n        }\n    }\n}\nstatic void canvas_lib(t_canvas *x, t_canvasenvironment *e, const char *lib)\n{\n    t_namelist *nl;\n    char strbuf[MAXPDSTRING];\n    if (sys_isabsolutepath(lib))\n    {\n        sys_load_lib(x, lib);\n        return;\n    }\n\n        /* explicit relative path, starts with ./ or ../ */\n    if ((strncmp(\"./\", lib, 2) == 0) || (strncmp(\"../\", lib, 3) == 0))\n    {\n        sys_load_lib(x, lib);\n        return;\n    }\n\n        /* prefix canvas-path */\n    canvas_completepath(lib, strbuf, MAXPDSTRING, x);\n    if (sys_load_lib(x, lib))\n        return;\n\n    /* check whether the given lib is located in one of the user search-paths */\n    for (nl=STUFF->st_searchpath; nl; nl=nl->nl_next)\n    {\n        snprintf(strbuf, MAXPDSTRING-1, \"%s/%s\", nl->nl_string, lib);\n        strbuf[MAXPDSTRING-1]=0;\n        if (sys_load_lib(x, strbuf))\n            return;\n    }\n}\n\nstatic void canvas_stdpath(t_canvasenvironment *e, const char *stdpath)\n{\n    t_namelist *nl;\n    char strbuf[MAXPDSTRING];\n    if (sys_isabsolutepath(stdpath))\n    {\n        e->ce_path = namelist_append(e->ce_path, stdpath, 0);\n        return;\n    }\n\n        /* strip \"extra/\"-prefix */\n    if (!strncmp(\"extra/\", stdpath, 6))\n        stdpath+=6;\n\n    /* prefix full pd-path (including extra) */\n    canvas_completepath(stdpath, strbuf, MAXPDSTRING, 0);\n    if (check_exists(strbuf))\n    {\n        e->ce_path = namelist_append(e->ce_path, strbuf, 0);\n        return;\n    }\n\n    /* check whether the given subdir is in one of the standard-paths */\n    for (nl=STUFF->st_staticpath; nl; nl=nl->nl_next)\n    {\n        snprintf(strbuf, MAXPDSTRING-1, \"%s/%s/\", nl->nl_string, stdpath);\n        strbuf[MAXPDSTRING-1]=0;\n        if (check_exists(strbuf))\n        {\n            e->ce_path = namelist_append(e->ce_path, strbuf, 0);\n            return;\n        }\n    }\n}\nstatic void canvas_stdlib(t_canvasenvironment *e, const char *stdlib)\n{\n    t_namelist *nl;\n    char strbuf[MAXPDSTRING];\n    if (sys_isabsolutepath(stdlib))\n    {\n        sys_load_lib(0, stdlib);\n        return;\n    }\n\n        /* strip    \"extra/\"-prefix */\n    if (!strncmp(\"extra/\", stdlib, 6))\n        stdlib+=6;\n\n        /* prefix full pd-path (including extra) */\n    canvas_completepath(stdlib, strbuf, MAXPDSTRING, 0);\n    if (sys_load_lib(0, strbuf))\n        return;\n\n    /* check whether the given lib is located in one of the standard-paths */\n    for (nl=STUFF->st_staticpath; nl; nl=nl->nl_next)\n    {\n        snprintf(strbuf, MAXPDSTRING-1, \"%s/%s\", nl->nl_string, stdlib);\n        strbuf[MAXPDSTRING-1]=0;\n        if (sys_load_lib(0, strbuf))\n            return;\n    }\n}\n\n\nvoid canvas_declare(t_canvas *x, t_symbol *s, int argc, t_atom *argv)\n{\n    int i;\n    t_canvasenvironment *e = canvas_getenv(x);\n#if 0\n    startpost(\"declare:: %s\", s->s_name);\n    postatom(argc, argv);\n    endpost();\n#endif\n    for (i = 0; i < argc; i++)\n    {\n        const char *flag = atom_getsymbolarg(i, argc, argv)->s_name;\n        const char *item = (argc > i+1)?atom_getsymbolarg(i+1, argc, argv)->s_name:0;\n        if ((item) && !strcmp(flag, \"-path\"))\n        {\n            canvas_path(x, e, item);\n            i++;\n        }\n        else if ((item) && !strcmp(flag, \"-stdpath\"))\n        {\n            canvas_stdpath(e, item);\n            i++;\n        }\n        else if ((item) && !strcmp(flag, \"-lib\"))\n        {\n            canvas_lib(x, e, item);\n            i++;\n        }\n        else if ((item) && !strcmp(flag, \"-stdlib\"))\n        {\n            canvas_stdlib(e, item);\n            i++;\n        }\n        else post(\"declare: %s: unknown declaration\", flag);\n    }\n}\n\ntypedef struct _canvasopen\n{\n    const char *name;\n    const char *ext;\n    char *dirresult;\n    char **nameresult;\n    unsigned int size;\n    int bin;\n    int fd;\n} t_canvasopen;\n\nstatic int canvas_open_iter(const char *path, t_canvasopen *co)\n{\n    int fd;\n    if ((fd = sys_trytoopenone(path, co->name, co->ext,\n        co->dirresult, co->nameresult, co->size, co->bin)) >= 0)\n    {\n        co->fd = fd;\n        return 0;\n    }\n    return 1;\n}\n\n    /* utility function to read a file, looking first down the canvas's search\n    path (set with \"declare\" objects in the patch and recursively in calling\n    patches), then down the system one.  The filename is the concatenation of\n    \"name\" and \"ext\".  \"Name\" may be absolute, or may be relative with\n    slashes.  If anything can be opened, the true directory\n    ais put in the buffer dirresult (provided by caller), which should\n    be \"size\" bytes.  The \"nameresult\" pointer will be set somewhere in\n    the interior of \"dirresult\" and will give the file basename (with\n    slashes trimmed).  If \"bin\" is set a 'binary' open is\n    attempted, otherwise ASCII (this only matters on Microsoft.)\n    If \"x\" is zero, the file is sought in the directory \".\" or in the\n    global path.*/\nint canvas_open(const t_canvas *x, const char *name, const char *ext,\n    char *dirresult, char **nameresult, unsigned int size, int bin)\n{\n    int fd = -1;\n    t_canvasopen co;\n\n        /* first check if \"name\" is absolute (and if so, try to open) */\n    if (sys_open_absolute(name, ext, dirresult, nameresult, size, bin, &fd))\n        return (fd);\n\n        /* otherwise \"name\" is relative; iterate over all the search-paths */\n    co.name = name;\n    co.ext = ext;\n    co.dirresult = dirresult;\n    co.nameresult = nameresult;\n    co.size = size;\n    co.bin = bin;\n    co.fd = -1;\n\n    canvas_path_iterate(x, (t_canvas_path_iterator)canvas_open_iter, &co);\n\n    return (co.fd);\n}\n\n/*\n * Iterate over all search-paths for <x> calling <fun> with the user-supplied\n * <data>.  The function is called with two arguments: a pathname to try to\n * open, and <data>.\n */\nint canvas_path_iterate(const t_canvas *x, t_canvas_path_iterator fun,\n    void *user_data)\n{\n    const t_canvas *y = 0;\n    t_namelist *nl = 0;\n    int count = 0;\n    if (!fun)\n        return 0;\n        /* iterate through canvas-local paths */\n    for (y = x; y; y = y->gl_owner)\n        if (y->gl_env)\n    {\n        const char *dir;\n        dir = canvas_getdir(y)->s_name;\n        for (nl = y->gl_env->ce_path; nl; nl = nl->nl_next)\n        {\n            char realname[MAXPDSTRING];\n            if (sys_isabsolutepath(nl->nl_string))\n                realname[0] = '\\0';\n            else\n            {   /* if not absolute path, append Pd lib dir */\n                strncpy(realname, dir, MAXPDSTRING);\n                realname[MAXPDSTRING-3] = 0;\n                strcat(realname, \"/\");\n            }\n            strncat(realname, nl->nl_string, MAXPDSTRING-strlen(realname));\n            realname[MAXPDSTRING-1] = 0;\n            if (!fun(realname, user_data))\n                return count+1;\n            count++;\n        }\n    }\n    /* try canvas dir */\n    if (!fun((x ? canvas_getdir(x)->s_name : \".\"), user_data))\n        return count+1;\n    count++;\n\n    /* now iterate through the global paths */\n    for (nl = STUFF->st_searchpath; nl; nl = nl->nl_next)\n    {\n        if (!fun(nl->nl_string, user_data))\n            return count+1;\n        count++;\n    }\n    /* and the temp paths from the commandline */\n    for (nl = STUFF->st_temppath; nl; nl = nl->nl_next)\n    {\n        if (!fun(nl->nl_string, user_data))\n            return count+1;\n        count++;\n    }\n    /* and the default paths */\n    if (sys_usestdpath)\n        for (nl = STUFF->st_staticpath; nl; nl = nl->nl_next)\n        {\n            if (!fun(nl->nl_string, user_data))\n                return count+1;\n            count++;\n        }\n\n    return count;\n}\n\nstatic void canvas_f(t_canvas *x, t_symbol *s, int argc, t_atom *argv)\n{\n    static int warned;\n    t_gobj *g, *g2;\n    t_object *ob;\n    if (argc > 1 && !warned)\n    {\n        post(\"** ignoring width or font settings from future Pd version **\");\n        warned = 1;\n    }\n    if (!x->gl_list)\n        return;\n    for (g = x->gl_list; (g2 = g->g_next); g = g2)\n        ;\n    if ((ob = pd_checkobject(&g->g_pd)))\n    {\n        ob->te_width = atom_getfloatarg(0, argc, argv);\n        if (glist_isvisible(x))\n        {\n            gobj_vis(g, x, 0);\n            gobj_vis(g, x, 1);\n        }\n    }\n}\n\nextern t_class *array_define_class;     /* LATER datum class too */\n\n    /* check if a pd can be treated as a glist - true if we're of any of\n    the glist classes, which all have 'glist' as the first item in struct */\nt_glist *pd_checkglist(t_pd *x)\n{\n    if (*x == canvas_class || *x == array_define_class)\n        return ((t_canvas *)x);\n    else return (0);\n}\n\n/* ------------------------------- setup routine ------------------------ */\n\n    /* why are some of these \"glist\" and others \"canvas\"? */\nextern void glist_text(t_glist *x, t_symbol *s, int argc, t_atom *argv);\nextern void canvas_obj(t_glist *gl, t_symbol *s, int argc, t_atom *argv);\nextern void canvas_bng(t_glist *gl, t_symbol *s, int argc, t_atom *argv);\nextern void canvas_toggle(t_glist *gl, t_symbol *s, int argc, t_atom *argv);\nextern void canvas_vslider(t_glist *gl, t_symbol *s, int argc, t_atom *argv);\nextern void canvas_hslider(t_glist *gl, t_symbol *s, int argc, t_atom *argv);\nextern void canvas_vdial(t_glist *gl, t_symbol *s, int argc, t_atom *argv);\n    /* old version... */\nextern void canvas_hdial(t_glist *gl, t_symbol *s, int argc, t_atom *argv);\nextern void canvas_hdial(t_glist *gl, t_symbol *s, int argc, t_atom *argv);\n    /* new version: */\nextern void canvas_hradio(t_glist *gl, t_symbol *s, int argc, t_atom *argv);\nextern void canvas_vradio(t_glist *gl, t_symbol *s, int argc, t_atom *argv);\nextern void canvas_vumeter(t_glist *gl, t_symbol *s, int argc, t_atom *argv);\nextern void canvas_mycnv(t_glist *gl, t_symbol *s, int argc, t_atom *argv);\nextern void canvas_numbox(t_glist *gl, t_symbol *s, int argc, t_atom *argv);\nextern void canvas_msg(t_glist *gl, t_symbol *s, int argc, t_atom *argv);\nextern void canvas_floatatom(t_glist *gl, t_symbol *s, int argc, t_atom *argv);\nextern void canvas_listbox(t_glist *gl, t_symbol *s, int argc, t_atom *argv);\nextern void canvas_symbolatom(t_glist *gl, t_symbol *s, int argc, t_atom *argv);\nextern void glist_scalar(t_glist *canvas, t_symbol *s, int argc, t_atom *argv);\n\nvoid g_graph_setup(void);\nvoid g_editor_setup(void);\nvoid g_readwrite_setup(void);\nextern void canvas_properties(t_gobj *z, t_glist *canvas);\n\nvoid g_canvas_setup(void)\n{\n        /* we prevent the user from typing \"canvas\" in an object box\n        by sending 0 for a creator function. */\n    canvas_class = class_new(gensym(\"canvas\"), 0,\n        (t_method)canvas_free, sizeof(t_canvas),\n            CLASS_NOINLET | CLASS_MULTICHANNEL, 0);\n            /* here is the real creator function, invoked in patch files\n            by sending the \"canvas\" message to #N, which is bound\n            to pd_camvasmaker. */\n    class_addmethod(pd_canvasmaker, (t_method)canvas_new, gensym(\"canvas\"),\n        A_GIMME, 0);\n    class_addmethod(canvas_class, (t_method)canvas_restore,\n        gensym(\"restore\"), A_GIMME, 0);\n    class_addmethod(canvas_class, (t_method)canvas_coords,\n        gensym(\"coords\"), A_GIMME, 0);\n\n/* -------------------------- objects ----------------------------- */\n    class_addmethod(canvas_class, (t_method)canvas_obj,\n        gensym(\"obj\"), A_GIMME, A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_msg,\n        gensym(\"msg\"), A_GIMME, A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_floatatom,\n        gensym(\"floatatom\"), A_GIMME, A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_listbox,\n        gensym(\"listbox\"), A_GIMME, A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_symbolatom,\n        gensym(\"symbolatom\"), A_GIMME, A_NULL);\n    class_addmethod(canvas_class, (t_method)glist_text,\n        gensym(\"text\"), A_GIMME, A_NULL);\n    class_addmethod(canvas_class, (t_method)glist_glist, gensym(\"graph\"),\n        A_GIMME, A_NULL);\n    class_addmethod(canvas_class, (t_method)glist_scalar,\n        gensym(\"scalar\"), A_GIMME, A_NULL);\n\n/* -------------- connect method used in reading files ------------------ */\n    class_addmethod(canvas_class, (t_method)canvas_connect,\n        gensym(\"connect\"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);\n\n\n/* -------------- IEMGUI: button, toggle, slider, etc.  ------------ */\n    class_addmethod(canvas_class, (t_method)canvas_bng, gensym(\"bng\"),\n                    A_GIMME, A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_toggle, gensym(\"toggle\"),\n                    A_GIMME, A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_vslider, gensym(\"vslider\"),\n                    A_GIMME, A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_hslider, gensym(\"hslider\"),\n                    A_GIMME, A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_hdial, gensym(\"hdial\"),\n                    A_GIMME, A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_vdial, gensym(\"vdial\"),\n                    A_GIMME, A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_hradio, gensym(\"hradio\"),\n                    A_GIMME, A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_vradio, gensym(\"vradio\"),\n                    A_GIMME, A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_vumeter, gensym(\"vumeter\"),\n                    A_GIMME, A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_mycnv, gensym(\"mycnv\"),\n                    A_GIMME, A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_numbox, gensym(\"numbox\"),\n                    A_GIMME, A_NULL);\n\n/* ------------------------ gui stuff --------------------------- */\n    class_addmethod(canvas_class, (t_method)canvas_pop, gensym(\"pop\"),\n        A_DEFFLOAT, A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_loadbang,\n        gensym(\"loadbang\"), A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_setbounds,\n        gensym(\"setbounds\"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_relocate,\n        gensym(\"relocate\"), A_SYMBOL, A_SYMBOL, A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_vis,\n        gensym(\"vis\"), A_FLOAT, A_NULL);\n    class_addmethod(canvas_class, (t_method)glist_menu_open,\n        gensym(\"menu-open\"), A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_map,\n        gensym(\"map\"), A_FLOAT, A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_dirty,\n        gensym(\"dirty\"), A_FLOAT, A_NULL);\n    class_setpropertiesfn(canvas_class, canvas_properties);\n\n/* ---------------------- list handling ------------------------ */\n    class_addmethod(canvas_class, (t_method)glist_clear, gensym(\"clear\"),\n        A_NULL);\n\n/* ----- subcanvases, which you get by typing \"pd\" in a box ---- */\n    class_addcreator((t_newmethod)subcanvas_new, gensym(\"pd\"), A_DEFSYMBOL, 0);\n    class_addcreator((t_newmethod)subcanvas_new, gensym(\"page\"),  A_DEFSYMBOL, 0);\n\n    class_addmethod(canvas_class, (t_method)canvas_click,\n        gensym(\"click\"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);\n    class_addmethod(canvas_class, (t_method)canvas_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_addmethod(canvas_class, (t_method)canvas_rename_method,\n        gensym(\"rename\"), A_GIMME, 0);\n\n/*---------------------------- declare ------------------- */\n    declare_class = class_new(gensym(\"declare\"), (t_newmethod)declare_new,\n        (t_method)declare_free, sizeof(t_declare), CLASS_NOINLET, A_GIMME, 0);\n    class_addmethod(canvas_class, (t_method)canvas_declare,\n        gensym(\"declare\"), A_GIMME, 0);\n\n/*--------------- future message to set formatting  -------------- */\n    class_addmethod(canvas_class, (t_method)canvas_f,\n        gensym(\"f\"), A_GIMME, 0);\n/* -------------- setups from other files for canvas_class ---------------- */\n    g_graph_setup();\n    g_editor_setup();\n    g_readwrite_setup();\n}\n\n    /* functions to add basic gui (e.g., clicking but not editing) to things\n    based on canvases that aren't editable, like \"array define\" object */\nvoid canvas_editor_for_class(t_class *c);\nvoid g_graph_setup_class(t_class *c);\nvoid canvas_readwrite_for_class(t_class *c);\n\nvoid canvas_add_for_class(t_class *c)\n{\n    class_addmethod(c, (t_method)canvas_restore,\n        gensym(\"restore\"), A_GIMME, 0);\n    class_addmethod(c, (t_method)canvas_click,\n        gensym(\"click\"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);\n    class_addmethod(c, (t_method)canvas_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_addmethod(c, (t_method)canvas_map,\n        gensym(\"map\"), A_FLOAT, A_NULL);\n    class_addmethod(c, (t_method)canvas_setbounds,\n        gensym(\"setbounds\"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);\n    canvas_editor_for_class(c);\n    canvas_readwrite_for_class(c);\n    /* g_graph_setup_class(c); */\n}\n\nvoid g_canvas_newpdinstance(void)\n{\n    THISGUI = getbytes(sizeof(*THISGUI));\n    THISGUI->i_newfilename = THISGUI->i_newdirectory = &s_;\n    THISGUI->i_newargc = 0;\n    THISGUI->i_newargv = 0;\n    THISGUI->i_reloadingabstraction = 0;\n    THISGUI->i_dspstate = 0;\n    THISGUI->i_dollarzero = 1000;\n    g_editor_newpdinstance();\n    g_template_newpdinstance();\n}\n\nvoid g_canvas_freepdinstance(void)\n{\n    g_editor_freepdinstance();\n    g_template_freepdinstance();\n    freebytes(THISGUI, sizeof(*THISGUI));\n}\n\nEXTERN int pd_getdspstate(void)\n{\n    return (THISGUI->i_dspstate);\n}\n\nvoid pd_doloadbang(void);\n\n    /* evaluate a file, which is expected to create a patch, and perform\n    post-evaluation cleanup and loadbang */\nt_pd *glob_evalfile(t_pd *ignore, t_symbol *name, t_symbol *dir)\n{\n    t_pd *x = 0, *boundx;\n    int dspstate;\n\n        /* even though binbuf_evalfile appears to take care of dspstate,\n        we have to do it again here, because canvas_startdsp() assumes\n        that all toplevel canvases are visible.  LATER check if this\n        is still necessary -- probably not. */\n    dspstate = canvas_suspend_dsp();\n    boundx = s__X.s_thing;\n        s__X.s_thing = 0;       /* don't save #X; we'll need to leave it bound\n                                for the caller to grab it. */\n    binbuf_evalfile(name, dir);\n    while ((x != s__X.s_thing) && s__X.s_thing)\n    {\n        x = s__X.s_thing;\n        vmess(x, gensym(\"pop\"), \"i\", 1);\n    }\n    if (!sys_noloadbang)\n        pd_doloadbang();\n    canvas_resume_dsp(dspstate);\n    s__X.s_thing = boundx;\n    return x;\n}\n\n    /* open a file as if from an open dialog from the GUI.  If the optional\n    argument \"f\" is nonzero, first check if the file is already open and if\n    so, just \"vis\" it.  This would be useful if you want merely to make sure a\n    patch is open, but don't want more than one copy. */\nvoid glob_open(t_pd *ignore, t_symbol *name, t_symbol *dir, t_floatarg f)\n{\n    t_glist *gl;\n    if (f != 0)\n        for (gl = pd_getcanvaslist(); gl; gl = gl->gl_next)\n            if (name == gl->gl_name && gl->gl_env && gl->gl_env->ce_dir == dir)\n    {\n            /* don't reopen already-open document, just vis it */\n        canvas_vis(gl, 1);\n        return;\n    }\n    if (!glob_evalfile(ignore, name, dir))\n        pdgui_vmess(\"::pdwindow::busyrelease\", 0);\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/g_canvas.h",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* this file defines the structure for \"glists\" and related structures and\nfunctions.  \"Glists\" and \"canvases\" and \"graphs\" used to be different\nstructures until being unified in version 0.35.\n\nA glist occupies its own window if the \"gl_havewindow\" flag is set.  Its\nappearance on its \"parent\", also called \"owner\", (if it has one) is as a graph\nif \"gl_isgraph\" is set, and otherwise as a text box.\n\nA glist is \"root\" if it has no owner, i.e., a document window.  In this\ncase \"gl_havewindow\" is always set.\n\nWe maintain a list of root windows, so that we can traverse the whole\ncollection of everything in a Pd process.\n\nIf a glist has a window it may still not be \"mapped.\"  Miniaturized\nwindows aren't mapped, for example, but a window is also not mapped\nimmediately upon creation.  In either case gl_havewindow is true but\ngl_mapped is false.\n\nClosing a non-root window makes it invisible; closing a root destroys it.\n\nA glist that's just a text object on its parent is always \"toplevel.\"  An\nembedded glist can switch back and forth to appear as a toplevel by double-\nclicking on it.  Single-clicking a text box makes the toplevel become visible\nand raises the window it's in.\n\nIf a glist shows up as a graph on its parent, the graph is blanked while the\nglist has its own window, even if miniaturized.\n\n*/\n\n/* NOTE: this file describes Pd implementation details which may change\nin future releases.  The public (stable) API is in m_pd.h. */\n\n#ifndef G_CANVAS_H\n#define G_CANVAS_H\n\n#if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus)\nextern \"C\" {\n#endif\n\n/* --------------------- geometry ---------------------------- */\n#define IOWIDTH 7       /* width of an inlet/outlet in pixels */\n#define IHEIGHT 3       /* height of an inlet in pixels */\n#define OHEIGHT 3       /* height of an outlet in pixels */\n#define IOMIDDLE ((IOWIDTH-1)/2)\n#define GLIST_DEFGRAPHWIDTH 200\n#define GLIST_DEFGRAPHHEIGHT 140\n\n#define GLIST_DEFCANVASXLOC 0\n#ifdef __APPLE__\n#define GLIST_DEFCANVASYLOC 22\n#else\n#define GLIST_DEFCANVASYLOC 50\n#endif\n\n/* ----------------------- data ------------------------------- */\n\ntypedef struct _updateheader\n{\n    struct _updateheader *upd_next;\n    unsigned int upd_array:1;       /* true if array, false if glist */\n    unsigned int upd_queued:1;      /* true if we're queued */\n} t_updateheader;\n\n    /* types to support glists grabbing mouse motion or keys from parent */\ntypedef void (*t_glistmotionfn)(void *z, t_floatarg dx, t_floatarg dy,\n    t_floatarg up);\ntypedef void (*t_glistkeyfn)(void *z, t_symbol *keysym, t_floatarg key);\n\nEXTERN_STRUCT _rtext;\n#define t_rtext struct _rtext\n\nEXTERN_STRUCT _gtemplate;\n#define t_gtemplate struct _gtemplate\n\nEXTERN_STRUCT _guiconnect;\n#define t_guiconnect struct _guiconnect\n\nEXTERN_STRUCT _tscalar;\n#define t_tscalar struct _tscalar\n\nEXTERN_STRUCT _canvasenvironment;\n#define t_canvasenvironment struct _canvasenvironment\n\nEXTERN_STRUCT _fielddesc;\n#define t_fielddesc struct _fielddesc\n\ntypedef struct _selection\n{\n    t_gobj *sel_what;\n    struct _selection *sel_next;\n} t_selection;\n\n    /* this structure is instantiated whenever a glist becomes visible. */\ntypedef struct _editor\n{\n    t_updateheader e_upd;           /* update header structure */\n    t_selection *e_updlist;         /* list of objects to update */\n    t_rtext *e_rtext;               /* text responder linked list */\n    t_selection *e_selection;       /* head of the selection list */\n    t_rtext *e_textedfor;           /* the rtext if any that we are editing */\n    t_gobj *e_grab;                 /* object being \"dragged\" */\n    t_glistmotionfn e_motionfn;     /* ... motion callback */\n    t_glistkeyfn e_keyfn;           /* ... keypress callback */\n    t_binbuf *e_connectbuf;         /* connections to deleted objects */\n    t_binbuf *e_deleted;            /* last stuff we deleted */\n    t_guiconnect *e_guiconnect;     /* GUI connection for filtering messages */\n    struct _glist *e_glist;         /* glist which owns this */\n    int e_xwas;                     /* xpos on last mousedown or motion event */\n    int e_ywas;                     /* ypos, similarly */\n    int e_selectline_index1;        /* indices for the selected line if any */\n    int e_selectline_outno;         /* (only valid if e_selectedline is set) */\n    int e_selectline_index2;\n    int e_selectline_inno;\n    t_outconnect *e_selectline_tag;\n    unsigned int e_onmotion: 3;     /* action to take on motion */\n    unsigned int e_lastmoved: 1;    /* one if mouse has moved since click */\n    unsigned int e_textdirty: 1;    /* one if e_textedfor has changed */\n    unsigned int e_selectedline: 1; /* one if a line is selected */\n    t_clock *e_clock;               /* clock to filter GUI move messages */\n    int e_xnew;                     /* xpos for next move event */\n    int e_ynew;                     /* ypos, similarly */\n} t_editor;\n\n#define MA_NONE    0    /* e_onmotion: do nothing on mouse motion */\n#define MA_MOVE    1    /* drag the selection around */\n#define MA_CONNECT 2    /* make a connection */\n#define MA_REGION  3    /* selection region */\n#define MA_PASSOUT 4    /* send on to e_grab */\n#define MA_DRAGTEXT 5   /* drag in text editor to alter selection */\n#define MA_RESIZE  6    /* drag to resize */\n\n/* editor structure for \"garrays\".  We don't bother to delete and regenerate\nthis structure when the \"garray\" becomes invisible or visible, although we\ncould do so if the structure gets big (like the \"editor\" above.) */\n\ntypedef struct _arrayvis\n{\n    t_updateheader av_upd;          /* update header structure */\n    t_garray *av_garray;            /* owning structure */\n} t_arrayvis;\n\n/* the t_tick structure describes where to draw x and y \"ticks\" for a glist */\n\ntypedef struct _tick    /* where to put ticks on x or y axes */\n{\n    t_float k_point;      /* one point to draw a big tick at */\n    t_float k_inc;        /* x or y increment per little tick */\n    int k_lperb;        /* little ticks per big; 0 if no ticks to draw */\n} t_tick;\n\n/* the t_glist structure, which describes a list of elements that live on an\narea of a window.\n\n*/\n\nstruct _glist\n{\n    t_object gl_obj;            /* header in case we're a glist */\n    t_gobj *gl_list;            /* the actual data */\n    struct _gstub *gl_stub;     /* safe pointer handler */\n    int gl_valid;               /* incremented when pointers might be stale */\n    struct _glist *gl_owner;    /* parent glist, supercanvas, or 0 if none */\n    int gl_pixwidth;            /* width in pixels (on parent, if a graph) */\n    int gl_pixheight;\n    t_float gl_x1;                /* bounding rectangle in our own coordinates */\n    t_float gl_y1;\n    t_float gl_x2;\n    t_float gl_y2;\n    int gl_screenx1;            /* screen coordinates when toplevel */\n    int gl_screeny1;\n    int gl_screenx2;\n    int gl_screeny2;\n    int gl_xmargin;                /* origin for GOP rectangle */\n    int gl_ymargin;\n    t_tick gl_xtick;            /* ticks marking X values */\n    int gl_nxlabels;            /* number of X coordinate labels */\n    t_symbol **gl_xlabel;           /* ... an array to hold them */\n    t_float gl_xlabely;               /* ... and their Y coordinates */\n    t_tick gl_ytick;            /* same as above for Y ticks and labels */\n    int gl_nylabels;\n    t_symbol **gl_ylabel;\n    t_float gl_ylabelx;\n    t_editor *gl_editor;        /* editor structure when visible */\n    t_symbol *gl_name;          /* symbol bound here */\n    int gl_font;                /* nominal font size in points, e.g., 10 */\n    struct _glist *gl_next;         /* link in list of toplevels */\n    t_canvasenvironment *gl_env;    /* root canvases and abstractions only */\n    unsigned int gl_havewindow:1;   /* true if we own a window */\n    unsigned int gl_mapped:1;       /* true if, moreover, it's \"mapped\" */\n    unsigned int gl_dirty:1;        /* (root canvas only:) patch has changed */\n    unsigned int gl_loading:1;      /* am now loading from file */\n    unsigned int gl_willvis:1;      /* make me visible after loading */\n    unsigned int gl_edit:1;         /* edit mode */\n    unsigned int gl_isdeleting:1;   /* we're inside glist_delete -- hack! */\n    unsigned int gl_goprect:1;      /* draw rectangle for graph-on-parent */\n    unsigned int gl_isgraph:1;      /* show as graph on parent */\n    unsigned int gl_hidetext:1;     /* hide object-name + args when doing graph on parent */\n    unsigned int gl_private:1;      /* private flag used in x_scalar.c */\n    unsigned int gl_isclone:1;      /* exists as part of a clone object */\n    int gl_zoom;                    /* zoom factor (integer zoom-in only) */\n    void *gl_privatedata;           /* private data */\n};\n\n#define gl_gobj gl_obj.te_g\n#define gl_pd gl_gobj.g_pd\n\n/* a data structure to describe a field in a pure datum */\n\n#define DT_FLOAT 0\n#define DT_SYMBOL 1\n#define DT_TEXT 2\n#define DT_ARRAY 3\n\ntypedef struct _dataslot\n{\n    int ds_type;\n    t_symbol *ds_name;\n    t_symbol *ds_arraytemplate;     /* filled in for arrays only */\n} t_dataslot;\n\ntypedef struct _template\n{\n    t_pd t_pdobj;               /* header */\n    struct _gtemplate *t_list;  /* list of \"struct\"/gtemplate objects */\n    t_symbol *t_sym;            /* name */\n    int t_n;                    /* number of dataslots (fields) */\n    t_dataslot *t_vec;          /* array of dataslots */\n    struct _template *t_next;\n} t_template;\n\nstruct _array\n{\n    int a_n;            /* number of elements */\n    int a_elemsize;     /* size in bytes; LATER get this from template */\n    char *a_vec;        /* array of elements */\n    t_symbol *a_templatesym;    /* template for elements */\n    int a_valid;        /* protection against stale pointers into array */\n    t_gpointer a_gp;    /* pointer to scalar or array element we're in */\n    t_gstub *a_stub;    /* stub for pointing into this array */\n};\n\n    /* structure for traversing all the connections in a glist */\ntypedef struct _linetraverser\n{\n    t_canvas *tr_x;\n    t_object *tr_ob;\n    int tr_nout;\n    int tr_outno;\n    t_object *tr_ob2;\n    t_outlet *tr_outlet;\n    t_inlet *tr_inlet;\n    int tr_nin;\n    int tr_inno;\n    int tr_x11, tr_y11, tr_x12, tr_y12;\n    int tr_x21, tr_y21, tr_x22, tr_y22;\n    int tr_lx1, tr_ly1, tr_lx2, tr_ly2;\n    t_outconnect *tr_nextoc;\n    int tr_nextoutno;\n} t_linetraverser;\n\nstruct _instancecanvas\n{\n    struct _instanceeditor *i_editor;\n    struct _instancetemplate *i_template;\n    t_symbol *i_newfilename;\n    t_symbol *i_newdirectory;\n    int i_newargc;\n    t_atom *i_newargv;\n    t_glist *i_reloadingabstraction;\n    int i_dspstate;\n    int i_dollarzero;\n    t_float i_graph_lastxpix, i_graph_lastypix;\n};\n\nvoid g_editor_newpdinstance(void);\nvoid g_template_newpdinstance(void);\nvoid g_editor_freepdinstance(void);\nvoid g_template_freepdinstance(void);\n\n#define THISGUI (pd_this->pd_gui)\n#define EDITOR (pd_this->pd_gui->i_editor)\n#define TEMPLATE (pd_this->pd_gui->i_template)\n\n/* function types used to define graphical behavior for gobjs, a bit like X\nwidgets.  We don't use Pd methods because Pd's typechecking can't specify the\ntypes of pointer arguments.  Also it's more convenient this way, since\nevery \"patchable\" object can just get the \"text\" behaviors. */\n\n        /* Call this to get a gobj's bounding rectangle in pixels */\ntypedef void (*t_getrectfn)(t_gobj *x, struct _glist *glist,\n    int *x1, int *y1, int *x2, int *y2);\n        /* and this to displace a gobj: */\ntypedef void (*t_displacefn)(t_gobj *x, struct _glist *glist, int dx, int dy);\n        /* change color to show selection: */\ntypedef void (*t_selectfn)(t_gobj *x, struct _glist *glist, int state);\n        /* change appearance to show activation/deactivation: */\ntypedef void (*t_activatefn)(t_gobj *x, struct _glist *glist, int state);\n        /* warn a gobj it's about to be deleted */\ntypedef void (*t_deletefn)(t_gobj *x, struct _glist *glist);\n        /*  making visible or invisible */\ntypedef void (*t_visfn)(t_gobj *x, struct _glist *glist, int flag);\n        /* field a mouse click (when not in \"edit\" mode) */\ntypedef int (*t_clickfn)(t_gobj *x, struct _glist *glist,\n    int xpix, int ypix, int shift, int alt, int dbl, int doit);\n        /* ... and later, resizing; getting/setting font or color... */\n\nstruct _widgetbehavior\n{\n    t_getrectfn w_getrectfn;\n    t_displacefn w_displacefn;\n    t_selectfn w_selectfn;\n    t_activatefn w_activatefn;\n    t_deletefn w_deletefn;\n    t_visfn w_visfn;\n    t_clickfn w_clickfn;\n};\n\n/* -------- behaviors for scalars defined by objects in template --------- */\n/* these are set by \"drawing commands\" in g_template.c which add appearance to\nscalars, which live in some other window.  If the scalar is just included\nin a canvas the \"parent\" is a misnomer.  There is also a text scalar object\nwhich really does draw the scalar on the parent window; see g_scalar.c. */\n\n/* note how the click function wants the whole scalar, not the \"data\", so\ndoesn't work on array elements... LATER reconsider this */\n\n        /* bounding rectangle: */\ntypedef void (*t_parentgetrectfn)(t_gobj *x, struct _glist *glist,\n    t_word *data, t_template *tmpl, t_float basex, t_float basey,\n    int *x1, int *y1, int *x2, int *y2);\n        /* displace it */\ntypedef void (*t_parentdisplacefn)(t_gobj *x, struct _glist *glist,\n    t_word *data, t_template *tmpl, t_float basex, t_float basey,\n    int dx, int dy);\n        /* change color to show selection */\ntypedef void (*t_parentselectfn)(t_gobj *x, struct _glist *glist,\n    t_word *data, t_template *tmpl, t_float basex, t_float basey,\n    int state);\n        /* change appearance to show activation/deactivation: */\ntypedef void (*t_parentactivatefn)(t_gobj *x, struct _glist *glist,\n    t_word *data, t_template *tmpl, t_float basex, t_float basey,\n    int state);\n        /*  making visible or invisible */\ntypedef void (*t_parentvisfn)(t_gobj *x, struct _glist *glist,\n    t_word *data, t_template *tmpl, t_float basex, t_float basey,\n    int flag);\n        /*  field a mouse click */\ntypedef int (*t_parentclickfn)(t_gobj *x, struct _glist *glist,\n    t_word *data, t_template *tmpl, t_scalar *sc, t_array *ap,\n    t_float basex, t_float basey,\n    int xpix, int ypix, int shift, int alt, int dbl, int doit);\n\nstruct _parentwidgetbehavior\n{\n    t_parentgetrectfn w_parentgetrectfn;\n    t_parentdisplacefn w_parentdisplacefn;\n    t_parentselectfn w_parentselectfn;\n    t_parentactivatefn w_parentactivatefn;\n    t_parentvisfn w_parentvisfn;\n    t_parentclickfn w_parentclickfn;\n};\n\n    /* cursor definitions; used as return value for t_parentclickfn */\n#define CURSOR_RUNMODE_NOTHING 0\n#define CURSOR_RUNMODE_CLICKME 1\n#define CURSOR_RUNMODE_THICKEN 2\n#define CURSOR_RUNMODE_ADDPOINT 3\n#define CURSOR_EDITMODE_NOTHING 4\n#define CURSOR_EDITMODE_CONNECT 5\n#define CURSOR_EDITMODE_DISCONNECT 6\n#define CURSOR_EDITMODE_RESIZE 7\nEXTERN void canvas_setcursor(t_glist *x, unsigned int cursornum);\n\nextern t_canvas *canvas_whichfind;  /* last canvas we did a find in */\nextern t_class *vinlet_class, *voutlet_class;\nextern int glist_valid;         /* incremented when pointers might be stale */\n\n#define PLOTSTYLE_POINTS 0     /* plotting styles for arrays */\n#define PLOTSTYLE_POLY 1\n#define PLOTSTYLE_BEZ 2\n\n/* ------------------- functions on any gobj ----------------------------- */\nEXTERN void gobj_getrect(t_gobj *x, t_glist *owner, int *x1, int *y1,\n    int *x2, int *y2);\nEXTERN void gobj_displace(t_gobj *x, t_glist *owner, int dx, int dy);\nEXTERN void gobj_select(t_gobj *x, t_glist *owner, int state);\nEXTERN void gobj_activate(t_gobj *x, t_glist *owner, int state);\nEXTERN void gobj_delete(t_gobj *x, t_glist *owner);\nEXTERN void gobj_vis(t_gobj *x, t_glist *glist, int flag);\nEXTERN int gobj_click(t_gobj *x, struct _glist *glist,\n    int xpix, int ypix, int shift, int alt, int dbl, int doit);\nEXTERN void gobj_save(t_gobj *x, t_binbuf *b);\nEXTERN int gobj_shouldvis(t_gobj *x, struct _glist *glist);\n\n/* -------------------- functions on glists --------------------- */\nEXTERN void glist_init(t_glist *x);\nEXTERN void glist_add(t_glist *x, t_gobj *g);\n\nEXTERN void glist_clear(t_glist *x);\nEXTERN t_canvas *glist_getcanvas(t_glist *x);\nEXTERN int glist_isselected(t_glist *x, t_gobj *y);\nEXTERN void glist_select(t_glist *x, t_gobj *y);\nEXTERN void glist_deselect(t_glist *x, t_gobj *y);\nEXTERN void glist_noselect(t_glist *x);\nEXTERN void glist_selectall(t_glist *x);\nEXTERN void glist_delete(t_glist *x, t_gobj *y);\nEXTERN void glist_retext(t_glist *x, t_text *y);\nEXTERN void glist_grab(t_glist *x, t_gobj *y, t_glistmotionfn motionfn,\n    t_glistkeyfn keyfn, int xpos, int ypos);\nEXTERN int glist_isvisible(t_glist *x);\nEXTERN int glist_istoplevel(t_glist *x);\nEXTERN t_glist *glist_findgraph(t_glist *x);\nEXTERN int glist_getfont(t_glist *x);\nEXTERN int glist_fontwidth(t_glist *x);\nEXTERN int glist_fontheight(t_glist *x);\nEXTERN int glist_getzoom(t_glist *x);\nEXTERN void glist_sort(t_glist *canvas);\nEXTERN void glist_read(t_glist *x, t_symbol *filename, t_symbol *format);\nEXTERN void glist_mergefile(t_glist *x, t_symbol *filename, t_symbol *format);\n\nEXTERN t_float glist_pixelstox(t_glist *x, t_float xpix);\nEXTERN t_float glist_pixelstoy(t_glist *x, t_float ypix);\nEXTERN t_float glist_xtopixels(t_glist *x, t_float xval);\nEXTERN t_float glist_ytopixels(t_glist *x, t_float yval);\nEXTERN t_float glist_dpixtodx(t_glist *x, t_float dxpix);\nEXTERN t_float glist_dpixtody(t_glist *x, t_float dypix);\n\nEXTERN void glist_getnextxy(t_glist *gl, int *xval, int *yval);\nEXTERN void glist_glist(t_glist *g, t_symbol *s, int argc, t_atom *argv);\nEXTERN t_glist *glist_addglist(t_glist *g, t_symbol *sym,\n    t_float x1, t_float y1, t_float x2, t_float y2,\n    t_float px1, t_float py1, t_float px2, t_float py2);\nEXTERN void glist_arraydialog(t_glist *parent, t_symbol *name,\n    t_floatarg size, t_floatarg saveit, t_floatarg newgraph);\nEXTERN t_binbuf *glist_writetobinbuf(t_glist *x, int wholething);\nEXTERN int glist_isgraph(t_glist *x);\nEXTERN void glist_redraw(t_glist *x);\nEXTERN void glist_drawiofor(t_glist *glist, t_object *ob, int firsttime,\n    const char *tag, int x1, int y1, int x2, int y2);\nEXTERN void glist_eraseiofor(t_glist *glist, t_object *ob, const char *tag);\nEXTERN void canvas_create_editor(t_glist *x);\nEXTERN void canvas_destroy_editor(t_glist *x);\nvoid canvas_deletelinesforio(t_canvas *x, t_text *text,\n    t_inlet *inp, t_outlet *outp);\n\n/* -------------------- functions on texts ------------------------- */\nEXTERN void text_setto(t_text *x, t_glist *glist, const char *buf, int bufsize);\nEXTERN void text_drawborder(t_text *x, t_glist *glist, const char *tag,\n    int width, int height, int firsttime);\nEXTERN void text_eraseborder(t_text *x, t_glist *glist, const char *tag);\nEXTERN int text_xpix(t_text *x, t_glist *glist);\nEXTERN int text_ypix(t_text *x, t_glist *glist);\nextern const t_widgetbehavior text_widgetbehavior;\n\n/* -------------------- functions on rtexts ------------------------- */\n#define RTEXT_DOWN 1\n#define RTEXT_DRAG 2\n#define RTEXT_DBL 3\n#define RTEXT_SHIFT 4\n\nEXTERN t_rtext *rtext_new(t_glist *glist, t_text *who);\nEXTERN t_rtext *glist_findrtext(t_glist *gl, t_text *who);\nEXTERN void rtext_draw(t_rtext *x);\nEXTERN void rtext_erase(t_rtext *x);\nEXTERN int rtext_height(t_rtext *x);\nEXTERN void rtext_displace(t_rtext *x, int dx, int dy);\nEXTERN void rtext_select(t_rtext *x, int state);\nEXTERN void rtext_activate(t_rtext *x, int state);\nEXTERN void rtext_free(t_rtext *x);\nEXTERN void rtext_key(t_rtext *x, int n, t_symbol *s);\nEXTERN void rtext_mouse(t_rtext *x, int xval, int yval, int flag);\nEXTERN void rtext_retext(t_rtext *x);\nEXTERN int rtext_width(t_rtext *x);\nEXTERN const char *rtext_gettag(t_rtext *x);\nEXTERN void rtext_gettext(t_rtext *x, char **buf, int *bufsize);\nEXTERN void rtext_getseltext(t_rtext *x, char **buf, int *bufsize);\nEXTERN t_text *rtext_getowner(t_rtext *x);\n\n/* -------------------- functions on canvases ------------------------ */\nEXTERN t_class *canvas_class;\n\nEXTERN t_canvas *canvas_new(void *dummy, t_symbol *sel, int argc, t_atom *argv);\nEXTERN t_symbol *canvas_makebindsym(t_symbol *s);\nEXTERN void canvas_fixlinesfor(t_canvas *x, t_text *text);\nEXTERN void canvas_deletelinesfor(t_canvas *x, t_text *text);\nEXTERN void canvas_stowconnections(t_canvas *x);\nEXTERN void canvas_restoreconnections(t_canvas *x);\nEXTERN void canvas_redraw(t_canvas *x);\nEXTERN void canvas_closebang(t_canvas *x);\nEXTERN void canvas_initbang(t_canvas *x);\n\nEXTERN t_inlet *canvas_addinlet(t_canvas *x, t_pd *who, t_symbol *sym);\nEXTERN void canvas_rminlet(t_canvas *x, t_inlet *ip);\nEXTERN t_outlet *canvas_addoutlet(t_canvas *x, t_pd *who, t_symbol *sym);\nEXTERN void canvas_rmoutlet(t_canvas *x, t_outlet *op);\nEXTERN void canvas_redrawallfortemplate(t_template *tmpl, int action);\nEXTERN void canvas_redrawallfortemplatecanvas(t_canvas *x, int action);\nEXTERN void canvas_setcurrent(t_canvas *x);\nEXTERN void canvas_unsetcurrent(t_canvas *x);\nEXTERN t_symbol *canvas_realizedollar(t_canvas *x, t_symbol *s);\nEXTERN t_canvas *canvas_getrootfor(t_canvas *x);\nEXTERN void canvas_dirty(t_canvas *x, t_floatarg n);\ntypedef int (*t_canvasapply)(t_canvas *x, t_int x1, t_int x2, t_int x3);\n\nEXTERN void canvas_resortinlets(t_canvas *x);\nEXTERN void canvas_resortoutlets(t_canvas *x);\nEXTERN void canvas_free(t_canvas *x);\nEXTERN void canvas_updatewindowlist(void);\nEXTERN void canvas_editmode(t_canvas *x, t_floatarg state);\nEXTERN int canvas_isabstraction(const t_canvas *x);\nEXTERN int canvas_istable(const t_canvas *x);\nEXTERN int canvas_showtext(const t_canvas *x);\nEXTERN void canvas_vis(t_canvas *x, t_floatarg f);\nEXTERN t_canvasenvironment *canvas_getenv(const t_canvas *x);\nEXTERN void canvas_rename(t_canvas *x, t_symbol *s, t_symbol *dir);\nEXTERN void canvas_loadbang(t_canvas *x);\nEXTERN int canvas_hitbox(t_canvas *x, t_gobj *y, int xpos, int ypos,\n    int *x1p, int *y1p, int *x2p, int *y2p);\nEXTERN int canvas_setdeleting(t_canvas *x, int flag);\n\n#define LB_LOAD 0       /* \"loadbang\" actions - 0 for original meaning */\n#define LB_INIT 1       /* loaded but not yet connected to parent patch */\n#define LB_CLOSE 2      /* about to close */\n\ntypedef int (*t_undofn)(t_canvas *canvas, void *buf,\n    int action);        /* a function that does UNDO/REDO */\n#define UNDO_FREE 0                     /* free current undo/redo buffer */\n#define UNDO_UNDO 1                     /* undo */\n#define UNDO_REDO 2                     /* redo */\nEXTERN void canvas_setundo(t_canvas *x, t_undofn undofn, void *buf,\n    const char *name);\nEXTERN void canvas_noundo(t_canvas *x);\nEXTERN int canvas_getindex(t_canvas *x, t_gobj *y);\n\nEXTERN void canvas_connect(t_canvas *x,\n    t_floatarg fwhoout, t_floatarg foutno, t_floatarg fwhoin, t_floatarg finno);\nEXTERN void canvas_disconnect(t_canvas *x,\n    t_float index1, t_float outno, t_float index2, t_float inno);\nEXTERN int canvas_isconnected (t_canvas *x,\n    t_text *ob1, int n1, t_text *ob2, int n2);\nEXTERN void canvas_selectinrect(t_canvas *x, int lox, int loy, int hix, int hiy);\n\nEXTERN t_glist *pd_checkglist(t_pd *x);\ntypedef int (*t_canvas_path_iterator)(const char *path, void *user_data);\nEXTERN int canvas_path_iterate(const t_canvas *x, t_canvas_path_iterator fun,\n    void *user_data);\n\n/* check string for untitled canvas filename prefix */\n#define UNTITLED_STRNCMP(s) strncmp(s, \"PDUNTITLED\", 10)\n\n/* ---- functions on canvasses as objects  --------------------- */\n\nEXTERN void linetraverser_start(t_linetraverser *t, t_canvas *x);\nEXTERN t_outconnect *linetraverser_next(t_linetraverser *t);\nEXTERN void linetraverser_skipobject(t_linetraverser *t);\n\n/* --------- functions on garrays (graphical arrays) -------------------- */\n\nEXTERN t_template *garray_template(t_garray *x);\n\n/* -------------------- arrays --------------------- */\n#define GRAPH_ARRAY_SAVE 1      /* flags for graph_array() below */\n#define GRAPH_ARRAY_PLOTSTYLE 6 /* 2-bit field, PLOTSTYLE_POINTS, etc */\n#define GRAPH_ARRAY_SAVESIZE 8  /* save size as well as contents */\n\nEXTERN t_garray *graph_array(t_glist *gl, t_symbol *s, t_symbol *tmpl,\n    t_floatarg f, t_floatarg flags);\nEXTERN t_array *array_new(t_symbol *templatesym, t_gpointer *parent);\nEXTERN void array_resize(t_array *x, int n);\nEXTERN void array_free(t_array *x);\nEXTERN void array_redraw(t_array *a, t_glist *glist);\nEXTERN void array_resize_and_redraw(t_array *array, t_glist *glist, int n);\n\n/* --------------------- gpointers and stubs ---------------- */\nEXTERN t_gstub *gstub_new(t_glist *gl, t_array *a);\nEXTERN void gstub_cutoff(t_gstub *gs);\nEXTERN void gpointer_setglist(t_gpointer *gp, t_glist *glist, t_scalar *x);\nEXTERN void gpointer_setarray(t_gpointer *gp, t_array *array, t_word *w);\n\n/* --------------------- scalars ------------------------- */\nEXTERN void word_init(t_word *wp, t_template *tmpl, t_gpointer *gp);\nEXTERN void word_restore(t_word *wp, t_template *tmpl,\n    int argc, t_atom *argv);\nEXTERN t_scalar *scalar_new(t_glist *owner,\n    t_symbol *templatesym);\nEXTERN void word_free(t_word *wp, t_template *tmpl);\nEXTERN void scalar_getbasexy(t_scalar *x, t_float *basex, t_float *basey);\nEXTERN void scalar_redraw(t_scalar *x, t_glist *glist);\nEXTERN void canvas_writescalar(t_symbol *templatesym, t_word *w, t_binbuf *b,\n    int amarrayelement);\nEXTERN int canvas_readscalar(t_glist *x, int natoms, t_atom *vec,\n    int *p_nextmsg, int selectit);\n\n/* ------helper routines for \"garrays\" and \"plots\" -------------- */\nEXTERN void array_getcoordinate(t_glist *glist,\n    char *elem, int xonset, int yonset, int wonset, int indx,\n    t_float basex, t_float basey, t_float xinc,\n    t_fielddesc *xfielddesc, t_fielddesc *yfielddesc, t_fielddesc *wfielddesc,\n    t_float *xp, t_float *yp, t_float *wp);\n\nEXTERN int array_getfields(t_symbol *elemtemplatesym,\n    t_canvas **elemtemplatecanvasp,\n    t_template **elemtemplatep, int *elemsizep,\n    t_fielddesc *xfielddesc, t_fielddesc *yfielddesc, t_fielddesc *wfielddesc,\n    int *xonsetp, int *yonsetp, int *wonsetp);\n\n/* --------------------- templates ------------------------- */\nEXTERN t_template *template_new(t_symbol *sym, int argc, t_atom *argv);\nEXTERN void template_free(t_template *x);\nEXTERN int template_match(t_template *x1, t_template *x2);\nEXTERN int template_find_field(t_template *x, t_symbol *name, int *p_onset,\n    int *p_type, t_symbol **p_arraytype);\nEXTERN t_float template_getfloat(t_template *x, t_symbol *fieldname, t_word *wp,\n    int loud);\nEXTERN void template_setfloat(t_template *x, t_symbol *fieldname, t_word *wp,\n    t_float f, int loud);\nEXTERN t_symbol *template_getsymbol(t_template *x, t_symbol *fieldname,\n    t_word *wp, int loud);\nEXTERN void template_setsymbol(t_template *x, t_symbol *fieldname,\n    t_word *wp, t_symbol *s, int loud);\n\nEXTERN t_template *gtemplate_get(t_gtemplate *x);\nEXTERN t_template *template_findbyname(t_symbol *s);\nEXTERN t_canvas *template_findcanvas(t_template *tmpl);\nEXTERN void template_notify(t_template *tmpl,\n    t_symbol *s, int argc, t_atom *argv);\n\nEXTERN t_float fielddesc_getcoord(t_fielddesc *f, t_template *tmpl,\n    t_word *wp, int loud);\nEXTERN void fielddesc_setcoord(t_fielddesc *f, t_template *tmpl,\n    t_word *wp, t_float pix, int loud);\nEXTERN t_float fielddesc_cvttocoord(t_fielddesc *f, t_float val);\nEXTERN t_float fielddesc_cvtfromcoord(t_fielddesc *f, t_float coord);\n\n\n/* ----------------------- guiconnects, g_guiconnect.c --------- */\nEXTERN t_guiconnect *guiconnect_new(t_pd *who, t_symbol *sym);\nEXTERN void guiconnect_notarget(t_guiconnect *x, double timedelay);\n\n/* ------------- IEMGUI routines used in other g_ files ---------------- */\nEXTERN t_symbol *iemgui_raute2dollar(t_symbol *s);\nEXTERN t_symbol *iemgui_dollar2raute(t_symbol *s);\nEXTERN t_symbol *iemgui_put_in_braces(t_symbol *s);\n\n/*-------------  g_clone.c ------------- */\nEXTERN t_class *clone_class;\n\n/*-------------  d_ugen.c ------------- */\nEXTERN void signal_setborrowed(t_signal *sig, t_signal *sig2);\nEXTERN void signal_makereusable(t_signal *sig);\n\n\n#if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus)\n}\n#endif\n\n#endif // G_CANVAS_H\n"
  },
  {
    "path": "libs/libpd/pure-data/src/g_clone.c",
    "content": "#include \"m_pd.h\"\n#include \"g_canvas.h\"\n#include \"m_imp.h\"\n#include <string.h>\n\n/* ---------- clone - maintain copies of a patch ----------------- */\n\n#include \"m_private_utils.h\"\n#define LIST_NGETBYTE 100 /* bigger that this we use alloc, not alloca */\n\nt_class *clone_class;\nstatic t_class *clone_in_class, *clone_out_class;\n\ntypedef struct _outproxy\n{\n    t_class *o_pd;\n    t_outlet *o_outlet;\n    int o_n;\n} t_outproxy;\n\ntypedef struct _copy\n{\n    t_glist *c_gl;\n    t_outproxy *c_vec;\n} t_copy;\n\ntypedef struct _in\n{\n    t_class *i_pd;\n    struct _clone *i_owner;\n    int i_signal;\n    int i_n;\n} t_in;\n\ntypedef struct _out\n{\n    t_outlet *o_outlet;\n    int o_signal;\n} t_out;\n\n\ntypedef struct _clone\n{\n    t_object x_obj;\n    t_canvas *x_canvas; /* owning canvas */\n    int x_n;            /* number of copies */\n    t_copy *x_vec;      /* the copies */\n    int x_nin;\n    t_in *x_invec;      /* inlet proxies */\n    int x_nout;\n    t_out *x_outvec;    /* outlets */\n    t_symbol *x_s;      /* name of abstraction */\n    int x_argc;         /* creation arguments for abstractions */\n    t_atom *x_argv;\n    int x_phase;        /* phase for round-robin input message forwarding */\n    int x_startvoice;   /* number of first voice, 0 by default */\n    unsigned int x_suppressvoice:1; /* suppress voice number as $1 arg */\n    unsigned int x_distributein:1;  /* distribute input signals across clones */\n    unsigned int x_packout:1;       /* pack output signals */\n} t_clone;\n\nint clone_match(t_pd *z, t_symbol *name, t_symbol *dir)\n{\n    t_clone *x = (t_clone *)z;\n    if (!x->x_n)\n        return (0);\n    return (x->x_vec[0].c_gl->gl_name == name &&\n        canvas_getdir(x->x_vec[0].c_gl) == dir);\n}\n\nvoid obj_sendinlet(t_object *x, int n, t_symbol *s, int argc, t_atom *argv);\n\nstatic void clone_in_list(t_in *x, t_symbol *s, int argc, t_atom *argv)\n{\n    int n;\n    if (!x->i_owner->x_nin)\n        return;\n    if (argc < 1 || argv[0].a_type != A_FLOAT)\n        pd_error(x->i_owner, \"clone: no instance number in message\");\n    else if ((n = argv[0].a_w.w_float - x->i_owner->x_startvoice) < 0 ||\n        n >= x->i_owner->x_n)\n            pd_error(x->i_owner, \"clone: instance number %d out of range\",\n                n + x->i_owner->x_startvoice);\n    else if (argc > 1 && argv[1].a_type == A_SYMBOL)\n        obj_sendinlet(&x->i_owner->x_vec[n].c_gl->gl_obj, x->i_n,\n            argv[1].a_w.w_symbol, argc-2, argv+2);\n    else obj_sendinlet(&x->i_owner->x_vec[n].c_gl->gl_obj, x->i_n,\n            &s_list, argc-1, argv+1);\n}\n\nstatic void clone_in_this(t_in *x, t_symbol *s, int argc, t_atom *argv)\n{\n    int phase = x->i_owner->x_phase;\n    if (phase < 0 || phase >= x->i_owner->x_n)\n        phase = 0;\n    if (argc <= 0 || !x->i_owner->x_nin)\n        return;\n    if (argv->a_type == A_SYMBOL)\n        obj_sendinlet(&x->i_owner->x_vec[phase].c_gl->gl_obj, x->i_n,\n            argv[0].a_w.w_symbol, argc-1, argv+1);\n    else obj_sendinlet(&x->i_owner->x_vec[phase].c_gl->gl_obj, x->i_n,\n            &s_list, argc, argv);\n}\n\nstatic void clone_in_next(t_in *x, t_symbol *s, int argc, t_atom *argv)\n{\n    int phase = x->i_owner->x_phase + 1;\n    if (phase < 0 || phase >= x->i_owner->x_n)\n        phase = 0;\n    x->i_owner->x_phase = phase;\n    clone_in_this(x, s, argc, argv);\n}\n\nstatic void clone_in_set(t_in *x, t_floatarg f)\n{\n    int phase = f;\n    if (phase < 0 || phase >= x->i_owner->x_n)\n        phase = 0;\n    x->i_owner->x_phase = phase;\n}\n\nstatic void clone_in_all(t_in *x, t_symbol *s, int argc, t_atom *argv)\n{\n    int phasewas = x->i_owner->x_phase, i;\n    for (i = 0; i < x->i_owner->x_n; i++)\n    {\n        x->i_owner->x_phase = i;\n        clone_in_this(x, s, argc, argv);\n    }\n    x->i_owner->x_phase = phasewas;\n}\n\nstatic void clone_in_vis(t_in *x, t_floatarg fn, t_floatarg vis)\n{\n    int n = fn - x->i_owner->x_startvoice;\n    if (n < 0)\n        n = 0;\n    else if (n >= x->i_owner->x_n)\n        n = x->i_owner->x_n - 1;\n    canvas_vis(x->i_owner->x_vec[n].c_gl, (vis != 0));\n}\n\nstatic void clone_in_fwd(t_in *x, t_symbol *s, int argc, t_atom *argv)\n{\n    if (argc > 0 && argv->a_type == A_SYMBOL)\n        typedmess(&x->i_pd, argv->a_w.w_symbol, argc-1, argv+1);\n}\n\nstatic void clone_setn(t_clone *, t_floatarg);\n\nstatic void clone_in_resize(t_in *x, t_floatarg f)\n{\n    int i, oldn = x->i_owner->x_n;\n        /* We need to send closebangs to old instances. Currently,\n        this is done in clone_freeinstance(), but later we would\n        rather do it here, see comment in clone_loadbang() */\n    canvas_setcurrent(x->i_owner->x_canvas);\n    clone_setn(x->i_owner, f);\n    canvas_unsetcurrent(x->i_owner->x_canvas);\n        /* send loadbangs to new instances */\n    for (i = oldn; i < x->i_owner->x_n; i++)\n        canvas_loadbang(x->i_owner->x_vec[i].c_gl);\n}\n\nstatic void clone_out_anything(t_outproxy *x, t_symbol *s, int argc, t_atom *argv)\n{\n    t_atom *outv;\n    int first =\n        1 + (s != &s_list && s != &s_float && s != &s_symbol && s != &s_bang),\n            outc = argc + first;\n    ALLOCA(t_atom, outv, outc, LIST_NGETBYTE);\n    SETFLOAT(outv, x->o_n);\n    if (first == 2)\n        SETSYMBOL(outv + 1, s);\n    memcpy(outv+first, argv, sizeof(t_atom) * argc);\n    outlet_list(x->o_outlet, 0, outc, outv);\n    FREEA(t_atom, outv, outc, LIST_NGETBYTE);\n}\n\nstatic PERTHREAD int clone_voicetovis = -1;\n\nstatic t_canvas *clone_makeone(t_symbol *s, int argc, t_atom *argv)\n{\n    t_canvas *retval;\n    pd_this->pd_newest = 0;\n    typedmess(&pd_objectmaker, s, argc, argv);\n    if (pd_this->pd_newest == 0)\n    {\n        pd_error(0, \"clone: can't create subpatch '%s'\",\n            s->s_name);\n        return (0);\n    }\n    if (*pd_this->pd_newest != canvas_class)\n    {\n        pd_error(0, \"clone: can't clone '%s' because it's not an abstraction\",\n            s->s_name);\n        pd_free(pd_this->pd_newest);\n        pd_this->pd_newest = 0;\n        return (0);\n    }\n    retval = (t_canvas *)pd_this->pd_newest;\n    pd_this->pd_newest = 0;\n    retval->gl_isclone = 1;\n    return (retval);\n}\n\nstatic void clone_initinstance(t_clone *x, int which, t_canvas *c)\n{\n    t_outproxy *outvec;\n    int i;\n    x->x_vec[which].c_gl = c;\n    x->x_vec[which].c_vec = outvec =\n        (t_outproxy *)getbytes(x->x_nout * sizeof(*outvec));\n    for (i = 0; i < x->x_nout; i++)\n    {\n        outvec[i].o_pd = clone_out_class;\n        outvec[i].o_n = x->x_startvoice + which;\n        outvec[i].o_outlet = x->x_outvec[i].o_outlet;\n        obj_connect(&x->x_vec[which].c_gl->gl_obj, i,\n            (t_object *)(&outvec[i]), 0);\n    }\n}\n\nstatic void clone_freeinstance(t_clone *x, int which)\n{\n    t_copy *c = &x->x_vec[which];\n        /* see comment in clone_loadbang() */\n    canvas_closebang(c->c_gl);\n    pd_free(&c->c_gl->gl_pd);\n    t_freebytes(c->c_vec, x->x_nout * sizeof(*c->c_vec));\n}\n\nstatic void clone_setn(t_clone *x, t_floatarg f)\n{\n    int dspstate = canvas_suspend_dsp();\n    int nwas = x->x_n, wantn = f, i, j;\n    if (!nwas)\n    {\n        pd_error(x, \"clone: no abstraction\");\n        return;\n    }\n    if (wantn < 1)\n    {\n        pd_error(x, \"clone: can't resize to zero or negative number; setting to 1\");\n        wantn = 1;\n    }\n    if (wantn > nwas)\n        for (i = nwas; i < wantn; i++)\n    {\n        t_canvas *c;\n        SETFLOAT(x->x_argv, x->x_startvoice + i);\n        if (!(c = clone_makeone(x->x_s, x->x_argc - x->x_suppressvoice,\n            x->x_argv + x->x_suppressvoice)))\n        {\n            pd_error(x, \"clone: couldn't create '%s'\", x->x_s->s_name);\n            goto done;\n        }\n        x->x_vec = (t_copy *)t_resizebytes(x->x_vec, i * sizeof(t_copy),\n            (i+1) * sizeof(t_copy));\n        x->x_n++;\n        clone_initinstance(x, i, c);\n    }\n    else if (wantn < nwas)\n    {\n        for (i = wantn; i < nwas; i++)\n            clone_freeinstance(x, i);\n        x->x_vec = (t_copy *)t_resizebytes(x->x_vec, nwas * sizeof(t_copy),\n            wantn * sizeof(t_copy));\n        x->x_n = wantn;\n    }\ndone:\n    canvas_resume_dsp(dspstate);\n}\n\nstatic void clone_click(t_clone *x, t_floatarg xpos, t_floatarg ypos,\n    t_floatarg shift, t_floatarg ctrl, t_floatarg alt)\n{\n    if (!x->x_n)\n        return;\n    canvas_vis(x->x_vec[0].c_gl, 1);\n}\n\nstatic void clone_loadbang(t_clone *x, t_floatarg f)\n{\n    int i;\n    if (f == LB_LOAD)\n        for (i = 0; i < x->x_n; i++)\n            canvas_loadbang(x->x_vec[i].c_gl);\n#if 0\n        /* Pd currently does not send closebangs to objects on a root canvas\n        or when an object is retexted. There is a pending PR to fix this, but\n        in the meantime we just send the closebang in clone_freeinstance(). */\n    else if (f == LB_CLOSE)\n        for (i = 0; i < x->x_n; i++)\n            canvas_closebang(x->x_vec[i].c_gl);\n#endif\n}\n\nvoid canvas_dodsp(t_canvas *x, int toplevel, t_signal **sp);\nt_signal *signal_newfromcontext(int borrowed, int nchans);\nvoid signal_makereusable(t_signal *sig);\n\nstatic void clone_dsp(t_clone *x, t_signal **sp)\n{\n    int i, j, nin, nout, *noutchans;\n    t_signal **tempio;\n    if (!x->x_n)\n        return;\n    for (i = nin = 0; i < x->x_nin; i++)\n        if (x->x_invec[i].i_signal)\n            nin++;\n    for (i = nout = 0; i < x->x_nout; i++)\n        if (x->x_outvec[i].o_signal)\n            nout++;\n    for (j = 0; j < x->x_n; j++)\n    {\n        if (obj_ninlets(&x->x_vec[j].c_gl->gl_obj) != x->x_nin ||\n            obj_noutlets(&x->x_vec[j].c_gl->gl_obj) != x->x_nout ||\n                obj_nsiginlets(&x->x_vec[j].c_gl->gl_obj) != nin ||\n                    obj_nsigoutlets(&x->x_vec[j].c_gl->gl_obj) != nout)\n        {\n            pd_error(x, \"clone: can't do DSP until edited copy is saved\");\n            for (i = 0; i < nout; i++)\n            {\n                    /* create dummy output signals */\n                signal_setmultiout(&sp[nin+i], x->x_packout ? x->x_n : 1);\n                dsp_add_zero(sp[nin+i]->s_vec,\n                    sp[nin+i]->s_length * sp[nin+i]->s_nchans);\n            }\n            return;\n        }\n    }\n    tempio = (nin + nout) > 0 ?\n        (t_signal **)alloca((nin + nout) * sizeof(*tempio)) : 0;\n    noutchans = nout > 0 ? (int *)alloca(nout * sizeof(*noutchans)) : 0;\n        /* load input signals into signal vector to send subpatches */\n    if (x->x_packout)   /* pack individual mono outputs to a multichannel one */\n    {\n        for (j = 0; j < x->x_n; j++)\n        {\n            for (i = 0; i < nin; i++)\n            {\n                if (x->x_distributein)\n                {\n                        /* distribute multi-channel signal over instances;\n                        wrap around if channel count is lower than instance\n                        count */\n                    int offset = j % sp[i]->s_nchans;\n                    tempio[i] = signal_new(0, 1, sp[i]->s_sr, 0);\n                    signal_setborrowed(tempio[i], sp[i]);\n                    tempio[i]->s_nchans = 1;\n                    tempio[i]->s_vec = sp[i]->s_vec + offset * sp[i]->s_length;\n                    tempio[i]->s_refcount = 1;\n                }\n                else\n                    tempio[i] = sp[i];\n            }\n            for (i = 0; i < nout; i++)\n                tempio[nin + i] = signal_newfromcontext(1, 1);\n            canvas_dodsp(x->x_vec[j].c_gl, 0, tempio);\n            if (x->x_distributein)\n            {\n                for (i = 0; i < nin; i++)\n                {\n                    if (!--tempio[i]->s_refcount)\n                        signal_makereusable(tempio[i]);\n                    else\n                        bug(\"clone 1: %d\", tempio[i]->s_refcount);\n                }\n            }\n            for (i = 0; i < nout; i++)\n            {\n                int nchans = tempio[nin + i]->s_nchans;\n                int length = tempio[nin + i]->s_length;\n                t_sample *to, *from = tempio[nin + i]->s_vec;\n                if (j == 0) /* now we can the create the output signal */\n                {\n                    signal_setmultiout(&sp[nin + i], nchans * x->x_n);\n                    noutchans[i] = nchans;\n                }\n                    /* NB: it is possible for instances to have different\n                    output channel counts. In this case we always take the\n                    channel count of the first instance. */\n                to = sp[nin + i]->s_vec + j * length * noutchans[i];\n                if (nchans == noutchans[i])\n                    dsp_add_copy(from, to, length * nchans);\n                else\n                {\n                    if (nchans > noutchans[i]) /* ignore extra channels */\n                        dsp_add_copy(from, to, noutchans[i] * length);\n                    else /* fill missing channels with zeros */\n                    {\n                        dsp_add_copy(from, to, nchans * length);\n                        dsp_add_zero(to + length * nchans,\n                            length * (noutchans[i] - nchans));\n                    }\n                #if 1\n                    pd_error(x, \"warning: clone instance %d: channel count \"\n                        \"of outlet %d (%d) does not match first instance (%d)\",\n                            j, i, nchans, noutchans[i]);\n                #endif\n                }\n                signal_makereusable(tempio[nin + i]);\n            }\n        }\n    }\n    else    /* otherwise add the individual outputs */\n    {\n        for (j = 0; j < x->x_n; j++)\n        {\n            for (i = 0; i < nin; i++)\n            {\n                if (x->x_distributein)\n                {\n                        /* distribute multi-channel signal over instances;\n                        wrap around if channel count is lower than instance count */\n                    int offset = j % sp[i]->s_nchans;\n                    tempio[i] = signal_new(0, 1, sp[i]->s_sr, 0);\n                    signal_setborrowed(tempio[i], sp[i]);\n                    tempio[i]->s_nchans = 1;\n                    tempio[i]->s_vec = sp[i]->s_vec + offset * sp[i]->s_length;\n                    tempio[i]->s_refcount = 1;\n                }\n                else\n                    tempio[i] = sp[i];\n            }\n            for (i = 0; i < nout; i++)\n                tempio[nin + i] = signal_newfromcontext(1, 1);\n            canvas_dodsp(x->x_vec[j].c_gl, 0, tempio);\n            if (x->x_distributein)\n            {\n                for (i = 0; i < nin; i++)\n                {\n                    if (!--tempio[i]->s_refcount)\n                        signal_makereusable(tempio[i]);\n                    else\n                        bug(\"clone 2: %d\", tempio[i]->s_refcount);\n                }\n            }\n            for (i = 0; i < nout; i++)\n            {\n                int nchans = tempio[nin + i]->s_nchans;\n                int length = tempio[nin + i]->s_length;\n                if (j == 0)\n                {\n                        /* first instance: create output signal and copy content */\n                    signal_setmultiout(&sp[nin + i], nchans);\n                    dsp_add_copy(tempio[nin + i]->s_vec,\n                        sp[nin + i]->s_vec, length * nchans);\n                    noutchans[i] = nchans;\n                }\n                else /* add to existing signal */\n                {\n                    int nsamples = nchans > noutchans[i] ?\n                        noutchans[i] * length : nchans * length;\n                #if 1\n                    if (nchans != noutchans[i])\n                        pd_error(x, \"warning: clone instance %d: channel count \"\n                            \"of outlet %d (%d) does not match first instance (%d)\",\n                                j, i, nchans, noutchans[i]);\n                #endif\n                    dsp_add_plus(tempio[nin + i]->s_vec, sp[nin + i]->s_vec,\n                        sp[nin + i]->s_vec, nsamples);\n                }\n                signal_makereusable(tempio[nin + i]);\n            }\n        }\n    }\n    for (i = 0; i < nin; i++)\n    {\n        if (sp[i]->s_refcount <= 0)\n            bug(\"clone 3 %d\", sp[i]->s_refcount);\n    }\n}\n\nstatic void *clone_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_clone *x = (t_clone *)pd_new(clone_class);\n    t_canvas *c;\n    int wantn, dspstate, i, voicetovis = clone_voicetovis;\n    x->x_canvas = canvas_getcurrent();\n    x->x_invec = 0;\n    x->x_outvec = 0;\n    x->x_argv = 0;\n    x->x_startvoice = 0;\n    x->x_suppressvoice = 0;\n    x->x_distributein = 0;\n    x->x_packout = 0;\n    clone_voicetovis = -1;\n    if (argc == 0)\n    {\n        x->x_vec = 0;\n        x->x_n = 0;\n        return (x);\n    }\n    dspstate = canvas_suspend_dsp();\n    while (argc > 0 && argv[0].a_type == A_SYMBOL &&\n        argv[0].a_w.w_symbol->s_name[0] == '-')\n    {\n        if (!strcmp(argv[0].a_w.w_symbol->s_name, \"-s\") && argc > 1 &&\n            argv[1].a_type == A_FLOAT)\n        {\n            x->x_startvoice = argv[1].a_w.w_float;\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(argv[0].a_w.w_symbol->s_name, \"-x\"))\n            x->x_suppressvoice = 1, argc--, argv++;\n        else if (!strcmp(argv[0].a_w.w_symbol->s_name, \"-d\"))\n            x->x_distributein = x->x_packout = 1, argc--, argv++;\n        else if (!strcmp(argv[0].a_w.w_symbol->s_name, \"-di\"))\n            x->x_distributein = 1, argc--, argv++;\n        else if (!strcmp(argv[0].a_w.w_symbol->s_name, \"-do\"))\n            x->x_packout = 1, argc--, argv++;\n        else goto usage;\n    }\n    if (argc >= 2 && (wantn = atom_getfloatarg(0, argc, argv)) >= 0\n        && argv[1].a_type == A_SYMBOL)\n            x->x_s = argv[1].a_w.w_symbol;\n    else if (argc >= 2 && (wantn = atom_getfloatarg(1, argc, argv)) >= 0\n        && argv[0].a_type == A_SYMBOL)\n            x->x_s = argv[0].a_w.w_symbol;\n    else goto usage;\n        /* store a copy of the argmuents with an extra space (argc+1) for\n        supplying an instance number, which we'll bash as we go. */\n    x->x_argc = argc - 1;\n    x->x_argv = getbytes(x->x_argc * sizeof(*x->x_argv));\n    memcpy(x->x_argv, argv+1, x->x_argc * sizeof(*x->x_argv));\n    SETFLOAT(x->x_argv, x->x_startvoice);\n    if (!(c = clone_makeone(x->x_s, x->x_argc - x->x_suppressvoice,\n        x->x_argv + x->x_suppressvoice)))\n            goto fail;\n        /* inlets */\n    x->x_nin = obj_ninlets(&c->gl_obj);\n    if (x->x_nin > 0)\n    {\n        x->x_invec = (t_in *)getbytes(x->x_nin * sizeof(*x->x_invec));\n        for (i = 0; i < x->x_nin; i++)\n        {\n            x->x_invec[i].i_pd = clone_in_class;\n            x->x_invec[i].i_owner = x;\n            x->x_invec[i].i_signal =\n                obj_issignalinlet(&c->gl_obj, i);\n            x->x_invec[i].i_n = i;\n            if (x->x_invec[i].i_signal)\n                inlet_new(&x->x_obj, &x->x_invec[i].i_pd,\n                    &s_signal, &s_signal);\n            else inlet_new(&x->x_obj, &x->x_invec[i].i_pd, 0, 0);\n        }\n    }\n    else /* fake inlet to send messages like \"vis\" or \"resize\" */\n    {\n        x->x_invec = (t_in *)getbytes(sizeof(*x->x_invec));\n        x->x_invec->i_pd = clone_in_class;\n        x->x_invec->i_owner = x;\n        x->x_invec->i_signal = 0;\n        x->x_invec->i_n = 0;\n        inlet_new(&x->x_obj, &x->x_invec->i_pd, 0, 0);\n    }\n        /* outlets */\n    x->x_nout = obj_noutlets(&c->gl_obj);\n    x->x_outvec = (t_out *)getbytes(x->x_nout * sizeof(*x->x_outvec));\n    for (i = 0; i < x->x_nout; i++)\n    {\n        x->x_outvec[i].o_signal = obj_issignaloutlet(&c->gl_obj, i);\n        x->x_outvec[i].o_outlet =\n            outlet_new(&x->x_obj, (x->x_outvec[i].o_signal ? &s_signal : 0));\n    }\n        /* first copy */\n    x->x_vec = getbytes(sizeof(*x->x_vec));\n    x->x_n = 1;\n    clone_initinstance(x, 0, c);\n        /* remaining copies */\n    clone_setn(x, (t_floatarg)(wantn));\n    x->x_phase = wantn-1;\n    canvas_resume_dsp(dspstate);\n    if (voicetovis >= 0 && voicetovis < x->x_n)\n        canvas_vis(x->x_vec[voicetovis].c_gl, 1);\n    return (x);\nusage:\n    pd_error(0, \"usage: clone [-s starting-number] <number> <name> [arguments]\");\nfail:\n    if (x->x_argv)\n        freebytes(x->x_argv, sizeof(x->x_argc * sizeof(*x->x_argv)));\n    freebytes(x, sizeof(t_clone));\n    canvas_resume_dsp(dspstate);\n    return (0);\n}\n\nstatic void clone_free(t_clone *x)\n{\n    if (x->x_vec)\n    {\n        int i, voicetovis = -1;\n        if (THISGUI->i_reloadingabstraction)\n        {\n            for (i = 0; i < x->x_n; i++)\n                if (x->x_vec[i].c_gl == THISGUI->i_reloadingabstraction)\n                    voicetovis = i;\n        }\n        for (i = 0; i < x->x_n; i++)\n            clone_freeinstance(x, i);\n        t_freebytes(x->x_vec, x->x_n * sizeof(*x->x_vec));\n        t_freebytes(x->x_argv, x->x_argc * sizeof(*x->x_argv));\n        if (x->x_nin)\n            t_freebytes(x->x_invec, x->x_nin * sizeof(*x->x_invec));\n        else /* fake inlet */\n            t_freebytes(x->x_invec, sizeof(*x->x_invec));\n        t_freebytes(x->x_outvec, x->x_nout * sizeof(*x->x_outvec));\n        clone_voicetovis = voicetovis;\n    }\n}\n\nvoid clone_setup(void)\n{\n    clone_class = class_new(gensym(\"clone\"), (t_newmethod)clone_new,\n        (t_method)clone_free, sizeof(t_clone),\n            CLASS_NOINLET | CLASS_MULTICHANNEL, A_GIMME, 0);\n    class_addmethod(clone_class, (t_method)clone_click, gensym(\"click\"),\n        A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);\n    class_addmethod(clone_class, (t_method)clone_loadbang, gensym(\"loadbang\"),\n        A_FLOAT, 0);\n    class_addmethod(clone_class, (t_method)clone_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n\n    clone_in_class = class_new(gensym(\"clone-inlet\"), 0, 0,\n        sizeof(t_in), CLASS_PD, 0);\n    class_addmethod(clone_in_class, (t_method)clone_in_next, gensym(\"next\"),\n        A_GIMME, 0);\n    class_addmethod(clone_in_class, (t_method)clone_in_this, gensym(\"this\"),\n        A_GIMME, 0);\n    class_addmethod(clone_in_class, (t_method)clone_in_set, gensym(\"set\"),\n        A_FLOAT, 0);\n    class_addmethod(clone_in_class, (t_method)clone_in_all, gensym(\"all\"),\n        A_GIMME, 0);\n    class_addmethod(clone_in_class, (t_method)clone_in_vis, gensym(\"vis\"),\n        A_FLOAT, A_FLOAT, 0);\n    class_addmethod(clone_in_class, (t_method)clone_in_fwd, gensym(\"fwd\"),\n        A_GIMME, 0);\n    class_addmethod(clone_in_class, (t_method)clone_in_resize, gensym(\"resize\"),\n        A_FLOAT, 0);\n    class_addlist(clone_in_class, (t_method)clone_in_list);\n\n    clone_out_class = class_new(gensym(\"clone-outlet\"), 0, 0,\n        sizeof(t_in), CLASS_PD, 0);\n    class_addanything(clone_out_class, (t_method)clone_out_anything);\n}\n\n    /* for the needs of g_editor::glist_dofinderror(): */\n\nint clone_get_n(t_gobj *x)\n{\n    if (pd_class(&x->g_pd) != clone_class) return 0;\n    else return ((t_clone *)x)->x_n;\n}\n\nt_glist *clone_get_instance(t_gobj *x, int n)\n{\n    t_clone *c;\n\n    if (pd_class(&x->g_pd) != clone_class) return NULL;\n\n    c = (t_clone *)x;\n    n -= c->x_startvoice;\n    if (n < 0)\n        n = 0;\n    else if (n >= c->x_n)\n        n = c->x_n - 1;\n    return  c->x_vec[n].c_gl;\n}\n\n"
  },
  {
    "path": "libs/libpd/pure-data/src/g_editor.c",
    "content": "/* Copyright (c) 1997-2001 Miller Puckette and others.\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n#include <stdio.h>\n#include \"m_pd.h\"\n#include \"m_imp.h\"\n#include \"s_stuff.h\"\n#include \"g_canvas.h\"\n#include \"g_undo.h\"\n#include \"s_utf8.h\" /*-- moo --*/\n#include <string.h>\n#include \"m_private_utils.h\"\n\nstruct _instanceeditor\n{\n    t_binbuf *copy_binbuf;\n    char *canvas_textcopybuf;\n    int canvas_textcopybufsize;\n    t_undofn canvas_undo_fn;         /* current undo function if any */\n    int canvas_undo_whatnext;        /* whether we can now UNDO or REDO */\n    void *canvas_undo_buf;           /* data private to the undo function */\n    t_canvas *canvas_undo_canvas;    /* which canvas we can undo on */\n    const char *canvas_undo_name;\n    int canvas_undo_already_set_move;\n    double canvas_upclicktime;\n    int canvas_upx, canvas_upy;\n    int canvas_find_index, canvas_find_wholeword;\n    t_binbuf *canvas_findbuf;\n    int paste_onset;\n    t_canvas *paste_canvas;\n    t_glist *canvas_last_glist;\n    int canvas_last_glist_x, canvas_last_glist_y;\n    t_canvas *canvas_cursorcanvaswas;\n    unsigned int canvas_cursorwas;\n};\n\n/* positional offset for duplicated items */\n#define PASTE_OFFSET 10\n\nvoid glist_readfrombinbuf(t_glist *x, const t_binbuf *b, const char *filename,\n    int selectem);\n\n/* ------------------ forward declarations --------------- */\nstatic void canvas_doclear(t_canvas *x);\nstatic void glist_setlastxy(t_glist *gl, int xval, int yval);\nstatic void glist_donewloadbangs(t_glist *x);\nstatic t_binbuf *canvas_docopy(t_canvas *x);\nstatic void canvas_dopaste(t_canvas *x, t_binbuf *b);\nstatic void canvas_paste(t_canvas *x);\nstatic void canvas_clearline(t_canvas *x);\nstatic t_glist *glist_finddirty(t_glist *x);\nstatic void canvas_zoom(t_canvas *x, t_floatarg zoom);\nstatic void canvas_displaceselection(t_canvas *x, int dx, int dy);\nvoid canvas_setgraph(t_glist *x, int flag, int nogoprect);\n\n/* ------------------------ managing the selection ----------------- */\nvoid glist_deselectline(t_glist *x);\n\nstatic void _editor_selectlinecolor(t_glist*x, const char*color)\n{\n    char tag[128];\n    sprintf(tag, \"l%p\", x->gl_editor->e_selectline_tag);\n    pdgui_vmess(0, \"crs rs\",\n        x, \"itemconfigure\", tag,\n        \"-fill\", color);\n\n}\nvoid glist_selectline(t_glist *x, t_outconnect *oc, int index1,\n    int outno, int index2, int inno)\n{\n    if (x->gl_editor)\n    {\n        glist_deselectline(x);\n\n        x->gl_editor->e_selectedline = 1;\n        x->gl_editor->e_selectline_index1 = index1;\n        x->gl_editor->e_selectline_outno = outno;\n        x->gl_editor->e_selectline_index2 = index2;\n        x->gl_editor->e_selectline_inno = inno;\n        x->gl_editor->e_selectline_tag = oc;\n        _editor_selectlinecolor(x, \"blue\");\n    }\n}\n\nvoid glist_deselectline(t_glist *x)\n{\n    if (x->gl_editor)\n    {\n        x->gl_editor->e_selectedline = 0;\n        _editor_selectlinecolor(x, \"black\");\n    }\n}\n\nint glist_isselected(t_glist *x, t_gobj *y)\n{\n    if (x->gl_editor)\n    {\n        t_selection *sel;\n        for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next)\n            if (sel->sel_what == y) return (1);\n    }\n    return (0);\n}\n\n    /* call this for unselected objects only */\nvoid glist_select(t_glist *x, t_gobj *y)\n{\n    if (x->gl_editor)\n    {\n        t_selection *sel = (t_selection *)getbytes(sizeof(*sel));\n            /* LATER #ifdef out the following check */\n        if (glist_isselected(x, y)) bug(\"glist_select\");\n        sel->sel_next = x->gl_editor->e_selection;\n        sel->sel_what = y;\n        x->gl_editor->e_selection = sel;\n        gobj_select(y, x, 1);\n    }\n}\n\n    /* recursively deselect everything in a gobj \"g\", if it happens to be\n       a glist, in preparation for deselecting g itself in glist_dselect() */\nstatic void glist_checkanddeselectall(t_glist *gl, t_gobj *g)\n{\n    t_glist *gl2;\n    t_gobj *g2;\n    if (pd_class(&g->g_pd) != canvas_class)\n        return;\n    gl2 = (t_glist *)g;\n    for (g2 = gl2->gl_list; g2; g2 = g2->g_next)\n        glist_checkanddeselectall(gl2, g2);\n    glist_noselect(gl2);\n}\n\n    /* call this for selected objects only */\nvoid glist_deselect(t_glist *x, t_gobj *y)\n{\n    int fixdsp = 0;\n    t_selection *sel, *sel2;\n    t_rtext *z = 0;\n\n    if (!x->gl_editor)\n        return;\n\n    if (!glist_isselected(x, y)) bug(\"glist_deselect\");\n    if (x->gl_editor->e_textedfor)\n    {\n        t_rtext *fuddy = glist_findrtext(x, (t_text *)y);\n        if (x->gl_editor->e_textedfor == fuddy)\n        {\n            if (x->gl_editor->e_textdirty)\n            {\n                z = fuddy;\n                canvas_undo_add(x, UNDO_SEQUENCE_START, \"typing\", 0);\n                canvas_undo_add(x, UNDO_ARRANGE, \"arrange\",\n                    canvas_undo_set_arrange(x, y, 1));\n                canvas_stowconnections(glist_getcanvas(x));\n                glist_checkanddeselectall(x, y);\n            }\n            gobj_activate(y, x, 0);\n        }\n        if (zgetfn(&y->g_pd, gensym(\"dsp\")))\n            fixdsp = canvas_suspend_dsp();\n    }\n    if ((sel = x->gl_editor->e_selection)->sel_what == y)\n    {\n        x->gl_editor->e_selection = x->gl_editor->e_selection->sel_next;\n        gobj_select(sel->sel_what, x, 0);\n        freebytes(sel, sizeof(*sel));\n    }\n    else\n    {\n        for (sel = x->gl_editor->e_selection; (sel2 = sel->sel_next);\n             sel = sel2)\n        {\n            if (sel2->sel_what == y)\n            {\n                sel->sel_next = sel2->sel_next;\n                gobj_select(sel2->sel_what, x, 0);\n                freebytes(sel2, sizeof(*sel2));\n                break;\n            }\n        }\n    }\n    if (z)\n    {\n        char *buf;\n        int bufsize;\n\n        rtext_gettext(z, &buf, &bufsize);\n        text_setto((t_text *)y, x, buf, bufsize);\n        canvas_fixlinesfor(x, (t_text *)y);\n        x->gl_editor->e_textedfor = 0;\n        canvas_undo_add(x, UNDO_SEQUENCE_END, \"typing\", 0);\n    }\n    if (fixdsp)\n        canvas_resume_dsp(1);\n}\n\nvoid glist_noselect(t_glist *x)\n{\n    if (x->gl_editor)\n    {\n        while (x->gl_editor->e_selection)\n            glist_deselect(x, x->gl_editor->e_selection->sel_what);\n        if (x->gl_editor->e_selectedline)\n            glist_deselectline(x);\n    }\n}\n\nvoid glist_selectall(t_glist *x)\n{\n    if (x->gl_editor)\n    {\n        glist_noselect(x);\n        if (x->gl_list)\n        {\n            t_selection *sel = (t_selection *)getbytes(sizeof(*sel));\n            t_gobj *y = x->gl_list;\n            x->gl_editor->e_selection = sel;\n            sel->sel_what = y;\n            gobj_select(y, x, 1);\n            while ((y = y->g_next))\n            {\n                t_selection *sel2 = (t_selection *)getbytes(sizeof(*sel2));\n                sel->sel_next = sel2;\n                sel = sel2;\n                sel->sel_what = y;\n                gobj_select(y, x, 1);\n            }\n            sel->sel_next = 0;\n        }\n    }\n}\n\n    /* get the index of a gobj in a glist.  If y is zero, return the\n       total number of objects. */\nint glist_getindex(t_glist *x, t_gobj *y)\n{\n    t_gobj *y2;\n    int indx;\n\n    for (y2 = x->gl_list, indx = 0; y2 && y2 != y; y2 = y2->g_next)\n        indx++;\n    return (indx);\n}\n\n    /* get the index of the object, among selected items, if \"selected\"\n       is set; otherwise, among unselected ones.  If y is zero, just\n       counts the selected or unselected objects. */\nint glist_selectionindex(t_glist *x, t_gobj *y, int selected)\n{\n    t_gobj *y2;\n    int indx;\n\n    for (y2 = x->gl_list, indx = 0; y2 && y2 != y; y2 = y2->g_next)\n        if (selected == glist_isselected(x, y2))\n            indx++;\n    return (indx);\n}\n\nstatic t_gobj *glist_nth(t_glist *x, int n)\n{\n    t_gobj *y;\n    int indx;\n    for (y = x->gl_list, indx = 0; y; y = y->g_next, indx++)\n        if (indx == n)\n            return (y);\n    return (0);\n}\n\n/* ------------------- support for undo/redo  -------------------------- */\n\nstatic void canvas_applybinbuf(t_canvas *x, t_binbuf *b)\n{\n    t_symbol*asym = gensym(\"#A\");\n    t_pd *boundx = s__X.s_thing,\n        *bounda = asym->s_thing,\n        *boundn = s__N.s_thing;\n\n    asym->s_thing = 0;\n    s__X.s_thing = &x->gl_pd;\n    s__N.s_thing = &pd_canvasmaker;\n\n    binbuf_eval(b, 0, 0, 0);\n\n    asym->s_thing = bounda;\n    s__X.s_thing = boundx;\n    s__N.s_thing = boundn;\n}\n\n\nstatic int canvas_undo_confirmdiscard(t_gobj *g)\n{\n    t_glist *gl2;\n\n    if (pd_class(&g->g_pd) == canvas_class &&\n        canvas_isabstraction((t_glist *)g) &&\n        (gl2 = glist_finddirty((t_glist *)g)))\n    {\n        t_canvas*c = canvas_getrootfor(gl2);\n        const char *msg[]= {\"Discard changes to '%s'?\", c->gl_name->s_name};\n        char buf[80];\n        t_atom backmsg[2];\n        sprintf(buf, \".x%lx\", gl2);\n        SETSYMBOL(backmsg+0, gensym(\"dirty\"));\n        SETFLOAT (backmsg+1, 0);\n        vmess(&gl2->gl_pd, gensym(\"menu-open\"), \"\");\n        pdgui_vmess(\"pdtk_check\", \"^ Sms\",\n            c,\n            2, msg,\n            gensym(buf), 2, backmsg,\n            \"no\");\n\n        return 1;\n    }\n    return 0;\n}\n\nvoid canvas_undo_set_name(const char*name)\n{\n    EDITOR->canvas_undo_name = name;\n}\n\nvoid canvas_setundo(t_canvas *x, t_undofn undofn, void *buf,\n    const char *name)\n{\n    int hadone = 0;\n        /* blow away the old undo information.  In one special case the\n           old undo info is re-used; if so we shouldn't free it here. */\n    if (EDITOR->canvas_undo_fn && EDITOR->canvas_undo_buf && (buf != EDITOR->canvas_undo_buf))\n    {\n        (*EDITOR->canvas_undo_fn)(EDITOR->canvas_undo_canvas, EDITOR->canvas_undo_buf, UNDO_FREE);\n        hadone = 1;\n    }\n    EDITOR->canvas_undo_canvas = x;\n    EDITOR->canvas_undo_fn = undofn;\n    EDITOR->canvas_undo_buf = buf;\n    EDITOR->canvas_undo_whatnext = UNDO_UNDO;\n    EDITOR->canvas_undo_name = name;\n    if (x && glist_isvisible(x) && glist_istoplevel(x))\n            /* enable undo in menu */\n        pdgui_vmess(\"pdtk_undomenu\", \"^ss\", x, name, \"no\");\n    else if (hadone)\n        pdgui_vmess(\"pdtk_undomenu\", \"rss\", \"nobody\", \"no\", \"no\");\n}\n\n    /* clear undo if it happens to be for the canvas x.\n       (but if x is 0, clear it regardless of who owns it.) */\nvoid canvas_noundo(t_canvas *x)\n{\n    if (!x || (x == EDITOR->canvas_undo_canvas))\n        canvas_setundo(0, 0, 0, \"foo\");\n}\n\nstatic void canvas_undo(t_canvas *x)\n{\n    int dspwas = canvas_suspend_dsp();\n    if (x != EDITOR->canvas_undo_canvas)\n        bug(\"canvas_undo 1\");\n    else if (EDITOR->canvas_undo_whatnext != UNDO_UNDO)\n        bug(\"canvas_undo 2\");\n    else\n    {\n#if 0\n        post(\"undo\");\n#endif\n        (*EDITOR->canvas_undo_fn)(EDITOR->canvas_undo_canvas,\n            EDITOR->canvas_undo_buf, UNDO_UNDO);\n            /* enable redo in menu */\n        if (glist_isvisible(x) && glist_istoplevel(x))\n            pdgui_vmess(\"pdtk_undomenu\", \"^ss\", x, \"no\", EDITOR->canvas_undo_name);\n        EDITOR->canvas_undo_whatnext = UNDO_REDO;\n    }\n    canvas_resume_dsp(dspwas);\n}\n\nstatic void canvas_redo(t_canvas *x)\n{\n    int dspwas = canvas_suspend_dsp();\n    if (x != EDITOR->canvas_undo_canvas)\n        bug(\"canvas_undo 1\");\n    else if (EDITOR->canvas_undo_whatnext != UNDO_REDO)\n        bug(\"canvas_undo 2\");\n    else\n    {\n#if 0\n        post(\"redo\");\n#endif\n        (*EDITOR->canvas_undo_fn)(EDITOR->canvas_undo_canvas,\n            EDITOR->canvas_undo_buf, UNDO_REDO);\n            /* enable undo in menu */\n        if (glist_isvisible(x) && glist_istoplevel(x))\n            pdgui_vmess(\"pdtk_undomenu\", \"^ss\", x, EDITOR->canvas_undo_name, \"no\");\n        EDITOR->canvas_undo_whatnext = UNDO_UNDO;\n    }\n    canvas_resume_dsp(dspwas);\n}\n\n/* ------- specific undo methods: 1. connect -------- */\ntypedef struct _undo_connect\n{\n    int u_index1;\n    int u_outletno;\n    int u_index2;\n    int u_inletno;\n} t_undo_connect;\n\nvoid *canvas_undo_set_disconnect(t_canvas *x,\n    int index1, int outno, int index2, int inno);\n\n/* connect just calls disconnect actions backward... (see below) */\nvoid *canvas_undo_set_connect(t_canvas *x,\n    int index1, int outno, int index2, int inno)\n{\n    return (canvas_undo_set_disconnect(x, index1, outno, index2, inno));\n}\n\nint canvas_undo_connect(t_canvas *x, void *z, int action)\n{\n    int myaction;\n    if (action == UNDO_UNDO)\n        myaction = UNDO_REDO;\n    else if (action == UNDO_REDO)\n        myaction = UNDO_UNDO;\n    else myaction = action;\n    canvas_undo_disconnect(x, z, myaction);\n    return 1;\n}\n\nstatic void canvas_connect_with_undo(t_canvas *x,\n    t_float index1, t_float outno, t_float index2, t_float inno)\n{\n    canvas_connect(x, index1, outno, index2, inno);\n    canvas_undo_add(x, UNDO_CONNECT, \"connect\", canvas_undo_set_connect(x,\n        index1, outno, index2, inno));\n}\n\n/* ------- specific undo methods: 2. disconnect -------- */\n\nvoid *canvas_undo_set_disconnect(t_canvas *x,\n    int index1, int outno, int index2, int inno)\n{\n    t_undo_connect *buf = (t_undo_connect *)getbytes(sizeof(*buf));\n    buf->u_index1 = index1;\n    buf->u_outletno = outno;\n    buf->u_index2 = index2;\n    buf->u_inletno = inno;\n    return (buf);\n}\n\nvoid canvas_disconnect(t_canvas *x,\n    t_float index1, t_float outno, t_float index2, t_float inno)\n{\n    t_linetraverser t;\n    t_outconnect *oc;\n    linetraverser_start(&t, x);\n    while ((oc = linetraverser_next(&t)))\n    {\n        int srcno = canvas_getindex(x, &t.tr_ob->ob_g);\n        int sinkno = canvas_getindex(x, &t.tr_ob2->ob_g);\n        if (srcno == index1 && t.tr_outno == outno &&\n            sinkno == index2 && t.tr_inno == inno)\n        {\n            if (glist_isvisible(x) && x->gl_havewindow)\n            {\n                char tag[128];\n                sprintf(tag, \"l%p\", oc);\n                pdgui_vmess(0, \"crs\", x, \"delete\", tag);\n            }\n            obj_disconnect(t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno);\n            break;\n        }\n    }\n}\n\nint canvas_undo_disconnect(t_canvas *x, void *z, int action)\n{\n    t_undo_connect *buf = z;\n    if (action == UNDO_UNDO)\n    {\n        canvas_connect(x, buf->u_index1, buf->u_outletno,\n            buf->u_index2, buf->u_inletno);\n    }\n    else if (action == UNDO_REDO)\n    {\n        canvas_disconnect(x, buf->u_index1, buf->u_outletno,\n            buf->u_index2, buf->u_inletno);\n    }\n    else if (action == UNDO_FREE)\n        t_freebytes(buf, sizeof(*buf));\n    return 1;\n}\n\nstatic void canvas_disconnect_with_undo(t_canvas *x,\n    t_float index1, t_float outno, t_float index2, t_float inno)\n{\n    canvas_disconnect(x, index1, outno, index2, inno);\n    canvas_undo_add(x, UNDO_DISCONNECT, \"disconnect\", canvas_undo_set_disconnect(x,\n        index1, outno, index2, inno));\n}\n\n/* ---------- ... 3. cut, clear, and typing into objects: -------- */\n\n#define UCUT_CUT 1          /* operation was a cut */\n#define UCUT_CLEAR 2        /* .. a clear */\n\n/* following action is not needed any more LATER remove any signs of UCUT_TEXT\n * since recreate takes care of this in a more elegant way\n */\n#define UCUT_TEXT 3         /* text typed into a box */\n\ntypedef struct _undo_cut\n{\n    t_binbuf *u_objectbuf;      /* the object cleared or typed into */\n    t_binbuf *u_reconnectbuf;   /* connections into and out of object */\n    t_binbuf *u_redotextbuf;    /* buffer to paste back for redo if TEXT */\n    int u_mode;                 /* from flags above */\n    int n_obj;                  /* number of selected objects to be cut */\n    int p_a[1];    /* array of original glist positions of selected objects.\n                      At least one object is selected, we dynamically resize\n                      it later */\n} t_undo_cut;\n\nvoid *canvas_undo_set_cut(t_canvas *x, int mode)\n{\n    t_undo_cut *buf;\n    t_linetraverser t;\n    t_outconnect *oc;\n    int nnotsel= glist_selectionindex(x, 0, 0);\n    int nsel = glist_selectionindex(x, 0, 1);\n    buf = (t_undo_cut *)getbytes(sizeof(*buf) +\n        sizeof(buf->p_a[0]) * (nsel - 1));\n    buf->n_obj = nsel;\n    buf->u_mode = mode;\n    buf->u_redotextbuf = 0;\n\n        /* store connections into/out of the selection */\n    buf->u_reconnectbuf = binbuf_new();\n    linetraverser_start(&t, x);\n    while ((oc = linetraverser_next(&t)))\n    {\n        int issel1 = glist_isselected(x, &t.tr_ob->ob_g);\n        int issel2 = glist_isselected(x, &t.tr_ob2->ob_g);\n        if (issel1 != issel2)\n        {\n            binbuf_addv(buf->u_reconnectbuf, \"ssiiii;\",\n                gensym(\"#X\"), gensym(\"connect\"),\n                (issel1 ? nnotsel : 0)\n                    + glist_selectionindex(x, &t.tr_ob->ob_g, issel1),\n                t.tr_outno,\n                (issel2 ? nnotsel : 0)\n                    + glist_selectionindex(x, &t.tr_ob2->ob_g, issel2),\n                t.tr_inno);\n        }\n    }\n    if (mode == UCUT_TEXT)\n    {\n        buf->u_objectbuf = canvas_docopy(x);\n    }\n    else if (mode == UCUT_CUT)\n    {\n        buf->u_objectbuf = canvas_docopy(x);\n    }\n    else if (mode == UCUT_CLEAR)\n    {\n        buf->u_objectbuf = canvas_docopy(x);\n    }\n\n        /* instantiate num_obj and fill array of positions of selected objects */\n    if (mode == UCUT_CUT || mode == UCUT_CLEAR)\n    {\n        if (x->gl_list)\n        {\n            int i = 0, j = 0;\n            t_gobj *y;\n            for (y = x->gl_list; y; y = y->g_next)\n            {\n                if (glist_isselected(x, y))\n                {\n                    buf->p_a[i] = j;\n                    i++;\n                }\n                j++;\n            }\n        }\n    }\n\n    return (buf);\n}\n\nint canvas_undo_cut(t_canvas *x, void *z, int action)\n{\n    t_undo_cut *buf = z;\n    int mode = buf?(buf->u_mode):0;\n    if (action == UNDO_UNDO)\n    {\n        if (mode == UCUT_CUT)\n        {\n            canvas_dopaste(x, buf->u_objectbuf);\n        }\n        else if (mode == UCUT_CLEAR)\n        {\n            canvas_dopaste(x, buf->u_objectbuf);\n        }\n        else if (mode == UCUT_TEXT)\n        {\n            t_gobj *y1, *y2;\n            glist_noselect(x);\n            for (y1 = x->gl_list; (y2 = y1->g_next); y1 = y2)\n                ;\n            if (y1)\n            {\n                if (!buf->u_redotextbuf)\n                {\n                    glist_noselect(x);\n                    glist_select(x, y1);\n                    buf->u_redotextbuf = canvas_docopy(x);\n                    glist_noselect(x);\n                }\n                glist_delete(x, y1);\n            }\n            canvas_dopaste(x, buf->u_objectbuf);\n        }\n        if (buf)\n            canvas_applybinbuf(x, buf->u_reconnectbuf);\n\n            /* now reposition objects to their original locations */\n        if (mode == UCUT_CUT || mode == UCUT_CLEAR)\n        {\n            int i = 0;\n\n                /* location of the first newly pasted object */\n            int paste_pos = glist_getindex(x,0) - buf->n_obj;\n            t_gobj *y_prev, *y, *y_next;\n            for (i = 0; i < buf->n_obj; i++)\n            {\n                    /* first check if we are in the same position already */\n                if (paste_pos+i != buf->p_a[i])\n                {\n                    y_prev = glist_nth(x, paste_pos-1+i);\n                    y = glist_nth(x, paste_pos+i);\n                    y_next = glist_nth(x, paste_pos+1+i);\n                        /* if the object is supposed to be first in the gl_list */\n                    if (buf->p_a[i] == 0)\n                    {\n                        if (y_prev && y_next)\n                        {\n                            y_prev->g_next = y_next;\n                        }\n                        else if (y_prev && !y_next)\n                            y_prev->g_next = NULL;\n                            /* now put the moved object at the beginning of the cue */\n                        y->g_next = glist_nth(x, 0);\n                        x->gl_list = y;\n                            /* LATER when objects are properly tagged lower y here */\n                    }\n                        /* if the object is supposed to be in the middle of gl_list */\n                    else {\n                        if (y_prev && y_next)\n                        {\n                            y_prev->g_next = y_next;\n                        }\n                        else if (y_prev && !y_next)\n                        {\n                            y_prev->g_next = NULL;\n                        }\n                            /* now put the moved object in its right place */\n                        y_prev = glist_nth(x, buf->p_a[i]-1);\n                        y_next = glist_nth(x, buf->p_a[i]);\n\n                        y_prev->g_next = y;\n                        y->g_next = y_next;\n                            /* LATER when objects are properly tagged lower y here */\n                    }\n                }\n            }\n                /* LATER disable redrawing here */\n            if (x->gl_havewindow)\n                canvas_redraw(x);\n            if (x->gl_owner && !x->gl_isclone && glist_isvisible(x->gl_owner))\n            {\n                gobj_vis((t_gobj *)x, x->gl_owner, 0);\n                gobj_vis((t_gobj *)x, x->gl_owner, 1);\n            }\n        }\n    }\n    else if (action == UNDO_REDO)\n    {\n        if (mode == UCUT_CUT || mode == UCUT_CLEAR)\n        {\n            int i;\n                /* we can't just blindly do clear here when the user may have\n                 * unselected things between undo and redo, so first let's select\n                 * the right stuff\n                 */\n            glist_noselect(x);\n            i = 0;\n            for (i = 0; i < buf->n_obj; i++)\n                glist_select(x, glist_nth(x, buf->p_a[i]));\n            canvas_doclear(x);\n        }\n        else if (mode == UCUT_TEXT)\n        {\n            t_gobj *y1, *y2;\n            for (y1 = x->gl_list; (y2 = y1->g_next); y1 = y2)\n                ;\n            if (y1)\n                glist_delete(x, y1);\n            canvas_dopaste(x, buf->u_redotextbuf);\n            canvas_applybinbuf(x, buf->u_reconnectbuf);\n        }\n    }\n    else if (action == UNDO_FREE)\n        if (buf)\n        {\n            if (buf->u_objectbuf)\n                binbuf_free(buf->u_objectbuf);\n            if (buf->u_reconnectbuf)\n                binbuf_free(buf->u_reconnectbuf);\n            if (buf->u_redotextbuf)\n                binbuf_free(buf->u_redotextbuf);\n            t_freebytes(buf, sizeof(*buf) +\n                sizeof(buf->p_a[0]) * (buf->n_obj-1));\n        }\n    return 1;\n}\n\n/* --------- 4. motion, including \"tidy up\" and stretching ----------- */\n\ntypedef struct _undo_move_elem\n{\n    int e_index;\n    t_float e_xpix;\n    t_float e_ypix;\n} t_undo_move_elem;\n\ntypedef struct _undo_move\n{\n    t_undo_move_elem *u_vec;\n    int u_n;\n} t_undo_move;\n\nvoid *canvas_undo_set_move(t_canvas *x, int selected)\n{\n    int x1, y1, x2, y2, indx;\n    t_gobj *y;\n    t_undo_move *buf =  (t_undo_move *)getbytes(sizeof(*buf));\n    buf->u_n = selected ? glist_selectionindex(x, 0, 1) : glist_getindex(x, 0);\n    buf->u_vec = (t_undo_move_elem *)getbytes(sizeof(*buf->u_vec) *\n        (selected ? glist_selectionindex(x, 0, 1) : glist_getindex(x, 0)));\n    if (selected)\n    {\n        int i;\n        for (y = x->gl_list, i = indx = 0; y; y = y->g_next, indx++)\n            if (glist_isselected(x, y))\n            {\n                gobj_getrect(y, x, &x1, &y1, &x2, &y2);\n                buf->u_vec[i].e_index = indx;\n                buf->u_vec[i].e_xpix = x1 / x->gl_zoom;\n                buf->u_vec[i].e_ypix = y1 / x->gl_zoom;\n                i++;\n            }\n    }\n    else\n    {\n        for (y = x->gl_list, indx = 0; y; y = y->g_next, indx++)\n        {\n            gobj_getrect(y, x, &x1, &y1, &x2, &y2);\n            buf->u_vec[indx].e_index = indx;\n            buf->u_vec[indx].e_xpix = x1 / x->gl_zoom;\n            buf->u_vec[indx].e_ypix = y1 / x->gl_zoom;\n        }\n    }\n    EDITOR->canvas_undo_already_set_move = 1;\n    return (buf);\n}\n\nint canvas_undo_move(t_canvas *x, void *z, int action)\n{\n    t_undo_move *buf = z;\n    if (action == UNDO_UNDO || action == UNDO_REDO)\n    {\n        int resortin = 0, resortout = 0;\n        int i;\n        for (i = 0; i < buf->u_n; i++)\n        {\n            float newx = (buf->u_vec[i].e_xpix)*x->gl_zoom;\n            float newy = (buf->u_vec[i].e_ypix)*x->gl_zoom;\n            t_gobj*y = glist_nth(x, buf->u_vec[i].e_index);\n            if (y)\n            {\n                int x1=0, y1=0, x2=0, y2=0;\n                int doing = EDITOR->canvas_undo_already_set_move;\n                t_class *cl = pd_class(&y->g_pd);\n                glist_noselect(x);\n                glist_select(x, y);\n                gobj_getrect(y, x, &x1, &y1, &x2, &y2);\n                EDITOR->canvas_undo_already_set_move = 1;\n                canvas_displaceselection(x, (newx - x1)/(x->gl_zoom), (newy - y1)/(x->gl_zoom));\n                EDITOR->canvas_undo_already_set_move = doing;\n                buf->u_vec[i].e_xpix = x1/x->gl_zoom;\n                buf->u_vec[i].e_ypix = y1/x->gl_zoom;\n                if (cl == vinlet_class) resortin = 1;\n                else if (cl == voutlet_class) resortout = 1;\n            }\n        }\n        glist_noselect(x);\n        for (i = 0; i < buf->u_n; i++)\n        {\n            t_gobj* y = glist_nth(x, buf->u_vec[i].e_index);\n            if (y) glist_select(x, y);\n        }\n        if (resortin) canvas_resortinlets(x);\n        if (resortout) canvas_resortoutlets(x);\n    }\n    else if (action == UNDO_FREE)\n    {\n        t_freebytes(buf->u_vec, buf->u_n * sizeof(*buf->u_vec));\n        t_freebytes(buf, sizeof(*buf));\n    }\n    return 1;\n}\n\n/* --------- 5. paste (also duplicate) ----------- */\n\ntypedef struct _undo_paste\n{\n    int u_index;            /* index of first object pasted */\n    int u_sel_index;        /* index of object selected at the time the other\n                               object was pasted (for autopatching) */\n    int u_offset;           /* xy-offset for duplicated items (since it differs\n                               when duplicated into same or different canvas */\n    t_binbuf *u_objectbuf;  /* here we store actual copied data */\n} t_undo_paste;\n\nvoid *canvas_undo_set_paste(t_canvas *x, int numpasted, int duplicate,\n    int d_offset)\n{\n    t_undo_paste *buf =  (t_undo_paste *)getbytes(sizeof(*buf));\n    buf->u_index = glist_getindex(x, 0) - numpasted; /* do we need numpasted at all? */\n    if (!duplicate && x->gl_editor->e_selection &&\n        !x->gl_editor->e_selection->sel_next)\n    {\n            /* if only one object is selected which will warrant autopatching */\n        buf->u_sel_index = glist_getindex(x,\n            x->gl_editor->e_selection->sel_what);\n    }\n    else\n    {\n        buf->u_sel_index = -1;\n    }\n    buf->u_offset = d_offset;\n    buf->u_objectbuf = binbuf_duplicate(EDITOR->copy_binbuf);\n    return (buf);\n}\nvoid *canvas_undo_set_pastebinbuf(t_canvas *x, t_binbuf *b,\n    int numpasted, int duplicate, int d_offset)\n{\n    t_binbuf*tmpbuf = EDITOR->copy_binbuf;\n    void*ret=0;\n    EDITOR->copy_binbuf = b;\n    ret = canvas_undo_set_paste(x, numpasted, duplicate, d_offset);\n    EDITOR->copy_binbuf = tmpbuf;\n    return ret;\n}\n\n\nint canvas_undo_paste(t_canvas *x, void *z, int action)\n{\n    t_undo_paste *buf = z;\n    if (action == UNDO_UNDO)\n    {\n        t_gobj *y;\n            /* check if the paste/duplicate we are undoing contains any\n             * dirty abstractions; and if so, bail out */\n        for (y = glist_nth(x, buf->u_index); y; y = y->g_next)\n            if (canvas_undo_confirmdiscard(y))\n                return 0;\n\n        glist_noselect(x);\n        for (y = glist_nth(x, buf->u_index); y; y = y->g_next)\n            glist_select(x, y);\n        canvas_doclear(x);\n    }\n    else if (action == UNDO_REDO)\n    {\n        glist_noselect(x);\n            /* if the pasted object is supposed to be autopatched\n             * then select the object it should be autopatched to\n             */\n        if (buf->u_sel_index > -1)\n        {\n            glist_select(x, glist_nth(x, buf->u_sel_index));\n        }\n        canvas_dopaste(x, buf->u_objectbuf);\n\n            /* if it was \"duplicate\" have to re-enact the displacement. */\n        if (buf->u_offset)\n        {\n            t_selection *y;\n            for (y = x->gl_editor->e_selection; y; y = y->sel_next)\n                gobj_displace(y->sel_what, x,\n                    buf->u_offset, buf->u_offset);\n        }\n    }\n    else if (action == UNDO_FREE)\n    {\n        if (buf->u_objectbuf)\n            binbuf_free(buf->u_objectbuf);\n        t_freebytes(buf, sizeof(*buf));\n    }\n    return 1;\n}\n\n/* --------- 6. apply  ----------- */\n\ntypedef struct _undo_apply\n{\n    t_binbuf *u_objectbuf;      /* the object cleared or typed into */\n    t_binbuf *u_reconnectbuf;   /* connections into and out of object */\n    int u_index;                /* index of the previous object */\n} t_undo_apply;\n\nvoid *canvas_undo_set_apply(t_canvas *x, int n)\n{\n    t_undo_apply *buf;\n    t_gobj *obj;\n    t_linetraverser t;\n    t_outconnect *oc;\n    int nnotsel;\n        /* enable editor (in case it is disabled) and select the object\n           we are working on */\n    if (!x->gl_edit)\n        canvas_editmode(x, 1);\n\n        /* deselect all objects (if we are editing one while multiple are\n         * selected, upon undoing this will recreate other selected objects,\n         * effectively resulting in unwanted duplicates)\n         * LATER: consider allowing concurrent editing of multiple objects\n         */\n    glist_noselect(x);\n\n    obj = glist_nth(x, n);\n    if (obj && !glist_isselected(x, obj))\n        glist_select(x, obj);\n        /* get number of all items for the offset below */\n    nnotsel= glist_selectionindex(x, 0, 0);\n    buf = (t_undo_apply *)getbytes(sizeof(*buf));\n\n        /* store connections into/out of the selection */\n    buf->u_reconnectbuf = binbuf_new();\n    linetraverser_start(&t, x);\n    while ((oc = linetraverser_next(&t)))\n    {\n        int issel1 = glist_isselected(x, &t.tr_ob->ob_g);\n        int issel2 = glist_isselected(x, &t.tr_ob2->ob_g);\n        if (issel1 != issel2)\n        {\n            binbuf_addv(buf->u_reconnectbuf, \"ssiiii;\",\n                gensym(\"#X\"), gensym(\"connect\"),\n                (issel1 ? nnotsel : 0)\n                    + glist_selectionindex(x, &t.tr_ob->ob_g, issel1),\n                t.tr_outno,\n                (issel2 ? nnotsel : 0)\n                    + glist_selectionindex(x, &t.tr_ob2->ob_g, issel2),\n                t.tr_inno);\n        }\n    }\n        /* copy object in its current state */\n    buf->u_objectbuf = canvas_docopy(x);\n\n        /* store index of the currently selected object */\n    buf->u_index = n;\n\n    return (buf);\n}\n\nstatic int canvas_apply_restore_original_position(t_canvas *x, int orig_pos);\nint canvas_undo_apply(t_canvas *x, void *z, int action)\n{\n    t_undo_apply *buf = z;\n    if (action == UNDO_UNDO || action == UNDO_REDO)\n    {\n            /* find current instance */\n        t_binbuf *tmp;\n        glist_noselect(x);\n        glist_select(x, glist_nth(x, buf->u_index));\n\n            /* copy it for the new undo/redo */\n        tmp = canvas_docopy(x);\n\n            /* delete current instance */\n        canvas_doclear(x);\n\n            /* replace it with previous instance */\n        canvas_dopaste(x, buf->u_objectbuf);\n\n            /* change previous instance with current one */\n        buf->u_objectbuf = tmp;\n\n            /* connections should stay the same */\n        canvas_applybinbuf(x, buf->u_reconnectbuf);\n            /* now we need to reposition the object to its original place */\n        if (canvas_apply_restore_original_position(x, buf->u_index) && x->gl_havewindow)\n            canvas_redraw(x);\n    }\n    else if (action == UNDO_FREE)\n    {\n        if (buf->u_objectbuf)\n            binbuf_free(buf->u_objectbuf);\n        if (buf->u_reconnectbuf)\n            binbuf_free(buf->u_reconnectbuf);\n        t_freebytes(buf, sizeof(*buf));\n    }\n    return 1;\n}\n\nint canvas_apply_restore_original_position(t_canvas *x, int orig_pos)\n{\n    t_gobj *y, *y_prev, *y_next;\n        /* get the last object */\n    y = glist_nth(x, glist_getindex(x, 0) - 1);\n    if (glist_getindex(x, y) != orig_pos)\n    {\n            /* first make the object prior to the pasted one the end of the list */\n        y_prev = glist_nth(x, glist_getindex(x, 0) - 2);\n        if (y_prev)\n            y_prev->g_next = NULL;\n            /* if the object is supposed to be first in the gl_list */\n        if (orig_pos == 0)\n        {\n            y->g_next = glist_nth(x, 0);\n            x->gl_list = y;\n        }\n            /* if the object is supposed to be in the middle of the gl_list */\n        else {\n            y_prev = glist_nth(x, orig_pos-1);\n            y_next = y_prev->g_next;\n            y_prev->g_next = y;\n            y->g_next = y_next;\n        }\n        return(1);\n    }\n    return(0);\n}\n\n/* --------- 7. arrange (to front/back)  ----------- */\ntypedef struct _undo_arrange\n{\n    int u_previndex;            /* old index */\n    int u_newindex;                /* new index */\n} t_undo_arrange;\n\nvoid *canvas_undo_set_arrange(t_canvas *x, t_gobj *obj, int newindex)\n{\n        /* newindex tells us is the new index at the beginning (0) or the end (1) */\n\n    t_undo_arrange *buf;\n        /* enable editor (in case it is disabled) and select the object\n           we are working on */\n    if (!x->gl_edit)\n        canvas_editmode(x, 1);\n\n        /* select the object*/\n    if (!glist_isselected(x, obj))\n        glist_select(x, obj);\n\n    buf = (t_undo_arrange *)getbytes(sizeof(*buf));\n\n        /* set the u_newindex appropriately */\n    if (newindex == 0) buf->u_newindex = 0;\n    else buf->u_newindex = glist_getindex(x, 0) - 1;\n\n        /* store index of the currently selected object */\n    buf->u_previndex = glist_getindex(x, obj);\n\n    return (buf);\n}\n\n/* called by undo/redo arrange and done_canvas_popup. only done_canvas_popup\n   checks if it is a valid action and activates undo option */\nstatic void canvas_doarrange(t_canvas *x, t_float which, t_gobj *oldy,\n    t_gobj *oldy_prev, t_gobj *oldy_next)\n{\n    t_gobj *y_begin = x->gl_list;\n    t_gobj *y_end = glist_nth(x, glist_getindex(x,0) - 1);\n\n    switch((int)which)\n    {\n    case 3: /* to front */\n            /* put the object at the end of the cue */\n        y_end->g_next = oldy;\n        oldy->g_next = NULL;\n\n            /* now fix links in the hole made in the list due to moving of the oldy\n             * (we know there is oldy_next as y_end != oldy in canvas_done_popup)\n             */\n        if (oldy_prev) /* there is indeed more before the oldy position */\n            oldy_prev->g_next = oldy_next;\n        else x->gl_list = oldy_next;\n\n#if 0\n            /* and finally redraw */\n        pdgui_vmess(\"gui_raise\", \"or\", x, \"selected\");\n#endif\n        break;\n\n    case 4: /* to back */\n        x->gl_list = oldy; /* put it to the beginning of the cue */\n        oldy->g_next = y_begin; /* make it point to the old beginning */\n\n            /* now fix links in the hole made in the list due to moving of the oldy\n             * (we know there is oldy_prev as y_begin != oldy in canvas_done_popup)\n             */\n        if (oldy_prev)\n        {\n            if (oldy_next) /* there is indeed more after oldy position */\n                oldy_prev->g_next = oldy_next;\n            else oldy_prev->g_next = NULL; /* oldy was the last in the cue */\n        }\n\n#if 0\n            /* and finally redraw */\n        pdgui_vmess(\"gui_lower\", \"or\", x, \"selected\");\n#endif\n        break;\n    default:\n        bug(\"canvas_arrange\");\n        return;\n    }\n    canvas_dirty(x, 1);\n}\n\nint canvas_undo_arrange(t_canvas *x, void *z, int action)\n{\n    t_undo_arrange *buf = z;\n    t_gobj *y=NULL, *prev=NULL, *next=NULL;\n\n    if (!x->gl_edit)\n        canvas_editmode(x, 1);\n\n    switch(action)\n    {\n\n    case UNDO_UNDO:\n        if(buf->u_newindex == buf->u_previndex) return 1;\n            /* this is our object */\n        y = glist_nth(x, buf->u_newindex);\n\n            /* select object */\n        glist_noselect(x);\n        glist_select(x, y);\n\n        if (buf->u_newindex)\n        {\n                /* if it is the last object */\n\n                /* first previous object should point to nothing */\n            prev = glist_nth(x, buf->u_newindex - 1);\n            prev->g_next = NULL;\n\n                /* now we reuse vars for the following:\n                   old index should be right before the object previndex\n                   is pointing to as the object was moved to the end */\n\n                /* old position is not first */\n            if (buf->u_previndex)\n            {\n                prev = glist_nth(x, buf->u_previndex - 1);\n                next = prev->g_next;\n\n                    /* now readjust pointers */\n                prev->g_next = y;\n                y->g_next = next;\n            }\n                /* old position is first */\n            else {\n                prev = NULL;\n                next = x->gl_list;\n\n                    /* now readjust pointers */\n                y->g_next = next;\n                x->gl_list = y;\n            }\n        }\n        else {\n                /* if it is the first object */\n\n                /* old index should be right after the object previndex\n                   is pointing to as the object was moved to the end */\n            prev = glist_nth(x, buf->u_previndex);\n\n                /* next may be NULL and that is ok */\n            next = prev->g_next;\n\n                /* first glist pointer needs to point to the second object */\n            x->gl_list = y->g_next;\n\n                /* now readjust pointers */\n            prev->g_next = y;\n            y->g_next = next;\n        }\n            /* and finally redraw canvas */\n        if (x->gl_havewindow)\n            canvas_redraw(x);\n        break;\n    case UNDO_REDO:\n    {\n        t_gobj *oldy_prev=NULL, *oldy_next=NULL;\n        int arrangeaction;\n\n        if(buf->u_newindex == buf->u_previndex) return 1;\n            /* find our object */\n        y = glist_nth(x, buf->u_previndex);\n\n            /* select object */\n        glist_noselect(x);\n        glist_select(x, y);\n\n        if (!buf->u_newindex) arrangeaction = 4;\n        else arrangeaction = 3;\n\n\n            /* if there is an object before ours (in other words our index is > 0) */\n        if (glist_getindex(x,y))\n            oldy_prev = glist_nth(x, buf->u_previndex - 1);\n\n            /* if there is an object after ours */\n        if (y->g_next)\n            oldy_next = y->g_next;\n\n        canvas_doarrange(x, arrangeaction, y, oldy_prev, oldy_next);\n    }\n        break;\n    case UNDO_FREE:\n        t_freebytes(buf, sizeof(*buf));\n    }\n    return 1;\n}\n\n/* --------- 8. apply on canvas ----------- */\ntypedef struct _undo_canvas_properties\n{\n    int gl_pixwidth;            /* width in pixels (on parent, if a graph) */\n    int gl_pixheight;\n    t_float gl_x1;              /* bounding rectangle in our own coordinates */\n    t_float gl_y1;\n    t_float gl_x2;\n    t_float gl_y2;\n    int gl_screenx1;            /* screen coordinates when toplevel */\n    int gl_screeny1;\n    int gl_screenx2;\n    int gl_screeny2;\n    int gl_xmargin;             /* origin for GOP rectangle */\n    int gl_ymargin;\n\n    unsigned int gl_goprect:1;  /* draw rectangle for graph-on-parent */\n    unsigned int gl_isgraph:1;  /* show as graph on parent */\n    unsigned int gl_hidetext:1; /* hide object-name + args when doing\n                                   graph on parent */\n} t_undo_canvas_properties;\n\nvoid *canvas_undo_set_canvas(t_canvas *x)\n{\n        /* enable editor (in case it is disabled) */\n    t_undo_canvas_properties *buf =\n        (t_undo_canvas_properties *)getbytes(sizeof(*buf));\n\n    buf->gl_pixwidth = x->gl_pixwidth;\n    buf->gl_pixheight = x->gl_pixheight;\n    buf->gl_x1 = x->gl_x1;\n    buf->gl_y1 = x->gl_y1;\n    buf->gl_x2 = x->gl_x2;\n    buf->gl_y2 = x->gl_y2;\n    buf->gl_screenx1 = x->gl_screenx1;\n    buf->gl_screeny1 = x->gl_screeny1;\n    buf->gl_screenx2 = x->gl_screenx2;\n    buf->gl_screeny2 = x->gl_screeny2;\n    buf->gl_xmargin = x->gl_xmargin;\n    buf->gl_ymargin = x->gl_ymargin;\n    buf->gl_goprect = x->gl_goprect;\n    buf->gl_isgraph = x->gl_isgraph;\n    buf->gl_hidetext = x->gl_hidetext;\n\n    return (buf);\n}\n\nint canvas_undo_canvas_apply(t_canvas *x, void *z, int action)\n{\n    t_undo_canvas_properties *buf = (t_undo_canvas_properties *)z;\n    t_undo_canvas_properties tmp;\n\n    if (action == UNDO_UNDO || action == UNDO_REDO)\n    {\n        if (!x->gl_edit)\n            canvas_editmode(x, 1);\n#if 0\n            /* close properties window first */\n        t_int properties = gfxstub_haveproperties((void *)x);\n        if (properties)\n        {\n            pdgui_stub_deleteforkey(x);\n        }\n#endif\n            /* store current canvas values into temporary data holder */\n        tmp.gl_pixwidth = x->gl_pixwidth;\n        tmp.gl_pixheight = x->gl_pixheight;\n        tmp.gl_x1 = x->gl_x1;\n        tmp.gl_y1 = x->gl_y1;\n        tmp.gl_x2 = x->gl_x2;\n        tmp.gl_y2 = x->gl_y2;\n        tmp.gl_screenx1 = x->gl_screenx1;\n        tmp.gl_screeny1 = x->gl_screeny1;\n        tmp.gl_screenx2 = x->gl_screenx2;\n        tmp.gl_screeny2 = x->gl_screeny2;\n        tmp.gl_xmargin = x->gl_xmargin;\n        tmp.gl_ymargin = x->gl_ymargin;\n        tmp.gl_goprect = x->gl_goprect;\n        tmp.gl_isgraph = x->gl_isgraph;\n        tmp.gl_hidetext = x->gl_hidetext;\n\n            /* change canvas values with the ones from the undo buffer */\n        x->gl_pixwidth = buf->gl_pixwidth;\n        x->gl_pixheight = buf->gl_pixheight;\n        x->gl_x1 = buf->gl_x1;\n        x->gl_y1 = buf->gl_y1;\n        x->gl_x2 = buf->gl_x2;\n        x->gl_y2 = buf->gl_y2;\n        x->gl_screenx1 = buf->gl_screenx1;\n        x->gl_screeny1 = buf->gl_screeny1;\n        x->gl_screenx2 = buf->gl_screenx2;\n        x->gl_screeny2 = buf->gl_screeny2;\n        x->gl_xmargin = buf->gl_xmargin;\n        x->gl_ymargin = buf->gl_ymargin;\n        x->gl_goprect = buf->gl_goprect;\n        x->gl_isgraph = buf->gl_isgraph;\n        x->gl_hidetext = buf->gl_hidetext;\n\n            /* copy data values from the temporary data to the undo buffer */\n        buf->gl_pixwidth = tmp.gl_pixwidth;\n        buf->gl_pixheight = tmp.gl_pixheight;\n        buf->gl_x1 = tmp.gl_x1;\n        buf->gl_y1 = tmp.gl_y1;\n        buf->gl_x2 = tmp.gl_x2;\n        buf->gl_y2 = tmp.gl_y2;\n        buf->gl_screenx1 = tmp.gl_screenx1;\n        buf->gl_screeny1 = tmp.gl_screeny1;\n        buf->gl_screenx2 = tmp.gl_screenx2;\n        buf->gl_screeny2 = tmp.gl_screeny2;\n        buf->gl_xmargin = tmp.gl_xmargin;\n        buf->gl_ymargin = tmp.gl_ymargin;\n        buf->gl_goprect = tmp.gl_goprect;\n        buf->gl_isgraph = tmp.gl_isgraph;\n        buf->gl_hidetext = tmp.gl_hidetext;\n\n            /* redraw */\n        canvas_setgraph(x, x->gl_isgraph + 2*x->gl_hidetext, 0);\n        canvas_dirty(x, 1);\n\n        if (x->gl_havewindow)\n        {\n            canvas_redraw(x);\n        }\n        if (x->gl_owner && !x->gl_isclone && glist_isvisible(x->gl_owner))\n        {\n            glist_noselect(x);\n            gobj_vis(&x->gl_gobj, x->gl_owner, 0);\n            gobj_vis(&x->gl_gobj, x->gl_owner, 1);\n            if (x->gl_owner->gl_havewindow)\n                canvas_redraw(x->gl_owner);\n        }\n    }\n\n    else if (action == UNDO_FREE)\n    {\n        if (buf)\n            t_freebytes(buf, sizeof(*buf));\n    }\n    return 1;\n}\n/* --------- 9. create ----------- */\n\ntypedef struct _undo_create\n{\n    int u_index;                /* index of the created object object */\n    t_binbuf *u_objectbuf;      /* the object cleared or typed into */\n    t_binbuf *u_reconnectbuf;   /* connections into and out of object */\n} t_undo_create;\n\nvoid *canvas_undo_set_create(t_canvas *x)\n{\n    t_gobj *y;\n    t_linetraverser t;\n    int nnotsel;\n    t_undo_create *buf = (t_undo_create *)getbytes(sizeof(*buf));\n    buf->u_index = glist_getindex(x, 0) - 1;\n    nnotsel= glist_selectionindex(x, 0, 0);\n\n    buf->u_objectbuf = binbuf_new();\n    if (x->gl_list)\n    {\n        t_outconnect *oc;\n        for (y = x->gl_list; y; y = y->g_next)\n        {\n            if (!y->g_next)\n            {\n                gobj_save(y, buf->u_objectbuf);\n                break;\n            }\n        }\n        buf->u_reconnectbuf = binbuf_new();\n        linetraverser_start(&t, x);\n        while ((oc = linetraverser_next(&t)))\n        {\n            int issel1, issel2;\n            issel1 = ( &t.tr_ob->ob_g == y ? 1 : 0);\n            issel2 = ( &t.tr_ob2->ob_g == y ? 1 : 0);\n            if (issel1 != issel2)\n            {\n                binbuf_addv(buf->u_reconnectbuf, \"ssiiii;\",\n                    gensym(\"#X\"), gensym(\"connect\"),\n                    (issel1 ? nnotsel : 0)\n                        + glist_selectionindex(x, &t.tr_ob->ob_g, issel1),\n                    t.tr_outno,\n                    (issel2 ? nnotsel : 0)\n                        + glist_selectionindex(x, &t.tr_ob2->ob_g, issel2),\n                    t.tr_inno);\n            }\n        }\n    }\n    return (buf);\n}\n\nint canvas_undo_create(t_canvas *x, void *z, int action)\n{\n    t_undo_create *buf = z;\n    t_gobj *y;\n\n    if (action == UNDO_UNDO)\n    {\n        glist_noselect(x);\n        y = glist_nth(x, buf->u_index);\n        glist_select(x, y);\n        canvas_doclear(x);\n    }\n    else if (action == UNDO_REDO)\n    {\n        canvas_applybinbuf(x, buf->u_objectbuf);\n        canvas_applybinbuf(x, buf->u_reconnectbuf);\n        if (pd_this->pd_newest && pd_class(pd_this->pd_newest) == canvas_class)\n            canvas_loadbang((t_canvas *)pd_this->pd_newest);\n        y = glist_nth(x, buf->u_index);\n        glist_select(x, y);\n    }\n    else if (action == UNDO_FREE)\n    {\n        binbuf_free(buf->u_objectbuf);\n        binbuf_free(buf->u_reconnectbuf);\n        t_freebytes(buf, sizeof(*buf));\n    }\n    return 1;\n}\n\n/* ------ 10. recreate (called from text_setto after text has changed) ------ */\n\n/* recreate uses t_undo_create struct */\n\nvoid *canvas_undo_set_recreate(t_canvas *x, t_gobj *y, int pos)\n{\n    t_linetraverser t;\n    t_outconnect *oc;\n    int nnotsel;\n\n    t_undo_create *buf = (t_undo_create *)getbytes(sizeof(*buf));\n    buf->u_index = pos;\n    nnotsel= glist_selectionindex(x, 0, 0) - 1; /* - 1 is a critical difference from the create */\n    buf->u_objectbuf = binbuf_new();\n    gobj_save(y, buf->u_objectbuf);\n\n    buf->u_reconnectbuf = binbuf_new();\n    linetraverser_start(&t, x);\n    while ((oc = linetraverser_next(&t)))\n    {\n        int issel1, issel2;\n        issel1 = ( &t.tr_ob->ob_g == y ? 1 : 0);\n        issel2 = ( &t.tr_ob2->ob_g == y ? 1 : 0);\n        if (issel1 != issel2)\n        {\n            binbuf_addv(buf->u_reconnectbuf, \"ssiiii;\",\n                gensym(\"#X\"), gensym(\"connect\"),\n                (issel1 ? nnotsel : 0)\n                    + glist_selectionindex(x, &t.tr_ob->ob_g, issel1),\n                t.tr_outno,\n                (issel2 ? nnotsel : 0)\n                    + glist_selectionindex(x, &t.tr_ob2->ob_g, issel2),\n                t.tr_inno);\n        }\n    }\n    return (buf);\n}\n\nint canvas_undo_recreate(t_canvas *x, void *z, int action)\n{\n    t_undo_create *buf = z;\n    t_gobj *y = NULL;\n    if (action == UNDO_UNDO)\n        y = glist_nth(x, glist_getindex(x, 0) - 1);\n    else if (action == UNDO_REDO)\n        y = glist_nth(x, buf->u_index);\n\n        /* check if we are undoing the creation of a dirty abstraction;\n         * if so, bail out */\n    if ((action == UNDO_UNDO)\n        && canvas_undo_confirmdiscard(y))\n            return 0;\n\n    if (action == UNDO_UNDO || action == UNDO_REDO)\n    {\n            /* first copy new state of the current object */\n        t_undo_create *buf2 = (t_undo_create *)getbytes(sizeof(*buf));\n        buf2->u_index = buf->u_index;\n\n        buf2->u_objectbuf = binbuf_new();\n        gobj_save(y, buf2->u_objectbuf);\n\n        buf2->u_reconnectbuf = binbuf_duplicate(buf->u_reconnectbuf);\n\n            /* now cut the existing object */\n        glist_noselect(x);\n        glist_select(x, y);\n        canvas_doclear(x);\n\n            /* then paste the old object */\n\n            /* save and clear bindings to symbols #A, #N, #X; restore when done */\n        canvas_applybinbuf(x, buf->u_objectbuf);\n        canvas_applybinbuf(x, buf->u_reconnectbuf);\n\n            /* free the old data */\n        binbuf_free(buf->u_objectbuf);\n        binbuf_free(buf->u_reconnectbuf);\n        t_freebytes(buf, sizeof(*buf));\n\n            /* re-adjust pointer\n             * (this should probably belong into g_undo.c, but since it is\n             * a unique case, we'll let it be for the time being)\n             */\n        canvas_undo_get(x)->u_last->data = (void *)buf2;\n        buf = buf2;\n\n            /* reposition object to its original place */\n        if (action == UNDO_UNDO)\n            if (canvas_apply_restore_original_position(x, buf->u_index) && x->gl_havewindow)\n                canvas_redraw(x);\n\n            /* send a loadbang */\n        if (pd_this->pd_newest && pd_class(pd_this->pd_newest) == canvas_class)\n            canvas_loadbang((t_canvas *)pd_this->pd_newest);\n\n            /* select */\n        if (action == UNDO_REDO)\n            y = glist_nth(x, glist_getindex(x, 0) - 1);\n        else\n            y = glist_nth(x, buf->u_index);\n        glist_select(x, y);\n    }\n    else if (action == UNDO_FREE)\n    {\n        binbuf_free(buf->u_objectbuf);\n        binbuf_free(buf->u_reconnectbuf);\n        t_freebytes(buf, sizeof(*buf));\n    }\n    return 1;\n}\n\n/* ----------- 11. font -------------- */\nstatic void canvas_dofont(t_canvas *x, t_floatarg font, t_floatarg xresize, t_floatarg yresize);\n\ntypedef struct _undo_font\n{\n    int font;\n    t_float resize;\n    int which;\n} t_undo_font;\n\nvoid *canvas_undo_set_font(t_canvas *x, int font, t_float resize, int which)\n{\n    t_undo_font *u_f = (t_undo_font *)getbytes(sizeof(*u_f));\n    u_f->font = font;\n    u_f->resize = resize;\n    u_f->which = which;\n    return (u_f);\n}\n\nint canvas_undo_font(t_canvas *x, void *z, int action)\n{\n    t_undo_font *u_f = z;\n\n    if (action == UNDO_UNDO || action == UNDO_REDO)\n    {\n        t_canvas *x2 = canvas_getrootfor(x);\n        int tmp_font = x2->gl_font;\n\n        int whichresize = u_f->which;\n        t_float realresize = 1./u_f->resize;\n        t_float realresx = 1, realresy = 1;\n        if (whichresize != 3) realresx = realresize;\n        if (whichresize != 2) realresy = realresize;\n        canvas_dofont(x2, u_f->font, realresx, realresy);\n\n        u_f->resize = realresize;\n        u_f->font = tmp_font;\n    }\n    else if (action == UNDO_FREE)\n    {\n        if (u_f)\n            freebytes(u_f, sizeof(*u_f));\n    }\n    return 1;\n}\n\nint clone_match(t_pd *z, t_symbol *name, t_symbol *dir);\nstatic void canvas_cut(t_canvas *x);\n\n    /* recursively check for abstractions to reload as result of a save.\n       Don't reload the one we just saved (\"except\") though. */\n    /* LATER try to do the same trick for externs. */\nstatic void glist_doreload(t_glist *gl, t_symbol *name, t_symbol *dir,\n    t_gobj *except)\n{\n    t_gobj *g;\n    int hadwindow = gl->gl_havewindow;\n    int found = 0;\n        /* to optimize redrawing we select all objects that need to be updated\n           and redraw (cut+undo) them together. Then we look for sub-patches that may have\n           more of the same... */\n    for (g = gl->gl_list; g; g = g->g_next)\n    {\n            /* remake the object if it's an abstraction that appears to have\n               been loaded from the file we just saved */\n        int remakeit = (g != except && pd_class(&g->g_pd) == canvas_class &&\n            canvas_isabstraction((t_canvas *)g) &&\n                ((t_canvas *)g)->gl_name == name &&\n                    canvas_getdir((t_canvas *)g) == dir);\n            /* also remake it if it's a \"clone\" with that name */\n        if (pd_class(&g->g_pd) == clone_class &&\n            clone_match(&g->g_pd, name, dir))\n        {\n                /* LATER try not to remake the one that equals \"except\" */\n            remakeit = 1;\n        }\n        if (remakeit)\n        {\n                /* Bugfix for cases where canvas_vis doesn't actually create a\n                   new editor. We need to fix canvas_vis so that the bug\n                   doesn't get triggered. But since we know this fixes a\n                   regression we'll keep this as a point in the history as we\n                   fix canvas_vis. Once that's done we can remove this call. */\n            canvas_create_editor(gl);\n\n            if (!gl->gl_havewindow)\n            {\n                canvas_vis(glist_getcanvas(gl), 1);\n            }\n            if (!found)\n            {\n                glist_noselect(gl);\n                found = 1;\n            }\n            glist_select(gl, g);\n        }\n    }\n        /* cut all selected (matched) objects and undo, to reinstantiate them */\n    if (found)\n    {\n        canvas_cut(gl);\n        canvas_undo_undo(gl);\n        canvas_undo_rebranch(gl);\n        glist_noselect(gl);\n    }\n\n        /* now iterate over all the sub-patches... */\n    for (g = gl->gl_list; g; g = g->g_next)\n    {\n        if (g != except && pd_class(&g->g_pd) == canvas_class &&\n            (!canvas_isabstraction((t_canvas *)g) ||\n                 ((t_canvas *)g)->gl_name != name ||\n                 canvas_getdir((t_canvas *)g) != dir)\n           )\n                glist_doreload((t_canvas *)g, name, dir, except);\n    }\n    if (!hadwindow && gl->gl_havewindow)\n        canvas_vis(glist_getcanvas(gl), 0);\n}\n\n    /* call canvas_doreload on everyone */\nvoid canvas_reload(t_symbol *name, t_symbol *dir, t_glist *except)\n{\n    t_canvas *x;\n    int dspwas = canvas_suspend_dsp();\n    t_binbuf*b = 0;\n    if(EDITOR->copy_binbuf)\n        b = binbuf_duplicate(EDITOR->copy_binbuf);\n\n    THISGUI->i_reloadingabstraction = except;\n        /* find all root canvases */\n    for (x = pd_getcanvaslist(); x; x = x->gl_next)\n        glist_doreload(x, name, dir, &except->gl_gobj);\n    THISGUI->i_reloadingabstraction = 0;\n    if(b)\n    {\n        if(EDITOR->copy_binbuf)\n            binbuf_free(EDITOR->copy_binbuf);\n        EDITOR->copy_binbuf = b;\n    }\n    canvas_resume_dsp(dspwas);\n}\n\n/* ------------------------ event handling ------------------------ */\n\nstatic const char *cursorlist[] = {\n    \"$cursor_runmode_nothing\",\n    \"$cursor_runmode_clickme\",\n    \"$cursor_runmode_thicken\",\n    \"$cursor_runmode_addpoint\",\n    \"$cursor_editmode_nothing\",\n    \"$cursor_editmode_connect\",\n    \"$cursor_editmode_disconnect\",\n    \"$cursor_editmode_resize\"\n};\n\nvoid canvas_setcursor(t_canvas *x, unsigned int cursornum)\n{\n    if (cursornum >= sizeof(cursorlist)/sizeof *cursorlist)\n    {\n        bug(\"canvas_setcursor\");\n        return;\n    }\n    if (EDITOR->canvas_cursorcanvaswas != x ||\n        EDITOR->canvas_cursorwas != cursornum)\n    {\n        pdgui_vmess(0, \"^r rr\", x, \"configure\", \"-cursor\", cursorlist[cursornum]);\n        EDITOR->canvas_cursorcanvaswas = x;\n        EDITOR->canvas_cursorwas = cursornum;\n    }\n}\n\n    /* check if a point lies in a gobj.  */\nint canvas_hitbox(t_canvas *x, t_gobj *y, int xpos, int ypos,\n    int *x1p, int *y1p, int *x2p, int *y2p)\n{\n    int x1, y1, x2, y2;\n    if (!gobj_shouldvis(y, x))\n        return (0);\n    gobj_getrect(y, x, &x1, &y1, &x2, &y2);\n    if (xpos >= x1 && xpos <= x2 && ypos >= y1 && ypos <= y2)\n    {\n        *x1p = x1;\n        *y1p = y1;\n        *x2p = x2;\n        *y2p = y2;\n        return (1);\n    }\n    else return (0);\n}\n\n    /* find the last gobj, if any, containing the point. */\nstatic t_gobj *canvas_findhitbox(t_canvas *x, int xpos, int ypos,\n    int *x1p, int *y1p, int *x2p, int *y2p)\n{\n    t_gobj *y, *rval = 0;\n    int x1, y1, x2, y2;\n    *x1p = -0x7fffffff;\n    for (y = x->gl_list; y; y = y->g_next)\n    {\n        if (canvas_hitbox(x, y, xpos, ypos, &x1, &y1, &x2, &y2)\n            && (x1 > *x1p))\n                *x1p = x1, *y1p = y1, *x2p = x2, *y2p = y2, rval = y;\n    }\n        /* if there are at least two selected objects, we'd prefer\n           to find a selected one (never mind which) to the one we got. */\n    if (x->gl_editor && x->gl_editor->e_selection &&\n        x->gl_editor->e_selection->sel_next && !glist_isselected(x, y))\n    {\n        t_selection *sel;\n        for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next)\n            if (canvas_hitbox(x, sel->sel_what, xpos, ypos, &x1, &y1, &x2, &y2))\n                *x1p = x1, *y1p = y1, *x2p = x2, *y2p = y2,\n                    rval = sel->sel_what;\n    }\n    return (rval);\n}\n\n    /* right-clicking on a canvas object pops up a menu. */\nstatic void canvas_rightclick(t_canvas *x, int xpos, int ypos, t_gobj *y)\n{\n    int canprop, canopen;\n    canprop = (!y || class_getpropertiesfn(pd_class(&y->g_pd)));\n    canopen = (y && zgetfn(&y->g_pd, gensym(\"menu-open\")));\n    pdgui_vmess(\"pdtk_canvas_popup\", \"^ ii ii\",\n        x,\n        xpos, ypos,\n        canprop, canopen);\n}\n\n/* ----  editors -- perhaps this and \"vis\" should go to g_editor.c ------- */\n\nstatic t_editor *editor_new(t_glist *owner)\n{\n    char buf[40];\n    t_editor *x = (t_editor *)getbytes(sizeof(*x));\n    x->e_connectbuf = binbuf_new();\n    x->e_deleted = binbuf_new();\n    x->e_glist = owner;\n    sprintf(buf, \".x%lx\", (t_int)owner);\n    x->e_guiconnect = guiconnect_new(&owner->gl_pd, gensym(buf));\n    x->e_clock = 0;\n    return (x);\n}\n\nstatic void editor_free(t_editor *x, t_glist *y)\n{\n    glist_noselect(y);\n    guiconnect_notarget(x->e_guiconnect, 1000);\n    binbuf_free(x->e_connectbuf);\n    binbuf_free(x->e_deleted);\n    if (x->e_clock)\n        clock_free(x->e_clock);\n    freebytes((void *)x, sizeof(*x));\n}\n\n    /* recursively create or destroy all editors of a glist and its\n       sub-glists, as long as they aren't toplevels. */\nvoid canvas_create_editor(t_glist *x)\n{\n    t_gobj *y;\n    t_object *ob;\n    if (!x->gl_editor)\n    {\n        x->gl_editor = editor_new(x);\n        for (y = x->gl_list; y; y = y->g_next)\n            if ((ob = pd_checkobject(&y->g_pd)))\n                rtext_new(x, ob);\n    }\n}\n\nvoid canvas_destroy_editor(t_glist *x)\n{\n    glist_noselect(x);\n    if (x->gl_editor)\n    {\n        t_rtext *rtext;\n            /* this happens if we had activated an atom box in run mode: */\n        if (x->gl_editor->e_textedfor)\n            rtext_activate(x->gl_editor->e_textedfor, 0);\n        while ((rtext = x->gl_editor->e_rtext))\n            rtext_free(rtext);\n        editor_free(x->gl_editor, x);\n        x->gl_editor = 0;\n    }\n}\n\nvoid canvas_reflecttitle(t_canvas *x);\nvoid canvas_map(t_canvas *x, t_floatarg f);\n\n    /* we call this when we want the window to become visible, mapped, and\n       in front of all windows; or with \"f\" zero, when we want to get rid of\n       the window. */\nvoid canvas_vis(t_canvas *x, t_floatarg f)\n{\n    int flag = (f != 0);\n    if (flag)\n    {\n            /* If a subpatch/abstraction has GOP/gl_isgraph set, then it will have\n             * a gl_editor already, if its not, it will not have a gl_editor.\n             * canvas_create_editor(x) checks if a gl_editor is already created,\n             * so its ok to run it on a canvas that already has a gl_editor. */\n        if (x->gl_editor && x->gl_havewindow)\n        {           /* just put us in front */\n            pdgui_vmess(\"pdtk_canvas_raise\", \"^\", x);\n        }\n        else\n        {\n            char cbuf[MAXPDSTRING];\n            t_canvas *c = x;\n            t_undo *undo = canvas_undo_get(x);\n            t_undo_action *udo = undo ? undo->u_last : 0;\n            char winpos[128];\n            t_canvas**parents = getbytes(0);\n            size_t numparents;\n\n            canvas_create_editor(x);\n            if ((GLIST_DEFCANVASXLOC == x->gl_screenx1) && (GLIST_DEFCANVASYLOC == x->gl_screeny1)) /* initial values for new windows */\n            {\n                winpos[0]=0;\n            } else {\n                sprintf(winpos, \"+%d+%d\", (int)(x->gl_screenx1), (int)(x->gl_screeny1));\n            }\n\n            pdgui_vmess(\"pdtk_canvas_new\", \"^ ii si\", x,\n                (int)(x->gl_screenx2 - x->gl_screenx1),\n                (int)(x->gl_screeny2 - x->gl_screeny1),\n                winpos, x->gl_edit);\n\n            numparents = 0;\n            while (c->gl_owner && !c->gl_isclone) {\n                t_canvas**newparents = (t_canvas**)resizebytes(parents, numparents * sizeof(*newparents), (numparents+1) * sizeof(*newparents));\n                if (!newparents)\n                    break;\n                c = c->gl_owner;\n                parents = newparents;\n                parents[numparents] = c;\n                numparents++;\n            }\n            pdgui_vmess(\"pdtk_canvas_setparents\", \"^C\", x, numparents, parents);\n            freebytes(parents, numparents * sizeof(t_canvas));\n\n            x->gl_havewindow = 1;\n            canvas_reflecttitle(x);\n            canvas_updatewindowlist();\n            pdgui_vmess(\"pdtk_undomenu\", \"^ ss\",\n                x,\n                udo?(udo->name):\"no\",\n                (udo && udo->next)?(udo->next->name):\"no\");\n        }\n    }\n    else    /* make invisible */\n    {\n        int i;\n        t_canvas *x2;\n        if (!x->gl_havewindow)\n        {\n                /* bug workaround -- a graph in a visible patch gets \"invised\"\n                   when the patch is closed, and must lose the editor here.  It's\n                   probably not the natural place to do this.  Other cases like\n                   subpatches fall here too but don'd need the editor freed, so\n                   we check if it exists. */\n            if (x->gl_editor)\n                canvas_destroy_editor(x);\n            return;\n        }\n        glist_noselect(x);\n        if (glist_isvisible(x))\n            canvas_map(x, 0);\n        canvas_destroy_editor(x);\n        pdgui_vmess(\"destroy\", \"^\", x);\n        for (i = 1, x2 = x; x2; x2 = x2->gl_next, i++)\n            ;\n            /* if we're a graph on our parent, and if the parent exists\n               and is visible, show ourselves on parent. */\n        if (glist_isgraph(x) && x->gl_owner && !x->gl_isclone)\n        {\n            t_glist *gl2 = x->gl_owner;\n            if (glist_isvisible(gl2))\n                gobj_vis(&x->gl_gobj, gl2, 0);\n            x->gl_havewindow = 0;\n            if (glist_isvisible(gl2) && !gl2->gl_isdeleting)\n            {\n                    /* make sure zoom level matches parent, ie. after an open\n                       subpatch's zoom level was changed before being closed */\n                if(x->gl_zoom != gl2->gl_zoom)\n                    canvas_zoom(x, gl2->gl_zoom);\n                gobj_vis(&x->gl_gobj, gl2, 1);\n            }\n        }\n        else x->gl_havewindow = 0;\n        canvas_updatewindowlist();\n    }\n}\n\n    /* set a canvas up as a graph-on-parent.  Set reasonable defaults for\n       any missing parameters and redraw things if necessary. */\nvoid canvas_setgraph(t_glist *x, int flag, int nogoprect)\n{\n    int can_graph_on_parent = x->gl_owner && !x->gl_isclone && !x->gl_loading\n        && glist_isvisible(x->gl_owner);\n    if (!flag && glist_isgraph(x))\n    {\n        if (can_graph_on_parent)\n            gobj_vis(&x->gl_gobj, x->gl_owner, 0);\n        x->gl_isgraph = x->gl_hidetext = 0;\n        if (can_graph_on_parent)\n        {\n            gobj_vis(&x->gl_gobj, x->gl_owner, 1);\n            canvas_fixlinesfor(x->gl_owner, &x->gl_obj);\n        }\n    }\n    else if (flag)\n    {\n        if (x->gl_pixwidth <= 0)\n            x->gl_pixwidth = GLIST_DEFGRAPHWIDTH;\n\n        if (x->gl_pixheight <= 0)\n            x->gl_pixheight = GLIST_DEFGRAPHHEIGHT;\n\n        if (can_graph_on_parent)\n            gobj_vis(&x->gl_gobj, x->gl_owner, 0);\n        x->gl_isgraph = 1;\n        x->gl_hidetext = !(!(flag&2));\n        x->gl_goprect = !nogoprect;\n        if (glist_isvisible(x) && x->gl_goprect)\n            glist_redraw(x);\n        if (can_graph_on_parent)\n        {\n            gobj_vis(&x->gl_gobj, x->gl_owner, 1);\n            canvas_fixlinesfor(x->gl_owner, &x->gl_obj);\n        }\n    }\n}\n\nvoid garray_properties(t_garray *x);\n\n    /* tell GUI to create a properties dialog on the canvas.  We tell\n       the user the negative of the \"pixel\" y scale to make it appear to grow\n       naturally upward, whereas pixels grow downward. */\nvoid canvas_properties(t_gobj*z, t_glist*unused)\n{\n    t_glist *x = (t_glist*)z;\n    t_gobj *y;\n    int isgraph = glist_isgraph(x);\n    t_float x1=0., y1=-1., x2=1., y2=1.;\n    t_float xscale=0., yscale=0.;\n    if(isgraph) {\n        x1=x->gl_x1;\n        y1=x->gl_y1;\n        x2=x->gl_x2;\n        y2=x->gl_y2;\n    } else {\n        xscale= glist_dpixtodx(x, 1);\n        yscale=-glist_dpixtody(x, 1);\n    }\n    pdgui_stub_vnew(&x->gl_pd, \"pdtk_canvas_dialog\", x, \"ff i ffff ii ii\",\n        xscale, yscale,  /* used to be %g ... */\n        isgraph,\n        x1,y1, x2,y2,  /* used to be %g ... */\n        (int)x->gl_pixwidth, (int)x->gl_pixheight,\n        (int)x->gl_xmargin, (int)x->gl_ymargin);\n\n        /* if any arrays are in the graph, put out their dialogs too */\n    for (y = x->gl_list; y; y = y->g_next)\n        if (pd_class(&y->g_pd) == garray_class)\n            garray_properties((t_garray *)y);\n}\n\n    /* called from the gui when \"OK\" is selected on the canvas properties\n       dialog.  Again we negate \"y\" scale. */\nstatic void canvas_donecanvasdialog(t_glist *x,\n    t_symbol *s, int argc, t_atom *argv)\n{\n    t_float xperpix, yperpix, x1, y1, x2, y2, xpix, ypix, xmargin, ymargin;\n    int graphme, redraw = 0, fromgui;\n\n    xperpix = atom_getfloatarg(0, argc, argv);\n    yperpix = atom_getfloatarg(1, argc, argv);\n    graphme = (int)(atom_getfloatarg(2, argc, argv));\n    x1 = atom_getfloatarg(3, argc, argv);\n    y1 = atom_getfloatarg(4, argc, argv);\n    x2 = atom_getfloatarg(5, argc, argv);\n    y2 = atom_getfloatarg(6, argc, argv);\n    xpix = atom_getfloatarg(7, argc, argv);\n    ypix = atom_getfloatarg(8, argc, argv);\n    xmargin = atom_getfloatarg(9, argc, argv);\n    ymargin = atom_getfloatarg(10, argc, argv);\n    fromgui = atom_getfloatarg(11, argc, argv);\n        /* hack - if graphme is 2 (meaning, not GOP but hide the text anyhow),\n           perhaps we're happier with 0.  This is only checked if this is really\n           being called, as intended, from the GUI.  For compatibility with old\n           patches that reverse-engineered donecanvasdialog to modify patch\n           parameters, we leave the buggy behavior in when there's no \"fromgui\"\n           argument supplied. */\n    if (fromgui && (!(graphme & 1)))\n        graphme = 0;\n        /* parent windows are treated the same as\n           applies to individual objects */\n    canvas_undo_add(x, UNDO_CANVAS_APPLY, \"apply\", canvas_undo_set_canvas(x));\n\n    x->gl_pixwidth = xpix;\n    x->gl_pixheight = ypix;\n    x->gl_xmargin = xmargin;\n    x->gl_ymargin = ymargin;\n\n    yperpix = -yperpix;\n    if (xperpix == 0)\n        xperpix = 1;\n    if (yperpix == 0)\n        yperpix = 1;\n\n    if (graphme)\n    {\n        if (x1 != x2)\n            x->gl_x1 = x1, x->gl_x2 = x2;\n        else x->gl_x1 = 0, x->gl_x2 = 1;\n        if (y1 != y2)\n            x->gl_y1 = y1, x->gl_y2 = y2;\n        else x->gl_y1 = 0, x->gl_y2 = 1;\n    }\n    else\n    {\n        if (xperpix != glist_dpixtodx(x, 1) || yperpix != glist_dpixtody(x, 1))\n            redraw = 1;\n        if (xperpix > 0)\n        {\n            x->gl_x1 = 0;\n            x->gl_x2 = xperpix;\n        }\n        else\n        {\n            x->gl_x1 = -xperpix * (x->gl_screenx2 - x->gl_screenx1);\n            x->gl_x2 = x->gl_x1 + xperpix;\n        }\n        if (yperpix > 0)\n        {\n            x->gl_y1 = 0;\n            x->gl_y2 = yperpix;\n        }\n        else\n        {\n            x->gl_y1 = -yperpix * (x->gl_screeny2 - x->gl_screeny1);\n            x->gl_y2 = x->gl_y1 + yperpix;\n        }\n    }\n        /* LATER avoid doing 2 redraws here (possibly one inside setgraph) */\n    canvas_setgraph(x, graphme, 0);\n    canvas_dirty(x, 1);\n    if (x->gl_havewindow)\n        canvas_redraw(x);\n    else if (!x->gl_isclone && glist_isvisible(x->gl_owner))\n    {\n        gobj_vis(&x->gl_gobj, x->gl_owner, 0);\n        gobj_vis(&x->gl_gobj, x->gl_owner, 1);\n    }\n}\n\n    /* called from the gui when a popup menu comes back with \"properties,\"\n       \"open,\" or \"help.\" */\nstatic void canvas_done_popup(t_canvas *x, t_float which,\n    t_float xpos, t_float ypos)\n{\n    char namebuf[MAXPDSTRING], *basenamep;\n    t_gobj *y;\n    for (y = x->gl_list; y; y = y->g_next)\n    {\n        int x1, y1, x2, y2;\n        if (canvas_hitbox(x, y, xpos, ypos, &x1, &y1, &x2, &y2))\n        {\n            if (which == 0)     /* properties */\n            {\n                if (!class_getpropertiesfn(pd_class(&y->g_pd)))\n                    continue;\n                (*class_getpropertiesfn(pd_class(&y->g_pd)))(y, x);\n                return;\n            }\n            else if (which == 1)    /* open */\n            {\n                if (!zgetfn(&y->g_pd, gensym(\"menu-open\")))\n                    continue;\n                vmess(&y->g_pd, gensym(\"menu-open\"), \"\");\n                return;\n            }\n            else    /* help */\n            {\n                const char *dir;\n                if (pd_class(&y->g_pd) == canvas_class &&\n                    canvas_isabstraction((t_canvas *)y))\n                {\n                    t_object *ob = (t_object *)y;\n                    int ac = binbuf_getnatom(ob->te_binbuf);\n                    t_atom *av = binbuf_getvec(ob->te_binbuf);\n                    if (ac < 1)\n                        return;\n                    atom_string(av, namebuf, MAXPDSTRING);\n\n                        /* strip dir from name : */\n                    basenamep = strrchr(namebuf, '/');\n#ifdef _WIN32\n                    if (!basenamep)\n                        basenamep = strrchr(namebuf, '\\\\');\n#endif\n                    if (!basenamep)\n                        basenamep = namebuf;\n                    else basenamep++;   /* strip last '/' */\n\n                    dir = canvas_getdir((t_canvas *)y)->s_name;\n                }\n                else\n                {\n                    strncpy(namebuf, class_gethelpname(pd_class(&y->g_pd)),\n                        MAXPDSTRING-1);\n                    namebuf[MAXPDSTRING-1] = 0;\n                    dir = class_gethelpdir(pd_class(&y->g_pd));\n                    basenamep = namebuf;\n                }\n                if (strlen(namebuf) < 4 ||\n                    strcmp(namebuf + strlen(namebuf) - 3, \".pd\"))\n                        strcat(namebuf, \".pd\");\n                open_via_helppath(basenamep, dir);\n                return;\n            }\n        }\n    }\n    if (which == 0)\n        canvas_properties(&x->gl_gobj, 0);\n    else if (which == 2)\n        open_via_helppath(\"intro.pd\", canvas_getdir((t_canvas *)x)->s_name);\n}\n\n#define NOMOD 0\n#define SHIFTMOD 1\n#define CTRLMOD 2\n#define ALTMOD 4\n#define RIGHTCLICK 8\n\n#define DCLICKINTERVAL 0.25\n\n    /* undarken deselected gatoms:\n     * it's slightly ugly to have this in here,  but we cannot undarken\n     * in gatom_key (which is the gatom's e_keyfn) as this is also called\n     * when the user just hits <kbd>Enter</kbd>\n     */\nvoid gatom_undarken(t_text *x);\nstatic void undarken_if_gatom(t_gobj*gobj)\n{\n    t_object*obj = gobj?pd_checkobject(&gobj->g_pd):0;\n    if(obj && T_ATOM == obj->te_type)\n        gatom_undarken(obj);\n}\n\n    /* mouse click */\nstatic void canvas_doclick(t_canvas *x, int xpos, int ypos, int which,\n    int mod, int doit)\n{\n    t_gobj *hitbox;\n    int shiftmod, runmode, altmod, doublemod = 0, rightclick;\n    int x1=0, y1=0, x2=0, y2=0, clickreturned = 0;\n    t_text *hitobj;\n\n    if (!x->gl_editor)\n    {\n        bug(\"editor\");\n        return;\n    }\n\n    shiftmod = (mod & SHIFTMOD);\n    runmode = ((mod & CTRLMOD) || (!x->gl_edit));\n    altmod = (mod & ALTMOD);\n    rightclick = (mod & RIGHTCLICK);\n\n    EDITOR->canvas_undo_already_set_move = 0;\n\n        /* if keyboard was grabbed, notify grabber and cancel the grab */\n    if (doit && x->gl_editor->e_grab && x->gl_editor->e_keyfn)\n    {\n        (* x->gl_editor->e_keyfn) (x->gl_editor->e_grab, &s_, 0);\n        undarken_if_gatom(x->gl_editor->e_grab);\n        glist_grab(x, 0, 0, 0, 0, 0);\n    }\n\n    if (doit && xpos == EDITOR->canvas_upx &&\n        ypos == EDITOR->canvas_upy &&\n        sys_getrealtime() - EDITOR->canvas_upclicktime < DCLICKINTERVAL)\n            doublemod = 1;\n    x->gl_editor->e_lastmoved = 0;\n    if (doit)\n    {\n        x->gl_editor->e_grab = 0;\n        x->gl_editor->e_onmotion = MA_NONE;\n    }\n#if 0\n    post(\"click %d %d %d %d\", xpos, ypos, which, mod);\n#endif\n\n    if (x->gl_editor->e_onmotion != MA_NONE)\n        return;\n\n    x->gl_editor->e_xwas = xpos;\n    x->gl_editor->e_ywas = ypos;\n\n    if (runmode && !rightclick)\n    {\n            /* is a text activated ? */\n        if (x->gl_editor->e_textedfor  && doit)\n        {\n            hitobj = rtext_getowner(x->gl_editor->e_textedfor);\n            if (canvas_hitbox(x, &hitobj->te_g,\n                xpos, ypos, &x1, &y1, &x2, &y2))\n            {\n                rtext_mouse(x->gl_editor->e_textedfor, xpos - x1, ypos - y1,\n                    (shiftmod? RTEXT_SHIFT :\n                        (doublemod ? RTEXT_DBL : RTEXT_DOWN)));\n                    x->gl_editor->e_onmotion = MA_DRAGTEXT;\n                    x->gl_editor->e_xwas = x1;\n                    x->gl_editor->e_ywas = y1;\n            }\n            else\n            {\n                rtext_retext(x->gl_editor->e_textedfor);\n                rtext_activate(x->gl_editor->e_textedfor, 0);\n            }\n            return;\n        }\n        for (hitbox = x->gl_list; hitbox; hitbox = hitbox->g_next)\n        {\n                /* check if the object wants to be clicked */\n            if (canvas_hitbox(x, hitbox, xpos, ypos, &x1, &y1, &x2, &y2)\n                && (clickreturned = gobj_click(hitbox, x, xpos, ypos,\n                    shiftmod, ((mod & CTRLMOD) && (!x->gl_edit)) || altmod,\n                        doublemod, doit)))\n                            break;\n        }\n        if (!doit)\n        {\n            if (hitbox)\n                canvas_setcursor(x, clickreturned);\n            else canvas_setcursor(x, CURSOR_RUNMODE_NOTHING);\n        }\n        return;\n    }\n        /* if not a runmode left click, fall here. */\n    hitbox = canvas_findhitbox(x, xpos, ypos, &x1, &y1, &x2, &y2);\n    hitobj = (hitbox ? pd_checkobject(&hitbox->g_pd) : 0);\n        /* if text is activated while we're in locked state, this must\n        be a number box - so if we clicked outside the box, deactivate it.\n        This is a different situation from the \"deselect\" action below\n        when we're unlocked.  */\n    /* if (doit) post(\"%d %x %x %x\", x->gl_edit, x->gl_editor->e_textedfor,\n        hitobj, (hitobj ? glist_findrtext(x, hitobj) : 0));\n    if (doit && (!x->gl_edit) && x->gl_editor->e_textedfor &&\n        (!hitobj || (glist_findrtext(x, hitobj) != x->gl_editor->e_textedfor)))\n            rtext_activate(x->gl_editor->e_textedfor, 0);   */\n    if (hitbox)\n    {\n            /* we're in a rectangle */\n        if (rightclick)\n            canvas_rightclick(x, xpos, ypos, hitbox);\n        else if (shiftmod)\n        {\n            if (doit)\n            {\n                t_rtext *rt;\n                if (hitobj && (rt = x->gl_editor->e_textedfor) &&\n                    rt == glist_findrtext(x, hitobj))\n                {\n                    rtext_mouse(rt, xpos - x1, ypos - y1, RTEXT_SHIFT);\n                    x->gl_editor->e_onmotion = MA_DRAGTEXT;\n                    x->gl_editor->e_xwas = x1;\n                    x->gl_editor->e_ywas = y1;\n                }\n                else\n                {\n                    if (glist_isselected(x, hitbox))\n                        glist_deselect(x, hitbox);\n                    else glist_select(x, hitbox);\n                }\n            }\n        }\n        else\n        {\n            int noutlet;\n            int out_activeminh = (OHEIGHT + 1)  * x->gl_zoom;\n            int out_activemaxh = (y2 - y1) / 4;\n            int out_activeheight = OHEIGHT * 2 * x->gl_zoom;\n            if (out_activeheight > out_activemaxh) out_activeheight = out_activemaxh;\n            if (out_activeheight < out_activeminh) out_activeheight = out_activeminh;\n                /* resize? only for \"true\" text boxes or canvases */\n            if (xpos >= x2-4 && ypos < y2-4 && hitobj &&\n                    (hitobj->te_pd->c_wb == &text_widgetbehavior ||\n                    hitobj->te_type == T_ATOM ||\n                    pd_checkglist(&hitobj->te_pd)))\n            {\n                if (doit)\n                {\n                    if (!glist_isselected(x, hitbox))\n                    {\n                        glist_noselect(x);\n                        glist_select(x, hitbox);\n                    }\n                    x->gl_editor->e_onmotion = MA_RESIZE;\n                    x->gl_editor->e_xwas = x1;\n                    x->gl_editor->e_ywas = y1;\n                    x->gl_editor->e_xnew = xpos;\n                    x->gl_editor->e_ynew = ypos;\n                    canvas_undo_add(x, UNDO_APPLY, \"resize\",\n                        canvas_undo_set_apply(x, glist_getindex(x, hitbox)));\n                }\n                else canvas_setcursor(x, CURSOR_EDITMODE_RESIZE);\n            }\n                /* look for an outlet */\n            else if (hitobj && (noutlet = obj_noutlets(hitobj)) &&\n                ypos >= y2 - out_activeheight)\n            {\n                int width = x2 - x1;\n                int iow = IOWIDTH * x->gl_zoom;\n                int nout1 = (noutlet > 1 ? noutlet - 1 : 1);\n                int closest = ((xpos-x1) * (nout1) + width/2)/width;\n                if (noutlet == 1 || closest < noutlet)\n                {\n                    if (doit)\n                    {\n                        int issignal = obj_issignaloutlet(hitobj, closest);\n                        int xout = x1 + IOMIDDLE * x->gl_zoom +\n                            (noutlet > 1 ? ((width - iow) * closest)/nout1 : 0);\n                        x->gl_editor->e_onmotion = MA_CONNECT;\n                        x->gl_editor->e_xwas = xout;\n                        x->gl_editor->e_ywas = y2;\n                        pdgui_vmess(\"::pdtk_canvas::cords_to_foreground\", \"ci\", x, 0);\n                        pdgui_vmess(0, \"crr iiii ri rs\",\n                            x, \"create\", \"line\",\n                            x->gl_editor->e_xwas,x->gl_editor->e_ywas, xpos,ypos,\n                            \"-width\", (issignal ? 2 : 1) * x->gl_zoom,\n                            \"-tags\", \"x\");\n                    }\n                    else canvas_setcursor(x, CURSOR_EDITMODE_CONNECT);\n                }\n                else if (doit)\n                    goto nooutletafterall;\n                else canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);\n            }\n                /* not in an outlet; select and move */\n            else if (doit)\n            {\n                t_rtext *rt;\n                    /* check if the box is being text edited */\n            nooutletafterall:\n                if (hitobj && (rt = x->gl_editor->e_textedfor) &&\n                    rt == glist_findrtext(x, hitobj))\n                {\n                    rtext_mouse(rt, xpos - x1, ypos - y1,\n                        (doublemod ? RTEXT_DBL : RTEXT_DOWN));\n                    x->gl_editor->e_onmotion = MA_DRAGTEXT;\n                    x->gl_editor->e_xwas = x1;\n                    x->gl_editor->e_ywas = y1;\n                }\n                else\n                {\n                        /* otherwise select and drag to displace */\n                    if (!glist_isselected(x, hitbox))\n                    {\n                        glist_noselect(x);\n                        glist_select(x, hitbox);\n                    }\n                    x->gl_editor->e_onmotion = MA_MOVE;\n                }\n            }\n            else canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);\n        }\n        return;\n    }\n        /* if right click doesn't hit any boxes, call rightclick\n           routine anyway */\n    if (rightclick)\n        canvas_rightclick(x, xpos, ypos, 0);\n\n        /* if not an editing action, and if we didn't hit a\n           box, set cursor and return */\n    if (runmode || rightclick)\n    {\n        canvas_setcursor(x, CURSOR_RUNMODE_NOTHING);\n        return;\n    }\n        /* having failed to find a box, we try lines now. */\n    if (!runmode && !altmod)\n    {\n        t_linetraverser t;\n        t_outconnect *oc;\n        t_float fx = xpos, fy = ypos;\n        t_glist *glist2 = glist_getcanvas(x);\n        linetraverser_start(&t, glist2);\n        while ((oc = linetraverser_next(&t)))\n        {\n            int outindex, inindex;\n            t_float lx1 = t.tr_lx1, ly1 = t.tr_ly1,\n                lx2 = t.tr_lx2, ly2 = t.tr_ly2;\n            t_float area = (lx2 - lx1) * (fy - ly1) -\n                (ly2 - ly1) * (fx - lx1);\n            t_float dsquare = (lx2-lx1) * (lx2-lx1) + (ly2-ly1) * (ly2-ly1);\n            if (area * area >= 50 * dsquare) continue;\n            if ((lx2-lx1) * (fx-lx1) + (ly2-ly1) * (fy-ly1) < 0) continue;\n            if ((lx2-lx1) * (lx2-fx) + (ly2-ly1) * (ly2-fy) < 0) continue;\n            outindex = canvas_getindex(glist2, &t.tr_ob->ob_g);\n            inindex = canvas_getindex(glist2, &t.tr_ob2->ob_g);\n            if (shiftmod)\n            {\n                int soutindex, sinindex, soutno, sinno;\n                    /* if no line is selected, just add this line to the selection */\n                if(!x->gl_editor->e_selectedline)\n                {\n                    if (doit)\n                    {\n                        glist_selectline(glist2, oc,\n                            outindex, t.tr_outno,\n                            inindex, t.tr_inno);\n                    }\n                    canvas_setcursor(x, CURSOR_EDITMODE_DISCONNECT);\n                    return;\n                }\n                soutindex = x->gl_editor->e_selectline_index1;\n                sinindex = x->gl_editor->e_selectline_index2;\n                soutno = x->gl_editor->e_selectline_outno;\n                sinno = x->gl_editor->e_selectline_inno;\n                        /* if the hovered line is already selected, deselect it */\n                if ((outindex == soutindex) && (inindex == sinindex)\n                    && (soutno == t.tr_outno) && (sinno == t.tr_inno))\n                {\n                    if(doit)\n                        glist_deselectline(x);\n                    canvas_setcursor(x, CURSOR_EDITMODE_DISCONNECT);\n                    return;\n                }\n\n                    /* swap selected and hovered connection */\n                if ((!x->gl_editor->e_selection)\n                    && ((outindex == soutindex) || (inindex == sinindex)))\n                {\n                    if(doit)\n                    {\n                        canvas_undo_add(x, UNDO_SEQUENCE_START, \"reconnect\", 0);\n                        canvas_disconnect_with_undo(x, soutindex, soutno, sinindex, sinno);\n                        canvas_disconnect_with_undo(x, outindex, t.tr_outno, inindex, t.tr_inno);\n                        canvas_connect_with_undo(x, outindex, t.tr_outno, sinindex, sinno);\n                        canvas_connect_with_undo(x, soutindex, soutno, inindex, t.tr_inno);\n                        canvas_undo_add(x, UNDO_SEQUENCE_END, \"reconnect\", 0);\n\n                        x->gl_editor->e_selectline_index1 = soutindex;\n                        x->gl_editor->e_selectline_outno = soutno;\n                        x->gl_editor->e_selectline_index2 = inindex;\n                        x->gl_editor->e_selectline_inno = t.tr_inno;\n\n                        canvas_dirty(x, 1);\n                    }\n                    canvas_setcursor(x, CURSOR_EDITMODE_DISCONNECT);\n                    return;\n                }\n            }\n            if (!shiftmod)\n            {\n                    /* !shiftmode: clear selection before selecting line */\n                if (doit)\n                {\n                    glist_noselect(x);\n                    glist_selectline(glist2, oc,\n                        outindex, t.tr_outno,\n                        inindex, t.tr_inno);\n                }\n                canvas_setcursor(x, CURSOR_EDITMODE_DISCONNECT);\n                return;\n            }\n        }\n    }\n    canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);\n    if (doit)\n    {\n        if (!shiftmod) glist_noselect(x);\n        pdgui_vmess(0, \"crr iiii rs\",\n            x, \"create\", \"rectangle\",\n            xpos,ypos, xpos,ypos,\n            \"-tags\", \"x\");\n        x->gl_editor->e_xwas = xpos;\n        x->gl_editor->e_ywas = ypos;\n        x->gl_editor->e_onmotion = MA_REGION;\n    }\n}\n\nvoid canvas_mouse(t_canvas *x, t_floatarg xpos, t_floatarg ypos,\n    t_floatarg which, t_floatarg mod)\n{\n    canvas_doclick(x, xpos, ypos, which, mod, 1);\n}\n\nint canvas_isconnected (t_canvas *x, t_text *ob1, int n1,\n    t_text *ob2, int n2)\n{\n    t_linetraverser t;\n    t_outconnect *oc;\n    linetraverser_start(&t, x);\n    while ((oc = linetraverser_next(&t)))\n        if (t.tr_ob == ob1 && t.tr_outno == n1 &&\n            t.tr_ob2 == ob2 && t.tr_inno == n2)\n                return (1);\n    return (0);\n}\n\nstatic int canconnect(t_canvas*x, t_object*src, int nout, t_object*sink, int nin)\n{\n    if (!src || !sink || sink == src) /* do source and sink exist (and are not the same)?*/\n        return 0;\n    if (nin >= obj_ninlets(sink) || (nout >= obj_noutlets(src))) /* do the requested iolets exist? */\n        return 0;\n    if (canvas_isconnected(x, src, nout, sink, nin)) /* are the objects already connected? */\n        return 0;\n    return (!obj_issignaloutlet(src, nout) || /* are the iolets compatible? */\n            obj_issignalinlet(sink, nin));\n}\n\nstatic int tryconnect(t_canvas*x, t_object*src, int nout, t_object*sink, int nin)\n{\n    if(canconnect(x, src, nout, sink, nin))\n    {\n        t_outconnect *oc = obj_connect(src, nout, sink, nin);\n        if(oc)\n        {\n            int iow = IOWIDTH * x->gl_zoom;\n            int iom = IOMIDDLE * x->gl_zoom;\n            int x11=0, x12=0, x21=0, x22=0;\n            int y11=0, y12=0, y21=0, y22=0;\n            int noutlets1, ninlets, lx1, ly1, lx2, ly2;\n            char tag[128];\n            char*tags[] = {tag, \"cord\"};\n            sprintf(tag, \"l%p\", oc);\n            gobj_getrect(&src->ob_g, x, &x11, &y11, &x12, &y12);\n            gobj_getrect(&sink->ob_g, x, &x21, &y21, &x22, &y22);\n\n            noutlets1 = obj_noutlets(src);\n            ninlets = obj_ninlets(sink);\n\n            lx1 = x11 + (noutlets1 > 1 ?\n                             ((x12-x11-iow) * nout)/(noutlets1-1) : 0)\n                + iom;\n            ly1 = y12;\n            lx2 = x21 + (ninlets > 1 ?\n                             ((x22-x21-iow) * nin)/(ninlets-1) : 0)\n                + iom;\n            ly2 = y21;\n            pdgui_vmess(0, \"crr iiii ri rS\",\n                glist_getcanvas(x), \"create\", \"line\",\n                lx1,ly1, lx2,ly2,\n                \"-width\", (obj_issignaloutlet(src, nout) ? 2 : 1) * x->gl_zoom,\n                \"-tags\", 2, tags);\n            canvas_undo_add(x, UNDO_CONNECT, \"connect\", canvas_undo_set_connect(x,\n                    canvas_getindex(x, &src->ob_g), nout,\n                    canvas_getindex(x, &sink->ob_g), nin));\n            canvas_dirty(x, 1);\n            return 1;\n        }\n    }\n    return 0;\n}\n\nstatic void canvas_doconnect(t_canvas *x, int xpos, int ypos, int mod, int doit)\n{\n    int x11=0, y11=0, x12=0, y12=0;\n    t_gobj *y1;\n    int x21=0, y21=0, x22=0, y22=0;\n    t_gobj *y2;\n    int xwas = x->gl_editor->e_xwas,\n        ywas = x->gl_editor->e_ywas;\n#if 0\n    post(\"canvas_doconnect(%p, %d, %d, %d, %d)\", x, xpos, ypos, mod, doit);\n#endif\n    if (doit) {\n        pdgui_vmess(\"::pdtk_canvas::cords_to_foreground\", \"ci\", x, 1);\n        pdgui_vmess(0, \"crs\", x, \"delete\", \"x\");\n    }\n    else\n        pdgui_vmess(0, \"crs iiii\",\n            x, \"coords\", \"x\",\n            x->gl_editor->e_xwas,x->gl_editor->e_ywas, xpos,ypos);\n\n    if ((y1 = canvas_findhitbox(x, xwas, ywas, &x11, &y11, &x12, &y12))\n        && (y2 = canvas_findhitbox(x, xpos, ypos, &x21, &y21, &x22, &y22)))\n    {\n        t_object *ob1 = pd_checkobject(&y1->g_pd);\n        t_object *ob2 = pd_checkobject(&y2->g_pd);\n        int noutlet1, ninlet2;\n        if (ob1 && ob2 && ob1 != ob2 &&\n            (noutlet1 = obj_noutlets(ob1))\n            && (ninlet2 = obj_ninlets(ob2)))\n        {\n            int width1 = x12 - x11, closest1, hotspot1;\n            int width2 = x22 - x21, closest2, hotspot2;\n\n            if (noutlet1 > 1)\n            {\n                closest1 = ((xwas-x11) * (noutlet1-1) + width1/2)/width1;\n                hotspot1 = x11 +\n                    (width1 - IOWIDTH) * closest1 / (noutlet1-1);\n            }\n            else closest1 = 0, hotspot1 = x11;\n\n            if (ninlet2 > 1)\n            {\n                closest2 = ((xpos-x21) * (ninlet2-1) + width2/2)/width2;\n                hotspot2 = x21 +\n                    (width2 - IOWIDTH) * closest2 / (ninlet2-1);\n            }\n            else closest2 = 0, hotspot2 = x21;\n\n            if (closest1 >= noutlet1)\n                closest1 = noutlet1 - 1;\n            if (closest2 >= ninlet2)\n                closest2 = ninlet2 - 1;\n\n            if (canvas_isconnected (x, ob1, closest1, ob2, closest2))\n            {\n                canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);\n                return;\n            }\n            if (obj_issignaloutlet(ob1, closest1) &&\n                !obj_issignalinlet(ob2, closest2))\n            {\n                if (doit)\n                    pd_error(0, \"can't connect audio signal outlet to nonsignal inlet\");\n                canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);\n                return;\n            }\n            if (doit)\n            {\n                t_selection *sel;\n                int selmode = 0;\n                canvas_undo_add(x, UNDO_SEQUENCE_START, \"connect\", 0);\n                tryconnect(x, ob1, closest1, ob2, closest2);\n                canvas_dirty(x, 1);\n                    /* now find out if either ob1 xor ob2 are part of the selection,\n                     * and if so, connect the rest of the selection as well */\n                if(mod & SHIFTMOD) /* intelligent patching needs to be activated by modifier key */\n                    selmode = glist_isselected(x, &ob1->ob_g) + 2 * glist_isselected(x, &ob2->ob_g);\n                switch(selmode) {\n                case 3: /* both source and sink are selected */\n                        /* if only the source & sink are selected, keep connecting them */\n                    if(0 == x->gl_editor->e_selection->sel_next->sel_next)\n                    {\n                        int i, j;\n                        for(i=closest1, j=closest2; (i < noutlet1) && (j < ninlet2); i++, j++ )\n                            tryconnect(x, ob1, i, ob2, j);\n                    }\n                    else\n                            /* if other objects are selected as well, connect those either as\n                             * sources or sinks, whichever allows for more connections\n                             */\n                    {\n                            /* get a left-right sorted list of all selected objects\n                             * (but the already connected ones)\n                             * count the possibles sinks and sources\n                             */\n                        int mode = 0;\n                        int i;\n                        int sinks = 0, sources = 0;\n                        t_float ysinks = 0., ysources = 0.;\n                        int msgout = !obj_issignaloutlet(ob1, closest1);\n                        int sigin = obj_issignalinlet(ob2, closest2);\n                        t_selection*sortedsel = 0;\n                            /* sort the selected objects from left-right */\n                        for(sel = x->gl_editor->e_selection, i=1; sel; sel = sel->sel_next, i++)\n                        {\n                            t_object*ob = pd_checkobject(&sel->sel_what->g_pd);\n                            t_selection*sob = 0;\n                                /* skip illegal objects and the reference source&sink */\n                            if (!ob || (ob1 == ob) || (ob2 == ob))\n                                continue;\n\n                            if (canconnect(x, ob1, closest1 + 1 + sinks, ob, closest2))\n                            {\n                                sinks += 1;\n                                ysinks += ob->te_ypix;\n                            }\n                            if (canconnect(x, ob, closest1, ob2, closest2 + 1 + sources))\n                            {\n                                sources += 1;\n                                ysources += ob->te_ypix;\n                            }\n\n                                /* insert the object into the sortedsel list */\n                            if((sob = getbytes(sizeof(*sob)))) {\n                                t_selection*s, *slast=0;\n                                sob->sel_what = &ob->te_g;\n                                for(s=sortedsel; s; s=s->sel_next)\n                                {\n                                    t_object*o = pd_checkobject(&s->sel_what->g_pd);\n                                    if(!o) continue;\n                                    if((ob->te_xpix < o->te_xpix) || ((ob->te_xpix == o->te_xpix) && (ob->te_ypix < o->te_ypix)))\n                                    {\n                                        sob->sel_next = s;\n                                        if(slast)\n                                            slast->sel_next = sob;\n                                        else\n                                            sortedsel = sob;\n                                        break;\n                                    }\n                                    slast=s;\n                                }\n                                if(slast)\n                                    slast->sel_next = sob;\n                                else\n                                    sortedsel = sob;\n                            }\n                        }\n                            /* try to maximize connections */\n                        mode = (sinks > sources);\n\n                            /* maximizing failed, so prefer to connect from top to bottom */\n                        if (sinks && (sinks == sources)) {\n                            mode = ((ysinks - ob1->te_ypix) / sinks) > ((ysources - ob2->te_ypix) / sources) * -1.;\n                        }\n\n                        sinks = 0;\n                        sources = 0;\n                        if (mode)\n                            for(sel=sortedsel; ((closest1 + 1 + sinks) < noutlet1) && sel; sel=sel->sel_next)\n                            {\n                                sinks += tryconnect(x,\n                                                    ob1, closest1 + 1 + sinks,\n                                                    pd_checkobject(&sel->sel_what->g_pd), closest2);\n                            }\n                        else\n                            for(sel=sortedsel; ((closest2 + 1 + sources) < ninlet2) && sel; sel=sel->sel_next)\n                            {\n                                sources += tryconnect(x,\n                                                      pd_checkobject(&sel->sel_what->g_pd), closest1,\n                                                      ob2, closest2 + 1 + sources);\n                            }\n\n                            /* free the sorted list of selections */\n                        for(sel=sortedsel; sel; )\n                        {\n                            t_selection*s = sel->sel_next;\n                            freebytes(sel, sizeof(*sel));\n                            sel = s;\n                        }\n                    }\n                    break;\n                case 1: /* source(s) selected */\n                    for(sel = x->gl_editor->e_selection; sel; sel = sel->sel_next)\n                    {\n                        t_object*selo = pd_checkobject(&sel->sel_what->g_pd);\n                        if (!selo || selo == ob1)\n                            continue;\n                        tryconnect(x, selo, closest1, ob2, closest2);\n                    }\n                    break;\n                case 2: /* sink(s) selected */\n                    for(sel = x->gl_editor->e_selection; sel; sel = sel->sel_next)\n                    {\n                        t_object*selo = pd_checkobject(&sel->sel_what->g_pd);\n                        if (!selo || selo == ob2)\n                            continue;\n                        tryconnect(x, ob1, closest1, selo, closest2);\n                    }\n                    break;\n                default: break;\n                }\n                canvas_undo_add(x, UNDO_SEQUENCE_END, \"connect\", 0);\n            }\n            else canvas_setcursor(x, CURSOR_EDITMODE_CONNECT);\n            return;\n        }\n    }\n    canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);\n}\n\nvoid canvas_selectinrect(t_canvas *x, int lox, int loy, int hix, int hiy)\n{\n    t_gobj *y;\n    for (y = x->gl_list; y; y = y->g_next)\n    {\n        int x1, y1, x2, y2;\n        gobj_getrect(y, x, &x1, &y1, &x2, &y2);\n        if (hix >= x1 && lox <= x2 && hiy >= y1 && loy <= y2\n            && !glist_isselected(x, y))\n                glist_select(x, y);\n    }\n}\n\nstatic void canvas_doregion(t_canvas *x, int xpos, int ypos, int doit)\n{\n    if (doit)\n    {\n        int lox, loy, hix, hiy;\n        if (x->gl_editor->e_xwas < xpos)\n            lox = x->gl_editor->e_xwas, hix = xpos;\n        else hix = x->gl_editor->e_xwas, lox = xpos;\n        if (x->gl_editor->e_ywas < ypos)\n            loy = x->gl_editor->e_ywas, hiy = ypos;\n        else hiy = x->gl_editor->e_ywas, loy = ypos;\n        canvas_selectinrect(x, lox, loy, hix, hiy);\n        pdgui_vmess(0, \"crs\", x, \"delete\", \"x\");\n        x->gl_editor->e_onmotion = MA_NONE;\n    }\n    else\n        pdgui_vmess(0, \"crs iiii\",\n            x, \"coords\", \"x\",\n            x->gl_editor->e_xwas,x->gl_editor->e_ywas, xpos,ypos);\n}\n\nvoid canvas_mouseup(t_canvas *x,\n    t_floatarg fxpos, t_floatarg fypos, t_floatarg fwhich,\n    t_floatarg fmod)\n{\n    int xpos = fxpos, ypos = fypos, which = fwhich;\n    int mod = fmod;\n#if 0\n    post(\"mouseup %d %d %d %d\", xpos, ypos, which, mod);\n#endif\n    if (!x->gl_editor)\n    {\n        bug(\"editor\");\n        return;\n    }\n\n    EDITOR->canvas_upclicktime = sys_getrealtime();\n    EDITOR->canvas_upx = xpos;\n    EDITOR->canvas_upy = ypos;\n\n    if (x->gl_editor->e_onmotion == MA_CONNECT)\n        canvas_doconnect(x, xpos, ypos, mod, 1);\n    else if (x->gl_editor->e_onmotion == MA_REGION)\n        canvas_doregion(x, xpos, ypos, 1);\n    else if ((x->gl_editor->e_onmotion == MA_MOVE ||\n              x->gl_editor->e_onmotion == MA_RESIZE))\n    {\n            /* if there's only one text item selected activate the text.\n            LATER consider under sme conditions not activating it, for instance\n            if it appears to have been desired only to move the object.  Maybe\n            shift-click could allow dragging without activating text?  A\n            different solution (only activating if the object wasn't moved\n            (commit f0df4e586) turned out to flout ctrlD+move+retype. */\n        if (x->gl_editor->e_selection &&\n            !(x->gl_editor->e_selection->sel_next))\n        {\n            t_gobj *g = x->gl_editor->e_selection->sel_what;\n            t_glist *gl2;\n                /* first though, check we aren't an abstraction with a\n                   dirty sub-patch that would be discarded if we edit this. */\n            if (canvas_undo_confirmdiscard(g))\n                return;\n                /* OK, activate it */\n            gobj_activate(x->gl_editor->e_selection->sel_what, x, 1);\n        }\n    }\n    else if (x->gl_editor->e_onmotion == MA_PASSOUT)\n    {\n        if (!x->gl_editor->e_motionfn)\n            bug(\"e_motionfn\");\n        (*x->gl_editor->e_motionfn)(&x->gl_editor->e_grab->g_pd,\n            xpos - x->gl_editor->e_xwas, ypos - x->gl_editor->e_ywas, 1);\n    }\n    x->gl_editor->e_onmotion = MA_NONE;\n}\n\n    /* displace the selection by (dx, dy) pixels */\nstatic void canvas_displaceselection(t_canvas *x, int dx, int dy)\n{\n    t_selection *y;\n    int resortin = 0, resortout = 0;\n    if (x->gl_editor->e_selection && !EDITOR->canvas_undo_already_set_move)\n    {\n        canvas_undo_add(x, UNDO_MOTION, \"motion\", canvas_undo_set_move(x, 1));\n        EDITOR->canvas_undo_already_set_move = 1;\n    }\n    for (y = x->gl_editor->e_selection; y; y = y->sel_next)\n    {\n        t_class *cl = pd_class(&y->sel_what->g_pd);\n        gobj_displace(y->sel_what, x, dx, dy);\n        if (cl == vinlet_class) resortin = 1;\n        else if (cl == voutlet_class) resortout = 1;\n    }\n    if (resortin) canvas_resortinlets(x);\n    if (resortout) canvas_resortoutlets(x);\n    pdgui_vmess(\"pdtk_canvas_getscroll\", \"c\", x);\n    if (x->gl_editor->e_selection)\n        canvas_dirty(x, 1);\n}\n\n    /* this routine is called whenever a key is pressed or released.  \"x\"\n       may be zero if there's no current canvas.  The first argument is true or\n       false for down/up; the second one is either a symbolic key name (e.g.,\n       \"Right\" or an Ascii key number.  The third is the shift key. */\nvoid canvas_key(t_canvas *x, t_symbol *s, int ac, t_atom *av)\n{\n    int keynum, fflag;\n    t_symbol *gotkeysym;\n\n    int down, shift;\n\n    if (ac < 3)\n        return;\n\n    EDITOR->canvas_undo_already_set_move = 0;\n    down = (atom_getfloat(av) != 0);  /* nonzero if it's a key down */\n    shift = (atom_getfloat(av+2) != 0);  /* nonzero if shift-ed */\n    if (av[1].a_type == A_SYMBOL)\n        gotkeysym = av[1].a_w.w_symbol;\n    else if (av[1].a_type == A_FLOAT)\n    {\n        char buf[UTF8_MAXBYTES1];\n        switch((int)(av[1].a_w.w_float))\n        {\n        case 8:  gotkeysym = gensym(\"BackSpace\"); break;\n        case 9:  gotkeysym = gensym(\"Tab\"); break;\n        case 10: gotkeysym = gensym(\"Return\"); break;\n        case 27: gotkeysym = gensym(\"Escape\"); break;\n        case 32: gotkeysym = gensym(\"Space\"); break;\n        case 127:gotkeysym = gensym(\"Delete\"); break;\n        default:\n                /*-- moo: assume keynum is a Unicode codepoint; encode as UTF-8 --*/\n            u8_wc_toutf8_nul(buf, (UCS4)(av[1].a_w.w_float));\n            gotkeysym = gensym(buf);\n        }\n    }\n    else gotkeysym = gensym(\"?\");\n    fflag = (av[0].a_type == A_FLOAT ? av[0].a_w.w_float : 0);\n    keynum = (av[1].a_type == A_FLOAT ? av[1].a_w.w_float : 0);\n    if (keynum == '{' || keynum == '}')\n    {\n        post(\"keycode %d: dropped\", (int)keynum);\n        return;\n    }\n#if 0\n    post(\"keynum %d, down %d\", (int)keynum, down);\n#endif\n    if (keynum == '\\r') keynum = '\\n';\n    if (av[1].a_type == A_SYMBOL &&\n        !strcmp(av[1].a_w.w_symbol->s_name, \"Return\"))\n            keynum = '\\n';\n        /* alias Apple key numbers to symbols.  This is done unconditionally,\n           not just if we're on an Apple, just in case the GUI is remote. */\n    if (keynum == 30 || keynum == 63232)\n        keynum = 0, gotkeysym = gensym(\"Up\");\n    else if (keynum == 31 || keynum == 63233)\n        keynum = 0, gotkeysym = gensym(\"Down\");\n    else if (keynum == 28 || keynum == 63234)\n        keynum = 0, gotkeysym = gensym(\"Left\");\n    else if (keynum == 29 || keynum == 63235)\n        keynum = 0, gotkeysym = gensym(\"Right\");\n    else if (keynum == 63273)\n        keynum = 0, gotkeysym = gensym(\"Home\");\n    else if (keynum == 63275)\n        keynum = 0, gotkeysym = gensym(\"End\");\n    else if (keynum == 63276)\n        keynum = 0, gotkeysym = gensym(\"Prior\");\n    else if (keynum == 63277)\n        keynum = 0, gotkeysym = gensym(\"Next\");\n    else if (keynum == 63236)\n        keynum = 0, gotkeysym = gensym(\"F1\");\n    else if (keynum == 63237)\n        keynum = 0, gotkeysym = gensym(\"F2\");\n    else if (keynum == 63238)\n        keynum = 0, gotkeysym = gensym(\"F3\");\n    else if (keynum == 63239)\n        keynum = 0, gotkeysym = gensym(\"F4\");\n    else if (keynum == 63240)\n        keynum = 0, gotkeysym = gensym(\"F5\");\n    else if (keynum == 63241)\n        keynum = 0, gotkeysym = gensym(\"F6\");\n    else if (keynum == 63242)\n        keynum = 0, gotkeysym = gensym(\"F7\");\n    else if (keynum == 63243)\n        keynum = 0, gotkeysym = gensym(\"F8\");\n    else if (keynum == 63244)\n        keynum = 0, gotkeysym = gensym(\"F9\");\n    else if (keynum == 63245)\n        keynum = 0, gotkeysym = gensym(\"F10\");\n    else if (keynum == 63246)\n        keynum = 0, gotkeysym = gensym(\"F11\");\n    else if (keynum == 63247)\n        keynum = 0, gotkeysym = gensym(\"F12\");\n    if (gensym(\"#key\")->s_thing && down)\n        pd_float(gensym(\"#key\")->s_thing, (t_float)keynum);\n    if (gensym(\"#keyup\")->s_thing && !down)\n        pd_float(gensym(\"#keyup\")->s_thing, (t_float)keynum);\n    if (gensym(\"#keyname\")->s_thing)\n    {\n        t_atom at[2];\n        at[0] = av[0];\n        SETFLOAT(at, down);\n        SETSYMBOL(at+1, gotkeysym);\n        pd_list(gensym(\"#keyname\")->s_thing, 0, 2, at);\n    }\n    if (!x || !x->gl_editor)  /* if that 'invis'ed the window,  stop. */\n        return;\n    if (x && down)\n    {\n            /* cancel any dragging action */\n        if (x->gl_editor->e_onmotion == MA_MOVE)\n            x->gl_editor->e_onmotion = MA_NONE;\n            /* if an object has \"grabbed\" keys just send them on */\n        if (x->gl_editor->e_grab\n            && x->gl_editor->e_keyfn && keynum)\n                (* x->gl_editor->e_keyfn)\n                    (x->gl_editor->e_grab, gotkeysym, (t_float)keynum);\n            /* if a text editor is open send the key on, as long as\n               it is either \"real\" (has a key number) or else is an arrow key. */\n        else if (x->gl_editor->e_textedfor && (keynum\n            || !strcmp(gotkeysym->s_name, \"Home\")\n            || !strcmp(gotkeysym->s_name, \"End\")\n            || !strcmp(gotkeysym->s_name, \"Up\")\n            || !strcmp(gotkeysym->s_name, \"Down\")\n            || !strcmp(gotkeysym->s_name, \"Left\")\n            || !strcmp(gotkeysym->s_name, \"Right\")))\n        {\n                /* send the key to the box's editor */\n            if (!x->gl_editor->e_textdirty)\n            {\n                canvas_setundo(x, canvas_undo_cut,\n                    canvas_undo_set_cut(x, UCUT_TEXT), \"typing\");\n            }\n            rtext_key(x->gl_editor->e_textedfor,\n                (int)keynum, gotkeysym);\n            if (x->gl_editor->e_textdirty)\n                canvas_dirty(x, 1);\n        }\n            /* check for backspace or clear */\n        else if (keynum == 8 || keynum == 127)\n        {\n            if (x->gl_editor->e_selection)\n                canvas_undo_add(x, UNDO_SEQUENCE_START, \"clear\", 0);\n            if (x->gl_editor->e_selectedline)\n                canvas_clearline(x);\n            if (x->gl_editor->e_selection)\n            {\n                canvas_undo_add(x, UNDO_CUT, \"clear\",\n                    canvas_undo_set_cut(x, UCUT_CLEAR));\n                canvas_doclear(x);\n                canvas_undo_add(x, UNDO_SEQUENCE_END, \"clear\", 0);\n            }\n        }\n            /* check for arrow keys */\n        else if (!strcmp(gotkeysym->s_name, \"Up\"))\n            canvas_displaceselection(x, 0, shift ? -10 : -1);\n        else if (!strcmp(gotkeysym->s_name, \"Down\"))\n            canvas_displaceselection(x, 0, shift ? 10 : 1);\n        else if (!strcmp(gotkeysym->s_name, \"Left\"))\n            canvas_displaceselection(x, shift ? -10 : -1, 0);\n        else if (!strcmp(gotkeysym->s_name, \"Right\"))\n            canvas_displaceselection(x, shift ? 10 : 1, 0);\n        else if ((MA_CONNECT == x->gl_editor->e_onmotion)\n            && (CURSOR_EDITMODE_CONNECT == EDITOR->canvas_cursorwas)\n                 && !strncmp(gotkeysym->s_name, \"Shift\", 5))\n        {\n                /* <Shift> while in connect-mode: create connection... */\n            canvas_doconnect(x, x->gl_editor->e_xnew, x->gl_editor->e_ynew, 1, 1);\n                /* ... and continue in connect-mode */\n            canvas_doclick(x, x->gl_editor->e_xwas, x->gl_editor->e_ywas, 0, 0, 1);\n\n        }\n    }\n        /* if control key goes up or down, and if we're in edit mode, change\n           cursor to indicate how the click action changes */\n    if (x && keynum == 0 && x->gl_edit &&\n        !strncmp(gotkeysym->s_name, \"Control\", 7))\n            canvas_setcursor(x, down ?\n                CURSOR_RUNMODE_NOTHING :CURSOR_EDITMODE_NOTHING);\n}\n\nstatic void delay_move(t_canvas *x)\n{\n    int incx = (x->gl_editor->e_xnew - x->gl_editor->e_xwas)/x->gl_zoom,\n        incy = (x->gl_editor->e_ynew - x->gl_editor->e_ywas)/x->gl_zoom;\n    if (incx || incy)\n        canvas_displaceselection(x, incx, incy);\n    x->gl_editor->e_xwas += incx * x->gl_zoom;\n    x->gl_editor->e_ywas += incy * x->gl_zoom;\n}\n\n    /* defined in g_text.c: */\nextern void text_getfont(t_text *x, t_glist *thisglist,\n    int *fwidthp, int *fheightp, int *guifsize);\n\nvoid canvas_motion(t_canvas *x, t_floatarg xpos, t_floatarg ypos,\n    t_floatarg fmod)\n{\n#if 0\n    post(\"motion %g %g %g\", xpos, ypos, fmod);\n#endif\n    int mod = fmod;\n    if (!x->gl_editor)\n    {\n        bug(\"editor\");\n        return;\n    }\n    glist_setlastxy(x, xpos, ypos);\n    if (x->gl_editor->e_onmotion == MA_MOVE)\n    {\n        if (!x->gl_editor->e_clock)\n            x->gl_editor->e_clock = clock_new(x, (t_method)delay_move);\n        clock_unset(x->gl_editor->e_clock);\n        clock_delay(x->gl_editor->e_clock, 5);\n        x->gl_editor->e_xnew = xpos;\n        x->gl_editor->e_ynew = ypos;\n    }\n    else if (x->gl_editor->e_onmotion == MA_REGION)\n        canvas_doregion(x, xpos, ypos, 0);\n    else if (x->gl_editor->e_onmotion == MA_CONNECT)\n    {\n        canvas_doconnect(x, xpos, ypos, mod, 0);\n        x->gl_editor->e_xnew = xpos;\n        x->gl_editor->e_ynew = ypos;\n    }\n    else if (x->gl_editor->e_onmotion == MA_PASSOUT)\n    {\n        if (!x->gl_editor->e_motionfn)\n            bug(\"e_motionfn\");\n        (*x->gl_editor->e_motionfn)(&x->gl_editor->e_grab->g_pd,\n            xpos - x->gl_editor->e_xwas, ypos - x->gl_editor->e_ywas, 0);\n        x->gl_editor->e_xwas = xpos;\n        x->gl_editor->e_ywas = ypos;\n    }\n    else if (x->gl_editor->e_onmotion == MA_DRAGTEXT)\n    {\n        t_rtext *rt = x->gl_editor->e_textedfor;\n        if (rt)\n            rtext_mouse(rt, xpos - x->gl_editor->e_xwas,\n                ypos - x->gl_editor->e_ywas, RTEXT_DRAG);\n    }\n    else if (x->gl_editor->e_onmotion == MA_RESIZE)\n    {\n        int x11=0, y11=0, x12=0, y12=0;\n        t_gobj *y1;\n        if ((y1 = canvas_findhitbox(x,\n            x->gl_editor->e_xwas, x->gl_editor->e_ywas,\n                &x11, &y11, &x12, &y12)))\n        {\n            int wantwidth = xpos - x11;\n            t_object *ob = pd_checkobject(&y1->g_pd);\n            if (ob && ((ob->te_pd->c_wb == &text_widgetbehavior) ||\n                ob->te_type == T_ATOM ||\n                    (pd_checkglist(&ob->te_pd) &&\n                     !((t_canvas *)ob)->gl_isgraph)))\n            {\n                int fwidth, fheight, guifsize;\n                text_getfont(ob, x, &fwidth, &fheight, &guifsize);\n                wantwidth = wantwidth / fwidth;\n                if (wantwidth < 1)\n                    wantwidth = 1;\n                ob->te_width = wantwidth;\n                gobj_vis(y1, x, 0);\n                canvas_fixlinesfor(x, ob);\n                gobj_vis(y1, x, 1);\n            }\n            else if (ob && ob->ob_pd == canvas_class)\n            {\n                gobj_vis(y1, x, 0);\n                ((t_canvas *)ob)->gl_pixwidth += xpos - x->gl_editor->e_xnew;\n                ((t_canvas *)ob)->gl_pixheight += ypos - x->gl_editor->e_ynew;\n                x->gl_editor->e_xnew = xpos;\n                x->gl_editor->e_ynew = ypos;\n                canvas_fixlinesfor(x, ob);\n                gobj_vis(y1, x, 1);\n            }\n            else post(\"not resizable\");\n        }\n    }\n    else canvas_doclick(x, xpos, ypos, 0, mod, 0);\n\n    x->gl_editor->e_lastmoved = 1;\n}\n\nvoid canvas_startmotion(t_canvas *x)\n{\n    int xval, yval;\n    if (!x->gl_editor) return;\n    glist_getnextxy(x, &xval, &yval);\n    if (xval == 0 && yval == 0) return;\n    x->gl_editor->e_onmotion = MA_MOVE;\n    x->gl_editor->e_xwas = xval;\n    x->gl_editor->e_ywas = yval;\n}\n\n/* ----------------------------- window stuff ----------------------- */\nextern int sys_perf;\n\nvoid canvas_print(t_canvas *x, t_symbol *s)\n{\n    const char*filename = (*s->s_name)?(s->s_name):\"x.ps\";\n    pdgui_vmess(0, \"cr rs\",\n        x, \"postscript\",\n        \"-file\", filename);\n}\n\n    /* find the innermost dirty sub-glist, if any, of this one (including itself) */\nstatic t_glist *glist_finddirty(t_glist *x)\n{\n    t_gobj *g;\n    t_glist *g2;\n    for (g = x->gl_list; g; g = g->g_next)\n        if (pd_class(&g->g_pd) == canvas_class &&\n            (g2 = glist_finddirty((t_glist *)g)))\n                return (g2);\n\n    if (x->gl_env && x->gl_dirty)\n        return (x);\n    else\n        return (0);\n}\n\n    /* quit, after calling glist_finddirty() on all toplevels and verifying\n       the user really wants to discard changes  */\nvoid glob_verifyquit(void *dummy, t_floatarg f)\n{\n    t_glist *g, *g2;\n    const char*msg = \"really quit?\";\n        /* find all root canvases */\n    for (g = pd_getcanvaslist(); g; g = g->gl_next)\n        if ((g2 = glist_finddirty(g)))\n        {\n            t_atom backmsg[2];\n            char buf[40];\n            sprintf(buf, \".x%lx\", g2);\n            SETSYMBOL(backmsg+0, gensym(\"menuclose\"));\n            SETFLOAT (backmsg+1, 3);\n\n            canvas_vis(g2, 1);\n            pdgui_vmess(\"pdtk_canvas_menuclose\", \"^m\",\n                        canvas_getrootfor(g),\n                        gensym(buf), 2, backmsg);\n            return;\n        }\n    if (f == 0 && sys_perf)\n        pdgui_vmess(\"pdtk_check\", \"r Sss\",\n            \".pdwindow\",\n            1, &msg,\n            \"pd quit\", \"yes\");\n    else glob_quit(0);\n}\n\n    /* close a window (or possibly quit Pd), checking for dirty flags.\n       The \"force\" parameter is interpreted as follows:\n       0 - request from GUI to close, verifying whether clean or dirty\n       1 - request from GUI to close, no verification\n       2 - verified - mark this one clean, then continue as in 1\n       3 - verified - mark this one clean, then verify-and-quit\n    */\nvoid canvas_menuclose(t_canvas *x, t_floatarg fforce)\n{\n    int force = fforce;\n    t_glist *g;\n    t_atom backmsg[2];\n    char buf[40];\n    SETSYMBOL(backmsg+0, gensym(\"menuclose\"));\n    SETSYMBOL(backmsg+1, 0);\n    if (x->gl_owner && (force == 0 || force == 1))\n        canvas_vis(x, 0);   /* if subpatch, just invis it */\n    else if (force == 0)\n    {\n        g = glist_finddirty(x);\n        if (g)\n        {\n            sprintf(buf, \".x%lx\", g);\n            SETFLOAT(backmsg+1, 2);\n\n            vmess(&g->gl_pd, gensym(\"menu-open\"), \"\");\n            pdgui_vmess(\"pdtk_canvas_menuclose\", \"^m\",\n                canvas_getrootfor(g), gensym(buf), 2, backmsg);\n            return;\n        }\n        else if (sys_perf)\n        {\n            const char*msg = \"Close this window?\";\n            sprintf(buf, \".x%lx\", x);\n            SETFLOAT(backmsg+1, 1);\n\n            pdgui_vmess(\"pdtk_check\", \"^ Sms\",\n                canvas_getrootfor(x),\n                1, &msg,\n                gensym(buf), 2, backmsg,\n                \"yes\");\n        }\n        else pd_free(&x->gl_pd);\n    }\n    else if (force == 1)\n        pd_free(&x->gl_pd);\n    else if (force == 2)\n    {\n        canvas_dirty(x, 0);\n        while (x->gl_owner && !x->gl_isclone)\n            x = x->gl_owner;\n        g = glist_finddirty(x);\n        if (g)\n        {\n            sprintf(buf, \".x%lx\", g);\n            SETFLOAT(backmsg+1, 2);\n\n            vmess(&g->gl_pd, gensym(\"menu-open\"), \"\");\n            pdgui_vmess(\"pdtk_canvas_menuclose\", \"^m\",\n                        canvas_getrootfor(g), gensym(buf), 2, backmsg);\n            return;\n        }\n        else pd_free(&x->gl_pd);\n    }\n    else if (force == 3)\n    {\n        canvas_dirty(x, 0);\n        glob_verifyquit(0, 1);\n    }\n}\n\n    /* put up a dialog which may call canvas_font back to do the work */\nstatic void canvas_menufont(t_canvas *x)\n{\n    t_canvas *x2 = canvas_getrootfor(x);\n    pdgui_stub_deleteforkey(x2);\n    pdgui_stub_vnew(&x2->gl_pd, \"pdtk_canvas_dofont\", &x2->gl_pd,\n        \"i\", x2->gl_font);\n}\n\ntypedef void (*t_zoomfn)(void *x, t_floatarg arg1);\n\n/* LATER, if canvas is flipped, re-scroll to preserve bottom left corner */\nstatic void canvas_zoom(t_canvas *x, t_floatarg zoom)\n{\n    if (zoom != x->gl_zoom && (zoom == 1 || zoom == 2))\n    {\n        t_gobj *g;\n        t_object *obj;\n        for (g = x->gl_list; g; g = g->g_next)\n            if ((obj = pd_checkobject(&g->g_pd)))\n        {\n                /* pass zoom message on to all objects, except canvases\n                   that aren't GOP */\n            t_gotfn zoommethod;\n            if ((zoommethod = zgetfn(&obj->te_pd, gensym(\"zoom\"))) &&\n                (!(pd_class(&obj->te_pd) == canvas_class) ||\n                 (((t_glist *)obj)->gl_isgraph)))\n                    (*(t_zoomfn)zoommethod)(&obj->te_pd, zoom);\n        }\n        x->gl_zoom = zoom;\n        if (x->gl_havewindow)\n        {\n            if (!glist_isgraph(x) && (x->gl_y2 < x->gl_y1))\n            {\n                /* if it's flipped so that y grows upward,\n                fix so that zero is bottom edge as in canvas_dosetbounds() */\n                t_float diff = x->gl_y1 - x->gl_y2;\n                x->gl_y1 = (x->gl_screeny2 - x->gl_screeny1) * diff/x->gl_zoom;\n                x->gl_y2 = x->gl_y1 - diff;\n            }\n            canvas_redraw(x);\n        }\n    }\n}\n\n    /* function to support searching */\nstatic int atoms_match(int inargc, t_atom *inargv, int searchargc,\n    t_atom *searchargv, int wholeword)\n{\n    int indexin, nmatched;\n    for (indexin = 0; indexin <= inargc - searchargc; indexin++)\n    {\n        for (nmatched = 0; nmatched < searchargc; nmatched++)\n        {\n            t_atom *a1 = &inargv[indexin + nmatched],\n                *a2 = &searchargv[nmatched];\n            if (a1->a_type == A_SEMI || a1->a_type == A_COMMA)\n            {\n                if (a2->a_type != a1->a_type)\n                    goto nomatch;\n            }\n            else if (a1->a_type == A_FLOAT || a1->a_type == A_DOLLAR)\n            {\n                if (a2->a_type != a1->a_type ||\n                    a1->a_w.w_float != a2->a_w.w_float)\n                        goto nomatch;\n            }\n            else if (a1->a_type == A_SYMBOL || a1->a_type == A_DOLLSYM)\n            {\n                if ((a2->a_type != A_SYMBOL && a2->a_type != A_DOLLSYM)\n                    || (wholeword && a1->a_w.w_symbol != a2->a_w.w_symbol)\n                    || (!wholeword &&  !strstr(a1->a_w.w_symbol->s_name,\n                                        a2->a_w.w_symbol->s_name)))\n                        goto nomatch;\n            }\n        }\n        return (1);\n    nomatch: ;\n    }\n    return (0);\n}\n\nextern int clone_get_n(t_gobj *x);\nextern t_glist *clone_get_instance(t_gobj *x, int n);\n\n    /* find an atom or string of atoms */\nstatic int canvas_dofind(t_canvas *x, int *myindexp)\n{\n    t_gobj *y;\n    int findargc = binbuf_getnatom(EDITOR->canvas_findbuf), didit = 0;\n    t_atom *findargv = binbuf_getvec(EDITOR->canvas_findbuf);\n    for (y = x->gl_list; y; y = y->g_next)\n    {\n        t_object *ob = 0;\n        if ((ob = pd_checkobject(&y->g_pd)))\n        {\n            int n;\n            if (atoms_match(binbuf_getnatom(ob->ob_binbuf),\n                binbuf_getvec(ob->ob_binbuf), findargc, findargv,\n                    EDITOR->canvas_find_wholeword))\n            {\n                if (*myindexp == EDITOR->canvas_find_index)\n                {\n                    glist_noselect(x);\n                    vmess(&x->gl_pd, gensym(\"menu-open\"), \"\");\n                    canvas_editmode(x, 1.);\n                    glist_select(x, y);\n                    didit = 1;\n                }\n                (*myindexp)++;\n            }\n            if ((n = clone_get_n((t_gobj *)ob)) != 0)\n            {\n                int i = 0;\n                    /* should we search in every clone instance, or only the first one? */\n                /*for(i = 0; i < n; i++)\n                {*/\n                    didit |= canvas_dofind((t_canvas *)clone_get_instance((t_gobj *)ob, i), myindexp);\n                /*}*/\n            }\n        }\n    }\n    for (y = x->gl_list; y; y = y->g_next)\n        if (pd_class(&y->g_pd) == canvas_class)\n            didit |= canvas_dofind((t_canvas *)y, myindexp);\n    return (didit);\n}\n\nstatic void canvas_find(t_canvas *x, t_symbol *s, t_floatarg wholeword)\n{\n    int myindex = 0, found;\n    t_symbol *decodedsym = sys_decodedialog(s);\n    if (!EDITOR->canvas_findbuf)\n        EDITOR->canvas_findbuf = binbuf_new();\n    binbuf_text(EDITOR->canvas_findbuf, decodedsym->s_name,\n        strlen(decodedsym->s_name));\n    EDITOR->canvas_find_index = 0;\n    EDITOR->canvas_find_wholeword = wholeword;\n    canvas_whichfind = x;\n    found = canvas_dofind(x, &myindex);\n    if (found)\n        EDITOR->canvas_find_index = 1;\n    pdgui_vmess(\"pdtk_showfindresult\", \"^ iii\",\n        x,\n        found, EDITOR->canvas_find_index, myindex);\n}\n\nstatic void canvas_find_again(t_canvas *x)\n{\n    int myindex = 0, found;\n    if (!EDITOR->canvas_findbuf || !canvas_whichfind)\n        return;\n    found = canvas_dofind(canvas_whichfind, &myindex);\n    pdgui_vmess(\"pdtk_showfindresult\", \"^ iii\",\n        x,\n       found, ++EDITOR->canvas_find_index, myindex);\n    if (!found)\n        EDITOR->canvas_find_index = 0;\n}\n\nstatic void canvas_find_parent(t_canvas *x)\n{\n    if (x->gl_owner)\n        canvas_vis(x->gl_owner, 1);\n}\n\nextern t_pd *message_get_responder(t_gobj *x);\nextern t_class *text_class;\n\nstatic int glist_dofinderror(t_glist *gl, const void *error_object)\n{\n    t_gobj *g;\n    int n;\n\n    for (g = gl->gl_list; g; g = g->g_next)\n    {\n        if (((const void *)g == error_object) || (message_get_responder(g) == error_object))\n        {\n                /* got it... now show it. */\n            glist_noselect(gl);\n            canvas_vis((t_canvas *)gl, 1);\n            canvas_editmode((t_canvas *)gl, 1.);\n            glist_select(gl, g);\n            if (pd_class(&g->g_pd) == text_class) {\n                t_text* x = (t_text*)g;\n                int argc = binbuf_getnatom(x->te_binbuf);\n                t_atom*argv = binbuf_getvec(x->te_binbuf);\n                if(argc>0 && A_SYMBOL == argv[0].a_type) {\n                    t_symbol*s = atom_getsymbol(argv);\n                    if (s && s->s_name && *s->s_name)\n                        pdgui_vmess(\"::deken::open_search_objects\", \"s\", s->s_name);\n                }\n            }\n            return (1);\n        }\n        else if (g->g_pd == canvas_class)\n        {\n            if (glist_dofinderror((t_canvas *)g, error_object))\n                return (1);\n        }\n        else if ((n = clone_get_n(g)) != 0)\n        {\n            int i;\n            for(i = 0; i < n; i++)\n            {\n                if (glist_dofinderror(clone_get_instance(g, i), error_object))\n                    return 1;\n            }\n        }\n    }\n    return (0);\n}\n\nvoid canvas_finderror(const void *error_object)\n{\n    t_canvas *x;\n        /* find all root canvases */\n    for (x = pd_getcanvaslist(); x; x = x->gl_next)\n    {\n        if (glist_dofinderror(x, error_object))\n            return;\n    }\n    pd_error(0, \"... sorry, I couldn't find the source of that error.\");\n}\n\nvoid canvas_stowconnections(t_canvas *x)\n{\n    t_gobj *selhead = 0, *seltail = 0, *nonhead = 0, *nontail = 0, *y, *y2;\n    t_linetraverser t;\n    t_outconnect *oc;\n    if (!x->gl_editor) return;\n        /* split list to \"selected\" and \"unselected\" parts */\n    for (y = x->gl_list; y; y = y2)\n    {\n        y2 = y->g_next;\n        if (glist_isselected(x, y))\n        {\n            if (seltail)\n            {\n                seltail->g_next = y;\n                seltail = y;\n                y->g_next = 0;\n            }\n            else\n            {\n                selhead = seltail = y;\n                seltail->g_next = 0;\n            }\n        }\n        else\n        {\n            if (nontail)\n            {\n                nontail->g_next = y;\n                nontail = y;\n                y->g_next = 0;\n            }\n            else\n            {\n                nonhead = nontail = y;\n                nontail->g_next = 0;\n            }\n        }\n    }\n        /* move the selected part to the end */\n    if (!nonhead) x->gl_list = selhead;\n    else x->gl_list = nonhead, nontail->g_next = selhead;\n\n        /* add connections to binbuf */\n    binbuf_clear(x->gl_editor->e_connectbuf);\n    linetraverser_start(&t, x);\n    while ((oc = linetraverser_next(&t)))\n    {\n        int s1 = glist_isselected(x, &t.tr_ob->ob_g);\n        int s2 = glist_isselected(x, &t.tr_ob2->ob_g);\n        if (s1 != s2)\n            binbuf_addv(x->gl_editor->e_connectbuf, \"ssiiii;\",\n                gensym(\"#X\"), gensym(\"connect\"),\n                glist_getindex(x, &t.tr_ob->ob_g), t.tr_outno,\n                glist_getindex(x, &t.tr_ob2->ob_g), t.tr_inno);\n    }\n}\n\nvoid canvas_restoreconnections(t_canvas *x)\n{\n    t_pd *boundx = s__X.s_thing;\n    s__X.s_thing = &x->gl_pd;\n    binbuf_eval(x->gl_editor->e_connectbuf, 0, 0, 0);\n    s__X.s_thing = boundx;\n}\n\nstatic t_binbuf *canvas_docopy(t_canvas *x)\n{\n    t_gobj *y;\n    t_linetraverser t;\n    t_outconnect *oc;\n    t_binbuf *b = binbuf_new();\n    for (y = x->gl_list; y; y = y->g_next)\n    {\n        if (glist_isselected(x, y))\n            gobj_save(y, b);\n    }\n    linetraverser_start(&t, x);\n    while ((oc = linetraverser_next(&t)))\n    {\n        if (glist_isselected(x, &t.tr_ob->ob_g)\n            && glist_isselected(x, &t.tr_ob2->ob_g))\n        {\n            binbuf_addv(b, \"ssiiii;\", gensym(\"#X\"), gensym(\"connect\"),\n                glist_selectionindex(x, &t.tr_ob->ob_g, 1), t.tr_outno,\n                glist_selectionindex(x, &t.tr_ob2->ob_g, 1), t.tr_inno);\n        }\n    }\n    return (b);\n}\n\nstatic void canvas_copy(t_canvas *x)\n{\n    if (!x->gl_editor)\n        return;\n    if (x->gl_editor->e_selection)\n    {\n        binbuf_free(EDITOR->copy_binbuf);\n        EDITOR->copy_binbuf = canvas_docopy(x);\n    }\n    if (x->gl_editor->e_textedfor)\n    {\n        char *buf;\n        int bufsize;\n        rtext_getseltext(x->gl_editor->e_textedfor, &buf, &bufsize);\n        pdgui_vmess(\"clipboard\", \"r\", \"clear\");\n        pdgui_vmess(\"clipboard\", \"rp\",  \"append\", bufsize, buf);\n    }\n}\n\nstatic void canvas_clearline(t_canvas *x)\n{\n    if (x->gl_editor->e_selectedline)\n    {\n        canvas_disconnect_with_undo(x,\n            x->gl_editor->e_selectline_index1,\n            x->gl_editor->e_selectline_outno,\n            x->gl_editor->e_selectline_index2,\n            x->gl_editor->e_selectline_inno);\n        x->gl_editor->e_selectedline = 0;\n        canvas_dirty(x, 1);\n    }\n}\n\nstatic void canvas_doclear(t_canvas *x)\n{\n    t_gobj *y, *y2;\n    int dspstate;\n\n    dspstate = canvas_suspend_dsp();\n    if (x->gl_editor->e_selectedline)\n    {\n        canvas_disconnect_with_undo(x,\n            x->gl_editor->e_selectline_index1,\n            x->gl_editor->e_selectline_outno,\n            x->gl_editor->e_selectline_index2,\n            x->gl_editor->e_selectline_inno);\n        x->gl_editor->e_selectedline=0;\n    }\n        /* if text is selected, deselecting it might remake the\n           object. So we deselect it and hunt for a \"new\" object on\n           the glist to reselect. */\n    if (x->gl_editor->e_textedfor)\n    {\n        t_gobj *selwas = x->gl_editor->e_selection->sel_what;\n        pd_this->pd_newest = 0;\n        glist_noselect(x);\n        if (pd_this->pd_newest)\n        {\n            for (y = x->gl_list; y; y = y->g_next)\n                if (&y->g_pd == pd_this->pd_newest) glist_select(x, y);\n        }\n    }\n    while (1)   /* this is pretty weird...  should rewrite it */\n    {\n        for (y = x->gl_list; y; y = y2)\n        {\n            y2 = y->g_next;\n            if (glist_isselected(x, y))\n            {\n                glist_delete(x, y);\n                goto next;\n            }\n        }\n        goto restore;\n    next: ;\n    }\nrestore:\n    canvas_resume_dsp(dspstate);\n    canvas_dirty(x, 1);\n}\n\nstatic void canvas_cut(t_canvas *x)\n{\n    if (!x->gl_editor)  /* ignore if invisible */\n        return;\n    if (x->gl_editor->e_selectedline)   /* delete line */\n        canvas_clearline(x);\n    else if (x->gl_editor->e_textedfor) /* delete selected text in a box */\n    {\n        char *buf;\n        int bufsize;\n        rtext_getseltext(x->gl_editor->e_textedfor, &buf, &bufsize);\n        if (!bufsize && x->gl_editor->e_selection &&\n            !x->gl_editor->e_selection->sel_next)\n        {\n                /* if the text is already empty, delete the box.  We\n                   first clear 'textedfor' so that canvas_doclear later will\n                   think the whole box was selected, not the text */\n            x->gl_editor->e_textedfor = 0;\n            goto deleteobj;\n        }\n        canvas_copy(x);\n        rtext_key(x->gl_editor->e_textedfor, 127, &s_);\n        canvas_dirty(x, 1);\n    }\n    else if (x->gl_editor->e_selection)\n    {\n    deleteobj:      /* delete one or more objects */\n        canvas_undo_add(x, UNDO_CUT, \"cut\", canvas_undo_set_cut(x, UCUT_CUT));\n        canvas_copy(x);\n        canvas_doclear(x);\n        pdgui_vmess(\"pdtk_canvas_getscroll\", \"c\", x);\n    }\n}\n\nstatic void glist_donewloadbangs(t_glist *x)\n{\n    if (x->gl_editor)\n    {\n        t_selection *sel;\n        for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next)\n            if (pd_class(&sel->sel_what->g_pd) == canvas_class)\n                canvas_loadbang((t_canvas *)(&sel->sel_what->g_pd));\n            else if (zgetfn(&sel->sel_what->g_pd, gensym(\"loadbang\")))\n                vmess(&sel->sel_what->g_pd, gensym(\"loadbang\"), \"f\", LB_LOAD);\n    }\n}\n\nstatic int binbuf_nextmess(int argc, const t_atom *argv)\n{\n    int i=0;\n    while(argc--)\n    {\n        argv++;\n        i++;\n        if (A_SEMI == argv->a_type) {\n            return i+1;\n        }\n    }\n    return i;\n}\nstatic int binbuf_getpos(t_binbuf*b, int *x0, int *y0, t_symbol**type)\n{\n        /*\n         * checks how many objects the binbuf contains and where they are located\n         * for simplicity, we stop after the first object...\n         * \"objects\" are any patchable things\n         * returns: 0: no objects/...\n         *          1: single object in binbuf\n         *          2: more than one object in binbuf\n         * (x0,y0) are the coordinates of the first object\n         * (type) is the type of the first object (\"obj\", \"msg\",...)\n         */\n    t_atom*argv = binbuf_getvec(b);\n    int argc = binbuf_getnatom(b);\n    const int argc0 = argc;\n    int count = 0;\n    t_symbol*s;\n        /* get the position of the first object in the argv binbuf */\n    if(argc > 2\n       && atom_getsymbol(argv+0) == &s__N\n       && atom_getsymbol(argv+1) == gensym(\"canvas\"))\n    {\n        int ac = argc;\n        t_atom*ap = argv;\n        int stack = 0;\n        do {\n            int off = binbuf_nextmess(argc, argv);\n            if(!off)\n                break;\n            ac = argc;\n            ap = argv;\n            argc-=off;\n            argv+=off;\n            count+=off;\n            if(off >= 2)\n            {\n                if (atom_getsymbol(ap+1) == gensym(\"restore\")\n                    && atom_getsymbol(ap) == &s__X)\n                    {\n                        stack--;\n                    }\n                if (atom_getsymbol(ap+1) == gensym(\"canvas\")\n                    && atom_getsymbol(ap) == &s__N)\n                    {\n                        stack++;\n                    }\n            }\n            if(argc<0)\n                return 0;\n        } while (stack>0);\n        argc = ac;\n        argv = ap;\n    }\n    if(argc < 4 || atom_getsymbol(argv) != &s__X)\n        return 0;\n        /* #X obj|msg|text|floatatom|symbolatom <x> <y> ...\n         * TODO: subpatches #N canvas + #X restore <x> <y>\n         */\n    s = atom_getsymbol(argv+1);\n    if(gensym(\"restore\") == s\n       || gensym(\"obj\") == s\n       || gensym(\"msg\") == s\n       || gensym(\"text\") == s\n       || gensym(\"floatatom\") == s\n       || gensym(\"listbox\") == s\n       || gensym(\"symbolatom\") == s)\n    {\n        if(x0)*x0=atom_getfloat(argv+2);\n        if(y0)*y0=atom_getfloat(argv+3);\n        if(type)*type=s;\n    } else\n        return 0;\n\n        /* no wind the binbuf to the next message */\n    while(argc--)\n    {\n        count++;\n        if (A_SEMI == argv->a_type)\n            break;\n        argv++;\n    }\n    return 1+(argc0 > count);\n}\n\nstatic void canvas_dopaste(t_canvas *x, t_binbuf *b)\n{\n    t_gobj *g2;\n    int dspstate = canvas_suspend_dsp(), nbox, count;\n    t_symbol *asym = gensym(\"#A\");\n        /* save and clear bindings to symbols #A, #N, #X; restore when done */\n    t_pd *boundx = s__X.s_thing, *bounda = asym->s_thing,\n        *boundn = s__N.s_thing;\n    asym->s_thing = 0;\n    s__X.s_thing = &x->gl_pd;\n    s__N.s_thing = &pd_canvasmaker;\n\n    canvas_editmode(x, 1.);\n    glist_noselect(x);\n    for (g2 = x->gl_list, nbox = 0; g2; g2 = g2->g_next) nbox++;\n\n    EDITOR->paste_onset = nbox;\n    EDITOR->paste_canvas = x;\n\n    binbuf_eval(b, 0, 0, 0);\n    for (g2 = x->gl_list, count = 0; g2; g2 = g2->g_next, count++)\n        if (count >= nbox)\n            glist_select(x, g2);\n    EDITOR->paste_canvas = 0;\n    canvas_resume_dsp(dspstate);\n    canvas_dirty(x, 1);\n    if (x->gl_mapped)\n        pdgui_vmess(\"pdtk_canvas_getscroll\", \"c\", x);\n    if (!sys_noloadbang)\n        glist_donewloadbangs(x);\n    asym->s_thing = bounda;\n    s__X.s_thing = boundx;\n    s__N.s_thing = boundn;\n}\n\nstatic t_symbol*get_object_type(t_object *obj)\n{\n    t_symbol *s=0;\n    t_binbuf *bb=0;\n    if(!obj)\n        return 0;\n    switch(obj->te_type) {\n    case T_OBJECT:\n        return gensym(\"obj\");\n    case T_MESSAGE:\n        return gensym(\"msg\");\n    case T_TEXT:\n        return gensym(\"text\");\n    default:\n            /* detecting the type of a gatom by using it's save function */\n        bb=binbuf_new();\n        gobj_save(&obj->te_g, bb);\n        binbuf_getpos(bb, 0, 0, &s);\n        binbuf_free(bb);\n        return s;\n    }\n    return 0;\n}\n\nstatic void canvas_paste_replace(t_canvas *x)\n{\n    int x0=0, y0=0;\n    t_symbol *typ0 = 0;\n    if (!x->gl_editor)\n        return;\n    if(x->gl_editor->e_selection && 1==binbuf_getpos(EDITOR->copy_binbuf, &x0, &y0, &typ0))\n    {\n        t_canvas *canvas = glist_getcanvas(x);\n        t_selection *mysel = 0, *y;\n        t_symbol *seltype = 0;\n\n            /* check whether all the selected objects have the same type */\n        for (y = x->gl_editor->e_selection; y; y = y->sel_next)\n        {\n            t_symbol *s=get_object_type(pd_checkobject(&y->sel_what->g_pd));\n            if (!s)\n                continue;\n            if(seltype && seltype != s)\n            {\n                seltype = 0;\n                break;\n            }\n            seltype = s;\n        }\n\n            /* we will do a lot of reslecting; so copy the selection */\n        for (y = x->gl_editor->e_selection; y; y = y->sel_next)\n        {\n            t_selection *sel = 0;\n            t_object *obj=pd_checkobject(&y->sel_what->g_pd);\n            if(!obj)\n               continue;\n                    /* if the selection mixes obj, msg,... we only want to\n                     * replace the same type;\n                     * if the selection is homogeneous (seltype==NULL), we also allow typechanges\n                     */\n            if (!seltype && get_object_type(obj) != typ0)\n                continue;\n            sel = (t_selection *)getbytes(sizeof(*sel));\n            sel->sel_what = y->sel_what;\n            sel->sel_next = mysel;\n            mysel = sel;\n        }\n\n        canvas_undo_add(x, UNDO_SEQUENCE_START, \"paste/replace\", 0);\n\n        for (y = mysel; y; y = y->sel_next)\n        {\n            t_object *o = (t_object *)(&y->sel_what->g_pd);\n            int dx = o->te_xpix - x0;\n            int dy = o->te_ypix - y0;\n            glist_noselect(x);\n            EDITOR->canvas_undo_already_set_move = 0;\n                /* save connections and move object to the end */\n                /* note: the undo sequence selects the object as a side-effect */\n            canvas_undo_add(x, UNDO_ARRANGE, \"arrange\",\n                canvas_undo_set_arrange(x, y->sel_what, 1));\n            canvas_stowconnections(canvas);\n                /* recreate object */\n                /* remove the old object */\n            canvas_undo_add(x, UNDO_CUT, \"clear\",\n                canvas_undo_set_cut(x, UCUT_CLEAR));\n            canvas_doclear(x);\n\n                /* create the new object (and loadbang if needed) */\n            canvas_applybinbuf(x, EDITOR->copy_binbuf);\n\n            glist_noselect(x);\n            glist_select(x, glist_nth(x, glist_getindex(x, 0) - 1));\n                /* displace object (includes UNDO) */\n            canvas_displaceselection(x, dx, dy);\n                /* restore connections */\n            canvas_restoreconnections(canvas);\n\n            canvas_undo_add(x, UNDO_CREATE, \"create\",\n                (void *)canvas_undo_set_create(x));\n\n            if (pd_this->pd_newest && pd_class(pd_this->pd_newest) == canvas_class)\n                canvas_loadbang((t_canvas *)pd_this->pd_newest);\n        }\n        canvas_undo_add(x, UNDO_SEQUENCE_END, \"paste/replace\", 0);\n\n            /* free the selection copy */\n        for (y = mysel; y; )\n        {\n            t_selection*next = y->sel_next;\n            freebytes(y, sizeof(*y));\n            y = next;\n        }\n\n    }\n}\n\n\nstatic void canvas_paste(t_canvas *x)\n{\n    if (!x->gl_editor)\n        return;\n    if (x->gl_editor->e_textedfor)\n    {\n            /* simulate keystrokes as if the copy buffer were typed in. */\n        pdgui_vmess(\"pdtk_pastetext\", \"^\", x);\n    }\n    else\n    {\n        int offset = 0;\n        int x0 = 0, y0 = 0;\n        int foundplace = 0;\n        binbuf_getpos(EDITOR->copy_binbuf, &x0, &y0, 0);\n        do {\n                /* iterate over all existing objects\n                 * to see whether one occupies the space we want.\n                 * if so, move along\n                 */\n            t_gobj *y;\n            foundplace = 1;\n            for (y = x->gl_list; y; y = y->g_next) {\n                t_text *txt = (t_text *)y;\n                if((x0 + offset) == txt->te_xpix && (y0 + offset) == txt->te_ypix)\n                {\n                    foundplace = 0;\n                    offset += PASTE_OFFSET;\n                    break;\n                }\n            }\n        } while(!foundplace);\n        canvas_undo_add(x, UNDO_PASTE, \"paste\",\n            (void *)canvas_undo_set_paste(x, 0, 0, offset));\n        canvas_dopaste(x, EDITOR->copy_binbuf);\n        if(offset)\n        {\n            t_selection *y;\n            for (y = x->gl_editor->e_selection; y; y = y->sel_next)\n                gobj_displace(y->sel_what, x,\n                    offset, offset);\n        }\n    }\n}\n\nstatic void canvas_duplicate(t_canvas *x)\n{\n    if (!x->gl_editor)\n        return;\n\n    if (x->gl_editor->e_selection && x->gl_editor->e_selectedline)\n        glist_deselectline(x);\n\n        /* if a connection is selected, we extend it to the right (if possible) */\n    if (x->gl_editor->e_selectedline)\n    {\n        int outindex = x->gl_editor->e_selectline_index1;\n        int inindex  = x->gl_editor->e_selectline_index2;\n        int outno = x->gl_editor->e_selectline_outno + 1;\n        int inno  = x->gl_editor->e_selectline_inno + 1;\n        t_gobj *outgobj = 0, *ingobj = 0;\n        t_object *outobj = 0, *inobj = 0;\n        int whoout = outindex;\n        int whoin = inindex;\n\n        for (outgobj = x->gl_list; whoout; outgobj = outgobj->g_next, whoout--)\n            if (!outgobj->g_next) return;\n        for (ingobj = x->gl_list; whoin; ingobj = ingobj->g_next, whoin--)\n            if (!ingobj->g_next) return;\n        outobj = (t_object*)outgobj;\n        inobj = (t_object*)ingobj;\n\n        while(!canconnect(x, outobj, outno, inobj, inno))\n        {\n            if (!outobj || obj_noutlets(outobj) <= outno)\n                return;\n            if (!inobj  || obj_ninlets (inobj ) <= inno )\n                return;\n            outno++;\n            inno++;\n        }\n\n        if(tryconnect(x, outobj, outno, inobj, inno))\n        {\n            x->gl_editor->e_selectline_outno = outno;\n            x->gl_editor->e_selectline_inno = inno;\n        }\n        return;\n    }\n    if (x->gl_editor->e_onmotion == MA_NONE && x->gl_editor->e_selection)\n    {\n        t_selection *y;\n        t_binbuf*b = 0;\n        if(EDITOR->copy_binbuf)\n            b = binbuf_duplicate(EDITOR->copy_binbuf);\n        canvas_copy(x);\n        canvas_undo_add(x, UNDO_PASTE, \"duplicate\",\n            (void *)canvas_undo_set_paste(x, 0, 1, PASTE_OFFSET));\n        canvas_dopaste(x, EDITOR->copy_binbuf);\n        for (y = x->gl_editor->e_selection; y; y = y->sel_next)\n            gobj_displace(y->sel_what, x,\n                PASTE_OFFSET, PASTE_OFFSET);\n        if(b)\n        {\n            if(EDITOR->copy_binbuf)\n                binbuf_free(EDITOR->copy_binbuf);\n            EDITOR->copy_binbuf = b;\n        }\n        canvas_dirty(x, 1);\n    }\n}\n\nstatic void canvas_selectall(t_canvas *x)\n{\n    t_gobj *y;\n    if (!x->gl_editor)\n        return;\n    if (!x->gl_edit)\n        canvas_editmode(x, 1);\n        /* if everyone is already selected deselect everyone */\n    if (!glist_selectionindex(x, 0, 0))\n        glist_noselect(x);\n    else for (y = x->gl_list; y; y = y->g_next)\n         {\n             if (!glist_isselected(x, y))\n                 glist_select(x, y);\n         }\n}\n\nstatic void canvas_deselectall(t_canvas *x)\n{\n    if(x)glist_noselect(x);\n}\nstatic void canvas_cycleselect(t_canvas*x, t_float foffset)\n{\n        /* select (currentselection+offset)%objectcount */\n    int offset = (int)foffset;\n    if (!x->gl_editor)\n        return;\n\n    if (x->gl_editor->e_onmotion == MA_CONNECT)\n    {\n            /* during connection, cycle through inlets/outlets */\n        int xwas = x->gl_editor->e_xwas,\n            ywas = x->gl_editor->e_ywas;\n        int xpos = EDITOR->canvas_last_glist_x,\n            ypos = EDITOR->canvas_last_glist_y;\n        t_object *src, *snk;\n        t_gobj*gobj;\n        int srcX1=0, srcY1=0, srcX2=0, srcY2=0;\n        int snkX1=0, snkY1=0, snkX2=0, snkY2=0;\n        if(EDITOR->canvas_last_glist != x)\n                /* we don't know the current mouse coordinates in this canvas, so return... */\n            return;\n        gobj = canvas_findhitbox(x, xwas, ywas, &srcX1, &srcY1, &srcX2, &srcY2);\n        src = gobj?pd_checkobject(&gobj->g_pd):0;\n        gobj = canvas_findhitbox(x, xpos, ypos, &snkX1, &snkY1, &snkX2, &snkY2);\n        snk = gobj?pd_checkobject(&gobj->g_pd):0;\n\n        if(!src) /* this should never happen */\n            return;\n\n            /* are we hovering over an object?\n             * - if so, cycle through inlets\n             * - else, cycle through outlets\n             */\n        if(snk && snk != src) {\n                /* cycle inlets */\n            int width = snkX2 - snkX1, hotspot, closest;\n            int nios = obj_ninlets(snk);\n            if (nios <= 1) /* no use cycling */\n                return;\n            closest = ((xpos-snkX1) * (nios-1) + width/2)/width;\n            closest = ((closest + offset) % nios + nios) % nios;\n            hotspot = snkX1 + (width - IOWIDTH) * closest / (nios-1.0) + IOWIDTH*0.5;\n            pdgui_vmess(\"::pdtk_canvas::setmouse\", \"cii\",  glist_getcanvas(x), hotspot, ypos);\n        } else {\n                /* cycle outlets */\n            int width = srcX2 - srcX1, hotspot, closest;\n            int nios = obj_noutlets(src);\n            if (nios <= 1) /* no use cycling */\n                return;\n            closest = ((xwas-srcX1) * (nios-1) + width/2)/width;\n            closest = ((closest + offset) % nios + nios) % nios;\n            hotspot = srcX1 + (width - IOWIDTH) * closest / (nios-1.0) + IOWIDTH*0.5;\n            x->gl_editor->e_xwas = hotspot;\n            canvas_doconnect(x, xpos, ypos, 0, 0);\n        }\n        return;\n    }\n    if (x->gl_editor->e_selection)\n    {\n            /* cycle the selection to the next object */\n        int newindex;\n        int objectcount = glist_getindex(x, 0);\n            /* only cycle selection if the current selection contains exactly 1 item */\n        t_gobj* y = x->gl_editor->e_selection->sel_next ? 0 : x->gl_editor->e_selection->sel_what;\n        if (!y || !objectcount)\n            return;\n        newindex = (glist_getindex(x, y) + offset) % objectcount;\n        if (newindex < 0) newindex += objectcount;\n        glist_deselect(x, y);\n        glist_select(x, glist_nth(x, newindex));\n        return;\n    }\n    if (x->gl_editor->e_selectedline)\n    {\n            /* if (only) a line is selected, cycle to next line */\n        int connectioncount = 0;\n        int foundit = 0;\n        t_linetraverser t;\n        t_outconnect *oc = 0;\n\n        linetraverser_start(&t, x);\n        while (offset && (oc = linetraverser_next(&t)))\n        {\n            connectioncount++;\n            if(!foundit) {\n                int srcno = glist_getindex(x, &t.tr_ob->ob_g);\n                int sinkno = glist_getindex(x, &t.tr_ob2->ob_g);\n                if((srcno      == x->gl_editor->e_selectline_index1) &&\n                    (t.tr_outno == x->gl_editor->e_selectline_outno) &&\n                    (sinkno     == x->gl_editor->e_selectline_index2) &&\n                    (t.tr_inno  == x->gl_editor->e_selectline_inno)) {\n                    foundit = connectioncount;\n                }\n            } else\n                offset--;\n        }\n\n        if (!connectioncount)\n            offset = 0;\n\n            /* if the offset is non-0, wrap it... */\n        if (offset)\n        {\n            offset = (((offset - 1) % connectioncount) + connectioncount)\n                % connectioncount;\n            /* ... and start from the beginning */\n            linetraverser_start(&t, x);\n            while ((oc = linetraverser_next(&t)))\n            {\n                if(!offset)break;\n                offset--;\n            }\n        }\n        if (oc)\n            glist_selectline(x, oc,\n                glist_getindex(x, &t.tr_ob->ob_g), t.tr_outno,\n                glist_getindex(x, &t.tr_ob2->ob_g), t.tr_inno);\n        return;\n    }\n}\n\n\nstatic void canvas_reselect(t_canvas *x)\n{\n    t_gobj *g, *gwas;\n        /* if someone is text editing, and if only one object is\n           selected,  deselect everyone and reselect.  */\n    if (x->gl_editor->e_textedfor)\n    {\n            /* only do this if exactly one item is selected. */\n        if ((gwas = x->gl_editor->e_selection->sel_what) &&\n            !x->gl_editor->e_selection->sel_next)\n        {\n            int nobjwas = glist_getindex(x, 0),\n                indx = canvas_getindex(x, x->gl_editor->e_selection->sel_what);\n            glist_noselect(x);\n            for (g = x->gl_list; g; g = g->g_next)\n                if (g == gwas)\n                {\n                    glist_select(x, g);\n                    return;\n                }\n                /* \"gwas\" must have disappeared; just search to the last\n                   object and select it */\n            for (g = x->gl_list; g; g = g->g_next)\n                if (!g->g_next)\n                    glist_select(x, g);\n        }\n    }\n    else if (x->gl_editor->e_selection &&\n             !x->gl_editor->e_selection->sel_next)\n            /* otherwise activate first item in selection */\n        gobj_activate(x->gl_editor->e_selection->sel_what, x, 1);\n}\n\nvoid canvas_connect(t_canvas *x, t_floatarg fwhoout, t_floatarg foutno,\n    t_floatarg fwhoin, t_floatarg finno)\n{\n    int whoout = fwhoout, outno = foutno, whoin = fwhoin, inno = finno;\n    t_gobj *src = 0, *sink = 0;\n    t_object *objsrc, *objsink;\n    t_outconnect *oc;\n    int nin = whoin, nout = whoout;\n    if (EDITOR->paste_canvas == x) whoout += EDITOR->paste_onset,\n        whoin += EDITOR->paste_onset;\n    for (src = x->gl_list; whoout; src = src->g_next, whoout--)\n        if (!src->g_next) {\n            src = NULL;\n            logpost(sink, 3, \"cannot connect non-existing object\");\n            goto bad; /* bug fix thanks to Hannes */\n        }\n    for (sink = x->gl_list; whoin; sink = sink->g_next, whoin--)\n        if (!sink->g_next) {\n            sink = NULL;\n            logpost(src, 3, \"cannot connect to non-existing object\");\n            goto bad;\n        }\n\n        /* check they're both patchable objects */\n    if (!(objsrc = pd_checkobject(&src->g_pd)) ||\n        !(objsink = pd_checkobject(&sink->g_pd))) {\n        logpost(src?src:sink, 3, \"cannot connect unpatchable object\");\n        goto bad;\n    }\n\n        /* check if objects are already connected */\n    if (canvas_isconnected(x, objsrc, outno, objsink, inno)) {\n        logpost(src, 3, \"io pair already connected\");\n        goto bad;\n    }\n\n        /* if object creation failed, make dummy inlets or outlets\n           as needed */\n    if (pd_class(&src->g_pd) == text_class && objsrc->te_type == T_OBJECT)\n        while (outno >= obj_noutlets(objsrc))\n            outlet_new(objsrc, 0);\n    if (pd_class(&sink->g_pd) == text_class && objsink->te_type == T_OBJECT)\n        while (inno >= obj_ninlets(objsink))\n            inlet_new(objsink, &objsink->ob_pd, 0, 0);\n\n    if (!(oc = obj_connect(objsrc, outno, objsink, inno))) goto bad;\n    if (glist_isvisible(x) && x->gl_havewindow)\n    {\n        char tag[128];\n        char*tags[] = {tag, \"cord\"};\n        sprintf(tag, \"l%p\", oc);\n        pdgui_vmess(0, \"crr iiii ri rS\",\n            glist_getcanvas(x), \"create\", \"line\",\n            0, 0, 0, 0,\n            \"-width\", (obj_issignaloutlet(objsrc, outno) ? 2 : 1) * x->gl_zoom,\n            \"-tags\", 2, tags);\n        canvas_fixlinesfor(x, objsrc);\n    }\n    return;\n\nbad:\n    post(\"%s %d %d %d %d (%s->%s) connection failed\",\n        x->gl_name->s_name, nout, outno, nin, inno,\n            (src? class_getname(pd_class(&src->g_pd)) : \"???\"),\n            (sink? class_getname(pd_class(&sink->g_pd)) : \"???\"));\n}\n\n#define XTOLERANCE 18\n#define YTOLERANCE 17\n#define NHIST 35\n\n    /* LATER might have to speed this up */\nstatic void canvas_tidy(t_canvas *x)\n{\n    t_gobj *y, *y2;\n    int ax1, ay1, ax2, ay2, bx1, by1, bx2, by2;\n    int histogram[NHIST], *ip, i, besthist, bestdist;\n        /* if nobody is selected, this means do it to all boxes;\n           otherwise just the selection */\n    int all = (x->gl_editor ? (x->gl_editor->e_selection == 0) : 1);\n\n    canvas_undo_add(x, UNDO_MOTION, \"{tidy up}\", canvas_undo_set_move(x, !all));\n\n        /* tidy horizontally */\n    for (y = x->gl_list; y; y = y->g_next)\n        if (all || glist_isselected(x, y))\n        {\n            gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2);\n\n            for (y2 = x->gl_list; y2; y2 = y2->g_next)\n                if (all || glist_isselected(x, y2))\n                {\n                    gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);\n                    if (by1 <= ay1 + YTOLERANCE && by1 >= ay1 - YTOLERANCE &&\n                        bx1 < ax1)\n                        goto nothorizhead;\n                }\n\n            for (y2 = x->gl_list; y2; y2 = y2->g_next)\n                if (all || glist_isselected(x, y2))\n                {\n                    gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);\n                    if (by1 <= ay1 + YTOLERANCE && by1 >= ay1 - YTOLERANCE\n                        && by1 != ay1)\n                        gobj_displace(y2, x, 0, ay1-by1);\n                }\n        nothorizhead: ;\n        }\n        /* tidy vertically.  First guess the user's favorite vertical spacing */\n    for (i = NHIST, ip = histogram; i--; ip++) *ip = 0;\n    for (y = x->gl_list; y; y = y->g_next)\n        if (all || glist_isselected(x, y))\n        {\n            gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2);\n            for (y2 = x->gl_list; y2; y2 = y2->g_next)\n                if (all || glist_isselected(x, y2))\n                {\n                    gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);\n                    if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE)\n                    {\n                        int distance = by1-ay2;\n                        if (distance >= 0 && distance < NHIST)\n                            histogram[distance]++;\n                    }\n                }\n        }\n    for (i = 2, besthist = 0, bestdist = 4, ip = histogram + 2;\n         i < (NHIST-2); i++, ip++)\n    {\n        int hit = ip[-2] + 2 * ip[-1] + 3 * ip[0] + 2* ip[1] + ip[2];\n        if (hit > besthist)\n        {\n            besthist = hit;\n            bestdist = i;\n        }\n    }\n    logpost(NULL, 3, \"tidy: best vertical distance %d\", bestdist);\n    for (y = x->gl_list; y; y = y->g_next)\n        if (all || glist_isselected(x, y))\n        {\n            int keep = 1;\n            gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2);\n            for (y2 = x->gl_list; y2; y2 = y2->g_next)\n                if (all || glist_isselected(x, y2))\n                {\n                    gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);\n                    if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE &&\n                        ay1 >= by2 - 10 && ay1 < by2 + NHIST)\n                        goto nothead;\n                }\n            while (keep)\n            {\n                keep = 0;\n                for (y2 = x->gl_list; y2; y2 = y2->g_next)\n                    if (all || glist_isselected(x, y2))\n                    {\n                        gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);\n                        if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE &&\n                            by1 > ay1 && by1 < ay2 + NHIST)\n                        {\n                            int vmove = ay2 + bestdist - by1;\n                            gobj_displace(y2, x, ax1-bx1, vmove);\n                            ay1 = by1 + vmove;\n                            ay2 = by2 + vmove;\n                            keep = 1;\n                            break;\n                        }\n                    }\n            }\n        nothead: ;\n        }\n    canvas_dirty(x, 1);\n}\n\n/* returns the total number of connections between two objects\n * outno/inno are set to the last connection indices\n */\nstatic int canvas_getconns(t_object*objsrc, int *outno, t_object*objsink, int *inno)\n{\n    int count = 0;\n    int n;\n    for(n=0; n<obj_noutlets(objsrc); n++)\n    {\n        t_outlet*out = 0;\n        t_outconnect *oc = obj_starttraverseoutlet(objsrc, &out, n);\n        while(oc)\n        {\n            t_object*o;\n            t_inlet*in;\n            int which;\n            oc = obj_nexttraverseoutlet(oc, &o, &in, &which);\n            if(o == objsink)\n                *outno = n, *inno = which, count++;\n        }\n    }\n    return count;\n}\nstatic int canvas_try_bypassobj1(t_canvas* x,\n    t_object* obj0, int in0, int out0,\n    t_object* obj1, int in1, int out1,\n    t_object* obj2, int in2, int out2)\n{\n        /* tries to bypass 'obj1' so 'obj0->obj1->obj2' becomes\n         * 'obj0->obj2' (+ 'obj1'); only bypass if there's exactly one\n         * connection between both obj0->obj1 and obj1->obj2 and the two\n         * connections are of the same type\n         * skip connection, if it's already there\n         */\n        /* this is doing an awful lot of iterating over the same things again and again\n         * LATER speed this up */\n    int A, B, C;\n        /* check connections (obj0->obj1->obj2, but not obj2->obj0) */\n        /*\n           valid:   out0, in1, out1, in2\n           invalid: in0, out2\n        */\n    if(out0<0 || out1<0 || out2>=0 || in0>=0 || in1<0 || in2<0)\n        return 0;\n        /* check whether the connection types match */\n    if(obj_issignaloutlet(obj0, out0) ^ obj_issignaloutlet(obj1, out1))\n        return 0;\n    A = glist_getindex(x, &obj0->te_g);\n    B = glist_getindex(x, &obj1->te_g);\n    C = glist_getindex(x, &obj2->te_g);\n    canvas_disconnect_with_undo(x, A, out0, B, in1);\n    canvas_disconnect_with_undo(x, B, out1, C, in2);\n    if (!canvas_isconnected(x, obj0, out0, obj2, in2))\n        canvas_connect_with_undo(x, A, out0, C, in2);\n    return 1;\n}\nstatic int canvas_try_insert(t_canvas *x\n    , t_object* obj00, int in00, int out00 /* source */\n    , t_object* obj11, int in11, int out11 /* sink   */\n    , t_object* obj22, int in22, int out22 /* insert */\n    )\n{\n    int out21 = 0, in02 = 0; /* iolets of the insert-objects */\n    int A, B, C;\n\n        /* check connections (obj00->obj11, but not obj22) */\n    if(out00<0 || out22>=0 || out11>=0 || in00>=0 || in22>=0 || in11<0)\n        return 0;\n\n        /* check whether the connection types match */\n    if((obj_issignaloutlet(obj00, out00) && !obj_issignalinlet(obj22, in02)))\n        return 0;\n    if((obj_issignaloutlet(obj22, out21) && !obj_issignalinlet(obj11, in11)))\n        return 0;\n\n        /* then connect them */\n    A = glist_getindex(x, &obj00->te_g);\n    B = glist_getindex(x, &obj11->te_g);\n    C = glist_getindex(x, &obj22->te_g);\n\n    canvas_disconnect_with_undo(x, A, out00, B, in11);\n    if (!canvas_isconnected     (x, obj00, out00, obj22, in02))\n        canvas_connect_with_undo(x, A,     out00, C,     in02);\n    if (!canvas_isconnected     (x, obj22, out21, obj11, in11))\n        canvas_connect_with_undo(x, C,     out21, B,     in11);\n    return 1;\n}\n    /* If we have two selected objects on the canvas, try to connect\n       the first outlet of the upper object to the first inlet with\n       a compatible type in the lower one. */\nstatic void canvas_connect_selection(t_canvas *x)\n{\n    t_gobj *a, *b, *c;\n    t_selection *sel;\n    t_object *objsrc, *objsink;\n\n    a = b = c = NULL;\n    sel = x->gl_editor ? x->gl_editor->e_selection : NULL;\n    for (; sel; sel = sel->sel_next)\n    {\n        if (!a)\n            a = sel->sel_what;\n        else if (!b)\n            b = sel->sel_what;\n        else if (!c)\n            c = sel->sel_what;\n        else\n            return;\n    }\n\n    if(!a)\n        return;\n\n    if(!b)\n    {\n            /* only a single object is selected.\n             * if a connection is selected, insert the object\n             * if no connection is selected, disconnect the object\n             */\n        t_object*obj = pd_checkobject(&a->g_pd);\n        if(!obj)\n            return;\n        if(x->gl_editor->e_selectedline)\n        {\n            b = glist_nth(x, x->gl_editor->e_selectline_index1);\n            objsrc = b?pd_checkobject(&b->g_pd):0;\n            b = glist_nth(x, x->gl_editor->e_selectline_index2);\n            objsink = b?pd_checkobject(&b->g_pd):0;\n\n            if(canconnect(x, objsrc, x->gl_editor->e_selectline_outno, obj, 0)\n               && canconnect(x, obj, 0, objsink, x->gl_editor->e_selectline_inno))\n            {\n                canvas_undo_add(x, UNDO_SEQUENCE_START, \"reconnect\", 0);\n                tryconnect(x, objsrc, x->gl_editor->e_selectline_outno, obj, 0);\n                tryconnect(x, obj, 0, objsink, x->gl_editor->e_selectline_inno);\n                canvas_clearline(x);\n                canvas_undo_add(x, UNDO_SEQUENCE_END, \"reconnect\", 0);\n            }\n        }\n        else\n        {\n                /* disconnect the entire object */\n            t_linetraverser t;\n            t_outconnect *oc;\n            canvas_undo_add(x, UNDO_SEQUENCE_START, \"disconnect\", 0);\n            linetraverser_start(&t, x);\n            while ((oc = linetraverser_next(&t)))\n            {\n                if ((obj == t.tr_ob) || (obj == t.tr_ob2))\n                {\n                    int srcno = glist_getindex(x, &t.tr_ob->ob_g);\n                    int sinkno = glist_getindex(x, &t.tr_ob2->ob_g);\n                    canvas_disconnect_with_undo(x, srcno, t.tr_outno, sinkno, t.tr_inno);\n                }\n            }\n            canvas_undo_add(x, UNDO_SEQUENCE_END, \"disconnect\", 0);\n        }\n            /* need to return since we have touched 'b' */\n        return;\n    }\n\n    if(!c)\n    {\n            /* exactly two objects are selected\n             * connect them (top to bottom) if they are patchable\n             */\n        if (!(objsrc = pd_checkobject(&a->g_pd)) ||\n            !(objsink = pd_checkobject(&b->g_pd)))\n            return;\n\n        if(objsink->te_ypix < objsrc->te_ypix)\n        {\n            t_object*obj = objsink;\n            objsink = objsrc;\n            objsrc = obj;\n        }\n        if (!objsrc || !objsink)\n            return;\n        if (obj_noutlets(objsrc))\n        {\n            int noutlets = obj_noutlets(objsrc);\n            int ninlets = obj_ninlets(objsink);\n            int fanout = (noutlets == 1) && obj_issignaloutlet(objsrc, 0);\n            int out = 0, in = 0;\n            while(!tryconnect(x, objsrc, out, objsink, in))\n            {\n                if (noutlets <= out)\n                    return;\n                if (ninlets <= in )\n                    return;\n                in++;\n                if(!fanout)\n                    out++;\n            }\n        }\n        return;\n    }\n\n        /* exactly three objects are selected\n         * if they are chained up, unconnect the middle object, and connect the source to the sink\n         * if only two of them are connected, insert the third\n         */\n    if ((objsrc = pd_checkobject(&a->g_pd)) &&\n        (objsink = pd_checkobject(&b->g_pd)))\n    {\n        t_object *obj0 = objsrc, *obj2 = objsink;\n        t_object *obj1 = pd_checkobject(&c->g_pd);\n        int out01, out02, out10, out12, out20, out21;\n        int in01, in02, in10, in12, in20, in21;\n        if(!obj1\n           || (obj0 == obj1)\n           || (obj2 == obj1)\n           || (obj0 == obj2))\n            return;\n#define GET1CONN(a, b) \\\n        if (1 != canvas_getconns(obj##a, &out##a##b, obj##b, &in##b##a)) \\\n            out##a##b = in##b##a = -1\n        GET1CONN(0, 1);\n        GET1CONN(0, 2);\n        GET1CONN(1, 0);\n        GET1CONN(1, 2);\n        GET1CONN(2, 0);\n        GET1CONN(2, 1);\n#define TRYCONNCHANGE(fun, a, b, c)                                      \\\n        canvas_try_##fun(x, obj##a, in##a##c, out##a##b, obj##b, in##b##a, out##b##c, obj##c, in##c##b, out##c##a)\n\n        canvas_undo_add(x, UNDO_SEQUENCE_START, \"reconnect\", 0);\n        0\n            || TRYCONNCHANGE(bypassobj1, 0, 1, 2)\n            || TRYCONNCHANGE(bypassobj1, 0, 2, 1)\n            || TRYCONNCHANGE(bypassobj1, 1, 0, 2)\n            || TRYCONNCHANGE(bypassobj1, 1, 2, 0)\n            || TRYCONNCHANGE(bypassobj1, 2, 0, 1)\n            || TRYCONNCHANGE(bypassobj1, 2, 1, 0)\n            || TRYCONNCHANGE(insert,     0, 1, 2)\n            || TRYCONNCHANGE(insert,     0, 2, 1)\n            || TRYCONNCHANGE(insert,     1, 0, 2)\n            || TRYCONNCHANGE(insert,     1, 2, 0)\n            || TRYCONNCHANGE(insert,     2, 0, 1)\n            || TRYCONNCHANGE(insert,     2, 1, 0)\n            ;\n        canvas_undo_add(x, UNDO_SEQUENCE_END, \"reconnect\", 0);\n    }\n}\n\nstatic void canvas_texteditor(t_canvas *x)\n{\n    t_rtext *foo;\n    char *buf;\n    int bufsize;\n    if ((foo = x->gl_editor->e_textedfor))\n        rtext_gettext(foo, &buf, &bufsize);\n    else buf = \"\", bufsize = 0;\n    pdgui_vmess(\"pdtk_pd_texteditor\", \"p\", bufsize, buf);\n}\n\nvoid glob_key(void *dummy, t_symbol *s, int ac, t_atom *av)\n{\n        /* canvas_key checks for zero */\n    canvas_key(0, s, ac, av);\n}\n\nvoid canvas_editmode(t_canvas *x, t_floatarg state)\n{\n    if (x->gl_edit == (unsigned int) state)\n        return;\n    x->gl_edit = (unsigned int) state;\n    if (x->gl_edit && glist_isvisible(x) && glist_istoplevel(x))\n    {\n        t_gobj *g;\n        t_object *ob;\n        canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);\n        for (g = x->gl_list; g; g = g->g_next)\n            if ((ob = pd_checkobject(&g->g_pd)) && ob->te_type == T_TEXT)\n            {\n                t_rtext *y = glist_findrtext(x, ob);\n                text_drawborder(ob, x,\n                                rtext_gettag(y), rtext_width(y), rtext_height(y), 1);\n            }\n    }\n    else\n    {\n        glist_noselect(x);  /* this can knock us back into edit mode so : */\n        x->gl_edit = (unsigned int) state;\n        if (glist_isvisible(x) && glist_istoplevel(x))\n        {\n            canvas_setcursor(x, CURSOR_RUNMODE_NOTHING);\n            pdgui_vmess(0, \"crs\",\n                glist_getcanvas(x), \"delete\", \"commentbar\");\n        }\n    }\n    if (glist_isvisible(x) && x->gl_havewindow)\n    {\n        pdgui_vmess(\"pdtk_canvas_editmode\", \"^i\",\n            glist_getcanvas(x), x->gl_edit);\n        canvas_reflecttitle(x);\n    }\n}\n\n    /* called by canvas_font below */\nstatic void canvas_dofont(t_canvas *x, t_floatarg font, t_floatarg xresize,\n    t_floatarg yresize)\n{\n    t_gobj *y;\n    x->gl_font = font;\n    if (xresize != 1 || yresize != 1)\n    {\n        canvas_setundo(x, canvas_undo_move, canvas_undo_set_move(x, 0),\n            \"motion\");\n        for (y = x->gl_list; y; y = y->g_next)\n        {\n            int x1, x2, y1, y2, nx1, ny1;\n            gobj_getrect(y, x, &x1, &y1, &x2, &y2);\n            nx1 = x1 * xresize + 0.5;\n            ny1 = y1 * yresize + 0.5;\n            gobj_displace(y, x, nx1-x1, ny1-y1);\n        }\n    }\n    for (y = x->gl_list; y; y = y->g_next)\n        if (pd_checkglist(&y->g_pd)  && !canvas_isabstraction((t_canvas *)y))\n            canvas_dofont((t_canvas *)y, font, xresize, yresize);\n    if(x->gl_havewindow) canvas_redraw(x);\n}\n\n    /* canvas_menufont calls up a TK dialog which calls this back */\nstatic void canvas_font(t_canvas *x, t_floatarg font, t_floatarg resize,\n    t_floatarg whichresize)\n{\n    t_float realresize, realresx = 1, realresy = 1;\n    t_canvas *x2 = canvas_getrootfor(x);\n    int oldfont = x2->gl_font;\n    if (!resize) realresize = 1;\n    else\n    {\n        if (resize < 20) resize = 20;\n        if (resize > 500) resize = 500;\n        realresize = resize * 0.01;\n    }\n    if (whichresize != 3) realresx = realresize;\n    if (whichresize != 2) realresy = realresize;\n    canvas_dofont(x2, font, realresx, realresy);\n    canvas_undo_add(x2, UNDO_FONT, \"font\",\n        canvas_undo_set_font(x2, oldfont, realresize, whichresize));\n\n    sys_defaultfont = font;\n}\n\nvoid glist_getnextxy(t_glist *gl, int *xpix, int *ypix)\n{\n    if (EDITOR->canvas_last_glist == gl)\n        *xpix = EDITOR->canvas_last_glist_x,\n        *ypix = EDITOR->canvas_last_glist_y;\n    else *xpix = *ypix = 40;\n}\n\nstatic void glist_setlastxy(t_glist *gl, int xval, int yval)\n{\n    EDITOR->canvas_last_glist = gl;\n    EDITOR->canvas_last_glist_x = xval;\n    EDITOR->canvas_last_glist_y = yval;\n}\n\n\nvoid canvas_triggerize(t_glist*cnv);\n\nvoid g_editor_setup(void)\n{\n/* ------------------------ events ---------------------------------- */\n    class_addmethod(canvas_class, (t_method)canvas_mouse, gensym(\"mouse\"),\n        A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_mouseup, gensym(\"mouseup\"),\n        A_FLOAT, A_FLOAT, A_FLOAT, A_DEFFLOAT, A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_key, gensym(\"key\"),\n        A_GIMME, A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_motion, gensym(\"motion\"),\n        A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);\n\n/* ------------------------ menu actions ---------------------------- */\n    class_addmethod(canvas_class, (t_method)canvas_menuclose,\n        gensym(\"menuclose\"), A_DEFFLOAT, 0);\n    class_addmethod(canvas_class, (t_method)canvas_cut,\n        gensym(\"cut\"), A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_copy,\n        gensym(\"copy\"), A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_paste,\n        gensym(\"paste\"), A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_paste_replace,\n        gensym(\"paste-replace\"), A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_duplicate,\n        gensym(\"duplicate\"), A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_selectall,\n        gensym(\"selectall\"), A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_deselectall,\n        gensym(\"deselectall\"), A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_reselect,\n        gensym(\"reselect\"), A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_cycleselect,\n        gensym(\"cycleselect\"), A_FLOAT, A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_undo_undo,\n        gensym(\"undo\"), A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_undo_redo,\n        gensym(\"redo\"), A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_tidy,\n        gensym(\"tidy\"), A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_connect_selection,\n        gensym(\"connect_selection\"), A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_texteditor,\n        gensym(\"texteditor\"), A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_editmode,\n        gensym(\"editmode\"), A_DEFFLOAT, A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_print,\n        gensym(\"print\"), A_SYMBOL, A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_menufont,\n        gensym(\"menufont\"), A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_font,\n        gensym(\"font\"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_zoom,\n        gensym(\"zoom\"), A_FLOAT, A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_find,\n        gensym(\"find\"), A_SYMBOL, A_FLOAT, A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_find_again,\n        gensym(\"findagain\"), A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_find_parent,\n        gensym(\"findparent\"), A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_done_popup,\n        gensym(\"done-popup\"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_donecanvasdialog,\n        gensym(\"donecanvasdialog\"), A_GIMME, A_NULL);\n    class_addmethod(canvas_class, (t_method)glist_arraydialog,\n        gensym(\"arraydialog\"), A_SYMBOL, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_triggerize,\n        gensym(\"triggerize\"), 0);\n    class_addmethod(canvas_class, (t_method)canvas_disconnect,\n        gensym(\"disconnect\"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);\n}\n\nvoid canvas_editor_for_class(t_class *c)\n{\n    class_addmethod(c, (t_method)canvas_mouse, gensym(\"mouse\"),\n        A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);\n    class_addmethod(c, (t_method)canvas_mouseup, gensym(\"mouseup\"),\n        A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);\n    class_addmethod(c, (t_method)canvas_key, gensym(\"key\"),\n        A_GIMME, A_NULL);\n    class_addmethod(c, (t_method)canvas_motion, gensym(\"motion\"),\n        A_FLOAT, A_FLOAT, A_FLOAT, A_DEFFLOAT, A_NULL);\n\n/* ------------------------ menu actions ---------------------------- */\n    class_addmethod(c, (t_method)canvas_menuclose,\n        gensym(\"menuclose\"), A_DEFFLOAT, 0);\n    class_addmethod(c, (t_method)canvas_find_parent,\n        gensym(\"findparent\"), A_NULL);\n}\n\nvoid g_editor_newpdinstance(void)\n{\n    EDITOR = getbytes(sizeof(*EDITOR));\n        /* other stuff is null-checked but this needs to exist: */\n    EDITOR->copy_binbuf = binbuf_new();\n}\n\nvoid g_editor_freepdinstance(void)\n{\n    if (EDITOR->copy_binbuf)\n        binbuf_free(EDITOR->copy_binbuf);\n    if (EDITOR->canvas_undo_buf)\n    {\n        if (!EDITOR->canvas_undo_fn)\n            bug(\"g_editor_freepdinstance\");\n        else (*EDITOR->canvas_undo_fn)\n            (EDITOR->canvas_undo_canvas, EDITOR->canvas_undo_buf, UNDO_FREE);\n    }\n    if (EDITOR->canvas_findbuf)\n        binbuf_free(EDITOR->canvas_findbuf);\n    freebytes(EDITOR, sizeof(*EDITOR));\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/g_editor_extras.c",
    "content": "/* Copyright (c) 2018 Miller Puckette, IOhannes m zmölnig and others.\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n\n#include \"m_pd.h\"\n\n#include \"g_canvas.h\"\n#include \"m_imp.h\"\n#include \"g_undo.h\"\n\n\nvoid *canvas_undo_set_pastebinbuf(t_canvas *x, t_binbuf *b,\n    int numpasted, int duplicate, int d_offset);\n\n/* ------------ utilities ---------- */\ntypedef struct _triggerize_return {\n        /* data to return from triggerize */\n    t_gobj*tr_editgobj; /* if set, immediately switch this object to being edited */\n} t_triggerize_return;\n\n\nstatic t_gobj*o2g(t_object*obj)\n{\n    return &(obj->te_g);\n}\nstatic t_object*g2o(t_gobj*gobj)\n{\n    return pd_checkobject(&gobj->g_pd);\n}\nstatic t_gobj*glist_getlast(t_glist*cnv)\n{\n        /* get last object on <ncv> */\n    t_gobj*result=NULL;\n    for(result=cnv->gl_list; result->g_next;) result=result->g_next;\n    return result;\n}\nstatic void dereconnect(t_glist*cnv, t_object*org, t_object*replace)\n{\n    t_gobj*gobj;\n    int replace_i = canvas_getindex(cnv, o2g(replace));\n    for(gobj=cnv->gl_list; gobj; gobj=gobj->g_next)\n    {\n        t_object*obj=g2o(gobj);\n        int obj_i = canvas_getindex(cnv, gobj);\n        int obj_nout=0;\n        int nout;\n        if(!obj)continue;\n        obj_nout=obj_noutlets(obj);\n        for(nout=0; nout<obj_nout; nout++)\n        {\n            t_outlet*out=0;\n            t_outconnect*conn=obj_starttraverseoutlet(obj, &out, nout);\n            while(conn)\n            {\n                int which;\n                t_object*dest=0;\n                t_inlet *in =0;\n                int dest_i;\n                conn=obj_nexttraverseoutlet(conn, &dest, &in, &which);\n                if(dest!=org)\n                    continue;\n                dest_i = canvas_getindex(cnv, o2g(dest));\n                obj_disconnect(obj, nout, dest, which);\n                canvas_undo_add(cnv, UNDO_DISCONNECT, \"disconnect\",\n                    canvas_undo_set_disconnect(cnv, obj_i, nout, dest_i, which));\n                obj_connect(obj, nout, replace, which);\n                canvas_undo_add(cnv, UNDO_CONNECT, \"connect\",\n                    canvas_undo_set_connect(cnv, obj_i, nout, replace_i, which));\n            }\n        }\n    }\n}\nstatic void obj_delete_undo(t_glist*x, t_object *obj)\n{\n        /* delete an object with undo */\n    t_gobj*gobj=o2g(obj);\n    canvas_undo_add(x, UNDO_RECREATE, \"recreate\",\n        (void *)canvas_undo_set_recreate(x, gobj,canvas_getindex(x, gobj)));\n    glist_delete(x, gobj);\n}\nstatic t_object*triggerize_createobj(t_glist*x, t_binbuf*b)\n{\n        /* send a binbuf to a canvas */\n    t_pd *boundx = s__X.s_thing, *boundn = s__N.s_thing;\n    s__X.s_thing = &x->gl_pd;\n    s__N.s_thing = &pd_canvasmaker;\n\n    binbuf_eval(b, 0, 0, 0);\n\n    s__X.s_thing = boundx;\n    s__N.s_thing = boundn;\n    return g2o(glist_getlast(x));\n}\nstatic void stack_conn(t_glist*x, t_object*new, int*newoutlet, t_object*org,\n    int orgoutlet, t_outconnect*conn)\n{\n    t_object*dest=0;\n    t_inlet *in =0;\n    int which;\n    int new_i, org_i, dest_i;\n    if(!conn)\n        return;\n    conn=obj_nexttraverseoutlet(conn, &dest, &in, &which);\n    stack_conn(x, new, newoutlet, org, orgoutlet, conn);\n    new_i = canvas_getindex(x, o2g(new));\n    org_i = canvas_getindex(x, o2g(org));\n    dest_i = canvas_getindex(x, o2g(dest));\n    obj_disconnect(org, orgoutlet, dest, which);\n    canvas_undo_add(x, UNDO_DISCONNECT, \"disconnect\",\n        canvas_undo_set_disconnect(x, org_i, orgoutlet, dest_i, which));\n    obj_connect(new, *newoutlet, dest, which);\n    canvas_undo_add(x, UNDO_CONNECT, \"connect\",\n        canvas_undo_set_connect(x, new_i, *newoutlet, dest_i, which));\n    (*newoutlet)++;\n}\nstatic int has_fanout(t_object*obj)\n{\n        /* check if we actually do have a fan out */\n    int obj_nout=obj_noutlets(obj);\n    int nout;\n    for(nout=0; nout<obj_nout; nout++)\n    {\n        t_outlet*out=0;\n        t_outconnect*conn=obj_starttraverseoutlet(obj, &out, nout);\n        int count=0;\n        if(obj_issignaloutlet(obj, nout))\n            continue;\n        while(conn)\n        {\n            int which;\n            t_object*dest=0;\n            t_inlet *in =0;\n            if(count)return 1;\n            conn=obj_nexttraverseoutlet(conn, &dest, &in, &which);\n            count++;\n        }\n    }\n    return 0;\n}\nstatic int only_triggers_selected(t_glist*cnv)\n{\n        /* check if all selected objects in <cnv> are triggers */\n    const t_symbol*s_trigger=gensym(\"trigger\");\n    t_gobj*gobj = NULL;\n\n    for(gobj=cnv->gl_list; gobj; gobj=gobj->g_next)\n    {\n        t_object*obj=g2o(gobj);\n        if(obj && glist_isselected(cnv, gobj)\n           && (s_trigger != obj->te_g.g_pd->c_name))\n        {\n            return 0;\n        }\n    }\n    return 1;\n}\n\n/* ------------------------- triggerize ---------------------------- */\nstatic int triggerize_fanout_inplace(t_glist*x, t_object*obj)\n{\n        /* avoid fanouts in [t] objects by adding additional outlets */\n\n    int posX=obj->te_xpix;\n    int posY=obj->te_ypix;\n    t_atom*argv=binbuf_getvec(obj->te_binbuf);\n    int obj_nout=obj_noutlets(obj);\n    int nout, newout;\n    t_binbuf*b=0;\n    t_object*stub=0;\n        /* check if we actually do have a fan out */\n    if(!has_fanout(obj))return 0;\n        /* create a new trigger object, that has outlets for the fans */\n    b=binbuf_new();\n    binbuf_addv(b, \"ssii\", gensym(\"#X\"), gensym(\"obj\"), posX, posY);\n    binbuf_add(b, 1, argv);\n    argv++;\n    for(nout=0; nout<obj_nout; nout++)\n    {\n        t_outlet*out=0;\n        t_outconnect*conn=obj_starttraverseoutlet(obj, &out, nout);\n        while(conn)\n        {\n            int which;\n            t_object*dest=0;\n            t_inlet *in =0;\n            conn=obj_nexttraverseoutlet(conn, &dest, &in, &which);\n            binbuf_add(b, 1, argv);\n        }\n        argv++;\n    }\n    binbuf_addsemi(b);\n    canvas_undo_add(x, UNDO_PASTE, \"paste\",\n        canvas_undo_set_pastebinbuf(x, b, 0, 0, 0));\n    stub=triggerize_createobj(x, b);\n    binbuf_free(b);\n\n        /* connect */\n    newout=0;\n    dereconnect(x, obj, stub);\n    for(nout=0; nout<obj_nout; nout++)\n    {\n        t_outlet*out=0;\n        t_outconnect*conn=obj_starttraverseoutlet(obj, &out, nout);\n        stack_conn(x, stub, &newout, obj, nout, conn);\n    }\n\n        /* free old object */\n    obj_delete_undo(x, obj);\n    return 1;\n}\nstatic void triggerize_defanout(t_glist*x, int count, t_outconnect*conn,\n    t_object*obj, t_object*trigger, int nout)\n{\n    t_object *dest=0;\n    t_inlet *in=0;\n    int which=0;\n    int obj_i = canvas_getindex(x, o2g(obj));\n    int trigger_i = canvas_getindex(x, o2g(trigger));\n    int dest_i;\n    if(!conn) return;\n    conn = obj_nexttraverseoutlet(conn, &dest, &in, &which);\n    triggerize_defanout(x, count-1, conn, obj, trigger, nout);\n\n    dest_i = canvas_getindex(x, o2g(dest));\n    obj_disconnect(obj, nout, dest, which);\n    canvas_undo_add(x, UNDO_DISCONNECT, \"disconnect\",\n        canvas_undo_set_disconnect(x, obj_i, nout, dest_i, which));\n    obj_connect(trigger, count, dest, which);\n    canvas_undo_add(x, UNDO_CONNECT, \"connect\",\n        canvas_undo_set_connect(x, trigger_i, count, dest_i, which));\n}\n\nstatic int triggerize_fanout(t_glist*x, t_object*obj)\n{\n        /* replace all fanouts with [trigger]s\n         * if the fanning out object is already a trigger, just expand it */\n    const t_symbol*s_trigger=gensym(\"trigger\");\n        /* shift trigger object slightly, to make it easier selectable in case of overlaps: */\n    const int xoffset = -10;\n    const int yoffset = 5;\n    int obj_nout=obj_noutlets(obj);\n    int nout;\n    int posX = 0, posY;\n    int didit=0;\n\n    int _x; /* dummy variable */\n    gobj_getrect(o2g(obj), x, &_x, &_x, &_x, &posY);\n    posY /= x->gl_zoom;\n    posY += yoffset;\n\n        /* if the object is a [trigger], we just insert new outlets */\n    if(s_trigger == obj->te_g.g_pd->c_name)\n    {\n        return triggerize_fanout_inplace(x, obj);\n    }\n        /* for other objects, we create a new [trigger a a] object\n         *  and replace the fan-out with that */\n    for(nout=0; nout<obj_nout; nout++)\n    {\n        t_outlet*out=0;\n        t_outconnect*conn=obj_starttraverseoutlet(obj, &out, nout);\n        int count=0;\n        if(obj_issignaloutlet(obj, nout))\n            continue;\n        while(conn)\n        {\n            int which;\n            t_object*dest=0;\n            t_inlet *in =0;\n            conn=obj_nexttraverseoutlet(conn, &dest, &in, &which);\n            count++;\n        }\n        if(count>1)\n        {\n            int i, obj_i, stub_i;\n            t_object *stub;\n                /* fan out: create a [t] object to resolve it */\n\n                /* need to get the coordinates of the fanning outlet */\n            t_linetraverser t;\n            t_binbuf*b=binbuf_new();\n            linetraverser_start(&t, x);\n            while((conn = linetraverser_next(&t)))\n            {\n                if((obj == t.tr_ob) && nout == t.tr_outno)\n                {\n                    posX = (t.tr_lx1 / t.tr_x->gl_zoom) - (IOMIDDLE - xoffset);\n                    break;\n                }\n            }\n\n            obj_i = canvas_getindex(x, o2g(obj));\n            stub=0;\n            binbuf_clear(b);\n            binbuf_addv(b, \"ssiis\", gensym(\"#X\"), gensym(\"obj\"), posX, posY, gensym(\"t\"));\n            for(i=0; i<count; i++)\n            {\n                binbuf_addv(b, \"s\", gensym(\"a\"));\n            }\n            binbuf_addsemi(b);\n            canvas_undo_add(x, UNDO_PASTE, \"paste\",\n                canvas_undo_set_pastebinbuf(x, b, 0, 0, 0));\n            stub=triggerize_createobj(x, b);\n            binbuf_free(b);\n            stub_i = canvas_getindex(x, o2g(stub));\n            conn=obj_starttraverseoutlet(obj, &out, nout);\n            triggerize_defanout(x, count-1, conn, obj, stub, nout);\n            obj_connect(obj, nout, stub, 0);\n            canvas_undo_add(x, UNDO_CONNECT, \"connect\",\n                canvas_undo_set_connect(x, obj_i, nout, stub_i, 0));\n            glist_select(x, o2g(stub));\n            didit++;\n        }\n    }\n    return didit;\n}\nstatic int triggerize_fanouts(t_glist*cnv)\n{\n        /* iterate over all selected objects and try to eliminate fanouts */\n    t_gobj*gobj = NULL;\n    int count=0;\n    canvas_undo_add(cnv, UNDO_SEQUENCE_START, \"triggerize\", 0);\n    for(gobj=cnv->gl_list; gobj; )\n    {\n        t_gobj*next=gobj->g_next;\n        t_object*obj=g2o(gobj);\n        if(obj && glist_isselected(cnv, gobj) && triggerize_fanout(cnv, obj))\n            count++;\n        gobj = next;\n    }\n    canvas_undo_add(cnv, UNDO_SEQUENCE_END, \"triggerize\", 0);\n    return count;\n}\n\nstatic int triggerize_line(t_glist*x, t_triggerize_return*tr)\n{\n        /* triggerize a single selected line, by inserting a [t a] object\n         * (or it's signal equivalent) */\n    t_editor*ed=x->gl_editor;\n    int src_obj, src_out, dst_obj, dst_in, new_obj;\n    t_gobj *src = 0, *dst = 0;\n    t_binbuf*b=0;\n    int posx=100, posy=100;\n    t_object*stub=0;\n    int sigline = 0;\n    int dspstate = 0;\n\n    if(!ed->e_selectedline)\n        return 0;\n    src_obj=ed->e_selectline_index1;\n    src_out=ed->e_selectline_outno;\n    dst_obj=ed->e_selectline_index2;\n    dst_in =ed->e_selectline_inno;\n    for (src = x->gl_list; src_obj; src = src->g_next, src_obj--)\n        if (!src->g_next) goto bad;\n    for (dst = x->gl_list; dst_obj; dst = dst->g_next, dst_obj--)\n        if (!dst->g_next) goto bad;\n    src_obj=ed->e_selectline_index1;\n    dst_obj=ed->e_selectline_index2;\n\n    if(1)\n    {\n        t_object*obj1=g2o(src);\n        t_object*obj2=g2o(dst);\n        if(obj1 && obj2)\n        {\n            float posSource, posSink;\n            int nio;\n            int _x; /* dummy variable */\n            int posSourceY, posSinkY;\n            int boxHeight;  /* height of inserted box */\n            int posLeft, posRight;\n\n                /* get real x-position of the outlet */\n            gobj_getrect(src, x, &posLeft, &_x, &posRight, &posSourceY);\n            posLeft /= x->gl_zoom;\n            posRight /= x->gl_zoom;\n            posSourceY /= x->gl_zoom;\n            nio = obj_noutlets(obj1);\n            posSource = posLeft + (posRight - posLeft - IOWIDTH) * src_out / ((nio==1)?1.:(nio-1.));\n\n                /* get real x-position of the inlet */\n            gobj_getrect(dst, x, &posLeft, &posSinkY, &posRight, &_x);\n            posLeft /= x->gl_zoom;\n            posRight /= x->gl_zoom;\n            posSinkY /= x->gl_zoom;\n            nio = obj_ninlets(obj2);\n            posSink = posLeft + (posRight - posLeft - IOWIDTH) * dst_in / ((nio==1)?1.:(nio-1.));\n\n\t\t/* get height of the box that will be inserted */\n            boxHeight = glist_fontheight(x) / x->gl_zoom + 4; /* ATOM_BMARGIN = 4 */\n\n            posx = (posSource + posSink) * 0.5;\n            posy = (posSourceY + posSinkY - boxHeight) >> 1;\n        }\n    }\n\n    sigline = obj_issignaloutlet(g2o(src), src_out);\n    if(sigline)\n        dspstate = canvas_suspend_dsp();\n\n    canvas_undo_add(x, UNDO_SEQUENCE_START, \"{insert object}\", 0);\n    b=binbuf_new();\n    if(sigline)\n    {\n        binbuf_addv(b, \"ssiiiisi;\", gensym(\"#N\"),\n            gensym(\"canvas\"), 200, 100, 190, 200, gensym(\"nop~\"), 0);\n        binbuf_addv(b, \"ssiis;\", gensym(\"#X\"),\n            gensym(\"obj\"), 50, 70, gensym(\"inlet~\"));\n        binbuf_addv(b, \"ssiis;\", gensym(\"#X\"),\n            gensym(\"obj\"), 50,140, gensym(\"outlet~\"));\n        binbuf_addv(b, \"ssiiii;\", gensym(\"#X\"),\n            gensym(\"connect\"), 0,0,1,0);\n        binbuf_addv(b, \"ssiiss\", gensym(\"#X\"),\n            gensym(\"restore\"), posx, posy, gensym(\"pd\"), gensym(\"nop~\"));\n    } else {\n        binbuf_addv(b,\"ssii\", gensym(\"#X\"), gensym(\"obj\"), posx, posy);\n        binbuf_addv(b,\"ss\", gensym(\"t\"), gensym(\"a\"));\n    }\n    binbuf_addsemi(b);\n    canvas_undo_add(x, UNDO_PASTE, \"paste\",\n        canvas_undo_set_pastebinbuf(x, b, 0, 0, 0));\n\n    stub=triggerize_createobj(x, b);\n    new_obj = canvas_getindex(x, &stub->ob_g);\n    binbuf_free(b);b=0;\n\n    obj_disconnect(g2o(src), src_out, g2o(dst), dst_in);\n    canvas_undo_add(x, UNDO_DISCONNECT, \"disconnect\",\n        canvas_undo_set_disconnect(x, src_obj, src_out, dst_obj, dst_in));\n\n    obj_connect(g2o(src), src_out, stub, 0);\n    canvas_undo_add(x, UNDO_CONNECT, \"connect\",\n        canvas_undo_set_connect(x, src_obj, src_out, new_obj, 0));\n\n    obj_connect(stub, 0, g2o(dst), dst_in);\n    canvas_undo_add(x, UNDO_CONNECT, \"connect\",\n        canvas_undo_set_connect(x, new_obj, 0, dst_obj, dst_in));\n\n    glist_select(x, o2g(stub));\n\n    canvas_undo_add(x, UNDO_SEQUENCE_END, \"{insert object}\", 0);\n        /* remember the inserted object, so we can select/edit it later */\n    if(tr)\n        tr->tr_editgobj = o2g(stub);\n    if(sigline)\n        canvas_resume_dsp(dspstate);\n    return 1;\nbad:\n    return 0;\n}\nstatic int minimize_trigger(t_glist*cnv, t_object*obj)\n{\n        /* remove all unused outlets from [trigger] */\n    t_binbuf*b=binbuf_new();\n    t_atom*argv=binbuf_getvec(obj->te_binbuf);\n    t_object*stub=0;\n    int obj_nout=obj_noutlets(obj);\n    int nout, stub_i, obj_i = canvas_getindex(cnv, o2g(obj));\n\n    int count = 0;\n\n    binbuf_addv(b, \"ssii\", gensym(\"#X\"),\n        gensym(\"obj\"), obj->te_xpix, obj->te_ypix);\n    binbuf_add(b, 1, argv);\n        /* go through all the outlets, and add those that have connections */\n    for(nout = 0; nout < obj_nout; nout++)\n    {\n        t_outlet*out=0;\n        t_outconnect*conn = obj_starttraverseoutlet(obj, &out, nout);\n        if(conn)\n        {\n            binbuf_add(b, 1, argv + 1 + nout);\n        } else {\n            count++;\n        }\n    }\n    if(!count || count == obj_nout)\n    {\n            /* either no outlet to delete or all: skip this object */\n        binbuf_free(b);\n        return 0;\n    }\n        /* create the replacement object (which only has outlets that\n         * are going to be connected) */\n    canvas_undo_add(cnv, UNDO_PASTE, \"paste\",\n        canvas_undo_set_pastebinbuf(cnv, b, 0, 0, 0));\n    stub=triggerize_createobj(cnv, b);\n    stub_i = canvas_getindex(cnv, o2g(stub));\n\n        /* no go through the original object's outlets, and duplicate the\n         * connection of each to the new object */\n    for(nout=0, count=0; nout<obj_nout; nout++)\n    {\n        t_outlet*out=0;\n        t_outconnect*conn=obj_starttraverseoutlet(obj, &out, nout);\n        if(!conn) /* nothing connected here; skip it */\n            continue;\n\n            /* repeat all connections of this outlet (should only be one) */\n        while(conn)\n        {\n            int which, dest_i;\n            t_object*dest=0;\n            t_inlet *in =0;\n            conn=obj_nexttraverseoutlet(conn, &dest, &in, &which);\n            dest_i = canvas_getindex(cnv, o2g(dest));\n            obj_disconnect(obj, nout, dest, which);\n            canvas_undo_add(cnv, UNDO_DISCONNECT, \"disconnect\",\n                canvas_undo_set_disconnect(cnv, obj_i, nout, dest_i, which));\n            obj_connect(stub, count, dest, which);\n            canvas_undo_add(cnv, UNDO_CONNECT, \"connect\",\n                canvas_undo_set_connect(cnv, stub_i, count, dest_i, which));\n        }\n        count++;\n    }\n    binbuf_free(b);\n    dereconnect(cnv, obj, stub);\n    obj_delete_undo(cnv, obj);\n    return 1;\n}\nstatic int expand_trigger(t_glist*cnv, t_object*obj)\n{\n        /* expand the trigger to the left, by inserting a first \"a\" outlet */\n    t_binbuf*b=binbuf_new();\n    int argc=binbuf_getnatom(obj->te_binbuf);\n    t_atom*argv=binbuf_getvec(obj->te_binbuf);\n    t_object*stub=0;\n    int obj_nout=obj_noutlets(obj);\n    int stub_i, obj_i = canvas_getindex(cnv, o2g(obj));\n    int nout;\n\n    binbuf_addv(b, \"ssii\", gensym(\"#X\"),\n        gensym(\"obj\"), obj->te_xpix, obj->te_ypix);\n    binbuf_add(b, 1, argv);\n    binbuf_addv(b, \"s\", gensym(\"a\"));\n    binbuf_add(b, argc-1, argv+1);\n    canvas_undo_add(cnv, UNDO_PASTE, \"paste\",\n        canvas_undo_set_pastebinbuf(cnv, b, 0, 0, 0));\n    stub=triggerize_createobj(cnv, b);\n    stub_i = canvas_getindex(cnv, o2g(stub));\n    for(nout=0; nout<obj_nout; nout++)\n    {\n        t_outlet*out=0;\n        t_outconnect*conn=obj_starttraverseoutlet(obj, &out, nout);\n        while(conn)\n        {\n            int which;\n            t_object*dest=0;\n            t_inlet *in =0;\n            int dest_i;\n            conn=obj_nexttraverseoutlet(conn, &dest, &in, &which);\n            dest_i = canvas_getindex(cnv, o2g(dest));\n            obj_disconnect(obj, nout, dest, which);\n            canvas_undo_add(cnv, UNDO_DISCONNECT, \"disconnect\",\n                canvas_undo_set_disconnect(cnv, obj_i, nout, dest_i, which));\n            obj_connect(stub, nout+1, dest, which);\n            canvas_undo_add(cnv, UNDO_CONNECT, \"connect\",\n                canvas_undo_set_connect(cnv, stub_i, nout+1, dest_i, which));\n        }\n    }\n    binbuf_free(b);\n    dereconnect(cnv, obj, stub);\n    obj_delete_undo(cnv, obj);\n    return 1;\n}\ntypedef int (*t_fun_withobject)(t_glist*, t_object*);\nstatic int with_triggers(t_glist*cnv, t_fun_withobject fun)\n{\n        /* run <fun> on all selected [trigger] objects */\n    const t_symbol*s_trigger=gensym(\"trigger\");\n    int count=0;\n    t_gobj*gobj = NULL;\n    for(gobj=cnv->gl_list; gobj;)\n    {\n        t_gobj*next=gobj->g_next;\n        t_object*obj=g2o(gobj);\n        if(obj && glist_isselected(cnv, gobj))\n        {\n            const t_symbol*c_name=obj->te_g.g_pd->c_name;\n            if((s_trigger == c_name) && fun(cnv, obj))\n                count++;\n        }\n        gobj=next;\n    }\n    return count;\n}\nstatic int triggerize_triggers(t_glist*cnv)\n{\n        /* cleanup [trigger] objects */\n    int count=0;\n\n        /* nothing to do, if the selection is not exclusively [trigger] objects */\n    if(!only_triggers_selected(cnv))\n        return 0;\n\n        /* TODO: here's the place to merge multiple connected triggers */\n\n        /* remove all unused outlets from (selected) triggers */\n    canvas_undo_add(cnv, UNDO_SEQUENCE_START, \"{minimize triggers}\", 0);\n    count = with_triggers(cnv, minimize_trigger);\n    canvas_undo_add(cnv, UNDO_SEQUENCE_END, \"{minimize triggers}\", 0);\n    if(count)\n        return count;\n\n        /* expand each (selected) trigger to the left */\n    canvas_undo_add(cnv, UNDO_SEQUENCE_START, \"{expand triggers}\", 0);\n    count = with_triggers(cnv, expand_trigger);\n    canvas_undo_add(cnv, UNDO_SEQUENCE_END, \"{expand triggers}\", 0);\n    if(count)\n        return count;\n\n        /* nothing more to do */\n    return 0;\n}\n\nstatic int canvas_do_triggerize(t_glist*cnv, t_triggerize_return*tr)\n{\n        /*\n         * selected msg-connection: insert [t a] (->triggerize_line)\n         * selected sig-connection: insert [pd nop~] (->triggerize_line)\n         * selected [trigger]s with fan-outs: remove them (by inserting new outlets of the correct type) (->triggerize_fanouts)\n         * selected objects with fan-outs: remove fan-outs (->triggerize_fanouts)\n         * selected [trigger]: remove unused outlets (->triggerize_triggers)\n         * selected [trigger]: else, add left-most \"a\" outlet (->triggerize_triggers)\n         */\n\n    return(triggerize_line(cnv, tr)\n        || triggerize_fanouts(cnv)\n        || triggerize_triggers(cnv));\n}\nvoid canvas_triggerize(t_glist*cnv)\n{\n    int count = 0;\n    t_triggerize_return*tr;\n    if(!cnv || !cnv->gl_editor)\n        return;\n    if(!cnv->gl_editor->e_selection && !cnv->gl_editor->e_selectedline)\n        return;\n    tr = getbytes(sizeof(*tr));\n    if((count = canvas_do_triggerize(cnv, tr))) {\n        canvas_dirty(cnv, 1);\n            /* fix display of connections, objects,... */\n        canvas_redraw(cnv);\n        glist_redraw(cnv);\n            /* if we inserted an object, allow the user to change it now */\n        if(tr->tr_editgobj)\n            gobj_activate(tr->tr_editgobj, cnv, 1);\n    }\n    freebytes(tr, sizeof(*tr));\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/g_graph.c",
    "content": "/* Copyright (c) 1997-2001 Miller Puckette and others.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* This file deals with the behavior of glists as either \"text objects\" or\n\"graphs\" inside another glist.  LATER move the inlet/outlet code of g_canvas.c\nto this file... */\n\n#include <stdlib.h>\n#include \"m_pd.h\"\n\n#include \"g_canvas.h\"\n#include <stdio.h>\n\n/* ---------------------- forward definitions ----------------- */\n\nstatic void graph_vis(t_gobj *gr, t_glist *unused_glist, int vis);\nstatic void graph_graphrect(t_gobj *z, t_glist *glist,\n    int *xp1, int *yp1, int *xp2, int *yp2);\nstatic void graph_getrect(t_gobj *z, t_glist *glist,\n    int *xp1, int *yp1, int *xp2, int *yp2);\n\n/* -------------------- maintaining the list -------------------- */\n\nvoid canvas_drawredrect(t_canvas *x, int doit);\n\nvoid glist_add(t_glist *x, t_gobj *y)\n{\n    t_object *ob;\n    y->g_next = 0;\n    if (!x->gl_list) x->gl_list = y;\n    else\n    {\n        t_gobj *y2;\n        for (y2 = x->gl_list; y2->g_next; y2 = y2->g_next);\n        y2->g_next = y;\n    }\n    if (x->gl_editor && (ob = pd_checkobject(&y->g_pd)))\n        rtext_new(x, ob);\n    if (x->gl_editor && x->gl_isgraph && !x->gl_goprect\n        && pd_checkobject(&y->g_pd))\n    {\n        x->gl_goprect = 1;\n        canvas_drawredrect(x, 1);\n    }\n    if (glist_isvisible(x))\n        gobj_vis(y, x, 1);\n    if (class_isdrawcommand(y->g_pd))\n        canvas_redrawallfortemplate(template_findbyname(canvas_makebindsym(\n            glist_getcanvas(x)->gl_name)), 0);\n}\n\n    /* this is to protect against a hairy problem in which deleting\n    a sub-canvas might delete an inlet on a box, after the box had\n    been invisible-ized, so that we have to protect against redrawing it! */\nint canvas_setdeleting(t_canvas *x, int flag)\n{\n    int ret = x->gl_isdeleting;\n    x->gl_isdeleting = flag;\n    return (ret);\n}\n\n    /* JMZ: emit a closebang message */\nvoid rtext_freefortext(t_glist *gl, t_text *who);\n\n    /* delete an object from a glist and free it */\nvoid glist_delete(t_glist *x, t_gobj *y)\n{\n    t_gobj *g;\n    t_object *ob;\n    t_gotfn chkdsp = zgetfn(&y->g_pd, gensym(\"dsp\"));\n    t_canvas *canvas = glist_getcanvas(x);\n    t_rtext *rtext = 0;\n    int drawcommand = class_isdrawcommand(y->g_pd);\n    int wasdeleting;\n\n    if (pd_class(&y->g_pd) == canvas_class) {\n            /* JMZ: send a closebang to the canvas */\n        canvas_closebang((t_canvas *)y);\n    }\n\n    wasdeleting = canvas_setdeleting(canvas, 1);\n    if (x->gl_editor)\n    {\n            /* if we've grabbed events from canvas release them */\n        if (canvas->gl_editor && canvas->gl_editor->e_grab == y)\n            canvas->gl_editor->e_grab = 0;\n                /* perhaps we grabbed our own glist instead? don't know if\n                this ever happens: */\n        if (x->gl_editor->e_grab == y)\n            x->gl_editor->e_grab = 0;\n        if (glist_isselected(x, y)) glist_deselect(x, y);\n\n            /* HACK -- we had phantom outlets not getting erased on the\n            screen because the canvas_setdeleting() mechanism is too\n            crude.  LATER carefully set up rules for when the rtexts\n            should exist, so that they stay around until all the\n            steps of becoming invisible are done.  In the meantime, just\n            zap the inlets and outlets here... */\n        if (pd_class(&y->g_pd) == canvas_class)\n        {\n            t_glist *gl = (t_glist *)y;\n            if (gl->gl_isgraph && glist_isvisible(x))\n            {\n                char tag[80];\n                sprintf(tag, \"graph%lx\", (t_int)gl);\n                glist_eraseiofor(x, &gl->gl_obj, tag);\n            }\n            else\n            {\n                if (glist_isvisible(x))\n                    text_eraseborder(&gl->gl_obj, x,\n                        rtext_gettag(glist_findrtext(x, &gl->gl_obj)));\n            }\n        }\n    }\n        /* if we're a drawing command, erase all scalars now, before deleting\n        it; we'll redraw them once it's deleted below. */\n    if (drawcommand)\n        canvas_redrawallfortemplate(template_findbyname(canvas_makebindsym(\n            glist_getcanvas(x)->gl_name)), 2);\n    gobj_delete(y, x);\n    if (glist_isvisible(canvas))\n    {\n        gobj_vis(y, x, 0);\n    }\n    if (x->gl_editor && (ob = pd_checkobject(&y->g_pd)) &&\n        !(rtext = glist_findrtext(x, ob)))\n            rtext = rtext_new(x, ob);\n    if (x->gl_list == y) x->gl_list = y->g_next;\n    else for (g = x->gl_list; g; g = g->g_next)\n        if (g->g_next == y)\n    {\n        g->g_next = y->g_next;\n        break;\n    }\n    if (y->g_pd == scalar_class)\n        x->gl_valid = ++glist_valid;\n    pd_free(&y->g_pd);\n    if (rtext)\n        rtext_free(rtext);\n    if (chkdsp) canvas_update_dsp();\n    if (drawcommand)\n        canvas_redrawallfortemplate(template_findbyname(canvas_makebindsym(\n            glist_getcanvas(x)->gl_name)), 1);\n    canvas_setdeleting(canvas, wasdeleting);\n}\n\n    /* remove every object from a glist.  Experimental. */\nvoid glist_clear(t_glist *x)\n{\n    t_gobj *y;\n    int dspstate = 0, suspended = 0;\n    t_symbol *dspsym = gensym(\"dsp\");\n    while ((y = x->gl_list))\n    {\n            /* to avoid unnecessary DSP resorting, we suspend DSP\n            only if we hit a patchable object. */\n        if (!suspended && pd_checkobject(&y->g_pd) && zgetfn(&y->g_pd, dspsym))\n        {\n            dspstate = canvas_suspend_dsp();\n            suspended = 1;\n        }\n            /* here's the real deletion. */\n        glist_delete(x, y);\n    }\n    if (suspended)\n        canvas_resume_dsp(dspstate);\n}\n\nvoid glist_retext(t_glist *glist, t_text *y)\n{\n    t_canvas *c = glist_getcanvas(glist);\n        /* check that we have built rtexts yet.  LATER need a better test. */\n    if (glist->gl_editor && glist->gl_editor->e_rtext)\n    {\n        t_rtext *rt = glist_findrtext(glist, y);\n        if (rt)\n            rtext_retext(rt);\n    }\n}\n\nvoid glist_grab(t_glist *x, t_gobj *y, t_glistmotionfn motionfn,\n    t_glistkeyfn keyfn, int xpos, int ypos)\n{\n    t_glist *x2 = glist_getcanvas(x);\n    if (motionfn)\n        x2->gl_editor->e_onmotion = MA_PASSOUT;\n    else x2->gl_editor->e_onmotion = 0;\n    x2->gl_editor->e_grab = y;\n    x2->gl_editor->e_motionfn = motionfn;\n    x2->gl_editor->e_keyfn = keyfn;\n    x2->gl_editor->e_xwas = xpos;\n    x2->gl_editor->e_ywas = ypos;\n}\n\nt_canvas *glist_getcanvas(t_glist *x)\n{\n    while (x->gl_owner && !x->gl_isclone && !x->gl_havewindow && x->gl_isgraph)\n            x = x->gl_owner;\n    return((t_canvas *)x);\n}\n\nstatic t_float gobj_getxforsort(t_gobj *g)\n{\n    if (pd_class(&g->g_pd) == scalar_class)\n    {\n        t_float x1, y1;\n        scalar_getbasexy((t_scalar *)g, &x1, &y1);\n        return(x1);\n    }\n    else return (0);\n}\n\nstatic t_gobj *glist_merge(t_glist *x, t_gobj *g1, t_gobj *g2)\n{\n    t_gobj *g = 0, *g9 = 0;\n    t_float f1 = 0, f2 = 0;\n    if (g1)\n        f1 = gobj_getxforsort(g1);\n    if (g2)\n        f2 = gobj_getxforsort(g2);\n    while (1)\n    {\n        if (g1)\n        {\n            if (g2)\n            {\n                if (f1 <= f2)\n                    goto put1;\n                else goto put2;\n            }\n            else goto put1;\n        }\n        else if (g2)\n            goto put2;\n        else break;\n    put1:\n        if (g9)\n            g9->g_next = g1, g9 = g1;\n        else g9 = g = g1;\n        if ((g1 = g1->g_next))\n            f1 = gobj_getxforsort(g1);\n        g9->g_next = 0;\n        continue;\n    put2:\n        if (g9)\n            g9->g_next = g2, g9 = g2;\n        else g9 = g = g2;\n        if ((g2 = g2->g_next))\n            f2 = gobj_getxforsort(g2);\n        g9->g_next = 0;\n        continue;\n    }\n    return (g);\n}\n\nstatic t_gobj *glist_dosort(t_glist *x,\n    t_gobj *g, int nitems)\n{\n    if (nitems < 2)\n        return (g);\n    else\n    {\n        int n1 = nitems/2, n2 = nitems - n1, i;\n        t_gobj *g2, *g3;\n        for (g2 = g, i = n1-1; i--; g2 = g2->g_next)\n            ;\n        g3 = g2->g_next;\n        g2->g_next = 0;\n        g = glist_dosort(x, g, n1);\n        g3 = glist_dosort(x, g3, n2);\n        return (glist_merge(x, g, g3));\n    }\n}\n\nvoid glist_sort(t_glist *x)\n{\n    int nitems = 0, foo = 0;\n    t_float lastx = -1e37;\n    t_gobj *g;\n    for (g = x->gl_list; g; g = g->g_next)\n    {\n        t_float x1 = gobj_getxforsort(g);\n        if (x1 < lastx)\n            foo = 1;\n        lastx = x1;\n        nitems++;\n    }\n    if (foo)\n        x->gl_list = glist_dosort(x, x->gl_list, nitems);\n}\n\n/* --------------- inlets and outlets  ----------- */\n\n\nt_inlet *canvas_addinlet(t_canvas *x, t_pd *who, t_symbol *s)\n{\n    t_inlet *ip = inlet_new(&x->gl_obj, who, s, 0);\n    if (!x->gl_loading && x->gl_owner && !x->gl_isclone && glist_isvisible(x->gl_owner))\n    {\n        gobj_vis(&x->gl_gobj, x->gl_owner, 0);\n        gobj_vis(&x->gl_gobj, x->gl_owner, 1);\n        canvas_fixlinesfor(x->gl_owner, &x->gl_obj);\n    }\n    if (!x->gl_loading) canvas_resortinlets(x);\n    return (ip);\n}\n\nvoid canvas_rminlet(t_canvas *x, t_inlet *ip)\n{\n    t_canvas *owner = x->gl_isclone ? NULL : x->gl_owner;\n    int redraw = (owner && glist_isvisible(owner) && (!owner->gl_isdeleting)\n        && glist_istoplevel(owner));\n\n    if (owner) canvas_deletelinesforio(owner, &x->gl_obj, ip, 0);\n    if (redraw)\n        gobj_vis(&x->gl_gobj, x->gl_owner, 0);\n    inlet_free(ip);\n    if (redraw)\n    {\n        gobj_vis(&x->gl_gobj, x->gl_owner, 1);\n        canvas_fixlinesfor(x->gl_owner, &x->gl_obj);\n    }\n}\n\nextern t_inlet *vinlet_getit(t_pd *x);\nextern void obj_moveinletfirst(t_object *x, t_inlet *i);\n\nvoid canvas_resortinlets(t_canvas *x)\n{\n    int ninlets = 0, i, j, xmax;\n    t_gobj *y, **vec, **vp, **maxp;\n\n    for (ninlets = 0, y = x->gl_list; y; y = y->g_next)\n        if (pd_class(&y->g_pd) == vinlet_class) ninlets++;\n\n    if (ninlets < 2) return;\n\n    vec = (t_gobj **)getbytes(ninlets * sizeof(*vec));\n\n    for (y = x->gl_list, vp = vec; y; y = y->g_next)\n        if (pd_class(&y->g_pd) == vinlet_class) *vp++ = y;\n\n    for (i = ninlets; i--;)\n    {\n        t_inlet *ip;\n        for (vp = vec, xmax = -0x7fffffff, maxp = 0, j = ninlets;\n            j--; vp++)\n        {\n            int x1, y1, x2, y2;\n            t_gobj *g = *vp;\n            if (!g) continue;\n            gobj_getrect(g, x, &x1, &y1, &x2, &y2);\n            if (x1 > xmax) xmax = x1, maxp = vp;\n        }\n        if (!maxp) break;\n        y = *maxp;\n        *maxp = 0;\n        ip = vinlet_getit(&y->g_pd);\n\n        obj_moveinletfirst(&x->gl_obj, ip);\n    }\n    freebytes(vec, ninlets * sizeof(*vec));\n    if (x->gl_owner && !x->gl_isclone && glist_isvisible(x->gl_owner))\n        canvas_fixlinesfor(x->gl_owner, &x->gl_obj);\n}\n\nt_outlet *canvas_addoutlet(t_canvas *x, t_pd *who, t_symbol *s)\n{\n    t_outlet *op = outlet_new(&x->gl_obj, s);\n    if (!x->gl_loading && !x->gl_isclone && x->gl_owner && glist_isvisible(x->gl_owner))\n    {\n        gobj_vis(&x->gl_gobj, x->gl_owner, 0);\n        gobj_vis(&x->gl_gobj, x->gl_owner, 1);\n        canvas_fixlinesfor(x->gl_owner, &x->gl_obj);\n    }\n    if (!x->gl_loading) canvas_resortoutlets(x);\n    return (op);\n}\n\nvoid canvas_rmoutlet(t_canvas *x, t_outlet *op)\n{\n    t_canvas *owner = x->gl_isclone ? NULL : x->gl_owner;\n    int redraw = (owner && glist_isvisible(owner) && (!owner->gl_isdeleting)\n        && glist_istoplevel(owner));\n\n    if (owner) canvas_deletelinesforio(owner, &x->gl_obj, 0, op);\n    if (redraw)\n        gobj_vis(&x->gl_gobj, x->gl_owner, 0);\n\n    outlet_free(op);\n    if (redraw)\n    {\n        gobj_vis(&x->gl_gobj, x->gl_owner, 1);\n        canvas_fixlinesfor(x->gl_owner, &x->gl_obj);\n    }\n}\n\nextern t_outlet *voutlet_getit(t_pd *x);\nextern void obj_moveoutletfirst(t_object *x, t_outlet *i);\n\nvoid canvas_resortoutlets(t_canvas *x)\n{\n    int noutlets = 0, i, j, xmax;\n    t_gobj *y, **vec, **vp, **maxp;\n\n    for (noutlets = 0, y = x->gl_list; y; y = y->g_next)\n        if (pd_class(&y->g_pd) == voutlet_class) noutlets++;\n\n    if (noutlets < 2) return;\n\n    vec = (t_gobj **)getbytes(noutlets * sizeof(*vec));\n\n    for (y = x->gl_list, vp = vec; y; y = y->g_next)\n        if (pd_class(&y->g_pd) == voutlet_class) *vp++ = y;\n\n    for (i = noutlets; i--;)\n    {\n        t_outlet *ip;\n        for (vp = vec, xmax = -0x7fffffff, maxp = 0, j = noutlets;\n            j--; vp++)\n        {\n            int x1, y1, x2, y2;\n            t_gobj *g = *vp;\n            if (!g) continue;\n            gobj_getrect(g, x, &x1, &y1, &x2, &y2);\n            if (x1 > xmax) xmax = x1, maxp = vp;\n        }\n        if (!maxp) break;\n        y = *maxp;\n        *maxp = 0;\n        ip = voutlet_getit(&y->g_pd);\n\n        obj_moveoutletfirst(&x->gl_obj, ip);\n    }\n    freebytes(vec, noutlets * sizeof(*vec));\n    if (x->gl_owner && !x->gl_isclone && glist_isvisible(x->gl_owner))\n        canvas_fixlinesfor(x->gl_owner, &x->gl_obj);\n}\n\n/* ----------calculating coordinates and controlling appearance --------- */\n\n\nstatic void graph_bounds(t_glist *x, t_floatarg x1, t_floatarg y1,\n    t_floatarg x2, t_floatarg y2)\n{\n    x->gl_x1 = x1;\n    x->gl_x2 = x2;\n    x->gl_y1 = y1;\n    x->gl_y2 = y2;\n    if (x->gl_x2 == x->gl_x1 ||\n        x->gl_y2 == x->gl_y1)\n    {\n        pd_error(0, \"graph: empty bounds rectangle\");\n        x1 = y1 = 0;\n        x2 = y2 = 1;\n    }\n    glist_redraw(x);\n}\n\nstatic void graph_xticks(t_glist *x,\n    t_floatarg point, t_floatarg inc, t_floatarg f)\n{\n    x->gl_xtick.k_point = point;\n    x->gl_xtick.k_inc = inc;\n    x->gl_xtick.k_lperb = f;\n    glist_redraw(x);\n}\n\nstatic void graph_yticks(t_glist *x,\n    t_floatarg point, t_floatarg inc, t_floatarg f)\n{\n    x->gl_ytick.k_point = point;\n    x->gl_ytick.k_inc = inc;\n    x->gl_ytick.k_lperb = f;\n    glist_redraw(x);\n}\n\nstatic void graph_xlabel(t_glist *x, t_symbol *s, int argc, t_atom *argv)\n{\n    int i;\n    if (argc < 1) pd_error(0, \"graph_xlabel: no y value given\");\n    else\n    {\n        x->gl_xlabely = atom_getfloat(argv);\n        argv++; argc--;\n        x->gl_xlabel = (t_symbol **)t_resizebytes(x->gl_xlabel,\n            x->gl_nxlabels * sizeof (t_symbol *), argc * sizeof (t_symbol *));\n        x->gl_nxlabels = argc;\n        for (i = 0; i < argc; i++) x->gl_xlabel[i] = atom_gensym(&argv[i]);\n    }\n    glist_redraw(x);\n}\n\nstatic void graph_ylabel(t_glist *x, t_symbol *s, int argc, t_atom *argv)\n{\n    int i;\n    if (argc < 1) pd_error(0, \"graph_ylabel: no x value given\");\n    else\n    {\n        x->gl_ylabelx = atom_getfloat(argv);\n        argv++; argc--;\n        x->gl_ylabel = (t_symbol **)t_resizebytes(x->gl_ylabel,\n            x->gl_nylabels * sizeof (t_symbol *), argc * sizeof (t_symbol *));\n        x->gl_nylabels = argc;\n        for (i = 0; i < argc; i++) x->gl_ylabel[i] = atom_gensym(&argv[i]);\n    }\n    glist_redraw(x);\n}\n\n/****** routines to convert pixels to X or Y value and vice versa ******/\n\n    /* convert an x pixel value to an x coordinate value */\nt_float glist_pixelstox(t_glist *x, t_float xpix)\n{\n        /* if we appear as a text box on parent, our range in our\n        coordinates (x1, etc.) specifies the coordinate range\n        of a one-pixel square at top left of the window. */\n    if (!x->gl_isgraph)\n        return (x->gl_x1 + (x->gl_x2 - x->gl_x1) * xpix / x->gl_zoom);\n\n        /* if we're a graph when shown on parent, but own our own\n        window right now, our range in our coordinates (x1, etc.) is spread\n        over the visible window size, given by screenx1, etc. */\n    else if (x->gl_isgraph && x->gl_havewindow)\n        return (x->gl_x1 + (x->gl_x2 - x->gl_x1) *\n            (xpix) / (x->gl_screenx2 - x->gl_screenx1));\n\n        /* otherwise, we appear in a graph within a parent glist,\n         so get our screen rectangle on parent and transform. */\n    else\n    {\n        int x1, y1, x2, y2;\n        if (!x->gl_owner)\n            bug(\"glist_pixelstox\");\n        graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2);\n        return (x->gl_x1 + (x->gl_x2 - x->gl_x1) *\n            (xpix - x1) / (x2 - x1));\n    }\n}\n\nt_float glist_pixelstoy(t_glist *x, t_float ypix)\n{\n    if (!x->gl_isgraph)\n        return (x->gl_y1 + (x->gl_y2 - x->gl_y1) * ypix / x->gl_zoom);\n    else if (x->gl_isgraph && x->gl_havewindow)\n        return (x->gl_y1 + (x->gl_y2 - x->gl_y1) *\n                (ypix) / (x->gl_screeny2 - x->gl_screeny1));\n    else\n    {\n        int x1, y1, x2, y2;\n        if (!x->gl_owner)\n            bug(\"glist_pixelstox\");\n        graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2);\n        return (x->gl_y1 + (x->gl_y2 - x->gl_y1) *\n            (ypix - y1) / (y2 - y1));\n    }\n}\n\n    /* convert an x coordinate value to an x pixel location in window */\nt_float glist_xtopixels(t_glist *x, t_float xval)\n{\n    if (!x->gl_isgraph)\n        return (((xval - x->gl_x1) * x->gl_zoom) / (x->gl_x2 - x->gl_x1));\n    else if (x->gl_isgraph && x->gl_havewindow)\n        return (x->gl_screenx2 - x->gl_screenx1) *\n            (xval - x->gl_x1) / (x->gl_x2 - x->gl_x1);\n    else\n    {\n        int x1, y1, x2, y2;\n        if (!x->gl_owner)\n            bug(\"glist_pixelstox\");\n        graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2);\n        return (x1 + (x2 - x1) * (xval - x->gl_x1) / (x->gl_x2 - x->gl_x1));\n    }\n}\n\nt_float glist_ytopixels(t_glist *x, t_float yval)\n{\n    if (!x->gl_isgraph)\n        return (((yval - x->gl_y1) * x->gl_zoom) / (x->gl_y2 - x->gl_y1));\n    else if (x->gl_isgraph && x->gl_havewindow)\n        return (x->gl_screeny2 - x->gl_screeny1) *\n                (yval - x->gl_y1) / (x->gl_y2 - x->gl_y1);\n    else\n    {\n        int x1, y1, x2, y2;\n        if (!x->gl_owner)\n            bug(\"glist_pixelstox\");\n        graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2);\n        return (y1 + (y2 - y1) * (yval - x->gl_y1) / (x->gl_y2 - x->gl_y1));\n    }\n}\n\n    /* convert an X screen distance to an X coordinate increment.\n      This is terribly inefficient;\n      but probably not a big enough CPU hog to warrant optimizing. */\nt_float glist_dpixtodx(t_glist *x, t_float dxpix)\n{\n    return (dxpix * (glist_pixelstox(x, 1) - glist_pixelstox(x, 0)));\n}\n\nt_float glist_dpixtody(t_glist *x, t_float dypix)\n{\n    return (dypix * (glist_pixelstoy(x, 1) - glist_pixelstoy(x, 0)));\n}\n\n    /* get the window location in pixels of a \"text\" object.  The\n    object's x and y positions are in pixels when the glist they're\n    in is toplevel.  Otherwise, if it's a new-style graph-on-parent\n    (so gl_goprect is set) we use the offset into the framing subrectangle\n    as an offset into the parent rectangle.  Finally, it might be an old,\n    proportional-style GOP.  In this case we do a coordinate transformation. */\nint text_xpix(t_text *x, t_glist *glist)\n{\n    if (glist->gl_havewindow || !glist->gl_isgraph)\n        return (x->te_xpix * glist->gl_zoom);\n    else if (glist->gl_goprect)\n        return (glist_xtopixels(glist, glist->gl_x1) +\n            glist->gl_zoom * (x->te_xpix - glist->gl_xmargin));\n    else return (glist_xtopixels(glist,\n            glist->gl_x1 + (glist->gl_x2 - glist->gl_x1) *\n                x->te_xpix / (glist->gl_screenx2 - glist->gl_screenx1)));\n}\n\nint text_ypix(t_text *x, t_glist *glist)\n{\n    if (glist->gl_havewindow || !glist->gl_isgraph)\n        return (x->te_ypix * glist->gl_zoom);\n    else if (glist->gl_goprect)\n        return (glist_ytopixels(glist, glist->gl_y1) +\n            glist->gl_zoom * (x->te_ypix - glist->gl_ymargin));\n    else return (glist_ytopixels(glist,\n            glist->gl_y1 + (glist->gl_y2 - glist->gl_y1) *\n                x->te_ypix / (glist->gl_screeny2 - glist->gl_screeny1)));\n}\n\n    /* redraw all the items in a glist.  We construe this to mean\n    redrawing in its own window and on parent, as needed in each case.\n    This is too conservative -- for instance, when you draw an \"open\"\n    rectangle on the parent, you shouldn't have to redraw the window!  */\nvoid glist_redraw(t_glist *x)\n{\n    if (glist_isvisible(x))\n    {\n            /* LATER fix the graph_vis() code to handle both cases */\n        if (glist_istoplevel(x))\n        {\n            t_gobj *g;\n            t_linetraverser t;\n            t_outconnect *oc;\n            for (g = x->gl_list; g; g = g->g_next)\n            {\n                gobj_vis(g, x, 0);\n                gobj_vis(g, x, 1);\n            }\n                /* redraw all the lines */\n            linetraverser_start(&t, x);\n            while ((oc = linetraverser_next(&t)))\n            {\n                char tagbuf[128];\n                sprintf(tagbuf, \"l%p\", oc);\n                pdgui_vmess(0, \"crs iiii\",\n                          glist_getcanvas(x),\n                          \"coords\",\n                          tagbuf,\n                          t.tr_lx1, t.tr_ly1, t.tr_lx2, t.tr_ly2);\n            }\n            canvas_drawredrect(x, 0);\n            if (x->gl_goprect)\n            {\n                canvas_drawredrect(x, 1);\n            }\n        }\n        if (x->gl_owner && !x->gl_isclone && glist_isvisible(x->gl_owner))\n        {\n            graph_vis(&x->gl_gobj, x->gl_owner, 0);\n            graph_vis(&x->gl_gobj, x->gl_owner, 1);\n        }\n    }\n}\n\n/* --------------------------- widget behavior  ------------------- */\n\nint garray_getname(t_garray *x, t_symbol **namep);\n\nstatic void _graph_create_line4(t_glist *x, int x1, int y1, int x2, int y2, const char**tags2)\n{\n    pdgui_vmess(0, \"crr iiii ri rS\",\n              glist_getcanvas(x->gl_owner),\n              \"create\", \"line\",\n              x1,y1, x2,y2,\n              \"-width\", glist_getzoom(x),\n              \"-tags\", 2, tags2);\n}\n\nstatic void _graph_create_text(\n    t_glist *x, int posX, int posY,\n    const char*name,\n    const char*anchor,\n    int fontsize,\n    int numtags, const char**tags)\n{\n    t_atom fontatoms[3];\n    SETSYMBOL(fontatoms+0, gensym(sys_font));\n    SETFLOAT (fontatoms+1, fontsize);\n    SETSYMBOL(fontatoms+2, gensym(sys_fontweight));\n    pdgui_vmess(0, \"crr ii rs rr rA rS\",\n              glist_getcanvas(x),\n              \"create\", \"text\",\n              posX, posY,\n              \"-text\", name,\n              \"-anchor\", anchor,\n              \"-font\", 3, fontatoms,\n              \"-tags\", numtags, tags);\n}\n\n\n    /* Note that some code in here would also be useful for drawing\n    graph decorations in toplevels... */\nstatic void graph_vis(t_gobj *gr, t_glist *parent_glist, int vis)\n{\n    t_glist *x = (t_glist *)gr;\n    char tag[50];\n    const char *tags2[] = {tag, \"graph\" };\n    t_gobj *g;\n    int x1, y1, x2, y2;\n        /* ordinary subpatches: just act like a text object */\n    if (!x->gl_isgraph)\n    {\n        text_widgetbehavior.w_visfn(gr, parent_glist, vis);\n        return;\n    }\n\n    if (vis && canvas_showtext(x))\n        rtext_draw(glist_findrtext(parent_glist, &x->gl_obj));\n    graph_getrect(gr, parent_glist, &x1, &y1, &x2, &y2);\n    if (!vis)\n        rtext_erase(glist_findrtext(parent_glist, &x->gl_obj));\n\n    sprintf(tag, \"graph%lx\", (t_int)x);\n    if (vis)\n        glist_drawiofor(parent_glist, &x->gl_obj, 1,\n            tag, x1, y1, x2, y2);\n    else glist_eraseiofor(parent_glist, &x->gl_obj, tag);\n        /* if we look like a graph but have been moved to a toplevel,\n        just show the bounding rectangle */\n    if (x->gl_havewindow)\n    {\n        if (vis)\n            pdgui_vmess(0, \"crr iiiiiiiiii ri rr rr rS\",\n                glist_getcanvas(x->gl_owner), \"create\", \"polygon\",\n                x1,y1, x1,y2, x2,y2, x2,y1, x1,y1,\n                \"-width\", glist_getzoom(x),\n                \"-fill\", \"#c0c0c0\",\n                \"-joinstyle\", \"miter\",\n                \"-tags\", 2, tags2);\n        else\n            pdgui_vmess(0, \"crs\",\n                glist_getcanvas(x->gl_owner), \"delete\", tag);\n        return;\n    }\n        /* otherwise draw (or erase) us as a graph inside another glist. */\n    if (vis)\n    {\n        int i;\n        t_float f;\n        t_gobj *g;\n        t_symbol *arrayname;\n        char *ylabelanchor =\n            (x->gl_ylabelx > 0.5*(x->gl_x1 + x->gl_x2) ? \"w\" : \"e\");\n        char *xlabelanchor =\n            (x->gl_xlabely > 0.5*(x->gl_y1 + x->gl_y2) ? \"s\" : \"n\");\n        int fs = sys_hostfontsize(glist_getfont(x), glist_getzoom(x));\n        const char *tags3[] = {tag, \"label\", \"graph\" };\n\n            /* draw a rectangle around the graph */\n        pdgui_vmess(0, \"crr iiiiiiiiii ri rr rS\",\n                  glist_getcanvas(x->gl_owner),\n                  \"create\", \"line\",\n                  x1,y1, x1,y2, x2,y2, x2,y1, x1,y1,\n                  \"-width\", glist_getzoom(x),\n                  \"-capstyle\", \"projecting\",\n                  \"-tags\", 2, tags2);\n            /* if there's just one \"garray\" in the graph, write its name\n                along the top */\n        for (i = (y1 < y2 ? y1 : y2)-1, g = x->gl_list; g; g = g->g_next)\n            if (g->g_pd == garray_class &&\n                !garray_getname((t_garray *)g, &arrayname))\n        {\n            i -= glist_fontheight(x);\n            _graph_create_text(x,\n                x1, i,\n                arrayname->s_name,\n                \"nw\", -fs,\n                3, tags3);\n        }\n\n            /* draw ticks on horizontal borders.  If lperb field is\n            zero, this is disabled. */\n        if (x->gl_xtick.k_lperb)\n        {\n            t_float upix, lpix;\n            if (y2 < y1)\n                upix = y1, lpix = y2;\n            else upix = y2, lpix = y1;\n            for (i = 0, f = x->gl_xtick.k_point;\n                f < 0.99 * x->gl_x2 + 0.01*x->gl_x1; i++,\n                    f += x->gl_xtick.k_inc)\n            {\n                int tickpix = (i % x->gl_xtick.k_lperb ? 2 : 4);\n                _graph_create_line4(x,\n                    (int)glist_xtopixels(x, f), (int)upix,\n                    (int)glist_xtopixels(x, f), (int)upix - tickpix,\n                    tags2);\n                _graph_create_line4(x,\n                    (int)glist_xtopixels(x, f), (int)lpix,\n                    (int)glist_xtopixels(x, f), (int)lpix + tickpix,\n                    tags2);\n            }\n            for (i = 1, f = x->gl_xtick.k_point - x->gl_xtick.k_inc;\n                f > 0.99 * x->gl_x1 + 0.01*x->gl_x2;\n                    i++, f -= x->gl_xtick.k_inc)\n            {\n                int tickpix = (i % x->gl_xtick.k_lperb ? 2 : 4);\n                _graph_create_line4(x,\n                    (int)glist_xtopixels(x, f), (int)upix,\n                    (int)glist_xtopixels(x, f), (int)upix - tickpix,\n                    tags2);\n                _graph_create_line4(x,\n                    (int)glist_xtopixels(x, f), (int)lpix,\n                    (int)glist_xtopixels(x, f), (int)lpix + tickpix,\n                    tags2);\n            }\n        }\n\n            /* draw ticks in vertical borders*/\n        if (x->gl_ytick.k_lperb)\n        {\n            t_float ubound, lbound;\n            if (x->gl_y2 < x->gl_y1)\n                ubound = x->gl_y1, lbound = x->gl_y2;\n            else ubound = x->gl_y2, lbound = x->gl_y1;\n            for (i = 0, f = x->gl_ytick.k_point;\n                f < 0.99 * ubound + 0.01 * lbound;\n                    i++, f += x->gl_ytick.k_inc)\n            {\n                int tickpix = (i % x->gl_ytick.k_lperb ? 2 : 4);\n                _graph_create_line4(x,\n                    x1, (int)glist_ytopixels(x, f),\n                    x1 + tickpix, (int)glist_ytopixels(x, f),\n                    tags2);\n                _graph_create_line4(x,\n                    x2, (int)glist_ytopixels(x, f),\n                    x2 - tickpix, (int)glist_ytopixels(x, f),\n                    tags2);\n            }\n            for (i = 1, f = x->gl_ytick.k_point - x->gl_ytick.k_inc;\n                f > 0.99 * lbound + 0.01 * ubound;\n                    i++, f -= x->gl_ytick.k_inc)\n            {\n                int tickpix = (i % x->gl_ytick.k_lperb ? 2 : 4);\n                _graph_create_line4(x,\n                    x1, (int)glist_ytopixels(x, f),\n                    x1 + tickpix, (int)glist_ytopixels(x, f),\n                    tags2);\n                _graph_create_line4(x,\n                    x2, (int)glist_ytopixels(x, f),\n                    x2 - tickpix, (int)glist_ytopixels(x, f),\n                    tags2);\n            }\n        }\n            /* draw x labels */\n        for (i = 0; i < x->gl_nxlabels; i++)\n            _graph_create_text(x,\n                (int)glist_xtopixels(x, atof(x->gl_xlabel[i]->s_name)),\n                (int)glist_ytopixels(x, x->gl_xlabely),\n                x->gl_xlabel[i]->s_name,\n                xlabelanchor, -fs,\n                3, tags3);\n            /* draw y labels */\n        for (i = 0; i < x->gl_nylabels; i++)\n            _graph_create_text(x,\n                (int)glist_xtopixels(x, x->gl_ylabelx),\n                (int)glist_ytopixels(x, atof(x->gl_ylabel[i]->s_name)),\n                x->gl_ylabel[i]->s_name,\n                ylabelanchor, -fs,\n                3, tags3);\n\n            /* draw contents of graph as glist */\n        for (g = x->gl_list; g; g = g->g_next)\n            gobj_vis(g, x, 1);\n    }\n    else\n    {\n        pdgui_vmess(0, \"crs\", glist_getcanvas(x->gl_owner), \"delete\", tag);\n        for (g = x->gl_list; g; g = g->g_next)\n            gobj_vis(g, x, 0);\n    }\n}\n\n    /* get the graph's rectangle, not counting extra swelling for controls\n    to keep them inside the graph.  This is the \"logical\" pixel size. */\n\nstatic void graph_graphrect(t_gobj *z, t_glist *glist,\n    int *xp1, int *yp1, int *xp2, int *yp2)\n{\n    t_glist *x = (t_glist *)z;\n    int x1 = text_xpix(&x->gl_obj, glist);\n    int y1 = text_ypix(&x->gl_obj, glist);\n    int x2, y2;\n    x2 = x1 + x->gl_zoom * x->gl_pixwidth;\n    y2 = y1 + x->gl_zoom * x->gl_pixheight;\n\n    *xp1 = x1;\n    *yp1 = y1;\n    *xp2 = x2;\n    *yp2 = y2;\n}\n\n    /* get the rectangle, enlarged to contain all the \"contents\" --\n    meaning their formal bounds rectangles. */\nstatic void graph_getrect(t_gobj *z, t_glist *glist,\n    int *xp1, int *yp1, int *xp2, int *yp2)\n{\n    int x1 = 0x7fffffff, y1 = 0x7fffffff, x2 = -0x7fffffff, y2 = -0x7fffffff;\n    t_glist *x = (t_glist *)z;\n    if (x->gl_isgraph)\n    {\n        int hadwindow;\n        t_gobj *g;\n        int x21, y21, x22, y22;\n\n        graph_graphrect(z, glist, &x1, &y1, &x2, &y2);\n        if (canvas_showtext(x))\n        {\n            text_widgetbehavior.w_getrectfn(z, glist, &x21, &y21, &x22, &y22);\n            if (x22 > x2)\n                x2 = x22;\n            if (y22 > y2)\n                y2 = y22;\n        }\n        if (!x->gl_goprect)\n        {\n            /* expand the rectangle to fit in text objects; this applies only\n            to the old (0.37) graph-on-parent behavior. */\n            /* lie about whether we have our own window to affect gobj_getrect\n            calls below.  */\n            hadwindow = x->gl_havewindow;\n            x->gl_havewindow = 0;\n            for (g = x->gl_list; g; g = g->g_next)\n            {\n                    /* don't do this for arrays, just let them hang outside the\n                    box.  And ignore \"text\" objects which aren't shown on\n                    parent */\n                if (pd_class(&g->g_pd) == garray_class ||\n                    pd_checkobject(&g->g_pd))\n                        continue;\n                gobj_getrect(g, x, &x21, &y21, &x22, &y22);\n                if (x22 > x2)\n                    x2 = x22;\n                if (y22 > y2)\n                    y2 = y22;\n            }\n            x->gl_havewindow = hadwindow;\n        }\n    }\n    else text_widgetbehavior.w_getrectfn(z, glist, &x1, &y1, &x2, &y2);\n    *xp1 = x1;\n    *yp1 = y1;\n    *xp2 = x2;\n    *yp2 = y2;\n}\n\nstatic void graph_displace(t_gobj *z, t_glist *glist, int dx, int dy)\n{\n    t_glist *x = (t_glist *)z;\n    if (!x->gl_isgraph)\n        text_widgetbehavior.w_displacefn(z, glist, dx, dy);\n    else\n    {\n        x->gl_obj.te_xpix += dx;\n        x->gl_obj.te_ypix += dy;\n        if (glist_isvisible(glist)) {\n            glist_redraw(x);\n            canvas_fixlinesfor(glist, &x->gl_obj);\n        }\n    }\n}\n\nstatic void graph_select(t_gobj *z, t_glist *glist, int state)\n{\n    t_glist *x = (t_glist *)z;\n    if (!x->gl_isgraph)\n        text_widgetbehavior.w_selectfn(z, glist, state);\n    else\n    {\n        t_rtext *y = glist_findrtext(glist, &x->gl_obj);\n        char tag[80];\n        if (canvas_showtext(x))\n            rtext_select(y, state);\n\n        sprintf(tag, \"%sR\",  rtext_gettag(y));\n        pdgui_vmess(0, \"crs rr\",\n                  glist, \"itemconfigure\", tag,\n                  \"-fill\", (state? \"blue\" : \"black\"));\n        sprintf(tag, \"graph%lx\", (t_int)z);\n        pdgui_vmess(0, \"crs rr\",\n                  glist_getcanvas(glist), \"itemconfigure\", tag,\n                  \"-fill\", (state? \"blue\" : \"black\"));\n    }\n}\n\nstatic void graph_activate(t_gobj *z, t_glist *glist, int state)\n{\n    t_glist *x = (t_glist *)z;\n    if (canvas_showtext(x))\n        text_widgetbehavior.w_activatefn(z, glist, state);\n}\n\nstatic void graph_delete(t_gobj *z, t_glist *glist)\n{\n    t_glist *x = (t_glist *)z;\n    t_gobj *y;\n    while ((y = x->gl_list))\n        glist_delete(x, y);\n    if (glist_isvisible(x))\n        text_widgetbehavior.w_deletefn(z, glist);\n            /* if we have connections to the actual 'canvas' object, zap\n            them as well (e.g., array or scalar objects that are implemented\n            as canvases with \"real\" inlets).  Connections to ordinary canvas\n            in/outlets already got zapped when we cleared the contents above */\n    canvas_deletelinesfor(glist, &x->gl_obj);\n}\n\nstatic void graph_motion(void *z, t_floatarg dx, t_floatarg dy)\n{\n    t_glist *x = (t_glist *)z;\n    t_float newxpix = THISGUI->i_graph_lastxpix + dx,\n        newypix = THISGUI->i_graph_lastypix + dy;\n    t_garray *a = (t_garray *)(x->gl_list);\n    int oldx = 0.5 + glist_pixelstox(x, THISGUI->i_graph_lastxpix);\n    int newx = 0.5 + glist_pixelstox(x, newxpix);\n    t_word *vec;\n    int nelem, i;\n    t_float oldy = glist_pixelstoy(x, THISGUI->i_graph_lastypix);\n    t_float newy = glist_pixelstoy(x, newypix);\n    THISGUI->i_graph_lastxpix = newxpix;\n    THISGUI->i_graph_lastypix = newypix;\n        /* verify that the array is OK */\n    if (!a || pd_class((t_pd *)a) != garray_class)\n        return;\n    if (!garray_getfloatwords(a, &nelem, &vec))\n        return;\n    if (oldx < 0) oldx = 0;\n    if (oldx >= nelem)\n        oldx = nelem - 1;\n    if (newx < 0) newx = 0;\n    if (newx >= nelem)\n        newx = nelem - 1;\n    if (oldx < newx - 1)\n    {\n        for (i = oldx + 1; i <= newx; i++)\n            vec[i].w_float = newy + (oldy - newy) *\n                ((t_float)(newx - i))/(t_float)(newx - oldx);\n    }\n    else if (oldx > newx + 1)\n    {\n        for (i = oldx - 1; i >= newx; i--)\n            vec[i].w_float = newy + (oldy - newy) *\n                ((t_float)(newx - i))/(t_float)(newx - oldx);\n    }\n    else vec[newx].w_float = newy;\n    garray_redraw(a);\n}\n\nstatic int graph_click(t_gobj *z, struct _glist *glist,\n    int xpix, int ypix, int shift, int alt, int dbl, int doit)\n{\n    t_glist *x = (t_glist *)z;\n    t_gobj *y;\n    int clickreturned = 0;\n    if (!x->gl_isgraph)\n        return (text_widgetbehavior.w_clickfn(z, glist,\n            xpix, ypix, shift, alt, dbl, doit));\n    else if (x->gl_havewindow)\n        return (0);\n    else\n    {\n        for (y = x->gl_list; y; y = y->g_next)\n        {\n            int x1, y1, x2, y2;\n                /* check if the object wants to be clicked */\n            if (canvas_hitbox(x, y, xpix, ypix, &x1, &y1, &x2, &y2)\n                &&  (clickreturned = gobj_click(y, x, xpix, ypix,\n                    shift, alt, 0, doit)))\n                        break;\n        }\n        if (!doit)\n        {\n            if (y)\n                canvas_setcursor(glist_getcanvas(x), clickreturned);\n            else canvas_setcursor(glist_getcanvas(x), CURSOR_RUNMODE_NOTHING);\n        }\n        return (clickreturned);\n    }\n}\n\nconst t_widgetbehavior graph_widgetbehavior =\n{\n    graph_getrect,\n    graph_displace,\n    graph_select,\n    graph_activate,\n    graph_delete,\n    graph_vis,\n    graph_click,\n};\n\n    /* find the graph most recently added to this glist;\n        if none exists, return 0. */\n\nt_glist *glist_findgraph(t_glist *x)\n{\n    t_gobj *y = 0, *z;\n    for (z = x->gl_list; z; z = z->g_next)\n        if (pd_class(&z->g_pd) == canvas_class && ((t_glist *)z)->gl_isgraph)\n            y = z;\n    return ((t_glist *)y);\n}\n\nextern void canvas_menuarray(t_glist *canvas);\n\nvoid g_graph_setup_class(t_class *c)\n{\n    class_setwidget(c, &graph_widgetbehavior);\n    class_addmethod(c, (t_method)graph_bounds, gensym(\"bounds\"),\n        A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);\n    class_addmethod(c, (t_method)graph_xticks, gensym(\"xticks\"),\n        A_FLOAT, A_FLOAT, A_FLOAT, 0);\n    class_addmethod(c, (t_method)graph_xlabel, gensym(\"xlabel\"),\n        A_GIMME, 0);\n    class_addmethod(c, (t_method)graph_yticks, gensym(\"yticks\"),\n        A_FLOAT, A_FLOAT, A_FLOAT, 0);\n    class_addmethod(c, (t_method)graph_ylabel, gensym(\"ylabel\"),\n        A_GIMME, 0);\n    class_addmethod(c, (t_method)graph_array, gensym(\"array\"),\n        A_SYMBOL, A_FLOAT, A_SYMBOL, A_DEFFLOAT, A_NULL);\n    class_addmethod(c, (t_method)canvas_menuarray,\n        gensym(\"menuarray\"), A_NULL);\n    class_addmethod(c, (t_method)glist_sort,\n        gensym(\"sort\"), A_NULL);\n}\n\nvoid g_graph_setup(void)\n{\n    g_graph_setup_class(canvas_class);\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/g_guiconnect.c",
    "content": "/* Copyright (c) 1997-2000 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/*  a thing to forward messages from the GUI, dealing with race conditions\nin which the \"target\" gets deleted while the GUI is sending it something.\n\nSee also the gfxstub object that doesn't oblige the owner to keep a pointer\naround (so is better suited to one-off dialogs)\n*/\n\n#include \"m_pd.h\"\n#include \"g_canvas.h\"\n\nstruct _guiconnect\n{\n    t_object x_obj;\n    t_pd *x_who;\n    t_symbol *x_sym;\n    t_clock *x_clock;\n};\n\nstatic t_class *guiconnect_class;\n\nt_guiconnect *guiconnect_new(t_pd *who, t_symbol *sym)\n{\n    t_guiconnect *x = (t_guiconnect *)pd_new(guiconnect_class);\n    x->x_who = who;\n    x->x_sym = sym;\n    pd_bind(&x->x_obj.ob_pd, sym);\n    return (x);\n}\n\n    /* cleanup routine; delete any resources we have */\nstatic void guiconnect_free(t_guiconnect *x)\n{\n    if (x->x_sym)\n        pd_unbind(&x->x_obj.ob_pd, x->x_sym);\n    if (x->x_clock)\n        clock_free(x->x_clock);\n}\n\n    /* this is called when the clock times out to indicate the GUI should\n    be gone by now. */\nstatic void guiconnect_tick(t_guiconnect *x)\n{\n    pd_free(&x->x_obj.ob_pd);\n}\n\n    /* the target calls this to disconnect.  If the gui has \"signed off\"\n    we're ready to delete the object; otherwise we wait either for signoff\n    or for a timeout. */\nvoid guiconnect_notarget(t_guiconnect *x, double timedelay)\n{\n    if (!x->x_sym)\n        pd_free(&x->x_obj.ob_pd);\n    else\n    {\n        x->x_who = 0;\n        if (timedelay > 0)\n        {\n            x->x_clock = clock_new(x, (t_method)guiconnect_tick);\n            clock_delay(x->x_clock, timedelay);\n        }\n    }\n}\n\n    /* the GUI calls this to send messages to the target. */\nstatic void guiconnect_anything(t_guiconnect *x,\n    t_symbol *s, int ac, t_atom *av)\n{\n    if (x->x_who)\n        typedmess(x->x_who, s, ac, av);\n}\n\n    /* the GUI calls this when it disappears.  (If there's any chance the\n    GUI will fail to do this, the \"target\", when it signs off, should specify\n    a timeout after which the guiconnect will disappear.) */\nstatic void guiconnect_signoff(t_guiconnect *x)\n{\n    if (!x->x_who)\n        pd_free(&x->x_obj.ob_pd);\n    else\n    {\n        pd_unbind(&x->x_obj.ob_pd, x->x_sym);\n        x->x_sym = 0;\n    }\n}\n\nvoid g_guiconnect_setup(void)\n{\n    guiconnect_class = class_new(gensym(\"guiconnect\"), 0,\n        (t_method)guiconnect_free, sizeof(t_guiconnect), CLASS_PD, 0);\n    class_addanything(guiconnect_class, guiconnect_anything);\n    class_addmethod(guiconnect_class, (t_method)guiconnect_signoff,\n        gensym(\"signoff\"), 0);\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/g_io.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* graphical inlets and outlets, both for control and signals.  */\n\n/* This code is highly inefficient; messages actually have to be forwarded\nby inlets and outlets.  The outlet is in even worse shape than the inlet;\nin order to avoid having a \"signal\" method in the class, the oulet actually\nsprouts an inlet, which forwards the message to the \"outlet\" object, which\nsends it on to the outlet proper.  Another way to do it would be to have\nseparate classes for \"signal\" and \"control\" outlets, but this would complicate\nlife elsewhere. */\n\n\n#include \"m_pd.h\"\n#include \"g_canvas.h\"\n#include <string.h>\n\nstatic int symbol2resamplemethod(t_symbol*s)\n{\n    if      (s == gensym(\"hold\"  )) return 1; /* up: sample and hold */\n    else if (s == gensym(\"lin\"   )) return 2; /* up: linear interpolation */\n    else if (s == gensym(\"linear\")) return 2; /* up: linear interpolation */\n    else if (s == gensym(\"pad\"   )) return 0; /* up: zero pad */\n    return -1;  /* default: sample/hold except zero-pad if version<0.44 */\n}\n\n/* ------------------------- vinlet -------------------------- */\nt_class *vinlet_class;\n\ntypedef struct _reblocker\n{\n    t_sample *r_buf;         /* signal buffer; zero if not a signal */\n    t_resample r_updown;\n} t_reblocker;\n\nstatic void reblocker_init(t_reblocker *rb, int buflength)\n{\n    rb->r_buf = (t_sample *)getbytes(buflength * sizeof(t_sample));\n    resample_init(&rb->r_updown);\n}\n\nstatic void reblocker_resize(t_reblocker **rb, int oldn, int newn,\n    int buflength)\n{\n    int i;\n    if (oldn == newn)\n        return;\n    for (i = newn; i < oldn; i++)\n    {\n        freebytes((*rb)[i].r_buf, buflength * sizeof(t_sample));\n        resample_free(&(*rb)[i].r_updown);\n    }\n    *rb = (t_reblocker *)resizebytes(*rb, oldn * sizeof(t_reblocker),\n        newn * sizeof(t_reblocker));\n    for (i = oldn; i < newn; i++)\n        reblocker_init(&(*rb)[i], buflength);\n}\n\ntypedef struct _vinlet\n{\n    t_object x_obj;\n    t_canvas *x_canvas;\n    t_inlet *x_inlet;\n    int x_buflength;        /* number of samples per channel in buffer */\n    int x_fill;\n    int x_read;\n    int x_hop;\n    int x_updownmethod;\n            /* if not reblocking, the next slot communicates the parent's\n                inlet signal from the prolog to the DSP routine: */\n    t_signal *x_directsignal;\n    int x_nchans;       /* this is also set in prolog & used in dsp */\n    t_outlet *x_fwdout; /* optional outlet for forwarding messages to inlet~ */\n    t_reblocker *x_rb;  /* reblocking and resampling, one per channel */\n} t_vinlet;\n\nstatic void *vinlet_new(t_symbol *s)\n{\n    t_vinlet *x = (t_vinlet *)pd_new(vinlet_class);\n    x->x_canvas = canvas_getcurrent();\n    x->x_inlet = canvas_addinlet(x->x_canvas, &x->x_obj.ob_pd, 0);\n    x->x_buflength = 0;\n    x->x_nchans = 0;\n    x->x_rb = 0;\n    outlet_new(&x->x_obj, 0);\n    return (x);\n}\n\nstatic void vinlet_bang(t_vinlet *x)\n{\n    outlet_bang(x->x_obj.ob_outlet);\n}\n\nstatic void vinlet_pointer(t_vinlet *x, t_gpointer *gp)\n{\n    outlet_pointer(x->x_obj.ob_outlet, gp);\n}\n\nstatic void vinlet_float(t_vinlet *x, t_float f)\n{\n    outlet_float(x->x_obj.ob_outlet, f);\n}\n\nstatic void vinlet_symbol(t_vinlet *x, t_symbol *s)\n{\n    outlet_symbol(x->x_obj.ob_outlet, s);\n}\n\nstatic void vinlet_list(t_vinlet *x, t_symbol *s, int argc, t_atom *argv)\n{\n    outlet_list(x->x_obj.ob_outlet, s, argc, argv);\n}\n\nstatic void vinlet_anything(t_vinlet *x, t_symbol *s, int argc, t_atom *argv)\n{\n    outlet_anything(x->x_obj.ob_outlet, s, argc, argv);\n}\n\nstatic void vinlet_free(t_vinlet *x)\n{\n    int i;\n    canvas_rminlet(x->x_canvas, x->x_inlet);\n    if (x->x_rb)\n        for (i = 0; i < x->x_nchans; i++)\n    {\n        freebytes((x->x_rb)[i].r_buf, x->x_buflength * sizeof(t_sample));\n        resample_free(&(x->x_rb)[i].r_updown);\n    }\n}\n\nt_inlet *vinlet_getit(t_pd *x)\n{\n    if (pd_class(x) != vinlet_class) bug(\"vinlet_getit\");\n    return (((t_vinlet *)x)->x_inlet);\n}\n\n/* ------------------------- signal inlet -------------------------- */\nint vinlet_issignal(t_vinlet *x)\n{\n    return (x->x_rb != 0);\n}\n\nt_int *vinlet_perform(t_int *w)\n{\n    t_vinlet *x = (t_vinlet *)(w[1]);\n    t_sample *out = (t_sample *)(w[2]);\n    t_reblocker *rb = (t_reblocker *)(w[3]);\n    int hop = (int)(w[4]), n = (int)(w[5]), read = x->x_read;\n    t_sample *in = rb->r_buf + read;\n    while (n--)\n        *out++ = *in++;\n    if (hop)\n    {\n        if ((read += hop) == x->x_buflength)\n            read = 0;\n        x->x_read = read;\n    }\n    return (w+6);\n}\n\nstatic void vinlet_fwd(t_vinlet *x, t_symbol *s, int argc, t_atom *argv)\n{\n\n    if (!x->x_rb)   /* if we're not signal, just forward */\n        outlet_anything(x->x_obj.ob_outlet, s, argc, argv);\n    else if (x->x_fwdout && argc > 0 && argv->a_type == A_SYMBOL)\n        outlet_anything(x->x_fwdout, argv->a_w.w_symbol, argc-1, argv+1);\n}\n\nstatic void vinlet_dsp(t_vinlet *x, t_signal **sp)\n{\n        /* no buffer means we're not a signal inlet */\n    if (!x->x_rb)\n        return;\n    if (x->x_directsignal)\n    {\n        sp[0] = signal_new(0, 1, sp[0]->s_sr, 0);\n        signal_setborrowed(sp[0], x->x_directsignal);\n    }\n    else\n    {\n        int i;\n        signal_setmultiout(sp, x->x_nchans);\n        for (i = 0; i < x->x_nchans; i++)\n            dsp_add(vinlet_perform, 5, x, sp[0]->s_vec + i * sp[0]->s_length,\n                &x->x_rb[i], (t_int)(i == x->x_nchans-1 ? sp[0]->s_length : 0),\n                    (t_int)(sp[0]->s_length));\n        x->x_read = 0;\n    }\n}\n\n    /* prolog code: loads buffer from parent patch */\nt_int *vinlet_doprolog(t_int *w)\n{\n    t_vinlet *x = (t_vinlet *)(w[1]);\n    t_sample *in = (t_sample *)(w[2]), *out;\n    t_sample *buf = (t_sample *)(w[3]);\n    int lastone = (int)(w[4]), n = (int)(w[5]), fill = x->x_fill;\n\n    if (fill == x->x_buflength)\n    {\n        t_sample *f1 = buf, *f2 = buf + x->x_hop;\n        int nshift = x->x_buflength - x->x_hop;\n        while (nshift--) *f1++ = *f2++;\n        fill -= x->x_hop;\n    }\n    out = buf + fill;\n    if (lastone)\n        x->x_fill = fill + n;\n    while (n--)\n        *out++ = *in++;\n    return (w+6);\n}\n\nint inlet_getsignalindex(t_inlet *x);\n\n        /* set up prolog DSP code  */\nvoid vinlet_dspprolog(struct _vinlet *x, t_signal **parentsigs,\n    int myvecsize, int phase, int period, int frequency,\n    int downsample, int upsample,  int reblock, int switched)\n{\n    t_signal *insig;\n        /* no buffer means we're not a signal inlet */\n    if (!x->x_rb)\n        return;\n\n        /* if the \"reblock\" flag is set, arrange to copy data in from the\n        parent. */\n    if (reblock)\n    {\n        int parentvecsize, bufsize, oldbufsize, prologphase, i;\n        int re_parentvecsize; /* resampled parentvectorsize */\n\n            /* the prolog code counts from 0 to period-1; the\n            phase is backed up by one so that AFTER the prolog code\n            runs, the \"x_fill\" phase is in sync with the \"x_read\" phase. */\n        prologphase = (phase + period - 1) % period;\n        if (parentsigs)\n        {\n            insig = parentsigs[inlet_getsignalindex(x->x_inlet)];\n            parentvecsize = insig->s_length;\n            re_parentvecsize = parentvecsize * upsample / downsample;\n            reblocker_resize(&x->x_rb, x->x_nchans, insig->s_nchans,\n                x->x_buflength);\n            x->x_nchans = insig->s_nchans;\n        }\n        else\n        {\n            insig = 0;\n            parentvecsize = re_parentvecsize = 1;\n        }\n\n        bufsize = re_parentvecsize;\n        if (bufsize < myvecsize)\n            bufsize = myvecsize;\n        if (bufsize != (oldbufsize = x->x_buflength))\n        {\n            for (i = 0; i < x->x_nchans; i++)\n            {\n                x->x_rb[i].r_buf = (t_sample *)t_resizebytes(x->x_rb[i].r_buf,\n                    oldbufsize * sizeof(t_sample), bufsize * sizeof(t_sample));\n                memset((char *)x->x_rb[i].r_buf, 0, bufsize * sizeof(t_sample));\n            }\n            x->x_buflength = bufsize;\n        }\n        if (parentsigs)\n        {\n            x->x_hop = period * re_parentvecsize;\n\n            x->x_fill = prologphase ?\n                x->x_buflength - (x->x_hop - prologphase * re_parentvecsize) :\n                    x->x_buflength;\n            for (i = 0; i < x->x_nchans; i++)\n            {\n                x->x_rb[i].r_updown.downsample = downsample;\n                x->x_rb[i].r_updown.upsample   = upsample;\n                if (upsample == 1 && downsample == 1)\n                    dsp_add(vinlet_doprolog, 5, x,\n                        insig->s_vec + i * parentvecsize, x->x_rb[i].r_buf,\n                            (t_int)(i == x->x_nchans-1),\n                                (t_int)re_parentvecsize);\n                else\n                {\n                    int method = (x->x_updownmethod == -1?\n                        (pd_compatibilitylevel < 44 ? 0 : 1) :\n                            x->x_updownmethod);\n                    resamplefrom_dsp(&x->x_rb[i].r_updown,\n                        insig->s_vec + i * parentvecsize, parentvecsize,\n                            re_parentvecsize, method);\n                    dsp_add(vinlet_doprolog, 5, x, x->x_rb[i].r_updown.s_vec,\n                        x->x_rb[i].r_buf, (t_int)(i == x->x_nchans-1),\n                                (t_int)re_parentvecsize);\n                }\n            }\n        }\n        else for (i = 0; i < x->x_nchans; i++)\n            memset((char *)(x->x_rb[i].r_buf), 0, bufsize * sizeof(t_sample));\n        x->x_directsignal = 0;\n    }\n    else\n    {\n            /* no reblocking; in this case our output signal is \"borrowed\"\n            and merely needs to be pointed to the real one. */\n        x->x_directsignal = parentsigs[inlet_getsignalindex(x->x_inlet)];\n    }\n}\n\nstatic void *vinlet_newsig(t_symbol *s, int argc, t_atom *argv)\n{\n    t_vinlet *x = (t_vinlet *)pd_new(vinlet_class);\n    x->x_canvas = canvas_getcurrent();\n    x->x_inlet = canvas_addinlet(x->x_canvas, &x->x_obj.ob_pd, &s_signal);\n    x->x_nchans = 1;\n    x->x_buflength = 0;\n    x->x_rb = (t_reblocker *)getbytes(sizeof(*x->x_rb));\n    reblocker_init(x->x_rb, x->x_buflength);\n    x->x_directsignal = 0;\n    x->x_fwdout = 0;\n    outlet_new(&x->x_obj, &s_signal);\n    inlet_new(&x->x_obj, (t_pd *)x->x_inlet, 0, 0);\n\n    /* this should be thought over:\n     * it might prove hard to provide consistency between labeled up- &\n     * downsampling methods - maybe indices would be better...\n     *\n     * up till now we provide several upsampling methods and 1 single\n     *  downsampling method (no filtering !)\n     */\n    x->x_updownmethod = -1;\n    while (argc-->0)\n    {\n        int method;\n        s = atom_getsymbol(argv++);\n        method = symbol2resamplemethod(s);\n        if (method >= 0)\n            x->x_updownmethod = method;\n    }\n    x->x_fwdout = outlet_new(&x->x_obj, 0);\n    return (x);\n}\n\nstatic void vinlet_setup(void)\n{\n    vinlet_class = class_new(gensym(\"inlet\"), (t_newmethod)vinlet_new,\n        (t_method)vinlet_free, sizeof(t_vinlet),\n            CLASS_NOINLET | CLASS_MULTICHANNEL, A_DEFSYM, 0);\n    class_addcreator((t_newmethod)vinlet_newsig, gensym(\"inlet~\"), A_GIMME, 0);\n    class_addbang(vinlet_class, vinlet_bang);\n    class_addpointer(vinlet_class, vinlet_pointer);\n    class_addfloat(vinlet_class, vinlet_float);\n    class_addsymbol(vinlet_class, vinlet_symbol);\n    class_addlist(vinlet_class, vinlet_list);\n    class_addanything(vinlet_class, vinlet_anything);\n    class_addmethod(vinlet_class,(t_method)vinlet_fwd,  gensym(\"fwd\"),\n        A_GIMME, 0);\n    class_addmethod(vinlet_class, (t_method)vinlet_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_sethelpsymbol(vinlet_class, gensym(\"inlet-outlet\"));\n}\n\n/* ------------------------- voutlet -------------------------- */\n\nt_class *voutlet_class;\n\ntypedef struct _voutlet\n{\n    t_object x_obj;\n    t_canvas *x_canvas;\n    t_outlet *x_parentoutlet;\n    int x_buflength;\n    int x_empty;            /* next to read out of buffer in epilog code */\n    int x_write;            /* next to write in to buffer */\n    int x_hop;              /* hopsize */\n    int x_updownmethod;\n        /*  parent's outlet signal, valid between the prolog and the dsp setup\n        routines.  */\n    t_signal **x_parentsignal;\n    int x_nchans;       /* this is also set in prolog & used in dsp */\n    t_reblocker *x_rb;  /* reblocking and resampling, one per channel */\n    unsigned int x_justcopyout:1;   /* switched but not blocked */\n    unsigned int x_borrowed:1;      /* output is borrowed from our inlet */\n} t_voutlet;\n\nstatic void *voutlet_new(t_symbol *s)\n{\n    t_voutlet *x = (t_voutlet *)pd_new(voutlet_class);\n    x->x_canvas = canvas_getcurrent();\n    x->x_parentoutlet = canvas_addoutlet(x->x_canvas, &x->x_obj.ob_pd, 0);\n    inlet_new(&x->x_obj, &x->x_obj.ob_pd, 0, 0);\n    x->x_buflength = 0;\n    x->x_rb = 0;\n    x->x_nchans = 0;\n    return (x);\n}\n\nstatic void voutlet_bang(t_voutlet *x)\n{\n    outlet_bang(x->x_parentoutlet);\n}\n\nstatic void voutlet_pointer(t_voutlet *x, t_gpointer *gp)\n{\n    outlet_pointer(x->x_parentoutlet, gp);\n}\n\nstatic void voutlet_float(t_voutlet *x, t_float f)\n{\n    outlet_float(x->x_parentoutlet, f);\n}\n\nstatic void voutlet_symbol(t_voutlet *x, t_symbol *s)\n{\n    outlet_symbol(x->x_parentoutlet, s);\n}\n\nstatic void voutlet_list(t_voutlet *x, t_symbol *s, int argc, t_atom *argv)\n{\n    outlet_list(x->x_parentoutlet, s, argc, argv);\n}\n\nstatic void voutlet_anything(t_voutlet *x, t_symbol *s, int argc, t_atom *argv)\n{\n    outlet_anything(x->x_parentoutlet, s, argc, argv);\n}\n\nstatic void voutlet_free(t_voutlet *x)\n{\n    int i;\n    canvas_rmoutlet(x->x_canvas, x->x_parentoutlet);\n    if (x->x_rb)\n        for (i = 0; i < x->x_nchans; i++)\n    {\n        freebytes((x->x_rb)[i].r_buf, x->x_buflength * sizeof(t_sample));\n        resample_free(&(x->x_rb)[i].r_updown);\n    }\n}\n\nt_outlet *voutlet_getit(t_pd *x)\n{\n    if (pd_class(x) != voutlet_class) bug(\"voutlet_getit\");\n    return (((t_voutlet *)x)->x_parentoutlet);\n}\n\n/* ------------------------- signal outlet -------------------------- */\n\nint voutlet_issignal(t_voutlet *x)\n{\n    return (x->x_rb != 0);\n}\n\n    /* LATER optimize for non-overlapped case where the \"+=\" isn't needed */\nt_int *voutlet_perform(t_int *w)\n{\n    t_voutlet *x = (t_voutlet *)(w[1]);\n    t_sample *in = (t_sample *)(w[2]), *buf= (t_sample *)(w[3]);\n    int xwrite = x->x_write;\n    int hop = (int)(w[4]);\n    int n = (int)(w[5]);\n    t_sample *out = buf + xwrite,\n        *endbuf = buf + x->x_buflength;\n    while (n--)\n    {\n        *out++ += *in++;\n        if (out == endbuf)\n            out = buf;\n    }\n    xwrite += x->x_hop;\n    if (xwrite >= x->x_buflength)\n        xwrite = 0;\n    x->x_write = xwrite;\n    return (w+6);\n}\n\n    /* epilog code for blocking: write buffer to parent patch */\nstatic t_int *voutlet_doepilog(t_int *w)\n{\n    t_voutlet *x = (t_voutlet *)(w[1]);\n    t_sample *in, *out = (t_sample *)(w[2]), *buf = (t_sample *)(w[3]);\n    int hop = (int)(w[4]), n = (int)(w[5]), empty = x->x_empty;\n    if (empty == x->x_buflength)\n        empty = 0;\n    x->x_empty = empty + hop;\n    in = buf + empty;\n    for (; n--; in++)\n        *out++ = *in, *in = 0;\n    return (w+6);\n}\n\n    /* It's dumb that we have to offer a duplicate but we don't have the\n    goddam resampling buffer allocated by the time this goes on the DSP\n    chain so we have to look it up dynamically. */\nstatic t_int *voutlet_doepilog_resample(t_int *w)\n{\n    t_voutlet *x = (t_voutlet *)(w[1]);\n    t_sample *in, *out = ((t_resample *)(w[2]))->s_vec,\n        *buf = (t_sample *)(w[3]);\n    int hop = (int)(w[4]), n = (int)(w[5]), empty = x->x_empty;\n    if (empty == x->x_buflength)\n        empty = 0;\n    x->x_empty = empty + hop;\n    in = buf + empty;\n    for (; n--; in++)\n        *out++ = *in, *in = 0;\n    return (w+6);\n}\n\nint outlet_getsignalindex(t_outlet *x);\n\n        /* prolog for outlets -- store pointer to the outlet on the\n        parent, which, if \"reblock\" is false, will want to refer\n        back to whatever we see on our input during the \"dsp\" method\n        called later.  */\nvoid voutlet_dspprolog(struct _voutlet *x, t_signal **parentsigs,\n    int myvecsize, int phase, int period, int frequency,\n    int downsample, int upsample, int reblock, int switched)\n{\n        /* no buffer means we're not a signal outlet */\n    if (!x->x_rb)\n        return;\n    x->x_justcopyout = (switched && !reblock);\n    x->x_parentsignal = (parentsigs?\n        &parentsigs[outlet_getsignalindex(x->x_parentoutlet)] : 0);\n    if (!parentsigs)\n        return;\n    if (switched || reblock)\n        x->x_borrowed = 0;\n    else    /* OK, borrow it */\n    {\n        int overlap = (*(x->x_parentsignal))->s_overlap;\n        x->x_borrowed = 1;\n        if (!parentsigs)\n            bug(\"voutlet_dspprolog\");\n                /* create new borrowed signal to be set in dsp routine below */\n        *(x->x_parentsignal) = signal_new(0, 1,\n            (*(x->x_parentsignal))->s_sr, 0);\n        (*(x->x_parentsignal))->s_overlap = overlap;\n    }\n    if (reblock)\n    {\n        int parentvecsize, buflength, i;\n        int re_parentvecsize;\n        int bigperiod, epilogphase, blockphase;\n\n        parentvecsize = (*x->x_parentsignal)->s_length;\n        re_parentvecsize = parentvecsize * upsample / downsample;\n        buflength = re_parentvecsize;\n        if (buflength < myvecsize)\n            buflength = myvecsize;\n        if (buflength != x->x_buflength)\n        {\n            for (i = 0; i < x->x_nchans; i++)\n            {\n                x->x_rb[i].r_buf = (t_sample *)t_resizebytes(x->x_rb[i].r_buf,\n                    x->x_buflength * sizeof(t_sample),\n                        buflength * sizeof(t_sample));\n                memset((char *)x->x_rb[i].r_buf, 0,\n                    buflength * sizeof(t_sample));\n            }\n            x->x_buflength = buflength;\n        }\n    }\n}\n\nstatic void voutlet_dsp(t_voutlet *x, t_signal **sp)\n{\n    if (!x->x_rb)\n        return;\n    reblocker_resize(&x->x_rb, x->x_nchans, sp[0]->s_nchans, x->x_buflength);\n    x->x_nchans = sp[0]->s_nchans;\n    if (x->x_borrowed)\n    {\n            /* if we're just going to make the signal available on the\n            parent patch, hand it off to the parent signal. */\n        signal_setborrowed(*x->x_parentsignal, sp[0]);\n    }\n    else if (x->x_parentsignal)\n    {\n        int i;\n        signal_setmultiout(x->x_parentsignal, sp[0]->s_nchans);\n        if (x->x_justcopyout)\n            dsp_add_copy(sp[0]->s_vec, (*x->x_parentsignal)->s_vec,\n                sp[0]->s_length * sp[0]->s_nchans);\n        else for (i = 0; i < x->x_nchans; i++)\n            dsp_add(voutlet_perform, 5, x, sp[0]->s_vec + i * sp[0]->s_length,\n                x->x_rb[i].r_buf, (t_int)(i == x->x_nchans - 1 ? x->x_hop : 0),\n                    (t_int)sp[0]->s_length);\n    }\n}\n\n        /* set up epilog DSP code.  If we're reblocking, this is the\n        time to copy the samples out to the containing object's outlets.\n        If we aren't reblocking, there's nothing to do here.  */\nvoid voutlet_dspepilog(struct _voutlet *x, t_signal **parentsigs,\n    int myvecsize, int phase, int period, int frequency,\n    int downsample, int upsample, int reblock, int switched)\n{\n    if (!x->x_rb)\n        return;  /* this shouldn't be necesssary... */\n    if (!x->x_parentsignal) /* toplevels do nothing */\n        return;\n    if (reblock)\n    {\n        int parentvecsize, i;\n        int re_parentvecsize;\n        int bigperiod, epilogphase, blockphase;\n        if (x->x_parentsignal)\n        {\n            parentvecsize = (*x->x_parentsignal)->s_length;\n            re_parentvecsize = parentvecsize * upsample / downsample;\n        }\n        else\n        {\n            bug(\"voutlet_dspepilog\");\n            parentvecsize = re_parentvecsize = 1;\n        }\n        bigperiod = myvecsize/re_parentvecsize;\n        if (!bigperiod)\n            bigperiod = 1;\n        epilogphase = phase & (bigperiod - 1);\n        blockphase = (phase + period - 1) & (bigperiod - 1) & (- period);\n        if (re_parentvecsize * period > x->x_buflength)\n            bug(\"voutlet_dspepilog\");\n        x->x_write = re_parentvecsize * blockphase;\n        if (x->x_write == x->x_buflength)\n            x->x_write = 0;\n        if (period == 1 && frequency > 1)\n            x->x_hop = re_parentvecsize / frequency;\n        else x->x_hop = period * re_parentvecsize;\n        if (x->x_parentsignal)\n        {\n                    /* set epilog pointer and schedule it */\n            x->x_empty = re_parentvecsize * epilogphase;\n            for (i = 0; i < x->x_nchans; i++)\n            {\n                t_int hop = (i == x->x_nchans-1 ? re_parentvecsize : 0);\n                if (upsample * downsample == 1)\n                    dsp_add(voutlet_doepilog, 5, x,\n                        (*x->x_parentsignal)->s_vec + i * parentvecsize,\n                            x->x_rb[i].r_buf,  hop, (t_int)parentvecsize);\n                else\n                {\n                    int method = (x->x_updownmethod < 0 ?\n                        (pd_compatibilitylevel < 44 ? 0 : 1) :\n                            x->x_updownmethod);\n                    x->x_rb[i].r_updown.downsample=downsample;\n                    x->x_rb[i].r_updown.upsample=upsample;\n                    dsp_add(voutlet_doepilog_resample, 5, x,\n                        &x->x_rb[i].r_updown,\n                            x->x_rb[i].r_buf, hop, (t_int)re_parentvecsize);\n                    resampleto_dsp(&x->x_rb[i].r_updown,\n                        (*x->x_parentsignal)->s_vec + i * parentvecsize,\n                            re_parentvecsize, parentvecsize, method);\n                }\n            }\n        }\n    }\n        /* if we aren't blocked but we are switched, the epilog code just\n        copies zeros to the output.  In this case the DSP chain contains a\n        function that causes DSP passes to jump over the epilog when the block\n        is switched on, so this only happens when switched off. */\n    else if (switched)\n    {\n        if (*x->x_parentsignal)\n            dsp_add_zero((*x->x_parentsignal)->s_vec,\n                (*x->x_parentsignal)->s_length *\n                    (*x->x_parentsignal)->s_nchans);\n    }\n}\n\nstatic void *voutlet_newsig(t_symbol *s)\n{\n    t_voutlet *x = (t_voutlet *)pd_new(voutlet_class);\n    x->x_canvas = canvas_getcurrent();\n    x->x_parentoutlet = canvas_addoutlet(x->x_canvas,\n        &x->x_obj.ob_pd, &s_signal);\n    inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);\n    x->x_buflength = 0;\n    x->x_rb = (t_reblocker *)getbytes(sizeof(*x->x_rb));\n    reblocker_init(x->x_rb, x->x_buflength);\n    x->x_buflength = 0;\n    x->x_nchans = 1;\n\n    /*\n     * up till now we provide several upsampling methods and 1 single\n     * downsampling method (no filtering !)\n     */\n    x->x_updownmethod = symbol2resamplemethod(s);\n\n    return (x);\n}\n\n\nstatic void voutlet_setup(void)\n{\n    voutlet_class = class_new(gensym(\"outlet\"), (t_newmethod)voutlet_new,\n        (t_method)voutlet_free, sizeof(t_voutlet),\n            CLASS_NOINLET | CLASS_MULTICHANNEL, A_DEFSYM, 0);\n    class_addcreator((t_newmethod)voutlet_newsig, gensym(\"outlet~\"), A_DEFSYM, 0);\n    class_addbang(voutlet_class, voutlet_bang);\n    class_addpointer(voutlet_class, voutlet_pointer);\n    class_addfloat(voutlet_class, (t_method)voutlet_float);\n    class_addsymbol(voutlet_class, voutlet_symbol);\n    class_addlist(voutlet_class, voutlet_list);\n    class_addanything(voutlet_class, voutlet_anything);\n    class_addmethod(voutlet_class, (t_method)voutlet_dsp,\n        gensym(\"dsp\"), A_CANT, 0);\n    class_sethelpsymbol(voutlet_class, gensym(\"inlet-outlet\"));\n}\n\n/* ---------------------------- overall setup ----------------------------- */\n\nvoid g_io_setup(void)\n{\n    vinlet_setup();\n    voutlet_setup();\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/g_mycanvas.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution. */\n\n/* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */\n/* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */\n\n#include <string.h>\n#include <stdio.h>\n#include \"m_pd.h\"\n\n#include \"g_all_guis.h\"\n\n#ifdef _WIN32\n#include <io.h>\n#else\n#include <unistd.h>\n#endif\n\n/* ---------- cnv  my gui-canvas for a window ---------------- */\n\nt_widgetbehavior my_canvas_widgetbehavior;\nstatic t_class *my_canvas_class;\n\n/* widget helper functions */\n\n#define my_canvas_draw_update 0\n\nstatic void my_canvas_draw_io(t_my_canvas* x, t_glist* glist, int mode) { ; }\nstatic void my_canvas_draw_config(t_my_canvas* x, t_glist* glist)\n{\n    const int zoom = IEMGUI_ZOOM(x);\n    t_canvas *canvas = glist_getcanvas(glist);\n    int xpos = text_xpix(&x->x_gui.x_obj, glist);\n    int ypos = text_ypix(&x->x_gui.x_obj, glist);\n    int offset = (zoom > 1 ? zoom : 0); /* keep zoomed border inside visible area */\n    char tag[128];\n    t_atom fontatoms[3];\n    SETSYMBOL(fontatoms+0, gensym(x->x_gui.x_font));\n    SETFLOAT (fontatoms+1, -(x->x_gui.x_fontsize)*zoom);\n    SETSYMBOL(fontatoms+2, gensym(sys_fontweight));\n\n    sprintf(tag, \"%pRECT\", x);\n    pdgui_vmess(0, \"crs iiii\", canvas, \"coords\", tag,\n        xpos, ypos, xpos + x->x_vis_w * zoom, ypos + x->x_vis_h * zoom);\n    pdgui_vmess(0, \"crs rk rk\", canvas, \"itemconfigure\", tag,\n        \"-fill\", x->x_gui.x_bcol,\n        \"-outline\", x->x_gui.x_bcol);\n\n    sprintf(tag, \"%pBASE\", x);\n    pdgui_vmess(0, \"crs iiii\", canvas, \"coords\", tag,\n        xpos + offset, ypos + offset,\n        xpos + offset + x->x_gui.x_w, ypos + offset + x->x_gui.x_h);\n    pdgui_vmess(0, \"crs ri rk\", canvas, \"itemconfigure\", tag,\n        \"-width\", zoom,\n        \"-outline\", (x->x_gui.x_fsf.x_selected ? IEM_GUI_COLOR_SELECTED : x->x_gui.x_bcol));\n\n    sprintf(tag, \"%pLABEL\", x);\n    pdgui_vmess(0, \"crs ii\", canvas, \"coords\", tag,\n        xpos + x->x_gui.x_ldx * zoom,\n        ypos + x->x_gui.x_ldy * zoom);\n    pdgui_vmess(0, \"crs rA rk\", canvas, \"itemconfigure\", tag,\n        \"-font\", 3, fontatoms,\n        \"-fill\", x->x_gui.x_lcol);\n    iemgui_dolabel(x, &x->x_gui, x->x_gui.x_lab, 1);\n}\n\nstatic void my_canvas_draw_new(t_my_canvas *x, t_glist *glist)\n{\n    t_canvas *canvas = glist_getcanvas(glist);\n    char tag_object[128], tag[128];\n    char *tags[] = {tag_object, tag, \"label\", \"text\"};\n    sprintf(tag_object, \"%pOBJ\", x);\n\n    sprintf(tag, \"%pRECT\", x);\n    pdgui_vmess(0, \"crr iiii rS\", canvas, \"create\", \"rectangle\",\n        0, 0, 0, 0, \"-tags\", 2, tags);\n\n    sprintf(tag, \"%pBASE\", x);\n    pdgui_vmess(0, \"crr iiii rS\", canvas, \"create\", \"rectangle\",\n        0, 0, 0, 0, \"-tags\", 2, tags);\n\n    sprintf(tag, \"%pLABEL\", x);\n    pdgui_vmess(0, \"crr ii rs rS\", canvas, \"create\", \"text\",\n        0, 0, \"-anchor\", \"w\", \"-tags\", 4, tags);\n\n    my_canvas_draw_config(x, glist);\n}\n\nstatic void my_canvas_draw_select(t_my_canvas* x, t_glist* glist)\n{\n    t_canvas *canvas = glist_getcanvas(glist);\n    char tag[128];\n    sprintf(tag, \"%pBASE\", x);\n    pdgui_vmess(0, \"crs rk\", canvas, \"itemconfigure\", tag,\n        \"-outline\", (x->x_gui.x_fsf.x_selected ? IEM_GUI_COLOR_SELECTED : x->x_gui.x_bcol));\n}\n\n/* ------------------------ cnv widgetbehaviour----------------------------- */\n\nstatic void my_canvas_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2)\n{\n    t_my_canvas *x = (t_my_canvas *)z;\n\n    *xp1 = text_xpix(&x->x_gui.x_obj, glist);\n    *yp1 = text_ypix(&x->x_gui.x_obj, glist);\n    *xp2 = *xp1 + x->x_gui.x_w;\n    *yp2 = *yp1 + x->x_gui.x_h;\n}\n\nstatic void my_canvas_save(t_gobj *z, t_binbuf *b)\n{\n    t_my_canvas *x = (t_my_canvas *)z;\n    t_symbol *bflcol[3];\n    t_symbol *srl[3];\n\n    iemgui_save(&x->x_gui, srl, bflcol);\n    binbuf_addv(b, \"ssiisiiisssiiiissi\", gensym(\"#X\"),gensym(\"obj\"),\n                (int)x->x_gui.x_obj.te_xpix, (int)x->x_gui.x_obj.te_ypix,\n                gensym(\"cnv\"), x->x_gui.x_w/IEMGUI_ZOOM(x), x->x_vis_w, x->x_vis_h,\n                srl[0], srl[1], srl[2], x->x_gui.x_ldx, x->x_gui.x_ldy,\n                iem_fstyletoint(&x->x_gui.x_fsf), x->x_gui.x_fontsize,\n                bflcol[0], bflcol[2], iem_symargstoint(&x->x_gui.x_isa));\n    binbuf_addv(b, \";\");\n}\n\nstatic void my_canvas_properties(t_gobj *z, t_glist *owner)\n{\n    t_my_canvas *x = (t_my_canvas *)z;\n    iemgui_new_dialog(x, &x->x_gui, \"cnv\",\n                      x->x_gui.x_w/IEMGUI_ZOOM(x), 1,\n                      0, 0,\n                      x->x_vis_w, x->x_vis_h,\n                      0,\n                      -1, \"\", \"\",\n                      0, -1, -1);\n\n}\n\nstatic void my_canvas_get_pos(t_my_canvas *x)\n{\n    if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)\n    {\n        x->x_at[0].a_w.w_float = text_xpix(&x->x_gui.x_obj, x->x_gui.x_glist)/IEMGUI_ZOOM(x);\n        x->x_at[1].a_w.w_float = text_ypix(&x->x_gui.x_obj, x->x_gui.x_glist)/IEMGUI_ZOOM(x);\n        pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at);\n    }\n}\n\nstatic void my_canvas_dialog(t_my_canvas *x, t_symbol *s, int argc, t_atom *argv)\n{\n    t_symbol *srl[3];\n    int a = atom_getfloatarg(0, argc, argv);\n    int w = atom_getfloatarg(2, argc, argv);\n    int h = atom_getfloatarg(3, argc, argv);\n    int sr_flags;\n    t_atom undo[18];\n    iemgui_setdialogatoms(&x->x_gui, 18, undo);\n    SETFLOAT (undo+1, 0);\n    SETFLOAT (undo+2, x->x_vis_w);\n    SETFLOAT (undo+3, x->x_vis_h);\n    SETFLOAT (undo+5, -1);\n    SETSYMBOL(undo+15, gensym(\"none\"));\n\n    pd_undo_set_objectstate(x->x_gui.x_glist, (t_pd*)x, gensym(\"dialog\"),\n                            18, undo,\n                            argc, argv);\n\n    sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv);\n    x->x_gui.x_isa.x_loadinit = 0;\n    if(a < 1)\n        a = 1;\n    x->x_gui.x_w = a * IEMGUI_ZOOM(x);\n    x->x_gui.x_h = x->x_gui.x_w;\n    if(w < 1)\n        w = 1;\n    x->x_vis_w = w;\n    if(h < 1)\n        h = 1;\n    x->x_vis_h = h;\n    iemgui_size((void *)x, &x->x_gui);\n}\n\nstatic void my_canvas_size(t_my_canvas *x, t_symbol *s, int ac, t_atom *av)\n{\n    int i = atom_getfloatarg(0, ac, av);\n\n    if(i < 1)\n        i = 1;\n    x->x_gui.x_w = i*IEMGUI_ZOOM(x);\n    x->x_gui.x_h = x->x_gui.x_w;\n    iemgui_size((void *)x, &x->x_gui);\n}\n\nstatic void my_canvas_delta(t_my_canvas *x, t_symbol *s, int ac, t_atom *av)\n{iemgui_delta((void *)x, &x->x_gui, s, ac, av);}\n\nstatic void my_canvas_pos(t_my_canvas *x, t_symbol *s, int ac, t_atom *av)\n{iemgui_pos((void *)x, &x->x_gui, s, ac, av);}\n\nstatic void my_canvas_vis_size(t_my_canvas *x, t_symbol *s, int ac, t_atom *av)\n{\n    int i;\n\n    i = atom_getfloatarg(0, ac, av);\n    if(i < 1)\n        i = 1;\n    x->x_vis_w = i;\n    if(ac > 1)\n    {\n        i = atom_getfloatarg(1, ac, av);\n        if(i < 1)\n            i = 1;\n    }\n    x->x_vis_h = i;\n    iemgui_size(x, &x->x_gui);\n}\n\nstatic void my_canvas_color(t_my_canvas *x, t_symbol *s, int ac, t_atom *av)\n{iemgui_color((void *)x, &x->x_gui, s, ac, av);}\n\nstatic void my_canvas_send(t_my_canvas *x, t_symbol *s)\n{iemgui_send(x, &x->x_gui, s);}\n\nstatic void my_canvas_receive(t_my_canvas *x, t_symbol *s)\n{iemgui_receive(x, &x->x_gui, s);}\n\nstatic void my_canvas_label(t_my_canvas *x, t_symbol *s)\n{iemgui_label((void *)x, &x->x_gui, s);}\n\nstatic void my_canvas_label_pos(t_my_canvas *x, t_symbol *s, int ac, t_atom *av)\n{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);}\n\nstatic void my_canvas_label_font(t_my_canvas *x, t_symbol *s, int ac, t_atom *av)\n{iemgui_label_font((void *)x, &x->x_gui, s, ac, av);}\n\nstatic void *my_canvas_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_my_canvas *x = (t_my_canvas *)iemgui_new(my_canvas_class);\n    int a = IEM_GUI_DEFAULTSIZE;\n    int w = 100 * IEM_GUI_DEFAULTSIZE_SCALE, h = 60 * IEM_GUI_DEFAULTSIZE_SCALE;\n    int ldx = 20, ldy = 12, f = 2, i = 0;\n    int fs = x->x_gui.x_fontsize;\n\n    IEMGUI_SETDRAWFUNCTIONS(x, my_canvas);\n\n    x->x_gui.x_bcol = 0xE0E0E0;\n    x->x_gui.x_fcol = 0x00;\n    x->x_gui.x_lcol = 0x404040;\n\n    if(((argc >= 10)&&(argc <= 13))\n       &&IS_A_FLOAT(argv,0)&&IS_A_FLOAT(argv,1)&&IS_A_FLOAT(argv,2))\n    {\n        a = atom_getfloatarg(0, argc, argv);\n        w = atom_getfloatarg(1, argc, argv);\n        h = atom_getfloatarg(2, argc, argv);\n    }\n    if((argc >= 12)&&(IS_A_SYMBOL(argv,3)||IS_A_FLOAT(argv,3))&&(IS_A_SYMBOL(argv,4)||IS_A_FLOAT(argv,4)))\n    {\n        i = 2;\n        iemgui_new_getnames(&x->x_gui, 3, argv);\n    }\n    else if((argc == 11)&&(IS_A_SYMBOL(argv,3)||IS_A_FLOAT(argv,3)))\n    {\n        i = 1;\n        iemgui_new_getnames(&x->x_gui, 3, argv);\n    }\n    else iemgui_new_getnames(&x->x_gui, 3, 0);\n\n    if(((argc >= 10)&&(argc <= 13))\n       &&(IS_A_SYMBOL(argv,i+3)||IS_A_FLOAT(argv,i+3))&&IS_A_FLOAT(argv,i+4)\n       &&IS_A_FLOAT(argv,i+5)&&IS_A_FLOAT(argv,i+6)\n       &&IS_A_FLOAT(argv,i+7))\n    {\n            /* disastrously, the \"label\" sits in a different part of the\n            message.  So we have to track its location separately (in\n            the slot x_labelbindex) and initialize it specially here. */\n        if(IS_A_FLOAT(argv, i+3))\n        {\n            char str[80];\n            atom_string(argv+i+3, str, sizeof(str));\n            x->x_gui.x_lab = gensym(str);\n        } else {\n            x->x_gui.x_lab = iemgui_new_dogetname(&x->x_gui, i+3, argv);\n        }\n        x->x_gui.x_labelbindex = i+4;\n        ldx = atom_getfloatarg(i+4, argc, argv);\n        ldy = atom_getfloatarg(i+5, argc, argv);\n        iem_inttofstyle(&x->x_gui.x_fsf, atom_getfloatarg(i+6, argc, argv));\n        fs = atom_getfloatarg(i+7, argc, argv);\n        x->x_gui.x_fontsize = fs;\n        iemgui_all_loadcolors(&x->x_gui, argv+i+8, 0, argv+i+9);\n    }\n    if((argc == 13)&&IS_A_FLOAT(argv,i+10))\n    {\n        iem_inttosymargs(&x->x_gui.x_isa, atom_getfloatarg(i+10, argc, argv));\n    }\n    x->x_gui.x_fsf.x_snd_able = (0 != x->x_gui.x_snd);\n    x->x_gui.x_fsf.x_rcv_able = (0 != x->x_gui.x_rcv);\n    if(a < 1)\n        a = 1;\n    x->x_gui.x_w = a;\n    x->x_gui.x_h = x->x_gui.x_w;\n    if(w < 1)\n        w = 1;\n    x->x_vis_w = w;\n    if(h < 1)\n        h = 1;\n    x->x_vis_h = h;\n    if(x->x_gui.x_fsf.x_font_style == 1) strcpy(x->x_gui.x_font, \"helvetica\");\n    else if(x->x_gui.x_fsf.x_font_style == 2) strcpy(x->x_gui.x_font, \"times\");\n    else { x->x_gui.x_fsf.x_font_style = 0;\n        strcpy(x->x_gui.x_font, sys_font); }\n    if (x->x_gui.x_fsf.x_rcv_able)\n        pd_bind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv);\n    x->x_gui.x_ldx = ldx;\n    x->x_gui.x_ldy = ldy;\n    x->x_gui.x_fontsize = (fs < 4)?4:fs;\n    x->x_at[0].a_type = A_FLOAT;\n    x->x_at[1].a_type = A_FLOAT;\n    iemgui_verify_snd_ne_rcv(&x->x_gui);\n    iemgui_newzoom(&x->x_gui);\n    return (x);\n}\n\nstatic void my_canvas_free(t_my_canvas *x)\n{\n    if(x->x_gui.x_fsf.x_rcv_able)\n        pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv);\n    pdgui_stub_deleteforkey(x);\n}\n\nvoid g_mycanvas_setup(void)\n{\n    my_canvas_class = class_new(gensym(\"cnv\"), (t_newmethod)my_canvas_new,\n        (t_method)my_canvas_free, sizeof(t_my_canvas), CLASS_NOINLET, A_GIMME, 0);\n    class_addcreator((t_newmethod)my_canvas_new, gensym(\"my_canvas\"), A_GIMME, 0);\n    class_addmethod(my_canvas_class, (t_method)my_canvas_dialog,\n        gensym(\"dialog\"), A_GIMME, 0);\n    class_addmethod(my_canvas_class, (t_method)my_canvas_size,\n        gensym(\"size\"), A_GIMME, 0);\n    class_addmethod(my_canvas_class, (t_method)my_canvas_delta,\n        gensym(\"delta\"), A_GIMME, 0);\n    class_addmethod(my_canvas_class, (t_method)my_canvas_pos,\n        gensym(\"pos\"), A_GIMME, 0);\n    class_addmethod(my_canvas_class, (t_method)my_canvas_vis_size,\n        gensym(\"vis_size\"), A_GIMME, 0);\n    class_addmethod(my_canvas_class, (t_method)my_canvas_color,\n        gensym(\"color\"), A_GIMME, 0);\n    class_addmethod(my_canvas_class, (t_method)my_canvas_send,\n        gensym(\"send\"), A_DEFSYM, 0);\n    class_addmethod(my_canvas_class, (t_method)my_canvas_receive,\n        gensym(\"receive\"), A_DEFSYM, 0);\n    class_addmethod(my_canvas_class, (t_method)my_canvas_label,\n        gensym(\"label\"), A_DEFSYM, 0);\n    class_addmethod(my_canvas_class, (t_method)my_canvas_label_pos,\n        gensym(\"label_pos\"), A_GIMME, 0);\n    class_addmethod(my_canvas_class, (t_method)my_canvas_label_font,\n        gensym(\"label_font\"), A_GIMME, 0);\n    class_addmethod(my_canvas_class, (t_method)my_canvas_get_pos,\n        gensym(\"get_pos\"), 0);\n    class_addmethod(my_canvas_class, (t_method)iemgui_zoom,\n        gensym(\"zoom\"), A_CANT, 0);\n    my_canvas_widgetbehavior.w_getrectfn  = my_canvas_getrect;\n    my_canvas_widgetbehavior.w_displacefn = iemgui_displace;\n    my_canvas_widgetbehavior.w_selectfn   = iemgui_select;\n    my_canvas_widgetbehavior.w_activatefn = NULL;\n    my_canvas_widgetbehavior.w_deletefn   = iemgui_delete;\n    my_canvas_widgetbehavior.w_visfn      = iemgui_vis;\n    my_canvas_widgetbehavior.w_clickfn    = NULL;\n    class_setwidget(my_canvas_class, &my_canvas_widgetbehavior);\n    class_setsavefn(my_canvas_class, my_canvas_save);\n    class_setpropertiesfn(my_canvas_class, my_canvas_properties);\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/g_numbox.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution. */\n\n/* my_numbox.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */\n\n#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n#include \"m_pd.h\"\n\n#include \"g_all_guis.h\"\n#include <math.h>\n\n#define MINDIGITS 1\n#define MINFONT   4\n\n/*------------------ global functions -------------------------*/\n\nstatic void my_numbox_key(void *z, t_symbol *keysym, t_floatarg fkey);\nstatic void my_numbox_draw_update(t_gobj *client, t_glist *glist);\n\nvoid my_numbox_clip(t_my_numbox *x)\n{\n    if(x->x_val < x->x_min)\n        x->x_val = x->x_min;\n    if(x->x_val > x->x_max)\n        x->x_val = x->x_max;\n}\n\nvoid my_numbox_calc_fontwidth(t_my_numbox *x)\n{\n    int w, f = 31;\n\n    if(x->x_gui.x_fsf.x_font_style == 1)\n        f = 27;\n    else if(x->x_gui.x_fsf.x_font_style == 2)\n        f = 25;\n\n    w = x->x_gui.x_fontsize * f * x->x_numwidth;\n    w /= 36;\n    x->x_gui.x_w = (w + (x->x_gui.x_h/2)/IEMGUI_ZOOM(x) + 4) * IEMGUI_ZOOM(x);\n}\n\nvoid my_numbox_ftoa(t_my_numbox *x)\n{\n    double f = x->x_val;\n    int bufsize, is_exp = 0, i, idecimal;\n\n    sprintf(x->x_buf, \"%g\", f);\n    bufsize = (int)strlen(x->x_buf);\n    if(bufsize >= 5)/* if it is in exponential mode */\n    {\n        i = bufsize - 4;\n        if((x->x_buf[i] == 'e') || (x->x_buf[i] == 'E'))\n            is_exp = 1;\n    }\n    if(bufsize > x->x_numwidth)/* if to reduce */\n    {\n        if(is_exp)\n        {\n            if(x->x_numwidth <= 5)\n            {\n                x->x_buf[0] = (f < 0.0 ? '-' : '+');\n                x->x_buf[1] = 0;\n            }\n            i = bufsize - 4;\n            for(idecimal = 0; idecimal < i; idecimal++)\n                if(x->x_buf[idecimal] == '.')\n                    break;\n            if(idecimal > (x->x_numwidth - 4))\n            {\n                x->x_buf[0] = (f < 0.0 ? '-' : '+');\n                x->x_buf[1] = 0;\n            }\n            else\n            {\n                int new_exp_index = x->x_numwidth - 4;\n                int old_exp_index = bufsize - 4;\n\n                for(i = 0; i < 4; i++, new_exp_index++, old_exp_index++)\n                    x->x_buf[new_exp_index] = x->x_buf[old_exp_index];\n                x->x_buf[x->x_numwidth] = 0;\n            }\n        }\n        else\n        {\n            for(idecimal = 0; idecimal < bufsize; idecimal++)\n                if(x->x_buf[idecimal] == '.')\n                    break;\n            if(idecimal > x->x_numwidth)\n            {\n                x->x_buf[0] = (f < 0.0 ? '-' : '+');\n                x->x_buf[1] = 0;\n            }\n            else\n                x->x_buf[x->x_numwidth] = 0;\n        }\n    }\n}\n\n/* ------------ nbx gui-my number box ----------------------- */\n\nt_widgetbehavior my_numbox_widgetbehavior;\nstatic t_class *my_numbox_class;\n\n#define my_numbox_draw_io 0\n\nstatic void my_numbox_draw_config(t_my_numbox* x, t_glist* glist)\n{\n    const int zoom = IEMGUI_ZOOM(x);\n    t_canvas *canvas = glist_getcanvas(glist);\n    t_iemgui *iemgui = &x->x_gui;\n    int xpos = text_xpix(&x->x_gui.x_obj, glist);\n    int ypos = text_ypix(&x->x_gui.x_obj, glist);\n    int w = x->x_gui.x_w, half = x->x_gui.x_h/2;\n    int d = zoom + x->x_gui.x_h/(34*zoom);\n    int corner = x->x_gui.x_h/4;\n    int iow = IOWIDTH * zoom, ioh = IEM_GUI_IOHEIGHT * zoom;\n    char tag[128];\n\n    int lcol = x->x_gui.x_lcol;\n    int fcol = x->x_gui.x_fcol;\n    t_atom fontatoms[3];\n    SETSYMBOL(fontatoms+0, gensym(iemgui->x_font));\n    SETFLOAT (fontatoms+1, -iemgui->x_fontsize*zoom);\n    SETSYMBOL(fontatoms+2, gensym(sys_fontweight));\n\n    if(x->x_gui.x_fsf.x_selected)\n        fcol = lcol = IEM_GUI_COLOR_SELECTED;\n    if(x->x_gui.x_fsf.x_change)\n        fcol =  IEM_GUI_COLOR_EDITED;\n\n    my_numbox_ftoa(x);\n\n    sprintf(tag, \"%pBASE1\", x);\n    pdgui_vmess(0, \"crs  ii ii ii ii ii ii\", canvas, \"coords\", tag,\n        xpos,              ypos,\n        xpos + w - corner, ypos,\n        xpos + w,          ypos + corner,\n        xpos + w,          ypos + x->x_gui.x_h,\n        xpos,              ypos + x->x_gui.x_h,\n        xpos,              ypos);\n    pdgui_vmess(0, \"crs  ri rk rk\", canvas, \"itemconfigure\", tag,\n        \"-width\", zoom,\n        \"-outline\", IEM_GUI_COLOR_NORMAL,\n        \"-fill\", x->x_gui.x_bcol);\n\n\n    sprintf(tag, \"%pBASE2\", x);\n    pdgui_vmess(0, \"crs  ii ii ii\", canvas, \"coords\", tag,\n        xpos + zoom, ypos + zoom,\n        xpos + half, ypos + half,\n        xpos + zoom, ypos + x->x_gui.x_h - zoom);\n    pdgui_vmess(0, \"crs  ri rk\", canvas, \"itemconfigure\", tag,\n        \"-width\", zoom,\n        \"-fill\", x->x_gui.x_fcol);\n\n    sprintf(tag, \"%pLABEL\", x);\n    pdgui_vmess(0, \"crs  ii\", canvas, \"coords\", tag,\n        xpos + x->x_gui.x_ldx * zoom,\n        ypos + x->x_gui.x_ldy * zoom);\n    pdgui_vmess(0, \"crs  rA rk\", canvas, \"itemconfigure\", tag,\n        \"-font\", 3, fontatoms,\n        \"-fill\", lcol);\n    iemgui_dolabel(x, &x->x_gui, x->x_gui.x_lab, 1);\n\n    sprintf(tag, \"%pNUMBER\", x);\n    pdgui_vmess(0, \"crs  ii\", canvas, \"coords\", tag,\n        xpos + half + 2*zoom, ypos + half + d);\n    pdgui_vmess(0, \"crs  rs rA rk\", canvas, \"itemconfigure\", tag,\n        \"-text\", x->x_buf,\n        \"-font\", 3, fontatoms,\n        \"-fill\", fcol);\n}\n\nstatic void my_numbox_draw_new(t_my_numbox *x, t_glist *glist)\n{\n    t_canvas *canvas = glist_getcanvas(glist);\n    char tag[128], tag_object[128];\n    char*tags[] = {tag_object, tag, \"label\", \"text\"};\n    sprintf(tag_object, \"%pOBJ\", x);\n\n    sprintf(tag, \"%pBASE1\", x);\n    pdgui_vmess(0, \"crr ii rS\", canvas, \"create\", \"polygon\",\n        0, 0, \"-tags\", 2, tags);\n\n    sprintf(tag, \"%pBASE2\", x);\n    pdgui_vmess(0, \"crr iiii rS\", canvas, \"create\", \"line\",\n        0, 0, 0, 0, \"-tags\", 2, tags);\n\n    sprintf(tag, \"%pLABEL\", x);\n    pdgui_vmess(0, \"crr ii rs rS\", canvas, \"create\", \"text\",\n        0, 0, \"-anchor\", \"w\", \"-tags\", 4, tags);\n\n    sprintf(tag, \"%pNUMBER\", x);\n    pdgui_vmess(0, \"crr ii rs rS\", canvas, \"create\", \"text\",\n        0, 0, \"-anchor\", \"w\", \"-tags\", 2, tags);\n\n    my_numbox_draw_config(x, glist);\n    (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_IO);\n}\n\nstatic void my_numbox_draw_select(t_my_numbox *x, t_glist *glist)\n{\n    t_canvas *canvas = glist_getcanvas(glist);\n    int bcol = IEM_GUI_COLOR_NORMAL, lcol = x->x_gui.x_lcol, fcol = x->x_gui.x_fcol;\n    char tag[128];\n\n    if(x->x_gui.x_fsf.x_selected)\n    {\n        if(x->x_gui.x_fsf.x_change)\n        {\n            x->x_gui.x_fsf.x_change = 0;\n            x->x_buf[0] = 0;\n            sys_queuegui(x, x->x_gui.x_glist, my_numbox_draw_update);\n        }\n        bcol = lcol = fcol = IEM_GUI_COLOR_SELECTED;\n    }\n\n    sprintf(tag, \"%pBASE1\", x);\n    pdgui_vmess(0, \"crs rk\", canvas, \"itemconfigure\", tag, \"-outline\", bcol);\n    sprintf(tag, \"%pBASE2\", x);\n    pdgui_vmess(0, \"crs rk\", canvas, \"itemconfigure\", tag, \"-fill\", fcol);\n    sprintf(tag, \"%pLABEL\", x);\n    pdgui_vmess(0, \"crs rk\", canvas, \"itemconfigure\", tag, \"-fill\", lcol);\n    sprintf(tag, \"%pNUMBER\", x);\n    pdgui_vmess(0, \"crs rk\", canvas, \"itemconfigure\", tag, \"-fill\", fcol);\n}\n\nstatic void my_numbox_draw_update(t_gobj *client, t_glist *glist)\n{\n    t_my_numbox *x = (t_my_numbox *)client;\n    if(glist_isvisible(glist))\n    {\n        t_canvas *canvas = glist_getcanvas(glist);\n        char tag[128];\n        sprintf(tag, \"%pNUMBER\", x);\n        if(x->x_gui.x_fsf.x_change)\n        {\n            if(x->x_buf[0])\n            {\n                char *cp = x->x_buf;\n                int sl = (int)strlen(x->x_buf);\n\n                x->x_buf[sl] = '>';\n                x->x_buf[sl+1] = 0;\n                if(sl >= x->x_numwidth)\n                    cp += sl - x->x_numwidth + 1;\n                pdgui_vmess(0, \"crs rk rs\", canvas, \"itemconfigure\", tag,\n                    \"-fill\", IEM_GUI_COLOR_EDITED, \"-text\", cp);\n                x->x_buf[sl] = 0;\n            }\n            else\n            {\n                my_numbox_ftoa(x);\n                pdgui_vmess(0, \"crs rk rs\", canvas, \"itemconfigure\", tag,\n                    \"-fill\", IEM_GUI_COLOR_EDITED, \"-text\", x->x_buf);\n                x->x_buf[0] = 0;\n            }\n        }\n        else\n        {\n            my_numbox_ftoa(x);\n                pdgui_vmess(0, \"crs rk rs\", canvas, \"itemconfigure\", tag,\n                    \"-fill\", (x->x_gui.x_fsf.x_selected ? IEM_GUI_COLOR_SELECTED : x->x_gui.x_fcol),\n                    \"-text\", x->x_buf);\n            x->x_buf[0] = 0;\n        }\n    }\n}\n\n/* widget helper functions */\n\nstatic void my_numbox_tick_wait(t_my_numbox *x)\n{\n    sys_queuegui(x, x->x_gui.x_glist, my_numbox_draw_update);\n}\n\n/* ------------------------ nbx widgetbehaviour----------------------------- */\n\nstatic void my_numbox_getrect(t_gobj *z, t_glist *glist,\n                              int *xp1, int *yp1, int *xp2, int *yp2)\n{\n    t_my_numbox* x = (t_my_numbox*)z;\n\n    *xp1 = text_xpix(&x->x_gui.x_obj, glist);\n    *yp1 = text_ypix(&x->x_gui.x_obj, glist);\n    *xp2 = *xp1 + x->x_gui.x_w;\n    *yp2 = *yp1 + x->x_gui.x_h;\n}\n\nstatic void my_numbox_save(t_gobj *z, t_binbuf *b)\n{\n    t_my_numbox *x = (t_my_numbox *)z;\n    t_symbol *bflcol[3];\n    t_symbol *srl[3];\n\n    iemgui_save(&x->x_gui, srl, bflcol);\n    if(x->x_gui.x_fsf.x_change)\n    {\n        x->x_gui.x_fsf.x_change = 0;\n        sys_queuegui(x, x->x_gui.x_glist, my_numbox_draw_update);\n    }\n    binbuf_addv(b, \"ssiisiiffiisssiiiisssfi\", gensym(\"#X\"), gensym(\"obj\"),\n                (int)x->x_gui.x_obj.te_xpix, (int)x->x_gui.x_obj.te_ypix,\n                gensym(\"nbx\"), x->x_numwidth, x->x_gui.x_h/IEMGUI_ZOOM(x),\n                (t_float)x->x_min, (t_float)x->x_max,\n                x->x_lin0_log1, iem_symargstoint(&x->x_gui.x_isa),\n                srl[0], srl[1], srl[2],\n                x->x_gui.x_ldx, x->x_gui.x_ldy,\n                iem_fstyletoint(&x->x_gui.x_fsf), x->x_gui.x_fontsize,\n                bflcol[0], bflcol[1], bflcol[2],\n                x->x_gui.x_isa.x_loadinit?x->x_val:0., x->x_log_height);\n    binbuf_addv(b, \";\");\n}\n\nint my_numbox_check_minmax(t_my_numbox *x, double min, double max)\n{\n    int ret = 0;\n\n    if(x->x_lin0_log1)\n    {\n        if((min == 0.0) && (max == 0.0))\n            max = 1.0;\n        if(max > 0.0)\n        {\n            if(min <= 0.0)\n                min = 0.01 * max;\n        }\n        else\n        {\n            if(min > 0.0)\n                max = 0.01 * min;\n        }\n    }\n    x->x_min = min;\n    x->x_max = max;\n    if(x->x_val < x->x_min)\n    {\n        x->x_val = x->x_min;\n        ret = 1;\n    }\n    if(x->x_val > x->x_max)\n    {\n        x->x_val = x->x_max;\n        ret = 1;\n    }\n    if(x->x_lin0_log1)\n        x->x_k = exp(log(x->x_max/x->x_min) / (double)(x->x_log_height));\n    else\n        x->x_k = 1.0;\n    return(ret);\n}\n\nstatic void my_numbox_properties(t_gobj *z, t_glist *owner)\n{\n    t_my_numbox *x = (t_my_numbox *)z;\n\n    if(x->x_gui.x_fsf.x_change)\n    {\n        x->x_gui.x_fsf.x_change = 0;\n        sys_queuegui(x, x->x_gui.x_glist, my_numbox_draw_update);\n    }\n    iemgui_new_dialog(x, &x->x_gui, \"nbx\",\n                      x->x_numwidth, MINDIGITS,\n                      x->x_gui.x_h/IEMGUI_ZOOM(x), IEM_GUI_MINSIZE,\n                      x->x_min, x->x_max,\n                      0,\n                      x->x_lin0_log1, \"linear\", \"logarithmic\",\n                      1, -1, x->x_log_height);\n}\n\nstatic void my_numbox_bang(t_my_numbox *x)\n{\n    outlet_float(x->x_gui.x_obj.ob_outlet, x->x_val);\n    if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)\n        pd_float(x->x_gui.x_snd->s_thing, x->x_val);\n}\n\nstatic void my_numbox_dialog(t_my_numbox *x, t_symbol *s, int argc,\n    t_atom *argv)\n{\n    t_symbol *srl[3];\n    int w = (int)atom_getfloatarg(0, argc, argv);\n    int h = (int)atom_getfloatarg(1, argc, argv);\n    double min = (double)atom_getfloatarg(2, argc, argv);\n    double max = (double)atom_getfloatarg(3, argc, argv);\n    int lilo = (int)atom_getfloatarg(4, argc, argv);\n    int log_height = (int)atom_getfloatarg(6, argc, argv);\n    int sr_flags;\n    t_atom undo[18];\n    iemgui_setdialogatoms(&x->x_gui, 18, undo);\n    SETFLOAT(undo+0, x->x_numwidth);\n    SETFLOAT(undo+2, x->x_min);\n    SETFLOAT(undo+3, x->x_max);\n    SETFLOAT(undo+4, x->x_lin0_log1);\n    SETFLOAT(undo+6, x->x_log_height);\n\n    pd_undo_set_objectstate(x->x_gui.x_glist, (t_pd*)x, gensym(\"dialog\"),\n                            18, undo,\n                            argc, argv);\n\n    if(lilo != 0) lilo = 1;\n    x->x_lin0_log1 = lilo;\n    sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv);\n    if(w < MINDIGITS)\n        w = MINDIGITS;\n    x->x_numwidth = w;\n    if(h < IEM_GUI_MINSIZE)\n        h = IEM_GUI_MINSIZE;\n    x->x_gui.x_h = h * IEMGUI_ZOOM(x);\n    if(log_height < 10)\n        log_height = 10;\n    x->x_log_height = log_height;\n    my_numbox_calc_fontwidth(x);\n    /*if(my_numbox_check_minmax(x, min, max))\n     my_numbox_bang(x);*/\n    my_numbox_check_minmax(x, min, max);\n    (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);\n    iemgui_size(x, &x->x_gui);\n}\n\nstatic void my_numbox_motion(t_my_numbox *x, t_floatarg dx, t_floatarg dy,\n    t_floatarg up)\n{\n    double k2 = 1.0;\n    if (up != 0)\n        return;\n\n    if(x->x_gui.x_fsf.x_finemoved)\n        k2 = 0.01;\n    if(x->x_lin0_log1)\n        x->x_val *= pow(x->x_k, -k2*dy);\n    else\n        x->x_val -= k2*dy;\n    my_numbox_clip(x);\n    sys_queuegui(x, x->x_gui.x_glist, my_numbox_draw_update);\n    my_numbox_bang(x);\n}\n\nstatic void my_numbox_click(t_my_numbox *x, t_floatarg xpos, t_floatarg ypos,\n                            t_floatarg shift, t_floatarg ctrl, t_floatarg alt)\n{\n    glist_grab(x->x_gui.x_glist, &x->x_gui.x_obj.te_g,\n        (t_glistmotionfn)my_numbox_motion, my_numbox_key, xpos, ypos);\n}\n\nstatic int my_numbox_newclick(t_gobj *z, struct _glist *glist,\n    int xpix, int ypix, int shift, int alt, int dbl, int doit)\n{\n    t_my_numbox* x = (t_my_numbox *)z;\n\n    if(doit)\n    {\n        my_numbox_click( x, (t_floatarg)xpix, (t_floatarg)ypix,\n            (t_floatarg)shift, 0, (t_floatarg)alt);\n        if(shift)\n            x->x_gui.x_fsf.x_finemoved = 1;\n        else\n            x->x_gui.x_fsf.x_finemoved = 0;\n        if(!x->x_gui.x_fsf.x_change)\n        {\n            clock_delay(x->x_clock_wait, 50);\n            x->x_gui.x_fsf.x_change = 1;\n            x->x_buf[0] = 0;\n        }\n        else\n        {\n            x->x_gui.x_fsf.x_change = 0;\n            x->x_buf[0] = 0;\n            sys_queuegui(x, x->x_gui.x_glist, my_numbox_draw_update);\n        }\n    }\n    return (1);\n}\n\nstatic void my_numbox_set(t_my_numbox *x, t_floatarg f)\n{\n    t_float ftocompare = f;\n        /* bitwise comparison, suggested by Dan Borstein - to make this work\n        ftocompare must be t_float type like x_val. */\n    if (memcmp(&ftocompare, &x->x_val, sizeof(ftocompare)))\n    {\n        x->x_val = ftocompare;\n        if (pd_compatibilitylevel < 53)\n            my_numbox_clip(x);\n        sys_queuegui(x, x->x_gui.x_glist, my_numbox_draw_update);\n    }\n}\n\nstatic void my_numbox_log_height(t_my_numbox *x, t_floatarg lh)\n{\n    if(lh < 10.0)\n        lh = 10.0;\n    x->x_log_height = (int)lh;\n    if(x->x_lin0_log1)\n        x->x_k = exp(log(x->x_max/x->x_min)/(double)(x->x_log_height));\n    else\n        x->x_k = 1.0;\n}\n\nstatic void my_numbox_float(t_my_numbox *x, t_floatarg f)\n{\n    my_numbox_set(x, f);\n    if(x->x_gui.x_fsf.x_put_in2out)\n        my_numbox_bang(x);\n}\n\nstatic void my_numbox_size(t_my_numbox *x, t_symbol *s, int ac, t_atom *av)\n{\n    int h, w;\n\n    w = (int)atom_getfloatarg(0, ac, av);\n    if(w < MINDIGITS)\n        w = MINDIGITS;\n    x->x_numwidth = w;\n    if(ac > 1)\n    {\n        h = (int)atom_getfloatarg(1, ac, av);\n        if(h < IEM_GUI_MINSIZE)\n            h = IEM_GUI_MINSIZE;\n        x->x_gui.x_h = h * IEMGUI_ZOOM(x);\n    }\n    my_numbox_calc_fontwidth(x);\n    iemgui_size((void *)x, &x->x_gui);\n    (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);\n}\n\nstatic void my_numbox_delta(t_my_numbox *x, t_symbol *s, int ac, t_atom *av)\n{iemgui_delta((void *)x, &x->x_gui, s, ac, av);}\n\nstatic void my_numbox_pos(t_my_numbox *x, t_symbol *s, int ac, t_atom *av)\n{iemgui_pos((void *)x, &x->x_gui, s, ac, av);}\n\nstatic void my_numbox_range(t_my_numbox *x, t_symbol *s, int ac, t_atom *av)\n{\n    if(my_numbox_check_minmax(x, (double)atom_getfloatarg(0, ac, av),\n                                 (double)atom_getfloatarg(1, ac, av)))\n    {\n        sys_queuegui(x, x->x_gui.x_glist, my_numbox_draw_update);\n        /*my_numbox_bang(x);*/\n    }\n}\n\nstatic void my_numbox_color(t_my_numbox *x, t_symbol *s, int ac, t_atom *av)\n{iemgui_color((void *)x, &x->x_gui, s, ac, av);}\n\nstatic void my_numbox_send(t_my_numbox *x, t_symbol *s)\n{iemgui_send(x, &x->x_gui, s);}\n\nstatic void my_numbox_receive(t_my_numbox *x, t_symbol *s)\n{iemgui_receive(x, &x->x_gui, s);}\n\nstatic void my_numbox_label(t_my_numbox *x, t_symbol *s)\n{iemgui_label((void *)x, &x->x_gui, s);}\n\nstatic void my_numbox_label_pos(t_my_numbox *x, t_symbol *s, int ac, t_atom *av)\n{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);}\n\nstatic void my_numbox_label_font(t_my_numbox *x,\n    t_symbol *s, int ac, t_atom *av)\n{\n    int f = (int)atom_getfloatarg(1, ac, av);\n\n    if(f < 4)\n        f = 4;\n    x->x_gui.x_fontsize = f;\n    f = (int)atom_getfloatarg(0, ac, av);\n    if((f < 0) || (f > 2))\n        f = 0;\n    x->x_gui.x_fsf.x_font_style = f;\n    my_numbox_calc_fontwidth(x);\n    iemgui_label_font((void *)x, &x->x_gui, s, ac, av);\n}\n\nstatic void my_numbox_log(t_my_numbox *x)\n{\n    x->x_lin0_log1 = 1;\n    if(my_numbox_check_minmax(x, x->x_min, x->x_max))\n    {\n        sys_queuegui(x, x->x_gui.x_glist, my_numbox_draw_update);\n        /*my_numbox_bang(x);*/\n    }\n}\n\nstatic void my_numbox_lin(t_my_numbox *x)\n{\n    x->x_lin0_log1 = 0;\n}\n\nstatic void my_numbox_init(t_my_numbox *x, t_floatarg f)\n{\n    x->x_gui.x_isa.x_loadinit = (f == 0.0) ? 0 : 1;\n}\n\nstatic void my_numbox_loadbang(t_my_numbox *x, t_floatarg action)\n{\n    if(action == LB_LOAD && x->x_gui.x_isa.x_loadinit)\n    {\n        sys_queuegui(x, x->x_gui.x_glist, my_numbox_draw_update);\n        my_numbox_bang(x);\n    }\n}\n\nstatic void my_numbox_key(void *z, t_symbol *keysym, t_floatarg fkey)\n{\n    t_my_numbox *x = z;\n    char c = fkey;\n    char buf[3];\n    buf[1] = 0;\n\n    if(c == 0)\n    {\n        x->x_gui.x_fsf.x_change = 0;\n        sys_queuegui(x, x->x_gui.x_glist, my_numbox_draw_update);\n        return;\n    }\n    if(((c >= '0') && (c <= '9')) || (c == '.') || (c == '-') ||\n        (c == 'e') || (c == '+') || (c == 'E'))\n    {\n        if(strlen(x->x_buf) < (IEMGUI_MAX_NUM_LEN-2))\n        {\n            buf[0] = c;\n            strcat(x->x_buf, buf);\n            sys_queuegui(x, x->x_gui.x_glist, my_numbox_draw_update);\n        }\n    }\n    else if((c == '\\b') || (c == 127))\n    {\n        int sl = (int)strlen(x->x_buf) - 1;\n\n        if(sl < 0)\n            sl = 0;\n        x->x_buf[sl] = 0;\n        sys_queuegui(x, x->x_gui.x_glist, my_numbox_draw_update);\n    }\n    else if((c == '\\n') || (c == 13))\n    {\n        if(x->x_buf[0])\n        {\n            x->x_val = atof(x->x_buf);\n            x->x_buf[0] = 0;\n            if (pd_compatibilitylevel < 53)\n                my_numbox_clip(x);\n            sys_queuegui(x, x->x_gui.x_glist, my_numbox_draw_update);\n        }\n        my_numbox_bang(x);\n    }\n}\n\nstatic void my_numbox_list(t_my_numbox *x, t_symbol *s, int ac, t_atom *av)\n{\n    if(!ac) {\n        my_numbox_bang(x);\n    }\n    else if(IS_A_FLOAT(av, 0))\n    {\n        my_numbox_set(x, atom_getfloatarg(0, ac, av));\n        my_numbox_bang(x);\n    }\n}\n\nstatic void *my_numbox_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_my_numbox *x = (t_my_numbox *)iemgui_new(my_numbox_class);\n    int w = 5, h = 14 * IEM_GUI_DEFAULTSIZE_SCALE;\n    int lilo = 0, ldx = 0, ldy = -8 * IEM_GUI_DEFAULTSIZE_SCALE;\n    int fs = x->x_gui.x_fontsize;\n    int log_height = 256;\n    double min = -1.0e+37, max = 1.0e+37, v = 0.0;\n\n    IEMGUI_SETDRAWFUNCTIONS(x, my_numbox);\n\n    if((argc >= 17)&&IS_A_FLOAT(argv,0)&&IS_A_FLOAT(argv,1)\n       &&IS_A_FLOAT(argv,2)&&IS_A_FLOAT(argv,3)\n       &&IS_A_FLOAT(argv,4)&&IS_A_FLOAT(argv,5)\n       &&(IS_A_SYMBOL(argv,6)||IS_A_FLOAT(argv,6))\n       &&(IS_A_SYMBOL(argv,7)||IS_A_FLOAT(argv,7))\n       &&(IS_A_SYMBOL(argv,8)||IS_A_FLOAT(argv,8))\n       &&IS_A_FLOAT(argv,9)&&IS_A_FLOAT(argv,10)\n       &&IS_A_FLOAT(argv,11)&&IS_A_FLOAT(argv,12)&&IS_A_FLOAT(argv,16))\n    {\n        w = (int)atom_getfloatarg(0, argc, argv);\n        h = (int)atom_getfloatarg(1, argc, argv);\n        min = (double)atom_getfloatarg(2, argc, argv);\n        max = (double)atom_getfloatarg(3, argc, argv);\n        lilo = (int)atom_getfloatarg(4, argc, argv);\n        iem_inttosymargs(&x->x_gui.x_isa, atom_getfloatarg(5, argc, argv));\n        iemgui_new_getnames(&x->x_gui, 6, argv);\n        ldx = (int)atom_getfloatarg(9, argc, argv);\n        ldy = (int)atom_getfloatarg(10, argc, argv);\n        iem_inttofstyle(&x->x_gui.x_fsf, atom_getfloatarg(11, argc, argv));\n        fs = (int)atom_getfloatarg(12, argc, argv);\n        iemgui_all_loadcolors(&x->x_gui, argv+13, argv+14, argv+15);\n        v = atom_getfloatarg(16, argc, argv);\n    }\n    else iemgui_new_getnames(&x->x_gui, 6, 0);\n    if((argc == 18)&&IS_A_FLOAT(argv,17))\n    {\n        log_height = (int)atom_getfloatarg(17, argc, argv);\n    }\n    x->x_gui.x_fsf.x_snd_able = (0 != x->x_gui.x_snd);\n    x->x_gui.x_fsf.x_rcv_able = (0 != x->x_gui.x_rcv);\n    if(x->x_gui.x_isa.x_loadinit)\n        x->x_val = v;\n    else\n        x->x_val = 0.0;\n    if(lilo != 0) lilo = 1;\n    x->x_lin0_log1 = lilo;\n    if(log_height < 10)\n        log_height = 10;\n    x->x_log_height = log_height;\n    if(x->x_gui.x_fsf.x_font_style == 1) strcpy(x->x_gui.x_font, \"helvetica\");\n    else if(x->x_gui.x_fsf.x_font_style == 2) strcpy(x->x_gui.x_font, \"times\");\n    else { x->x_gui.x_fsf.x_font_style = 0;\n        strcpy(x->x_gui.x_font, sys_font); }\n    if(x->x_gui.x_fsf.x_rcv_able)\n        pd_bind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv);\n    x->x_gui.x_ldx = ldx;\n    x->x_gui.x_ldy = ldy;\n    x->x_gui.x_fontsize = (fs < MINFONT)?MINFONT:fs;\n    if(w < MINDIGITS)\n        w = MINDIGITS;\n    x->x_numwidth = w;\n    if(h < IEM_GUI_MINSIZE)\n        h = IEM_GUI_MINSIZE;\n    x->x_gui.x_h = h;\n    x->x_buf[0] = 0;\n    my_numbox_check_minmax(x, min, max);\n    iemgui_verify_snd_ne_rcv(&x->x_gui);\n    x->x_clock_wait = clock_new(x, (t_method)my_numbox_tick_wait);\n    x->x_gui.x_fsf.x_change = 0;\n    iemgui_newzoom(&x->x_gui);\n    my_numbox_calc_fontwidth(x);\n    outlet_new(&x->x_gui.x_obj, &s_float);\n    return (x);\n}\n\nstatic void my_numbox_free(t_my_numbox *x)\n{\n    if(x->x_gui.x_fsf.x_rcv_able)\n        pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv);\n    clock_free(x->x_clock_wait);\n    pdgui_stub_deleteforkey(x);\n}\n\nvoid g_numbox_setup(void)\n{\n    my_numbox_class = class_new(gensym(\"nbx\"), (t_newmethod)my_numbox_new,\n        (t_method)my_numbox_free, sizeof(t_my_numbox), 0, A_GIMME, 0);\n    class_addcreator((t_newmethod)my_numbox_new, gensym(\"my_numbox\"), A_GIMME, 0);\n    class_addbang(my_numbox_class, my_numbox_bang);\n    class_addfloat(my_numbox_class, my_numbox_float);\n    class_addlist(my_numbox_class, my_numbox_list);\n    class_addmethod(my_numbox_class, (t_method)my_numbox_click,\n        gensym(\"click\"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);\n    class_addmethod(my_numbox_class, (t_method)my_numbox_motion,\n        gensym(\"motion\"), A_FLOAT, A_FLOAT, A_DEFFLOAT, 0);\n    class_addmethod(my_numbox_class, (t_method)my_numbox_dialog,\n        gensym(\"dialog\"), A_GIMME, 0);\n    class_addmethod(my_numbox_class, (t_method)my_numbox_loadbang,\n        gensym(\"loadbang\"), A_DEFFLOAT, 0);\n    class_addmethod(my_numbox_class, (t_method)my_numbox_set,\n        gensym(\"set\"), A_FLOAT, 0);\n    class_addmethod(my_numbox_class, (t_method)my_numbox_size,\n        gensym(\"size\"), A_GIMME, 0);\n    class_addmethod(my_numbox_class, (t_method)my_numbox_delta,\n        gensym(\"delta\"), A_GIMME, 0);\n    class_addmethod(my_numbox_class, (t_method)my_numbox_pos,\n        gensym(\"pos\"), A_GIMME, 0);\n    class_addmethod(my_numbox_class, (t_method)my_numbox_range,\n        gensym(\"range\"), A_GIMME, 0);\n    class_addmethod(my_numbox_class, (t_method)my_numbox_color,\n        gensym(\"color\"), A_GIMME, 0);\n    class_addmethod(my_numbox_class, (t_method)my_numbox_send,\n        gensym(\"send\"), A_DEFSYM, 0);\n    class_addmethod(my_numbox_class, (t_method)my_numbox_receive,\n        gensym(\"receive\"), A_DEFSYM, 0);\n    class_addmethod(my_numbox_class, (t_method)my_numbox_label,\n        gensym(\"label\"), A_DEFSYM, 0);\n    class_addmethod(my_numbox_class, (t_method)my_numbox_label_pos,\n        gensym(\"label_pos\"), A_GIMME, 0);\n    class_addmethod(my_numbox_class, (t_method)my_numbox_label_font,\n        gensym(\"label_font\"), A_GIMME, 0);\n    class_addmethod(my_numbox_class, (t_method)my_numbox_log,\n        gensym(\"log\"), 0);\n    class_addmethod(my_numbox_class, (t_method)my_numbox_lin,\n        gensym(\"lin\"), 0);\n    class_addmethod(my_numbox_class, (t_method)my_numbox_init,\n        gensym(\"init\"), A_FLOAT, 0);\n    class_addmethod(my_numbox_class, (t_method)my_numbox_log_height,\n        gensym(\"log_height\"), A_FLOAT, 0);\n    class_addmethod(my_numbox_class, (t_method)iemgui_zoom,\n        gensym(\"zoom\"), A_CANT, 0);\n    my_numbox_widgetbehavior.w_getrectfn =    my_numbox_getrect;\n    my_numbox_widgetbehavior.w_displacefn =   iemgui_displace;\n    my_numbox_widgetbehavior.w_selectfn =     iemgui_select;\n    my_numbox_widgetbehavior.w_activatefn =   NULL;\n    my_numbox_widgetbehavior.w_deletefn =     iemgui_delete;\n    my_numbox_widgetbehavior.w_visfn =        iemgui_vis;\n    my_numbox_widgetbehavior.w_clickfn =      my_numbox_newclick;\n    class_setwidget(my_numbox_class, &my_numbox_widgetbehavior);\n    class_setsavefn(my_numbox_class, my_numbox_save);\n    class_setpropertiesfn(my_numbox_class, my_numbox_properties);\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/g_radio.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution. */\n\n/* [hv]dial.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */\n/* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */\n\n/* name change to [vv]radio by MSP (it's a radio button really) and changed to\n   put out a \"float\" as in sliders, toggles, etc. */\n\n#include <string.h>\n#include <stdio.h>\n#include \"m_pd.h\"\n\n#include \"g_all_guis.h\"\n\n/* ------------- hdl     gui-horizontal dial ---------------------- */\n\nt_widgetbehavior radio_widgetbehavior;\nstatic t_class *radio_class;\n\n/* widget helper functions */\n\n/* cannot use iemgui's default draw_iolets, because\n * - vradio would use show the outlet at the 0th button rather than the last...\n */\nstatic void radio_draw_io(t_radio* x, t_glist* glist, int old_snd_rcv_flags)\n{\n    const int zoom = IEMGUI_ZOOM(x);\n    int xpos = text_xpix(&x->x_gui.x_obj, glist);\n    int ypos = text_ypix(&x->x_gui.x_obj, glist);\n    int iow = IOWIDTH * zoom, ioh = IEM_GUI_IOHEIGHT * zoom;\n    t_canvas *canvas = glist_getcanvas(glist);\n    char tag_object[128], tag_but[128], tag[128];\n    char *tags[] = {tag_object, tag};\n\n    (void)old_snd_rcv_flags;\n\n    sprintf(tag_object, \"%pOBJ\", x);\n    sprintf(tag_but, \"%pBUT\", x);\n\n    sprintf(tag, \"%pOUT%d\", x, 0);\n    pdgui_vmess(0, \"crs\", canvas, \"delete\", tag);\n    if(!x->x_gui.x_fsf.x_snd_able)\n    {\n        int height = x->x_gui.x_h * ((x->x_orientation == horizontal)? 1: x->x_number);\n        pdgui_vmess(0, \"crr iiii rs rS\", canvas, \"create\", \"rectangle\",\n            xpos, ypos + height + zoom - ioh,\n            xpos + iow, ypos + height,\n            \"-fill\", \"black\",\n            \"-tags\", 2, tags);\n\n            /* keep buttons above outlet */\n        pdgui_vmess(0, \"crss\", canvas, \"lower\", tag, tag_but);\n    }\n\n    sprintf(tag, \"%pIN%d\", x, 0);\n    pdgui_vmess(0, \"crs\", canvas, \"delete\", tag);\n    if(!x->x_gui.x_fsf.x_rcv_able)\n    {\n        pdgui_vmess(0, \"crr iiii rs rS\", canvas, \"create\", \"rectangle\",\n            xpos, ypos,\n            xpos + iow, ypos - zoom + ioh,\n            \"-fill\", \"black\",\n            \"-tags\", 2, tags);\n\n            /* keep buttons above inlet */\n        pdgui_vmess(0, \"crss\", canvas, \"lower\", tag, tag_but);\n    }\n}\n\nstatic void radio_draw_config(t_radio* x, t_glist* glist)\n{\n    int i;\n    const int zoom = IEMGUI_ZOOM(x);\n    t_iemgui *iemgui = &x->x_gui;\n    t_canvas *canvas = glist_getcanvas(glist);\n    int iow = IOWIDTH * zoom, ioh = IEM_GUI_IOHEIGHT * zoom;\n    int xx11b = text_xpix(&x->x_gui.x_obj, glist);\n    int yy11b = text_ypix(&x->x_gui.x_obj, glist);\n    int d, dx = 0, dy = 0, d4;\n\n    int xx11=xx11b, xx12=0, xx21=0, xx22=0;\n    int yy11=yy11b, yy12=0, yy21=0, yy22=0;\n\n    char tag[128];\n    t_atom fontatoms[3];\n    SETSYMBOL(fontatoms+0, gensym(iemgui->x_font));\n    SETFLOAT (fontatoms+1, -iemgui->x_fontsize*zoom);\n    SETSYMBOL(fontatoms+2, gensym(sys_fontweight));\n\n    if(x->x_orientation == horizontal)\n    {\n        d = dx = x->x_gui.x_w;\n    } else {\n        d = dy = x->x_gui.x_h;\n    }\n    d4 = d / 4;\n    xx12 = xx11 + d;\n    xx21 = xx11 + d4;\n    xx22 = xx12 - d4;\n    yy12 = yy11 + d;\n    yy21 = yy11 + d4;\n    yy22 = yy12 - d4;\n\n    for(i = 0; i < x->x_number; i++)\n    {\n        int col = (x->x_on == i) ? x->x_gui.x_fcol : x->x_gui.x_bcol;\n        sprintf(tag, \"%pBASE%d\", x, i);\n        pdgui_vmess(0, \"crs iiii\", canvas, \"coords\", tag,\n            xx11, yy11, xx12, yy12);\n        pdgui_vmess(0, \"crs ri rk\", canvas, \"itemconfigure\", tag,\n            \"-width\", zoom, \"-fill\", x->x_gui.x_bcol);\n\n        sprintf(tag, \"%pBUT%d\", x, i);\n        pdgui_vmess(0, \"crs iiii\", canvas, \"coords\", tag,\n            xx21, yy21, xx22, yy22);\n        pdgui_vmess(0, \"crs rk rk\", canvas, \"itemconfigure\", tag,\n            \"-fill\", col, \"-outline\", col);\n        xx11 += dx; xx12 += dx; xx21 += dx; xx22 += dx;\n        yy11 += dy; yy12 += dy; yy21 += dy; yy22 += dy;\n\n        x->x_drawn = x->x_on;\n    }\n\n    sprintf(tag, \"%pLABEL\", x);\n    pdgui_vmess(0, \"crs ii\", canvas, \"coords\", tag,\n        xx11b + x->x_gui.x_ldx * zoom, yy11b + x->x_gui.x_ldy * zoom);\n    pdgui_vmess(0, \"crs rA rk\", canvas, \"itemconfigure\", tag,\n        \"-font\", 3, fontatoms,\n        \"-fill\", x->x_gui.x_lcol);\n    iemgui_dolabel(x, &x->x_gui, x->x_gui.x_lab, 1);\n}\n\nstatic void radio_draw_new(t_radio *x, t_glist *glist)\n{\n    t_canvas *canvas = glist_getcanvas(glist);\n    int i;\n    char tag_n[128], tag[128], tag_object[128];\n    char *tags[] = {tag_object, tag, tag_n, \"text\"};\n    sprintf(tag_object, \"%pOBJ\", x);\n\n    for(i=0; i<x->x_number; i++) {\n        sprintf(tag, \"%pBASE\", x);\n        sprintf(tag_n, \"%pBASE%d\", x, i);\n        pdgui_vmess(0, \"crr iiii rS\", canvas, \"create\", \"rectangle\",\n            0, 0, 0, 0, \"-tags\", 3, tags);\n\n        sprintf(tag, \"%pBUT\", x);\n        sprintf(tag_n, \"%pBUT%d\", x, i);\n        pdgui_vmess(0, \"crr iiii rS\", canvas, \"create\", \"rectangle\",\n            0, 0, 0, 0, \"-tags\", 3, tags);\n    }\n    /* make sure the buttons are above their base */\n    sprintf(tag, \"%pBUT\", x);\n    sprintf(tag_n, \"%pBASE\", x);\n    pdgui_vmess(0, \"crss\", canvas, \"raise\", tag, tag_n);\n\n    sprintf(tag, \"%pLABEL\", x);\n    sprintf(tag_n, \"label\");\n    pdgui_vmess(0, \"crr ii rs rS\", canvas, \"create\", \"text\",\n        0, 0, \"-anchor\", \"w\", \"-tags\", 4, tags);\n\n    radio_draw_config(x, glist);\n    (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_IO);\n}\n\nstatic void radio_draw_select(t_radio* x, t_glist* glist)\n{\n    int n = x->x_number, i;\n    t_canvas *canvas = glist_getcanvas(glist);\n    int lcol = x->x_gui.x_lcol;\n    int col = IEM_GUI_COLOR_NORMAL;\n    char tag[128];\n\n    if(x->x_gui.x_fsf.x_selected)\n        lcol = col =  IEM_GUI_COLOR_SELECTED;\n\n    sprintf(tag, \"%pBASE\", x);\n    pdgui_vmess(0, \"crs rk\", canvas, \"itemconfigure\", tag, \"-outline\", col);\n    sprintf(tag, \"%pLABEL\", x);\n    pdgui_vmess(0, \"crs rk\", canvas, \"itemconfigure\", tag, \"-fill\", lcol);\n}\n\nstatic void radio_draw_update(t_gobj *client, t_glist *glist)\n{\n    t_radio *x = (t_radio *)client;\n    if(glist_isvisible(glist))\n    {\n        t_canvas *canvas = glist_getcanvas(glist);\n        char tag[128];\n\n        sprintf(tag, \"%pBUT%d\", x, x->x_drawn);\n        pdgui_vmess(0, \"crs rk rk\", canvas, \"itemconfigure\", tag,\n            \"-fill\", x->x_gui.x_bcol,\n            \"-outline\", x->x_gui.x_bcol);\n\n        sprintf(tag, \"%pBUT%d\", x, x->x_on);\n        pdgui_vmess(0, \"crs rk rk\", canvas, \"itemconfigure\", tag,\n            \"-fill\", x->x_gui.x_fcol,\n            \"-outline\", x->x_gui.x_fcol);\n\n        x->x_drawn = x->x_on;\n    }\n}\n\n/* ------------------------ hdl widgetbehaviour----------------------------- */\n\nstatic void radio_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2)\n{\n    t_radio *x = (t_radio *)z;\n\n    *xp1 = text_xpix(&x->x_gui.x_obj, glist);\n    *yp1 = text_ypix(&x->x_gui.x_obj, glist);\n    if(x->x_orientation == horizontal)\n    {\n        *xp2 = *xp1 + x->x_gui.x_w * x->x_number;\n        *yp2 = *yp1 + x->x_gui.x_h;\n    } else {\n        *xp2 = *xp1 + x->x_gui.x_w;\n        *yp2 = *yp1 + x->x_gui.x_h * x->x_number;\n    }\n}\n\nstatic void radio_save(t_gobj *z, t_binbuf *b)\n{\n    t_radio *x = (t_radio *)z;\n    t_symbol *bflcol[3];\n    t_symbol *srl[3];\n\n    const char*objname;\n    if(x->x_orientation == horizontal)\n    {\n        if(x->x_compat)\n            objname=\"hdl\";\n        else\n            objname=\"hradio\";\n    } else {\n        if(x->x_compat)\n            objname=\"vdl\";\n        else\n            objname=\"vradio\";\n    }\n\n    iemgui_save(&x->x_gui, srl, bflcol);\n    binbuf_addv(b, \"ssiisiiiisssiiiisssf\", gensym(\"#X\"), gensym(\"obj\"),\n                (int)x->x_gui.x_obj.te_xpix,\n                (int)x->x_gui.x_obj.te_ypix,\n        gensym(objname),\n                x->x_gui.x_w/IEMGUI_ZOOM(x),\n                x->x_change, iem_symargstoint(&x->x_gui.x_isa), x->x_number,\n                srl[0], srl[1], srl[2],\n                x->x_gui.x_ldx, x->x_gui.x_ldy,\n                iem_fstyletoint(&x->x_gui.x_fsf), x->x_gui.x_fontsize,\n                bflcol[0], bflcol[1], bflcol[2],\n                x->x_gui.x_isa.x_loadinit?x->x_fval:0.);\n    binbuf_addv(b, \";\");\n}\n\nstatic void radio_properties(t_gobj *z, t_glist *owner)\n{\n    t_radio *x = (t_radio *)z;\n    int hchange = -1;\n    const char*objname;\n\n    if(x->x_orientation == horizontal)\n    {\n        objname = \"hradio\";\n    } else {\n        objname = \"vradio\";\n    }\n    if(x->x_compat)\n        hchange = x->x_change;\n\n    iemgui_new_dialog(x, &x->x_gui, objname,\n        x->x_gui.x_w/IEMGUI_ZOOM(x), IEM_GUI_MINSIZE,\n        0, 0,\n        0, 0,\n        0,\n        hchange, \"new-only\", \"new&old\",\n        1, -1, x->x_number);\n}\n\nstatic void radio_dialog(t_radio *x, t_symbol *s, int argc, t_atom *argv)\n{\n    t_symbol *srl[3];\n    int a = (int)atom_getfloatarg(0, argc, argv);\n    int chg = (int)atom_getfloatarg(4, argc, argv);\n    int num = (int)atom_getfloatarg(6, argc, argv);\n    int sr_flags;\n    int redraw = 0;\n    t_atom undo[18];\n    iemgui_setdialogatoms(&x->x_gui, 18, undo);\n    SETFLOAT(undo+1, 0);\n    SETFLOAT(undo+2, 0);\n    SETFLOAT(undo+3, 0);\n    SETFLOAT(undo+4, (x->x_compat)?x->x_change:-1);\n    SETFLOAT(undo+6, x->x_number);\n\n    pd_undo_set_objectstate(x->x_gui.x_glist, (t_pd*)x, gensym(\"dialog\"),\n                            18, undo,\n                            argc, argv);\n\n    if(chg != 0) chg = 1;\n    x->x_change = chg;\n    sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv);\n    x->x_gui.x_w = iemgui_clip_size(a) * IEMGUI_ZOOM(x);\n    x->x_gui.x_h = x->x_gui.x_w;\n    if (num != x->x_number && glist_isvisible(x->x_gui.x_glist))\n    {\n        /* we need to recreate the buttons */\n        (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_ERASE);\n        redraw = 1;\n    }\n    x->x_number = num;\n    if(x->x_on >= x->x_number)\n    {\n        x->x_on_old = x->x_on = x->x_number - 1;\n        x->x_on_old = x->x_on;\n    }\n\n    if (redraw && gobj_shouldvis((t_gobj *)x, x->x_gui.x_glist))\n    {\n        (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_NEW);\n        canvas_fixlinesfor(x->x_gui.x_glist, (t_text*)x);\n    } else {\n        /* just reconfigure */\n        iemgui_size((void *)x, &x->x_gui);\n    }\n}\n\nstatic void radio_set(t_radio *x, t_floatarg f)\n{\n    int i = (int)f;\n\n    x->x_fval = f;\n    if(i < 0)\n        i = 0;\n    if(i >= x->x_number)\n        i = x->x_number - 1;\n    if(x->x_on != x->x_on_old)\n    {\n        int old = x->x_on_old;\n        x->x_on_old = x->x_on;\n        x->x_on = i;\n        (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);\n        x->x_on_old = old;\n    }\n    else\n    {\n        x->x_on = i;\n        (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);\n    }\n}\n\nstatic void radio_bang(t_radio *x)\n{\n    if(x->x_compat)\n    {\n            /* compatibility with earlier \"[hv]dial\" behavior */\n        t_atom at[2];\n        if((x->x_change) && (x->x_on != x->x_on_old))\n        {\n            SETFLOAT(at+0, (t_float)x->x_on_old);\n            SETFLOAT(at+1, 0.0);\n            outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, at);\n            if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)\n                pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, at);\n        }\n        x->x_on_old = x->x_on;\n        SETFLOAT(at+0, (t_float)x->x_on);\n        SETFLOAT(at+1, 1.0);\n        outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, at);\n        if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)\n            pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, at);\n    }\n    else\n    {\n        t_float outval = (pd_compatibilitylevel < 46 ? x->x_on : x->x_fval);\n        outlet_float(x->x_gui.x_obj.ob_outlet, outval);\n        if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)\n            pd_float(x->x_gui.x_snd->s_thing, outval);\n    }\n}\n\nstatic void radio_fout(t_radio *x, t_floatarg f)\n{\n    int i = (int)f;\n\n    x->x_fval = f;\n    if(i < 0)\n        i = 0;\n    if(i >= x->x_number)\n        i = x->x_number - 1;\n\n    if(x->x_compat)\n    {\n            /* compatibility with earlier \"[hv]dial\" behavior */\n        t_atom at[2];\n        if((x->x_change) && (i != x->x_on_old))\n        {\n            SETFLOAT(at+0, (t_float)x->x_on_old);\n            SETFLOAT(at+1, 0.0);\n            outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, at);\n            if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)\n                pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, at);\n        }\n        if(x->x_on != x->x_on_old)\n            x->x_on_old = x->x_on;\n        x->x_on = i;\n        (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);\n        x->x_on_old = x->x_on;\n        SETFLOAT(at+0, (t_float)x->x_on);\n        SETFLOAT(at+1, 1.0);\n        outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, at);\n        if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)\n            pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, at);\n    }\n    else\n    {\n        t_float outval = (pd_compatibilitylevel < 46 ? i : x->x_fval);\n        x->x_on_old = x->x_on;\n        x->x_on = i;\n        (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);\n        outlet_float(x->x_gui.x_obj.ob_outlet, outval);\n        if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)\n            pd_float(x->x_gui.x_snd->s_thing, outval);\n    }\n}\n\nstatic void radio_float(t_radio *x, t_floatarg f)\n{\n    int i = (int)f;\n    x->x_fval = f;\n    if(i < 0)\n        i = 0;\n    if(i >= x->x_number)\n        i = x->x_number - 1;\n\n    if(x->x_compat)\n    {\n        t_atom at[2];\n            /* compatibility with earlier \"[hv]dial\" behavior */\n        if((x->x_change) && (i != x->x_on_old))\n        {\n            if(x->x_gui.x_fsf.x_put_in2out)\n            {\n                SETFLOAT(at+0, (t_float)x->x_on_old);\n                SETFLOAT(at+1, 0.0);\n                outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, at);\n                if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)\n                    pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, at);\n            }\n        }\n        if(x->x_on != x->x_on_old)\n            x->x_on_old = x->x_on;\n        x->x_on = i;\n        (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);\n        x->x_on_old = x->x_on;\n        if(x->x_gui.x_fsf.x_put_in2out)\n        {\n            SETFLOAT(at+0, (t_float)x->x_on);\n            SETFLOAT(at+1, 1.0);\n            outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, at);\n            if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)\n                pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, at);\n        }\n    }\n    else\n    {\n        t_float outval = (pd_compatibilitylevel < 46 ? i : x->x_fval);\n        x->x_on_old = x->x_on;\n        x->x_on = i;\n        (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);\n        if(x->x_gui.x_fsf.x_put_in2out)\n        {\n            outlet_float(x->x_gui.x_obj.ob_outlet, outval);\n            if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)\n                pd_float(x->x_gui.x_snd->s_thing, outval);\n        }\n    }\n}\n\nstatic void radio_click(t_radio *x, t_floatarg xpos, t_floatarg ypos, t_floatarg shift, t_floatarg ctrl, t_floatarg alt)\n{\n    int selected = 0.;\n    if (x->x_orientation == horizontal)\n    {\n        int xx = (int)xpos - (int)text_xpix(&x->x_gui.x_obj, x->x_gui.x_glist);\n        selected = xx / x->x_gui.x_w;\n    } else {\n        int yy =  (int)ypos - text_ypix(&x->x_gui.x_obj, x->x_gui.x_glist);\n        selected = yy / x->x_gui.x_h;\n    }\n    if(selected >= x->x_number)\n        selected = x->x_number-1;\n    if(selected < 0)\n        selected = 0;\n    radio_fout(x, (t_float)selected);\n}\n\nstatic int radio_newclick(t_gobj *z, struct _glist *glist, int xpix, int ypix, int shift, int alt, int dbl, int doit)\n{\n    if(doit)\n        radio_click((t_radio *)z, (t_floatarg)xpix, (t_floatarg)ypix, (t_floatarg)shift, 0, (t_floatarg)alt);\n    return (1);\n}\n\nstatic void radio_loadbang(t_radio *x, t_floatarg action)\n{\n    if(action == LB_LOAD && x->x_gui.x_isa.x_loadinit)\n        radio_bang(x);\n}\n\nstatic void radio_number(t_radio *x, t_floatarg num)\n{\n    int n = (int)num;\n\n    if(n < 1)\n        n = 1;\n    if(n > IEM_RADIO_MAX)\n        n = IEM_RADIO_MAX;\n    if(n != x->x_number)\n    {\n        int vis = glist_isvisible(x->x_gui.x_glist);\n        if(vis)\n            (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_ERASE);\n        x->x_number = n;\n        if(x->x_on >= x->x_number)\n            x->x_on = x->x_number - 1;\n        x->x_on_old = x->x_on;\n        if(vis && gobj_shouldvis((t_gobj *)x, x->x_gui.x_glist))\n        {\n            (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_NEW);\n            canvas_fixlinesfor(x->x_gui.x_glist, (t_text*)x);\n        }\n    }\n}\n\nstatic void radio_orientation(t_radio *x, t_floatarg forient)\n{\n    x->x_orientation = !!(int)forient;\n    iemgui_size(x, &x->x_gui);\n}\n\nstatic void radio_size(t_radio *x, t_symbol *s, int ac, t_atom *av)\n{\n    x->x_gui.x_w = iemgui_clip_size((int)atom_getfloatarg(0, ac, av)) * IEMGUI_ZOOM(x);\n    x->x_gui.x_h = x->x_gui.x_w;\n    iemgui_size((void *)x, &x->x_gui);\n}\n\nstatic void radio_delta(t_radio *x, t_symbol *s, int ac, t_atom *av)\n{iemgui_delta((void *)x, &x->x_gui, s, ac, av);}\n\nstatic void radio_pos(t_radio *x, t_symbol *s, int ac, t_atom *av)\n{iemgui_pos((void *)x, &x->x_gui, s, ac, av);}\n\nstatic void radio_color(t_radio *x, t_symbol *s, int ac, t_atom *av)\n{iemgui_color((void *)x, &x->x_gui, s, ac, av);}\n\nstatic void radio_send(t_radio *x, t_symbol *s)\n{iemgui_send(x, &x->x_gui, s);}\n\nstatic void radio_receive(t_radio *x, t_symbol *s)\n{iemgui_receive(x, &x->x_gui, s);}\n\nstatic void radio_label(t_radio *x, t_symbol *s)\n{iemgui_label((void *)x, &x->x_gui, s);}\n\nstatic void radio_label_pos(t_radio *x, t_symbol *s, int ac, t_atom *av)\n{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);}\n\nstatic void radio_label_font(t_radio *x, t_symbol *s, int ac, t_atom *av)\n{iemgui_label_font((void *)x, &x->x_gui, s, ac, av);}\n\nstatic void radio_init(t_radio *x, t_floatarg f)\n{x->x_gui.x_isa.x_loadinit = (f == 0.0) ? 0 : 1;}\n\nstatic void radio_double_change(t_radio *x)\n{\n    if(x->x_compat)\n        x->x_change = 1;\n    else\n        pd_error(x, \"radio: no method for 'double_change'\");\n}\n\nstatic void radio_single_change(t_radio *x)\n{\n    if(x->x_compat)\n        x->x_change = 0;\n    else\n        pd_error(x, \"radio: no method for 'single_change'\");\n}\n\nstatic void *radio_donew(t_symbol *s, int argc, t_atom *argv, int old)\n{\n    t_radio *x = (t_radio *)iemgui_new(radio_class);\n    int a = IEM_GUI_DEFAULTSIZE, on = 0;\n    int ldx = 0, ldy = -8 * IEM_GUI_DEFAULTSIZE_SCALE, chg = 1, num = 8;\n    int fs = x->x_gui.x_fontsize;\n    t_float fval = 0;\n    if('v' == *s->s_name)\n        x->x_orientation = vertical;\n    x->x_compat = old;\n\n    IEMGUI_SETDRAWFUNCTIONS(x, radio);\n\n    if((argc == 15)&&IS_A_FLOAT(argv,0)&&IS_A_FLOAT(argv,1)&&IS_A_FLOAT(argv,2)\n       &&IS_A_FLOAT(argv,3)\n       &&(IS_A_SYMBOL(argv,4)||IS_A_FLOAT(argv,4))\n       &&(IS_A_SYMBOL(argv,5)||IS_A_FLOAT(argv,5))\n       &&(IS_A_SYMBOL(argv,6)||IS_A_FLOAT(argv,6))\n       &&IS_A_FLOAT(argv,7)&&IS_A_FLOAT(argv,8)\n       &&IS_A_FLOAT(argv,9)&&IS_A_FLOAT(argv,10)&&IS_A_FLOAT(argv,14))\n    {\n        a = (int)atom_getfloatarg(0, argc, argv);\n        chg = (int)atom_getfloatarg(1, argc, argv);\n        iem_inttosymargs(&x->x_gui.x_isa, atom_getfloatarg(2, argc, argv));\n        num = (int)atom_getfloatarg(3, argc, argv);\n        iemgui_new_getnames(&x->x_gui, 4, argv);\n        ldx = (int)atom_getfloatarg(7, argc, argv);\n        ldy = (int)atom_getfloatarg(8, argc, argv);\n        iem_inttofstyle(&x->x_gui.x_fsf, atom_getfloatarg(9, argc, argv));\n        fs = (int)atom_getfloatarg(10, argc, argv);\n        iemgui_all_loadcolors(&x->x_gui, argv+11, argv+12, argv+13);\n        fval = atom_getfloatarg(14, argc, argv);\n    }\n    else iemgui_new_getnames(&x->x_gui, 4, 0);\n    x->x_gui.x_fsf.x_snd_able = (0 != x->x_gui.x_snd);\n    x->x_gui.x_fsf.x_rcv_able = (0 != x->x_gui.x_rcv);\n    if(x->x_gui.x_fsf.x_font_style == 1) strcpy(x->x_gui.x_font, \"helvetica\");\n    else if(x->x_gui.x_fsf.x_font_style == 2) strcpy(x->x_gui.x_font, \"times\");\n    else { x->x_gui.x_fsf.x_font_style = 0;\n        strcpy(x->x_gui.x_font, sys_font); }\n    if(num < 1)\n        num = 1;\n    if(num > IEM_RADIO_MAX)\n        num = IEM_RADIO_MAX;\n    x->x_number = num;\n    x->x_fval = fval;\n    on = fval;\n    if(on < 0)\n        on = 0;\n    if(on >= x->x_number)\n        on = x->x_number - 1;\n    if(x->x_gui.x_isa.x_loadinit)\n        x->x_on = on;\n    else\n        x->x_on = 0;\n    x->x_on_old = x->x_on;\n    x->x_change = (chg == 0) ? 0 : 1;\n    if(x->x_gui.x_fsf.x_rcv_able)\n        pd_bind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv);\n    x->x_gui.x_ldx = ldx;\n    x->x_gui.x_ldy = ldy;\n    x->x_gui.x_fontsize = (fs < 4)?4:fs;\n    x->x_gui.x_w = iemgui_clip_size(a);\n    x->x_gui.x_h = x->x_gui.x_w;\n    iemgui_verify_snd_ne_rcv(&x->x_gui);\n    iemgui_newzoom(&x->x_gui);\n    outlet_new(&x->x_gui.x_obj, &s_list);\n    return (x);\n}\n\nstatic void *radio_new(t_symbol *s, int argc, t_atom *argv)\n{\n    return (radio_donew(s, argc, argv, 0));\n}\n\nstatic void *dial_new(t_symbol *s, int argc, t_atom *argv)\n{\n    return (radio_donew(s, argc, argv, 1));\n}\n\nstatic void radio_free(t_radio *x)\n{\n    if(x->x_gui.x_fsf.x_rcv_able)\n        pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv);\n    pdgui_stub_deleteforkey(x);\n}\n\nvoid g_radio_setup(void)\n{\n    radio_class = class_new(gensym(\"hradio\"), (t_newmethod)radio_new,\n        (t_method)radio_free, sizeof(t_radio), 0, A_GIMME, 0);\n    class_addcreator((t_newmethod)radio_new, gensym(\"vradio\"), A_GIMME, 0);\n\n    class_addcreator((t_newmethod)radio_new, gensym(\"rdb\"), A_GIMME, 0);\n    class_addcreator((t_newmethod)radio_new, gensym(\"radiobut\"), A_GIMME, 0);\n    class_addcreator((t_newmethod)radio_new, gensym(\"radiobutton\"), A_GIMME, 0);\n\n    class_addbang(radio_class, radio_bang);\n    class_addfloat(radio_class, radio_float);\n    class_addmethod(radio_class, (t_method)radio_click,\n        gensym(\"click\"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);\n    class_addmethod(radio_class, (t_method)radio_dialog,\n        gensym(\"dialog\"), A_GIMME, 0);\n    class_addmethod(radio_class, (t_method)radio_loadbang,\n        gensym(\"loadbang\"), A_DEFFLOAT, 0);\n    class_addmethod(radio_class, (t_method)radio_set,\n        gensym(\"set\"), A_FLOAT, 0);\n    class_addmethod(radio_class, (t_method)radio_size,\n        gensym(\"size\"), A_GIMME, 0);\n    class_addmethod(radio_class, (t_method)radio_delta,\n        gensym(\"delta\"), A_GIMME, 0);\n    class_addmethod(radio_class, (t_method)radio_pos,\n        gensym(\"pos\"), A_GIMME, 0);\n    class_addmethod(radio_class, (t_method)radio_color,\n        gensym(\"color\"), A_GIMME, 0);\n    class_addmethod(radio_class, (t_method)radio_send,\n        gensym(\"send\"), A_DEFSYM, 0);\n    class_addmethod(radio_class, (t_method)radio_receive,\n        gensym(\"receive\"), A_DEFSYM, 0);\n    class_addmethod(radio_class, (t_method)radio_label,\n        gensym(\"label\"), A_DEFSYM, 0);\n    class_addmethod(radio_class, (t_method)radio_label_pos,\n        gensym(\"label_pos\"), A_GIMME, 0);\n    class_addmethod(radio_class, (t_method)radio_label_font,\n        gensym(\"label_font\"), A_GIMME, 0);\n    class_addmethod(radio_class, (t_method)radio_init,\n        gensym(\"init\"), A_FLOAT, 0);\n    class_addmethod(radio_class, (t_method)radio_number,\n        gensym(\"number\"), A_FLOAT, 0);\n    class_addmethod(radio_class, (t_method)radio_orientation,\n        gensym(\"orientation\"), A_FLOAT, 0);\n    class_addmethod(radio_class, (t_method)iemgui_zoom,\n        gensym(\"zoom\"), A_CANT, 0);\n    radio_widgetbehavior.w_getrectfn = radio_getrect;\n    radio_widgetbehavior.w_displacefn = iemgui_displace;\n    radio_widgetbehavior.w_selectfn = iemgui_select;\n    radio_widgetbehavior.w_activatefn = NULL;\n    radio_widgetbehavior.w_deletefn = iemgui_delete;\n    radio_widgetbehavior.w_visfn = iemgui_vis;\n    radio_widgetbehavior.w_clickfn = radio_newclick;\n    class_setwidget(radio_class, &radio_widgetbehavior);\n\n    class_sethelpsymbol(radio_class, gensym(\"radio\"));\n    class_setsavefn(radio_class, radio_save);\n    class_setpropertiesfn(radio_class, radio_properties);\n\n        /* obsolete version (0.34-0.35) */\n    class_addcreator((t_newmethod)dial_new, gensym(\"hdl\"), A_GIMME, 0);\n    class_addcreator((t_newmethod)dial_new, gensym(\"vdl\"), A_GIMME, 0);\n    class_addmethod(radio_class, (t_method)radio_single_change,\n        gensym(\"single_change\"), 0);\n    class_addmethod(radio_class, (t_method)radio_double_change,\n        gensym(\"double_change\"), 0);\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/g_readwrite.c",
    "content": "/* Copyright (c) 1997-2002 Miller Puckette and others.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/*\nRoutines to read and write canvases to files:\ncanvas_savetofile() writes a root canvas to a \"pd\" file.  (Reading \"pd\" files\nis done simply by passing the contents to the pd message interpreter.)\nAlternatively, the  glist_read() and glist_write() routines read and write\n\"data\" from and to files (reading reads into an existing canvas), using a\nfile format as in the dialog window for data.\n*/\n\n#include \"m_pd.h\"\n#include \"g_canvas.h\"\n#include <string.h>\n#include <errno.h>\n\n/* object to assist in saving state by abstractions */\nstatic t_class *savestate_class;\n\ntypedef struct _savestate\n{\n    t_object x_obj;\n    t_outlet *x_stateout;\n    t_outlet *x_bangout;\n    t_binbuf *x_savetobuf;\n} t_savestate;\n\nstatic void *savestate_new(void)\n{\n    t_savestate *x = (t_savestate *)pd_new(savestate_class);\n    x->x_stateout = outlet_new(&x->x_obj, &s_list);\n    x->x_bangout = outlet_new(&x->x_obj, &s_bang);\n    x->x_savetobuf = 0;\n    return (x);\n}\n\n    /* call this when the owning abstraction's parent patch is saved so we\n    can add state-restoring messages to binbuf */\nstatic void savestate_doit(t_savestate *x, t_binbuf *b)\n{\n    x->x_savetobuf = b;\n    outlet_bang(x->x_bangout);\n    x->x_savetobuf = 0;\n}\n\n    /* called by abstraction in response to savestate_doit(); lists received\n    here are added to the parent patch's save buffer after the line that will\n    create the abstraction, addressed to \"#A\" which will be this patch after\n    it is recreated by reopening the parent patch, pasting, or \"undo\". */\nstatic void savestate_list(t_savestate *x, t_symbol *s, int argc, t_atom *argv)\n{\n    if (x->x_savetobuf)\n    {\n        binbuf_addv(x->x_savetobuf, \"ss\", gensym(\"#A\"), gensym(\"saved\"));\n        binbuf_add(x->x_savetobuf, argc, argv);\n        binbuf_addv(x->x_savetobuf, \";\");\n    }\n    else pd_error(x, \"savestate: ignoring message sent when not saving parent\");\n}\n\nstatic void savestate_setup(void)\n{\n    savestate_class = class_new(gensym(\"savestate\"),\n        (t_newmethod)savestate_new, 0, sizeof(t_savestate), 0, 0);\n    class_addlist(savestate_class, savestate_list);\n}\n\nvoid canvas_statesavers_doit(t_glist *x, t_binbuf *b)\n{\n    t_gobj *g;\n    for (g = x->gl_list; g; g = g->g_next)\n        if (g->g_pd == savestate_class)\n            savestate_doit((t_savestate *)g, b);\n        else if (g->g_pd == canvas_class && !canvas_isabstraction((t_canvas *)g))\n            canvas_statesavers_doit((t_glist *)g, b);\n}\n\nvoid canvas_saved(t_glist *x, t_symbol *s, int argc, t_atom *argv)\n{\n    t_gobj *g;\n    for (g = x->gl_list; g; g = g->g_next)\n        if (g->g_pd == savestate_class)\n            outlet_list(((t_savestate *)g)->x_stateout, 0, argc, argv);\n        else if (g->g_pd == canvas_class && !canvas_isabstraction((t_canvas *)g))\n            canvas_saved((t_glist *)g, s, argc, argv);\n}\n\nstatic t_class *declare_class;\nvoid canvas_savedeclarationsto(t_canvas *x, t_binbuf *b);\n\n    /* the following routines read \"scalars\" from a file into a canvas. */\n\nstatic int canvas_scanbinbuf(int natoms, t_atom *vec, int *p_indexout,\n    int *p_next)\n{\n    int i;\n    int indexwas = *p_next;\n    *p_indexout = indexwas;\n    if (indexwas >= natoms)\n        return (0);\n    for (i = indexwas; i < natoms && vec[i].a_type != A_SEMI; i++)\n        ;\n    if (i >= natoms)\n        *p_next = i;\n    else *p_next = i + 1;\n    return (i - indexwas);\n}\n\nint canvas_readscalar(t_glist *x, int natoms, t_atom *vec,\n    int *p_nextmsg, int selectit);\n\nstatic void canvas_readerror(int natoms, t_atom *vec, int message,\n    int nline, char *s)\n{\n    pd_error(0, \"%s\", s);\n    startpost(\"line was:\");\n    postatom(nline, vec + message);\n    endpost();\n}\n\n    /* fill in the contents of the scalar into the vector w. */\n\nstatic void glist_readatoms(t_glist *x, int natoms, t_atom *vec,\n    int *p_nextmsg, t_symbol *templatesym, t_word *w, int argc, t_atom *argv)\n{\n    int message, n, i;\n\n    t_template *template = template_findbyname(templatesym);\n    if (!template)\n    {\n        pd_error(0, \"%s: no such template\", templatesym->s_name);\n        *p_nextmsg = natoms;\n        return;\n    }\n    word_restore(w, template, argc, argv);\n    n = template->t_n;\n    for (i = 0; i < n; i++)\n    {\n        if (template->t_vec[i].ds_type == DT_ARRAY)\n        {\n            t_array *a = w[i].w_array;\n            int elemsize = a->a_elemsize, nitems = 0;\n            t_symbol *arraytemplatesym = template->t_vec[i].ds_arraytemplate;\n            t_template *arraytemplate =\n                template_findbyname(arraytemplatesym);\n            if (!arraytemplate)\n            {\n                pd_error(0, \"%s: no such template\", arraytemplatesym->s_name);\n            }\n            else while (1)\n            {\n                t_word *element;\n                int nline = canvas_scanbinbuf(natoms, vec, &message, p_nextmsg);\n                    /* empty line terminates array */\n                if (!nline)\n                    break;\n                array_resize(a, nitems + 1);\n                element = (t_word *)(((char *)a->a_vec) +\n                    nitems * elemsize);\n                glist_readatoms(x, natoms, vec, p_nextmsg, arraytemplatesym,\n                    element, nline, vec + message);\n                nitems++;\n            }\n        }\n        else if (template->t_vec[i].ds_type == DT_TEXT)\n        {\n            t_binbuf *z = binbuf_new();\n            int first = *p_nextmsg, last;\n            for (last = first; last < natoms && vec[last].a_type != A_SEMI;\n                last++);\n            binbuf_restore(z, last-first, vec+first);\n            binbuf_add(w[i].w_binbuf, binbuf_getnatom(z), binbuf_getvec(z));\n            binbuf_free(z);\n            last++;\n            if (last > natoms) last = natoms;\n            *p_nextmsg = last;\n        }\n    }\n}\n\nint canvas_readscalar(t_glist *x, int natoms, t_atom *vec,\n    int *p_nextmsg, int selectit)\n{\n    int message, nline;\n    t_template *template;\n    t_symbol *templatesym;\n    t_scalar *sc;\n    int nextmsg = *p_nextmsg;\n    int wasvis = glist_isvisible(x);\n\n    if (nextmsg >= natoms || vec[nextmsg].a_type != A_SYMBOL)\n    {\n        if (nextmsg < natoms)\n            post(\"stopping early: type %d\", vec[nextmsg].a_type);\n        *p_nextmsg = natoms;\n        return (0);\n    }\n    templatesym = canvas_makebindsym(vec[nextmsg].a_w.w_symbol);\n    *p_nextmsg = nextmsg + 1;\n\n    if (!(template = template_findbyname(templatesym)))\n    {\n        pd_error(0, \"canvas_read: %s: no such template\", templatesym->s_name);\n        *p_nextmsg = natoms;\n        return (0);\n    }\n    sc = scalar_new(x, templatesym);\n    if (!sc)\n    {\n        pd_error(0, \"couldn't create scalar \\\"%s\\\"\", templatesym->s_name);\n        *p_nextmsg = natoms;\n        return (0);\n    }\n    if (wasvis)\n    {\n            /* temporarily lie about vis flag while this is built */\n        glist_getcanvas(x)->gl_mapped = 0;\n    }\n    glist_add(x, &sc->sc_gobj);\n\n    nline = canvas_scanbinbuf(natoms, vec, &message, p_nextmsg);\n    glist_readatoms(x, natoms, vec, p_nextmsg, templatesym, sc->sc_vec,\n        nline, vec + message);\n    if (wasvis)\n    {\n            /* reset vis flag as before */\n        glist_getcanvas(x)->gl_mapped = 1;\n        gobj_vis(&sc->sc_gobj, x, 1);\n    }\n    if (selectit)\n    {\n        glist_select(x, &sc->sc_gobj);\n    }\n    return (1);\n}\n\nvoid glist_readfrombinbuf(t_glist *x, const t_binbuf *b, const char *filename, int selectem)\n{\n    t_canvas *canvas = glist_getcanvas(x);\n    int natoms, nline, message, nextmsg = 0;\n    t_atom *vec;\n\n    natoms = binbuf_getnatom(b);\n    vec = binbuf_getvec(b);\n\n\n            /* check for file type */\n    nline = canvas_scanbinbuf(natoms, vec, &message, &nextmsg);\n    if (nline != 1 && vec[message].a_type != A_SYMBOL &&\n        strcmp(vec[message].a_w.w_symbol->s_name, \"data\"))\n    {\n        pd_error(x, \"%s: file apparently of wrong type\", filename);\n        return;\n    }\n        /* read in templates and check for consistency */\n    while (1)\n    {\n        t_template *newtemplate, *existtemplate;\n        t_symbol *templatesym;\n        t_atom *templateargs = getbytes(0);\n        int ntemplateargs = 0, newnargs;\n        nline = canvas_scanbinbuf(natoms, vec, &message, &nextmsg);\n        if (nline < 2)\n        {\n            t_freebytes(templateargs, sizeof (*templateargs) * ntemplateargs);\n            break;\n        }\n        else if (nline > 2)\n            canvas_readerror(natoms, vec, message, nline,\n                \"extra items ignored\");\n        else if (vec[message].a_type != A_SYMBOL ||\n            strcmp(vec[message].a_w.w_symbol->s_name, \"template\") ||\n            vec[message + 1].a_type != A_SYMBOL)\n        {\n            canvas_readerror(natoms, vec, message, nline,\n                \"bad template header\");\n            continue;\n        }\n        templatesym = canvas_makebindsym(vec[message + 1].a_w.w_symbol);\n        while (1)\n        {\n            nline = canvas_scanbinbuf(natoms, vec, &message, &nextmsg);\n            if (nline != 2 && nline != 3)\n                break;\n            newnargs = ntemplateargs + nline;\n            templateargs = (t_atom *)t_resizebytes(templateargs,\n                sizeof(*templateargs) * ntemplateargs,\n                sizeof(*templateargs) * newnargs);\n            templateargs[ntemplateargs] = vec[message];\n            templateargs[ntemplateargs + 1] = vec[message + 1];\n            if (nline == 3)\n                templateargs[ntemplateargs + 2] = vec[message + 2];\n            ntemplateargs = newnargs;\n        }\n        if (!(existtemplate = template_findbyname(templatesym)))\n        {\n            pd_error(0, \"%s: template not found in current patch\",\n                templatesym->s_name);\n            t_freebytes(templateargs, sizeof (*templateargs) * ntemplateargs);\n            return;\n        }\n        newtemplate = template_new(templatesym, ntemplateargs, templateargs);\n        t_freebytes(templateargs, sizeof (*templateargs) * ntemplateargs);\n        if (!template_match(existtemplate, newtemplate))\n        {\n            pd_error(0, \"%s: template doesn't match current one\",\n                templatesym->s_name);\n            pd_free(&newtemplate->t_pdobj);\n            return;\n        }\n        pd_free(&newtemplate->t_pdobj);\n    }\n    while (nextmsg < natoms)\n    {\n        canvas_readscalar(x, natoms, vec, &nextmsg, selectem);\n    }\n}\n\nstatic void glist_doread(t_glist *x, t_symbol *filename, t_symbol *format,\n    int clearme)\n{\n    t_binbuf *b = binbuf_new();\n    t_canvas *canvas = glist_getcanvas(x);\n    int wasvis = glist_isvisible(canvas);\n    int cr = 0;\n\n    if (!strcmp(format->s_name, \"cr\"))\n        cr = 1;\n    else if (*format->s_name)\n        pd_error(0, \"qlist_read: unknown flag: %s\", format->s_name);\n\n    if (binbuf_read_via_canvas(b, filename->s_name, canvas, cr))\n    {\n        pd_error(x, \"read failed\");\n        binbuf_free(b);\n        return;\n    }\n    if (wasvis)\n        canvas_vis(canvas, 0);\n    if (clearme)\n        glist_clear(x);\n    glist_readfrombinbuf(x, b, filename->s_name, 0);\n    if (wasvis)\n        canvas_vis(canvas, 1);\n    binbuf_free(b);\n}\n\nvoid glist_read(t_glist *x, t_symbol *filename, t_symbol *format)\n{\n    glist_doread(x, filename, format, 1);\n}\n\nvoid glist_mergefile(t_glist *x, t_symbol *filename, t_symbol *format)\n{\n    glist_doread(x, filename, format, 0);\n}\n\n    /* read text from a \"properties\" window, called from a gfxstub set\n    up in scalar_properties().  We try to restore the object; if successful\n    we either copy the data from the new scalar to the old one in place\n    (if their templates match) or else delete the old scalar and put the new\n    thing in its place on the list. */\nvoid canvas_dataproperties(t_canvas *x, t_scalar *sc, t_binbuf *b)\n{\n    int ntotal, nnew, scindex;\n    t_gobj *y, *y2 = 0, *newone, *oldone = 0;\n    t_template *template;\n    glist_noselect(x);\n    for (y = x->gl_list, ntotal = 0, scindex = -1; y; y = y->g_next)\n    {\n        if (y == &sc->sc_gobj)\n            scindex = ntotal, oldone = y;\n        ntotal++;\n    }\n\n    if (scindex == -1)\n    {\n        pd_error(x, \"data_properties: scalar disappeared\");\n        return;\n    }\n    if (!b) {\n        pd_error(x, \"couldn't update properties (none given)\");\n        return;\n    }\n    glist_readfrombinbuf(x, b, \"properties dialog\", 0);\n    newone = 0;\n        /* take the new object off the list */\n    if (ntotal)\n    {\n        for (y = x->gl_list, nnew = 1; (y2 = y->g_next);\n            y = y2, nnew++)\n                if (nnew == ntotal)\n        {\n            newone = y2;\n            gobj_vis(newone, x, 0);\n            y->g_next = y2->g_next;\n            break;\n        }\n    }\n    else gobj_vis((newone = x->gl_list), x, 0), x->gl_list = newone->g_next;\n    if (!newone)\n        pd_error(x, \"couldn't update properties (perhaps a format problem?)\");\n    else if (!oldone)\n        bug(\"data_properties: couldn't find old element\");\n    else if (newone->g_pd == scalar_class && oldone->g_pd == scalar_class\n        && ((t_scalar *)newone)->sc_template ==\n            ((t_scalar *)oldone)->sc_template\n        && (template = template_findbyname(((t_scalar *)newone)->sc_template)))\n    {\n            /* swap new one with old one; then delete new one */\n        int i;\n        for (i = 0; i < template->t_n; i++)\n        {\n            t_word w = ((t_scalar *)newone)->sc_vec[i];\n            ((t_scalar *)newone)->sc_vec[i] = ((t_scalar *)oldone)->sc_vec[i];\n            ((t_scalar *)oldone)->sc_vec[i] = w;\n        }\n        pd_free(&newone->g_pd);\n        if (glist_isvisible(x))\n        {\n            gobj_vis(oldone, x, 0);\n            gobj_vis(oldone, x, 1);\n        }\n    }\n    else\n    {\n            /* delete old one; put new one where the old one was on glist */\n        glist_delete(x, oldone);\n        if (scindex > 0)\n        {\n            for (y = x->gl_list, nnew = 1; y;\n                y = y->g_next, nnew++)\n                    if (nnew == scindex || !y->g_next)\n            {\n                newone->g_next = y->g_next;\n                y->g_next = newone;\n                goto didit;\n            }\n            bug(\"data_properties: can't reinsert\");\n        }\n        else newone->g_next = x->gl_list, x->gl_list = newone;\n    }\ndidit:\n    ;\n}\n\n    /* ----------- routines to write data to a binbuf ----------- */\n\nvoid canvas_doaddtemplate(t_symbol *templatesym,\n    int *p_ntemplates, t_symbol ***p_templatevec)\n{\n    int n = *p_ntemplates, i;\n    t_symbol **templatevec = *p_templatevec;\n    for (i = 0; i < n; i++)\n        if (templatevec[i] == templatesym)\n            return;\n    templatevec = (t_symbol **)t_resizebytes(templatevec,\n        n * sizeof(*templatevec), (n+1) * sizeof(*templatevec));\n    templatevec[n] = templatesym;\n    *p_templatevec = templatevec;\n    *p_ntemplates = n+1;\n}\n\nstatic void glist_writelist(t_gobj *y, t_binbuf *b);\n\nvoid binbuf_savetext(t_binbuf *bfrom, t_binbuf *bto);\n\nvoid canvas_writescalar(t_symbol *templatesym, t_word *w, t_binbuf *b,\n    int amarrayelement)\n{\n    t_template *template = template_findbyname(templatesym);\n    t_atom *a = (t_atom *)t_getbytes(0);\n    int i, n = template?(template->t_n):0, natom = 0;\n    if (!amarrayelement)\n    {\n        t_atom templatename;\n        SETSYMBOL(&templatename, gensym(templatesym->s_name + 3));\n        binbuf_add(b, 1, &templatename);\n    }\n    if (!template)\n        bug(\"canvas_writescalar\");\n        /* write the atoms (floats and symbols) */\n    for (i = 0; i < n; i++)\n    {\n        if (template->t_vec[i].ds_type == DT_FLOAT ||\n            template->t_vec[i].ds_type == DT_SYMBOL)\n        {\n            a = (t_atom *)t_resizebytes(a,\n                natom * sizeof(*a), (natom + 1) * sizeof (*a));\n            if (template->t_vec[i].ds_type == DT_FLOAT)\n                SETFLOAT(a + natom, w[i].w_float);\n            else SETSYMBOL(a + natom,  w[i].w_symbol);\n            natom++;\n        }\n    }\n        /* array elements have to have at least something */\n    if (natom == 0 && amarrayelement)\n        SETSYMBOL(a + natom,  &s_bang), natom++;\n    binbuf_add(b, natom, a);\n    binbuf_addsemi(b);\n    t_freebytes(a, natom * sizeof(*a));\n    for (i = 0; i < n; i++)\n    {\n        if (template->t_vec[i].ds_type == DT_ARRAY)\n        {\n            int j;\n            t_array *a = w[i].w_array;\n            int elemsize = a->a_elemsize, nitems = a->a_n;\n            t_symbol *arraytemplatesym = template->t_vec[i].ds_arraytemplate;\n            for (j = 0; j < nitems; j++)\n                canvas_writescalar(arraytemplatesym,\n                    (t_word *)(((char *)a->a_vec) + elemsize * j), b, 1);\n            binbuf_addsemi(b);\n        }\n        else if (template->t_vec[i].ds_type == DT_TEXT)\n            binbuf_savetext(w[i].w_binbuf, b);\n    }\n}\n\nstatic void glist_writelist(t_gobj *y, t_binbuf *b)\n{\n    for (; y; y = y->g_next)\n    {\n        if (pd_class(&y->g_pd) == scalar_class)\n        {\n            canvas_writescalar(((t_scalar *)y)->sc_template,\n                ((t_scalar *)y)->sc_vec, b, 0);\n        }\n    }\n}\n\n    /* ------------ routines to write out templates for data ------- */\n\nstatic void canvas_addtemplatesforlist(t_gobj *y,\n    int  *p_ntemplates, t_symbol ***p_templatevec);\n\nstatic void canvas_addtemplatesforscalar(t_symbol *templatesym,\n    t_word *w, int *p_ntemplates, t_symbol ***p_templatevec)\n{\n    t_dataslot *ds;\n    int i;\n    t_template *template = template_findbyname(templatesym);\n    canvas_doaddtemplate(templatesym, p_ntemplates, p_templatevec);\n    if (!template)\n        bug(\"canvas_addtemplatesforscalar\");\n    else for (ds = template->t_vec, i = template->t_n; i--; ds++, w++)\n    {\n        if (ds->ds_type == DT_ARRAY)\n        {\n            int j;\n            t_array *a = w->w_array;\n            int elemsize = a->a_elemsize, nitems = a->a_n;\n            t_symbol *arraytemplatesym = ds->ds_arraytemplate;\n            canvas_doaddtemplate(arraytemplatesym, p_ntemplates, p_templatevec);\n            for (j = 0; j < nitems; j++)\n                canvas_addtemplatesforscalar(arraytemplatesym,\n                    (t_word *)(((char *)a->a_vec) + elemsize * j),\n                        p_ntemplates, p_templatevec);\n        }\n    }\n}\n\nstatic void canvas_addtemplatesforlist(t_gobj *y,\n    int  *p_ntemplates, t_symbol ***p_templatevec)\n{\n    for (; y; y = y->g_next)\n    {\n        if (pd_class(&y->g_pd) == scalar_class)\n        {\n            canvas_addtemplatesforscalar(((t_scalar *)y)->sc_template,\n                ((t_scalar *)y)->sc_vec, p_ntemplates, p_templatevec);\n        }\n    }\n}\n\n    /* write all \"scalars\" in a glist to a binbuf. */\nt_binbuf *glist_writetobinbuf(t_glist *x, int wholething)\n{\n    int i;\n    t_symbol **templatevec = getbytes(0);\n    int ntemplates = 0;\n    t_gobj *y;\n    t_binbuf *b = binbuf_new();\n\n    for (y = x->gl_list; y; y = y->g_next)\n    {\n        if ((pd_class(&y->g_pd) == scalar_class) &&\n            (wholething || glist_isselected(x, y)))\n        {\n            canvas_addtemplatesforscalar(((t_scalar *)y)->sc_template,\n                ((t_scalar *)y)->sc_vec,  &ntemplates, &templatevec);\n        }\n    }\n    binbuf_addv(b, \"s;\", gensym(\"data\"));\n    for (i = 0; i < ntemplates; i++)\n    {\n        t_template *template = template_findbyname(templatevec[i]);\n        int j, m = template->t_n;\n            /* drop \"pd-\" prefix from template symbol to print it: */\n        binbuf_addv(b, \"ss;\", gensym(\"template\"),\n            gensym(templatevec[i]->s_name + 3));\n        for (j = 0; j < m; j++)\n        {\n            t_symbol *type;\n            switch (template->t_vec[j].ds_type)\n            {\n                case DT_FLOAT: type = &s_float; break;\n                case DT_SYMBOL: type = &s_symbol; break;\n                case DT_ARRAY: type = gensym(\"array\"); break;\n                case DT_TEXT: type = &s_list; break;\n                default: type = &s_float; bug(\"canvas_write\");\n            }\n            if (template->t_vec[j].ds_type == DT_ARRAY)\n                binbuf_addv(b, \"sss;\", type, template->t_vec[j].ds_name,\n                    gensym(template->t_vec[j].ds_arraytemplate->s_name + 3));\n            else binbuf_addv(b, \"ss;\", type, template->t_vec[j].ds_name);\n        }\n        binbuf_addsemi(b);\n    }\n    binbuf_addsemi(b);\n        /* now write out the objects themselves */\n    for (y = x->gl_list; y; y = y->g_next)\n    {\n        if ((pd_class(&y->g_pd) == scalar_class) &&\n            (wholething || glist_isselected(x, y)))\n        {\n            canvas_writescalar(((t_scalar *)y)->sc_template,\n                ((t_scalar *)y)->sc_vec,  b, 0);\n        }\n    }\n    t_freebytes(templatevec, ntemplates*sizeof(*templatevec));\n    return (b);\n}\n\nstatic void glist_write(t_glist *x, t_symbol *filename, t_symbol *format)\n{\n    int cr = 0;\n    t_binbuf *b;\n    char buf[MAXPDSTRING];\n    t_canvas *canvas = glist_getcanvas(x);\n    canvas_makefilename(canvas, filename->s_name, buf, MAXPDSTRING);\n    if (!strcmp(format->s_name, \"cr\"))\n        cr = 1;\n    else if (*format->s_name)\n        pd_error(0, \"qlist_read: unknown flag: %s\", format->s_name);\n\n    b = glist_writetobinbuf(x, 1);\n    if (b)\n    {\n        if (binbuf_write(b, buf, \"\", cr))\n            pd_error(0, \"%s: write failed\", filename->s_name);\n        binbuf_free(b);\n    }\n}\n\n/* ------ routines to save and restore canvases (patches) recursively. ----*/\n\ntypedef void (*t_zoomfn)(void *x, t_floatarg arg1);\n\n    /* save to a binbuf, called recursively; cf. canvas_savetofile() which\n    saves the document, and is only called on root canvases. */\nstatic void canvas_saveto(t_canvas *x, t_binbuf *b)\n{\n    t_gobj *y;\n    t_linetraverser t;\n    t_outconnect *oc;\n\n        /* subpatch */\n    if (x->gl_owner && !x->gl_env)\n    {\n        /* have to go to original binbuf to find out how we were named. */\n        t_binbuf *bz = binbuf_new();\n        t_symbol *patchsym;\n        binbuf_addbinbuf(bz, x->gl_obj.ob_binbuf);\n        patchsym = atom_getsymbolarg(1, binbuf_getnatom(bz), binbuf_getvec(bz));\n        binbuf_free(bz);\n        binbuf_addv(b, \"ssiiiisi;\", gensym(\"#N\"), gensym(\"canvas\"),\n            (int)(x->gl_screenx1),\n            (int)(x->gl_screeny1),\n            (int)(x->gl_screenx2 - x->gl_screenx1),\n            (int)(x->gl_screeny2 - x->gl_screeny1),\n            (patchsym != &s_ ? patchsym: gensym(\"(subpatch)\")),\n            x->gl_mapped);\n    }\n        /* root or abstraction */\n    else\n    {\n        binbuf_addv(b, \"ssiiiii;\", gensym(\"#N\"), gensym(\"canvas\"),\n            (int)(x->gl_screenx1),\n            (int)(x->gl_screeny1),\n            (int)(x->gl_screenx2 - x->gl_screenx1),\n            (int)(x->gl_screeny2 - x->gl_screeny1),\n                (int)x->gl_font);\n        canvas_savedeclarationsto(x, b);\n    }\n    for (y = x->gl_list; y; y = y->g_next)\n        gobj_save(y, b);\n\n    linetraverser_start(&t, x);\n    while ((oc = linetraverser_next(&t)))\n    {\n        int srcno = canvas_getindex(x, &t.tr_ob->ob_g);\n        int sinkno = canvas_getindex(x, &t.tr_ob2->ob_g);\n        binbuf_addv(b, \"ssiiii;\", gensym(\"#X\"), gensym(\"connect\"),\n            srcno, t.tr_outno, sinkno, t.tr_inno);\n    }\n        /* unless everything is the default (as in ordinary subpatches)\n        print out a \"coords\" message to set up the coordinate systems */\n    if (x->gl_isgraph || x->gl_x1 || x->gl_y1 ||\n        x->gl_x2 != 1 ||  x->gl_y2 != 1 || x->gl_pixwidth || x->gl_pixheight)\n    {\n        if (x->gl_isgraph && x->gl_goprect)\n                /* if we have a graph-on-parent rectangle, we're new style.\n                The format is arranged so\n                that old versions of Pd can at least do something with it. */\n            binbuf_addv(b, \"ssfffffffff;\", gensym(\"#X\"), gensym(\"coords\"),\n                x->gl_x1, x->gl_y1,\n                x->gl_x2, x->gl_y2,\n                (t_float)x->gl_pixwidth, (t_float)x->gl_pixheight,\n                (t_float)((x->gl_hidetext)?2.:1.),\n                (t_float)x->gl_xmargin, (t_float)x->gl_ymargin);\n                    /* otherwise write in 0.38-compatible form */\n        else binbuf_addv(b, \"ssfffffff;\", gensym(\"#X\"), gensym(\"coords\"),\n                x->gl_x1, x->gl_y1,\n                x->gl_x2, x->gl_y2,\n                (t_float)x->gl_pixwidth, (t_float)x->gl_pixheight,\n                (t_float)x->gl_isgraph);\n    }\n}\n\n    /* call this recursively to collect all the template names for\n    a canvas or for the selection. */\nstatic void canvas_collecttemplatesfor(t_canvas *x, int *ntemplatesp,\n    t_symbol ***templatevecp, int wholething)\n{\n    t_gobj *y;\n\n    for (y = x->gl_list; y; y = y->g_next)\n    {\n        if ((pd_class(&y->g_pd) == scalar_class) &&\n            (wholething || glist_isselected(x, y)))\n                canvas_addtemplatesforscalar(((t_scalar *)y)->sc_template,\n                    ((t_scalar *)y)->sc_vec,  ntemplatesp, templatevecp);\n        else if ((pd_class(&y->g_pd) == canvas_class) &&\n            (wholething || glist_isselected(x, y)))\n                canvas_collecttemplatesfor((t_canvas *)y,\n                    ntemplatesp, templatevecp, 1);\n    }\n}\n\n    /* save the templates needed by a canvas to a binbuf. */\nstatic void canvas_savetemplatesto(t_canvas *x, t_binbuf *b, int wholething)\n{\n    t_symbol **templatevec = getbytes(0);\n    int i, ntemplates = 0;\n    canvas_collecttemplatesfor(x, &ntemplates, &templatevec, wholething);\n    for (i = 0; i < ntemplates; i++)\n    {\n        t_template *template = template_findbyname(templatevec[i]);\n        int j, m;\n        if (!template)\n        {\n            bug(\"canvas_savetemplatesto\");\n            continue;\n        }\n        m = template->t_n;\n            /* drop \"pd-\" prefix from template symbol to print */\n        binbuf_addv(b, \"sss\", &s__N, gensym(\"struct\"),\n            gensym(templatevec[i]->s_name + 3));\n        for (j = 0; j < m; j++)\n        {\n            t_symbol *type;\n            switch (template->t_vec[j].ds_type)\n            {\n                case DT_FLOAT: type = &s_float; break;\n                case DT_SYMBOL: type = &s_symbol; break;\n                case DT_ARRAY: type = gensym(\"array\"); break;\n                case DT_TEXT: type = gensym(\"text\"); break;\n                default: type = &s_float; bug(\"canvas_write\");\n            }\n            if (template->t_vec[j].ds_type == DT_ARRAY)\n                binbuf_addv(b, \"sss\", type, template->t_vec[j].ds_name,\n                    gensym(template->t_vec[j].ds_arraytemplate->s_name + 3));\n            else binbuf_addv(b, \"ss\", type, template->t_vec[j].ds_name);\n        }\n        binbuf_addsemi(b);\n    }\n    freebytes(templatevec, ntemplates * sizeof(*templatevec));\n}\n\nvoid canvas_reload(t_symbol *name, t_symbol *dir, t_glist *except);\n\n    /* save a \"root\" canvas to a file; cf. canvas_saveto() which saves the\n    body (and which is called recursively.) */\nstatic void canvas_savetofile(t_canvas *x, t_symbol *filename, t_symbol *dir,\n    float fdestroy)\n{\n    t_binbuf *b = binbuf_new();\n    canvas_savetemplatesto(x, b, 1);\n    canvas_saveto(x, b);\n    errno = 0;\n    if (binbuf_write(b, filename->s_name, dir->s_name, 0))\n        post(\"%s/%s: %s\", dir->s_name, filename->s_name,\n            (errno ? strerror(errno) : \"write failed\"));\n    else\n    {\n            /* if not an abstraction, reset title bar and directory */\n        if (!x->gl_owner)\n{\n            canvas_rename(x, filename, dir);\n            /* update window list in case Save As changed the window name */\n            canvas_updatewindowlist();\n}\n        post(\"saved to: %s/%s\", dir->s_name, filename->s_name);\n        canvas_dirty(x, 0);\n        canvas_reload(filename, dir, x);\n        if (fdestroy != 0)\n            vmess(&x->gl_pd, gensym(\"menuclose\"), \"f\", 1.);\n    }\n    binbuf_free(b);\n}\n\nstatic void canvas_menusaveas(t_canvas *x, t_float fdestroy)\n{\n    t_canvas *x2 = canvas_getrootfor(x);\n    pdgui_vmess(\"pdtk_canvas_saveas\", \"^ ss i\",\n        x2,\n        x2->gl_name->s_name, canvas_getdir(x2)->s_name,\n        (fdestroy != 0));\n}\n\nstatic void canvas_menusave(t_canvas *x, t_float fdestroy)\n{\n    t_canvas *x2 = canvas_getrootfor(x);\n    const char *name = x2->gl_name->s_name;\n    if (*name && UNTITLED_STRNCMP(name)\n            && (strlen(name) < 4 || strcmp(name + strlen(name)-4, \".pat\")\n                || strcmp(name + strlen(name)-4, \".mxt\")))\n    {\n        canvas_savetofile(x2, x2->gl_name, canvas_getdir(x2), fdestroy);\n    }\n    else canvas_menusaveas(x2, fdestroy);\n}\n\nvoid g_readwrite_setup(void)\n{\n    savestate_setup();\n    class_addmethod(canvas_class, (t_method)glist_write,\n        gensym(\"write\"), A_SYMBOL, A_DEFSYM, A_NULL);\n    class_addmethod(canvas_class, (t_method)glist_read,\n        gensym(\"read\"), A_SYMBOL, A_DEFSYM, A_NULL);\n    class_addmethod(canvas_class, (t_method)glist_mergefile,\n        gensym(\"mergefile\"), A_SYMBOL, A_DEFSYM, A_NULL);\n    class_addmethod(canvas_class, (t_method)canvas_savetofile,\n        gensym(\"savetofile\"), A_SYMBOL, A_SYMBOL, A_DEFFLOAT, 0);\n    class_addmethod(canvas_class, (t_method)canvas_saveto,\n        gensym(\"saveto\"), A_CANT, 0);\n    class_addmethod(canvas_class, (t_method)canvas_saved,\n        gensym(\"saved\"), A_GIMME, 0);\n/* ------------------ from the menu ------------------------- */\n    class_addmethod(canvas_class, (t_method)canvas_menusave,\n        gensym(\"menusave\"), A_DEFFLOAT, 0);\n    class_addmethod(canvas_class, (t_method)canvas_menusaveas,\n        gensym(\"menusaveas\"), A_DEFFLOAT, 0);\n}\n\nvoid canvas_readwrite_for_class(t_class *c)\n{\n    class_addmethod(c, (t_method)canvas_menusave,\n        gensym(\"menusave\"), A_DEFFLOAT, 0);\n    class_addmethod(c, (t_method)canvas_menusaveas,\n        gensym(\"menusaveas\"), A_DEFFLOAT, 0);\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/g_rtext.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n#include <string.h>\n#include <stdio.h>\n#include \"m_pd.h\"\n#include \"s_stuff.h\"\n#include \"g_canvas.h\"\n#include \"s_utf8.h\"\n\n#define LMARGIN 2\n#define RMARGIN 2\n#define TMARGIN 3\n#define BMARGIN 2\n\n#define SEND_FIRST 1\n#define SEND_UPDATE 2\n#define SEND_CHECK 0\n\nstruct _rtext\n{\n    char *x_buf;    /*-- raw byte string, assumed UTF-8 encoded (moo) --*/\n    int x_bufsize;  /*-- byte length --*/\n    int x_selstart; /*-- byte offset --*/\n    int x_selend;   /*-- byte offset --*/\n    int x_active;       /* 1 if 'actively editing */\n    int x_dragfrom;     /* character onset we're dragging from */\n    int x_drawnwidth;   /* screen size, pixels */\n    int x_drawnheight;\n    t_text *x_text;     /* owner */\n    t_glist *x_glist;   /* glist owner belongs to */\n    char x_tag[50];     /* tag for gui */\n    struct _rtext *x_next;  /* next in editor list */\n};\n\nt_rtext *rtext_new(t_glist *glist, t_text *who)\n{\n    t_rtext *x = (t_rtext *)getbytes(sizeof *x);\n    int w = 0, h = 0, indx;\n    x->x_text = who;\n    x->x_glist = glist;\n    x->x_next = glist->gl_editor->e_rtext;\n    x->x_selstart = x->x_selend = x->x_active =\n        x->x_drawnwidth = x->x_drawnheight = 0;\n    binbuf_gettext(who->te_binbuf, &x->x_buf, &x->x_bufsize);\n        /* allocate extra space for hidden null terminator */\n    x->x_buf = resizebytes(x->x_buf, x->x_bufsize, x->x_bufsize+1);\n    x->x_buf[x->x_bufsize] = 0;\n    glist->gl_editor->e_rtext = x;\n    sprintf(x->x_tag, \".x%lx.t%lx\", (t_int)glist_getcanvas(x->x_glist),\n        (t_int)x);\n    return (x);\n}\n\nvoid rtext_free(t_rtext *x)\n{\n    if (x->x_glist->gl_editor->e_textedfor == x)\n        x->x_glist->gl_editor->e_textedfor = 0;\n    if (x->x_glist->gl_editor->e_rtext == x)\n        x->x_glist->gl_editor->e_rtext = x->x_next;\n    else\n    {\n        t_rtext *e2;\n        for (e2 = x->x_glist->gl_editor->e_rtext; e2; e2 = e2->x_next)\n            if (e2->x_next == x)\n        {\n            e2->x_next = x->x_next;\n            break;\n        }\n    }\n    freebytes(x->x_buf, x->x_bufsize + 1); /* extra 0 byte */\n    freebytes(x, sizeof *x);\n}\n\nconst char *rtext_gettag(t_rtext *x)\n{\n    return (x->x_tag);\n}\n\nvoid rtext_gettext(t_rtext *x, char **buf, int *bufsize)\n{\n    *buf = x->x_buf;\n    *bufsize = x->x_bufsize;\n}\nvoid rtext_getseltext(t_rtext *x, char **buf, int *bufsize)\n{\n    *buf = x->x_buf + x->x_selstart;\n    *bufsize = x->x_selend - x->x_selstart;\n}\n\nt_text *rtext_getowner(t_rtext *x)\n{\n    return (x->x_text);\n}\n\n/* convert t_text te_type symbol for use as a Tk tag */\nstatic t_symbol *rtext_gettype(t_rtext *x)\n{\n    switch (x->x_text->te_type)\n    {\n    case T_TEXT: return gensym(\"text\");\n    case T_OBJECT: return gensym(\"obj\");\n    case T_MESSAGE: return gensym(\"msg\");\n    case T_ATOM: return gensym(\"atom\");\n    }\n    return (&s_);\n}\n\n/* LATER deal with tcl-significant characters */\n\n/* firstone(), lastone()\n *  + returns byte offset of (first|last) occurrence of 'c' in 's[0..n-1]', or\n *    -1 if none was found\n *  + 's' is a raw byte string\n *  + 'c' is a byte value\n *  + 'n' is the length (in bytes) of the prefix of 's' to be searched.\n *  + we could make these functions work on logical characters in utf8 strings,\n *    but we don't really need to...\n */\nstatic int firstone(char *s, int c, int n)\n{\n    char *s2 = s + n;\n    int i = 0;\n    while (s != s2)\n    {\n        if (*s == c) return (i);\n        i++;\n        s++;\n    }\n    return (-1);\n}\n\nstatic int lastone(char *s, int c, int n)\n{\n    char *s2 = s + n;\n    while (s2 != s)\n    {\n        s2--;\n        n--;\n        if (*s2 == c) return (n);\n    }\n    return (-1);\n}\n\n    /* break the text into lines, and compute byte index of character at\n    location (width, height).  Then reset (width, height) to report size of\n    resulting line-broken text.  Used for object, message, and comment boxes;\n    another version below is for atoms.  Also we report the onsets of\n    the beginning and end of the selection, as byte onsets into the reformatted\n    text, which we'll use to inform the GUI how to show the selection.\n\n    The input is taken from x->buf and x->bufsize fields of the text object;\n    the wrapped text is put in \"tempbuf\" with byte length outchars_b_p.\n\n    x->x_buf is assumed to contain text in UTF-8 format, in which characters\n    may occupy multiple bytes. variables with a \"_b\" suffix are raw byte\n    strings, lengths, or offsets;  those with a \"_c\" suffix are logical\n    character lengths or offsets.\n    The UTF8 handling was contributed by Bryan Jurish, who says \"moo.\" */\n\n#define DEFAULTBOXWIDTH 60\n\nstatic void rtext_formattext(t_rtext *x, int *widthp, int *heightp,\n    int *indexp,  char *tempbuf, int *outchars_b_p, int *selstart_b_p,\n    int *selend_b_p, int fontwidth, int fontheight)\n{\n    int widthspec_c = x->x_text->te_width;\n    int widthlimit_c = (widthspec_c ? widthspec_c : DEFAULTBOXWIDTH);\n    int inindex_b = 0;\n    int inindex_c = 0;\n    int x_bufsize_c = u8_charnum(x->x_buf, x->x_bufsize);\n    int nlines = 0, ncolumns = 0, reportedindex = 0;\n    int findx = (*widthp + (fontwidth/2)) / fontwidth;\n    int findy = *heightp / fontheight;\n\n    *selstart_b_p = *selend_b_p = 0;\n\n    while (x_bufsize_c - inindex_c > 0)\n    {\n        int inchars_b  = x->x_bufsize - inindex_b;\n        int inchars_c  = x_bufsize_c  - inindex_c;\n        int maxindex_c = (inchars_c > widthlimit_c ? widthlimit_c : inchars_c);\n        int maxindex_b = u8_offset(x->x_buf + inindex_b, maxindex_c);\n        int eatchar = 1;\n        int foundit_b  = firstone(x->x_buf + inindex_b, '\\n', maxindex_b);\n        int foundit_c;\n        if (foundit_b < 0)\n        {\n                /* too much text to fit in one line? */\n            if (inchars_c > widthlimit_c)\n            {\n                    /* is there a space to break the line at?  OK if it's even\n                    one byte past the end since in this context we know there's\n                    more text */\n                foundit_b = lastone(x->x_buf + inindex_b, ' ', maxindex_b + 1);\n                if (foundit_b < 0)\n                {\n                    foundit_b = maxindex_b;\n                    foundit_c = maxindex_c;\n                    eatchar = 0;\n                }\n                else\n                    foundit_c = u8_charnum(x->x_buf + inindex_b, foundit_b);\n            }\n            else\n            {\n                foundit_b = inchars_b;\n                foundit_c = inchars_c;\n                eatchar = 0;\n            }\n        }\n        else\n            foundit_c = u8_charnum(x->x_buf + inindex_b, foundit_b);\n\n        if (nlines == findy)\n        {\n            int actualx = (findx < 0 ? 0 :\n                (findx > foundit_c ? foundit_c : findx));\n            *indexp = inindex_b + u8_offset(x->x_buf + inindex_b, actualx);\n            reportedindex = 1;\n        }\n        strncpy(tempbuf+ *outchars_b_p, x->x_buf + inindex_b, foundit_b);\n        if (x->x_selstart >= inindex_b &&\n            x->x_selstart <= inindex_b + foundit_b + eatchar)\n                *selstart_b_p = x->x_selstart + *outchars_b_p - inindex_b;\n        if (x->x_selend >= inindex_b &&\n            x->x_selend <= inindex_b + foundit_b + eatchar)\n                *selend_b_p = x->x_selend + *outchars_b_p - inindex_b;\n        *outchars_b_p += foundit_b;\n        inindex_b += (foundit_b + eatchar);\n        inindex_c += (foundit_c + eatchar);\n        if (inindex_b < x->x_bufsize)\n            tempbuf[(*outchars_b_p)++] = '\\n';\n        if (foundit_c > ncolumns)\n            ncolumns = foundit_c;\n        nlines++;\n    }\n    if (!reportedindex)\n        *indexp = *outchars_b_p;\n    if (nlines < 1) nlines = 1;\n    if (!widthspec_c)\n    {\n        while (ncolumns < (x->x_text->te_type == T_TEXT ? 1 : 3))\n        {\n            tempbuf[(*outchars_b_p)++] = ' ';\n            ncolumns++;\n        }\n    }\n    else ncolumns = widthspec_c;\n    *widthp = ncolumns * fontwidth;\n    *heightp = nlines * fontheight;\n    if (glist_getzoom(x->x_glist) > 1)\n    {\n        /* zoom margins */\n        *widthp += (LMARGIN + RMARGIN) * glist_getzoom(x->x_glist);\n        *heightp += (TMARGIN + BMARGIN) * glist_getzoom(x->x_glist);\n    }\n    else\n    {\n        *widthp += LMARGIN + RMARGIN;\n        *heightp += TMARGIN + BMARGIN;\n    }\n\n}\n\n    /* same as above, but for atom boxes, which are always on one line. */\nstatic void rtext_formatatom(t_rtext *x, int *widthp, int *heightp,\n    int *indexp,  char *tempbuf, int *outchars_b_p, int *selstart_b_p,\n    int *selend_b_p, int fontwidth, int fontheight)\n{\n    int findx = *widthp / fontwidth;  /* character index; want byte index */\n    *indexp = 0;\n        /* special case: for number boxes, try to pare the number down\n        to the specified width of the box. */\n    if (x->x_text->te_width > 0 && binbuf_getnatom(x->x_text->te_binbuf) == 1 &&\n        binbuf_getvec(x->x_text->te_binbuf)->a_type == A_FLOAT &&\n        x->x_bufsize > x->x_text->te_width)\n    {\n            /* try to reduce size by dropping decimal digits */\n        int wantreduce = x->x_bufsize - x->x_text->te_width;\n        char *decimal = 0, *nextchar, *ebuf = x->x_buf + x->x_bufsize,\n            *s1, *s2;\n        int ndecimals;\n        strncpy(tempbuf, x->x_buf, x->x_bufsize);\n        tempbuf[x->x_bufsize] = 0;\n        ebuf = tempbuf + x->x_bufsize;\n        for (decimal = tempbuf; decimal < ebuf; decimal++)\n            if (*decimal == '.')\n                break;\n        if (decimal >= ebuf)\n            goto giveup;\n        for (nextchar = decimal + 1; nextchar < ebuf; nextchar++)\n            if (*nextchar < '0' || *nextchar > '9')\n                break;\n        if (nextchar - decimal - 1 < wantreduce)\n            goto giveup;\n        for (s1 = nextchar - wantreduce, s2 = s1 + wantreduce;\n            s2 < ebuf; s1++, s2++)\n                *s1 = *s2;\n        *outchars_b_p = x->x_text->te_width;\n        goto done;\n    giveup:\n            /* give up and bash last char to '>' */\n        tempbuf[x->x_text->te_width-1] = '>';\n        tempbuf[x->x_text->te_width] = 0;\n        *outchars_b_p = x->x_text->te_width;\n    done: ;\n        *indexp = findx;\n        *widthp = x->x_text->te_width * fontwidth;\n    }\n    else\n    {\n        int outchars_c = 0, prev_b = 0;\n        int widthlimit_c = (x->x_text->te_width > 0 ? x->x_text->te_width :\n                1000);   /* nice big fat limit since we can't wrap */\n        uint32_t thischar;\n        *outchars_b_p = 0;\n        for (outchars_c = 0;\n            *outchars_b_p < x->x_bufsize && outchars_c < widthlimit_c;\n                outchars_c++)\n        {\n            prev_b = *outchars_b_p;\n            thischar = u8_nextchar(x->x_buf, outchars_b_p);\n            if (findx > outchars_c)\n                *indexp = *outchars_b_p;\n            if (thischar == '\\n' || !thischar)\n            {\n                *(outchars_b_p) = prev_b;\n                break;\n            }\n            memcpy(tempbuf + prev_b, x->x_buf + prev_b, *outchars_b_p - prev_b);\n                /* if box is full and there's more, bash last char to '>' */\n            if (outchars_c == widthlimit_c-1 && x->x_bufsize > *(outchars_b_p)\n                 && (x->x_buf[*(outchars_b_p)] != ' ' ||\n                    x->x_bufsize > *(outchars_b_p)+1))\n            {\n                tempbuf[prev_b] = '>';\n            }\n        }\n        if (x->x_text->te_width > 0)\n            *widthp = x->x_text->te_width * fontwidth;\n        else *widthp = (outchars_c > 3 ? outchars_c : 3) * fontwidth;\n        tempbuf[*outchars_b_p] = 0;\n    }\n    if (*indexp > *outchars_b_p)\n        *indexp = *outchars_b_p;\n    if (*indexp < 0)\n        *indexp = 0;\n    *selstart_b_p = x->x_selstart;\n    *selend_b_p = x->x_selend;\n    *widthp += (LMARGIN + RMARGIN - 2) * glist_getzoom(x->x_glist);\n    *heightp = fontheight + (TMARGIN + BMARGIN - 1) * glist_getzoom(x->x_glist);\n}\n\n    /* the following routine computes line breaks and carries out\n    some action which could be:\n        SEND_FIRST - draw the box  for the first time\n        SEND_UPDATE - redraw the updated box\n        otherwise - don't draw, just calculate.\n    Called with *widthp and *heightp as coordinates of\n    a test point, the routine reports the index of the character found\n    there in *indexp.  *widthp and *heightp are set to the width and height\n    of the entire text in pixels.\n    */\n\n    /* LATER get this and sys_vgui to work together properly,\n        breaking up messages as needed.  As of now, there's\n        a limit of 1950 characters, imposed by sys_vgui(). */\n#define UPBUFSIZE 4000\n\nvoid text_getfont(t_text *x, t_glist *thisglist,\n    int *fheightp, int *fwidthp, int *guifsize);\n\nstatic void rtext_senditup(t_rtext *x, int action, int *widthp, int *heightp,\n    int *indexp)\n{\n    char smallbuf[200], *tempbuf;\n    int outchars_b = 0, guifontsize, fontwidth, fontheight;\n    t_canvas *canvas = glist_getcanvas(x->x_glist);\n    int selstart_b, selend_b;   /* beginning and end of selection in bytes */\n        /* if we're a GOP (the new, \"goprect\" style) borrow the font size\n        from the inside to preserve the spacing */\n\n    text_getfont(x->x_text, x->x_glist, &fontwidth, &fontheight, &guifontsize);\n    if (x->x_bufsize >= 100)\n         tempbuf = (char *)t_getbytes(2 * x->x_bufsize + 1);\n    else tempbuf = smallbuf;\n    tempbuf[0] = 0;\n\n    if (x->x_text->te_type == T_ATOM)\n        rtext_formatatom(x, widthp, heightp, indexp,\n            tempbuf, &outchars_b, &selstart_b,  &selend_b,\n            fontwidth, fontheight);\n    else rtext_formattext(x, widthp, heightp, indexp,\n            tempbuf, &outchars_b, &selstart_b, &selend_b,\n            fontwidth, fontheight);\n    tempbuf[outchars_b]=0;\n\n    if (action && x->x_text->te_width && x->x_text->te_type != T_ATOM)\n    {\n            /* if our width is specified but the \"natural\" width is the\n            same as the specified width, set specified width to zero\n            so future text editing will automatically change width.\n            Except atoms whose content changes at runtime. */\n        int widthwas = x->x_text->te_width, newwidth = 0, newheight = 0,\n            newindex = 0;\n        x->x_text->te_width = 0;\n        rtext_senditup(x, 0, &newwidth, &newheight, &newindex);\n        if (newwidth != *widthp)\n            x->x_text->te_width = widthwas;\n    }\n\n    if (action && !canvas->gl_havewindow)\n        action = 0;\n\n    if (action == SEND_FIRST)\n    {\n        const char *tags[] = {x->x_tag, rtext_gettype(x)->s_name, \"text\"};\n        int lmargin = LMARGIN, tmargin = TMARGIN;\n        if (glist_getzoom(x->x_glist) > 1)\n        {\n            /* zoom margins */\n            lmargin *= glist_getzoom(x->x_glist);\n            tmargin *= glist_getzoom(x->x_glist);\n        }\n            /* we add an extra space to the string just in case the last\n            character is an unescaped backslash ('\\') which would have confused\n            tcl/tk by escaping the close brace otherwise.  The GUI code\n            drops the last character in the string. */\n        pdgui_vmess(\"pdtk_text_new\", \"c S ii s i r\",\n            canvas,\n            3, tags,\n            text_xpix(x->x_text, x->x_glist) + lmargin, text_ypix(x->x_text, x->x_glist) + tmargin,\n            tempbuf,\n            guifontsize,\n            (glist_isselected(x->x_glist, &x->x_text->te_g)? \"blue\" : \"black\"));\n    }\n    else if (action == SEND_UPDATE)\n    {\n        pdgui_vmess(\"pdtk_text_set\", \"cs s\",\n                  canvas, x->x_tag,\n                  tempbuf);\n        if (*widthp != x->x_drawnwidth || *heightp != x->x_drawnheight)\n            text_drawborder(x->x_text, x->x_glist, x->x_tag,\n                *widthp, *heightp, 0);\n        if (x->x_active)\n        {\n            if (selend_b > selstart_b)\n            {\n                pdgui_vmess(0, \"crr si\",\n                    canvas, \"select\", \"from\",\n                    x->x_tag, u8_charnum(x->x_buf, selstart_b));\n                pdgui_vmess(0, \"crr si\",\n                    canvas, \"select\", \"to\",\n                    x->x_tag, u8_charnum(x->x_buf, selend_b) - 1);\n                pdgui_vmess(0, \"crs\", canvas, \"focus\", \"\");\n            }\n            else\n            {\n                pdgui_vmess(0, \"crr\", canvas, \"select\", \"clear\");\n                pdgui_vmess(0, \"cr si\", canvas, \"icursor\", x->x_tag, u8_charnum(x->x_buf, selstart_b));\n                pdgui_vmess(\"focus\", \"c\", canvas);\n                pdgui_vmess(0, \"crs\", canvas, \"focus\", x->x_tag);\n            }\n        }\n    }\n    x->x_drawnwidth = *widthp;\n    x->x_drawnheight = *heightp;\n\n    if (tempbuf != smallbuf)\n        t_freebytes(tempbuf, 2 * x->x_bufsize + 1);\n}\n\n    /* remake text buffer from binbuf */\nvoid rtext_retext(t_rtext *x)\n{\n    int w = 0, h = 0, indx;\n    t_text *text = x->x_text;\n    t_freebytes(x->x_buf, x->x_bufsize + 1); /* extra 0 byte */\n    binbuf_gettext(text->te_binbuf, &x->x_buf, &x->x_bufsize);\n        /* allocate extra space for hidden null terminator */\n    x->x_buf = resizebytes(x->x_buf, x->x_bufsize, x->x_bufsize+1);\n    x->x_buf[x->x_bufsize] = 0;\n    rtext_senditup(x, SEND_UPDATE, &w, &h, &indx);\n}\n\n/* find the rtext that goes with a text item */\nt_rtext *glist_findrtext(t_glist *gl, t_text *who)\n{\n    t_rtext *x;\n    if (!gl->gl_editor)\n        canvas_create_editor(gl);\n    for (x = gl->gl_editor->e_rtext; x && x->x_text != who; x = x->x_next)\n        ;\n    return (x);\n}\n\nint rtext_width(t_rtext *x)\n{\n    int w = 0, h = 0, indx;\n    rtext_senditup(x, SEND_CHECK, &w, &h, &indx);\n    return (w);\n}\n\nint rtext_height(t_rtext *x)\n{\n    int w = 0, h = 0, indx;\n    rtext_senditup(x, SEND_CHECK, &w, &h, &indx);\n    return (h);\n}\n\nvoid rtext_draw(t_rtext *x)\n{\n    int w = 0, h = 0, indx;\n    rtext_senditup(x, SEND_FIRST, &w, &h, &indx);\n}\n\nvoid rtext_erase(t_rtext *x)\n{\n    pdgui_vmess(0, \"crs\", glist_getcanvas(x->x_glist), \"delete\", x->x_tag);\n}\n\nvoid rtext_displace(t_rtext *x, int dx, int dy)\n{\n    pdgui_vmess(0, \"crs ii\", glist_getcanvas(x->x_glist), \"move\", x->x_tag,\n        dx, dy);\n}\n\nvoid rtext_select(t_rtext *x, int state)\n{\n    pdgui_vmess(0, \"crs rr\",\n        glist_getcanvas(x->x_glist), \"itemconfigure\", x->x_tag,\n        \"-fill\", (state? \"blue\" : \"black\"));\n}\n\nvoid gatom_undarken(t_text *x);\n\nvoid rtext_activate(t_rtext *x, int state)\n{\n    int w = 0, h = 0, indx;\n    t_glist *glist = x->x_glist;\n    t_canvas *canvas = glist_getcanvas(glist);\n    if (state)\n    {\n        pdgui_vmess(\"pdtk_text_editing\", \"^si\", canvas, x->x_tag, 1);\n        glist->gl_editor->e_textedfor = x;\n        glist->gl_editor->e_textdirty = 0;\n        x->x_dragfrom = x->x_selstart = 0;\n        x->x_selend = x->x_bufsize;\n        x->x_active = 1;\n    }\n    else\n    {\n        pdgui_vmess(\"pdtk_text_editing\", \"^si\", canvas, \"\", 0);\n        if (glist->gl_editor->e_textedfor == x)\n            glist->gl_editor->e_textedfor = 0;\n        x->x_active = 0;\n    }\n    rtext_senditup(x, SEND_UPDATE, &w, &h, &indx);\n}\n\n    /* figure out which atom a click falls into if any; -1 if you\n    clicked on a space or something */\nint rtext_findatomfor(t_rtext *x, int xpos, int ypos)\n{\n    int w = xpos, h = ypos, indx, natom = 0, i, gotone = 0;\n        /* get byte index of character clicked on */\n    rtext_senditup(x, SEND_UPDATE, &w, &h, &indx);\n        /* search through for whitespace before that index */\n    for (i = 0; i <= indx; i++)\n    {\n        if (x->x_buf[i] == ';' || x->x_buf[i] == ',')\n            natom++, gotone = 0;\n        else if (x->x_buf[i] == ' ' || x->x_buf[i] == '\\n')\n            gotone = 0;\n        else\n        {\n            if (!gotone)\n                natom++;\n            gotone = 1;\n        }\n    }\n    return (natom-1);\n}\n\nvoid gatom_key(void *z, t_symbol *keysym, t_floatarg f);\n\nvoid rtext_key(t_rtext *x, int keynum, t_symbol *keysym)\n{\n    int w = 0, h = 0, indx, i, newsize, ndel;\n    char *s1, *s2;\n        /* CR to atom boxes sends message and resets */\n    if (keynum == '\\n' && x->x_text->te_type == T_ATOM)\n    {\n        gatom_key(x->x_text, keysym, keynum);\n        return;\n    }\n    if (keynum)\n    {\n        int n = keynum;\n        if (n == '\\r') n = '\\n';\n        if (n == '\\b')  /* backspace */\n        {\n                    /* LATER delete the box if all text is selected...\n                    this causes reentrancy problems now. */\n            /* if ((!x->x_selstart) && (x->x_selend == x->x_bufsize))\n            {\n                ....\n            } */\n            if (x->x_selstart && (x->x_selstart == x->x_selend))\n                u8_dec(x->x_buf, &x->x_selstart);\n        }\n        else if (n == 127)      /* delete */\n        {\n            if (x->x_selend < x->x_bufsize && (x->x_selstart == x->x_selend))\n                u8_inc(x->x_buf, &x->x_selend);\n        }\n\n        ndel = x->x_selend - x->x_selstart;\n        for (i = x->x_selend; i < x->x_bufsize; i++)\n            x->x_buf[i- ndel] = x->x_buf[i];\n        newsize = x->x_bufsize - ndel;\n            /* allocate extra space for hidden null terminator */\n        x->x_buf = resizebytes(x->x_buf, x->x_bufsize, newsize+1);\n        x->x_buf[newsize] = 0;\n        x->x_bufsize = newsize;\n\n/* at Guenter's suggestion, use 'n>31' to test whether a character might\nbe printable in whatever 8-bit character set we find ourselves. */\n\n/*-- moo:\n  ... but test with \"<\" rather than \"!=\" in order to accommodate unicode\n  codepoints for n (which we get since Tk is sending the \"%A\" substitution\n  for bind <Key>), effectively reducing the coverage of this clause to 7\n  bits.  Case n>127 is covered by the next clause.\n*/\n        if (n == '\\n' || (n > 31 && n < 127))\n        {\n            newsize = x->x_bufsize+1;\n                /* allocate extra space for hidden null terminator */\n            x->x_buf = resizebytes(x->x_buf, x->x_bufsize, newsize+1);\n            for (i = x->x_bufsize; i > x->x_selstart; i--)\n                x->x_buf[i] = x->x_buf[i-1];\n            x->x_buf[x->x_selstart] = n;\n            x->x_buf[newsize] = 0;\n            x->x_bufsize = newsize;\n            x->x_selstart = x->x_selstart + 1;\n        }\n        /*--moo: check for unicode codepoints beyond 7-bit ASCII --*/\n        else if (n > 127)\n        {\n            int ch_nbytes = u8_wc_nbytes(n);\n            newsize = x->x_bufsize + ch_nbytes;\n                /* allocate extra space for hidden null terminator */\n            x->x_buf = resizebytes(x->x_buf, x->x_bufsize, newsize+1);\n\n            for (i = newsize-1; i > x->x_selstart; i--)\n                x->x_buf[i] = x->x_buf[i-ch_nbytes];\n            x->x_buf[newsize] = 0;\n            x->x_bufsize = newsize;\n            /*-- moo: assume canvas_key() has encoded keysym as UTF-8 */\n            strncpy(x->x_buf+x->x_selstart, keysym->s_name, ch_nbytes);\n            x->x_selstart = x->x_selstart + ch_nbytes;\n        }\n        x->x_selend = x->x_selstart;\n        x->x_glist->gl_editor->e_textdirty = 1;\n    }\n    else if (!strcmp(keysym->s_name, \"Home\"))\n    {\n        if (x->x_selend == x->x_selstart)\n        {\n            x->x_selend = x->x_selstart = 0;\n        }\n        else\n            x->x_selstart = 0;\n    }\n    else if (!strcmp(keysym->s_name, \"End\"))\n    {\n        if (x->x_selend == x->x_selstart)\n        {\n            x->x_selend = x->x_selstart = x->x_bufsize;\n        }\n        else\n            x->x_selend = x->x_bufsize;\n    }\n    else if (!strcmp(keysym->s_name, \"Right\"))\n    {\n        if (x->x_selend == x->x_selstart && x->x_selstart < x->x_bufsize)\n        {\n            u8_inc(x->x_buf, &x->x_selstart);\n            x->x_selend = x->x_selstart;\n        }\n        else\n            x->x_selstart = x->x_selend;\n    }\n    else if (!strcmp(keysym->s_name, \"Left\"))\n    {\n        if (x->x_selend == x->x_selstart && x->x_selstart > 0)\n        {\n            u8_dec(x->x_buf, &x->x_selstart);\n            x->x_selend = x->x_selstart;\n        }\n        else\n            x->x_selend = x->x_selstart;\n    }\n        /* this should be improved...  life's too short */\n    else if (!strcmp(keysym->s_name, \"Up\"))\n    {\n        if (x->x_selstart)\n            u8_dec(x->x_buf, &x->x_selstart);\n        while (x->x_selstart > 0 && x->x_buf[x->x_selstart] != '\\n')\n            u8_dec(x->x_buf, &x->x_selstart);\n        x->x_selend = x->x_selstart;\n    }\n    else if (!strcmp(keysym->s_name, \"Down\"))\n    {\n        while (x->x_selend < x->x_bufsize &&\n            x->x_buf[x->x_selend] != '\\n')\n            u8_inc(x->x_buf, &x->x_selend);\n        if (x->x_selend < x->x_bufsize)\n            u8_inc(x->x_buf, &x->x_selend);\n        x->x_selstart = x->x_selend;\n    }\n    rtext_senditup(x, SEND_UPDATE, &w, &h, &indx);\n}\n\nvoid rtext_mouse(t_rtext *x, int xval, int yval, int flag)\n{\n    int w = xval, h = yval, indx;\n    rtext_senditup(x, SEND_CHECK, &w, &h, &indx);\n    if (flag == RTEXT_DOWN)\n    {\n        x->x_dragfrom = x->x_selstart = x->x_selend = indx;\n    }\n    else if (flag == RTEXT_DBL)\n    {\n        int whereseparator, newseparator;\n        x->x_dragfrom = -1;\n        whereseparator = 0;\n        if ((newseparator = lastone(x->x_buf, ' ', indx)) > whereseparator)\n            whereseparator = newseparator+1;\n        if ((newseparator = lastone(x->x_buf, '\\n', indx)) > whereseparator)\n            whereseparator = newseparator+1;\n        if ((newseparator = lastone(x->x_buf, ';', indx)) > whereseparator)\n            whereseparator = newseparator+1;\n        if ((newseparator = lastone(x->x_buf, ',', indx)) > whereseparator)\n            whereseparator = newseparator+1;\n        x->x_selstart = whereseparator;\n\n        whereseparator = x->x_bufsize - indx;\n        if ((newseparator =\n            firstone(x->x_buf+indx, ' ', x->x_bufsize - indx)) >= 0 &&\n                newseparator < whereseparator)\n                    whereseparator = newseparator;\n        if ((newseparator =\n            firstone(x->x_buf+indx, '\\n', x->x_bufsize - indx)) >= 0 &&\n                newseparator < whereseparator)\n                    whereseparator = newseparator;\n        if ((newseparator =\n            firstone(x->x_buf+indx, ';', x->x_bufsize - indx)) >= 0 &&\n                newseparator < whereseparator)\n                    whereseparator = newseparator;\n        if ((newseparator =\n            firstone(x->x_buf+indx, ',', x->x_bufsize - indx)) >= 0 &&\n                newseparator < whereseparator)\n                    whereseparator = newseparator;\n        x->x_selend = indx + whereseparator;\n    }\n    else if (flag == RTEXT_SHIFT)\n    {\n        if (indx * 2 > x->x_selstart + x->x_selend)\n            x->x_dragfrom = x->x_selstart, x->x_selend = indx;\n        else\n            x->x_dragfrom = x->x_selend, x->x_selstart = indx;\n    }\n    else if (flag == RTEXT_DRAG)\n    {\n        if (x->x_dragfrom < 0)\n            return;\n        x->x_selstart = (x->x_dragfrom < indx ? x->x_dragfrom : indx);\n        x->x_selend = (x->x_dragfrom > indx ? x->x_dragfrom : indx);\n    }\n    rtext_senditup(x, SEND_UPDATE, &w, &h, &indx);\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/g_scalar.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* This file defines the \"scalar\" object, which is not a text object, just a\n\"gobj\".  Scalars have templates which describe their structures, which\ncan contain numbers, sublists, and arrays.\n\n*/\n\n#include <stdio.h>      /* for read/write to files */\n#include \"m_pd.h\"\n#include \"g_canvas.h\"\n\n/* ------------- gstubs and gpointers - safe pointing --------------- */\n\n/* create a gstub which is \"owned\" by a glist (gl) or an array (\"a\"). */\n\nt_gstub *gstub_new(t_glist *gl, t_array *a)\n{\n    t_gstub *gs = t_getbytes(sizeof(*gs));\n    if (gl)\n    {\n        gs->gs_which = GP_GLIST;\n        gs->gs_un.gs_glist = gl;\n    }\n    else\n    {\n        gs->gs_which = GP_ARRAY;\n        gs->gs_un.gs_array = a;\n    }\n    gs->gs_refcount = 0;\n    return (gs);\n}\n\n/* when a \"gpointer\" is set to point to this stub (so we can later chase\ndown the owner) we increase a reference count.  The following routine is called\nwhenever a gpointer is unset from pointing here.  If the owner is\ngone and the refcount goes to zero, we can free the gstub safely. */\n\nstatic void gstub_dis(t_gstub *gs)\n{\n    int refcount = --gs->gs_refcount;\n    if ((!refcount) && gs->gs_which == GP_NONE)\n        t_freebytes(gs, sizeof (*gs));\n    else if (refcount < 0) bug(\"gstub_dis\");\n}\n\n/* this routing is called by the owner to inform the gstub that it is\nbeing deleted.  If no gpointers are pointing here, we can free the gstub;\notherwise we wait for the last gstub_dis() to free it. */\n\nvoid gstub_cutoff(t_gstub *gs)\n{\n    gs->gs_which = GP_NONE;\n    if (gs->gs_refcount < 0) bug(\"gstub_cutoff\");\n    if (!gs->gs_refcount) t_freebytes(gs, sizeof (*gs));\n}\n\n/* call this to verify that a pointer is fresh, i.e., that it either\npoints to real data or to the head of a list, and that in either case\nthe object hasn't disappeared since this pointer was generated.\nUnless \"headok\" is set,  the routine also fails for the head of a list. */\n\nint gpointer_check(const t_gpointer *gp, int headok)\n{\n    t_gstub *gs = gp->gp_stub;\n    if (!gs) return (0);\n    if (gs->gs_which == GP_ARRAY)\n    {\n        if (gs->gs_un.gs_array->a_valid != gp->gp_valid) return (0);\n        else return (1);\n    }\n    else if (gs->gs_which == GP_GLIST)\n    {\n        if (!headok && !gp->gp_un.gp_scalar) return (0);\n        else if (gs->gs_un.gs_glist->gl_valid != gp->gp_valid) return (0);\n        else return (1);\n    }\n    else return (0);\n}\n\n    /* copy a pointer to another, assuming the second one hasn't yet been\n    initialized.  New gpointers should be initialized either by this\n    routine or by gpointer_init below. */\nvoid gpointer_copy(const t_gpointer *gpfrom, t_gpointer *gpto)\n{\n    *gpto = *gpfrom;\n    if (gpto->gp_stub)\n        gpto->gp_stub->gs_refcount++;\n    else bug(\"gpointer_copy\");\n}\n\n    /* clear a gpointer that was previously set, releasing the associated\n    gstub if this was the last reference to it. */\nvoid gpointer_unset(t_gpointer *gp)\n{\n    t_gstub *gs;\n    if ((gs = gp->gp_stub))\n    {\n        gstub_dis(gs);\n        gp->gp_stub = 0;\n    }\n}\n\nvoid gpointer_setglist(t_gpointer *gp, t_glist *glist, t_scalar *x)\n{\n    t_gstub *gs;\n    if ((gs = gp->gp_stub)) gstub_dis(gs);\n    gp->gp_stub = gs = glist->gl_stub;\n    gp->gp_valid = glist->gl_valid;\n    gp->gp_un.gp_scalar = x;\n    gs->gs_refcount++;\n}\n\nvoid gpointer_setarray(t_gpointer *gp, t_array *array, t_word *w)\n{\n    t_gstub *gs;\n    if ((gs = gp->gp_stub)) gstub_dis(gs);\n    gp->gp_stub = gs = array->a_stub;\n    gp->gp_valid = array->a_valid;\n    gp->gp_un.gp_w = w;\n    gs->gs_refcount++;\n}\n\nvoid gpointer_init(t_gpointer *gp)\n{\n    gp->gp_stub = 0;\n    gp->gp_valid = 0;\n    gp->gp_un.gp_scalar = 0;\n}\n\n/*********  random utility function to find a binbuf in a datum */\n\n/* get the template for the object pointer to.  Assumes we've already checked\nfreshness. */\n\nt_symbol *gpointer_gettemplatesym(const t_gpointer *gp)\n{\n    t_gstub *gs = gp->gp_stub;\n    if (gs->gs_which == GP_GLIST)\n    {\n        t_scalar *sc = gp->gp_un.gp_scalar;\n        if (sc)\n            return (sc->sc_template);\n        else return (0);\n    }\n    else\n    {\n        t_array *a = gs->gs_un.gs_array;\n        return (a->a_templatesym);\n    }\n}\n\nt_binbuf *pointertobinbuf(t_pd *x, t_gpointer *gp, t_symbol *s,\n    const char *fname)\n{\n    t_symbol *templatesym = gpointer_gettemplatesym(gp), *arraytype;\n    t_template *template;\n    int onset, type;\n    t_binbuf *b;\n    t_gstub *gs = gp->gp_stub;\n    t_word *vec;\n    if (!templatesym)\n    {\n        pd_error(x, \"%s: bad pointer\", fname);\n        return (0);\n    }\n    if (!(template = template_findbyname(templatesym)))\n    {\n        pd_error(x, \"%s: couldn't find template %s\", fname,\n            templatesym->s_name);\n        return (0);\n    }\n    if (!template_find_field(template, s, &onset, &type, &arraytype))\n    {\n        pd_error(x, \"%s: %s.%s: no such field\", fname,\n            templatesym->s_name, s->s_name);\n        return (0);\n    }\n    if (type != DT_TEXT)\n    {\n        pd_error(x, \"%s: %s.%s: not a list\", fname,\n            templatesym->s_name, s->s_name);\n        return (0);\n    }\n    if (gs->gs_which == GP_ARRAY)\n        vec = gp->gp_un.gp_w;\n    else vec = gp->gp_un.gp_scalar->sc_vec;\n    return (vec[onset].w_binbuf);\n}\n\nvoid word_init(t_word *wp, t_template *template, t_gpointer *gp)\n{\n    int i, nitems = template->t_n;\n    t_dataslot *datatypes = template->t_vec;\n    for (i = 0; i < nitems; i++, datatypes++, wp++)\n    {\n        int type = datatypes->ds_type;\n        if (type == DT_FLOAT)\n            wp->w_float = 0;\n        else if (type == DT_SYMBOL)\n            wp->w_symbol = &s_symbol;\n        else if (type == DT_ARRAY)\n            wp->w_array = array_new(datatypes->ds_arraytemplate, gp);\n        else if (type == DT_TEXT)\n            wp->w_binbuf = binbuf_new();\n    }\n}\n\nvoid word_restore(t_word *wp, t_template *template,\n    int argc, t_atom *argv)\n{\n    int i, nitems = template->t_n;\n    t_dataslot *datatypes = template->t_vec;\n    for (i = 0; i < nitems; i++, datatypes++, wp++)\n    {\n        int type = datatypes->ds_type;\n        if (type == DT_FLOAT)\n        {\n            t_float f;\n            if (argc)\n            {\n                f =  atom_getfloat(argv);\n                argv++, argc--;\n            }\n            else f = 0;\n            wp->w_float = f;\n        }\n        else if (type == DT_SYMBOL)\n        {\n            t_symbol *s;\n            if (argc)\n            {\n                s =  atom_getsymbol(argv);\n                argv++, argc--;\n            }\n            else s = &s_;\n            wp->w_symbol = s;\n        }\n    }\n    if (argc)\n        post(\"warning: word_restore: extra arguments\");\n}\n\nvoid word_free(t_word *wp, t_template *template)\n{\n    int i;\n    t_dataslot *dt;\n    for (dt = template->t_vec, i = 0; i < template->t_n; i++, dt++)\n    {\n        if (dt->ds_type == DT_ARRAY)\n            array_free(wp[i].w_array);\n        else if (dt->ds_type == DT_TEXT)\n            binbuf_free(wp[i].w_binbuf);\n    }\n}\n\nstatic int template_cancreate(t_template *template)\n{\n    int i, type, nitems = template->t_n;\n    t_dataslot *datatypes = template->t_vec;\n    t_template *elemtemplate;\n    for (i = 0; i < nitems; i++, datatypes++)\n        if (datatypes->ds_type == DT_ARRAY &&\n            (!(elemtemplate = template_findbyname(datatypes->ds_arraytemplate))\n                || !template_cancreate(elemtemplate)))\n    {\n        pd_error(0, \"%s: no such template\", datatypes->ds_arraytemplate->s_name);\n        return (0);\n    }\n    return (1);\n}\n\n/* ------------------- scalars ---------------------- */\n\nt_class *scalar_class;\n    /* make a new scalar and add to the glist.  We create a \"gp\" here which\n    will be used for array items to point back here.  This gp doesn't do\n    reference counting or \"validation\" updates though; the parent won't go away\n    without the contained arrays going away too.  The \"gp\" is copied out\n    by value in the word_init() routine so we can throw our copy away. */\n\nt_scalar *scalar_new(t_glist *owner, t_symbol *templatesym)\n{\n    t_scalar *x;\n    t_template *template;\n    t_gpointer gp;\n    gpointer_init(&gp);\n    template = template_findbyname(templatesym);\n    if (!template)\n    {\n        pd_error(0, \"scalar: couldn't find template %s\", templatesym->s_name);\n        return (0);\n    }\n    if (!template_cancreate(template))\n        return (0);\n    x = (t_scalar *)getbytes(sizeof(t_scalar) +\n        (template->t_n - 1) * sizeof(*x->sc_vec));\n    x->sc_gobj.g_pd = scalar_class;\n    x->sc_template = templatesym;\n    gpointer_setglist(&gp, owner, x);\n    word_init(x->sc_vec, template, &gp);\n    return (x);\n}\n\n    /* Pd method to create a new scalar, add it to a glist, and initialize\n    it from the message arguments. */\n\nvoid glist_scalar(t_glist *glist,\n    t_symbol *classname, int argc, t_atom *argv)\n{\n    t_symbol *templatesym =\n        canvas_makebindsym(atom_getsymbolarg(0, argc, argv));\n    t_binbuf *b;\n    int natoms, nextmsg = 0;\n    t_atom *vec;\n    if (!template_findbyname(templatesym))\n    {\n        pd_error(glist, \"%s: no such template\",\n            atom_getsymbolarg(0, argc, argv)->s_name);\n        return;\n    }\n\n    b = binbuf_new();\n    binbuf_restore(b, argc, argv);\n    natoms = binbuf_getnatom(b);\n    vec = binbuf_getvec(b);\n    canvas_readscalar(glist, natoms, vec, &nextmsg, 0);\n    binbuf_free(b);\n}\n\n/* -------------------- widget behavior for scalar ------------ */\nvoid scalar_getbasexy(t_scalar *x, t_float *basex, t_float *basey)\n{\n    t_template *template = template_findbyname(x->sc_template);\n    *basex = template_getfloat(template, gensym(\"x\"), x->sc_vec, 0);\n    *basey = template_getfloat(template, gensym(\"y\"), x->sc_vec, 0);\n}\n\nstatic void scalar_getrect(t_gobj *z, t_glist *owner,\n    int *xp1, int *yp1, int *xp2, int *yp2)\n{\n    t_scalar *x = (t_scalar *)z;\n    t_template *template = template_findbyname(x->sc_template);\n    t_canvas *templatecanvas = template_findcanvas(template);\n    int x1 = 0x7fffffff, x2 = -0x7fffffff, y1 = 0x7fffffff, y2 = -0x7fffffff;\n    t_gobj *y;\n    t_float basex, basey;\n    scalar_getbasexy(x, &basex, &basey);\n        /* if someone deleted the template canvas, we're just a point */\n    if (!templatecanvas)\n    {\n        x1 = x2 = glist_xtopixels(owner, basex);\n        y1 = y2 = glist_ytopixels(owner, basey);\n    }\n    else\n    {\n        x1 = y1 = 0x7fffffff;\n        x2 = y2 = -0x7fffffff;\n        for (y = templatecanvas->gl_list; y; y = y->g_next)\n        {\n            const t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd);\n            int nx1, ny1, nx2, ny2;\n            if (!wb) continue;\n            (*wb->w_parentgetrectfn)(y, owner,\n                x->sc_vec, template, basex, basey,\n                &nx1, &ny1, &nx2, &ny2);\n            if (nx1 < x1) x1 = nx1;\n            if (ny1 < y1) y1 = ny1;\n            if (nx2 > x2) x2 = nx2;\n            if (ny2 > y2) y2 = ny2;\n        }\n        if (x2 < x1 || y2 < y1)\n            x1 = y1 = x2 = y2 = 0;\n    }\n    /* post(\"scalar x1 %d y1 %d x2 %d y2 %d\", x1, y1, x2, y2); */\n    *xp1 = x1;\n    *yp1 = y1;\n    *xp2 = x2;\n    *yp2 = y2;\n}\n\nstatic void scalar_drawselectrect(t_scalar *x, t_glist *glist, int state)\n{\n    char tag[128];\n        /* FIXME: get rid of this tag (if it's unused) */\n    sprintf(tag, \"select%p\", x);\n    if (state)\n    {\n        int x1, y1, x2, y2;\n        scalar_getrect(&x->sc_gobj, glist, &x1, &y1, &x2, &y2);\n        x1--; x2++; y1--; y2++;\n        pdgui_vmess(0, \"crr iiiiiiiiii ri rr rs\",\n                  glist_getcanvas(glist), \"create\", \"line\",\n                  x1,y1, x1,y2, x2,y2, x2,y1, x1,y1,\n                  \"-width\", 0,\n                  \"-fill\", \"blue\",\n                  \"-tags\", tag);\n    } else {\n        pdgui_vmess(0, \"crs\", glist_getcanvas(glist), \"delete\", tag);\n    }\n}\n\nstatic void scalar_select(t_gobj *z, t_glist *owner, int state)\n{\n    t_scalar *x = (t_scalar *)z;\n    t_template *tmpl;\n    t_symbol *templatesym = x->sc_template;\n    t_atom at;\n    t_gpointer gp;\n    gpointer_init(&gp);\n    gpointer_setglist(&gp, owner, x);\n    SETPOINTER(&at, &gp);\n    if ((tmpl = template_findbyname(templatesym)))\n        template_notify(tmpl, (state ? gensym(\"select\") : gensym(\"deselect\")),\n            1, &at);\n    gpointer_unset(&gp);\n    scalar_drawselectrect(x, owner, state);\n}\n\nstatic void scalar_displace(t_gobj *z, t_glist *glist, int dx, int dy)\n{\n    t_scalar *x = (t_scalar *)z;\n    t_symbol *templatesym = x->sc_template;\n    t_template *template = template_findbyname(templatesym);\n    t_symbol *zz;\n    t_atom at[3];\n    t_gpointer gp;\n    int xonset, yonset, xtype, ytype, gotx, goty;\n    if (!template)\n    {\n        pd_error(0, \"scalar: couldn't find template %s\", templatesym->s_name);\n        return;\n    }\n    gotx = template_find_field(template, gensym(\"x\"), &xonset, &xtype, &zz);\n    if (gotx && (xtype != DT_FLOAT))\n        gotx = 0;\n    goty = template_find_field(template, gensym(\"y\"), &yonset, &ytype, &zz);\n    if (goty && (ytype != DT_FLOAT))\n        goty = 0;\n    if (gotx)\n        *(t_float *)(((char *)(x->sc_vec)) + xonset) +=\n            glist->gl_zoom * dx * (glist_pixelstox(glist, 1) - glist_pixelstox(glist, 0));\n    if (goty)\n        *(t_float *)(((char *)(x->sc_vec)) + yonset) +=\n            glist->gl_zoom * dy * (glist_pixelstoy(glist, 1) - glist_pixelstoy(glist, 0));\n    gpointer_init(&gp);\n    gpointer_setglist(&gp, glist, x);\n    SETPOINTER(&at[0], &gp);\n    SETFLOAT(&at[1], (t_float)dx);\n    SETFLOAT(&at[2], (t_float)dy);\n    template_notify(template, gensym(\"displace\"), 2, at);\n    scalar_redraw(x, glist);\n}\n\nstatic void scalar_activate(t_gobj *z, t_glist *owner, int state)\n{\n    /* post(\"scalar_activate %d\", state); */\n    /* later */\n}\n\nstatic void scalar_delete(t_gobj *z, t_glist *glist)\n{\n    /* nothing to do */\n}\n\nstatic void scalar_vis(t_gobj *z, t_glist *owner, int vis)\n{\n    t_scalar *x = (t_scalar *)z;\n    t_template *template = template_findbyname(x->sc_template);\n    t_canvas *templatecanvas = template_findcanvas(template);\n    t_gobj *y;\n    t_float basex, basey;\n    scalar_getbasexy(x, &basex, &basey);\n        /* if we don't know how to draw it, make a small rectangle */\n    if (!templatecanvas)\n    {\n        char tag[128];\n            /* FIXME: get rid of this tag (if it's unused) */\n        sprintf(tag, \"scalar%p\", x);\n\n        if (vis)\n        {\n            int x1 = glist_xtopixels(owner, basex);\n            int y1 = glist_ytopixels(owner, basey);\n            pdgui_vmess(0, \"crr iiii rs\",\n                      glist_getcanvas(owner),\n                      \"create\", \"rectangle\",\n                      x1-1,y1-1, x1+1,y1+1,\n                      \"-tags\", tag);\n        }\n        else\n            pdgui_vmess(0, \"crs\", glist_getcanvas(owner), \"delete\", tag);\n        return;\n    }\n\n    for (y = templatecanvas->gl_list; y; y = y->g_next)\n    {\n        const t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd);\n        if (!wb) continue;\n        (*wb->w_parentvisfn)(y, owner, x->sc_vec, template, basex, basey, vis);\n    }\n    if (glist_isselected(owner, &x->sc_gobj))\n    {\n        scalar_drawselectrect(x, owner, 0);\n        scalar_drawselectrect(x, owner, 1);\n    }\n    sys_unqueuegui(x);\n}\n\nstatic void scalar_doredraw(t_gobj *client, t_glist *glist)\n{\n    if (glist_isvisible(glist))\n    {\n        scalar_vis(client, glist, 0);\n        scalar_vis(client, glist, 1);\n    }\n}\n\nvoid scalar_redraw(t_scalar *x, t_glist *glist)\n{\n    if (glist_isvisible(glist))\n        sys_queuegui(x, glist, scalar_doredraw);\n}\n\nextern void template_notifyforscalar(t_template *template, t_glist *owner,\n    t_scalar *sc, t_symbol *s, int argc, t_atom *argv);\n\nint scalar_doclick(t_word *data, t_template *template, t_scalar *sc,\n    t_array *ap, struct _glist *owner,\n    t_float xloc, t_float yloc, int xpix, int ypix,\n    int shift, int alt, int dbl, int doit)\n{\n    int hit = 0;\n    t_canvas *templatecanvas = template_findcanvas(template);\n    t_gobj *y;\n    t_atom at[3];\n    t_float basex = template_getfloat(template, gensym(\"x\"), data, 0);\n    t_float basey = template_getfloat(template, gensym(\"y\"), data, 0);\n    SETFLOAT(at, 0); /* unused - this is later bashed to the gpointer */\n    SETFLOAT(at+1, basex + xloc);\n    SETFLOAT(at+2, basey + yloc);\n    if (doit)\n        template_notifyforscalar(template, owner,\n            sc, gensym(\"click\"), 3, at);\n    for (y = templatecanvas->gl_list; y; y = y->g_next)\n    {\n        const t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd);\n        if (!wb) continue;\n        if ((hit = (*wb->w_parentclickfn)(y, owner,\n            data, template, sc, ap, basex + xloc, basey + yloc,\n            xpix, ypix, shift, alt, dbl, doit)))\n                return (hit);\n    }\n    return (0);\n}\n\nstatic int scalar_click(t_gobj *z, struct _glist *owner,\n    int xpix, int ypix, int shift, int alt, int dbl, int doit)\n{\n    t_scalar *x = (t_scalar *)z;\n    t_template *template = template_findbyname(x->sc_template);\n    return (scalar_doclick(x->sc_vec, template, x, 0,\n        owner, 0, 0, xpix, ypix, shift, alt, dbl, doit));\n}\n\nstatic void scalar_save(t_gobj *z, t_binbuf *b)\n{\n    t_scalar *x = (t_scalar *)z;\n    t_binbuf *b2 = binbuf_new();\n    t_atom a, *argv;\n    int i, argc;\n    canvas_writescalar(x->sc_template, x->sc_vec, b2, 0);\n    binbuf_addv(b, \"ss\", &s__X, gensym(\"scalar\"));\n    binbuf_addbinbuf(b, b2);\n    binbuf_addsemi(b);\n    binbuf_free(b2);\n}\n\nstatic void scalar_properties(t_gobj *z, struct _glist *owner)\n{\n    t_scalar *x = (t_scalar *)z;\n    char *buf, buf2[80];\n    int bufsize;\n    t_binbuf *b;\n    glist_noselect(owner);\n    glist_select(owner, z);\n    b = glist_writetobinbuf(owner, 0);\n    binbuf_gettext(b, &buf, &bufsize);\n    binbuf_free(b);\n    pdgui_stub_vnew((t_pd*)owner, \"pdtk_data_dialog\", x,\n        \"p\", bufsize, buf);\n    t_freebytes(buf, bufsize);\n}\n\nstatic const t_widgetbehavior scalar_widgetbehavior =\n{\n    scalar_getrect,\n    scalar_displace,\n    scalar_select,\n    scalar_activate,\n    scalar_delete,\n    scalar_vis,\n    scalar_click,\n};\n\nstatic void scalar_free(t_scalar *x)\n{\n    int i;\n    t_dataslot *datatypes, *dt;\n    t_symbol *templatesym = x->sc_template;\n    t_template *template = template_findbyname(templatesym);\n    sys_unqueuegui(x);\n    if (!template)\n    {\n        pd_error(0, \"scalar: couldn't find template %s\", templatesym->s_name);\n        return;\n    }\n    word_free(x->sc_vec, template);\n    pdgui_stub_deleteforkey(x);\n        /* the \"size\" field in the class is zero, so Pd doesn't try to free\n        us automatically (see pd_free()) */\n    freebytes(x, sizeof(t_scalar) + (template->t_n - 1) * sizeof(*x->sc_vec));\n}\n\n/* ----------------- setup function ------------------- */\n\nvoid g_scalar_setup(void)\n{\n    scalar_class = class_new(gensym(\"scalar\"), 0, (t_method)scalar_free, 0,\n        CLASS_GOBJ, 0);\n    class_setwidget(scalar_class, &scalar_widgetbehavior);\n    class_setsavefn(scalar_class, scalar_save);\n    class_setpropertiesfn(scalar_class, scalar_properties);\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/g_slider.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution. */\n\n/* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */\n/* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja\n * refactored 2022 by IOhannes m zmölnig\n */\n\n#include <string.h>\n#include <stdio.h>\n#include \"m_pd.h\"\n\n#include \"g_all_guis.h\"\n#include <math.h>\n\n#define LMARGIN 3\n#define RMARGIN 2\n\n#define TMARGIN 2\n#define BMARGIN 3\n\n/* ------------ horizontal/vertical  slider ----------------------- */\n\nt_widgetbehavior slider_widgetbehavior;\nstatic t_class *slider_class;\n\n/* forward declarations */\nstatic void slider_set(t_slider *x, t_floatarg f);\n\n/* widget helper functions */\n\n/* cannot use iemgui's default draw_iolets, because\n * - we have to deal with those stupid offsets,...\n * - we want to make sure that the iolets are below the KNOB (rather than the LABEL)\n */\nstatic void slider_draw_io(t_slider* x, t_glist* glist, int old_snd_rcv_flags)\n{\n    const int zoom = IEMGUI_ZOOM(x);\n    t_canvas *canvas = glist_getcanvas(glist);\n    int xpos = text_xpix(&x->x_gui.x_obj, glist);\n    int ypos = text_ypix(&x->x_gui.x_obj, glist);\n    int iow = IOWIDTH * zoom, ioh = IEM_GUI_IOHEIGHT * zoom;\n    int lmargin = 0, tmargin=0, bmargin = 0;\n    char tag_object[128], tag_knob[128], tag[128];\n    char *tags[] = {tag_object, tag};\n\n    (void)old_snd_rcv_flags;\n\n    sprintf(tag_object, \"%pOBJ\", x);\n    sprintf(tag_knob, \"%pKNOB\", x);\n\n    if(x->x_orientation == horizontal)\n    {\n        lmargin = LMARGIN * zoom;\n    } else {\n        tmargin = TMARGIN * zoom;\n        bmargin = BMARGIN * zoom;\n    }\n\n    sprintf(tag, \"%pOUT%d\", x, 0);\n    pdgui_vmess(0, \"crs\", canvas, \"delete\", tag);\n    if(!x->x_gui.x_fsf.x_snd_able)\n    {\n        pdgui_vmess(0, \"crr iiii rs rS\", canvas, \"create\", \"rectangle\",\n            xpos - lmargin, ypos + x->x_gui.x_h + bmargin + zoom - ioh,\n            xpos - lmargin + iow, ypos + x->x_gui.x_h + bmargin,\n            \"-fill\", \"black\",\n            \"-tags\", 2, tags);\n\n            /* keep knob above outlet */\n        pdgui_vmess(0, \"crss\", canvas, \"lower\", tag, tag_knob);\n    }\n\n    sprintf(tag, \"%pIN%d\", x, 0);\n    pdgui_vmess(0, \"crs\", canvas, \"delete\", tag);\n    if(!x->x_gui.x_fsf.x_rcv_able)\n    {\n        pdgui_vmess(0, \"crr iiii rs rS\", canvas, \"create\", \"rectangle\",\n            xpos - lmargin, ypos - tmargin,\n            xpos - lmargin + iow, ypos - tmargin - zoom + ioh,\n            \"-fill\", \"black\",\n            \"-tags\", 2, tags);\n\n            /* keep knob above inlet */\n        pdgui_vmess(0, \"crss\", canvas, \"lower\", tag, tag_knob);\n    }\n}\n\nstatic void slider_knob_position(t_slider*x, t_glist *glist, int val, int *x0, int *y0, int *x1, int *y1)\n{\n    const int zoom = IEMGUI_ZOOM(x);\n    int xpos = text_xpix(&x->x_gui.x_obj, glist);\n    int ypos = text_ypix(&x->x_gui.x_obj, glist);\n    if(x->x_orientation == horizontal)\n    {\n        int r = xpos + val;\n        *x0 = r;\n        *y0 = ypos + (zoom + 1);\n        *x1 = r;\n        *y1 = ypos + x->x_gui.x_h - (zoom * 2);\n    } else {\n        int r = ypos + x->x_gui.x_h - val;\n        *x0 = xpos + (zoom + 1);\n        *y0 = r;\n        *x1 = xpos + x->x_gui.x_w - (zoom * 2);\n        *y1 = r;\n    }\n\n}\n\nstatic void slider_draw_config(t_slider* x, t_glist* glist)\n{\n    const int zoom = IEMGUI_ZOOM(x);\n    t_canvas *canvas = glist_getcanvas(glist);\n    t_iemgui *iemgui = &x->x_gui;\n    int xpos = text_xpix(&x->x_gui.x_obj, glist);\n    int ypos = text_ypix(&x->x_gui.x_obj, glist);\n    int val = ((x->x_val + 50)/100);\n    int iow = IOWIDTH * zoom, ioh = IEM_GUI_IOHEIGHT * zoom;\n    int lmargin = 0, rmargin = 0, tmargin = 0, bmargin = 0;\n    int a, b, c, d;\n    char tag[128];\n    t_atom fontatoms[3];\n    SETSYMBOL(fontatoms+0, gensym(iemgui->x_font));\n    SETFLOAT (fontatoms+1, -iemgui->x_fontsize*zoom);\n    SETSYMBOL(fontatoms+2, gensym(sys_fontweight));\n\n    if(x->x_orientation == horizontal)\n    {\n        lmargin = LMARGIN * zoom;\n        rmargin = RMARGIN * zoom;\n    } else {\n        tmargin = TMARGIN * zoom;\n        bmargin = BMARGIN * zoom;\n    }\n    slider_knob_position(x, glist, val, &a, &b, &c, &d);\n\n    sprintf(tag, \"%pBASE\", x);\n    pdgui_vmess(0, \"crs iiii\", canvas, \"coords\", tag,\n        xpos - lmargin, ypos - tmargin,\n        xpos + x->x_gui.x_w + rmargin, ypos + x->x_gui.x_h + bmargin);\n    pdgui_vmess(0, \"crs ri rk\", canvas, \"itemconfigure\", tag,\n        \"-width\", zoom,\n        \"-fill\", x->x_gui.x_bcol);\n\n    sprintf(tag, \"%pKNOB\", x);\n    pdgui_vmess(0, \"crs iiii\", canvas, \"coords\", tag,\n        a, b, c, d);\n    pdgui_vmess(0, \"crs ri rk\", canvas, \"itemconfigure\", tag,\n        \"-width\", 1 + 2 * zoom,\n        \"-outline\", x->x_gui.x_fcol);\n\n    sprintf(tag, \"%pLABEL\", x);\n    pdgui_vmess(0, \"crs ii\", canvas, \"coords\", tag,\n        xpos + x->x_gui.x_ldx * zoom, ypos + x->x_gui.x_ldy * zoom);\n\n    pdgui_vmess(0, \"crs rA rk\", canvas, \"itemconfigure\", tag,\n        \"-font\", 3, fontatoms,\n        \"-fill\", (x->x_gui.x_fsf.x_selected ? IEM_GUI_COLOR_SELECTED : x->x_gui.x_lcol));\n    iemgui_dolabel(x, &x->x_gui, x->x_gui.x_lab, 1);\n}\n\nstatic void slider_draw_new(t_slider *x, t_glist *glist)\n{\n    t_canvas *canvas = glist_getcanvas(glist);\n    char tag[128], tag_object[128];\n    char*tags[] = {tag_object, tag, \"label\", \"text\"};\n    sprintf(tag_object, \"%pOBJ\", x);\n\n\n    sprintf(tag, \"%pBASE\", x);\n    pdgui_vmess(0, \"crr iiii rS\", canvas, \"create\", \"rectangle\",\n         0, 0, 0, 0, \"-tags\", 2, tags);\n\n    sprintf(tag, \"%pKNOB\", x);\n    pdgui_vmess(0, \"crr iiii rS\", canvas, \"create\", \"rectangle\",\n         0, 0, 0, 0, \"-tags\", 2, tags);\n\n    sprintf(tag, \"%pLABEL\", x);\n    pdgui_vmess(0, \"crr ii rs rS\", canvas, \"create\", \"text\",\n         0, 0, \"-anchor\", \"w\", \"-tags\", 4, tags);\n\n    slider_draw_config(x, glist);\n    (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_IO);\n}\n\nstatic void slider_draw_select(t_slider* x, t_glist* glist)\n{\n    t_canvas *canvas = glist_getcanvas(glist);\n    int col = IEM_GUI_COLOR_NORMAL, lcol = x->x_gui.x_lcol;\n    char tag[128];\n\n    if(x->x_gui.x_fsf.x_selected)\n        col = lcol = IEM_GUI_COLOR_SELECTED;\n\n    sprintf(tag, \"%pBASE\", x);\n    pdgui_vmess(0, \"crs rk\", canvas, \"itemconfigure\", tag, \"-outline\", col);\n    sprintf(tag, \"%pLABEL\", x);\n    pdgui_vmess(0, \"crs rk\", canvas, \"itemconfigure\", tag, \"-fill\", lcol);\n}\n\nstatic void slider_draw_update(t_gobj *client, t_glist *glist)\n{\n    t_slider *x = (t_slider *)client;\n    int a, b, c, d;\n    if (glist_isvisible(glist))\n    {\n        const int zoom = IEMGUI_ZOOM(x);\n        t_canvas *canvas = glist_getcanvas(glist);\n        int xpos = text_xpix(&x->x_gui.x_obj, glist);\n        int ypos = text_ypix(&x->x_gui.x_obj, glist);\n        int val = ((x->x_val + 50) / 100) * zoom;\n        char tag[128];\n        sprintf(tag, \"%pKNOB\", x);\n\n        slider_knob_position(x, glist, val, &a, &b, &c, &d);\n        pdgui_vmess(0, \"crs iiii\", canvas, \"coords\", tag, a, b, c, d);\n    }\n}\n\n\n/* ------------------------ hsl widgetbehaviour----------------------------- */\n\n\nstatic void slider_getrect(t_gobj *z, t_glist *glist,\n                            int *xp1, int *yp1, int *xp2, int *yp2)\n{\n    t_slider* x = (t_slider*)z;\n    int zoom = glist_getzoom(glist);\n    int dx1=0, dx2=0, dy1=0, dy2=0;\n    if(x->x_orientation == horizontal)\n    {\n        dx1 = LMARGIN*zoom;\n        dx2 = (LMARGIN + RMARGIN)*zoom;\n    } else {\n        dy1 = TMARGIN*zoom;\n        dy2 = (TMARGIN + BMARGIN)*zoom;\n    }\n\n\n    *xp1 = text_xpix(&x->x_gui.x_obj, glist) - dx1;\n    *yp1 = text_ypix(&x->x_gui.x_obj, glist) - dy1;\n\n    *xp2 = *xp1 + x->x_gui.x_w + dx2;\n    *yp2 = *yp1 + x->x_gui.x_h + dy2;\n}\n\nstatic void slider_save(t_gobj *z, t_binbuf *b)\n{\n    t_slider *x = (t_slider *)z;\n    t_symbol *bflcol[3];\n    t_symbol *srl[3];\n\n    iemgui_save(&x->x_gui, srl, bflcol);\n    binbuf_addv(b, \"ssiisiiffiisssiiiisssii\", gensym(\"#X\"), gensym(\"obj\"),\n                (int)x->x_gui.x_obj.te_xpix, (int)x->x_gui.x_obj.te_ypix,\n                gensym((x->x_orientation==horizontal)?\"hsl\":\"vsl\"),\n                x->x_gui.x_w/IEMGUI_ZOOM(x), x->x_gui.x_h/IEMGUI_ZOOM(x),\n                (t_float)x->x_min, (t_float)x->x_max,\n                x->x_lin0_log1, iem_symargstoint(&x->x_gui.x_isa),\n                srl[0], srl[1], srl[2],\n                x->x_gui.x_ldx, x->x_gui.x_ldy,\n                iem_fstyletoint(&x->x_gui.x_fsf), x->x_gui.x_fontsize,\n                bflcol[0], bflcol[1], bflcol[2],\n                x->x_gui.x_isa.x_loadinit?x->x_val:0, x->x_steady);\n    binbuf_addv(b, \";\");\n}\n\nstatic int slider_check_range(t_slider *x, int v)\n{\n    if(v < IEM_SL_MINSIZE * IEMGUI_ZOOM(x))\n        v = IEM_SL_MINSIZE * IEMGUI_ZOOM(x);\n    if(x->x_val > (v * 100 - 100))\n    {\n        x->x_val = v * 100 - 100;\n    }\n    if(x->x_lin0_log1)\n        x->x_k = log(x->x_max / x->x_min) / (double)(v/IEMGUI_ZOOM(x) - 1);\n    else\n        x->x_k = (x->x_max - x->x_min) / (double)(v/IEMGUI_ZOOM(x) - 1);\n\n    return v;\n}\n\nstatic void slider_check_minmax(t_slider *x, double min, double max, t_float value)\n{\n    if(x->x_lin0_log1)\n    {\n        if((min == 0.0) && (max == 0.0))\n            max = 1.0;\n        if(max > 0.0)\n        {\n            if(min <= 0.0)\n                min = 0.01 * max;\n        }\n        else\n        {\n            if(min > 0.0)\n                max = 0.01 * min;\n        }\n    }\n    x->x_min = min;\n    x->x_max = max;\n    if(x->x_lin0_log1)\n        x->x_k = log(x->x_max/x->x_min) / (double)(value/IEMGUI_ZOOM(x) - 1);\n    else\n        x->x_k = (x->x_max - x->x_min) / (double)(value/IEMGUI_ZOOM(x) - 1);\n}\n\nstatic void slider_properties(t_gobj *z, t_glist *owner)\n{\n    t_slider *x = (t_slider *)z;\n    const char*objname, *rangeA, *rangeB;\n    int minWidth, minHeight;\n\n    if(x->x_orientation == horizontal)\n    {\n        objname = \"hsl\";\n        minWidth = IEM_SL_MINSIZE;\n        minHeight = IEM_GUI_MINSIZE;\n    } else {\n        objname = \"vsl\";\n        minWidth = IEM_GUI_MINSIZE;\n        minHeight = IEM_SL_MINSIZE;\n    }\n\n\n    iemgui_new_dialog(x, &x->x_gui, objname,\n                      x->x_gui.x_w/IEMGUI_ZOOM(x), minWidth,\n                      x->x_gui.x_h/IEMGUI_ZOOM(x), minHeight,\n                      x->x_min, x->x_max,\n                      0,\n                      x->x_lin0_log1, \"linear\", \"logarithmic\",\n                      1, x->x_steady, -1);\n}\n\n    /* compute numeric value (fval) from pixel location (val) and range */\nstatic t_float slider_getfval(t_slider *x)\n{\n    t_float fval;\n    int rounded_val = (x->x_gui.x_fsf.x_finemoved) ? x->x_val : (x->x_val / 100) * 100;\n\n    /* if rcv==snd, don't round the value to prevent bad dragging when zoomed-in */\n    if(x->x_gui.x_fsf.x_snd_able && (x->x_gui.x_snd == x->x_gui.x_rcv))\n        rounded_val = x->x_val;\n\n    if (x->x_lin0_log1)\n        fval = x->x_min * exp(x->x_k * (double)(rounded_val) * 0.01);\n    else fval = (double)(rounded_val) * 0.01 * x->x_k + x->x_min;\n    if ((fval < 1.0e-10) && (fval > -1.0e-10))\n        fval = 0.0;\n    return (fval);\n}\n\nstatic void slider_bang(t_slider *x)\n{\n    double out;\n\n    if (pd_compatibilitylevel < 46)\n        out = slider_getfval(x);\n    else out = x->x_fval;\n    outlet_float(x->x_gui.x_obj.ob_outlet, out);\n    if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)\n        pd_float(x->x_gui.x_snd->s_thing, out);\n}\n\nstatic void slider_dialog(t_slider *x, t_symbol *s, int argc, t_atom *argv)\n{\n    t_symbol *srl[3];\n    int w = (int)atom_getfloatarg(0, argc, argv);\n    int h = (int)atom_getfloatarg(1, argc, argv);\n    double min = (double)atom_getfloatarg(2, argc, argv);\n    double max = (double)atom_getfloatarg(3, argc, argv);\n    int lilo = (int)atom_getfloatarg(4, argc, argv);\n    int steady = (int)atom_getfloatarg(17, argc, argv);\n    int sr_flags;\n    t_atom undo[18];\n\n    if(x->x_orientation == horizontal)\n        w *= IEMGUI_ZOOM(x);\n    else\n        h *= IEMGUI_ZOOM(x);\n\n    iemgui_setdialogatoms(&x->x_gui, 18, undo);\n    SETFLOAT(undo+2, x->x_min);\n    SETFLOAT(undo+3, x->x_max);\n    SETFLOAT(undo+4, x->x_lin0_log1);\n    SETFLOAT(undo+17, x->x_steady);\n\n    pd_undo_set_objectstate(x->x_gui.x_glist, (t_pd*)x, gensym(\"dialog\"),\n                            18, undo,\n                            argc, argv);\n\n    x->x_lin0_log1 = !!lilo;\n    x->x_steady = !!steady;\n\n    sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv);\n\n    if(x->x_orientation == horizontal)\n    {\n        x->x_gui.x_h = iemgui_clip_size(h) * IEMGUI_ZOOM(x);\n        x->x_gui.x_w = slider_check_range(x, w);\n        slider_check_minmax(x, min, max, x->x_gui.x_w);\n    } else {\n        x->x_gui.x_h = slider_check_range(x, h);\n        x->x_gui.x_w = iemgui_clip_size(w) * IEMGUI_ZOOM(x);\n        slider_check_minmax(x, min, max, x->x_gui.x_h);\n    }\n\n    iemgui_size(x, &x->x_gui);\n    slider_set(x, x->x_fval);\n}\n\nstatic void slider_motion(t_slider *x, t_floatarg dx, t_floatarg dy,\n    t_floatarg up)\n{\n    int old = x->x_val;\n    int pos;\n    float delta;\n\n    if (up != 0)\n        return;\n\n    if(x->x_orientation == horizontal)\n    {\n        pos = x->x_gui.x_w / IEMGUI_ZOOM(x);\n        delta = dx;\n    } else {\n        pos = x->x_gui.x_h / IEMGUI_ZOOM(x);\n        delta = -dy;\n    }\n\n    if(!x->x_gui.x_fsf.x_finemoved)\n        delta = (100 * delta) / IEMGUI_ZOOM(x);\n\n    x->x_pos += (int)delta;\n    x->x_val = x->x_pos;\n\n    if(x->x_val > (100 * pos - 100))\n    {\n        x->x_val = 100 * pos - 100;\n        x->x_pos += 50 / IEMGUI_ZOOM(x);\n        x->x_pos -= x->x_pos % (100 / IEMGUI_ZOOM(x));\n    }\n    if(x->x_val < 0)\n    {\n        x->x_val = 0;\n        x->x_pos -= 50 / IEMGUI_ZOOM(x);\n        x->x_pos -= x->x_pos % (100 / IEMGUI_ZOOM(x));\n    }\n    x->x_fval = slider_getfval(x);\n    if (old != x->x_val)\n    {\n        (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);\n        slider_bang(x);\n    }\n}\n\nstatic void slider_click(t_slider *x, t_floatarg xpos, t_floatarg ypos,\n    t_floatarg shift, t_floatarg ctrl, t_floatarg alt)\n{\n    t_float val;\n    int maxval;\n    if(x->x_orientation == horizontal)\n    {\n        val = (xpos - text_xpix(&x->x_gui.x_obj, x->x_gui.x_glist));\n        maxval = x->x_gui.x_w / IEMGUI_ZOOM(x);\n    } else {\n        val = (x->x_gui.x_h + text_ypix(&x->x_gui.x_obj, x->x_gui.x_glist) - ypos);\n        maxval = x->x_gui.x_h / IEMGUI_ZOOM(x);\n    }\n    maxval = 100 * maxval - 100;\n\n    if(!x->x_steady)\n        x->x_val = (int)((100.0 * val) / IEMGUI_ZOOM(x));\n    if(x->x_val > maxval)\n        x->x_val = maxval;\n\n    if(x->x_val < 0)\n        x->x_val = 0;\n    x->x_fval = slider_getfval(x);\n    x->x_pos = x->x_val;\n    (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);\n    slider_bang(x);\n    glist_grab(x->x_gui.x_glist, &x->x_gui.x_obj.te_g,\n        (t_glistmotionfn)slider_motion, 0, xpos, ypos);\n}\n\nstatic int slider_newclick(t_gobj *z, struct _glist *glist,\n    int xpix, int ypix, int shift, int alt, int dbl, int doit)\n{\n    t_slider* x = (t_slider *)z;\n\n    if(doit)\n    {\n        slider_click(x, (t_floatarg)xpix, (t_floatarg)ypix, (t_floatarg)shift,\n            0, (t_floatarg)alt);\n        if(shift)\n            x->x_gui.x_fsf.x_finemoved = 1;\n        else\n            x->x_gui.x_fsf.x_finemoved = 0;\n    }\n    return (1);\n}\n\nstatic void slider_set(t_slider *x, t_floatarg f)\n{\n    int old = x->x_val;\n    double g;\n\n    x->x_fval = f;\n    if (x->x_min > x->x_max)\n    {\n        if(f > x->x_min)\n            f = x->x_min;\n        if(f < x->x_max)\n            f = x->x_max;\n    }\n    else\n    {\n        if(f > x->x_max)\n            f = x->x_max;\n        if(f < x->x_min)\n            f = x->x_min;\n    }\n    if(x->x_lin0_log1)\n        g = log(f/x->x_min) / x->x_k;\n    else\n        g = (f - x->x_min) / x->x_k;\n    x->x_val = (int)(100.0*g + 0.49999);\n    if (x->x_val < 0) x->x_val = 0;\n    x->x_pos = x->x_val;\n    if(x->x_val != old)\n        (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);\n}\n\nstatic void slider_float(t_slider *x, t_floatarg f)\n{\n    slider_set(x, f);\n    if(x->x_gui.x_fsf.x_put_in2out)\n        slider_bang(x);\n}\n\nstatic void slider_size(t_slider *x, t_symbol *s, int ac, t_atom *av)\n{\n    int w = (int)atom_getfloatarg(0, ac, av);\n    int h = (int)atom_getfloatarg(1, ac, av);\n    if(x->x_orientation == horizontal)\n    {\n        x->x_gui.x_w = slider_check_range(x, w*IEMGUI_ZOOM(x));\n        if(ac > 1)\n            x->x_gui.x_h = iemgui_clip_size(h) * IEMGUI_ZOOM(x);\n    } else {\n        x->x_gui.x_w = iemgui_clip_size(w) * IEMGUI_ZOOM(x);\n        if(ac > 1)\n            x->x_gui.x_h = slider_check_range(x, h*IEMGUI_ZOOM(x));\n    }\n    iemgui_size((void *)x, &x->x_gui);\n    slider_set(x, x->x_fval);\n}\n\nstatic void slider_delta(t_slider *x, t_symbol *s, int ac, t_atom *av)\n{iemgui_delta((void *)x, &x->x_gui, s, ac, av);}\n\nstatic void slider_pos(t_slider *x, t_symbol *s, int ac, t_atom *av)\n{iemgui_pos((void *)x, &x->x_gui, s, ac, av);}\n\nstatic void slider_range(t_slider *x, t_symbol *s, int ac, t_atom *av)\n{\n    slider_check_minmax(x,\n        (double)atom_getfloatarg(0, ac, av),\n        (double)atom_getfloatarg(1, ac, av),\n        (x->x_orientation==horizontal)?x->x_gui.x_w:x->x_gui.x_h);\n    slider_set(x, x->x_fval);\n}\n\nstatic void slider_color(t_slider *x, t_symbol *s, int ac, t_atom *av)\n{iemgui_color((void *)x, &x->x_gui, s, ac, av);}\n\nstatic void slider_send(t_slider *x, t_symbol *s)\n{iemgui_send(x, &x->x_gui, s);}\n\nstatic void slider_receive(t_slider *x, t_symbol *s)\n{iemgui_receive(x, &x->x_gui, s);}\n\nstatic void slider_label(t_slider *x, t_symbol *s)\n{iemgui_label((void *)x, &x->x_gui, s);}\n\nstatic void slider_label_pos(t_slider *x, t_symbol *s, int ac, t_atom *av)\n{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);}\n\nstatic void slider_label_font(t_slider *x, t_symbol *s, int ac, t_atom *av)\n{iemgui_label_font((void *)x, &x->x_gui, s, ac, av);}\n\nstatic void slider_log(t_slider *x)\n{\n    x->x_lin0_log1 = 1;\n    slider_check_minmax(x, x->x_min, x->x_max,\n        (x->x_orientation==horizontal)?x->x_gui.x_w:x->x_gui.x_h);\n    slider_set(x, x->x_fval);\n}\n\nstatic void slider_lin(t_slider *x)\n{\n    double v = (x->x_orientation==horizontal)?x->x_gui.x_w:x->x_gui.x_h;\n    x->x_lin0_log1 = 0;\n    x->x_k = (x->x_max - x->x_min) / (v/IEMGUI_ZOOM(x) - 1);\n    slider_set(x, x->x_fval);\n}\n\nstatic void slider_init(t_slider *x, t_floatarg f)\n{\n    x->x_gui.x_isa.x_loadinit = (f == 0.0) ? 0 : 1;\n}\n\nstatic void slider_steady(t_slider *x, t_floatarg f)\n{\n    x->x_steady = (f == 0.0) ? 0 : 1;\n}\n\nstatic void slider_orientation(t_slider *x, t_floatarg forient)\n{\n    int orient = !!(int)forient;\n    if(orient != x->x_orientation)\n    {\n        int w = x->x_gui.x_w;\n        x->x_gui.x_w = x->x_gui.x_h;\n        x->x_gui.x_h = w;\n\n            /* urgh. for historical reasons,\n             * sliders have a different offset depending on their orientation.\n             * for backwards-compatibility, an ideal solution would handle\n             * these offsets during load/save,\n             * but we cannot access the object position in the constructor,\n             * so we need to do it here...\n             */\n        x->x_gui.x_obj.te_xpix += LMARGIN * ((horizontal==orient)?1:-1);\n        x->x_gui.x_obj.te_ypix -= TMARGIN * ((horizontal==orient)?1:-1);\n    }\n    x->x_orientation = orient;\n\n    iemgui_size(x, &x->x_gui);\n}\n\nstatic void slider_zoom(t_slider *x, t_floatarg f)\n{\n    iemgui_zoom(&x->x_gui, f);\n    (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);\n}\n\nstatic void slider_loadbang(t_slider *x, t_floatarg action)\n{\n    if (action == LB_LOAD && x->x_gui.x_isa.x_loadinit)\n    {\n        (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);\n        slider_bang(x);\n    }\n}\n\nstatic void *slider_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_slider *x = (t_slider *)iemgui_new(slider_class);\n    int lilo = 0, steady = 1;\n    int fs = x->x_gui.x_fontsize;\n    double min = 0.0, max = (double)(IEM_SL_DEFAULTSIZE-1);\n    t_float v = 0;\n    int w, h, ldx, ldy;\n\n    IEMGUI_SETDRAWFUNCTIONS(x, slider);\n\n    if('v' == *s->s_name)\n        x->x_orientation = vertical;\n\n    if(x->x_orientation == horizontal)\n    {\n        w = IEM_SL_DEFAULTSIZE;\n        h = IEM_GUI_DEFAULTSIZE;\n        w *= IEM_GUI_DEFAULTSIZE_SCALE; /* keep aspect ratio */\n\n        ldx = -2;\n        ldy = -8 * IEM_GUI_DEFAULTSIZE_SCALE;\n    } else {\n        w = IEM_GUI_DEFAULTSIZE;\n        h = IEM_SL_DEFAULTSIZE;\n        h *= IEM_GUI_DEFAULTSIZE_SCALE; /* keep aspect ratio */\n\n        ldx = 0;\n        ldy = -9;\n    }\n\n    if(((argc == 17)||(argc == 18))&&IS_A_FLOAT(argv,0)&&IS_A_FLOAT(argv,1)\n       &&IS_A_FLOAT(argv,2)&&IS_A_FLOAT(argv,3)\n       &&IS_A_FLOAT(argv,4)&&IS_A_FLOAT(argv,5)\n       &&(IS_A_SYMBOL(argv,6)||IS_A_FLOAT(argv,6))\n       &&(IS_A_SYMBOL(argv,7)||IS_A_FLOAT(argv,7))\n       &&(IS_A_SYMBOL(argv,8)||IS_A_FLOAT(argv,8))\n       &&IS_A_FLOAT(argv,9)&&IS_A_FLOAT(argv,10)\n       &&IS_A_FLOAT(argv,11)&&IS_A_FLOAT(argv,12)&&IS_A_FLOAT(argv,16))\n    {\n        w = (int)atom_getfloatarg(0, argc, argv);\n        h = (int)atom_getfloatarg(1, argc, argv);\n        min = (double)atom_getfloatarg(2, argc, argv);\n        max = (double)atom_getfloatarg(3, argc, argv);\n        lilo = (int)atom_getfloatarg(4, argc, argv);\n        iem_inttosymargs(&x->x_gui.x_isa, atom_getfloatarg(5, argc, argv));\n        iemgui_new_getnames(&x->x_gui, 6, argv);\n        ldx = (int)atom_getfloatarg(9, argc, argv);\n        ldy = (int)atom_getfloatarg(10, argc, argv);\n        iem_inttofstyle(&x->x_gui.x_fsf, atom_getfloatarg(11, argc, argv));\n        fs = (int)atom_getfloatarg(12, argc, argv);\n        iemgui_all_loadcolors(&x->x_gui, argv+13, argv+14, argv+15);\n        v = atom_getfloatarg(16, argc, argv);\n    }\n    else iemgui_new_getnames(&x->x_gui, 6, 0);\n    if((argc == 18)&&IS_A_FLOAT(argv,17))\n        steady = (int)atom_getfloatarg(17, argc, argv);\n    x->x_gui.x_fsf.x_snd_able = (0 != x->x_gui.x_snd);\n    x->x_gui.x_fsf.x_rcv_able = (0 != x->x_gui.x_rcv);\n    if (x->x_gui.x_isa.x_loadinit)\n        x->x_val = v;\n    else x->x_val = 0;\n    if(lilo != 0) lilo = 1;\n    x->x_lin0_log1 = lilo;\n    if(steady != 0) steady = 1;\n    x->x_steady = steady;\n    if(x->x_gui.x_fsf.x_font_style == 1) strcpy(x->x_gui.x_font, \"helvetica\");\n    else if(x->x_gui.x_fsf.x_font_style == 2) strcpy(x->x_gui.x_font, \"times\");\n    else { x->x_gui.x_fsf.x_font_style = 0;\n        strcpy(x->x_gui.x_font, sys_font); }\n    if(x->x_gui.x_fsf.x_rcv_able) pd_bind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv);\n    x->x_gui.x_ldx = ldx;\n    x->x_gui.x_ldy = ldy;\n    x->x_gui.x_fontsize = (fs < 4)?4:fs;\n\n    if(x->x_orientation == horizontal)\n    {\n        x->x_gui.x_w = slider_check_range(x, w);\n        x->x_gui.x_h = iemgui_clip_size(h);\n    } else {\n        x->x_gui.x_w = iemgui_clip_size(w);\n        x->x_gui.x_h = slider_check_range(x, h);\n    }\n\n    iemgui_verify_snd_ne_rcv(&x->x_gui);\n    iemgui_newzoom(&x->x_gui);\n    slider_check_minmax(x, min, max,\n        (x->x_orientation==horizontal)?x->x_gui.x_w:x->x_gui.x_h);\n\n    outlet_new(&x->x_gui.x_obj, &s_float);\n    x->x_fval = slider_getfval(x);\n    return (x);\n}\n\nstatic void slider_free(t_slider *x)\n{\n    if(x->x_gui.x_fsf.x_rcv_able)\n        pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv);\n    pdgui_stub_deleteforkey(x);\n}\n\nvoid g_slider_setup(void)\n{\n    slider_class = class_new(gensym(\"hsl\"), (t_newmethod)slider_new,\n        (t_method)slider_free, sizeof(t_slider), 0, A_GIMME, 0);\n    class_addcreator((t_newmethod)slider_new, gensym(\"vsl\"), A_GIMME, 0);\n    class_addcreator((t_newmethod)slider_new, gensym(\"hslider\"), A_GIMME, 0);\n    class_addcreator((t_newmethod)slider_new, gensym(\"vslider\"), A_GIMME, 0);\n\n    class_addbang(slider_class, slider_bang);\n    class_addfloat(slider_class, slider_float);\n    class_addmethod(slider_class, (t_method)slider_click,\n        gensym(\"click\"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);\n    class_addmethod(slider_class, (t_method)slider_motion,\n        gensym(\"motion\"), A_FLOAT, A_FLOAT, A_DEFFLOAT, 0);\n    class_addmethod(slider_class, (t_method)slider_dialog,\n        gensym(\"dialog\"), A_GIMME, 0);\n    class_addmethod(slider_class, (t_method)slider_loadbang,\n        gensym(\"loadbang\"), A_DEFFLOAT, 0);\n    class_addmethod(slider_class, (t_method)slider_set,\n        gensym(\"set\"), A_FLOAT, 0);\n    class_addmethod(slider_class, (t_method)slider_size,\n        gensym(\"size\"), A_GIMME, 0);\n    class_addmethod(slider_class, (t_method)slider_delta,\n        gensym(\"delta\"), A_GIMME, 0);\n    class_addmethod(slider_class, (t_method)slider_pos,\n        gensym(\"pos\"), A_GIMME, 0);\n    class_addmethod(slider_class, (t_method)slider_range,\n        gensym(\"range\"), A_GIMME, 0);\n    class_addmethod(slider_class, (t_method)slider_color,\n        gensym(\"color\"), A_GIMME, 0);\n    class_addmethod(slider_class, (t_method)slider_send,\n        gensym(\"send\"), A_DEFSYM, 0);\n    class_addmethod(slider_class, (t_method)slider_receive,\n        gensym(\"receive\"), A_DEFSYM, 0);\n    class_addmethod(slider_class, (t_method)slider_label,\n        gensym(\"label\"), A_DEFSYM, 0);\n    class_addmethod(slider_class, (t_method)slider_label_pos,\n        gensym(\"label_pos\"), A_GIMME, 0);\n    class_addmethod(slider_class, (t_method)slider_label_font,\n        gensym(\"label_font\"), A_GIMME, 0);\n    class_addmethod(slider_class, (t_method)slider_log,\n        gensym(\"log\"), 0);\n    class_addmethod(slider_class, (t_method)slider_lin,\n        gensym(\"lin\"), 0);\n    class_addmethod(slider_class, (t_method)slider_init,\n        gensym(\"init\"), A_FLOAT, 0);\n    class_addmethod(slider_class, (t_method)slider_steady,\n        gensym(\"steady\"), A_FLOAT, 0);\n    class_addmethod(slider_class, (t_method)slider_orientation,\n        gensym(\"orientation\"), A_FLOAT, 0);\n    class_addmethod(slider_class, (t_method)slider_zoom,\n        gensym(\"zoom\"), A_CANT, 0);\n    slider_widgetbehavior.w_getrectfn =    slider_getrect;\n    slider_widgetbehavior.w_displacefn =   iemgui_displace;\n    slider_widgetbehavior.w_selectfn =     iemgui_select;\n    slider_widgetbehavior.w_activatefn =   NULL;\n    slider_widgetbehavior.w_deletefn =     iemgui_delete;\n    slider_widgetbehavior.w_visfn =        iemgui_vis;\n    slider_widgetbehavior.w_clickfn =      slider_newclick;\n    class_setwidget(slider_class, &slider_widgetbehavior);\n    class_sethelpsymbol(slider_class, gensym(\"sliders\"));\n    class_setsavefn(slider_class, slider_save);\n    class_setpropertiesfn(slider_class, slider_properties);\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/g_template.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n#include <string.h>\n#include <stdio.h>\n\n#include \"m_pd.h\"\n#include \"g_canvas.h\"\n\n/*\nThis file contains text objects you would put in a canvas to define a\ntemplate.  Templates describe objects of type \"array\" (g_array.c) and\n\"scalar\" (g_scalar.c).\n*/\n\n    /* the structure of a \"struct\" object (also the obsolete \"gtemplate\"\n    you get when using the name \"template\" in a box.) */\n\nstruct _gtemplate\n{\n    t_object x_obj;\n    t_template *x_template;\n    t_canvas *x_owner;\n    t_symbol *x_sym;\n    struct _gtemplate *x_next;\n    int x_argc;\n    t_atom *x_argv;\n};\n\nstruct _instancetemplate\n{\n    int curve_motion_field;\n    t_float curve_motion_xcumulative;\n    t_float curve_motion_xbase;\n    t_float curve_motion_xper;\n    t_float curve_motion_ycumulative;\n    t_float curve_motion_ybase;\n    t_float curve_motion_yper;\n    t_glist *curve_motion_glist;\n    t_scalar *curve_motion_scalar;\n    t_array *curve_motion_array;\n    t_word *curve_motion_wp;\n    t_template *curve_motion_template;\n    t_gpointer curve_motion_gpointer;\n    t_float array_motion_xcumulative;\n    t_float array_motion_ycumulative;\n    t_fielddesc *array_motion_xfield;\n    t_fielddesc *array_motion_yfield;\n    t_glist *array_motion_glist;\n    t_scalar *array_motion_scalar;\n    t_array *array_motion_array;\n    t_word *array_motion_wp;\n    t_template *array_motion_template;\n    int array_motion_npoints;\n    int array_motion_elemsize;\n    int array_motion_altkey;\n    t_float array_motion_initx;\n    t_float array_motion_xperpix;\n    t_float array_motion_yperpix;\n    int array_motion_lastx;\n    int array_motion_fatten;\n    t_float drawnumber_motion_ycumulative;\n    t_glist *drawnumber_motion_glist;\n    t_scalar *drawnumber_motion_scalar;\n    t_array *drawnumber_motion_array;\n    t_word *drawnumber_motion_wp;\n    t_template *drawnumber_motion_template;\n    t_gpointer drawnumber_motion_gpointer;\n    int drawnumber_motion_type;\n    int drawnumber_motion_firstkey;\n};\n\n\n/* ---------------- forward definitions ---------------- */\n\nstatic void template_addtolist(t_template *x);\nstatic void template_takeofflist(t_template *x);\nstatic void template_conformarray(t_template *tfrom, t_template *tto,\n    int *conformaction, t_array *a);\nstatic void template_conformglist(t_template *tfrom, t_template *tto,\n    t_glist *glist,  int *conformaction);\n\n/* ---------------------- storage ------------------------- */\n\nstatic t_class *gtemplate_class;\nstatic t_class *template_class;\n\n/* there's a pre-defined \"float\" template.  LATER should we bind this\nto a symbol such as \"pd-float\"??? */\n\n    /* return true if two dataslot definitions match */\nstatic int dataslot_matches(t_dataslot *ds1, t_dataslot *ds2,\n    int nametoo)\n{\n    return ((!nametoo || ds1->ds_name == ds2->ds_name) &&\n        ds1->ds_type == ds2->ds_type &&\n            (ds1->ds_type != DT_ARRAY ||\n                ds1->ds_arraytemplate == ds2->ds_arraytemplate));\n}\n\n/* -- templates, the active ingredient in gtemplates defined below. ------- */\n\n    /* add a template to the list */\nstatic void template_addtolist(t_template *x)\n{\n    x->t_next = pd_this->pd_templatelist;\n    pd_this->pd_templatelist = x;\n}\n\nstatic void template_takeofflist(t_template *x)\n{\n        /* take it off the template list */\n    if (x == pd_this->pd_templatelist) pd_this->pd_templatelist = x->t_next;\n    else\n    {\n        t_template *z;\n        for (z = pd_this->pd_templatelist; z->t_next != x; z = z->t_next)\n            if (!z->t_next) return;\n        z->t_next = x->t_next;\n    }\n}\n\nt_template *template_new(t_symbol *templatesym, int argc, t_atom *argv)\n{\n    t_template *x = (t_template *)pd_new(template_class);\n    x->t_n = 0;\n    x->t_vec = (t_dataslot *)t_getbytes(0);\n    x->t_next = 0;\n    template_addtolist(x);\n    while (argc > 0)\n    {\n        int newtype, oldn, newn;\n        t_symbol *newname, *newarraytemplate = &s_, *newtypesym;\n        if (argc < 2 || argv[0].a_type != A_SYMBOL ||\n            argv[1].a_type != A_SYMBOL)\n                goto bad;\n        newtypesym = argv[0].a_w.w_symbol;\n        newname = argv[1].a_w.w_symbol;\n        if (newtypesym == &s_float)\n            newtype = DT_FLOAT;\n        else if (newtypesym == &s_symbol)\n            newtype = DT_SYMBOL;\n                /* \"list\" is old name.. accepted here but never saved as such */\n        else if (newtypesym == gensym(\"text\") || newtypesym == &s_list)\n            newtype = DT_TEXT;\n        else if (newtypesym == gensym(\"array\"))\n        {\n            if (argc < 3 || argv[2].a_type != A_SYMBOL)\n            {\n                pd_error(x, \"array lacks element template or name\");\n                goto bad;\n            }\n            newarraytemplate = canvas_makebindsym(argv[2].a_w.w_symbol);\n            newtype = DT_ARRAY;\n            argc--;\n            argv++;\n        }\n        else\n        {\n            pd_error(x, \"%s: no such type\", newtypesym->s_name);\n            goto bad;\n        }\n        newn = (oldn = x->t_n) + 1;\n        x->t_vec = (t_dataslot *)t_resizebytes(x->t_vec,\n            oldn * sizeof(*x->t_vec), newn * sizeof(*x->t_vec));\n        x->t_n = newn;\n        x->t_vec[oldn].ds_type = newtype;\n        x->t_vec[oldn].ds_name = newname;\n        x->t_vec[oldn].ds_arraytemplate = newarraytemplate;\n    bad:\n        argc -= 2; argv += 2;\n    }\n    if (*templatesym->s_name)\n    {\n        x->t_sym = templatesym;\n        pd_bind(&x->t_pdobj, x->t_sym);\n    }\n    else x->t_sym = templatesym;\n    return (x);\n}\n\nint template_find_field(t_template *x, t_symbol *name, int *p_onset,\n    int *p_type, t_symbol **p_arraytype)\n{\n    t_template *t;\n    int i, n;\n    if (!x)\n    {\n        bug(\"template_find_field\");\n        return (0);\n    }\n    n = x->t_n;\n    for (i = 0; i < n; i++)\n        if (x->t_vec[i].ds_name == name)\n    {\n        *p_onset = i * sizeof(t_word);\n        *p_type = x->t_vec[i].ds_type;\n        *p_arraytype = x->t_vec[i].ds_arraytemplate;\n        return (1);\n    }\n    return (0);\n}\n\nt_float template_getfloat(t_template *x, t_symbol *fieldname, t_word *wp,\n    int loud)\n{\n    int onset, type;\n    t_symbol *arraytype;\n    t_float val = 0;\n    if (template_find_field(x, fieldname, &onset, &type, &arraytype))\n    {\n        if (type == DT_FLOAT)\n            val = *(t_float *)(((char *)wp) + onset);\n        else if (loud) pd_error(0, \"%s.%s: not a number\",\n            x->t_sym->s_name, fieldname->s_name);\n    }\n    else if (loud) pd_error(0, \"%s.%s: no such field\",\n        x->t_sym->s_name, fieldname->s_name);\n    return (val);\n}\n\nvoid template_setfloat(t_template *x, t_symbol *fieldname, t_word *wp,\n    t_float f, int loud)\n{\n    int onset, type;\n    t_symbol *arraytype;\n    if (template_find_field(x, fieldname, &onset, &type, &arraytype))\n     {\n        if (type == DT_FLOAT)\n            *(t_float *)(((char *)wp) + onset) = f;\n        else if (loud) pd_error(0, \"%s.%s: not a number\",\n            x->t_sym->s_name, fieldname->s_name);\n    }\n    else if (loud) pd_error(0, \"%s.%s: no such field\",\n        x->t_sym->s_name, fieldname->s_name);\n}\n\nt_symbol *template_getsymbol(t_template *x, t_symbol *fieldname, t_word *wp,\n    int loud)\n{\n    int onset, type;\n    t_symbol *arraytype;\n    t_symbol *val = &s_;\n    if (template_find_field(x, fieldname, &onset, &type, &arraytype))\n    {\n        if (type == DT_SYMBOL)\n            val = *(t_symbol **)(((char *)wp) + onset);\n        else if (loud) pd_error(0, \"%s.%s: not a symbol\",\n            x->t_sym->s_name, fieldname->s_name);\n    }\n    else if (loud) pd_error(0, \"%s.%s: no such field\",\n        x->t_sym->s_name, fieldname->s_name);\n    return (val);\n}\n\nvoid template_setsymbol(t_template *x, t_symbol *fieldname, t_word *wp,\n    t_symbol *s, int loud)\n{\n    int onset, type;\n    t_symbol *arraytype;\n    if (template_find_field(x, fieldname, &onset, &type, &arraytype))\n     {\n        if (type == DT_SYMBOL)\n            *(t_symbol **)(((char *)wp) + onset) = s;\n        else if (loud) pd_error(0, \"%s.%s: not a symbol\",\n            x->t_sym->s_name, fieldname->s_name);\n    }\n    else if (loud) pd_error(0, \"%s.%s: no such field\",\n        x->t_sym->s_name, fieldname->s_name);\n}\n\n    /* stringent check to see if a \"saved\" template, x2, matches the current\n        one (x1).  It's OK if x1 has additional scalar elements but not (yet)\n        arrays.  This is used for reading in \"data files\". */\nint template_match(t_template *x1, t_template *x2)\n{\n    int i;\n    if (x1->t_n < x2->t_n)\n        return (0);\n    for (i = x2->t_n; i < x1->t_n; i++)\n    {\n        if (x1->t_vec[i].ds_type == DT_ARRAY)\n                return (0);\n    }\n    if (x2->t_n > x1->t_n)\n        post(\"add elements...\");\n    for (i = 0; i < x2->t_n; i++)\n        if (!dataslot_matches(&x1->t_vec[i], &x2->t_vec[i], 1))\n            return (0);\n    return (1);\n}\n\n/* --------------- CONFORMING TO CHANGES IN A TEMPLATE ------------ */\n\n/* the following routines handle updating scalars to agree with changes\nin their template.  The old template is assumed to be the \"installed\" one\nso we can delete old items; but making new ones we have to avoid scalar_new\nwhich would make an old one whereas we will want a new one (but whose array\nelements might still be old ones.)\n    LATER deal with graphics updates too... */\n\n    /* conform the word vector of a scalar to the new template */\nstatic void template_conformwords(t_template *tfrom, t_template *tto,\n    int *conformaction, t_word *wfrom, t_word *wto)\n{\n    int nfrom = tfrom->t_n, nto = tto->t_n, i;\n    for (i = 0; i < nto; i++)\n    {\n        if (conformaction[i] >= 0)\n        {\n                /* we swap the two, in case it's an array or list, so that\n                when \"wfrom\" is deleted the old one gets cleaned up. */\n            t_word wwas = wto[i];\n            wto[i] = wfrom[conformaction[i]];\n            wfrom[conformaction[i]] = wwas;\n        }\n    }\n}\n\n    /* conform a scalar, recursively conforming arrays  */\nstatic t_scalar *template_conformscalar(t_template *tfrom, t_template *tto,\n    int *conformaction, t_glist *glist, t_scalar *scfrom)\n{\n    t_scalar *x;\n    t_gpointer gp;\n    int nto = tto->t_n, nfrom = tfrom->t_n, i;\n    t_template *scalartemplate;\n    /* post(\"conform scalar\"); */\n        /* possibly replace the scalar */\n    if (scfrom->sc_template == tfrom->t_sym)\n    {\n            /* see scalar_new() for comment about the gpointer. */\n        gpointer_init(&gp);\n        x = (t_scalar *)getbytes(sizeof(t_scalar) +\n            (tto->t_n - 1) * sizeof(*x->sc_vec));\n        x->sc_gobj.g_pd = scalar_class;\n        x->sc_template = tfrom->t_sym;\n        gpointer_setglist(&gp, glist, x);\n            /* Here we initialize to the new template, but array and list\n            elements will still belong to old template. */\n        word_init(x->sc_vec, tto, &gp);\n\n        template_conformwords(tfrom, tto, conformaction,\n            scfrom->sc_vec, x->sc_vec);\n\n            /* replace the old one with the new one in the list */\n        if (glist->gl_list == &scfrom->sc_gobj)\n        {\n            glist->gl_list = &x->sc_gobj;\n            x->sc_gobj.g_next = scfrom->sc_gobj.g_next;\n        }\n        else\n        {\n            t_gobj *y, *y2;\n            for (y = glist->gl_list; (y2 = y->g_next); y = y2)\n                if (y2 == &scfrom->sc_gobj)\n            {\n                x->sc_gobj.g_next = y2->g_next;\n                y->g_next = &x->sc_gobj;\n                goto nobug;\n            }\n            bug(\"template_conformscalar\");\n        nobug: ;\n        }\n            /* burn the old one */\n        pd_free(&scfrom->sc_gobj.g_pd);\n        scalartemplate = tto;\n    }\n    else\n    {\n        x = scfrom;\n        scalartemplate = template_findbyname(x->sc_template);\n    }\n        /* convert all array elements */\n    for (i = 0; i < scalartemplate->t_n; i++)\n    {\n        t_dataslot *ds = scalartemplate->t_vec + i;\n        if (ds->ds_type == DT_ARRAY)\n        {\n            template_conformarray(tfrom, tto, conformaction,\n                x->sc_vec[i].w_array);\n        }\n    }\n    return (x);\n}\n\n    /* conform an array, recursively conforming sublists and arrays  */\nstatic void template_conformarray(t_template *tfrom, t_template *tto,\n    int *conformaction, t_array *a)\n{\n    int i, j;\n    t_template *scalartemplate = 0;\n    if (a->a_templatesym == tfrom->t_sym)\n    {\n        /* the array elements must all be conformed */\n        int oldelemsize = sizeof(t_word) * tfrom->t_n,\n            newelemsize = sizeof(t_word) * tto->t_n;\n        char *newarray = getbytes(newelemsize * a->a_n);\n        char *oldarray = a->a_vec;\n        if (a->a_elemsize != oldelemsize)\n            bug(\"template_conformarray\");\n        for (i = 0; i < a->a_n; i++)\n        {\n            t_word *wp = (t_word *)(newarray + newelemsize * i);\n            word_init(wp, tto, &a->a_gp);\n            template_conformwords(tfrom, tto, conformaction,\n                (t_word *)(oldarray + oldelemsize * i), wp);\n            word_free((t_word *)(oldarray + oldelemsize * i), tfrom);\n        }\n        scalartemplate = tto;\n        a->a_vec = newarray;\n        freebytes(oldarray, oldelemsize * a->a_n);\n    }\n    else scalartemplate = template_findbyname(a->a_templatesym);\n        /* convert all arrays and sublist fields in each element of the array */\n    for (i = 0; i < a->a_n; i++)\n    {\n        t_word *wp = (t_word *)(a->a_vec + sizeof(t_word) * a->a_n * i);\n        for (j = 0; j < scalartemplate->t_n; j++)\n        {\n            t_dataslot *ds = scalartemplate->t_vec + j;\n            if (ds->ds_type == DT_ARRAY)\n            {\n                template_conformarray(tfrom, tto, conformaction,\n                    wp[j].w_array);\n            }\n        }\n    }\n}\n\n    /* this routine searches for every scalar in the glist that belongs\n    to the \"from\" template and makes it belong to the \"to\" template.  Descend\n    glists recursively.\n    We don't handle redrawing here; this is to be filled in LATER... */\n\nt_array *garray_getarray(t_garray *x);\n\nstatic void template_conformglist(t_template *tfrom, t_template *tto,\n    t_glist *glist,  int *conformaction)\n{\n    t_gobj *g;\n    /* post(\"conform glist %s\", glist->gl_name->s_name); */\n    for (g = glist->gl_list; g; g = g->g_next)\n    {\n        if (pd_class(&g->g_pd) == scalar_class)\n            g = &template_conformscalar(tfrom, tto, conformaction,\n                glist, (t_scalar *)g)->sc_gobj;\n        else if (pd_class(&g->g_pd) == canvas_class)\n            template_conformglist(tfrom, tto, (t_glist *)g, conformaction);\n        else if (pd_class(&g->g_pd) == garray_class)\n            template_conformarray(tfrom, tto, conformaction,\n                garray_getarray((t_garray *)g));\n    }\n}\n\n    /* globally conform all scalars from one template to another */\nvoid template_conform(t_template *tfrom, t_template *tto)\n{\n    int nto = tto->t_n, nfrom = tfrom->t_n, i, j,\n        *conformaction = (int *)getbytes(sizeof(int) * nto),\n        *conformedfrom = (int *)getbytes(sizeof(int) * nfrom), doit = 0;\n    for (i = 0; i < nto; i++)\n        conformaction[i] = -1;\n    for (i = 0; i < nfrom; i++)\n        conformedfrom[i] = 0;\n    for (i = 0; i < nto; i++)\n    {\n        t_dataslot *dataslot = &tto->t_vec[i];\n        for (j = 0; j < nfrom; j++)\n        {\n            t_dataslot *dataslot2 = &tfrom->t_vec[j];\n            if (dataslot_matches(dataslot, dataslot2, 1))\n            {\n                conformaction[i] = j;\n                conformedfrom[j] = 1;\n            }\n        }\n    }\n    for (i = 0; i < nto; i++)\n        if (conformaction[i] < 0)\n    {\n        t_dataslot *dataslot = &tto->t_vec[i];\n        for (j = 0; j < nfrom; j++)\n            if (!conformedfrom[j] &&\n                dataslot_matches(dataslot, &tfrom->t_vec[j], 0))\n        {\n            conformaction[i] = j;\n            conformedfrom[j] = 1;\n        }\n    }\n    if (nto != nfrom)\n        doit = 1;\n    else for (i = 0; i < nto; i++)\n        if (conformaction[i] != i)\n            doit = 1;\n\n    if (doit)\n    {\n        t_glist *gl;\n        for (gl = pd_getcanvaslist(); gl; gl = gl->gl_next)\n            template_conformglist(tfrom, tto, gl, conformaction);\n    }\n    freebytes(conformaction, sizeof(int) * nto);\n    freebytes(conformedfrom, sizeof(int) * nfrom);\n}\n\nt_template *template_findbyname(t_symbol *s)\n{\n    return ((t_template *)pd_findbyclass(s, template_class));\n}\n\nt_canvas *template_findcanvas(t_template *template)\n{\n    t_gtemplate *gt;\n    if (!template)\n    {\n        bug(\"template_findcanvas\");\n        return (0);\n    }\n    if (!(gt = template->t_list))\n        return (0);\n    return (gt->x_owner);\n    /* return ((t_canvas *)pd_findbyclass(template->t_sym, canvas_class)); */\n}\n\nvoid template_notify(t_template *template, t_symbol *s, int argc, t_atom *argv)\n{\n    if (template->t_list)\n        outlet_anything(template->t_list->x_obj.ob_outlet, s, argc, argv);\n}\n\n    /* bash the first of (argv) with a pointer to a scalar, and send on\n    to template as a notification message */\nvoid template_notifyforscalar(t_template *template, t_glist *owner,\n    t_scalar *sc, t_symbol *s, int argc, t_atom *argv)\n{\n    t_gpointer gp;\n    gpointer_init(&gp);\n    gpointer_setglist(&gp, owner, sc);\n    SETPOINTER(argv, &gp);\n    template_notify(template, s, argc, argv);\n    gpointer_unset(&gp);\n}\n\n    /* call this when reading a patch from a file to declare what templates\n    we'll need.  If there's already a template, check if it matches.\n    If it doesn't it's still OK as long as there are no \"struct\" (gtemplate)\n    objects hanging from it; we just conform everyone to the new template.\n    If there are still struct objects belonging to the other template, we're\n    in trouble.  LATER we'll figure out how to conform the new patch's objects\n    to the pre-existing struct. */\nstatic void *template_usetemplate(void *dummy, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    t_template *x;\n    t_symbol *templatesym =\n        canvas_makebindsym(atom_getsymbolarg(0, argc, argv));\n    if (!argc)\n        return (0);\n    argc--; argv++;\n            /* check if there's already a template by this name. */\n    if ((x = (t_template *)pd_findbyclass(templatesym, template_class)))\n    {\n        t_template *y = template_new(&s_, argc, argv), *y2;\n            /* If the new template is the same as the old one,\n            there's nothing to do.  */\n        if (!template_match(x, y))\n        {\n                /* Are there \"struct\" objects upholding this template? */\n            if (x->t_list)\n            {\n                    /* don't know what to do here! */\n                pd_error(0, \"%s: template mismatch\",\n                    templatesym->s_name);\n            }\n            else\n            {\n                    /* conform everyone to the new template */\n                template_conform(x, y);\n                pd_free(&x->t_pdobj);\n                y2 = template_new(templatesym, argc, argv);\n                y2->t_list = 0;\n            }\n        }\n        pd_free(&y->t_pdobj);\n    }\n        /* otherwise, just make one. */\n    else template_new(templatesym, argc, argv);\n    return (0);\n}\n\n    /* here we assume someone has already cleaned up all instances of this. */\nvoid template_free(t_template *x)\n{\n    if (*x->t_sym->s_name)\n        pd_unbind(&x->t_pdobj, x->t_sym);\n    t_freebytes(x->t_vec, x->t_n * sizeof(*x->t_vec));\n    template_takeofflist(x);\n}\n\nstatic void template_setup(void)\n{\n    template_class = class_new(gensym(\"template\"), 0, (t_method)template_free,\n        sizeof(t_template), CLASS_PD, 0);\n    class_addmethod(pd_canvasmaker, (t_method)template_usetemplate,\n        gensym(\"struct\"), A_GIMME, 0);\n\n}\n\n/* ---------------- gtemplates.  One per canvas. ----------- */\n\n/* \"Struct\": an object that searches for, and if necessary creates,\na template (above).  Other objects in the canvas then can give drawing\ninstructions for the template.  The template doesn't go away when the\n\"struct\" is deleted, so that you can replace it with\nanother one to add new fields, for example. */\n\nstatic void *gtemplate_donew(t_symbol *sym, int argc, t_atom *argv)\n{\n    t_gtemplate *x = (t_gtemplate *)pd_new(gtemplate_class);\n    t_template *t = template_findbyname(sym);\n    int i;\n    t_symbol *sx = gensym(\"x\");\n    x->x_owner = canvas_getcurrent();\n    x->x_next = 0;\n    x->x_sym = sym;\n    x->x_argc = argc;\n    x->x_argv = (t_atom *)getbytes(argc * sizeof(t_atom));\n    for (i = 0; i < argc; i++)\n        x->x_argv[i] = argv[i];\n\n        /* already have a template by this name? */\n    if (t)\n    {\n        x->x_template = t;\n            /* if it's already got a \"struct\" object we\n            just tack this one to the end of the list and leave it\n            there. */\n        if (t->t_list)\n        {\n            t_gtemplate *x2, *x3;\n            for (x2 = x->x_template->t_list; (x3 = x2->x_next); x2 = x3)\n                ;\n            x2->x_next = x;\n            post(\"template %s: warning: already exists.\", sym->s_name);\n        }\n        else\n        {\n                /* if there's none, we just replace the template with\n                our own and conform it. */\n            t_template *y = template_new(&s_, argc, argv);\n            canvas_redrawallfortemplate(t, 2);\n                /* Unless the new template is different from the old one,\n                there's nothing to do.  */\n            if (!template_match(t, y))\n            {\n                    /* conform everyone to the new template */\n                template_conform(t, y);\n                pd_free(&t->t_pdobj);\n                x->x_template = t = template_new(sym, argc, argv);\n            }\n            pd_free(&y->t_pdobj);\n            t->t_list = x;\n            canvas_redrawallfortemplate(t, 1);\n        }\n    }\n    else\n    {\n            /* otherwise make a new one and we're the only struct on it. */\n        x->x_template = t = template_new(sym, argc, argv);\n        t->t_list = x;\n    }\n    outlet_new(&x->x_obj, 0);\n    return (x);\n}\n\nstatic void *gtemplate_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_symbol *sym = atom_getsymbolarg(0, argc, argv);\n    if (argc >= 1)\n        argc--, argv++;\n    if (sym->s_name[0] == '-')\n        post(\"warning: struct '%s' initial '-' may confuse get/set, etc.\",\n            sym->s_name);\n    return (gtemplate_donew(canvas_makebindsym(sym), argc, argv));\n}\n\n    /* old version (0.34) -- delete 2003 or so */\nstatic void *gtemplate_new_old(t_symbol *s, int argc, t_atom *argv)\n{\n    t_symbol *sym = canvas_makebindsym(canvas_getcurrent()->gl_name);\n    static int warned;\n    if (!warned)\n    {\n        post(\"warning -- 'template' (%s) is obsolete; replace with 'struct'\",\n            sym->s_name);\n        warned = 1;\n    }\n    return (gtemplate_donew(sym, argc, argv));\n}\n\nt_template *gtemplate_get(t_gtemplate *x)\n{\n    return (x->x_template);\n}\n\nstatic void gtemplate_free(t_gtemplate *x)\n{\n        /* get off the template's list */\n    t_template *t = x->x_template;\n    t_gtemplate *y;\n    if (x == t->t_list)\n    {\n        canvas_redrawallfortemplate(t, 2);\n        if (x->x_next)\n        {\n                /* if we were first on the list, and there are others on\n                the list, make a new template corresponding to the new\n                first-on-list and replace the existing template with it. */\n            t_template *z = template_new(&s_,\n                x->x_next->x_argc, x->x_next->x_argv);\n            template_conform(t, z);\n            pd_free(&t->t_pdobj);\n            pd_free(&z->t_pdobj);\n            z = template_new(x->x_sym, x->x_next->x_argc, x->x_next->x_argv);\n            z->t_list = x->x_next;\n            for (y = z->t_list; y ; y = y->x_next)\n                y->x_template = z;\n        }\n        else t->t_list = 0;\n        canvas_redrawallfortemplate(t, 1);\n    }\n    else\n    {\n        t_gtemplate *x2, *x3;\n        for (x2 = t->t_list; (x3 = x2->x_next); x2 = x3)\n        {\n            if (x == x3)\n            {\n                x2->x_next = x3->x_next;\n                break;\n            }\n        }\n    }\n    freebytes(x->x_argv, sizeof(t_atom) * x->x_argc);\n}\n\nstatic void gtemplate_setup(void)\n{\n    gtemplate_class = class_new(gensym(\"struct\"),\n        (t_newmethod)gtemplate_new, (t_method)gtemplate_free,\n        sizeof(t_gtemplate), CLASS_NOINLET, A_GIMME, 0);\n    class_addcreator((t_newmethod)gtemplate_new_old, gensym(\"template\"),\n        A_GIMME, 0);\n}\n\n/* ---------------  FIELD DESCRIPTORS ---------------------- */\n\n/* a field descriptor can hold a constant or a variable; if a variable,\nit's the name of a field in the template we belong to.  LATER, we might\nwant to cache the offset of the field so we don't have to search for it\nevery single time we draw the object.\n*/\n\nstruct _fielddesc\n{\n    char fd_type;       /* LATER consider removing this? */\n    char fd_var;\n    union\n    {\n        t_float fd_float;       /* the field is a constant float */\n        t_symbol *fd_symbol;    /* the field is a constant symbol */\n        t_symbol *fd_varsym;    /* the field is variable and this is the name */\n    } fd_un;\n    float fd_v1;        /* min and max values */\n    float fd_v2;\n    float fd_screen1;   /* min and max screen values */\n    float fd_screen2;\n    float fd_quantum;   /* quantization in value */\n};\n\nstatic void fielddesc_setfloat_const(t_fielddesc *fd, t_float f)\n{\n    fd->fd_type = A_FLOAT;\n    fd->fd_var = 0;\n    fd->fd_un.fd_float = f;\n    fd->fd_v1 = fd->fd_v2 = fd->fd_screen1 = fd->fd_screen2 =\n        fd->fd_quantum = 0;\n}\n\nstatic void fielddesc_setsymbol_const(t_fielddesc *fd, t_symbol *s)\n{\n    fd->fd_type = A_SYMBOL;\n    fd->fd_var = 0;\n    fd->fd_un.fd_symbol = s;\n    fd->fd_v1 = fd->fd_v2 = fd->fd_screen1 = fd->fd_screen2 =\n        fd->fd_quantum = 0;\n}\n\nstatic void fielddesc_setfloat_var(t_fielddesc *fd, t_symbol *s)\n{\n    char *s1, *s2, *s3, strbuf[MAXPDSTRING];\n    int i;\n    fd->fd_type = A_FLOAT;\n    fd->fd_var = 1;\n    if (!(s1 = strchr(s->s_name, '(')) || !(s2 = strchr(s->s_name, ')'))\n        || (s1 > s2))\n    {\n        fd->fd_un.fd_varsym = s;\n        fd->fd_v1 = fd->fd_v2 = fd->fd_screen1 = fd->fd_screen2 =\n            fd->fd_quantum = 0;\n    }\n    else\n    {\n        int cpy = (int)(s1 - s->s_name), got;\n        double v1, v2, screen1, screen2, quantum;\n        if (cpy > MAXPDSTRING-5)\n            cpy = MAXPDSTRING-5;\n        strncpy(strbuf, s->s_name, cpy);\n        strbuf[cpy] = 0;\n        fd->fd_un.fd_varsym = gensym(strbuf);\n        got = sscanf(s1, \"(%lf:%lf)(%lf:%lf)(%lf)\",\n            &v1, &v2, &screen1, &screen2,\n                &quantum);\n        fd->fd_v1=v1;\n        fd->fd_v2=v2;\n        fd->fd_screen1=screen1;\n        fd->fd_screen2=screen2;\n        fd->fd_quantum=quantum;\n        if (got < 2)\n            goto fail;\n        if (got == 3 || (got < 4 && strchr(s2, '(')))\n            goto fail;\n        if (got < 5 && (s3 = strchr(s2, '(')) && strchr(s3+1, '('))\n            goto fail;\n        if (got == 4)\n            fd->fd_quantum = 0;\n        else if (got == 2)\n        {\n            fd->fd_quantum = 0;\n            fd->fd_screen1 = fd->fd_v1;\n            fd->fd_screen2 = fd->fd_v2;\n        }\n        return;\n    fail:\n        post(\"parse error: %s\", s->s_name);\n        fd->fd_v1 = fd->fd_screen1 = fd->fd_v2 = fd->fd_screen2 =\n            fd->fd_quantum = 0;\n    }\n}\n\n#define CLOSED 1      /* polygon */\n#define BEZ 2         /* bezier shape */\n#define NOMOUSERUN 4  /* disable mouse interaction when in run mode  */\n#define NOMOUSEEDIT 8 /* same in edit mode */\n#define NOVERTICES 16 /* disable only vertex grabbing in run mode */\n#define A_ARRAY 55      /* LATER decide whether to enshrine this in m_pd.h */\n\nstatic void fielddesc_setfloatarg(t_fielddesc *fd, int argc, t_atom *argv)\n{\n        if (argc <= 0) fielddesc_setfloat_const(fd, 0);\n        else if (argv->a_type == A_SYMBOL)\n            fielddesc_setfloat_var(fd, argv->a_w.w_symbol);\n        else fielddesc_setfloat_const(fd, argv->a_w.w_float);\n}\n\nstatic void fielddesc_setsymbolarg(t_fielddesc *fd, int argc, t_atom *argv)\n{\n        if (argc <= 0) fielddesc_setsymbol_const(fd, &s_);\n        else if (argv->a_type == A_SYMBOL)\n        {\n            fd->fd_type = A_SYMBOL;\n            fd->fd_var = 1;\n            fd->fd_un.fd_varsym = argv->a_w.w_symbol;\n            fd->fd_v1 = fd->fd_v2 = fd->fd_screen1 = fd->fd_screen2 =\n                fd->fd_quantum = 0;\n        }\n        else fielddesc_setsymbol_const(fd, &s_);\n}\n\nstatic void fielddesc_setarrayarg(t_fielddesc *fd, int argc, t_atom *argv)\n{\n        if (argc <= 0) fielddesc_setfloat_const(fd, 0);\n        else if (argv->a_type == A_SYMBOL)\n        {\n            fd->fd_type = A_ARRAY;\n            fd->fd_var = 1;\n            fd->fd_un.fd_varsym = argv->a_w.w_symbol;\n        }\n        else fielddesc_setfloat_const(fd, argv->a_w.w_float);\n}\n\n    /* getting and setting values via fielddescs -- note confusing names;\n    the above are setting up the fielddesc itself. */\nstatic t_float fielddesc_getfloat(t_fielddesc *f, t_template *template,\n    t_word *wp, int loud)\n{\n    if (f->fd_type == A_FLOAT)\n    {\n        if (f->fd_var)\n            return (template_getfloat(template, f->fd_un.fd_varsym, wp, loud));\n        else return (f->fd_un.fd_float);\n    }\n    else\n    {\n        if (loud)\n            pd_error(0, \"symbolic data field used as number\");\n        return (0);\n    }\n}\n\n    /* convert a variable's value to a screen coordinate via its fielddesc */\nt_float fielddesc_cvttocoord(t_fielddesc *f, t_float val)\n{\n    t_float coord, pix, extreme, div;\n    if (f->fd_v2 == f->fd_v1)\n        return (val);\n    div = (f->fd_screen2 - f->fd_screen1)/(f->fd_v2 - f->fd_v1);\n    coord = f->fd_screen1 + (val - f->fd_v1) * div;\n    extreme = (f->fd_screen1 < f->fd_screen2 ?\n        f->fd_screen1 : f->fd_screen2);\n    if (coord < extreme)\n        coord = extreme;\n    extreme = (f->fd_screen1 > f->fd_screen2 ?\n        f->fd_screen1 : f->fd_screen2);\n    if (coord > extreme)\n        coord = extreme;\n    return (coord);\n}\n\n    /* read a variable via fielddesc and convert to screen coordinate */\nt_float fielddesc_getcoord(t_fielddesc *f, t_template *template,\n    t_word *wp, int loud)\n{\n    if (f->fd_type == A_FLOAT)\n    {\n        if (f->fd_var)\n        {\n            t_float val = template_getfloat(template,\n                f->fd_un.fd_varsym, wp, loud);\n            return (fielddesc_cvttocoord(f, val));\n        }\n        else return (f->fd_un.fd_float);\n    }\n    else\n    {\n        if (loud)\n            pd_error(0, \"symbolic data field used as number\");\n        return (0);\n    }\n}\n\nstatic t_symbol *fielddesc_getsymbol(t_fielddesc *f, t_template *template,\n    t_word *wp, int loud)\n{\n    if (f->fd_type == A_SYMBOL)\n    {\n        if (f->fd_var)\n            return(template_getsymbol(template, f->fd_un.fd_varsym, wp, loud));\n        else return (f->fd_un.fd_symbol);\n    }\n    else\n    {\n        if (loud)\n            pd_error(0, \"numeric data field used as symbol\");\n        return (&s_);\n    }\n}\n\n    /* convert from a screen coordinate to a variable value */\nt_float fielddesc_cvtfromcoord(t_fielddesc *f, t_float coord)\n{\n    t_float val;\n    if (f->fd_screen2 == f->fd_screen1)\n        val = coord;\n    else\n    {\n        t_float div = (f->fd_v2 - f->fd_v1)/(f->fd_screen2 - f->fd_screen1);\n        t_float extreme;\n        val = f->fd_v1 + (coord - f->fd_screen1) * div;\n        if (f->fd_quantum != 0)\n            val = ((int)((val/f->fd_quantum) + 0.5)) *  f->fd_quantum;\n        extreme = (f->fd_v1 < f->fd_v2 ?\n            f->fd_v1 : f->fd_v2);\n        if (val < extreme) val = extreme;\n        extreme = (f->fd_v1 > f->fd_v2 ?\n            f->fd_v1 : f->fd_v2);\n        if (val > extreme) val = extreme;\n    }\n    return (val);\n }\n\nvoid fielddesc_setcoord(t_fielddesc *f, t_template *template,\n    t_word *wp, t_float coord, int loud)\n{\n    if (f->fd_type == A_FLOAT && f->fd_var)\n    {\n        t_float val = fielddesc_cvtfromcoord(f, coord);\n        template_setfloat(template,\n                f->fd_un.fd_varsym, wp, val, loud);\n    }\n    else\n    {\n        if (loud)\n            pd_error(0, \"attempt to set constant or symbolic data field to a number\");\n    }\n}\n\n/* ---------------- curves and polygons (joined segments) ---------------- */\n\n/*\ncurves belong to templates and describe how the data in the template are to\nbe drawn.  The coordinates of the curve (and other display features) can\nbe attached to fields in the template.\n*/\n\nt_class *curve_class;\n\ntypedef struct _curve\n{\n    t_object x_obj;\n    int x_flags;    /* CLOSED, BEZ, NOMOUSERUN, NOMOUSEEDIT */\n    t_fielddesc x_fillcolor;\n    t_fielddesc x_outlinecolor;\n    t_fielddesc x_width;\n    t_fielddesc x_vis;\n    int x_npoints;\n    t_fielddesc *x_vec;\n    t_canvas *x_canvas;\n} t_curve;\n\nstatic void *curve_new(t_symbol *classsym, int argc, t_atom *argv)\n{\n    t_curve *x = (t_curve *)pd_new(curve_class);\n    const char *classname = classsym->s_name;\n    int flags = 0;\n    int nxy, i;\n    t_fielddesc *fd;\n    x->x_canvas = canvas_getcurrent();\n    if (classname[0] == 'f')\n    {\n        classname += 6;\n        flags |= CLOSED;\n    }\n    else classname += 4;\n    if (classname[0] == 'c') flags |= BEZ;\n    fielddesc_setfloat_const(&x->x_vis, 1);\n    while (argc && argv->a_type == A_SYMBOL &&\n        *argv->a_w.w_symbol->s_name == '-')\n    {\n        const char *flag = argv->a_w.w_symbol->s_name;\n        if (!strcmp(flag, \"-n\"))\n        {\n            fielddesc_setfloat_const(&x->x_vis, 0);\n        }\n        else if (!strcmp(flag, \"-v\") && argc > 1)\n        {\n            fielddesc_setfloatarg(&x->x_vis, 1, argv+1);\n            argc -= 1; argv += 1;\n        }\n        else if (!strcmp(flag, \"-x\"))\n        {\n            /* disable all mouse interaction */\n            flags |= (NOMOUSERUN | NOMOUSEEDIT);\n        }\n        else if (!strcmp(flag, \"-xr\"))\n        {\n            /* disable mouse actions in run mode */\n            flags |= NOMOUSERUN;\n        }\n        else if (!strcmp(flag, \"-xe\"))\n        {\n            /* disable mouse actions in edit mode */\n            flags |= NOMOUSEEDIT;\n        }\n        else if (!strcmp(flag, \"-xv\"))\n        {\n            /* disable changing vertices in run mode */\n            flags |= NOVERTICES;\n        }\n        else\n        {\n            pd_error(x, \"%s: unknown flag '%s'...\", classsym->s_name,\n                flag);\n        }\n        argc--; argv++;\n    }\n    x->x_flags = flags;\n    if ((flags & CLOSED) && argc)\n        fielddesc_setfloatarg(&x->x_fillcolor, argc--, argv++);\n    else fielddesc_setfloat_const(&x->x_fillcolor, 0);\n    if (argc) fielddesc_setfloatarg(&x->x_outlinecolor, argc--, argv++);\n    else fielddesc_setfloat_const(&x->x_outlinecolor, 0);\n    if (argc) fielddesc_setfloatarg(&x->x_width, argc--, argv++);\n    else fielddesc_setfloat_const(&x->x_width, 1);\n    if (argc < 0) argc = 0;\n    nxy =  (argc + (argc & 1));\n    x->x_npoints = (nxy>>1);\n    x->x_vec = (t_fielddesc *)t_getbytes(nxy * sizeof(t_fielddesc));\n    for (i = 0, fd = x->x_vec; i < argc; i++, fd++, argv++)\n        fielddesc_setfloatarg(fd, 1, argv);\n    if (argc & 1) fielddesc_setfloat_const(fd, 0);\n\n    return (x);\n}\n\nvoid curve_float(t_curve *x, t_floatarg f)\n{\n    int viswas;\n    if (x->x_vis.fd_type != A_FLOAT || x->x_vis.fd_var)\n    {\n        pd_error(x, \"global vis/invis for a template with variable visibility\");\n        return;\n    }\n    viswas = (x->x_vis.fd_un.fd_float != 0);\n\n    if ((f != 0 && viswas) || (f == 0 && !viswas))\n        return;\n    canvas_redrawallfortemplatecanvas(x->x_canvas, 2);\n    fielddesc_setfloat_const(&x->x_vis, (f != 0));\n    canvas_redrawallfortemplatecanvas(x->x_canvas, 1);\n}\n\n/* -------------------- widget behavior for curve ------------ */\n\nstatic void curve_getrect(t_gobj *z, t_glist *glist,\n    t_word *data, t_template *template, t_float basex, t_float basey,\n    int *xp1, int *yp1, int *xp2, int *yp2)\n{\n    t_curve *x = (t_curve *)z;\n    int i, n = x->x_npoints;\n    t_fielddesc *f = x->x_vec;\n    int x1 = 0x7fffffff, x2 = -0x7fffffff, y1 = 0x7fffffff, y2 = -0x7fffffff;\n    if (!fielddesc_getfloat(&x->x_vis, template, data, 0) ||\n        (glist->gl_edit && x->x_flags & NOMOUSEEDIT) ||\n        (!glist->gl_edit && x->x_flags & NOMOUSERUN))\n    {\n        *xp1 = *yp1 = 0x7fffffff;\n        *xp2 = *yp2 = -0x7fffffff;\n        return;\n    }\n    for (i = 0, f = x->x_vec; i < n; i++, f += 2)\n    {\n        int xloc = glist_xtopixels(glist,\n            basex + fielddesc_getcoord(f, template, data, 0));\n        int yloc = glist_ytopixels(glist,\n            basey + fielddesc_getcoord(f+1, template, data, 0));\n        if (xloc < x1) x1 = xloc;\n        if (xloc > x2) x2 = xloc;\n        if (yloc < y1) y1 = yloc;\n        if (yloc > y2) y2 = yloc;\n    }\n    *xp1 = x1;\n    *yp1 = y1;\n    *xp2 = x2;\n    *yp2 = y2;\n}\n\nstatic void curve_displace(t_gobj *z, t_glist *glist,\n    t_word *data, t_template *template, t_float basex, t_float basey,\n    int dx, int dy)\n{\n    /* refuse */\n}\n\nstatic void curve_select(t_gobj *z, t_glist *glist,\n    t_word *data, t_template *template, t_float basex, t_float basey,\n    int state)\n{\n    /* fill in later */\n}\n\nstatic void curve_activate(t_gobj *z, t_glist *glist,\n    t_word *data, t_template *template, t_float basex, t_float basey,\n    int state)\n{\n    /* fill in later */\n}\n\n#if 0\nstatic int rangecolor(int n)    /* 0 to 9 in 5 steps */\n{\n    int n2 = n/2;               /* 0 to 4 */\n    int ret = (n2 << 6);        /* 0 to 256 in 5 steps */\n    if (ret > 255) ret = 255;\n    return (ret);\n}\n#endif\n\nstatic int rangecolor(int n)    /* 0 to 9 in 5 steps */\n{\n    int n2 = (n == 9 ? 8 : n);               /* 0 to 8 */\n    int ret = (n2 << 5);        /* 0 to 256 in 9 steps */\n    if (ret > 255) ret = 255;\n    return (ret);\n}\n\nstatic int numbertocolor(int n)\n{\n    int red, green, blue, color = 0;\n    if (n < 0) n = 0;\n    red = n / 100;\n    green = n % 10;\n    blue = ((n / 10) % 10);\n    color |= rangecolor(red)   << 16;\n    color |= rangecolor(blue)  <<  8;\n    color |= rangecolor(green) <<  0;\n    return color;\n}\n\nstatic void curve_vis(t_gobj *z, t_glist *glist,\n    t_word *data, t_template *template, t_float basex, t_float basey,\n    int vis)\n{\n    t_curve *x = (t_curve *)z;\n    int i, n = x->x_npoints;\n    t_fielddesc *f = x->x_vec;\n    char tag0[80], tag[80];\n    const char*tags[] = {tag, tag0, \"curve\"};\n        /* see comment in plot_vis() */\n    if (vis && !fielddesc_getfloat(&x->x_vis, template, data, 0))\n        return;\n    sprintf(tag0, \"curve%p\", x);\n    sprintf(tag , \"curve%p_data%p\", x, data);\n    if (vis)\n    {\n        if (n > 1)\n        {\n            int flags = x->x_flags, closed = (flags & CLOSED);\n            t_float width = fielddesc_getfloat(&x->x_width, template, data, 1);\n            int outline;\n            t_word pix[200];\n\n            if (n > 100)\n                n = 100;\n                /* calculate the pixel values before we start printing\n                out the TK message so that \"error\" printout won't be\n                interspersed with it.  Only show up to 100 points so we don't\n                have to allocate memory here. */\n            for (i = 0, f = x->x_vec; i < n; i++, f += 2)\n            {\n                pix[2*i].w_float = glist_xtopixels(glist,\n                    basex + fielddesc_getcoord(f, template, data, 1));\n                pix[2*i+1].w_float = glist_ytopixels(glist,\n                    basey + fielddesc_getcoord(f+1, template, data, 1));\n            }\n            if (width < 1) width = 1;\n            if (glist->gl_isgraph)\n                width *= glist_getzoom(glist);\n            outline = numbertocolor(\n                fielddesc_getfloat(&x->x_outlinecolor, template, data, 1));\n\n            pdgui_vmess(0, \"crr iiii rf ri rS\",\n                glist_getcanvas(glist), \"create\",\n                (flags & CLOSED)?\"polygon\":\"line\",\n                0, 0, 0, 0,\n                \"-width\", width,\n                \"-smooth\", !!(flags & BEZ),\n                \"-tags\", 3, tags);\n\n            pdgui_vmess(0, \"crs w\",\n                glist_getcanvas(glist), \"coords\", tag,\n                2*n, pix);\n\n            if (flags & CLOSED)\n            {\n                int fill = numbertocolor(\n                    fielddesc_getfloat(&x->x_fillcolor, template, data, 1));\n                pdgui_vmess(0, \"crs rk rk\",\n                    glist_getcanvas(glist), \"itemconfigure\", tag,\n                    \"-fill\", fill,\n                    \"-outline\", outline);\n            } else\n                pdgui_vmess(0, \"crs rk\",\n                    glist_getcanvas(glist), \"itemconfigure\", tag,\n                    \"-fill\", outline);\n        }\n        else post(\"warning: drawing shapes need at least two points to be graphed\");\n    }\n    else\n    {\n        if (n > 1)\n            pdgui_vmess(0, \"crs\", glist_getcanvas(glist), \"delete\", tag);\n    }\n}\n\n    /* LATER protect against the template changing or the scalar disappearing\n    probably by attaching a gpointer here ... */\n\nstatic void curve_motionfn(void *z, t_floatarg dx, t_floatarg dy, t_floatarg up)\n{\n    t_curve *x = (t_curve *)z;\n    t_fielddesc *f = x->x_vec + TEMPLATE->curve_motion_field;\n    t_atom at;\n    if (up != 0)\n        return;\n    if (!gpointer_check(&TEMPLATE->curve_motion_gpointer, 0))\n    {\n        post(\"curve_motion: scalar disappeared\");\n        return;\n    }\n    TEMPLATE->curve_motion_xcumulative += dx;\n    TEMPLATE->curve_motion_ycumulative += dy;\n    if (f->fd_var && (dx != 0))\n    {\n        fielddesc_setcoord(f, TEMPLATE->curve_motion_template,\n            TEMPLATE->curve_motion_wp,\n            TEMPLATE->curve_motion_xbase +\n            TEMPLATE->curve_motion_xcumulative * TEMPLATE->curve_motion_xper,\n                1);\n    }\n    if ((f+1)->fd_var && (dy != 0))\n    {\n        fielddesc_setcoord(f+1, TEMPLATE->curve_motion_template,\n            TEMPLATE->curve_motion_wp,\n            TEMPLATE->curve_motion_ybase +\n            TEMPLATE->curve_motion_ycumulative * TEMPLATE->curve_motion_yper,\n                1);\n    }\n        /* LATER figure out what to do to notify for an array? */\n    if (TEMPLATE->curve_motion_scalar)\n        template_notifyforscalar(TEMPLATE->curve_motion_template,\n            TEMPLATE->curve_motion_glist,\n            TEMPLATE->curve_motion_scalar, gensym(\"change\"), 1, &at);\n    if (TEMPLATE->curve_motion_scalar)\n        scalar_redraw(TEMPLATE->curve_motion_scalar,\n            TEMPLATE->curve_motion_glist);\n    if (TEMPLATE->curve_motion_array)\n        array_redraw(TEMPLATE->curve_motion_array,\n            TEMPLATE->curve_motion_glist);\n}\n\nstatic int curve_click(t_gobj *z, t_glist *glist,\n    t_word *data, t_template *template, t_scalar *sc, t_array *ap,\n    t_float basex, t_float basey,\n    int xpix, int ypix, int shift, int alt, int dbl, int doit)\n{\n    t_curve *x = (t_curve *)z;\n    int i, n = x->x_npoints;\n    int bestn = -1;\n    int besterror = 0x7fffffff;\n    t_fielddesc *f;\n    if ((x->x_flags & NOMOUSERUN) || (x->x_flags & NOVERTICES) ||\n        !fielddesc_getfloat(&x->x_vis, template, data, 0))\n            return (0);\n    for (i = 0, f = x->x_vec; i < n; i++, f += 2)\n    {\n        int xval = fielddesc_getcoord(f, template, data, 0),\n            xloc = glist_xtopixels(glist, basex + xval);\n        int yval = fielddesc_getcoord(f+1, template, data, 0),\n            yloc = glist_ytopixels(glist, basey + yval);\n        int xerr = xloc - xpix, yerr = yloc - ypix;\n        if (!f->fd_var && !(f+1)->fd_var)\n            continue;\n        if (xerr < 0)\n            xerr = -xerr;\n        if (yerr < 0)\n            yerr = -yerr;\n        if (yerr > xerr)\n            xerr = yerr;\n        if (xerr < besterror)\n        {\n            TEMPLATE->curve_motion_xbase = xval;\n            TEMPLATE->curve_motion_ybase = yval;\n            besterror = xerr;\n            bestn = i;\n        }\n    }\n    if (besterror > 6)\n        return (0);\n    if (doit)\n    {\n        TEMPLATE->curve_motion_xper = glist_pixelstox(glist, 1)\n            - glist_pixelstox(glist, 0);\n        TEMPLATE->curve_motion_yper = glist_pixelstoy(glist, 1)\n            - glist_pixelstoy(glist, 0);\n        TEMPLATE->curve_motion_xcumulative = 0;\n        TEMPLATE->curve_motion_ycumulative = 0;\n        TEMPLATE->curve_motion_glist = glist;\n        TEMPLATE->curve_motion_scalar = sc;\n        TEMPLATE->curve_motion_array = ap;\n        TEMPLATE->curve_motion_wp = data;\n        TEMPLATE->curve_motion_field = 2*bestn;\n        TEMPLATE->curve_motion_template = template;\n        if (TEMPLATE->curve_motion_scalar)\n            gpointer_setglist(&TEMPLATE->curve_motion_gpointer,\n                TEMPLATE->curve_motion_glist, TEMPLATE->curve_motion_scalar);\n        else gpointer_setarray(&TEMPLATE->curve_motion_gpointer,\n                TEMPLATE->curve_motion_array, TEMPLATE->curve_motion_wp);\n        glist_grab(glist, z, curve_motionfn, 0, xpix, ypix);\n    }\n    return (1);\n}\n\nconst t_parentwidgetbehavior curve_widgetbehavior =\n{\n    curve_getrect,\n    curve_displace,\n    curve_select,\n    curve_activate,\n    curve_vis,\n    curve_click,\n};\n\nstatic void curve_free(t_curve *x)\n{\n    t_freebytes(x->x_vec, 2 * x->x_npoints * sizeof(*x->x_vec));\n}\n\nstatic void curve_setup(void)\n{\n    curve_class = class_new(gensym(\"drawpolygon\"), (t_newmethod)curve_new,\n        (t_method)curve_free, sizeof(t_curve), 0, A_GIMME, 0);\n    class_setdrawcommand(curve_class);\n    class_addcreator((t_newmethod)curve_new, gensym(\"drawcurve\"),\n        A_GIMME, 0);\n    class_addcreator((t_newmethod)curve_new, gensym(\"filledpolygon\"),\n        A_GIMME, 0);\n    class_addcreator((t_newmethod)curve_new, gensym(\"filledcurve\"),\n        A_GIMME, 0);\n    class_sethelpsymbol(curve_class, gensym(\"draw-shapes\"));\n    class_setparentwidget(curve_class, &curve_widgetbehavior);\n    class_addfloat(curve_class, curve_float);\n}\n\n/* --------- plots for showing arrays --------------- */\n\nt_class *plot_class;\n\ntypedef struct _plot\n{\n    t_object x_obj;\n    t_canvas *x_canvas;\n    t_fielddesc x_outlinecolor;\n    t_fielddesc x_width;\n    t_fielddesc x_xloc;\n    t_fielddesc x_yloc;\n    t_fielddesc x_xinc;\n    t_fielddesc x_style;\n    t_fielddesc x_data;\n    t_fielddesc x_xpoints;\n    t_fielddesc x_ypoints;\n    t_fielddesc x_wpoints;\n    t_fielddesc x_vis;          /* visible */\n    t_fielddesc x_scalarvis;    /* true if drawing the scalar at each point */\n    t_fielddesc x_edit;         /* enable/disable mouse editing */\n} t_plot;\n\nstatic void *plot_new(t_symbol *classsym, int argc, t_atom *argv)\n{\n    t_plot *x = (t_plot *)pd_new(plot_class);\n    int defstyle = PLOTSTYLE_POLY;\n    x->x_canvas = canvas_getcurrent();\n\n    fielddesc_setfloat_var(&x->x_xpoints, gensym(\"x\"));\n    fielddesc_setfloat_var(&x->x_ypoints, gensym(\"y\"));\n    fielddesc_setfloat_var(&x->x_wpoints, gensym(\"w\"));\n\n    fielddesc_setfloat_const(&x->x_vis, 1);\n    fielddesc_setfloat_const(&x->x_scalarvis, 1);\n    fielddesc_setfloat_const(&x->x_edit, 1);\n    while (1)\n    {\n        t_symbol *firstarg = atom_getsymbolarg(0, argc, argv);\n        if (!strcmp(firstarg->s_name, \"curve\") ||\n            !strcmp(firstarg->s_name, \"-c\"))\n        {\n            defstyle = PLOTSTYLE_BEZ;\n            argc--, argv++;\n        }\n        else if (!strcmp(firstarg->s_name, \"-v\") && argc > 1)\n        {\n            fielddesc_setfloatarg(&x->x_vis, 1, argv+1);\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(firstarg->s_name, \"-vs\") && argc > 1)\n        {\n            fielddesc_setfloatarg(&x->x_scalarvis, 1, argv+1);\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(firstarg->s_name, \"-x\") && argc > 1)\n        {\n            fielddesc_setfloatarg(&x->x_xpoints, 1, argv+1);\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(firstarg->s_name, \"-y\") && argc > 1)\n        {\n            fielddesc_setfloatarg(&x->x_ypoints, 1, argv+1);\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(firstarg->s_name, \"-w\") && argc > 1)\n        {\n            fielddesc_setfloatarg(&x->x_wpoints, 1, argv+1);\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(firstarg->s_name, \"-e\") && argc > 1)\n        {\n            fielddesc_setfloatarg(&x->x_edit, 1, argv+1);\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(firstarg->s_name, \"-n\"))\n        {\n            fielddesc_setfloat_const(&x->x_vis, 0);\n            argc--; argv++;\n        }\n        else if (*firstarg->s_name == '-')\n        {\n            pd_error(x, \"%s: unknown flag '%s'...\", classsym->s_name,\n                firstarg->s_name);\n            argc--; argv++;\n        }\n        else break;\n    }\n    if (argc) fielddesc_setarrayarg(&x->x_data, argc--, argv++);\n    else fielddesc_setfloat_const(&x->x_data, 1);\n    if (argc) fielddesc_setfloatarg(&x->x_outlinecolor, argc--, argv++);\n    else fielddesc_setfloat_const(&x->x_outlinecolor, 0);\n    if (argc) fielddesc_setfloatarg(&x->x_width, argc--, argv++);\n    else fielddesc_setfloat_const(&x->x_width, 1);\n    if (argc) fielddesc_setfloatarg(&x->x_xloc, argc--, argv++);\n    else fielddesc_setfloat_const(&x->x_xloc, 1);\n    if (argc) fielddesc_setfloatarg(&x->x_yloc, argc--, argv++);\n    else fielddesc_setfloat_const(&x->x_yloc, 1);\n    if (argc) fielddesc_setfloatarg(&x->x_xinc, argc--, argv++);\n    else fielddesc_setfloat_const(&x->x_xinc, 1);\n    if (argc) fielddesc_setfloatarg(&x->x_style, argc--, argv++);\n    else fielddesc_setfloat_const(&x->x_style, defstyle);\n    return (x);\n}\n\nvoid plot_float(t_plot *x, t_floatarg f)\n{\n    int viswas;\n    if (x->x_vis.fd_type != A_FLOAT || x->x_vis.fd_var)\n    {\n        pd_error(x, \"global vis/invis for a template with variable visibility\");\n        return;\n    }\n    viswas = (x->x_vis.fd_un.fd_float != 0);\n\n    if ((f != 0 && viswas) || (f == 0 && !viswas))\n        return;\n    canvas_redrawallfortemplatecanvas(x->x_canvas, 2);\n    fielddesc_setfloat_const(&x->x_vis, (f != 0));\n    canvas_redrawallfortemplatecanvas(x->x_canvas, 1);\n}\n\n/* -------------------- widget behavior for plot ------------ */\n\n\n    /* get everything we'll need from the owner template of the array being\n    plotted. Not used for garrays, but see below */\nstatic int plot_readownertemplate(t_plot *x,\n    t_word *data, t_template *ownertemplate,\n    t_symbol **elemtemplatesymp, t_array **arrayp,\n    t_float *linewidthp, t_float *xlocp, t_float *xincp, t_float *ylocp,\n    t_float *stylep, t_float *visp, t_float *scalarvisp, t_float *editp,\n    t_fielddesc **xfield, t_fielddesc **yfield, t_fielddesc **wfield)\n{\n    int arrayonset, type;\n    t_symbol *elemtemplatesym;\n    t_array *array;\n\n        /* find the data and verify it's an array */\n    if (x->x_data.fd_type != A_ARRAY || !x->x_data.fd_var)\n    {\n        pd_error(0, \"plot: needs an array field\");\n        return (-1);\n    }\n    if (!template_find_field(ownertemplate, x->x_data.fd_un.fd_varsym,\n        &arrayonset, &type, &elemtemplatesym))\n    {\n        pd_error(0, \"plot: %s: no such field\", x->x_data.fd_un.fd_varsym->s_name);\n        return (-1);\n    }\n    if (type != DT_ARRAY)\n    {\n        pd_error(0, \"plot: %s: not an array\", x->x_data.fd_un.fd_varsym->s_name);\n        return (-1);\n    }\n    array = *(t_array **)(((char *)data) + arrayonset);\n    *linewidthp = fielddesc_getfloat(&x->x_width, ownertemplate, data, 1);\n    *xlocp = fielddesc_getfloat(&x->x_xloc, ownertemplate, data, 1);\n    *xincp = fielddesc_getfloat(&x->x_xinc, ownertemplate, data, 1);\n    *ylocp = fielddesc_getfloat(&x->x_yloc, ownertemplate, data, 1);\n    *stylep = fielddesc_getfloat(&x->x_style, ownertemplate, data, 1);\n    *visp = fielddesc_getfloat(&x->x_vis, ownertemplate, data, 1);\n    *scalarvisp = fielddesc_getfloat(&x->x_scalarvis, ownertemplate, data, 1);\n    *editp = fielddesc_getfloat(&x->x_edit, ownertemplate, data, 1);\n    *elemtemplatesymp = elemtemplatesym;\n    *arrayp = array;\n    *xfield = &x->x_xpoints;\n    *yfield = &x->x_ypoints;\n    *wfield = &x->x_wpoints;\n    return (0);\n}\n\n    /* get everything else you could possibly need about a plot,\n    either for plot's own purposes or for plotting a \"garray\" */\nint array_getfields(t_symbol *elemtemplatesym,\n    t_canvas **elemtemplatecanvasp,\n    t_template **elemtemplatep, int *elemsizep,\n    t_fielddesc *xfielddesc, t_fielddesc *yfielddesc, t_fielddesc *wfielddesc,\n    int *xonsetp, int *yonsetp, int *wonsetp)\n{\n    int arrayonset, elemsize, yonset, wonset, xonset, type;\n    t_template *elemtemplate;\n    t_symbol *dummy, *varname;\n    t_canvas *elemtemplatecanvas = 0;\n\n        /* the \"float\" template is special in not having to have a canvas;\n        template_findbyname is hardwired to return a predefined\n        template. */\n\n    if (!(elemtemplate =  template_findbyname(elemtemplatesym)))\n    {\n        pd_error(0, \"plot: %s: no such template\", elemtemplatesym->s_name);\n        return (-1);\n    }\n    if (!((elemtemplatesym == &s_float) ||\n        (elemtemplatecanvas = template_findcanvas(elemtemplate))))\n    {\n        pd_error(0, \"plot: %s: no canvas for this template\", elemtemplatesym->s_name);\n        return (-1);\n    }\n    elemsize = elemtemplate->t_n * sizeof(t_word);\n    if (yfielddesc && yfielddesc->fd_var)\n        varname = yfielddesc->fd_un.fd_varsym;\n    else varname = gensym(\"y\");\n    if (!template_find_field(elemtemplate, varname, &yonset, &type, &dummy)\n        || type != DT_FLOAT)\n            yonset = -1;\n    if (xfielddesc && xfielddesc->fd_var)\n        varname = xfielddesc->fd_un.fd_varsym;\n    else varname = gensym(\"x\");\n    if (!template_find_field(elemtemplate, varname, &xonset, &type, &dummy)\n        || type != DT_FLOAT)\n            xonset = -1;\n    if (wfielddesc && wfielddesc->fd_var)\n        varname = wfielddesc->fd_un.fd_varsym;\n    else varname = gensym(\"w\");\n    if (!template_find_field(elemtemplate, varname, &wonset, &type, &dummy)\n        || type != DT_FLOAT)\n            wonset = -1;\n\n        /* fill in slots for return values */\n    *elemtemplatecanvasp = elemtemplatecanvas;\n    *elemtemplatep = elemtemplate;\n    *elemsizep = elemsize;\n    *xonsetp = xonset;\n    *yonsetp = yonset;\n    *wonsetp = wonset;\n    return (0);\n}\n\nstatic void plot_getrect(t_gobj *z, t_glist *glist,\n    t_word *data, t_template *template, t_float basex, t_float basey,\n    int *xp1, int *yp1, int *xp2, int *yp2)\n{\n    t_plot *x = (t_plot *)z;\n    int elemsize, yonset, wonset, xonset;\n    t_canvas *elemtemplatecanvas;\n    t_template *elemtemplate;\n    t_symbol *elemtemplatesym;\n    t_float linewidth, xloc, xinc, yloc, style, yval, vis, scalarvis, edit;\n    double xsum;\n    t_array *array;\n    int x1 = 0x7fffffff, y1 = 0x7fffffff, x2 = -0x7fffffff, y2 = -0x7fffffff;\n    int i;\n    t_float xpix, ypix, wpix;\n    t_fielddesc *xfielddesc, *yfielddesc, *wfielddesc;\n        /* if we're the only plot in the glist claim the whole thing */\n    if (glist->gl_list && !glist->gl_list->g_next)\n    {\n        *xp1 = *yp1 = -0x7fffffff;\n        *xp2 = *yp2 = 0x7fffffff;\n        return;\n    }\n    if (!plot_readownertemplate(x, data, template,\n        &elemtemplatesym, &array, &linewidth, &xloc, &xinc, &yloc, &style,\n            &vis, &scalarvis, &edit, &xfielddesc, &yfielddesc, &wfielddesc) &&\n                (vis != 0) &&\n            !array_getfields(elemtemplatesym, &elemtemplatecanvas,\n                &elemtemplate, &elemsize,\n                xfielddesc, yfielddesc, wfielddesc,\n                &xonset, &yonset, &wonset))\n    {\n            /* if it has more than 2000 points, just check 1000 of them. */\n        int incr = (array->a_n <= 2000 ? 1 : array->a_n / 1000);\n        for (i = 0, xsum = 0; i < array->a_n; i += incr)\n        {\n            t_float usexloc, useyloc;\n            t_gobj *y;\n                /* get the coords of the point proper */\n            array_getcoordinate(glist, (char *)(array->a_vec) + i * elemsize,\n                xonset, yonset, wonset, i, basex + xloc, basey + yloc, xinc,\n                xfielddesc, yfielddesc, wfielddesc, &xpix, &ypix, &wpix);\n            if (xpix < x1)\n                x1 = xpix;\n            if (xpix > x2)\n                x2 = xpix;\n            if (ypix - wpix < y1)\n                y1 = ypix - wpix;\n            if (ypix + wpix > y2)\n                y2 = ypix + wpix;\n\n            if (scalarvis != 0)\n            {\n                    /* check also the drawing instructions for the scalar */\n                if (xonset >= 0)\n                    usexloc = basex + xloc + fielddesc_cvttocoord(xfielddesc,\n                        *(t_float *)(((char *)(array->a_vec) + elemsize * i)\n                            + xonset));\n                else usexloc = basex + xsum, xsum += xinc;\n                if (yonset >= 0)\n                    yval = *(t_float *)(((char *)(array->a_vec) + elemsize * i)\n                        + yonset);\n                else yval = 0;\n                useyloc = basey + yloc + fielddesc_cvttocoord(yfielddesc, yval);\n                for (y = elemtemplatecanvas->gl_list; y; y = y->g_next)\n                {\n                    int xx1, xx2, yy1, yy2;\n                    const t_parentwidgetbehavior *wb =\n                        pd_getparentwidget(&y->g_pd);\n                    if (!wb) continue;\n                    (*wb->w_parentgetrectfn)(y, glist,\n                        (t_word *)((char *)(array->a_vec) + elemsize * i),\n                            elemtemplate, usexloc, useyloc,\n                                &xx1, &yy1, &xx2, &yy2);\n                    if (xx1 < x1)\n                        x1 = xx1;\n                    if (yy1 < y1)\n                        y1 = yy1;\n                     if (xx2 > x2)\n                        x2 = xx2;\n                    if (yy2 > y2)\n                        y2 = yy2;\n                }\n            }\n        }\n    }\n\n    *xp1 = x1;\n    *yp1 = y1;\n    *xp2 = x2;\n    *yp2 = y2;\n}\n\nstatic void plot_displace(t_gobj *z, t_glist *glist,\n    t_word *data, t_template *template, t_float basex, t_float basey,\n    int dx, int dy)\n{\n        /* not yet */\n}\n\nstatic void plot_select(t_gobj *z, t_glist *glist,\n    t_word *data, t_template *template, t_float basex, t_float basey,\n    int state)\n{\n    /* not yet */\n}\n\nstatic void plot_activate(t_gobj *z, t_glist *glist,\n    t_word *data, t_template *template, t_float basex, t_float basey,\n    int state)\n{\n        /* not yet */\n}\n\n#define CLIP(x) ((x) < 1e20 && (x) > -1e20 ? x : 0)\n\nstatic void plot_vis(t_gobj *z, t_glist *glist,\n    t_word *data, t_template *template, t_float basex, t_float basey,\n    int tovis)\n{\n    t_plot *x = (t_plot *)z;\n    int elemsize, yonset, wonset, xonset, i;\n    t_canvas *elemtemplatecanvas;\n    t_template *elemtemplate;\n    t_symbol *elemtemplatesym;\n    t_float linewidth, xloc, xinc, yloc, style, yval,\n        vis, scalarvis, edit;\n    double xsum;\n    t_array *array;\n    int nelem;\n    char *elem;\n    t_fielddesc *xfielddesc, *yfielddesc, *wfielddesc;\n    char tag[80], tag0[80];\n    const char*tags[] = {tag, tag0, \"array\"};\n        /* even if the array is \"invisible\", if its visibility is\n        set by an instance variable you have to explicitly erase it,\n        because the flag could earlier have been on when we were getting\n        drawn.  Rather than look to try to find out whether we're\n        visible we just do the erasure.  At the TK level this should\n        cause no action because the tag matches nobody.  LATER we\n        might want to optimize this somehow.  Ditto the \"vis()\" routines\n        for other drawing instructions. */\n\n    if (plot_readownertemplate(x, data, template,\n        &elemtemplatesym, &array, &linewidth, &xloc, &xinc, &yloc, &style,\n        &vis, &scalarvis, &edit, &xfielddesc, &yfielddesc, &wfielddesc) ||\n            ((vis == 0) && tovis) /* see above for 'tovis' */\n            || array_getfields(elemtemplatesym, &elemtemplatecanvas,\n                &elemtemplate, &elemsize, xfielddesc, yfielddesc, wfielddesc,\n                &xonset, &yonset, &wonset))\n                    return;\n    nelem = array->a_n;\n    elem = (char *)array->a_vec;\n\n    sprintf(tag , \"plot%p\", data);\n        /* a tag that uniquely identifies the sub-plot */\n    sprintf(tag0, \"plot%p_array%p_onset%+d%+d%+d\", data, elem, wonset, xonset, yonset);\n\n    if (glist->gl_isgraph)\n        linewidth *= glist_getzoom(glist);\n\n    if (tovis)\n    {\n         /* we use t_word because pdgui_vmess() has a convenient FLOATWORDS type\n          * FLOATARRAY is impractical (as it sends a list, and the GUI expects arguments)\n          */\n        t_word coordinates[1024*2];\n\n        if (style == PLOTSTYLE_POINTS)\n        {\n            t_float minyval = 1e20, maxyval = -1e20;\n            int ndrawn = 0;\n            int color = numbertocolor(\n                fielddesc_getfloat(&x->x_outlinecolor, template, data, 1));\n\n            for (xsum = basex + xloc, i = 0; i < nelem; i++)\n            {\n                t_float yval, xpix, ypix, nextxloc, usexloc;\n                int ixpix, inextx;\n\n                if (xonset >= 0)\n                {\n                    usexloc = basex + xloc +\n                        *(t_float *)((elem + elemsize * i) + xonset);\n                    ixpix = glist_xtopixels(glist,\n                        fielddesc_cvttocoord(xfielddesc, usexloc));\n                    inextx = ixpix + 2;\n                }\n                else\n                {\n                    usexloc = xsum;\n                    xsum += xinc;\n                    ixpix = glist_xtopixels(glist,\n                        fielddesc_cvttocoord(xfielddesc, usexloc));\n                    inextx = glist_xtopixels(glist,\n                        fielddesc_cvttocoord(xfielddesc, xsum));\n                }\n\n                if (yonset >= 0)\n                    yval = yloc + *(t_float *)((elem + elemsize * i) + yonset);\n                else yval = 0;\n                yval = CLIP(yval);\n                if (yval < minyval)\n                    minyval = yval;\n                if (yval > maxyval)\n                    maxyval = yval;\n                if (i == nelem-1 || inextx != ixpix)\n                {\n\n                    pdgui_vmess(0, \"crr iiii rk rf rS\",\n                        glist_getcanvas(glist), \"create\", \"rectangle\",\n                        ixpix , (int) glist_ytopixels(glist, basey + fielddesc_cvttocoord(yfielddesc, minyval)),\n                        inextx, (int)(glist_ytopixels(glist, basey + fielddesc_cvttocoord(yfielddesc, maxyval)) + linewidth),\n                        \"-fill\", color,\n                        \"-width\", 0.,\n                        \"-tags\", 3, tags);\n                    ndrawn++;\n                    minyval = 1e20;\n                    maxyval = -1e20;\n                }\n                if (ndrawn > 2000) break;\n            }\n        }\n        else\n        {\n            int outline = numbertocolor(\n                fielddesc_getfloat(&x->x_outlinecolor, template, data, 1));\n            int lastpixel = -1, ndrawn = 0;\n            t_float yval = 0, wval = 0, xpix;\n            int ixpix = 0;\n                /* draw the trace */\n\n\n            if (wonset >= 0)\n            {\n                    /* found \"w\" field which controls linewidth.  The trace is\n                    a filled polygon with 2n points. */\n                for (i = 0, xsum = xloc; i < nelem; i++)\n                {\n                    t_float usexloc;\n                    if (xonset >= 0)\n                        usexloc = xloc + *(t_float *)((elem + elemsize * i)\n                            + xonset);\n                    else usexloc = xsum, xsum += xinc;\n                    if (yonset >= 0)\n                        yval = *(t_float *)((elem + elemsize * i) + yonset);\n                    else yval = 0;\n                    yval = CLIP(yval);\n                    wval = *(t_float *)((elem + elemsize * i) + wonset);\n                    wval = CLIP(wval);\n                    xpix = glist_xtopixels(glist,\n                        basex + fielddesc_cvttocoord(xfielddesc, usexloc));\n                    ixpix = xpix + 0.5;\n                    if (xonset >= 0 || ixpix != lastpixel)\n                    {\n                        coordinates[ndrawn*2+0].w_float = ixpix;\n                        coordinates[ndrawn*2+1].w_float = glist_ytopixels(\n                            glist,\n                            basey\n                            + yloc\n                            + fielddesc_cvttocoord(yfielddesc, yval)\n                            - fielddesc_cvttocoord(wfielddesc, wval));\n                        ndrawn++;\n                    }\n                    lastpixel = ixpix;\n                    if (ndrawn*2 >= sizeof(coordinates)/sizeof(*coordinates))\n                        goto ouch;\n                }\n                lastpixel = -1;\n                for (i = nelem-1; i >= 0; i--)\n                {\n                    t_float usexloc;\n                    if (xonset >= 0)\n                        usexloc = xloc + *(t_float *)((elem + elemsize * i)\n                            + xonset);\n                    else xsum -= xinc, usexloc = xsum;\n                    if (yonset >= 0)\n                        yval = *(t_float *)((elem + elemsize * i) + yonset);\n                    else yval = 0;\n                    yval = CLIP(yval);\n                    wval = *(t_float *)((elem + elemsize * i) + wonset);\n                    wval = CLIP(wval);\n                    xpix = glist_xtopixels(glist,\n                        basex + fielddesc_cvttocoord(xfielddesc, usexloc));\n                    ixpix = xpix + 0.5;\n                    if (xonset >= 0 || ixpix != lastpixel)\n                    {\n                        coordinates[ndrawn*2+0].w_float = ixpix;\n                        coordinates[ndrawn*2+1].w_float = glist_ytopixels(\n                            glist,\n                            basey\n                            + yloc\n                            + fielddesc_cvttocoord(yfielddesc, yval)\n                            + fielddesc_cvttocoord(wfielddesc, wval));\n                        ndrawn++;\n                    }\n                    lastpixel = ixpix;\n                    if (ndrawn*2 >= sizeof(coordinates)/sizeof(*coordinates))\n                        goto ouch;\n                }\n\n                    /* TK will complain if there aren't at least 3 points.\n                    There should be at least two already. */\n                if (ndrawn < 4)\n                {\n                    coordinates[ndrawn*2+0].w_float = ixpix + 10;\n                    coordinates[ndrawn*2+1].w_float = glist_ytopixels(\n                        glist,\n                        basey\n                        + yloc\n                        + fielddesc_cvttocoord(yfielddesc, yval)\n                        - fielddesc_cvttocoord(wfielddesc, wval));\n                    ndrawn++;\n\n                    coordinates[ndrawn*2+0].w_float = ixpix + 10;\n                    coordinates[ndrawn*2+1].w_float = glist_ytopixels(\n                        glist,\n                        basey\n                        + yloc\n                        + fielddesc_cvttocoord(yfielddesc, yval)\n                        + fielddesc_cvttocoord(wfielddesc, wval));\n                    ndrawn++;\n                }\n            ouch:\n\n                pdgui_vmess(0, \"crr ri rk rk ri rS\",\n                    glist_getcanvas(glist), \"create\", \"polygon\",\n                    \"-width\", (glist->gl_isgraph ? glist_getzoom(glist) : 1),\n                    \"-fill\", outline,\n                    \"-outline\", outline,\n                    \"-smooth\", (style == PLOTSTYLE_BEZ),\n                    \"-tags\", 3, tags);\n\n                pdgui_vmess(0, \"crs w\",\n                    glist_getcanvas(glist), \"coords\", tag0,\n                    ndrawn*2, coordinates);\n            }\n            else if (linewidth > 0)\n            {\n                    /* no \"w\" field.  If the linewidth is positive, draw a\n                    segmented line with the requested width; otherwise don't\n                    draw the trace at all. */\n                for (i = 0, xsum = xloc; i < nelem; i++)\n                {\n                    t_float usexloc;\n                    if (xonset >= 0)\n                        usexloc = xloc + *(t_float *)((elem + elemsize * i)\n                            + xonset);\n                    else usexloc = xsum, xsum += xinc;\n                    if (yonset >= 0)\n                        yval = *(t_float *)((elem + elemsize * i) + yonset);\n                    else yval = 0;\n                    yval = CLIP(yval);\n\n\n                    xpix = glist_xtopixels(glist,\n                        basex + fielddesc_cvttocoord(xfielddesc, usexloc));\n                    ixpix = xpix + 0.5;\n                    if (xonset >= 0 || ixpix != lastpixel)\n                    {\n                        coordinates[ndrawn*2+0].w_float = ixpix;\n                        coordinates[ndrawn*2+1].w_float = glist_ytopixels(\n                            glist,\n                            basey\n                            + yloc\n                            + fielddesc_cvttocoord(yfielddesc, yval));\n                        ndrawn++;\n                    }\n                    lastpixel = ixpix;\n                    if (ndrawn*2 >= sizeof(coordinates)/sizeof(*coordinates)) break;\n                }\n\n                    /* TK will complain if there aren't at least 2 points... */\n                if (ndrawn == 1)\n                {\n                    coordinates[2].w_float = ixpix + 10;\n                    coordinates[3].w_float = glist_ytopixels(glist, basey + yloc + fielddesc_cvttocoord(yfielddesc, yval));\n                    ndrawn = 2;\n                }\n\n                if(ndrawn)\n                {\n                    pdgui_vmess(0, \"crr iiii rf rk ri rS\",\n                        glist_getcanvas(glist), \"create\", \"line\",\n                        0, 0, 0, 0,\n                        \"-width\", linewidth,\n                        \"-fill\", outline,\n                        \"-smooth\", (style == PLOTSTYLE_BEZ),\n                        \"-tags\", 3, tags);\n                    pdgui_vmess(0, \"crs w\",\n                        glist_getcanvas(glist), \"coords\", tag0,\n                        ndrawn*2, coordinates);\n                }\n            }\n        }\n            /* We're done with the outline; now draw all the points.\n            This code is inefficient since the template has to be\n            searched for drawing instructions for every last point. */\n        if (scalarvis != 0)\n        {\n            for (xsum = xloc, i = 0; i < nelem; i++)\n            {\n                t_float usexloc, useyloc;\n                t_gobj *y;\n                if (xonset >= 0)\n                    usexloc = basex + xloc +\n                        *(t_float *)((elem + elemsize * i) + xonset);\n                else usexloc = basex + xsum, xsum += xinc;\n                if (yonset >= 0)\n                    yval = *(t_float *)((elem + elemsize * i) + yonset);\n                else yval = 0;\n                useyloc = basey + yloc +\n                    fielddesc_cvttocoord(yfielddesc, yval);\n                for (y = elemtemplatecanvas->gl_list; y; y = y->g_next)\n                {\n                    const t_parentwidgetbehavior *wb =\n                        pd_getparentwidget(&y->g_pd);\n                    if (!wb) continue;\n                    (*wb->w_parentvisfn)(y, glist,\n                        (t_word *)(elem + elemsize * i),\n                            elemtemplate, usexloc, useyloc, tovis);\n                }\n            }\n        }\n    }\n    else\n    {\n            /* un-draw the individual points */\n        if (scalarvis != 0)\n        {\n            int i;\n            for (i = 0; i < nelem; i++)\n            {\n                t_gobj *y;\n                for (y = elemtemplatecanvas->gl_list; y; y = y->g_next)\n                {\n                    const t_parentwidgetbehavior *wb =\n                        pd_getparentwidget(&y->g_pd);\n                    if (!wb) continue;\n                    (*wb->w_parentvisfn)(y, glist,\n                        (t_word *)(elem + elemsize * i), elemtemplate,\n                            0, 0, 0);\n                }\n            }\n        }\n            /* and then the trace */\n        pdgui_vmess(0, \"crs\", glist_getcanvas(glist), \"delete\", tag);\n    }\n}\n\n    /* LATER protect against the template changing or the scalar disappearing\n    probably by attaching a gpointer here ... */\n\nstatic void array_motionfn(void *z, t_floatarg dx, t_floatarg dy, t_floatarg up)\n{\n    if (up != 0)\n        return;\n    TEMPLATE->array_motion_xcumulative += dx * TEMPLATE->array_motion_xperpix;\n    TEMPLATE->array_motion_ycumulative += dy * TEMPLATE->array_motion_yperpix;\n    if (TEMPLATE->array_motion_xfield)\n    {\n            /* it's an x, y plot */\n        int i;\n        for (i = 0; i < TEMPLATE->array_motion_npoints; i++)\n        {\n            t_word *thisword = (t_word *)(((char *)TEMPLATE->array_motion_wp) +\n                i * TEMPLATE->array_motion_elemsize);\n            t_float xwas = fielddesc_getcoord(TEMPLATE->array_motion_xfield,\n                TEMPLATE->array_motion_template, thisword, 1);\n            t_float ywas = (TEMPLATE->array_motion_yfield ?\n                fielddesc_getcoord(TEMPLATE->array_motion_yfield,\n                    TEMPLATE->array_motion_template, thisword, 1) : 0);\n            fielddesc_setcoord(TEMPLATE->array_motion_xfield,\n                TEMPLATE->array_motion_template, thisword,\n                    xwas + dx * TEMPLATE->array_motion_xperpix, 1);\n            if (TEMPLATE->array_motion_yfield)\n            {\n                if (TEMPLATE->array_motion_fatten)\n                {\n                    if (i == 0)\n                    {\n                        t_float newy = ywas +\n                            dy * TEMPLATE->array_motion_yperpix;\n                        if (newy < 0)\n                            newy = 0;\n                        fielddesc_setcoord(TEMPLATE->array_motion_yfield,\n                            TEMPLATE->array_motion_template, thisword, newy, 1);\n                    }\n                }\n                else\n                {\n                    fielddesc_setcoord(TEMPLATE->array_motion_yfield,\n                        TEMPLATE->array_motion_template, thisword,\n                            ywas + dy * TEMPLATE->array_motion_yperpix, 1);\n                }\n            }\n        }\n    }\n    else if (TEMPLATE->array_motion_yfield)\n    {\n            /* a y-only plot. */\n        int thisx = TEMPLATE->array_motion_initx +\n            TEMPLATE->array_motion_xcumulative + 0.5, x2;\n        int increment, i, nchange;\n        t_float newy = TEMPLATE->array_motion_ycumulative,\n            oldy = fielddesc_getcoord(TEMPLATE->array_motion_yfield,\n                TEMPLATE->array_motion_template,\n                    (t_word *)(((char *)TEMPLATE->array_motion_wp) +\n                        TEMPLATE->array_motion_elemsize *\n                            TEMPLATE->array_motion_lastx),\n                            1);\n        t_float ydiff = newy - oldy;\n        if (thisx < 0) thisx = 0;\n        else if (thisx >= TEMPLATE->array_motion_npoints)\n            thisx = TEMPLATE->array_motion_npoints - 1;\n        increment = (thisx > TEMPLATE->array_motion_lastx ? -1 : 1);\n        nchange = 1 + increment * (TEMPLATE->array_motion_lastx - thisx);\n\n        for (i = 0, x2 = thisx; i < nchange; i++, x2 += increment)\n        {\n            fielddesc_setcoord(TEMPLATE->array_motion_yfield,\n                TEMPLATE->array_motion_template,\n                    (t_word *)(((char *)TEMPLATE->array_motion_wp) +\n                        TEMPLATE->array_motion_elemsize * x2), newy, 1);\n            if (nchange > 1)\n                newy -= ydiff * (1./(nchange - 1));\n         }\n         TEMPLATE->array_motion_lastx = thisx;\n    }\n    if (TEMPLATE->array_motion_scalar)\n        scalar_redraw(TEMPLATE->array_motion_scalar,\n            TEMPLATE->array_motion_glist);\n    if (TEMPLATE->array_motion_array)\n        array_redraw(TEMPLATE->array_motion_array,\n            TEMPLATE->array_motion_glist);\n}\n\nint scalar_doclick(t_word *data, t_template *template, t_scalar *sc,\n    t_array *ap, struct _glist *owner,\n    t_float xloc, t_float yloc, int xpix, int ypix,\n    int shift, int alt, int dbl, int doit);\n\n    /* try clicking on an element of the array as a scalar (if clicking\n    on the trace of the array failed) */\nstatic int array_doclick_element(t_array *array, t_glist *glist,\n    t_symbol *elemtemplatesym,\n    t_float xloc, t_float xinc, t_float yloc,\n    t_fielddesc *xfield, t_fielddesc *yfield, t_fielddesc *wfield,\n    int xpix, int ypix, int shift, int alt, int dbl, int doit)\n{\n    t_canvas *elemtemplatecanvas;\n    t_template *elemtemplate;\n    int elemsize, yonset, wonset, xonset, i, incr, hit;\n    double xsum;\n\n    if (elemtemplatesym == &s_float)\n        return (0);\n    if (array_getfields(elemtemplatesym, &elemtemplatecanvas,\n        &elemtemplate, &elemsize, xfield, yfield, wfield,\n            &xonset, &yonset, &wonset))\n                return (0);\n        /* if it has more than 2000 points, just check 300 of them. */\n    if (array->a_n < 2000)\n        incr = 1;\n    else incr = array->a_n / 300;\n    for (i = 0, xsum = 0; i < array->a_n; i += incr)\n    {\n        t_float usexloc, useyloc;\n        if (xonset >= 0)\n            usexloc = xloc + fielddesc_cvttocoord(xfield,\n                *(t_float *)(((char *)(array->a_vec) + elemsize * i) + xonset));\n        else usexloc = xloc + xsum, xsum += xinc;\n        useyloc = yloc + (yonset >= 0 ? fielddesc_cvttocoord(yfield,\n            *(t_float *)\n                (((char *)(array->a_vec) + elemsize * i) + yonset)) : 0);\n\n        if ((hit = scalar_doclick(\n            (t_word *)((char *)(array->a_vec) + i * elemsize),\n            elemtemplate, 0, array,\n            glist, usexloc, useyloc,\n            xpix, ypix, shift, alt, dbl, doit)))\n                return (hit);\n    }\n    return (0);\n}\n\nstatic int array_doclick(t_array *array, t_glist *glist, t_scalar *sc,\n    t_array *ap, t_symbol *elemtemplatesym,\n    t_float xloc, t_float xinc, t_float yloc,\n    t_float scalarvis, t_float edit,\n    t_fielddesc *xfield, t_fielddesc *yfield, t_fielddesc *wfield,\n    int xpix, int ypix, int shift, int alt, int dbl, int doit)\n{\n    t_canvas *elemtemplatecanvas;\n    t_template *elemtemplate;\n    int elemsize, yonset, wonset, xonset, i;\n\n    if (!array_getfields(elemtemplatesym, &elemtemplatecanvas,\n        &elemtemplate, &elemsize, xfield, yfield, wfield,\n        &xonset, &yonset, &wonset))\n    {\n        t_float best = 100;\n            /* if it has more than 2000 points, just check 1000 of them. */\n        int incr = (array->a_n <= 2000 ? 1 : array->a_n / 1000);\n        TEMPLATE->array_motion_elemsize = elemsize;\n        TEMPLATE->array_motion_glist = glist;\n        TEMPLATE->array_motion_scalar = sc;\n        TEMPLATE->array_motion_array = ap;\n        TEMPLATE->array_motion_template = elemtemplate;\n        TEMPLATE->array_motion_xperpix = glist_dpixtodx(glist, 1);\n        TEMPLATE->array_motion_yperpix = glist_dpixtody(glist, 1);\n            /* if we're a garray, the only one here, and if we appear to have\n            only a 'y' field, click always succeeds and furthermore we'll\n            call \"motion\" later. */\n        if (glist->gl_list && pd_class(&glist->gl_list->g_pd) == garray_class\n            && !glist->gl_list->g_next &&\n                elemsize == sizeof(t_word))\n        {\n            int xval = glist_pixelstox(glist, xpix);\n            if (xval < 0)\n                xval = 0;\n            else if (xval >= array->a_n)\n                xval = array->a_n - 1;\n            TEMPLATE->array_motion_yfield = yfield;\n            TEMPLATE->array_motion_ycumulative = glist_pixelstoy(glist, ypix);\n            TEMPLATE->array_motion_fatten = 0;\n            TEMPLATE->array_motion_xfield = 0;\n            TEMPLATE->array_motion_xcumulative = 0;\n            TEMPLATE->array_motion_lastx = TEMPLATE->array_motion_initx = xval;\n            TEMPLATE->array_motion_npoints = array->a_n;\n            TEMPLATE->array_motion_wp = (t_word *)((char *)array->a_vec);\n            if (doit)\n            {\n                fielddesc_setcoord(yfield, elemtemplate,\n                    (t_word *)(((char *)array->a_vec) + elemsize * xval),\n                        glist_pixelstoy(glist, ypix), 1);\n                glist_grab(glist, 0, array_motionfn, 0, xpix, ypix);\n                if (TEMPLATE->array_motion_scalar)\n                    scalar_redraw(TEMPLATE->array_motion_scalar,\n                        TEMPLATE->array_motion_glist);\n                if (TEMPLATE->array_motion_array)\n                    array_redraw(TEMPLATE->array_motion_array,\n                        TEMPLATE->array_motion_glist);\n            }\n        }\n        else\n        {\n                /* First we get the closest distance to any element */\n            for (i = 0; i < array->a_n; i += incr)\n            {\n                t_float pxpix, pypix, pwpix, dx, dy;\n                array_getcoordinate(glist,\n                    (char *)(array->a_vec) + i * elemsize,\n                    xonset, yonset, wonset, i, xloc, yloc, xinc,\n                    xfield, yfield, wfield, &pxpix, &pypix, &pwpix);\n                if (pwpix < 4)\n                    pwpix = 4;\n                dx = pxpix - xpix;\n                if (dx < 0) dx = -dx;\n                if (dx > 8)\n                    continue;\n                dy = pypix - ypix;\n                if (dy < 0) dy = -dy;\n                if (dx + dy < best)\n                    best = dx + dy;\n                if (wonset >= 0)\n                {\n                    dy = (pypix + pwpix) - ypix;\n                    if (dy < 0) dy = -dy;\n                    if (dx + dy < best)\n                        best = dx + dy;\n                    dy = (pypix - pwpix) - ypix;\n                    if (dy < 0) dy = -dy;\n                    if (dx + dy < best)\n                        best = dx + dy;\n                }\n            }\n                /* If we're not too close, we first try to click a scalar\n                (if visible). This would not affect the array */\n            if (best > 8)\n            {\n                if (scalarvis != 0)\n                {\n                    return (array_doclick_element(array, glist,\n                        elemtemplatesym, xloc, xinc, yloc,\n                            xfield, yfield, wfield,\n                            xpix, ypix, shift, alt, dbl, doit));\n\n                }\n                else return (0);\n            }\n                /* Now we walk over the array again and decide whether we\n                a) grab an element, b) change the line width,\n                c) delete an element or d) add a new element */\n            best += 0.001;  /* add truncation error margin */\n             /* otherwise we try to grab a vertex */\n            if (!edit)\n                return (0);\n            for (i = 0; i < array->a_n; i += incr)\n            {\n                t_float pxpix, pypix, pwpix, dx, dy, dy2, dy3;\n                array_getcoordinate(glist,\n                    (char *)(array->a_vec) + i * elemsize,\n                    xonset, yonset, wonset, i, xloc, yloc, xinc,\n                    xfield, yfield, wfield, &pxpix, &pypix, &pwpix);\n                if (pwpix < 4)\n                    pwpix = 4;\n                dx = pxpix - xpix;\n                if (dx < 0) dx = -dx;\n                dy = pypix - ypix;\n                if (dy < 0) dy = -dy;\n                if (wonset >= 0)\n                {\n                    dy2 = (pypix + pwpix) - ypix;\n                    if (dy2 < 0) dy2 = -dy2;\n                    dy3 = (pypix - pwpix) - ypix;\n                    if (dy3 < 0) dy3 = -dy3;\n                    if (yonset < 0)\n                        dy = 100;\n                }\n                else dy2 = dy3 = 100;\n                if (dx + dy <= best || dx + dy2 <= best || dx + dy3 <= best)\n                {\n                    if (dy < dy2 && dy < dy3)\n                        TEMPLATE->array_motion_fatten = 0;\n                    else if (dy2 < dy3)\n                        TEMPLATE->array_motion_fatten = -1;\n                    else TEMPLATE->array_motion_fatten = 1;\n                    if (doit)\n                    {\n                        char *elem = (char *)array->a_vec;\n                        if (alt && xpix < pxpix) /* delete a point */\n                        {\n                            if (array->a_n <= 1)\n                                return (0);\n                            memmove((char *)(array->a_vec) + elemsize * i,\n                                (char *)(array->a_vec) + elemsize * (i+1),\n                                    (array->a_n - 1 - i) * elemsize);\n                            array_resize_and_redraw(array,\n                                glist, array->a_n - 1);\n                            return (0);\n                        }\n                        else if (alt)\n                        {\n                            /* add a point (after the clicked-on one) */\n                            array_resize_and_redraw(array, glist,\n                                array->a_n + 1);\n                            elem = (char *)array->a_vec;\n                            memmove(elem + elemsize * (i+1),\n                                elem + elemsize * i,\n                                    (array->a_n - i - 1) * elemsize);\n                            i++;\n                        }\n                        if (xonset >= 0)\n                        {\n                            TEMPLATE->array_motion_xfield = xfield;\n                            TEMPLATE->array_motion_xcumulative =\n                                fielddesc_getcoord(xfield,\n                                    TEMPLATE->array_motion_template,\n                                    (t_word *)(elem + i * elemsize), 1);\n                                TEMPLATE->array_motion_wp =\n                                    (t_word *)(elem + i * elemsize);\n                            if (shift)\n                                TEMPLATE->array_motion_npoints =\n                                    array->a_n - i;\n                            else TEMPLATE->array_motion_npoints = 1;\n                        }\n                        else\n                        {\n                            TEMPLATE->array_motion_xfield = 0;\n                            TEMPLATE->array_motion_xcumulative = 0;\n                            TEMPLATE->array_motion_wp = (t_word *)elem;\n                            TEMPLATE->array_motion_npoints = array->a_n;\n\n                            TEMPLATE->array_motion_initx = i;\n                            TEMPLATE->array_motion_lastx = i;\n                            TEMPLATE->array_motion_xperpix *=\n                                (xinc == 0 ? 1 : 1./xinc);\n                        }\n                        if (TEMPLATE->array_motion_fatten)\n                        {\n                            TEMPLATE->array_motion_yfield = wfield;\n                            TEMPLATE->array_motion_ycumulative =\n                                fielddesc_getcoord(wfield,\n                                    TEMPLATE->array_motion_template,\n                                    (t_word *)(elem + i * elemsize), 1);\n                            if (TEMPLATE->array_motion_yperpix < 0)\n                                TEMPLATE->array_motion_yperpix *= -1;\n                            TEMPLATE->array_motion_yperpix *=\n                                -TEMPLATE->array_motion_fatten;\n                        }\n                        else if (yonset >= 0)\n                        {\n                            TEMPLATE->array_motion_yfield = yfield;\n                            TEMPLATE->array_motion_ycumulative =\n                                fielddesc_getcoord(yfield,\n                                    TEMPLATE->array_motion_template,\n                                    (t_word *)(elem + i * elemsize), 1);\n                        }\n                        else\n                        {\n                            TEMPLATE->array_motion_yfield = 0;\n                            TEMPLATE->array_motion_ycumulative = 0;\n                        }\n                        glist_grab(glist, 0, array_motionfn, 0, xpix, ypix);\n                    }\n                    if (alt)\n                    {\n                        if (xpix < pxpix)\n                            return (CURSOR_EDITMODE_DISCONNECT);\n                        else return (CURSOR_RUNMODE_ADDPOINT);\n                    }\n                    else return (TEMPLATE->array_motion_fatten ?\n                        CURSOR_RUNMODE_THICKEN : CURSOR_RUNMODE_CLICKME);\n                }\n            }\n        }\n    }\n        /* JMZ: change the cursor to \"clickme\" if the array can be edited */\n    return (!edit)?CURSOR_RUNMODE_NOTHING:CURSOR_RUNMODE_CLICKME;\n}\n\nstatic int plot_click(t_gobj *z, t_glist *glist,\n    t_word *data, t_template *template, t_scalar *sc, t_array *ap,\n    t_float basex, t_float basey,\n    int xpix, int ypix, int shift, int alt, int dbl, int doit)\n{\n    t_plot *x = (t_plot *)z;\n    t_symbol *elemtemplatesym;\n    t_float linewidth, xloc, xinc, yloc, style, vis, scalarvis, edit;\n    t_array *array;\n    t_fielddesc *xfielddesc, *yfielddesc, *wfielddesc;\n\n    if (!plot_readownertemplate(x, data, template,\n        &elemtemplatesym, &array, &linewidth, &xloc, &xinc, &yloc, &style,\n        &vis, &scalarvis, &edit,\n        &xfielddesc, &yfielddesc, &wfielddesc) && (vis != 0))\n    {\n        return (array_doclick(array, glist, sc, ap, elemtemplatesym,\n            basex + xloc, xinc, basey + yloc, scalarvis, edit,\n            xfielddesc, yfielddesc, wfielddesc,\n            xpix, ypix, shift, alt, dbl, doit));\n    }\n    else return (0);\n}\n\nconst t_parentwidgetbehavior plot_widgetbehavior =\n{\n    plot_getrect,\n    plot_displace,\n    plot_select,\n    plot_activate,\n    plot_vis,\n    plot_click,\n};\n\nstatic void plot_setup(void)\n{\n    plot_class = class_new(gensym(\"plot\"), (t_newmethod)plot_new, 0,\n        sizeof(t_plot), 0, A_GIMME, 0);\n    class_setdrawcommand(plot_class);\n    class_addfloat(plot_class, plot_float);\n    class_setparentwidget(plot_class, &plot_widgetbehavior);\n}\n\n/* ---------------- drawnumber: draw a number (or symbol) ---------------- */\n\n/*\n    drawnumbers draw numeric fields at controllable locations, with\n    controllable color and label.  invocation:\n    (drawnumber|drawsymbol) [-v <visible>] variable x y color label\n*/\n\nt_class *drawnumber_class;\n\ntypedef struct _drawnumber\n{\n    t_object x_obj;\n    t_symbol *x_fieldname;\n    t_fielddesc x_xloc;\n    t_fielddesc x_yloc;\n    t_fielddesc x_color;\n    t_fielddesc x_vis;\n    t_symbol *x_label;\n    t_canvas *x_canvas;\n} t_drawnumber;\n\nstatic void *drawnumber_new(t_symbol *classsym, int argc, t_atom *argv)\n{\n    t_drawnumber *x = (t_drawnumber *)pd_new(drawnumber_class);\n    const char *classname = classsym->s_name;\n\n    fielddesc_setfloat_const(&x->x_vis, 1);\n    x->x_canvas = canvas_getcurrent();\n    while (1)\n    {\n        t_symbol *firstarg = atom_getsymbolarg(0, argc, argv);\n        if (!strcmp(firstarg->s_name, \"-v\") && argc > 1)\n        {\n            fielddesc_setfloatarg(&x->x_vis, 1, argv+1);\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(firstarg->s_name, \"-n\"))\n        {\n            fielddesc_setfloat_const(&x->x_vis, 0);\n            argc--; argv++;\n        }\n        else if (*firstarg->s_name == '-')\n        {\n            pd_error(x, \"%s: unknown flag '%s'...\", classsym->s_name,\n                firstarg->s_name);\n            argc--; argv++;\n        }\n        else break;\n    }\n        /* next argument is name of field to draw - we don't know its type yet\n        but fielddesc_setfloatarg() will do fine here. */\n    x->x_fieldname = atom_getsymbolarg(0, argc, argv);\n    if (argc)\n        argc--, argv++;\n    if (argc) fielddesc_setfloatarg(&x->x_xloc, argc--, argv++);\n    else fielddesc_setfloat_const(&x->x_xloc, 0);\n    if (argc) fielddesc_setfloatarg(&x->x_yloc, argc--, argv++);\n    else fielddesc_setfloat_const(&x->x_yloc, 0);\n    if (argc) fielddesc_setfloatarg(&x->x_color, argc--, argv++);\n    else fielddesc_setfloat_const(&x->x_color, 0);\n    if (argc)\n        x->x_label = atom_getsymbolarg(0, argc, argv);\n    else x->x_label = &s_;\n\n    return (x);\n}\n\nvoid drawnumber_float(t_drawnumber *x, t_floatarg f)\n{\n    int viswas;\n    if (x->x_vis.fd_type != A_FLOAT || x->x_vis.fd_var)\n    {\n        pd_error(x, \"global vis/invis for a template with variable visibility\");\n        return;\n    }\n    viswas = (x->x_vis.fd_un.fd_float != 0);\n\n    if ((f != 0 && viswas) || (f == 0 && !viswas))\n        return;\n    canvas_redrawallfortemplatecanvas(x->x_canvas, 2);\n    fielddesc_setfloat_const(&x->x_vis, (f != 0));\n    canvas_redrawallfortemplatecanvas(x->x_canvas, 1);\n}\n\n/* -------------------- widget behavior for drawnumber ------------ */\n\nstatic int drawnumber_gettype(t_drawnumber *x, t_word *data,\n    t_template *template, int *onsetp)\n{\n    int type;\n    t_symbol *arraytype;\n    if (template_find_field(template, x->x_fieldname, onsetp, &type,\n        &arraytype) && type != DT_ARRAY)\n            return (type);\n    else return (-1);\n}\n\n#define DRAWNUMBER_BUFSIZE 1024\nstatic void drawnumber_getbuf(t_drawnumber *x, t_word *data,\n    t_template *template, char *buf)\n{\n    int nchars, onset, type = drawnumber_gettype(x, data, template, &onset);\n    if (type < 0)\n        buf[0] = 0;\n    else\n    {\n        strncpy(buf, x->x_label->s_name, DRAWNUMBER_BUFSIZE);\n        buf[DRAWNUMBER_BUFSIZE - 1] = 0;\n        nchars = (int)strlen(buf);\n        if (type == DT_TEXT)\n        {\n            char *buf2;\n            int size2, ncopy;\n            binbuf_gettext(((t_word *)((char *)data + onset))->w_binbuf,\n                &buf2, &size2);\n            ncopy = (size2 > DRAWNUMBER_BUFSIZE-1-nchars ?\n                DRAWNUMBER_BUFSIZE-1-nchars: size2);\n            memcpy(buf+nchars, buf2, ncopy);\n            buf[nchars+ncopy] = 0;\n            if (nchars+ncopy == DRAWNUMBER_BUFSIZE-1)\n                strcpy(buf+(DRAWNUMBER_BUFSIZE-4), \"...\");\n            t_freebytes(buf2, size2);\n        }\n        else\n        {\n            t_atom at;\n            switch(type)\n            {\n            default:\n                SETSYMBOL(&at, ((t_word *)((char *)data + onset))->w_symbol);\n                atom_string(&at, buf + nchars, DRAWNUMBER_BUFSIZE - nchars);\n                break;\n            case DT_FLOAT:\n                SETFLOAT(&at, ((t_word *)((char *)data + onset))->w_float);\n                atom_string(&at, buf + nchars, DRAWNUMBER_BUFSIZE - nchars);\n                break;\n            case DT_SYMBOL:\n                strncpy(buf + nchars,\n                    ((t_word *)((char *)data + onset))->w_symbol->s_name,\n                    DRAWNUMBER_BUFSIZE - nchars);\n            }\n        }\n    }\n}\n\nstatic void drawnumber_getrect(t_gobj *z, t_glist *glist,\n    t_word *data, t_template *template, t_float basex, t_float basey,\n    int *xp1, int *yp1, int *xp2, int *yp2)\n{\n    t_drawnumber *x = (t_drawnumber *)z;\n    t_atom at;\n    int xloc, yloc, fontwidth, fontheight, bufsize, width, height;\n    char buf[DRAWNUMBER_BUFSIZE], *startline, *newline;\n\n    if (!fielddesc_getfloat(&x->x_vis, template, data, 0))\n    {\n        *xp1 = *yp1 = 0x7fffffff;\n        *xp2 = *yp2 = -0x7fffffff;\n        return;\n    }\n    xloc = glist_xtopixels(glist,\n        basex + fielddesc_getcoord(&x->x_xloc, template, data, 0));\n    yloc = glist_ytopixels(glist,\n        basey + fielddesc_getcoord(&x->x_yloc, template, data, 0));\n    fontwidth = glist_fontwidth(glist);\n    fontheight = glist_fontheight(glist);\n    drawnumber_getbuf(x, data, template, buf);\n    width = 0;\n    height = 1;\n    for (startline = buf; (newline = strchr(startline, '\\n'));\n        startline = newline+1)\n    {\n        if (newline - startline > width)\n            width = (int)(newline - startline);\n        height++;\n    }\n    if (strlen(startline) > (unsigned)width)\n        width = (int)strlen(startline);\n    *xp1 = xloc;\n    *yp1 = yloc;\n    *xp2 = xloc + fontwidth * width;\n    *yp2 = yloc + fontheight * height;\n}\n\nstatic void drawnumber_displace(t_gobj *z, t_glist *glist,\n    t_word *data, t_template *template, t_float basex, t_float basey,\n    int dx, int dy)\n{\n    /* refuse */\n}\n\nstatic void drawnumber_select(t_gobj *z, t_glist *glist,\n    t_word *data, t_template *template, t_float basex, t_float basey,\n    int state)\n{\n    post(\"drawnumber_select %d\", state);\n    /* fill in later */\n}\n\nstatic void drawnumber_activate(t_gobj *z, t_glist *glist,\n    t_word *data, t_template *template, t_float basex, t_float basey,\n    int state)\n{\n    post(\"drawnumber_activate %d\", state);\n}\n\nstatic void drawnumber_vis(t_gobj *z, t_glist *glist,\n    t_word *data, t_template *template, t_float basex, t_float basey,\n    int vis)\n{\n    t_drawnumber *x = (t_drawnumber *)z;\n    char tag[80];\n    const char*tags[] = {tag, \"label\"};\n\n        /* see comment in plot_vis() */\n    if (vis && !fielddesc_getfloat(&x->x_vis, template, data, 0))\n        return;\n    sprintf(tag, \"drawnumber%p\", data);\n    if (vis)\n    {\n        t_atom fontatoms[3];\n        t_atom at;\n        int xloc = glist_xtopixels(glist,\n            basex + fielddesc_getcoord(&x->x_xloc, template, data, 0));\n        int yloc = glist_ytopixels(glist,\n            basey + fielddesc_getcoord(&x->x_yloc, template, data, 0));\n        char buf[DRAWNUMBER_BUFSIZE];\n        int color = numbertocolor(\n            fielddesc_getfloat(&x->x_color, template, data, 1));\n        drawnumber_getbuf(x, data, template, buf);\n\n        SETSYMBOL(fontatoms+0, gensym(sys_font));\n        SETFLOAT (fontatoms+1,-sys_hostfontsize(glist_getfont(glist), glist_getzoom(glist)));\n        SETSYMBOL(fontatoms+2, gensym(sys_fontweight));\n        pdgui_vmess(0, \"crr ii rs rk rs rA rS\",\n            glist_getcanvas(glist), \"create\", \"text\",\n            xloc, yloc,\n            \"-anchor\", \"nw\",\n            \"-fill\", color,\n            \"-text\", buf,\n            \"-font\", 3, fontatoms,\n            \"-tags\", 2, tags);\n    }\n    else\n        pdgui_vmess(0, \"crs\", glist_getcanvas(glist), \"delete\", tag);\n}\n\nstatic void drawnumber_motionfn(void *z, t_floatarg dx, t_floatarg dy,\n    t_floatarg up)\n{\n    t_drawnumber *x = (t_drawnumber *)z;\n    t_atom at;\n    if (up != 0)\n        return;\n    if (!gpointer_check(&TEMPLATE->drawnumber_motion_gpointer, 0))\n    {\n        post(\"drawnumber_motion: scalar disappeared\");\n        return;\n    }\n    if (TEMPLATE->drawnumber_motion_type != DT_FLOAT)\n        return;\n    TEMPLATE->drawnumber_motion_ycumulative -= dy;\n    template_setfloat(TEMPLATE->drawnumber_motion_template,\n        x->x_fieldname,\n            TEMPLATE->drawnumber_motion_wp,\n            TEMPLATE->drawnumber_motion_ycumulative,\n                1);\n    if (TEMPLATE->drawnumber_motion_scalar)\n        template_notifyforscalar(TEMPLATE->drawnumber_motion_template,\n            TEMPLATE->drawnumber_motion_glist,\n                TEMPLATE->drawnumber_motion_scalar,\n                gensym(\"change\"), 1, &at);\n\n    if (TEMPLATE->drawnumber_motion_scalar)\n        scalar_redraw(TEMPLATE->drawnumber_motion_scalar,\n            TEMPLATE->drawnumber_motion_glist);\n    if (TEMPLATE->drawnumber_motion_array)\n        array_redraw(TEMPLATE->drawnumber_motion_array,\n            TEMPLATE->drawnumber_motion_glist);\n}\n\nstatic void drawnumber_key(void *z, t_symbol *keysym, t_floatarg fkey)\n{\n    t_drawnumber *x = (t_drawnumber *)z;\n    int key = fkey;\n    char sbuf[MAXPDSTRING];\n    t_atom at;\n    if (!gpointer_check(&TEMPLATE->drawnumber_motion_gpointer, 0))\n    {\n        post(\"drawnumber_motion: scalar disappeared\");\n        return;\n    }\n    if (key == 0)\n        return;\n    if (TEMPLATE->drawnumber_motion_type == DT_SYMBOL)\n    {\n            /* key entry for a symbol field */\n        if (TEMPLATE->drawnumber_motion_firstkey)\n            sbuf[0] = 0;\n        else strncpy(sbuf,\n            template_getsymbol(TEMPLATE->drawnumber_motion_template,\n            x->x_fieldname, TEMPLATE->drawnumber_motion_wp, 1)->s_name,\n                MAXPDSTRING);\n        sbuf[MAXPDSTRING-1] = 0;\n        if (key == '\\b')\n        {\n            if (*sbuf)\n                sbuf[strlen(sbuf)-1] = 0;\n        }\n        else\n        {\n            sbuf[strlen(sbuf)+1] = 0;\n            sbuf[strlen(sbuf)] = key;\n        }\n    }\n    else if (TEMPLATE->drawnumber_motion_type == DT_FLOAT)\n    {\n            /* key entry for a numeric field.  This is just a stopgap. */\n        double newf;\n        if (TEMPLATE->drawnumber_motion_firstkey)\n            sbuf[0] = 0;\n        else sprintf(sbuf, \"%g\",\n            template_getfloat(TEMPLATE->drawnumber_motion_template,\n            x->x_fieldname, TEMPLATE->drawnumber_motion_wp, 1));\n        TEMPLATE->drawnumber_motion_firstkey = (key == '\\n');\n        if (key == '\\b')\n        {\n            if (*sbuf)\n                sbuf[strlen(sbuf)-1] = 0;\n        }\n        else\n        {\n            sbuf[strlen(sbuf)+1] = 0;\n            sbuf[strlen(sbuf)] = key;\n        }\n        if (sscanf(sbuf, \"%lg\", &newf) < 1)\n            newf = 0;\n        template_setfloat(TEMPLATE->drawnumber_motion_template,\n            x->x_fieldname, TEMPLATE->drawnumber_motion_wp, (t_float)newf, 1);\n        if (TEMPLATE->drawnumber_motion_scalar)\n            template_notifyforscalar(TEMPLATE->drawnumber_motion_template,\n                TEMPLATE->drawnumber_motion_glist,\n                    TEMPLATE->drawnumber_motion_scalar,\n                    gensym(\"change\"), 1, &at);\n        if (TEMPLATE->drawnumber_motion_scalar)\n            scalar_redraw(TEMPLATE->drawnumber_motion_scalar,\n                TEMPLATE->drawnumber_motion_glist);\n        if (TEMPLATE->drawnumber_motion_array)\n            array_redraw(TEMPLATE->drawnumber_motion_array,\n                TEMPLATE->drawnumber_motion_glist);\n    }\n    else post(\"typing at text fields not yet implemented\");\n}\n\nstatic int drawnumber_click(t_gobj *z, t_glist *glist,\n    t_word *data, t_template *template, t_scalar *sc, t_array *ap,\n    t_float basex, t_float basey,\n    int xpix, int ypix, int shift, int alt, int dbl, int doit)\n{\n    t_drawnumber *x = (t_drawnumber *)z;\n    int x1, y1, x2, y2, type, onset;\n    drawnumber_getrect(z, glist,\n        data, template, basex, basey,\n        &x1, &y1, &x2, &y2);\n    if (xpix >= x1 && xpix <= x2 && ypix >= y1 && ypix <= y2 &&\n        ((type = drawnumber_gettype(x, data, template, &onset)) == DT_FLOAT ||\n            type == DT_SYMBOL))\n    {\n        if (doit)\n        {\n            TEMPLATE->drawnumber_motion_glist = glist;\n            TEMPLATE->drawnumber_motion_wp = data;\n            TEMPLATE->drawnumber_motion_template = template;\n            TEMPLATE->drawnumber_motion_scalar = sc;\n            TEMPLATE->drawnumber_motion_array = ap;\n            TEMPLATE->drawnumber_motion_firstkey = 1;\n            TEMPLATE->drawnumber_motion_ycumulative =\n                template_getfloat(template, x->x_fieldname, data, 0);\n            TEMPLATE->drawnumber_motion_type = type;\n            if (TEMPLATE->drawnumber_motion_scalar)\n                gpointer_setglist(&TEMPLATE->drawnumber_motion_gpointer,\n                    TEMPLATE->drawnumber_motion_glist,\n                        TEMPLATE->drawnumber_motion_scalar);\n            else gpointer_setarray(&TEMPLATE->drawnumber_motion_gpointer,\n                    TEMPLATE->drawnumber_motion_array,\n                        TEMPLATE->drawnumber_motion_wp);\n            glist_grab(glist, z, drawnumber_motionfn, drawnumber_key,\n                xpix, ypix);\n        }\n        return (1);\n    }\n    else return (0);\n}\n\nconst t_parentwidgetbehavior drawnumber_widgetbehavior =\n{\n    drawnumber_getrect,\n    drawnumber_displace,\n    drawnumber_select,\n    drawnumber_activate,\n    drawnumber_vis,\n    drawnumber_click,\n};\n\nstatic void drawnumber_free(t_drawnumber *x)\n{\n}\n\nstatic void drawnumber_setup(void)\n{\n    drawnumber_class = class_new(gensym(\"drawtext\"),\n        (t_newmethod)drawnumber_new, (t_method)drawnumber_free,\n        sizeof(t_drawnumber), 0, A_GIMME, 0);\n    class_setdrawcommand(drawnumber_class);\n    class_addfloat(drawnumber_class, drawnumber_float);\n    class_addcreator((t_newmethod)drawnumber_new, gensym(\"drawsymbol\"),\n        A_GIMME, 0);\n    class_addcreator((t_newmethod)drawnumber_new, gensym(\"drawnumber\"),\n        A_GIMME, 0);\n    class_setparentwidget(drawnumber_class, &drawnumber_widgetbehavior);\n}\n\n/* ---------------------- setup function ---------------------------- */\n\nvoid g_template_setup(void)\n{\n    template_setup();\n    gtemplate_setup();\n    curve_setup();\n    plot_setup();\n    drawnumber_setup();\n}\n\nvoid g_template_newpdinstance(void)\n{\n    TEMPLATE = getbytes(sizeof(*TEMPLATE));\n}\n\nvoid g_template_freepdinstance(void)\n{\n    freebytes(TEMPLATE, sizeof(*TEMPLATE));\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/g_text.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* changes by Thomas Musil IEM KUG Graz Austria 2001 */\n/* the methods for calling the gui-objects from menu are implemented */\n/* all changes are labeled with      iemlib      */\n\n#include <stdlib.h>\n#include \"m_pd.h\"\n#include \"m_imp.h\"\n\n#include \"g_canvas.h\"\n#include <stdio.h>\n#include <string.h>\n#include <math.h>\n\n#include \"g_undo.h\"\n\n/* borrowed from RMARGIN and BMARGIN in g_rtext.c */\n#define ATOM_RMARGIN 2 /* 2 pixels smaller than object LMARGIN + RMARGIN */\n#define ATOM_BMARGIN 4 /* 1 pixel smaller than object TMARGIN+BMARGIN */\n\n#define MESSAGE_CLICK_WIDTH 5\n\nt_class *text_class;\nstatic t_class *message_class;\nstatic t_class *gatom_class;\nstatic void text_vis(t_gobj *z, t_glist *glist, int vis);\nstatic void text_displace(t_gobj *z, t_glist *glist,\n    int dx, int dy);\nstatic void text_getrect(t_gobj *z, t_glist *glist,\n    int *xp1, int *yp1, int *xp2, int *yp2);\n\nvoid canvas_startmotion(t_canvas *x);\nint glist_getindex(t_glist *x, t_gobj *y);\nvoid gatom_undarken(t_text *x);\n\nstatic void glist_nograb(t_glist *x)\n{\n    if (x->gl_editor)\n    {\n        t_canvas *canvas = glist_getcanvas(x);\n        t_object *ob;\n        t_gobj*g;\n        for (g = canvas->gl_list; g; g = g->g_next)\n            if ((ob = pd_checkobject(&g->g_pd)) && T_ATOM == ob->te_type)\n                gatom_undarken(ob);\n        x->gl_editor->e_grab = 0;\n    }\n}\n\n/* ----------------- the \"text\" object.  ------------------ */\n\n    /* add a \"text\" object (comment) to a glist.  Called without args\n    if invoked from the GUI; otherwise at least x and y are provided.  */\n\nvoid glist_text(t_glist *gl, t_symbol *s, int argc, t_atom *argv)\n{\n    t_text *x = (t_text *)pd_new(text_class);\n    t_atom at;\n    x->te_width = 0;                            /* don't know it yet. */\n    x->te_type = T_TEXT;\n    x->te_binbuf = binbuf_new();\n    if (argc > 1)\n    {\n        x->te_xpix = atom_getfloatarg(0, argc, argv);\n        x->te_ypix = atom_getfloatarg(1, argc, argv);\n        if (argc > 2) binbuf_restore(x->te_binbuf, argc-2, argv+2);\n        else\n        {\n            SETSYMBOL(&at, gensym(\"comment\"));\n            binbuf_restore(x->te_binbuf, 1, &at);\n        }\n        glist_add(gl, &x->te_g);\n    }\n    else\n    {\n        int xpix, ypix;\n        pd_vmess((t_pd *)glist_getcanvas(gl), gensym(\"editmode\"), \"i\", 1);\n        SETSYMBOL(&at, gensym(\"comment\"));\n        glist_noselect(gl);\n        glist_nograb(gl);\n        glist_getnextxy(gl, &xpix, &ypix);\n        x->te_xpix = xpix/gl->gl_zoom - 1;\n        x->te_ypix = ypix/gl->gl_zoom - 1;\n        binbuf_restore(x->te_binbuf, 1, &at);\n        glist_add(gl, &x->te_g);\n        glist_noselect(gl);\n        glist_select(gl, &x->te_g);\n            /* it would be nice to \"activate\" here, but then the second,\n            \"put-me-down\" click changes the text selection, which is quite\n            irritating, so I took this back out.  It's OK in messages\n            and objects though since there's no text in them at menu\n            creation. */\n            /* gobj_activate(&x->te_g, gl, 1); */\n        if (!canvas_undo_get(glist_getcanvas(gl))->u_doing)\n            canvas_undo_add(glist_getcanvas(gl), UNDO_CREATE, \"create\",\n                (void *)canvas_undo_set_create(glist_getcanvas(gl)));\n        canvas_startmotion(glist_getcanvas(gl));\n    }\n}\n\n/* ----------------- the \"object\" object.  ------------------ */\n\nvoid canvas_getargs(int *argcp, t_atom **argvp);\n\nstatic void canvas_error_couldntcreate(void*x, t_binbuf*b, const char*errmsg)\n{\n    char *buf=0;\n    int bufsize=0;\n    if (!binbuf_getnatom(b))\n        return;\n    binbuf_gettext(b, &buf, &bufsize);\n    buf = resizebytes(buf, bufsize, bufsize+1);\n    buf[bufsize] = 0;\n    logpost(x, PD_CRITICAL, \"%s\", buf);\n    logpost(x, PD_ERROR, \"%s\", errmsg);\n    freebytes(buf, bufsize);\n}\n\nstatic void canvas_objtext(t_glist *gl, int xpix, int ypix, int width,\n    int selected, t_binbuf *b)\n{\n    t_text *x;\n    int argc;\n    t_atom *argv;\n    pd_this->pd_newest = 0;\n    canvas_setcurrent((t_canvas *)gl);\n    canvas_getargs(&argc, &argv);\n    binbuf_eval(b, &pd_objectmaker, argc, argv);\n    if (binbuf_getnatom(b))\n    {\n        if (!pd_this->pd_newest)\n            x = 0;\n        else if (!(x = pd_checkobject(pd_this->pd_newest)))\n        {\n            canvas_error_couldntcreate(0, b, \"... didn't return a patchable object\");\n        }\n    }\n    else x = 0;\n    if (!x)\n    {\n        x = (t_text *)pd_new(text_class);\n        canvas_error_couldntcreate(x, b, \"... couldn't create\");\n    }\n    x->te_binbuf = b;\n    x->te_xpix = xpix;\n    x->te_ypix = ypix;\n    x->te_width = width;\n    x->te_type = T_OBJECT;\n    glist_add(gl, &x->te_g);\n    if (selected)\n    {\n            /* this is called if we've been created from the menu. */\n        glist_select(gl, &x->te_g);\n        gobj_activate(&x->te_g, gl, 1);\n    }\n    if (pd_class(&x->ob_pd) == vinlet_class)\n        canvas_resortinlets(glist_getcanvas(gl));\n    if (pd_class(&x->ob_pd) == voutlet_class)\n        canvas_resortoutlets(glist_getcanvas(gl));\n    canvas_unsetcurrent((t_canvas *)gl);\n}\n\nextern int sys_noautopatch;\n    /* utility routine to figure out where to put a new text box from menu\n    and whether to connect to it automatically */\nstatic void canvas_howputnew(t_canvas *x, int *connectp, int *xpixp, int *ypixp,\n    int *indexp, int *totalp)\n{\n    float dx = 5.5 * x->gl_zoom;\n    int xpix, ypix, indx = 0, nobj = 0, n2, x1, x2, y1, y2;\n    int connectme = (x->gl_editor->e_selection &&\n        !x->gl_editor->e_selection->sel_next && !sys_noautopatch);\n    glist_nograb(x);\n    if (connectme)\n    {\n        t_gobj *g, *selected = x->gl_editor->e_selection->sel_what;\n            /* get number of objects */\n        for (g = x->gl_list, nobj = 0; g; g = g->g_next, nobj++) ;\n\n            /* deselect the current selection (and create pending objects) */\n        glist_noselect(x);\n\n            /* search back for 'selected' and if it isn't on the list,\n                plan just to connect from the last item on the list. */\n        for (g = x->gl_list, n2 = 0; g; g = g->g_next, n2++)\n        {\n            if (g == selected)\n            {\n                indx = n2;\n                break;\n            }\n            else if (!g->g_next) {\n                    /* we couldn't find the selected object any more\n                     * this probably means, that it was replaced by a newly\n                     * created object, which is now the last on the list.\n                     */\n                indx = nobj-1;\n                break;\n            }\n        }\n            /* so where do we put the new object?\n               just below the one we connect from! */\n        if(g) {\n            gobj_getrect(g, x, &x1, &y1, &x2, &y2);\n            *xpixp = x1 / x->gl_zoom;\n            *ypixp = (y2+dx) / x->gl_zoom;  /* 5 pixels down, rounded */\n        } else {\n                /* just in case */\n            glist_getnextxy(x, xpixp, ypixp);\n            *xpixp = *xpixp/x->gl_zoom - 3;\n            *ypixp = *ypixp/x->gl_zoom - 3;\n        }\n    }\n    else\n    {\n        glist_getnextxy(x, xpixp, ypixp);\n        *xpixp = *xpixp/x->gl_zoom - 3;\n        *ypixp = *ypixp/x->gl_zoom - 3;\n        glist_noselect(x);\n    }\n    *connectp = connectme;\n    *indexp = indx;\n    *totalp = nobj;\n}\n\n    /* object creation routine.  These are called without any arguments if\n    they're invoked from the gui; when pasting or restoring from a file, we\n    get at least x and y. */\n\nvoid canvas_obj(t_glist *gl, t_symbol *s, int argc, t_atom *argv)\n{\n    t_text *x;\n    if (argc >= 2)\n    {\n        t_binbuf *b = binbuf_new();\n        binbuf_restore(b, argc-2, argv+2);\n        canvas_objtext(gl, atom_getfloatarg(0, argc, argv),\n            atom_getfloatarg(1, argc, argv), 0, 0, b);\n    }\n        /* JMZ: don't go into interactive mode in a closed canvas */\n    else if (!glist_isvisible(gl))\n        post(\"unable to create stub object in closed canvas!\");\n    else\n    {\n            /* interactively create new object */\n        t_binbuf *b = binbuf_new();\n        int connectme, xpix, ypix, indx, nobj;\n        canvas_howputnew(gl, &connectme, &xpix, &ypix, &indx, &nobj);\n        pd_vmess(&gl->gl_pd, gensym(\"editmode\"), \"i\", 1);\n        canvas_objtext(gl, xpix, ypix, 0, 1, b);\n        if (connectme)\n            canvas_connect(gl, indx, 0, nobj, 0);\n        else canvas_startmotion(glist_getcanvas(gl));\n        if (!canvas_undo_get(glist_getcanvas(gl))->u_doing)\n            canvas_undo_add(glist_getcanvas(gl), UNDO_CREATE, \"create\",\n                (void *)canvas_undo_set_create(glist_getcanvas(gl)));\n    }\n}\n\n/* make an object box for an object that's already there. */\n\n/* iemlib */\nvoid canvas_iemguis(t_glist *gl, t_symbol *guiobjname)\n{\n    t_atom at;\n    t_binbuf *b = binbuf_new();\n\n    int connectme, xpix, ypix, indx, nobj;\n    canvas_howputnew(gl, &connectme, &xpix, &ypix, &indx, &nobj);\n\n    pd_vmess(&gl->gl_pd, gensym(\"editmode\"), \"i\", 1);\n\n    glist_noselect(gl);\n    SETSYMBOL(&at, guiobjname);\n    binbuf_restore(b, 1, &at);\n\n    canvas_objtext(gl, xpix, ypix, 0, 1, b);\n    if(connectme)\n        canvas_connect(gl, indx, 0, nobj, 0);\n    else canvas_startmotion(glist_getcanvas(gl));\n    canvas_undo_add(glist_getcanvas(gl), UNDO_CREATE, \"create\",\n        (void *)canvas_undo_set_create(glist_getcanvas(gl)));\n}\n\nvoid canvas_bng(t_glist *gl, t_symbol *s, int argc, t_atom *argv)\n{\n    canvas_iemguis(gl, gensym(\"bng\"));\n}\n\nvoid canvas_toggle(t_glist *gl, t_symbol *s, int argc, t_atom *argv)\n{\n    canvas_iemguis(gl, gensym(\"tgl\"));\n}\n\nvoid canvas_vslider(t_glist *gl, t_symbol *s, int argc, t_atom *argv)\n{\n    canvas_iemguis(gl, gensym(\"vsl\"));\n}\n\nvoid canvas_hslider(t_glist *gl, t_symbol *s, int argc, t_atom *argv)\n{\n    canvas_iemguis(gl, gensym(\"hsl\"));\n}\n\nvoid canvas_hdial(t_glist *gl, t_symbol *s, int argc, t_atom *argv)\n{\n    canvas_iemguis(gl, gensym(\"hdl\"));\n}\n\nvoid canvas_vdial(t_glist *gl, t_symbol *s, int argc, t_atom *argv)\n{\n    canvas_iemguis(gl, gensym(\"vdl\"));\n}\n\nvoid canvas_hradio(t_glist *gl, t_symbol *s, int argc, t_atom *argv)\n{\n    canvas_iemguis(gl, gensym(\"hradio\"));\n}\n\nvoid canvas_vradio(t_glist *gl, t_symbol *s, int argc, t_atom *argv)\n{\n    canvas_iemguis(gl, gensym(\"vradio\"));\n}\n\nvoid canvas_vumeter(t_glist *gl, t_symbol *s, int argc, t_atom *argv)\n{\n    canvas_iemguis(gl, gensym(\"vu\"));\n}\n\nvoid canvas_mycnv(t_glist *gl, t_symbol *s, int argc, t_atom *argv)\n{\n    canvas_iemguis(gl, gensym(\"cnv\"));\n}\n\nvoid canvas_numbox(t_glist *gl, t_symbol *s, int argc, t_atom *argv)\n{\n    canvas_iemguis(gl, gensym(\"nbx\"));\n}\n\n/* iemlib */\n\nvoid canvas_objfor(t_glist *gl, t_text *x, int argc, t_atom *argv)\n{\n    x->te_width = 0;                            /* don't know it yet. */\n    x->te_type = T_OBJECT;\n    x->te_binbuf = binbuf_new();\n    x->te_xpix = atom_getfloatarg(0, argc, argv);\n    x->te_ypix = atom_getfloatarg(1, argc, argv);\n    if (argc > 2) binbuf_restore(x->te_binbuf, argc-2, argv+2);\n    glist_add(gl, &x->te_g);\n}\n\n/* ---------------------- the \"message\" text item ------------------------ */\n\ntypedef struct _messresponder\n{\n    t_pd mr_pd;\n    t_outlet *mr_outlet;\n} t_messresponder;\n\ntypedef struct _message\n{\n    t_text m_text;\n    t_messresponder m_messresponder;\n    t_glist *m_glist;\n    t_clock *m_clock;\n} t_message;\n\nstatic t_class *messresponder_class;\n\nstatic void messresponder_bang(t_messresponder *x)\n{\n    outlet_bang(x->mr_outlet);\n}\n\nstatic void messresponder_float(t_messresponder *x, t_float f)\n{\n    outlet_float(x->mr_outlet, f);\n}\n\nstatic void messresponder_symbol(t_messresponder *x, t_symbol *s)\n{\n    outlet_symbol(x->mr_outlet, s);\n}\n\nstatic void messresponder_list(t_messresponder *x,\n    t_symbol *s, int argc, t_atom *argv)\n{\n    outlet_list(x->mr_outlet, s, argc, argv);\n}\n\nstatic void messresponder_anything(t_messresponder *x,\n    t_symbol *s, int argc, t_atom *argv)\n{\n    outlet_anything(x->mr_outlet, s, argc, argv);\n}\n\nstatic void message_bang(t_message *x)\n{\n    binbuf_eval(x->m_text.te_binbuf, &x->m_messresponder.mr_pd, 0, 0);\n}\n\nstatic void message_float(t_message *x, t_float f)\n{\n    t_atom at;\n    SETFLOAT(&at, f);\n    binbuf_eval(x->m_text.te_binbuf, &x->m_messresponder.mr_pd, 1, &at);\n}\n\nstatic void message_symbol(t_message *x, t_symbol *s)\n{\n    t_atom at;\n    SETSYMBOL(&at, s);\n    binbuf_eval(x->m_text.te_binbuf, &x->m_messresponder.mr_pd, 1, &at);\n}\n\nstatic void message_list(t_message *x, t_symbol *s, int argc, t_atom *argv)\n{\n    binbuf_eval(x->m_text.te_binbuf, &x->m_messresponder.mr_pd, argc, argv);\n}\n\nstatic void message_set(t_message *x, t_symbol *s, int argc, t_atom *argv)\n{\n    binbuf_clear(x->m_text.te_binbuf);\n    binbuf_add(x->m_text.te_binbuf, argc, argv);\n    glist_retext(x->m_glist, &x->m_text);\n}\n\nstatic void message_add2(t_message *x, t_symbol *s, int argc, t_atom *argv)\n{\n    binbuf_add(x->m_text.te_binbuf, argc, argv);\n    glist_retext(x->m_glist, &x->m_text);\n}\n\nstatic void message_add(t_message *x, t_symbol *s, int argc, t_atom *argv)\n{\n    binbuf_add(x->m_text.te_binbuf, argc, argv);\n    binbuf_addsemi(x->m_text.te_binbuf);\n    glist_retext(x->m_glist, &x->m_text);\n}\n\nstatic void message_addcomma(t_message *x)\n{\n    t_atom a;\n    SETCOMMA(&a);\n    binbuf_add(x->m_text.te_binbuf, 1, &a);\n    glist_retext(x->m_glist, &x->m_text);\n}\n\nstatic void message_addsemi(t_message *x)\n{\n    message_add(x, 0, 0, 0);\n}\n\nstatic void message_adddollar(t_message *x, t_floatarg f)\n{\n    t_atom a;\n    int n = f;\n    if (n < 0)\n        n = 0;\n    SETDOLLAR(&a, n);\n    binbuf_add(x->m_text.te_binbuf, 1, &a);\n    glist_retext(x->m_glist, &x->m_text);\n}\n\nstatic void message_adddollsym(t_message *x, t_symbol *s)\n{\n    t_atom a;\n    char buf[MAXPDSTRING];\n    buf[0] = '$';\n    strncpy(buf+1, s->s_name, MAXPDSTRING-2);\n    buf[MAXPDSTRING-1] = 0;\n    SETDOLLSYM(&a, gensym(buf));\n    binbuf_add(x->m_text.te_binbuf, 1, &a);\n    glist_retext(x->m_glist, &x->m_text);\n}\n\nstatic void message_click(t_message *x,\n    t_floatarg xpos, t_floatarg ypos, t_floatarg shift,\n        t_floatarg ctrl, t_floatarg alt)\n{\n    if (glist_isvisible(x->m_glist))\n    {\n        /* not zooming click width for now as it gets too fat */\n        t_rtext *y = glist_findrtext(x->m_glist, &x->m_text);\n        char buf[MAXPDSTRING];\n        sprintf(buf, \"%sR\", rtext_gettag(y));\n        pdgui_vmess(0, \"crs ri\",\n            glist_getcanvas(x->m_glist),\n            \"itemconfigure\",\n            buf,\n            \"-width\", MESSAGE_CLICK_WIDTH);\n        clock_delay(x->m_clock, 120);\n    }\n    message_float(x, 0);\n}\n\nstatic void message_tick(t_message *x)\n{\n    if (glist_isvisible(x->m_glist))\n    {\n        t_rtext *y = glist_findrtext(x->m_glist, &x->m_text);\n        char buf[MAXPDSTRING];\n        sprintf(buf, \"%sR\", rtext_gettag(y));\n        pdgui_vmess(0, \"crs ri\",\n            glist_getcanvas(x->m_glist),\n            \"itemconfigure\",\n            buf,\n            \"-width\", glist_getzoom(x->m_glist));\n    }\n}\n\nstatic void message_free(t_message *x)\n{\n    clock_free(x->m_clock);\n}\n\nvoid canvas_msg(t_glist *gl, t_symbol *s, int argc, t_atom *argv)\n{\n    t_message *x = (t_message *)pd_new(message_class);\n    x->m_messresponder.mr_pd = messresponder_class;\n    x->m_messresponder.mr_outlet = outlet_new(&x->m_text, &s_float);\n    x->m_text.te_width = 0;                             /* don't know it yet. */\n    x->m_text.te_type = T_MESSAGE;\n    x->m_text.te_binbuf = binbuf_new();\n    x->m_glist = gl;\n    x->m_clock = clock_new(x, (t_method)message_tick);\n    if (argc > 1)\n    {\n        x->m_text.te_xpix = atom_getfloatarg(0, argc, argv);\n        x->m_text.te_ypix = atom_getfloatarg(1, argc, argv);\n        if (argc > 2) binbuf_restore(x->m_text.te_binbuf, argc-2, argv+2);\n        glist_add(gl, &x->m_text.te_g);\n    }\n    else if (!glist_isvisible(gl))\n        post(\"unable to create stub message in closed canvas!\");\n    else\n    {\n        int connectme, xpix, ypix, indx, nobj;\n        canvas_howputnew(gl, &connectme, &xpix, &ypix, &indx, &nobj);\n\n        pd_vmess(&gl->gl_pd, gensym(\"editmode\"), \"i\", 1);\n        x->m_text.te_xpix = xpix;\n        x->m_text.te_ypix = ypix;\n        glist_add(gl, &x->m_text.te_g);\n        glist_noselect(gl);\n        glist_select(gl, &x->m_text.te_g);\n        gobj_activate(&x->m_text.te_g, gl, 1);\n        if (connectme)\n            canvas_connect(gl, indx, 0, nobj, 0);\n        else canvas_startmotion(glist_getcanvas(gl));\n        canvas_undo_add(glist_getcanvas(gl), UNDO_CREATE, \"create\",\n            (void *)canvas_undo_set_create(glist_getcanvas(gl)));\n    }\n}\n\n    /* for the needs of g_editor::glist_dofinderror() */\nt_pd *message_get_responder(t_gobj *x)\n{\n    if (pd_class(&x->g_pd) != message_class) return NULL;\n    else return (t_pd *)&((t_message *)x)->m_messresponder.mr_pd;\n}\n\n/* ---------------------- the \"atom\" text item ------------------------ */\n\n#define ATOM_LABELLEFT 0\n#define ATOM_LABELRIGHT 1\n#define ATOM_LABELUP 2\n#define ATOM_LABELDOWN 3\n#define A_LIST A_NULL /* fake atom type - use A_NULL for list 'flavor' */\n\ntypedef struct _gatom\n{\n    t_text a_text;\n    int a_flavor;           /* A_FLOAT, A_SYMBOL, or A_LIST */\n    t_glist *a_glist;       /* owning glist */\n    t_float a_toggle;       /* value to toggle to */\n    t_float a_draghi;       /* high end of drag range */\n    t_float a_draglo;       /* low end of drag range */\n    t_symbol *a_label;      /* symbol to show as label next to box */\n    t_symbol *a_symfrom;    /* \"receive\" name -- bind ourselves to this */\n    t_symbol *a_symto;      /* \"send\" name -- send to this on output */\n    t_binbuf *a_revertbuf;  /* binbuf to revert to if typing canceled */\n    int a_dragindex;        /* index of atom being dragged */\n    int a_fontsize;\n    unsigned int a_shift:1;         /* was shift key down when drag started? */\n    unsigned int a_wherelabel:2;    /* 0-3 for left, right, above, below */\n    unsigned int a_grabbed:1;       /* 1 if we've grabbed keyboard */\n    unsigned int a_doubleclicked:1; /* 1 if dragging from a double click */\n    t_symbol *a_expanded_to; /* a_symto after $0, $1, ...  expansion */\n} t_gatom;\n\n    /* prepend \"-\" as necessary to avoid empty strings, so we can\n    use them in Pd messages. */\nstatic t_symbol *gatom_escapit(t_symbol *s)\n{\n    if (!*s->s_name)\n        return (gensym(\"-\"));\n    else if (*s->s_name == '-')\n    {\n        char shmo[100];\n        shmo[0] = '-';\n        strncpy(shmo+1, s->s_name, 99);\n        shmo[99] = 0;\n        return (gensym(shmo));\n    }\n    else return (s);\n}\n\n    /* undo previous operation: strip leading \"-\" if found.  This is used\n    both to restore send, etc, names when loading from a file, and to\n    set them from the properties dialog.  In the former case, since before\n    version 0.52 '$\" was aliases to \"#\", we also bash any \"#\" characters\n    to \"$\".  This is unnecessary when reading files saved from 0.52 or later,\n    and really we should test for that and only bash when necessary, just\n    in case someone wants to have a \"#\" in a name. */\nstatic t_symbol *gatom_unescapit(t_symbol *s)\n{\n    if (*s->s_name == '-')\n        return (gensym(s->s_name+1));\n    else return (iemgui_raute2dollar(s));\n}\n\nstatic void gatom_redraw(t_gobj *client, t_glist *glist)\n{\n    t_gatom *x = (t_gatom *)client;\n    if (glist->gl_editor)\n        glist_retext(x->a_glist, &x->a_text);\n}\n\nstatic void gatom_senditup(t_gatom *x)\n{\n    if (x->a_glist->gl_editor\n        && gobj_shouldvis(&x->a_text.te_g, x->a_glist))\n            sys_queuegui(x, x->a_glist, gatom_redraw);\n}\n\nstatic t_atom *gatom_getatom(t_gatom *x)\n{\n    int ac = binbuf_getnatom(x->a_text.te_binbuf);\n    t_atom *av = binbuf_getvec(x->a_text.te_binbuf);\n    if (x->a_flavor == A_FLOAT && (ac != 1 || av[0].a_type != A_FLOAT))\n    {\n        binbuf_clear(x->a_text.te_binbuf);\n        binbuf_addv(x->a_text.te_binbuf, \"f\", 0.);\n    }\n    else if (x->a_flavor == A_SYMBOL && (ac != 1 || av[0].a_type != A_SYMBOL))\n    {\n        binbuf_clear(x->a_text.te_binbuf);\n        binbuf_addv(x->a_text.te_binbuf, \"s\", &s_);\n    }\n    return (binbuf_getvec(x->a_text.te_binbuf));\n}\n\nstatic void gatom_set(t_gatom *x, t_symbol *s, int argc, t_atom *argv)\n{\n    t_atom *ap = gatom_getatom(x), oldatom;\n    int changed = 0;\n    if (!argc && x->a_flavor != A_LIST)\n        return;\n    if (x->a_flavor == A_FLOAT)\n    {\n        oldatom = *ap;\n        ap->a_w.w_float = atom_getfloat(argv);\n            /* github PR 791 by Dan Bornstein: treat \"-0\" as different from\n            \"0\" and deal with NaNfoo all in one swipe by comparing bitwise: */\n        changed = memcmp(&ap->a_w.w_float, &oldatom.a_w.w_float,\n            sizeof(t_float));\n    }\n    else if (x->a_flavor == A_SYMBOL)\n    {\n        oldatom = *ap;\n        ap->a_w.w_symbol = atom_getsymbol(argv),\n            changed = (ap->a_w.w_symbol != oldatom.a_w.w_symbol);\n    }\n    else if (x->a_flavor == A_LIST)     /* list */\n    {\n        t_atom *av = binbuf_getvec(x->a_text.te_binbuf);\n        int ac = binbuf_getnatom(x->a_text.te_binbuf), i;\n        if (ac == argc)\n        {\n            for (i = 0; i < argc; i++)\n                if ((argv[i].a_type != av[i].a_type) ||\n                (argv[i].a_type == A_FLOAT &&\n                     argv[i].a_w.w_float != av[i].a_w.w_float) ||\n                (argv[i].a_type == A_SYMBOL &&\n                    argv[i].a_w.w_symbol != av[i].a_w.w_symbol))\n                        break;\n            if (i == argc)\n                goto nochange;\n        }\n            binbuf_clear(x->a_text.te_binbuf);\n            binbuf_add(x->a_text.te_binbuf, argc, argv);\n            av = binbuf_getvec(x->a_text.te_binbuf);\n            for (i = 0; i < argc; i++)\n                if (argv[i].a_type == A_POINTER)\n                    SETSYMBOL(&argv[i], gensym(\"(pointer)\"));\n            changed = 1;\n    }\n    if (changed)\n        gatom_senditup(x);\nnochange: ;\n}\n\nstatic void gatom_bang(t_gatom *x)\n{\n    t_atom *ap = gatom_getatom(x);\n    if (x->a_flavor == A_FLOAT)\n    {\n        if (x->a_text.te_outlet)\n            outlet_float(x->a_text.te_outlet, ap->a_w.w_float);\n        if (*x->a_expanded_to->s_name && x->a_expanded_to->s_thing)\n        {\n            if (x->a_symto == x->a_symfrom)\n                pd_error(x,\n                    \"%s: atom with same send/receive name (infinite loop)\",\n                        x->a_symto->s_name);\n            else pd_float(x->a_expanded_to->s_thing, ap->a_w.w_float);\n        }\n    }\n    else if (x->a_flavor == A_SYMBOL)\n    {\n        if (x->a_text.te_outlet)\n            outlet_symbol(x->a_text.te_outlet, ap->a_w.w_symbol);\n        if (*x->a_symto->s_name && x->a_expanded_to->s_thing)\n        {\n            if (x->a_symto == x->a_symfrom)\n                pd_error(x,\n                    \"%s: atom with same send/receive name (infinite loop)\",\n                        x->a_symto->s_name);\n            else pd_symbol(x->a_expanded_to->s_thing, ap->a_w.w_symbol);\n        }\n    }\n    else    /* list */\n    {\n        int argc = binbuf_getnatom(x->a_text.te_binbuf), i;\n        t_atom *argv = binbuf_getvec(x->a_text.te_binbuf);\n        for (i = 0; i < argc; i++)\n            if (argv[i].a_type != A_FLOAT && argv[i].a_type != A_SYMBOL)\n        {\n            pd_error(x, \"list: only sends literal numbers and symbols\");\n            return;\n        }\n        if (x->a_text.te_outlet)\n            outlet_list(x->a_text.te_outlet, &s_list, argc, argv);\n        if (*x->a_expanded_to->s_name && x->a_expanded_to->s_thing)\n        {\n            if (x->a_symto == x->a_symfrom)\n                pd_error(x,\n                    \"%s: atom with same send/receive name (infinite loop)\",\n                        x->a_symto->s_name);\n            else pd_list(x->a_expanded_to->s_thing, &s_list, argc, argv);\n        }\n    }\n}\n\nstatic void gatom_float(t_gatom *x, t_float f)\n{\n    t_atom at;\n    SETFLOAT(&at, f);\n    gatom_set(x, 0, 1, &at);\n    gatom_bang(x);\n}\n\nstatic void gatom_clipfloat(t_gatom *x, t_atom *ap, t_float f)\n{\n    if (x->a_draglo != 0 || x->a_draghi != 0)\n    {\n        if (f < x->a_draglo)\n            f = x->a_draglo;\n        if (f > x->a_draghi)\n            f = x->a_draghi;\n    }\n    ap->a_w.w_float = f;\n    gatom_senditup(x);\n    gatom_bang(x);\n}\n\nstatic void gatom_symbol(t_gatom *x, t_symbol *s)\n{\n    t_atom at;\n    SETSYMBOL(&at, s);\n    gatom_set(x, 0, 1, &at);\n    gatom_bang(x);\n}\n\n    /* We need a list method because, since there's both an \"inlet\" and a\n    \"nofirstin\" flag, the standard list behavior gets confused. */\nstatic void gatom_list(t_gatom *x, t_symbol *s, int argc, t_atom *argv)\n{\n    /* empty list is like a bang */\n    if (argc)\n        gatom_set(x, s, argc, argv);\n    gatom_bang(x);\n}\n\nstatic void gatom_reborder(t_gatom *x)\n{\n    t_rtext *y = glist_findrtext(x->a_glist, &x->a_text);\n    text_drawborder(&x->a_text, x->a_glist, rtext_gettag(y),\n        rtext_width(y), rtext_height(y), 0);\n}\n\nvoid gatom_undarken(t_text *x)\n{\n    if (x->te_type == T_ATOM)\n    {\n        ((t_gatom *)x)->a_doubleclicked =\n            ((t_gatom *)x)->a_grabbed = 0;\n        gatom_reborder((t_gatom *)x);\n    }\n    else bug(\"gatom_undarken\");\n}\n\nvoid gatom_key(void *z, t_symbol *keysym, t_floatarg f)\n{\n    t_gatom *x = (t_gatom *)z;\n    int c = f, bufsize, i;\n    char *buf;\n    t_atom *ap = gatom_getatom(x);\n\n    t_rtext *t = glist_findrtext(x->a_glist, &x->a_text);\n    if (c == 0 && !x->a_doubleclicked)\n    {\n        /* we're being notified that no more keys will come for this grab */\n        if (t == x->a_glist->gl_editor->e_textedfor)\n            rtext_activate(t, 0);\n        x->a_grabbed = 0;\n        gatom_reborder(x);\n        gatom_redraw(&x->a_text.te_g, x->a_glist);\n    }\n    else if (c == '\\n')\n    {\n        x->a_doubleclicked = 0;\n        if (t == x->a_glist->gl_editor->e_textedfor)\n        {\n            rtext_gettext(t, &buf, &bufsize);\n            rtext_key(t, 0, gensym(\"End\"));\n            for (i = 0; i < 3; i++)\n                if (buf[bufsize-i-1] != '.')\n                    break;\n            while (i--)\n                rtext_key(t, '\\b', &s_);\n            rtext_gettext(t, &buf, &bufsize);\n            if (x->a_flavor == A_FLOAT)\n                ap->a_w.w_float = atof(buf);\n            else if (x->a_flavor == A_SYMBOL)\n                ap->a_w.w_symbol = gensym(buf);\n            else\n                text_setto(&x->a_text, x->a_glist, buf, bufsize);\n            rtext_activate(t, 0);\n        }\n        gatom_bang(x);\n        gatom_senditup(x);\n    }\n    else\n    {\n        if (t != x->a_glist->gl_editor->e_textedfor)\n        {\n            rtext_activate(t, 1);\n            rtext_key(t, '.', &s_);\n            rtext_key(t, '.', &s_);\n            rtext_key(t, '.', &s_);\n            rtext_key(t, 0, gensym(\"Home\"));\n        }\n        if (x->a_flavor == A_SYMBOL || x->a_flavor == A_LIST)\n            rtext_key(t, c, keysym);  /* insert the character */\n        else if (x->a_flavor == A_FLOAT && ((c >= '0' && c <= '9') || c == '.' ||\n            c == '-' || c == '+' || c == 'e' || c == 'E' || c == '\\b'))\n                rtext_key(t, c, keysym);  /* insert the accepted characters */\n    }\n}\n\nstatic void gatom_motion(void *z, t_floatarg dx, t_floatarg dy,\n    t_floatarg up)\n{\n    t_gatom *x = (t_gatom *)z;\n    if (up != 0)\n    {\n        t_rtext *t = glist_findrtext(x->a_glist, &x->a_text);\n        rtext_retext(t);\n        if (x->a_doubleclicked)    /* double click - activate text on release */\n            rtext_activate(t, 1);\n    }\n    else\n    {\n        t_atom *ap;\n        x->a_doubleclicked = 0;\n        if (x->a_dragindex <0)\n            return;\n        if (dy == 0 || x->a_dragindex < 0 ||\n            x->a_dragindex >= binbuf_getnatom(x->a_text.te_binbuf)\n            || binbuf_getvec(x->a_text.te_binbuf)[x->a_dragindex].a_type != A_FLOAT)\n                return;\n        ap = &binbuf_getvec(x->a_text.te_binbuf)[x->a_dragindex];\n        if (x->a_shift)\n        {\n            double nval = ap->a_w.w_float - 0.01 * dy;\n            double trunc = 0.01 * (floor(100. * nval + 0.5));\n            if (trunc < nval + 0.0001 && trunc > nval - 0.0001)\n                nval = trunc;\n            gatom_clipfloat(x, ap, nval);\n        }\n        else\n        {\n            double nval = ap->a_w.w_float - dy;\n            double trunc = 0.01 * (floor(100. * nval + 0.5));\n            if (trunc < nval + 0.0001 && trunc > nval - 0.0001)\n                nval = trunc;\n            trunc = floor(nval + 0.5);\n            if (trunc < nval + 0.001 && trunc > nval - 0.001)\n                nval = trunc;\n            gatom_clipfloat(x, ap, nval);\n        }\n    }\n}\n\nint rtext_findatomfor(t_rtext *x, int xpos, int ypos);\n\n    /* this is called when gatom is clicked on with patch in run mode. */\nstatic int gatom_doclick(t_gobj *z, t_glist *gl, int xpos, int ypos,\n    int shift, int alt, int dbl, int doit)\n{\n    t_gatom *x = (t_gatom *)z;\n    t_atom *ap = gatom_getatom(x);\n    t_rtext *t;\n\n    if (!doit)\n        return (1);\n    t = glist_findrtext(x->a_glist, &x->a_text);\n    if (t == x->a_glist->gl_editor->e_textedfor)\n    {\n        rtext_mouse(t, xpos, ypos, (dbl ? RTEXT_DBL : RTEXT_DOWN));\n        x->a_glist->gl_editor->e_onmotion = MA_DRAGTEXT;\n        x->a_glist->gl_editor->e_xwas = xpos;\n        x->a_glist->gl_editor->e_ywas = ypos;\n        return (1);\n    }\n    if (x->a_flavor == A_FLOAT)\n    {\n        if (x->a_text.te_width == 1)\n            gatom_float(x, (ap->a_w.w_float == 0));\n        else\n        {\n            if (alt)\n            {\n                if (ap->a_w.w_float != 0)\n                {\n                    x->a_toggle = ap->a_w.w_float;\n                    gatom_float(x, 0);\n                }\n                else gatom_float(x, x->a_toggle);\n            }\n            else\n            {\n                x->a_dragindex = 0;\n                x->a_shift = shift;\n            }\n        }\n    }\n    else if (x->a_flavor == A_LIST)\n    {\n        int x1, y1, x2, y2, indx, argc = binbuf_getnatom(x->a_text.te_binbuf);\n        t_atom *argv = binbuf_getvec(x->a_text.te_binbuf);\n        gobj_getrect(z, gl, &x1, &y1, &x2, &y2);\n        indx = rtext_findatomfor(t, xpos - x1, ypos - y1);\n        if (indx >= 0 && indx < argc && argv[indx].a_type == A_FLOAT)\n        {\n            x->a_dragindex = indx;\n            x->a_shift = shift;\n        }\n        else x->a_dragindex = -1;\n    }\n    x->a_grabbed = 1;\n    x->a_doubleclicked = dbl;\n    gatom_reborder(x);\n    glist_grab(x->a_glist, &x->a_text.te_g, gatom_motion, gatom_key,\n        xpos, ypos);\n    return (1);\n}\n\n    /* probably never used but included in case needed for compatibility */\nstatic void gatom_click(t_gatom *x, t_floatarg xpos, t_floatarg ypos,\n    t_floatarg shift, t_floatarg ctrl, t_floatarg alt)\n{\n    pd_error(x, \"gatom_click is obsolete and may be deleted in future\");\n    gatom_doclick(&x->a_text.te_g, x->a_glist, xpos, ypos, shift, ctrl, 0, 1);\n}\n\n    /* message back from dialog window */\nstatic void gatom_param(t_gatom *x, t_symbol *sel, int argc, t_atom *argv)\n{\n    t_float width = atom_getfloatarg(0, argc, argv);\n    t_float draglo = atom_getfloatarg(1, argc, argv);\n    t_float draghi = atom_getfloatarg(2, argc, argv);\n    t_symbol *label = gatom_unescapit(atom_getsymbolarg(3, argc, argv));\n    t_float wherelabel = atom_getfloatarg(4, argc, argv);\n    t_symbol *symfrom = gatom_unescapit(atom_getsymbolarg(5, argc, argv));\n    t_symbol *symto = gatom_unescapit(atom_getsymbolarg(6, argc, argv));\n    int newfont = atom_getfloatarg(7, argc, argv);\n    t_atom undo[8];\n    int is_visible = glist_isvisible(x->a_glist);\n    int should_visible = gobj_shouldvis((t_gobj*)x, x->a_glist);\n\n    SETFLOAT (undo+0, x->a_text.te_width);\n    SETFLOAT (undo+1, x->a_draglo);\n    SETFLOAT (undo+2, x->a_draghi);\n    SETSYMBOL(undo+3, gatom_escapit(x->a_label));\n    SETFLOAT (undo+4, x->a_wherelabel);\n    SETSYMBOL(undo+5, gatom_escapit(x->a_symfrom));\n    SETSYMBOL(undo+6, gatom_escapit(x->a_symto));\n    SETFLOAT (undo+7, x->a_fontsize);\n    pd_undo_set_objectstate(x->a_glist, (t_pd*)x, gensym(\"param\"),\n                            8, undo,\n                            argc, argv);\n\n    if(is_visible)\n        gobj_vis(&x->a_text.te_g, x->a_glist, 0);\n    if (!*symfrom->s_name && *x->a_symfrom->s_name)\n        inlet_new(&x->a_text, &x->a_text.te_pd, 0, 0);\n    else if (*symfrom->s_name && !*x->a_symfrom->s_name && x->a_text.te_inlet)\n    {\n        canvas_deletelinesforio(x->a_glist, &x->a_text,\n            x->a_text.te_inlet, 0);\n        inlet_free(x->a_text.te_inlet);\n    }\n    if (!*symto->s_name && *x->a_symto->s_name)\n        outlet_new(&x->a_text, 0);\n    else if (*symto->s_name && !*x->a_symto->s_name && x->a_text.te_outlet)\n    {\n        canvas_deletelinesforio(x->a_glist, &x->a_text,\n            0, x->a_text.te_outlet);\n        outlet_free(x->a_text.te_outlet);\n    }\n    if (draglo >= draghi)\n        draglo = draghi = 0;\n    x->a_draglo = draglo;\n    x->a_draghi = draghi;\n    if (width < 0)\n        width = 4;\n    else if (width > 1000)\n        width = 1000;\n    x->a_text.te_width = width;\n    x->a_wherelabel = ((int)wherelabel & 3);\n    x->a_label = label;\n    x->a_fontsize = newfont;\n    if (*x->a_symfrom->s_name)\n        pd_unbind(&x->a_text.te_pd,\n            canvas_realizedollar(x->a_glist, x->a_symfrom));\n    x->a_symfrom = symfrom;\n    if (*x->a_symfrom->s_name)\n        pd_bind(&x->a_text.te_pd,\n            canvas_realizedollar(x->a_glist, x->a_symfrom));\n    x->a_symto = symto;\n    x->a_expanded_to = canvas_realizedollar(x->a_glist, x->a_symto);\n\n    if(is_visible && should_visible)\n        gobj_vis(&x->a_text.te_g, x->a_glist, 1);\n    canvas_dirty(x->a_glist, 1);\n    if(is_visible)\n        canvas_fixlinesfor(x->a_glist, (t_text*)x);\n    /* glist_retext(x->a_glist, &x->a_text); */\n}\n\nstatic int gatom_fontsize(t_gatom *x)\n{\n    return (x->a_fontsize ? x->a_fontsize : glist_getfont(x->a_glist));\n}\n\n    /* ---------------- gatom-specific widget functions --------------- */\nstatic void gatom_getwherelabel(t_gatom *x, t_glist *glist, int *xp, int *yp)\n{\n    int x1, y1, x2, y2;\n    int zoom = glist_getzoom(glist), fontsize = gatom_fontsize(x);\n    text_getrect(&x->a_text.te_g, glist, &x1, &y1, &x2, &y2);\n    if (x->a_wherelabel == ATOM_LABELLEFT)\n    {\n        *xp = x1 - 3 * zoom - (\n            (int)strlen(canvas_realizedollar(x->a_glist, x->a_label)->s_name) *\n                sys_zoomfontwidth(fontsize, zoom, 0));\n        *yp = y1 + 2 * zoom;\n    }\n    else if (x->a_wherelabel == ATOM_LABELRIGHT)\n    {\n        *xp = x2 + 2 * zoom;\n        *yp = y1 + 2 * zoom;\n    }\n    else if (x->a_wherelabel == ATOM_LABELUP)\n    {\n        *xp = x1 - 1 * zoom;\n        *yp = y1 - 1 * zoom - sys_zoomfontheight(fontsize, zoom, 0);\n    }\n    else\n    {\n        *xp = x1 - 1 * zoom;\n        *yp = y2 + 3 * zoom;\n    }\n}\n\nstatic void gatom_displace(t_gobj *z, t_glist *glist,\n    int dx, int dy)\n{\n    t_gatom *x = (t_gatom*)z;\n    text_displace(z, glist, dx, dy);\n    if (glist_isvisible(glist))\n    {\n        char buf[MAXPDSTRING];\n        sprintf(buf, \"%p.l\", x);\n        pdgui_vmess(0, \"crs ii\",\n            glist_getcanvas(glist),\n            \"move\",\n            buf,\n            dx * glist->gl_zoom, dy * glist->gl_zoom);\n    }\n}\n\nstatic void gatom_vis(t_gobj *z, t_glist *glist, int vis)\n{\n    t_gatom *x = (t_gatom*)z;\n    text_vis(z, glist, vis);\n    if (*x->a_label->s_name)\n    {\n        char buf[MAXPDSTRING];\n        sprintf(buf, \"%p.l\", x);\n        if (vis)\n        {\n            int x1, y1;\n            char *tags[] = {\n                buf,\n                \"label\",\n                \"text\"\n            };\n            gatom_getwherelabel(x, glist, &x1, &y1);\n            pdgui_vmess(\"pdtk_text_new\", \"cS ff s ir\",\n                glist_getcanvas(glist),\n                3, tags,\n                (double)x1, (double)y1,\n                canvas_realizedollar(x->a_glist, x->a_label)->s_name,\n                gatom_fontsize(x) * glist_getzoom(glist), \"black\");\n        }\n        else\n            pdgui_vmess(0, \"crs\", glist_getcanvas(glist), \"delete\", buf);\n    }\n}\n\nvoid canvas_atom(t_glist *gl, t_atomtype type,\n    t_symbol *s, int argc, t_atom *argv)\n{\n    t_gatom *x = (t_gatom *)pd_new(gatom_class);\n    x->a_text.te_width = 0;                        /* don't know it yet. */\n    x->a_text.te_type = T_ATOM;\n    x->a_text.te_binbuf = binbuf_new();\n    x->a_glist = gl;\n    x->a_flavor = type;\n    x->a_toggle = 1;\n    x->a_draglo = 0;\n    x->a_draghi = 0;\n    x->a_wherelabel = 0;\n    x->a_label = &s_;\n    x->a_symfrom = &s_;\n    x->a_symto = x->a_expanded_to = &s_;\n    x->a_grabbed = 0;\n    x->a_revertbuf = 0;\n    x->a_fontsize = 0;\n    (void)gatom_getatom(x);  /* this forces initialization of binbuf */\n    if (argc > 1)\n        /* create from file. x, y, width, low-range, high-range, flags,\n            label, receive-name, send-name, fontsize */\n    {\n        x->a_text.te_xpix = atom_getfloatarg(0, argc, argv);\n        x->a_text.te_ypix = atom_getfloatarg(1, argc, argv);\n        x->a_text.te_width = atom_getfloatarg(2, argc, argv);\n            /* sanity check because some very old patches have trash in this\n            field... remove this in 2003 or so: */\n        if (x->a_text.te_width < 0 || x->a_text.te_width > 500)\n            x->a_text.te_width = 4;\n        x->a_draglo = atom_getfloatarg(3, argc, argv);\n        x->a_draghi = atom_getfloatarg(4, argc, argv);\n        x->a_wherelabel = (((int)atom_getfloatarg(5, argc, argv)) & 3);\n        x->a_label = gatom_unescapit(atom_getsymbolarg(6, argc, argv));\n        x->a_symfrom = gatom_unescapit(atom_getsymbolarg(7, argc, argv));\n        if (*x->a_symfrom->s_name)\n            pd_bind(&x->a_text.te_pd,\n                canvas_realizedollar(x->a_glist, x->a_symfrom));\n\n        x->a_symto = gatom_unescapit(atom_getsymbolarg(8, argc, argv));\n        x->a_expanded_to = canvas_realizedollar(x->a_glist, x->a_symto);\n        if (x->a_symto == &s_)\n            outlet_new(&x->a_text,\n                x->a_flavor == A_FLOAT ? &s_float: &s_symbol);\n        if (x->a_symfrom == &s_)\n            inlet_new(&x->a_text, &x->a_text.te_pd, 0, 0);\n        x->a_fontsize = atom_getfloatarg(9, argc, argv);\n        glist_add(gl, &x->a_text.te_g);\n    }\n    else    /* from menu - use default settings */\n    {\n        int connectme, xpix, ypix, indx, nobj;\n        canvas_howputnew(gl, &connectme, &xpix, &ypix, &indx, &nobj);\n        outlet_new(&x->a_text,\n            x->a_flavor == A_FLOAT ? &s_float: &s_symbol);\n        inlet_new(&x->a_text, &x->a_text.te_pd, 0, 0);\n        pd_vmess(&gl->gl_pd, gensym(\"editmode\"), \"i\", 1);\n        x->a_text.te_xpix = xpix;\n        x->a_text.te_ypix = ypix;\n        x->a_text.te_width = (x->a_flavor == A_FLOAT ? 5 :\n            (x->a_flavor == A_SYMBOL ? 10 : 20));\n        glist_add(gl, &x->a_text.te_g);\n        glist_noselect(gl);\n        glist_select(gl, &x->a_text.te_g);\n        if (connectme)\n            canvas_connect(gl, indx, 0, nobj, 0);\n        else canvas_startmotion(glist_getcanvas(gl));\n        canvas_undo_add(glist_getcanvas(gl), UNDO_CREATE, \"create\",\n            (void *)canvas_undo_set_create(glist_getcanvas(gl)));\n    }\n}\n\nvoid canvas_floatatom(t_glist *gl, t_symbol *s, int argc, t_atom *argv)\n{\n    canvas_atom(gl, A_FLOAT, s, argc, argv);\n}\n\nvoid canvas_symbolatom(t_glist *gl, t_symbol *s, int argc, t_atom *argv)\n{\n    canvas_atom(gl, A_SYMBOL, s, argc, argv);\n}\n\nvoid canvas_listbox(t_glist *gl, t_symbol *s, int argc, t_atom *argv)\n{\n    canvas_atom(gl, A_LIST, s, argc, argv);\n}\n\nstatic void gatom_free(t_gatom *x)\n{\n    if (*x->a_symfrom->s_name)\n        pd_unbind(&x->a_text.te_pd,\n            canvas_realizedollar(x->a_glist, x->a_symfrom));\n    pdgui_stub_deleteforkey(x);\n    sys_unqueuegui(x);\n}\n\nstatic void gatom_properties(t_gobj *z, t_glist *owner)\n{\n    t_gatom *x = (t_gatom *)z;\n    pdgui_stub_vnew(&x->a_text.te_pd, \"pdtk_gatom_dialog\", x,\n        \"i ff i sss i\",\n        x->a_text.te_width,\n        x->a_draglo, x->a_draghi,\n        x->a_wherelabel,\n        gatom_escapit(x->a_label)->s_name,\n        gatom_escapit(x->a_symfrom)->s_name,\n        gatom_escapit(x->a_symto)->s_name,\n        x->a_fontsize);\n}\n\n\n/* -------------------- widget behavior for text objects ------------ */\n\nstatic void text_getrect(t_gobj *z, t_glist *glist,\n    int *xp1, int *yp1, int *xp2, int *yp2)\n{\n    t_text *x = (t_text *)z;\n    int width, height, iscomment = (x->te_type == T_TEXT);\n    t_float x1, y1, x2, y2;\n\n    if (glist->gl_editor && glist->gl_editor->e_rtext)\n    {\n        t_rtext *y = glist_findrtext(glist, x);\n        width = rtext_width(y);\n        height = rtext_height(y) - (iscomment << 1);\n    }\n        /* for number boxes, we know width and height a priori, and should\n        report them here so that graphs can get swelled to fit. */\n\n    else if (x->te_type == T_ATOM && x->te_width > 0)\n    {\n        width = (x->te_width > 0 ? x->te_width : 6) * glist_fontwidth(glist);\n        height = glist_fontheight(glist);\n        if (glist_getzoom(glist) > 1)\n        {\n            /* zoom margins */\n            width += ATOM_RMARGIN * glist_getzoom(glist);\n            height += ATOM_BMARGIN * glist_getzoom(glist);\n        }\n        else\n        {\n            width += ATOM_RMARGIN;\n            height += ATOM_BMARGIN;\n        }\n    }\n        /* if we're invisible we don't know our size so we just lie about\n        it.  This is called on invisible boxes to establish order of inlets\n        and possibly other reasons.\n           To find out if the box is visible we can't just check the \"vis\"\n        flag because we might be within the vis() routine and not have set\n        that yet.  So we check directly whether the \"rtext\" list has been\n        built.  LATER reconsider when \"vis\" flag should be on and off? */\n\n    else width = height = 10;\n    x1 = text_xpix(x, glist);\n    y1 = text_ypix(x, glist);\n    x2 = x1 + width;\n    y2 = y1 + height;\n    y1 += iscomment;\n    *xp1 = x1;\n    *yp1 = y1;\n    *xp2 = x2;\n    *yp2 = y2;\n}\n\nstatic void text_displace(t_gobj *z, t_glist *glist,\n    int dx, int dy)\n{\n    t_text *x = (t_text *)z;\n    x->te_xpix += dx;\n    x->te_ypix += dy;\n    if (glist_isvisible(glist))\n    {\n        t_rtext *y = glist_findrtext(glist, x);\n        rtext_displace(y, glist->gl_zoom * dx, glist->gl_zoom * dy);\n        text_drawborder(x, glist, rtext_gettag(y),\n            rtext_width(y), rtext_height(y), 0);\n        canvas_fixlinesfor(glist, x);\n    }\n}\n\nstatic void text_select(t_gobj *z, t_glist *glist, int state)\n{\n    t_text *x = (t_text *)z;\n    t_rtext *y = glist_findrtext(glist, x);\n    rtext_select(y, state);\n    if (glist_isvisible(glist) && gobj_shouldvis(&x->te_g, glist))\n    {\n        char buf[MAXPDSTRING];\n        sprintf(buf, \"%sR\", rtext_gettag(y));\n        pdgui_vmess(0, \"crs rr\",\n            glist,\n            \"itemconfigure\",\n            buf,\n            \"-fill\", (state? \"blue\" : \"black\"));\n    }\n}\n\nstatic void text_activate(t_gobj *z, t_glist *glist, int state)\n{\n    t_text *x = (t_text *)z;\n    t_rtext *y = glist_findrtext(glist, x);\n    if (z->g_pd != gatom_class)\n        rtext_activate(y, state);\n}\n\nstatic void text_delete(t_gobj *z, t_glist *glist)\n{\n    t_text *x = (t_text *)z;\n        canvas_deletelinesfor(glist, x);\n}\n\nstatic void text_vis(t_gobj *z, t_glist *glist, int vis)\n{\n    t_text *x = (t_text *)z;\n    if (vis)\n    {\n        if (gobj_shouldvis(&x->te_g, glist))\n        {\n            t_rtext *y = glist_findrtext(glist, x);\n            text_drawborder(x, glist, rtext_gettag(y),\n                rtext_width(y), rtext_height(y), 1);\n            rtext_draw(y);\n        }\n    }\n    else\n    {\n        t_rtext *y = glist_findrtext(glist, x);\n        if (gobj_shouldvis(&x->te_g, glist))\n        {\n            text_eraseborder(x, glist, rtext_gettag(y));\n            rtext_erase(y);\n        }\n    }\n}\n\nstatic int text_click(t_gobj *z, struct _glist *glist,\n    int xpix, int ypix, int shift, int alt, int dbl, int doit)\n{\n    t_text *x = (t_text *)z;\n    if (x->te_type == T_OBJECT)\n    {\n        t_symbol *clicksym = gensym(\"click\");\n        if (zgetfn(&x->te_pd, clicksym))\n        {\n            if (doit)\n                pd_vmess(&x->te_pd, clicksym, \"fffff\",\n                    (double)xpix, (double)ypix,\n                        (double)shift, (double)0, (double)alt);\n            return (1);\n        }\n        else return (0);\n    }\n    else if (x->te_type == T_MESSAGE)\n    {\n        if (doit)\n            message_click((t_message *)x, (t_floatarg)xpix, (t_floatarg)ypix,\n                (t_floatarg)shift, (t_floatarg)0, (t_floatarg)alt);\n        return (1);\n    }\n    else return (0);\n}\n\nvoid canvas_statesavers_doit(t_glist *x, t_binbuf *b);\nvoid text_save(t_gobj *z, t_binbuf *b)\n{\n    t_text *x = (t_text *)z;\n    if (x->te_type == T_OBJECT)\n    {\n            /* if we have a \"saveto\" method, and if we don't happen to be\n            a canvas that's an abstraction, the saveto method does the work */\n        if (zgetfn(&x->te_pd, gensym(\"saveto\")) &&\n            !((pd_class(&x->te_pd) == canvas_class) &&\n                (canvas_isabstraction((t_canvas *)x)\n                    || canvas_istable((t_canvas *)x))))\n        {\n            mess1(&x->te_pd, gensym(\"saveto\"), b);\n            binbuf_addv(b, \"ssii\", gensym(\"#X\"), gensym(\"restore\"),\n                (int)x->te_xpix, (int)x->te_ypix);\n            binbuf_addbinbuf(b, x->te_binbuf);\n            binbuf_addv(b, \";\");\n            if (x->te_width)\n                binbuf_addv(b, \"ssi;\",\n                    gensym(\"#X\"), gensym(\"f\"), (int)x->te_width);\n        }\n        else    /* otherwise just save the text */\n        {\n            binbuf_addv(b, \"ssii\", gensym(\"#X\"), gensym(\"obj\"),\n                (int)x->te_xpix, (int)x->te_ypix);\n            binbuf_addbinbuf(b, x->te_binbuf);\n            if (x->te_width)\n                binbuf_addv(b, \",si\", gensym(\"f\"), (int)x->te_width);\n            binbuf_addv(b, \";\");\n        }\n            /* if an abstraction, give it a chance to save state */\n        if (pd_class(&x->te_pd) == canvas_class &&\n            canvas_isabstraction((t_canvas *)x))\n                canvas_statesavers_doit((t_glist *)x, b);\n    }\n    else if (x->te_type == T_MESSAGE)\n    {\n        binbuf_addv(b, \"ssii\", gensym(\"#X\"), gensym(\"msg\"),\n            (int)x->te_xpix, (int)x->te_ypix);\n        binbuf_addbinbuf(b, x->te_binbuf);\n        if (x->te_width)\n            binbuf_addv(b, \",si\", gensym(\"f\"), (int)x->te_width);\n        binbuf_addv(b, \";\");\n    }\n    else if (x->te_type == T_ATOM)\n    {\n        t_atomtype t = ((t_gatom *)x)->a_flavor;\n        t_symbol *sel = (t == A_SYMBOL ? gensym(\"symbolatom\") :\n            (t == A_FLOAT ? gensym(\"floatatom\") : gensym(\"listbox\")));\n        t_symbol *label = gatom_escapit(((t_gatom *)x)->a_label);\n        t_symbol *symfrom = gatom_escapit(((t_gatom *)x)->a_symfrom);\n        t_symbol *symto = gatom_escapit(((t_gatom *)x)->a_symto);\n        binbuf_addv(b, \"ssiiifffsssf;\", gensym(\"#X\"), sel,\n            (int)x->te_xpix, (int)x->te_ypix, (int)x->te_width,\n            (double)((t_gatom *)x)->a_draglo,\n            (double)((t_gatom *)x)->a_draghi,\n            (double)((t_gatom *)x)->a_wherelabel,\n            label, symfrom, symto, (double)((t_gatom *)x)->a_fontsize);\n    }\n    else\n    {\n        binbuf_addv(b, \"ssii\", gensym(\"#X\"), gensym(\"text\"),\n            (int)x->te_xpix, (int)x->te_ypix);\n        binbuf_addbinbuf(b, x->te_binbuf);\n        if (x->te_width)\n            binbuf_addv(b, \",si\", gensym(\"f\"), (int)x->te_width);\n        binbuf_addv(b, \";\");\n    }\n}\n\n    /* this one is for everyone but \"gatoms\"; it's imposed in m_class.c */\nconst t_widgetbehavior text_widgetbehavior =\n{\n    text_getrect,\n    text_displace,\n    text_select,\n    text_activate,\n    text_delete,\n    text_vis,\n    text_click,\n};\n\nstatic const t_widgetbehavior gatom_widgetbehavior =\n{\n    text_getrect,\n    gatom_displace,\n    text_select,\n    text_activate,\n    text_delete,\n    gatom_vis,\n    gatom_doclick,\n};\n\n/* -------------------- the \"text\" class  ------------ */\n\n    /* draw inlets and outlets for a text object or for a graph. */\nvoid glist_drawiofor(t_glist *glist, t_object *ob, int firsttime,\n    const char *tag, int x1, int y1, int x2, int y2)\n{\n    int n = obj_noutlets(ob), nplus = (n == 1 ? 1 : n-1), i;\n    int width = x2 - x1;\n    int iow = IOWIDTH * glist->gl_zoom;\n    int ih = IHEIGHT * glist->gl_zoom, oh = OHEIGHT * glist->gl_zoom;\n    char *tags[2];\n    char tagbuf[128];\n\n    /* draw over border, so assume border width = 1 pixel * glist->gl_zoom */\n    for (i = 0; i < n; i++)\n    {\n        int onset = x1 + (width - iow) * i / nplus;\n        sprintf(tagbuf, \"%so%d\", tag, i);\n        tags[0] = tagbuf;\n        tags[1] = \"outlet\";\n        if (firsttime)\n            pdgui_vmess(0, \"crr iiii rS rr\",\n                glist_getcanvas(glist), \"create\", \"rectangle\",\n                onset, y2 - oh + glist->gl_zoom, onset + iow, y2,\n                \"-tags\", (int)(sizeof(tags)/sizeof(*tags)), tags,\n                \"-fill\", \"black\");\n        else\n            pdgui_vmess(0, \"crs iiii\",\n                glist_getcanvas(glist), \"coords\", tagbuf,\n                onset, y2 - oh + glist->gl_zoom, onset + iow, y2);\n    }\n    n = obj_ninlets(ob);\n    nplus = (n == 1 ? 1 : n-1);\n    for (i = 0; i < n; i++)\n    {\n        int onset = x1 + (width - iow) * i / nplus;\n        sprintf(tagbuf, \"%si%d\", tag, i);\n        tags[0] = tagbuf;\n        tags[1] = \"inlet\";\n        if (firsttime)\n            pdgui_vmess(0, \"crr iiii rS rr\",\n                glist_getcanvas(glist),\n                \"create\", \"rectangle\",\n                onset, y1, onset + iow, y1 + ih - glist->gl_zoom,\n                \"-tags\", (int)(sizeof(tags)/sizeof(*tags)), tags,\n                \"-fill\", \"black\");\n        else\n            pdgui_vmess(0, \"crs iiii\",\n                glist_getcanvas(glist), \"coords\", tagbuf,\n                onset, y1, onset + iow, y1 + ih - glist->gl_zoom);\n    }\n}\n\nvoid text_drawborder(t_text *x, t_glist *glist,\n    const char *tag, int width2, int height2, int firsttime)\n{\n    t_object *ob;\n    int x1, y1, x2, y2, width, height, corner;\n    char tagR[128];\n    sprintf(tagR, \"%sR\", tag);\n    text_getrect(&x->te_g, glist, &x1, &y1, &x2, &y2);\n    width = x2 - x1;\n    height = y2 - y1;\n    if (x->te_type == T_OBJECT)\n    {\n        char *pattern = ((pd_class(&x->te_pd) == text_class) ? \"-\" : \"\\\"\\\"\");\n        char *tags[] = {tagR, \"obj\"};\n        if (firsttime)\n            pdgui_vmess(0, \"crr iiiiiiiiii rr ri rr rS\",\n                glist_getcanvas(glist), \"create\", \"line\",\n                x1, y1,  x2, y1,  x2, y2,  x1, y2,  x1, y1,\n                \"-dash\", pattern,\n                \"-width\", glist->gl_zoom,\n                \"-capstyle\", \"projecting\",\n                \"-tags\", 2, tags);\n        else\n        {\n            pdgui_vmess(0, \"crs iiiiiiiiii\",\n                glist_getcanvas(glist), \"coords\", tagR,\n                x1, y1,  x2, y1,  x2, y2,  x1, y2,  x1, y1);\n            pdgui_vmess(0, \"crs rr\",\n                glist_getcanvas(glist), \"itemconfigure\", tagR,\n                \"-dash\", pattern);\n        }\n    }\n    else if (x->te_type == T_MESSAGE)\n    {\n        char *tags[] = {tagR, \"msg\"};\n        corner = ((y2-y1)/4);\n        if (corner > 10*glist->gl_zoom)\n            corner = 10*glist->gl_zoom; /* looks bad if too big */\n        if (firsttime)\n            pdgui_vmess(0, \"crr iiiiiiiiiiiiii ri rr rS\",\n                glist_getcanvas(glist), \"create\", \"line\",\n                x1, y1,  x2+corner, y1,  x2, y1+corner,  x2, y2-corner,  x2+corner, y2,  x1, y2,  x1, y1,\n                \"-width\", glist->gl_zoom,\n                \"-capstyle\", \"projecting\",\n                \"-tags\", 2, tags);\n        else\n            pdgui_vmess(0, \"crs iiiiiiiiiiiiii\",\n                glist_getcanvas(glist), \"coords\", tagR,\n                x1, y1,  x2+corner, y1,  x2, y1+corner,  x2, y2-corner,  x2+corner, y2,  x1, y2,  x1, y1);\n    }\n    else if (x->te_type == T_ATOM && (((t_gatom *)x)->a_flavor == A_FLOAT ||\n           ((t_gatom *)x)->a_flavor == A_SYMBOL))\n    {\n            /* number or symbol */\n        int grabbed = glist->gl_zoom * ((t_gatom *)x)->a_grabbed;\n        int x1p = x1 + grabbed, y1p = y1 + grabbed;\n        char *tags[] = {tagR, \"atom\"};\n        corner = ((y2-y1)/4);\n        if (firsttime)\n            pdgui_vmess(0, \"crr iiiiiiiiiiii ri rr rS\",\n                glist_getcanvas(glist), \"create\", \"line\",\n                x1p, y1p,  x2-corner, y1p,  x2, y1p+corner, x2, y2,  x1p, y2,  x1p, y1p,\n                \"-width\", glist->gl_zoom+grabbed,\n                \"-capstyle\", \"projecting\",\n                \"-tags\", 2, tags);\n        else\n        {\n            pdgui_vmess(0, \"crs iiiiiiiiiiii\",\n                glist_getcanvas(glist), \"coords\", tagR,\n                x1p, y1p,  x2-corner, y1p,  x2, y1p+corner,  x2, y2,  x1p, y2,  x1p, y1p);\n            pdgui_vmess(0, \"crs ri\",\n                glist_getcanvas(glist), \"itemconfigure\", tagR,\n                \"-width\", glist->gl_zoom+grabbed);\n        }\n    }\n    else if (x->te_type == T_ATOM ) /* list (ATOM but not float or symbol) */\n    {\n        int grabbed = glist->gl_zoom * ((t_gatom *)x)->a_grabbed;\n        int x1p = x1 + grabbed, y1p = y1 + grabbed;\n        char *tags[] = {tagR, \"atom\"};\n        corner = ((y2-y1)/4);\n        if (firsttime)\n            pdgui_vmess(0, \"crr iiiiiiiiiiiiii ri rr rS\",\n                glist_getcanvas(glist),\n                \"create\", \"line\",\n                x1p, y1p,  x2-corner, y1p,  x2, y1p+corner,  x2, y2-corner,  x2-corner, y2,  x1p, y2,  x1p, y1p,\n                \"-width\", glist->gl_zoom+grabbed,\n                \"-capstyle\", \"projecting\",\n                \"-tags\", 2, tags);\n        else\n        {\n            pdgui_vmess(0, \"crs iiiiiiiiiiiiii\",\n                glist_getcanvas(glist), \"coords\", tagR,\n                x1p,y1p, x2-corner,y1p, x2,y1p+corner, x2,y2-corner, x2-corner,y2, x1p,y2, x1p,y1p);\n            pdgui_vmess(0, \"crs ri\",\n                glist_getcanvas(glist), \"itemconfigure\", tagR,\n                \"-width\", glist->gl_zoom+grabbed);\n        }\n    }\n        /* for comments, just draw a bar on RHS if unlocked; when a visible\n        canvas is unlocked we have to call this anew on all comments, and when\n        locked we erase them all via the annoying \"commentbar\" tag. */\n    else if (x->te_type == T_TEXT && glist->gl_edit)\n    {\n        char *tags[] = {tagR, \"commentbar\"};\n        if (firsttime)\n            pdgui_vmess(0, \"crr iiii rS\",\n                glist_getcanvas(glist), \"create\", \"line\",\n                x2, y1,  x2, y2,\n                \"-tags\", 2, tags);\n        else\n            pdgui_vmess(0, \"crs iiii\",\n                glist_getcanvas(glist), \"coords\", tagR,\n                x2, y1,  x2, y2);\n    }\n        /* draw inlets/outlets */\n\n    if ((ob = pd_checkobject(&x->te_pd)))\n        glist_drawiofor(glist, ob, firsttime, tag, x1, y1, x2, y2);\n    if (firsttime) /* raise cords over everything else */\n        pdgui_vmess(0, \"crr\", glist_getcanvas(glist), \"raise\", \"cord\");\n}\n\nvoid glist_eraseiofor(t_glist *glist, t_object *ob, const char *tag)\n{\n    int i, n;\n    n = obj_noutlets(ob);\n    char tagbuf[MAXPDSTRING];\n    for (i = 0; i < n; i++)\n    {\n        sprintf(tagbuf, \"%so%d\", tag, i);\n        pdgui_vmess(0, \"crs\",\n            glist_getcanvas(glist), \"delete\", tagbuf);\n    }\n    n = obj_ninlets(ob);\n    for (i = 0; i < n; i++)\n    {\n        sprintf(tagbuf, \"%si%d\", tag, i);\n        pdgui_vmess(0, \"crs\",\n            glist_getcanvas(glist), \"delete\", tagbuf);\n    }\n}\n\nvoid text_eraseborder(t_text *x, t_glist *glist, const char *tag)\n{\n    char tagbuf[MAXPDSTRING];\n    if (x->te_type == T_TEXT && !glist->gl_edit) return;\n    sprintf(tagbuf, \"%sR\", tag);\n    pdgui_vmess(0, \"crs\",\n        glist_getcanvas(glist), \"delete\", tagbuf);\n    glist_eraseiofor(glist, x, tag);\n}\n\n    /* change text; if T_OBJECT, remake it.  */\nvoid text_setto(t_text *x, t_glist *glist, const char *buf, int bufsize)\n{\n    int pos = glist_getindex(glist_getcanvas(glist), &x->te_g);\n    if (x->te_type == T_OBJECT)\n    {\n        t_binbuf *b = binbuf_new();\n        int natom1, natom2, widthwas = x->te_width;\n        t_atom *vec1, *vec2;\n        binbuf_text(b, buf, bufsize);\n        natom1 = binbuf_getnatom(x->te_binbuf);\n        vec1 = binbuf_getvec(x->te_binbuf);\n        natom2 = binbuf_getnatom(b);\n        vec2 = binbuf_getvec(b);\n            /* special case: if  pd args change just pass the message on. */\n        if (natom1 >= 1 && natom2 >= 1 && vec1[0].a_type == A_SYMBOL\n            && !strcmp(vec1[0].a_w.w_symbol->s_name, \"pd\") &&\n             vec2[0].a_type == A_SYMBOL\n            && !strcmp(vec2[0].a_w.w_symbol->s_name, \"pd\"))\n        {\n            canvas_undo_add(glist_getcanvas(glist), UNDO_RECREATE, \"recreate\",\n                (void *)canvas_undo_set_recreate(glist_getcanvas(glist),\n                &x->te_g, pos));\n\n            typedmess(&x->te_pd, gensym(\"rename\"), natom2-1, vec2+1);\n            binbuf_free(x->te_binbuf);\n            x->te_binbuf = b;\n        }\n        else  /* normally, just destroy the old one and make a new one. */\n        {\n            int xwas = x->te_xpix, ywas = x->te_ypix;\n            canvas_undo_add(glist_getcanvas(glist), UNDO_RECREATE, \"recreate\",\n                (void *)canvas_undo_set_recreate(glist_getcanvas(glist),\n                &x->te_g, pos));\n            glist_delete(glist, &x->te_g);\n            canvas_objtext(glist, xwas, ywas, widthwas, 0, b);\n            canvas_restoreconnections(glist_getcanvas(glist));\n                /* if it's an abstraction loadbang it here */\n            if (pd_this->pd_newest)\n            {\n                if (pd_class(pd_this->pd_newest) == canvas_class)\n                    canvas_loadbang((t_canvas *)pd_this->pd_newest);\n                else if (zgetfn(pd_this->pd_newest, gensym(\"loadbang\")))\n                    vmess(pd_this->pd_newest, gensym(\"loadbang\"), \"f\", LB_LOAD);\n            }\n        }\n            /* if we made a new \"pd\" or changed a window name,\n                update window list */\n        if (natom2 >= 1  && vec2[0].a_type == A_SYMBOL\n            && !strcmp(vec2[0].a_w.w_symbol->s_name, \"pd\"))\n                canvas_updatewindowlist();\n    }\n    else\n    {\n        canvas_undo_add(glist_getcanvas(glist), UNDO_RECREATE, \"recreate\",\n           (void *)canvas_undo_set_recreate(glist_getcanvas(glist),\n            &x->te_g, pos));\n        binbuf_text(x->te_binbuf, buf, bufsize);\n\n    }\n}\n\n    /* this gets called when a message gets sent to an object whose creation\n    failed, presumably because of loading a patch with a missing extern or\n    abstraction */\nstatic void text_anything(t_text *x, t_symbol *s, int argc, t_atom *argv)\n{\n}\n\nvoid text_getfont(t_text *x, t_glist *thisglist,\n    int *fwidthp, int *fheightp, int *guifsize)\n{\n    int font, zoom;\n    t_glist *gl;\n    if (pd_class(&x->te_pd) == canvas_class &&\n        ((t_glist *)(x))->gl_isgraph &&\n        ((t_glist *)(x))->gl_goprect)\n            gl = (t_glist *)(x);\n    else gl = thisglist;\n    font =  glist_getfont(gl);\n    zoom = glist_getzoom(gl);\n        /* override if atom box has its own specified font size */\n    if (x->te_type == T_ATOM && ((t_gatom *)x)->a_fontsize > 0)\n        font = ((t_gatom *)x)->a_fontsize;\n    *fwidthp = sys_zoomfontwidth(font, zoom, 0);\n    *fheightp = sys_zoomfontheight(font, zoom, 0);\n    *guifsize = sys_hostfontsize(font, zoom);\n}\n\n\n/*\n        *fontwidthp =  glist_fontwidth((t_glist *)(x->x_text));\n        fontheightp =  glist_fontheight((t_glist *)(x->x_text));\n  *guifontsizep = sys_hostfontsize(font, glist_getzoom(x->x_glist));\n*/\n\nvoid g_text_setup(void)\n{\n    text_class = class_new(gensym(\"text\"), 0, 0, sizeof(t_text),\n        CLASS_NOINLET | CLASS_PATCHABLE, 0);\n    class_addanything(text_class, text_anything);\n\n    message_class = class_new(gensym(\"message\"), 0, (t_method)message_free,\n        sizeof(t_message), CLASS_PATCHABLE, 0);\n    class_addbang(message_class, message_bang);\n    class_addfloat(message_class, message_float);\n    class_addsymbol(message_class, message_symbol);\n    class_addlist(message_class, message_list);\n    class_addanything(message_class, message_list);\n\n    class_addmethod(message_class, (t_method)message_click, gensym(\"click\"),\n        A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);\n    class_addmethod(message_class, (t_method)message_set, gensym(\"set\"),\n        A_GIMME, 0);\n    class_addmethod(message_class, (t_method)message_add, gensym(\"add\"),\n        A_GIMME, 0);\n    class_addmethod(message_class, (t_method)message_add2, gensym(\"add2\"),\n        A_GIMME, 0);\n    class_addmethod(message_class, (t_method)message_addcomma,\n        gensym(\"addcomma\"), 0);\n    class_addmethod(message_class, (t_method)message_addsemi,\n        gensym(\"addsemi\"), 0);\n    class_addmethod(message_class, (t_method)message_adddollar,\n        gensym(\"adddollar\"), A_FLOAT, 0);\n    class_addmethod(message_class, (t_method)message_adddollsym,\n        gensym(\"adddollsym\"), A_SYMBOL, 0);\n\n    messresponder_class = class_new(gensym(\"messresponder\"), 0, 0,\n        sizeof(t_text), CLASS_PD, 0);\n    class_addbang(messresponder_class, messresponder_bang);\n    class_addfloat(messresponder_class, (t_method) messresponder_float);\n    class_addsymbol(messresponder_class, messresponder_symbol);\n    class_addlist(messresponder_class, messresponder_list);\n    class_addanything(messresponder_class, messresponder_anything);\n\n    gatom_class = class_new(gensym(\"gatom\"), 0, (t_method)gatom_free,\n        sizeof(t_gatom), CLASS_NOINLET | CLASS_PATCHABLE, 0);\n    class_addbang(gatom_class, gatom_bang);\n    class_addfloat(gatom_class, gatom_float);\n    class_addsymbol(gatom_class, gatom_symbol);\n    class_addlist(gatom_class, gatom_list);\n    class_addmethod(gatom_class, (t_method)gatom_set, gensym(\"set\"),\n        A_GIMME, 0);\n    class_addmethod(gatom_class, (t_method)gatom_click, gensym(\"click\"),\n        A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);\n    class_addmethod(gatom_class, (t_method)gatom_param, gensym(\"param\"),\n        A_GIMME, 0);\n    class_setwidget(gatom_class, &gatom_widgetbehavior);\n    class_setpropertiesfn(gatom_class, gatom_properties);\n    class_sethelpsymbol(gatom_class, gensym(\"gui-boxes\"));\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/g_toggle.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution. */\n\n/* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */\n/* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */\n\n\n#include <string.h>\n#include <stdio.h>\n#include \"m_pd.h\"\n\n#include \"g_all_guis.h\"\n\n/* --------------- tgl     gui-toggle ------------------------- */\n\nt_widgetbehavior toggle_widgetbehavior;\nstatic t_class *toggle_class;\n\n/* widget helper functions */\n#define toggle_draw_io 0\nvoid toggle_draw_config(t_toggle* x, t_glist* glist)\n{\n    const int zoom = IEMGUI_ZOOM(x);\n    t_iemgui *iemgui = &x->x_gui;\n    t_canvas *canvas = glist_getcanvas(glist);\n    int xpos = text_xpix(&x->x_gui.x_obj, glist);\n    int ypos = text_ypix(&x->x_gui.x_obj, glist);\n    int iow = IOWIDTH * zoom, ioh = IEM_GUI_IOHEIGHT * zoom;\n    int crossw = 1, w = x->x_gui.x_w / zoom;\n    int col = x->x_on ? x->x_gui.x_fcol : x->x_gui.x_bcol;\n    char tag[128];\n    t_atom fontatoms[3];\n    SETSYMBOL(fontatoms+0, gensym(iemgui->x_font));\n    SETFLOAT (fontatoms+1, -iemgui->x_fontsize*zoom);\n    SETSYMBOL(fontatoms+2, gensym(sys_fontweight));\n\n    if(w >= 30)\n        crossw = 2;\n    if(w >= 60)\n        crossw = 3;\n    crossw *= zoom;\n\n    sprintf(tag, \"%pBASE\", x);\n    pdgui_vmess(0, \"crs iiii\", canvas, \"coords\", tag,\n        xpos, ypos, xpos + x->x_gui.x_w, ypos + x->x_gui.x_h);\n    pdgui_vmess(0, \"crs ri rk\", canvas, \"itemconfigure\", tag,\n        \"-width\", zoom, \"-fill\", x->x_gui.x_bcol);\n\n    sprintf(tag, \"%pX1\", x);\n    pdgui_vmess(0, \"crs iiii\", canvas, \"coords\", tag,\n        xpos + crossw + zoom, ypos + crossw + zoom,\n        xpos + x->x_gui.x_w - crossw - zoom, ypos + x->x_gui.x_h - crossw - zoom);\n    pdgui_vmess(0, \"crs ri rk\", canvas, \"itemconfigure\", tag,\n        \"-width\", crossw, \"-fill\", col);\n\n    sprintf(tag, \"%pX2\", x);\n    pdgui_vmess(0, \"crs iiii\", canvas, \"coords\", tag,\n        xpos + crossw + zoom, ypos + x->x_gui.x_h - crossw - zoom,\n        xpos + x->x_gui.x_w - crossw - zoom, ypos + crossw + zoom);\n    pdgui_vmess(0, \"crs ri rk\", canvas, \"itemconfigure\", tag,\n        \"-width\", crossw, \"-fill\", col);\n\n    sprintf(tag, \"%pLABEL\", x);\n    pdgui_vmess(0, \"crs ii\", canvas, \"coords\", tag,\n        xpos + x->x_gui.x_ldx * zoom, ypos + x->x_gui.x_ldy * zoom);\n    pdgui_vmess(0, \"crs rA rk\", canvas, \"itemconfigure\", tag,\n        \"-font\", 3, fontatoms,\n        \"-fill\", (x->x_gui.x_fsf.x_selected ? IEM_GUI_COLOR_SELECTED : x->x_gui.x_lcol));\n    iemgui_dolabel(x, &x->x_gui, x->x_gui.x_lab, 1);\n}\nvoid toggle_draw_new(t_toggle *x, t_glist *glist)\n{\n        /* create the widgets (but don't configure them yet) */\n    t_canvas *canvas = glist_getcanvas(glist);\n    char tag[128], tag_object[128];\n    char*tags[] = {tag_object, tag, \"label\", \"text\"};\n    sprintf(tag_object, \"%pOBJ\", x);\n\n    sprintf(tag, \"%pBASE\", x);\n    pdgui_vmess(0, \"crr iiii rS\", canvas, \"create\", \"rectangle\",\n        0, 0, 0, 0, \"-tags\", 2, tags);\n\n    sprintf(tag, \"%pX1\", x);\n    pdgui_vmess(0, \"crr iiii rS\", canvas, \"create\", \"line\",\n        0, 0, 0, 0, \"-tags\", 2, tags);\n\n    sprintf(tag, \"%pX2\", x);\n    pdgui_vmess(0, \"crr iiii rS\", canvas, \"create\", \"line\",\n        0, 0, 0, 0, \"-tags\", 2, tags);\n\n    sprintf(tag, \"%pLABEL\", x);\n    pdgui_vmess(0, \"crr ii rs rS\", canvas, \"create\", \"text\",\n        0, 0, \"-anchor\", \"w\", \"-tags\", 4, tags);\n\n    toggle_draw_config(x, glist);\n    (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_IO);\n}\n\nvoid toggle_draw_select(t_toggle* x, t_glist* glist)\n{\n    t_canvas *canvas = glist_getcanvas(glist);\n    int col = IEM_GUI_COLOR_NORMAL, lcol = x->x_gui.x_lcol;\n    char tag[128];\n\n    if(x->x_gui.x_fsf.x_selected)\n        col = lcol = IEM_GUI_COLOR_SELECTED;\n\n    sprintf(tag, \"%pBASE\", x);\n    pdgui_vmess(0, \"crs rk\", canvas, \"itemconfigure\", tag, \"-outline\", col);\n    sprintf(tag, \"%pLABEL\", x);\n    pdgui_vmess(0, \"crs rk\", canvas, \"itemconfigure\", tag, \"-fill\", lcol);\n}\n\nvoid toggle_draw_update(t_toggle *x, t_glist *glist)\n{\n    if(glist_isvisible(glist))\n    {\n        t_canvas *canvas = glist_getcanvas(glist);\n        int col = (x->x_on != 0.0) ? x->x_gui.x_fcol : x->x_gui.x_bcol;\n        char tag[128];\n\n        sprintf(tag, \"%pX1\", x);\n        pdgui_vmess(0, \"crs rk\", canvas, \"itemconfigure\", tag, \"-fill\", col);\n        sprintf(tag, \"%pX2\", x);\n        pdgui_vmess(0, \"crs rk\", canvas, \"itemconfigure\", tag, \"-fill\", col);\n    }\n}\n\n/* ------------------------ tgl widgetbehaviour----------------------------- */\n\nstatic void toggle_getrect(t_gobj *z, t_glist *glist,\n                           int *xp1, int *yp1, int *xp2, int *yp2)\n{\n    t_toggle *x = (t_toggle *)z;\n\n    *xp1 = text_xpix(&x->x_gui.x_obj, glist);\n    *yp1 = text_ypix(&x->x_gui.x_obj, glist);\n    *xp2 = *xp1 + x->x_gui.x_w;\n    *yp2 = *yp1 + x->x_gui.x_h;\n}\n\nstatic void toggle_save(t_gobj *z, t_binbuf *b)\n{\n    t_toggle *x = (t_toggle *)z;\n    t_symbol *bflcol[3];\n    t_symbol *srl[3];\n\n    iemgui_save(&x->x_gui, srl, bflcol);\n    binbuf_addv(b, \"ssiisiisssiiiisssff\", gensym(\"#X\"), gensym(\"obj\"),\n                (int)x->x_gui.x_obj.te_xpix,\n                (int)x->x_gui.x_obj.te_ypix,\n                gensym(\"tgl\"), x->x_gui.x_w/IEMGUI_ZOOM(x),\n                iem_symargstoint(&x->x_gui.x_isa),\n                srl[0], srl[1], srl[2],\n                x->x_gui.x_ldx, x->x_gui.x_ldy,\n                iem_fstyletoint(&x->x_gui.x_fsf), x->x_gui.x_fontsize,\n                bflcol[0], bflcol[1], bflcol[2],\n                x->x_gui.x_isa.x_loadinit?x->x_on:0.f,\n                x->x_nonzero);\n    binbuf_addv(b, \";\");\n}\n\nstatic void toggle_properties(t_gobj *z, t_glist *owner)\n{\n    t_toggle *x = (t_toggle *)z;\n    iemgui_new_dialog(x, &x->x_gui, \"tgl\",\n                      x->x_gui.x_w/IEMGUI_ZOOM(x), IEM_GUI_MINSIZE,\n                      0, 0,\n                      x->x_nonzero, 0,\n                      1,\n                      -1, \"\", \"\",\n                      1, -1, -1);\n}\n\nstatic void toggle_bang(t_toggle *x)\n{\n    x->x_on = (x->x_on == 0.0) ? x->x_nonzero : 0.0;\n    (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);\n    outlet_float(x->x_gui.x_obj.ob_outlet, x->x_on);\n    if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)\n        pd_float(x->x_gui.x_snd->s_thing, x->x_on);\n}\n\nstatic void toggle_dialog(t_toggle *x, t_symbol *s, int argc, t_atom *argv)\n{\n    t_symbol *srl[3];\n    int a = (int)atom_getfloatarg(0, argc, argv);\n    t_float nonzero = (t_float)atom_getfloatarg(2, argc, argv);\n    int sr_flags;\n    t_atom undo[18];\n    iemgui_setdialogatoms(&x->x_gui, 18, undo);\n    SETFLOAT (undo+1, 0);\n    SETFLOAT (undo+2, x->x_nonzero);\n    SETFLOAT (undo+3, 0);\n\n    pd_undo_set_objectstate(x->x_gui.x_glist, (t_pd*)x, gensym(\"dialog\"),\n                            18, undo,\n                            argc, argv);\n\n    if(nonzero == 0.0)\n        nonzero = 1.0;\n    x->x_nonzero = nonzero;\n    if(x->x_on != 0.0)\n        x->x_on = x->x_nonzero;\n    sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv);\n    x->x_gui.x_w = iemgui_clip_size(a) * IEMGUI_ZOOM(x);\n    x->x_gui.x_h = x->x_gui.x_w;\n    iemgui_size(x, &x->x_gui);\n}\n\nstatic void toggle_click(t_toggle *x, t_floatarg xpos, t_floatarg ypos, t_floatarg shift, t_floatarg ctrl, t_floatarg alt)\n{toggle_bang(x);}\n\nstatic int toggle_newclick(t_gobj *z, struct _glist *glist, int xpix, int ypix, int shift, int alt, int dbl, int doit)\n{\n    if(doit)\n        toggle_click((t_toggle *)z, (t_floatarg)xpix, (t_floatarg)ypix, (t_floatarg)shift, 0, (t_floatarg)alt);\n    return (1);\n}\n\nstatic void toggle_set(t_toggle *x, t_floatarg f)\n{\n    int old = (x->x_on != 0);\n    x->x_on = f;\n    if (f != 0.0 && pd_compatibilitylevel < 46)\n        x->x_nonzero = f;\n    if ((x->x_on != 0) != old)\n        (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE);\n}\n\nstatic void toggle_float(t_toggle *x, t_floatarg f)\n{\n    toggle_set(x, f);\n    if(x->x_gui.x_fsf.x_put_in2out)\n    {\n        outlet_float(x->x_gui.x_obj.ob_outlet, x->x_on);\n        if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)\n            pd_float(x->x_gui.x_snd->s_thing, x->x_on);\n    }\n}\n\nstatic void toggle_fout(t_toggle *x, t_floatarg f)\n{\n    toggle_set(x, f);\n    outlet_float(x->x_gui.x_obj.ob_outlet, x->x_on);\n    if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing)\n        pd_float(x->x_gui.x_snd->s_thing, x->x_on);\n}\n\nstatic void toggle_loadbang(t_toggle *x, t_floatarg action)\n{\n    if (action == LB_LOAD && x->x_gui.x_isa.x_loadinit)\n        toggle_fout(x, (t_float)x->x_on);\n}\n\nstatic void toggle_size(t_toggle *x, t_symbol *s, int ac, t_atom *av)\n{\n    x->x_gui.x_w = iemgui_clip_size((int)atom_getfloatarg(0, ac, av)) * IEMGUI_ZOOM(x);\n    x->x_gui.x_h = x->x_gui.x_w;\n    iemgui_size((void *)x, &x->x_gui);\n}\n\nstatic void toggle_delta(t_toggle *x, t_symbol *s, int ac, t_atom *av)\n{iemgui_delta((void *)x, &x->x_gui, s, ac, av);}\n\nstatic void toggle_pos(t_toggle *x, t_symbol *s, int ac, t_atom *av)\n{iemgui_pos((void *)x, &x->x_gui, s, ac, av);}\n\nstatic void toggle_color(t_toggle *x, t_symbol *s, int ac, t_atom *av)\n{iemgui_color((void *)x, &x->x_gui, s, ac, av);}\n\nstatic void toggle_send(t_toggle *x, t_symbol *s)\n{iemgui_send(x, &x->x_gui, s);}\n\nstatic void toggle_receive(t_toggle *x, t_symbol *s)\n{iemgui_receive(x, &x->x_gui, s);}\n\nstatic void toggle_label(t_toggle *x, t_symbol *s)\n{iemgui_label((void *)x, &x->x_gui, s);}\n\nstatic void toggle_label_font(t_toggle *x, t_symbol *s, int ac, t_atom *av)\n{iemgui_label_font((void *)x, &x->x_gui, s, ac, av);}\n\nstatic void toggle_label_pos(t_toggle *x, t_symbol *s, int ac, t_atom *av)\n{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);}\n\nstatic void toggle_init(t_toggle *x, t_floatarg f)\n{\n    x->x_gui.x_isa.x_loadinit = (f == 0.0) ? 0 : 1;\n}\n\nstatic void toggle_nonzero(t_toggle *x, t_floatarg f)\n{\n    if(f != 0.0)\n        x->x_nonzero = f;\n}\n\nstatic void *toggle_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_toggle *x = (t_toggle *)iemgui_new(toggle_class);\n    int a = IEM_GUI_DEFAULTSIZE, f = 0;\n    int ldx = 0, ldy = -8 * IEM_GUI_DEFAULTSIZE_SCALE;\n    int fs = x->x_gui.x_fontsize;\n    t_float on = 0.0, nonzero = 1.0;\n    char str[144];\n\n    IEMGUI_SETDRAWFUNCTIONS(x, toggle);\n\n    if(((argc == 13)||(argc == 14))&&IS_A_FLOAT(argv,0)\n       &&IS_A_FLOAT(argv,1)\n       &&(IS_A_SYMBOL(argv,2)||IS_A_FLOAT(argv,2))\n       &&(IS_A_SYMBOL(argv,3)||IS_A_FLOAT(argv,3))\n       &&(IS_A_SYMBOL(argv,4)||IS_A_FLOAT(argv,4))\n       &&IS_A_FLOAT(argv,5)&&IS_A_FLOAT(argv,6)\n       &&IS_A_FLOAT(argv,7)&&IS_A_FLOAT(argv,8)&&IS_A_FLOAT(argv,12))\n    {\n        a = (int)atom_getfloatarg(0, argc, argv);\n        iem_inttosymargs(&x->x_gui.x_isa, atom_getfloatarg(1, argc, argv));\n        iemgui_new_getnames(&x->x_gui, 2, argv);\n        ldx = (int)atom_getfloatarg(5, argc, argv);\n        ldy = (int)atom_getfloatarg(6, argc, argv);\n        iem_inttofstyle(&x->x_gui.x_fsf, atom_getfloatarg(7, argc, argv));\n        fs = (int)atom_getfloatarg(8, argc, argv);\n        iemgui_all_loadcolors(&x->x_gui, argv+9, argv+10, argv+11);\n        on = (t_float)atom_getfloatarg(12, argc, argv);\n    }\n    else iemgui_new_getnames(&x->x_gui, 2, 0);\n    if((argc == 14)&&IS_A_FLOAT(argv,13))\n        nonzero = (t_float)atom_getfloatarg(13, argc, argv);\n\n    x->x_gui.x_fsf.x_snd_able = (0 != x->x_gui.x_snd);\n    x->x_gui.x_fsf.x_rcv_able = (0 != x->x_gui.x_rcv);\n    if(x->x_gui.x_fsf.x_font_style == 1) strcpy(x->x_gui.x_font, \"helvetica\");\n    else if(x->x_gui.x_fsf.x_font_style == 2) strcpy(x->x_gui.x_font, \"times\");\n    else { x->x_gui.x_fsf.x_font_style = 0;\n        strcpy(x->x_gui.x_font, sys_font); }\n    x->x_nonzero = (nonzero != 0.0) ? nonzero : 1.0;\n    if(x->x_gui.x_isa.x_loadinit)\n        x->x_on = (on != 0.0) ? nonzero : 0.0;\n    else\n        x->x_on = 0.0;\n    if (x->x_gui.x_fsf.x_rcv_able)\n        pd_bind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv);\n    x->x_gui.x_ldx = ldx;\n    x->x_gui.x_ldy = ldy;\n    x->x_gui.x_fontsize = (fs < 4)?4:fs;\n    x->x_gui.x_w = iemgui_clip_size(a);\n    x->x_gui.x_h = x->x_gui.x_w;\n    iemgui_verify_snd_ne_rcv(&x->x_gui);\n    iemgui_newzoom(&x->x_gui);\n    outlet_new(&x->x_gui.x_obj, &s_float);\n    return (x);\n}\n\nstatic void toggle_free(t_toggle *x)\n{\n    if(x->x_gui.x_fsf.x_rcv_able)\n        pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv);\n    pdgui_stub_deleteforkey(x);\n}\n\nvoid g_toggle_setup(void)\n{\n    toggle_class = class_new(gensym(\"tgl\"), (t_newmethod)toggle_new,\n        (t_method)toggle_free, sizeof(t_toggle), 0, A_GIMME, 0);\n    class_addcreator((t_newmethod)toggle_new, gensym(\"toggle\"), A_GIMME, 0);\n    class_addbang(toggle_class, toggle_bang);\n    class_addfloat(toggle_class, toggle_float);\n    class_addmethod(toggle_class, (t_method)toggle_click, gensym(\"click\"),\n        A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);\n    class_addmethod(toggle_class, (t_method)toggle_dialog, gensym(\"dialog\"),\n        A_GIMME, 0);\n    class_addmethod(toggle_class, (t_method)toggle_loadbang,\n        gensym(\"loadbang\"), A_DEFFLOAT, 0);\n    class_addmethod(toggle_class, (t_method)toggle_set, gensym(\"set\"), A_FLOAT, 0);\n    class_addmethod(toggle_class, (t_method)toggle_size, gensym(\"size\"), A_GIMME, 0);\n    class_addmethod(toggle_class, (t_method)toggle_delta, gensym(\"delta\"), A_GIMME, 0);\n    class_addmethod(toggle_class, (t_method)toggle_pos, gensym(\"pos\"), A_GIMME, 0);\n    class_addmethod(toggle_class, (t_method)toggle_color, gensym(\"color\"), A_GIMME, 0);\n    class_addmethod(toggle_class, (t_method)toggle_send, gensym(\"send\"), A_DEFSYM, 0);\n    class_addmethod(toggle_class, (t_method)toggle_receive, gensym(\"receive\"), A_DEFSYM, 0);\n    class_addmethod(toggle_class, (t_method)toggle_label, gensym(\"label\"), A_DEFSYM, 0);\n    class_addmethod(toggle_class, (t_method)toggle_label_pos, gensym(\"label_pos\"), A_GIMME, 0);\n    class_addmethod(toggle_class, (t_method)toggle_label_font, gensym(\"label_font\"), A_GIMME, 0);\n    class_addmethod(toggle_class, (t_method)toggle_init, gensym(\"init\"), A_FLOAT, 0);\n    class_addmethod(toggle_class, (t_method)toggle_nonzero, gensym(\"nonzero\"), A_FLOAT, 0);\n    class_addmethod(toggle_class, (t_method)iemgui_zoom, gensym(\"zoom\"),\n        A_CANT, 0);\n    toggle_widgetbehavior.w_getrectfn = toggle_getrect;\n    toggle_widgetbehavior.w_displacefn = iemgui_displace;\n    toggle_widgetbehavior.w_selectfn = iemgui_select;\n    toggle_widgetbehavior.w_activatefn = NULL;\n    toggle_widgetbehavior.w_deletefn = iemgui_delete;\n    toggle_widgetbehavior.w_visfn = iemgui_vis;\n    toggle_widgetbehavior.w_clickfn = toggle_newclick;\n    class_setwidget(toggle_class, &toggle_widgetbehavior);\n    class_sethelpsymbol(toggle_class, gensym(\"toggle\"));\n    class_setsavefn(toggle_class, toggle_save);\n    class_setpropertiesfn(toggle_class, toggle_properties);\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/g_traversal.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* This file defines Text objects which traverse data contained in scalars\nand arrays:\n\npointer - point to an object belonging to a template\nget -     get numeric fields\nset -     set numeric/symbolic fields\nelement - get an array element\ngetsize - get the size of an array\nsetsize - set the size of an array\nappend -  add an element to a list\n\n*/\n\n#include <string.h>\n#include \"m_pd.h\"\n#include \"g_canvas.h\"\n\n    /* templates are named using the name-bashing by which canvases bind\n    thenselves, with a leading \"pd-\".  LATER see if we can have templates\n    occupy their real names.  Meanwhile, if a template has an empty name\n    or is named \"-\" (as when passed as a \"-\" argument to \"get\", etc.), just\n    return &s_; objects should check for this and allow it as a wild\n    card when appropriate. */\nstatic t_symbol *template_getbindsym(t_symbol *s)\n{\n    if (!*s->s_name || !strcmp(s->s_name, \"-\"))\n        return (&s_);\n    else return (canvas_makebindsym(s));\n}\n\n\n/* ---------------------- pointer ----------------------------- */\n\nstatic t_class *ptrobj_class;\n\ntypedef struct\n{\n    t_symbol *to_type;\n    t_outlet *to_outlet;\n} t_typedout;\n\ntypedef struct _ptrobj\n{\n    t_object x_obj;\n    t_gpointer x_gp;\n    t_typedout *x_typedout;\n    int x_ntypedout;\n    t_outlet *x_otherout;\n    t_outlet *x_bangout;\n} t_ptrobj;\n\nstatic void *ptrobj_new(t_symbol *classname, int argc, t_atom *argv)\n{\n    t_ptrobj *x = (t_ptrobj *)pd_new(ptrobj_class);\n    t_typedout *to;\n    int n;\n    gpointer_init(&x->x_gp);\n    x->x_typedout = to = (t_typedout *)getbytes(argc * sizeof (*to));\n    x->x_ntypedout = n = argc;\n    for (; n--; to++)\n    {\n        to->to_outlet = outlet_new(&x->x_obj, &s_pointer);\n        to->to_type = template_getbindsym(atom_getsymbol(argv++));\n    }\n    x->x_otherout = outlet_new(&x->x_obj, &s_pointer);\n    x->x_bangout = outlet_new(&x->x_obj, &s_bang);\n    pointerinlet_new(&x->x_obj, &x->x_gp);\n    return (x);\n}\n\nstatic void ptrobj_traverse(t_ptrobj *x, t_symbol *s)\n{\n    t_glist *glist = (t_glist *)pd_findbyclass(s, canvas_class);\n    if (glist) gpointer_setglist(&x->x_gp, glist, 0);\n    else pd_error(x, \"pointer: list '%s' not found\", s->s_name);\n}\n\nstatic void ptrobj_vnext(t_ptrobj *x, t_float f)\n{\n    t_gobj *gobj;\n    t_gpointer *gp = &x->x_gp;\n    t_gstub *gs = gp->gp_stub;\n    t_glist *glist;\n    int wantselected = (f != 0);\n\n    if (!gs)\n    {\n        pd_error(x, \"pointer next: no current pointer\");\n        return;\n    }\n    if (gs->gs_which != GP_GLIST)\n    {\n        pd_error(x, \"pointer next: lists only, not arrays\");\n        return;\n    }\n    glist = gs->gs_un.gs_glist;\n    if (glist->gl_valid != gp->gp_valid)\n    {\n        pd_error(x, \"pointer next: stale pointer\");\n        return;\n    }\n    if (wantselected && !glist_isvisible(glist))\n    {\n        pd_error(x,\n            \"pointer vnext: next-selected only works for a visible window\");\n        return;\n    }\n    gobj = &gp->gp_un.gp_scalar->sc_gobj;\n\n    if (!gobj) gobj = glist->gl_list;\n    else gobj = gobj->g_next;\n    while (gobj && ((pd_class(&gobj->g_pd) != scalar_class) ||\n        (wantselected && !glist_isselected(glist, gobj))))\n            gobj = gobj->g_next;\n\n    if (gobj)\n    {\n        t_typedout *to;\n        int n;\n        t_scalar *sc = (t_scalar *)gobj;\n        t_symbol *templatesym = sc->sc_template;\n\n        gp->gp_un.gp_scalar = sc;\n        for (n = x->x_ntypedout, to = x->x_typedout; n--; to++)\n        {\n            if (to->to_type == templatesym)\n            {\n                outlet_pointer(to->to_outlet, &x->x_gp);\n                return;\n            }\n        }\n        outlet_pointer(x->x_otherout, &x->x_gp);\n    }\n    else\n    {\n        gpointer_unset(gp);\n        outlet_bang(x->x_bangout);\n    }\n}\n\nstatic void ptrobj_next(t_ptrobj *x)\n{\n    ptrobj_vnext(x, 0);\n}\n\nstatic void ptrobj_delete(t_ptrobj *x)\n{\n    t_gobj *gobj, *old;\n    t_gpointer *gp = &x->x_gp;\n    t_gstub *gs = gp->gp_stub;\n    t_glist *glist;\n    if (!gs)\n    {\n        pd_error(x, \"pointer delete: no current pointer\");\n        return;\n    }\n    if (gs->gs_which != GP_GLIST)\n    {\n        pd_error(x, \"pointer delete: lists only, not arrays\");\n        return;\n    }\n    glist = gs->gs_un.gs_glist;\n    if (glist->gl_valid != gp->gp_valid)\n    {\n        pd_error(x, \"pointer delete: stale pointer\");\n        return;\n    }\n    if (!gp->gp_un.gp_scalar)\n    {\n        pd_error(x, \"pointer delete: pointing to head\");\n        return;\n    }\n    if (gp->gp_un.gp_scalar->sc_template == gensym(\"pd-text\"))\n    {\n        pd_error(x, \"pointer delete: can't delete 'pd-text' scalar\");\n        return;\n    }\n    if (gp->gp_un.gp_scalar->sc_template == gensym(\"pd-float-array\"))\n    {\n        pd_error(x, \"pointer delete: can't delete 'pd-float-array' scalar\");\n        return;\n    }\n\n    old = &gp->gp_un.gp_scalar->sc_gobj;\n    gobj = old->g_next;\n    while (gobj && (pd_class(&gobj->g_pd) != scalar_class))\n        gobj = gobj->g_next;\n    glist_delete(glist, old);\n    gp->gp_valid = glist->gl_valid;\n    if (gobj)\n    {\n        t_typedout *to;\n        int n;\n        t_scalar *sc = (t_scalar *)gobj;\n        t_symbol *templatesym = sc->sc_template;\n\n        gp->gp_un.gp_scalar = sc;\n        for (n = x->x_ntypedout, to = x->x_typedout; n--; to++)\n        {\n            if (to->to_type == templatesym)\n            {\n                outlet_pointer(to->to_outlet, &x->x_gp);\n                return;\n            }\n        }\n        outlet_pointer(x->x_otherout, &x->x_gp);\n    }\n    else\n    {\n        gpointer_unset(gp);\n        outlet_bang(x->x_bangout);\n    }\n}\n\nt_symbol *gpointer_gettemplatesym(const t_gpointer *gp);\n\nstatic void ptrobj_equal(t_ptrobj *x, t_gpointer *gp)\n{\n    t_symbol *templatesym;\n    int n, which, result;\n    t_typedout *to;\n    if (!gpointer_check(&x->x_gp, 1))\n    {\n        pd_error(x, \"pointer equal: empty pointer\");\n        return;\n    }\n    /* we don't care for the actual type in the union because they are all pointers */\n    result = (gp->gp_stub->gs_un.gs_glist == x->x_gp.gp_stub->gs_un.gs_glist) &&\n        (gp->gp_un.gp_scalar == x->x_gp.gp_un.gp_scalar);\n    if (!result)\n    {\n        outlet_bang(x->x_bangout);\n        return;\n    }\n    templatesym = gpointer_gettemplatesym(&x->x_gp);\n    for (n = x->x_ntypedout, to = x->x_typedout; n--; to++)\n    {\n        if (to->to_type == templatesym)\n        {\n            outlet_pointer(to->to_outlet, &x->x_gp);\n            return;\n        }\n    }\n    outlet_pointer(x->x_otherout, &x->x_gp);\n}\n\n    /* send a message to the window containing the object pointed to */\nstatic void ptrobj_sendwindow(t_ptrobj *x, t_symbol *s, int argc, t_atom *argv)\n{\n    t_scalar *sc;\n    t_symbol *templatesym;\n    int n;\n    t_typedout *to;\n    t_glist *glist;\n    t_pd *canvas;\n    t_gstub *gs;\n    if (!gpointer_check(&x->x_gp, 1))\n    {\n        pd_error(x, \"pointer send-window: empty pointer\");\n        return;\n    }\n    gs = x->x_gp.gp_stub;\n    if (gs->gs_which == GP_GLIST)\n        glist = gs->gs_un.gs_glist;\n    else\n    {\n        t_array *owner_array = gs->gs_un.gs_array;\n        while (owner_array->a_gp.gp_stub->gs_which == GP_ARRAY)\n            owner_array = owner_array->a_gp.gp_stub->gs_un.gs_array;\n        glist = owner_array->a_gp.gp_stub->gs_un.gs_glist;\n    }\n    canvas = (t_pd *)glist_getcanvas(glist);\n    if (argc && argv->a_type == A_SYMBOL)\n        pd_typedmess(canvas, argv->a_w.w_symbol, argc-1, argv+1);\n    else pd_error(x, \"pointer send-window: no message?\");\n}\n\n\n    /* send the pointer to the named object */\nstatic void ptrobj_send(t_ptrobj *x, t_symbol *s)\n{\n    if (!s->s_thing)\n        pd_error(x, \"%s: no such object\", s->s_name);\n    else if (!gpointer_check(&x->x_gp, 1))\n        pd_error(x, \"pointer send: empty pointer\");\n    else pd_pointer(s->s_thing, &x->x_gp);\n}\n\nstatic void ptrobj_bang(t_ptrobj *x)\n{\n    t_symbol *templatesym;\n    int n;\n    t_typedout *to;\n    if (!gpointer_check(&x->x_gp, 1))\n    {\n        pd_error(x, \"pointer bang: empty pointer\");\n        return;\n    }\n    templatesym = gpointer_gettemplatesym(&x->x_gp);\n    for (n = x->x_ntypedout, to = x->x_typedout; n--; to++)\n    {\n        if (to->to_type == templatesym)\n        {\n            outlet_pointer(to->to_outlet, &x->x_gp);\n            return;\n        }\n    }\n    outlet_pointer(x->x_otherout, &x->x_gp);\n}\n\n\nstatic void ptrobj_pointer(t_ptrobj *x, t_gpointer *gp)\n{\n    gpointer_unset(&x->x_gp);\n    gpointer_copy(gp, &x->x_gp);\n    ptrobj_bang(x);\n}\n\n\nstatic void ptrobj_rewind(t_ptrobj *x)\n{\n    t_scalar *sc;\n    t_symbol *templatesym;\n    int n;\n    t_typedout *to;\n    t_glist *glist;\n    t_pd *canvas;\n    t_gstub *gs;\n    if (!gpointer_check(&x->x_gp, 1))\n    {\n        pd_error(x, \"pointer rewind: empty pointer\");\n        return;\n    }\n    gs = x->x_gp.gp_stub;\n    if (gs->gs_which != GP_GLIST)\n    {\n        pd_error(x, \"pointer rewind: sorry, unavailable for arrays\");\n        return;\n    }\n    glist = gs->gs_un.gs_glist;\n    gpointer_setglist(&x->x_gp, glist, 0);\n    ptrobj_bang(x);\n}\n\nstatic void ptrobj_free(t_ptrobj *x)\n{\n    freebytes(x->x_typedout, x->x_ntypedout * sizeof (*x->x_typedout));\n    gpointer_unset(&x->x_gp);\n}\n\nstatic void ptrobj_setup(void)\n{\n    ptrobj_class = class_new(gensym(\"pointer\"), (t_newmethod)ptrobj_new,\n        (t_method)ptrobj_free, sizeof(t_ptrobj), 0, A_GIMME, 0);\n    class_addmethod(ptrobj_class, (t_method)ptrobj_next, gensym(\"next\"), 0);\n    class_addmethod(ptrobj_class, (t_method)ptrobj_send, gensym(\"send\"),\n        A_SYMBOL, 0);\n    class_addmethod(ptrobj_class, (t_method)ptrobj_traverse, gensym(\"traverse\"),\n        A_SYMBOL, 0);\n    class_addmethod(ptrobj_class, (t_method)ptrobj_vnext, gensym(\"vnext\"),\n        A_DEFFLOAT, 0);\n    class_addmethod(ptrobj_class, (t_method)ptrobj_delete, gensym(\"delete\"), 0);\n    class_addmethod(ptrobj_class, (t_method)ptrobj_equal, gensym(\"equal\"), A_POINTER, 0);\n    class_addmethod(ptrobj_class, (t_method)ptrobj_sendwindow,\n        gensym(\"send-window\"), A_GIMME, 0);\n    class_addmethod(ptrobj_class, (t_method)ptrobj_rewind,\n        gensym(\"rewind\"), 0);\n    class_addpointer(ptrobj_class, ptrobj_pointer);\n    class_addbang(ptrobj_class, ptrobj_bang);\n}\n\n/* ---------------------- get ----------------------------- */\n\nstatic t_class *get_class;\n\ntypedef struct _getvariable\n{\n    t_symbol *gv_sym;\n    t_outlet *gv_outlet;\n} t_getvariable;\n\ntypedef struct _get\n{\n    t_object x_obj;\n    t_symbol *x_templatesym;\n    int x_nout;\n    t_getvariable *x_variables;\n} t_get;\n\nstatic void *get_new(t_symbol *why, int argc, t_atom *argv)\n{\n    t_get *x = (t_get *)pd_new(get_class);\n    int varcount, i;\n    t_atom at, *varvec;\n    t_getvariable *sp;\n\n    x->x_templatesym = template_getbindsym(atom_getsymbolarg(0, argc, argv));\n    if (argc < 2)\n    {\n        varcount = 1;\n        varvec = &at;\n        SETSYMBOL(&at, &s_);\n    }\n    else varcount = argc - 1, varvec = argv + 1;\n    x->x_variables\n        = (t_getvariable *)getbytes(varcount * sizeof (*x->x_variables));\n    x->x_nout = varcount;\n    for (i = 0, sp = x->x_variables; i < varcount; i++, sp++)\n    {\n        sp->gv_sym = atom_getsymbolarg(i, varcount, varvec);\n        sp->gv_outlet = outlet_new(&x->x_obj, 0);\n            /* LATER connect with the template and set the outlet's type\n            correctly.  We can't yet guarantee that the template is there\n            before we hit this routine. */\n    }\n    return (x);\n}\n\nstatic void get_set(t_get *x, t_symbol *templatesym, t_symbol *field)\n{\n    if (x->x_nout != 1)\n        pd_error(x, \"get: cannot set multiple fields.\");\n    else\n    {\n        x->x_templatesym = template_getbindsym(templatesym);\n        x->x_variables->gv_sym = field;\n    }\n}\n\nstatic void get_pointer(t_get *x, t_gpointer *gp)\n{\n    int nitems = x->x_nout, i;\n    t_symbol *templatesym;\n    t_template *template;\n    t_gstub *gs = gp->gp_stub;\n    t_word *vec;\n    t_getvariable *vp;\n\n    if (!gpointer_check(gp, 0))\n    {\n        pd_error(x, \"get: stale or empty pointer\");\n        return;\n    }\n    if (*x->x_templatesym->s_name)\n    {\n        if ((templatesym = x->x_templatesym) != gpointer_gettemplatesym(gp))\n        {\n            pd_error(x, \"get %s: got wrong template (%s)\",\n                templatesym->s_name, gpointer_gettemplatesym(gp)->s_name);\n            return;\n        }\n    }\n    else templatesym = gpointer_gettemplatesym(gp);\n    if (!(template = template_findbyname(templatesym)))\n    {\n        pd_error(x, \"get: couldn't find template %s\", templatesym->s_name);\n        return;\n    }\n    if (gs->gs_which == GP_ARRAY) vec = gp->gp_un.gp_w;\n    else vec = gp->gp_un.gp_scalar->sc_vec;\n    for (i = nitems - 1, vp = x->x_variables + i; i >= 0; i--, vp--)\n    {\n        int onset, type;\n        t_symbol *arraytype;\n        if (template_find_field(template, vp->gv_sym, &onset, &type, &arraytype))\n        {\n            if (type == DT_FLOAT)\n                outlet_float(vp->gv_outlet,\n                    *(t_float *)(((char *)vec) + onset));\n            else if (type == DT_SYMBOL)\n                outlet_symbol(vp->gv_outlet,\n                    *(t_symbol **)(((char *)vec) + onset));\n            else pd_error(x, \"get: %s.%s is not a number or symbol\",\n                    template->t_sym->s_name, vp->gv_sym->s_name);\n        }\n        else pd_error(x, \"get: %s.%s: no such field\",\n            template->t_sym->s_name, vp->gv_sym->s_name);\n    }\n}\n\nstatic void get_free(t_get *x)\n{\n    freebytes(x->x_variables, x->x_nout * sizeof (*x->x_variables));\n}\n\nstatic void get_setup(void)\n{\n    get_class = class_new(gensym(\"get\"), (t_newmethod)get_new,\n        (t_method)get_free, sizeof(t_get), 0, A_GIMME, 0);\n    class_addpointer(get_class, get_pointer);\n    class_addmethod(get_class, (t_method)get_set, gensym(\"set\"),\n        A_SYMBOL, A_SYMBOL, 0);\n}\n\n/* ---------------------- set ----------------------------- */\n\nstatic t_class *set_class;\n\ntypedef struct _setvariable\n{\n    t_symbol *gv_sym;\n    union word gv_w;\n} t_setvariable;\n\ntypedef struct _set\n{\n    t_object x_obj;\n    t_gpointer x_gp;\n    t_symbol *x_templatesym;\n    int x_nin;\n    int x_issymbol;\n    t_setvariable *x_variables;\n} t_set;\n\nstatic void *set_new(t_symbol *why, int argc, t_atom *argv)\n{\n    t_set *x = (t_set *)pd_new(set_class);\n    int i, varcount;\n    t_setvariable *sp;\n    t_atom at, *varvec;\n    if (argc && (argv[0].a_type == A_SYMBOL) &&\n        !strcmp(argv[0].a_w.w_symbol->s_name, \"-symbol\"))\n    {\n        x->x_issymbol = 1;\n        argc--;\n        argv++;\n    }\n    else x->x_issymbol = 0;\n    x->x_templatesym = template_getbindsym(atom_getsymbolarg(0, argc, argv));\n    if (argc < 2)\n    {\n        varcount = 1;\n        varvec = &at;\n        SETSYMBOL(&at, &s_);\n    }\n    else varcount = argc - 1, varvec = argv + 1;\n    x->x_variables\n        = (t_setvariable *)getbytes(varcount * sizeof (*x->x_variables));\n    x->x_nin = varcount;\n    for (i = 0, sp = x->x_variables; i < varcount; i++, sp++)\n    {\n        sp->gv_sym = atom_getsymbolarg(i, varcount, varvec);\n        if (x->x_issymbol)\n            sp->gv_w.w_symbol = &s_;\n        else sp->gv_w.w_float = 0;\n        if (i)\n        {\n            if (x->x_issymbol)\n                symbolinlet_new(&x->x_obj, &sp->gv_w.w_symbol);\n            else floatinlet_new(&x->x_obj, &sp->gv_w.w_float);\n        }\n    }\n    pointerinlet_new(&x->x_obj, &x->x_gp);\n    gpointer_init(&x->x_gp);\n    return (x);\n}\n\nstatic void set_set(t_set *x, t_symbol *templatesym, t_symbol *field)\n{\n    if (x->x_nin != 1)\n        pd_error(x, \"set: cannot set multiple fields.\");\n    else\n    {\n       x->x_templatesym = template_getbindsym(templatesym);\n       x->x_variables->gv_sym = field;\n       if (x->x_issymbol)\n           x->x_variables->gv_w.w_symbol = &s_;\n       else\n           x->x_variables->gv_w.w_float = 0;\n    }\n}\n\nstatic void set_bang(t_set *x)\n{\n    int nitems = x->x_nin, i;\n    t_symbol *templatesym;\n    t_template *template;\n    t_setvariable *vp;\n    t_gpointer *gp = &x->x_gp;\n    t_gstub *gs = gp->gp_stub;\n    t_word *vec;\n    if (!gpointer_check(gp, 0))\n    {\n        pd_error(x, \"set: empty pointer\");\n        return;\n    }\n    if (*x->x_templatesym->s_name)\n    {\n        if ((templatesym = x->x_templatesym) != gpointer_gettemplatesym(gp))\n        {\n            pd_error(x, \"set %s: got wrong template (%s)\",\n                templatesym->s_name, gpointer_gettemplatesym(gp)->s_name);\n            return;\n        }\n    }\n    else templatesym = gpointer_gettemplatesym(gp);\n    if (!(template = template_findbyname(templatesym)))\n    {\n        pd_error(x, \"set: couldn't find template %s\", templatesym->s_name);\n        return;\n    }\n    if (!nitems)\n        return;\n    if (gs->gs_which == GP_ARRAY)\n        vec = gp->gp_un.gp_w;\n    else vec = gp->gp_un.gp_scalar->sc_vec;\n    if (x->x_issymbol)\n        for (i = 0, vp = x->x_variables; i < nitems; i++, vp++)\n            template_setsymbol(template, vp->gv_sym, vec, vp->gv_w.w_symbol, 1);\n    else for (i = 0, vp = x->x_variables; i < nitems; i++, vp++)\n        template_setfloat(template, vp->gv_sym, vec, vp->gv_w.w_float, 1);\n    if (gs->gs_which == GP_GLIST)\n        scalar_redraw(gp->gp_un.gp_scalar, gs->gs_un.gs_glist);\n    else\n    {\n        t_array *owner_array = gs->gs_un.gs_array;\n        while (owner_array->a_gp.gp_stub->gs_which == GP_ARRAY)\n            owner_array = owner_array->a_gp.gp_stub->gs_un.gs_array;\n        scalar_redraw(owner_array->a_gp.gp_un.gp_scalar,\n            owner_array->a_gp.gp_stub->gs_un.gs_glist);\n    }\n}\n\nstatic void set_float(t_set *x, t_float f)\n{\n    if (x->x_nin && !x->x_issymbol)\n    {\n        x->x_variables[0].gv_w.w_float = f;\n        set_bang(x);\n    }\n    else pd_error(x, \"set: type mismatch or no field specified\");\n}\n\nstatic void set_symbol(t_set *x, t_symbol *s)\n{\n    if (x->x_nin && x->x_issymbol)\n    {\n        x->x_variables[0].gv_w.w_symbol = s;\n        set_bang(x);\n    }\n    else pd_error(x, \"set: type mismatch or no field specified\");\n}\n\nstatic void set_free(t_set *x)\n{\n    freebytes(x->x_variables, x->x_nin * sizeof (*x->x_variables));\n    gpointer_unset(&x->x_gp);\n}\n\nstatic void set_setup(void)\n{\n    set_class = class_new(gensym(\"set\"), (t_newmethod)set_new,\n        (t_method)set_free, sizeof(t_set), 0, A_GIMME, 0);\n    class_addfloat(set_class, set_float);\n    class_addsymbol(set_class, set_symbol);\n    class_addbang(set_class, set_bang);\n    class_addmethod(set_class, (t_method)set_set, gensym(\"set\"),\n        A_SYMBOL, A_SYMBOL, 0);\n}\n\n/* ---------------------- element ----------------------------- */\n\nstatic t_class *elem_class;\n\ntypedef struct _elem\n{\n    t_object x_obj;\n    t_symbol *x_templatesym;\n    t_symbol *x_fieldsym;\n    t_gpointer x_gp;\n    t_gpointer x_gparent;\n} t_elem;\n\nstatic void *elem_new(t_symbol *templatesym, t_symbol *fieldsym)\n{\n    t_elem *x = (t_elem *)pd_new(elem_class);\n    x->x_templatesym = template_getbindsym(templatesym);\n    x->x_fieldsym = fieldsym;\n    gpointer_init(&x->x_gp);\n    gpointer_init(&x->x_gparent);\n    pointerinlet_new(&x->x_obj, &x->x_gparent);\n    outlet_new(&x->x_obj, &s_pointer);\n    return (x);\n}\n\nstatic void elem_set(t_elem *x, t_symbol *templatesym, t_symbol *fieldsym)\n{\n    x->x_templatesym = template_getbindsym(templatesym);\n    x->x_fieldsym = fieldsym;\n}\n\nstatic void elem_float(t_elem *x, t_float f)\n{\n    int indx = f, nitems, onset;\n    t_symbol *templatesym, *fieldsym = x->x_fieldsym, *elemtemplatesym;\n    t_template *template;\n    t_template *elemtemplate;\n    t_gpointer *gparent = &x->x_gparent;\n    t_word *w;\n    t_array *array;\n    int elemsize, type;\n\n    if (!gpointer_check(gparent, 0))\n    {\n        pd_error(x, \"element: empty pointer\");\n        return;\n    }\n    if (*x->x_templatesym->s_name)\n    {\n        if ((templatesym = x->x_templatesym) !=\n            gpointer_gettemplatesym(gparent))\n        {\n            pd_error(x, \"element %s: got wrong template (%s)\",\n                templatesym->s_name, gpointer_gettemplatesym(gparent)->s_name);\n            return;\n        }\n    }\n    else templatesym = gpointer_gettemplatesym(gparent);\n    if (!(template = template_findbyname(templatesym)))\n    {\n        pd_error(x, \"element: couldn't find template %s\", templatesym->s_name);\n        return;\n    }\n    if (gparent->gp_stub->gs_which == GP_ARRAY) w = gparent->gp_un.gp_w;\n    else w = gparent->gp_un.gp_scalar->sc_vec;\n    if (!template)\n    {\n        pd_error(x, \"element: couldn't find template %s\", templatesym->s_name);\n        return;\n    }\n    if (!template_find_field(template, fieldsym,\n        &onset, &type, &elemtemplatesym))\n    {\n        pd_error(x, \"element: couldn't find array field %s\", fieldsym->s_name);\n        return;\n    }\n    if (type != DT_ARRAY)\n    {\n        pd_error(x, \"element: field %s not of type array\", fieldsym->s_name);\n        return;\n    }\n    if (!(elemtemplate = template_findbyname(elemtemplatesym)))\n    {\n        pd_error(x, \"element: couldn't find field template %s\",\n            elemtemplatesym->s_name);\n        return;\n    }\n\n    elemsize = elemtemplate->t_n * sizeof(t_word);\n\n    array = *(t_array **)(((char *)w) + onset);\n\n    nitems = array->a_n;\n    if (indx < 0) indx = 0;\n    if (indx >= nitems) indx = nitems-1;\n\n    gpointer_setarray(&x->x_gp, array,\n        (t_word *)((char *)(array->a_vec) + indx * elemsize));\n    outlet_pointer(x->x_obj.ob_outlet, &x->x_gp);\n}\n\nstatic void elem_free(t_elem *x, t_gpointer *gp)\n{\n    gpointer_unset(&x->x_gp);\n    gpointer_unset(&x->x_gparent);\n}\n\nstatic void elem_setup(void)\n{\n    elem_class = class_new(gensym(\"element\"), (t_newmethod)elem_new,\n        (t_method)elem_free, sizeof(t_elem), 0, A_DEFSYM, A_DEFSYM, 0);\n    class_addfloat(elem_class, elem_float);\n    class_addmethod(elem_class, (t_method)elem_set, gensym(\"set\"),\n        A_SYMBOL, A_SYMBOL, 0);\n}\n\n/* ---------------------- getsize ----------------------------- */\n\nstatic t_class *getsize_class;\n\ntypedef struct _getsize\n{\n    t_object x_obj;\n    t_symbol *x_templatesym;\n    t_symbol *x_fieldsym;\n} t_getsize;\n\nstatic void *getsize_new(t_symbol *templatesym, t_symbol *fieldsym)\n{\n    t_getsize *x = (t_getsize *)pd_new(getsize_class);\n    x->x_templatesym = template_getbindsym(templatesym);\n    x->x_fieldsym = fieldsym;\n    outlet_new(&x->x_obj, &s_float);\n    return (x);\n}\n\nstatic void getsize_set(t_getsize *x, t_symbol *templatesym, t_symbol *fieldsym)\n{\n    x->x_templatesym = template_getbindsym(templatesym);\n    x->x_fieldsym = fieldsym;\n}\n\nstatic void getsize_pointer(t_getsize *x, t_gpointer *gp)\n{\n    int nitems, onset, type;\n    t_symbol *templatesym, *fieldsym = x->x_fieldsym, *elemtemplatesym;\n    t_template *template;\n    t_word *w;\n    t_array *array;\n    int elemsize;\n    t_gstub *gs = gp->gp_stub;\n    if (!gpointer_check(gp, 0))\n    {\n        pd_error(x, \"getsize: stale or empty pointer\");\n        return;\n    }\n    if (*x->x_templatesym->s_name)\n    {\n        if ((templatesym = x->x_templatesym) !=\n            gpointer_gettemplatesym(gp))\n        {\n            pd_error(x, \"getsize %s: got wrong template (%s)\",\n                templatesym->s_name, gpointer_gettemplatesym(gp)->s_name);\n            return;\n        }\n    }\n    else templatesym = gpointer_gettemplatesym(gp);\n    if (!(template = template_findbyname(templatesym)))\n    {\n        pd_error(x, \"getsize: couldn't find template %s\", templatesym->s_name);\n        return;\n    }\n    if (!template_find_field(template, fieldsym,\n        &onset, &type, &elemtemplatesym))\n    {\n        pd_error(x, \"getsize: couldn't find array field %s\", fieldsym->s_name);\n        return;\n    }\n    if (type != DT_ARRAY)\n    {\n        pd_error(x, \"getsize: field %s not of type array\", fieldsym->s_name);\n        return;\n    }\n    if (gs->gs_which == GP_ARRAY) w = gp->gp_un.gp_w;\n    else w = gp->gp_un.gp_scalar->sc_vec;\n\n    array = *(t_array **)(((char *)w) + onset);\n    outlet_float(x->x_obj.ob_outlet, (t_float)(array->a_n));\n}\n\nstatic void getsize_setup(void)\n{\n    getsize_class = class_new(gensym(\"getsize\"), (t_newmethod)getsize_new, 0,\n        sizeof(t_getsize), 0, A_DEFSYM, A_DEFSYM, 0);\n    class_addpointer(getsize_class, getsize_pointer);\n    class_addmethod(getsize_class, (t_method)getsize_set, gensym(\"set\"),\n        A_SYMBOL, A_SYMBOL, 0);\n}\n\n/* ---------------------- setsize ----------------------------- */\n\nstatic t_class *setsize_class;\n\ntypedef struct _setsize\n{\n    t_object x_obj;\n    t_symbol *x_templatesym;\n    t_symbol *x_fieldsym;\n    t_gpointer x_gp;\n} t_setsize;\n\nstatic void *setsize_new(t_symbol *templatesym, t_symbol *fieldsym,\n    t_floatarg newsize)\n{\n    t_setsize *x = (t_setsize *)pd_new(setsize_class);\n    x->x_templatesym = template_getbindsym(templatesym);\n    x->x_fieldsym = fieldsym;\n    gpointer_init(&x->x_gp);\n\n    pointerinlet_new(&x->x_obj, &x->x_gp);\n    return (x);\n}\n\nstatic void setsize_set(t_setsize *x, t_symbol *templatesym, t_symbol *fieldsym)\n{\n    x->x_templatesym = template_getbindsym(templatesym);\n    x->x_fieldsym = fieldsym;\n}\n\nstatic void setsize_float(t_setsize *x, t_float f)\n{\n    int nitems, onset, type;\n    t_symbol *templatesym, *fieldsym = x->x_fieldsym, *elemtemplatesym;\n    t_template *template;\n    t_template *elemtemplate;\n    t_word *w;\n    t_atom at;\n    t_array *array;\n    int elemsize;\n    int newsize = f;\n    t_gpointer *gp = &x->x_gp;\n    t_gstub *gs = gp->gp_stub;\n    if (!gpointer_check(&x->x_gp, 0))\n    {\n        pd_error(x, \"setsize: empty pointer\");\n        return;\n    }\n    if (*x->x_templatesym->s_name)\n    {\n        if ((templatesym = x->x_templatesym) !=\n            gpointer_gettemplatesym(gp))\n        {\n            pd_error(x, \"setsize %s: got wrong template (%s)\",\n                templatesym->s_name, gpointer_gettemplatesym(gp)->s_name);\n            return;\n        }\n    }\n    else templatesym = gpointer_gettemplatesym(gp);\n    if (!(template = template_findbyname(templatesym)))\n    {\n        pd_error(x, \"setsize: couldn't find template %s\", templatesym->s_name);\n        return;\n    }\n\n    if (!template_find_field(template, fieldsym,\n        &onset, &type, &elemtemplatesym))\n    {\n        pd_error(x,\"setsize: couldn't find array field %s\", fieldsym->s_name);\n        return;\n    }\n    if (type != DT_ARRAY)\n    {\n        pd_error(x,\"setsize: field %s not of type array\", fieldsym->s_name);\n        return;\n    }\n    if (gs->gs_which == GP_ARRAY) w = gp->gp_un.gp_w;\n    else w = gp->gp_un.gp_scalar->sc_vec;\n\n    if (!(elemtemplate = template_findbyname(elemtemplatesym)))\n    {\n        pd_error(x,\"setsize: couldn't find field template %s\",\n            elemtemplatesym->s_name);\n        return;\n    }\n\n    elemsize = elemtemplate->t_n * sizeof(t_word);\n\n    array = *(t_array **)(((char *)w) + onset);\n\n    if (elemsize != array->a_elemsize) bug(\"setsize_gpointer\");\n\n    nitems = array->a_n;\n    if (newsize < 1) newsize = 1;\n    if (newsize == nitems) return;\n\n        /* erase the array before resizing it.  If we belong to a\n        scalar it's easy, but if we belong to an element of another\n        array we have to search back until we get to a scalar to erase.\n        When graphics updates become queueable this may fall apart... */\n\n\n    if (gs->gs_which == GP_GLIST)\n    {\n        if (glist_isvisible(gs->gs_un.gs_glist))\n            gobj_vis((t_gobj *)(gp->gp_un.gp_scalar), gs->gs_un.gs_glist, 0);\n    }\n    else\n    {\n        t_array *owner_array = gs->gs_un.gs_array;\n        while (owner_array->a_gp.gp_stub->gs_which == GP_ARRAY)\n            owner_array = owner_array->a_gp.gp_stub->gs_un.gs_array;\n        if (glist_isvisible(owner_array->a_gp.gp_stub->gs_un.gs_glist))\n            gobj_vis((t_gobj *)(owner_array->a_gp.gp_un.gp_scalar),\n                owner_array->a_gp.gp_stub->gs_un.gs_glist, 0);\n    }\n        /* if shrinking, free the scalars that will disappear */\n    if (newsize < nitems)\n    {\n        char *elem;\n        int count;\n        for (elem = ((char *)array->a_vec) + newsize * elemsize,\n            count = nitems - newsize; count--; elem += elemsize)\n                word_free((t_word *)elem, elemtemplate);\n    }\n        /* resize the array  */\n    array->a_vec = (char *)resizebytes(array->a_vec,\n        elemsize * nitems, elemsize * newsize);\n    array->a_n = newsize;\n        /* if growing, initialize new scalars */\n    if (newsize > nitems)\n    {\n        char *elem;\n        int count;\n        for (elem = ((char *)array->a_vec) + nitems * elemsize,\n            count = newsize - nitems; count--; elem += elemsize)\n                word_init((t_word *)elem, elemtemplate, gp);\n    }\n        /* invalidate all gpointers into the array */\n    array->a_valid++;\n\n    /* redraw again. */\n    if (gs->gs_which == GP_GLIST)\n    {\n        if (glist_isvisible(gs->gs_un.gs_glist))\n            gobj_vis((t_gobj *)(gp->gp_un.gp_scalar), gs->gs_un.gs_glist, 1);\n    }\n    else\n    {\n        t_array *owner_array = gs->gs_un.gs_array;\n        while (owner_array->a_gp.gp_stub->gs_which == GP_ARRAY)\n            owner_array = owner_array->a_gp.gp_stub->gs_un.gs_array;\n        if (glist_isvisible(owner_array->a_gp.gp_stub->gs_un.gs_glist))\n            gobj_vis((t_gobj *)(owner_array->a_gp.gp_un.gp_scalar),\n                owner_array->a_gp.gp_stub->gs_un.gs_glist, 1);\n    }\n}\n\nstatic void setsize_free(t_setsize *x)\n{\n    gpointer_unset(&x->x_gp);\n}\n\nstatic void setsize_setup(void)\n{\n    setsize_class = class_new(gensym(\"setsize\"), (t_newmethod)setsize_new,\n        (t_method)setsize_free, sizeof(t_setsize), 0,\n        A_DEFSYM, A_DEFSYM, A_DEFFLOAT, 0);\n    class_addfloat(setsize_class, setsize_float);\n    class_addmethod(setsize_class, (t_method)setsize_set, gensym(\"set\"),\n        A_SYMBOL, A_SYMBOL, 0);\n\n}\n\n/* ---------------------- append ----------------------------- */\n\nstatic t_class *append_class;\n\ntypedef struct _appendvariable\n{\n    t_symbol *gv_sym;\n    t_float gv_f;\n} t_appendvariable;\n\ntypedef struct _append\n{\n    t_object x_obj;\n    t_gpointer x_gp;\n    t_symbol *x_templatesym;\n    int x_nin;\n    t_appendvariable *x_variables;\n} t_append;\n\nstatic void *append_new(t_symbol *why, int argc, t_atom *argv)\n{\n    t_append *x = (t_append *)pd_new(append_class);\n    int varcount, i;\n    t_atom at, *varvec;\n    t_appendvariable *sp;\n\n    x->x_templatesym = template_getbindsym(atom_getsymbolarg(0, argc, argv));\n    if (argc < 2)\n    {\n        varcount = 1;\n        varvec = &at;\n        SETSYMBOL(&at, &s_);\n    }\n    else varcount = argc - 1, varvec = argv + 1;\n    x->x_variables\n        = (t_appendvariable *)getbytes(varcount * sizeof (*x->x_variables));\n    x->x_nin = varcount;\n    for (i = 0, sp = x->x_variables; i < varcount; i++, sp++)\n    {\n        sp->gv_sym = atom_getsymbolarg(i, varcount, varvec);\n        sp->gv_f = 0;\n        if (i) floatinlet_new(&x->x_obj, &sp->gv_f);\n    }\n    pointerinlet_new(&x->x_obj, &x->x_gp);\n    outlet_new(&x->x_obj, &s_pointer);\n    gpointer_init(&x->x_gp);\n    return (x);\n}\n\nstatic void append_set(t_append *x, t_symbol *templatesym, t_symbol *field)\n{\n    if (x->x_nin != 1)\n        pd_error(x, \"append set: cannot set multiple fields.\");\n    else\n    {\n       x->x_templatesym = template_getbindsym(templatesym);\n       x->x_variables->gv_sym = field;\n       x->x_variables->gv_f = 0;\n    }\n}\n\nstatic void append_float(t_append *x, t_float f)\n{\n    int nitems = x->x_nin, i;\n    t_symbol *templatesym = x->x_templatesym;\n    t_template *template;\n    t_appendvariable *vp;\n    t_gpointer *gp = &x->x_gp;\n    t_gstub *gs = gp->gp_stub;\n    t_word *vec;\n    t_scalar *sc, *oldsc;\n    t_glist *glist;\n\n    if (!templatesym->s_name)\n    {\n        pd_error(x, \"append: no template supplied\");\n        return;\n    }\n    template = template_findbyname(templatesym);\n    if (!template)\n    {\n        pd_error(x, \"append: couldn't find template %s\", templatesym->s_name);\n        return;\n    }\n    if (!gs)\n    {\n        pd_error(x, \"append: no current pointer\");\n        return;\n    }\n    if (gs->gs_which != GP_GLIST)\n    {\n        pd_error(x, \"append: lists only, not arrays\");\n        return;\n    }\n    glist = gs->gs_un.gs_glist;\n    if (glist->gl_valid != gp->gp_valid)\n    {\n        pd_error(x, \"append: stale pointer\");\n        return;\n    }\n    if (!nitems) return;\n    x->x_variables[0].gv_f = f;\n\n    sc = scalar_new(glist, templatesym);\n    if (!sc)\n    {\n        pd_error(x, \"%s: couldn't create scalar\", templatesym->s_name);\n        return;\n    }\n    oldsc = gp->gp_un.gp_scalar;\n\n    if (oldsc)\n    {\n        sc->sc_gobj.g_next = oldsc->sc_gobj.g_next;\n        oldsc->sc_gobj.g_next = &sc->sc_gobj;\n    }\n    else\n    {\n        sc->sc_gobj.g_next = glist->gl_list;\n        glist->gl_list = &sc->sc_gobj;\n    }\n\n    gp->gp_un.gp_scalar = sc;\n    vec = sc->sc_vec;\n    for (i = 0, vp = x->x_variables; i < nitems; i++, vp++)\n    {\n        template_setfloat(template, vp->gv_sym, vec, vp->gv_f, 1);\n    }\n\n    if (glist_isvisible(glist_getcanvas(glist)))\n        gobj_vis(&sc->sc_gobj, glist, 1);\n    /*  scalar_redraw(sc, glist);  ... have to do 'vis' instead here because\n    redraw assumes we're already visible??? ... */\n\n    outlet_pointer(x->x_obj.ob_outlet, gp);\n}\n\nstatic void append_free(t_append *x)\n{\n    freebytes(x->x_variables, x->x_nin * sizeof (*x->x_variables));\n    gpointer_unset(&x->x_gp);\n}\n\nstatic void append_setup(void)\n{\n    append_class = class_new(gensym(\"append\"), (t_newmethod)append_new,\n        (t_method)append_free, sizeof(t_append), 0, A_GIMME, 0);\n    class_addfloat(append_class, append_float);\n    class_addmethod(append_class, (t_method)append_set, gensym(\"set\"),\n        A_SYMBOL, A_SYMBOL, 0);\n}\n\n/* ----------------- setup function ------------------- */\n\nvoid g_traversal_setup(void)\n{\n    ptrobj_setup();\n    get_setup();\n    set_setup();\n    elem_setup();\n    getsize_setup();\n    setsize_setup();\n    append_setup();\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/g_undo.c",
    "content": "#include \"m_pd.h\"\n#include \"g_canvas.h\"\n#include \"g_undo.h\"\n\n#if 0\n# define DEBUG_UNDO(x) startpost(\"[%s:%d] \", __FILE__, __LINE__), x\n#else\n# define DEBUG_UNDO(x) do { } while(0)\n#endif\n\n/* used for canvas_objtext to differentiate between objects being created\n   by user vs. those (re)created by the undo/redo actions */\n\nvoid canvas_undo_set_name(const char*name);\n\n/* --------- 12. internal object state --------------- */\nint glist_getindex(t_glist *x, t_gobj *y);\nstatic t_gobj *glist_nth(t_glist *x, int n)\n{\n    t_gobj *y;\n    int indx;\n    for (y = x->gl_list, indx = 0; y; y = y->g_next, indx++)\n        if (indx == n)\n            return (y);\n    return (0);\n}\ntypedef struct _undo_object_state {\n    int u_obj;\n    t_symbol*u_symbol;\n    t_binbuf*u_undo;\n    t_binbuf*u_redo;\n} t_undo_object_state;\n\nstatic int atom_equal(const t_atom*v0, const t_atom*v1)\n{\n    if(v0->a_type != v1->a_type)\n        return 0;\n    switch(v0->a_type) {\n    case (A_FLOAT):\n        return (v0->a_w.w_float == v1->a_w.w_float);\n    case (A_SYMBOL):\n        return (v0->a_w.w_symbol == v1->a_w.w_symbol);\n    default:\n        break;\n    }\n    return 0;\n\n}\nstatic int lists_are_equal(int c0, const t_atom*v0, int c1, const t_atom*v1)\n{\n    int i;\n    if(c0 != c1)\n        return 0;\n    for(i=0; i<c0; i++)\n        if(!atom_equal(v0++, v1++))\n            return 0;\n    return 1;\n}\nvoid pd_undo_set_objectstate(t_canvas*canvas, t_pd*x, t_symbol*s,\n                                    int undo_argc, t_atom*undo_argv,\n                                    int redo_argc, t_atom*redo_argv)\n{\n    t_undo_object_state *buf;\n    int pos = glist_getindex(canvas, (t_gobj*)x);\n    t_undo *udo = canvas_undo_get(canvas);\n    if (udo && udo->u_doing)\n        return;\n    if(lists_are_equal(undo_argc, undo_argv, redo_argc, redo_argv))\n        return;\n\n    buf = (t_undo_object_state*)getbytes(sizeof(t_undo_object_state));\n    buf->u_obj = pos;\n    buf->u_symbol = s;\n    buf->u_undo = binbuf_new();\n    buf->u_redo = binbuf_new();\n    binbuf_add(buf->u_undo, undo_argc, undo_argv);\n    binbuf_add(buf->u_redo, redo_argc, redo_argv);\n#if 0\n    startpost(\"UNDO:\"); binbuf_print(buf->u_undo);\n    startpost(\"REDO:\"); binbuf_print(buf->u_redo);\n#endif\n    canvas_undo_add(canvas, UNDO_OBJECT_STATE, \"state\", buf);\n}\n\nint canvas_undo_objectstate(t_canvas *cnv, void *z, int action) {\n    t_undo_object_state *buf = z;\n    t_binbuf*bbuf = buf->u_undo;\n    t_pd*x = (t_pd*)glist_nth(cnv, buf->u_obj);\n    switch(action) {\n    case UNDO_FREE:\n        binbuf_free(buf->u_undo);\n        binbuf_free(buf->u_redo);\n        t_freebytes(buf, sizeof(*buf));\n        break;\n    case UNDO_REDO:\n        bbuf = buf->u_redo;     /* falls through */\n    case UNDO_UNDO:\n        if(x)\n            pd_typedmess(x, buf->u_symbol, binbuf_getnatom(bbuf), binbuf_getvec(bbuf));\n        break;\n    }\n    return 1;\n}\n\n\n/* --------------- */\n\nstatic void canvas_show_undomenu(t_canvas*x, const char* undo_action, const char* redo_action)\n{\n    if (glist_isvisible(x) && glist_istoplevel(x))\n        pdgui_vmess(\"pdtk_undomenu\", \"^ ss\", x, undo_action, redo_action);\n}\n\nstatic void canvas_undo_docleardirty(t_canvas *x)\n{\n    t_undo *udo = canvas_undo_get(x);\n    if (udo) udo->u_cleanstate = udo->u_last;\n}\nvoid canvas_undo_cleardirty(t_canvas *x)\n{\n        /* clear dirty flags of this canvas\n         * and all sub-canvases (but not abstractions/\n         */\n    t_gobj*y;\n    canvas_undo_docleardirty(x);\n    for(y=x->gl_list; y; y=y->g_next)\n        if (pd_class(&y->g_pd) == canvas_class && !canvas_isabstraction((t_canvas*)y))\n            canvas_undo_cleardirty((t_canvas*)y);\n}\n\nstatic int canvas_undo_doisdirty(t_canvas*x)\n{\n    t_undo *udo = x?canvas_undo_get(x):0;\n    t_gobj*y;\n    if (!udo) return 0;\n    if (udo->u_last != udo->u_cleanstate) return 1;\n\n    for(y=x->gl_list; y; y=y->g_next)\n        if (pd_class(&y->g_pd) == canvas_class && !canvas_isabstraction((t_canvas*)y))\n            if (canvas_undo_doisdirty((t_canvas*)y))\n                return 1;\n    return 0;\n}\nstatic int canvas_undo_isdirty(t_canvas *x)\n{\n    t_undo *udo = x?canvas_undo_get(x):0;\n    return (0 != udo)\n        && ((udo->u_last != udo->u_cleanstate)\n            || canvas_undo_doisdirty(canvas_getrootfor(x)));\n}\n\nt_undo_action *canvas_undo_init(t_canvas *x)\n{\n    t_undo_action *a = 0;\n    t_undo *udo = canvas_undo_get(x);\n    if (!udo) return 0;\n    a = (t_undo_action *)getbytes(sizeof(*a));\n    a->type = 0;\n    a->x = x;\n    a->next = NULL;\n\n    if (!udo->u_queue)\n    {\n        DEBUG_UNDO(post(\"%s: first init\", __FUNCTION__));\n        //this is the first init\n        udo->u_queue = a;\n        udo->u_last = a;\n\n        canvas_undo_cleardirty(x);\n            /* invalidate clean-state for re-created subpatches\n             * since we do not know whether the re-created subpatch\n             * is clean or unclean (it's undo-queue got lost when\n             * it was deleted), we assume the worst */\n        if (!canvas_isabstraction(x))\n            udo->u_cleanstate = (void*)1;\n\n        a->prev = NULL;\n        a->name = \"no\";\n        canvas_show_undomenu(x, \"no\", \"no\");\n    }\n    else\n    {\n        DEBUG_UNDO(post(\"%s: re-init %p\", __FUNCTION__, udo->u_last->next));\n        if (udo->u_last->next)\n        {\n            //we need to rebranch first then add the new action\n            canvas_undo_rebranch(x);\n        }\n        udo->u_last->next = a;\n        a->prev = udo->u_last;\n        udo->u_last = a;\n    }\n    return(a);\n}\n\nt_undo_action *canvas_undo_add(t_canvas *x, t_undo_type type, const char *name,\n    void *data)\n{\n    t_undo_action *a = 0;\n    t_undo * udo = canvas_undo_get(x);\n    DEBUG_UNDO(post(\"%s: %d %s %p!\", __FUNCTION__, type, name, data));\n    if(UNDO_SEQUENCE_END == type\n       && udo && udo->u_last\n       && UNDO_SEQUENCE_START == udo->u_last->type)\n    {\n            /* empty undo sequence...get rid of it */\n        udo->u_last = udo->u_last->prev;\n        canvas_undo_rebranch(x);\n        udo->u_last->next = 0;\n        canvas_undo_set_name(udo->u_last->name);\n        canvas_show_undomenu(x, udo->u_last->name, \"no\");\n        return 0;\n    }\n\n    a = canvas_undo_init(x);\n    if(!a)return a;\n    a->type = type;\n    a->data = (void *)data;\n    a->name = (char *)name;\n    canvas_undo_set_name(name);\n    canvas_show_undomenu(x, a->name, \"no\");\n    DEBUG_UNDO(post(\"%s: done!\", __FUNCTION__));\n    return(a);\n}\n\nstatic int canvas_undo_doit(t_canvas *x, t_undo_action *udo, int action, const char*funname)\n{\n    DEBUG_UNDO(post(\"%s: %s(%d) %d %p\", __FUNCTION__, funname, action, udo->type, udo->data));\n\n    switch(udo->type)\n    {\n    case UNDO_CONNECT:      return canvas_undo_connect(x, udo->data, action);      //connect\n    case UNDO_DISCONNECT:   return canvas_undo_disconnect(x, udo->data, action);   //disconnect\n    case UNDO_CUT:          return canvas_undo_cut(x, udo->data, action);          //cut\n    case UNDO_MOTION:       return canvas_undo_move(x, udo->data, action);         //move\n    case UNDO_PASTE:        return canvas_undo_paste(x, udo->data, action);        //paste\n    case UNDO_APPLY:        return canvas_undo_apply(x, udo->data, action);        //apply\n    case UNDO_ARRANGE:      return canvas_undo_arrange(x, udo->data, action);      //arrange\n    case UNDO_CANVAS_APPLY: return canvas_undo_canvas_apply(x, udo->data, action); //canvas apply\n    case UNDO_CREATE:       return canvas_undo_create(x, udo->data, action);       //create\n    case UNDO_RECREATE:     return canvas_undo_recreate(x, udo->data, action);     //recreate\n    case UNDO_FONT:         return canvas_undo_font(x, udo->data, action);         //font\n    case UNDO_OBJECT_STATE: return canvas_undo_objectstate(x, udo->data, action);  //font\n            /* undo sequences are handled in canvas_undo_undo resp canvas_undo_redo */\n    case UNDO_SEQUENCE_START: return 1;                                            //start undo sequence\n    case UNDO_SEQUENCE_END: return 1;                                              //end undo sequence\n    case UNDO_INIT:         if (UNDO_FREE == action) return 1;/* FALLS THROUGH */  //init\n    default:\n        pd_error(0, \"%s: unsupported undo command %d\", funname, udo->type);\n    }\n    return 0;\n}\n\nvoid canvas_undo_undo(t_canvas *x)\n{\n    t_undo *udo = canvas_undo_get(x);\n    int dspwas;\n\n    if (!udo) return;\n    dspwas = canvas_suspend_dsp();\n    DEBUG_UNDO(post(\"%s: %p != %p\", __FUNCTION__, udo->u_queue, udo->u_last));\n    if (udo->u_queue && udo->u_last != udo->u_queue)\n    {\n        udo->u_doing = 1;\n        canvas_editmode(x, 1);\n        glist_noselect(x);\n        canvas_undo_set_name(udo->u_last->name);\n\n        if(UNDO_SEQUENCE_END == udo->u_last->type)\n        {\n            int sequence_depth = 1;\n            while((udo->u_last = udo->u_last->prev)\n                  && (UNDO_INIT != udo->u_last->type))\n            {\n                DEBUG_UNDO(post(\"%s:sequence[%d] %d\", __FUNCTION__, sequence_depth, udo->u_last->type));\n                switch(udo->u_last->type)\n                {\n                case UNDO_SEQUENCE_START:\n                    sequence_depth--;\n                    break;\n                case UNDO_SEQUENCE_END:\n                    sequence_depth++;\n                    break;\n                default:\n                    canvas_undo_doit(x, udo->u_last, UNDO_UNDO, __FUNCTION__);\n                }\n                if (sequence_depth < 1)\n                    break;\n            }\n            if (sequence_depth < 0)\n                bug(\"undo sequence missing end\");\n            else if (sequence_depth > 0)\n                bug(\"undo sequence missing start\");\n        }\n\n        if(canvas_undo_doit(x, udo->u_last, UNDO_UNDO, __FUNCTION__))\n        {\n            char *undo_action, *redo_action;\n            udo->u_last = udo->u_last->prev;\n            undo_action = udo->u_last->name;\n            redo_action = udo->u_last->next->name;\n\n            udo->u_doing = 0;\n                /* here we call updating of all unpaired hubs and nodes since\n                   their regular call will fail in case their position needed\n                   to be updated by undo/redo first to reflect the old one */\n            canvas_show_undomenu(x, undo_action, redo_action);\n            canvas_dirty(x, canvas_undo_isdirty(x));\n        }\n    }\n    canvas_resume_dsp(dspwas);\n}\n\nvoid canvas_undo_redo(t_canvas *x)\n{\n    int dspwas;\n    t_undo *udo = canvas_undo_get(x);\n    if (!udo) return;\n    dspwas = canvas_suspend_dsp();\n    if (udo->u_queue && udo->u_last->next)\n    {\n        char *undo_action, *redo_action;\n        udo->u_doing = 1;\n        udo->u_last = udo->u_last->next;\n        canvas_editmode(x, 1);\n        glist_noselect(x);\n        canvas_undo_set_name(udo->u_last->name);\n\n        if(UNDO_SEQUENCE_START == udo->u_last->type)\n        {\n            int sequence_depth = 1;\n            while(udo->u_last->next && (udo->u_last = udo->u_last->next))\n            {\n                DEBUG_UNDO(post(\"%s:sequence[%d] %d\", __FUNCTION__, sequence_depth, udo->u_last->type));\n                switch(udo->u_last->type)\n                {\n                case UNDO_SEQUENCE_END:\n                    sequence_depth--;\n                    break;\n                case UNDO_SEQUENCE_START:\n                    sequence_depth++;\n                    break;\n                default:\n                    canvas_undo_doit(x, udo->u_last, UNDO_REDO, __FUNCTION__);\n                }\n                if (sequence_depth < 1)\n                    break;\n            }\n            if (sequence_depth < 0)\n                bug(\"undo sequence end without start\");\n            else if (sequence_depth > 0)\n                bug(\"undo sequence start without end\");\n        }\n\n        canvas_undo_doit(x, udo->u_last, UNDO_REDO, __FUNCTION__);\n        undo_action = udo->u_last->name;\n        redo_action = (udo->u_last->next ? udo->u_last->next->name : \"no\");\n        udo->u_doing = 0;\n        /* here we call updating of all unpaired hubs and nodes since their\n           regular call will fail in case their position needed to be updated\n           by undo/redo first to reflect the old one */\n        canvas_show_undomenu(x, undo_action, redo_action);\n        canvas_dirty(x, canvas_undo_isdirty(x));\n    }\n    canvas_resume_dsp(dspwas);\n}\n\nvoid canvas_undo_rebranch(t_canvas *x)\n{\n    int dspwas = canvas_suspend_dsp();\n    t_undo_action *a1, *a2;\n    t_undo *udo = canvas_undo_get(x);\n    if (!udo) return;\n    if (udo->u_last->next)\n    {\n        a1 = udo->u_last->next;\n        while(a1)\n        {\n            canvas_undo_doit(x, a1, UNDO_FREE, __FUNCTION__);\n            a2 = a1->next;\n            freebytes(a1, sizeof(*a1));\n            a1 = a2;\n        }\n        udo->u_last->next = 0;\n    }\n    canvas_show_undomenu(x, udo->u_last->name, \"no\");\n    canvas_resume_dsp(dspwas);\n}\n\nvoid canvas_undo_check_canvas_pointers(t_canvas *x)\n{\n    /* currently unnecessary unless we decide to implement one central undo\n       for all patchers */\n}\n\nvoid canvas_undo_purge_abstraction_actions(t_canvas *x)\n{\n    /* currently unnecessary unless we decide to implement one central undo\n       for all patchers */\n}\n\nvoid canvas_undo_free(t_canvas *x)\n{\n    int dspwas;\n    t_undo_action *a1, *a2;\n    t_undo *udo = canvas_undo_get(x);\n    if (!udo) return;\n    dspwas = canvas_suspend_dsp();\n    if (udo->u_queue)\n    {\n        a1 = udo->u_queue;\n        while(a1)\n        {\n            canvas_undo_doit(x, a1, UNDO_FREE, __FUNCTION__);\n            a2 = a1->next;\n            freebytes(a1, sizeof(*a1));\n            a1 = a2;\n        }\n    }\n    canvas_resume_dsp(dspwas);\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/g_undo.h",
    "content": "\n#ifndef __g_undo_h_\n#define __g_undo_h_\n\n/*\nInfinite undo by Ivica Ico Bukvic <ico@vt.edu> Dec. 2011\nModified for Pd-vanilla by IOhannes m zmölnig <zmoelnig@iem.at> Jun. 2018\n\nThis is the home of infinite undo queue. Each root canvas has one of these.\nOnly canvas that is root will instantiate the pointer to t_undo_action struct.\nAll sub-canvases (including abstractions) will be linked to the root window.\nOnce the root window is destroyed, so is its undo queue. Each canvas (t_glist)\ndefined in g_canvas.h now also has information on t_undo_action queue and a\npointer to the last item in the doubly-linked list (queue).\n\nFirst initialized undo is never filled (it is of a type 0). First (new) action\ncreates another t_undo_action and saves its contents there and updates \"last\"\npointer inside the t_canvas (t_glist).\n\nt_undo_action requires canvas information in case we've deleted a canvas and\nthen undo its deletion which will in effect change its pointer (memory\nlocation). Then we need to call check_canvas_pointers to update all of the old\n(stale) undo actions to corresepond with the new memory location.\n\nWhat about abstractions? Once they are recreated  (e.g. after delete, followed\nby an undo) all undo actions (except for its deletion in the parent window should\nbe purged since abstraction's state will now default to its original (saved) state.\n\nTypes of undo data:\n0  - init data (start of the queue)\n1  - connect\n2  - disconnect\n3  - cut, clear & typing into objects\n4  - motion, including \"tidy up\" and stretching\n5  - paste & duplicate\n6  - apply\n7  - arrange (to front/back)\n8  - canvas apply\n9  - create\n10 - recreate\n11 - change font\n12 - meta: start undo sequence (undo sequences are undone/redone atomically)\n13 - meta: end undo sequence\n14 - internal object state\n*/\n\n#include \"m_pd.h\"\n\ntypedef enum\n{\n    UNDO_INIT = 0,\n    UNDO_CONNECT,      /* 1: OK */\n    UNDO_DISCONNECT,   /* 2: OK */\n    UNDO_CUT,          /* 3: OK */\n    UNDO_MOTION,       /* 4: OK */\n    UNDO_PASTE,        /* 5: OK */\n    UNDO_APPLY,        /* 6: how to test? */\n    UNDO_ARRANGE,      /* 7: FIXME: skipped */\n    UNDO_CANVAS_APPLY, /* 8: OK */\n    UNDO_CREATE,       /* 9: OK */\n    UNDO_RECREATE,     /* 10: OK */\n    UNDO_FONT,         /* 11: OK */\n    UNDO_SEQUENCE_START, /* 12 start an atomic sequence of undo actions*/\n    UNDO_SEQUENCE_END,   /* 13 end an atomic sequence of undo actions */\n    UNDO_OBJECT_STATE,  /* 14: internal object state: t_atom-list to send to the object */\n\n    UNDO_LAST\n} t_undo_type;\n\nstruct _undo_action\n{\n\tt_canvas *x;\t\t\t\t/* canvas undo is associated with */\n\tt_undo_type type;\t\t\t/* defines what kind of data container it is */\n\tvoid *data;\t\t\t\t\t/* each action will have a different data container */\n\tchar *name;\t\t\t\t\t/* name of current action */\n\tstruct _undo_action *prev;\t/* previous undo action */\n\tstruct _undo_action *next;\t/* next undo action */\n};\n\n#ifndef t_undo_action\n#define t_undo_action struct _undo_action\n#endif\n\nstruct _undo\n{\n    t_undo_action *u_queue;\n    t_undo_action *u_last;\n    void *u_cleanstate; /* pointer to non-dirty state */\n    int u_doing; /* currently undoing */\n};\n#define t_undo struct _undo\n\n\nEXTERN t_undo*canvas_undo_get(t_canvas*x);\n\nEXTERN void canvas_undo_cleardirty(t_canvas *x);\n\nEXTERN t_undo_action *canvas_undo_init(t_canvas *x);\nEXTERN t_undo_action *canvas_undo_add(t_canvas *x,\n\tt_undo_type type, const char *name, void *data);\nEXTERN void canvas_undo_undo(t_canvas *x);\nEXTERN void canvas_undo_redo(t_canvas *x);\nEXTERN void canvas_undo_rebranch(t_canvas *x);\nEXTERN void canvas_undo_check_canvas_pointers(t_canvas *x);\nEXTERN void canvas_undo_purge_abstraction_actions(t_canvas *x);\nEXTERN void canvas_undo_free(t_canvas *x);\n\n/* --------- 1. connect ---------- */\n\nEXTERN void *canvas_undo_set_connect(t_canvas *x,\n    int index1, int outno, int index2, int inno);\nEXTERN int canvas_undo_connect(t_canvas *x, void *z, int action);\n\n/* --------- 2. disconnect ------- */\n\nEXTERN void *canvas_undo_set_disconnect(t_canvas *x,\n    int index1, int outno, int index2, int inno);\nEXTERN int canvas_undo_disconnect(t_canvas *x, void *z, int action);\n\n/* --------- 3. cut -------------- */\n\nEXTERN void *canvas_undo_set_cut(t_canvas *x,\n    int mode);\nEXTERN int canvas_undo_cut(t_canvas *x, void *z, int action);\n\n/* --------- 4. move ------------- */\n\nEXTERN void *canvas_undo_set_move(t_canvas *x, int selected);\nEXTERN int canvas_undo_move(t_canvas *x, void *z, int action);\n\n/* --------- 5. paste ------------ */\n\nEXTERN void *canvas_undo_set_paste(t_canvas *x,\n    int offset, int duplicate, int d_offset);\nEXTERN int canvas_undo_paste(t_canvas *x, void *z, int action);\n\n/* --------- 6. apply ------------ */\n\nEXTERN void *canvas_undo_set_apply(t_canvas *x,\n    int n);\nEXTERN int canvas_undo_apply(t_canvas *x, void *z, int action);\n\n/* --------- 7. arrange ---------- */\n\nEXTERN void *canvas_undo_set_arrange(t_canvas *x,\n    t_gobj *obj, int newindex);\nEXTERN int canvas_undo_arrange(t_canvas *x, void *z, int action);\n\n/* --------- 8. canvas apply ----- */\n\nEXTERN void *canvas_undo_set_canvas(t_canvas *x);\nEXTERN int canvas_undo_canvas_apply(t_canvas *x, void *z, int action);\n\n/* --------- 9. create ----------- */\n\nEXTERN void *canvas_undo_set_create(t_canvas *x);\nEXTERN int canvas_undo_create(t_canvas *x, void *z, int action);\n\n/* --------- 10. recreate -------- */\n\nEXTERN void *canvas_undo_set_recreate(t_canvas *x,\n    t_gobj *y, int old_pos);\nEXTERN int canvas_undo_recreate(t_canvas *x, void *z, int action);\n\n/* --------- 11. font ------------ */\n\nEXTERN void *canvas_undo_set_font(t_canvas *x,\n    int font, t_float realresize, int whichresize);\nEXTERN int canvas_undo_font(t_canvas *x, void *z, int action);\n\n/* ------------------------------- */\n\n#endif /* __g_undo_h_ */\n"
  },
  {
    "path": "libs/libpd/pure-data/src/g_vumeter.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution. */\n\n/* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */\n/* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */\n\n\n#include <string.h>\n#include <stdio.h>\n#include \"m_pd.h\"\n\n#include \"g_all_guis.h\"\n\n#define HMARGIN 1\n#define VMARGIN 2\n#define PEAKHEIGHT 10\n\n/* ----- vu  gui-peak- & rms- vu-meter-display ---------- */\n\nt_widgetbehavior vu_widgetbehavior;\nstatic t_class *vu_class;\n\n/* widget helper functions */\n\nstatic void vu_update_rms(t_vu *x, t_glist *glist)\n{\n    if(glist_isvisible(glist))\n    {\n        const int zoom = IEMGUI_ZOOM(x);\n        int w4 = x->x_gui.x_w / 4, off = text_ypix(&x->x_gui.x_obj, glist) - zoom;\n        int xpos = text_xpix(&x->x_gui.x_obj, glist);\n        int quad1 = xpos + w4 - zoom, quad3 = xpos + x->x_gui.x_w - w4 + zoom;\n        char tag[128];\n\n        sprintf(tag, \"%pRCOVER\", x);\n        pdgui_vmess(0, \"crs iiii\", glist_getcanvas(glist), \"coords\", tag,\n             quad1, off, quad3,\n             off + (x->x_led_size+1)*zoom*(IEM_VU_STEPS-x->x_rms));\n    }\n}\n\nstatic void vu_update_peak(t_vu *x, t_glist *glist)\n{\n    t_canvas *canvas = glist_getcanvas(glist);\n\n    if(glist_isvisible(glist))\n    {\n        const int zoom = IEMGUI_ZOOM(x);\n        int xpos = text_xpix(&x->x_gui.x_obj, glist);\n        int ypos = text_ypix(&x->x_gui.x_obj, glist);\n        char tag[128];\n        sprintf(tag, \"%pPLED\", x);\n\n        if(x->x_peak)\n        {\n            int i = iemgui_vu_col[x->x_peak];\n            int j = ypos +\n                   (x->x_led_size+1)*zoom*(IEM_VU_STEPS+1-x->x_peak) -\n                   (x->x_led_size+1)*zoom/2;\n\n            pdgui_vmess(0, \"crs iiii\", canvas, \"coords\", tag,\n                xpos, j, xpos + x->x_gui.x_w + zoom, j);\n            pdgui_vmess(0, \"crs rk\", canvas, \"itemconfigure\", tag, \"-fill\",\n                iemgui_color_hex[i]);\n        }\n        else\n        {\n            int mid = xpos + x->x_gui.x_w/2;\n            int pkh = PEAKHEIGHT * zoom;\n\n            pdgui_vmess(0, \"crs rk\", canvas, \"itemconfigure\", tag, \"-fill\",\n                x->x_gui.x_bcol);\n            pdgui_vmess(0, \"crs iiii\", canvas, \"coords\", tag,\n                mid, ypos + pkh, mid, ypos + pkh);\n        }\n    }\n}\n\nstatic void vu_draw_update(t_gobj *client, t_glist *glist);\n\nstatic void vu_draw_io(t_vu* x, t_glist* glist, int old_snd_rcv_flags)\n{\n    const int zoom = IEMGUI_ZOOM(x);\n    t_canvas *canvas = glist_getcanvas(glist);\n    int xpos = text_xpix(&x->x_gui.x_obj, glist);\n    int ypos = text_ypix(&x->x_gui.x_obj, glist);\n    int hmargin = HMARGIN * zoom, vmargin = VMARGIN * zoom;\n    int iow = IOWIDTH * zoom, ioh = IEM_GUI_IOHEIGHT * zoom;\n    int snd_able = x->x_gui.x_fsf.x_snd_able || x->x_gui.x_fsf.x_rcv_able;\n    char tag_object[128], tag_label[128], tag[128], tag_n[128];\n    char *tags[] = {tag_object, tag_n, tag};\n\n    (void)old_snd_rcv_flags;\n\n    sprintf(tag_object, \"%pOBJ\", x);\n    sprintf(tag_label, \"%pLABEL\", x);\n\n    /* re-create outlets */\n    sprintf(tag, \"%pOUT\", x);\n    pdgui_vmess(0, \"crs\", canvas, \"delete\", tag);\n    if(!snd_able)\n    {\n        sprintf(tag_n, \"%pOUT%d\", x, 0);\n        pdgui_vmess(0, \"crr iiii rs rS\", canvas, \"create\", \"rectangle\",\n            xpos - hmargin, ypos + x->x_gui.x_h + vmargin + zoom - ioh,\n            xpos - hmargin + iow, ypos + x->x_gui.x_h + vmargin,\n            \"-fill\", \"black\",\n            \"-tags\", 3, tags);\n\n        sprintf(tag_n, \"%pOUT%d\", x, 1);\n        pdgui_vmess(0, \"crr iiii rs rS\", canvas, \"create\", \"rectangle\",\n            xpos + x->x_gui.x_w + hmargin - iow, ypos + x->x_gui.x_h + vmargin + zoom - ioh,\n            xpos + x->x_gui.x_w + hmargin, ypos + x->x_gui.x_h + vmargin,\n            \"-fill\", \"black\",\n            \"-tags\", 3, tags);\n            /* keep label above outlets */\n        pdgui_vmess(0, \"crss\", canvas, \"lower\", tag, tag_label);\n    }\n\n    sprintf(tag, \"%pIN\", x);\n    pdgui_vmess(0, \"crs\", canvas, \"delete\", tag);\n    if(!x->x_gui.x_fsf.x_rcv_able)\n    {\n        sprintf(tag_n, \"%pIN%d\", x, 0);\n        pdgui_vmess(0, \"crr iiii rs rS\", canvas, \"create\", \"rectangle\",\n            xpos - hmargin, ypos - vmargin,\n            xpos - hmargin + iow, ypos - vmargin - zoom + ioh,\n            \"-fill\", \"black\",\n            \"-tags\", 3, tags);\n\n        sprintf(tag_n, \"%pIN%d\", x, 1);\n        pdgui_vmess(0, \"crr iiii rs rS\", canvas, \"create\", \"rectangle\",\n            xpos + x->x_gui.x_w + hmargin - iow, ypos - vmargin,\n            xpos + x->x_gui.x_w + hmargin, ypos - vmargin - zoom + ioh,\n            \"-fill\", \"black\",\n            \"-tags\", 3, tags);\n            /* keep label above inlets */\n        pdgui_vmess(0, \"crss\", canvas, \"lower\", tag, tag_label);\n    }\n}\n\nstatic void vu_draw_config(t_vu* x, t_glist* glist)\n{\n    const int zoom = IEMGUI_ZOOM(x);\n    t_iemgui *iemgui = &x->x_gui;\n    t_canvas *canvas = glist_getcanvas(glist);\n    int xpos = text_xpix(&x->x_gui.x_obj, glist);\n    int ypos = text_ypix(&x->x_gui.x_obj, glist);\n    int hmargin = HMARGIN * zoom, vmargin = VMARGIN * zoom;\n    int iow = IOWIDTH * zoom, ioh = IEM_GUI_IOHEIGHT * zoom;\n    int ledw = x->x_led_size * zoom;\n    int fs = x->x_gui.x_fontsize * zoom;\n    int w4 = x->x_gui.x_w/4, mid = xpos + x->x_gui.x_w/2,\n        quad1 = xpos + w4 + zoom;\n    int quad3 = xpos + x->x_gui.x_w - w4,\n        end = xpos + x->x_gui.x_w + 4*zoom;\n    int k1 = (x->x_led_size+1)*zoom, k2 = IEM_VU_STEPS+1, k3 = k1/2;\n    int led_col, yyy, k4 = ypos - k3;\n    int i;\n    char tag[128];\n    t_atom fontatoms[3];\n    SETSYMBOL(fontatoms+0, gensym(iemgui->x_font));\n    SETFLOAT (fontatoms+1, -iemgui->x_fontsize*zoom);\n    SETSYMBOL(fontatoms+2, gensym(sys_fontweight));\n\n    sprintf(tag, \"%pBASE\", x);\n    pdgui_vmess(0, \"crs iiii\", canvas, \"coords\", tag,\n        xpos - hmargin, ypos - vmargin,\n        xpos+x->x_gui.x_w + hmargin, ypos+x->x_gui.x_h + vmargin);\n    pdgui_vmess(0, \"crs ri rk\", canvas, \"itemconfigure\", tag,\n        \"-width\", zoom,\n        \"-fill\", x->x_gui.x_bcol);\n\n    sprintf(tag, \"%pSCALE\", x);\n    pdgui_vmess(0, \"crs rA rk\", canvas, \"itemconfigure\", tag,\n        \"-font\", 3, fontatoms,\n        \"-fill\", x->x_gui.x_fsf.x_selected ? IEM_GUI_COLOR_SELECTED : x->x_gui.x_lcol);\n    sprintf(tag, \"%pRLED\", x);\n    pdgui_vmess(0, \"crs ri\", canvas, \"itemconfigure\", tag, \"-width\", ledw);\n\n    for(i = 1; i <= IEM_VU_STEPS + 1; i++)\n    {\n        sprintf(tag, \"%pRLED%d\", x, i);\n        led_col = iemgui_vu_col[i];\n        yyy = k4 + k1*(k2-i);\n        pdgui_vmess(0, \"crs iiii\", canvas, \"coords\", tag,\n            quad1, yyy, quad3, yyy);\n        pdgui_vmess(0, \"crs rk\", canvas, \"itemconfigure\", tag,\n            \"-fill\", iemgui_color_hex[led_col]);\n\n        sprintf(tag, \"%pSCALE%d\", x, i);\n        pdgui_vmess(0, \"crs ii\", canvas, \"coords\", tag,\n            end, yyy + k3);\n        if((i+2) & 3)\n            pdgui_vmess(0, \"crs rs\", canvas, \"itemconfigure\", tag,\n            \"-text\", (x->x_scale)?iemgui_vu_scale_str[i]:\"\");\n    }\n\n    i = IEM_VU_STEPS + 1;\n    yyy = k4 + k1 * (k2 - i);\n\n        /* this was already set in the loop above, overwritten here */\n    sprintf(tag, \"%pSCALE%d\", x, i);\n    pdgui_vmess(0, \"crs ii\", canvas, \"coords\", tag,\n        end, yyy + k3);\n\n    pdgui_vmess(0, \"crs rs\", canvas, \"itemconfigure\", tag,\n        \"-text\", (x->x_scale)?iemgui_vu_scale_str[i]:\"\");\n\n    sprintf(tag, \"%pRCOVER\", x);\n    pdgui_vmess(0, \"crs iiii\", canvas, \"coords\", tag,\n        quad1 - zoom, ypos - zoom,\n        quad3 + zoom, ypos - zoom + k1*IEM_VU_STEPS);\n    pdgui_vmess(0, \"crs rk rk\", canvas, \"itemconfigure\", tag,\n        \"-fill\", x->x_gui.x_bcol, \"-outline\", x->x_gui.x_bcol);\n\n    sprintf(tag, \"%pPLED\", x);\n    pdgui_vmess(0, \"crs iiii\", canvas, \"coords\", tag,\n        mid, ypos + PEAKHEIGHT * zoom,\n        mid, ypos + PEAKHEIGHT * zoom);\n    pdgui_vmess(0, \"crs ri rk\", canvas, \"itemconfigure\", tag,\n        \"-width\", ledw+zoom, /* peak LED is slightly fatter */\n        \"-fill\", x->x_gui.x_bcol);\n\n    sprintf(tag, \"%pLABEL\", x);\n    pdgui_vmess(0, \"crs ii\", canvas, \"coords\", tag,\n        xpos+x->x_gui.x_ldx * zoom, ypos+x->x_gui.x_ldy * zoom);\n    pdgui_vmess(0, \"crs rA rk\", canvas, \"itemconfigure\", tag,\n        \"-font\", 3, fontatoms,\n        \"-fill\", x->x_gui.x_fsf.x_selected ? IEM_GUI_COLOR_SELECTED : x->x_gui.x_lcol);\n    iemgui_dolabel(x, &x->x_gui, x->x_gui.x_lab, 1);\n\n    x->x_updaterms = x->x_updatepeak = 1;\n}\n\nstatic void vu_draw_new(t_vu *x, t_glist *glist)\n{\n    t_canvas *canvas = glist_getcanvas(glist);\n    char tag_object[128], tag[128], tag_n[128];\n    char *tags[] = {tag_object, tag, tag_n, \"text\"};\n    int i;\n\n    sprintf(tag_object, \"%pOBJ\", x);\n\n    sprintf(tag, \"%pBASE\", x);\n    pdgui_vmess(0, \"crr iiii rS\", canvas, \"create\", \"rectangle\",\n        0, 0, 0, 0, \"-tags\", 2, tags);\n\n    for(i = 1; i < IEM_VU_STEPS+1; i++)\n    {\n        sprintf(tag, \"%pRLED\", x);\n        sprintf(tag_n, \"%pRLED%d\", x, i);\n        pdgui_vmess(0, \"crr iiii rS\", canvas, \"create\", \"line\",\n            0, 0, 0, 0, \"-tags\", 3, tags);\n\n        sprintf(tag, \"%pSCALE\", x);\n        sprintf(tag_n, \"%pSCALE%d\", x, i);\n        pdgui_vmess(0, \"crr ii rs rS\", canvas, \"create\", \"text\",\n            0, 0, \"-anchor\", \"w\", \"-tags\", 3, tags);\n    }\n    /* and a final scale item */\n    sprintf(tag, \"%pSCALE\", x);\n    sprintf(tag_n, \"%pSCALE%d\", x, i);\n    pdgui_vmess(0, \"crr ii rs rS\", canvas, \"create\", \"text\",\n        0, 0, \"-anchor\", \"w\", \"-tags\", 3, tags);\n\n    sprintf(tag, \"%pRCOVER\", x);\n    pdgui_vmess(0, \"crr iiii rS\", canvas, \"create\", \"rectangle\",\n        0, 0, 0, 0, \"-tags\", 2, tags);\n\n    sprintf(tag, \"%pPLED\", x);\n    pdgui_vmess(0, \"crr iiii rS\", canvas, \"create\", \"line\",\n        0, 0, 0, 0, \"-tags\", 2, tags);\n\n    sprintf(tag, \"%pLABEL\", x);\n    sprintf(tag_n, \"label\");\n    pdgui_vmess(0, \"crr ii rs rS\", canvas, \"create\", \"text\",\n        0, 0, \"-anchor\", \"w\", \"-tags\", 4, tags);\n\n    vu_draw_config(x, glist);\n    vu_draw_io(x, glist, 0);\n    sys_queuegui(x, x->x_gui.x_glist, vu_draw_update);\n}\n\nstatic void vu_draw_select(t_vu* x,t_glist* glist)\n{\n    t_canvas *canvas = glist_getcanvas(glist);\n    int col = IEM_GUI_COLOR_NORMAL;\n    int lcol = x->x_gui.x_lcol;\n    char tag[128];\n    if(x->x_gui.x_fsf.x_selected)\n        col = lcol = IEM_GUI_COLOR_SELECTED;\n\n    sprintf(tag, \"%pBASE\", x);\n    pdgui_vmess(0, \"crs rk\", canvas, \"itemconfigure\", tag, \"-outline\", col);\n    sprintf(tag, \"%pSCALE\", x);\n    pdgui_vmess(0, \"crs rk\", canvas, \"itemconfigure\", tag, \"-fill\", lcol);\n    sprintf(tag, \"%pLABEL\", x);\n    pdgui_vmess(0, \"crs rk\", canvas, \"itemconfigure\", tag, \"-fill\", lcol);\n}\n\nstatic void vu_draw_update(t_gobj *client, t_glist *glist)\n{\n    t_vu *x = (t_vu *)client;\n    if (x->x_updaterms)\n    {\n        vu_update_rms(x, glist);\n        x->x_updaterms = 0;\n    }\n    if (x->x_updatepeak)\n    {\n        vu_update_peak(x, glist);\n        x->x_updatepeak = 0;\n    }\n}\n\n/* ------------------------ vu widgetbehaviour----------------------------- */\n\nstatic void vu_getrect(t_gobj *z, t_glist *glist,\n                       int *xp1, int *yp1, int *xp2, int *yp2)\n{\n    t_vu* x = (t_vu*)z;\n    int hmargin = HMARGIN * IEMGUI_ZOOM(x), vmargin = VMARGIN * IEMGUI_ZOOM(x);\n\n    *xp1 = text_xpix(&x->x_gui.x_obj, glist) - hmargin;\n    *yp1 = text_ypix(&x->x_gui.x_obj, glist) - vmargin;\n    *xp2 = *xp1 + x->x_gui.x_w + hmargin*2;\n    *yp2 = *yp1 + x->x_gui.x_h + vmargin*2;\n}\n\nstatic void vu_save(t_gobj *z, t_binbuf *b)\n{\n    t_vu *x = (t_vu *)z;\n    t_symbol *bflcol[3];\n    t_symbol *srl[3];\n\n    iemgui_save(&x->x_gui, srl, bflcol);\n    binbuf_addv(b, \"ssiisiissiiiissii\", gensym(\"#X\"), gensym(\"obj\"),\n                (int)x->x_gui.x_obj.te_xpix, (int)x->x_gui.x_obj.te_ypix,\n                gensym(\"vu\"), x->x_gui.x_w/IEMGUI_ZOOM(x), x->x_gui.x_h/IEMGUI_ZOOM(x),\n                srl[1], srl[2],\n                x->x_gui.x_ldx, x->x_gui.x_ldy,\n                iem_fstyletoint(&x->x_gui.x_fsf), x->x_gui.x_fontsize,\n                bflcol[0], bflcol[2], x->x_scale,\n                iem_symargstoint(&x->x_gui.x_isa));\n    binbuf_addv(b, \";\");\n}\n\nvoid vu_check_height(t_vu *x, int h)\n{\n    int n;\n\n    n = h / IEM_VU_STEPS;\n    if(n < IEM_VU_MINSIZE)\n        n = IEM_VU_MINSIZE;\n    x->x_led_size = n - 1;\n    x->x_gui.x_h = (IEM_VU_STEPS * n) * IEMGUI_ZOOM(x);\n}\n\nstatic void vu_scale(t_vu *x, t_floatarg fscale)\n{\n    x->x_scale = !!(int)fscale;\n    if(glist_isvisible(x->x_gui.x_glist))\n        vu_draw_config(x, x->x_gui.x_glist);\n}\n\nstatic void vu_properties(t_gobj *z, t_glist *owner)\n{\n    t_vu *x = (t_vu *)z;\n\n    iemgui_new_dialog(x, &x->x_gui, \"vu\",\n                      x->x_gui.x_w/IEMGUI_ZOOM(x), IEM_SL_MINSIZE,\n                      x->x_gui.x_h/IEMGUI_ZOOM(x), IEM_GUI_MINSIZE,\n                      0, 0,\n                      0,\n                      x->x_scale, \"no scale\", \"scale\",\n                      0, -1, -1);\n}\n\nstatic void vu_dialog(t_vu *x, t_symbol *s, int argc, t_atom *argv)\n{\n    t_symbol *srl[3];\n    int w = (int)atom_getfloatarg(0, argc, argv);\n    int h = (int)atom_getfloatarg(1, argc, argv);\n    int scale = (int)atom_getfloatarg(4, argc, argv);\n    int sr_flags;\n    t_atom undo[18];\n    iemgui_setdialogatoms(&x->x_gui, 18, undo);\n    SETFLOAT(undo+2, 0.01);\n    SETFLOAT(undo+3, 1);\n    SETFLOAT(undo+4, x->x_scale);\n    SETFLOAT(undo+5, -1);\n    SETSYMBOL(undo+15, gensym(\"none\"));\n    pd_undo_set_objectstate(x->x_gui.x_glist, (t_pd*)x, gensym(\"dialog\"),\n                            18, undo,\n                            argc, argv);\n\n    sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv);\n    x->x_gui.x_fsf.x_snd_able = 0;\n    x->x_gui.x_isa.x_loadinit = 0;\n    x->x_gui.x_w = iemgui_clip_size(w) * IEMGUI_ZOOM(x);\n    vu_check_height(x, h);\n    if(scale != 0)\n        scale = 1;\n    vu_scale(x, (t_float)scale);\n    iemgui_size(x, &x->x_gui);\n}\n\nstatic void vu_size(t_vu *x, t_symbol *s, int ac, t_atom *av)\n{\n    x->x_gui.x_w = iemgui_clip_size((int)atom_getfloatarg(0, ac, av)) * IEMGUI_ZOOM(x);\n    if(ac > 1)\n        vu_check_height(x, (int)atom_getfloatarg(1, ac, av));\n    iemgui_size((void *)x, &x->x_gui);\n}\n\nstatic void vu_delta(t_vu *x, t_symbol *s, int ac, t_atom *av)\n{iemgui_delta((void *)x, &x->x_gui, s, ac, av);}\n\nstatic void vu_pos(t_vu *x, t_symbol *s, int ac, t_atom *av)\n{iemgui_pos((void *)x, &x->x_gui, s, ac, av);}\n\nstatic void vu_color(t_vu *x, t_symbol *s, int ac, t_atom *av)\n{iemgui_color((void *)x, &x->x_gui, s, ac, av);}\n\nstatic void vu_receive(t_vu *x, t_symbol *s)\n{iemgui_receive(x, &x->x_gui, s);}\n\nstatic void vu_label(t_vu *x, t_symbol *s)\n{iemgui_label((void *)x, &x->x_gui, s);}\n\nstatic void vu_label_pos(t_vu *x, t_symbol *s, int ac, t_atom *av)\n{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);}\n\nstatic void vu_label_font(t_vu *x, t_symbol *s, int ac, t_atom *av)\n{\n    iemgui_label_font((void *)x, &x->x_gui, s, ac, av);\n    (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_CONFIG);\n}\n\nstatic void vu_float(t_vu *x, t_floatarg rms)\n{\n    int i;\n    int old = x->x_rms;\n    if(rms <= IEM_VU_MINDB)\n        x->x_rms = 0;\n    else if(rms >= IEM_VU_MAXDB)\n        x->x_rms = IEM_VU_STEPS;\n    else\n    {\n        int i = (int)(2.0*(rms + IEM_VU_OFFSET));\n        x->x_rms = iemgui_vu_db2i[i];\n    }\n    i = (int)(100.0*rms + 10000.5);\n    rms = 0.01*(t_float)(i - 10000);\n    x->x_fr = rms;\n    outlet_float(x->x_out_rms, rms);\n    x->x_updaterms = 1;\n    if(x->x_rms != old)\n        sys_queuegui(x, x->x_gui.x_glist, vu_draw_update);\n}\n\nstatic void vu_ft1(t_vu *x, t_floatarg peak)\n{\n    int i;\n    int old = x->x_peak;\n    if(peak <= IEM_VU_MINDB)\n        x->x_peak = 0;\n    else if(peak >= IEM_VU_MAXDB)\n        x->x_peak = IEM_VU_STEPS;\n    else\n    {\n        int i = (int)(2.0*(peak + IEM_VU_OFFSET));\n        x->x_peak = iemgui_vu_db2i[i];\n    }\n    i = (int)(100.0*peak + 10000.5);\n    peak = 0.01*(t_float)(i - 10000);\n    x->x_fp = peak;\n    x->x_updatepeak = 1;\n    if(x->x_peak != old)\n        sys_queuegui(x, x->x_gui.x_glist, vu_draw_update);\n    outlet_float(x->x_out_peak, peak);\n}\n\nstatic void vu_bang(t_vu *x)\n{\n    outlet_float(x->x_out_peak, x->x_fp);\n    outlet_float(x->x_out_rms, x->x_fr);\n    x->x_updaterms = x->x_updatepeak = 1;\n    sys_queuegui(x, x->x_gui.x_glist, vu_draw_update);\n}\n\nstatic void *vu_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_vu *x = (t_vu *)iemgui_new(vu_class);\n    int w = IEM_GUI_DEFAULTSIZE, h = IEM_VU_STEPS*IEM_VU_DEFAULTSIZE * IEM_GUI_DEFAULTSIZE_SCALE;\n    int ldx = -1, ldy = -8 * IEM_GUI_DEFAULTSIZE_SCALE, f = 0, scale = 1;\n    int fs = x->x_gui.x_fontsize;\n    int ftbreak = IEM_BNG_DEFAULTBREAKFLASHTIME, fthold = IEM_BNG_DEFAULTHOLDFLASHTIME;\n    char str[144];\n\n    IEMGUI_SETDRAWFUNCTIONS(x, vu);\n\n    x->x_gui.x_bcol = 0x404040;\n\n    if((argc >= 11)&&IS_A_FLOAT(argv,0)&&IS_A_FLOAT(argv,1)\n       &&(IS_A_SYMBOL(argv,2)||IS_A_FLOAT(argv,2))\n       &&(IS_A_SYMBOL(argv,3)||IS_A_FLOAT(argv,3))\n       &&IS_A_FLOAT(argv,4)&&IS_A_FLOAT(argv,5)\n       &&IS_A_FLOAT(argv,6)&&IS_A_FLOAT(argv,7)\n       &&IS_A_FLOAT(argv,10))\n    {\n        w = (int)atom_getfloatarg(0, argc, argv);\n        h = (int)atom_getfloatarg(1, argc, argv);\n        iemgui_new_getnames(&x->x_gui, 1, argv);\n        x->x_gui.x_snd_unexpanded = x->x_gui.x_snd = gensym(\"nosndno\"); /*no send*/\n        ldx = (int)atom_getfloatarg(4, argc, argv);\n        ldy = (int)atom_getfloatarg(5, argc, argv);\n        iem_inttofstyle(&x->x_gui.x_fsf, atom_getfloatarg(6, argc, argv));\n        fs = (int)atom_getfloatarg(7, argc, argv);\n        iemgui_all_loadcolors(&x->x_gui, argv+8, NULL, argv+9);\n        scale = (int)atom_getfloatarg(10, argc, argv);\n    }\n    else iemgui_new_getnames(&x->x_gui, 1, 0);\n    if((argc == 12)&&IS_A_FLOAT(argv,11))\n        iem_inttosymargs(&x->x_gui.x_isa, atom_getfloatarg(11, argc, argv));\n\n    x->x_gui.x_fsf.x_snd_able = 0;\n    x->x_gui.x_fsf.x_rcv_able = (0 != x->x_gui.x_rcv);\n    if (x->x_gui.x_fsf.x_font_style == 1)\n        strcpy(x->x_gui.x_font, \"helvetica\");\n    else if(x->x_gui.x_fsf.x_font_style == 2)\n        strcpy(x->x_gui.x_font, \"times\");\n    else { x->x_gui.x_fsf.x_font_style = 0;\n        strcpy(x->x_gui.x_font, sys_font); }\n    if(x->x_gui.x_fsf.x_rcv_able)\n        pd_bind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv);\n    x->x_gui.x_ldx = ldx;\n    x->x_gui.x_ldy = ldy;\n    x->x_gui.x_fontsize = (fs < 4)?4:fs;\n    x->x_gui.x_w = iemgui_clip_size(w);\n    vu_check_height(x, h);\n    if(scale != 0)\n        scale = 1;\n    x->x_scale = scale;\n    x->x_peak = 0;\n    x->x_rms = 0;\n    x->x_fp = -101.0;\n    x->x_fr = -101.0;\n    iemgui_verify_snd_ne_rcv(&x->x_gui);\n    inlet_new(&x->x_gui.x_obj, &x->x_gui.x_obj.ob_pd, &s_float, gensym(\"ft1\"));\n    x->x_out_rms = outlet_new(&x->x_gui.x_obj, &s_float);\n    x->x_out_peak = outlet_new(&x->x_gui.x_obj, &s_float);\n    x->x_gui.x_h /= IEMGUI_ZOOM(x); /* unzoom, to prevent double-application in iemgui_newzoom */\n    iemgui_newzoom(&x->x_gui);\n    return (x);\n}\n\nstatic void vu_free(t_vu *x)\n{\n    if(x->x_gui.x_fsf.x_rcv_able)\n        pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv);\n    pdgui_stub_deleteforkey(x);\n}\n\nvoid g_vumeter_setup(void)\n{\n    vu_class = class_new(gensym(\"vu\"), (t_newmethod)vu_new,\n        (t_method)vu_free, sizeof(t_vu), 0, A_GIMME, 0);\n    class_addbang(vu_class,vu_bang);\n    class_addfloat(vu_class,vu_float);\n    class_addmethod(vu_class, (t_method)vu_ft1,\n        gensym(\"ft1\"), A_FLOAT, 0);\n    class_addmethod(vu_class, (t_method)vu_dialog,\n        gensym(\"dialog\"), A_GIMME, 0);\n    class_addmethod(vu_class, (t_method)vu_size,\n        gensym(\"size\"), A_GIMME, 0);\n    class_addmethod(vu_class, (t_method)vu_scale,\n        gensym(\"scale\"), A_DEFFLOAT, 0);\n    class_addmethod(vu_class, (t_method)vu_delta,\n        gensym(\"delta\"), A_GIMME, 0);\n    class_addmethod(vu_class, (t_method)vu_pos,\n        gensym(\"pos\"), A_GIMME, 0);\n    class_addmethod(vu_class, (t_method)vu_color,\n        gensym(\"color\"), A_GIMME, 0);\n    class_addmethod(vu_class, (t_method)vu_receive,\n        gensym(\"receive\"), A_DEFSYM, 0);\n    class_addmethod(vu_class, (t_method)vu_label,\n        gensym(\"label\"), A_DEFSYM, 0);\n    class_addmethod(vu_class, (t_method)vu_label_pos,\n        gensym(\"label_pos\"), A_GIMME, 0);\n    class_addmethod(vu_class, (t_method)vu_label_font,\n        gensym(\"label_font\"), A_GIMME, 0);\n    class_addmethod(vu_class, (t_method)iemgui_zoom,\n        gensym(\"zoom\"), A_CANT, 0);\n    vu_widgetbehavior.w_getrectfn =    vu_getrect;\n    vu_widgetbehavior.w_displacefn =   iemgui_displace;\n    vu_widgetbehavior.w_selectfn =     iemgui_select;\n    vu_widgetbehavior.w_activatefn =   NULL;\n    vu_widgetbehavior.w_deletefn =     iemgui_delete;\n    vu_widgetbehavior.w_visfn =        iemgui_vis;\n    vu_widgetbehavior.w_clickfn =      NULL;\n    class_setwidget(vu_class,&vu_widgetbehavior);\n    class_setsavefn(vu_class, vu_save);\n    class_setpropertiesfn(vu_class, vu_properties);\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/m_atom.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n#include \"m_pd.h\"\n#include <stdio.h>\n#include <string.h>\n\n    /* convenience routines for checking and getting values of\n        atoms.  There's no \"pointer\" version since there's nothing\n        safe to return if there's an error. */\n\nt_float atom_getfloat(const t_atom *a)\n{\n    if (a->a_type == A_FLOAT) return (a->a_w.w_float);\n    else return (0);\n}\n\nt_int atom_getint(const t_atom *a)\n{\n    return (atom_getfloat(a));\n}\n\nt_symbol *atom_getsymbol(const t_atom *a)  /* LATER think about this more carefully */\n{\n    if (a->a_type == A_SYMBOL) return (a->a_w.w_symbol);\n    else return (&s_float);\n}\n\nt_symbol *atom_gensym(const t_atom *a)  /* this works  better for graph labels */\n{\n    char buf[30];\n    if (a->a_type == A_SYMBOL) return (a->a_w.w_symbol);\n    else if (a->a_type == A_FLOAT)\n        sprintf(buf, \"%g\", a->a_w.w_float);\n    else strcpy(buf, \"???\");\n    return (gensym(buf));\n}\n\nt_float atom_getfloatarg(int which, int argc, const t_atom *argv)\n{\n    if (argc <= which) return (0);\n    argv += which;\n    if (argv->a_type == A_FLOAT) return (argv->a_w.w_float);\n    else return (0);\n}\n\nt_int atom_getintarg(int which, int argc, const t_atom *argv)\n{\n    return (atom_getfloatarg(which, argc, argv));\n}\n\nt_symbol *atom_getsymbolarg(int which, int argc, const t_atom *argv)\n{\n    if (argc <= which) return (&s_);\n    argv += which;\n    if (argv->a_type == A_SYMBOL) return (argv->a_w.w_symbol);\n    else return (&s_);\n}\n\n/* convert an atom into a string, in the reverse sense of binbuf_text (q.v.)\n* special attention is paid to symbols containing the special characters\n* ';', ',', '$', and '\\'; these are quoted with a preceding '\\', except that\n* the '$' only gets quoted if followed by a digit.\n*/\n\nvoid atom_string(const t_atom *a, char *buf, unsigned int bufsize)\n{\n    char tbuf[30];\n    switch(a->a_type)\n    {\n    case A_SEMI: strcpy(buf, \";\"); break;\n    case A_COMMA: strcpy(buf, \",\"); break;\n    case A_POINTER:\n        strcpy(buf, \"(pointer)\");\n        break;\n    case A_FLOAT:\n        sprintf(tbuf, \"%g\", a->a_w.w_float);\n        if (strlen(tbuf) < bufsize-1) strcpy(buf, tbuf);\n        else if (a->a_w.w_float < 0) strcpy(buf, \"-\");\n        else  strcpy(buf, \"+\");\n        break;\n    case A_SYMBOL:\n    case A_DOLLSYM:\n    {\n        const char *sp;\n        unsigned int len;\n        int quote;\n        for (sp = a->a_w.w_symbol->s_name, len = 0, quote = 0; *sp; sp++, len++)\n            if (*sp == ';' || *sp == ',' || *sp == '\\\\' || *sp == ' ' ||\n                (a->a_type == A_SYMBOL && *sp == '$' &&\n                    sp[1] >= '0' && sp[1] <= '9'))\n                        quote = 1;\n        if (quote)\n        {\n            char *bp = buf, *ep = buf + (bufsize-2);\n            sp = a->a_w.w_symbol->s_name;\n            while (bp < ep && *sp)\n            {\n                if (*sp == ';' || *sp == ',' || *sp == '\\\\' || *sp == ' ' ||\n                    (a->a_type == A_SYMBOL && *sp == '$' &&\n                        sp[1] >= '0' && sp[1] <= '9'))\n                            *bp++ = '\\\\';\n                *bp++ = *sp++;\n            }\n            if (*sp) *bp++ = '*';\n            *bp = 0;\n            /* post(\"quote %s -> %s\", a->a_w.w_symbol->s_name, buf); */\n        }\n        else\n        {\n            if (len < bufsize-1) strcpy(buf, a->a_w.w_symbol->s_name);\n            else\n            {\n                strncpy(buf, a->a_w.w_symbol->s_name, bufsize - 2);\n                strcpy(buf + (bufsize - 2), \"*\");\n            }\n        }\n    }\n        break;\n    case A_DOLLAR:\n        sprintf(buf, \"$%d\", a->a_w.w_index);\n        break;\n    default:\n        bug(\"atom_string\");\n    }\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/m_binbuf.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n\n#include <stdlib.h>\n#include \"m_pd.h\"\n#include \"s_stuff.h\"\n#include \"g_canvas.h\"\n#include <stdio.h>\n#include <errno.h>\n#ifdef HAVE_UNISTD_H\n#include <unistd.h>\n#endif\n#ifdef _WIN32\n#include <io.h>\n#endif\n#include <string.h>\n#include <stdarg.h>\n\n#include \"m_private_utils.h\"\n\nstruct _binbuf\n{\n    int b_n;\n    t_atom *b_vec;\n};\n\nt_binbuf *binbuf_new(void)\n{\n    t_binbuf *x = (t_binbuf *)t_getbytes(sizeof(*x));\n    x->b_n = 0;\n    x->b_vec = t_getbytes(0);\n    return (x);\n}\n\nvoid binbuf_free(t_binbuf *x)\n{\n    t_freebytes(x->b_vec, x->b_n * sizeof(*x->b_vec));\n    t_freebytes(x,  sizeof(*x));\n}\n\nt_binbuf *binbuf_duplicate(const t_binbuf *y)\n{\n    t_binbuf *x = (t_binbuf *)t_getbytes(sizeof(*x));\n    x->b_n = y->b_n;\n    x->b_vec = t_getbytes(x->b_n * sizeof(*x->b_vec));\n    memcpy(x->b_vec, y->b_vec, x->b_n * sizeof(*x->b_vec));\n    return (x);\n}\n\nvoid binbuf_clear(t_binbuf *x)\n{\n    x->b_vec = t_resizebytes(x->b_vec, x->b_n * sizeof(*x->b_vec), 0);\n    x->b_n = 0;\n}\n\n    /* convert text to a binbuf */\nvoid binbuf_text(t_binbuf *x, const char *text, size_t size)\n{\n    char buf[MAXPDSTRING+1], *bufp, *ebuf = buf+MAXPDSTRING;\n    const char *textp = text, *etext = text+size;\n    t_atom *ap;\n    int nalloc = 16, natom = 0;\n    binbuf_clear(x);\n    if (!binbuf_resize(x, nalloc)) return;\n    ap = x->b_vec;\n    while (1)\n    {\n        int type;\n            /* skip leading space */\n        while ((textp != etext) && (*textp == ' ' || *textp == '\\n'\n            || *textp == '\\r' || *textp == '\\t')) textp++;\n        if (textp == etext) break;\n        if (*textp == ';') SETSEMI(ap), textp++;\n        else if (*textp == ',') SETCOMMA(ap), textp++;\n        else\n        {\n                /* it's an atom other than a comma or semi */\n            char c;\n            int floatstate = 0, slash = 0, lastslash = 0, dollar = 0;\n            bufp = buf;\n            do\n            {\n                c = *bufp = *textp++;\n                lastslash = slash;\n                slash = (c == '\\\\');\n\n                if (floatstate >= 0)\n                {\n                    int digit = (c >= '0' && c <= '9'),\n                        dot = (c == '.'), minus = (c == '-'),\n                        plusminus = (minus || (c == '+')),\n                        expon = (c == 'e' || c == 'E');\n                    if (floatstate == 0)    /* beginning */\n                    {\n                        if (minus) floatstate = 1;\n                        else if (digit) floatstate = 2;\n                        else if (dot) floatstate = 3;\n                        else floatstate = -1;\n                    }\n                    else if (floatstate == 1)   /* got minus */\n                    {\n                        if (digit) floatstate = 2;\n                        else if (dot) floatstate = 3;\n                        else floatstate = -1;\n                    }\n                    else if (floatstate == 2)   /* got digits */\n                    {\n                        if (dot) floatstate = 4;\n                        else if (expon) floatstate = 6;\n                        else if (!digit) floatstate = -1;\n                    }\n                    else if (floatstate == 3)   /* got '.' without digits */\n                    {\n                        if (digit) floatstate = 5;\n                        else floatstate = -1;\n                    }\n                    else if (floatstate == 4)   /* got '.' after digits */\n                    {\n                        if (digit) floatstate = 5;\n                        else if (expon) floatstate = 6;\n                        else floatstate = -1;\n                    }\n                    else if (floatstate == 5)   /* got digits after . */\n                    {\n                        if (expon) floatstate = 6;\n                        else if (!digit) floatstate = -1;\n                    }\n                    else if (floatstate == 6)   /* got 'e' */\n                    {\n                        if (plusminus) floatstate = 7;\n                        else if (digit) floatstate = 8;\n                        else floatstate = -1;\n                    }\n                    else if (floatstate == 7)   /* got plus or minus */\n                    {\n                        if (digit) floatstate = 8;\n                        else floatstate = -1;\n                    }\n                    else if (floatstate == 8)   /* got digits */\n                    {\n                        if (!digit) floatstate = -1;\n                    }\n                }\n                if (!lastslash && c == '$' && (textp != etext &&\n                    textp[0] >= '0' && textp[0] <= '9'))\n                        dollar = 1;\n                if (!slash) bufp++;\n                else if (lastslash)\n                {\n                    bufp++;\n                    slash = 0;\n                }\n            }\n            while (textp != etext && bufp != ebuf &&\n                (slash || (*textp != ' ' && *textp != '\\n' && *textp != '\\r'\n                    && *textp != '\\t' &&*textp != ',' && *textp != ';')));\n            *bufp = 0;\n#if 0\n            post(\"binbuf_text: buf %s\", buf);\n#endif\n            if (floatstate == 2 || floatstate == 4 || floatstate == 5 ||\n                floatstate == 8)\n                    SETFLOAT(ap, atof(buf));\n                /* LATER try to figure out how to mix \"$\" and \"\\$\" correctly;\n                here, the backslashes were already stripped so we assume all\n                \"$\" chars are real dollars.  In fact, we only know at least one\n                was. */\n            else if (dollar)\n            {\n                if (buf[0] != '$')\n                    dollar = 0;\n                for (bufp = buf+1; *bufp; bufp++)\n                    if (*bufp < '0' || *bufp > '9')\n                        dollar = 0;\n                if (dollar)\n                    SETDOLLAR(ap, atoi(buf+1));\n                else SETDOLLSYM(ap, gensym(buf));\n            }\n            else SETSYMBOL(ap, gensym(buf));\n        }\n        ap++;\n        natom++;\n        if (natom == nalloc)\n        {\n            if (!binbuf_resize(x, nalloc*2)) break;\n            nalloc = nalloc * 2;\n            ap = x->b_vec + natom;\n        }\n        if (textp == etext) break;\n    }\n    /* reallocate the vector to exactly the right size */\n    binbuf_resize(x, natom);\n}\n\n    /* convert a binbuf to text; no null termination. */\nvoid binbuf_gettext(const t_binbuf *x, char **bufp, int *lengthp)\n{\n    char *buf = getbytes(0), *newbuf;\n    int length = 0;\n    char string[MAXPDSTRING];\n    const t_atom *ap;\n    int indx;\n\n    for (ap = x->b_vec, indx = x->b_n; indx--; ap++)\n    {\n        int newlength;\n        if ((ap->a_type == A_SEMI || ap->a_type == A_COMMA) &&\n                length && buf[length-1] == ' ') length--;\n        atom_string(ap, string, MAXPDSTRING);\n        newlength = length + (int)strlen(string) + 1;\n        if (!(newbuf = resizebytes(buf, length, newlength))) break;\n        buf = newbuf;\n        strcpy(buf + length, string);\n        length = newlength;\n        if (ap->a_type == A_SEMI) buf[length-1] = '\\n';\n        else buf[length-1] = ' ';\n    }\n    if (length && buf[length-1] == ' ')\n    {\n        if ((newbuf = t_resizebytes(buf, length, length-1)))\n        {\n            buf = newbuf;\n            length--;\n        }\n    }\n    *bufp = buf;\n    *lengthp = length;\n}\n\n/* LATER improve the out-of-space behavior below.  Also fix this so that\nwriting to file doesn't buffer everything together. */\n\nvoid binbuf_add(t_binbuf *x, int argc, const t_atom *argv)\n{\n    int previoussize = x->b_n;\n    int newsize = previoussize + argc, i;\n    t_atom *ap;\n\n    if (!binbuf_resize(x, newsize))\n    {\n        pd_error(0, \"binbuf_addmessage: out of space\");\n        return;\n    }\n#if 0\n    startpost(\"binbuf_add: \");\n    postatom(argc, argv);\n    endpost();\n#endif\n    for (ap = x->b_vec + previoussize, i = argc; i--; ap++)\n        *ap = *(argv++);\n}\n\n#define MAXADDMESSV 100\nvoid binbuf_addv(t_binbuf *x, const char *fmt, ...)\n{\n    va_list ap;\n    t_atom arg[MAXADDMESSV], *at =arg;\n    int nargs = 0;\n    const char *fp = fmt;\n\n    va_start(ap, fmt);\n    while (1)\n    {\n        if (nargs >= MAXADDMESSV)\n        {\n            pd_error(0, \"binbuf_addmessv: only %d allowed\", MAXADDMESSV);\n            break;\n        }\n        switch(*fp++)\n        {\n        case 'i': SETFLOAT(at, va_arg(ap, int)); break;\n        case 'f': SETFLOAT(at, va_arg(ap, double)); break;\n        case 's': SETSYMBOL(at, va_arg(ap, t_symbol *)); break;\n        case ';': SETSEMI(at); break;\n        case ',': SETCOMMA(at); break;\n        default: goto done;\n        }\n        at++;\n        nargs++;\n    }\ndone:\n    va_end(ap);\n    binbuf_add(x, nargs, arg);\n}\n\n/* add a binbuf to another one for saving.  Semicolons and commas go to\nsymbols \";\", \"'\",; and inside symbols, characters ';', ',' and '$' get\nescaped.  LATER also figure out about escaping white space */\n\nvoid binbuf_addbinbuf(t_binbuf *x, const t_binbuf *y)\n{\n    t_binbuf *z = binbuf_new();\n    int i, fixit;\n    t_atom *ap;\n    binbuf_add(z, y->b_n, y->b_vec);\n    for (i = 0, ap = z->b_vec; i < z->b_n; i++, ap++)\n    {\n        char tbuf[MAXPDSTRING];\n        const char *s;\n        switch (ap->a_type)\n        {\n        case A_FLOAT:\n            break;\n        case A_SEMI:\n            SETSYMBOL(ap, gensym(\";\"));\n            break;\n        case A_COMMA:\n            SETSYMBOL(ap, gensym(\",\"));\n            break;\n        case A_DOLLAR:\n            sprintf(tbuf, \"$%d\", ap->a_w.w_index);\n            SETSYMBOL(ap, gensym(tbuf));\n            break;\n        case A_DOLLSYM:\n            atom_string(ap, tbuf, MAXPDSTRING);\n            SETSYMBOL(ap, gensym(tbuf));\n            break;\n        case A_SYMBOL:\n            for (s = ap->a_w.w_symbol->s_name, fixit = 0; *s; s++)\n                if (*s == ';' || *s == ',' || *s == '$' || *s == '\\\\')\n                    fixit = 1;\n            if (fixit)\n            {\n                atom_string(ap, tbuf, MAXPDSTRING);\n                SETSYMBOL(ap, gensym(tbuf));\n            }\n            break;\n        default:\n            bug(\"binbuf_addbinbuf\");\n        }\n    }\n\n    binbuf_add(x, z->b_n, z->b_vec);\n    binbuf_free(z);\n}\n\nvoid binbuf_addsemi(t_binbuf *x)\n{\n    t_atom a;\n    SETSEMI(&a);\n    binbuf_add(x, 1, &a);\n}\n\n/* Supply atoms to a binbuf from a message, making the opposite changes\nfrom binbuf_addbinbuf.  The symbol \";\" goes to a semicolon, etc. */\n\nvoid binbuf_restore(t_binbuf *x, int argc, const t_atom *argv)\n{\n    int previoussize = x->b_n;\n    int newsize = previoussize + argc, i;\n    t_atom *ap;\n\n    if (!binbuf_resize(x, newsize))\n    {\n        pd_error(0, \"binbuf_restore: out of space\");\n        return;\n    }\n\n    for (ap = x->b_vec + previoussize, i = argc; i--; ap++)\n    {\n        if (argv->a_type == A_SYMBOL)\n        {\n            const char *str = argv->a_w.w_symbol->s_name, *str2;\n            if (!strcmp(str, \";\")) SETSEMI(ap);\n            else if (!strcmp(str, \",\")) SETCOMMA(ap);\n            else\n            {\n                char buf[MAXPDSTRING], *sp1;\n                const char *sp2, *usestr;\n                int dollar = 0;\n                if (strchr(str, '\\\\'))\n                {\n                    int slashed = 0;\n                    for (sp1 = buf, sp2 = argv->a_w.w_symbol->s_name;\n                        *sp2 && sp1 < buf + (MAXPDSTRING-1);\n                            sp2++)\n                    {\n                        if (slashed)\n                            *sp1++ = *sp2, slashed = 0;\n                        else if (*sp2 == '\\\\')\n                            slashed = 1;\n                        else\n                        {\n                            if (*sp2 == '$' && sp2[1] >= '0' && sp2[1] <= '9')\n                                dollar = 1;\n                            *sp1++ = *sp2;\n                            slashed = 0;\n                        }\n                    }\n                    *sp1 = 0;\n                    usestr = buf;\n                }\n                else usestr = str;\n                if (dollar || (usestr== str && (str2 = strchr(usestr, '$')) &&\n                    str2[1] >= '0' && str2[1] <= '9'))\n                {\n                    int dollsym = 0;\n                    if (*usestr != '$')\n                        dollsym = 1;\n                    else for (str2 = usestr + 1; *str2; str2++)\n                        if (*str2 < '0' || *str2 > '9')\n                    {\n                        dollsym = 1;\n                        break;\n                    }\n                    if (dollsym)\n                        SETDOLLSYM(ap, usestr == str ?\n                            argv->a_w.w_symbol : gensym(usestr));\n                    else\n                    {\n                        int dollar = 0;\n                        sscanf(usestr + 1, \"%d\", &dollar);\n                        SETDOLLAR(ap, dollar);\n                    }\n                }\n                else SETSYMBOL(ap, usestr == str ?\n                    argv->a_w.w_symbol : gensym(usestr));\n                /* fprintf(stderr, \"arg %s -> binbuf %s type %d\\n\",\n                    argv->a_w.w_symbol->s_name, usestr, ap->a_type); */\n            }\n            argv++;\n        }\n        else *ap = *(argv++);\n    }\n}\n\nvoid binbuf_print(const t_binbuf *x)\n{\n    int i, startedpost = 0, newline = 1;\n    for (i = 0; i < x->b_n; i++)\n    {\n        if (newline)\n        {\n            if (startedpost) endpost();\n            startpost(\"\");\n            startedpost = 1;\n        }\n        postatom(1, x->b_vec + i);\n        if (x->b_vec[i].a_type == A_SEMI)\n            newline = 1;\n        else newline = 0;\n    }\n    if (startedpost) endpost();\n}\n\nint binbuf_getnatom(const t_binbuf *x)\n{\n    return (x->b_n);\n}\n\nt_atom *binbuf_getvec(const t_binbuf *x)\n{\n    return (x->b_vec);\n}\n\nint binbuf_resize(t_binbuf *x, int newsize)\n{\n    t_atom *new = t_resizebytes(x->b_vec,\n        x->b_n * sizeof(*x->b_vec), newsize * sizeof(*x->b_vec));\n    if (new)\n        x->b_vec = new, x->b_n = newsize;\n    return (new != 0);\n}\n\nint canvas_getdollarzero(void);\n\n/* JMZ:\n * s points to the first character after the $\n * (e.g. if the org.symbol is \"$1-bla\", then s will point to \"1-bla\")\n * (e.g. org.symbol=\"hu-$1mu\", s=\"1mu\")\n * LATER: think about more complex $args, like ${$1+3}\n *\n * the return value holds the length of the $arg (in most cases: 1)\n * buf holds the expanded $arg\n *\n * if some error occurred, \"-1\" is returned\n *\n * e.g. \"$1-bla\" with list \"10 20 30\"\n * s=\"1-bla\"\n * buf=\"10\"\n * return value = 1; (s+1==\"-bla\")\n */\nstatic int binbuf_expanddollsym(const char *s, char *buf, t_atom *dollar0,\n    int ac, const t_atom *av, int tonew)\n{\n    int argno = (int)atol(s);\n    int arglen = 0;\n    const char *cs = s;\n    char c = *cs;\n\n    *buf=0;\n    while (c && (c>='0') && (c<='9'))\n    {\n        c = *cs++;\n        arglen++;\n    }\n\n    if (cs==s)      /* invalid $-expansion (like \"$bla\") */\n    {\n        sprintf(buf, \"$\");\n        return 0;\n    }\n    else if (argno < 0 || argno > ac) /* undefined argument */\n    {\n        if (!tonew)\n            return 0;\n        sprintf(buf, \"$%d\", argno);\n    }\n    else        /* well formed; expand it */\n    {\n        const t_atom *dollarvalue = (argno ? &av[argno-1] : dollar0);\n        if (dollarvalue->a_type == A_SYMBOL)\n        {\n            strncpy(buf, dollarvalue->a_w.w_symbol->s_name, MAXPDSTRING/2-1);\n            buf[MAXPDSTRING/2-2] = 0;\n        }\n        else atom_string(dollarvalue, buf, MAXPDSTRING/2-1);\n    }\n    return (arglen-1);\n}\n\n/* expand any '$' variables in the symbol s.  \"tonow\" is set if this is in the\ncontext of a message to create a new object; in this case out-of-range '$'\nargs become 0 - otherwise zero is returned and the caller has to check the\nresult. */\n/* LATER remove the dependence on the current canvas for $0; should be another\nargument. */\nt_symbol *binbuf_realizedollsym(t_symbol *s, int ac, const t_atom *av,\n    int tonew)\n{\n    char buf[MAXPDSTRING];\n    char buf2[MAXPDSTRING];\n    const char*str=s->s_name;\n    char*substr;\n    int next=0;\n    t_atom dollarnull;\n    SETFLOAT(&dollarnull, canvas_getdollarzero());\n    buf2[0] = buf2[MAXPDSTRING-1] = 0;\n\n    substr=strchr(str, '$');\n    if (!substr || substr-str >= MAXPDSTRING)\n        return (s);\n\n    strncpy(buf2, str, (substr-str));\n    buf2[substr-str] = 0;\n    str=substr+1;\n\n    while((next=binbuf_expanddollsym(str, buf, &dollarnull, ac, av, tonew))>=0)\n    {\n        /*\n        * JMZ: i am not sure what this means, so i might have broken it\n        * it seems like that if \"tonew\" is set and the $arg cannot be expanded\n        * (or the dollarsym is in reality a A_DOLLAR)\n        * 0 is returned from binbuf_realizedollsym\n        * this happens, when expanding in a message-box, but does not happen\n        * when the A_DOLLSYM is the name of a subpatch\n        */\n        if (!tonew && (0==next) && (0==*buf))\n        {\n            return 0; /* JMZ: this should mimic the original behaviour */\n        }\n\n        strncat(buf2, buf, MAXPDSTRING-strlen(buf2)-1);\n        str+=next;\n        substr=strchr(str, '$');\n        if (substr)\n        {\n            unsigned long n = substr-str;\n            if(n>MAXPDSTRING-strlen(buf2)-1) n=MAXPDSTRING-strlen(buf2)-1;\n            strncat(buf2, str, n);\n            str=substr+1;\n        }\n        else\n        {\n            strncat(buf2, str, MAXPDSTRING-strlen(buf2)-1);\n            goto done;\n        }\n    }\ndone:\n    return (gensym(buf2));\n}\n\n#define SMALLMSG 5\n#define HUGEMSG 1000\n\nvoid binbuf_eval(const t_binbuf *x, t_pd *target, int argc, const t_atom *argv)\n{\n    t_atom smallstack[SMALLMSG], *mstack, *msp;\n    const t_atom *at = x->b_vec;\n    int ac = x->b_n;\n    int nargs, maxnargs = 0;\n    t_pd *initial_target = target;\n\n    if (ac <= SMALLMSG)\n        mstack = smallstack;\n    else\n    {\n#if 1\n            /* count number of args in biggest message.  The weird\n            treatment of \"pd_objectmaker\" is because when the message\n            goes out to objectmaker, commas and semis are passed\n            on as regular args (see below).  We're tacitly assuming here\n            that the pd_objectmaker target can't come up via a named\n            destination in the message, only because the original \"target\"\n            points there. */\n        if (target == &pd_objectmaker)\n            maxnargs = ac;\n        else\n        {\n            int i, j = (target ? 0 : -1);\n            for (i = 0; i < ac; i++)\n            {\n                if (at[i].a_type == A_SEMI)\n                    j = -1;\n                else if (at[i].a_type == A_COMMA)\n                    j = 0;\n                else if (++j > maxnargs)\n                    maxnargs = j;\n            }\n        }\n        if (maxnargs <= SMALLMSG)\n            mstack = smallstack;\n        else ALLOCA(t_atom, mstack, maxnargs, HUGEMSG);\n#else\n            /* just pessimistically allocate enough to hold everything\n            at once.  This turned out to run slower in a simple benchmark\n            I tried, perhaps because the extra memory allocation\n            hurt the cache hit rate. */\n        maxnargs = ac;\n        ALLOCA(t_atom, mstack, maxnargs, HUGEMSG);\n#endif\n\n    }\n    msp = mstack;\n    while (1)\n    {\n        t_pd *nexttarget;\n            /* get a target. */\n        while (!target)\n        {\n            t_symbol *s;\n            while (ac && (at->a_type == A_SEMI || at->a_type == A_COMMA))\n                ac--,  at++;\n            if (!ac) break;\n            if (at->a_type == A_DOLLAR)\n            {\n                if (at->a_w.w_index <= 0 || at->a_w.w_index > argc)\n                {\n                    pd_error(initial_target, \"$%d: not enough arguments supplied\",\n                            at->a_w.w_index);\n                    goto cleanup;\n                }\n                else if (argv[at->a_w.w_index-1].a_type != A_SYMBOL)\n                {\n                    pd_error(initial_target, \"$%d: symbol needed as message destination\",\n                        at->a_w.w_index);\n                    goto cleanup;\n                }\n                else s = argv[at->a_w.w_index-1].a_w.w_symbol;\n            }\n            else if (at->a_type == A_DOLLSYM)\n            {\n                if (!(s = binbuf_realizedollsym(at->a_w.w_symbol,\n                    argc, argv, 0)))\n                {\n                    pd_error(initial_target, \"$%s: not enough arguments supplied\",\n                        at->a_w.w_symbol->s_name);\n                    goto cleanup;\n                }\n            }\n            else s = atom_getsymbol(at);\n            if (!(target = s->s_thing))\n            {\n                pd_error(initial_target, \"%s: no such object \", s->s_name);\n            cleanup:\n                do at++, ac--;\n                while (ac && at->a_type != A_SEMI);\n                    /* LATER eat args until semicolon and continue */\n                continue;\n            }\n            else\n            {\n                at++, ac--;\n                break;\n            }\n        }\n        if (!ac) break;\n        nargs = 0;\n        nexttarget = target;\n        while (1)\n        {\n            t_symbol *s9;\n            if (!ac) goto gotmess;\n            switch (at->a_type)\n            {\n            case A_SEMI:\n                    /* semis and commas in new message just get bashed to\n                    a symbol.  This is needed so you can pass them to \"expr.\" */\n                if (target == &pd_objectmaker)\n                {\n                    SETSYMBOL(msp, gensym(\";\"));\n                    break;\n                }\n                else\n                {\n                    nexttarget = 0;\n                    goto gotmess;\n                }\n            case A_COMMA:\n                if (target == &pd_objectmaker)\n                {\n                    SETSYMBOL(msp, gensym(\",\"));\n                    break;\n                }\n                else goto gotmess;\n            case A_FLOAT:\n            case A_SYMBOL:\n                *msp = *at;\n                break;\n            case A_DOLLAR:\n                if (at->a_w.w_index > 0 && at->a_w.w_index <= argc)\n                    *msp = argv[at->a_w.w_index-1];\n                else if (at->a_w.w_index == 0)\n                    SETFLOAT(msp, canvas_getdollarzero());\n                else\n                {\n                    if (target == &pd_objectmaker)\n                        SETFLOAT(msp, 0);\n                    else\n                    {\n                        pd_error(target, \"$%d: argument number out of range\",\n                            at->a_w.w_index);\n                        SETFLOAT(msp, 0);\n                    }\n                }\n                break;\n            case A_DOLLSYM:\n                s9 = binbuf_realizedollsym(at->a_w.w_symbol, argc, argv,\n                    target == &pd_objectmaker);\n                if (!s9)\n                {\n                    pd_error(target, \"%s: argument number out of range\", at->a_w.w_symbol->s_name);\n                    SETSYMBOL(msp, at->a_w.w_symbol);\n                }\n                else SETSYMBOL(msp, s9);\n                break;\n            default:\n                bug(\"bad item in binbuf\");\n                goto broken;\n            }\n            msp++;\n            ac--;\n            at++;\n            nargs++;\n        }\n    gotmess:\n        if (nargs)\n        {\n            switch (mstack->a_type)\n            {\n            case A_SYMBOL:\n                typedmess(target, mstack->a_w.w_symbol, nargs-1, mstack+1);\n                break;\n            case A_FLOAT:\n                if (nargs == 1) pd_float(target, mstack->a_w.w_float);\n                else pd_list(target, 0, nargs, mstack);\n                break;\n            case A_POINTER:\n                if (nargs == 1) pd_pointer(target, mstack->a_w.w_gpointer);\n                else pd_list(target, 0, nargs, mstack);\n                break;\n            default:\n                bug(\"bad selector\");\n                break;\n            }\n        }\n        msp = mstack;\n        if (!ac) break;\n        target = nexttarget;\n        at++;\n        ac--;\n    }\nbroken:\n    if (maxnargs > SMALLMSG)\n         FREEA(t_atom, mstack, maxnargs, HUGEMSG);\n}\n\nint binbuf_read(t_binbuf *b, const char *filename, const char *dirname, int crflag)\n{\n    long length;\n    int fd;\n    int readret;\n    char *buf;\n    char namebuf[MAXPDSTRING];\n\n    if (*dirname)\n        snprintf(namebuf, MAXPDSTRING-1, \"%s/%s\", dirname, filename);\n    else\n        snprintf(namebuf, MAXPDSTRING-1, \"%s\", filename);\n    namebuf[MAXPDSTRING-1] = 0;\n\n    if ((fd = sys_open(namebuf, 0)) < 0)\n    {\n        fprintf(stderr, \"open: \");\n        perror(namebuf);\n        return (1);\n    }\n    if ((length = (long)lseek(fd, 0, SEEK_END)) < 0 || lseek(fd, 0, SEEK_SET) < 0\n        || !(buf = t_getbytes(length)))\n    {\n        fprintf(stderr, \"lseek: \");\n        perror(namebuf);\n        close(fd);\n        return(1);\n    }\n    if ((readret = (int)read(fd, buf, length)) < length)\n    {\n        fprintf(stderr, \"read (%d %ld) -> %d\\n\", fd, length, readret);\n        perror(namebuf);\n        close(fd);\n        t_freebytes(buf, length);\n        return(1);\n    }\n        /* optionally map carriage return to semicolon */\n    if (crflag)\n    {\n        int i;\n        for (i = 0; i < length; i++)\n            if (buf[i] == '\\n')\n                buf[i] = ';';\n    }\n    binbuf_text(b, buf, length);\n\n#if 0\n    startpost(\"binbuf_read \"); postatom(b->b_n, b->b_vec); endpost();\n#endif\n\n    t_freebytes(buf, length);\n    close(fd);\n    return (0);\n}\n\n    /* read a binbuf from a file, via the search patch of a canvas */\nint binbuf_read_via_canvas(t_binbuf *b, const char *filename,\n    const t_canvas *canvas, int crflag)\n{\n    int filedesc;\n    char buf[MAXPDSTRING], *bufptr;\n    if ((filedesc = canvas_open(canvas, filename, \"\",\n        buf, &bufptr, MAXPDSTRING, 0)) < 0)\n    {\n        pd_error(0, \"%s: can't open\", filename);\n        return (1);\n    }\n    else close (filedesc);\n    if (binbuf_read(b, bufptr, buf, crflag))\n        return (1);\n    else return (0);\n}\n\n    /* old version */\nint binbuf_read_via_path(t_binbuf *b, const char *filename, const char *dirname,\n    int crflag)\n{\n    int filedesc;\n    char buf[MAXPDSTRING], *bufptr;\n    if ((filedesc = open_via_path(\n        dirname, filename, \"\", buf, &bufptr, MAXPDSTRING, 0)) < 0)\n    {\n        pd_error(0, \"%s: can't open\", filename);\n        return (1);\n    }\n    else close (filedesc);\n    if (binbuf_read(b, bufptr, buf, crflag))\n        return (1);\n    else return (0);\n}\n\n#define WBUFSIZE 4096\nstatic t_binbuf *binbuf_convert(const t_binbuf *oldb, int maxtopd);\n\n    /* write a binbuf to a text file.  If \"crflag\" is set we suppress\n    semicolons. */\nint binbuf_write(const t_binbuf *x, const char *filename, const char *dir, int crflag)\n{\n    FILE *f = 0;\n    char sbuf[WBUFSIZE], fbuf[MAXPDSTRING], *bp = sbuf, *ep = sbuf + WBUFSIZE;\n    t_atom *ap;\n    t_binbuf *y = 0;\n    const t_binbuf *z = x;\n    int indx;\n\n    if (*dir)\n        snprintf(fbuf, MAXPDSTRING-1, \"%s/%s\", dir, filename);\n    else\n        snprintf(fbuf, MAXPDSTRING-1, \"%s\", filename);\n    fbuf[MAXPDSTRING-1] = 0;\n\n    if (!strcmp(filename + strlen(filename) - 4, \".pat\") ||\n        !strcmp(filename + strlen(filename) - 4, \".mxt\"))\n    {\n        y = binbuf_convert(x, 0);\n        z = y;\n    }\n\n    if (!(f = sys_fopen(fbuf, \"w\")))\n        goto fail;\n    for (ap = z->b_vec, indx = z->b_n; indx--; ap++)\n    {\n        int length;\n            /* estimate how many characters will be needed.  Printing out\n            symbols may need extra characters for inserting backslashes. */\n        if (ap->a_type == A_SYMBOL || ap->a_type == A_DOLLSYM)\n            length = 80 + (int)strlen(ap->a_w.w_symbol->s_name);\n        else length = 40;\n        if (ep - bp < length)\n        {\n            if (fwrite(sbuf, bp-sbuf, 1, f) < 1)\n                goto fail;\n            bp = sbuf;\n        }\n        if ((ap->a_type == A_SEMI || ap->a_type == A_COMMA) &&\n            bp > sbuf && bp[-1] == ' ') bp--;\n        if (!crflag || ap->a_type != A_SEMI)\n        {\n            atom_string(ap, bp, (unsigned int)((ep-bp)-2));\n            length = (int)strlen(bp);\n            bp += length;\n        }\n        if (ap->a_type == A_SEMI)\n        {\n            *bp++ = '\\n';\n        }\n        else\n        {\n            *bp++ = ' ';\n        }\n    }\n    if (fwrite(sbuf, bp-sbuf, 1, f) < 1)\n        goto fail;\n\n    if (fflush(f) != 0)\n        goto fail;\n\n    if (y)\n        binbuf_free(y);\n    fclose(f);\n    return (0);\nfail:\n    if (y)\n        binbuf_free(y);\n    if (f)\n        fclose(f);\n    return (1);\n}\n\n/* The following routine attempts to convert from max to pd or back.  The\nmax to pd direction is working OK but you will need to make lots of\nabstractions for objects like \"gate\" which don't exist in Pd.  conversion\nfrom Pd to Max hasn't been tested for patches with subpatches yet!  */\n\n#define MAXSTACK 1000\n\n#define ISSYMBOL(a, b) ((a)->a_type == A_SYMBOL && \\\n    !strcmp((a)->a_w.w_symbol->s_name, (b)))\n\nstatic t_binbuf *binbuf_convert(const t_binbuf *oldb, int maxtopd)\n{\n    t_binbuf *newb = binbuf_new();\n    t_atom *vec = oldb->b_vec;\n    t_int n = oldb->b_n, nextindex, stackdepth = 0, stack[MAXSTACK] = {0},\n        nobj = 0, gotfontsize = 0;\n\tint i;\n    t_atom outmess[MAXSTACK], *nextmess;\n    t_float fontsize = 10;\n    if (!maxtopd)\n        binbuf_addv(newb, \"ss;\", gensym(\"max\"), gensym(\"v2\"));\n    for (nextindex = 0; nextindex < n; )\n    {\n        int endmess, natom;\n        const char *first, *second, *third;\n        for (endmess = (int)nextindex; endmess < n && vec[endmess].a_type != A_SEMI;\n            endmess++)\n                ;\n        if (endmess == n) break;\n        if (endmess == nextindex || endmess == nextindex + 1\n            || vec[nextindex].a_type != A_SYMBOL ||\n                vec[nextindex+1].a_type != A_SYMBOL)\n        {\n            nextindex = endmess + 1;\n            continue;\n        }\n        natom = endmess - (int)nextindex;\n        if (natom > MAXSTACK-10) natom = MAXSTACK-10;\n        nextmess = vec + nextindex;\n        first = nextmess->a_w.w_symbol->s_name;\n        second = (nextmess+1)->a_w.w_symbol->s_name;\n        if (maxtopd)\n        {\n                /* case 1: importing a \".pat\" file into Pd. */\n\n                /* dollar signs in file translate to symbols */\n            for (i = 0; i < natom; i++)\n            {\n                if (nextmess[i].a_type == A_DOLLAR)\n                {\n                    char buf[100];\n                    sprintf(buf, \"$%d\", nextmess[i].a_w.w_index);\n                    SETSYMBOL(nextmess+i, gensym(buf));\n                }\n                else if (nextmess[i].a_type == A_DOLLSYM)\n                {\n                    char buf[100];\n                    sprintf(buf, \"%s\", nextmess[i].a_w.w_symbol->s_name);\n                    SETSYMBOL(nextmess+i, gensym(buf));\n                }\n            }\n            if (!strcmp(first, \"#N\"))\n            {\n                if (!strcmp(second, \"vpatcher\"))\n                {\n                    if (stackdepth >= MAXSTACK)\n                    {\n                        pd_error(0, \"stack depth exceeded: too many embedded patches\");\n                        return (newb);\n                    }\n                    stack[stackdepth] = nobj;\n                    stackdepth++;\n                    nobj = 0;\n                    binbuf_addv(newb, \"ssfffff;\",\n                        gensym(\"#N\"), gensym(\"canvas\"),\n                            atom_getfloatarg(2, natom, nextmess),\n                            atom_getfloatarg(3, natom, nextmess),\n                            atom_getfloatarg(4, natom, nextmess) -\n                                atom_getfloatarg(2, natom, nextmess),\n                            atom_getfloatarg(5, natom, nextmess) -\n                                atom_getfloatarg(3, natom, nextmess),\n                            (t_float)sys_defaultfont);\n                }\n            }\n            if (!strcmp(first, \"#P\"))\n            {\n                    /* drop initial \"hidden\" flag */\n                if (!strcmp(second, \"hidden\"))\n                {\n                    nextmess++;\n                    natom--;\n                    second = (nextmess+1)->a_w.w_symbol->s_name;\n                }\n                if (natom >= 7 && !strcmp(second, \"newobj\")\n                    && (ISSYMBOL(&nextmess[6], \"patcher\") ||\n                        ISSYMBOL(&nextmess[6], \"p\")))\n                {\n                    binbuf_addv(newb, \"ssffss;\",\n                        gensym(\"#X\"), gensym(\"restore\"),\n                        atom_getfloatarg(2, natom, nextmess),\n                        atom_getfloatarg(3, natom, nextmess),\n                        gensym(\"pd\"), atom_getsymbolarg(7, natom, nextmess));\n                    if (stackdepth) stackdepth--;\n                    nobj = stack[stackdepth];\n                    nobj++;\n                }\n                else if (!strcmp(second, \"newex\") || !strcmp(second, \"newobj\"))\n                {\n                    t_symbol *classname =\n                        atom_getsymbolarg(6, natom, nextmess);\n                    if (classname == gensym(\"trigger\") ||\n                        classname == gensym(\"t\"))\n                    {\n                        for (i = 7; i < natom; i++)\n                            if (nextmess[i].a_type == A_SYMBOL &&\n                                nextmess[i].a_w.w_symbol == gensym(\"i\"))\n                                    nextmess[i].a_w.w_symbol = gensym(\"f\");\n                    }\n                    if (classname == gensym(\"table\"))\n                        classname = gensym(\"TABLE\");\n                    SETSYMBOL(outmess, gensym(\"#X\"));\n                    SETSYMBOL(outmess + 1, gensym(\"obj\"));\n                    outmess[2] = nextmess[2];\n                    outmess[3] = nextmess[3];\n                    SETSYMBOL(outmess+4, classname);\n                    for (i = 7; i < natom; i++)\n                        outmess[i-2] = nextmess[i];\n                    SETSEMI(outmess + natom - 2);\n                    binbuf_add(newb, natom - 1, outmess);\n                    nobj++;\n                }\n                else if (!strcmp(second, \"message\") ||\n                    !strcmp(second, \"comment\"))\n                {\n                    SETSYMBOL(outmess, gensym(\"#X\"));\n                    SETSYMBOL(outmess + 1, gensym(\n                        (strcmp(second, \"message\") ? \"text\" : \"msg\")));\n                    outmess[2] = nextmess[2];\n                    outmess[3] = nextmess[3];\n                    for (i = 6; i < natom; i++)\n                        outmess[i-2] = nextmess[i];\n                    SETSEMI(outmess + natom - 2);\n                    binbuf_add(newb, natom - 1, outmess);\n                    nobj++;\n                }\n                else if (!strcmp(second, \"button\"))\n                {\n                    binbuf_addv(newb, \"ssffs;\",\n                        gensym(\"#X\"), gensym(\"obj\"),\n                        atom_getfloatarg(2, natom, nextmess),\n                        atom_getfloatarg(3, natom, nextmess),\n                        gensym(\"bng\"));\n                    nobj++;\n                }\n                else if (!strcmp(second, \"number\") || !strcmp(second, \"flonum\"))\n                {\n                    binbuf_addv(newb, \"ssff;\",\n                        gensym(\"#X\"), gensym(\"floatatom\"),\n                        atom_getfloatarg(2, natom, nextmess),\n                        atom_getfloatarg(3, natom, nextmess));\n                    nobj++;\n                }\n                else if (!strcmp(second, \"slider\"))\n                {\n                    t_float inc = atom_getfloatarg(7, natom, nextmess);\n                    if (inc <= 0)\n                        inc = 1;\n                    binbuf_addv(newb, \"ssffsffffffsssfffffffff;\",\n                        gensym(\"#X\"), gensym(\"obj\"),\n                        atom_getfloatarg(2, natom, nextmess),\n                        atom_getfloatarg(3, natom, nextmess),\n                        gensym(\"vsl\"),\n                        atom_getfloatarg(4, natom, nextmess),\n                        atom_getfloatarg(5, natom, nextmess),\n                        atom_getfloatarg(6, natom, nextmess),\n                        atom_getfloatarg(6, natom, nextmess)\n                            + (atom_getfloatarg(5, natom, nextmess) - 1) * inc,\n                        0., 0.,\n                        gensym(\"empty\"), gensym(\"empty\"), gensym(\"empty\"),\n                        0., -8., 0., 8., -262144., -1., -1., 0., 1.);\n                    nobj++;\n                }\n                else if (!strcmp(second, \"toggle\"))\n                {\n                    binbuf_addv(newb, \"ssffs;\",\n                        gensym(\"#X\"), gensym(\"obj\"),\n                        atom_getfloatarg(2, natom, nextmess),\n                        atom_getfloatarg(3, natom, nextmess),\n                        gensym(\"tgl\"));\n                    nobj++;\n                }\n                else if (!strcmp(second, \"inlet\"))\n                {\n                    binbuf_addv(newb, \"ssffs;\",\n                        gensym(\"#X\"), gensym(\"obj\"),\n                        atom_getfloatarg(2, natom, nextmess),\n                        atom_getfloatarg(3, natom, nextmess),\n                        gensym((natom > 5 ? \"inlet~\" : \"inlet\")));\n                    nobj++;\n                }\n                else if (!strcmp(second, \"outlet\"))\n                {\n                    binbuf_addv(newb, \"ssffs;\",\n                        gensym(\"#X\"), gensym(\"obj\"),\n                        atom_getfloatarg(2, natom, nextmess),\n                        atom_getfloatarg(3, natom, nextmess),\n                        gensym((natom > 5 ? \"outlet~\" : \"outlet\")));\n                    nobj++;\n                }\n                else if (!strcmp(second, \"user\"))\n                {\n                    third = (nextmess+2)->a_w.w_symbol->s_name;\n                    if (!strcmp(third, \"hslider\"))\n                    {\n                        t_float range = atom_getfloatarg(7, natom, nextmess);\n                        t_float multiplier = atom_getfloatarg(8, natom, nextmess);\n                        t_float offset = atom_getfloatarg(9, natom, nextmess);\n                        binbuf_addv(newb, \"ssffsffffffsssfffffffff;\",\n                                    gensym(\"#X\"), gensym(\"obj\"),\n                                    atom_getfloatarg(3, natom, nextmess),\n                                    atom_getfloatarg(4, natom, nextmess),\n                                    gensym(\"hsl\"),\n                                    atom_getfloatarg(6, natom, nextmess),\n                                    atom_getfloatarg(5, natom, nextmess),\n                                    offset,\n                                    range + offset,\n                                    0., 0.,\n                                    gensym(\"empty\"), gensym(\"empty\"), gensym(\"empty\"),\n                                    0., -8., 0., 8., -262144., -1., -1., 0., 1.);\n                   }\n                    else if (!strcmp(third, \"uslider\"))\n                    {\n                        t_float range = atom_getfloatarg(7, natom, nextmess);\n                        t_float multiplier = atom_getfloatarg(8, natom, nextmess);\n                        t_float offset = atom_getfloatarg(9, natom, nextmess);\n                        binbuf_addv(newb, \"ssffsffffffsssfffffffff;\",\n                                    gensym(\"#X\"), gensym(\"obj\"),\n                                    atom_getfloatarg(3, natom, nextmess),\n                                    atom_getfloatarg(4, natom, nextmess),\n                                    gensym(\"vsl\"),\n                                    atom_getfloatarg(5, natom, nextmess),\n                                    atom_getfloatarg(6, natom, nextmess),\n                                    offset,\n                                    range + offset,\n                                    0., 0.,\n                                    gensym(\"empty\"), gensym(\"empty\"), gensym(\"empty\"),\n                                    0., -8., 0., 8., -262144., -1., -1., 0., 1.);\n                    }\n                    else\n                        binbuf_addv(newb, \"ssffs;\",\n                                    gensym(\"#X\"), gensym(\"obj\"),\n                                    atom_getfloatarg(3, natom, nextmess),\n                                    atom_getfloatarg(4, natom, nextmess),\n                                    atom_getsymbolarg(2, natom, nextmess));\n                    nobj++;\n                }\n                else if (!strcmp(second, \"connect\")||\n                    !strcmp(second, \"fasten\"))\n                {\n                    binbuf_addv(newb, \"ssffff;\",\n                        gensym(\"#X\"), gensym(\"connect\"),\n                        nobj - atom_getfloatarg(2, natom, nextmess) - 1,\n                        atom_getfloatarg(3, natom, nextmess),\n                        nobj - atom_getfloatarg(4, natom, nextmess) - 1,\n                        atom_getfloatarg(5, natom, nextmess));\n                }\n            }\n        }\n        else        /* Pd to Max */\n        {\n            if (!strcmp(first, \"#N\"))\n            {\n                if (!strcmp(second, \"canvas\"))\n                {\n                    t_float x, y;\n                    if (stackdepth >= MAXSTACK)\n                    {\n                        pd_error(0, \"stack depth exceeded: too many embedded patches\");\n                        return (newb);\n                    }\n                    stack[stackdepth] = nobj;\n                    stackdepth++;\n                    nobj = 0;\n                    if(!gotfontsize) { /* only the first canvas sets the font size */\n                        fontsize = atom_getfloatarg(6, natom, nextmess);\n                        gotfontsize = 1;\n                    }\n                    x = atom_getfloatarg(2, natom, nextmess);\n                    y = atom_getfloatarg(3, natom, nextmess);\n                    binbuf_addv(newb, \"ssffff;\",\n                        gensym(\"#N\"), gensym(\"vpatcher\"),\n                            x, y,\n                            atom_getfloatarg(4, natom, nextmess) + x,\n                            atom_getfloatarg(5, natom, nextmess) + y);\n                }\n            }\n            if (!strcmp(first, \"#X\"))\n            {\n                if (natom >= 5 && !strcmp(second, \"restore\")\n                    && (ISSYMBOL (&nextmess[4], \"pd\")))\n                {\n                    binbuf_addv(newb, \"ss;\", gensym(\"#P\"), gensym(\"pop\"));\n                    SETSYMBOL(outmess, gensym(\"#P\"));\n                    SETSYMBOL(outmess + 1, gensym(\"newobj\"));\n                    outmess[2] = nextmess[2];\n                    outmess[3] = nextmess[3];\n                    SETFLOAT(outmess + 4, 50.*(natom-5));\n                    SETFLOAT(outmess + 5, fontsize);\n                    SETSYMBOL(outmess + 6, gensym(\"p\"));\n                    for (i = 5; i < natom; i++)\n                        outmess[i+2] = nextmess[i];\n                    SETSEMI(outmess + natom + 2);\n                    binbuf_add(newb, natom + 3, outmess);\n                    if (stackdepth) stackdepth--;\n                    nobj = stack[stackdepth];\n                    nobj++;\n                }\n                else if (!strcmp(second, \"obj\"))\n                {\n                    t_symbol *classname =\n                        atom_getsymbolarg(4, natom, nextmess);\n                    if (classname == gensym(\"inlet\"))\n                        binbuf_addv(newb, \"ssfff;\", gensym(\"#P\"),\n                            gensym(\"inlet\"),\n                            atom_getfloatarg(2, natom, nextmess),\n                            atom_getfloatarg(3, natom, nextmess),\n                            10. + fontsize);\n                    else if (classname == gensym(\"inlet~\"))\n                        binbuf_addv(newb, \"ssffff;\", gensym(\"#P\"),\n                            gensym(\"inlet\"),\n                            atom_getfloatarg(2, natom, nextmess),\n                            atom_getfloatarg(3, natom, nextmess),\n                            10. + fontsize, 1.);\n                    else if (classname == gensym(\"outlet\"))\n                        binbuf_addv(newb, \"ssfff;\", gensym(\"#P\"),\n                            gensym(\"outlet\"),\n                            atom_getfloatarg(2, natom, nextmess),\n                            atom_getfloatarg(3, natom, nextmess),\n                            10. + fontsize);\n                    else if (classname == gensym(\"outlet~\"))\n                        binbuf_addv(newb, \"ssffff;\", gensym(\"#P\"),\n                            gensym(\"outlet\"),\n                            atom_getfloatarg(2, natom, nextmess),\n                            atom_getfloatarg(3, natom, nextmess),\n                            10. + fontsize, 1.);\n                    else if (classname == gensym(\"bng\"))\n                        binbuf_addv(newb, \"ssffff;\", gensym(\"#P\"),\n                            gensym(\"button\"),\n                            atom_getfloatarg(2, natom, nextmess),\n                            atom_getfloatarg(3, natom, nextmess),\n                            atom_getfloatarg(5, natom, nextmess), 0.);\n                    else if (classname == gensym(\"tgl\"))\n                        binbuf_addv(newb, \"ssffff;\", gensym(\"#P\"),\n                            gensym(\"toggle\"),\n                            atom_getfloatarg(2, natom, nextmess),\n                            atom_getfloatarg(3, natom, nextmess),\n                            atom_getfloatarg(5, natom, nextmess), 0.);\n                    else if (classname == gensym(\"vsl\"))\n                        binbuf_addv(newb, \"ssffffff;\", gensym(\"#P\"),\n                            gensym(\"slider\"),\n                            atom_getfloatarg(2, natom, nextmess),\n                            atom_getfloatarg(3, natom, nextmess),\n                            atom_getfloatarg(5, natom, nextmess),\n                            atom_getfloatarg(6, natom, nextmess),\n                            (atom_getfloatarg(8, natom, nextmess) -\n                                atom_getfloatarg(7, natom, nextmess)) /\n                                    (atom_getfloatarg(6, natom, nextmess) == 1? 1 :\n                                         atom_getfloatarg(6, natom, nextmess) - 1),\n                            atom_getfloatarg(7, natom, nextmess));\n                    else if (classname == gensym(\"hsl\"))\n                    {\n                        t_float slmin = atom_getfloatarg(7, natom, nextmess);\n                        t_float slmax = atom_getfloatarg(8, natom, nextmess);\n                        binbuf_addv(newb, \"sssffffffff;\", gensym(\"#P\"),\n                            gensym(\"user\"),\n                            gensym(\"hslider\"),\n                            atom_getfloatarg(2, natom, nextmess),\n                            atom_getfloatarg(3, natom, nextmess),\n                            atom_getfloatarg(6, natom, nextmess),\n                            atom_getfloatarg(5, natom, nextmess),\n                            slmax - slmin + 1, /* range */\n                            1.,            /* multiplier */\n                            slmin,         /* offset */\n                            0.);\n                    }\n                    else if ( (classname == gensym(\"trigger\")) ||\n                              (classname == gensym(\"t\")) )\n                    {\n                        t_symbol *arg;\n                        SETSYMBOL(outmess, gensym(\"#P\"));\n                        SETSYMBOL(outmess + 1, gensym(\"newex\"));\n                        outmess[2] = nextmess[2];\n                        outmess[3] = nextmess[3];\n                        SETFLOAT(outmess + 4, 50.*(natom-4));\n                        SETFLOAT(outmess + 5, fontsize);\n                        outmess[6] = nextmess[4];\n                        for (i = 5; i < natom; i++) {\n                            arg = atom_getsymbolarg(i, natom, nextmess);\n                            if (arg == gensym(\"a\"))\n                                SETSYMBOL(outmess + i + 2, gensym(\"l\"));\n                            else if (arg == gensym(\"anything\"))\n                                SETSYMBOL(outmess + i + 2, gensym(\"l\"));\n                            else if (arg == gensym(\"bang\"))\n                                SETSYMBOL(outmess + i + 2, gensym(\"b\"));\n                            else if (arg == gensym(\"float\"))\n                                SETSYMBOL(outmess + i + 2, gensym(\"f\"));\n                            else if (arg == gensym(\"list\"))\n                                SETSYMBOL(outmess + i + 2, gensym(\"l\"));\n                            else if (arg == gensym(\"symbol\"))\n                                SETSYMBOL(outmess + i + 2, gensym(\"s\"));\n                            else\n                                outmess[i+2] = nextmess[i];\n                        }\n                        SETSEMI(outmess + natom + 2);\n                        binbuf_add(newb, natom + 3, outmess);\n                    }\n                    else\n                    {\n                        SETSYMBOL(outmess, gensym(\"#P\"));\n                        SETSYMBOL(outmess + 1, gensym(\"newex\"));\n                        outmess[2] = nextmess[2];\n                        outmess[3] = nextmess[3];\n                        SETFLOAT(outmess + 4, 50.*(natom-4));\n                        SETFLOAT(outmess + 5, fontsize);\n                        for (i = 4; i < natom; i++)\n                            outmess[i+2] = nextmess[i];\n                        if (classname == gensym(\"osc~\"))\n                            SETSYMBOL(outmess + 6, gensym(\"cycle~\"));\n                        SETSEMI(outmess + natom + 2);\n                        binbuf_add(newb, natom + 3, outmess);\n                    }\n                    nobj++;\n\n                }\n                else if (!strcmp(second, \"msg\") ||\n                    !strcmp(second, \"text\"))\n                {\n                    SETSYMBOL(outmess, gensym(\"#P\"));\n                    SETSYMBOL(outmess + 1, gensym(\n                        (strcmp(second, \"msg\") ? \"comment\" : \"message\")));\n                    outmess[2] = nextmess[2];\n                    outmess[3] = nextmess[3];\n                    SETFLOAT(outmess + 4, 50.*(natom-4));\n                    SETFLOAT(outmess + 5, fontsize);\n                    for (i = 4; i < natom; i++)\n                        outmess[i+2] = nextmess[i];\n                    SETSEMI(outmess + natom + 2);\n                    binbuf_add(newb, natom + 3, outmess);\n                    nobj++;\n                }\n                else if (!strcmp(second, \"floatatom\"))\n                {\n                    t_float width = atom_getfloatarg(4, natom, nextmess)*fontsize;\n                    if(width<8) width = 150; /* if pd width=0, set it big */\n                    binbuf_addv(newb, \"ssfff;\",\n                        gensym(\"#P\"), gensym(\"flonum\"),\n                        atom_getfloatarg(2, natom, nextmess),\n                        atom_getfloatarg(3, natom, nextmess),\n                        width);\n                    nobj++;\n                }\n                else if (!strcmp(second, \"connect\"))\n                {\n                    binbuf_addv(newb, \"ssffff;\",\n                        gensym(\"#P\"), gensym(\"connect\"),\n                        nobj - atom_getfloatarg(2, natom, nextmess) - 1,\n                        atom_getfloatarg(3, natom, nextmess),\n                        nobj - atom_getfloatarg(4, natom, nextmess) - 1,\n                        atom_getfloatarg(5, natom, nextmess));\n                }\n            }\n        }\n        nextindex = endmess + 1;\n    }\n    if (!maxtopd)\n        binbuf_addv(newb, \"ss;\", gensym(\"#P\"), gensym(\"pop\"));\n#if 0\n    binbuf_write(newb, \"import-result.pd\", \"/tmp\", 0);\n#endif\n    return (newb);\n}\n\n/* LATER make this evaluate the file on-the-fly. */\n/* LATER figure out how to log errors */\nvoid binbuf_evalfile(t_symbol *name, t_symbol *dir)\n{\n    t_binbuf *b = binbuf_new();\n    int import = !strcmp(name->s_name + strlen(name->s_name) - 4, \".pat\") ||\n        !strcmp(name->s_name + strlen(name->s_name) - 4, \".mxt\");\n    int dspstate = canvas_suspend_dsp();\n        /* set filename so that new canvases can pick them up */\n    glob_setfilename(0, name, dir);\n    if (binbuf_read(b, name->s_name, dir->s_name, 0))\n        pd_error(0, \"%s: read failed; %s\", name->s_name, strerror(errno));\n    else\n    {\n            /* save bindings of symbols #N, #A (and restore afterward) */\n        t_pd *bounda = gensym(\"#A\")->s_thing, *boundn = s__N.s_thing;\n        gensym(\"#A\")->s_thing = 0;\n        s__N.s_thing = &pd_canvasmaker;\n        if (import)\n        {\n            t_binbuf *newb = binbuf_convert(b, 1);\n            binbuf_free(b);\n            b = newb;\n        }\n        binbuf_eval(b, 0, 0, 0);\n            /* avoid crashing if no canvas was created by binbuf eval */\n        if (s__X.s_thing && *s__X.s_thing == canvas_class)\n            canvas_initbang((t_canvas *)(s__X.s_thing)); /* JMZ*/\n        gensym(\"#A\")->s_thing = bounda;\n        s__N.s_thing = boundn;\n    }\n    glob_setfilename(0, &s_, &s_);\n    binbuf_free(b);\n    canvas_resume_dsp(dspstate);\n}\n\n    /* save a text object to a binbuf for a file or copy buf */\nvoid binbuf_savetext(const t_binbuf *bfrom, t_binbuf *bto)\n{\n    int k, n = binbuf_getnatom(bfrom);\n    const t_atom *ap = binbuf_getvec(bfrom);\n    t_atom at;\n    for (k = 0; k < n; k++)\n    {\n        if (ap[k].a_type == A_FLOAT ||\n            (ap[k].a_type == A_SYMBOL &&\n                !strchr(ap[k].a_w.w_symbol->s_name, ';') &&\n                !strchr(ap[k].a_w.w_symbol->s_name, ',') &&\n                !strchr(ap[k].a_w.w_symbol->s_name, '$')))\n                    binbuf_add(bto, 1, &ap[k]);\n        else\n        {\n            char buf[MAXPDSTRING+1];\n            atom_string(&ap[k], buf, MAXPDSTRING);\n            SETSYMBOL(&at, gensym(buf));\n            binbuf_add(bto, 1, &at);\n        }\n    }\n    binbuf_addsemi(bto);\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/m_class.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n#define PD_CLASS_DEF\n#include \"m_pd.h\"\n#include \"m_imp.h\"\n#include \"s_stuff.h\"\n#include \"g_canvas.h\"\n#include <stdlib.h>\n#ifdef HAVE_UNISTD_H\n#include <unistd.h>\n#endif\n#ifdef _WIN32\n#include <io.h>\n#endif\n\n#include <stdarg.h>\n#include <string.h>\n#include <stdio.h>\n\n#include \"m_private_utils.h\"\n\nstatic t_symbol *class_loadsym;     /* name under which an extern is invoked */\nstatic void pd_defaultfloat(t_pd *x, t_float f);\nstatic void pd_defaultlist(t_pd *x, t_symbol *s, int argc, t_atom *argv);\nt_pd pd_objectmaker;    /* factory for creating \"object\" boxes */\nt_pd pd_canvasmaker;    /* factory for creating canvases */\n\nstatic t_symbol *class_extern_dir;\n\n#ifdef PDINSTANCE\nstatic t_class *class_list = 0;\nPERTHREAD t_pdinstance *pd_this = NULL;\nt_pdinstance **pd_instances;\nint pd_ninstances;\n#else\nt_symbol s_pointer, s_float, s_symbol, s_bang, s_list, s_anything,\n   s_signal, s__N, s__X, s_x, s_y, s_;\n#endif\nt_pdinstance pd_maininstance;\n\nstatic t_symbol *dogensym(const char *s, t_symbol *oldsym,\n    t_pdinstance *pdinstance);\nvoid x_midi_newpdinstance( void);\nvoid x_midi_freepdinstance( void);\nvoid s_inter_newpdinstance( void);\nvoid s_inter_free(t_instanceinter *inter);\nvoid g_canvas_newpdinstance( void);\nvoid g_canvas_freepdinstance( void);\nvoid d_ugen_newpdinstance( void);\nvoid d_ugen_freepdinstance( void);\nvoid new_anything(void *dummy, t_symbol *s, int argc, t_atom *argv);\n\nvoid s_stuff_newpdinstance(void)\n{\n    STUFF = getbytes(sizeof(*STUFF));\n    STUFF->st_externlist = STUFF->st_searchpath =\n        STUFF->st_staticpath = STUFF->st_helppath = STUFF->st_temppath = 0;\n    STUFF->st_schedblocksize = STUFF->st_blocksize = DEFDACBLKSIZE;\n    STUFF->st_dacsr = DEFDACSAMPLERATE;\n    STUFF->st_printhook = sys_printhook;\n    STUFF->st_impdata = NULL;\n}\n\nvoid s_stuff_freepdinstance(void)\n{\n    freebytes(STUFF, sizeof(*STUFF));\n}\n\nstatic t_pdinstance *pdinstance_init(t_pdinstance *x)\n{\n    int i;\n    x->pd_systime = 0;\n    x->pd_clock_setlist = 0;\n    x->pd_canvaslist = 0;\n    x->pd_templatelist = 0;\n    x->pd_symhash = getbytes(SYMTABHASHSIZE * sizeof(*x->pd_symhash));\n    for (i = 0; i < SYMTABHASHSIZE; i++)\n        x->pd_symhash[i] = 0;\n#ifdef PDINSTANCE\n    dogensym(\"pointer\",   &x->pd_s_pointer,  x);\n    dogensym(\"float\",     &x->pd_s_float,    x);\n    dogensym(\"symbol\",    &x->pd_s_symbol,   x);\n    dogensym(\"bang\",      &x->pd_s_bang,     x);\n    dogensym(\"list\",      &x->pd_s_list,     x);\n    dogensym(\"anything\",  &x->pd_s_anything, x);\n    dogensym(\"signal\",    &x->pd_s_signal,   x);\n    dogensym(\"#N\",        &x->pd_s__N,       x);\n    dogensym(\"#X\",        &x->pd_s__X,       x);\n    dogensym(\"x\",         &x->pd_s_x,        x);\n    dogensym(\"y\",         &x->pd_s_y,        x);\n    dogensym(\"\",          &x->pd_s_,         x);\n    pd_this = x;\n#else\n    dogensym(\"pointer\",   &s_pointer,  x);\n    dogensym(\"float\",     &s_float,    x);\n    dogensym(\"symbol\",    &s_symbol,   x);\n    dogensym(\"bang\",      &s_bang,     x);\n    dogensym(\"list\",      &s_list,     x);\n    dogensym(\"anything\",  &s_anything, x);\n    dogensym(\"signal\",    &s_signal,   x);\n    dogensym(\"#N\",        &s__N,       x);\n    dogensym(\"#X\",        &s__X,       x);\n    dogensym(\"x\",         &s_x,        x);\n    dogensym(\"y\",         &s_y,        x);\n    dogensym(\"\",          &s_,         x);\n#endif\n    x_midi_newpdinstance();\n    g_canvas_newpdinstance();\n    d_ugen_newpdinstance();\n    s_stuff_newpdinstance();\n    return (x);\n}\n\nstatic void class_addmethodtolist(t_class *c, t_methodentry **methodlist,\n    int nmethod, t_gotfn fn, t_symbol *sel, unsigned char *args,\n        t_pdinstance *pdinstance)\n{\n    int i;\n    t_methodentry *m;\n    for (i = 0; i < nmethod; i++)\n        if (sel && (*methodlist)[i].me_name == sel)\n    {\n        char nbuf[80];\n        snprintf(nbuf, 80, \"%s_aliased\", sel->s_name);\n        nbuf[79] = 0;\n        (*methodlist)[i].me_name = dogensym(nbuf, 0, pdinstance);\n        if (c == pd_objectmaker)\n            logpost(NULL, PD_VERBOSE, \"warning: class '%s' overwritten; old one renamed '%s'\",\n                sel->s_name, nbuf);\n        else logpost(NULL, PD_VERBOSE, \"warning: old method '%s' for class '%s' renamed '%s'\",\n            sel->s_name, c->c_name->s_name, nbuf);\n    }\n    (*methodlist) = t_resizebytes((*methodlist),\n        nmethod * sizeof(**methodlist),\n        (nmethod + 1) * sizeof(**methodlist));\n    m = (*methodlist) + nmethod;\n    m->me_name = sel;\n    m->me_fun = (t_gotfn)fn;\n    memcpy(m->me_arg, args, MAXPDARG+1);\n}\n\n#ifdef PDINSTANCE\nEXTERN void pd_setinstance(t_pdinstance *x)\n{\n    pd_this = x;\n}\n\nstatic void pdinstance_renumber(void)\n{\n    int i;\n    for (i = 0; i < pd_ninstances; i++)\n        pd_instances[i]->pd_instanceno = i;\n}\n\nextern void text_template_init(void);\nextern void garray_init(void);\n\nEXTERN t_pdinstance *pdinstance_new(void)\n{\n    t_pdinstance *x = (t_pdinstance *)getbytes(sizeof(t_pdinstance));\n    t_class *c;\n    int i;\n    pd_this = x;\n    s_inter_newpdinstance();\n    pdinstance_init(x);\n    sys_lock();\n    pd_globallock();\n    pd_instances = (t_pdinstance **)resizebytes(pd_instances,\n        pd_ninstances * sizeof(*pd_instances),\n        (pd_ninstances+1) * sizeof(*pd_instances));\n    pd_instances[pd_ninstances] = x;\n    for (c = class_list; c; c = c->c_next)\n    {\n        c->c_methods = (t_methodentry **)t_resizebytes(c->c_methods,\n            pd_ninstances * sizeof(*c->c_methods),\n            (pd_ninstances + 1) * sizeof(*c->c_methods));\n        c->c_methods[pd_ninstances] = t_getbytes(0);\n        for (i = 0; i < c->c_nmethod; i++)\n            class_addmethodtolist(c, &c->c_methods[pd_ninstances], i,\n                c->c_methods[0][i].me_fun,\n                dogensym(c->c_methods[0][i].me_name->s_name, 0, x),\n                    c->c_methods[0][i].me_arg, x);\n    }\n    pd_ninstances++;\n    pdinstance_renumber();\n    pd_bind(&glob_pdobject, gensym(\"pd\"));\n    text_template_init();\n    garray_init();\n    pd_globalunlock();\n    sys_unlock();\n    return (x);\n}\n\nEXTERN void pdinstance_free(t_pdinstance *x)\n{\n    t_symbol *s;\n    t_canvas *canvas;\n    int i, instanceno;\n    t_class *c;\n    t_instanceinter *inter;\n    pd_setinstance(x);\n    sys_lock();\n    pd_globallock();\n    \n    instanceno = x->pd_instanceno;\n    inter = x->pd_inter;\n    canvas_suspend_dsp();\n    while (x->pd_canvaslist)\n        pd_free((t_pd *)x->pd_canvaslist);\n    while (x->pd_templatelist)\n        pd_free((t_pd *)x->pd_templatelist);\n    for (c = class_list; c; c = c->c_next)\n    {\n        if(c->c_methods[instanceno])\n            freebytes(c->c_methods[instanceno],\n                      c->c_nmethod * sizeof(**c->c_methods));\n        c->c_methods[instanceno] = NULL;\n        for (i = instanceno; i < pd_ninstances-1; i++)\n            c->c_methods[i] = c->c_methods[i+1];\n        c->c_methods = (t_methodentry **)t_resizebytes(c->c_methods,\n            pd_ninstances * sizeof(*c->c_methods),\n            (pd_ninstances - 1) * sizeof(*c->c_methods));\n    }\n    for (i =0; i < SYMTABHASHSIZE; i++)\n    {\n        while ((s = x->pd_symhash[i]))\n        {\n            x->pd_symhash[i] = s->s_next;\n            if(s != &x->pd_s_pointer &&\n               s != &x->pd_s_float &&\n               s != &x->pd_s_symbol &&\n               s != &x->pd_s_bang &&\n               s != &x->pd_s_list &&\n               s != &x->pd_s_anything &&\n               s != &x->pd_s_signal &&\n               s != &x->pd_s__N &&\n               s != &x->pd_s__X &&\n               s != &x->pd_s_x &&\n               s != &x->pd_s_y &&\n               s != &x->pd_s_)\n            {\n                freebytes((void *)s->s_name, strlen(s->s_name)+1);\n                freebytes(s, sizeof(*s));\n            }\n        }\n    }\n    freebytes(x->pd_symhash, SYMTABHASHSIZE * sizeof (*x->pd_symhash));\n    x_midi_freepdinstance();\n    g_canvas_freepdinstance();\n    d_ugen_freepdinstance();\n    s_stuff_freepdinstance();\n    for (i = instanceno; i < pd_ninstances-1; i++)\n        pd_instances[i] = pd_instances[i+1];\n    pd_instances = (t_pdinstance **)resizebytes(pd_instances,\n        pd_ninstances * sizeof(*pd_instances),\n        (pd_ninstances-1) * sizeof(*pd_instances));\n    pd_ninstances--;\n    pdinstance_renumber();\n    pd_globalunlock();\n    sys_unlock();\n    pd_setinstance(&pd_maininstance);\n    s_inter_free(inter);  /* must happen after sys_unlock() */\n}\n\n#endif /* PDINSTANCE */\n\n/* this bootstraps the class management system (pd_objectmaker, pd_canvasmaker)\n * it has been moved from the bottom of the file up here, before the class_new() undefine\n */\nvoid mess_init(void)\n{\n    if (pd_objectmaker)\n        return;\n#ifdef PDINSTANCE\n    pd_this = &pd_maininstance;\n#endif\n    s_inter_newpdinstance();\n    sys_lock();\n    pd_globallock();\n    pdinstance_init(&pd_maininstance);\n    class_extern_dir = &s_;\n    pd_objectmaker = class_new(gensym(\"objectmaker\"), 0, 0, sizeof(t_pd),\n        CLASS_DEFAULT, A_NULL);\n    pd_canvasmaker = class_new(gensym(\"canvasmaker\"), 0, 0, sizeof(t_pd),\n        CLASS_DEFAULT, A_NULL);\n    class_addanything(pd_objectmaker, (t_method)new_anything);\n    pd_globalunlock();\n    sys_unlock();\n}\n\nstatic void pd_defaultanything(t_pd *x, t_symbol *s, int argc, t_atom *argv)\n{\n    pd_error(x, \"%s: no method for '%s'\", (*x)->c_name->s_name, s->s_name);\n}\n\nstatic void pd_defaultbang(t_pd *x)\n{\n    if (*(*x)->c_listmethod != pd_defaultlist)\n        (*(*x)->c_listmethod)(x, 0, 0, 0);\n    else (*(*x)->c_anymethod)(x, &s_bang, 0, 0);\n}\n\n    /* am empty list calls the 'bang' method unless it's the default\n    bang method -- that might turn around and call our 'list' method\n    which could be an infinite recorsion.  Fall through to calling our\n    'anything' method.  That had better not turn around and call us with\n    an empty list.  */\nvoid pd_emptylist(t_pd *x)\n{\n    if (*(*x)->c_bangmethod != pd_defaultbang)\n        (*(*x)->c_bangmethod)(x);\n    else (*(*x)->c_anymethod)(x, &s_bang, 0, 0);\n}\n\nstatic void pd_defaultpointer(t_pd *x, t_gpointer *gp)\n{\n    if (*(*x)->c_listmethod != pd_defaultlist)\n    {\n        t_atom at;\n        SETPOINTER(&at, gp);\n        (*(*x)->c_listmethod)(x, 0, 1, &at);\n    }\n    else\n    {\n        t_atom at;\n        SETPOINTER(&at, gp);\n        (*(*x)->c_anymethod)(x, &s_pointer, 1, &at);\n    }\n}\n\nstatic void pd_defaultfloat(t_pd *x, t_float f)\n{\n    if (*(*x)->c_listmethod != pd_defaultlist)\n    {\n        t_atom at;\n        SETFLOAT(&at, f);\n        (*(*x)->c_listmethod)(x, 0, 1, &at);\n    }\n    else\n    {\n        t_atom at;\n        SETFLOAT(&at, f);\n        (*(*x)->c_anymethod)(x, &s_float, 1, &at);\n    }\n}\n\nstatic void pd_defaultsymbol(t_pd *x, t_symbol *s)\n{\n    if (*(*x)->c_listmethod != pd_defaultlist)\n    {\n        t_atom at;\n        SETSYMBOL(&at, s);\n        (*(*x)->c_listmethod)(x, 0, 1, &at);\n    }\n    else\n    {\n        t_atom at;\n        SETSYMBOL(&at, s);\n        (*(*x)->c_anymethod)(x, &s_symbol, 1, &at);\n    }\n}\n\nvoid obj_list(t_object *x, t_symbol *s, int argc, t_atom *argv);\nstatic void class_nosavefn(t_gobj *z, t_binbuf *b);\n\n    /* handle \"list\" messages to Pds without explicit list methods defined. */\nstatic void pd_defaultlist(t_pd *x, t_symbol *s, int argc, t_atom *argv)\n{\n            /* a list with no elements is handled by the 'bang' method if\n            one exists. */\n    if (argc == 0 && *(*x)->c_bangmethod != pd_defaultbang)\n    {\n        (*(*x)->c_bangmethod)(x);\n        return;\n    }\n            /* a list with one element which is a number can be handled by a\n            \"float\" method if any is defined; same for \"symbol\", \"pointer\". */\n    if (argc == 1)\n    {\n        if (argv->a_type == A_FLOAT &&\n        *(*x)->c_floatmethod != pd_defaultfloat)\n        {\n            (*(*x)->c_floatmethod)(x, argv->a_w.w_float);\n            return;\n        }\n        else if (argv->a_type == A_SYMBOL &&\n            *(*x)->c_symbolmethod != pd_defaultsymbol)\n        {\n            (*(*x)->c_symbolmethod)(x, argv->a_w.w_symbol);\n            return;\n        }\n        else if (argv->a_type == A_POINTER &&\n            *(*x)->c_pointermethod != pd_defaultpointer)\n        {\n            (*(*x)->c_pointermethod)(x, argv->a_w.w_gpointer);\n            return;\n        }\n    }\n        /* Next try for an \"anything\" method */\n    if ((*x)->c_anymethod != pd_defaultanything)\n        (*(*x)->c_anymethod)(x, &s_list, argc, argv);\n\n        /* if the object is patchable (i.e., can have proper inlets)\n            send it on to obj_list which will unpack the list into the inlets */\n    else if ((*x)->c_patchable)\n        obj_list((t_object *)x, s, argc, argv);\n            /* otherwise gove up and complain. */\n    else pd_defaultanything(x, &s_list, argc, argv);\n}\n\n    /* for now we assume that all \"gobjs\" are text unless explicitly\n    overridden later by calling class_setbehavior().  I'm not sure\n    how to deal with Pds that aren't gobjs; shouldn't there be a\n    way to check that at run time?  Perhaps the presence of a \"newmethod\"\n    should be our cue, or perhaps the \"tiny\" flag.  */\n\n    /* another matter.  This routine does two unrelated things: it creates\n    a Pd class, but also adds a \"new\" method to create an instance of it.\n    These are combined for historical reasons and for brevity in writing\n    objects.  To avoid adding a \"new\" method send a null function pointer.\n    To add additional ones, use class_addcreator below.  Some \"classes\", like\n    \"select\", are actually two classes of the same name, one for the single-\n    argument form, one for the multiple one; see select_setup() to find out\n    how this is handled.  */\n\nextern void text_save(t_gobj *z, t_binbuf *b);\n\nt_class *class_new(t_symbol *s, t_newmethod newmethod, t_method freemethod,\n    size_t size, int flags, t_atomtype type1, ...)\n{\n    va_list ap;\n    t_atomtype vec[MAXPDARG+1], *vp = vec;\n    int count = 0, i;\n    t_class *c;\n    int typeflag = flags & CLASS_TYPEMASK;\n    if (!typeflag) typeflag = CLASS_PATCHABLE;\n    *vp = type1;\n\n    va_start(ap, type1);\n    while (*vp)\n    {\n        if (count == MAXPDARG)\n        {\n            if (s)\n                pd_error(0, \"class %s: sorry: only %d args typechecked; use A_GIMME\",\n                      s->s_name, MAXPDARG);\n            else\n                pd_error(0, \"unnamed class: sorry: only %d args typechecked; use A_GIMME\",\n                      MAXPDARG);\n            break;\n        }\n        vp++;\n        count++;\n        *vp = va_arg(ap, t_atomtype);\n    }\n    va_end(ap);\n\n    if (pd_objectmaker && newmethod)\n    {\n            /* add a \"new\" method by the name specified by the object */\n        class_addmethod(pd_objectmaker, (t_method)newmethod, s,\n            vec[0], vec[1], vec[2], vec[3], vec[4], vec[5]);\n        if (s && class_loadsym && !zgetfn(&pd_objectmaker, class_loadsym))\n        {\n                /* if we're loading an extern it might have been invoked by a\n                longer file name; in this case, make this an admissible name\n                too. */\n            const char *loadstring = class_loadsym->s_name;\n            size_t l1 = strlen(s->s_name), l2 = strlen(loadstring);\n            if (l2 > l1 && !strcmp(s->s_name, loadstring + (l2 - l1)))\n                class_addmethod(pd_objectmaker, (t_method)newmethod,\n                    class_loadsym,\n                    vec[0], vec[1], vec[2], vec[3], vec[4], vec[5]);\n        }\n    }\n    c = (t_class *)t_getbytes(sizeof(*c));\n    c->c_name = c->c_helpname = s;\n    c->c_size = size;\n    c->c_nmethod = 0;\n    c->c_freemethod = (t_method)freemethod;\n    c->c_bangmethod = pd_defaultbang;\n    c->c_pointermethod = pd_defaultpointer;\n    c->c_floatmethod = pd_defaultfloat;\n    c->c_symbolmethod = pd_defaultsymbol;\n    c->c_listmethod = pd_defaultlist;\n    c->c_anymethod = pd_defaultanything;\n        /* set default widget behavior.  Things like IEM GUIs override\n        this; they're patchable but have bespoke widget behaviors */\n    c->c_wb = (typeflag == CLASS_PATCHABLE ? &text_widgetbehavior : 0);\n    c->c_pwb = 0;\n    c->c_firstin = ((flags & CLASS_NOINLET) == 0);\n    c->c_patchable = (typeflag == CLASS_PATCHABLE);\n    c->c_gobj = (typeflag >= CLASS_GOBJ);\n    c->c_multichannel = (flags & CLASS_MULTICHANNEL) != 0;\n    c->c_nopromotesig = (flags & CLASS_NOPROMOTESIG) != 0;\n    c->c_nopromoteleft = (flags & CLASS_NOPROMOTELEFT) != 0;\n    c->c_drawcommand = 0;\n    c->c_floatsignalin = 0;\n    c->c_externdir = class_extern_dir;\n    c->c_savefn = (typeflag == CLASS_PATCHABLE ? text_save : class_nosavefn);\n    c->c_classfreefn = 0;\n#ifdef PDINSTANCE\n    c->c_methods = (t_methodentry **)t_getbytes(\n        pd_ninstances * sizeof(*c->c_methods));\n    for (i = 0; i < pd_ninstances; i++)\n        c->c_methods[i] = t_getbytes(0);\n    c->c_next = class_list;\n    class_list = c;\n#else\n    c->c_methods = t_getbytes(0);\n#endif\n#if 0       /* enable this if you want to see a list of all classes */\n    post(\"class: %s\", c->c_name->s_name);\n#endif\n    return (c);\n}\n\nvoid class_free(t_class *c)\n{\n    int i;\n#ifdef PDINSTANCE\n    t_class *prev;\n    if (class_list == c)\n        class_list = c->c_next;\n    else\n    {\n        prev = class_list;\n        while (prev->c_next != c)\n          prev = prev->c_next;\n        prev->c_next = c->c_next;\n    }\n#endif\n    if (c->c_classfreefn)\n        c->c_classfreefn(c);\n#ifdef PDINSTANCE\n    for (i = 0; i < pd_ninstances; i++)\n    {\n        if(c->c_methods[i])\n            freebytes(c->c_methods[i], c->c_nmethod * sizeof(*c->c_methods[i]));\n        c->c_methods[i] = NULL;\n    }\n    freebytes(c->c_methods, pd_ninstances * sizeof(*c->c_methods));\n#else\n    freebytes(c->c_methods, c->c_nmethod * sizeof(*c->c_methods));\n#endif\n    freebytes(c, sizeof(*c));\n}\n\nvoid class_setfreefn(t_class *c, t_classfreefn fn)\n{\n    c->c_classfreefn = fn;\n}\n\n#ifdef PDINSTANCE\nt_class *class_getfirst(void)\n{\n    return class_list;\n}\n#endif\n\n    /* add a creation method, which is a function that returns a Pd object\n    suitable for putting in an object box.  We presume you've got a class it\n    can belong to, but this won't be used until the newmethod is actually\n    called back (and the new method explicitly takes care of this.) */\n\nvoid class_addcreator(t_newmethod newmethod, t_symbol *s,\n    t_atomtype type1, ...)\n{\n    va_list ap;\n    t_atomtype vec[MAXPDARG+1], *vp = vec;\n    int count = 0;\n    *vp = type1;\n\n    va_start(ap, type1);\n    while (*vp)\n    {\n        if (count == MAXPDARG)\n        {\n            if(s)\n                pd_error(0, \"class %s: sorry: only %d creation args allowed\",\n                      s->s_name, MAXPDARG);\n            else\n                pd_error(0, \"unnamed class: sorry: only %d creation args allowed\",\n                      MAXPDARG);\n            break;\n        }\n        vp++;\n        count++;\n        *vp = va_arg(ap, t_atomtype);\n    }\n    va_end(ap);\n    class_addmethod(pd_objectmaker, (t_method)newmethod, s,\n        vec[0], vec[1], vec[2], vec[3], vec[4], vec[5]);\n}\n\nvoid class_addmethod(t_class *c, t_method fn, t_symbol *sel,\n    t_atomtype arg1, ...)\n{\n    va_list ap;\n    t_atomtype argtype = arg1;\n    int nargs, i;\n    if(!c)\n        return;\n    va_start(ap, arg1);\n        /* \"signal\" method specifies that we take audio signals but\n        that we don't want automatic float to signal conversion.  This\n        is obsolete; you should now use the CLASS_MAINSIGNALIN macro. */\n    if (sel == &s_signal)\n    {\n        if (c->c_floatsignalin)\n            post(\"warning: signal method overrides class_mainsignalin\");\n        c->c_floatsignalin = -1;\n    }\n        /* check for special cases.  \"Pointer\" is missing here so that\n        pd_objectmaker's pointer method can be typechecked differently.  */\n    if (sel == &s_bang)\n    {\n        if (argtype) goto phooey;\n        class_addbang(c, fn);\n    }\n    else if (sel == &s_float)\n    {\n        if (argtype != A_FLOAT || va_arg(ap, t_atomtype)) goto phooey;\n        class_doaddfloat(c, fn);\n    }\n    else if (sel == &s_symbol)\n    {\n        if (argtype != A_SYMBOL || va_arg(ap, t_atomtype)) goto phooey;\n        class_addsymbol(c, fn);\n    }\n    else if (sel == &s_list)\n    {\n        if (argtype != A_GIMME) goto phooey;\n        class_addlist(c, fn);\n    }\n    else if (sel == &s_anything)\n    {\n        if (argtype != A_GIMME) goto phooey;\n        class_addanything(c, fn);\n    }\n    else\n    {\n        unsigned char argvec[MAXPDARG+1];\n        nargs = 0;\n        while (argtype != A_NULL && nargs < MAXPDARG)\n        {\n            argvec[nargs++] = argtype;\n            argtype = va_arg(ap, t_atomtype);\n        }\n        if (argtype != A_NULL)\n            pd_error(0, \"%s_%s: only 5 arguments are typecheckable; use A_GIMME\",\n                (c->c_name)?(c->c_name->s_name):\"<anon>\", sel?(sel->s_name):\"<nomethod>\");\n        argvec[nargs] = 0;\n#ifdef PDINSTANCE\n        for (i = 0; i < pd_ninstances; i++)\n        {\n            class_addmethodtolist(c, &c->c_methods[i], c->c_nmethod,\n                (t_gotfn)fn, sel?dogensym(sel->s_name, 0, pd_instances[i]):0,\n                    argvec, pd_instances[i]);\n        }\n#else\n        class_addmethodtolist(c, &c->c_methods, c->c_nmethod,\n            (t_gotfn)fn, sel, argvec, &pd_maininstance);\n#endif\n        c->c_nmethod++;\n    }\n    goto done;\nphooey:\n    bug(\"class_addmethod: %s_%s: bad argument types\\n\",\n        (c->c_name)?(c->c_name->s_name):\"<anon>\", sel?(sel->s_name):\"<nomethod>\");\ndone:\n    va_end(ap);\n    return;\n}\n\n    /* Instead of these, see the \"class_addfloat\", etc.,  macros in m_pd.h */\nvoid class_addbang(t_class *c, t_method fn)\n{\n    if(!c)\n        return;\n    c->c_bangmethod = (t_bangmethod)fn;\n}\n\nvoid class_addpointer(t_class *c, t_method fn)\n{\n    if(!c)\n        return;\n    c->c_pointermethod = (t_pointermethod)fn;\n}\n\nvoid class_doaddfloat(t_class *c, t_method fn)\n{\n    if(!c)\n        return;\n    c->c_floatmethod = (t_floatmethod)fn;\n}\n\nvoid class_addsymbol(t_class *c, t_method fn)\n{\n    if(!c)\n        return;\n    c->c_symbolmethod = (t_symbolmethod)fn;\n}\n\nvoid class_addlist(t_class *c, t_method fn)\n{\n    if(!c)\n        return;\n    c->c_listmethod = (t_listmethod)fn;\n}\n\nvoid class_addanything(t_class *c, t_method fn)\n{\n    if(!c)\n        return;\n    c->c_anymethod = (t_anymethod)fn;\n}\n\nvoid class_setwidget(t_class *c, const t_widgetbehavior *w)\n{\n    if(!c)\n        return;\n    c->c_wb = w;\n}\n\nvoid class_setparentwidget(t_class *c, const t_parentwidgetbehavior *pw)\n{\n    if(!c)\n        return;\n    c->c_pwb = pw;\n}\n\nconst char *class_getname(const t_class *c)\n{\n    if(!c)\n        return 0;\n    return (c->c_name->s_name);\n}\n\nconst char *class_gethelpname(const t_class *c)\n{\n    if(!c)\n        return 0;\n    return (c->c_helpname->s_name);\n}\n\nvoid class_sethelpsymbol(t_class *c, t_symbol *s)\n{\n    if(!c)\n        return;\n    c->c_helpname = s;\n}\n\nconst t_parentwidgetbehavior *pd_getparentwidget(t_pd *x)\n{\n    return ((*x)->c_pwb);\n}\n\nvoid class_setdrawcommand(t_class *c)\n{\n    if(!c)\n        return;\n    c->c_drawcommand = 1;\n}\n\nint class_isdrawcommand(const t_class *c)\n{\n    if(!c)\n        return 0;\n    return (c->c_drawcommand);\n}\n\nstatic void pd_floatforsignal(t_pd *x, t_float f)\n{\n    int offset = (*x)->c_floatsignalin;\n    if (offset > 0)\n        *(t_float *)(((char *)x) + offset) = f;\n    else\n        pd_error(x, \"%s: float unexpected for signal input\",\n            (*x)->c_name->s_name);\n}\n\nvoid class_domainsignalin(t_class *c, int onset)\n{\n    if(!c)\n        return;\n    if (onset <= 0) onset = -1;\n    else\n    {\n        if (c->c_floatmethod != pd_defaultfloat)\n            post(\"warning: %s: float method overwritten\", c->c_name->s_name);\n        c->c_floatmethod = (t_floatmethod)pd_floatforsignal;\n    }\n    c->c_floatsignalin = onset;\n}\n\nvoid class_set_extern_dir(t_symbol *s)\n{\n    class_extern_dir = s;\n}\n\nconst char *class_gethelpdir(const t_class *c)\n{\n    if(!c)\n        return 0;\n    return (c->c_externdir->s_name);\n}\n\nstatic void class_nosavefn(t_gobj *z, t_binbuf *b)\n{\n    bug(\"save function called but not defined\");\n}\n\nvoid class_setsavefn(t_class *c, t_savefn f)\n{\n    if(!c)\n        return;\n    c->c_savefn = f;\n}\n\nt_savefn class_getsavefn(const t_class *c)\n{\n    if(!c)\n        return 0;\n    return (c->c_savefn);\n}\n\nvoid class_setpropertiesfn(t_class *c, t_propertiesfn f)\n{\n    if(!c)\n        return;\n    c->c_propertiesfn = f;\n}\n\nt_propertiesfn class_getpropertiesfn(const t_class *c)\n{\n    if(!c)\n        return 0;\n    return (c->c_propertiesfn);\n}\n\n/* ---------------- the symbol table ------------------------ */\n\nstatic t_symbol *dogensym(const char *s, t_symbol *oldsym,\n    t_pdinstance *pdinstance)\n{\n    char *symname = 0;\n    t_symbol **symhashloc, *sym2;\n    unsigned int hash = 5381;\n    int length = 0;\n    const char *s2 = s;\n    while (*s2) /* djb2 hash algo */\n    {\n        hash = ((hash << 5) + hash) + *s2;\n        length++;\n        s2++;\n    }\n    symhashloc = pdinstance->pd_symhash + (hash & (SYMTABHASHSIZE-1));\n    while ((sym2 = *symhashloc))\n    {\n        if (!strcmp(sym2->s_name, s))\n            return(sym2);\n        symhashloc = &sym2->s_next;\n    }\n    if (oldsym)\n        sym2 = oldsym;\n    else sym2 = (t_symbol *)t_getbytes(sizeof(*sym2));\n    symname = t_getbytes(length+1);\n    sym2->s_next = 0;\n    sym2->s_thing = 0;\n    strcpy(symname, s);\n    sym2->s_name = symname;\n    *symhashloc = sym2;\n    return (sym2);\n}\n\nt_symbol *gensym(const char *s)\n{\n    return(dogensym(s, 0, pd_this));\n}\n\nstatic t_symbol *addfileextent(t_symbol *s)\n{\n    char namebuf[MAXPDSTRING];\n    const char *str = s->s_name;\n    int ln = (int)strlen(str);\n    if (!strcmp(str + ln - 3, \".pd\")) return (s);\n    strcpy(namebuf, str);\n    strcpy(namebuf+ln, \".pd\");\n    return (gensym(namebuf));\n}\n\n#define MAXOBJDEPTH 1000\nstatic int tryingalready;\n\nvoid canvas_popabstraction(t_canvas *x);\n\nt_symbol* pathsearch(t_symbol *s,char* ext);\nint pd_setloadingabstraction(t_symbol *sym);\n\n    /* this routine is called when a new \"object\" is requested whose class Pd\n    doesn't know.  Pd tries to load it as an extern, then as an abstraction. */\nvoid new_anything(void *dummy, t_symbol *s, int argc, t_atom *argv)\n{\n    int fd;\n    char dirbuf[MAXPDSTRING], classslashclass[MAXPDSTRING], *nameptr;\n    if (tryingalready>MAXOBJDEPTH){\n      pd_error(0, \"maximum object loading depth %d reached\", MAXOBJDEPTH);\n      return;\n    }\n    if (s == &s_anything){\n      pd_error(0, \"object name \\\"%s\\\" not allowed\", s->s_name);\n      return;\n    }\n    pd_this->pd_newest = 0;\n    class_loadsym = s;\n    pd_globallock();\n    if (sys_load_lib(canvas_getcurrent(), s->s_name))\n    {\n        tryingalready++;\n        typedmess(dummy, s, argc, argv);\n        tryingalready--;\n        return;\n    }\n    class_loadsym = 0;\n    pd_globalunlock();\n}\n\n/* This is externally available, but note that it might later disappear; the\nwhole \"newest\" thing is a hack which needs to be redesigned. */\nt_pd *pd_newest(void)\n{\n    return (pd_this->pd_newest);\n}\n\n    /* horribly, we need prototypes for each of the artificial function\n    calls in typedmess(), to keep the compiler quiet. */\ntypedef t_pd *(*t_newgimme)(t_symbol *s, int argc, t_atom *argv);\ntypedef void(*t_messgimme)(t_pd *x, t_symbol *s, int argc, t_atom *argv);\n\ntypedef t_pd *(*t_fun0)(\n    t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5);\ntypedef t_pd *(*t_fun1)(t_int i1,\n    t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5);\ntypedef t_pd *(*t_fun2)(t_int i1, t_int i2,\n    t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5);\ntypedef t_pd *(*t_fun3)(t_int i1, t_int i2, t_int i3,\n    t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5);\ntypedef t_pd *(*t_fun4)(t_int i1, t_int i2, t_int i3, t_int i4,\n    t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5);\ntypedef t_pd *(*t_fun5)(t_int i1, t_int i2, t_int i3, t_int i4, t_int i5,\n    t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5);\ntypedef t_pd *(*t_fun6)(t_int i1, t_int i2, t_int i3, t_int i4, t_int i5, t_int i6,\n    t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5);\n\nvoid pd_typedmess(t_pd *x, t_symbol *s, int argc, t_atom *argv)\n{\n    t_method *f;\n    t_class *c = *x;\n    t_methodentry *m, *mlist;\n    unsigned char *wp, wanttype;\n    int i;\n    t_int ai[MAXPDARG+1], *ap = ai;\n    t_floatarg ad[MAXPDARG+1], *dp = ad;\n    int narg = 0;\n    t_pd *bonzo;\n\n        /* check for messages that are handled by fixed slots in the class\n        structure. */\n    if (s == &s_float)\n    {\n        if (!argc) (*c->c_floatmethod)(x, 0.);\n        else if (argv->a_type == A_FLOAT)\n            (*c->c_floatmethod)(x, argv->a_w.w_float);\n        else goto badarg;\n        return;\n    }\n    if (s == &s_bang)\n    {\n        (*c->c_bangmethod)(x);\n        return;\n    }\n    if (s == &s_list)\n    {\n        (*c->c_listmethod)(x, s, argc, argv);\n        return;\n    }\n    if (s == &s_symbol)\n    {\n        if (argc && argv->a_type == A_SYMBOL)\n            (*c->c_symbolmethod)(x, argv->a_w.w_symbol);\n        else\n            (*c->c_symbolmethod)(x, &s_);\n        return;\n    }\n        /* pd_objectmaker doesn't require\n        an actual pointer value */\n    if (s == &s_pointer && x != &pd_objectmaker)\n    {\n        if (argc && argv->a_type == A_POINTER)\n            (*c->c_pointermethod)(x, argv->a_w.w_gpointer);\n        else goto badarg;\n        return;\n    }\n#ifdef PDINSTANCE\n    mlist = c->c_methods[pd_this->pd_instanceno];\n#else\n    mlist = c->c_methods;\n#endif\n    for (i = c->c_nmethod, m = mlist; i--; m++)\n        if (m->me_name == s)\n    {\n        wp = m->me_arg;\n        if (*wp == A_GIMME)\n        {\n            if (x == &pd_objectmaker)\n                pd_this->pd_newest =\n                    (*((t_newgimme)(m->me_fun)))(s, argc, argv);\n            else (*((t_messgimme)(m->me_fun)))(x, s, argc, argv);\n            return;\n        }\n        if (argc > MAXPDARG) argc = MAXPDARG;\n        if (x != &pd_objectmaker) *(ap++) = (t_int)x, narg++;\n        while ((wanttype = *wp++))\n        {\n            switch (wanttype)\n            {\n            case A_POINTER:\n                if (!argc) goto badarg;\n                else\n                {\n                    if (argv->a_type == A_POINTER)\n                        *ap = (t_int)(argv->a_w.w_gpointer);\n                    else goto badarg;\n                    argc--;\n                    argv++;\n                }\n                narg++;\n                ap++;\n                break;\n            case A_FLOAT:\n                if (!argc) goto badarg;  /* falls through */\n            case A_DEFFLOAT:\n                if (!argc) *dp = 0;\n                else\n                {\n                    if (argv->a_type == A_FLOAT)\n                        *dp = argv->a_w.w_float;\n                    else goto badarg;\n                    argc--;\n                    argv++;\n                }\n                dp++;\n                break;\n            case A_SYMBOL:\n                if (!argc) goto badarg;  /* falls through */\n            case A_DEFSYM:\n                if (!argc) *ap = (t_int)(&s_);\n                else\n                {\n                    if (argv->a_type == A_SYMBOL)\n                        *ap = (t_int)(argv->a_w.w_symbol);\n                            /* if it's an unfilled \"dollar\" argument it appears\n                            as zero here; cheat and bash it to the null\n                            symbol.  Unfortunately, this lets real zeros\n                            pass as symbols too, which seems wrong... */\n                    else if (x == &pd_objectmaker && argv->a_type == A_FLOAT\n                        && argv->a_w.w_float == 0)\n                        *ap = (t_int)(&s_);\n                    else goto badarg;\n                    argc--;\n                    argv++;\n                }\n                narg++;\n                ap++;\n                break;\n            default:\n                goto badarg;\n            }\n        }\n        switch (narg)\n        {\n        case 0 : bonzo = (*(t_fun0)(m->me_fun))\n            (ad[0], ad[1], ad[2], ad[3], ad[4]); break;\n        case 1 : bonzo = (*(t_fun1)(m->me_fun))\n            (ai[0], ad[0], ad[1], ad[2], ad[3], ad[4]); break;\n        case 2 : bonzo = (*(t_fun2)(m->me_fun))\n            (ai[0], ai[1], ad[0], ad[1], ad[2], ad[3], ad[4]); break;\n        case 3 : bonzo = (*(t_fun3)(m->me_fun))\n            (ai[0], ai[1], ai[2], ad[0], ad[1], ad[2], ad[3], ad[4]); break;\n        case 4 : bonzo = (*(t_fun4)(m->me_fun))\n            (ai[0], ai[1], ai[2], ai[3],\n                ad[0], ad[1], ad[2], ad[3], ad[4]); break;\n        case 5 : bonzo = (*(t_fun5)(m->me_fun))\n            (ai[0], ai[1], ai[2], ai[3], ai[4],\n                ad[0], ad[1], ad[2], ad[3], ad[4]); break;\n        case 6 : bonzo = (*(t_fun6)(m->me_fun))\n            (ai[0], ai[1], ai[2], ai[3], ai[4], ai[5],\n                ad[0], ad[1], ad[2], ad[3], ad[4]); break;\n        default: bonzo = 0;\n        }\n        if (x == &pd_objectmaker)\n            pd_this->pd_newest = bonzo;\n        return;\n    }\n    (*c->c_anymethod)(x, s, argc, argv);\n    return;\nbadarg:\n    pd_error(x, \"bad arguments for message '%s' to object '%s'\",\n        s->s_name, c->c_name->s_name);\n}\n\n    /* convenience routine giving a stdarg interface to typedmess().  Only\n    ten args supported; it seems unlikely anyone will need more since\n    longer messages are likely to be programmatically generated anyway. */\nvoid pd_vmess(t_pd *x, t_symbol *sel, const char *fmt, ...)\n{\n    va_list ap;\n    t_atom arg[10], *at = arg;\n    int nargs = 0;\n    const char *fp = fmt;\n\n    va_start(ap, fmt);\n    while (1)\n    {\n        if (nargs >= 10)\n        {\n            pd_error(x, \"pd_vmess: only 10 allowed\");\n            break;\n        }\n        switch(*fp++)\n        {\n        case 'f': SETFLOAT(at, va_arg(ap, double)); break;\n        case 's': SETSYMBOL(at, va_arg(ap, t_symbol *)); break;\n        case 'i': SETFLOAT(at, va_arg(ap, t_int)); break;\n        case 'p': SETPOINTER(at, va_arg(ap, t_gpointer *)); break;\n        default: goto done;\n        }\n        at++;\n        nargs++;\n    }\ndone:\n    va_end(ap);\n    typedmess(x, sel, nargs, arg);\n}\n\nvoid pd_forwardmess(t_pd *x, int argc, t_atom *argv)\n{\n    if (argc)\n    {\n        t_atomtype t = argv->a_type;\n        if (t == A_SYMBOL) pd_typedmess(x, argv->a_w.w_symbol, argc-1, argv+1);\n        else if (t == A_POINTER)\n        {\n            if (argc == 1) pd_pointer(x, argv->a_w.w_gpointer);\n            else pd_list(x, &s_list, argc, argv);\n        }\n        else if (t == A_FLOAT)\n        {\n            if (argc == 1) pd_float(x, argv->a_w.w_float);\n            else pd_list(x, &s_list, argc, argv);\n        }\n        else bug(\"pd_forwardmess\");\n    }\n\n}\n\nvoid nullfn(void) {}\n\nt_gotfn getfn(const t_pd *x, t_symbol *s)\n{\n    const t_class *c = *x;\n    t_methodentry *m, *mlist;\n    int i;\n\n#ifdef PDINSTANCE\n    mlist = c->c_methods[pd_this->pd_instanceno];\n#else\n    mlist = c->c_methods;\n#endif\n    for (i = c->c_nmethod, m = mlist; i--; m++)\n        if (m->me_name == s) return(m->me_fun);\n    pd_error(x, \"%s: no method for message '%s'\", c->c_name->s_name, s->s_name);\n    return((t_gotfn)nullfn);\n}\n\nt_gotfn zgetfn(const t_pd *x, t_symbol *s)\n{\n    const t_class *c = *x;\n    t_methodentry *m, *mlist;\n    int i;\n\n#ifdef PDINSTANCE\n    mlist = c->c_methods[pd_this->pd_instanceno];\n#else\n    mlist = c->c_methods;\n#endif\n    for (i = c->c_nmethod, m = mlist; i--; m++)\n        if (m->me_name == s) return(m->me_fun);\n    return(0);\n}\n\nvoid c_extern(t_externclass *cls, t_newmethod newroutine,\n    t_method freeroutine, t_symbol *name, size_t size, int tiny, \\\n    t_atomtype arg1, ...)\n{\n    bug(\"'c_extern' not implemented.\");\n}\nvoid c_addmess(t_method fn, t_symbol *sel, t_atomtype arg1, ...)\n{\n    bug(\"'c_addmess' not implemented.\");\n}\n\n/* provide 'class_new' fallbacks, in case a double-precision Pd attempts to\n * load a single-precision external, or vice versa\n */\n#ifdef class_new\n# undef class_new\n#endif\nt_class *\n#if PD_FLOATSIZE == 32\n  class_new64\n#else\n  class_new\n#endif\n   (t_symbol *s, t_newmethod newmethod, t_method freemethod,\n    size_t size, int flags, t_atomtype type1, ...)\n{\n    const int ext_floatsize =\n#if PD_FLOATSIZE == 32\n        64\n#else\n        32\n#endif\n        ;\n    static int loglevel = 0;\n    if(s) {\n        logpost(0, loglevel, \"refusing to load %dbit-float object '%s' into %dbit-float Pd\", ext_floatsize, s->s_name, PD_FLOATSIZE);\n        loglevel=3;\n    } else\n        logpost(0, 3, \"refusing to load unnamed %dbit-float object into %dbit-float Pd\", ext_floatsize, PD_FLOATSIZE);\n\n    return 0;\n}\n\n/* this is privately shared with d_ugen.c */\nint class_getdspflags(const t_class *c)\n{\n    return ((c->c_multichannel ? CLASS_MULTICHANNEL : 0) |\n            (c->c_nopromotesig ? CLASS_NOPROMOTESIG : 0) |\n            (c->c_nopromoteleft ? CLASS_NOPROMOTELEFT : 0) );\n}\n\n"
  },
  {
    "path": "libs/libpd/pure-data/src/m_conf.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* changes by Thomas Musil IEM KUG Graz Austria 2001 */\n/* all changes are labeled with      iemlib      */\n\nvoid g_array_setup(void);\nvoid g_canvas_setup(void);\nvoid g_guiconnect_setup(void);\n/* iemlib */\nvoid g_bang_setup(void);\nvoid g_mycanvas_setup(void);\nvoid g_numbox_setup(void);\nvoid g_radio_setup(void);\nvoid g_slider_setup(void);\nvoid g_toggle_setup(void);\nvoid g_vumeter_setup(void);\n/* iemlib */\nvoid g_io_setup(void);\nvoid g_scalar_setup(void);\nvoid g_template_setup(void);\nvoid g_text_setup(void);\nvoid g_traversal_setup(void);\nvoid clone_setup(void);\nvoid m_pd_setup(void);\nvoid x_acoustics_setup(void);\nvoid x_interface_setup(void);\nvoid x_connective_setup(void);\nvoid x_time_setup(void);\nvoid x_arithmetic_setup(void);\nvoid x_array_setup(void);\nvoid x_midi_setup(void);\nvoid x_misc_setup(void);\nvoid x_net_setup(void);\nvoid x_file_setup(void);\nvoid x_qlist_setup(void);\nvoid x_gui_setup(void);\nvoid x_list_setup(void);\nvoid x_scalar_setup(void);\nvoid expr_setup(void);\nvoid d_arithmetic_setup(void);\nvoid d_array_setup(void);\nvoid d_ctl_setup(void);\nvoid d_dac_setup(void);\nvoid d_delay_setup(void);\nvoid d_fft_setup(void);\nvoid d_filter_setup(void);\nvoid d_global_setup(void);\nvoid d_math_setup(void);\nvoid d_misc_setup(void);\nvoid d_osc_setup(void);\nvoid d_soundfile_setup(void);\nvoid d_ugen_setup(void);\n\nvoid conf_init(void)\n{\n    g_array_setup();\n    g_canvas_setup();\n    g_guiconnect_setup();\n/* iemlib */\n    g_bang_setup();\n    g_mycanvas_setup();\n    g_numbox_setup();\n    g_radio_setup();\n    g_slider_setup();\n    g_toggle_setup();\n    g_vumeter_setup();\n/* iemlib */\n    g_io_setup();\n    g_scalar_setup();\n    g_template_setup();\n    g_text_setup();\n    g_traversal_setup();\n    clone_setup();\n    m_pd_setup();\n    x_acoustics_setup();\n    x_interface_setup();\n    x_connective_setup();\n    x_time_setup();\n    x_arithmetic_setup();\n    x_array_setup();\n    x_midi_setup();\n    x_misc_setup();\n    x_net_setup();\n    x_file_setup();\n    x_qlist_setup();\n    x_gui_setup();\n    x_list_setup();\n    x_scalar_setup();\n    expr_setup();\n    d_arithmetic_setup();\n    d_array_setup();\n    d_ctl_setup();\n    d_dac_setup();\n    d_delay_setup();\n    d_fft_setup();\n    d_filter_setup();\n    d_global_setup();\n    d_math_setup();\n    d_misc_setup();\n    d_osc_setup();\n    d_soundfile_setup();\n    d_ugen_setup();\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/m_glob.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n#include \"m_pd.h\"\n#include \"m_imp.h\"\n\nt_class *glob_pdobject;\nstatic t_class *maxclass;\n\nint sys_perf;   /* true if we should query user on close and quit */\nint pd_compatibilitylevel = 100000;  /* e.g., 43 for pd 0.43 compatibility */\n\n/* These \"glob\" routines, which implement messages to Pd, are from all\nover.  Some others are prototyped in m_imp.h as well. */\n\nvoid glob_menunew(void *dummy, t_symbol *name, t_symbol *dir);\nvoid glob_verifyquit(void *dummy, t_floatarg f);\nvoid glob_dsp(void *dummy, t_symbol *s, int argc, t_atom *argv);\nvoid glob_key(void *dummy, t_symbol *s, int ac, t_atom *av);\nvoid glob_audiostatus(void *dummy);\nvoid glob_finderror(t_pd *dummy);\nvoid glob_findinstance(t_pd *dummy, t_symbol*s);\nvoid glob_start_preference_dialog(t_pd *dummy, t_symbol*s);\nvoid glob_audio_properties(t_pd *dummy, t_floatarg flongform);\nvoid glob_audio_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv);\nvoid glob_audio_setapi(t_pd *dummy, t_floatarg f);\nvoid glob_midi_properties(t_pd *dummy, t_floatarg flongform);\nvoid glob_midi_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv);\nvoid glob_midi_setapi(t_pd *dummy, t_floatarg f);\nvoid glob_start_path_dialog(t_pd *dummy, t_floatarg flongform);\nvoid glob_path_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv);\nvoid glob_addtopath(t_pd *dummy, t_symbol *path, t_float saveit);\nvoid glob_start_startup_dialog(t_pd *dummy, t_floatarg flongform);\nvoid glob_startup_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv);\nvoid glob_ping(t_pd *dummy);\nvoid glob_plugindispatch(t_pd *dummy, t_symbol *s, int argc, t_atom *argv);\nvoid glob_watchdog(t_pd *dummy);\nvoid glob_loadpreferences(t_pd *dummy, t_symbol *s);\nvoid glob_savepreferences(t_pd *dummy, t_symbol *s);\nvoid glob_forgetpreferences(t_pd *dummy);\nvoid glob_open(t_pd *ignore, t_symbol *name, t_symbol *dir, t_floatarg f);\nvoid glob_fastforward(t_pd *ignore, t_floatarg f);\nvoid glob_settracing(void *dummy, t_float f);\n\nstatic void glob_helpintro(t_pd *dummy)\n{\n    open_via_helppath(\"intro.pd\", \"\");\n}\n\nstatic void glob_compatibility(t_pd *dummy, t_floatarg level)\n{\n    int dspwas = canvas_suspend_dsp();\n    pd_compatibilitylevel = 0.5 + 100. * level;\n    canvas_resume_dsp(dspwas);\n}\n\n#ifdef _WIN32\nvoid glob_audio(void *dummy, t_floatarg adc, t_floatarg dac);\n#endif\n\n/* a method you add for debugging printout */\nvoid glob_foo(void *dummy, t_symbol *s, int argc, t_atom *argv);\n\n#if 1\nvoid glob_foo(void *dummy, t_symbol *s, int argc, t_atom *argv)\n{\n#ifdef USEAPI_ALSA\n    void alsa_printstate(void);\n    alsa_printstate();\n#endif\n}\n#endif\n\nstatic void glob_version(t_pd *dummy, t_float f)\n{\n    if (f > (PD_MAJOR_VERSION + 0.01*PD_MINOR_VERSION + 0.001))\n    {\n        static int warned;\n        if (warned < 1)\n            post(\"warning: file format (%g) newer than this version (%g) of Pd\",\n                f, PD_MAJOR_VERSION + 0.01*PD_MINOR_VERSION);\n        else if (warned < 2)\n            post(\"(... more file format messages suppressed)\");\n        warned++;\n    }\n}\n\nstatic void glob_perf(t_pd *dummy, t_float f)\n{\n    sys_perf = (f != 0);\n}\n\nvoid max_default(t_pd *x, t_symbol *s, int argc, t_atom *argv)\n{\n    int i;\n    char str[80];\n    startpost(\"%s: unknown message %s \", class_getname(pd_class(x)),\n        s->s_name);\n    for (i = 0; i < argc; i++)\n    {\n        atom_string(argv+i, str, 80);\n        poststring(str);\n    }\n    endpost();\n}\n\nvoid glob_plugindispatch(t_pd *dummy, t_symbol *s, int argc, t_atom *argv)\n{\n    pdgui_vmess(\"pdtk_plugin_dispatch\", \"a\", argc, argv);\n}\n\nint sys_zoom_open = 1;\nvoid glob_zoom_open(t_pd *dummy, t_floatarg f)\n{\n    sys_zoom_open = (f != 0 ? 2 : 1);\n}\n\nvoid glob_init(void)\n{\n    maxclass = class_new(gensym(\"max\"), 0, 0, sizeof(t_pd),\n        CLASS_DEFAULT, A_NULL);\n    class_addanything(maxclass, max_default);\n    pd_bind(&maxclass, gensym(\"max\"));\n\n    glob_pdobject = class_new(gensym(\"pd\"), 0, 0, sizeof(t_pd),\n        CLASS_DEFAULT, A_NULL);\n    class_addmethod(glob_pdobject, (t_method)glob_initfromgui, gensym(\"init\"),\n        A_GIMME, 0);\n    class_addmethod(glob_pdobject, (t_method)glob_menunew, gensym(\"menunew\"),\n        A_SYMBOL, A_SYMBOL, 0);\n    class_addmethod(glob_pdobject, (t_method)glob_open, gensym(\"open\"),\n        A_SYMBOL, A_SYMBOL, A_DEFFLOAT, 0);\n    class_addmethod(glob_pdobject, (t_method)glob_exit, gensym(\"quit\"), A_DEFFLOAT, 0);\n    class_addmethod(glob_pdobject, (t_method)glob_verifyquit,\n        gensym(\"verifyquit\"), A_DEFFLOAT, 0);\n    class_addmethod(glob_pdobject, (t_method)glob_foo, gensym(\"foo\"), A_GIMME, 0);\n    class_addmethod(glob_pdobject, (t_method)glob_dsp, gensym(\"dsp\"), A_GIMME, 0);\n    class_addmethod(glob_pdobject, (t_method)glob_key, gensym(\"key\"), A_GIMME, 0);\n    class_addmethod(glob_pdobject, (t_method)glob_audiostatus,\n        gensym(\"audiostatus\"), 0);\n    class_addmethod(glob_pdobject, (t_method)glob_finderror,\n        gensym(\"finderror\"), 0);\n    class_addmethod(glob_pdobject, (t_method)glob_findinstance,\n        gensym(\"findinstance\"), A_SYMBOL, 0);\n    class_addmethod(glob_pdobject, (t_method)glob_start_preference_dialog,\n        gensym(\"start-preference-dialog\"), A_DEFSYM, 0);\n    class_addmethod(glob_pdobject, (t_method)glob_audio_properties,\n        gensym(\"audio-properties\"), A_DEFFLOAT, 0);\n    class_addmethod(glob_pdobject, (t_method)glob_audio_dialog,\n        gensym(\"audio-dialog\"), A_GIMME, 0);\n    class_addmethod(glob_pdobject, (t_method)glob_audio_setapi,\n        gensym(\"audio-setapi\"), A_FLOAT, 0);\n    class_addmethod(glob_pdobject, (t_method)glob_midi_setapi,\n        gensym(\"midi-setapi\"), A_FLOAT, 0);\n    class_addmethod(glob_pdobject, (t_method)glob_midi_properties,\n        gensym(\"midi-properties\"), A_DEFFLOAT, 0);\n    class_addmethod(glob_pdobject, (t_method)glob_midi_dialog,\n        gensym(\"midi-dialog\"), A_GIMME, 0);\n    class_addmethod(glob_pdobject, (t_method)glob_start_path_dialog,\n        gensym(\"start-path-dialog\"), 0);\n    class_addmethod(glob_pdobject, (t_method)glob_path_dialog,\n        gensym(\"path-dialog\"), A_GIMME, 0);\n    class_addmethod(glob_pdobject, (t_method)glob_addtopath,\n        gensym(\"add-to-path\"), A_SYMBOL, A_DEFFLOAT, 0);\n    class_addmethod(glob_pdobject, (t_method)glob_start_startup_dialog,\n        gensym(\"start-startup-dialog\"), 0);\n    class_addmethod(glob_pdobject, (t_method)glob_startup_dialog,\n        gensym(\"startup-dialog\"), A_GIMME, 0);\n    class_addmethod(glob_pdobject, (t_method)glob_ping, gensym(\"ping\"), 0);\n    class_addmethod(glob_pdobject, (t_method)glob_loadpreferences,\n        gensym(\"load-preferences\"), A_DEFSYM, 0);\n    class_addmethod(glob_pdobject, (t_method)glob_savepreferences,\n        gensym(\"save-preferences\"), A_DEFSYM, 0);\n    class_addmethod(glob_pdobject, (t_method)glob_forgetpreferences,\n        gensym(\"forget-preferences\"), A_DEFSYM, 0);\n    class_addmethod(glob_pdobject, (t_method)glob_zoom_open,\n        gensym(\"zoom-open\"), A_FLOAT, 0);\n    class_addmethod(glob_pdobject, (t_method)glob_version,\n        gensym(\"version\"), A_FLOAT, 0);\n    class_addmethod(glob_pdobject, (t_method)glob_perf,\n        gensym(\"perf\"), A_FLOAT, 0);\n    class_addmethod(glob_pdobject, (t_method)glob_compatibility,\n        gensym(\"compatibility\"), A_FLOAT, 0);\n    class_addmethod(glob_pdobject, (t_method)glob_plugindispatch,\n        gensym(\"plugin-dispatch\"), A_GIMME, 0);\n    class_addmethod(glob_pdobject, (t_method)glob_helpintro,\n        gensym(\"help-intro\"), A_GIMME, 0);\n    class_addmethod(glob_pdobject, (t_method)glob_fastforward,\n         gensym(\"fast-forward\"), A_FLOAT, 0);\n    class_addmethod(glob_pdobject, (t_method)glob_settracing,\n         gensym(\"set-tracing\"), A_FLOAT, 0);\n    class_addmethod(glob_pdobject, (t_method)glob_watchdog,\n        gensym(\"watchdog\"), 0);\n    class_addanything(glob_pdobject, max_default);\n    pd_bind(&glob_pdobject, gensym(\"pd\"));\n}\n\n    /* function to return version number at run time.  Any of the\n    calling pointers may be zero in case you don't need all of them. */\nvoid sys_getversion(int *major, int *minor, int *bugfix)\n{\n    if (major)\n        *major = PD_MAJOR_VERSION;\n    if (minor)\n        *minor = PD_MINOR_VERSION;\n    if (bugfix)\n        *bugfix = PD_BUGFIX_VERSION;\n}\n\nunsigned int sys_getfloatsize()\n{\n    return sizeof(t_float);\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/m_imp.h",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* This file contains function prototypes and data types used to implement\nPd, but not shared with Pd objects. */\n\n/* NOTE: this file describes Pd implementation details which may change\nin future releases.  The public (stable) API is in m_pd.h. */\n\n#ifndef __m_imp_h_\n\n/* the structure for a method handler ala Max */\ntypedef struct _methodentry\n{\n    t_symbol *me_name;\n    t_gotfn me_fun;\n    unsigned char me_arg[MAXPDARG+1];\n} t_methodentry;\n\nEXTERN_STRUCT _widgetbehavior;\n\ntypedef void (*t_bangmethod)(t_pd *x);\ntypedef void (*t_pointermethod)(t_pd *x, t_gpointer *gp);\ntypedef void (*t_floatmethod)(t_pd *x, t_float f);\ntypedef void (*t_symbolmethod)(t_pd *x, t_symbol *s);\ntypedef void (*t_listmethod)(t_pd *x, t_symbol *s, int argc, t_atom *argv);\ntypedef void (*t_anymethod)(t_pd *x, t_symbol *s, int argc, t_atom *argv);\n\nstruct _class\n{\n    t_symbol *c_name;                   /* name (mostly for error reporting) */\n    t_symbol *c_helpname;               /* name of help file */\n    t_symbol *c_externdir;              /* directory extern was loaded from */\n    size_t c_size;                      /* size of an instance */\n#ifdef PDINSTANCE\n    t_methodentry **c_methods;          /* methods other than bang, etc below */\n#else\n    t_methodentry *c_methods;\n#endif\n    int c_nmethod;                      /* number of methods */\n    t_method c_freemethod;              /* function to call before freeing */\n    t_bangmethod c_bangmethod;          /* common methods */\n    t_pointermethod c_pointermethod;\n    t_floatmethod c_floatmethod;\n    t_symbolmethod c_symbolmethod;\n    t_listmethod c_listmethod;\n    t_anymethod c_anymethod;\n    const struct _widgetbehavior *c_wb; /* \"gobjs\" only */\n    const struct _parentwidgetbehavior *c_pwb;/* widget behavior in parent */\n    t_savefn c_savefn;                  /* function to call when saving */\n    t_propertiesfn c_propertiesfn;      /* function to start prop dialog */\n    struct _class *c_next;\n    int c_floatsignalin;                /* onset to float for signal input */\n    unsigned int c_gobj:1;              /* true if is a gobj */\n    unsigned int c_patchable:1;         /* true if we have a t_object header */\n    unsigned int c_firstin:1;           /* if so, true if drawing first inlet */\n    unsigned int c_drawcommand:1;       /* drawing command for a template */\n    unsigned int c_multichannel:1;      /* can deal with multichannel sigs */\n    unsigned int c_nopromotesig:1;      /* don't promote scalars to signals */\n    unsigned int c_nopromoteleft:1;     /* not even the main (left) inlet */\n    t_classfreefn c_classfreefn;    /* function to call before freeing class */\n};\n\n/* m_pd.c */\nEXTERN void pd_init_systems(void);\nEXTERN void pd_term_systems(void);\n\n/* m_class.c */\nEXTERN void pd_emptylist(t_pd *x);\n\n/* m_obj.c */\nEXTERN int obj_noutlets(const t_object *x);\nEXTERN int obj_ninlets(const t_object *x);\nEXTERN t_outconnect *obj_starttraverseoutlet(const t_object *x, t_outlet **op,\n    int nout);\nEXTERN t_outconnect *obj_nexttraverseoutlet(t_outconnect *lastconnect,\n    t_object **destp, t_inlet **inletp, int *whichp);\nEXTERN t_outconnect *obj_connect(t_object *source, int outno,\n    t_object *sink, int inno);\nEXTERN void obj_disconnect(t_object *source, int outno, t_object *sink,\n    int inno);\nEXTERN void outlet_setstacklim(void);\nEXTERN int obj_issignalinlet(const t_object *x, int m);\nEXTERN int obj_issignaloutlet(const t_object *x, int m);\nEXTERN int obj_nsiginlets(const t_object *x);\nEXTERN int obj_nsigoutlets(const t_object *x);\nEXTERN int obj_siginletindex(const t_object *x, int m);\nEXTERN int obj_sigoutletindex(const t_object *x, int m);\nEXTERN t_float *obj_findsignalscalar(const t_object *x, int m);\n\n/* s_inter.c */\nvoid pd_globallock(void);\nvoid pd_globalunlock(void);\n\n/* misc */\n#ifndef SYMTABHASHSIZE  /* set this to, say, 1024 for small memory footprint */\n#define SYMTABHASHSIZE 16384\n#endif /* SYMTABHASHSIZE */\n\nEXTERN t_pd *glob_evalfile(t_pd *ignore, t_symbol *name, t_symbol *dir);\nEXTERN void glob_initfromgui(void *dummy, t_symbol *s, int argc, t_atom *argv);\nEXTERN void glob_quit(void *dummy); /* glob_exit(0); */\nEXTERN void glob_exit(void *dummy, t_float status);\nEXTERN void open_via_helppath(const char *name, const char *dir);\n\n#define __m_imp_h_\n#endif /* __m_imp_h_ */\n"
  },
  {
    "path": "libs/libpd/pure-data/src/m_memory.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n#include <stdlib.h>\n#include <string.h>\n#include \"m_pd.h\"\n#if (defined LOUD) || (defined DEBUGMEM)\n# include <stdio.h>\n#endif\n\n/* #define DEBUGMEM */\n#ifdef DEBUGMEM\nstatic int totalmem = 0;\n#endif\n\nvoid *getbytes(size_t nbytes)\n{\n    void *ret;\n    if (nbytes < 1) nbytes = 1;\n    ret = (void *)calloc(nbytes, 1);\n#ifdef LOUD\n    fprintf(stderr, \"new  %lx %d\\n\", (int)ret, nbytes);\n#endif /* LOUD */\n#ifdef DEBUGMEM\n    totalmem += nbytes;\n#endif\n    if (!ret)\n        post(\"pd: getbytes() failed -- out of memory\");\n    return (ret);\n}\n\nvoid *getzbytes(size_t nbytes)  /* obsolete name */\n{\n    return (getbytes(nbytes));\n}\n\nvoid *copybytes(const void *src, size_t nbytes)\n{\n    void *ret;\n    ret = getbytes(nbytes);\n    if (nbytes && ret)\n        memcpy(ret, src, nbytes);\n    return (ret);\n}\n\nvoid *resizebytes(void *old, size_t oldsize, size_t newsize)\n{\n    void *ret;\n    if (newsize < 1) newsize = 1;\n    if (oldsize < 1) oldsize = 1;\n    ret = (void *)realloc((char *)old, newsize);\n    if (newsize > oldsize && ret)\n        memset(((char *)ret) + oldsize, 0, newsize - oldsize);\n#ifdef LOUD\n    fprintf(stderr, \"resize %lx %d --> %lx %d\\n\", (int)old, oldsize, (int)ret, newsize);\n#endif /* LOUD */\n#ifdef DEBUGMEM\n    totalmem += (newsize - oldsize);\n#endif\n    if (!ret)\n        post(\"pd: resizebytes() failed -- out of memory\");\n    return (ret);\n}\n\nvoid freebytes(void *fatso, size_t nbytes)\n{\n    if (nbytes == 0)\n        nbytes = 1;\n#ifdef LOUD\n    fprintf(stderr, \"free %lx %d\\n\", (int)fatso, nbytes);\n#endif /* LOUD */\n#ifdef DEBUGMEM\n    totalmem -= nbytes;\n#endif\n    free(fatso);\n}\n\n#ifdef DEBUGMEM\nvoid glob_foo(void *dummy, t_symbol *s, int argc, t_atom *argv)\n{\n    fprintf(stderr, \"total mem %d\\n\", totalmem);\n}\n#endif\n"
  },
  {
    "path": "libs/libpd/pure-data/src/m_obj.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* this file handles Max-style patchable objects, i.e., objects which\ncan interconnect via inlets and outlets; also, the (terse) generic\nbehavior for \"gobjs\" appears at the end of this file.  */\n\n#include \"m_pd.h\"\n#include \"m_imp.h\"\n#include <string.h>\n\n#include \"m_private_utils.h\"\n\n#if defined(_MSC_VER)\n#define INLINE __forceinline\n#elif defined(__GNUC__)\n#define INLINE inline __attribute__((always_inline))\n#else\n#define INLINE inline\n#endif\n\nunion inletunion\n{\n    t_symbol *iu_symto;\n    t_gpointer *iu_pointerslot;\n    t_float *iu_floatslot;\n    t_symbol **iu_symslot;\n    t_float iu_floatsignalvalue;\n};\n\nstruct _inlet\n{\n    t_pd i_pd;\n    struct _inlet *i_next;\n    t_object *i_owner;\n    t_pd *i_dest;\n    t_symbol *i_symfrom;\n    union inletunion i_un;\n};\n\n#define i_symto i_un.iu_symto\n#define i_pointerslot i_un.iu_pointerslot\n#define i_floatslot i_un.iu_floatslot\n#define i_symslot i_un.iu_symslot\n\nstatic t_class *inlet_class, *pointerinlet_class, *floatinlet_class,\n    *symbolinlet_class;\n\n#define ISINLET(pd) ((*(pd) == inlet_class) || \\\n    (*(pd) == pointerinlet_class) || \\\n    (*(pd) == floatinlet_class) || \\\n    (*(pd) == symbolinlet_class))\n\n/* --------------------- generic inlets ala max ------------------ */\n\nt_inlet *inlet_new(t_object *owner, t_pd *dest, t_symbol *s1, t_symbol *s2)\n{\n    t_inlet *x = (t_inlet *)pd_new(inlet_class), *y, *y2;\n    x->i_owner = owner;\n    x->i_dest = dest;\n    if (s1 == &s_signal)\n        x->i_un.iu_floatsignalvalue = 0;\n    else x->i_symto = s2;\n    x->i_symfrom = s1;\n    x->i_next = 0;\n    if ((y = owner->ob_inlet))\n    {\n        while ((y2 = y->i_next)) y = y2;\n        y->i_next = x;\n    }\n    else owner->ob_inlet = x;\n    return (x);\n}\n\nt_inlet *signalinlet_new(t_object *owner, t_float f)\n{\n    t_inlet *x = inlet_new(owner, &owner->ob_pd, &s_signal, &s_signal);\n    x->i_un.iu_floatsignalvalue = f;\n    return (x);\n}\n\nstatic void inlet_wrong(t_inlet *x, t_symbol *s)\n{\n    pd_error(x->i_owner, \"inlet: expected '%s' but got '%s'\",\n        x->i_symfrom->s_name, s->s_name);\n}\n\nstatic void inlet_list(t_inlet *x, t_symbol *s, int argc, t_atom *argv);\nextern t_class *vinlet_class;\n\n    /* LATER figure out how to make these efficient: */\nstatic void inlet_bang(t_inlet *x)\n{\n    if (x->i_symfrom == &s_bang)\n        pd_vmess(x->i_dest, x->i_symto, \"\");\n    else if (!x->i_symfrom) pd_bang(x->i_dest);\n    else if (x->i_symfrom == &s_list)\n        inlet_list(x, &s_bang, 0, 0);\n    else if (x->i_symfrom == &s_signal && zgetfn(x->i_dest, gensym(\"fwd\")))\n        vmess(x->i_dest, gensym(\"fwd\"), \"s\", &s_bang);\n    else inlet_wrong(x, &s_bang);\n}\n\nstatic void inlet_pointer(t_inlet *x, t_gpointer *gp)\n{\n    if (x->i_symfrom == &s_pointer)\n        pd_vmess(x->i_dest, x->i_symto, \"p\", gp);\n    else if (!x->i_symfrom) pd_pointer(x->i_dest, gp);\n    else if (x->i_symfrom == &s_list)\n    {\n        t_atom a;\n        SETPOINTER(&a, gp);\n        inlet_list(x, &s_pointer, 1, &a);\n    }\n    else inlet_wrong(x, &s_pointer);\n}\n\nstatic void inlet_float(t_inlet *x, t_float f)\n{\n    if (x->i_symfrom == &s_float)\n        pd_vmess(x->i_dest, x->i_symto, \"f\", (t_floatarg)f);\n    else if (x->i_symfrom == &s_signal)\n        x->i_un.iu_floatsignalvalue = f;\n    else if (!x->i_symfrom)\n        pd_float(x->i_dest, f);\n    else if (x->i_symfrom == &s_list)\n    {\n        t_atom a;\n        SETFLOAT(&a, f);\n        inlet_list(x, &s_float, 1, &a);\n    }\n    else inlet_wrong(x, &s_float);\n}\n\nstatic void inlet_symbol(t_inlet *x, t_symbol *s)\n{\n    if (x->i_symfrom == &s_symbol)\n        pd_vmess(x->i_dest, x->i_symto, \"s\", s);\n    else if (!x->i_symfrom) pd_symbol(x->i_dest, s);\n    else if (x->i_symfrom == &s_list)\n    {\n        t_atom a;\n        SETSYMBOL(&a, s);\n        inlet_list(x, &s_symbol, 1, &a);\n    }\n    else if (x->i_symfrom == &s_signal && zgetfn(x->i_dest, gensym(\"fwd\")))\n        vmess(x->i_dest, gensym(\"fwd\"), \"ss\", &s_symbol, s);\n    else inlet_wrong(x, &s_symbol);\n}\n\n    /* forward a message to an inlet~ object */\nstatic void inlet_fwd(t_inlet *x, t_symbol *s, int argc, t_atom *argv)\n{\n    t_atom *argvec = (t_atom *)alloca((argc+1) * sizeof(t_atom));\n    int i;\n    SETSYMBOL(argvec, s);\n    for (i = 0; i < argc; i++)\n        argvec[i+1] = argv[i];\n    typedmess(x->i_dest, gensym(\"fwd\"), argc+1, argvec);\n}\n\nstatic void inlet_list(t_inlet *x, t_symbol *s, int argc, t_atom *argv)\n{\n    t_atom at;\n    if (x->i_symfrom == &s_list || x->i_symfrom == &s_float\n        || x->i_symfrom == &s_symbol || x->i_symfrom == &s_pointer)\n            typedmess(x->i_dest, x->i_symto, argc, argv);\n    else if (!x->i_symfrom) pd_list(x->i_dest, s, argc, argv);\n    else if (!argc)\n      inlet_bang(x);\n    else if (argc==1 && argv->a_type == A_FLOAT)\n      inlet_float(x, atom_getfloat(argv));\n    else if (argc==1 && argv->a_type == A_SYMBOL)\n      inlet_symbol(x, atom_getsymbol(argv));\n    else if (x->i_symfrom == &s_signal && zgetfn(x->i_dest, gensym(\"fwd\")))\n        inlet_fwd(x, &s_list, argc, argv);\n    else post(\"class %s\", class_getname(*x->i_dest)), inlet_wrong(x, &s_list);\n}\n\nstatic void inlet_anything(t_inlet *x, t_symbol *s, int argc, t_atom *argv)\n{\n    if (x->i_symfrom == s)\n    {\n        /* the \"symto\" field is undefined for signal inlets, so we don't\n         attempt to translate the selector, just forward the original msg. */\n\n        if (x->i_symfrom == &s_signal)\n            typedmess(x->i_dest, s, argc, argv);\n        else\n            typedmess(x->i_dest, x->i_symto, argc, argv);\n    }\n    else if (!x->i_symfrom)\n        typedmess(x->i_dest, s, argc, argv);\n    else if (x->i_symfrom == &s_signal && zgetfn(x->i_dest, gensym(\"fwd\")))\n        inlet_fwd(x, s, argc, argv);\n    else inlet_wrong(x, s);\n}\n\nvoid inlet_free(t_inlet *x)\n{\n    t_object *y = x->i_owner;\n    t_inlet *x2;\n    if (y->ob_inlet == x) y->ob_inlet = x->i_next;\n    else for (x2 = y->ob_inlet; x2; x2 = x2->i_next)\n        if (x2->i_next == x)\n    {\n        x2->i_next = x->i_next;\n        break;\n    }\n    t_freebytes(x, sizeof(*x));\n}\n\n/* ----- pointerinlets, floatinlets, syminlets: optimized inlets ------- */\n\nstatic void pointerinlet_pointer(t_inlet *x, t_gpointer *gp)\n{\n    gpointer_unset(x->i_pointerslot);\n    *(x->i_pointerslot) = *gp;\n    if (gp->gp_stub) gp->gp_stub->gs_refcount++;\n}\n\nt_inlet *pointerinlet_new(t_object *owner, t_gpointer *gp)\n{\n    t_inlet *x = (t_inlet *)pd_new(pointerinlet_class), *y, *y2;\n    x->i_owner = owner;\n    x->i_dest = 0;\n    x->i_symfrom = &s_pointer;\n    x->i_pointerslot = gp;\n    x->i_next = 0;\n    if ((y = owner->ob_inlet))\n    {\n        while ((y2 = y->i_next)) y = y2;\n        y->i_next = x;\n    }\n    else owner->ob_inlet = x;\n    return (x);\n}\n\nstatic void floatinlet_float(t_inlet *x, t_float f)\n{\n    *(x->i_floatslot) = f;\n}\n\nt_inlet *floatinlet_new(t_object *owner, t_float *fp)\n{\n    t_inlet *x = (t_inlet *)pd_new(floatinlet_class), *y, *y2;\n    x->i_owner = owner;\n    x->i_dest = 0;\n    x->i_symfrom = &s_float;\n    x->i_floatslot = fp;\n    x->i_next = 0;\n    if ((y = owner->ob_inlet))\n    {\n        while ((y2 = y->i_next)) y = y2;\n        y->i_next = x;\n    }\n    else owner->ob_inlet = x;\n    return (x);\n}\n\nstatic void symbolinlet_symbol(t_inlet *x, t_symbol *s)\n{\n    *(x->i_symslot) = s;\n}\n\nt_inlet *symbolinlet_new(t_object *owner, t_symbol **sp)\n{\n    t_inlet *x = (t_inlet *)pd_new(symbolinlet_class), *y, *y2;\n    x->i_owner = owner;\n    x->i_dest = 0;\n    x->i_symfrom = &s_symbol;\n    x->i_symslot = sp;\n    x->i_next = 0;\n    if ((y = owner->ob_inlet))\n    {\n        while ((y2 = y->i_next)) y = y2;\n        y->i_next = x;\n    }\n    else owner->ob_inlet = x;\n    return (x);\n}\n\n/* ---------------------- routine to handle lists ---------------------- */\n\n    /* objects interpret lists by feeding them to the individual inlets.\n    Before you call this check that the object doesn't have a more\n    specific way to handle lists. */\nvoid obj_list(t_object *x, t_symbol *s, int argc, t_atom *argv)\n{\n    t_atom *ap;\n    int count;\n    t_inlet *ip = ((t_object *)x)->ob_inlet;\n    if (!argc)\n    {\n        pd_emptylist(&x->ob_pd);\n        return;\n    }\n    for (count = argc-1, ap = argv+1; ip && count--; ap++, ip = ip->i_next)\n    {\n        if (ap->a_type == A_POINTER) pd_pointer(&ip->i_pd, ap->a_w.w_gpointer);\n        else if (ap->a_type == A_FLOAT) pd_float(&ip->i_pd, ap->a_w.w_float);\n        else pd_symbol(&ip->i_pd, ap->a_w.w_symbol);\n    }\n    if (argv->a_type == A_POINTER) pd_pointer(&x->ob_pd, argv->a_w.w_gpointer);\n    else if (argv->a_type == A_FLOAT) pd_float(&x->ob_pd, argv->a_w.w_float);\n    else pd_symbol(&x->ob_pd, argv->a_w.w_symbol);\n}\n\n/* --------------------------- outlets ------------------------------ */\n\n\nstruct _outconnect\n{\n    struct _outconnect *oc_next;\n    t_pd *oc_to;\n};\n\nstruct _outlet\n{\n    t_object *o_owner;\n    struct _outlet *o_next;\n    t_outconnect *o_connections;\n    t_symbol *o_sym;\n};\n\n/* ------- backtracer - keep track of stack for backtracing  --------- */\n#define NARGS 5\ntypedef struct _msgstack\n{\n    struct _backtracer *m_owner;\n    t_symbol *m_sel;\n    int m_argc;\n    t_atom m_argv[NARGS];\n    struct _msgstack *m_next;\n} t_msgstack;\n\ntypedef struct _backtracer\n{\n    t_pd b_pd;\n    t_outconnect *b_connections;\n    t_pd *b_owner;\n} t_backtracer;\n\nstatic t_msgstack *backtracer_stack;\nint backtracer_cantrace = 0;\nint backtracer_tracing;\nt_class *backtracer_class;\n\nstatic PERTHREAD int stackcount = 0; /* iteration counter */\nstatic PERTHREAD int overflow = 0;\n#define STACKITER 1000 /* maximum iterations allowed */\n\nstatic PERTHREAD int outlet_eventno;\n\n    /* initialize stack depth count on each incoming event that can set off\n    messages so that  the outlet functions can check to prevent stack overflow]\n    from message recursion.  Also count message initiations. */\n\nstatic INLINE int stackcount_add(void)\n{\n        /* set overflow flag to prevent any further messaging */\n    if (++stackcount >= STACKITER)\n        overflow = 1;\n    return !overflow;\n}\n\nstatic INLINE void stackcount_release(void)\n{\n        /* once the stack is completely unwound, we can clear the overflow flag */\n    if (--stackcount == 0)\n        overflow = 0;\n}\n\nvoid outlet_setstacklim(void)\n{\n    t_msgstack *m;\n    while ((m = backtracer_stack))\n        backtracer_stack = m->m_next, t_freebytes(m, sizeof (*m));\n    stackcount = 0;\n    outlet_eventno++;\n}\n\n    /* get a number unique to the (clock, MIDI, GUI, etc.) event we're on */\nint sched_geteventno(void)\n{\n    return (outlet_eventno);\n}\n\n    /* get pointer to connection list for an outlet (for editing/traversing) */\nstatic t_outconnect **outlet_getconnectionpointer(t_outlet *x)\n{\n    if (x->o_connections && *(x->o_connections->oc_to) == backtracer_class)\n        return (&((t_backtracer *)(x->o_connections->oc_to))->b_connections);\n    else return (&x->o_connections);\n}\n\nstatic void backtracer_printmsg(t_pd *who, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    char msgbuf[104];\n    int nprint = (argc > NARGS ? NARGS : argc), nchar, i;\n    snprintf(msgbuf, 100, \"%s: %s \", class_getname(*who), s->s_name);\n    nchar = strlen(msgbuf);\n    for (i = 0; i < nprint && nchar < 100; i++)\n        if (nchar < 100)\n    {\n        char buf[100];\n        atom_string(&argv[i], buf, 100);\n        snprintf(msgbuf + nchar, 100-nchar, \" %s\", buf);\n        nchar = strlen(msgbuf);\n    }\n    if (argc > nprint && nchar < 100)\n        sprintf(msgbuf + nchar, \"...\");\n    else memcpy(msgbuf+100, \"...\", 4); /* in case we didn't finish */\n    logpost(who, 2, \"%s\", msgbuf);\n}\n\nstatic void backtracer_anything(t_backtracer *x, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    t_msgstack *m = (t_msgstack *)t_getbytes(sizeof(t_msgstack));\n    t_outconnect *oc;\n    int ncopy = (argc > NARGS ? NARGS : argc), i;\n    m->m_next = backtracer_stack;\n    backtracer_stack = m;\n    m->m_sel = s;\n    m->m_argc = argc;\n    for (i = 0; i < ncopy; i++)\n        m->m_argv[i] = argv[i];\n    m->m_owner = x;\n    if (backtracer_tracing)\n        backtracer_printmsg(x->b_owner, s, argc, argv);\n    for (oc = x->b_connections; oc; oc = oc->oc_next)\n        typedmess(oc->oc_to, s, argc, argv);\n    backtracer_stack = m->m_next;\n    t_freebytes(m, sizeof(*m));\n}\n\nt_backtracer *backtracer_new(t_pd *owner)\n{\n    t_backtracer *x = (t_backtracer *)pd_new(backtracer_class);\n    x->b_connections = 0;\n    x->b_owner = owner;\n    return (x);\n}\n\nint backtracer_settracing(void *x, int tracing)\n{\n    if (tracing)\n    {\n        if (backtracer_tracing)\n        {\n            pd_error(x, \"trace: already tracing\");\n            return (0);\n        }\n        else\n        {\n            backtracer_tracing = 1;\n            return (1);\n        }\n    }\n    else    /* when stopping, print backtrace to here */\n    {\n        t_msgstack *m = backtracer_stack;\n        post(\"backtrace:\");\n        while (m)\n        {\n            backtracer_printmsg(m->m_owner->b_owner, m->m_sel,\n                m->m_argc, m->m_argv);\n            m = m->m_next;\n        }\n        backtracer_tracing = 0;\n        return (0);\n    }\n}\n\nvoid canvas_settracing(int onoff);\nstatic t_clock *backtrace_unsetclock;\n\nstatic void backtrace_dounsettracing(void *dummy)\n{\n    canvas_settracing(0);\n    backtracer_cantrace = 0;\n    clock_free(backtrace_unsetclock);\n    backtrace_unsetclock = 0;\n}\n\n    /* globally turn tracing on and off. */\nvoid glob_settracing(void *dummy, t_float f)\n{\n#ifndef PDINSTANCE  /* this won't work with pd instances so just don't */\n    if (f != 0)\n    {\n        if (backtracer_cantrace)\n            post(\"pd: tracing already enabled\");\n        else canvas_settracing(1);\n        backtracer_cantrace = 1;\n    }\n    else\n    {\n        if (!backtracer_cantrace)\n            post(\"pd: tracing already disabled\");\n        else if (!backtrace_unsetclock)\n        {\n            backtrace_unsetclock = clock_new(dummy,\n                (t_method)backtrace_dounsettracing);\n            clock_delay(backtrace_unsetclock, 0);\n        }\n    }\n#endif\n}\n\n    /* this is called on every object, via canvas_settracing() call above */\nvoid obj_dosettracing(t_object *ob, int onoff)\n{\n    t_outlet *o;\n    for (o = ob->ob_outlet; o; o = o->o_next)\n    {\n        if (onoff)\n        {\n            t_backtracer *b = backtracer_new(&ob->ob_pd);\n            b->b_connections = o->o_connections;\n            o->o_connections =  (t_outconnect *)t_getbytes(sizeof(t_outconnect));\n            o->o_connections->oc_next = 0;\n            o->o_connections->oc_to = &b->b_pd;\n        }\n        else if (o->o_connections &&\n            (*o->o_connections->oc_to == backtracer_class))\n        {\n            t_backtracer *b = (t_backtracer *)o->o_connections->oc_to;\n            t_freebytes(o->o_connections, sizeof(*o->o_connections));\n            o->o_connections = b->b_connections;\n            t_freebytes(b, sizeof(*b));\n        }\n        else bug(\"obj_dosettracing\");\n    }\n}\n\nt_outlet *outlet_new(t_object *owner, t_symbol *s)\n{\n    t_outlet *x = (t_outlet *)getbytes(sizeof(*x)), *y, *y2;\n    x->o_owner = owner;\n    x->o_next = 0;\n    if ((y = owner->ob_outlet))\n    {\n        while ((y2 = y->o_next)) y = y2;\n        y->o_next = x;\n    }\n    else owner->ob_outlet = x;\n    if (backtracer_cantrace)\n    {\n        t_backtracer *b = backtracer_new(&owner->ob_pd);\n        x->o_connections =  (t_outconnect *)t_getbytes(sizeof(t_outconnect));\n        x->o_connections->oc_next = 0;\n        x->o_connections->oc_to = &b->b_pd;\n    }\n    else x->o_connections = 0;\n    x->o_sym = s;\n    return (x);\n}\n\nstatic void outlet_stackerror(t_outlet *x)\n{\n    pd_error(x->o_owner, \"stack overflow\");\n}\n\nvoid outlet_bang(t_outlet *x)\n{\n    t_outconnect *oc;\n    if(!stackcount_add())\n        outlet_stackerror(x);\n    else\n        for (oc = x->o_connections; oc; oc = oc->oc_next)\n            pd_bang(oc->oc_to);\n    stackcount_release();\n}\n\nvoid outlet_pointer(t_outlet *x, t_gpointer *gp)\n{\n    t_outconnect *oc;\n    t_gpointer gpointer;\n    if(!stackcount_add())\n        outlet_stackerror(x);\n    else\n    {\n        gpointer = *gp;\n        for (oc = x->o_connections; oc; oc = oc->oc_next)\n            pd_pointer(oc->oc_to, &gpointer);\n    }\n    stackcount_release();\n}\n\nvoid outlet_float(t_outlet *x, t_float f)\n{\n    t_outconnect *oc;\n    if(!stackcount_add())\n        outlet_stackerror(x);\n    else\n        for (oc = x->o_connections; oc; oc = oc->oc_next)\n            pd_float(oc->oc_to, f);\n    stackcount_release();\n}\n\nvoid outlet_symbol(t_outlet *x, t_symbol *s)\n{\n    t_outconnect *oc;\n    if(!stackcount_add())\n        outlet_stackerror(x);\n    else\n        for (oc = x->o_connections; oc; oc = oc->oc_next)\n            pd_symbol(oc->oc_to, s);\n    stackcount_release();\n}\n\nvoid outlet_list(t_outlet *x, t_symbol *s, int argc, t_atom *argv)\n{\n    t_outconnect *oc;\n    if(!stackcount_add())\n        outlet_stackerror(x);\n    else\n        for (oc = x->o_connections; oc; oc = oc->oc_next)\n            pd_list(oc->oc_to, s, argc, argv);\n    stackcount_release();\n}\n\nvoid outlet_anything(t_outlet *x, t_symbol *s, int argc, t_atom *argv)\n{\n    t_outconnect *oc;\n    if(!stackcount_add())\n        outlet_stackerror(x);\n    else\n        for (oc = x->o_connections; oc; oc = oc->oc_next)\n            typedmess(oc->oc_to, s, argc, argv);\n    stackcount_release();\n}\n\n    /* get the outlet's declared symbol */\nt_symbol *outlet_getsymbol(t_outlet *x)\n{\n    return (x->o_sym);\n}\n\nvoid outlet_free(t_outlet *x)\n{\n    t_object *y = x->o_owner;\n    t_outlet *x2;\n    if (y->ob_outlet == x) y->ob_outlet = x->o_next;\n    else for (x2 = y->ob_outlet; x2; x2 = x2->o_next)\n        if (x2->o_next == x)\n    {\n        x2->o_next = x->o_next;\n        break;\n    }\n    t_freebytes(x, sizeof(*x));\n}\n\n    /* connect an outlet of one object to an inlet of another.  The receiving\n    \"pd\" is usually a patchable object, but this may be used to add a\n    non-patchable pd to an outlet by specifying the 0th inlet. */\nt_outconnect *obj_connect(t_object *source, int outno,\n    t_object *sink, int inno)\n{\n    t_inlet *i;\n    t_outlet *o;\n    t_pd *to;\n    t_outconnect *oc, *oc2, **ochead;\n\n    for (o = source->ob_outlet; o && outno; o = o->o_next, outno--) ;\n    if (!o) return (0);\n\n    if (sink->ob_pd->c_firstin)\n    {\n        if (!inno)\n        {\n            to = &sink->ob_pd;\n            goto doit;\n        }\n        else inno--;\n    }\n    for (i = sink->ob_inlet; i && inno; i = i->i_next, inno--) ;\n    if (!i) return (0);\n    to = &i->i_pd;\ndoit:\n    ochead = outlet_getconnectionpointer(o);\n    oc = (t_outconnect *)t_getbytes(sizeof(*oc));\n    oc->oc_next = 0;\n    oc->oc_to = to;\n        /* append it to the end of the list */\n        /* LATER we might cache the last \"oc\" to make this faster. */\n    if ((oc2 = *ochead))\n    {\n        while (oc2->oc_next)\n            oc2 = oc2->oc_next;\n        oc2->oc_next = oc;\n    }\n    else *ochead = oc;\n    if (o->o_sym == &s_signal) canvas_update_dsp();\n\n    return (oc);\n}\n\nvoid obj_disconnect(t_object *source, int outno, t_object *sink, int inno)\n{\n    t_inlet *i;\n    t_outlet *o;\n    t_pd *to;\n    t_outconnect *oc, *oc2, **ochead;\n\n    for (o = source->ob_outlet; o && outno; o = o->o_next, outno--) ;\n    if (!o) return;\n    if (sink->ob_pd->c_firstin)\n    {\n        if (!inno)\n        {\n            to = &sink->ob_pd;\n            goto doit;\n        }\n        else inno--;\n    }\n    for (i = sink->ob_inlet; i && inno; i = i->i_next, inno--) ;\n    if (!i) return;\n    to = &i->i_pd;\ndoit:\n    ochead = outlet_getconnectionpointer(o);\n    if (!(oc = *ochead)) return;\n    if (oc->oc_to == to)\n    {\n        *ochead = oc->oc_next;\n        freebytes(oc, sizeof(*oc));\n        goto done;\n    }\n    while ((oc2 = oc->oc_next))\n    {\n        if (oc2->oc_to == to)\n        {\n            oc->oc_next = oc2->oc_next;\n            freebytes(oc2, sizeof(*oc2));\n            goto done;\n        }\n        oc = oc2;\n    }\ndone:\n    if (o->o_sym == &s_signal) canvas_update_dsp();\n}\n\n/* ------ traversal routines for code that can't see our structures ------ */\n\nint obj_noutlets(const t_object *x)\n{\n    int n;\n    t_outlet *o;\n    for (o = x->ob_outlet, n = 0; o; o = o->o_next) n++;\n    return (n);\n}\n\nint obj_ninlets(const t_object *x)\n{\n    int n;\n    t_inlet *i;\n    for (i = x->ob_inlet, n = 0; i; i = i->i_next) n++;\n    if (x->ob_pd->c_firstin) n++;\n    return (n);\n}\n\nt_outconnect *obj_starttraverseoutlet(const t_object *x, t_outlet **op, int nout)\n{\n    t_outlet *o = x->ob_outlet;\n    while (nout-- && o) o = o->o_next;\n    *op = o;\n    if (o)\n        return (*outlet_getconnectionpointer(o));\n    else return (0);\n}\n\nt_outconnect *obj_nexttraverseoutlet(t_outconnect *lastconnect,\n    t_object **destp, t_inlet **inletp, int *whichp)\n{\n    t_pd *y;\n    y = lastconnect->oc_to;\n    if (ISINLET(y))\n    {\n        int n;\n        t_inlet *i = (t_inlet *)y, *i2;\n        t_object *dest = i->i_owner;\n        for (n = dest->ob_pd->c_firstin, i2 = dest->ob_inlet;\n            i2 && i2 != i; i2 = i2->i_next) n++;\n        *whichp = n;\n        *destp = dest;\n        *inletp = i;\n    }\n    else\n    {\n        *whichp = 0;\n        *inletp = 0;\n        *destp = ((t_object *)y);\n    }\n    return (lastconnect->oc_next);\n}\n\n    /* this one checks that a pd is indeed a patchable object, and returns\n    it, correctly typed, or zero if the check failed. */\nt_object *pd_checkobject(t_pd *x)\n{\n    if ((*x)->c_patchable) return ((t_object *)x);\n    else return (0);\n}\n\n    /* move an inlet or outlet to the head of the list */\nvoid obj_moveinletfirst(t_object *x, t_inlet *i)\n{\n    t_inlet *i2;\n    if (x->ob_inlet == i) return;\n    else for (i2 = x->ob_inlet; i2; i2 = i2->i_next)\n        if (i2->i_next == i)\n    {\n        i2->i_next = i->i_next;\n        i->i_next = x->ob_inlet;\n        x->ob_inlet = i;\n        return;\n    }\n}\n\nvoid obj_moveoutletfirst(t_object *x, t_outlet *o)\n{\n    t_outlet *o2;\n    if (x->ob_outlet == o) return;\n    else for (o2 = x->ob_outlet; o2; o2 = o2->o_next)\n        if (o2->o_next == o)\n    {\n        o2->o_next = o->o_next;\n        o->o_next = x->ob_outlet;\n        x->ob_outlet = o;\n        return;\n    }\n}\n\n    /* routines for DSP sorting, which are used in d_ugen.c and g_canvas.c */\n    /* LATER try to consolidate all the slightly different routines. */\n\nint obj_nsiginlets(const t_object *x)\n{\n    int n;\n    t_inlet *i;\n    for (i = x->ob_inlet, n = 0; i; i = i->i_next)\n        if (i->i_symfrom == &s_signal) n++;\n    if (x->ob_pd->c_firstin && x->ob_pd->c_floatsignalin) n++;\n    return (n);\n}\n\n    /* get the index, among signal inlets, of the mth inlet overall */\nint obj_siginletindex(const t_object *x, int m)\n{\n    int n = 0;\n    t_inlet *i;\n    if (x->ob_pd->c_firstin)\n    {\n        if (!m--)\n            return (0);\n        if (x->ob_pd->c_floatsignalin)\n            n++;\n    }\n    for (i = x->ob_inlet; i; i = i->i_next, m--)\n        if (i->i_symfrom == &s_signal)\n    {\n        if (m == 0) return (n);\n        n++;\n    }\n    return (-1);\n}\n\nint obj_issignalinlet(const t_object *x, int m)\n{\n    t_inlet *i;\n    if (x->ob_pd->c_firstin)\n    {\n        if (!m)\n            return (x->ob_pd->c_firstin && x->ob_pd->c_floatsignalin);\n        else m--;\n    }\n    for (i = x->ob_inlet; i && m; i = i->i_next, m--)\n        ;\n    return (i && (i->i_symfrom == &s_signal));\n}\n\nint obj_nsigoutlets(const t_object *x)\n{\n    int n;\n    t_outlet *o;\n    for (o = x->ob_outlet, n = 0; o; o = o->o_next)\n        if (o->o_sym == &s_signal) n++;\n    return (n);\n}\n\nint obj_sigoutletindex(const t_object *x, int m)\n{\n    int n;\n    t_outlet *o2;\n    for (o2 = x->ob_outlet, n = 0; o2; o2 = o2->o_next, m--)\n        if (o2->o_sym == &s_signal)\n    {\n        if (m == 0) return (n);\n        n++;\n    }\n    return (-1);\n}\n\nint obj_issignaloutlet(const t_object *x, int m)\n{\n    int n;\n    t_outlet *o2;\n    for (o2 = x->ob_outlet, n = 0; o2 && m--; o2 = o2->o_next);\n    return (o2 && (o2->o_sym == &s_signal));\n}\n\n    /* return a pointer to a scalar holding the inlet's value.  If we\n    can't find a value, return a pointer to a fixed location holding zero.\n    This should only happen for a left-hand signal inlet for which no\n    \"MAINSIGNALIN\" has been provided, in which case the object won't\n    promote scalars correctly.  Nonetheless we provide it so that at least\n    such a badly written object won't crash Pd. */\nt_float *obj_findsignalscalar(const t_object *x, int m)\n{\n    t_inlet *i;\n    static float obj_scalarzero = 0;\n    if (x->ob_pd->c_firstin && x->ob_pd->c_floatsignalin)\n    {\n        if (!m--)\n            return (x->ob_pd->c_floatsignalin > 0 ?\n                (t_float *)(((char *)x) + x->ob_pd->c_floatsignalin) :\n                    &obj_scalarzero);\n    }\n    for (i = x->ob_inlet; i; i = i->i_next)\n        if (i->i_symfrom == &s_signal)\n    {\n        if (m-- == 0)\n            return (&i->i_un.iu_floatsignalvalue);\n    }\n    return (&obj_scalarzero);   /* this should never happen but OK wtf. */\n}\n\n/* and these are only used in g_io.c... */\n\nint inlet_getsignalindex(t_inlet *x)\n{\n    int n = 0;\n    t_inlet *i;\n    if (x->i_symfrom != &s_signal)\n        bug(\"inlet_getsignalindex\");\n    for (i = x->i_owner->ob_inlet, n = 0; i && i != x; i = i->i_next)\n        if (i->i_symfrom == &s_signal) n++;\n    return (n);\n}\n\nint outlet_getsignalindex(t_outlet *x)\n{\n    int n = 0;\n    t_outlet *o;\n    for (o = x->o_owner->ob_outlet, n = 0; o && o != x; o = o->o_next)\n        if (o->o_sym == &s_signal) n++;\n    return (n);\n}\n\nvoid obj_saveformat(const t_object *x, t_binbuf *bb)\n{\n    if (x->te_width)\n        binbuf_addv(bb, \"ssf;\", &s__X, gensym(\"f\"), (float)x->te_width);\n}\n\n/* this one only in g_clone.c -- LATER consider sending the message\nwithout having to chase the linked list every time? */\nvoid obj_sendinlet(t_object *x, int n, t_symbol *s, int argc, t_atom *argv)\n{\n    t_inlet *i;\n    for (i = x->ob_inlet; i && n; i = i->i_next, n--)\n        ;\n    if (i)\n        typedmess(&i->i_pd, s, argc, argv);\n    else bug(\"obj_sendinlet\");\n}\n\n/* ------------------- setup routine, somewhat misnamed */\nvoid obj_init(void)\n{\n    inlet_class = class_new(gensym(\"inlet\"), 0, 0,\n        sizeof(t_inlet), CLASS_PD, 0);\n    class_addbang(inlet_class, inlet_bang);\n    class_addpointer(inlet_class, inlet_pointer);\n    class_addfloat(inlet_class, inlet_float);\n    class_addsymbol(inlet_class, inlet_symbol);\n    class_addlist(inlet_class, inlet_list);\n    class_addanything(inlet_class, inlet_anything);\n\n    pointerinlet_class = class_new(gensym(\"inlet\"), 0, 0,\n        sizeof(t_inlet), CLASS_PD, 0);\n    class_addpointer(pointerinlet_class, pointerinlet_pointer);\n    class_addanything(pointerinlet_class, inlet_wrong);\n\n    floatinlet_class = class_new(gensym(\"inlet\"), 0, 0,\n        sizeof(t_inlet), CLASS_PD, 0);\n    class_addfloat(floatinlet_class, (t_method)floatinlet_float);\n    class_addanything(floatinlet_class, inlet_wrong);\n\n    symbolinlet_class = class_new(gensym(\"inlet\"), 0, 0,\n        sizeof(t_inlet), CLASS_PD, 0);\n    class_addsymbol(symbolinlet_class, symbolinlet_symbol);\n    class_addanything(symbolinlet_class, inlet_wrong);\n\n    backtracer_class = class_new(gensym(\"backtracer\"), 0, 0,\n        sizeof(t_backtracer), CLASS_PD, 0);\n    class_addanything(backtracer_class, backtracer_anything);\n\n}\n\n\n"
  },
  {
    "path": "libs/libpd/pure-data/src/m_pd.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n#include \"m_pd.h\"\n#include \"m_imp.h\"\n#include \"g_canvas.h\"   /* just for LB_LOAD */\n\n    /* FIXME no out-of-memory testing yet! */\n\nt_pd *pd_new(t_class *c)\n{\n    t_pd *x;\n    if (!c) {\n        bug (\"pd_new: apparently called before setup routine\");\n        return NULL;\n    }\n    x = (t_pd *)t_getbytes(c->c_size);\n    *x = c;\n    if (c->c_patchable)\n    {\n        ((t_object *)x)->ob_inlet = 0;\n        ((t_object *)x)->ob_outlet = 0;\n    }\n    return (x);\n}\n\nvoid pd_free(t_pd *x)\n{\n    t_class *c = *x;\n    if (c->c_freemethod) (*(t_gotfn)(c->c_freemethod))(x);\n    if (c->c_patchable)\n    {\n        while (((t_object *)x)->ob_outlet)\n            outlet_free(((t_object *)x)->ob_outlet);\n        while (((t_object *)x)->ob_inlet)\n            inlet_free(((t_object *)x)->ob_inlet);\n        if (((t_object *)x)->ob_binbuf)\n            binbuf_free(((t_object *)x)->ob_binbuf);\n    }\n    if (c->c_size) t_freebytes(x, c->c_size);\n}\n\nvoid gobj_save(t_gobj *x, t_binbuf *b)\n{\n    t_class *c = x->g_pd;\n    if (c->c_savefn)\n        (c->c_savefn)(x, b);\n}\n\n/* deal with several objects bound to the same symbol.  If more than one, we\nactually bind a collection object to the symbol, which forwards messages sent\nto the symbol. */\n\nstatic t_class *bindlist_class;\n\ntypedef struct _bindelem\n{\n    t_pd *e_who;\n    struct _bindelem *e_next;\n} t_bindelem;\n\ntypedef struct _bindlist\n{\n    t_pd b_pd;\n    t_bindelem *b_list;\n} t_bindlist;\n\nstatic void bindlist_bang(t_bindlist *x)\n{\n    t_bindelem *e;\n    for (e = x->b_list; e; e = e->e_next)\n        pd_bang(e->e_who);\n}\n\nstatic void bindlist_float(t_bindlist *x, t_float f)\n{\n    t_bindelem *e;\n    for (e = x->b_list; e; e = e->e_next)\n        pd_float(e->e_who, f);\n}\n\nstatic void bindlist_symbol(t_bindlist *x, t_symbol *s)\n{\n    t_bindelem *e;\n    for (e = x->b_list; e; e = e->e_next)\n        pd_symbol(e->e_who, s);\n}\n\nstatic void bindlist_pointer(t_bindlist *x, t_gpointer *gp)\n{\n    t_bindelem *e;\n    for (e = x->b_list; e; e = e->e_next)\n        pd_pointer(e->e_who, gp);\n}\n\nstatic void bindlist_list(t_bindlist *x, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    t_bindelem *e;\n    for (e = x->b_list; e; e = e->e_next)\n        pd_list(e->e_who, s, argc, argv);\n}\n\nstatic void bindlist_anything(t_bindlist *x, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    t_bindelem *e;\n    for (e = x->b_list; e; e = e->e_next)\n        pd_typedmess(e->e_who, s, argc, argv);\n}\n\nvoid m_pd_setup(void)\n{\n    bindlist_class = class_new(gensym(\"bindlist\"), 0, 0,\n        sizeof(t_bindlist), CLASS_PD, 0);\n    class_addbang(bindlist_class, bindlist_bang);\n    class_addfloat(bindlist_class, (t_method)bindlist_float);\n    class_addsymbol(bindlist_class, bindlist_symbol);\n    class_addpointer(bindlist_class, bindlist_pointer);\n    class_addlist(bindlist_class, bindlist_list);\n    class_addanything(bindlist_class, bindlist_anything);\n}\n\nvoid pd_bind(t_pd *x, t_symbol *s)\n{\n    if (s->s_thing)\n    {\n        if (*s->s_thing == bindlist_class)\n        {\n            t_bindlist *b = (t_bindlist *)s->s_thing;\n            t_bindelem *e = (t_bindelem *)getbytes(sizeof(t_bindelem));\n            e->e_next = b->b_list;\n            e->e_who = x;\n            b->b_list = e;\n        }\n        else\n        {\n            t_bindlist *b = (t_bindlist *)pd_new(bindlist_class);\n            t_bindelem *e1 = (t_bindelem *)getbytes(sizeof(t_bindelem));\n            t_bindelem *e2 = (t_bindelem *)getbytes(sizeof(t_bindelem));\n            b->b_list = e1;\n            e1->e_who = x;\n            e1->e_next = e2;\n            e2->e_who = s->s_thing;\n            e2->e_next = 0;\n            s->s_thing = &b->b_pd;\n        }\n    }\n    else s->s_thing = x;\n}\n\nvoid pd_unbind(t_pd *x, t_symbol *s)\n{\n    if (s->s_thing == x) s->s_thing = 0;\n    else if (s->s_thing && *s->s_thing == bindlist_class)\n    {\n            /* bindlists always have at least two elements... if the number\n            goes down to one, get rid of the bindlist and bind the symbol\n            straight to the remaining element. */\n\n        t_bindlist *b = (t_bindlist *)s->s_thing;\n        t_bindelem *e, *e2;\n        if ((e = b->b_list)->e_who == x)\n        {\n            b->b_list = e->e_next;\n            e->e_who = 0; e->e_next = 0;\n            freebytes(e, sizeof(t_bindelem));\n        }\n        else for (e = b->b_list; (e2 = e->e_next); e = e2)\n            if (e2->e_who == x)\n        {\n            e->e_next = e2->e_next;\n            e2->e_who = 0; e2->e_next = 0;\n            freebytes(e2, sizeof(t_bindelem));\n            break;\n        }\n        if (!b->b_list->e_next)\n        {\n\n            s->s_thing = b->b_list->e_who;\n            freebytes(b->b_list, sizeof(t_bindelem));\n            b->b_list = 0;\n            pd_free(&b->b_pd);\n            b = 0;\n        }\n    }\n    else pd_error(x, \"%s: couldn't unbind\", s->s_name);\n}\n\nt_pd *pd_findbyclass(t_symbol *s, const t_class *c)\n{\n    t_pd *x = 0;\n\n    if (!s->s_thing) return (0);\n    if (*s->s_thing == c) return (s->s_thing);\n    if (*s->s_thing == bindlist_class)\n    {\n        t_bindlist *b = (t_bindlist *)s->s_thing;\n        t_bindelem *e, *e2;\n        int warned = 0;\n        for (e = b->b_list; e; e = e->e_next)\n            if (*e->e_who == c)\n        {\n            if (x && !warned)\n            {\n                post(\"warning: %s: multiply defined\", s->s_name);\n                warned = 1;\n            }\n            x = e->e_who;\n        }\n    }\n    return x;\n}\n\n/* stack for maintaining bindings for the #X symbol during nestable loads.\n*/\n\ntypedef struct _gstack\n{\n    t_pd *g_what;\n    t_symbol *g_loadingabstraction;\n    struct _gstack *g_next;\n} t_gstack;\n\nstatic t_gstack *gstack_head = 0;\nstatic t_pd *lastpopped;\nstatic t_symbol *pd_loadingabstraction;\n\nint pd_setloadingabstraction(t_symbol *sym)\n{\n    t_gstack *foo = gstack_head;\n    for (foo = gstack_head; foo; foo = foo->g_next)\n        if (foo->g_loadingabstraction == sym)\n            return (1);\n    pd_loadingabstraction = sym;\n    return (0);\n}\n\nvoid pd_pushsym(t_pd *x)\n{\n    t_gstack *y = (t_gstack *)t_getbytes(sizeof(*y));\n    y->g_what = s__X.s_thing;\n    y->g_next = gstack_head;\n    y->g_loadingabstraction = pd_loadingabstraction;\n    pd_loadingabstraction = 0;\n    gstack_head = y;\n    s__X.s_thing = x;\n}\n\nvoid pd_popsym(t_pd *x)\n{\n    if (!gstack_head || s__X.s_thing != x) bug(\"gstack_pop\");\n    else\n    {\n        t_gstack *headwas = gstack_head;\n        s__X.s_thing = headwas->g_what;\n        gstack_head = headwas->g_next;\n        t_freebytes(headwas, sizeof(*headwas));\n        lastpopped = x;\n    }\n}\n\nvoid pd_doloadbang(void)\n{\n    if (lastpopped)\n        pd_vmess(lastpopped, gensym(\"loadbang\"), \"f\", LB_LOAD);\n    lastpopped = 0;\n}\n\nvoid pd_bang(t_pd *x)\n{\n    (*(*x)->c_bangmethod)(x);\n}\n\nvoid pd_float(t_pd *x, t_float f)\n{\n    (*(*x)->c_floatmethod)(x, f);\n}\n\nvoid pd_pointer(t_pd *x, t_gpointer *gp)\n{\n    (*(*x)->c_pointermethod)(x, gp);\n}\n\nvoid pd_symbol(t_pd *x, t_symbol *s)\n{\n    (*(*x)->c_symbolmethod)(x, s);\n}\n\nvoid pd_list(t_pd *x, t_symbol *s, int argc, t_atom *argv)\n{\n    (*(*x)->c_listmethod)(x, &s_list, argc, argv);\n}\n\nvoid pd_anything(t_pd *x, t_symbol *s, int argc, t_atom *argv)\n{\n    (*(*x)->c_anymethod)(x, s, argc, argv);\n}\n\nvoid mess_init(void);\nvoid obj_init(void);\nvoid conf_init(void);\nvoid glob_init(void);\nvoid garray_init(void);\nvoid ooura_term(void);\n\nvoid pd_init(void)\n{\n#ifndef PDINSTANCE\n    static int initted = 0;\n    if (initted)\n        return;\n    initted = 1;\n#else\n    if (pd_instances)\n        return;\n    pd_instances = (t_pdinstance **)getbytes(sizeof(*pd_instances));\n    pd_instances[0] = &pd_maininstance;\n    pd_ninstances = 1;\n#endif\n    pd_init_systems();\n}\n\nEXTERN void pd_init_systems(void) {\n    mess_init();\n    sys_lock();\n    obj_init();\n    conf_init();\n    glob_init();\n    garray_init();\n    sys_unlock();\n}\n\nEXTERN void pd_term_systems(void) {\n    sys_lock();\n    sys_unlock();\n}\n\nEXTERN t_canvas *pd_getcanvaslist(void)\n{\n    return (pd_this->pd_canvaslist);\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/m_pd.h",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n#ifndef __m_pd_h_\n\n#if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus)\nextern \"C\" {\n#endif\n\n#define PD_MAJOR_VERSION 0\n#define PD_MINOR_VERSION 54\n#define PD_BUGFIX_VERSION 1\n#define PD_TEST_VERSION \"\"\nextern int pd_compatibilitylevel;   /* e.g., 43 for pd 0.43 compatibility */\n\n/* old name for \"MSW\" flag -- we have to take it for the sake of many old\n\"nmakefiles\" for externs, which will define NT and not MSW */\n#if defined(NT) && !defined(MSW)\n#define MSW\n#endif\n\n/* These pragmas are only used for MSVC, not MinGW or Cygwin <hans@at.or.at> */\n#ifdef _MSC_VER\n/* #pragma warning( disable : 4091 ) */\n#pragma warning( disable : 4305 )  /* uncast const double to float */\n#pragma warning( disable : 4244 )  /* uncast float/int conversion etc. */\n#pragma warning( disable : 4101 )  /* unused automatic variables */\n#endif /* _MSC_VER */\n\n    /* the external storage class is \"extern\" in UNIX; in MSW it's ugly. */\n#ifndef EXTERN\n#ifdef _WIN32\n#ifdef PD_INTERNAL\n#define EXTERN __declspec(dllexport) extern\n#else\n#define EXTERN __declspec(dllimport) extern\n#endif /* PD_INTERNAL */\n#else\n#define EXTERN extern\n#endif /* _WIN32 */\n#endif /* EXTERN */\n\n    /* On most c compilers, you can just say \"struct foo;\" to declare a\n    structure whose elements are defined elsewhere.  On very old MSVC versions,\n    when compiling C (but not C++) code, you have to say \"extern struct foo;\".\n    So we make a stupid macro: */\n#if defined(_MSC_VER) && !defined(_LANGUAGE_C_PLUS_PLUS) \\\n    && !defined(__cplusplus) && (_MSC_VER < 1700)\n#define EXTERN_STRUCT extern struct\n#else\n#define EXTERN_STRUCT struct\n#endif\n\n/* Define some attributes, specific to the compiler */\n#if defined(__GNUC__)\n#define ATTRIBUTE_FORMAT_PRINTF(a, b) __attribute__ ((format (printf, a, b)))\n#else\n#define ATTRIBUTE_FORMAT_PRINTF(a, b)\n#endif\n\n#if !defined(_SIZE_T) && !defined(_SIZE_T_)\n#include <stddef.h>     /* just for size_t -- how lame! */\n#endif\n\n/* Microsoft Visual Studio is not C99, but since VS2015 has included most C99 headers:\n   https://docs.microsoft.com/en-us/previous-versions/hh409293(v=vs.140)#c-runtime-library\n   These definitions recreate stdint.h types, but only in pre-2015 Visual Studio: */\n#if defined(_MSC_VER) && _MSC_VER < 1900\ntypedef signed __int8     int8_t;\ntypedef signed __int16    int16_t;\ntypedef signed __int32    int32_t;\ntypedef signed __int64    int64_t;\ntypedef unsigned __int8   uint8_t;\ntypedef unsigned __int16  uint16_t;\ntypedef unsigned __int32  uint32_t;\ntypedef unsigned __int64  uint64_t;\n#else\n# include <stdint.h>\n#endif\n\n/* for FILE, needed by sys_fopen() and sys_fclose() only */\n#include <stdio.h>\n\n#define MAXPDSTRING 1000        /* use this for anything you want */\n#define MAXPDARG 5              /* max number of args we can typecheck today */\n\n/* signed and unsigned integer types the size of a pointer:  */\n#if !defined(PD_LONGINTTYPE)\n#if defined(_WIN32) && defined(_WIN64)\n#define PD_LONGINTTYPE long long\n#else\n#define PD_LONGINTTYPE long\n#endif\n#endif\n\n#if !defined(PD_FLOATSIZE)\n  /* normally, our floats (t_float, t_sample,...) are 32bit */\n# define PD_FLOATSIZE 32\n#endif\n\n#if PD_FLOATSIZE == 32\n# define PD_FLOATTYPE float\n/* an unsigned int of the same size as FLOATTYPE: */\n# define PD_FLOATUINTTYPE uint32_t\n\n#elif PD_FLOATSIZE == 64\n# define PD_FLOATTYPE double\n# define PD_FLOATUINTTYPE uint64_t\n#else\n# error invalid FLOATSIZE: must be 32 or 64\n#endif\n\ntypedef PD_LONGINTTYPE t_int;       /* pointer-size integer */\ntypedef PD_FLOATTYPE t_float;       /* a float type at most the same size */\ntypedef PD_FLOATTYPE t_floatarg;    /* float type for function calls */\n\ntypedef struct _symbol\n{\n    const char *s_name;\n    struct _class **s_thing;\n    struct _symbol *s_next;\n} t_symbol;\n\nEXTERN_STRUCT _array;\n#define t_array struct _array       /* g_canvas.h */\n\n/* pointers to glist and array elements go through a \"stub\" which sticks\naround after the glist or array is freed.  The stub itself is deleted when\nboth the glist/array is gone and the refcount is zero, ensuring that no\ngpointers are pointing here. */\n\n#define GP_NONE 0       /* the stub points nowhere (has been cut off) */\n#define GP_GLIST 1      /* the stub points to a glist element */\n#define GP_ARRAY 2      /* ... or array */\n\ntypedef struct _gstub\n{\n    union\n    {\n        struct _glist *gs_glist;    /* glist we're in */\n        struct _array *gs_array;    /* array we're in */\n    } gs_un;\n    int gs_which;                   /* GP_GLIST/GP_ARRAY */\n    int gs_refcount;                /* number of gpointers pointing here */\n} t_gstub;\n\ntypedef struct _gpointer           /* pointer to a gobj in a glist */\n{\n    union\n    {\n        struct _scalar *gp_scalar;  /* scalar we're in (if glist) */\n        union word *gp_w;           /* raw data (if array) */\n    } gp_un;\n    int gp_valid;                   /* number which must match gpointee */\n    t_gstub *gp_stub;               /* stub which points to glist/array */\n} t_gpointer;\n\ntypedef union word\n{\n    t_float w_float;\n    t_symbol *w_symbol;\n    t_gpointer *w_gpointer;\n    t_array *w_array;\n    struct _binbuf *w_binbuf;\n    int w_index;\n} t_word;\n\ntypedef enum\n{\n    A_NULL,\n    A_FLOAT,\n    A_SYMBOL,\n    A_POINTER,\n    A_SEMI,\n    A_COMMA,\n    A_DEFFLOAT,\n    A_DEFSYM,\n    A_DOLLAR,\n    A_DOLLSYM,\n    A_GIMME,\n    A_CANT\n}  t_atomtype;\n\n#define A_DEFSYMBOL A_DEFSYM    /* better name for this */\n\ntypedef struct _atom\n{\n    t_atomtype a_type;\n    union word a_w;\n} t_atom;\n\nEXTERN_STRUCT _class;\n#define t_class struct _class\n\nEXTERN_STRUCT _outlet;\n#define t_outlet struct _outlet\n\nEXTERN_STRUCT _inlet;\n#define t_inlet struct _inlet\n\nEXTERN_STRUCT _binbuf;\n#define t_binbuf struct _binbuf\n\nEXTERN_STRUCT _clock;\n#define t_clock struct _clock\n\nEXTERN_STRUCT _outconnect;\n#define t_outconnect struct _outconnect\n\nEXTERN_STRUCT _glist;\n#define t_glist struct _glist\n#define t_canvas struct _glist  /* LATER lose this */\n\nEXTERN_STRUCT _template;\n\ntypedef t_class *t_pd;      /* pure datum: nothing but a class pointer */\n\ntypedef struct _gobj        /* a graphical object */\n{\n    t_pd g_pd;              /* pure datum header (class) */\n    struct _gobj *g_next;   /* next in list */\n} t_gobj;\n\ntypedef struct _scalar      /* a graphical object holding data */\n{\n    t_gobj sc_gobj;         /* header for graphical object */\n    t_symbol *sc_template;  /* template name (LATER replace with pointer) */\n    t_word sc_vec[1];       /* indeterminate-length array of words */\n} t_scalar;\n\ntypedef struct _text        /* patchable object - graphical, with text */\n{\n    t_gobj te_g;                /* header for graphical object */\n    t_binbuf *te_binbuf;        /* holder for the text */\n    t_outlet *te_outlet;        /* linked list of outlets */\n    t_inlet *te_inlet;          /* linked list of inlets */\n    short te_xpix;              /* x&y location (within the toplevel) */\n    short te_ypix;\n    short te_width;             /* requested width in chars, 0 if auto */\n    unsigned int te_type:2;     /* from defs below */\n} t_text;\n\n#define T_TEXT 0        /* just a textual comment */\n#define T_OBJECT 1      /* a MAX style patchable object */\n#define T_MESSAGE 2     /* a MAX type message */\n#define T_ATOM 3        /* a cell to display a number or symbol */\n\n#define te_pd te_g.g_pd\n\n   /* t_object is synonym for t_text (LATER unify them) */\n\ntypedef struct _text t_object;\n\n#define ob_outlet te_outlet\n#define ob_inlet te_inlet\n#define ob_binbuf te_binbuf\n#define ob_pd te_g.g_pd\n#define ob_g te_g\n\ntypedef void (*t_method)(void);\ntypedef void *(*t_newmethod)(void);\n\n/* in ARM 64 a varargs prototype generates a different function call sequence\nfrom a fixed one, so in that special case we make a more restrictive\ndefinition for t_gotfn.  This will break some code in the \"chaos\" package\nin Pd extended.  (that code will run incorrectly anyhow so why not catch it\nat compile time anyhow.) */\n#if defined(__APPLE__) && defined(__aarch64__)\ntypedef void (*t_gotfn)(void *x);\n#else\ntypedef void (*t_gotfn)(void *x, ...);\n#endif\n\n/* ---------------- pre-defined objects and symbols --------------*/\nEXTERN t_pd pd_objectmaker;     /* factory for creating \"object\" boxes */\nEXTERN t_pd pd_canvasmaker;     /* factory for creating canvases */\n\n/* --------- prototypes from the central message system ----------- */\nEXTERN void pd_typedmess(t_pd *x, t_symbol *s, int argc, t_atom *argv);\nEXTERN void pd_forwardmess(t_pd *x, int argc, t_atom *argv);\nEXTERN t_symbol *gensym(const char *s);\nEXTERN t_gotfn getfn(const t_pd *x, t_symbol *s);\nEXTERN t_gotfn zgetfn(const t_pd *x, t_symbol *s);\nEXTERN void nullfn(void);\nEXTERN void pd_vmess(t_pd *x, t_symbol *s, const char *fmt, ...);\n\n/* the following macros are for sending non-type-checkable messages, i.e.,\nusing function lookup but circumventing type checking on arguments.  Only\nuse for internal messaging protected by A_CANT so that the message can't\nbe generated at patch level. */\n#define mess0(x, s) ((*getfn((x), (s)))((x)))\ntypedef void (*t_gotfn1)(void *x, void *arg1);\n#define mess1(x, s, a) ((*(t_gotfn1)getfn((x), (s)))((x), (a)))\ntypedef void (*t_gotfn2)(void *x, void *arg1, void *arg2);\n#define mess2(x, s, a,b) ((*(t_gotfn2)getfn((x), (s)))((x), (a),(b)))\ntypedef void (*t_gotfn3)(void *x, void *arg1, void *arg2, void *arg3);\n#define mess3(x, s, a,b,c) ((*(t_gotfn3)getfn((x), (s)))((x), (a),(b),(c)))\ntypedef void (*t_gotfn4)(void *x,\n    void *arg1, void *arg2, void *arg3, void *arg4);\n#define mess4(x, s, a,b,c,d) \\\n    ((*(t_gotfn4)getfn((x), (s)))((x), (a),(b),(c),(d)))\ntypedef void (*t_gotfn5)(void *x,\n    void *arg1, void *arg2, void *arg3, void *arg4, void *arg5);\n#define mess5(x, s, a,b,c,d,e) \\\n    ((*(t_gotfn5)getfn((x), (s)))((x), (a),(b),(c),(d),(e)))\n\nEXTERN void obj_list(t_object *x, t_symbol *s, int argc, t_atom *argv);\nEXTERN t_pd *pd_newest(void);\n\n/* --------------- memory management -------------------- */\nEXTERN void *getbytes(size_t nbytes);\nEXTERN void *getzbytes(size_t nbytes);\nEXTERN void *copybytes(const void *src, size_t nbytes);\nEXTERN void freebytes(void *x, size_t nbytes);\nEXTERN void *resizebytes(void *x, size_t oldsize, size_t newsize);\n\n/* -------------------- atoms ----------------------------- */\n\n#define SETSEMI(atom) ((atom)->a_type = A_SEMI, (atom)->a_w.w_index = 0)\n#define SETCOMMA(atom) ((atom)->a_type = A_COMMA, (atom)->a_w.w_index = 0)\n#define SETPOINTER(atom, gp) ((atom)->a_type = A_POINTER, \\\n    (atom)->a_w.w_gpointer = (gp))\n#define SETFLOAT(atom, f) ((atom)->a_type = A_FLOAT, (atom)->a_w.w_float = (f))\n#define SETSYMBOL(atom, s) ((atom)->a_type = A_SYMBOL, \\\n    (atom)->a_w.w_symbol = (s))\n#define SETDOLLAR(atom, n) ((atom)->a_type = A_DOLLAR, \\\n    (atom)->a_w.w_index = (n))\n#define SETDOLLSYM(atom, s) ((atom)->a_type = A_DOLLSYM, \\\n    (atom)->a_w.w_symbol= (s))\n\nEXTERN t_float atom_getfloat(const t_atom *a);\nEXTERN t_int atom_getint(const t_atom *a);\nEXTERN t_symbol *atom_getsymbol(const t_atom *a);\nEXTERN t_symbol *atom_gensym(const t_atom *a);\nEXTERN t_float atom_getfloatarg(int which, int argc, const t_atom *argv);\nEXTERN t_int atom_getintarg(int which, int argc, const t_atom *argv);\nEXTERN t_symbol *atom_getsymbolarg(int which, int argc, const t_atom *argv);\n\nEXTERN void atom_string(const t_atom *a, char *buf, unsigned int bufsize);\n\n/* ------------------  binbufs --------------- */\n\nEXTERN t_binbuf *binbuf_new(void);\nEXTERN void binbuf_free(t_binbuf *x);\nEXTERN t_binbuf *binbuf_duplicate(const t_binbuf *y);\n\nEXTERN void binbuf_text(t_binbuf *x, const char *text, size_t size);\nEXTERN void binbuf_gettext(const t_binbuf *x, char **bufp, int *lengthp);\nEXTERN void binbuf_clear(t_binbuf *x);\nEXTERN void binbuf_add(t_binbuf *x, int argc, const t_atom *argv);\nEXTERN void binbuf_addv(t_binbuf *x, const char *fmt, ...);\nEXTERN void binbuf_addbinbuf(t_binbuf *x, const t_binbuf *y);\nEXTERN void binbuf_addsemi(t_binbuf *x);\nEXTERN void binbuf_restore(t_binbuf *x, int argc, const t_atom *argv);\nEXTERN void binbuf_print(const t_binbuf *x);\nEXTERN int binbuf_getnatom(const t_binbuf *x);\nEXTERN t_atom *binbuf_getvec(const t_binbuf *x);\nEXTERN int binbuf_resize(t_binbuf *x, int newsize);\nEXTERN void binbuf_eval(const t_binbuf *x, t_pd *target, int argc, const t_atom *argv);\nEXTERN int binbuf_read(t_binbuf *b, const char *filename, const char *dirname,\n    int crflag);\nEXTERN int binbuf_read_via_canvas(t_binbuf *b, const char *filename, const t_canvas *canvas,\n    int crflag);\nEXTERN int binbuf_read_via_path(t_binbuf *b, const char *filename, const char *dirname,\n    int crflag);\nEXTERN int binbuf_write(const t_binbuf *x, const char *filename, const char *dir,\n    int crflag);\nEXTERN void binbuf_evalfile(t_symbol *name, t_symbol *dir);\nEXTERN t_symbol *binbuf_realizedollsym(t_symbol *s, int ac, const t_atom *av,\n    int tonew);\n\n/* ------------------  clocks --------------- */\n\nEXTERN t_clock *clock_new(void *owner, t_method fn);\nEXTERN void clock_set(t_clock *x, double systime);\nEXTERN void clock_delay(t_clock *x, double delaytime);\nEXTERN void clock_unset(t_clock *x);\nEXTERN void clock_setunit(t_clock *x, double timeunit, int sampflag);\nEXTERN double clock_getlogicaltime(void);\nEXTERN double clock_getsystime(void); /* OBSOLETE; use clock_getlogicaltime() */\nEXTERN double clock_gettimesince(double prevsystime);\nEXTERN double clock_gettimesincewithunits(double prevsystime,\n    double units, int sampflag);\nEXTERN double clock_getsystimeafter(double delaytime);\nEXTERN void clock_free(t_clock *x);\n\n/* ----------------- pure data ---------------- */\nEXTERN t_pd *pd_new(t_class *cls);\nEXTERN void pd_free(t_pd *x);\nEXTERN void pd_bind(t_pd *x, t_symbol *s);\nEXTERN void pd_unbind(t_pd *x, t_symbol *s);\nEXTERN t_pd *pd_findbyclass(t_symbol *s, const t_class *c);\nEXTERN void pd_pushsym(t_pd *x);\nEXTERN void pd_popsym(t_pd *x);\nEXTERN void pd_bang(t_pd *x);\nEXTERN void pd_pointer(t_pd *x, t_gpointer *gp);\nEXTERN void pd_float(t_pd *x, t_float f);\nEXTERN void pd_symbol(t_pd *x, t_symbol *s);\nEXTERN void pd_list(t_pd *x, t_symbol *s, int argc, t_atom *argv);\nEXTERN void pd_anything(t_pd *x, t_symbol *s, int argc, t_atom *argv);\n#define pd_class(x) (*(x))\n\n/* ----------------- pointers ---------------- */\nEXTERN void gpointer_init(t_gpointer *gp);\nEXTERN void gpointer_copy(const t_gpointer *gpfrom, t_gpointer *gpto);\nEXTERN void gpointer_unset(t_gpointer *gp);\nEXTERN int gpointer_check(const t_gpointer *gp, int headok);\n\n/* ----------------- patchable \"objects\" -------------- */\nEXTERN t_inlet *inlet_new(t_object *owner, t_pd *dest, t_symbol *s1,\n    t_symbol *s2);\nEXTERN t_inlet *pointerinlet_new(t_object *owner, t_gpointer *gp);\nEXTERN t_inlet *floatinlet_new(t_object *owner, t_float *fp);\nEXTERN t_inlet *symbolinlet_new(t_object *owner, t_symbol **sp);\nEXTERN t_inlet *signalinlet_new(t_object *owner, t_float f);\nEXTERN void inlet_free(t_inlet *x);\n\nEXTERN t_outlet *outlet_new(t_object *owner, t_symbol *s);\nEXTERN void outlet_bang(t_outlet *x);\nEXTERN void outlet_pointer(t_outlet *x, t_gpointer *gp);\nEXTERN void outlet_float(t_outlet *x, t_float f);\nEXTERN void outlet_symbol(t_outlet *x, t_symbol *s);\nEXTERN void outlet_list(t_outlet *x, t_symbol *s, int argc, t_atom *argv);\nEXTERN void outlet_anything(t_outlet *x, t_symbol *s, int argc, t_atom *argv);\nEXTERN t_symbol *outlet_getsymbol(t_outlet *x);\nEXTERN void outlet_free(t_outlet *x);\nEXTERN t_object *pd_checkobject(t_pd *x);\n\n\n/* -------------------- canvases -------------- */\n\nEXTERN void glob_setfilename(void *dummy, t_symbol *name, t_symbol *dir);\n\nEXTERN void canvas_setargs(int argc, const t_atom *argv);\nEXTERN void canvas_getargs(int *argcp, t_atom **argvp);\nEXTERN t_symbol *canvas_getcurrentdir(void);\nEXTERN t_glist *canvas_getcurrent(void);\nEXTERN void canvas_makefilename(const t_glist *c, const char *file,\n    char *result, int resultsize);\nEXTERN t_symbol *canvas_getdir(const t_glist *x);\nEXTERN char sys_font[]; /* default typeface set in s_main.c */\nEXTERN char sys_fontweight[]; /* default font weight set in s_main.c */\nEXTERN int sys_hostfontsize(int fontsize, int zoom);\nEXTERN int sys_zoomfontwidth(int fontsize, int zoom, int worstcase);\nEXTERN int sys_zoomfontheight(int fontsize, int zoom, int worstcase);\nEXTERN int sys_fontwidth(int fontsize);\nEXTERN int sys_fontheight(int fontsize);\nEXTERN void canvas_dataproperties(t_glist *x, t_scalar *sc, t_binbuf *b);\nEXTERN int canvas_open(const t_canvas *x, const char *name, const char *ext,\n    char *dirresult, char **nameresult, unsigned int size, int bin);\nEXTERN t_float canvas_getsr(t_canvas *x);\nEXTERN int canvas_getsignallength(t_canvas *x);\n\n/* ---------------- widget behaviors ---------------------- */\n\nEXTERN_STRUCT _widgetbehavior;\n#define t_widgetbehavior struct _widgetbehavior\n\nEXTERN_STRUCT _parentwidgetbehavior;\n#define t_parentwidgetbehavior struct _parentwidgetbehavior\nEXTERN const t_parentwidgetbehavior *pd_getparentwidget(t_pd *x);\n\n/* -------------------- classes -------------- */\n\n#define CLASS_DEFAULT 0         /* flags for new classes below */\n#define CLASS_PD 1              /* non-canvasable (bare) pd such as an inlet */\n#define CLASS_GOBJ 2            /* pd that can belong to a canvas */\n#define CLASS_PATCHABLE 3       /* pd that also can have inlets and outlets */\n#define CLASS_TYPEMASK 3\n\n#define CLASS_NOINLET 8             /* suppress left inlet */\n#define CLASS_MULTICHANNEL 0x10     /* can deal with multichannel sigs */\n#define CLASS_NOPROMOTESIG 0x20     /* don't promote scalars to signals */\n#define CLASS_NOPROMOTELEFT 0x40    /* not even the main (left) inlet */\n\n/*\n    Setting a tilde object's CLASS_MULTICHANNEL flag declares that it can\n    deal with multichannel inputs.  In this case the channel counts of\n    the inputs might not match; it's up to the dsp method to figure out what\n    to do.  Also, the output signal vectors aren't allocated.  The output\n    channel counts have to be specified by the object at DSP time.  If\n    the object can't put itself on the DSP chain it then has to create\n    outputs anyway and arrange to zero them.\n\n    By default, if a tilde object's inputs are unconnected, Pd fills them\n    in by adding scalar-to-vector conversions to the DSP chain as needed before\n    calling the dsp method.  This behavior can be suppressed for the left\n    (main) inlet by setting CLASS_NOPROMOTELEFT and for one or more non-main\n    inlets by setting CLASS_NOPROMOTESIG.  Seeing this, the object can then\n    opt to supply a faster routine; for example, \"+\" can do a vector-scalar\n    add.  In any case, signal outputs are all vectors, and are allocated\n    automatically unless the CLASS_MULTICHANNEL flag is also set.\n*/\n\nEXTERN t_class *class_new(t_symbol *name, t_newmethod newmethod,\n    t_method freemethod, size_t size, int flags, t_atomtype arg1, ...);\n\nEXTERN t_class *class_new64(t_symbol *name, t_newmethod newmethod,\n    t_method freemethod, size_t size, int flags, t_atomtype arg1, ...);\n\nEXTERN void class_free(t_class *c);\n\n#ifdef PDINSTANCE\nEXTERN t_class *class_getfirst(void);\n#endif\n\nEXTERN void class_addcreator(t_newmethod newmethod, t_symbol *s,\n    t_atomtype type1, ...);\nEXTERN void class_addmethod(t_class *c, t_method fn, t_symbol *sel,\n    t_atomtype arg1, ...);\nEXTERN void class_addbang(t_class *c, t_method fn);\nEXTERN void class_addpointer(t_class *c, t_method fn);\nEXTERN void class_doaddfloat(t_class *c, t_method fn);\nEXTERN void class_addsymbol(t_class *c, t_method fn);\nEXTERN void class_addlist(t_class *c, t_method fn);\nEXTERN void class_addanything(t_class *c, t_method fn);\nEXTERN void class_sethelpsymbol(t_class *c, t_symbol *s);\nEXTERN void class_setwidget(t_class *c, const t_widgetbehavior *w);\nEXTERN void class_setparentwidget(t_class *c, const t_parentwidgetbehavior *w);\nEXTERN const char *class_getname(const t_class *c);\nEXTERN const char *class_gethelpname(const t_class *c);\nEXTERN const char *class_gethelpdir(const t_class *c);\nEXTERN void class_setdrawcommand(t_class *c);\nEXTERN int class_isdrawcommand(const t_class *c);\nEXTERN void class_set_extern_dir(t_symbol *s);\nEXTERN void class_domainsignalin(t_class *c, int onset);\n#define CLASS_MAINSIGNALIN(c, type, field) \\\n    class_domainsignalin(c, (char *)(&((type *)0)->field) - (char *)0)\n\n         /* prototype for functions to save Pd's to a binbuf */\ntypedef void (*t_savefn)(t_gobj *x, t_binbuf *b);\nEXTERN void class_setsavefn(t_class *c, t_savefn f);\nEXTERN t_savefn class_getsavefn(const t_class *c);\nEXTERN void obj_saveformat(const t_object *x, t_binbuf *bb); /* add format to bb */\n\n        /* prototype for functions to open properties dialogs */\ntypedef void (*t_propertiesfn)(t_gobj *x, struct _glist *glist);\nEXTERN void class_setpropertiesfn(t_class *c, t_propertiesfn f);\nEXTERN t_propertiesfn class_getpropertiesfn(const t_class *c);\n\ntypedef void (*t_classfreefn)(t_class *);\nEXTERN void class_setfreefn(t_class *c, t_classfreefn fn);\n\n#ifndef PD_CLASS_DEF\n#define class_addbang(x, y) class_addbang((x), (t_method)(y))\n#define class_addpointer(x, y) class_addpointer((x), (t_method)(y))\n#define class_addfloat(x, y) class_doaddfloat((x), (t_method)(y))\n#define class_addsymbol(x, y) class_addsymbol((x), (t_method)(y))\n#define class_addlist(x, y) class_addlist((x), (t_method)(y))\n#define class_addanything(x, y) class_addanything((x), (t_method)(y))\n#endif\n\n#if PD_FLOATSIZE == 64\n# define class_new class_new64\n#endif\n\n/* ------------   printing --------------------------------- */\n\nEXTERN void post(const char *fmt, ...);\nEXTERN void startpost(const char *fmt, ...);\nEXTERN void poststring(const char *s);\nEXTERN void postfloat(t_floatarg f);\nEXTERN void postatom(int argc, const t_atom *argv);\nEXTERN void endpost(void);\n\nEXTERN void bug(const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(1, 2);\nEXTERN void pd_error(const void *object, const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(2, 3);\n\n/* for logpost(); does *not* work with verbose()! */\ntypedef enum {\n    PD_CRITICAL = 0,\n    PD_ERROR,\n    PD_NORMAL,\n    PD_DEBUG,\n    PD_VERBOSE\n} t_loglevel;\n\nEXTERN void logpost(const void *object, int level, const char *fmt, ...)\n    ATTRIBUTE_FORMAT_PRINTF(3, 4);\n\n/* deprecated, use logpost() instead. */\nEXTERN void verbose(int level, const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(2, 3);\n\n\n/* ------------  system interface routines ------------------- */\nEXTERN int sys_isabsolutepath(const char *dir);\nEXTERN void sys_bashfilename(const char *from, char *to);\nEXTERN void sys_unbashfilename(const char *from, char *to);\nEXTERN int open_via_path(const char *dir, const char *name, const char *ext,\n    char *dirresult, char **nameresult, unsigned int size, int bin);\nEXTERN int sched_geteventno(void);\nEXTERN double sys_getrealtime(void);\nEXTERN int (*sys_idlehook)(void);   /* hook to add idle time computation */\n\n/* Win32's open()/fopen() do not handle UTF-8 filenames so we need\n * these internal versions that handle UTF-8 filenames the same across\n * all platforms.  They are recommended for use in external\n * objectclasses as well so they work with Unicode filenames on Windows */\nEXTERN int sys_open(const char *path, int oflag, ...);\nEXTERN int sys_close(int fd);\nEXTERN FILE *sys_fopen(const char *filename, const char *mode);\nEXTERN int sys_fclose(FILE *stream);\n\n/* ------------  threading ------------------- */\nEXTERN void sys_lock(void);\nEXTERN void sys_unlock(void);\nEXTERN int sys_trylock(void);\n\n\n/* --------------- signals ----------------------------------- */\n\ntypedef PD_FLOATTYPE t_sample;\ntypedef union _sampleint_union {\n  t_sample f;\n  PD_FLOATUINTTYPE i;\n} t_sampleint_union;\n#define MAXLOGSIG 32\n#define MAXSIGSIZE (1 << MAXLOGSIG)\n\ntypedef struct _signal\n{\n    union\n    {\n        int s_length;       /* number of items per channel */\n        int s_n;            /* for source compatibility: pre-0.54 name */\n    };\n    t_sample *s_vec;        /* the samples, s_nchans vectors of s_length */\n    t_float s_sr;           /* samples per second per channel */\n    int s_nchans;           /* number of channels */\n    int s_overlap;          /* number of times each sample appears */\n    int s_refcount;         /* number of times signal is referenced */\n    int s_isborrowed;       /* whether we're going to borrow our array */\n    int s_isscalar;         /* scalar for an unconnected signal input */\n    struct _signal *s_borrowedfrom;     /* signal to borrow it from */\n    struct _signal *s_nextfree;         /* next in freelist */\n    struct _signal *s_nextused;         /* next in used list */\n    int s_nalloc;      /* allocated size of array in points */\n} t_signal;\n\ntypedef t_int *(*t_perfroutine)(t_int *args);\n\nEXTERN t_signal *signal_new(int length, int nchans, t_float sr,\n    t_sample *scalarptr);\nEXTERN void signal_setmultiout(t_signal **sig, int nchans);\n\nEXTERN t_int *plus_perform(t_int *args);\nEXTERN t_int *plus_perf8(t_int *args);\nEXTERN t_int *zero_perform(t_int *args);\nEXTERN t_int *zero_perf8(t_int *args);\nEXTERN t_int *copy_perform(t_int *args);\nEXTERN t_int *copy_perf8(t_int *args);\nEXTERN t_int *scalarcopy_perform(t_int *args);\nEXTERN t_int *scalarcopy_perf8(t_int *args);\n\nEXTERN void dsp_add_plus(t_sample *in1, t_sample *in2, t_sample *out, int n);\nEXTERN void dsp_add_copy(t_sample *in, t_sample *out, int n);\nEXTERN void dsp_add_scalarcopy(t_float *in, t_sample *out, int n);\nEXTERN void dsp_add_zero(t_sample *out, int n);\n\nEXTERN int sys_getblksize(void);\nEXTERN t_float sys_getsr(void);\nEXTERN int sys_get_inchannels(void);\nEXTERN int sys_get_outchannels(void);\n\nEXTERN void dsp_add(t_perfroutine f, int n, ...);\nEXTERN void dsp_addv(t_perfroutine f, int n, t_int *vec);\nEXTERN void pd_fft(t_float *buf, int npoints, int inverse);\nEXTERN int ilog2(int n);\n\nEXTERN void mayer_fht(t_sample *fz, int n);\nEXTERN void mayer_fft(int n, t_sample *real, t_sample *imag);\nEXTERN void mayer_ifft(int n, t_sample *real, t_sample *imag);\nEXTERN void mayer_realfft(int n, t_sample *real);\nEXTERN void mayer_realifft(int n, t_sample *real);\n\nEXTERN float *cos_table;\n#define LOGCOSTABSIZE 9\n#define COSTABSIZE (1<<LOGCOSTABSIZE)\n\nEXTERN int canvas_suspend_dsp(void);\nEXTERN void canvas_resume_dsp(int oldstate);\nEXTERN void canvas_update_dsp(void);\nEXTERN int canvas_dspstate;\n\n/*   up/downsampling */\ntypedef struct _resample\n{\n  int method;       /* unused */\n\n  int downsample; /* downsampling factor */\n  int upsample;   /* upsampling factor */\n\n  t_sample *s_vec;   /* here we hold the resampled data */\n  int      s_n;\n\n  t_sample *coeffs;  /* coefficients for filtering... */\n  int      coefsize;\n\n  t_sample *buffer;  /* buffer for filtering */\n  int      bufsize;\n} t_resample;\n\nEXTERN void resample_init(t_resample *x);\nEXTERN void resample_free(t_resample *x);\n\nEXTERN void resample_dsp(t_resample *x, t_sample *in, int insize, t_sample *out, int outsize, int method);\nEXTERN void resamplefrom_dsp(t_resample *x, t_sample *in, int insize, int outsize, int method);\nEXTERN void resampleto_dsp(t_resample *x, t_sample *out, int insize, int outsize, int method);\n\n/* ----------------------- utility functions for signals -------------- */\nEXTERN t_float mtof(t_float);\nEXTERN t_float ftom(t_float);\nEXTERN t_float rmstodb(t_float);\nEXTERN t_float powtodb(t_float);\nEXTERN t_float dbtorms(t_float);\nEXTERN t_float dbtopow(t_float);\n\nEXTERN t_float q8_sqrt(t_float);\nEXTERN t_float q8_rsqrt(t_float);\n#ifndef N32\nEXTERN t_float qsqrt(t_float);  /* old names kept for extern compatibility */\nEXTERN t_float qrsqrt(t_float);\n#endif\n\n/* --------------------- data --------------------------------- */\n\n    /* graphical arrays */\nEXTERN_STRUCT _garray;\n#define t_garray struct _garray\n\nEXTERN t_class *garray_class;\nEXTERN int garray_getfloatarray(t_garray *x, int *size, t_float **vec);\nEXTERN int garray_getfloatwords(t_garray *x, int *size, t_word **vec);\nEXTERN void garray_redraw(t_garray *x);\nEXTERN int garray_npoints(t_garray *x);\nEXTERN char *garray_vec(t_garray *x);\nEXTERN void garray_resize(t_garray *x, t_floatarg f);  /* avoid; use this: */\nEXTERN void garray_resize_long(t_garray *x, long n);   /* better version */\nEXTERN void garray_usedindsp(t_garray *x);\nEXTERN void garray_setsaveit(t_garray *x, int saveit);\nEXTERN t_glist *garray_getglist(t_garray *x);\nEXTERN t_array *garray_getarray(t_garray *x);\nEXTERN t_class *scalar_class;\n\nEXTERN t_float *value_get(t_symbol *s);\nEXTERN void value_release(t_symbol *s);\nEXTERN int value_getfloat(t_symbol *s, t_float *f);\nEXTERN int value_setfloat(t_symbol *s, t_float f);\n\n/* ------- GUI interface - functions to send strings to TK --------- */\ntypedef void (*t_guicallbackfn)(t_gobj *client, t_glist *glist);\n\nEXTERN void sys_vgui(const char *fmt, ...); /* avoid this: use pdgui_vmess() instead */\nEXTERN void sys_gui(const char *s); /* avoid this: use pdgui_vmess() instead */\n\nEXTERN void sys_pretendguibytes(int n);\nEXTERN void sys_queuegui(void *client, t_glist *glist, t_guicallbackfn f);\nEXTERN void sys_unqueuegui(void *client);\n    /* dialog window creation and destruction */\nEXTERN void gfxstub_new(t_pd *owner, void *key, const char *cmd); /* avoid this: use pdgui_stub_vnew() instead */\nEXTERN void gfxstub_deleteforkey(void *key); /* avoid this: use pdgui_stub_deleteforkey() instead */\n\n/*\n * send a message to the GUI, with a simplified formatting syntax\n * <destination>: receiver on the GUI side (e.g. a Tcl/Tk 'proc')\n * <fmt>: string of format specifiers\n * <...>: values according to the format specifiers\n *\n * the <destination> can be a NULL pointer (in which case it is ignored)\n * the user of NULL as a <destination> is discouraged\n\n * depending on the format specifiers, one or more values are passed\n *    'f' : <double:value>        : a floating point number\n *    'i' : <int:value>           : an integer number\n *    's' : <const char*:value>   : a string\n *    'r' : <const char*:value>   : a raw string\n *    'x' : <void*:value>         : a generic pointer\n *    'o' : <t_object*:value>     : an graphical object\n *    '^' : <t_canvas*:value>     : a toplevel window (legacy)\n *    'c' : <t_canvas*:value>     : a canvas (on a window)\n *    'F' : <int:size> <const t_float*:values>: array of t_float's\n *    'S' : <int:size> <const char**:values>: array of strings\n *    'R' : <int:size> <const char**:values>: array of raw strings\n *    'a' : <int:size> <const t_atom*:values>: list of atoms\n *    'A' : <int:size> <const t_atom*:values>: array of atoms\n *    'w' : <int:size> <const t_word*:values>: list of floatwords\n *    'W' : <int:size> <const t_word*:values>: array of floatwords\n *    'm' : <t_symbol*s:recv> <int:argc> <t_atom*:argv>: a Pd message\n *    'p' : <int:size> <const char*:values>  : a pascal string (explicit size; not \\0-terminated)\n *    'k' : <int:color>           : a color (or kolor, if you prefer)\n *    ' ' : none                  : ignored\n * the use of the specifiers 'x^' is discouraged\n * raw-strings ('rR') should only be used for constant, well-known strings\n */\nEXTERN void pdgui_vmess(const char* destination, const char* fmt, ...);\n\n\n/* improved dialog window creation\n * this will insert a first argument to <destination> based on <key>\n * which the GUI can then use to callback.\n * gfxstub_new() ensures that the given receiver will be available,\n * even if the <owner> has been removed in the meantime.\n * see pdgui_vmess() for a description of <fmt> and the varargs\n */\n\nEXTERN void pdgui_stub_vnew(t_pd *owner, const char* destination, void *key, const char* fmt, ...);\nEXTERN void pdgui_stub_deleteforkey(void *key);\n\n\nextern t_class *glob_pdobject;  /* object to send \"pd\" messages */\n\n/*-------------  Max 0.26 compatibility --------------------*/\n\n/* the following reflects the new way classes are laid out, with the class\n   pointing to the messlist and not vice versa. Externs shouldn't feel it. */\ntypedef t_class *t_externclass;\n\nEXTERN void c_extern(t_externclass *cls, t_newmethod newroutine,\n    t_method freeroutine, t_symbol *name, size_t size, int tiny, \\\n    t_atomtype arg1, ...);\nEXTERN void c_addmess(t_method fn, t_symbol *sel, t_atomtype arg1, ...);\n\n#define t_getbytes getbytes\n#define t_freebytes freebytes\n#define t_resizebytes resizebytes\n#define typedmess pd_typedmess\n#define vmess pd_vmess\n\n/* A definition to help gui objects straddle 0.34-0.35 changes.  If this is\ndefined, there is a \"te_xpix\" field in objects, not a \"te_xpos\" as before: */\n\n#define PD_USE_TE_XPIX\n\n#ifndef _MSC_VER /* Microoft compiler can't handle \"inline\" function/macros */\n#if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)\n/* a test for NANs and denormals. Should only be necessary on i386. */\n#if PD_FLOATSIZE == 32\n\ntypedef union\n{\n    t_float f;\n    unsigned int ui;\n} t_bigorsmall32;\n\nstatic inline int PD_BADFLOAT(t_float f)  /* malformed float */\n{\n    t_bigorsmall32 pun;\n    pun.f = f;\n    pun.ui &= 0x7f800000;\n    return((pun.ui == 0) | (pun.ui == 0x7f800000));\n}\n\nstatic inline int PD_BIGORSMALL(t_float f)  /* exponent outside (-64,64) */\n{\n    t_bigorsmall32 pun;\n    pun.f = f;\n    return((pun.ui & 0x20000000) == ((pun.ui >> 1) & 0x20000000));\n}\n\n#elif PD_FLOATSIZE == 64\n\ntypedef union\n{\n    t_float f;\n    unsigned int ui[2];\n} t_bigorsmall64;\n\nstatic inline int PD_BADFLOAT(t_float f)  /* malformed double */\n{\n    t_bigorsmall64 pun;\n    pun.f = f;\n    pun.ui[1] &= 0x7ff00000;\n    return((pun.ui[1] == 0) | (pun.ui[1] == 0x7ff00000));\n}\n\nstatic inline int PD_BIGORSMALL(t_float f)  /* exponent outside (-512,512) */\n{\n    t_bigorsmall64 pun;\n    pun.f = f;\n    return((pun.ui[1] & 0x20000000) == ((pun.ui[1] >> 1) & 0x20000000));\n}\n\n#endif /* PD_FLOATSIZE */\n#else /* not INTEL or ARM */\n#define PD_BADFLOAT(f) 0\n#define PD_BIGORSMALL(f) 0\n#endif\n\n#else   /* _MSC_VER */\n#if PD_FLOATSIZE == 32\n#define PD_BADFLOAT(f) ((((*(unsigned int*)&(f))&0x7f800000)==0) || \\\n    (((*(unsigned int*)&(f))&0x7f800000)==0x7f800000))\n/* more stringent test: anything not between 1e-19 and 1e19 in absolute val */\n#define PD_BIGORSMALL(f) ((((*(unsigned int*)&(f))&0x60000000)==0) || \\\n    (((*(unsigned int*)&(f))&0x60000000)==0x60000000))\n#else   /* 64 bits... don't know what to do here */\n#define PD_BADFLOAT(f) (!(((f) >= 0) || ((f) <= 0)))\n#define PD_BIGORSMALL(f) ((f) > 1e150 || (f) <  -1e150 \\\n    || (f) > -1e-150 && (f) < 1e-150 )\n#endif\n#endif /* _MSC_VER */\n\n    /* get version number at run time */\nEXTERN void sys_getversion(int *major, int *minor, int *bugfix);\n\n    /* get floatsize at run time */\nEXTERN unsigned int sys_getfloatsize(void);\n\nEXTERN_STRUCT _instancemidi;\n#define t_instancemidi struct _instancemidi\n\nEXTERN_STRUCT _instanceinter;\n#define t_instanceinter struct _instanceinter\n\nEXTERN_STRUCT _instancecanvas;\n#define t_instancecanvas struct _instancecanvas\n\nEXTERN_STRUCT _instanceugen;\n#define t_instanceugen struct _instanceugen\n\nEXTERN_STRUCT _instancestuff;\n#define t_instancestuff struct _instancestuff\n\n#ifndef PDTHREADS\n#define PDTHREADS 1\n#endif\n\nstruct _pdinstance\n{\n    double pd_systime;          /* global time in Pd ticks */\n    t_clock *pd_clock_setlist;  /* list of set clocks */\n    t_canvas *pd_canvaslist;    /* list of all root canvases */\n    struct _template *pd_templatelist;  /* list of all templates */\n    int pd_instanceno;          /* ordinal number of this instance */\n    t_symbol **pd_symhash;      /* symbol table hash table */\n    t_instancemidi *pd_midi;    /* private stuff for x_midi.c */\n    t_instanceinter *pd_inter;  /* private stuff for s_inter.c */\n    t_instanceugen *pd_ugen;    /* private stuff for d_ugen.c */\n    t_instancecanvas *pd_gui;   /* semi-private stuff in g_canvas.h */\n    t_instancestuff *pd_stuff;  /* semi-private stuff in s_stuff.h */\n    t_pd *pd_newest;            /* most recently created object */\n#ifdef PDINSTANCE\n    t_symbol  pd_s_pointer;\n    t_symbol  pd_s_float;\n    t_symbol  pd_s_symbol;\n    t_symbol  pd_s_bang;\n    t_symbol  pd_s_list;\n    t_symbol  pd_s_anything;\n    t_symbol  pd_s_signal;\n    t_symbol  pd_s__N;\n    t_symbol  pd_s__X;\n    t_symbol  pd_s_x;\n    t_symbol  pd_s_y;\n    t_symbol  pd_s_;\n#endif\n#if PDTHREADS\n    int pd_islocked;\n#endif\n};\n#define t_pdinstance struct _pdinstance\nEXTERN t_pdinstance pd_maininstance;\n\n/* m_pd.c */\n#ifdef PDINSTANCE\nEXTERN t_pdinstance *pdinstance_new(void);\nEXTERN void pd_setinstance(t_pdinstance *x);\nEXTERN void pdinstance_free(t_pdinstance *x);\n#endif /* PDINSTANCE */\n\n#if defined(PDTHREADS) && defined(PDINSTANCE)\n#ifdef _MSC_VER\n#define PERTHREAD __declspec(thread)\n#else\n#define PERTHREAD __thread\n#endif /* _MSC_VER */\n#else\n#define PERTHREAD\n#endif\n\n#ifdef PDINSTANCE\nextern PERTHREAD t_pdinstance *pd_this;\nEXTERN t_pdinstance **pd_instances;\nEXTERN int pd_ninstances;\n#else\n#define pd_this (&pd_maininstance)\n#endif /* PDINSTANCE */\n\n#ifdef PDINSTANCE\n#define s_pointer   (pd_this->pd_s_pointer)\n#define s_float     (pd_this->pd_s_float)\n#define s_symbol    (pd_this->pd_s_symbol)\n#define s_bang      (pd_this->pd_s_bang)\n#define s_list      (pd_this->pd_s_list)\n#define s_anything  (pd_this->pd_s_anything)\n#define s_signal    (pd_this->pd_s_signal)\n#define s__N        (pd_this->pd_s__N)\n#define s__X        (pd_this->pd_s__X)\n#define s_x         (pd_this->pd_s_x)\n#define s_y         (pd_this->pd_s_y)\n#define s_          (pd_this->pd_s_)\n#else\nEXTERN t_symbol s_pointer, s_float, s_symbol, s_bang, s_list, s_anything,\n  s_signal, s__N, s__X, s_x, s_y, s_;\n#endif\n\nEXTERN t_canvas *pd_getcanvaslist(void);\nEXTERN int pd_getdspstate(void);\n\n/* x_text.c */\nEXTERN t_binbuf *text_getbufbyname(t_symbol *s); /* get binbuf from text obj */\nEXTERN void text_notifybyname(t_symbol *s);      /* notify it was modified */\n\n/* g_undo.c */\n/* store two message-sets to be sent to the object's <s> method for 'undo'ing\n * resp. 'redo'ing the current state of an object.\n * this creates an internal copy of the atom-lists (so the caller is responsible\n * for freeing any dynamically allocated data)\n * this is a no-op if called during 'undo' (resp. 'redo').\n */\nEXTERN void pd_undo_set_objectstate(t_canvas*canvas, t_pd*x, t_symbol*s,\n                                    int undo_argc, t_atom*undo_argv,\n                                    int redo_argc, t_atom*redo_argv);\n\n#if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus)\n}\n#endif\n\n#define __m_pd_h_\n#endif /* __m_pd_h_ */\n"
  },
  {
    "path": "libs/libpd/pure-data/src/m_private_utils.h",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette and others.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* This file contains some small header-only utilities to be used by Pd's code. */\n\n/* NOTE: this file is an implementation detail of Pd, not to be in externals */\n\n#ifndef M_PRIVATE_UTILS_H\n#define M_PRIVATE_UTILS_H\n\n#ifndef PD_INTERNAL\n# error m_private_utils.h is a PRIVATE header. do *not* use it in your externals\n#endif\n\n#ifdef HAVE_CONFIG_H\n/* autotools might put all the HAVE_... defines into \"config.h\" */\n# include \"config.h\"\n#endif\n\n\n/* --------------------------- stack allocation helpers --------------------- */\n/* alloca helpers\n * - ALLOCA(type, array, nmemb, maxnmemb)\n *     allocates <nmemb> elements of <type> and points <array> to the newly\n *     allocated memory. if <nmemb> exceeds <maxnmemb>, allocation is done on\n *     the heap, otherwise on the stack\n * - FREEA(type, array, nmemb, maxnmemb)\n *     frees the <array> allocated by ALLOCA() (unless allocation was done on\n *     the heap)\n * if DONT_USE_ALLOCA is defined (and true), always allocates on the heap\n *\n * usage example:\n * {\n *   t_atom*outv;\n *   ALLOCA(t_atom, outv, argc, 100);\n *   // do something with 'outv'\n *   FREEA(t_atom, outv, argc, 100);\n * }\n */\n# ifdef ALLOCA\n#  undef ALLOCA\n# endif\n# ifdef FREEA\n#  undef FREEA\n# endif\n\n#if DONT_USE_ALLOCA\n/* heap versions */\n# define ALLOCA(type, array, nmemb, maxnmemb) ((array) = (type *)getbytes((nmemb) * sizeof(type)))\n# define FREEA(type, array, nmemb, maxnmemb) (freebytes((array), (nmemb) * sizeof(type)))\n\n#else /* !DONT_USE_ALLOCA */\n/* stack version (unless <nmemb> exceeds <maxnmemb>) */\n\n# ifdef HAVE_ALLOCA_H\n#  include <alloca.h> /* linux, mac, mingw, cygwin,... */\n# elif defined _WIN32\n#  include <malloc.h> /* MSVC or mingw on windows */\n# else\n#  include <stdlib.h> /* BSDs for example */\n# endif\n\n# define ALLOCA(type, array, nmemb, maxnmemb) ((array) = (type *)((nmemb) < (maxnmemb) ? \\\n            alloca((nmemb) * sizeof(type)) : getbytes((nmemb) * sizeof(type))))\n# define FREEA(type, array, nmemb, maxnmemb) (                          \\\n        ((nmemb) < (maxnmemb) || (freebytes((array), (nmemb) * sizeof(type)), 0)))\n#endif /* !DONT_USE_ALLOCA */\n\n\n/* --------------------------- endianness helpers --------------------- */\n#ifdef HAVE_MACHINE_ENDIAN_H\n# include <machine/endian.h>\n#elif defined HAVE_ENDIAN_H\n# include <endian.h>\n#endif\n\n#ifdef __MINGW32__\n# include <sys/param.h>\n#endif\n\n/* BSD has deprecated BYTE_ORDER in favour of _BYTE_ORDER\n * others might follow...\n */\n#if !defined(BYTE_ORDER) && defined(_BYTE_ORDER)\n# define BYTE_ORDER _BYTE_ORDER\n#endif\n#if !defined(LITTLE_ENDIAN) && defined(_LITTLE_ENDIAN)\n# define LITTLE_ENDIAN _LITTLE_ENDIAN\n#endif\n\n#ifdef _MSC_VER\n/* _MSVC lacks BYTE_ORDER and LITTLE_ENDIAN */\n# if !defined(LITTLE_ENDIAN)\n#  define LITTLE_ENDIAN 0x0001\n# endif\n# if !defined(BYTE_ORDER)\n#  define BYTE_ORDER LITTLE_ENDIAN\n# endif\n#endif\n\n#if !defined(BYTE_ORDER) || !defined(LITTLE_ENDIAN)\n# if defined(__GNUC__) && defined(_XOPEN_SOURCE)\n#  warning unable to detect endianness (continuing anyhow)\n# else\n#  error unable to detect endianness\n# endif\n#endif\n\n\n/* -------------------------MSVC compat defines --------------------- */\n#ifdef _MSC_VER\n# define snprintf _snprintf\n#endif\n\n\n#endif /* M_PRIVATE_UTILS_H */\n"
  },
  {
    "path": "libs/libpd/pure-data/src/m_sched.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/*  scheduling stuff  */\n\n#include \"m_pd.h\"\n#include \"m_imp.h\"\n#include \"s_stuff.h\"\n#ifdef _WIN32\n#include <windows.h>\n#endif\n\n    /* LATER consider making this variable.  It's now the LCM of all sample\n    rates we expect to see: 32000, 44100, 48000, 88200, 96000. */\n#define TIMEUNITPERMSEC (32. * 441.)\n#define TIMEUNITPERSECOND (TIMEUNITPERMSEC * 1000.)\n#define SYSTIMEPERTICK \\\n    ((STUFF->st_schedblocksize/STUFF->st_dacsr) * TIMEUNITPERSECOND)\n#define APPROXTICKSPERSEC \\\n    ((int)(STUFF->st_dacsr /(double)STUFF->st_schedblocksize))\n\n#define SYS_QUIT_QUIT 1\n#define SYS_QUIT_RESTART 2\nstatic int sys_quit;\nextern int sys_nosleep;\n\nint sys_usecsincelastsleep(void);\nint sys_sleepgrain;\n\ntypedef void (*t_clockmethod)(void *client);\n\nstruct _clock\n{\n    double c_settime;       /* in TIMEUNITS; <0 if unset */\n    void *c_owner;\n    t_clockmethod c_fn;\n    struct _clock *c_next;\n    t_float c_unit;         /* >0 if in TIMEUNITS; <0 if in samples */\n};\n\n#ifdef HAVE_UNISTD_H\n#include <unistd.h>\n#endif\n\nt_clock *clock_new(void *owner, t_method fn)\n{\n    t_clock *x = (t_clock *)getbytes(sizeof *x);\n    x->c_settime = -1;\n    x->c_owner = owner;\n    x->c_fn = (t_clockmethod)fn;\n    x->c_next = 0;\n    x->c_unit = TIMEUNITPERMSEC;\n    return (x);\n}\n\nvoid clock_unset(t_clock *x)\n{\n    if (x->c_settime >= 0)\n    {\n        if (x == pd_this->pd_clock_setlist)\n            pd_this->pd_clock_setlist = x->c_next;\n        else\n        {\n            t_clock *x2 = pd_this->pd_clock_setlist;\n            while (x2->c_next != x) x2 = x2->c_next;\n            x2->c_next = x->c_next;\n        }\n        x->c_settime = -1;\n    }\n}\n\n    /* set the clock to call back at an absolute system time */\nvoid clock_set(t_clock *x, double setticks)\n{\n    if (setticks < pd_this->pd_systime) setticks = pd_this->pd_systime;\n    clock_unset(x);\n    x->c_settime = setticks;\n    if (pd_this->pd_clock_setlist &&\n        pd_this->pd_clock_setlist->c_settime <= setticks)\n    {\n        t_clock *cbefore, *cafter;\n        for (cbefore = pd_this->pd_clock_setlist,\n            cafter = pd_this->pd_clock_setlist->c_next;\n                cbefore; cbefore = cafter, cafter = cbefore->c_next)\n        {\n            if (!cafter || cafter->c_settime > setticks)\n            {\n                cbefore->c_next = x;\n                x->c_next = cafter;\n                return;\n            }\n        }\n    }\n    else x->c_next = pd_this->pd_clock_setlist, pd_this->pd_clock_setlist = x;\n}\n\n    /* set the clock to call back after a delay in msec */\nvoid clock_delay(t_clock *x, double delaytime)\n{\n    clock_set(x, (x->c_unit > 0 ?\n        pd_this->pd_systime + x->c_unit * delaytime :\n            pd_this->pd_systime -\n                (x->c_unit*(TIMEUNITPERSECOND/STUFF->st_dacsr)) * delaytime));\n}\n\n    /* set the time unit in msec or (if 'samps' is set) in samples.  This\n    is flagged by setting c_unit negative.  If the clock is currently set,\n    recalculate the delay based on the new unit and reschedule */\nvoid clock_setunit(t_clock *x, double timeunit, int sampflag)\n{\n    double timeleft;\n    if (timeunit <= 0)\n        timeunit = 1;\n    /* if no change, return to avoid truncation errors recalculating delay */\n    if ((sampflag && (timeunit == -x->c_unit)) ||\n        (!sampflag && (timeunit == x->c_unit * TIMEUNITPERMSEC)))\n            return;\n\n        /* figure out time left in the units we were in */\n    timeleft = (x->c_settime < 0 ? -1 :\n        (x->c_settime - pd_this->pd_systime)/((x->c_unit > 0)? x->c_unit :\n            (x->c_unit*(TIMEUNITPERSECOND/STUFF->st_dacsr))));\n    if (sampflag)\n        x->c_unit = -timeunit;  /* negate to flag sample-based */\n    else x->c_unit = timeunit * TIMEUNITPERMSEC;\n    if (timeleft >= 0)  /* reschedule if already set */\n        clock_delay(x, timeleft);\n}\n\n    /* get current logical time.  We don't specify what units this is in;\n    use clock_gettimesince() to measure intervals from time of this call. */\ndouble clock_getlogicaltime(void)\n{\n    return (pd_this->pd_systime);\n}\n\n    /* OBSOLETE (misleading) function name kept for compatibility */\ndouble clock_getsystime(void) { return (pd_this->pd_systime); }\n\n    /* elapsed time in milliseconds since the given system time */\ndouble clock_gettimesince(double prevsystime)\n{\n    return ((pd_this->pd_systime - prevsystime)/TIMEUNITPERMSEC);\n}\n\n    /* elapsed time in units, ala clock_setunit(), since given system time */\ndouble clock_gettimesincewithunits(double prevsystime,\n    double units, int sampflag)\n{\n            /* If in samples, divide TIMEUNITPERSECOND/sys_dacsr first (at\n            cost of an extra division) since it's probably an integer and if\n            units == 1 and (sys_time - prevsystime) is an integer number of\n            DSP ticks, the result will be exact. */\n    if (sampflag)\n        return ((pd_this->pd_systime - prevsystime)/\n            ((TIMEUNITPERSECOND/STUFF->st_dacsr)*units));\n    else return ((pd_this->pd_systime - prevsystime)/(TIMEUNITPERMSEC*units));\n}\n\n    /* what value the system clock will have after a delay */\ndouble clock_getsystimeafter(double delaytime)\n{\n    return (pd_this->pd_systime + TIMEUNITPERMSEC * delaytime);\n}\n\nvoid clock_free(t_clock *x)\n{\n    clock_unset(x);\n    freebytes(x, sizeof *x);\n}\n\n\nvoid glob_audiostatus(void)\n{\n    /* rewrite me */\n}\n\nstatic int sched_diored;\nstatic int sched_dioredtime;\nstatic int sched_meterson;\nstatic int sched_counter;\n\nstatic void sys_addhist(int n) {}   /* maybe revive this later for profiling */\nstatic void sys_clearhist(void) {}\n\nvoid sys_log_error(int type)\n{\n    if (type != ERR_NOTHING && !sched_diored &&\n        (sched_counter >= sched_dioredtime))\n    {\n        pdgui_vmess(\"pdtk_pd_dio\", \"i\", 1);\n        sched_diored = 1;\n    }\n    sched_dioredtime = sched_counter + APPROXTICKSPERSEC;\n}\n\nstatic int sched_lastinclip, sched_lastoutclip,\n    sched_lastindb, sched_lastoutdb;\n\nvoid glob_watchdog(t_pd *dummy);\n\nstatic float sched_fastforward;\n\nvoid glob_fastforward(void *dummy, t_floatarg f)\n{\n    sched_fastforward = TIMEUNITPERMSEC * f;\n}\n\nvoid dsp_tick(void);\n\nstatic int sched_useaudio = SCHED_AUDIO_NONE;\nstatic double sched_referencerealtime, sched_referencelogicaltime;\n\nvoid sched_reopenmeplease(void)   /* request from s_audio for deferred reopen */\n{\n    sys_quit = SYS_QUIT_RESTART;\n}\n\nvoid sched_set_using_audio(int flag)\n{\n    sched_useaudio = flag;\n    if (flag == SCHED_AUDIO_NONE)\n    {\n        sched_referencerealtime = sys_getrealtime();\n        sched_referencelogicaltime = clock_getlogicaltime();\n    }\n        if (flag == SCHED_AUDIO_CALLBACK &&\n            sched_useaudio != SCHED_AUDIO_CALLBACK)\n                sys_quit = SYS_QUIT_RESTART;\n        if (flag != SCHED_AUDIO_CALLBACK &&\n            sched_useaudio == SCHED_AUDIO_CALLBACK)\n                post(\"sorry, can't turn off callbacks yet; restart Pd\");\n                    /* not right yet! */\n\n    pdgui_vmess(\"pdtk_pd_audio\", \"r\", flag ? \"on\" : \"off\");\n}\n\n    /* take the scheduler forward one DSP tick, also handling clock timeouts */\nvoid sched_tick(void)\n{\n    double next_sys_time = pd_this->pd_systime + SYSTIMEPERTICK;\n    int countdown = 5000;\n    while (pd_this->pd_clock_setlist &&\n        pd_this->pd_clock_setlist->c_settime < next_sys_time)\n    {\n        t_clock *c = pd_this->pd_clock_setlist;\n        pd_this->pd_systime = c->c_settime;\n        clock_unset(pd_this->pd_clock_setlist);\n        outlet_setstacklim();\n        (*c->c_fn)(c->c_owner);\n        if (!countdown--)\n        {\n            countdown = 5000;\n            (void)sys_pollgui();\n        }\n        if (sys_quit)\n            return;\n    }\n    pd_this->pd_systime = next_sys_time;\n    dsp_tick();\n    sched_counter++;\n}\n\nint sched_get_sleepgrain( void)\n{\n    return (sys_sleepgrain > 0 ? sys_sleepgrain :\n        (sys_schedadvance/4 > 5000 ? 5000 : (sys_schedadvance/4 < 100 ? 100 :\n            sys_schedadvance/4)));\n}\n\n    /* old stuff for extern binary compatibility -- remove someday */\nint *get_sys_sleepgrain(void) {return(&sys_sleepgrain);}\n\n/*\nHere is Pd's \"main loop.\"  This routine dispatches clock timeouts and DSP\n\"ticks\" deterministically, and polls for input from MIDI and the GUI.  If\nwe're left idle we also poll for graphics updates; but these are considered\nlower priority than the rest.\n\nThe time source is normally the audio I/O subsystem via the \"sys_send_dacs()\"\ncall.  This call returns true if samples were transferred; false means that\nthe audio I/O system is still busy with previous transfers.\n*/\n\nvoid sys_pollmidiqueue(void);\nvoid sys_initmidiqueue(void);\n\n /* sys_idlehook is a hook the user can fill in to grab idle time.  Return\nnonzero if you actually used the time; otherwise we're really really idle and\nwill now sleep. */\nint (*sys_idlehook)(void);\n\n    /* when audio is idle, see to GUI and other stuff */\nstatic int sched_idletask( void)\n{\n    static int sched_nextmeterpolltime, sched_nextpingtime;\n    int rtn = 0;\n    sys_lock();\n    if (sys_pollgui())\n        rtn = 1;\n    sys_unlock();\n\n        /* if there's no GUI but we're running in \"realtime\", here is\n        where we arrange to ping the watchdog every 2 seconds.  (If there's\n        a GUI, it initiates the ping instead to be sure there's communication\n        back and forth.) */\n    if (!sys_havegui() && sys_hipriority && sched_counter > sched_nextpingtime)\n    {\n        glob_watchdog(0);\n            /* ping every 2 seconds */\n        sched_nextpingtime = sched_counter + 2 * APPROXTICKSPERSEC;\n    }\n\n        /* clear the \"DIO error\" warning 1 sec after it flashes */\n    if (sched_counter > sched_nextmeterpolltime)\n    {\n        if (sched_diored && (sched_counter - sched_dioredtime > 0))\n        {\n            pdgui_vmess(\"pdtk_pd_dio\", \"i\", 0);\n            sched_diored = 0;\n        }\n        sched_nextmeterpolltime = sched_counter + APPROXTICKSPERSEC;\n    }\n    return (rtn || sys_idlehook && sys_idlehook());\n}\n\nstatic void m_pollingscheduler(void)\n{\n    sys_lock();\n    sys_initmidiqueue();\n    while (!sys_quit)   /* outer loop runs once per tick */\n    {\n        sys_addhist(0);\n        sched_tick();\n        sys_addhist(1);\n\n            /* fast forward, in which the scheduler advances without waiting\n            for real time; for patches that alternate between interactive\n            and batch-like computations. */\n        if (sched_fastforward > 0)\n        {\n            sched_fastforward -= SYSTIMEPERTICK;\n            sched_referencerealtime = sys_getrealtime();\n            sched_referencelogicaltime = pd_this->pd_systime;\n            continue;\n        }\n        sys_pollgui();\n        sys_pollmidiqueue();\n        sys_addhist(2);\n        while (!sys_quit)   /* inner loop runs until it can transfer audio */\n        {\n            int timeforward; /* SENDDACS_YES if audio was transferred, SENDDACS_NO if not,\n                                or SENDDACS_SLEPT if yes but time elapsed during xfer */\n            sys_unlock();\n            if (sched_useaudio == SCHED_AUDIO_NONE)\n            {\n                    /* no audio; use system clock */\n                double lateness = 1000. *\n                    (sys_getrealtime() - sched_referencerealtime) -\n                        clock_gettimesince(sched_referencelogicaltime);\n                if (lateness > 20000)   /* if 20\" late, don't try to catch up */\n                {\n                    sched_referencerealtime = sys_getrealtime();\n                    sched_referencelogicaltime = pd_this->pd_systime;\n                }\n                timeforward = (lateness > 0 ? SENDDACS_YES : SENDDACS_NO);\n            }\n            else timeforward = sys_send_dacs();\n            sys_addhist(3);\n                /* test for idle; if so, do graphics updates. */\n            if (timeforward != SENDDACS_YES && !sched_idletask() && !sys_nosleep)\n            {\n                /* if even that had nothing to do, sleep. */\n                sys_addhist(4);\n                sys_microsleep();\n            }\n            sys_addhist(5);\n            sys_lock();\n            if (timeforward != SENDDACS_NO)\n                break;\n        }\n    }\n    sys_unlock();\n}\n\nvoid sched_audio_callbackfn(void)\n{\n    sys_lock();\n    sys_addhist(0);\n    sched_tick();\n    sys_addhist(1);\n    sys_pollmidiqueue();\n    sys_addhist(2);\n    sys_unlock();\n    (void)sched_idletask();\n    sys_addhist(3);\n}\n\nstatic void m_callbackscheduler(void)\n{\n    sys_initmidiqueue();\n    while (!sys_quit)\n    {\n        double timewas = pd_this->pd_systime;\n#ifdef _WIN32\n        Sleep(1000);\n#else\n        sleep(1);\n#endif\n        if (pd_this->pd_systime == timewas)\n        {\n            sys_lock();\n            (void)sys_pollgui();\n            sched_tick();\n            sys_unlock();\n        }\n        if (sys_idlehook)\n            sys_idlehook();\n    }\n}\n\nint m_mainloop(void)\n{\n    while (sys_quit != SYS_QUIT_QUIT)\n    {\n        if (sched_useaudio == SCHED_AUDIO_CALLBACK)\n            m_callbackscheduler();\n        else m_pollingscheduler();\n        if (sys_quit == SYS_QUIT_RESTART)\n        {\n            sys_quit = 0;\n            if (audio_isopen())\n            {\n                sys_close_audio();\n                sys_reopen_audio();\n            }\n        }\n    }\n    return (0);\n}\n\nint m_batchmain(void)\n{\n    while (sys_quit != SYS_QUIT_QUIT)\n        sched_tick();\n    return (0);\n}\n\nvoid sys_exit(void)\n{\n    sys_quit = SYS_QUIT_QUIT;\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/s_audio.c",
    "content": "/* Copyright (c) 2003, Miller Puckette and others.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/*  machine-independent (well, mostly!) audio layer.  Stores and recalls\n    audio settings from argparse routine and from dialog window.\n\n    LATER: save audio settings for various APIs for easier switching\n\n*/\n\n#include \"m_pd.h\"\n#include \"s_stuff.h\"\n#include <stdio.h>\n#ifdef _WIN32\n#include <time.h>\n#else\n#include <unistd.h>\n#include <sys/time.h>\n#include <sys/resource.h>\n#endif /* _WIN32 */\n#include <string.h>\n#include <math.h>\n\n#include \"m_private_utils.h\"\n\n#define SYS_DEFAULTCH 2\n#define MAXNDEV 128\n#define DEVDESCSIZE 128\n#define MAXBLOCKSIZE 2048\n\n    /* exported variables */\nint sys_schedadvance;   /* scheduler advance in microseconds */\n\nstatic int sys_audioapiopened; /* what API is open, API_NONE if none */\nstatic int audio_callback_is_open;  /* true if we're open in callback mode */\n\n    /* current parameters (if an API is open) or requested ones otherwise: */\nstatic t_audiosettings audio_nextsettings;\n\nvoid sched_audio_callbackfn(void);\nvoid sched_reopenmeplease(void);\n\nint audio_isopen(void)\n{\n    return (sys_audioapiopened > 0);\n}\n\nstatic int audio_isfixedsr(int api)\n{\n#ifdef USEAPI_JACK\n    /* JACK server sets it's own samplerate */\n    return (api == API_JACK);\n#endif\n    return 0;\n}\n\nstatic int audio_isfixedblocksize(int api)\n{\n#ifdef USEAPI_JACK\n    /* JACK server sets it's own blocksize */\n    return (api == API_JACK);\n#endif\n    return 0;\n}\n\n#ifdef USEAPI_JACK\nint jack_get_blocksize(void);\n#endif\n\nstatic int audio_getfixedblocksize(int api)\n{\n#ifdef USEAPI_JACK\n    /* JACK server sets it's own blocksize */\n    return (api == API_JACK ? jack_get_blocksize() : 0);\n#endif\n    return 0;\n}\n\n    /* inform rest of Pd of current channels and sample rate.  Do this when\n    opening audio device.  This is also called from alsamm but I think that\n    is no longer in use, so in principle this could be static. */\n\nvoid sys_setchsr(int chin, int chout, int sr)\n{\n    int inbytes = (chin ? chin : 2) *\n                (DEFDACBLKSIZE*sizeof(t_sample));\n    int outbytes = (chout ? chout : 2) *\n                (DEFDACBLKSIZE*sizeof(t_sample));\n\n    if (STUFF->st_soundin)\n        freebytes(STUFF->st_soundin,\n            (STUFF->st_inchannels? STUFF->st_inchannels : 2) *\n                (DEFDACBLKSIZE*sizeof(t_sample)));\n    if (STUFF->st_soundout)\n        freebytes(STUFF->st_soundout,\n            (STUFF->st_outchannels? STUFF->st_outchannels : 2) *\n                (DEFDACBLKSIZE*sizeof(t_sample)));\n    STUFF->st_inchannels = chin;\n    STUFF->st_outchannels = chout;\n    if (!audio_isfixedsr(sys_audioapiopened))\n        STUFF->st_dacsr = sr;\n\n    STUFF->st_soundin = (t_sample *)getbytes(inbytes);\n    memset(STUFF->st_soundin, 0, inbytes);\n\n    STUFF->st_soundout = (t_sample *)getbytes(outbytes);\n    memset(STUFF->st_soundout, 0, outbytes);\n\n    logpost(NULL, PD_VERBOSE, \"input channels = %d, output channels = %d\",\n            STUFF->st_inchannels, STUFF->st_outchannels);\n    canvas_resume_dsp(canvas_suspend_dsp());\n}\n\nstatic void audio_make_sane(int *ndev, int *devvec,\n    int *nchan, int *chanvec, int maxdev)\n{\n    int i;\n    if (*ndev == -1)\n    {           /* no input audio devices specified */\n        if (*nchan == -1)\n        {\n            if (*ndev >= 1)\n            {\n                *nchan=1;\n                chanvec[0] = SYS_DEFAULTCH;\n                *ndev = 1;\n                devvec[0] = DEFAULTAUDIODEV;\n            }\n            else *ndev = *nchan = 0;\n        }\n        else\n        {\n            for (i = 0; i < maxdev; i++)\n                devvec[i] = i;\n            *ndev = *nchan;\n        }\n    }\n    else\n    {\n        if (*nchan == -1)\n        {\n            *nchan = *ndev;\n            for (i = 0; i < *ndev; i++)\n                chanvec[i] = SYS_DEFAULTCH;\n        }\n        else if (*nchan > *ndev)\n        {\n            for (i = *ndev; i < *nchan; i++)\n            {\n                if (i == 0)\n                    devvec[0] = DEFAULTAUDIODEV;\n                else devvec[i] = devvec[i-1] + 1;\n            }\n            *ndev = *nchan;\n        }\n        else if (*nchan < *ndev)\n        {\n            for (i = *nchan; i < *ndev; i++)\n            {\n                if (i == 0)\n                    chanvec[0] = SYS_DEFAULTCH;\n                else chanvec[i] = chanvec[i-1];\n            }\n            *ndev = *nchan;\n        }\n    }\n    for (i = *ndev; i < maxdev; i++)\n        devvec[i] = -1;\n    for (i = *nchan; i < maxdev; i++)\n        chanvec[i] = 0;\n\n}\n\n    /* compact the list of audio devices by skipping those whose channel\n    counts are zero, and add up all channel counts.  Assumes you've already\n    called make_sane above */\n\nstatic void audio_compact_and_count_channels(int *ndev, int *devvec,\n    int *chanvec, int *totalchans, int maxdev)\n{\n    int i, newndev;\n            /* count total number of input and output channels */\n    for (i = newndev = *totalchans = 0; i < *ndev; i++)\n        if (chanvec[i] > 0)\n    {\n        chanvec[newndev] = chanvec[i];\n        devvec[newndev] = devvec[i];\n        *totalchans += chanvec[i];\n        newndev++;\n    }\n    *ndev = newndev;\n}\n\n/* ----------------------- public routines ----------------------- */\n\nstatic int initted = 0;\n\nvoid sys_get_audio_settings(t_audiosettings *a)\n{\n    if (!initted)\n    {\n        audio_nextsettings.a_api = API_DEFAULT;\n        audio_nextsettings.a_srate = DEFAULTSRATE;\n        audio_nextsettings.a_nindev = audio_nextsettings.a_nchindev =\n            audio_nextsettings.a_noutdev = audio_nextsettings.a_nchoutdev\n                = 1;\n        audio_nextsettings.a_indevvec[0] =\n            audio_nextsettings.a_outdevvec[0] = DEFAULTAUDIODEV;\n        audio_nextsettings.a_chindevvec[0] =\n            audio_nextsettings.a_choutdevvec[0] = SYS_DEFAULTCH;\n        audio_nextsettings.a_advance = DEFAULTADVANCE;\n        audio_nextsettings.a_blocksize = DEFDACBLKSIZE;\n        initted = 1;\n    }\n    *a = audio_nextsettings;\n    if (audio_isfixedsr(a->a_api))\n        a->a_srate = STUFF->st_dacsr;\n    if (audio_isfixedblocksize(a->a_api))\n        a->a_blocksize = audio_getfixedblocksize(a->a_api);\n}\n\n    /* Since the channel vector might be longer than the\n    audio device vector, or vice versa, we fill the shorter one\n    in to match the longer one.  Also, if both are empty, we fill in\n    one device (the default) and two channels. This function can leave number\n    of channels at zero which is appropriate for the dialog window but before\n    starting audio we also call audio_compact_and_count_channels below.*/\n    /* set audio device settings (after cleaning up the specified device and\n    channel vectors).  The audio devices are \"zero based\" (i.e. \"0\" means the\n    first one.)  We can later re-open audio and/or show the settings on a\n    dialog window.   */\n\nvoid sys_set_audio_settings(t_audiosettings *a)\n{\n    int i;\n    char indevlist[MAXNDEV*DEVDESCSIZE], outdevlist[MAXNDEV*DEVDESCSIZE];\n    int indevs = 0, outdevs = 0, canmulti = 0, cancallback = 0;\n    sys_get_audio_devs(indevlist, &indevs, outdevlist, &outdevs, &canmulti,\n        &cancallback, MAXNDEV, DEVDESCSIZE, a->a_api);\n\n    if (a->a_srate < 1)\n        a->a_srate = DEFAULTSRATE;\n    if (a->a_advance < 0)\n        a->a_advance = DEFAULTADVANCE;\n    a->a_blocksize = 1 << ilog2(a->a_blocksize);\n    if (a->a_blocksize < DEFDACBLKSIZE || a->a_blocksize > MAXBLOCKSIZE)\n        a->a_blocksize = DEFDACBLKSIZE;\n\n    audio_make_sane(&a->a_noutdev, a->a_outdevvec,\n        &a->a_nchoutdev, a->a_choutdevvec, MAXAUDIOOUTDEV);\n    audio_make_sane(&a->a_nindev, a->a_indevvec,\n        &a->a_nchindev, a->a_chindevvec, MAXAUDIOINDEV);\n\n    sys_schedadvance = a->a_advance * 1000;\n    audio_nextsettings = *a;\n    initted = 1;\n\n    sys_log_error(ERR_NOTHING);\n    pdgui_vmess(\"set\", \"ri\", \"pd_whichapi\", audio_nextsettings.a_api);\n}\n\nvoid sys_close_audio(void)\n{\n    if (sys_externalschedlib)\n    {\n        return;\n    }\n    if (!audio_isopen())\n        return;\n#ifdef USEAPI_PORTAUDIO\n    if (sys_audioapiopened == API_PORTAUDIO)\n        pa_close_audio();\n    else\n#endif\n#ifdef USEAPI_JACK\n    if (sys_audioapiopened == API_JACK)\n        jack_close_audio();\n    else\n#endif\n#ifdef USEAPI_OSS\n    if (sys_audioapiopened == API_OSS)\n        oss_close_audio();\n    else\n#endif\n#ifdef USEAPI_ALSA\n    if (sys_audioapiopened == API_ALSA)\n        alsa_close_audio();\n    else\n#endif\n#ifdef USEAPI_MMIO\n    if (sys_audioapiopened == API_MMIO)\n        mmio_close_audio();\n    else\n#endif\n#ifdef USEAPI_AUDIOUNIT\n    if (sys_audioapiopened == API_AUDIOUNIT)\n        audiounit_close_audio();\n    else\n#endif\n#ifdef USEAPI_ESD\n    if (sys_audioapiopened == API_ESD)\n        esd_close_audio();\n    else\n#endif\n#ifdef USEAPI_DUMMY\n    if (sys_audioapiopened == API_DUMMY)\n        dummy_close_audio();\n    else\n#endif\n        post(\"sys_close_audio: unknown API %d\", sys_audioapiopened);\n    sys_audioapiopened = API_NONE;\n    sched_set_using_audio(SCHED_AUDIO_NONE);\n    audio_callback_is_open = 0;\n\n    pdgui_vmess(\"set\", \"ri\", \"pd_whichapi\", 0);\n}\n\nvoid sys_init_audio(void)\n{\n    t_audiosettings as;\n    int totalinchans, totaloutchans;\n    sys_get_audio_settings(&as);\n    audio_compact_and_count_channels(&as.a_nindev, as.a_indevvec,\n        as.a_chindevvec, &totalinchans, MAXAUDIOINDEV);\n    audio_compact_and_count_channels(&as.a_noutdev, as.a_outdevvec,\n        as.a_choutdevvec, &totaloutchans, MAXAUDIOOUTDEV);\n    sys_setchsr(totalinchans, totaloutchans, as.a_srate);\n}\n\n    /* open audio using currently requested parameters */\nvoid sys_reopen_audio(void)\n{\n    t_audiosettings as;\n    int outcome = 0, totalinchans, totaloutchans;\n    sys_get_audio_settings(&as);\n    /* fprintf(stderr, \"audio in ndev %d, dev %d; out ndev %d, dev %d\\n\",\n        as.a_nindev, as.a_indevvec[0], as.a_noutdev, as.a_outdevvec[0]); */\n    audio_compact_and_count_channels(&as.a_nindev, as.a_indevvec,\n        as.a_chindevvec, &totalinchans, MAXAUDIOINDEV);\n    audio_compact_and_count_channels(&as.a_noutdev, as.a_outdevvec,\n        as.a_choutdevvec, &totaloutchans, MAXAUDIOOUTDEV);\n    sys_setchsr(totalinchans, totaloutchans, as.a_srate);\n    if (!as.a_nindev && !as.a_noutdev)\n    {\n        sched_set_using_audio(SCHED_AUDIO_NONE);\n        return;\n    }\n#ifdef USEAPI_PORTAUDIO\n    if (as.a_api == API_PORTAUDIO)\n    {\n        int blksize = (as.a_blocksize ? as.a_blocksize : 64);\n        int nbufs = (double)sys_schedadvance / 1000000. * as.a_srate / blksize;\n            /* make sure that the delay is not smaller than the hardware blocksize */\n        if (nbufs < 1)\n        {\n            int delay = ((double)sys_schedadvance / 1000.) + 0.5;\n            int limit = ceil(blksize * 1000. / (double)as.a_srate);\n            nbufs = 1;\n            post(\"warning: 'delay' setting (%d ms) too small for current blocksize \"\n                 \"(%d samples); falling back to minimum value (%d ms)\",\n                 delay, blksize, limit);\n        }\n        outcome = pa_open_audio((as.a_nindev > 0 ? as.a_chindevvec[0] : 0),\n        (as.a_noutdev > 0 ? as.a_choutdevvec[0] : 0), as.a_srate,\n            STUFF->st_soundin, STUFF->st_soundout, blksize, nbufs,\n             (as.a_nindev > 0 ? as.a_indevvec[0] : 0),\n              (as.a_noutdev > 0 ? as.a_outdevvec[0] : 0),\n               (as.a_callback ? sched_audio_callbackfn : 0));\n    }\n    else\n#endif\n#ifdef USEAPI_JACK\n    if (as.a_api == API_JACK)\n        outcome = jack_open_audio((as.a_nindev > 0 ? as.a_chindevvec[0] : 0),\n            (as.a_noutdev > 0 ? as.a_choutdevvec[0] : 0),\n                (as.a_callback ? sched_audio_callbackfn : 0));\n\n    else\n#endif\n#ifdef USEAPI_OSS\n    if (as.a_api == API_OSS)\n        outcome = oss_open_audio(as.a_nindev, as.a_indevvec,\n        as.a_nindev, as.a_chindevvec, as.a_noutdev, as.a_outdevvec,\n        as.a_noutdev, as.a_choutdevvec, as.a_srate,\n                as.a_blocksize);\n    else\n#endif\n#ifdef USEAPI_ALSA\n    if (as.a_api == API_ALSA)\n        outcome = alsa_open_audio(as.a_nindev, as.a_indevvec,\n            as.a_nindev, as.a_chindevvec, as.a_noutdev,\n            as.a_outdevvec, as.a_noutdev, as.a_choutdevvec, as.a_srate,\n                as.a_blocksize);\n    else\n#endif\n#ifdef USEAPI_MMIO\n    if (as.a_api == API_MMIO)\n        outcome = mmio_open_audio(as.a_nindev, as.a_indevvec,\n            as.a_nindev, as.a_chindevvec, as.a_noutdev,\n                as.a_outdevvec, as.a_noutdev, as.a_choutdevvec, as.a_srate,\n                as.a_blocksize);\n    else\n#endif\n#ifdef USEAPI_AUDIOUNIT\n    if (as.a_api == API_AUDIOUNIT)\n        outcome = audiounit_open_audio(\n            (as.a_nindev > 0 ? as.a_chindev[0] : 0),\n            (as.a_nindev > 0 ? as.a_choutdev[0] : 0), as.a_srate);\n    else\n#endif\n#ifdef USEAPI_ESD\n    if (as.a_api == API_ALSA)\n        outcome = esd_open_audio(as.a_nindev, as.a_indevvec,\n            as.a_nindev, as.a_chindevvec, as.a_noutdev,\n                as.a_outdevvec, as.a_noutdev, as.a_choutdevvec, as.a_srate);\n    else\n#endif\n#ifdef USEAPI_DUMMY\n    if (as.a_api == API_DUMMY)\n        outcome = dummy_open_audio(as.a_nindev, as.a_noutdev,\n            as.a_srate);\n    else\n#endif\n    if (as.a_api == API_NONE)\n        ;\n    else post(\"unknown audio API specified\");\n    if (outcome)    /* failed */\n    {\n        sys_audioapiopened = API_NONE;\n        sched_set_using_audio(SCHED_AUDIO_NONE);\n        audio_callback_is_open = 0;\n    }\n    else\n    {\n        sys_audioapiopened = as.a_api;\n        sched_set_using_audio(\n            (as.a_callback ? SCHED_AUDIO_CALLBACK : SCHED_AUDIO_POLL));\n        audio_callback_is_open = as.a_callback;\n    }\n    pdgui_vmess(\"set\", \"ri\", \"pd_whichapi\", sys_audioapiopened);\n}\n\nint sys_send_dacs(void)\n{\n#ifdef USEAPI_PORTAUDIO\n    if (sys_audioapiopened == API_PORTAUDIO)\n        return (pa_send_dacs());\n    else\n#endif\n#ifdef USEAPI_JACK\n      if (sys_audioapiopened == API_JACK)\n        return (jack_send_dacs());\n    else\n#endif\n#ifdef USEAPI_OSS\n    if (sys_audioapiopened == API_OSS)\n        return (oss_send_dacs());\n    else\n#endif\n#ifdef USEAPI_ALSA\n    if (sys_audioapiopened == API_ALSA)\n        return (alsa_send_dacs());\n    else\n#endif\n#ifdef USEAPI_MMIO\n    if (sys_audioapiopened == API_MMIO)\n        return (mmio_send_dacs());\n    else\n#endif\n#ifdef USEAPI_AUDIOUNIT\n    if (sys_audioapiopened == API_AUDIOUNIT)\n        return (audiounit_send_dacs());\n    else\n#endif\n#ifdef USEAPI_ESD\n    if (sys_audioapiopened == API_ESD)\n        return (esd_send_dacs());\n    else\n#endif\n#ifdef USEAPI_DUMMY\n    if (sys_audioapiopened == API_DUMMY)\n        return (dummy_send_dacs());\n    else\n#endif\n    post(\"unknown API\");\n    return (0);\n}\n\nt_float sys_getsr(void)\n{\n     return (STUFF->st_dacsr);\n}\n\nint sys_get_outchannels(void)\n{\n     return (STUFF->st_outchannels);\n}\n\nint sys_get_inchannels(void)\n{\n     return (STUFF->st_inchannels);\n}\n\n/* this could later be set by a preference but for now it seems OK to just\nkeep jack audio open but close unused audio devices for any other API */\nint audio_shouldkeepopen(void)\n{\n    return (sys_audioapiopened == API_JACK);\n}\n\n    /* get names of available audio devices for the specified API */\nvoid sys_get_audio_devs(char *indevlist, int *nindevs,\n    char *outdevlist, int *noutdevs, int *canmulti, int *cancallback,\n        int maxndev, int devdescsize, int api)\n{\n    *cancallback = 0;   /* may be overridden by specific API implementation */\n#ifdef USEAPI_PORTAUDIO\n    if (api == API_PORTAUDIO)\n    {\n        pa_getdevs(indevlist, nindevs, outdevlist, noutdevs, canmulti,\n            maxndev, devdescsize);\n        /* portaudio officially allows callbacks but it hangs Pd on MacOS\n        and perhaps on other platforms too - this would potentially reduce\n        latency but doesn't appear to be worth the danger. */\n        *cancallback = 0;\n    }\n    else\n#endif\n#ifdef USEAPI_JACK\n    if (api == API_JACK)\n    {\n        jack_getdevs(indevlist, nindevs, outdevlist, noutdevs, canmulti,\n            maxndev, devdescsize);\n        *cancallback = 1;\n    }\n    else\n#endif\n#ifdef USEAPI_OSS\n    if (api == API_OSS)\n    {\n        oss_getdevs(indevlist, nindevs, outdevlist, noutdevs, canmulti,\n            maxndev, devdescsize);\n    }\n    else\n#endif\n#ifdef USEAPI_ALSA\n    if (api == API_ALSA)\n    {\n        alsa_getdevs(indevlist, nindevs, outdevlist, noutdevs, canmulti,\n            maxndev, devdescsize);\n    }\n    else\n#endif\n#ifdef USEAPI_MMIO\n    if (api == API_MMIO)\n    {\n        mmio_getdevs(indevlist, nindevs, outdevlist, noutdevs, canmulti,\n            maxndev, devdescsize);\n    }\n    else\n#endif\n#ifdef USEAPI_AUDIOUNIT\n    if (api == API_AUDIOUNIT)\n    {\n    }\n    else\n#endif\n#ifdef USEAPI_ESD\n    if (api == API_ESD)\n    {\n        esd_getdevs(indevlist, nindevs, outdevlist, noutdevs, canmulti,\n            maxndev, devdescsize);\n    }\n    else\n#endif\n#ifdef USEAPI_DUMMY\n    if (api == API_DUMMY)\n    {\n        dummy_getdevs(indevlist, nindevs, outdevlist, noutdevs, canmulti,\n            maxndev, devdescsize);\n    }\n    else\n#endif\n    {\n            /* this shouldn't happen once all the above get filled in. */\n        int i;\n        *nindevs = *noutdevs = 3;\n        for (i = 0; i < 3; i++)\n        {\n            sprintf(indevlist + i * devdescsize, \"input device #%d\", i+1);\n            sprintf(outdevlist + i * devdescsize, \"output device #%d\", i+1);\n        }\n        *canmulti = 0;\n    }\n}\n\n\nvoid sys_gui_audiopreferences(void) {\n    t_audiosettings as;\n        /* these are all the devices on your system: */\n    char indevlist[MAXNDEV*DEVDESCSIZE], outdevlist[MAXNDEV*DEVDESCSIZE];\n    char srate[80], callback[80], blocksize[80];\n    const char *devicesI[MAXNDEV], *devicesO[MAXNDEV];\n    t_float usedevsI[MAXAUDIOINDEV], devchansI[MAXAUDIOINDEV];\n    t_float usedevsO[MAXAUDIOOUTDEV], devchansO[MAXAUDIOOUTDEV];\n    int num_usedevsI, num_devchansI, num_usedevsO, num_devchansO;\n    int num_devicesI = 0, num_devicesO = 0, canmulti = 0, cancallback = 0;\n    int i;\n\n        /* query the current AUDIO settings */\n    sys_get_audio_settings(&as);\n    sys_get_audio_devs(indevlist, &num_devicesI, outdevlist, &num_devicesO, &canmulti,\n        &cancallback, MAXNDEV, DEVDESCSIZE, as.a_api);\n\n        /* normalize the data a bit */\n    if(!num_devicesI) {\n        num_devicesI = 1;\n        devicesI[0] = \"\";\n    } else {\n        for(i=0; i<num_devicesI; i++)\n            devicesI[i] = indevlist + i*DEVDESCSIZE;\n    }\n    num_usedevsI = sizeof(as.a_indevvec)/sizeof(*as.a_indevvec);\n    for(i=0; i<num_usedevsI; i++) {\n        usedevsI[i] = (t_float)as.a_indevvec[i];\n    }\n    num_devchansI = sizeof(as.a_indevvec)/sizeof(*as.a_indevvec);\n    for(i=0; i<num_devchansI; i++) {\n        devchansI[i] = (t_float)as.a_chindevvec[i];\n    }\n\n    if(!num_devicesO) {\n        num_devicesO = 1;\n        devicesO[0] = \"\";\n    } else {\n        for(i=0; i<num_devicesO; i++)\n            devicesO[i] = outdevlist + i*DEVDESCSIZE;\n    }\n    num_usedevsO = sizeof(as.a_outdevvec)/sizeof(*as.a_outdevvec);\n    for(i=0; i<num_usedevsO; i++) {\n        usedevsO[i] = (t_float)as.a_outdevvec[i];\n    }\n    num_devchansO = sizeof(as.a_outdevvec)/sizeof(*as.a_outdevvec);\n    for(i=0; i<num_devchansO; i++) {\n        devchansO[i] = (t_float)as.a_choutdevvec[i];\n    }\n\n    sprintf(srate, \"%s%d\", audio_isfixedsr(as.a_api)?\"!\":\"\", as.a_srate);\n    sprintf(callback, \"%s%d\", cancallback?\"\":\"!\", as.a_callback);\n    sprintf(blocksize, \"%s%d\", audio_isfixedblocksize(as.a_api)?\"!\":\"\", as.a_blocksize);\n\n        /* and send it over to the GUI */\n    pdgui_vmess(\"::dialog_audio::set_configuration\", \"SFF SFF ssi si\",\n        num_devicesI, devicesI, num_usedevsI, usedevsI, num_devchansI, devchansI,\n        num_devicesO, devicesO, num_usedevsO, usedevsO, num_devchansO, devchansO,\n        srate, blocksize, as.a_advance,\n        callback, canmulti);\n}\n\n    /* start an audio settings dialog window */\nvoid glob_audio_properties(t_pd *dummy, t_floatarg flongform)\n{\n    sys_gui_audiopreferences();\n    pdgui_stub_deleteforkey(0);\n    pdgui_stub_vnew(&glob_pdobject, \"::dialog_audio::create\",\n        (void *)glob_audio_properties, \"\");\n}\n\n    /* new values from dialog window */\nvoid glob_audio_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv)\n{\n    int i;\n    t_audiosettings as;\n    as.a_api = audio_nextsettings.a_api;\n    as.a_srate = atom_getfloatarg(16, argc, argv);\n    as.a_advance = atom_getfloatarg(17, argc, argv);\n    as.a_callback = atom_getfloatarg(18, argc, argv);\n    as.a_blocksize = atom_getfloatarg(19, argc, argv);\n\n    for (i = 0; i < 4; i++)\n    {\n        as.a_indevvec[i] = atom_getfloatarg(i, argc, argv);\n        as.a_chindevvec[i] = (as.a_indevvec[i] >= 0) ? atom_getfloatarg(i+4, argc, argv) : 0;\n        as.a_outdevvec[i] = atom_getfloatarg(i+8, argc, argv);\n        as.a_choutdevvec[i] = (as.a_outdevvec[i] >= 0) ? atom_getfloatarg(i+12, argc, argv) : 0;\n    }\n        /* compact out any zeros and count nonzero entries */\n    for (i = 0, as.a_nindev = 0; i < MAXAUDIOINDEV; i++)\n    {\n        if (as.a_chindevvec[i])\n        {\n            as.a_indevvec[as.a_nindev] = as.a_indevvec[i];\n            as.a_chindevvec[as.a_nindev] = as.a_chindevvec[i];\n            as.a_nindev++;\n        }\n    }\n    for (i = 0, as.a_noutdev = 0; i < MAXAUDIOOUTDEV; i++)\n    {\n        if (as.a_choutdevvec[i])\n        {\n            as.a_outdevvec[as.a_noutdev] = as.a_outdevvec[i];\n            as.a_choutdevvec[as.a_noutdev] = as.a_choutdevvec[i];\n            as.a_noutdev++;\n        }\n    }\n    as.a_nchindev = as.a_nindev;\n    as.a_nchoutdev = as.a_noutdev;\n    if (as.a_callback < 0)\n        as.a_callback = 0;\n    as.a_blocksize = (1<<ilog2(as.a_blocksize));\n    if (as.a_blocksize < DEFDACBLKSIZE || as.a_blocksize > MAXBLOCKSIZE)\n            as.a_blocksize = DEFDACBLKSIZE;\n\n    if (!audio_callback_is_open && !as.a_callback)\n        sys_close_audio();\n    sys_set_audio_settings(&as);\n    if (!audio_callback_is_open && !as.a_callback)\n        sys_reopen_audio();\n    else sched_reopenmeplease();\n}\n\nvoid sys_listdevs(void)\n{\n    char indevlist[MAXNDEV*DEVDESCSIZE], outdevlist[MAXNDEV*DEVDESCSIZE];\n    int nindevs = 0, noutdevs = 0, i, canmulti = 0, cancallback = 0;\n    int offset = 0;\n\n    sys_get_audio_devs(indevlist, &nindevs, outdevlist, &noutdevs,\n        &canmulti, &cancallback, MAXNDEV, DEVDESCSIZE,\n            audio_nextsettings.a_api);\n\n#if 0\n        /* To agree with command line flags, normally start at 1 */\n        /* But microsoft \"MMIO\" device list starts at 0 (the \"mapper\"). */\n\n       /* JMZ: otoh, it seems that the '-audiodev' flags 0-based\n        * indices on ALSA and PORTAUDIO as well,\n        * so we better show the correct ones here\n        * (hence this line is disabled via #ifdef's)\n        */\n    offset = (audio_nextsettings.a_api != API_MMIO);\n#endif\n\n    if (!nindevs)\n        post(\"no audio input devices found\");\n    else\n    {\n        post(\"audio input devices:\");\n        for (i = 0; i < nindevs; i++)\n            post(\"%d. %s\", i + offset,\n                indevlist + i * DEVDESCSIZE);\n    }\n    if (!noutdevs)\n        post(\"no audio output devices found\");\n    else\n    {\n        post(\"audio output devices:\");\n        for (i = 0; i < noutdevs; i++)\n            post(\"%d. %s\", i + offset,\n                outdevlist + i * DEVDESCSIZE);\n    }\n    post(\"API number %d\\n\", audio_nextsettings.a_api);\n    sys_listmididevs();\n}\n\nvoid glob_audio_setapi(void *dummy, t_floatarg f)\n{\n    int newapi = f;\n    if (newapi)\n    {\n        if (newapi == audio_nextsettings.a_api)\n        {\n            if (!audio_isopen() && audio_shouldkeepopen())\n                sys_reopen_audio();\n        }\n        else\n        {\n            sys_close_audio();\n            audio_nextsettings.a_api = newapi;\n                /* bash device params back to default */\n            audio_nextsettings.a_nindev = audio_nextsettings.a_nchindev =\n                audio_nextsettings.a_noutdev = audio_nextsettings.a_nchoutdev\n                    = 1;\n            audio_nextsettings.a_indevvec[0] =\n                audio_nextsettings.a_outdevvec[0] = DEFAULTAUDIODEV;\n            audio_nextsettings.a_chindevvec[0] =\n                audio_nextsettings.a_choutdevvec[0] = SYS_DEFAULTCH;\n            audio_nextsettings.a_blocksize = DEFDACBLKSIZE;\n            sys_reopen_audio();\n        }\n        glob_audio_properties(0, 0);\n    }\n    else if (audio_isopen())\n    {\n        sys_close_audio();\n    }\n}\n\n    /* start or stop the audio hardware */\nvoid sys_set_audio_state(int onoff)\n{\n    if (onoff)  /* start */\n    {\n        if (!audio_isopen())\n            sys_reopen_audio();\n    }\n    else\n    {\n        if (audio_isopen())\n            sys_close_audio();\n    }\n}\n\n#define MAXAPIENTRY 10\ntypedef struct _apientry\n{\n    char a_name[30];\n    int a_id;\n} t_apientry;\n\nstatic t_apientry audio_apilist[] = {\n#ifdef USEAPI_OSS\n    {\"OSS\", API_OSS},\n#endif\n#ifdef USEAPI_ALSA\n    {\"ALSA\", API_ALSA},\n#endif\n#ifdef USEAPI_PORTAUDIO\n#ifdef _WIN32\n    {\"\\\"standard (portaudio)\\\"\", API_PORTAUDIO},\n#else\n#ifdef __APPLE__\n    {\"\\\"standard (portaudio)\\\"\", API_PORTAUDIO},\n#else\n    {\"portaudio\", API_PORTAUDIO},\n#endif\n#endif\n#endif  /* USEAPI_PORTAUDIO */\n#ifdef USEAPI_MMIO\n    {\"\\\"old MMIO system\\\"\", API_MMIO},\n#endif\n#ifdef USEAPI_JACK\n    {\"jack\", API_JACK},\n#endif\n#ifdef USEAPI_AUDIOUNIT\n    {\"Audiounit\", API_AUDIOUNIT},\n#endif\n#ifdef USEAPI_ESD\n    {\"ESD\",  API_ESD},\n#endif\n#ifdef USEAPI_DUMMY\n    {\"dummy\", API_DUMMY},\n#endif\n};\n\nvoid sys_get_audio_apis(char *buf)\n{\n        /* FIXXME: this returns a raw Tcl-list!\n         *  instead it should return something we can use with pdgui_vmess()\n         */\n    unsigned int n;\n    if (sizeof(audio_apilist)/sizeof(t_apientry) < 2)\n        strcpy(buf, \"{}\");\n    else\n    {\n        strcpy(buf, \"{ \");\n        for (n = 0; n < sizeof(audio_apilist)/sizeof(t_apientry); n++)\n            sprintf(buf + strlen(buf), \"{%s %d} \",\n                audio_apilist[n].a_name, audio_apilist[n].a_id);\n        strcat(buf, \"}\");\n    }\n}\n\n/* convert a device name to a (1-based) device number.  (Output device if\n'output' parameter is true, otherwise input device).  Negative on failure. */\n\nint sys_audiodevnametonumber(int output, const char *name)\n{\n    char indevlist[MAXNDEV*DEVDESCSIZE], outdevlist[MAXNDEV*DEVDESCSIZE];\n    int nindevs = 0, noutdevs = 0, i, canmulti, cancallback;\n\n    sys_get_audio_devs(indevlist, &nindevs, outdevlist, &noutdevs,\n        &canmulti, &cancallback, MAXNDEV, DEVDESCSIZE,\n            audio_nextsettings.a_api);\n\n    if (output)\n    {\n            /* try first for exact match */\n        for (i = 0; i < noutdevs; i++)\n            if (!strcmp(name, outdevlist + i * DEVDESCSIZE))\n                return (i);\n            /* failing that, a match up to end of shorter string */\n        for (i = 0; i < noutdevs; i++)\n        {\n            unsigned long comp = strlen(name);\n            if (comp > strlen(outdevlist + i * DEVDESCSIZE))\n                comp = strlen(outdevlist + i * DEVDESCSIZE);\n            if (!strncmp(name, outdevlist + i * DEVDESCSIZE, comp))\n                return (i);\n        }\n    }\n    else\n    {\n        for (i = 0; i < nindevs; i++)\n            if (!strcmp(name, indevlist + i * DEVDESCSIZE))\n                return (i);\n        for (i = 0; i < nindevs; i++)\n        {\n            unsigned long comp = strlen(name);\n            if (comp > strlen(indevlist + i * DEVDESCSIZE))\n                comp = strlen(indevlist + i * DEVDESCSIZE);\n            if (!strncmp(name, indevlist + i * DEVDESCSIZE, comp))\n                return (i);\n        }\n    }\n    return (-1);\n}\n\n/* convert a (1-based) device number to a device name.  (Output device if\n'output' parameter is true, otherwise input device).  Empty string on failure.\n*/\n\nvoid sys_audiodevnumbertoname(int output, int devno, char *name, int namesize)\n{\n    char indevlist[MAXNDEV*DEVDESCSIZE], outdevlist[MAXNDEV*DEVDESCSIZE];\n    int nindevs = 0, noutdevs = 0, canmulti, cancallback;\n    if (devno < 0)\n    {\n        *name = 0;\n        return;\n    }\n    sys_get_audio_devs(indevlist, &nindevs, outdevlist, &noutdevs,\n        &canmulti, &cancallback, MAXNDEV, DEVDESCSIZE,\n            audio_nextsettings.a_api);\n    if (output && (devno < noutdevs))\n        strncpy(name, outdevlist + devno * DEVDESCSIZE, namesize);\n    else if (!output && (devno < nindevs))\n        strncpy(name, indevlist + devno * DEVDESCSIZE, namesize);\n    else *name = 0;\n    name[namesize-1] = 0;\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/s_audio_dummy.c",
    "content": "/*\n * Copyright (c) 2010 Peter Brinkmann (peter.brinkmann@gmail.com)\n *\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.\n */\n\n#ifdef USEAPI_DUMMY\n\n#include <stdio.h>\n\nint dummy_open_audio(int nin, int nout, int sr) {\n  return 0;\n}\n\nint dummy_close_audio(void) {\n  return 0;\n}\n\nint dummy_send_dacs(void) {\n  return 0;\n}\n\nvoid dummy_getdevs(char *indevlist, int *nindevs, char *outdevlist,\n    int *noutdevs, int *canmulti, int maxndev, int devdescsize) {\n  sprintf(indevlist, \"NONE\");\n  sprintf(outdevlist, \"NONE\");\n  *nindevs = *noutdevs = 1;\n  *canmulti = 0;\n}\n\nvoid dummy_listdevs(void) {\n  // do nothing\n}\n\n#endif\n\n"
  },
  {
    "path": "libs/libpd/pure-data/src/s_inter.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* Pd side of the Pd/Pd-gui interface.  Also, some system interface routines\nthat didn't really belong anywhere. */\n\n#include \"m_pd.h\"\n#include \"s_stuff.h\"\n#include \"m_imp.h\"\n#include \"g_canvas.h\"   /* for GUI queueing stuff */\n#include \"s_net.h\"\n#include <errno.h>\n#ifndef _WIN32\n#include <unistd.h>\n#include <stdlib.h>\n#include <sys/time.h>\n#include <sys/mman.h>\n#include <sys/resource.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#endif\n#ifdef HAVE_BSTRING_H\n#include <bstring.h>\n#endif\n#ifdef _WIN32\n#include <io.h>\n#include <process.h>\n#include <windows.h>\n#endif\n\n#include <stdarg.h>\n#include <signal.h>\n#include <fcntl.h>\n#include <string.h>\n#include <stdio.h>\n#include <ctype.h>\n\n#ifdef __APPLE__\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <glob.h>\n#else\n#include <stdlib.h>\n#endif\n\n#ifdef HAVE_SYS_UTSNAME_H\n# include <sys/utsname.h>\n# ifndef USE_UNAME\n#  define USE_UNAME 1\n# endif\n#endif\n\n#include \"m_private_utils.h\"\n\n/* colorize output, but only on a TTY */\n#ifdef HAVE_UNISTD_H\n# include <unistd.h>\n#else /* if isatty exists outside unistd, please add another #ifdef */\n# define isatty(fd) 0\n#endif\nstatic int stderr_isatty;\n\n\n#define stringify(s) str(s)\n#define str(s) #s\n\n#define INTER (pd_this->pd_inter)\n\n#define DEBUG_MESSUP   1<<0    /* messages up from pd to pd-gui */\n#define DEBUG_MESSDOWN 1<<1    /* messages down from pd-gui to pd */\n#define DEBUG_COLORIZE 1<<2    /* colorize messages (if we are on a TTY) */\n\n\n#ifndef PDBINDIR\n#define PDBINDIR \"bin/\"\n#endif\n\n#ifndef PDGUIDIR\n#define PDGUIDIR \"tcl\"\n#endif\n\n#ifndef WISH\n# if defined _WIN32\n#  define WISH \"wish85.exe\"\n# elif defined __APPLE__\n   // leave undefined to use dummy search path, otherwise\n   // this should be a full path to wish on mac\n#else\n#  define WISH \"wish\"\n# endif\n#endif\n\n#define LOCALHOST \"localhost\"\n\n#if PDTHREADS\n#include \"pthread.h\"\n#endif\n\ntypedef struct _fdpoll\n{\n    int fdp_fd;\n    t_fdpollfn fdp_fn;\n    void *fdp_ptr;\n} t_fdpoll;\n\nstruct _socketreceiver\n{\n    char *sr_inbuf;\n    int sr_inhead;\n    int sr_intail;\n    void *sr_owner;\n    int sr_udp;\n    struct sockaddr_storage *sr_fromaddr; /* optional */\n    t_socketnotifier sr_notifier;\n    t_socketreceivefn sr_socketreceivefn;\n    t_socketfromaddrfn sr_fromaddrfn; /* optional */\n};\n\ntypedef struct _guiqueue\n{\n    void *gq_client;\n    t_glist *gq_glist;\n    t_guicallbackfn gq_fn;\n    struct _guiqueue *gq_next;\n} t_guiqueue;\n\nstruct _instanceinter\n{\n    int i_havegui;\n    int i_nfdpoll;\n    t_fdpoll *i_fdpoll;\n    int i_maxfd;\n    int i_guisock;\n    t_socketreceiver *i_socketreceiver;\n    t_guiqueue *i_guiqueuehead;\n    t_binbuf *i_inbinbuf;\n    char *i_guibuf;\n    int i_guihead;\n    int i_guitail;\n    int i_guisize;\n    int i_waitingforping;\n    int i_bytessincelastping;\n    int i_fdschanged;   /* flag to break fdpoll loop if fd list changes */\n\n#ifdef _WIN32\n    LARGE_INTEGER i_inittime;\n    double i_freq;\n#endif\n#if PDTHREADS\n    pthread_mutex_t i_mutex;\n#endif\n\n    unsigned char i_recvbuf[NET_MAXPACKETSIZE];\n};\n\nextern int sys_guisetportnumber;\nextern int sys_addhist(int phase);\nvoid sys_stopgui(void);\n\n/* ----------- functions for timing, signals, priorities, etc  --------- */\n\n#ifdef _WIN32\n\nstatic void sys_initntclock(void)\n{\n    LARGE_INTEGER f1;\n    LARGE_INTEGER now;\n    QueryPerformanceCounter(&now);\n    if (!QueryPerformanceFrequency(&f1))\n    {\n          fprintf(stderr, \"pd: QueryPerformanceFrequency failed\\n\");\n          f1.QuadPart = 1;\n    }\n    INTER->i_freq = f1.QuadPart;\n    INTER->i_inittime = now;\n}\n\n#if 0\n    /* this is a version you can call if you did the QueryPerformanceCounter\n    call yourself.  Necessary for time tagging incoming MIDI at interrupt\n    level, for instance; but we're not doing that just now. */\n\ndouble nt_tixtotime(LARGE_INTEGER *dumbass)\n{\n    if (INTER->i_freq == 0) sys_initntclock();\n    return (((double)(dumbass->QuadPart -\n        INTER->i_inittime.QuadPart)) / INTER->i_freq);\n}\n#endif\n#endif /* _WIN32 */\n\n    /* get \"real time\" in seconds; take the\n    first time we get called as a reference time of zero. */\ndouble sys_getrealtime(void)\n{\n#ifndef _WIN32\n    static struct timeval then;\n    struct timeval now;\n    gettimeofday(&now, 0);\n    if (then.tv_sec == 0 && then.tv_usec == 0) then = now;\n    return ((now.tv_sec - then.tv_sec) +\n        (1./1000000.) * (now.tv_usec - then.tv_usec));\n#else\n    LARGE_INTEGER now;\n    QueryPerformanceCounter(&now);\n    if (INTER->i_freq == 0) sys_initntclock();\n    return (((double)(now.QuadPart -\n        INTER->i_inittime.QuadPart)) / INTER->i_freq);\n#endif\n}\n\n/* sleep (but cancel the sleeping if any file descriptors are\nready - in that case, dispatch any resulting Pd messages and return.  Called\nwith sys_lock() set.  We will temporarily release the lock if we actually\nsleep. */\nstatic int sys_domicrosleep(int microsec)\n{\n    struct timeval timeout;\n    int i, didsomething = 0;\n    t_fdpoll *fp;\n    timeout.tv_sec = 0;\n    timeout.tv_usec = 0;\n    if (INTER->i_nfdpoll)\n    {\n        fd_set readset, writeset, exceptset;\n        FD_ZERO(&writeset);\n        FD_ZERO(&readset);\n        FD_ZERO(&exceptset);\n        for (fp = INTER->i_fdpoll,\n            i = INTER->i_nfdpoll; i--; fp++)\n                FD_SET(fp->fdp_fd, &readset);\n        if(select(INTER->i_maxfd+1,\n                  &readset, &writeset, &exceptset, &timeout) < 0)\n          perror(\"microsleep select\");\n        INTER->i_fdschanged = 0;\n        for (i = 0; i < INTER->i_nfdpoll &&\n            !INTER->i_fdschanged; i++)\n                if (FD_ISSET(INTER->i_fdpoll[i].fdp_fd, &readset))\n        {\n            (*INTER->i_fdpoll[i].fdp_fn)\n                (INTER->i_fdpoll[i].fdp_ptr,\n                    INTER->i_fdpoll[i].fdp_fd);\n            didsomething = 1;\n        }\n        if (didsomething)\n            return (1);\n    }\n    if (microsec)\n    {\n        sys_unlock();\n#ifdef _WIN32\n        Sleep(microsec/1000);\n#else\n        usleep(microsec);\n#endif\n        sys_lock();\n    }\n    return (0);\n}\n\n    /* sleep (but if any incoming or to-gui sending to do, do that instead.)\n    Call with the PD instance lock UNSET - we set it here. */\nvoid sys_microsleep( void)\n{\n    sys_lock();\n    sys_domicrosleep(sched_get_sleepgrain());\n    sys_unlock();\n}\n\n#if !defined(_WIN32) && !defined(__CYGWIN__)\nstatic void sys_signal(int signo, sig_t sigfun)\n{\n    struct sigaction action;\n    action.sa_flags = 0;\n    action.sa_handler = sigfun;\n    memset(&action.sa_mask, 0, sizeof(action.sa_mask));\n#if 0  /* GG says: don't use that */\n    action.sa_restorer = 0;\n#endif\n    if (sigaction(signo, &action, 0) < 0)\n        perror(\"sigaction\");\n}\n\nstatic void sys_exithandler(int n)\n{\n    static int trouble = 0;\n    if (!trouble)\n    {\n        trouble = 1;\n        fprintf(stderr, \"Pd: signal %d\\n\", n);\n        sys_bail(1);\n    }\n    else _exit(1);\n}\n\nstatic void sys_alarmhandler(int n)\n{\n    fprintf(stderr, \"Pd: system call timed out\\n\");\n}\n\nstatic void sys_huphandler(int n)\n{\n    struct timeval timeout;\n    timeout.tv_sec = 0;\n    timeout.tv_usec = 30000;\n    select(1, 0, 0, 0, &timeout);\n}\n\nvoid sys_setalarm(int microsec)\n{\n    struct itimerval gonzo;\n    int sec = (int)(microsec/1000000);\n    microsec %= 1000000;\n#if 0\n    fprintf(stderr, \"timer %d:%d\\n\", sec, microsec);\n#endif\n    gonzo.it_interval.tv_sec = 0;\n    gonzo.it_interval.tv_usec = 0;\n    gonzo.it_value.tv_sec = sec;\n    gonzo.it_value.tv_usec = microsec;\n    if (microsec)\n        sys_signal(SIGALRM, sys_alarmhandler);\n    else sys_signal(SIGALRM, SIG_IGN);\n    setitimer(ITIMER_REAL, &gonzo, 0);\n}\n\n#endif /* NOT _WIN32 && NOT __CYGWIN__ */\n\n    /* on startup, set various signal handlers */\nvoid sys_setsignalhandlers(void)\n{\n#if !defined(_WIN32) && !defined(__CYGWIN__)\n    signal(SIGHUP, sys_huphandler);\n    signal(SIGINT, sys_exithandler);\n    signal(SIGQUIT, sys_exithandler);\n# ifdef SIGIOT\n    signal(SIGIOT, sys_exithandler);\n# endif\n    signal(SIGFPE, SIG_IGN);\n    /* signal(SIGILL, sys_exithandler);\n    signal(SIGBUS, sys_exithandler);\n    signal(SIGSEGV, sys_exithandler); */\n    signal(SIGPIPE, SIG_IGN);\n    signal(SIGALRM, SIG_IGN);\n#if 0  /* GG says: don't use that */\n    signal(SIGSTKFLT, sys_exithandler);\n#endif\n#endif /* NOT _WIN32 && NOT __CYGWIN__ */\n}\n\n#define MODE_NRT 0\n#define MODE_RT 1\n#define MODE_WATCHDOG 2\n#if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__GNU__)\n\n#if defined(_POSIX_PRIORITY_SCHEDULING) || defined(_POSIX_MEMLOCK)\n#include <sched.h>\n#endif\n\nvoid sys_set_priority(int mode)\n{\n#ifdef _POSIX_PRIORITY_SCHEDULING\n    struct sched_param par;\n    int p1, p2, p3;\n    p1 = sched_get_priority_min(SCHED_FIFO);\n    p2 = sched_get_priority_max(SCHED_FIFO);\n#ifdef USEAPI_JACK\n    p3 = (mode == MODE_WATCHDOG ? p1 + 7 : (mode == MODE_RT ? p1 + 5 : 0));\n#else\n    p3 = (mode == MODE_WATCHDOG ? p2 - 5 : (mode == MODE_RT ? p2 - 7 : 0));\n#endif\n    par.sched_priority = p3;\n    if (sched_setscheduler(0,\n        (mode == MODE_NRT ? SCHED_OTHER : SCHED_FIFO), &par) < 0)\n    {\n        if (mode == MODE_WATCHDOG)\n            fprintf(stderr, \"priority %d scheduling failed.\\n\", p3);\n        else post(\"priority %d scheduling failed; running at normal priority\",\n                p3);\n    }\n    else\n    {\n        if (mode == MODE_RT)\n            logpost(NULL, PD_VERBOSE, \"priority %d scheduling enabled.\\n\", p3);\n        else logpost(NULL, PD_VERBOSE, \"running at normal (non-real-time) priority.\\n\");\n    }\n#endif /* _POSIX_PRIORITY_SCHEDULING */\n\n#if !defined(USEAPI_JACK)\n    if (mode != MODE_NRT)\n    {\n            /* tb: force memlock to physical memory { */\n        struct rlimit mlock_limit;\n        mlock_limit.rlim_cur=0;\n        mlock_limit.rlim_max=0;\n        setrlimit(RLIMIT_MEMLOCK,&mlock_limit);\n            /* } tb */\n        if (mlockall(MCL_FUTURE) != -1 && sys_verbose)\n            fprintf(stderr, \"memory locking enabled.\\n\");\n    }\n    else munlockall();\n#endif /* ! USEAPI_JACK */\n}\n\n#else /* !__linux__ */\nvoid sys_set_priority(int mode)\n{\n        /* dummy */\n    (void)mode;\n}\n\n#endif /* !__linux__ */\n\n/* ------------------ receiving incoming messages over sockets ------------- */\n\nunsigned char *sys_getrecvbuf(unsigned int *size)\n{\n    if (size)\n        *size = NET_MAXPACKETSIZE;\n    return INTER->i_recvbuf;\n}\n\nvoid sys_sockerror(const char *s)\n{\n    char buf[MAXPDSTRING];\n    int err = socket_errno();\n    socket_strerror(err, buf, sizeof(buf));\n    pd_error(0, \"%s: %s (%d)\", s, buf, err);\n}\n\nvoid sys_addpollfn(int fd, t_fdpollfn fn, void *ptr)\n{\n    int nfd, size;\n    t_fdpoll *fp;\n    sys_init_fdpoll();\n    nfd = INTER->i_nfdpoll;\n    size = nfd * sizeof(t_fdpoll);\n    INTER->i_fdpoll = (t_fdpoll *)t_resizebytes(\n        INTER->i_fdpoll, size, size + sizeof(t_fdpoll));\n    fp = INTER->i_fdpoll + nfd;\n    fp->fdp_fd = fd;\n    fp->fdp_fn = fn;\n    fp->fdp_ptr = ptr;\n    INTER->i_nfdpoll = nfd + 1;\n    if (fd >= INTER->i_maxfd)\n        INTER->i_maxfd = fd + 1;\n    INTER->i_fdschanged = 1;\n}\n\nvoid sys_rmpollfn(int fd)\n{\n    int nfd = INTER->i_nfdpoll;\n    int i, size = nfd * sizeof(t_fdpoll);\n    t_fdpoll *fp;\n    INTER->i_fdschanged = 1;\n    for (i = nfd, fp = INTER->i_fdpoll; i--; fp++)\n    {\n        if (fp->fdp_fd == fd)\n        {\n            while (i--)\n            {\n                fp[0] = fp[1];\n                fp++;\n            }\n            INTER->i_fdpoll = (t_fdpoll *)t_resizebytes(\n                INTER->i_fdpoll, size, size - sizeof(t_fdpoll));\n            INTER->i_nfdpoll = nfd - 1;\n            return;\n        }\n    }\n    post(\"warning: %d removed from poll list but not found\", fd);\n}\n\n    /* Size of the buffer used for parsing FUDI messages\n    received over TCP. Must be a power of two!\n    LATER make this settable per socketreceiver instance */\n#define INBUFSIZE 4096\n\nt_socketreceiver *socketreceiver_new(void *owner, t_socketnotifier notifier,\n    t_socketreceivefn socketreceivefn, int udp)\n{\n    t_socketreceiver *x = (t_socketreceiver *)getbytes(sizeof(*x));\n    x->sr_inhead = x->sr_intail = 0;\n    x->sr_owner = owner;\n    x->sr_notifier = notifier;\n    x->sr_socketreceivefn = socketreceivefn;\n    x->sr_udp = udp;\n    x->sr_fromaddr = NULL;\n    x->sr_fromaddrfn = NULL;\n    if (!udp)\n    {\n        if (!(x->sr_inbuf = malloc(INBUFSIZE)))\n            bug(\"t_socketreceiver\");\n    }\n    else\n        x->sr_inbuf = NULL;\n    return (x);\n}\n\nvoid socketreceiver_free(t_socketreceiver *x)\n{\n    if (x->sr_inbuf)\n        free(x->sr_inbuf);\n    if (x->sr_fromaddr) free(x->sr_fromaddr);\n    freebytes(x, sizeof(*x));\n}\n\n    /* this is in a separately called subroutine so that the buffer isn't\n    sitting on the stack while the messages are getting passed. */\nstatic int socketreceiver_doread(t_socketreceiver *x)\n{\n    char messbuf[INBUFSIZE], *bp = messbuf;\n    int indx, first = 1;\n    int inhead = x->sr_inhead;\n    int intail = x->sr_intail;\n    char *inbuf = x->sr_inbuf;\n    for (indx = intail; first || (indx != inhead);\n        first = 0, (indx = (indx+1)&(INBUFSIZE-1)))\n    {\n            /* if we hit a semi that isn't preceded by a \\, it's a message\n            boundary. LATER we should deal with the possibility that the\n            preceding \\ might itself be escaped! */\n        char c = *bp++ = inbuf[indx];\n        if (c == ';' && (!indx || inbuf[indx-1] != '\\\\'))\n        {\n            intail = (indx+1)&(INBUFSIZE-1);\n            binbuf_text(INTER->i_inbinbuf, messbuf, bp - messbuf);\n            if (sys_debuglevel & DEBUG_MESSDOWN)\n            {\n                size_t bufsize = (bp>messbuf)?(bp-messbuf):0;\n                int colorize = stderr_isatty && (sys_debuglevel & DEBUG_COLORIZE);\n                const char*msg = messbuf;\n                if (('\\r' == messbuf[0]) && ('\\n' == messbuf[1]))\n                {\n                    bufsize-=2;\n                    msg+=2;\n                }\n        #ifdef _WIN32\n            #ifdef _MSC_VER\n                fwprintf(stderr, L\"<< %.*S\\n\", (int)bufsize, msg);\n            #else\n                fwprintf(stderr, L\"<< %.*s\\n\", (int)bufsize, msg);\n            #endif\n                fflush(stderr);\n        #else\n                if(colorize)\n                    fprintf(stderr, \"\\e[0;1;36m<< %.*s\\e[0m\\n\", (int)bufsize, msg);\n                else\n                    fprintf(stderr, \"<< %.*s\\n\", (int)bufsize, msg);\n        #endif\n            }\n            x->sr_inhead = inhead;\n            x->sr_intail = intail;\n            return (1);\n        }\n    }\n    return (0);\n}\n\nstatic void socketreceiver_getudp(t_socketreceiver *x, int fd)\n{\n    char *buf = (char *)sys_getrecvbuf(0);\n    socklen_t fromaddrlen = sizeof(struct sockaddr_storage);\n    int ret, readbytes = 0;\n    while (1)\n    {\n        ret = (int)recvfrom(fd, buf, NET_MAXPACKETSIZE-1, 0,\n            (struct sockaddr *)x->sr_fromaddr, (x->sr_fromaddr ? &fromaddrlen : 0));\n        if (ret < 0)\n        {\n                /* socket_errno_udp() ignores some error codes */\n            if (socket_errno_udp())\n            {\n                sys_sockerror(\"recv (udp)\");\n                    /* only notify and shutdown a UDP sender! */\n                if (x->sr_notifier)\n                {\n                    (*x->sr_notifier)(x->sr_owner, fd);\n                    sys_rmpollfn(fd);\n                    sys_closesocket(fd);\n                }\n            }\n            return;\n        }\n        else if (ret > 0)\n        {\n                /* handle too large UDP packets */\n            if (ret > NET_MAXPACKETSIZE-1)\n            {\n                post(\"warning: incoming UDP packet truncated from %d to %d bytes.\",\n                    ret, NET_MAXPACKETSIZE-1);\n                ret = NET_MAXPACKETSIZE-1;\n            }\n            buf[ret] = 0;\n    #if 0\n            post(\"%s\", buf);\n    #endif\n            if (buf[ret-1] != '\\n')\n            {\n    #if 0\n                pd_error(0, \"dropped bad buffer %s\\n\", buf);\n    #endif\n            }\n            else\n            {\n                char *semi = strchr(buf, ';');\n                if (semi)\n                    *semi = 0;\n                if (x->sr_fromaddrfn)\n                    (*x->sr_fromaddrfn)(x->sr_owner, (const void *)x->sr_fromaddr);\n                binbuf_text(INTER->i_inbinbuf, buf, strlen(buf));\n                outlet_setstacklim();\n                if (x->sr_socketreceivefn)\n                    (*x->sr_socketreceivefn)(x->sr_owner,\n                        INTER->i_inbinbuf);\n                else bug(\"socketreceiver_getudp\");\n            }\n            readbytes += ret;\n            /* throttle */\n            if (readbytes >= NET_MAXPACKETSIZE)\n                return;\n            /* check for pending UDP packets */\n            if (socket_bytes_available(fd) <= 0)\n                return;\n        }\n    }\n}\n\nvoid socketreceiver_read(t_socketreceiver *x, int fd)\n{\n    if (x->sr_udp)   /* UDP (\"datagram\") socket protocol */\n        socketreceiver_getudp(x, fd);\n    else  /* TCP (\"streaming\") socket protocol */\n    {\n        char *semi;\n        int readto =\n            (x->sr_inhead >= x->sr_intail ? INBUFSIZE : x->sr_intail-1);\n        int ret;\n\n            /* the input buffer might be full. If so, drop the whole thing */\n        if (readto == x->sr_inhead)\n        {\n            fprintf(stderr, \"pd: dropped message from gui\\n\");\n            x->sr_inhead = x->sr_intail = 0;\n            readto = INBUFSIZE;\n        }\n        else\n        {\n            ret = (int)recv(fd, x->sr_inbuf + x->sr_inhead,\n                readto - x->sr_inhead, 0);\n            if (ret <= 0)\n            {\n                if (ret < 0)\n                    sys_sockerror(\"recv (tcp)\");\n                if (x == INTER->i_socketreceiver)\n                {\n                    if (pd_this == &pd_maininstance)\n                    {\n                        fprintf(stderr, \"read from GUI socket: %s; stopping\\n\",\n                            strerror(errno));\n                        sys_bail(1);\n                    }\n                    else\n                    {\n                        sys_rmpollfn(fd);\n                        sys_closesocket(fd);\n                        sys_stopgui();\n                    }\n                }\n                else\n                {\n                    if (x->sr_notifier)\n                        (*x->sr_notifier)(x->sr_owner, fd);\n                    sys_rmpollfn(fd);\n                    sys_closesocket(fd);\n                }\n            }\n            else\n            {\n                x->sr_inhead += ret;\n                if (x->sr_inhead >= INBUFSIZE) x->sr_inhead = 0;\n                while (socketreceiver_doread(x))\n                {\n                    if (x->sr_fromaddrfn)\n                    {\n                        socklen_t fromaddrlen = sizeof(struct sockaddr_storage);\n                        if(!getpeername(fd,\n                                        (struct sockaddr *)x->sr_fromaddr,\n                                        &fromaddrlen))\n                            (*x->sr_fromaddrfn)(x->sr_owner,\n                                (const void *)x->sr_fromaddr);\n                    }\n                    outlet_setstacklim();\n                    if (x->sr_socketreceivefn)\n                        (*x->sr_socketreceivefn)(x->sr_owner,\n                            INTER->i_inbinbuf);\n                    else binbuf_eval(INTER->i_inbinbuf, 0, 0, 0);\n                    if (x->sr_inhead == x->sr_intail)\n                        break;\n                }\n            }\n        }\n    }\n}\n\nvoid socketreceiver_set_fromaddrfn(t_socketreceiver *x,\n    t_socketfromaddrfn fromaddrfn)\n{\n    x->sr_fromaddrfn = fromaddrfn;\n    if (fromaddrfn)\n    {\n        if (!x->sr_fromaddr)\n            x->sr_fromaddr = malloc(sizeof(struct sockaddr_storage));\n    }\n    else if (x->sr_fromaddr)\n    {\n        free(x->sr_fromaddr);\n        x->sr_fromaddr = NULL;\n    }\n}\n\nvoid sys_closesocket(int sockfd)\n{\n    socket_close(sockfd);\n}\n\n/* ---------------------- sending messages to the GUI ------------------ */\n#define GUI_ALLOCCHUNK 8192\n#define GUI_UPDATESLICE 512 /* how much we try to do in one idle period */\n#define GUI_BYTESPERPING 1024 /* how much we send up per ping */\n\nstatic void sys_trytogetmoreguibuf(int newsize)\n{\n        /* newsize can be negative if it overflows (at 0x7FFFFFFF)\n         * which only happens if we push a huge amount of data to the GUI,\n         * such as printing a billion numbers\n         *\n         * we could fix this by using size_t (or ssize_t), but this will\n         * possibly lead to memory exhaustion.\n         * as the overflow happens at 2GB which is rather large anyhow,\n         * but most machines will still be able to handle this without swapping\n         * and crashing, we just use the 2GB limit to trigger a synchronous write.\n\t * also note that on the Tcl/Tk side, the maximum size of a buffer is 2GB,\n\t * so there's a nice analogy here.\n         */\n    char *newbuf = (newsize>=0)?realloc(INTER->i_guibuf, newsize):0;\n#if 0\n    static int sizewas;\n    if (newsize > 70000 && sizewas < 70000)\n    {\n        int i;\n        for (i = INTER->i_guitail; i < INTER->i_guihead; i++)\n                fputc(INTER->i_guibuf[i], stderr);\n    }\n    sizewas = newsize;\n#endif\n#if 0\n    fprintf(stderr, \"new size %d (head %d, tail %d)\\n\",\n        newsize, INTER->i_guihead, INTER->i_guitail);\n#endif\n\n        /* if realloc fails, make a last-ditch attempt to stay alive by\n        synchronously writing out the existing contents.  LATER test\n        this by intentionally setting newbuf to zero */\n    if (!newbuf)\n    {\n        int bytestowrite = INTER->i_guihead - INTER->i_guitail;\n        int written = 0;\n        while (1)\n        {\n            int res = (int)send(\n                INTER->i_guisock,\n                INTER->i_guibuf + INTER->i_guitail + written,\n                bytestowrite, 0);\n            if (res < 0)\n            {\n                perror(\"pd output pipe\");\n                sys_bail(1);\n            }\n            else\n            {\n                written += res;\n                if (written >= bytestowrite)\n                    break;\n            }\n        }\n        INTER->i_guihead = INTER->i_guitail = 0;\n    }\n    else\n    {\n        INTER->i_guisize = newsize;\n        INTER->i_guibuf = newbuf;\n    }\n}\n\nint sys_havegui(void)\n{\n    return (INTER->i_havegui);\n}\n\nvoid sys_vgui(const char *fmt, ...)\n{\n    int msglen, bytesleft, headwas, nwrote;\n    va_list ap;\n\n    if (!sys_havegui())\n        return;\n    if (!INTER->i_guibuf)\n    {\n        if (!(INTER->i_guibuf = malloc(GUI_ALLOCCHUNK)))\n        {\n            fprintf(stderr, \"Pd: couldn't allocate GUI buffer\\n\");\n            sys_bail(1);\n        }\n        INTER->i_guisize = GUI_ALLOCCHUNK;\n        INTER->i_guihead = INTER->i_guitail = 0;\n    }\n    if (INTER->i_guihead > INTER->i_guisize - (GUI_ALLOCCHUNK/2)) {\n            sys_trytogetmoreguibuf(INTER->i_guisize + GUI_ALLOCCHUNK);\n    }\n    va_start(ap, fmt);\n    msglen = vsnprintf(\n        INTER->i_guibuf  + INTER->i_guihead,\n        INTER->i_guisize - INTER->i_guihead,\n        fmt, ap);\n    va_end(ap);\n    if(msglen < 0)\n    {\n        fprintf(stderr,\n            \"Pd: buffer space wasn't sufficient for long GUI string\\n\");\n        return;\n    }\n    if (msglen >= INTER->i_guisize - INTER->i_guihead)\n    {\n        int msglen2, newsize =\n            INTER->i_guisize\n            + 1\n            + (msglen > GUI_ALLOCCHUNK ? msglen : GUI_ALLOCCHUNK);\n        sys_trytogetmoreguibuf(newsize);\n\n        va_start(ap, fmt);\n        msglen2 = vsnprintf(\n            INTER->i_guibuf  + INTER->i_guihead,\n            INTER->i_guisize - INTER->i_guihead,\n            fmt, ap);\n        va_end(ap);\n        if (msglen2 != msglen)\n            bug(\"sys_vgui\");\n        if (msglen >= INTER->i_guisize - INTER->i_guihead)\n            msglen  = INTER->i_guisize - INTER->i_guihead;\n    }\n    if (sys_debuglevel & DEBUG_MESSUP)\n    {\n        const char *mess = INTER->i_guibuf + INTER->i_guihead;\n        int colorize = stderr_isatty && (sys_debuglevel & DEBUG_COLORIZE);\n        static int newmess = 1;\n#ifdef _WIN32\n    #ifdef _MSC_VER\n        fwprintf(stderr, L\"%S\", mess);\n    #else\n        fwprintf(stderr, L\"%s\", mess);\n    #endif\n        fflush(stderr);\n#else\n        if (colorize)\n            fprintf(stderr, \"\\e[0;1;35m%s%s\\e[0m\", (newmess)?\">> \":\"\", mess);\n        else\n            fprintf(stderr, \"%s%s\", (newmess)?\">> \":\"\", mess);\n\n        newmess = ('\\n' == mess[msglen-1]);\n#endif\n    }\n    INTER->i_guihead += msglen;\n    INTER->i_bytessincelastping += msglen;\n}\n\nvoid sys_gui(const char *s)\n{\n    sys_vgui(\"%s\", s);\n}\n\nstatic const char**namelist2strings(t_namelist *nl, unsigned int *N) {\n    const char**result = 0;\n    unsigned int n=0;\n    *N = 0;\n    for(; nl; nl = nl->nl_next) {\n        const char**newresult = resizebytes(result, n*sizeof(*result), (n+1)*sizeof(*result));\n        if(!newresult)\n            break;\n        result = newresult;\n        result[n] = nl->nl_string;\n        n++;\n        *N = n;\n    }\n    return result;\n}\n\nstatic int sys_flushtogui(void)\n{\n    int writesize = INTER->i_guihead - INTER->i_guitail,\n        nwrote = 0;\n    if (writesize > 0)\n        nwrote = (int)send(\n            INTER->i_guisock,\n            INTER->i_guibuf + INTER->i_guitail,\n            writesize, 0);\n\n#if 0\n    if (writesize)\n        fprintf(stderr, \"wrote %d of %d\\n\", nwrote, writesize);\n#endif\n\n    if (nwrote < 0)\n    {\n        perror(\"pd-to-gui socket\");\n        sys_bail(1);\n    }\n    else if (!nwrote)\n        return (0);\n    else if (nwrote >= INTER->i_guihead - INTER->i_guitail)\n        INTER->i_guihead = INTER->i_guitail = 0;\n    else if (nwrote)\n    {\n        INTER->i_guitail += nwrote;\n        if (INTER->i_guitail > (INTER->i_guisize >> 2))\n        {\n            memmove(INTER->i_guibuf,\n                INTER->i_guibuf  + INTER->i_guitail,\n                INTER->i_guihead - INTER->i_guitail);\n            INTER->i_guihead = INTER->i_guihead - INTER->i_guitail;\n            INTER->i_guitail = 0;\n        }\n    }\n    return (1);\n}\n\nvoid glob_ping(t_pd *dummy)\n{\n    INTER->i_waitingforping = 0;\n}\n\nstatic int sys_flushqueue(void)\n{\n    int wherestop = INTER->i_bytessincelastping + GUI_UPDATESLICE;\n    if (wherestop + (GUI_UPDATESLICE >> 1) > GUI_BYTESPERPING)\n        wherestop = 0x7fffffff;\n    if (INTER->i_waitingforping)\n        return (0);\n    if (!INTER->i_guiqueuehead)\n        return (0);\n    while (1)\n    {\n        if (INTER->i_bytessincelastping >= GUI_BYTESPERPING)\n        {\n            sys_gui(\"pdtk_ping\\n\");\n            INTER->i_bytessincelastping = 0;\n            INTER->i_waitingforping = 1;\n            return (1);\n        }\n        if (INTER->i_guiqueuehead)\n        {\n            t_guiqueue *headwas = INTER->i_guiqueuehead;\n            INTER->i_guiqueuehead = headwas->gq_next;\n            (*headwas->gq_fn)(headwas->gq_client, headwas->gq_glist);\n            t_freebytes(headwas, sizeof(*headwas));\n            if (INTER->i_bytessincelastping >= wherestop)\n                break;\n        }\n        else break;\n    }\n    sys_flushtogui();\n    return (1);\n}\n\n    /* flush output buffer and update queue to gui in small time slices */\nstatic int sys_poll_togui(void) /* returns 1 if did anything */\n{\n    if (!sys_havegui())\n        return (0);\n        /* in case there is stuff still in the buffer, try to flush it. */\n    sys_flushtogui();\n        /* if the flush wasn't complete, wait. */\n    if (INTER->i_guihead > INTER->i_guitail)\n        return (0);\n\n        /* check for queued updates */\n    if (sys_flushqueue())\n        return (1);\n\n    return (0);\n}\n\n    /* if some GUI object is having to do heavy computations, it can tell\n    us to back off from doing more updates by faking a big one itself. */\nvoid sys_pretendguibytes(int n)\n{\n    INTER->i_bytessincelastping += n;\n}\n\nvoid sys_queuegui(void *client, t_glist *glist, t_guicallbackfn f)\n{\n    t_guiqueue **gqnextptr, *gq;\n    if (!INTER->i_guiqueuehead)\n        gqnextptr = &INTER->i_guiqueuehead;\n    else\n    {\n        for (gq = INTER->i_guiqueuehead; gq->gq_next;\n            gq = gq->gq_next)\n                if (gq->gq_client == client)\n                    return;\n        if (gq->gq_client == client)\n            return;\n        gqnextptr = &gq->gq_next;\n    }\n    gq = t_getbytes(sizeof(*gq));\n    gq->gq_next = 0;\n    gq->gq_client = client;\n    gq->gq_glist = glist;\n    gq->gq_fn = f;\n    gq->gq_next = 0;\n    *gqnextptr = gq;\n}\n\nvoid sys_unqueuegui(void *client)\n{\n    t_guiqueue *gq, *gq2;\n    while (INTER->i_guiqueuehead && INTER->i_guiqueuehead->gq_client == client)\n    {\n        gq = INTER->i_guiqueuehead;\n        INTER->i_guiqueuehead = INTER->i_guiqueuehead->gq_next;\n        t_freebytes(gq, sizeof(*gq));\n    }\n    if (!INTER->i_guiqueuehead)\n        return;\n    for (gq = INTER->i_guiqueuehead; (gq2 = gq->gq_next); gq = gq2)\n        if (gq2->gq_client == client)\n        {\n            gq->gq_next = gq2->gq_next;\n            t_freebytes(gq2, sizeof(*gq2));\n            break;\n        }\n}\n\n    /* poll for any incoming packets, or for GUI updates to send.  call with\n    the PD instance lock set. */\nint sys_pollgui(void)\n{\n    static double lasttime = 0;\n    double now = 0;\n    int didsomething = sys_domicrosleep(0);\n    if (!didsomething || (now = sys_getrealtime()) > lasttime + 0.5)\n    {\n        didsomething |= sys_poll_togui();\n        if (now)\n            lasttime = now;\n    }\n    return (didsomething);\n}\n\nvoid sys_init_fdpoll(void)\n{\n    if (INTER->i_fdpoll)\n        return;\n    /* create an empty FD poll list */\n    INTER->i_fdpoll = (t_fdpoll *)t_getbytes(0);\n    INTER->i_nfdpoll = 0;\n    INTER->i_inbinbuf = binbuf_new();\n}\n\nvoid sys_gui_preferences(void)\n{\n    unsigned int nsearch, ntemp, nstatic, nlibs;\n    const char**searchpath = namelist2strings(STUFF->st_searchpath, &nsearch);\n    const char**temppath = namelist2strings(STUFF->st_temppath, &ntemp);\n    const char**staticpath = namelist2strings(STUFF->st_staticpath, &nstatic);\n    const char**startuplibs = namelist2strings(STUFF->st_externlist, &nlibs);\n    pdgui_vmess(\"::dialog_path::set_paths\", \"SSS\"\n                , nsearch, searchpath\n                , ntemp, temppath\n                , nstatic, staticpath\n                );\n\n        /* send the list of loaded libraries ... */\n    pdgui_vmess(\"::dialog_startup::set_libraries\", \"S\"\n                , nlibs, startuplibs\n                );\n\n    sys_vgui(\"set_escaped ::sys_verbose %d\\n\", sys_verbose);\n    sys_vgui(\"set_escaped ::sys_use_stdpath %d\\n\", sys_usestdpath);\n    sys_vgui(\"set_escaped ::sys_defeatrt %d\\n\", sys_defeatrt);\n    sys_vgui(\"set_escaped ::sys_zoom_open %d\\n\", (sys_zoom_open == 2));\n    pdgui_vmess(\"::dialog_startup::set_flags\", \"s\",\n                (sys_flags? sys_flags->s_name : \"\"));\n\n    freebytes(searchpath, nsearch * sizeof(*searchpath));\n    freebytes(temppath, ntemp * sizeof(*temppath));\n    freebytes(staticpath, nstatic * sizeof(*staticpath));\n    freebytes(startuplibs, nlibs * sizeof(*startuplibs));\n}\n\n\n\n\n/* --------------------- starting up the GUI connection ------------- */\n\nstatic int sys_watchfd = -1;\n\nvoid glob_watchdog(t_pd *dummy)\n{\n    if (sys_watchfd < 0)\n        return;\n    if (write(sys_watchfd, \"\\n\", 1) < 1)\n    {\n        fprintf(stderr, \"pd: watchdog process died\\n\");\n        sys_bail(1);\n    }\n}\n\nstatic const char*deken_OS =\n#if defined DEKEN_OS\n        stringify(DEKEN_OS)\n#elif defined __linux__\n        \"Linux\"\n#elif defined __APPLE__\n        \"Darwin\"\n#elif defined _WIN32\n        \"Windows\"\n#else\n# if defined(__GNUC__)\n#  warning unknown OS\n# endif\n        0\n#endif\n        ;\nstatic const char*deken_CPU[] = {\n#if defined DEKEN_CPU\n        stringify(DEKEN_CPU)\n#elif defined(__x86_64__) || defined(__amd64__) || defined(_M_X64) || defined(_M_AMD64)\n        \"amd64\"\n#elif defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) || defined(_M_IX86)\n        \"i386\"\n#elif defined(__ppc__)\n        \"ppc\"\n#elif defined(__aarch64__)\n        \"arm64\"\n#elif defined (__ARM_ARCH)\n        \"armv\" stringify(__ARM_ARCH)\n# if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__)\n#  if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__\n        \"b\"\n#  endif\n# endif\n#else\n# if defined(__GNUC__)\n#  warning unknown architecture\n# endif\n        0\n#endif\n        , 0, 0, 0, 0, 0, 0, 0, 0, 0};\n\nstatic const char*strip_quotes(const char*s, char*outbuf, size_t outsize) {\n    size_t len = strlen(s);\n    const char q = (len>1)?s[0]:0;\n        /* only strip single or double quotes */\n    switch(q) {\n    case '\\'':\n    case '\"':\n        break;\n    case 0:\n    default:\n        return s;\n    }\n        /* only strip quotes if they are both at the beginning and the end */\n    if (q != s[len-1])\n        return s;\n\n    if(len>outsize)\n        len = outsize;\n\n    outbuf[0] = 0;\n    strncpy(outbuf, s+1, len-2);\n    outbuf[outsize-1] = 0;\n    return outbuf;\n}\n\nstatic void init_deken_arch(void)\n{\n    static int initialized = 0;\n    static char deken_OS_noquotes[MAXPDSTRING];\n    static char deken_CPU_noquotes[MAXPDSTRING];\n\n    if(initialized)\n        return;\n    initialized = 1;\n\n#if defined(DEKEN_OS)\n    deken_OS = strip_quotes(deken_OS, deken_OS_noquotes, MAXPDSTRING);\n#endif /* DEKEN_OS */\n\n#define CPUNAME_SIZE 15\n#if defined(DEKEN_CPU)\n    deken_CPU[0] = strip_quotes(deken_CPU[0], deken_CPU_noquotes, MAXPDSTRING);\n#else /* !DEKEN_CPU */\n# if defined __ARM_ARCH\n        /* ARM-specific:\n         * if we are running ARMv7, we can also load ARMv6 externals\n         */\n    if (deken_CPU && sizeof(deken_CPU)/sizeof(*deken_CPU) > 0)\n    {\n        int arm_cpu = __ARM_ARCH;\n        int cpu_v;\n        int n;\n        const char endianness =\n#  if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)\n            'b';\n#  else\n            0;\n#  endif\n\n#  if USE_UNAME\n        /*\n         * Pd might be compiled for ARMv6 (as in Raspbian),\n         * but run on an ARMv7 (or higher) (e.g. RPi2 and newer).\n         * Therefore we try to detect the actual CPU, and announce that\n         */\n        struct utsname name;\n        if (uname (&name) >= 0) {\n            if(!strncmp(name.machine, \"armv\", 4)) {\n                cpu_v = name.machine[4] - '0';\n                if((cpu_v >= 6) && (cpu_v <= 9))\n                    arm_cpu = cpu_v;\n            }\n        }\n#  endif /* uname() */\n\n            /* list all compatible ARM CPUs */\n        for(cpu_v = arm_cpu;\n            (cpu_v >= 6 && n < (sizeof(deken_CPU)/sizeof(*deken_CPU)));\n            cpu_v--)\n        {\n            static char cpuname[CPUNAME_SIZE+1];\n            snprintf(cpuname, CPUNAME_SIZE, \"armv%d%c\", cpu_v, endianness);\n            deken_CPU[n++] = gensym(cpuname)->s_name;\n        }\n    }\n# endif /* arm */\n#endif /* !DEKEN_CPU */\n}\n\n/* get the (normalized) deken-specifier\n * if 'float_agnostic' is non-0, the float-size is included.\n *   otherwise a floatsize-agnostic specifier is generated.\n * 'cpu' is an index in the list of preferred compatible CPUs\n *   (higher numbers indicate less preferred CPUs)\n *   a negative 'cpu' indicates 'fat' binaries\n * returns 0, if the deken-specifier cannot be determined\n * (e.g. on new architectures, or because the 'cpu' index is invalid)\n */\nconst char*sys_deken_specifier(char*buf, size_t bufsize, int float_agnostic, int cpu) {\n    unsigned int i;\n    init_deken_arch();\n    if (!deken_OS)\n        return 0;\n    if ((cpu>=0) && (((!deken_CPU) || (cpu >= (sizeof(deken_CPU)/sizeof(*deken_CPU))) || (!deken_CPU[cpu]))))\n        return 0;\n\n    snprintf(buf, bufsize-1,\n        \"%s-%s-%d\", deken_OS, (cpu<0)?\"fat\":deken_CPU[cpu], (int)((float_agnostic?0:8) * sizeof(t_float)));\n\n    buf[bufsize-1] = 0;\n    for(i=0; i<bufsize && buf[i]; i++)\n        buf[i] = tolower(buf[i]);\n    return buf;\n}\n\nstatic void sys_init_deken(void)\n{\n    init_deken_arch();\n        /* only send the arch info, if we are sure about it... */\n    if (deken_OS && deken_CPU && deken_CPU[0])\n        pdgui_vmess(\"::deken::set_platform\", \"ssff\",\n                 deken_OS, deken_CPU[0],\n                 8. * sizeof(char*),\n                 8. * sizeof(t_float));\n}\n\nstatic int sys_do_startgui(const char *libdir)\n{\n    char quotebuf[MAXPDSTRING];\n    char apibuf[256], apibuf2[256];\n    struct addrinfo *ailist = NULL, *ai;\n    int sockfd = -1;\n    int portno = -1;\n#ifndef _WIN32\n    int stdinpipe[2];\n    pid_t childpid;\n#endif /* _WIN32 */\n\n    sys_init_fdpoll();\n\n    if (sys_guisetportnumber)  /* GUI exists and sent us a port number */\n    {\n        int status;\n#ifdef __APPLE__\n            /* guisock might be 1 or 2, which will have offensive results\n            if somebody writes to stdout or stderr - so we just open a few\n            files to try to fill fds 0 through 2.  (I tried using dup()\n            instead, which would seem the logical way to do this, but couldn't\n            get it to work.) */\n        int burnfd1 = open(\"/dev/null\", 0), burnfd2 = open(\"/dev/null\", 0),\n            burnfd3 = open(\"/dev/null\", 0);\n        if (burnfd1 > 2)\n            close(burnfd1);\n        if (burnfd2 > 2)\n            close(burnfd2);\n        if (burnfd3 > 2)\n            close(burnfd3);\n#endif\n\n        /* get addrinfo list using hostname & port */\n        status = addrinfo_get_list(&ailist,\n            LOCALHOST, sys_guisetportnumber, SOCK_STREAM);\n        if (status != 0)\n        {\n            fprintf(stderr,\n                \"localhost not found (inet protocol not installed?)\\n%s (%d)\",\n                gai_strerror(status), status);\n            return (1);\n        }\n\n        /* Sort to IPv4 for now as the Pd gui uses IPv4. */\n        addrinfo_sort_list(&ailist, addrinfo_ipv4_first);\n\n        /* We don't know in advance whether the GUI uses IPv4 or IPv6,\n           so we try both and pick the one which works. */\n        for (ai = ailist; ai != NULL; ai = ai->ai_next)\n        {\n            /* create a socket */\n            sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);\n            if (sockfd < 0)\n                continue;\n        #if 1\n            if (socket_set_boolopt(sockfd, IPPROTO_TCP, TCP_NODELAY, 1) < 0)\n                fprintf(stderr, \"setsockopt (TCP_NODELAY) failed\");\n        #endif\n            /* try to connect */\n            if (socket_connect(sockfd, ai->ai_addr, ai->ai_addrlen, 10.f) < 0)\n            {\n                sys_closesocket(sockfd);\n                sockfd = -1;\n                continue;\n            }\n            /* this addr worked */\n            break;\n        }\n        freeaddrinfo(ailist);\n\n        /* confirm that we could connect */\n        if (sockfd < 0)\n        {\n            sys_sockerror(\"connecting stream socket\");\n            return (1);\n        }\n\n        INTER->i_guisock = sockfd;\n    }\n    else    /* default behavior: start up the GUI ourselves. */\n    {\n        struct sockaddr_storage addr;\n        int status;\n#ifdef _WIN32\n        char scriptbuf[MAXPDSTRING+30], wishbuf[MAXPDSTRING+30];\n        STARTUPINFO si;\n        PROCESS_INFORMATION pi;\n#else\n        const char *guicmd;\n#endif\n        char cmdbuf[4*MAXPDSTRING];\n        /* get addrinfo list using hostname (get random port from OS) */\n        status = addrinfo_get_list(&ailist, LOCALHOST, 0, SOCK_STREAM);\n        if (status != 0)\n        {\n            fprintf(stderr,\n                \"localhost not found (inet protocol not installed?)\\n%s (%d)\",\n                gai_strerror(status), status);\n            return (1);\n        }\n        /* we prefer the IPv4 addresses because the GUI might not be IPv6 capable. */\n        addrinfo_sort_list(&ailist, addrinfo_ipv4_first);\n        /* try each addr until we find one that works */\n        for (ai = ailist; ai != NULL; ai = ai->ai_next)\n        {\n            sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);\n            if (sockfd < 0)\n                continue;\n        #if 1\n            /* ask OS to allow another process to reopen this port after we close it */\n            if (socket_set_boolopt(sockfd, SOL_SOCKET, SO_REUSEADDR, 1) < 0)\n                fprintf(stderr, \"setsockopt (SO_REUSEADDR) failed\\n\");\n        #endif\n        #if 1\n            /* stream (TCP) sockets are set NODELAY */\n            if (socket_set_boolopt(sockfd, IPPROTO_TCP, TCP_NODELAY, 1) < 0)\n                fprintf(stderr, \"setsockopt (TCP_NODELAY) failed\");\n        #endif\n            /* name the socket */\n            if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0)\n            {\n                socket_close(sockfd);\n                sockfd = -1;\n                continue;\n            }\n            /* this addr worked */\n            memcpy(&addr, ai->ai_addr, ai->ai_addrlen);\n            break;\n        }\n        freeaddrinfo(ailist);\n\n        /* confirm that socket/bind worked */\n        if (sockfd < 0)\n        {\n            sys_sockerror(\"bind\");\n            return (1);\n        }\n        /* get the actual port number */\n        portno = socket_get_port(sockfd);\n        if (sys_verbose) fprintf(stderr, \"port %d\\n\", portno);\n\n#ifndef _WIN32\n        if (sys_guicmd)\n            guicmd = sys_guicmd;\n        else\n        {\n#ifdef __APPLE__\n            int i;\n            struct stat statbuf;\n            glob_t glob_buffer;\n            char *homedir = getenv(\"HOME\");\n            char embed_glob[FILENAME_MAX];\n            char home_filename[FILENAME_MAX];\n            char *wish_paths[11] = {\n \"(custom wish not defined)\",\n \"(did not find a home directory)\",\n \"/Applications/Utilities/Wish.app/Contents/MacOS/Wish\",\n \"/Applications/Utilities/Wish Shell.app/Contents/MacOS/Wish Shell\",\n \"/Applications/Wish.app/Contents/MacOS/Wish\",\n \"/Applications/Wish Shell.app/Contents/MacOS/Wish Shell\",\n \"/Library/Frameworks/Tk.framework/Resources/Wish.app/Contents/MacOS/Wish\",\n \"/Library/Frameworks/Tk.framework/Resources/Wish Shell.app/Contents/MacOS/Wish Shell\",\n \"/System/Library/Frameworks/Tk.framework/Resources/Wish.app/Contents/MacOS/Wish\",\n \"/System/Library/Frameworks/Tk.framework/Resources/Wish Shell.app/Contents/MacOS/Wish Shell\",\n \"/usr/bin/wish\"\n            };\n            /* this glob is needed so the Wish executable can have the same\n             * filename as the Pd.app, i.e. 'Pd-0.42-3.app' should have a Wish\n             * executable called 'Pd-0.42-3.app/Contents/MacOS/Pd-0.42-3' */\n            sprintf(embed_glob, \"%s/../MacOS/Pd*\", libdir);\n            glob_buffer.gl_matchc = 1; /* we only need one match */\n            glob(embed_glob, GLOB_LIMIT, NULL, &glob_buffer);\n            /* If we are using a copy of Wish embedded in the Pd.app, then it\n             * will automatically load pd-gui.tcl if that embedded Wish can\n             * find ../Resources/Scripts/AppMain.tcl, then Wish doesn't want\n             * to receive the pd-gui.tcl as an argument.  Otherwise it needs\n             * to know how to find pd-gui.tcl */\n            if (glob_buffer.gl_pathc > 0)\n                sprintf(cmdbuf, \"\\\"%s\\\" %d\\n\", glob_buffer.gl_pathv[0], portno);\n            else\n            {\n                int wish_paths_count = sizeof(wish_paths)/sizeof(*wish_paths);\n                #ifdef WISH\n                    wish_paths[0] = WISH;\n                #endif\n                sprintf(home_filename,\n                        \"%s/Applications/Wish.app/Contents/MacOS/Wish\",homedir);\n                wish_paths[1] = home_filename;\n                for(i=0; i<wish_paths_count; i++)\n                {\n                    if (sys_verbose)\n                        fprintf(stderr, \"Trying Wish at \\\"%s\\\"\\n\",\n                            wish_paths[i]);\n                    if (stat(wish_paths[i], &statbuf) >= 0)\n                        break;\n                }\n                if(i>=wish_paths_count)\n                {\n                    fprintf(stderr, \"sys_startgui couldn't find tcl/tk\\n\");\n                    sys_closesocket(sockfd);\n                    return (1);\n                }\n                sprintf(cmdbuf, \"\\\"%s\\\" \\\"%s/%s/pd-gui.tcl\\\" %d\\n\",\n                        wish_paths[i], libdir, PDGUIDIR, portno);\n            }\n#else /* __APPLE__ */\n            /* sprintf the wish command with needed environment variables.\n            For some reason the wish script fails if HOME isn't defined so\n            if necessary we put that in here too. */\n            sprintf(cmdbuf,\n  \"TCL_LIBRARY=\\\"%s/lib/tcl/library\\\" TK_LIBRARY=\\\"%s/lib/tk/library\\\"%s \\\n  \" WISH \" \\\"%s/\" PDGUIDIR \"/pd-gui.tcl\\\" %d\\n\",\n                 libdir, libdir, (getenv(\"HOME\") ? \"\" : \" HOME=/tmp\"),\n                    libdir, portno);\n#endif /* __APPLE__ */\n            guicmd = cmdbuf;\n        }\n        if (sys_verbose)\n            fprintf(stderr, \"%s\", guicmd);\n\n        childpid = fork();\n        if (childpid < 0)\n        {\n            if (errno) perror(\"sys_startgui\");\n            else fprintf(stderr, \"sys_startgui failed\\n\");\n            sys_closesocket(sockfd);\n            return (1);\n        }\n        else if (!childpid)                     /* we're the child */\n        {\n            sys_closesocket(sockfd);     /* child doesn't listen */\n            sys_set_priority(MODE_NRT);  /* child runs non-real-time */\n#ifndef __APPLE__\n// TODO this seems unneeded on any platform hans@eds.org\n                /* the wish process in Unix will make a wish shell and\n                    read/write standard in and out unless we close the\n                    file descriptors.  Somehow this doesn't make the MAC OSX\n                        version of Wish happy...*/\n            if (pipe(stdinpipe) < 0)\n                sys_sockerror(\"pipe\");\n            else\n            {\n                if (stdinpipe[0] != 0)\n                {\n                    close (0);\n                    dup2(stdinpipe[0], 0);\n                    close(stdinpipe[0]);\n                }\n            }\n#endif /* NOT __APPLE__ */\n            execl(\"/bin/sh\", \"sh\", \"-c\", guicmd, (char*)0);\n            perror(\"pd: exec\");\n            fprintf(stderr, \"Perhaps tcl and tk aren't yet installed?\\n\");\n            _exit(1);\n       }\n#else /* NOT _WIN32 */\n        /* fprintf(stderr, \"%s\\n\", libdir); */\n\n        snprintf(wishbuf, sizeof(wishbuf), \"%s/\" PDBINDIR WISH, libdir);\n        sys_bashfilename(wishbuf, wishbuf);\n\n        snprintf(scriptbuf, sizeof(scriptbuf), \"%s/\" PDGUIDIR \"/pd-gui.tcl\", libdir);\n        sys_bashfilename(scriptbuf, scriptbuf);\n\n        snprintf(cmdbuf, sizeof(cmdbuf), \"%s \\\"%s\\\" %d\", /* quote script path! */\n            WISH, scriptbuf, portno);\n\n        memset(&si, 0, sizeof(si));\n        si.cb = sizeof(si);\n            /* CHR: DETACHED_PROCESS makes sure that the GUI process cannot\n            possibly interfere with the core. */\n        if (!CreateProcessA(wishbuf, cmdbuf, NULL, NULL, FALSE,\n            DETACHED_PROCESS, NULL, NULL, &si, &pi))\n        {\n            char errbuf[MAXPDSTRING];\n            socket_strerror(GetLastError(), errbuf, sizeof(errbuf));\n            fprintf(stderr, \"could not start %s: %s\\n\", wishbuf, errbuf);\n            return (1);\n        }\n#endif /* NOT _WIN32 */\n        if (sys_verbose)\n            fprintf(stderr, \"Waiting for connection request... \\n\");\n        if (listen(sockfd, 5) < 0)\n        {\n            sys_sockerror(\"listen\");\n            sys_closesocket(sockfd);\n            return (1);\n        }\n\n        INTER->i_guisock = accept(sockfd, 0, 0);\n\n        sys_closesocket(sockfd);\n\n        if (INTER->i_guisock < 0)\n        {\n            sys_sockerror(\"accept\");\n            return (1);\n        }\n        if (sys_verbose)\n            fprintf(stderr, \"... connected\\n\");\n        INTER->i_guihead = INTER->i_guitail = 0;\n    }\n\n    INTER->i_socketreceiver = socketreceiver_new(0, 0, 0, 0);\n    sys_addpollfn(INTER->i_guisock,\n        (t_fdpollfn)socketreceiver_read,\n            INTER->i_socketreceiver);\n\n            /* here is where we start the pinging. */\n#if PD_WATCHDOG\n    if (sys_hipriority)\n        sys_gui(\"pdtk_watchdog\\n\");\n#endif\n    sys_get_audio_apis(apibuf);\n    sys_get_midi_apis(apibuf2);\n\n    sys_gui_preferences();     /* tell GUI about path and startup flags */\n\n        /* ... and about font, media APIS, etc */\n    sys_vgui(\"pdtk_pd_startup %d %d %d {%s} %s %s {%s} %s\\n\",\n             PD_MAJOR_VERSION, PD_MINOR_VERSION,\n             PD_BUGFIX_VERSION, PD_TEST_VERSION,\n             apibuf, apibuf2,\n             pdgui_strnescape(quotebuf, MAXPDSTRING, sys_font, 0),\n             sys_fontweight);\n\n    sys_init_deken();\n\n    do {\n        t_audiosettings as;\n        sys_get_audio_settings(&as);\n        sys_vgui(\"set pd_whichapi %d\\n\", as.a_api);\n    } while(0);\n    return (0);\n}\n\nvoid sys_setrealtime(const char *libdir)\n{\n    char cmdbuf[MAXPDSTRING];\n#if PD_WATCHDOG\n        /*  promote this process's priority, if we can and want to.\n        If sys_hipriority not specified (-1), we assume real-time was wanted.\n        Starting in Linux 2.6 one can permit real-time operation of Pd by]\n        putting lines like:\n                @audio - rtprio 99\n                @audio - memlock unlimited\n        in the system limits file, perhaps /etc/limits.conf or\n        /etc/security/limits.conf, and calling Pd from a user in group audio. */\n    if (sys_hipriority == -1)\n        sys_hipriority = 1;\n\n    snprintf(cmdbuf, MAXPDSTRING, \"%s/bin/pd-watchdog\", libdir);\n    cmdbuf[MAXPDSTRING-1] = 0;\n    if (sys_hipriority)\n    {\n        struct stat statbuf;\n        if (stat(cmdbuf, &statbuf) < 0)\n        {\n            fprintf(stderr,\n              \"disabling real-time priority due to missing pd-watchdog (%s)\\n\",\n                cmdbuf);\n            sys_hipriority = 0;\n        }\n    }\n    if (sys_hipriority)\n    {\n        int pipe9[2], watchpid;\n            /* To prevent lockup, we fork off a watchdog process with\n            higher real-time priority than ours.  The GUI has to send\n            a stream of ping messages to the watchdog THROUGH the Pd\n            process which has to pick them up from the GUI and forward\n            them.  If any of these things aren't happening the watchdog\n            starts sending \"stop\" and \"cont\" signals to the Pd process\n            to make it timeshare with the rest of the system.  (Version\n            0.33P2 : if there's no GUI, the watchdog pinging is done\n            from the scheduler idle routine in this process instead.) */\n\n        if (pipe(pipe9) < 0)\n        {\n            sys_sockerror(\"pipe\");\n            return;\n        }\n        watchpid = fork();\n        if (watchpid < 0)\n        {\n            if (errno)\n                perror(\"sys_setpriority\");\n            else fprintf(stderr, \"sys_setpriority failed\\n\");\n            return;\n        }\n        else if (!watchpid)             /* we're the child */\n        {\n            sys_set_priority(MODE_WATCHDOG);\n            if (pipe9[1] != 0)\n            {\n                dup2(pipe9[0], 0);\n                close(pipe9[0]);\n            }\n            close(pipe9[1]);\n\n            if (sys_verbose) fprintf(stderr, \"%s\\n\", cmdbuf);\n            execl(cmdbuf, cmdbuf, (char*)0);\n            perror(\"pd: exec\");\n            _exit(1);\n        }\n        else                            /* we're the parent */\n        {\n            sys_set_priority(MODE_RT);\n            close(pipe9[0]);\n                /* set close-on-exec so that watchdog will see an EOF when we\n                close our copy - otherwise it might hang waiting for some\n                stupid child process (as seems to happen if jackd auto-starts\n                for us.) */\n            if(fcntl(pipe9[1], F_SETFD, FD_CLOEXEC) < 0)\n              perror(\"close-on-exec\");\n            sys_watchfd = pipe9[1];\n                /* We also have to start the ping loop in the GUI;\n                this is done later when the socket is open. */\n        }\n    }\n    else logpost(NULL, PD_VERBOSE, \"not setting real-time priority\");\n#endif /* __linux__ */\n\n#ifdef _WIN32\n    if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS))\n        fprintf(stderr, \"pd: couldn't set high priority class\\n\");\n#endif\n#ifdef __APPLE__\n    if (sys_hipriority)\n    {\n        struct sched_param param;\n        int policy = SCHED_RR;\n        int err;\n        param.sched_priority = 80; /* adjust 0 : 100 */\n\n        err = pthread_setschedparam(pthread_self(), policy, &param);\n        if (err)\n            post(\"warning: high priority scheduling failed\");\n    }\n#endif /* __APPLE__ */\n}\n\n/* This is called when something bad has happened, like a segfault.\nCall glob_quit() below to exit cleanly.\nLATER try to save dirty documents even in the bad case. */\nvoid sys_bail(int n)\n{\n    static int reentered = 0;\n    if (!reentered)\n    {\n        reentered = 1;\n#if !defined(__linux__) && !defined(__FreeBSD_kernel__) && !defined(__GNU__)\n            /* sys_close_audio() hangs if you're in a signal? */\n        fprintf(stderr ,\"gui socket %d - \\n\", INTER->i_guisock);\n        fprintf(stderr, \"closing audio...\\n\");\n        sys_close_audio();\n        fprintf(stderr, \"closing MIDI...\\n\");\n        sys_close_midi();\n        fprintf(stderr, \"... done.\\n\");\n#endif\n        exit(n);\n    }\n    else _exit(1);\n}\n\nextern void sys_exit(void);\n\nvoid glob_exit(void *dummy, t_float status)\n{\n        /* sys_exit() sets the sys_quit flag, so all loops end */\n    sys_exit();\n    sys_close_audio();\n    sys_close_midi();\n    if (sys_havegui())\n    {\n        sys_closesocket(INTER->i_guisock);\n        sys_rmpollfn(INTER->i_guisock);\n    }\n    exit((int)status);\n}\nvoid glob_quit(void *dummy)\n{\n    glob_exit(dummy, 0);\n}\n\n    /* recursively descend to all canvases and send them \"vis\" messages\n    if they believe they're visible, to make it really so. */\nstatic void glist_maybevis(t_glist *gl)\n{\n    t_gobj *g;\n    for (g = gl->gl_list; g; g = g->g_next)\n        if (pd_class(&g->g_pd) == canvas_class)\n            glist_maybevis((t_glist *)g);\n    if (gl->gl_havewindow)\n    {\n        canvas_vis(gl, 0);\n        canvas_vis(gl, 1);\n    }\n}\n\nint sys_startgui(const char *libdir)\n{\n    t_canvas *x;\n    stderr_isatty = isatty(2);\n    for (x = pd_getcanvaslist(); x; x = x->gl_next)\n        canvas_vis(x, 0);\n    INTER->i_havegui = 1;\n    INTER->i_guihead = INTER->i_guitail = 0;\n    if (sys_do_startgui(libdir))\n        return (-1);\n    for (x = pd_getcanvaslist(); x; x = x->gl_next)\n        if (strcmp(x->gl_name->s_name, \"_float_template\") &&\n            strcmp(x->gl_name->s_name, \"_float_array_template\") &&\n                strcmp(x->gl_name->s_name, \"_text_template\"))\n    {\n        glist_maybevis(x);\n        canvas_vis(x, 1);\n    }\n    return (0);\n}\n\n /* more work needed here - for some reason we can't restart the gui after\n shutting it down this way.  I think the second 'init' message never makes\n it because the to-gui buffer isn't re-initialized. */\nvoid sys_stopgui(void)\n{\n    t_canvas *x;\n    for (x = pd_getcanvaslist(); x; x = x->gl_next)\n        canvas_vis(x, 0);\n    sys_vgui(\"%s\", \"exit\\n\");\n    if (INTER->i_guisock >= 0)\n    {\n        sys_closesocket(INTER->i_guisock);\n        sys_rmpollfn(INTER->i_guisock);\n        INTER->i_guisock = -1;\n    }\n    INTER->i_havegui = 0;\n}\n\n/* ----------- mutexes for thread safety --------------- */\n\nvoid s_inter_newpdinstance(void)\n{\n    INTER = getbytes(sizeof(*INTER));\n#if PDTHREADS\n    pthread_mutex_init(&INTER->i_mutex, NULL);\n    pd_this->pd_islocked = 0;\n#endif\n#ifdef _WIN32\n    INTER->i_freq = 0;\n#endif\n    INTER->i_havegui = 0;\n}\n\nvoid s_inter_free(t_instanceinter *inter)\n{\n    if (inter->i_fdpoll)\n    {\n        binbuf_free(inter->i_inbinbuf);\n        inter->i_inbinbuf = 0;\n        t_freebytes(inter->i_fdpoll, inter->i_nfdpoll * sizeof(t_fdpoll));\n        inter->i_fdpoll = 0;\n        inter->i_nfdpoll = 0;\n    }\n#if PDTHREADS\n    pthread_mutex_destroy(&INTER->i_mutex);\n#endif\n    freebytes(inter, sizeof(*inter));\n}\n\nvoid s_inter_freepdinstance(void)\n{\n    s_inter_free(INTER);\n}\n\n#if PDTHREADS\n#ifdef PDINSTANCE\nstatic pthread_rwlock_t sys_rwlock = PTHREAD_RWLOCK_INITIALIZER;\n#else /* PDINSTANCE */\nstatic pthread_mutex_t sys_mutex = PTHREAD_MUTEX_INITIALIZER;\n#endif /* PDINSTANCE */\n#endif /* PDTHREADS */\n\n#if PDTHREADS\n\n/* routines to lock and unlock Pd's global class structure or list of Pd\ninstances.  These are called internally within Pd when creating classes, adding\nmethods to them, or creating or freeing Pd instances.  They should probably\nnot be called from outside Pd.  They should be called at a point where the\ncurrent instance of Pd is currently locked via sys_lock() below; this gains\nread access to the class and instance lists which must be released for the\nwrite-lock to be available. */\n\nvoid pd_globallock(void)\n{\n#ifdef PDINSTANCE\n    if (!pd_this->pd_islocked)\n        bug(\"pd_globallock\");\n    pthread_rwlock_unlock(&sys_rwlock);\n    pthread_rwlock_wrlock(&sys_rwlock);\n#endif /* PDINSTANCE */\n}\n\nvoid pd_globalunlock(void)\n{\n#ifdef PDINSTANCE\n    pthread_rwlock_unlock(&sys_rwlock);\n    pthread_rwlock_rdlock(&sys_rwlock);\n#endif /* PDINSTANCE */\n}\n\n/* routines to lock/unlock a Pd instance for thread safety.  Call pd_setinsance\nfirst.  The \"pd_this\"  variable can be written and read thread-safely as it\nis defined as per-thread storage. */\nvoid sys_lock(void)\n{\n#ifdef PDINSTANCE\n    pthread_mutex_lock(&INTER->i_mutex);\n    pthread_rwlock_rdlock(&sys_rwlock);\n    pd_this->pd_islocked = 1;\n#else\n    pthread_mutex_lock(&sys_mutex);\n#endif\n}\n\nvoid sys_unlock(void)\n{\n#ifdef PDINSTANCE\n    pd_this->pd_islocked = 0;\n    pthread_rwlock_unlock(&sys_rwlock);\n    pthread_mutex_unlock(&INTER->i_mutex);\n#else\n    pthread_mutex_unlock(&sys_mutex);\n#endif\n}\n\nint sys_trylock(void)\n{\n#ifdef PDINSTANCE\n    int ret;\n    if (!(ret = pthread_mutex_trylock(&INTER->i_mutex)))\n    {\n        if (!(ret = pthread_rwlock_tryrdlock(&sys_rwlock)))\n            return (0);\n        else\n        {\n            pthread_mutex_unlock(&INTER->i_mutex);\n            return (ret);\n        }\n    }\n    else return (ret);\n#else\n    return pthread_mutex_trylock(&sys_mutex);\n#endif\n}\n\n#else /* PDTHREADS */\n\n#ifdef TEST_LOCKING /* run standalone Pd with this to find deadlocks */\nstatic int amlocked;\nvoid sys_lock(void)\n{\n    if (amlocked) bug(\"duplicate lock\");\n    amlocked = 1;\n}\n\nvoid sys_unlock(void)\n{\n    if (!amlocked) bug(\"duplicate unlock\");\n    amlocked = 0;\n}\n#else\nvoid sys_lock(void) {}\nvoid sys_unlock(void) {}\n#endif\nvoid pd_globallock(void) {}\nvoid pd_globalunlock(void) {}\n\n#endif /* PDTHREADS */\n"
  },
  {
    "path": "libs/libpd/pure-data/src/s_inter_gui.c",
    "content": "/* Copyright (c) 2022 IOhannes m zmölnig.\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* Pd side of the Pd/Pd-gui interface. */\n\n#include \"m_pd.h\"\n#include \"s_stuff.h\"\n#include <stdarg.h>\n#include <string.h>\n\n/* NULL-terminated */\n#define GUI_VMESS__END     0\n/* use space to structure the format-string */\n#define GUI_VMESS__IGNORE  ' '\n\n/* floating point number (automatically promoted to double) */\n#define GUI_VMESS__FLOAT   'f'\n/* fixed point number (automatically promoted to int) */\n#define GUI_VMESS__INT     'i'\n\n/* 0-terminated strings are untrusted and need escaping */\n#define GUI_VMESS__STRING  's'\n/* the same goes for char arrays (\"pascalstring\") */\n#define GUI_VMESS__PASCALSTRING 'p'\n/* rawstrings don't need encoding */\n#define GUI_VMESS__RAWSTRING  'r'\n\n/* takes an int-representation of a 24bit RGB color */\n#define GUI_VMESS__COLOR      'k'\n\n/* generic pointer; this is not actually used, and probably should stay that way */\n#define GUI_VMESS__POINTER    'x'\n/* a Pd-object */\n#define GUI_VMESS__OBJECT     'o'\n\n/* takes a Pd-message of the form \"t_symbol*, int argc, t_atom*argv\" */\n#define GUI_VMESS__MESSAGE    'm'\n\n/* arrays are capitalized */\n#define GUI_VMESS__ATOMS          'a' /* flat list of atoms */\n#define GUI_VMESS__ATOMARRAY      'A'\n#define GUI_VMESS__FLOATARRAY     'F'\n#define GUI_VMESS__FLOATWORDS     'w' /* flat list of float words */\n#define GUI_VMESS__FLOATWORDARRAY 'W'\n#define GUI_VMESS__STRINGARRAY    'S'\n#define GUI_VMESS__RAWSTRINGARRAY 'R'\n\n/* canvases */\n#define GUI_VMESS__CANVAS 'c'\n#define GUI_VMESS__CANVASARRAY 'C'\n\n/* toplevel windows are legacy: this should go away (use 'c' instead) */\n#define GUI_VMESS__WINDOW '^'\n\n/* more ideas for types (the IDs need discussion)\n * - float32 array ('1'), float64 array ('2'): think libpd\n * - symbols ('y'), symbolarray ('Y'): this is just a shorthand for 's',\n *   so probably overkill\n * - t_word array ('W')\n\n * - a continuation char ('+'), to keep formating and values close together:\n *   e.g.  pdgui_vmess(..., \"i+\", 12, \"i+\", 13);\n */\n\n\nstatic PERTHREAD char* s_escbuffer = 0;\nstatic PERTHREAD size_t s_esclength = 0;\n#ifndef GUI_ALLOCCHUNK\n# define GUI_ALLOCCHUNK 8192\n#endif\nstatic char*get_escapebuffer(const char*s, int size)\n{\n    size_t len = (size>0)?size:strlen(s);\n        /* worst case needs escaping each character; AND a terminating \\0... */\n    len = 2*len + 1;\n    if (len > s_esclength) {\n        freebytes(s_escbuffer, s_esclength);\n        s_esclength = GUI_ALLOCCHUNK*(1+len/GUI_ALLOCCHUNK);\n        s_escbuffer = getbytes(s_esclength);\n    }\n    return s_escbuffer;\n}\nstatic const char* str_escape(const char*s, int size)\n{\n    if(!s)\n        return s;\n    if (!get_escapebuffer(s, size))\n        return 0;\n    return pdgui_strnescape(s_escbuffer, s_esclength, s, size);\n}\n\ntypedef struct _val {\n    int type;\n    int size;\n    const char* string;\n    union\n    {\n        double d;\n        int i;\n        const void*p;\n    } value;\n} t_val;\n\n//#define DEBUGME\n/* constructs a string that can be passed on to sys_gui() */\n#ifdef DEBUGME\nstatic void print_val(t_val v) {\n    char str[80];\n    dprintf(2, \"value[%c] \", v.type);\n    switch (v.type) {\n    case GUI_VMESS__ATOMS:\n        atom_string(v.value.p, str, sizeof(str));\n        dprintf(2, \"(atom)%s\", str);\n        break;\n    case GUI_VMESS__FLOAT:\n        dprintf(2, \"(float)%f\", v.value.d);\n        break;\n    case GUI_VMESS__INT:\n        dprintf(2, \"(int)%d\", v.value.i);\n        break;\n    case GUI_VMESS__COLOR:\n        dprintf(2, \"(color)#%06x\", v.value.i & 0xFFFFFF);\n        break;\n    case GUI_VMESS__STRING:\n        dprintf(2, \"(string)\\\"%s\\\"\", v.value.p);\n        break;\n    case GUI_VMESS__PASCALSTRING:\n        dprintf(2, \"(pascalstring)\\\"%.*s\\\"\", v.size, v.value.p);\n        break;\n    case GUI_VMESS__RAWSTRING:\n        dprintf(2, \"(rawstring)\\\"%s\\\"\", v.value.p);\n        break;\n    case GUI_VMESS__WINDOW:\n    case GUI_VMESS__CANVAS:\n        dprintf(2, \"(glist)%p\", v.value.p);\n            /* estimate the size required for the array */\n        break;\n    case GUI_VMESS__ATOMARRAY:\n    case GUI_VMESS__FLOATARRAY:\n    case GUI_VMESS__FLOATWORDS:\n    case GUI_VMESS__FLOATWORDARRAY:\n    case GUI_VMESS__STRINGARRAY:\n    case GUI_VMESS__RAWSTRINGARRAY:\n    case GUI_VMESS__POINTER:\n    case GUI_VMESS__OBJECT:\n    case GUI_VMESS__CANVASARRAY:\n        dprintf(2, \"%dx @%p\", v.size, v.value.p);\n            /* estimate the size required for the array */\n        break;\n    case GUI_VMESS__MESSAGE:\n        dprintf(2, \"(message)%s %d@%p\", v.string, v.size, v.value.p);\n        break;\n    default:\n        break;\n    }\n    dprintf(2, \"\\n\");\n}\n#else\nstatic void print_val(t_val v) { ; }\nint dprintf(int fd, const char* format, ...) { return -1; }\n#endif\n\nstatic void sendatoms(int argc, t_atom*argv, int raw) {\n    int i;\n    for(i=0; i<argc; i++)\n    {\n        t_atom *a=argv++;\n        switch (a->a_type) {\n        default:\n            break;\n        case A_FLOAT:\n                /* HACK: currently ATOMARRAY is only used for setting fonts\n                 * and Tcl/Tk is picky when it receives non-integers as fontsize\n                 */\n            sys_vgui(\"%g \", atom_getfloat(a));\n            break;\n        case A_DOLLAR:\n            if(raw)\n                sys_vgui(\"$%d \", a->a_w.w_index);\n            else\n                sys_vgui(\"{$%d} \", a->a_w.w_index);\n            break;\n        case A_DOLLSYM:\n        case A_SYMBOL:\n            if(raw)\n                sys_vgui(\"%s \", a->a_w.w_symbol->s_name);\n            else\n                sys_vgui(\"{%s} \", str_escape(a->a_w.w_symbol->s_name, 0));\n            break;\n        case A_POINTER:\n            sys_vgui(\"%p \", a->a_w.w_gpointer);\n            break;\n        case A_SEMI:\n            sys_vgui(\"\\\\; \");\n            break;\n        case A_COMMA:\n            if (raw)\n                sys_vgui(\", \");\n            else\n                sys_vgui(\"{,} \");\n            break;\n        }\n    }\n}\n\nstatic int addmess(const t_val *v)\n{\n    int i;\n    char escbuf[MAXPDSTRING];\n        //dprintf(2, \"add-message: \"); print_val(v);\n    switch (v->type) {\n    case GUI_VMESS__IGNORE:\n        break;\n    case GUI_VMESS__FLOAT:\n        sys_vgui(\"%g\", v->value.d);\n        break;\n    case GUI_VMESS__INT:\n        sys_vgui(\"%d\", v->value.i);\n        break;\n    case GUI_VMESS__COLOR:\n        sys_vgui(\"#%06x\", v->value.i & 0xFFFFFF);\n        break;\n    case GUI_VMESS__RAWSTRING:\n        sys_vgui(\"%s\", v->value.p);\n        break;\n    case GUI_VMESS__STRING:\n        sys_vgui(\"{%s}\", str_escape(v->value.p, 0));\n        break;\n    case GUI_VMESS__PASCALSTRING:\n        sys_vgui(\"{%s}\", str_escape(v->value.p, v->size));\n        break;\n    case GUI_VMESS__POINTER:\n    case GUI_VMESS__OBJECT:\n        sys_vgui(\"%p\", v->value.p);\n        break;\n    case GUI_VMESS__MESSAGE:\n        sys_vgui(\"{\");\n        if (v->string)\n            sys_vgui(\"%s \", v->string);\n        else\n            ;\n        sendatoms(v->size, (t_atom*)v->value.p, 1);\n        sys_vgui(\"}\");\n        break;\n    case GUI_VMESS__WINDOW:\n        sys_vgui(\".x%lx\", v->value.p);\n        break;\n    case GUI_VMESS__CANVAS:\n        sys_vgui(\".x%lx.c\", v->value.p);\n        break;\n    case GUI_VMESS__CANVASARRAY:\n    {\n        const t_canvas**data = (const t_canvas**)v->value.p;\n        sys_vgui(\"{\");\n        for(i=0; i<v->size; i++)\n            sys_vgui(\".x%lx.c \", data[i]);\n        sys_vgui(\"}\");\n        break;\n    }\n    case GUI_VMESS__FLOATARRAY:\n    {\n        const t_float*data = (const t_float*)v->value.p;\n        sys_vgui(\"{\");\n        for(i=0; i<v->size; i++)\n            sys_vgui(\"%f \", *data++);\n        sys_vgui(\"}\");\n        break;\n    }\n    case GUI_VMESS__FLOATWORDS:\n    case GUI_VMESS__FLOATWORDARRAY:\n    {\n        const t_word*data = (const t_word*)v->value.p;\n        if (GUI_VMESS__FLOATWORDARRAY == v->type)\n            sys_vgui(\"{\");\n        for(i=0; i<v->size; i++)\n            sys_vgui(\"%g \", data[i].w_float);\n        if (GUI_VMESS__FLOATWORDARRAY == v->type)\n            sys_vgui(\"}\");\n        break;\n    }\n    case GUI_VMESS__RAWSTRINGARRAY:\n    case GUI_VMESS__STRINGARRAY:\n    {\n        const char**data = (const char**)v->value.p;\n        sys_vgui(\"{\");\n        for(i=0; i<v->size; i++)\n        {\n            const char*s=data[i];\n            if (GUI_VMESS__RAWSTRINGARRAY == v->type)\n                sys_vgui(\"%s \", s);\n            else\n                sys_vgui(\"{%s} \", str_escape(s, 0));\n        }\n        sys_vgui(\"}\");\n        break;\n    }\n    case GUI_VMESS__ATOMS:\n    case GUI_VMESS__ATOMARRAY:\n    {\n        if (GUI_VMESS__ATOMARRAY == v->type)\n            sys_vgui(\"{\");\n        sendatoms(v->size, (t_atom*)v->value.p, 0);\n        if (GUI_VMESS__ATOMARRAY == v->type)\n            sys_vgui(\"}\");\n        break;\n    }\n    default:\n        return 1;\n    }\n    return 0;\n}\n\nstatic int va2value(const char fmt, va_list *args, t_val*v) {\n    int result = 1;\n    v->type = fmt;\n    v->size = 1;\n    switch (fmt) {\n    case GUI_VMESS__IGNORE: /* <space> */\n    case '\\n': case '\\t': /* other whitespace */\n        v->type = GUI_VMESS__IGNORE;\n        break;\n    case GUI_VMESS__ATOMS:\n        v->size = va_arg(*args, int);\n        v->value.p = va_arg(*args, t_atom*);\n        break;\n    case GUI_VMESS__FLOAT:\n        v->value.d = va_arg(*args, double);\n        break;\n    case GUI_VMESS__INT:\n    case GUI_VMESS__COLOR:\n        v->value.i = va_arg(*args, int);\n        break;\n    case GUI_VMESS__RAWSTRING:\n    case GUI_VMESS__STRING:\n    case GUI_VMESS__OBJECT:\n    case GUI_VMESS__POINTER:\n    case GUI_VMESS__WINDOW:\n    case GUI_VMESS__CANVAS:\n        v->value.p = va_arg(*args, void*);\n        break;\n    case GUI_VMESS__ATOMARRAY:\n    case GUI_VMESS__CANVASARRAY:\n    case GUI_VMESS__FLOATARRAY:\n    case GUI_VMESS__FLOATWORDS:\n    case GUI_VMESS__FLOATWORDARRAY:\n    case GUI_VMESS__STRINGARRAY:\n    case GUI_VMESS__RAWSTRINGARRAY:\n    case GUI_VMESS__PASCALSTRING:\n        v->size = va_arg(*args, int);\n        v->value.p = va_arg(*args, void*);\n        break;\n    case GUI_VMESS__MESSAGE:\n    {\n        t_symbol*s = va_arg(*args, t_symbol*);\n        v->string = s?s->s_name:0;\n        v->size = va_arg(*args, int);\n        v->value.p = va_arg(*args, void*);\n        break;\n    }\n    default:\n        result = v->size = 0;\n        fprintf(stderr, \"pdgui_vmess: unknown type-ID %d ('%c')\\n\", fmt, fmt);\n        break;\n    }\n    return result;\n}\n\n\nvoid pdgui_vamess(const char* message, const char* format, va_list args_)\n{\n    const char* fmt;\n    char* buf;\n    t_val v;\n    va_list args;\n\n    v.type = GUI_VMESS__RAWSTRING;\n    v.size = 1;\n    v.value.p = message;\n\n    if(message) {\n        addmess(&v);\n        sys_vgui(\"%s\", \" \");\n    }\n\n    va_copy(args, args_);\n        /* iterate over the format-string and add elements */\n    for(fmt = format; *fmt; fmt++) {\n        if(va2value(*fmt, &args, &v) < 1)\n            continue;\n        addmess(&v);\n        if(GUI_VMESS__IGNORE != v.type)\n            sys_vgui(\"%s\", \" \");\n    }\n    va_end(args);\n}\nvoid pdgui_endmess(void)\n{\n    t_val v;\n    v.type = GUI_VMESS__RAWSTRING;\n    v.size = 1;\n    v.value.p = \";\\n\";\n    addmess(&v);\n}\n\n\n/* constructs a string that can be passed on to sys_gui() */\n/* TODO: shouldn't this have a pointer to t_pdinstance? */\nvoid pdgui_vmess(const char* message, const char* format, ...)\n{\n    va_list args;\n    if (!sys_havegui())return;\n    if(!format)\n    {\n        if (message)\n            sys_vgui(\"%s;\\n\", message);\n        return;\n    }\n    va_start(args, format);\n    pdgui_vamess(message, format, args);\n    va_end(args);\n    pdgui_endmess();\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/s_loader.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n#if !defined (HAVE_LIBDL) && HAVE_DLOPEN\n# define HAVE_LIBDL 1\n#endif\n\n#if HAVE_LIBDL\n# include <dlfcn.h>\n#endif\n#ifdef HAVE_UNISTD_H\n#include <stdlib.h>\n#include <unistd.h>\n#include <sys/types.h>\n#endif\n#ifdef _WIN32\n#include <io.h>\n#include <windows.h>\n#endif\n#ifdef __APPLE__\n#include <mach-o/dyld.h>\n#endif\n#include <string.h>\n#include \"m_pd.h\"\n#include \"s_stuff.h\"\n#include <stdio.h>\n#include <sys/stat.h>\n#include \"m_private_utils.h\"\n#ifdef _MSC_VER  /* This is only for Microsoft's compiler, not cygwin, e.g. */\n#define stat _stat\n#endif\n\ntypedef void (*t_xxx)(void);\n\n/* naming convention for externs.  The names are kept distinct for those\nwho wish to make \"fat\" externs compiled for many platforms.  Less specific\nfallbacks are provided, primarily for back-compatibility; these suffice if\nyou are building a package which will run with a single set of compiled\nobjects.  The specific name is the letter b, l, d, or m for  BSD, linux,\ndarwin, or microsoft, followed by a more specific string, either \"fat\" for\na fat binary or an indication of the instruction set. */\n\n#ifdef __APPLE__\n# define FAT_BINARIES 1\n#endif\n\n#define STR(s) #s\n#define STRINGIFY(s) STR(s)\n\n\n#if defined(__x86_64__) || defined(_M_X64)\n# define ARCHEXT \"amd64\"\n#elif defined(__i386__) || defined(_M_IX86)\n# define ARCHEXT \"i386\"\n#elif defined(__arm__)\n# define ARCHEXT \"arm\"\n#elif defined(__aarch64__)\n# define ARCHEXT \"arm64\"\n#elif defined(__ppc__)\n# define ARCHEXT \"ppc\"\n#endif\n\n#ifdef ARCHEXT\n#define ARCHDLLEXT(prefix) prefix ARCHEXT ,\n#else\n#define ARCHDLLEXT(prefix)\n#endif\n\n\n#if defined(_WIN32) || defined(__CYGWIN__)\n# define SYSTEMEXT \".dll\"\n#else\n# define SYSTEMEXT \".so\"\n#endif\n\nstatic const char*sys_dllextent_base[] = {\n#if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__GNU__)\n    ARCHDLLEXT(\".l_\")\n# if defined(__x86_64__) || defined(_M_X64)\n    \".l_ia64\",      /* incorrect but probably in wide use */\n# endif\n    \".pd_linux\",\n#elif defined(__APPLE__)\n    ARCHDLLEXT(\".d_\")\n    \".d_fat\",\n    \".pd_darwin\",\n#elif defined(_WIN32) || defined(__CYGWIN__)\n    ARCHDLLEXT(\".m_\")\n    SYSTEMEXT,\n#endif\n    0\n    };\n\nstatic const char**sys_dllextent = 0;\nstatic size_t num_dllextents = 0;\nstatic void add_dllextension(const char*ext) {\n    const char**extensions;\n    if(ext) {\n        /* prevent duplicate entries */\n        int i;\n        for(i=0; i<num_dllextents; i++) {\n            if(!strcmp(ext, sys_dllextent[i]))\n                return;\n        }\n    }\n    extensions = resizebytes(sys_dllextent\n        , sizeof(*sys_dllextent) * num_dllextents\n        , sizeof(*sys_dllextent) * (num_dllextents+1)\n        );\n    if(!extensions)\n        return;\n    sys_dllextent = extensions;\n    sys_dllextent[num_dllextents] = ext;\n    num_dllextents++;\n}\n\nconst char*sys_deken_specifier(char*buf, size_t bufsize, int include_floatsize, int cpu);\n\nstatic char*add_deken_extension(const char*systemext, int float_agnostic, int cpu)\n{\n    char extbuf[MAXPDSTRING];\n    char*ext = 0;\n\n    if(!(sys_deken_specifier(extbuf, sizeof(extbuf), float_agnostic, cpu)))\n        return 0;\n\n    ext = getbytes(MAXPDSTRING);\n    if(!ext)\n        return 0;\n    ext[MAXPDSTRING-1] = 0;\n\n    if(snprintf(ext, MAXPDSTRING-1, \".%s%s\", extbuf, systemext) > 0)\n        add_dllextension(ext);\n    else\n    {\n        freebytes(ext, MAXPDSTRING);\n        ext = 0;\n    }\n    return ext;\n}\n\n    /* get an array of dll-extensions */\nconst char**sys_get_dllextensions(void)\n{\n    if(!sys_dllextent) {\n        const char *extraext = 0;\n#if defined EXTERNAL_EXTENSION\n        do {\n            /* the EXTERNAL_EXTENSION might be surrounded by single-quotes\n             * to prevent macro-expansion within the macro\n             * if so, get rid of them\n             */\n            unsigned int i,j;\n            static char extern_extension[MAXPDSTRING];\n            strcpy(extern_extension, STRINGIFY(EXTERNAL_EXTENSION));\n            extern_extension[MAXPDSTRING-1] = 0;\n            for(i=0,j=0; i<MAXPDSTRING; i++) {\n                if(!extern_extension[i]) {\n                    extern_extension[j] = 0;\n                    break;\n                }\n                switch(extern_extension[i]) {\n                case '\\'': break;\n                default:\n                    extern_extension[j++] = extern_extension[i];\n                }\n            }\n            extraext = extern_extension;\n        } while (0);\n#endif\n\n        int i, cpu;\n\n            /* create deken-based extensions */\n        for(cpu=0; ; cpu++)\n        {\n            /* iterate over compatible CPUs  */\n            if (!add_deken_extension(SYSTEMEXT, 0, cpu))\n                break;\n            if (!add_deken_extension(SYSTEMEXT, 1, cpu))\n                break;\n        }\n#if FAT_BINARIES\n        add_deken_extension(SYSTEMEXT, 0, -1);\n        add_deken_extension(SYSTEMEXT, 1, -1);\n#endif\n\n        if(extraext) {\n            /* check if the extra-extension is part of sys_dllextent_base\n             * and if so drop it as redundant.\n             */\n            for(i=0; i<sizeof(sys_dllextent_base)/sizeof(*sys_dllextent_base); i++) {\n                if(!sys_dllextent_base[i])\n                    continue;\n                if(!strcmp(sys_dllextent_base[i], extraext)) {\n                    extraext=0;\n                    break;\n                }\n            }\n        }\n        if(extraext)\n            add_dllextension(extraext);\n\n#if PD_FLOATSIZE == 32\n            /* and add the legacy extensions */\n        for(i=0; i<sizeof(sys_dllextent_base)/sizeof(*sys_dllextent_base); i++)\n        {\n            if(sys_dllextent_base[i])\n                add_dllextension(sys_dllextent_base[i]);\n        }\n#endif\n            /* 0-terminate the extension list */\n        add_dllextension(0);\n    }\n    return sys_dllextent;\n}\n\n    /* maintain list of loaded modules to avoid repeating loads */\ntypedef struct _loadedlist\n{\n    struct _loadedlist *ll_next;\n    t_symbol *ll_name;\n} t_loadlist;\n\nstatic t_loadlist *sys_loaded;\nint sys_onloadlist(const char *classname) /* return true if already loaded */\n{\n    t_symbol *s = gensym(classname);\n    t_loadlist *ll;\n    for (ll = sys_loaded; ll; ll = ll->ll_next)\n        if (ll->ll_name == s)\n            return (1);\n    return (0);\n}\n\n     /* add to list of loaded modules */\nvoid sys_putonloadlist(const char *classname)\n{\n    t_loadlist *ll = (t_loadlist *)getbytes(sizeof(*ll));\n    ll->ll_name = gensym(classname);\n    ll->ll_next = sys_loaded;\n    sys_loaded = ll;\n    /* post(\"put on list %s\", classname); */\n}\n\nvoid class_set_extern_dir(t_symbol *s);\n\nstatic int sys_do_load_abs(t_canvas *canvas, const char *objectname,\n    const char *path);\n\n\nstatic int sys_do_load_lib_from_file(int fd,\n    const char*objectname,\n    const char*dirbuf,\n    const char*nameptr,\n    const char*symname) {\n    char filename[MAXPDSTRING];\n    t_xxx makeout = NULL;\n#ifdef _WIN32\n    HINSTANCE dlobj;\n#else\n    void*dlobj = NULL;\n#endif\n        /* close dangling filedescriptor */\n    close(fd);\n\n        /* attempt to open the library and call the setup function */\n\n\n    class_set_extern_dir(gensym(dirbuf));\n\n        /* rebuild the absolute pathname */\n    strncpy(filename, dirbuf, MAXPDSTRING);\n    filename[MAXPDSTRING-2] = 0;\n    strcat(filename, \"/\");\n    strncat(filename, nameptr, MAXPDSTRING-strlen(filename));\n    filename[MAXPDSTRING-1] = 0;\n\n#ifdef _WIN32\n    {\n        char dirname[MAXPDSTRING], *s, *basename;\n        sys_bashfilename(filename, filename);\n        /* set the dirname as DllDirectory, meaning in the path for\n           loading other DLLs so that dependent libraries can be included\n           in the same folder as the external. SetDllDirectory() needs a\n           minimum supported version of Windows XP SP1 for\n           SetDllDirectory, so WINVER must be 0x0502 */\n        strncpy(dirname, filename, MAXPDSTRING);\n        s = strrchr(dirname, '\\\\');\n        basename = s;\n        if (s && *s)\n          *s = '\\0';\n        if (!SetDllDirectory(dirname))\n           pd_error(0, \"could not set '%s' as DllDirectory(), '%s' might not load.\",\n                 dirname, basename);\n        /* now load the DLL for the external */\n        dlobj = LoadLibrary(filename);\n        if (!dlobj)\n        {\n            wchar_t wbuf[MAXPDSTRING];\n            char buf[MAXPDSTRING];\n            DWORD count, err = GetLastError();\n            count = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,\n                0, err, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), wbuf, MAXPDSTRING, NULL);\n            if (!count || !WideCharToMultiByte(CP_UTF8, 0, wbuf, count+1, buf, MAXPDSTRING, 0, 0))\n                *buf = '\\0';\n            pd_error(0, \"%s: %s (%d)\", filename, buf, err);\n        } else {\n            makeout = (t_xxx)GetProcAddress(dlobj, symname);\n            if (!makeout)\n                makeout = (t_xxx)GetProcAddress(dlobj, \"setup\");\n        }\n        SetDllDirectory(NULL); /* reset DLL dir to nothing */\n    }\n#elif defined(HAVE_LIBDL)\n    {\n        dlobj = dlopen(filename, RTLD_NOW | RTLD_GLOBAL);\n        if (!dlobj)\n        {\n            pd_error(0, \"%s:%s\", filename, dlerror());\n        } else {\n            makeout = (t_xxx)dlsym(dlobj,  symname);\n            if(!makeout)\n                makeout = (t_xxx)dlsym(dlobj,  \"setup\");\n        }\n    }\n#else\n#warning \"No dynamic loading mechanism specified, \\\nlibdl or WIN32 required for loading externals!\"\n#endif\n\n    if(makeout)\n        (*makeout)();\n    else if (dlobj)\n        pd_error(0, \"load_object: Symbol \\\"%s\\\" not found in \\\"%s\\\"\", symname, filename);\n\n    class_set_extern_dir(&s_);\n\n    return (makeout)?1:0;\n}\n\nstatic int sys_do_load_lib(t_canvas *canvas, const char *objectname,\n    const char *path)\n{\n    char symname[MAXPDSTRING], filename[MAXPDSTRING], dirbuf[MAXPDSTRING],\n        *nameptr;\n    const char**dllextent;\n    const char *classname, *cnameptr;\n    void *dlobj;\n    t_xxx makeout = NULL;\n    int i, hexmunge = 0, fd;\n        /* NULL-path is only used as a last resort,\n           but we have already tried all paths */\n    if(!path)return (0);\n\n    if ((classname = strrchr(objectname, '/')))\n        classname++;\n    else classname = objectname;\n    for (i = 0, cnameptr = classname; i < MAXPDSTRING-7 && *cnameptr;\n        cnameptr++)\n    {\n        char c = *cnameptr;\n        if ((c>='0' && c<='9') || (c>='A' && c<='Z')||\n           (c>='a' && c<='z' )|| c == '_')\n        {\n            symname[i] = c;\n            i++;\n        }\n            /* trailing tilde becomes \"_tilde\" */\n        else if (c == '~' && cnameptr[1] == 0)\n        {\n            strcpy(symname+i, \"_tilde\");\n            i += strlen(symname+i);\n        }\n        else /* anything you can't put in a C symbol is sprintf'ed in hex */\n        {\n            sprintf(symname+i, \"0x%02x\", c);\n            i += strlen(symname+i);\n            hexmunge = 1;\n        }\n    }\n    symname[i] = 0;\n    if (hexmunge)\n    {\n        memmove(symname+6, symname, strlen(symname)+1);\n        strncpy(symname, \"setup_\", 6);\n    }\n    else strcat(symname, \"_setup\");\n\n#if 0\n    fprintf(stderr, \"lib: %s\\n\", classname);\n#endif\n        /* try looking in the path for (objectname).(sys_dllextent) ... */\n    for(dllextent=sys_get_dllextensions(); *dllextent; dllextent++)\n    {\n        if ((fd = sys_trytoopenone(path, objectname, *dllextent,\n            dirbuf, &nameptr, MAXPDSTRING, 1)) >= 0)\n            if(sys_do_load_lib_from_file(fd, objectname, dirbuf, nameptr, symname))\n                return 1;\n    }\n        /* next try (objectname)/(classname).(sys_dllextent) ... */\n    strncpy(filename, objectname, MAXPDSTRING);\n    filename[MAXPDSTRING-2] = 0;\n    strcat(filename, \"/\");\n    strncat(filename, classname, MAXPDSTRING-strlen(filename));\n    filename[MAXPDSTRING-1] = 0;\n    for(dllextent=sys_get_dllextensions(); *dllextent; dllextent++)\n    {\n        if ((fd = sys_trytoopenone(path, filename, *dllextent,\n            dirbuf, &nameptr, MAXPDSTRING, 1)) >= 0)\n            if(sys_do_load_lib_from_file(fd, objectname, dirbuf, nameptr, symname))\n                return 1;\n    }\n#ifdef ANDROID\n    /* Android libs have a 'lib' prefix, '.so' suffix and don't allow ~ */\n    char libname[MAXPDSTRING] = \"lib\";\n    strncat(libname, objectname, MAXPDSTRING - 4);\n    int len = strlen(libname);\n    if (libname[len-1] == '~' && len < MAXPDSTRING - 6) {\n        strcpy(libname+len-1, \"_tilde\");\n    }\n    if ((fd = sys_trytoopenone(path, libname, \".so\",\n        dirbuf, &nameptr, MAXPDSTRING, 1)) >= 0)\n            if(sys_do_load_lib_from_file(fd, objectname, dirbuf, nameptr, symname))\n                return 1;\n#endif\n    return (0);\n}\n\n\n/* linked list of loaders */\ntypedef struct loader_queue {\n    loader_t loader;\n    struct loader_queue *next;\n} loader_queue_t;\n\nstatic loader_queue_t loaders = {sys_do_load_lib, NULL};\n\n/* register class loader function */\nvoid sys_register_loader(loader_t loader)\n{\n    loader_queue_t *q = &loaders;\n    while (1)\n    {\n        if (q->loader == loader)    /* already loaded - nothing to do */\n            return;\n        else if (q->next)\n            q = q->next;\n        else\n        {\n            q->next = (loader_queue_t *)getbytes(sizeof(loader_queue_t));\n            q->next->loader = loader;\n            q->next->next = NULL;\n            break;\n        }\n    }\n}\n\n#include \"g_canvas.h\"\n\n/* the data passed to the iter-function */\nstruct _loadlib_data\n{\n    t_canvas *canvas;\n    const char *classname;\n    int ok;\n};\n\nint sys_loadlib_iter(const char *path, struct _loadlib_data *data)\n{\n    int ok = 0;\n    loader_queue_t *q;\n    for(q = &loaders; q; q = q->next)\n        if ((ok = q->loader(data->canvas, data->classname, path)))\n            break;\n    /* if all loaders failed, try to load as abstraction */\n    if (!ok)\n        ok = sys_do_load_abs(data->canvas, data->classname, path);\n    data->ok = ok;\n    return (ok == 0);\n}\n\nint sys_load_lib(t_canvas *canvas, const char *classname)\n{\n    int dspstate = canvas_suspend_dsp();\n    struct _loadlib_data data;\n    data.canvas = canvas;\n    data.ok = 0;\n\n    if (sys_onloadlist(classname))\n        return (1); /* if lib is already loaded, dismiss. */\n\n        /* if classname is absolute, try this first */\n    if (sys_isabsolutepath(classname))\n    {\n            /* this is just copied from sys_open_absolute()\n               LATER avoid code duplication */\n        char dirbuf[MAXPDSTRING], *z = strrchr(classname, '/');\n        int dirlen;\n        if (!z)\n            return (0);\n        dirlen = (int)(z - classname);\n        if (dirlen > MAXPDSTRING-1)\n            dirlen = MAXPDSTRING-1;\n        strncpy(dirbuf, classname, dirlen);\n        dirbuf[dirlen] = 0;\n        data.classname=classname+(dirlen+1);\n        sys_loadlib_iter(dirbuf, &data);\n    }\n    data.classname = classname;\n    if(!data.ok && !sys_isabsolutepath(classname)) /* don't iterate if classname is absolute */\n        canvas_path_iterate(canvas, (t_canvas_path_iterator)sys_loadlib_iter,\n            &data);\n\n    /* if loaders failed so far, we try a last time without a PATH\n     * let the loaders search wherever they want */\n    if (!data.ok)\n        sys_loadlib_iter(0, &data);\n\n    if(data.ok)\n      sys_putonloadlist(classname);\n\n\n    canvas_resume_dsp(dspstate);\n    return data.ok;\n}\n\nint sys_run_scheduler(const char *externalschedlibname,\n    const char *sys_extraflagsstring)\n{\n    typedef int (*t_externalschedlibmain)(const char *);\n    t_externalschedlibmain externalmainfunc;\n    char filename[MAXPDSTRING];\n    const char**dllextent;\n    for(dllextent=sys_get_dllextensions(); *dllextent; dllextent++)\n    {\n        struct stat statbuf;\n        snprintf(filename, sizeof(filename), \"%s%s\", externalschedlibname,\n            *dllextent);\n        sys_bashfilename(filename, filename);\n        if(!stat(filename, &statbuf))\n            break;\n    }\n\n#ifdef _WIN32\n    {\n        HINSTANCE ntdll = LoadLibrary(filename);\n        if (!ntdll)\n        {\n            fprintf(stderr, \"%s: couldn't load external scheduler\\n\", filename);\n            pd_error(0, \"%s: couldn't load external scheduler\", filename);\n            return (1);\n        }\n        externalmainfunc =\n            (t_externalschedlibmain)GetProcAddress(ntdll, \"pd_extern_sched\");\n        if (!externalmainfunc)\n            externalmainfunc =\n                (t_externalschedlibmain)GetProcAddress(ntdll, \"main\");\n    }\n#elif HAVE_LIBDL\n    {\n        void *dlobj;\n        dlobj = dlopen(filename, RTLD_NOW | RTLD_GLOBAL);\n        if (!dlobj)\n        {\n            pd_error(0, \"%s: %s\", filename, dlerror());\n            fprintf(stderr, \"dlopen failed for %s: %s\\n\", filename, dlerror());\n            return (1);\n        }\n        externalmainfunc = (t_externalschedlibmain)dlsym(dlobj,\n            \"pd_extern_sched\");\n    }\n#else\n    return (0);\n#endif\n    if (externalmainfunc)\n        return((*externalmainfunc)(sys_extraflagsstring));\n    else\n    {\n        fprintf(stderr, \"%s: couldn't find pd_extern_sched() or main()\\n\",\n            filename);\n        return (0);\n    }\n}\n\n\n/* abstraction loading */\nvoid canvas_popabstraction(t_canvas *x);\nint pd_setloadingabstraction(t_symbol *sym);\n\nstatic t_pd *do_create_abstraction(t_symbol*s, int argc, t_atom *argv)\n{\n    /*\n     * TODO: check if the there is a binbuf cached for <canvas::symbol>\n        and use that instead.  We'll have to invalidate the cache once we\n        are done (either with a clock_delay(0) or something else)\n     */\n    if (!pd_setloadingabstraction(s))\n    {\n        const char *objectname = s->s_name;\n        char dirbuf[MAXPDSTRING], classslashclass[MAXPDSTRING], *nameptr;\n        t_glist *glist = (t_glist *)canvas_getcurrent();\n        t_canvas *canvas = (t_canvas*)glist_getcanvas(glist);\n        int fd = -1;\n\n        t_pd *was = s__X.s_thing;\n        snprintf(classslashclass, MAXPDSTRING, \"%s/%s\", objectname, objectname);\n        if ((fd = canvas_open(canvas, objectname, \".pd\",\n                  dirbuf, &nameptr, MAXPDSTRING, 0)) >= 0 ||\n            (fd = canvas_open(canvas, objectname, \".pat\",\n                  dirbuf, &nameptr, MAXPDSTRING, 0)) >= 0 ||\n            (fd = canvas_open(canvas, classslashclass, \".pd\",\n                  dirbuf, &nameptr, MAXPDSTRING, 0)) >= 0)\n        {\n            close(fd);\n            canvas_setargs(argc, argv);\n\n            binbuf_evalfile(gensym(nameptr), gensym(dirbuf));\n            if (s__X.s_thing && was != s__X.s_thing)\n                canvas_popabstraction((t_canvas *)(s__X.s_thing));\n            else s__X.s_thing = was;\n            canvas_setargs(0, 0);\n            return (pd_this->pd_newest);\n        }\n            /* otherwise we couldn't do it; just return 0 */\n    }\n    else pd_error(0, \"%s: can't load abstraction within itself\\n\", s->s_name);\n    pd_this->pd_newest = 0;\n    return (0);\n}\n\n/* search for abstraction; register a creator if found */\nstatic int sys_do_load_abs(t_canvas *canvas, const char *objectname,\n    const char *path)\n{\n    int fd;\n    static t_gobj*abstraction_classes = 0;\n    char dirbuf[MAXPDSTRING], classslashclass[MAXPDSTRING], *nameptr;\n        /* NULL-path is only used as a last resort,\n           but we have already tried all paths */\n    if (!path) return (0);\n\n    snprintf(classslashclass, MAXPDSTRING, \"%s/%s\", objectname, objectname);\n    if ((fd = sys_trytoopenone(path, objectname, \".pd\",\n              dirbuf, &nameptr, MAXPDSTRING, 1)) >= 0 ||\n        (fd = sys_trytoopenone(path, objectname, \".pat\",\n              dirbuf, &nameptr, MAXPDSTRING, 1)) >= 0 ||\n        (fd = sys_trytoopenone(path, classslashclass, \".pd\",\n              dirbuf, &nameptr, MAXPDSTRING, 1)) >= 0)\n    {\n        t_class*c=0;\n        close(fd);\n            /* found an abstraction, now register it as a new pseudo-class */\n        class_set_extern_dir(gensym(dirbuf));\n        if((c=class_new(gensym(objectname),\n                        (t_newmethod)do_create_abstraction, 0,\n                        0, 0, A_GIMME, 0)))\n        {\n                /* store away the newly created class, maybe we will need it one day */\n            t_gobj*absclass=0;\n            absclass=t_getbytes(sizeof(*absclass));\n            absclass->g_pd=c;\n            absclass->g_next=abstraction_classes;\n            abstraction_classes=absclass;\n        }\n        class_set_extern_dir(&s_);\n\n        return (1);\n    }\n    return (0);\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/s_main.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette and others.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n#include \"m_pd.h\"\n#include \"m_imp.h\"\n#include \"s_stuff.h\"\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <limits.h>\n#include <string.h>\n#include <stdio.h>\n#include <fcntl.h>\n#include <stdlib.h>\n\n#ifdef HAVE_UNISTD_H\n#include <unistd.h>\n#endif\n#ifdef _WIN32\n#include <io.h>\n#include <windows.h>\n#include <winbase.h>\n#endif\n#ifdef __APPLE__\n#include <mach-o/dyld.h> // for _NSGetExecutablePath\n#endif\n#include \"m_private_utils.h\"\n\n#define stringify(s) str(s)\n#define str(s) #s\n\nchar *pd_version = \"Pd-\" stringify(PD_MAJOR_VERSION) \".\" \\\nstringify(PD_MINOR_VERSION) \".\" stringify(PD_BUGFIX_VERSION) \"\\\n (\" stringify(PD_TEST_VERSION) \")\";\n\nchar pd_compiletime[] = __TIME__;\nchar pd_compiledate[] = __DATE__;\n\nvoid pd_init(void);\nint sys_argparse(int argc, const char **argv);\nvoid sys_findprogdir(const char *progname);\nvoid sys_setsignalhandlers(void);\nint sys_startgui(const char *guipath);\nvoid sys_setrealtime(const char *guipath);\nint m_mainloop(void);\nint m_batchmain(void);\nvoid sys_addhelppath(char *p);\n#ifdef USEAPI_ALSA\nvoid alsa_adddev(const char *name);\n#endif\nint sys_oktoloadfiles(int done);\n\nint sys_debuglevel;\nint sys_verbose;\nint sys_noloadbang;\nstatic int sys_dontstartgui;\nint sys_hipriority = -1;    /* -1 = not specified; 0 = no; 1 = yes */\nint sys_guisetportnumber;   /* if started from the GUI, this is the port # */\nint sys_nosleep = 0;  /* skip all \"sleep\" calls and spin instead */\nint sys_defeatrt;       /* flag to cancel real-time */\nt_symbol *sys_flags;    /* more command-line flags */\n\nconst char *sys_guicmd;\nt_symbol *sys_libdir;\n\ntypedef struct _patchlist\n{\n    struct _patchlist *pl_next;\n    char *pl_file;\n    char *pl_args;\n} t_patchlist;\n\nstatic t_patchlist *sys_openlist;\nstatic t_namelist *sys_messagelist;\nstatic int sys_version;\nint sys_oldtclversion;      /* hack to warn g_rtext.c about old text sel */\n\nint sys_nmidiout = -1;\nint sys_nmidiin = -1;\nint sys_midiindevlist[MAXMIDIINDEV] = {1};\nint sys_midioutdevlist[MAXMIDIOUTDEV] = {1};\n\n#if __APPLE__\nchar sys_font[100] = \"Menlo\"; /* hack until DVSM bug is fixed on macOS 10.15+ */\nchar sys_fontweight[10] = \"normal\";\n#else\nchar sys_font[100] = \"DejaVu Sans Mono\";\nchar sys_fontweight[10] = \"bold\";\n#endif\n\nstatic int sys_listplease;\n\nint sys_externalschedlib;\nchar sys_externalschedlibname[MAXPDSTRING];\nstatic int sys_batch;\nint sys_extraflags;\nchar sys_extraflagsstring[MAXPDSTRING];\nint sys_run_scheduler(const char *externalschedlibname,\n    const char *sys_extraflagsstring);\nint sys_noautopatch;    /* temporary hack to defeat new 0.42 editing */\n\nt_sample *get_sys_soundout() { return STUFF->st_soundout; }\nt_sample *get_sys_soundin() { return STUFF->st_soundin; }\ndouble *get_sys_time_per_dsp_tick() { return &STUFF->st_time_per_dsp_tick; }\nint *get_sys_schedblocksize() { return &STUFF->st_schedblocksize; }\ndouble *get_sys_time() { return &pd_this->pd_systime; }\nt_float *get_sys_dacsr() { return &STUFF->st_dacsr; }\nint *get_sys_schedadvance() { return &sys_schedadvance; }\nt_namelist *sys_searchpath;  /* so old versions of GEM might compile */\n\ntypedef struct _fontinfo\n{\n    int fi_pointsize;\n    int fi_width;\n    int fi_height;\n} t_fontinfo;\n\n    /* these give the nominal point size and maximum height of the characters\n    in the six fonts.  */\n\nstatic t_fontinfo sys_fontspec[] = {\n    {8, 5, 11}, {10, 6, 13}, {12, 7, 16},\n    {16, 10, 19}, {24, 14, 29}, {36, 22, 44}};\n#define NFONT (sizeof(sys_fontspec)/sizeof(*sys_fontspec))\n#define NZOOM 2\nstatic t_fontinfo sys_gotfonts[NZOOM][NFONT];\n\n/* here are the actual font size structs on msp's systems:\nMSW:\nfont 8 5 9 8 5 11\nfont 10 7 13 10 6 13\nfont 12 9 16 14 8 16\nfont 16 10 20 16 10 18\nfont 24 15 25 16 10 18\nfont 36 25 42 36 22 41\n\nlinux:\nfont 8 5 9 8 5 9\nfont 10 7 13 12 7 13\nfont 12 9 16 14 9 15\nfont 16 10 20 16 10 19\nfont 24 15 25 24 15 24\nfont 36 25 42 36 22 41\n*/\n\nstatic int sys_findfont(int fontsize)\n{\n    unsigned int i;\n    t_fontinfo *fi;\n    for (i = 0, fi = sys_fontspec; i < (NFONT-1); i++, fi++)\n        if (fontsize < fi[1].fi_pointsize) return (i);\n    return ((NFONT-1));\n}\n\nint sys_nearestfontsize(int fontsize)\n{\n    return (sys_fontspec[sys_findfont(fontsize)].fi_pointsize);\n}\n\nint sys_hostfontsize(int fontsize, int zoom)\n{\n    zoom = (zoom < 1 ? 1 : (zoom > NZOOM ? NZOOM : zoom));\n    return (sys_gotfonts[zoom-1][sys_findfont(fontsize)].fi_pointsize);\n}\n\nint sys_zoomfontwidth(int fontsize, int zoomarg, int worstcase)\n{\n    int zoom = (zoomarg < 1 ? 1 : (zoomarg > NZOOM ? NZOOM : zoomarg)), ret;\n    if (worstcase)\n        ret = zoom * sys_fontspec[sys_findfont(fontsize)].fi_width;\n    else ret = sys_gotfonts[zoom-1][sys_findfont(fontsize)].fi_width;\n    return (ret < 1 ? 1 : ret);\n}\n\nint sys_zoomfontheight(int fontsize, int zoomarg, int worstcase)\n{\n    int zoom = (zoomarg < 1 ? 1 : (zoomarg > NZOOM ? NZOOM : zoomarg)), ret;\n    if (worstcase)\n        ret = (zoom * sys_fontspec[sys_findfont(fontsize)].fi_height);\n    else ret = sys_gotfonts[zoom-1][sys_findfont(fontsize)].fi_height;\n    return (ret < 1 ? 1 : ret);\n}\n\nint sys_fontwidth(int fontsize) /* old version for extern compatibility */\n{\n    return (sys_zoomfontwidth(fontsize, 1, 0));\n}\n\nint sys_fontheight(int fontsize)\n{\n    return (sys_zoomfontheight(fontsize, 1, 0));\n}\n\nint sys_defaultfont;\n#define DEFAULTFONT 12\n\nstatic t_patchlist * patchlist_append(t_patchlist *listwas,\n    const char *files, const char *args)\n{\n    t_namelist *nl, *nl2;\n    nl = namelist_append_files(0, files);\n    for (nl2 = nl; nl2; nl2 = nl2->nl_next)\n    {\n        t_patchlist *pl, *pl2;\n        pl = (t_patchlist *)(getbytes(sizeof(*pl)));\n        pl->pl_next = 0;\n        pl->pl_file = (char *)getbytes(strlen(nl2->nl_string) + 1);\n        strcpy(pl->pl_file, nl2->nl_string);\n        if (args)\n        {\n            pl->pl_args = (char *)getbytes(strlen(args) + 1);\n            strcpy(pl->pl_args, args);\n        }\n        else pl->pl_args = 0;\n        if (!listwas)\n            listwas = pl;\n        else\n        {\n            for (pl2 = listwas; pl2->pl_next; pl2 = pl2->pl_next) ;\n            pl2->pl_next = pl;\n        }\n    }\n    namelist_free(nl);\n    return (listwas);\n}\n\nstatic void patchlist_free(t_patchlist *list)\n{\n    t_patchlist *pl, *pl2;\n    for (pl = list; pl; pl = pl2)\n    {\n        pl2 = pl->pl_next;\n        freebytes(pl->pl_file, strlen(pl->pl_file) + 1);\n        if (pl->pl_args)\n            freebytes(pl->pl_args, strlen(pl->pl_args) + 1);\n        freebytes(pl, sizeof(*pl));\n    }\n}\n\nstatic void openit(const char *dirname, const char *filename, const char *args)\n{\n    char dirbuf[MAXPDSTRING], *nameptr;\n    int fd = open_via_path(dirname, filename, \"\", dirbuf, &nameptr,\n        MAXPDSTRING, 0);\n    if (fd >= 0)\n    {\n        close (fd);\n        if (args && *args)\n        {\n            t_binbuf *b1 = binbuf_new(), *b2 = binbuf_new();\n            binbuf_text(b1, args, strlen(args));\n            binbuf_addbinbuf(b2, b1); // bash semis, commas and dollars\n            canvas_setargs(binbuf_getnatom(b2), binbuf_getvec(b2));\n            binbuf_free(b1);\n            binbuf_free(b2);\n        }\n        glob_evalfile(0, gensym(nameptr), gensym(dirbuf));\n    }\n    else\n        pd_error(0, \"%s: can't open\", filename);\n}\n\n/* this is called from the gui process.  The first argument is the cwd, and\nsucceeding args give the widths and heights of known fonts.  We wait until\nthese are known to open files and send messages specified on the command line.\nWe ask the GUI to specify the \"cwd\" in case we don't have a local OS to get it\nfrom; for instance we could be some kind of RT embedded system.  However, to\nreally make this make sense we would have to implement\nopen(), read(), etc, calls to be served somehow from the GUI too. */\n\nvoid glob_initfromgui(void *dummy, t_symbol *s, int argc, t_atom *argv)\n{\n    const char *cwd = atom_getsymbolarg(0, argc, argv)->s_name;\n    t_patchlist *pl;\n    t_namelist *nl;\n    unsigned int i;\n    int did_fontwarning = 0;\n    int j;\n    sys_oldtclversion = atom_getfloatarg(1, argc, argv);\n    if (argc != 2 + 3 * NZOOM * NFONT)\n        bug(\"glob_initfromgui\");\n    for (j = 0; j < NZOOM; j++)\n        for (i = 0; i < NFONT; i++)\n    {\n        int size   = atom_getfloatarg(3 * (i + j * NFONT) + 2, argc, argv);\n        int width  = atom_getfloatarg(3 * (i + j * NFONT) + 3, argc, argv);\n        int height = atom_getfloatarg(3 * (i + j * NFONT) + 4, argc, argv);\n        if (!(size && width && height))\n        {\n            size   = (j+1)*sys_fontspec[i].fi_pointsize;\n            width  = (j+1)*sys_fontspec[i].fi_width;\n            height = (j+1)*sys_fontspec[i].fi_height;\n            if (!did_fontwarning)\n            {\n                logpost(NULL, PD_VERBOSE, \"ignoring invalid font-metrics from GUI\");\n                did_fontwarning = 1;\n            }\n        }\n        sys_gotfonts[j][i].fi_pointsize = size;\n        sys_gotfonts[j][i].fi_width = width;\n        sys_gotfonts[j][i].fi_height = height;\n#if 0\n            fprintf(stderr, \"font (%d %d %d)\\n\",\n                sys_gotfonts[j][i].fi_pointsize, sys_gotfonts[j][i].fi_width,\n                    sys_gotfonts[j][i].fi_height);\n#endif\n    }\n        /* load dynamic libraries specified with \"-lib\" args */\n    if (sys_oktoloadfiles(0))\n    {\n        for  (nl = STUFF->st_externlist; nl; nl = nl->nl_next)\n            if (!sys_load_lib(0, nl->nl_string))\n                post(\"%s: can't load library\", nl->nl_string);\n        sys_oktoloadfiles(1);\n    }\n        /* open patches specifies with \"-open\" args */\n    for (pl = sys_openlist; pl; pl = pl->pl_next)\n        openit(cwd, pl->pl_file, pl->pl_args);\n    patchlist_free(sys_openlist);\n    sys_openlist = 0;\n        /* send messages specified with \"-send\" args */\n    for  (nl = sys_messagelist; nl; nl = nl->nl_next)\n    {\n        t_binbuf *b = binbuf_new();\n        binbuf_text(b, nl->nl_string, strlen(nl->nl_string));\n        binbuf_eval(b, 0, 0, 0);\n        binbuf_free(b);\n    }\n    namelist_free(sys_messagelist);\n    sys_messagelist = 0;\n}\n\n// font char metric triples: pointsize width(pixels) height(pixels)\nstatic int defaultfontshit[] = {\n  8,  5, 11,  10,  6, 13,  12,  7, 16,  16, 10, 19,  24, 14, 29,  36, 22, 44,\n 16, 10, 22,  20, 12, 26,  24, 14, 32,  32, 20, 38,  48, 28, 58,  72, 44, 88\n}; // normal & zoomed (2x)\n#define NDEFAULTFONT (sizeof(defaultfontshit)/sizeof(*defaultfontshit))\n\nstatic t_clock *sys_fakefromguiclk;\nint socket_init(void);\nstatic void sys_fakefromgui(void)\n{\n        /* fake the GUI's message giving cwd and font sizes in case\n        we aren't starting the gui. */\n    t_atom zz[NDEFAULTFONT+2];\n    int i;\n    char buf[MAXPDSTRING];\n#ifdef _WIN32\n    if (GetCurrentDirectory(MAXPDSTRING, buf) == 0)\n        strcpy(buf, \".\");\n#else\n    if (!getcwd(buf, MAXPDSTRING))\n        strcpy(buf, \".\");\n#endif\n    SETSYMBOL(zz, gensym(buf));\n    SETFLOAT(zz+1, 0);\n    for (i = 0; i < (int)NDEFAULTFONT; i++)\n        SETFLOAT(zz+i+2, defaultfontshit[i]);\n    glob_initfromgui(0, 0, 2+NDEFAULTFONT, zz);\n    clock_free(sys_fakefromguiclk);\n}\n\nstatic void sys_afterargparse(void);\nstatic void sys_printusage(void);\n\n/* this is called from main() in s_entry.c */\nint sys_main(int argc, const char **argv)\n{\n    int i, noprefs;\n    const char *prefsfile = \"\";\n    sys_externalschedlib = 0;\n    sys_extraflags = 0;\n#ifdef PD_DEBUG\n    fprintf(stderr, \"Pd: COMPILED FOR DEBUGGING\\n\");\n#endif\n    /* use Win32 \"binary\" mode by default since we don't want the\n     * translation that Win32 does by default */\n#ifdef _WIN32\n# ifdef _MSC_VER /* MS Visual Studio */\n    _set_fmode( _O_BINARY );\n# else  /* MinGW */\n    {\n#ifndef _fmode\n        extern int _fmode;\n#endif\n        _fmode = _O_BINARY;\n    }\n# endif /* _MSC_VER */\n#endif  /* _WIN32 */\n#ifndef _WIN32\n    /* long ago Pd used setuid to promote itself to real-time priority.\n    Just in case anyone's installation script still makes it setuid, we\n    complain to stderr and lose setuid here. */\n    if (getuid() != geteuid())\n    {\n        fprintf(stderr, \"warning: canceling setuid privilege\\n\");\n        if(setuid(getuid()) < 0) {\n                /* sometimes this fails (which, according to 'man 2 setuid' is a\n                 * grave security error), in which case we bail out and quit. */\n            fprintf(stderr, \"\\n\\nFATAL: could not cancel setuid privilege\");\n            fprintf(stderr, \"\\nTo fix this, please remove the setuid flag from the Pd binary\");\n            if(argc>0) {\n                fprintf(stderr, \"\\ne.g. by running the following as root/superuser:\");\n                fprintf(stderr, \"\\n chmod u-s '%s'\", argv[0]);\n            }\n            fprintf(stderr, \"\\n\\n\");\n            perror(\"setuid\");\n            return (1);\n        }\n    }\n#endif  /* _WIN32 */\n    if (socket_init())\n        sys_sockerror(\"socket_init()\");\n    pd_init();                                  /* start the message system */\n    sys_findprogdir(argv[0]);                   /* set sys_progname, guipath */\n    for (i = noprefs = 0; i < argc; i++)    /* prescan ... */\n    {\n        /* for prefs override */\n        if (!strcmp(argv[i], \"-noprefs\"))\n            noprefs = 1;\n        else if (!strcmp(argv[i], \"-prefsfile\") && i < argc-1)\n            prefsfile = argv[i+1];\n        /* for external scheduler (to ignore audio api in sys_loadpreferences) */\n        else if (!strcmp(argv[i], \"-schedlib\") && i < argc-1)\n            sys_externalschedlib = 1;\n        else if (!strcmp(argv[i], \"-h\") || !strcmp(argv[i], \"-help\"))\n        {\n            sys_printusage();\n            return (1);\n        }\n    }\n    if (!noprefs)       /* load preferences before parsing args to allow ... */\n        sys_loadpreferences(prefsfile, 1);  /* args to override prefs */\n    if (sys_argparse(argc-1, argv+1))           /* parse cmd line args */\n        return (1);\n    if (sys_verbose || sys_version) fprintf(stderr, \"%s compiled %s %s\\n\",\n        pd_version, pd_compiletime, pd_compiledate);\n    if (sys_verbose)\n        fprintf(stderr, \"float precision = %lu bits\\n\", sizeof(t_float)*8);\n    if (sys_version)    /* if we were just asked our version, exit here. */\n    {\n        fflush(stderr);\n        return (0);\n    }\n    sys_setsignalhandlers();\n    sys_afterargparse();                    /* post-argparse settings */\n    if (sys_dontstartgui)\n        clock_set((sys_fakefromguiclk =\n            clock_new(0, (t_method)sys_fakefromgui)), 0);\n    else if (sys_startgui(sys_libdir->s_name)) /* start the gui */\n        return (1);\n    if (sys_hipriority)\n        sys_setrealtime(sys_libdir->s_name); /* set desired process priority */\n    if (sys_externalschedlib)\n        return (sys_run_scheduler(sys_externalschedlibname,\n            sys_extraflagsstring));\n    else if (sys_batch)\n        return (m_batchmain());\n    else\n    {\n            /* open audio and MIDI */\n        sys_reopen_midi();\n        if (audio_shouldkeepopen())\n            sys_reopen_audio();\n            /* run scheduler until it quits */\n        return (m_mainloop());\n    }\n}\n\nstatic char *(usagemessage[]) = {\n\"usage: pd [-flags] [file]...\\n\",\n\"\\naudio configuration flags:\\n\",\n\"-r <n>           -- specify sample rate\\n\",\n\"-audioindev ...  -- audio in devices; e.g., \\\"1,3\\\" for first and third\\n\",\n\"-audiooutdev ... -- audio out devices (same)\\n\",\n\"-audiodev ...    -- specify input and output together\\n\",\n\"-audioaddindev   -- add an audio input device by name\\n\",\n\"-audioaddoutdev  -- add an audio output device by name\\n\",\n\"-audioadddev     -- add an audio input and output device by name\\n\",\n\"-inchannels ...  -- audio input channels (by device, like \\\"2\\\" or \\\"16,8\\\")\\n\",\n\"-outchannels ... -- number of audio out channels (same)\\n\",\n\"-channels ...    -- specify both input and output channels\\n\",\n\"-audiobuf <n>    -- specify size of audio buffer in msec\\n\",\n\"-blocksize <n>   -- specify audio I/O block size in sample frames\\n\",\n\"-sleepgrain <n>  -- specify number of milliseconds to sleep when idle\\n\",\n\"-nodac           -- suppress audio output\\n\",\n\"-noadc           -- suppress audio input\\n\",\n\"-noaudio         -- suppress audio input and output (-nosound is synonym) \\n\",\n\"-callback        -- use callbacks if possible\\n\",\n\"-nocallback      -- use polling-mode (true by default)\\n\",\n\"-listdev         -- list audio and MIDI devices\\n\",\n\n#ifdef USEAPI_OSS\n\"-oss             -- use OSS audio API\\n\",\n#endif\n\n#ifdef USEAPI_ALSA\n\"-alsa            -- use ALSA audio API\\n\",\n\"-alsaadd <name>  -- add an ALSA device name to list\\n\",\n#endif\n\n#ifdef USEAPI_JACK\n\"-jack            -- use JACK audio API\\n\",\n\"-jackname <name> -- a name for your JACK client\\n\",\n\"-nojackconnect   -- do not automatically connect pd to the JACK graph\\n\",\n\"-jackconnect     -- automatically connect pd to the JACK graph [default]\\n\",\n\n#endif\n\n#ifdef USEAPI_PORTAUDIO\n#ifdef _WIN32\n\"-pa              -- use Portaudio API (for ASIO or WASAPI)\\n\",\n\"-asio            -- synonym for -pa\\n\",\n#else\n\"-pa              -- use Portaudio API\\n\",\n#endif\n#endif\n\n#ifdef USEAPI_MMIO\n\"-mmio            -- use legacy MMIO audio API\\n\",\n#endif\n\n#ifdef USEAPI_AUDIOUNIT\n\"-audiounit       -- use Apple AudioUnit API\\n\",\n#endif\n\n#ifdef USEAPI_ESD\n\"-esd             -- use Enlightenment Sound Daemon (ESD) API\\n\",\n#endif\n\n\"      (default audio API for this platform:  \", API_DEFSTRING, \")\\n\\n\",\n\n\"\\nMIDI configuration flags:\\n\",\n\"-midiindev ...   -- midi in device list; e.g., \\\"1,3\\\" for first and third\\n\",\n\"-midioutdev ...  -- midi out device list, same format\\n\",\n\"-mididev ...     -- specify -midioutdev and -midiindev together\\n\",\n\"-midiaddindev    -- add a MIDI input device by name\\n\",\n\"-midiaddoutdev   -- add a MIDI output device by name\\n\",\n\"-midiadddev      -- add a MIDI input and output device by name\\n\",\n\"-nomidiin        -- suppress MIDI input\\n\",\n\"-nomidiout       -- suppress MIDI output\\n\",\n\"-nomidi          -- suppress MIDI input and output\\n\",\n#ifdef USEAPI_OSS\n\"-ossmidi         -- use OSS midi API\\n\",\n#endif\n#ifdef USEAPI_ALSA\n\"-alsamidi        -- use ALSA midi API\\n\",\n#endif\n\n\n\"\\nother flags:\\n\",\n\"-path <path>     -- add to file search path\\n\",\n\"-nostdpath       -- don't search standard (\\\"extra\\\") directory\\n\",\n\"-stdpath         -- search standard directory (true by default)\\n\",\n\"-helppath <path> -- add to help file search path\\n\",\n\"-open <file>     -- open file(s) on startup\\n\",\n\"-open-with-args <file> <args> -- open file(s) on startup with arguments\\n\",\n\"-lib <file>      -- load object library(s) (omit file extensions)\\n\",\n\"-font-size <n>      -- specify default font size in points\\n\",\n\"-font-face <name>   -- specify default font\\n\",\n\"-font-weight <name> -- specify default font weight (normal or bold)\\n\",\n\"-verbose         -- extra printout on startup and when searching for files\\n\",\n\"-noverbose       -- no extra printout\\n\",\n\"-version         -- don't run Pd; just print out which version it is \\n\",\n\"-d <n>           -- specify debug level for inspecting the GUI communication\\n\",\n\"-loadbang        -- do not suppress all loadbangs (true by default)\\n\",\n\"-noloadbang      -- suppress all loadbangs\\n\",\n\"-stderr          -- send printout to standard error instead of GUI\\n\",\n\"-nostderr        -- send printout to GUI (true by default)\\n\",\n\"-gui             -- start GUI (true by default)\\n\",\n\"-nogui           -- suppress starting the GUI\\n\",\n\"-guiport <n>     -- connect to pre-existing GUI over port <n>\\n\",\n\"-guicmd \\\"cmd...\\\" -- start alternative GUI program (e.g., remote via ssh)\\n\",\n\"-send \\\"msg...\\\"   -- send a message at startup, after patches are loaded\\n\",\n\"-prefs           -- load preferences on startup (true by default)\\n\",\n\"-noprefs         -- suppress loading preferences on startup\\n\",\n\"-prefsfile <file>  -- load preferences from a file\\n\",\n#ifdef HAVE_UNISTD_H\n\"-rt or -realtime -- use real-time priority\\n\",\n\"-nrt             -- don't use real-time priority\\n\",\n#endif\n\"-sleep           -- sleep when idle, don't spin (true by default)\\n\",\n\"-nosleep         -- spin, don't sleep (may lower latency on multi-CPUs)\\n\",\n\"-schedlib <file> -- plug in external scheduler (omit file extensions)\\n\",\n\"-extraflags <s>  -- string argument to send schedlib\\n\",\n\"-batch           -- run off-line as a batch process\\n\",\n\"-nobatch         -- run interactively (true by default)\\n\",\n\"-autopatch       -- enable auto-patching to new objects (true by default)\\n\",\n\"-noautopatch     -- defeat auto-patching\\n\",\n\"-compatibility <f> -- set back-compatibility to version <f>\\n\",\n};\n\nstatic void sys_printusage(void)\n{\n    unsigned int i;\n    for (i = 0; i < sizeof(usagemessage)/sizeof(*usagemessage); i++)\n    {\n        fprintf(stderr, \"%s\", usagemessage[i]);\n        fflush(stderr);\n    }\n}\n\n    /* parse a comma-separated numeric list, returning the number found */\nstatic int sys_parsedevlist(int *np, int *vecp, int max, const char *str)\n{\n    int n = 0;\n    while (n < max)\n    {\n        if (! *str) break;\n        else\n        {\n            char *endp;\n            vecp[n] = (int)strtol(str, &endp, 10);\n            if (endp == str)\n                break;\n            n++;\n            if (! *endp)\n                break;\n            str = endp + 1;\n        }\n    }\n    return (*np = n);\n}\n\nstatic int sys_getmultidevchannels(int n, int *devlist)\n{\n    int sum = 0;\n    if (n<0)return(-1);\n    if (n==0)return 0;\n    while(n--)sum+=*devlist++;\n    return sum;\n}\n\n\n    /* this routine tries to figure out where to find the auxiliary files\n    Pd will need to run.  This is either done by looking at the command line\n    invocation for Pd, or if that fails, by consulting the variable\n    INSTALL_PREFIX.  In MSW, we don't try to use INSTALL_PREFIX. */\nvoid sys_findprogdir(const char *progname)\n{\n    char sbuf[MAXPDSTRING], sbuf2[MAXPDSTRING];\n    char *lastslash;\n#ifndef _WIN32\n    struct stat statbuf;\n#endif /* NOT _WIN32 */\n    /* find out by what string Pd was invoked; put answer in \"sbuf\". */\n    *sbuf2 = 0;\n#ifdef _WIN32\n    GetModuleFileName(NULL, sbuf2, sizeof(sbuf2));\n    sbuf2[MAXPDSTRING-1] = 0;\n#else /* !_WIN32 */\n    if(!*sbuf2) {\n        ssize_t path_length = readlink(\"/proc/self/exe\", sbuf2, sizeof(sbuf2));\n        if (path_length > 0 && path_length < MAXPDSTRING)\n            sbuf2[path_length] = 0;\n    }\n#endif /* _WIN32 */\n#ifdef __APPLE__\n    if(!*sbuf2) {\n        uint32_t size = sizeof(sbuf2);\n        _NSGetExecutablePath(sbuf2, &size);\n    }\n#endif /* ! __APPLE__ */\n\n        /* fallback to just using argv[0] */\n    if(!*sbuf2)\n        strncpy(sbuf2, progname, MAXPDSTRING);\n    sbuf2[MAXPDSTRING-1] = 0;\n    sys_unbashfilename(sbuf2, sbuf);\n\n    lastslash = strrchr(sbuf, '/');\n    if (lastslash)\n    {\n            /* bash last slash to zero so that sbuf is directory pd was in,\n                e.g., ~/pd/bin */\n        *lastslash = 0;\n            /* go back to the parent from there, e.g., ~/pd */\n        lastslash = strrchr(sbuf, '/');\n        if (lastslash)\n        {\n            strncpy(sbuf2, sbuf, lastslash-sbuf);\n            sbuf2[lastslash-sbuf] = 0;\n        }\n        else strcpy(sbuf2, \"..\");\n    }\n    else\n    {\n            /* no slashes found.  Try INSTALL_PREFIX. */\n#ifdef INSTALL_PREFIX\n        strcpy(sbuf2, INSTALL_PREFIX);\n#else\n        strcpy(sbuf2, \".\");\n#endif\n    }\n        /* now we believe sbuf2 holds the parent directory of the directory\n        pd was found in.  We now want to infer the \"lib\" directory and the\n        \"gui\" directory.  In \"simple\" unix installations, the layout is\n            .../bin/pd\n            .../bin/pd-watchdog (etc)\n            .../bin/pd-gui.tcl\n            .../doc\n        and in \"complicated\" unix installations, it's:\n            .../bin/pd\n            .../lib/pd/bin/pd-watchdog\n            .../lib/pd/bin/pd-gui.tcl\n            .../lib/pd/doc\n        To decide which, we stat .../lib/pd; if that exists, we assume it's\n        the complicated layout.  In MSW, it's the \"simple\" layout, but\n        \"wish\" is found in bin:\n            .../bin/pd\n            .../bin/wish80.exe\n            .../doc\n        */\n#ifdef _WIN32\n    sys_libdir = gensym(sbuf2);\n#else\n\n    strncpy(sbuf, sbuf2, MAXPDSTRING-30);\n    sbuf[MAXPDSTRING-30] = 0;\n    strcat(sbuf, \"/lib/pd\");\n    if (stat(sbuf, &statbuf) >= 0)\n    {\n            /* complicated layout: lib dir is the one we just stat-ed above */\n        sys_libdir = gensym(sbuf);\n    }\n    else\n    {\n            /* simple layout: lib dir is the parent */\n        sys_libdir = gensym(sbuf2);\n    }\n#endif\n}\n\nint sys_argparse(int argc, const char **argv)\n{\n    t_audiosettings as;\n            /* get the current audio parameters.  These are set\n            by the preferences mechanism (sys_loadpreferences()) or\n            else are the default.  Overwrite them with any results\n            of argument parsing, and store them again. */\n    sys_get_audio_settings(&as);\n    while ((argc > 0) && **argv == '-')\n    {\n                /* audio flags */\n        if (!strcmp(*argv, \"-r\") && argc > 1 &&\n            sscanf(argv[1], \"%d\", &as.a_srate) >= 1)\n        {\n            argc -= 2;\n            argv += 2;\n        }\n        else if (!strcmp(*argv, \"-inchannels\"))\n        {\n            if (argc < 2 ||\n                !sys_parsedevlist(&as.a_nchindev,\n                    as.a_chindevvec, MAXAUDIOINDEV, argv[1]))\n                        goto usage;\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(*argv, \"-outchannels\"))\n        {\n            if (argc < 2 ||\n                !sys_parsedevlist(&as.a_nchoutdev,\n                    as.a_choutdevvec, MAXAUDIOINDEV, argv[1]))\n                        goto usage;\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(*argv, \"-channels\"))\n        {\n            if (argc < 2 ||\n                !sys_parsedevlist(&as.a_nchindev,\n                    as.a_chindevvec, MAXAUDIOINDEV, argv[1]) ||\n                !sys_parsedevlist(&as.a_nchoutdev,\n                    as.a_choutdevvec, MAXAUDIOINDEV, argv[1]))\n                        goto usage;\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(*argv, \"-soundbuf\") || (!strcmp(*argv, \"-audiobuf\")))\n        {\n            if (argc < 2)\n                goto usage;\n\n            as.a_advance = atoi(argv[1]);\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(*argv, \"-callback\"))\n        {\n            as.a_callback = 1;\n            argc--; argv++;\n        }\n        else if (!strcmp(*argv, \"-nocallback\"))\n        {\n            as.a_callback = 0;\n            argc--; argv++;\n        }\n        else if (!strcmp(*argv, \"-blocksize\"))\n        {\n            as.a_blocksize = atoi(argv[1]);\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(*argv, \"-sleepgrain\"))\n        {\n            if (argc < 2)\n                goto usage;\n            sys_sleepgrain = 1000 * atof(argv[1]);\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(*argv, \"-nodac\"))\n        {\n            as.a_noutdev = as.a_nchoutdev = 0;\n            argc--; argv++;\n        }\n        else if (!strcmp(*argv, \"-noadc\"))\n        {\n            as.a_nindev = as.a_nchindev = 0;\n            argc--; argv++;\n        }\n        else if (!strcmp(*argv, \"-nosound\") || !strcmp(*argv, \"-noaudio\"))\n        {\n            as.a_noutdev = as.a_nchoutdev =  as.a_nindev = as.a_nchindev = 0;\n            argc--; argv++;\n        }\n#ifdef USEAPI_OSS\n        else if (!strcmp(*argv, \"-oss\"))\n        {\n            as.a_api = API_OSS;\n            argc--; argv++;\n        }\n        else if (!strcmp(*argv, \"-ossmidi\"))\n        {\n            sys_set_midi_api(API_OSS);\n            argc--; argv++;\n        }\n#else\n        else if ((!strcmp(*argv, \"-oss\")) || (!strcmp(*argv, \"-ossmidi\")))\n        {\n            fprintf(stderr, \"Pd compiled without OSS-support, ignoring '%s' flag\\n\", *argv);\n            argc--; argv++;\n        }\n#endif\n#ifdef USEAPI_ALSA\n        else if (!strcmp(*argv, \"-alsa\"))\n        {\n            as.a_api = API_ALSA;\n            argc--; argv++;\n        }\n        else if (!strcmp(*argv, \"-alsaadd\"))\n        {\n            if (argc < 2)\n                goto usage;\n            as.a_api = API_ALSA;\n            alsa_adddev(argv[1]);\n            argc -= 2; argv +=2;\n        }\n        else if (!strcmp(*argv, \"-alsamidi\"))\n        {\n            sys_set_midi_api(API_ALSA);\n            argc--; argv++;\n        }\n#else\n        else if ((!strcmp(*argv, \"-alsa\")) || (!strcmp(*argv, \"-alsamidi\")))\n        {\n            fprintf(stderr, \"Pd compiled without ALSA-support, ignoring '%s' flag\\n\", *argv);\n            argc--; argv++;\n        }\n        else if (!strcmp(*argv, \"-alsaadd\"))\n        {\n            if (argc < 2)\n                goto usage;\n            fprintf(stderr, \"Pd compiled without ALSA-support, ignoring '%s' flag\\n\", *argv);\n            argc -= 2; argv +=2;\n        }\n#endif\n#ifdef USEAPI_JACK\n        else if (!strcmp(*argv, \"-jack\"))\n        {\n            as.a_api = API_JACK;\n            argc--; argv++;\n        }\n        else if (!strcmp(*argv, \"-nojackconnect\"))\n        {\n            as.a_api = API_JACK;\n            jack_autoconnect(0);\n            argc--; argv++;\n        }\n        else if (!strcmp(*argv, \"-jackconnect\"))\n        {\n            as.a_api = API_JACK;\n            jack_autoconnect(1);\n            argc--; argv++;\n        }\n        else if (!strcmp(*argv, \"-jackname\"))\n        {\n            if (argc < 2)\n                goto usage;\n            as.a_api = API_JACK;\n            jack_client_name(argv[1]);\n            argc -= 2; argv +=2;\n        }\n#else\n        else if ((!strcmp(*argv, \"-jack\"))\n            || (!strcmp(*argv, \"-jackconnect\"))\n            || (!strcmp(*argv, \"-nojackconnect\")))\n        {\n            fprintf(stderr, \"Pd compiled without JACK-support, ignoring '%s' flag\\n\", *argv);\n            argc--; argv++;\n        }\n        else if (!strcmp(*argv, \"-jackname\"))\n        {\n            if (argc < 2)\n                goto usage;\n            fprintf(stderr, \"Pd compiled without JACK-support, ignoring '%s' flag\\n\", *argv);\n            argc -= 2; argv +=2;\n        }\n#endif\n#ifdef USEAPI_PORTAUDIO\n        else if (!strcmp(*argv, \"-pa\") || !strcmp(*argv, \"-portaudio\")\n            || !strcmp(*argv, \"-asio\"))\n        {\n            as.a_api = API_PORTAUDIO;\n            argc--; argv++;\n        }\n#else\n        else if ((!strcmp(*argv, \"-pa\")) || (!strcmp(*argv, \"-portaudio\")))\n        {\n            fprintf(stderr, \"Pd compiled without PortAudio-support, ignoring '%s' flag\\n\", *argv);\n            argc--; argv++;\n        }\n        else if (!strcmp(*argv, \"-asio\"))\n        {\n            fprintf(stderr, \"Pd compiled without ASIO-support, ignoring '%s' flag\\n\", *argv);\n            argc -= 2; argv +=2;\n        }\n#endif\n#ifdef USEAPI_MMIO\n        else if (!strcmp(*argv, \"-mmio\"))\n        {\n            as.a_api = API_MMIO;\n            argc--; argv++;\n        }\n#else\n        else if (!strcmp(*argv, \"-mmio\"))\n        {\n            fprintf(stderr, \"Pd compiled without MMIO-support, ignoring '%s' flag\\n\", *argv);\n            argc--; argv++;\n        }\n#endif\n#ifdef USEAPI_AUDIOUNIT\n        else if (!strcmp(*argv, \"-audiounit\"))\n        {\n            as.a_api = API_AUDIOUNIT;\n            argc--; argv++;\n        }\n#else\n        else if (!strcmp(*argv, \"-audiounit\"))\n        {\n            fprintf(stderr, \"Pd compiled without AudioUnit-support, ignoring '%s' flag\\n\", *argv);\n            argc--; argv++;\n        }\n#endif\n#ifdef USEAPI_ESD\n        else if (!strcmp(*argv, \"-esd\"))\n        {\n            as.a_api = API_ESD;\n            argc--; argv++;\n        }\n#else\n        else if (!strcmp(*argv, \"-esd\"))\n        {\n            fprintf(stderr, \"Pd compiled without ESD-support, ignoring '%s' flag\\n\", *argv);\n            argc--; argv++;\n        }\n#endif\n        else if (!strcmp(*argv, \"-listdev\"))\n        {\n            sys_listplease = 1;\n            argc--; argv++;\n        }\n        else if (!strcmp(*argv, \"-soundindev\") ||\n            !strcmp(*argv, \"-audioindev\"))\n        {\n            if (argc < 2 ||\n                !sys_parsedevlist(&as.a_nindev, as.a_indevvec,\n                    MAXAUDIOINDEV, argv[1]))\n                        goto usage;\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(*argv, \"-soundoutdev\") ||\n            !strcmp(*argv, \"-audiooutdev\"))\n        {\n            if (argc < 2 ||\n                !sys_parsedevlist(&as.a_noutdev, as.a_outdevvec,\n                    MAXAUDIOOUTDEV, argv[1]))\n                        goto usage;\n            argc -= 2; argv += 2;\n        }\n        else if ((!strcmp(*argv, \"-sounddev\") || !strcmp(*argv, \"-audiodev\")))\n        {\n            if (argc < 2 ||\n                !sys_parsedevlist(&as.a_nindev, as.a_indevvec,\n                    MAXAUDIOINDEV, argv[1]) ||\n                !sys_parsedevlist(&as.a_noutdev, as.a_outdevvec,\n                    MAXAUDIOOUTDEV, argv[1]))\n                        goto usage;\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(*argv, \"-audioaddindev\"))\n        {\n            if (argc < 2)\n                goto usage;\n\n            if (as.a_nindev < 0)\n                as.a_nindev = 0;\n            if (as.a_nindev < MAXAUDIOINDEV)\n            {\n                int devn = sys_audiodevnametonumber(0, argv[1]);\n                if (devn < 0)\n                    fprintf(stderr, \"Couldn't find audio input device: %s\\n\",\n                        argv[1]);\n                else as.a_indevvec[as.a_nindev++] = devn + 1;\n            }\n            else fprintf(stderr, \"number of audio devices limited to %d\\n\",\n                MAXAUDIOINDEV);\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(*argv, \"-audioaddoutdev\"))\n        {\n            if (argc < 2)\n                goto usage;\n\n            if (as.a_noutdev < 0)\n                as.a_noutdev = 0;\n            if (as.a_noutdev < MAXAUDIOINDEV)\n            {\n                int devn = sys_audiodevnametonumber(1, argv[1]);\n                if (devn < 0)\n                    fprintf(stderr, \"Couldn't find audio output device: %s\\n\",\n                        argv[1]);\n                else as.a_outdevvec[as.a_noutdev++] = devn + 1;\n            }\n            else fprintf(stderr, \"number of audio devices limited to %d\\n\",\n                MAXAUDIOINDEV);\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(*argv, \"-audioadddev\"))\n        {\n            if (argc < 2)\n                goto usage;\n\n            if (as.a_nindev < 0)\n                as.a_nindev = 0;\n            if (as.a_noutdev < 0)\n                as.a_noutdev = 0;\n            if (as.a_nindev < MAXAUDIOINDEV && as.a_noutdev < MAXAUDIOINDEV)\n            {\n                int devn = sys_audiodevnametonumber(0, argv[1]);\n                if (devn < 0)\n                    fprintf(stderr, \"Couldn't find audio input device: %s\\n\",\n                        argv[1]);\n                else as.a_indevvec[as.a_nindev++] = devn + 1;\n                devn = sys_audiodevnametonumber(1, argv[1]);\n                if (devn < 0)\n                    fprintf(stderr, \"Couldn't find audio output device: %s\\n\",\n                        argv[1]);\n                else as.a_outdevvec[as.a_noutdev++] = devn + 1;\n            }\n            else fprintf(stderr, \"number of audio devices limited to %d\",\n                MAXAUDIOINDEV);\n            argc -= 2; argv += 2;\n        }\n                /* MIDI flags */\n        else if (!strcmp(*argv, \"-nomidiin\"))\n        {\n            sys_nmidiin = 0;\n            argc--; argv++;\n        }\n        else if (!strcmp(*argv, \"-nomidiout\"))\n        {\n            sys_nmidiout = 0;\n            argc--; argv++;\n        }\n        else if (!strcmp(*argv, \"-nomidi\"))\n        {\n            sys_nmidiin = sys_nmidiout = 0;\n            argc--; argv++;\n        }\n        else if (!strcmp(*argv, \"-midiindev\"))\n        {\n            if (argc < 2)\n                goto usage;\n\n            sys_parsedevlist(&sys_nmidiin, sys_midiindevlist, MAXMIDIINDEV,\n                argv[1]);\n            if (!sys_nmidiin)\n                goto usage;\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(*argv, \"-midioutdev\"))\n        {\n            if (argc < 2)\n                goto usage;\n\n            sys_parsedevlist(&sys_nmidiout, sys_midioutdevlist, MAXMIDIOUTDEV,\n                argv[1]);\n            if (!sys_nmidiout)\n                goto usage;\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(*argv, \"-mididev\"))\n        {\n            if (argc < 2)\n                goto usage;\n\n            sys_parsedevlist(&sys_nmidiin, sys_midiindevlist, MAXMIDIINDEV,\n                argv[1]);\n            sys_parsedevlist(&sys_nmidiout, sys_midioutdevlist, MAXMIDIOUTDEV,\n                argv[1]);\n            if (!sys_nmidiout)\n                goto usage;\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(*argv, \"-midiaddindev\"))\n        {\n            if (argc < 2)\n                goto usage;\n\n            if (sys_nmidiin < 0)\n                sys_nmidiin = 0;\n            if (sys_nmidiin < MAXMIDIINDEV)\n            {\n                int devn = sys_mididevnametonumber(0, argv[1]);\n                if (devn < 0)\n                    fprintf(stderr, \"Couldn't find MIDI input device: %s\\n\",\n                        argv[1]);\n                else sys_midiindevlist[sys_nmidiin++] = devn + 1;\n            }\n            else fprintf(stderr, \"number of MIDI devices limited to %d\\n\",\n                MAXMIDIINDEV);\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(*argv, \"-midiaddoutdev\"))\n        {\n            if (argc < 2)\n                goto usage;\n\n            if (sys_nmidiout < 0)\n                sys_nmidiout = 0;\n            if (sys_nmidiout < MAXMIDIINDEV)\n            {\n                int devn = sys_mididevnametonumber(1, argv[1]);\n                if (devn < 0)\n                    fprintf(stderr, \"Couldn't find MIDI output device: %s\\n\",\n                        argv[1]);\n                else sys_midioutdevlist[sys_nmidiout++] = devn + 1;\n            }\n            else fprintf(stderr, \"number of MIDI devices limited to %d\\n\",\n                MAXMIDIINDEV);\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(*argv, \"-midiadddev\"))\n        {\n            if (argc < 2)\n                goto usage;\n\n            if (sys_nmidiin < 0)\n                sys_nmidiin = 0;\n            if (sys_nmidiout < 0)\n                sys_nmidiout = 0;\n            if (sys_nmidiin < MAXMIDIINDEV && sys_nmidiout < MAXMIDIINDEV)\n            {\n                int devn = sys_mididevnametonumber(1, argv[1]);\n                if (devn < 0)\n                    fprintf(stderr, \"Couldn't find MIDI output device: %s\\n\",\n                        argv[1]);\n                else sys_midioutdevlist[sys_nmidiin++] = devn + 1;\n                devn = sys_mididevnametonumber(1, argv[1]);\n                if (devn < 0)\n                    fprintf(stderr, \"Couldn't find MIDI output device: %s\\n\",\n                        argv[1]);\n                else sys_midioutdevlist[sys_nmidiout++] = devn + 1;\n            }\n            else fprintf(stderr, \"number of MIDI devices limited to %d\",\n                MAXMIDIINDEV);\n            argc -= 2; argv += 2;\n        }\n                /* other flags */\n        else if (!strcmp(*argv, \"-path\"))\n        {\n            if (argc < 2)\n                goto usage;\n            STUFF->st_temppath =\n                namelist_append_files(STUFF->st_temppath, argv[1]);\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(*argv, \"-nostdpath\"))\n        {\n            sys_usestdpath = 0;\n            argc--; argv++;\n        }\n        else if (!strcmp(*argv, \"-stdpath\"))\n        {\n            sys_usestdpath = 1;\n            argc--; argv++;\n        }\n        else if (!strcmp(*argv, \"-helppath\"))\n        {\n            if (argc < 2)\n                goto usage;\n            STUFF->st_helppath =\n                namelist_append_files(STUFF->st_helppath, argv[1]);\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(*argv, \"-open\"))\n        {\n            if (argc < 2)\n                goto usage;\n            sys_openlist = patchlist_append(sys_openlist, argv[1], 0);\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(*argv, \"-open-with-args\"))\n        {\n            if (argc < 3)\n                goto usage;\n            sys_openlist = patchlist_append(sys_openlist, argv[1], argv[2]);\n            argc -= 3; argv += 3;\n        }\n        else if (!strcmp(*argv, \"-lib\"))\n        {\n            if (argc < 2)\n                goto usage;\n\n            STUFF->st_externlist =\n                namelist_append_files(STUFF->st_externlist, argv[1]);\n            argc -= 2; argv += 2;\n        }\n        else if ((!strcmp(*argv, \"-font-size\") || !strcmp(*argv, \"-font\")))\n        {\n            if (argc < 2)\n                goto usage;\n\n            sys_defaultfont = sys_nearestfontsize(atoi(argv[1]));\n            argc -= 2;\n            argv += 2;\n        }\n        else if ((!strcmp(*argv, \"-font-face\") || !strcmp(*argv, \"-typeface\")))\n        {\n            if (argc < 2)\n                goto usage;\n\n            strncpy(sys_font,*(argv+1),sizeof(sys_font)-1);\n            sys_font[sizeof(sys_font)-1] = 0;\n            argc -= 2;\n            argv += 2;\n        }\n        else if (!strcmp(*argv, \"-font-weight\"))\n        {\n            if (argc < 2)\n                goto usage;\n\n            strncpy(sys_fontweight,*(argv+1),sizeof(sys_fontweight)-1);\n            sys_fontweight[sizeof(sys_fontweight)-1] = 0;\n            argc -= 2;\n            argv += 2;\n        }\n        else if (!strcmp(*argv, \"-verbose\"))\n        {\n            sys_verbose++;\n            argc--; argv++;\n        }\n        else if (!strcmp(*argv, \"-noverbose\"))\n        {\n            sys_verbose=0;\n            argc--; argv++;\n        }\n        else if (!strcmp(*argv, \"-version\"))\n        {\n            sys_version = 1;\n            argc--; argv++;\n        }\n        else if (!strcmp(*argv, \"-d\") && argc > 1 &&\n            sscanf(argv[1], \"%d\", &sys_debuglevel) >= 1)\n        {\n            argc -= 2;\n            argv += 2;\n        }\n        else if (!strcmp(*argv, \"-loadbang\"))\n        {\n            sys_noloadbang = 0;\n            argc--; argv++;\n        }\n        else if (!strcmp(*argv, \"-noloadbang\"))\n        {\n            sys_noloadbang = 1;\n            argc--; argv++;\n        }\n        else if (!strcmp(*argv, \"-gui\"))\n        {\n            sys_dontstartgui = 0;\n            argc--; argv++;\n        }\n        else if (!strcmp(*argv, \"-nogui\"))\n        {\n            sys_dontstartgui = 1;\n            argc--; argv++;\n        }\n        else if (!strcmp(*argv, \"-guiport\") && argc > 1 &&\n            sscanf(argv[1], \"%d\", &sys_guisetportnumber) >= 1)\n        {\n            argc -= 2;\n            argv += 2;\n        }\n        else if (!strcmp(*argv, \"-nostderr\"))\n        {\n            sys_printtostderr = 0;\n            argc--; argv++;\n        }\n        else if (!strcmp(*argv, \"-stderr\"))\n        {\n            sys_printtostderr = 1;\n            argc--; argv++;\n        }\n        else if (!strcmp(*argv, \"-guicmd\"))\n        {\n            if (argc < 2)\n                goto usage;\n\n            sys_guicmd = argv[1];\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(*argv, \"-send\"))\n        {\n            if (argc < 2)\n                goto usage;\n\n            sys_messagelist = namelist_append(sys_messagelist, argv[1], 1);\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(*argv, \"-schedlib\"))\n        {\n            if (argc < 2)\n                goto usage;\n\n            sys_externalschedlib = 1;\n            strncpy(sys_externalschedlibname, argv[1],\n                sizeof(sys_externalschedlibname) - 1);\n#ifndef  __APPLE__\n                /* no audio I/O please, unless overwritten by later args.\n                This is to circumvent a problem running pd~ subprocesses\n                with -nogui; they would open an audio device before pdsched.c\n                could set the API to nothing.  For some reason though, on\n                MACOSX this causes Pd to switch to JACK so we just give up\n                and suppress the workaround there. */\n            as.a_api = 0;\n#endif\n            argv += 2;\n            argc -= 2;\n        }\n        else if (!strcmp(*argv, \"-extraflags\"))\n        {\n            if (argc < 2)\n                goto usage;\n\n            sys_extraflags = 1;\n            strncpy(sys_extraflagsstring, argv[1],\n                sizeof(sys_extraflagsstring) - 1);\n            argv += 2;\n            argc -= 2;\n        }\n        else if (!strcmp(*argv, \"-batch\"))\n        {\n            sys_batch = 1;\n            argc--; argv++;\n        }\n        else if (!strcmp(*argv, \"-nobatch\"))\n        {\n            sys_batch = 0;\n            argc--; argv++;\n        }\n        else if (!strcmp(*argv, \"-autopatch\"))\n        {\n            sys_noautopatch = 0;\n            argc--; argv++;\n        }\n        else if (!strcmp(*argv, \"-noautopatch\"))\n        {\n            sys_noautopatch = 1;\n            argc--; argv++;\n        }\n        else if (!strcmp(*argv, \"-compatibility\"))\n        {\n            float f;\n            if (argc < 2)\n                goto usage;\n\n            if (sscanf(argv[1], \"%f\", &f) < 1)\n                goto usage;\n            pd_compatibilitylevel = 0.5 + 100. * f; /* e.g., 2.44 --> 244 */\n            argv += 2;\n            argc -= 2;\n        }\n#ifdef HAVE_UNISTD_H\n        else if (!strcmp(*argv, \"-rt\") || !strcmp(*argv, \"-realtime\"))\n        {\n            sys_hipriority = 1;\n            argc--; argv++;\n        }\n        else if (!strcmp(*argv, \"-nrt\") || !strcmp(*argv, \"-nort\") || !strcmp(*argv, \"-norealtime\"))\n        {\n            sys_hipriority = 0;\n            argc--; argv++;\n        }\n#else\n        else if (!strcmp(*argv, \"-rt\") || !strcmp(*argv, \"-realtime\")\n                 || !strcmp(*argv, \"-nrt\") || !strcmp(*argv, \"-nort\")\n                 || !strcmp(*argv, \"-norealtime\"))\n        {\n            fprintf(stderr, \"Pd compiled without realtime priority-support, ignoring '%s' flag\\n\", *argv);\n            argc--; argv++;\n        }\n#endif\n        else if (!strcmp(*argv, \"-sleep\"))\n        {\n            sys_nosleep = 0;\n            argc--; argv++;\n        }\n        else if (!strcmp(*argv, \"-nosleep\"))\n        {\n            sys_nosleep = 1;\n            argc--; argv++;\n        }\n        else if (!strcmp(*argv, \"-noprefs\")) /* did this earlier */\n            argc--, argv++;\n        else if (!strcmp(*argv, \"-prefsfile\") && argc > 1) /* this too */\n            argc -= 2, argv +=2;\n        else\n        {\n        usage:\n            sys_printusage();\n            return (1);\n        }\n    }\n    if (sys_batch)\n        sys_dontstartgui = 1;\n    if (sys_dontstartgui)\n        sys_printtostderr = 1;\n#ifdef _WIN32\n    if (sys_printtostderr)\n        /* we need to tell Windows to output UTF-8 */\n        SetConsoleOutputCP(CP_UTF8);\n#endif\n    if (!sys_defaultfont)\n        sys_defaultfont = DEFAULTFONT;\n    for (; argc > 0; argc--, argv++)\n        sys_openlist = patchlist_append(sys_openlist, *argv, 0);\n\n    sys_set_audio_settings(&as);\n\n    return (0);\n}\n\nint sys_getblksize(void)\n{\n    return (DEFDACBLKSIZE);\n}\n\nvoid sys_init_audio(void);\n\n    /* stuff to do, once, after calling sys_argparse() -- which may itself\n    be called more than once (first from \"settings, second from .pdrc, then\n    from command-line arguments */\nstatic void sys_afterargparse(void)\n{\n    char sbuf[MAXPDSTRING];\n    int i;\n    t_audiosettings as;\n    int nmidiindev = 0, midiindev[MAXMIDIINDEV];\n    int nmidioutdev = 0, midioutdev[MAXMIDIOUTDEV];\n            /* add \"extra\" library to path */\n    strncpy(sbuf, sys_libdir->s_name, MAXPDSTRING-30);\n    sbuf[MAXPDSTRING-30] = 0;\n    strcat(sbuf, \"/extra\");\n    sys_setextrapath(sbuf);\n            /* add \"doc/5.reference\" library to helppath */\n    strncpy(sbuf, sys_libdir->s_name, MAXPDSTRING-30);\n    sbuf[MAXPDSTRING-30] = 0;\n    strcat(sbuf, \"/doc/5.reference\");\n    STUFF->st_helppath = namelist_append_files(STUFF->st_helppath, sbuf);\n\n    for (i = 0; i < sys_nmidiin; i++)\n        sys_midiindevlist[i]--;\n    for (i = 0; i < sys_nmidiout; i++)\n        sys_midioutdevlist[i]--;\n\n    if (sys_listplease)\n        sys_listdevs();\n\n    sys_get_midi_params(&nmidiindev, midiindev, &nmidioutdev, midioutdev);\n    if (sys_nmidiin >= 0)\n    {\n        nmidiindev = sys_nmidiin;\n        for (i = 0; i < nmidiindev; i++)\n            midiindev[i] = sys_midiindevlist[i];\n    }\n    if (sys_nmidiout >= 0)\n    {\n        nmidioutdev = sys_nmidiout;\n        for (i = 0; i < nmidioutdev; i++)\n            midioutdev[i] = sys_midioutdevlist[i];\n    }\n    sys_open_midi(nmidiindev, midiindev, nmidioutdev, midioutdev, 0);\n\n    sys_init_audio();\n}\n\nstatic void sys_addreferencepath(void)\n{\n    char sbuf[MAXPDSTRING];\n}\n\nint sys_argparse(int argc, const char **argv);\nstatic int string2args(const char * cmd, int * retArgc, const char *** retArgv);\n\nvoid sys_doflags(void)\n{\n    int rcargc=0;\n    const char**rcargv = NULL;\n    int len;\n    int rcode = 0;\n    if (!sys_flags)\n        sys_flags = &s_;\n    len = (int)strlen(sys_flags->s_name);\n    if (len > MAXPDSTRING)\n    {\n        pd_error(0, \"flags: %s: too long\", sys_flags->s_name);\n        return;\n    }\n    rcode = string2args(sys_flags->s_name, &rcargc, &rcargv);\n    if(rcode < 0) {\n        pd_error(0, \"error#%d while parsing flags\", rcode);\n        return;\n    }\n\n    if (sys_argparse(rcargc, rcargv))\n        pd_error(0, \"error parsing startup arguments\");\n\n    for(len=0; len<rcargc; len++)\n        free((void*)rcargv[len]);\n    free(rcargv);\n}\n\n/* undo pdtl_encodedialog.  This allows dialogs to send spaces, commas,\n    dollars, and semis down here. */\nt_symbol *sys_decodedialog(t_symbol *s)\n{\n    char buf[MAXPDSTRING];\n    const char *sp = s->s_name;\n    int i;\n    if (*sp != '+')\n        bug(\"sys_decodedialog: %s\", sp);\n    else sp++;\n    for (i = 0; i < MAXPDSTRING-1; i++, sp++)\n    {\n        if (!sp[0])\n            break;\n        if (sp[0] == '+')\n        {\n            if (sp[1] == '_')\n                buf[i] = ' ', sp++;\n            else if (sp[1] == '+')\n                buf[i] = '+', sp++;\n            else if (sp[1] == 'c')\n                buf[i] = ',', sp++;\n            else if (sp[1] == 's')\n                buf[i] = ';', sp++;\n            else if (sp[1] == 'd')\n                buf[i] = '$', sp++;\n            else buf[i] = sp[0];\n        }\n        else buf[i] = sp[0];\n    }\n    buf[i] = 0;\n    return (gensym(buf));\n}\n\n\n/* start the generic preference window */\nvoid sys_gui_preferences(void);\nvoid sys_gui_audiopreferences(void);\nvoid sys_gui_midipreferences(void);\nvoid glob_start_preference_dialog(t_pd *dummy, t_symbol*s)\n{\n    sys_gui_preferences();\n    sys_gui_audiopreferences();\n    sys_gui_midipreferences();\n    pdgui_vmess(\"::dialog_preferences::create\", \"\");\n}\n\n\n    /* start a search path dialog window */\nvoid glob_start_path_dialog(t_pd *dummy)\n{\n    sys_gui_preferences();\n    pdgui_stub_vnew(\n        &glob_pdobject,\n        \"pdtk_path_dialog\", (void *)glob_start_path_dialog,\n        \"ii\", sys_usestdpath, sys_verbose);\n}\n\n    /* new values from dialog window */\nvoid glob_path_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv)\n{\n    int i;\n    namelist_free(STUFF->st_searchpath);\n    STUFF->st_searchpath = 0;\n    sys_usestdpath = atom_getfloatarg(0, argc, argv);\n    sys_verbose = atom_getfloatarg(1, argc, argv);\n    for (i = 0; i < argc-2; i++)\n    {\n        t_symbol *s = sys_decodedialog(atom_getsymbolarg(i+2, argc, argv));\n        if (*s->s_name)\n            STUFF->st_searchpath =\n                namelist_append_files(STUFF->st_searchpath, s->s_name);\n    }\n}\n\n    /* add one item to search path (intended for use by Deken plugin).\n    if \"saveit\" is > 0, this also saves all settings,\n    if \"saveit\" is < 0, the path is only added temporarily */\nvoid glob_addtopath(t_pd *dummy, t_symbol *path, t_float saveit)\n{\n    int saveflag = (int)saveit;\n    t_symbol *s = sys_decodedialog(path);\n    if (*s->s_name)\n    {\n        if (saveflag < 0)\n            STUFF->st_temppath =\n                namelist_append_files(STUFF->st_temppath, s->s_name);\n        else\n            STUFF->st_searchpath =\n                namelist_append_files(STUFF->st_searchpath, s->s_name);\n        if (saveit > 0)\n            sys_savepreferences(0);\n    }\n}\n\n    /* start a startup dialog window */\nvoid glob_start_startup_dialog(t_pd *dummy)\n{\n    sys_gui_preferences();\n    pdgui_stub_vnew(\n        &glob_pdobject,\n        \"pdtk_startup_dialog\", (void *)glob_start_path_dialog,\n        \"is\", sys_defeatrt, sys_flags?sys_flags->s_name:\"\");\n}\n\n    /* new values from dialog window */\nvoid glob_startup_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv)\n{\n    int i;\n    namelist_free(STUFF->st_externlist);\n    STUFF->st_externlist = 0;\n    sys_defeatrt = atom_getfloatarg(0, argc, argv);\n    sys_flags = sys_decodedialog(atom_getsymbolarg(1, argc, argv));\n    for (i = 0; i < argc-2; i++)\n    {\n        t_symbol *s = sys_decodedialog(atom_getsymbolarg(i+2, argc, argv));\n        if (*s->s_name)\n            STUFF->st_externlist =\n                namelist_append_files(STUFF->st_externlist, s->s_name);\n    }\n}\n\n\n/*\n * the following string2args function is based on from sash-3.8 (the StandAlone SHell)\n * Copyright (c) 2014 by David I. Bell\n * Permission is granted to use, distribute, or modify this source,\n * provided that this copyright notice remains intact.\n */\n#define\tisBlank(ch)\t(((ch) == ' ') || ((ch) == '\\t'))\nint string2args(const char * cmd, int * retArgc, const char *** retArgv)\n{\n    int errCode = 1;\n    int len = strlen(cmd), argCount = 0;\n    char strings[MAXPDSTRING], *cp;\n    const char **argTable = 0, **newArgTable;\n\n    if(retArgc) *retArgc = 0;\n    if(retArgv) *retArgv = NULL;\n\n        /*\n         * Copy the command string into a buffer that we can modify,\n         * reallocating it if necessary.\n         */\n    if(len >= MAXPDSTRING) {\n        errCode = 1; goto ouch;\n    }\n    memset(strings, 0, MAXPDSTRING);\n    memcpy(strings, cmd, len);\n    cp = strings;\n\n        /* Keep parsing the command string as long as there are any arguments left. */\n    while (*cp) {\n        const char *cpIn = cp;\n        char *cpOut = cp, *argument;\n        int quote = '\\0';\n\n            /*\n             * Loop over the string collecting the next argument while\n             * looking for quoted strings or quoted characters.\n             */\n        while (*cp) {\n            int ch = *cp++;\n\n                /* If we are not in a quote and we see a blank then this argument is done. */\n            if (isBlank(ch) && (quote == '\\0'))\n                break;\n\n                /* If we see a backslash then accept the next character no matter what it is. */\n            if (ch == '\\\\') {\n                ch = *cp++;\n                if (ch == '\\0') { /* but only if there is a next char */\n                    errCode = 10; goto ouch;\n                }\n                *cpOut++ = ch;\n                continue;\n            }\n\n                /* If we were in a quote and we saw the same quote character again then the quote is done. */\n            if (ch == quote) {\n                quote = '\\0';\n                continue;\n            }\n\n                /* If we weren't in a quote and we see either type of quote character,\n                 * then remember that we are now inside of a quote. */\n            if ((quote == '\\0') && ((ch == '\\'') || (ch == '\"')))  {\n                quote = ch;\n                continue;\n            }\n\n                /* Store the character. */\n            *cpOut++ = ch;\n        }\n\n        if (quote) { /* Unmatched quote character */\n            errCode = 11; goto ouch;\n        }\n\n            /*\n             * Null terminate the argument if it had shrunk, and then\n             * skip over all blanks to the next argument, nulling them\n             * out too.\n             */\n        if (cp != cpOut)\n            *cpOut = '\\0';\n        while (isBlank(*cp))\n            *cp++ = '\\0';\n\n        if (!(argument = calloc(1+cpOut-cpIn, 1))) {\n            errCode = 22; goto ouch;\n        }\n        memcpy(argument, cpIn, cpOut-cpIn);\n\n            /* Now reallocate the argument table to hold the argument, add add it. */\n        if (!(newArgTable = (const char **) realloc(argTable, (sizeof(const char *) * (argCount + 1))))) {\n            free(argument);\n            errCode= 23; goto ouch;\n        } else argTable = newArgTable;\n\n        argTable[argCount] = argument;\n\n        argCount++;\n    }\n\n        /*\n         * Null terminate the argument list and return it.\n         */\n    if (!(newArgTable = (const char **) realloc(argTable, (sizeof(const char *) * (argCount + 1))))) {\n        errCode = 23; goto ouch;\n    } else argTable = newArgTable;\n\n    argTable[argCount] = NULL;\n\n    if(retArgc) *retArgc = argCount;\n    if(retArgv)\n        *retArgv = argTable;\n    else\n        free(argTable);\n    return argCount;\n\n ouch:\n    free(argTable);\n    return -errCode;\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/s_net.c",
    "content": "/* Copyright (c) 2019 Dan Wilcox.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n#include \"s_net.h\"\n\n#include <stdio.h>\n#include <string.h>\n#include <errno.h>\n#ifndef _WIN32\n#include <unistd.h>\n#include <fcntl.h>\n#include <sys/ioctl.h>\n#include <sys/time.h>\n#endif\n\n    /* Windows XP winsock doesn't provide inet_ntop */\n#ifdef _WIN32\nconst char* INET_NTOP(int af, const void *src, char *dst, socklen_t size) {\n    struct sockaddr_storage addr;\n    socklen_t addrlen;\n    memset(&addr, 0, sizeof(struct sockaddr_storage));\n    addr.ss_family = af;\n    if (af == AF_INET6)\n    {\n        struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&addr;\n        memcpy(&(sa6->sin6_addr.s6_addr), src, sizeof(sa6->sin6_addr.s6_addr));\n        addrlen = sizeof(struct sockaddr_in6);\n    }\n    else if (af == AF_INET)\n    {\n        struct sockaddr_in *sa4 = (struct sockaddr_in *)&addr;\n        memcpy(&(sa4->sin_addr.s_addr), src, sizeof(sa4->sin_addr.s_addr));\n        addrlen = sizeof(struct sockaddr_in);\n    }\n    else\n        return NULL;\n    if (WSAAddressToStringA((struct sockaddr *)&addr, addrlen, 0, dst,\n        (LPDWORD)&size) != 0)\n        return NULL;\n    return dst;\n}\n#else /* _WIN32 */\n#define INET_NTOP inet_ntop\n#endif\n\nint addrinfo_get_list(struct addrinfo **ailist, const char *hostname,\n                             int port, int protocol) {\n    struct addrinfo hints;\n    int result;\n    char portstr[10]; /* largest port is 65535 */\n    memset(&hints, 0, sizeof hints);\n    hints.ai_family = AF_UNSPEC; /* IPv4 or IPv6 */\n    hints.ai_socktype = protocol;\n    hints.ai_protocol = (protocol == SOCK_STREAM ? IPPROTO_TCP : IPPROTO_UDP);\n    hints.ai_flags =\n#ifdef AI_ALL\n                     AI_ALL |       /* both IPv4 and IPv6 addrs */\n#endif\n#ifdef AI_V4MAPPED\n                     AI_V4MAPPED |  /* fallback to IPv4-mapped IPv6 addrs */\n#endif\n                     AI_PASSIVE;    /* listen to any addr if hostname is NULL */\n    portstr[0] = '\\0';\n    sprintf(portstr, \"%d\", port);\n    result = getaddrinfo(hostname, portstr, &hints, ailist);\n        /* There's currently a bug in the BSD libc where getaddrinfo()\n         * will return EAI_BADFLAGS for the AI_ALL and AI_V4MAPPED flags.\n         * NOTE: this also seems to affect Android!\n         * In practice, this means we can't use dual stack sockets,\n         * so we fall back to IPv4 networking... */\n    if (result == EAI_BADFLAGS)\n    {\n        static int warned = 0;\n        if (!warned)\n        {\n            fprintf(stderr, \"Warning: can't create IPv6 dual-stack socket - falling \"\n                \"back to IPv4. (This is a known bug in the BSD libc, which doesn't \"\n                \"implement the AI_ALL and AI_V4MAPPED flags for getaddrinfo().)\\n\");\n            warned = 1;\n        }\n        hints.ai_family = AF_INET;\n        hints.ai_flags = AI_PASSIVE;\n        result = getaddrinfo(hostname, portstr, &hints, ailist);\n    }\n    return result;\n}\n\nint addrinfo_ipv4_first(const struct addrinfo* ai1, const struct addrinfo* ai2)\n{\n    return (ai1->ai_family == AF_INET) ?\n        ((ai2->ai_family == AF_INET) ? 0 : -1) : 1;\n}\n\nint addrinfo_ipv6_first(const struct addrinfo* ai1, const struct addrinfo* ai2){\n    return (ai1->ai_family == AF_INET6) ?\n        ((ai2->ai_family == AF_INET6) ? 0 : -1) : 1;\n}\n\nvoid addrinfo_sort_list(struct addrinfo **ailist,\n    int (*compare)(const struct addrinfo*, const struct addrinfo*))\n{\n    struct addrinfo *result = NULL, *ai = (*ailist);\n    while (ai)\n    {\n        struct addrinfo *temp = ai;\n        ai = ai->ai_next;\n        if (result)\n        {\n            struct addrinfo *ai2 = result, *last = NULL;\n            while (ai2 && (compare(temp, ai2) >= 0))\n            {\n                last = ai2;\n                ai2 = ai2->ai_next;\n            }\n            if (last)\n            {\n                last->ai_next = temp;\n                temp->ai_next = ai2;\n            }\n            else\n            {\n                result = temp;\n                temp->ai_next = ai2;\n            }\n        }\n        else\n        {\n            result = temp;\n            temp->ai_next = NULL;\n        }\n    }\n    *ailist = result;\n}\n\nvoid addrinfo_print_list(const struct addrinfo *ailist)\n{\n    const struct addrinfo *ai;\n    char addrstr[INET6_ADDRSTRLEN];\n    for (ai = ailist; ai != NULL; ai = ai->ai_next)\n    {\n        char *ipver;\n        void *addr;\n        unsigned int port = 0;\n        if (ai->ai_family == AF_INET6)\n        {\n            struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)ai->ai_addr;\n            addr = &(sa6->sin6_addr);\n            ipver = \"IPv6\";\n            port = ntohs(sa6->sin6_port);\n        }\n        else if (ai->ai_family == AF_INET)\n        {\n            struct sockaddr_in *sa4 = (struct sockaddr_in *)ai->ai_addr;\n            addr = &(sa4->sin_addr);\n            ipver = \"IPv4\";\n            port = ntohs(sa4->sin_port);\n        }\n        else continue;\n        INET_NTOP(ai->ai_family, addr, addrstr, INET6_ADDRSTRLEN);\n        printf(\"%s %s %d\\n\", ipver, addrstr, port);\n    }\n}\n\nconst char* sockaddr_get_addrstr(const struct sockaddr *sa, char *addrstr,\n    int addrstrlen)\n{\n    const void *addr;\n    addrstr[0] = '\\0';\n    if (sa->sa_family == AF_INET6)\n    {\n        struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa;\n        addr = (const void *)&sa6->sin6_addr.s6_addr;\n    }\n    else if (sa->sa_family == AF_INET)\n    {\n        struct sockaddr_in *sa4 = (struct sockaddr_in *)sa;\n        addr = (const void *)&sa4->sin_addr.s_addr;\n    }\n    else return NULL;\n    return INET_NTOP(sa->sa_family, addr, addrstr, addrstrlen);\n}\n\nunsigned int sockaddr_get_port(const struct sockaddr *sa)\n{\n    if (sa->sa_family == AF_INET6)\n    {\n        struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa;\n        return ntohs(sa6->sin6_port);\n    }\n    else if (sa->sa_family == AF_INET)\n    {\n        struct sockaddr_in *sa4 = (struct sockaddr_in *)sa;\n        return ntohs(sa4->sin_port);\n    }\n    else\n        return 0;\n}\n\nvoid sockaddr_set_port(const struct sockaddr *sa, unsigned int port) {\n    if (sa->sa_family == AF_INET6)\n    {\n        struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa;\n        sa6->sin6_port = htons(port);\n    }\n    else if (sa->sa_family == AF_INET)\n    {\n        struct sockaddr_in *sa4 = (struct sockaddr_in *)sa;\n        sa4->sin_port = htons(port);\n    }\n}\n\nint sockaddr_is_multicast(const struct sockaddr *sa)\n{\n    if (sa->sa_family == AF_INET6)\n    {\n        struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa;\n        return (sa6->sin6_addr.s6_addr[0] == 0xFF);\n    }\n    else if (sa->sa_family == AF_INET)\n    {\n        struct sockaddr_in *sa4 = (struct sockaddr_in *)sa;\n        return ((ntohl(sa4->sin_addr.s_addr) & 0xF0000000) == 0xE0000000);\n    }\n    return 0;\n}\n\nint socket_init(void)\n{\n#ifdef _WIN32\n    static int initialized = 0;\n    if (!initialized)\n    {\n        short version = MAKEWORD(2, 0);\n        WSADATA nobby;\n        if (WSAStartup(version, &nobby))\n            return -1;\n        initialized = 1;\n    }\n#endif\n    return 0;\n}\n\n    /* kudos to https://stackoverflow.com/a/46062474/6063908 */\nint socket_connect(int socket, const struct sockaddr *addr,\n                   socklen_t addrlen, float timeout)\n{\n    /* set nonblocking and connect */\n    socket_set_nonblocking(socket, 1);\n\n    if (connect(socket, addr, addrlen) < 0)\n    {\n        int status;\n        struct timeval timeoutval;\n        fd_set writefds, errfds;\n    #ifdef _WIN32\n        if (socket_errno() != WSAEWOULDBLOCK)\n    #else\n        if (socket_errno() != EINPROGRESS)\n    #endif\n            return -1; /* break on \"real\" error */\n\n        /* block with select using timeout */\n        if (timeout < 0) timeout = 0;\n        timeoutval.tv_sec = (int)timeout;\n        timeoutval.tv_usec = (timeout - timeoutval.tv_sec) * 1000000;\n        FD_ZERO(&writefds);\n        FD_SET(socket, &writefds); /* socket is connected when writable */\n        FD_ZERO(&errfds);\n        FD_SET(socket, &errfds); /* catch exceptions */\n\n        status = select(socket+1, NULL, &writefds, &errfds, &timeoutval);\n        if (status < 0) /* select failed */\n        {\n            fprintf(stderr, \"socket_connect: select failed\");\n            return -1;\n        }\n        else if (status == 0) /* connection timed out */\n        {\n        #ifdef _WIN32\n            WSASetLastError(WSAETIMEDOUT);\n        #else\n            errno = ETIMEDOUT;\n        #endif\n            return -1;\n        }\n\n        if (FD_ISSET(socket, &errfds)) /* connection failed */\n        {\n            int err; socklen_t len = sizeof(err);\n            getsockopt(socket, SOL_SOCKET, SO_ERROR, (void *)&err, &len);\n        #ifdef _WIN32\n            WSASetLastError(err);\n        #else\n            errno = err;\n        #endif\n            return -1;\n        }\n    }\n    /* done, set blocking again */\n    socket_set_nonblocking(socket, 0);\n    return 0;\n}\n\nvoid socket_close(int socket)\n{\n#ifdef _WIN32\n    closesocket(socket);\n#else\n    if (socket < 0) return;\n    close(socket);\n#endif\n}\n\nunsigned int socket_get_port(int socket)\n{\n    struct sockaddr_storage sa;\n    socklen_t len = sizeof(sa);\n    if (getsockname(socket, (struct sockaddr *)&sa, &len) < 0)\n        return 0;\n\n    if (sa.ss_family == AF_INET6)\n    {\n        struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&sa;\n        return ntohs(sa6->sin6_port);\n    }\n    else if (sa.ss_family == AF_INET)\n    {\n        struct sockaddr_in *sa4 = (struct sockaddr_in *)&sa;\n        return ntohs(sa4->sin_port);\n    }\n    else\n        return 0;\n}\n\nint socket_set_boolopt(int socket, int level, int option_name, int bool_value)\n{\n    return setsockopt(socket, level, option_name,\n        (void *)&bool_value, sizeof(int));\n}\n\nint socket_set_nonblocking(int socket, int nonblocking)\n{\n#ifdef _WIN32\n    u_long modearg = nonblocking;\n    if (ioctlsocket(socket, FIONBIO, &modearg) != NO_ERROR)\n        return -1;\n#else\n    int sockflags = fcntl(socket, F_GETFL, 0);\n    if (nonblocking)\n        sockflags |= O_NONBLOCK;\n    else\n        sockflags &= ~O_NONBLOCK;\n    if (fcntl(socket, F_SETFL, sockflags) < 0)\n        return -1;\n#endif\n    return 0;\n}\n\nint socket_bytes_available(int socket)\n{\n#ifdef _WIN32\n    u_long n = 0;\n    if (ioctlsocket(socket, FIONREAD, &n) != NO_ERROR)\n        return -1;\n    return n;\n#else\n    int n = 0;\n    if (ioctl(socket, FIONREAD, &n) < 0)\n        return -1;\n    return n;\n#endif\n}\n\nint socket_join_multicast_group(int socket, const struct sockaddr *sa)\n{\n    if (sa->sa_family == AF_INET6)\n    {\n        struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa;\n        struct ipv6_mreq mreq6 = {0};\n        memcpy(&mreq6.ipv6mr_multiaddr, &sa6->sin6_addr,\n            sizeof(struct in6_addr));\n        return setsockopt(socket, IPPROTO_IPV6, IPV6_JOIN_GROUP,\n            (char *)&mreq6, sizeof(mreq6));\n    }\n    else if (sa->sa_family == AF_INET)\n    {\n        struct sockaddr_in *sa4 = (struct sockaddr_in *)sa;\n        struct ip_mreq mreq = {0};\n        mreq.imr_multiaddr.s_addr = sa4->sin_addr.s_addr;\n        mreq.imr_interface.s_addr = INADDR_ANY;\n        return setsockopt(socket, IPPROTO_IP, IP_ADD_MEMBERSHIP,\n            (char *)&mreq, sizeof(mreq));\n    }\n    return -1;\n}\n\nint socket_leave_multicast_group(int socket, const struct sockaddr *sa)\n{\n    if (sa->sa_family == AF_INET6)\n    {\n        struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa;\n        struct ipv6_mreq mreq6 = {0};\n        memcpy(&mreq6.ipv6mr_multiaddr, &sa6->sin6_addr,\n            sizeof(struct in6_addr));\n        return setsockopt(socket, IPPROTO_IPV6, IPV6_LEAVE_GROUP,\n            (char *)&mreq6, sizeof(mreq6));\n    }\n    else if (sa->sa_family == AF_INET)\n    {\n        struct sockaddr_in *sa4 = (struct sockaddr_in *)sa;\n        struct ip_mreq mreq = {0};\n        mreq.imr_multiaddr.s_addr = sa4->sin_addr.s_addr;\n        mreq.imr_interface.s_addr = INADDR_ANY;\n        return setsockopt(socket, IPPROTO_IP, IP_DROP_MEMBERSHIP,\n            (char *)&mreq, sizeof(mreq));\n    }\n    return -1;\n}\n\nint socket_errno(void)\n{\n#ifdef _WIN32\n    int err = WSAGetLastError();\n    if (err == 10044) /* WSAESOCKTNOSUPPORT */\n    {\n        fprintf(stderr,\n            \"Warning: you might not have TCP/IP \\\"networking\\\" turned on\\n\");\n    }\n    return err;\n#else\n    return errno;\n#endif\n}\n\nint socket_errno_udp(void)\n{\n#ifdef _WIN32\n    int err = socket_errno();\n    /* ignore WSAECONNRESET to prevent UDP sockets\n       from closing in case of a missing receiver */\n    if (err == 10054)\n        return 0;\n    else\n        return err;\n#else\n    return socket_errno();\n#endif\n}\n\nvoid socket_strerror(int err, char *buf, int size)\n{\n    if (size > 0)\n    {\n    #ifdef _WIN32\n        wchar_t wbuf[1024];\n        int count = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,\n            0, err, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), wbuf, 1024, NULL);\n        if (!count || !WideCharToMultiByte(CP_UTF8, 0, wbuf, count+1, buf, size, 0, 0))\n            *buf = '\\0';\n    #else\n        snprintf(buf, size, \"%s\", strerror(err));\n    #endif\n    }\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/s_net.h",
    "content": "/* Copyright (c) 2019 Dan Wilcox.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* networking headers and helper functions */\n\n#pragma once\n\n#ifdef _WIN32\n#include <winsock2.h>\n#include <ws2tcpip.h>\ntypedef int socklen_t;\n#else\n#include <arpa/inet.h>\n#include <sys/socket.h>\n#include <sys/select.h>\n#include <netinet/in.h>\n#include <netinet/tcp.h>\n#include <netdb.h>\n#endif\n\n#ifndef NET_MAXPACKETSIZE\n#define NET_MAXPACKETSIZE 65536\n#endif\n\n/* ----- socket address ----- */\n\n    /** getaddrinfo() convenience wrapper which generates a list of IPv4 & IPv6\n        addresses from a given address/hostname string, port, and protocol\n        (SOCK_STREAM or SOCK_DGRAM), set hostname to NULL for \"any\" address\n\n        returns 0 on success or < 0 on error\n\n        the ailist must be freed after usage using freeaddrinfo(),\n        status errno can be printed using gai_strerr()\n\n        basic usage example:\n\n        int sockfd, status;\n        struct addrinfo *ailist = NULL, *ai;\n        struct sockaddr_storage ss = {0}; // IPv4 or IPv6 addr\n\n        // generate addrinfo list\n        status = addrinfo_get_list(&ailist, \"127.0.0.1\", 5000, SOCK_DGRAM);\n        if (status != 0)\n        {\n            printf(\"bad host or port? %s (%d)\", gai_strerror(status), status);\n            return;\n        }\n\n        // try each addr until we find one that works\n        for (ai = ailist; ai != NULL; ai = ai->ai_next)\n        {\n            sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);\n            if (sockfd < 0) continue; // go to the next addr\n\n            // perform socket setsockopt(), bind(), connect(), etc as required,\n            // cleanup and continue to next addr on failure\n\n            // save addr that worked and exit loop\n            memcpy(&ss, ai->ai_addr, ai->ai_addrlen);\n            break;\n        }\n        freeaddrinfo(ailist); // cleanup\n\n        // confirm that socket setup succeeded\n        if (sockfd == -1)\n        {\n            int err = socket_errno();\n            printf(\"socket setup failed: %s (%d)\", strerror(errno), errno);\n            return;\n        }\n    */\nint addrinfo_get_list(struct addrinfo **ailist, const char *hostname,\n                      int port, int protocol);\n\n    /** sort an address list with a compare function */\nvoid addrinfo_sort_list(struct addrinfo **ailist,\n    int (*compare)(const struct addrinfo*, const struct addrinfo*));\n\n    /** compare function which puts IPv4 addresses first */\nint addrinfo_ipv4_first(const struct addrinfo* ai1, const struct addrinfo* ai2);\n\n    /** compare function which puts IPv6 addresses first */\nint addrinfo_ipv6_first(const struct addrinfo* ai1, const struct addrinfo* ai2);\n\n    /** print addrinfo linked list sockaddrs: IP version, hostname, port */\nvoid addrinfo_print_list(const struct addrinfo *ailist);\n\n    /** read address/hostname string from a sockaddr,\n        fills addrstr and returns pointer on success or NULL on failure */\nconst char* sockaddr_get_addrstr(const struct sockaddr *sa,\n                                 char *addrstr, int addrstrlen);\n\n    /** returns sockaddr port or 0 on failure */\nunsigned int sockaddr_get_port(const struct sockaddr *sa);\n\n    /** sets sockaddr port */\nvoid sockaddr_set_port(const struct sockaddr *sa, unsigned int port);\n\n    /** returns 1 if address is a IPv4 or IPv6 multicast address, otherwise 0 */\nint sockaddr_is_multicast(const struct sockaddr *sa);\n\n/* ----- socket ----- */\n\n    /** cross-platform initialization routine, returns -1 on failure */\nint socket_init(void);\n\n    /** connect a socket to an address with a settable timeout in seconds\n        returns -1 on error, use socket_errno() to get the actual error code */\nint socket_connect(int socket, const struct sockaddr *addr,\n                   socklen_t addrlen, float timeout);\n\n    /** cross-platform socket close() */\nvoid socket_close(int socket);\n\n    /** returns port or 0 on failure */\nunsigned int socket_get_port(int socket);\n\n    /** get number of immediately readable bytes for the socket\n        returns -1 on error or bytes available on success */\nint socket_bytes_available(int socket);\n\n    /** setsockopt() convenience wrapper for socket bool options */\nint socket_set_boolopt(int socket, int level, int option_name, int bool_value);\n\n    /** enable/disable socket non-blocking mode */\nint socket_set_nonblocking(int socket, int nonblocking);\n\n    /** join a multicast group address, returns < 0 on error */\nint socket_join_multicast_group(int socket, const struct sockaddr *sa);\n\n    /** leave a multicast group address, returns < 0 on error */\nint socket_leave_multicast_group(int socket, const struct sockaddr *sa);\n\n    /** cross-platform socket errno() which catches\n        WSAESOCKTNOSUPPORT on Windows */\nint socket_errno(void);\n\n    /** like socket_errno() but ignores WSAECONNRESET on Windows */\nint socket_errno_udp(void);\n\n    /** get an error string from errno */\nvoid socket_strerror(int err, char *buf, int size);\n"
  },
  {
    "path": "libs/libpd/pure-data/src/s_path.c",
    "content": "/* Copyright (c) 1999 Guenter Geiger and others.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/*\n * This file implements the loader for linux, which includes\n * a little bit of path handling.\n *\n * Generalized by MSP to provide an open_via_path function\n * and lists of files for all purposes.\n */\n\n/* #define DEBUG(x) x */\n#define DEBUG(x)\n\n#include <stdlib.h>\n#ifdef HAVE_UNISTD_H\n#include <unistd.h>\n#endif\n#include <sys/stat.h>\n#ifdef _WIN32\n#include <io.h>\n#include <windows.h>\n#endif\n\n#include <string.h>\n#include \"m_pd.h\"\n#include \"m_imp.h\"\n#include \"s_stuff.h\"\n#include \"s_utf8.h\"\n#include <stdio.h>\n#include <fcntl.h>\n\n#ifdef _LARGEFILE64_SOURCE\n# define open  open64\n# define lseek lseek64\n# define fstat fstat64\n# define stat  stat64\n#endif\n\n#include \"m_private_utils.h\"\n\n    /* change '/' characters to the system's native file separator */\nvoid sys_bashfilename(const char *from, char *to)\n{\n    char c;\n    while ((c = *from++))\n    {\n#ifdef _WIN32\n        if (c == '/') c = '\\\\';\n#endif\n        *to++ = c;\n    }\n    *to = 0;\n}\n\n    /* change the system's native file separator to '/' characters  */\nvoid sys_unbashfilename(const char *from, char *to)\n{\n    char c;\n    while ((c = *from++))\n    {\n#ifdef _WIN32\n        if (c == '\\\\') c = '/';\n#endif\n        *to++ = c;\n    }\n    *to = 0;\n}\n\n/* test if path is absolute or relative, based on leading /, env vars, ~, etc */\nint sys_isabsolutepath(const char *dir)\n{\n    if (dir[0] == '/' || dir[0] == '~'\n#ifdef _WIN32\n        || dir[0] == '%' || (dir[1] == ':' && dir[2] == '/')\n#endif\n        )\n    {\n        return 1;\n    }\n    else\n    {\n        return 0;\n    }\n}\n\n/* expand env vars and ~ at the beginning of a path and make a copy to return */\nstatic void sys_expandpath(const char *from, char *to, int bufsize)\n{\n    if ((strlen(from) == 1 && from[0] == '~') || (strncmp(from,\"~/\", 2) == 0))\n    {\n#ifdef _WIN32\n        const char *home = getenv(\"USERPROFILE\");\n#else\n        const char *home = getenv(\"HOME\");\n#endif\n        if (home)\n        {\n            strncpy(to, home, bufsize);\n            to[bufsize-1] = 0;\n            strncpy(to + strlen(to), from + 1, bufsize - strlen(to));\n            to[bufsize-1] = 0;\n        }\n        else *to = 0;\n    }\n    else\n    {\n        strncpy(to, from, bufsize);\n        to[bufsize-1] = 0;\n    }\n#ifdef _WIN32\n    {\n        char *buf = alloca(bufsize);\n        ExpandEnvironmentStrings(to, buf, bufsize-1);\n        buf[bufsize-1] = 0;\n        strncpy(to, buf, bufsize);\n        to[bufsize-1] = 0;\n    }\n#endif\n}\n\n/*******************  Utility functions used below ******************/\n\n/*!\n * \\brief copy until delimiter\n *\n * \\arg to destination buffer\n * \\arg to_len destination buffer length\n * \\arg from source buffer\n * \\arg delim string delimiter to stop copying on\n *\n * \\return position after delimiter in string.  If it was the last\n *         substring, return NULL.\n */\nstatic const char *strtokcpy(char *to, size_t to_len, const char *from, char delim)\n{\n    unsigned int i = 0;\n\n        for (; i < (to_len - 1) && from[i] && from[i] != delim; i++)\n                to[i] = from[i];\n        to[i] = '\\0';\n\n        if (i && from[i] != '\\0')\n                return from + i + 1;\n\n        return NULL;\n}\n\n/* add a single item to a namelist.  If \"allowdup\" is true, duplicates\nmay be added; otherwise they're dropped.  */\n\nt_namelist *namelist_append(t_namelist *listwas, const char *s, int allowdup)\n{\n    t_namelist *nl, *nl2;\n    nl2 = (t_namelist *)(getbytes(sizeof(*nl)));\n    nl2->nl_next = 0;\n    nl2->nl_string = (char *)getbytes(strlen(s) + 1);\n    strcpy(nl2->nl_string, s);\n    sys_unbashfilename(nl2->nl_string, nl2->nl_string);\n    if (!listwas)\n        return (nl2);\n    else\n    {\n        for (nl = listwas; ;)\n        {\n            if (!allowdup && !strcmp(nl->nl_string, s))\n            {\n                freebytes(nl2->nl_string, strlen(nl2->nl_string) + 1);\n                return (listwas);\n            }\n            if (!nl->nl_next)\n                break;\n            nl = nl->nl_next;\n        }\n        nl->nl_next = nl2;\n    }\n    return (listwas);\n}\n\n/* add a colon-separated list of names to a namelist */\n\n#ifdef _WIN32\n#define SEPARATOR ';'   /* in MSW the natural separator is semicolon instead */\n#else\n#define SEPARATOR ':'\n#endif\n\nt_namelist *namelist_append_files(t_namelist *listwas, const char *s)\n{\n    const char *npos;\n    char temp[MAXPDSTRING];\n    t_namelist *nl = listwas;\n\n    npos = s;\n    do\n    {\n        npos = strtokcpy(temp, sizeof(temp), npos, SEPARATOR);\n        if (! *temp) continue;\n        nl = namelist_append(nl, temp, 0);\n    }\n        while (npos);\n    return (nl);\n}\n\nvoid namelist_free(t_namelist *listwas)\n{\n    t_namelist *nl, *nl2;\n    for (nl = listwas; nl; nl = nl2)\n    {\n        nl2 = nl->nl_next;\n        t_freebytes(nl->nl_string, strlen(nl->nl_string) + 1);\n        t_freebytes(nl, sizeof(*nl));\n    }\n}\n\nconst char *namelist_get(const t_namelist *namelist, int n)\n{\n    int i;\n    const t_namelist *nl;\n    for (i = 0, nl = namelist; i < n && nl; i++, nl = nl->nl_next)\n        ;\n    return (nl ? nl->nl_string : 0);\n}\n\nint sys_usestdpath = 1;\n\nvoid sys_setextrapath(const char *p)\n{\n    char pathbuf[MAXPDSTRING];\n    namelist_free(STUFF->st_staticpath);\n    /* add standard place for users to install stuff first */\n#ifdef __gnu_linux__\n    sys_expandpath(\"~/.local/lib/pd/extra/\", pathbuf, MAXPDSTRING);\n    STUFF->st_staticpath = namelist_append(0, pathbuf, 0);\n    sys_expandpath(\"~/pd-externals\", pathbuf, MAXPDSTRING);\n    STUFF->st_staticpath = namelist_append(STUFF->st_staticpath, pathbuf, 0);\n    STUFF->st_staticpath = namelist_append(STUFF->st_staticpath,\n        \"/usr/local/lib/pd-externals\", 0);\n#endif\n\n#ifdef __APPLE__\n    sys_expandpath(\"~/Library/Pd\", pathbuf, MAXPDSTRING);\n    STUFF->st_staticpath = namelist_append(0, pathbuf, 0);\n    STUFF->st_staticpath = namelist_append(STUFF->st_staticpath, \"/Library/Pd\", 0);\n#endif\n\n#ifdef _WIN32\n    sys_expandpath(\"%AppData%/Pd\", pathbuf, MAXPDSTRING);\n    STUFF->st_staticpath = namelist_append(0, pathbuf, 0);\n    sys_expandpath(\"%CommonProgramFiles%/Pd\", pathbuf, MAXPDSTRING);\n    STUFF->st_staticpath = namelist_append(STUFF->st_staticpath, pathbuf, 0);\n#endif\n    /* add built-in \"extra\" path last so its checked last */\n    STUFF->st_staticpath = namelist_append(STUFF->st_staticpath, p, 0);\n}\n\n    /* try to open a file in the directory \"dir\", named \"name\"\"ext\",\n    for reading.  \"Name\" may have slashes.  The directory is copied to\n    \"dirresult\" which must be at least \"size\" bytes.  \"nameresult\" is set\n    to point to the filename (copied elsewhere into the same buffer).\n    The \"bin\" flag requests opening for binary (which only makes a difference\n    on Windows). */\n\nint sys_trytoopenone(const char *dir, const char *name, const char* ext,\n    char *dirresult, char **nameresult, unsigned int size, int bin)\n{\n    int fd;\n    char buf[MAXPDSTRING];\n    if (strlen(dir) + strlen(name) + strlen(ext) + 4 > size)\n        return (-1);\n    sys_expandpath(dir, buf, MAXPDSTRING);\n    strcpy(dirresult, buf);\n    if (*dirresult && dirresult[strlen(dirresult)-1] != '/')\n        strcat(dirresult, \"/\");\n    strcat(dirresult, name);\n    strcat(dirresult, ext);\n\n    DEBUG(post(\"looking for %s\",dirresult));\n        /* see if we can open the file for reading */\n    if ((fd=sys_open(dirresult, O_RDONLY)) >= 0)\n    {\n            /* in unix, further check that it's not a directory */\n#ifdef HAVE_UNISTD_H\n        struct stat statbuf;\n        int ok =  ((fstat(fd, &statbuf) >= 0) &&\n            !S_ISDIR(statbuf.st_mode));\n        if (!ok)\n        {\n            logpost(NULL, PD_VERBOSE, \"tried %s; stat failed or directory\",\n                dirresult);\n            close (fd);\n            fd = -1;\n        }\n        else\n#endif\n        {\n            char *slash;\n            logpost(NULL, PD_VERBOSE, \"tried %s and succeeded\", dirresult);\n            sys_unbashfilename(dirresult, dirresult);\n            slash = strrchr(dirresult, '/');\n            if (slash)\n            {\n                *slash = 0;\n                *nameresult = slash + 1;\n            }\n            else *nameresult = dirresult;\n\n            return (fd);\n        }\n    }\n    else\n    {\n        logpost(NULL, PD_VERBOSE, \"tried %s and failed\", dirresult);\n    }\n    return (-1);\n}\n\n    /* check if we were given an absolute pathname, if so try to open it\n    and return 1 to signal the caller to cancel any path searches */\nint sys_open_absolute(const char *name, const char* ext,\n    char *dirresult, char **nameresult, unsigned int size, int bin, int *fdp)\n{\n    if (sys_isabsolutepath(name))\n    {\n        char dirbuf[MAXPDSTRING], *z = strrchr(name, '/');\n        int dirlen;\n        if (!z)\n            return (0);\n        dirlen = (int)(z - name);\n        if (dirlen > MAXPDSTRING-1)\n            dirlen = MAXPDSTRING-1;\n        strncpy(dirbuf, name, dirlen);\n        dirbuf[dirlen] = 0;\n        *fdp = sys_trytoopenone(dirbuf, name+(dirlen+1), ext,\n            dirresult, nameresult, size, bin);\n        return (1);\n    }\n    else return (0);\n}\n\n/* search for a file in a specified directory, then along the globally\ndefined search path, using ext as filename extension.  The\nfd is returned, the directory ends up in the \"dirresult\" which must be at\nleast \"size\" bytes.  \"nameresult\" is set to point to the filename, which\nends up in the same buffer as dirresult.  Exception:\nif the 'name' starts with a slash or a letter, colon, and slash in MSW,\nthere is no search and instead we just try to open the file literally.  */\n\n/* see also canvas_open() which, in addition, searches down the\ncanvas-specific path. */\n\nstatic int do_open_via_path(const char *dir, const char *name,\n    const char *ext, char *dirresult, char **nameresult, unsigned int size,\n    int bin, t_namelist *searchpath)\n{\n    t_namelist *nl;\n    int fd = -1;\n\n        /* first check if \"name\" is absolute (and if so, try to open) */\n    if (sys_open_absolute(name, ext, dirresult, nameresult, size, bin, &fd))\n        return (fd);\n\n        /* otherwise \"name\" is relative; try the directory \"dir\" first. */\n    if ((fd = sys_trytoopenone(dir, name, ext,\n        dirresult, nameresult, size, bin)) >= 0)\n            return (fd);\n\n        /* next go through the temp paths from the commandline */\n    for (nl = STUFF->st_temppath; nl; nl = nl->nl_next)\n        if ((fd = sys_trytoopenone(nl->nl_string, name, ext,\n            dirresult, nameresult, size, bin)) >= 0)\n                return (fd);\n        /* next look in built-in paths like \"extra\" */\n    for (nl = searchpath; nl; nl = nl->nl_next)\n        if ((fd = sys_trytoopenone(nl->nl_string, name, ext,\n            dirresult, nameresult, size, bin)) >= 0)\n                return (fd);\n        /* next look in built-in paths like \"extra\" */\n    if (sys_usestdpath)\n        for (nl = STUFF->st_staticpath; nl; nl = nl->nl_next)\n            if ((fd = sys_trytoopenone(nl->nl_string, name, ext,\n                dirresult, nameresult, size, bin)) >= 0)\n                    return (fd);\n\n    *dirresult = 0;\n    *nameresult = dirresult;\n    return (-1);\n}\n\n    /* open via path, using the global search path. */\nint open_via_path(const char *dir, const char *name, const char *ext,\n    char *dirresult, char **nameresult, unsigned int size, int bin)\n{\n    return (do_open_via_path(dir, name, ext, dirresult, nameresult,\n        size, bin, STUFF->st_searchpath));\n}\n\n    /* open a file with a UTF-8 filename\n    This is needed because WIN32 does not support UTF-8 filenames, only UCS2.\n    Having this function prevents lots of #ifdefs all over the place.\n    */\n#ifdef _WIN32\nint sys_open(const char *path, int oflag, ...)\n{\n    int i, fd;\n    char pathbuf[MAXPDSTRING];\n    wchar_t ucs2path[MAXPDSTRING];\n    sys_bashfilename(path, pathbuf);\n    u8_utf8toucs2(ucs2path, MAXPDSTRING, pathbuf, MAXPDSTRING-1);\n    /* For the create mode, Win32 does not have the same possibilities,\n     * so we ignore the argument and just hard-code read/write. */\n    if (oflag & O_CREAT)\n        fd = _wopen(ucs2path, oflag | O_BINARY, _S_IREAD | _S_IWRITE);\n    else\n        fd = _wopen(ucs2path, oflag | O_BINARY);\n    return fd;\n}\n\nFILE *sys_fopen(const char *filename, const char *mode)\n{\n    char namebuf[MAXPDSTRING];\n    wchar_t ucs2buf[MAXPDSTRING];\n    wchar_t ucs2mode[MAXPDSTRING];\n    sys_bashfilename(filename, namebuf);\n    u8_utf8toucs2(ucs2buf, MAXPDSTRING, namebuf, MAXPDSTRING-1);\n    /* mode only uses ASCII, so no need for a full conversion, just copy it */\n    mbstowcs(ucs2mode, mode, MAXPDSTRING);\n    return (_wfopen(ucs2buf, ucs2mode));\n}\n#else\n#include <stdarg.h>\nint sys_open(const char *path, int oflag, ...)\n{\n    int i, fd;\n    char pathbuf[MAXPDSTRING];\n    sys_bashfilename(path, pathbuf);\n    if (oflag & O_CREAT)\n    {\n        mode_t mode;\n        int imode;\n        va_list ap;\n        va_start(ap, oflag);\n\n        /* Mac compiler complains if we just set mode = va_arg ... so, even\n        though we all know it's just an int, we explicitly va_arg to an int\n        and then convert.\n           -> http://www.mail-archive.com/bug-gnulib@gnu.org/msg14212.html\n           -> http://bugs.debian.org/647345\n        */\n\n        imode = va_arg (ap, int);\n        mode = (mode_t)imode;\n        va_end(ap);\n        fd = open(pathbuf, oflag, mode);\n    }\n    else\n        fd = open(pathbuf, oflag);\n    return fd;\n}\n\nFILE *sys_fopen(const char *filename, const char *mode)\n{\n  char namebuf[MAXPDSTRING];\n  sys_bashfilename(filename, namebuf);\n  return fopen(namebuf, mode);\n}\n#endif /* _WIN32 */\n\n   /* close a previously opened file\n   this is needed on platforms where you cannot open/close resources\n   across dll-boundaries, but we provide it for other platforms as well */\nint sys_close(int fd)\n{\n#ifdef _WIN32\n    return _close(fd);  /* Bill Gates is a big fat hen */\n#else\n    return close(fd);\n#endif\n}\n\nint sys_fclose(FILE *stream)\n{\n    return fclose(stream);\n}\n\n    /* Open a help file using the help search path.  We expect the \".pd\"\n    suffix here, even though we have to tear it back off for one of the\n    search attempts. */\nvoid open_via_helppath(const char *name, const char *dir)\n{\n    char realname[MAXPDSTRING], newname[MAXPDSTRING], dirbuf[MAXPDSTRING], *basename;\n        /* make up a silly \"dir\" if none is supplied */\n    const char *usedir = (*dir ? dir : \"./\");\n    int fd;\n\n        /* 1. \"objectname-help.pd\" */\n    strncpy(realname, name, MAXPDSTRING-10);\n    realname[MAXPDSTRING-10] = 0;\n    if (strlen(realname) > 3 && !strcmp(realname+strlen(realname)-3, \".pd\"))\n        realname[strlen(realname)-3] = 0;\n    strncpy(newname, realname, MAXPDSTRING-10);\n    strcat(realname, \"-help.pd\");\n    if ((fd = do_open_via_path(usedir, realname, \"\", dirbuf, &basename,\n        MAXPDSTRING, 0, STUFF->st_helppath)) >= 0)\n            goto gotone;\n\n        /* 2. \"help-objectname.pd\" */\n    strcpy(realname, \"help-\");\n    strncat(realname, name, MAXPDSTRING-10);\n    realname[MAXPDSTRING-1] = 0;\n    if ((fd = do_open_via_path(usedir, realname, \"\", dirbuf, &basename,\n        MAXPDSTRING, 0, STUFF->st_helppath)) >= 0)\n            goto gotone;\n    post(\"sorry, couldn't find help patch for \\\"%s\\\"\", newname);\n    return;\ngotone:\n    close (fd);\n    glob_evalfile(0, gensym((char*)basename), gensym(dirbuf));\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/s_print.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n#include \"m_pd.h\"\n#include <stdlib.h>\n#include <stdio.h>\n#include <stdarg.h>\n#include <string.h>\n#include <errno.h>\n#include \"s_stuff.h\"\n#include \"m_private_utils.h\"\n\nt_printhook sys_printhook = NULL;\nint sys_printtostderr;\n\n/* escape characters for tcl/tk */\nchar* pdgui_strnescape(char *dst, size_t dstlen, const char *src, size_t srclen)\n{\n    unsigned ptin = 0, ptout = 0;\n    if(!dst || !src)return 0;\n    while(1)\n    {\n        int c = src[ptin];\n        if (c == '\\\\' || c == '{' || c == '}' || c == '[' || c == ']') {\n            dst[ptout++] = '\\\\';\n            if (dstlen && ptout >= dstlen){\n                dst[ptout-1] = 0;\n                break;\n            }\n        }\n        dst[ptout] = c;\n        ptin++;\n        ptout++;\n        if (c==0) break;\n        if (srclen && ptin  >= srclen) break;\n        if (dstlen && ptout >= dstlen) break;\n    }\n\n    if(!dstlen || ptout < dstlen)\n        dst[ptout]=0;\n    else\n        dst[dstlen-1]=0;\n\n    return dst;\n}\n\nstatic void dopost(const char *s)\n{\n    if (STUFF->st_printhook)\n        (*STUFF->st_printhook)(s);\n    else if (sys_printtostderr || !sys_havegui())\n    {\n#ifdef _WIN32\n    #ifdef _MSC_VER\n        fwprintf(stderr, L\"%S\", s);\n    #else\n        fwprintf(stderr, L\"%s\", s);\n    #endif\n        fflush(stderr);\n#else\n        fprintf(stderr, \"%s\", s);\n#endif\n    }\n    else\n    {\n        pdgui_vmess(\"::pdwindow::post\", \"s\", s);\n    }\n}\n\nstatic void doerror(const void *object, const char *s)\n{\n    char upbuf[MAXPDSTRING];\n    upbuf[MAXPDSTRING-1]=0;\n\n    // what about sys_printhook_error ?\n    if (STUFF->st_printhook)\n    {\n        snprintf(upbuf, MAXPDSTRING-1, \"error: %s\", s);\n        (*STUFF->st_printhook)(upbuf);\n    }\n    else if (sys_printtostderr)\n    {\n#ifdef _WIN32\n    #ifdef _MSC_VER\n        fwprintf(stderr, L\"error: %S\", s);\n    #else\n        fwprintf(stderr, L\"error: %s\", s);\n    #endif\n        fflush(stderr);\n#else\n        fprintf(stderr, \"error: %s\", s);\n#endif\n    }\n    else\n        pdgui_vmess(\"::pdwindow::logpost\", \"ois\",\n                  object, 1, s);\n}\n\nstatic void dologpost(const void *object, const int level, const char *s)\n{\n    char upbuf[MAXPDSTRING];\n    upbuf[MAXPDSTRING-1]=0;\n        /* if it's a verbose message and we aren't set to 'verbose' just do\n            nothing */\n    if (level >= PD_VERBOSE && !sys_verbose)\n        return;\n    // what about sys_printhook_verbose ?\n    if (STUFF->st_printhook)\n    {\n        snprintf(upbuf, MAXPDSTRING-1, \"verbose(%d): %s\", level, s);\n        (*STUFF->st_printhook)(upbuf);\n    }\n    else if (sys_printtostderr)\n    {\n#ifdef _WIN32\n    #ifdef _MSC_VER\n        fwprintf(stderr, L\"verbose(%d): %S\", level, s);\n    #else\n        fwprintf(stderr, L\"verbose(%d): %s\", level, s);\n    #endif\n        fflush(stderr);\n#else\n        fprintf(stderr, \"verbose(%d): %s\", level, s);\n#endif\n    }\n    else\n        pdgui_vmess(\"::pdwindow::logpost\", \"ois\",\n                  object, level, s);\n}\n\nvoid logpost(const void *object, int level, const char *fmt, ...)\n{\n    char buf[MAXPDSTRING];\n    va_list ap;\n    if (level > PD_DEBUG && !sys_verbose) return;\n    va_start(ap, fmt);\n    vsnprintf(buf, MAXPDSTRING-1, fmt, ap);\n    va_end(ap);\n    strcat(buf, \"\\n\");\n\n    dologpost(object, level, buf);\n}\n\nvoid startlogpost(const void *object, const int level, const char *fmt, ...)\n{\n    char buf[MAXPDSTRING];\n    va_list ap;\n    if (level > PD_DEBUG && !sys_verbose) return;\n    va_start(ap, fmt);\n    vsnprintf(buf, MAXPDSTRING-1, fmt, ap);\n    va_end(ap);\n\n    dologpost(object, level, buf);\n}\n\n\nvoid post(const char *fmt, ...)\n{\n    char buf[MAXPDSTRING];\n    va_list ap;\n    t_int arg[8];\n    int i;\n    va_start(ap, fmt);\n    vsnprintf(buf, MAXPDSTRING-1, fmt, ap);\n    va_end(ap);\n    strcat(buf, \"\\n\");\n\n    dopost(buf);\n}\n\nvoid startpost(const char *fmt, ...)\n{\n    char buf[MAXPDSTRING];\n    va_list ap;\n    t_int arg[8];\n    int i;\n    va_start(ap, fmt);\n    vsnprintf(buf, MAXPDSTRING-1, fmt, ap);\n    va_end(ap);\n\n    dopost(buf);\n}\n\nvoid poststring(const char *s)\n{\n    dopost(\" \");\n\n    dopost(s);\n}\n\nvoid postatom(int argc, const t_atom *argv)\n{\n    int i;\n    for (i = 0; i < argc; i++)\n    {\n        char buf[MAXPDSTRING];\n        atom_string(argv+i, buf, MAXPDSTRING);\n        poststring(buf);\n    }\n}\n\nvoid postfloat(t_float f)\n{\n    char buf[80];\n    t_atom a;\n    SETFLOAT(&a, f);\n\n    postatom(1, &a);\n}\n\nvoid endpost(void)\n{\n    if (STUFF->st_printhook)\n        (*STUFF->st_printhook)(\"\\n\");\n    else if (sys_printtostderr)\n        fprintf(stderr, \"\\n\");\n    else post(\"\");\n}\n\n  /* keep this in the Pd app for binary extern compatibility but don't\n  include in libpd because it conflicts with the posix pd_error(0, ) function. */\n#ifdef PD_INTERNAL\nEXTERN void error(const char *fmt, ...)\n{\n    char buf[MAXPDSTRING];\n    va_list ap;\n    t_int arg[8];\n    int i;\n\n    va_start(ap, fmt);\n    vsnprintf(buf, MAXPDSTRING-1, fmt, ap);\n    va_end(ap);\n    strcat(buf, \"\\n\");\n\n    doerror(NULL, buf);\n}\n#endif\n\n/* deprecated in favor of logpost() */\nvoid verbose(int level, const char *fmt, ...)\n{\n    char buf[MAXPDSTRING];\n    va_list ap;\n\n    if (level > sys_verbose) return;\n\n    va_start(ap, fmt);\n    vsnprintf(buf, MAXPDSTRING-1, fmt, ap);\n    va_end(ap);\n    strcat(buf, \"\\n\");\n\n        /* log levels for verbose() traditionally start at -3,\n        so we have to adjust it before passing it on to dologpost() */\n    dologpost(NULL, level + 3, buf);\n}\n\n    /* here's the good way to log errors -- keep a pointer to the\n    offending or offended object around so the user can search for it\n    later. */\n\nstatic const void *error_object;\nstatic char error_string[256];\nvoid canvas_finderror(const void *object);\n\nvoid pd_error(const void *object, const char *fmt, ...)\n{\n    char buf[MAXPDSTRING];\n    va_list ap;\n    t_int arg[8];\n    int i;\n    static int saidit = 0;\n\n    va_start(ap, fmt);\n    vsnprintf(buf, MAXPDSTRING-1, fmt, ap);\n    va_end(ap);\n    strcat(buf, \"\\n\");\n\n    doerror(object, buf);\n\n    error_object = object;\n    strncpy(error_string, buf, 256);\n    error_string[255] = 0;\n\n    if (object && !saidit)\n    {\n        if (sys_havegui())\n            logpost(NULL, 4,\n                \"... you might be able to track this down from the Find menu.\");\n        saidit = 1;\n    }\n}\n\nvoid glob_finderror(t_pd *dummy)\n{\n    if (!error_object)\n        post(\"no findable error yet\");\n    else\n    {\n        post(\"last trackable error:\");\n        post(\"%s\", error_string);\n        canvas_finderror(error_object);\n    }\n}\n\nvoid glob_findinstance(t_pd *dummy, t_symbol*s)\n{\n    // revert s to (potential) pointer to object\n    PD_LONGINTTYPE obj = 0;\n    const char*addr;\n    if(!s || !s->s_name)\n        return;\n    addr = s->s_name;\n    if (('.' != addr[0]) && ('0' != addr[0]))\n        return;\n    if (!sscanf(addr+1, \"x%lx\", &obj))\n        return;\n\n    if(!obj)\n        return;\n\n    canvas_finderror((void *)obj);\n}\n\nvoid bug(const char *fmt, ...)\n{\n    char buf[MAXPDSTRING];\n    va_list ap;\n    t_int arg[8];\n    int i;\n    va_start(ap, fmt);\n    vsnprintf(buf, MAXPDSTRING-1, fmt, ap);\n    va_end(ap);\n\n    pd_error(0, \"consistency check failed: %s\", buf);\n}\n\n    /* don't use these.  They're included for binary compatibility with\n    old externs but never worked and now do nothing. */\nvoid sys_logerror(const char *object, const char *s) {}\nvoid sys_unixerror(const char *object) {}\nvoid sys_ouch(void) {}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/s_stuff.h",
    "content": "#pragma once\n/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* Audio and MIDI I/O, and other scheduling and system stuff. */\n\n/* NOTE: this file describes Pd implementation details which may change\nin future releases.  The public (stable) API is in m_pd.h. */\n\n/* in s_path.c */\n\ntypedef struct _namelist    /* element in a linked list of stored strings */\n{\n    struct _namelist *nl_next;  /* next in list */\n    char *nl_string;            /* the string */\n} t_namelist;\n\nt_namelist *namelist_append(t_namelist *listwas, const char *s, int allowdup);\nEXTERN t_namelist *namelist_append_files(t_namelist *listwas, const char *s);\nvoid namelist_free(t_namelist *listwas);\nconst char *namelist_get(const t_namelist *namelist, int n);\nvoid sys_setextrapath(const char *p);\nextern int sys_usestdpath;\nint sys_open_absolute(const char *name, const char* ext,\n    char *dirresult, char **nameresult, unsigned int size, int bin, int *fdp);\nint sys_trytoopenone(const char *dir, const char *name, const char* ext,\n    char *dirresult, char **nameresult, unsigned int size, int bin);\nt_symbol *sys_decodedialog(t_symbol *s);\n\n/* s_file.c */\n\nvoid sys_loadpreferences(const char *filename, int startingup);\nvoid sys_savepreferences(const char *filename);\nextern int sys_defeatrt;\nextern t_symbol *sys_flags;\n\n/* s_main.c */\nextern int sys_debuglevel;\nextern int sys_verbose;\nextern int sys_noloadbang;\nEXTERN int sys_havegui(void);\nextern const char *sys_guicmd;\n\nEXTERN int sys_nearestfontsize(int fontsize);\n\nextern int sys_defaultfont;\nEXTERN t_symbol *sys_libdir;    /* library directory for auxiliary files */\n\n/* s_loader.c */\n\ntypedef int (*loader_t)(t_canvas *canvas, const char *classname, const char*path); /* callback type */\nEXTERN int sys_load_lib(t_canvas *canvas, const char *classname);\nEXTERN void sys_register_loader(loader_t loader);\nEXTERN const char**sys_get_dllextensions(void);\n\n                        /* s_audio.c */\n\n#define MAXAUDIOINDEV 4\n#define MAXAUDIOOUTDEV 4\n\ntypedef struct _audiosettings\n{\n    int a_api;\n    int a_nindev;\n    int a_indevvec[MAXAUDIOINDEV];\n    int a_nchindev;\n    int a_chindevvec[MAXAUDIOINDEV];\n    int a_noutdev;\n    int a_outdevvec[MAXAUDIOOUTDEV];\n    int a_nchoutdev;\n    int a_choutdevvec[MAXAUDIOOUTDEV];\n    int a_srate;\n    int a_advance;\n    int a_callback;\n    int a_blocksize;\n} t_audiosettings;\n\n#define SENDDACS_NO 0           /* return values for sys_send_dacs() */\n#define SENDDACS_YES 1\n#define SENDDACS_SLEPT 2\n\n#define DEFDACBLKSIZE 64\n#define DEFDACSAMPLERATE 48000\n\n                    /* s_audio.c */\n#define API_NONE 0\n#define API_ALSA 1\n#define API_OSS 2\n#define API_MMIO 3\n#define API_PORTAUDIO 4\n#define API_JACK 5\n#define API_SGI 6           /* gone */\n#define API_AUDIOUNIT 7\n#define API_ESD 8           /* no idea what this was, probably gone now */\n#define API_DUMMY 9\n\n    /* figure out which API should be the default.  The one we judge most\n    likely to offer a working device takes precedence so that if you\n    start up Pd for the first time there's a reasonable chance you'll have\n    sound.  (You'd think portaudio would be best but it seems to default\n    to jack on linux, and on Windows we only use it for ASIO).\n    If nobody shows up, define DUMMY and make it the default.*/\n#if defined(USEAPI_ALSA)\n# define API_DEFAULT API_ALSA\n# define API_DEFSTRING \"ALSA\"\n#elif defined(USEAPI_PORTAUDIO)\n# define API_DEFAULT API_PORTAUDIO\n# define API_DEFSTRING \"portaudio\"\n#elif defined(USEAPI_OSS)\n# define API_DEFAULT API_OSS\n# define API_DEFSTRING \"OSS\"\n#elif defined(USEAPI_AUDIOUNIT)\n# define API_DEFAULT API_AUDIOUNIT\n# define API_DEFSTRING \"AudioUnit\"\n#elif defined(USEAPI_ESD)\n# define API_DEFAULT API_ESD\n# define API_DEFSTRING \"ESD (?)\"\n#elif defined(USEAPI_JACK)\n# define API_DEFAULT API_JACK\n# define API_DEFSTRING \"Jack audio connection kit\"\n#elif defined(USEAPI_MMIO)\n# define API_DEFAULT API_MMIO\n# define API_DEFSTRING \"MMIO\"\n#else\n# ifndef USEAPI_DUMMY   /* we need at least one so bring in the dummy */\n# define USEAPI_DUMMY\n# endif /* USEAPI_DUMMY */\n# define API_DEFAULT API_DUMMY\n# define API_DEFSTRING \"dummy audio\"\n#endif\n\n#define DEFAULTAUDIODEV 0\n\n#define DEFMIDIDEV 0\n\n#define DEFAULTSRATE 44100\n#if defined(_WIN32)\n#define DEFAULTADVANCE 80\n#elif defined(__APPLE__)\n#define DEFAULTADVANCE 5    /* this is in addition to their own delay */\n#else\n#define DEFAULTADVANCE 25\n#endif\n\ntypedef void (*t_audiocallback)(void);\n\nextern int sys_schedadvance;\n\nvoid sys_set_audio_state(int onoff);\nint sys_send_dacs(void);\nvoid sys_reportidle(void);\nvoid sys_listdevs(void);\nEXTERN void sys_set_audio_settings(t_audiosettings *as);\nEXTERN void sys_get_audio_settings(t_audiosettings *as);\nEXTERN void sys_reopen_audio(void);\nEXTERN void sys_close_audio(void);\n    /* return true if the interface prefers always being open (ala jack) : */\nEXTERN int audio_shouldkeepopen(void);\nEXTERN int audio_isopen(void);     /* true if audio interface is open */\nEXTERN int sys_audiodevnametonumber(int output, const char *name);\nEXTERN void sys_audiodevnumbertoname(int output, int devno, char *name,\n    int namesize);\nEXTERN void sys_get_audio_devs(char *indevlist, int *nindevs,\n                          char *outdevlist, int *noutdevs, int *canmulti, int *cancallback,\n                          int maxndev, int devdescsize, int api);\nEXTERN void sys_get_audio_apis(char *buf);\n\n\n        /* audio API specific functions */\nint pa_open_audio(int inchans, int outchans, int rate, t_sample *soundin,\n    t_sample *soundout, int framesperbuf, int nbuffers,\n    int indeviceno, int outdeviceno, t_audiocallback callback);\nvoid pa_close_audio(void);\nint pa_send_dacs(void);\nvoid pa_listdevs(void);\nvoid pa_getdevs(char *indevlist, int *nindevs,\n    char *outdevlist, int *noutdevs, int *canmulti,\n        int maxndev, int devdescsize);\n\nint oss_open_audio(int naudioindev, int *audioindev, int nchindev,\n    int *chindev, int naudiooutdev, int *audiooutdev, int nchoutdev,\n    int *choutdev, int rate, int blocksize);\nvoid oss_close_audio(void);\nint oss_send_dacs(void);\nvoid oss_reportidle(void);\nvoid oss_getdevs(char *indevlist, int *nindevs,\n    char *outdevlist, int *noutdevs, int *canmulti,\n        int maxndev, int devdescsize);\n\nint alsa_open_audio(int naudioindev, int *audioindev, int nchindev,\n    int *chindev, int naudiooutdev, int *audiooutdev, int nchoutdev,\n    int *choutdev, int rate, int blocksize);\nvoid alsa_close_audio(void);\nint alsa_send_dacs(void);\nvoid alsa_reportidle(void);\nvoid alsa_getdevs(char *indevlist, int *nindevs,\n    char *outdevlist, int *noutdevs, int *canmulti,\n        int maxndev, int devdescsize);\n\nint jack_open_audio(int inchans, int outchans, t_audiocallback callback);\nvoid jack_close_audio(void);\nint jack_send_dacs(void);\nvoid jack_reportidle(void);\nvoid jack_getdevs(char *indevlist, int *nindevs,\n    char *outdevlist, int *noutdevs, int *canmulti,\n        int maxndev, int devdescsize);\nvoid jack_listdevs(void);\nvoid jack_client_name(const char *name);\nvoid jack_autoconnect(int);\n\nint mmio_open_audio(int naudioindev, int *audioindev,\n    int nchindev, int *chindev, int naudiooutdev, int *audiooutdev,\n    int nchoutdev, int *choutdev, int rate, int blocksize);\nvoid mmio_close_audio(void);\nvoid mmio_reportidle(void);\nint mmio_send_dacs(void);\nvoid mmio_getdevs(char *indevlist, int *nindevs,\n    char *outdevlist, int *noutdevs, int *canmulti,\n        int maxndev, int devdescsize);\n\nint audiounit_open_audio(int naudioindev, int *audioindev, int nchindev,\n    int *chindev, int naudiooutdev, int *audiooutdev, int nchoutdev,\n    int *choutdev, int rate);\nvoid audiounit_close_audio(void);\nint audiounit_send_dacs(void);\nvoid audiounit_listdevs(void);\nvoid audiounit_getdevs(char *indevlist, int *nindevs,\n    char *outdevlist, int *noutdevs, int *canmulti,\n        int maxndev, int devdescsize);\n\nint esd_open_audio(int naudioindev, int *audioindev, int nchindev,\n    int *chindev, int naudiooutdev, int *audiooutdev, int nchoutdev,\n    int *choutdev, int rate);\nvoid esd_close_audio(void);\nint esd_send_dacs(void);\nvoid esd_listdevs(void);\nvoid esd_getdevs(char *indevlist, int *nindevs,\n    char *outdevlist, int *noutdevs, int *canmulti,\n        int maxndev, int devdescsize);\n\nint dummy_open_audio(int nin, int nout, int sr);\nint dummy_close_audio(void);\nint dummy_send_dacs(void);\nvoid dummy_getdevs(char *indevlist, int *nindevs, char *outdevlist,\n    int *noutdevs, int *canmulti, int maxndev, int devdescsize);\nvoid dummy_listdevs(void);\n\n                    /* s_midi.c */\n#define MAXMIDIINDEV 16         /* max. number of input ports */\n#define MAXMIDIOUTDEV 16        /* max. number of output ports */\nextern int sys_midiapi;\nextern int sys_nmidiin;\nextern int sys_nmidiout;\nextern int sys_midiindevlist[];\nextern int sys_midioutdevlist[];\n\nEXTERN void sys_open_midi(int nmidiin, int *midiinvec,\n    int nmidiout, int *midioutvec, int enable);\n\nEXTERN void sys_get_midi_apis(char *buf);\nEXTERN void sys_get_midi_devs(char *indevlist, int *nindevs,\n    char *outdevlist, int *noutdevs,\n   int maxndev, int devdescsize);\nEXTERN void sys_get_midi_params(int *pnmidiindev, int *pmidiindev,\n    int *pnmidioutdev, int *pmidioutdev);\nEXTERN int sys_mididevnametonumber(int output, const char *name);\nEXTERN void sys_mididevnumbertoname(int output, int devno, char *name,\n    int namesize);\n\nEXTERN void sys_reopen_midi(void);\nEXTERN void sys_close_midi(void);\nEXTERN void sys_putmidimess(int portno, int a, int b, int c);\nEXTERN void sys_putmidibyte(int portno, int a);\nEXTERN void sys_poll_midi(void);\nEXTERN void sys_midibytein(int portno, int byte);\n\nvoid sys_listmididevs(void);\nEXTERN void sys_set_midi_api(int whichapi);\n\n    /* implemented in the system dependent MIDI code (s_midi_pm.c, etc. ) */\nvoid midi_getdevs(char *indevlist, int *nindevs,\n    char *outdevlist, int *noutdevs, int maxndev, int devdescsize);\nvoid sys_do_open_midi(int nmidiindev, int *midiindev,\n    int nmidioutdev, int *midioutdev);\n\n#ifdef USEAPI_ALSA\nEXTERN void sys_alsa_putmidimess(int portno, int a, int b, int c);\nEXTERN void sys_alsa_putmidibyte(int portno, int a);\nEXTERN void sys_alsa_poll_midi(void);\nEXTERN void sys_alsa_close_midi(void);\n\n\n    /* implemented in the system dependent MIDI code (s_midi_pm.c, etc. ) */\nvoid midi_alsa_getdevs(char *indevlist, int *nindevs,\n    char *outdevlist, int *noutdevs, int maxndev, int devdescsize);\nvoid sys_alsa_do_open_midi(int nmidiindev, int *midiindev,\n    int nmidioutdev, int *midioutdev);\n#endif\n\n/* m_sched.c */\nEXTERN void sys_log_error(int type);\n#define ERR_NOTHING 0\n#define ERR_ADCSLEPT 1\n#define ERR_DACSLEPT 2\n#define ERR_RESYNC 3\n#define ERR_DATALATE 4\n\n#define SCHED_AUDIO_NONE 0\n#define SCHED_AUDIO_POLL 1\n#define SCHED_AUDIO_CALLBACK 2\nvoid sched_set_using_audio(int flag);\nextern int sys_sleepgrain;      /* override value set in command line */\nEXTERN int sched_get_sleepgrain( void);     /* returns actual value */\n\n/* s_inter.c */\n\nEXTERN void sys_microsleep( void);\nEXTERN void sys_init_fdpoll(void);\n\nEXTERN void sys_bail(int exitcode);\nEXTERN int sys_pollgui(void);\n\nEXTERN_STRUCT _socketreceiver;\n#define t_socketreceiver struct _socketreceiver\n\ntypedef void (*t_socketnotifier)(void *x, int n);\ntypedef void (*t_socketreceivefn)(void *x, t_binbuf *b);\n    /* from addr sockaddr_storage struct, optional */\ntypedef void (*t_socketfromaddrfn)(void *x, const void *fromaddr);\n\nEXTERN t_socketreceiver *socketreceiver_new(void *owner,\n    t_socketnotifier notifier, t_socketreceivefn socketreceivefn, int udp);\nEXTERN void socketreceiver_read(t_socketreceiver *x, int fd);\nEXTERN void socketreceiver_set_fromaddrfn(t_socketreceiver *x,\n    t_socketfromaddrfn fromaddrfn);\nEXTERN void sys_sockerror(const char *s);\nEXTERN void sys_closesocket(int fd);\nEXTERN unsigned char *sys_getrecvbuf(unsigned int *size);\n\ntypedef void (*t_fdpollfn)(void *ptr, int fd);\nEXTERN void sys_addpollfn(int fd, t_fdpollfn fn, void *ptr);\nEXTERN void sys_rmpollfn(int fd);\n#if defined(USEAPI_OSS) || defined(USEAPI_ALSA)\nvoid sys_setalarm(int microsec);\n#endif\n\nvoid sys_set_priority(int higher);\nextern int sys_hipriority;      /* real-time flag, true if priority boosted */\n\n/* s_print.c */\n\ntypedef void (*t_printhook)(const char *s);\n\n/* set this to override printing; used as default for STUFF->st_printhook */\nextern t_printhook sys_printhook;\nextern int sys_printtostderr;\n\n/* jsarlo { */\n\nEXTERN int sys_externalschedlib;\n\nEXTERN t_sample* get_sys_soundout(void);\nEXTERN t_sample* get_sys_soundin(void);\nEXTERN int* get_sys_main_advance(void);\nEXTERN double* get_sys_time_per_dsp_tick(void);\nEXTERN int* get_sys_schedblocksize(void);\nEXTERN double* get_sys_time(void);\nEXTERN t_float* get_sys_dacsr(void);\nEXTERN int* get_sys_sleepgrain(void);\nEXTERN int* get_sys_schedadvance(void);\n\nEXTERN void sys_initmidiqueue(void);\nEXTERN void sched_tick(void);\nEXTERN void sys_pollmidiqueue(void);\nEXTERN void sys_setchsr(int chin, int chout, int sr);\n\nEXTERN void inmidi_realtimein(int portno, int cmd);\nEXTERN void inmidi_byte(int portno, int byte);\nEXTERN void inmidi_sysex(int portno, int byte);\nEXTERN void inmidi_noteon(int portno, int channel, int pitch, int velo);\nEXTERN void inmidi_controlchange(int portno,\n                                 int channel,\n                                 int ctlnumber,\n                                 int value);\nEXTERN void inmidi_programchange(int portno, int channel, int value);\nEXTERN void inmidi_pitchbend(int portno, int channel, int value);\nEXTERN void inmidi_aftertouch(int portno, int channel, int value);\nEXTERN void inmidi_polyaftertouch(int portno,\n                                  int channel,\n                                  int pitch,\n                                  int value);\n/* } jsarlo */\nEXTERN int sys_zoom_open;\n\nstruct _instancestuff\n{\n    t_namelist *st_externlist;\n    t_namelist *st_searchpath;\n    t_namelist *st_staticpath;\n    t_namelist *st_helppath;\n    t_namelist *st_temppath;    /* temp search paths ie. -path on commandline */\n    int st_schedblocksize;      /* audio block size for scheduler */\n    int st_blocksize;           /* audio I/O block size in sample frames */\n    t_float st_dacsr;           /* I/O sample rate */\n    int st_inchannels;\n    int st_outchannels;\n    t_sample *st_soundout;\n    t_sample *st_soundin;\n    double st_time_per_dsp_tick;    /* obsolete - included for GEM?? */\n    t_printhook st_printhook;   /* set this to override per-instance printing */\n    void *st_impdata; /* optional implementation-specific data for libpd, etc */\n};\n\n#define STUFF (pd_this->pd_stuff)\n\n/* escape characters for tcl/tk\n * escapes special characters (\"{}\\\") in the string 'src', which\n * has a maximum length of 'srclen' and might be 0-terminated,\n * and writes them into the 'dstlen' sized output buffer 'dst'\n * the result is zero-terminated; if the 'dst' buffer cannot hold the\n * fully escaped 'src' string, the result might be incomplete.\n * 'srclen' can be 0, in which case the 'src' string must be 0-terminated.\n */\nEXTERN char*pdgui_strnescape(char* dst, size_t dstlen, const char*src, size_t srclen);\n"
  },
  {
    "path": "libs/libpd/pure-data/src/s_utf8.c",
    "content": "/*\n  Basic UTF-8 manipulation routines\n  by Jeff Bezanson\n  placed in the public domain Fall 2005\n\n  This code is designed to provide the utilities you need to manipulate\n  UTF-8 as an internal string encoding. These functions do not perform the\n  error checking normally needed when handling UTF-8 data, so if you happen\n  to be from the Unicode Consortium you will want to flay me alive.\n  I do this because error checking can be performed at the boundaries (I/O),\n  with these routines reserved for higher performance on data known to be\n  valid.\n\n  modified by Bryan Jurish (moo) March 2009\n  + removed some unneeded functions (escapes, printf etc), added others\n\n  modified by IOhannes m zmölnig (umlaeute) Nov 2021\n  + convert native strings to UTF-8\n*/\n#include <string.h>\n#include <stdarg.h>\n\n#include \"s_utf8.h\"\n\nstatic const uint32_t offsetsFromUTF8[6] = {\n    0x00000000UL, 0x00003080UL, 0x000E2080UL,\n    0x03C82080UL, 0xFA082080UL, 0x82082080UL\n};\n\nstatic const char trailingBytesForUTF8[256] = {\n    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\n    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\n    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\n    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\n    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\n    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\n    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,\n    2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5\n};\n\n\n/* returns length of next utf-8 sequence */\nint u8_seqlen(const char *s)\n{\n    return trailingBytesForUTF8[(unsigned int)(unsigned char)s[0]] + 1;\n}\n\n/* conversions without error checking\n   only works for valid UTF-8, i.e. no 5- or 6-byte sequences\n   srcsz = source size in bytes, or -1 if 0-terminated\n   sz = dest size in # of wide characters\n\n   returns # characters converted\n   dest will always be L'\\0'-terminated, even if there isn't enough room\n   for all the characters.\n   if sz = srcsz+1 (i.e. 4*srcsz+4 bytes), there will always be enough space.\n*/\nint u8_utf8toucs2(uint16_t *dest, int sz, const char *src, int srcsz)\n{\n    uint16_t ch;\n    const char *src_end = src + srcsz;\n    int nb;\n    int i=0;\n\n    while (i < sz-1) {\n        nb = trailingBytesForUTF8[(unsigned char)*src];\n        if (srcsz == -1) {\n            if (*src == 0)\n                goto done_toucs;\n        }\n        else {\n            if (src + nb >= src_end)\n                goto done_toucs;\n        }\n        ch = 0;\n        switch (nb) {\n        case 3: ch += (unsigned char)*src++; ch <<= 6; /* falls through */\n        case 2: ch += (unsigned char)*src++; ch <<= 6; /* falls through */\n        case 1: ch += (unsigned char)*src++; ch <<= 6; /* falls through */\n        case 0: ch += (unsigned char)*src++; /* falls through */\n        }\n        ch -= offsetsFromUTF8[nb];\n        dest[i++] = ch;\n    }\n done_toucs:\n    dest[i] = 0;\n    return i;\n}\n\n/* srcsz = number of source characters, or -1 if 0-terminated\n   sz = size of dest buffer in bytes\n\n   returns # characters converted\n   dest will only be '\\0'-terminated if there is enough space. this is\n   for consistency; imagine there are 2 bytes of space left, but the next\n   character requires 3 bytes. in this case we could NUL-terminate, but in\n   general we can't when there's insufficient space. therefore this function\n   only NUL-terminates if all the characters fit, and there's space for\n   the NUL as well.\n   the destination string will never be bigger than the source string.\n*/\nint u8_ucs2toutf8(char *dest, int sz, const uint16_t *src, int srcsz)\n{\n    uint16_t ch;\n    int i = 0;\n    char *dest_end = dest + sz;\n\n    while (srcsz<0 ? src[i]!=0 : i < srcsz) {\n        ch = src[i];\n        if (ch < 0x80) {\n            if (dest >= dest_end)\n                return i;\n            *dest++ = (char)ch;\n        }\n        else if (ch < 0x800) {\n            if (dest >= dest_end-1)\n                return i;\n            *dest++ = (ch>>6) | 0xC0;\n            *dest++ = (ch & 0x3F) | 0x80;\n        }\n        else {\n            if (dest >= dest_end-2)\n                return i;\n            *dest++ = (ch>>12) | 0xE0;\n            *dest++ = ((ch>>6) & 0x3F) | 0x80;\n            *dest++ = (ch & 0x3F) | 0x80;\n        }\n        i++;\n    }\n    if (dest < dest_end)\n        *dest = '\\0';\n    return i;\n}\n\n/* moo: get byte length of character number, or 0 if not supported */\nint u8_wc_nbytes(uint32_t ch)\n{\n  if (ch < 0x80) return 1;\n  if (ch < 0x800) return 2;\n  if (ch < 0x10000) return 3;\n  if (ch < 0x200000) return 4;\n  return 0; /*-- bad input --*/\n}\n\nint u8_wc_toutf8(char *dest, uint32_t ch)\n{\n    if (ch < 0x80) {\n        dest[0] = (char)ch;\n        return 1;\n    }\n    if (ch < 0x800) {\n        dest[0] = (ch>>6) | 0xC0;\n        dest[1] = (ch & 0x3F) | 0x80;\n        return 2;\n    }\n    if (ch < 0x10000) {\n        dest[0] = (ch>>12) | 0xE0;\n        dest[1] = ((ch>>6) & 0x3F) | 0x80;\n        dest[2] = (ch & 0x3F) | 0x80;\n        return 3;\n    }\n    if (ch < 0x110000) {\n        dest[0] = (ch>>18) | 0xF0;\n        dest[1] = ((ch>>12) & 0x3F) | 0x80;\n        dest[2] = ((ch>>6) & 0x3F) | 0x80;\n        dest[3] = (ch & 0x3F) | 0x80;\n        return 4;\n    }\n    return 0;\n}\n\n/*-- moo --*/\nint u8_wc_toutf8_nul(char *dest, uint32_t ch)\n{\n  int sz = u8_wc_toutf8(dest,ch);\n  dest[sz] = '\\0';\n  return sz;\n}\n\n/* charnum => byte offset */\nint u8_offset(const char *str, int charnum)\n{\n    const char *string = str;\n\n    while (charnum > 0 && *string != '\\0') {\n        if (*string++ & 0x80) {\n            if (!isutf(*string)) {\n                ++string;\n                if (!isutf(*string)) {\n                    ++string;\n                    if (!isutf(*string)) {\n                        ++string;\n                    }\n                }\n            }\n        }\n        --charnum;\n    }\n\n    return (int)(string - str);\n}\n\n/* byte offset => charnum */\nint u8_charnum(const char *s, int offset)\n{\n    int charnum = 0;\n    const char *string = s;\n    const char *const end = string + offset;\n\n    while (string < end && *string != '\\0') {\n        if (*string++ & 0x80) {\n            if (!isutf(*string)) {\n                ++string;\n                if (!isutf(*string)) {\n                    ++string;\n                    if (!isutf(*string)) {\n                        ++string;\n                    }\n                }\n            }\n        }\n        ++charnum;\n    }\n    return charnum;\n}\n\n/* reads the next utf-8 sequence out of a string, updating an index */\nuint32_t u8_nextchar(const char *s, int *i)\n{\n    uint32_t ch = 0;\n    int sz = 0;\n\n    do {\n        ch <<= 6;\n        ch += (unsigned char)s[(*i)++];\n        sz++;\n    } while (s[*i] && !isutf(s[*i]));\n    ch -= offsetsFromUTF8[sz-1];\n\n    return ch;\n}\n\n/* number of characters */\nint u8_strlen(const char *s)\n{\n    int count = 0;\n    int i = 0;\n\n    while (u8_nextchar(s, &i) != 0)\n        count++;\n\n    return count;\n}\n\nvoid u8_inc(const char *s, int *i)\n{\n    if (s[(*i)++] & 0x80) {\n        if (!isutf(s[*i])) {\n            ++(*i);\n            if (!isutf(s[*i])) {\n                ++(*i);\n                if (!isutf(s[*i])) {\n                    ++(*i);\n                }\n            }\n        }\n    }\n}\n\nvoid u8_dec(const char *s, int *i)\n{\n    (void)(isutf(s[--(*i)]) || isutf(s[--(*i)]) ||\n           isutf(s[--(*i)]) || --(*i));\n}\n\n\n/* srcsz = number of source characters, or -1 if 0-terminated\n   sz = size of dest buffer in bytes\n\n   returns # characters converted\n*/\n#ifdef _WIN32\n#include <windows.h>\n#endif\nint u8_nativetoutf8(char *dest, int sz, const char *src, int srcsz)\n{\n    int len;\n#ifdef _WIN32\n    int res;\n    wchar_t*wbuf = 0;\n    if(srcsz < 0) {\n        len = MultiByteToWideChar(CP_OEMCP, 0, src, srcsz, wbuf, 0);\n    } else {\n        len = (srcsz < sz)?srcsz:sz;\n    }\n    wbuf = getbytes(len * sizeof(*wbuf));\n    res = MultiByteToWideChar(CP_OEMCP, 0, src, srcsz, wbuf, len);\n    if(res) {\n        res = WideCharToMultiByte(CP_UTF8, 0, wbuf, len, dest, sz, 0, 0);\n    }\n    freebytes(wbuf, len * sizeof(*wbuf));\n    return (res)?len:0;\n#endif\n        /* on other systems, we use UTF-8 for everything, so this is a no-op */\n    if(srcsz < 0)\n        srcsz = strlen(src) + 1;\n    len = (srcsz < sz)?srcsz:sz;\n    strncpy(dest, src, len);\n    return len;\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/s_utf8.h",
    "content": "#ifndef S_UTF8_H\n#define S_UTF8_H\n\n#include \"m_pd.h\"\n\n#ifndef UCS4\n# define UCS4 uint32_t\n#endif\n\n/* UTF8_SUPPORT_FULL_UCS4\n *  define this to support the full potential range of UCS-4 codepoints\n *  (in anticipation of a future UTF-8 standard)\n */\n/*#define UTF8_SUPPORT_FULL_UCS4 1*/\n#undef UTF8_SUPPORT_FULL_UCS4\n\n/* UTF8_MAXBYTES\n *   maximum number of bytes required to represent a single character in UTF-8\n *\n * UTF8_MAXBYTES1 = UTF8_MAXBYTES+1\n *  maximum bytes per character including NUL terminator\n */\n#ifdef UTF8_SUPPORT_FULL_UCS4\n# ifndef UTF8_MAXBYTES\n#  define UTF8_MAXBYTES  6\n# endif\n# ifndef UTF8_MAXBYTES1\n#  define UTF8_MAXBYTES1 7\n# endif\n#else\n# ifndef UTF8_MAXBYTES\n#  define UTF8_MAXBYTES  4\n# endif\n# ifndef UTF8_MAXBYTES1\n#  define UTF8_MAXBYTES1 5\n# endif\n#endif\n/*--/moo--*/\n\n/* is c the start of a utf8 sequence? */\n#define isutf(c) (((c)&0xC0)!=0x80)\n\n/* convert UTF-8 data to UCS-2 wide character */\nint u8_utf8toucs2(uint16_t *dest, int sz, const char *src, int srcsz);\n\n/* the opposite conversion */\nint u8_ucs2toutf8(char *dest, int sz, const uint16_t *src, int srcsz);\n\n/* moo: get byte length of character number, or 0 if not supported */\nint u8_wc_nbytes(uint32_t ch);\n\n/* moo: compute required storage for UTF-8 encoding of 's[0..n-1]' */\nint u8_wcs_nbytes(const uint32_t *ucs, int size);\n\n/* single character to UTF-8, no NUL termination */\nint u8_wc_toutf8(char *dest, uint32_t ch);\n\n/* moo: single character to UTF-8, with NUL termination */\nint u8_wc_toutf8_nul(char *dest, uint32_t ch);\n\n/* character number to byte offset */\nint u8_offset(const char *str, int charnum);\n\n/* byte offset to character number */\nint u8_charnum(const char *s, int offset);\n\n/* return next character, updating an index variable */\nuint32_t u8_nextchar(const char *s, int *i);\n\n/* move to next character */\nvoid u8_inc(const char *s, int *i);\n\n/* move to previous character */\nvoid u8_dec(const char *s, int *i);\n\n/* moo: move pointer to next character */\nvoid u8_inc_ptr(char **sp);\n\n/* moo: move pointer to previous character */\nvoid u8_dec_ptr(char **sp);\n\n/* returns length of next utf-8 sequence */\nint u8_seqlen(const char *s);\n\n/* convert a string in the current encoding to UTF-8 */\nint u8_nativetoutf8(char* dest, int sz, const char* src, int srcsz);\n\n#endif /* S_UTF8_H */\n"
  },
  {
    "path": "libs/libpd/pure-data/src/x_acoustics.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/*  utility functions for signals\n*/\n\n#include \"m_pd.h\"\n#include <math.h>\n#define LOGTEN 2.302585092994046\n\nt_float mtof(t_float f)\n{\n    if (f <= -1500) return(0);\n    else if (f > 1499) return(mtof(1499));\n    else return (8.17579891564 * exp(.0577622650 * f));\n}\n\nt_float ftom(t_float f)\n{\n    return (f > 0 ? 17.3123405046 * log(.12231220585 * f) : -1500);\n}\n\nt_float powtodb(t_float f)\n{\n    if (f <= 0) return (0);\n    else\n    {\n        t_float val = 100 + 10./LOGTEN * log(f);\n        return (val < 0 ? 0 : val);\n    }\n}\n\nt_float rmstodb(t_float f)\n{\n    if (f <= 0) return (0);\n    else\n    {\n        t_float val = 100 + 20./LOGTEN * log(f);\n        return (val < 0 ? 0 : val);\n    }\n}\n\nt_float dbtopow(t_float f)\n{\n    if (f <= 0)\n        return(0);\n    else\n    {\n        if (f > 870)\n            f = 870;\n        return (exp((LOGTEN * 0.1) * (f-100.)));\n    }\n}\n\nt_float dbtorms(t_float f)\n{\n    if (f <= 0)\n        return(0);\n    else\n    {\n        if (f > 485)\n            f = 485;\n    }\n    return (exp((LOGTEN * 0.05) * (f-100.)));\n}\n\n/* ------------- corresponding objects ----------------------- */\n\nstatic t_class *mtof_class;\n\nstatic void *mtof_new(void)\n{\n    t_object *x = (t_object *)pd_new(mtof_class);\n    outlet_new(x, &s_float);\n    return (x);\n}\n\nstatic void mtof_float(t_object *x, t_float f)\n{\n    outlet_float(x->ob_outlet, mtof(f));\n}\n\n\nstatic t_class *ftom_class;\n\nstatic void *ftom_new(void)\n{\n    t_object *x = (t_object *)pd_new(ftom_class);\n    outlet_new(x, &s_float);\n    return (x);\n}\n\nstatic void ftom_float(t_object *x, t_float f)\n{\n    outlet_float(x->ob_outlet, ftom(f));\n}\n\n\nstatic t_class *rmstodb_class;\n\nstatic void *rmstodb_new(void)\n{\n    t_object *x = (t_object *)pd_new(rmstodb_class);\n    outlet_new(x, &s_float);\n    return (x);\n}\n\nstatic void rmstodb_float(t_object *x, t_float f)\n{\n    outlet_float(x->ob_outlet, rmstodb(f));\n}\n\n\nstatic t_class *powtodb_class;\n\nstatic void *powtodb_new(void)\n{\n    t_object *x = (t_object *)pd_new(powtodb_class);\n    outlet_new(x, &s_float);\n    return (x);\n}\n\nstatic void powtodb_float(t_object *x, t_float f)\n{\n    outlet_float(x->ob_outlet, powtodb(f));\n}\n\n\nstatic t_class *dbtopow_class;\n\nstatic void *dbtopow_new(void)\n{\n    t_object *x = (t_object *)pd_new(dbtopow_class);\n    outlet_new(x, &s_float);\n    return (x);\n}\n\nstatic void dbtopow_float(t_object *x, t_float f)\n{\n    outlet_float(x->ob_outlet, dbtopow(f));\n}\n\n\nstatic t_class *dbtorms_class;\n\nstatic void *dbtorms_new(void)\n{\n    t_object *x = (t_object *)pd_new(dbtorms_class);\n    outlet_new(x, &s_float);\n    return (x);\n}\n\nstatic void dbtorms_float(t_object *x, t_float f)\n{\n    outlet_float(x->ob_outlet, dbtorms(f));\n}\n\n\nvoid x_acoustics_setup(void)\n{\n    t_symbol *s = gensym(\"acoustics.pd\");\n    mtof_class = class_new(gensym(\"mtof\"), mtof_new, 0,\n        sizeof(t_object), 0, 0);\n    class_addfloat(mtof_class, (t_method)mtof_float);\n    class_sethelpsymbol(mtof_class, s);\n\n    ftom_class = class_new(gensym(\"ftom\"), ftom_new, 0,\n        sizeof(t_object), 0, 0);\n    class_addfloat(ftom_class, (t_method)ftom_float);\n    class_sethelpsymbol(ftom_class, s);\n\n    powtodb_class = class_new(gensym(\"powtodb\"), powtodb_new, 0,\n        sizeof(t_object), 0, 0);\n    class_addfloat(powtodb_class, (t_method)powtodb_float);\n    class_sethelpsymbol(powtodb_class, s);\n\n    rmstodb_class = class_new(gensym(\"rmstodb\"), rmstodb_new, 0,\n        sizeof(t_object), 0, 0);\n    class_addfloat(rmstodb_class, (t_method)rmstodb_float);\n    class_sethelpsymbol(rmstodb_class, s);\n\n    dbtopow_class = class_new(gensym(\"dbtopow\"), dbtopow_new, 0,\n        sizeof(t_object), 0, 0);\n    class_addfloat(dbtopow_class, (t_method)dbtopow_float);\n    class_sethelpsymbol(dbtopow_class, s);\n\n    dbtorms_class = class_new(gensym(\"dbtorms\"), dbtorms_new, 0,\n        sizeof(t_object), 0, 0);\n    class_addfloat(dbtorms_class, (t_method)dbtorms_float);\n    class_sethelpsymbol(dbtorms_class, s);\n}\n\n"
  },
  {
    "path": "libs/libpd/pure-data/src/x_arithmetic.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* arithmetic: binops ala C language.  The 4 functions and relationals are\ndone on floats; the logical and bitwise binops convert their\ninputs to int and their outputs back to float. */\n\n#include \"m_pd.h\"\n#include <math.h>\n\n#if PD_FLOATSIZE == 32\n# define POW powf\n# define SIN sinf\n# define COS cosf\n# define ATAN atanf\n# define ATAN2 atan2f\n# define SQRT sqrtf\n# define LOG logf\n# define EXP expf\n# define FABS fabsf\n# define MAXLOG 87.3365 /* log(FLT_MAX / 4.) */\n#else\n# define POW pow\n# define SIN sin\n# define COS cos\n# define ATAN atan\n# define ATAN2 atan2\n# define SQRT sqrt\n# define LOG log\n# define EXP exp\n# define FABS fabs\n# define MAXLOG 708.396 /* log(DBL_MAX / 4.) */\n#endif\n\ntypedef struct _binop\n{\n    t_object x_obj;\n    t_float x_f1;\n    t_float x_f2;\n} t_binop;\n\n/* ------------------ binop1:  +, -, *, / ----------------------------- */\n\nstatic void *binop1_new(t_class *floatclass, t_floatarg f)\n{\n    t_binop *x = (t_binop *)pd_new(floatclass);\n    outlet_new(&x->x_obj, &s_float);\n    floatinlet_new(&x->x_obj, &x->x_f2);\n    x->x_f1 = 0;\n    x->x_f2 = f;\n    return (x);\n}\n\n/* --------------------- addition ------------------------------- */\n\nstatic t_class *binop1_plus_class;\n\nstatic void *binop1_plus_new(t_floatarg f)\n{\n    return (binop1_new(binop1_plus_class, f));\n}\n\nstatic void binop1_plus_bang(t_binop *x)\n{\n    outlet_float(x->x_obj.ob_outlet, x->x_f1 + x->x_f2);\n}\n\nstatic void binop1_plus_float(t_binop *x, t_float f)\n{\n    outlet_float(x->x_obj.ob_outlet, (x->x_f1 = f) + x->x_f2);\n}\n\n/* --------------------- subtraction ------------------------------- */\n\nstatic t_class *binop1_minus_class;\n\nstatic void *binop1_minus_new(t_floatarg f)\n{\n    return (binop1_new(binop1_minus_class, f));\n}\n\nstatic void binop1_minus_bang(t_binop *x)\n{\n    outlet_float(x->x_obj.ob_outlet, x->x_f1 - x->x_f2);\n}\n\nstatic void binop1_minus_float(t_binop *x, t_float f)\n{\n    outlet_float(x->x_obj.ob_outlet, (x->x_f1 = f) - x->x_f2);\n}\n\n/* --------------------- multiplication ------------------------------- */\n\nstatic t_class *binop1_times_class;\n\nstatic void *binop1_times_new(t_floatarg f)\n{\n    return (binop1_new(binop1_times_class, f));\n}\n\nstatic void binop1_times_bang(t_binop *x)\n{\n    outlet_float(x->x_obj.ob_outlet, x->x_f1 * x->x_f2);\n}\n\nstatic void binop1_times_float(t_binop *x, t_float f)\n{\n    outlet_float(x->x_obj.ob_outlet, (x->x_f1 = f) * x->x_f2);\n}\n\n/* --------------------- division ------------------------------- */\n\nstatic t_class *binop1_div_class;\n\nstatic void *binop1_div_new(t_floatarg f)\n{\n    return (binop1_new(binop1_div_class, f));\n}\n\nstatic void binop1_div_bang(t_binop *x)\n{\n    outlet_float(x->x_obj.ob_outlet,\n        (x->x_f2 != 0 ? x->x_f1 / x->x_f2 : 0));\n}\n\nstatic void binop1_div_float(t_binop *x, t_float f)\n{\n    x->x_f1 = f;\n    outlet_float(x->x_obj.ob_outlet,\n        (x->x_f2 != 0 ? x->x_f1 / x->x_f2 : 0));\n}\n\n/* ------------------------ pow -------------------------------- */\n\nstatic t_class *binop1_pow_class;\n\nstatic void *binop1_pow_new(t_floatarg f)\n{\n    return (binop1_new(binop1_pow_class, f));\n}\n\nstatic void binop1_pow_bang(t_binop *x)\n{\n    t_float r = (x->x_f1 == 0 && x->x_f2 < 0) ||\n        (x->x_f1 < 0 && (x->x_f2 - (int)x->x_f2) != 0) ?\n            0 : POW(x->x_f1, x->x_f2);\n    outlet_float(x->x_obj.ob_outlet, r);\n}\n\nstatic void binop1_pow_float(t_binop *x, t_float f)\n{\n    x->x_f1 = f;\n    binop1_pow_bang(x);\n}\n\n/* ------------------------ max -------------------------------- */\n\nstatic t_class *binop1_max_class;\n\nstatic void *binop1_max_new(t_floatarg f)\n{\n    return (binop1_new(binop1_max_class, f));\n}\n\nstatic void binop1_max_bang(t_binop *x)\n{\n    outlet_float(x->x_obj.ob_outlet,\n        (x->x_f1 > x->x_f2 ? x->x_f1 : x->x_f2));\n}\n\nstatic void binop1_max_float(t_binop *x, t_float f)\n{\n    x->x_f1 = f;\n    outlet_float(x->x_obj.ob_outlet,\n        (x->x_f1 > x->x_f2 ? x->x_f1 : x->x_f2));\n}\n\n/* ------------------------ min -------------------------------- */\n\nstatic t_class *binop1_min_class;\n\nstatic void *binop1_min_new(t_floatarg f)\n{\n    return (binop1_new(binop1_min_class, f));\n}\n\nstatic void binop1_min_bang(t_binop *x)\n{\n    outlet_float(x->x_obj.ob_outlet,\n        (x->x_f1 < x->x_f2 ? x->x_f1 : x->x_f2));\n}\n\nstatic void binop1_min_float(t_binop *x, t_float f)\n{\n    x->x_f1 = f;\n    outlet_float(x->x_obj.ob_outlet,\n        (x->x_f1 < x->x_f2 ? x->x_f1 : x->x_f2));\n}\n\n/* ------------------ binop2: ==, !=, >, <, >=, <=. -------------------- */\n\nstatic void *binop2_new(t_class *floatclass, t_floatarg f)\n{\n    t_binop *x = (t_binop *)pd_new(floatclass);\n    outlet_new(&x->x_obj, &s_float);\n    floatinlet_new(&x->x_obj, &x->x_f2);\n    x->x_f1 = 0;\n    x->x_f2 = f;\n    return (x);\n}\n\n/* --------------------- == ------------------------------- */\n\nstatic t_class *binop2_ee_class;\n\nstatic void *binop2_ee_new(t_floatarg f)\n{\n    return (binop2_new(binop2_ee_class, f));\n}\n\nstatic void binop2_ee_bang(t_binop *x)\n{\n    outlet_float(x->x_obj.ob_outlet, x->x_f1 == x->x_f2);\n}\n\nstatic void binop2_ee_float(t_binop *x, t_float f)\n{\n    outlet_float(x->x_obj.ob_outlet, (x->x_f1 = f) == x->x_f2);\n}\n\n/* --------------------- != ------------------------------- */\n\nstatic t_class *binop2_ne_class;\n\nstatic void *binop2_ne_new(t_floatarg f)\n{\n    return (binop2_new(binop2_ne_class, f));\n}\n\nstatic void binop2_ne_bang(t_binop *x)\n{\n    outlet_float(x->x_obj.ob_outlet, x->x_f1 != x->x_f2);\n}\n\nstatic void binop2_ne_float(t_binop *x, t_float f)\n{\n    outlet_float(x->x_obj.ob_outlet, (x->x_f1 = f) != x->x_f2);\n}\n\n/* --------------------- > ------------------------------- */\n\nstatic t_class *binop2_gt_class;\n\nstatic void *binop2_gt_new(t_floatarg f)\n{\n    return (binop2_new(binop2_gt_class, f));\n}\n\nstatic void binop2_gt_bang(t_binop *x)\n{\n    outlet_float(x->x_obj.ob_outlet, x->x_f1 > x->x_f2);\n}\n\nstatic void binop2_gt_float(t_binop *x, t_float f)\n{\n    outlet_float(x->x_obj.ob_outlet, (x->x_f1 = f) > x->x_f2);\n}\n\n/* --------------------- < ------------------------------- */\n\nstatic t_class *binop2_lt_class;\n\nstatic void *binop2_lt_new(t_floatarg f)\n{\n    return (binop2_new(binop2_lt_class, f));\n}\n\nstatic void binop2_lt_bang(t_binop *x)\n{\n    outlet_float(x->x_obj.ob_outlet, x->x_f1 < x->x_f2);\n}\n\nstatic void binop2_lt_float(t_binop *x, t_float f)\n{\n    outlet_float(x->x_obj.ob_outlet, (x->x_f1 = f) < x->x_f2);\n}\n\n/* --------------------- >= ------------------------------- */\n\nstatic t_class *binop2_ge_class;\n\nstatic void *binop2_ge_new(t_floatarg f)\n{\n    return (binop2_new(binop2_ge_class, f));\n}\n\nstatic void binop2_ge_bang(t_binop *x)\n{\n    outlet_float(x->x_obj.ob_outlet, x->x_f1 >= x->x_f2);\n}\n\nstatic void binop2_ge_float(t_binop *x, t_float f)\n{\n    outlet_float(x->x_obj.ob_outlet, (x->x_f1 = f) >= x->x_f2);\n}\n\n/* --------------------- <= ------------------------------- */\n\nstatic t_class *binop2_le_class;\n\nstatic void *binop2_le_new(t_floatarg f)\n{\n    return (binop2_new(binop2_le_class, f));\n}\n\nstatic void binop2_le_bang(t_binop *x)\n{\n    outlet_float(x->x_obj.ob_outlet, x->x_f1 <= x->x_f2);\n}\n\nstatic void binop2_le_float(t_binop *x, t_float f)\n{\n    outlet_float(x->x_obj.ob_outlet, (x->x_f1 = f) <= x->x_f2);\n}\n\n/* ------------- binop3: &, |, &&, ||, <<, >>, %, mod, div ------------------ */\n\nstatic void *binop3_new(t_class *fixclass, t_floatarg f)\n{\n    t_binop *x = (t_binop *)pd_new(fixclass);\n    outlet_new(&x->x_obj, &s_float);\n    floatinlet_new(&x->x_obj, &x->x_f2);\n    x->x_f1 = 0;\n    x->x_f2 = f;\n    return (x);\n}\n\n/* --------------------------- & ---------------------------- */\n\nstatic t_class *binop3_ba_class;\n\nstatic void *binop3_ba_new(t_floatarg f)\n{\n    return (binop3_new(binop3_ba_class, f));\n}\n\nstatic void binop2_ba_bang(t_binop *x)\n{\n    outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1)) & (int)(x->x_f2));\n}\n\nstatic void binop2_ba_float(t_binop *x, t_float f)\n{\n    outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1 = f)) & (int)(x->x_f2));\n}\n\n/* --------------------------- && ---------------------------- */\n\nstatic t_class *binop3_la_class;\n\nstatic void *binop3_la_new(t_floatarg f)\n{\n    return (binop3_new(binop3_la_class, f));\n}\n\nstatic void binop2_la_bang(t_binop *x)\n{\n    outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1)) && (int)(x->x_f2));\n}\n\nstatic void binop2_la_float(t_binop *x, t_float f)\n{\n    outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1 = f)) && (int)(x->x_f2));\n}\n\n/* --------------------------- | ---------------------------- */\n\nstatic t_class *binop3_bo_class;\n\nstatic void *binop3_bo_new(t_floatarg f)\n{\n    return (binop3_new(binop3_bo_class, f));\n}\n\nstatic void binop2_bo_bang(t_binop *x)\n{\n    outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1)) | (int)(x->x_f2));\n}\n\nstatic void binop2_bo_float(t_binop *x, t_float f)\n{\n    outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1 = f)) | (int)(x->x_f2));\n}\n\n/* --------------------------- || ---------------------------- */\n\nstatic t_class *binop3_lo_class;\n\nstatic void *binop3_lo_new(t_floatarg f)\n{\n    return (binop3_new(binop3_lo_class, f));\n}\n\nstatic void binop2_lo_bang(t_binop *x)\n{\n    outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1)) || (int)(x->x_f2));\n}\n\nstatic void binop2_lo_float(t_binop *x, t_float f)\n{\n    outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1 = f)) || (int)(x->x_f2));\n}\n\n/* --------------------------- << ---------------------------- */\n\nstatic t_class *binop3_ls_class;\n\nstatic void *binop3_ls_new(t_floatarg f)\n{\n    return (binop3_new(binop3_ls_class, f));\n}\n\nstatic void binop2_ls_bang(t_binop *x)\n{\n    outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1)) << (int)(x->x_f2));\n}\n\nstatic void binop2_ls_float(t_binop *x, t_float f)\n{\n    outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1 = f)) << (int)(x->x_f2));\n}\n\n/* --------------------------- >> ---------------------------- */\n\nstatic t_class *binop3_rs_class;\n\nstatic void *binop3_rs_new(t_floatarg f)\n{\n    return (binop3_new(binop3_rs_class, f));\n}\n\nstatic void binop2_rs_bang(t_binop *x)\n{\n    outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1)) >> (int)(x->x_f2));\n}\n\nstatic void binop2_rs_float(t_binop *x, t_float f)\n{\n    outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1 = f)) >> (int)(x->x_f2));\n}\n\n/* --------------------------- % ---------------------------- */\n\nstatic t_class *binop3_pc_class;\n\nstatic void *binop3_pc_new(t_floatarg f)\n{\n    return (binop3_new(binop3_pc_class, f));\n}\n\nstatic void binop2_pc_bang(t_binop *x)\n{\n    int n2 = x->x_f2;\n        /* apparently \"%\" raises an exception for INT_MIN and -1 */\n    if (n2 == -1)\n        outlet_float(x->x_obj.ob_outlet, 0);\n    else outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1)) % (n2 ? n2 : 1));\n}\n\nstatic void binop2_pc_float(t_binop *x, t_float f)\n{\n    int n2 = x->x_f2;\n    if (n2 == -1)\n        outlet_float(x->x_obj.ob_outlet, 0);\n    else outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1 = f)) % (n2 ? n2 : 1));\n}\n\n/* --------------------------- mod ---------------------------- */\n\nstatic t_class *binop3_mod_class;\n\nstatic void *binop3_mod_new(t_floatarg f)\n{\n    return (binop3_new(binop3_mod_class, f));\n}\n\nstatic void binop3_mod_bang(t_binop *x)\n{\n    int n2 = x->x_f2, result;\n    if (n2 < 0) n2 = -n2;\n    else if (!n2) n2 = 1;\n    result = ((int)(x->x_f1)) % n2;\n    if (result < 0) result += n2;\n    outlet_float(x->x_obj.ob_outlet, (t_float)result);\n}\n\nstatic void binop3_mod_float(t_binop *x, t_float f)\n{\n    x->x_f1 = f;\n    binop3_mod_bang(x);\n}\n\n/* --------------------------- div ---------------------------- */\n\nstatic t_class *binop3_div_class;\n\nstatic void *binop3_div_new(t_floatarg f)\n{\n    return (binop3_new(binop3_div_class, f));\n}\n\nstatic void binop3_div_bang(t_binop *x)\n{\n    int n1 = x->x_f1, n2 = x->x_f2, result;\n    if (n2 < 0) n2 = -n2;\n    else if (!n2) n2 = 1;\n    if (n1 < 0) n1 -= (n2-1);\n    result = n1 / n2;\n    outlet_float(x->x_obj.ob_outlet, (t_float)result);\n}\n\nstatic void binop3_div_float(t_binop *x, t_float f)\n{\n    x->x_f1 = f;\n    binop3_div_bang(x);\n}\n\n/* -------------------- mathematical functions ------------------ */\n\nstatic t_class *sin_class;      /* ----------- sin --------------- */\n\nstatic void *sin_new(void)\n{\n    t_object *x = (t_object *)pd_new(sin_class);\n    outlet_new(x, &s_float);\n    return (x);\n}\n\nstatic void sin_float(t_object *x, t_float f)\n{\n    outlet_float(x->ob_outlet, SIN(f));\n}\n\nstatic t_class *cos_class;      /* ----------- cos --------------- */\n\nstatic void *cos_new(void)\n{\n    t_object *x = (t_object *)pd_new(cos_class);\n    outlet_new(x, &s_float);\n    return (x);\n}\n\nstatic void cos_float(t_object *x, t_float f)\n{\n    outlet_float(x->ob_outlet, COS(f));\n}\n\nstatic t_class *tan_class;      /* ----------- tan --------------- */\n\nstatic void *tan_new(void)\n{\n    t_object *x = (t_object *)pd_new(tan_class);\n    outlet_new(x, &s_float);\n    return (x);\n}\n\nstatic void tan_float(t_object *x, t_float f)\n{\n    t_float c = cosf(f);\n    t_float t = (c == 0 ? 0 : SIN(f)/c);\n    outlet_float(x->ob_outlet, t);\n}\n\nstatic t_class *atan_class;     /* ----------- atan --------------- */\n\nstatic void *atan_new(void)\n{\n    t_object *x = (t_object *)pd_new(atan_class);\n    outlet_new(x, &s_float);\n    return (x);\n}\n\nstatic void atan_float(t_object *x, t_float f)\n{\n    outlet_float(x->ob_outlet, ATAN(f));\n}\n\nstatic t_class *atan2_class;    /* ----------- atan2 --------------- */\n\ntypedef struct _atan2\n{\n    t_object x_ob;\n    t_float  x_f1;\n    t_float  x_f2;\n} t_atan2;\n\nstatic void *atan2_new(void)\n{\n    t_atan2 *x = (t_atan2 *)pd_new(atan2_class);\n    floatinlet_new(&x->x_ob, &x->x_f2);\n    x->x_f1 = x->x_f2 = 0;\n    outlet_new(&x->x_ob, &s_float);\n    return (x);\n}\n\nstatic void atan2_bang(t_atan2 *x)\n{\n    outlet_float(x->x_ob.ob_outlet,\n        (x->x_f1 == 0 && x->x_f2 == 0 ? 0 : ATAN2(x->x_f1, x->x_f2)));\n}\n\nstatic void atan2_float(t_atan2 *x, t_float f)\n{\n    x->x_f1 = f;\n    atan2_bang(x);\n}\n\nstatic t_class *sqrt_class;     /* ----------- sqrt --------------- */\n\nstatic void *sqrt_new(void)\n{\n    t_object *x = (t_object *)pd_new(sqrt_class);\n    outlet_new(x, &s_float);\n    return (x);\n}\n\nstatic void sqrt_float(t_object *x, t_float f)\n{\n    t_float r = (f > 0 ? SQRT(f) : 0);\n    outlet_float(x->ob_outlet, r);\n}\n\n/* --------------------- log ------------------------------- */\n\nstatic t_class *binop1_log_class;\n\nstatic void *binop1_log_new(t_floatarg f)\n{\n    return (binop1_new(binop1_log_class, f));\n}\n\nstatic void binop1_log_bang(t_binop *x)\n{\n    t_float r;\n    if (x->x_f1 <= 0)\n        r = -1000;\n    else if (x->x_f2 <= 0)\n        r = LOG(x->x_f1);\n    else r = LOG(x->x_f1)/LOG(x->x_f2);\n    outlet_float(x->x_obj.ob_outlet, r);\n}\n\nstatic void binop1_log_float(t_binop *x, t_float f)\n{\n    x->x_f1 = f;\n    binop1_log_bang(x);\n}\n\nstatic t_class *exp_class;      /* ----------- exp --------------- */\n\nstatic void *exp_new(void)\n{\n    t_object *x = (t_object *)pd_new(exp_class);\n    outlet_new(x, &s_float);\n    return (x);\n}\n\nstatic void exp_float(t_object *x, t_float f)\n{\n    t_float g;\n#ifdef _WIN32\n    char buf[10];\n#endif\n    if (f > MAXLOG) f = MAXLOG;\n    g = EXP(f);\n    outlet_float(x->ob_outlet, g);\n}\n\nstatic t_class *abs_class;      /* ----------- abs --------------- */\n\nstatic void *abs_new(void)\n{\n    t_object *x = (t_object *)pd_new(abs_class);\n    outlet_new(x, &s_float);\n    return (x);\n}\n\nstatic void abs_float(t_object *x, t_float f)\n{\n    outlet_float(x->ob_outlet, FABS(f));\n}\n\nstatic t_class *wrap_class;      /* ----------- wrap --------------- */\n\nstatic void *wrap_new(void)\n{\n    t_object *x = (t_object *)pd_new(wrap_class);\n    outlet_new(x, &s_float);\n    return (x);\n}\n\nstatic void wrap_float(t_object *x, t_float f)\n{\n    outlet_float(x->ob_outlet, f - floor(f));\n}\n\n/* ------------------------  misc ------------------------ */\n\nstatic t_class *clip_class;\n\ntypedef struct _clip\n{\n    t_object x_ob;\n    t_float x_f1;\n    t_float x_f2;\n    t_float x_f3;\n} t_clip;\n\nstatic void *clip_new(t_floatarg f1, t_floatarg f2)\n{\n    t_clip *x = (t_clip *)pd_new(clip_class);\n    floatinlet_new(&x->x_ob, &x->x_f2);\n    floatinlet_new(&x->x_ob, &x->x_f3);\n    outlet_new(&x->x_ob, &s_float);\n    x->x_f2 = f1;\n    x->x_f3 = f2;\n    return (x);\n}\n\nstatic void clip_bang(t_clip *x)\n{\n        outlet_float(x->x_ob.ob_outlet, (x->x_f1 < x->x_f2 ? x->x_f2 : (\n        x->x_f1 > x->x_f3 ? x->x_f3 : x->x_f1)));\n}\n\nstatic void clip_float(t_clip *x, t_float f)\n{\n        x->x_f1 = f;\n        outlet_float(x->x_ob.ob_outlet, (x->x_f1 < x->x_f2 ? x->x_f2 : (\n        x->x_f1 > x->x_f3 ? x->x_f3 : x->x_f1)));\n}\n\nstatic void clip_setup(void)\n{\n    clip_class = class_new(gensym(\"clip\"), (t_newmethod)clip_new, 0,\n        sizeof(t_clip), 0, A_DEFFLOAT, A_DEFFLOAT, 0);\n    class_addfloat(clip_class, clip_float);\n    class_addbang(clip_class, clip_bang);\n}\n\nvoid x_arithmetic_setup(void)\n{\n    t_symbol *binop1_sym = gensym(\"binops\");\n    t_symbol *binop23_sym = gensym(\"binops-other\");\n    t_symbol *trig_sym = gensym(\"trigonometric\");\n    t_symbol *unop_sym = gensym(\"unops\");\n\n    binop1_plus_class = class_new(gensym(\"+\"), (t_newmethod)binop1_plus_new, 0,\n        sizeof(t_binop), 0, A_DEFFLOAT, 0);\n    class_addbang(binop1_plus_class, binop1_plus_bang);\n    class_addfloat(binop1_plus_class, (t_method)binop1_plus_float);\n    class_sethelpsymbol(binop1_plus_class, binop1_sym);\n\n    binop1_minus_class = class_new(gensym(\"-\"),\n        (t_newmethod)binop1_minus_new, 0,\n        sizeof(t_binop), 0, A_DEFFLOAT, 0);\n    class_addbang(binop1_minus_class, binop1_minus_bang);\n    class_addfloat(binop1_minus_class, (t_method)binop1_minus_float);\n    class_sethelpsymbol(binop1_minus_class, binop1_sym);\n\n    binop1_times_class = class_new(gensym(\"*\"),\n        (t_newmethod)binop1_times_new, 0,\n        sizeof(t_binop), 0, A_DEFFLOAT, 0);\n    class_addbang(binop1_times_class, binop1_times_bang);\n    class_addfloat(binop1_times_class, (t_method)binop1_times_float);\n    class_sethelpsymbol(binop1_times_class, binop1_sym);\n\n    binop1_div_class = class_new(gensym(\"/\"),\n        (t_newmethod)binop1_div_new, 0,\n        sizeof(t_binop), 0, A_DEFFLOAT, 0);\n    class_addbang(binop1_div_class, binop1_div_bang);\n    class_addfloat(binop1_div_class, (t_method)binop1_div_float);\n    class_sethelpsymbol(binop1_div_class, binop1_sym);\n\n    binop1_pow_class = class_new(gensym(\"pow\"),\n        (t_newmethod)binop1_pow_new, 0,\n        sizeof(t_binop), 0, A_DEFFLOAT, 0);\n    class_addbang(binop1_pow_class, binop1_pow_bang);\n    class_addfloat(binop1_pow_class, (t_method)binop1_pow_float);\n    class_sethelpsymbol(binop1_pow_class, binop1_sym);\n\n    binop1_max_class = class_new(gensym(\"max\"),\n        (t_newmethod)binop1_max_new, 0,\n        sizeof(t_binop), 0, A_DEFFLOAT, 0);\n    class_addbang(binop1_max_class, binop1_max_bang);\n    class_addfloat(binop1_max_class, (t_method)binop1_max_float);\n    class_sethelpsymbol(binop1_max_class, binop1_sym);\n\n    binop1_min_class = class_new(gensym(\"min\"),\n        (t_newmethod)binop1_min_new, 0,\n        sizeof(t_binop), 0, A_DEFFLOAT, 0);\n    class_addbang(binop1_min_class, binop1_min_bang);\n    class_addfloat(binop1_min_class, (t_method)binop1_min_float);\n    class_sethelpsymbol(binop1_min_class, binop1_sym);\n\n    binop1_log_class = class_new(gensym(\"log\"),\n        (t_newmethod)binop1_log_new, 0,\n        sizeof(t_binop), 0, A_DEFFLOAT, 0);\n    class_addbang(binop1_log_class, binop1_log_bang);\n    class_addfloat(binop1_log_class, (t_method)binop1_log_float);\n    class_sethelpsymbol(binop1_log_class, binop1_sym);\n\n        /* ------------------ binop2 ----------------------- */\n\n    binop2_ee_class = class_new(gensym(\"==\"), (t_newmethod)binop2_ee_new, 0,\n        sizeof(t_binop), 0, A_DEFFLOAT, 0);\n    class_addbang(binop2_ee_class, binop2_ee_bang);\n    class_addfloat(binop2_ee_class, (t_method)binop2_ee_float);\n    class_sethelpsymbol(binop2_ee_class, binop23_sym);\n\n    binop2_ne_class = class_new(gensym(\"!=\"), (t_newmethod)binop2_ne_new, 0,\n        sizeof(t_binop), 0, A_DEFFLOAT, 0);\n    class_addbang(binop2_ne_class, binop2_ne_bang);\n    class_addfloat(binop2_ne_class, (t_method)binop2_ne_float);\n    class_sethelpsymbol(binop2_ne_class, binop23_sym);\n\n    binop2_gt_class = class_new(gensym(\">\"), (t_newmethod)binop2_gt_new, 0,\n        sizeof(t_binop), 0, A_DEFFLOAT, 0);\n    class_addbang(binop2_gt_class, binop2_gt_bang);\n    class_addfloat(binop2_gt_class, (t_method)binop2_gt_float);\n    class_sethelpsymbol(binop2_gt_class, binop23_sym);\n\n    binop2_lt_class = class_new(gensym(\"<\"), (t_newmethod)binop2_lt_new, 0,\n        sizeof(t_binop), 0, A_DEFFLOAT, 0);\n    class_addbang(binop2_lt_class, binop2_lt_bang);\n    class_addfloat(binop2_lt_class, (t_method)binop2_lt_float);\n    class_sethelpsymbol(binop2_lt_class, binop23_sym);\n\n    binop2_ge_class = class_new(gensym(\">=\"), (t_newmethod)binop2_ge_new, 0,\n        sizeof(t_binop), 0, A_DEFFLOAT, 0);\n    class_addbang(binop2_ge_class, binop2_ge_bang);\n    class_addfloat(binop2_ge_class, (t_method)binop2_ge_float);\n    class_sethelpsymbol(binop2_ge_class, binop23_sym);\n\n    binop2_le_class = class_new(gensym(\"<=\"), (t_newmethod)binop2_le_new, 0,\n        sizeof(t_binop), 0, A_DEFFLOAT, 0);\n    class_addbang(binop2_le_class, binop2_le_bang);\n    class_addfloat(binop2_le_class, (t_method)binop2_le_float);\n    class_sethelpsymbol(binop2_le_class, binop23_sym);\n\n        /* ------------------ binop3 ----------------------- */\n\n    binop3_ba_class = class_new(gensym(\"&\"), (t_newmethod)binop3_ba_new, 0,\n        sizeof(t_binop), 0, A_DEFFLOAT, 0);\n    class_addbang(binop3_ba_class, binop2_ba_bang);\n    class_addfloat(binop3_ba_class, (t_method)binop2_ba_float);\n    class_sethelpsymbol(binop3_ba_class, binop23_sym);\n\n    binop3_la_class = class_new(gensym(\"&&\"), (t_newmethod)binop3_la_new, 0,\n        sizeof(t_binop), 0, A_DEFFLOAT, 0);\n    class_addbang(binop3_la_class, binop2_la_bang);\n    class_addfloat(binop3_la_class, (t_method)binop2_la_float);\n    class_sethelpsymbol(binop3_la_class, binop23_sym);\n\n    binop3_bo_class = class_new(gensym(\"|\"), (t_newmethod)binop3_bo_new, 0,\n        sizeof(t_binop), 0, A_DEFFLOAT, 0);\n    class_addbang(binop3_bo_class, binop2_bo_bang);\n    class_addfloat(binop3_bo_class, (t_method)binop2_bo_float);\n    class_sethelpsymbol(binop3_bo_class, binop23_sym);\n\n    binop3_lo_class = class_new(gensym(\"||\"), (t_newmethod)binop3_lo_new, 0,\n        sizeof(t_binop), 0, A_DEFFLOAT, 0);\n    class_addbang(binop3_lo_class, binop2_lo_bang);\n    class_addfloat(binop3_lo_class, (t_method)binop2_lo_float);\n    class_sethelpsymbol(binop3_lo_class, binop23_sym);\n\n    binop3_ls_class = class_new(gensym(\"<<\"), (t_newmethod)binop3_ls_new, 0,\n        sizeof(t_binop), 0, A_DEFFLOAT, 0);\n    class_addbang(binop3_ls_class, binop2_ls_bang);\n    class_addfloat(binop3_ls_class, (t_method)binop2_ls_float);\n    class_sethelpsymbol(binop3_ls_class, binop23_sym);\n\n    binop3_rs_class = class_new(gensym(\">>\"), (t_newmethod)binop3_rs_new, 0,\n        sizeof(t_binop), 0, A_DEFFLOAT, 0);\n    class_addbang(binop3_rs_class, binop2_rs_bang);\n    class_addfloat(binop3_rs_class, (t_method)binop2_rs_float);\n    class_sethelpsymbol(binop3_rs_class, binop23_sym);\n\n    binop3_pc_class = class_new(gensym(\"%\"), (t_newmethod)binop3_pc_new, 0,\n        sizeof(t_binop), 0, A_DEFFLOAT, 0);\n    class_addbang(binop3_pc_class, binop2_pc_bang);\n    class_addfloat(binop3_pc_class, (t_method)binop2_pc_float);\n    class_sethelpsymbol(binop3_pc_class, binop23_sym);\n\n    binop3_mod_class = class_new(gensym(\"mod\"), (t_newmethod)binop3_mod_new, 0,\n        sizeof(t_binop), 0, A_DEFFLOAT, 0);\n    class_addbang(binop3_mod_class, binop3_mod_bang);\n    class_addfloat(binop3_mod_class, (t_method)binop3_mod_float);\n    class_sethelpsymbol(binop3_mod_class, binop23_sym);\n\n    binop3_div_class = class_new(gensym(\"div\"), (t_newmethod)binop3_div_new, 0,\n        sizeof(t_binop), 0, A_DEFFLOAT, 0);\n    class_addbang(binop3_div_class, binop3_div_bang);\n    class_addfloat(binop3_div_class, (t_method)binop3_div_float);\n    class_sethelpsymbol(binop3_div_class, binop23_sym);\n\n        /* ------------------- trig functions --------------- */\n\n    sin_class = class_new(gensym(\"sin\"), sin_new, 0,\n        sizeof(t_object), 0, 0);\n    class_addfloat(sin_class, (t_method)sin_float);\n    class_sethelpsymbol(sin_class, trig_sym);\n\n    cos_class = class_new(gensym(\"cos\"), cos_new, 0,\n        sizeof(t_object), 0, 0);\n    class_addfloat(cos_class, (t_method)cos_float);\n    class_sethelpsymbol(cos_class, trig_sym);\n\n    tan_class = class_new(gensym(\"tan\"), tan_new, 0,\n        sizeof(t_object), 0, 0);\n    class_addfloat(tan_class, (t_method)tan_float);\n    class_sethelpsymbol(tan_class, trig_sym);\n\n    atan_class = class_new(gensym(\"atan\"), atan_new, 0,\n        sizeof(t_object), 0, 0);\n    class_addfloat(atan_class, (t_method)atan_float);\n    class_sethelpsymbol(atan_class, trig_sym);\n\n    atan2_class = class_new(gensym(\"atan2\"), atan2_new, 0,\n        sizeof(t_atan2), 0, 0);\n    class_addfloat(atan2_class, (t_method)atan2_float);\n    class_addbang(atan2_class, atan2_bang);\n    class_sethelpsymbol(atan2_class, trig_sym);\n\n    /* ------------------- trig functions --------------- */\n\n    sqrt_class = class_new(gensym(\"sqrt\"), sqrt_new, 0,\n        sizeof(t_object), 0, 0);\n    class_addfloat(sqrt_class, (t_method)sqrt_float);\n    class_sethelpsymbol(sqrt_class, unop_sym);\n\n    exp_class = class_new(gensym(\"exp\"), exp_new, 0,\n        sizeof(t_object), 0, 0);\n    class_addfloat(exp_class, (t_method)exp_float);\n    class_sethelpsymbol(exp_class, unop_sym);\n\n    abs_class = class_new(gensym(\"abs\"), abs_new, 0,\n        sizeof(t_object), 0, 0);\n    class_addfloat(abs_class, (t_method)abs_float);\n    class_sethelpsymbol(abs_class, unop_sym);\n\n    wrap_class = class_new(gensym(\"wrap\"), wrap_new, 0,\n        sizeof(t_object), 0, 0);\n    class_addfloat(wrap_class, (t_method)wrap_float);\n    class_sethelpsymbol(wrap_class, unop_sym);\n\n/* ------------------------  misc ------------------------ */\n\n    clip_setup();\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/x_array.c",
    "content": "/* Copyright (c) 1997-2013 Miller Puckette and others.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* The \"array\" object. */\n\n#include \"m_pd.h\"\n#include \"g_canvas.h\"\n#include \"m_imp.h\"\n#include <string.h>\n#include <stdio.h>\n#ifdef HAVE_UNISTD_H\n#include <unistd.h>\n#endif\n#ifdef _WIN32\n#include <io.h>\n#endif\n\n#include \"m_private_utils.h\"\n\n#define TEXT_NGETBYTE 100 /* bigger that this we use alloc, not alloca */\n\n/* -- \"table\" - classic \"array define\" object by Guenter Geiger --*/\n\nstatic int tabcount = 0;\n\nstatic void *table_donew(t_symbol *s, int size, int save, int savesize,\n    int xpix, int ypix)\n{\n    t_atom a[9];\n    t_glist *gl;\n    t_canvas *x, *z = canvas_getcurrent();\n    if (s == &s_)\n    {\n         char  tabname[255];\n         t_symbol *t = gensym(\"table\");\n         sprintf(tabname, \"%s%d\", t->s_name, tabcount++);\n         s = gensym(tabname);\n    }\n    if (size < 1)\n        size = 100;\n    SETFLOAT(a, GLIST_DEFCANVASXLOC);\n    SETFLOAT(a+1, GLIST_DEFCANVASYLOC);\n    SETFLOAT(a+2, xpix + 100);\n    SETFLOAT(a+3, ypix + 100);\n    SETSYMBOL(a+4, s);\n    SETFLOAT(a+5, 0);\n    x = canvas_new(0, 0, 6, a);\n\n    x->gl_owner = z;\n\n        /* create a graph for the table */\n    gl = glist_addglist((t_glist*)x, &s_, 0, -1, (size > 1 ? size-1 : 1), 1,\n        50, ypix+50, xpix+50, 50);\n\n    graph_array(gl, s, &s_float, size,\n        save*GRAPH_ARRAY_SAVE + savesize*GRAPH_ARRAY_SAVESIZE);\n\n    pd_this->pd_newest = &x->gl_pd;     /* mimic action of canvas_pop() */\n    pd_popsym(&x->gl_pd);\n    x->gl_loading = 0;\n\n    return (x);\n}\n\nstatic void *table_new(t_symbol *s, t_floatarg f)\n{\n    return (table_donew(s, f, 0, 0, 500, 300));\n}\n\n    /* return true if the \"canvas\" object is a \"table\". */\nint canvas_istable(const t_canvas *x)\n{\n    t_atom *argv = (x->gl_obj.te_binbuf? binbuf_getvec(x->gl_obj.te_binbuf):0);\n    int argc = (x->gl_obj.te_binbuf? binbuf_getnatom(x->gl_obj.te_binbuf) : 0);\n    int istable = (argc && argv[0].a_type == A_SYMBOL &&\n        argv[0].a_w.w_symbol == gensym(\"table\"));\n    return (istable);\n}\n\nt_class *array_define_class;\n\nstatic void array_define_yrange(t_glist *x, t_floatarg ylo, t_floatarg yhi)\n{\n    t_glist *gl = (x->gl_list ? pd_checkglist(&x->gl_list->g_pd) : 0);\n    if (gl && gl->gl_list && pd_class(&gl->gl_list->g_pd) == garray_class)\n    {\n        int n = garray_getarray((t_garray *)gl->gl_list)->a_n;\n        vmess(&x->gl_list->g_pd, gensym(\"bounds\"),\n            \"ffff\", 0., yhi, (double)(n == 1 ? n : n-1), ylo);\n        vmess(&x->gl_list->g_pd, gensym(\"xlabel\"),\n            \"fff\", ylo + glist_pixelstoy(gl, 2) - glist_pixelstoy(gl, 0),\n                0., (t_float)(n-1));\n        vmess(&x->gl_list->g_pd, gensym(\"ylabel\"),\n            \"fff\", glist_pixelstox(gl, 0) - glist_pixelstox(gl, 5), ylo, yhi);\n    }\n    else bug(\"array_define_yrange\");\n}\n\nstatic void *array_define_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_symbol *arrayname = &s_;\n    t_float arraysize = 100;\n    t_glist *x;\n    int keep = 0, gavesize = 0;\n    t_float ylo = -1, yhi = 1;\n    t_float xpix = 500, ypix = 300;\n    while (argc && argv->a_type == A_SYMBOL &&\n        *argv->a_w.w_symbol->s_name == '-')\n    {\n        if (!strcmp(argv->a_w.w_symbol->s_name, \"-k\"))\n            keep = 1;\n        else if (!strcmp(argv->a_w.w_symbol->s_name, \"-yrange\") &&\n            argc >= 3 && argv[1].a_type == A_FLOAT &&\n                argv[2].a_type == A_FLOAT)\n        {\n            ylo = atom_getfloatarg(1, argc, argv);\n            yhi = atom_getfloatarg(2, argc, argv);\n            if (ylo == yhi)\n                ylo = -1, yhi = 1;\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(argv->a_w.w_symbol->s_name, \"-pix\") &&\n            argc >= 3 && argv[1].a_type == A_FLOAT &&\n                argv[2].a_type == A_FLOAT)\n        {\n            if ((xpix = atom_getfloatarg(1, argc, argv)) < 10)\n                xpix = 10;\n            if ((ypix = atom_getfloatarg(2, argc, argv)) < 10)\n                ypix = 10;\n            argc -= 2; argv += 2;\n        }\n        else\n        {\n            pd_error(0, \"array define: unknown flag ...\");\n            postatom(argc, argv); endpost();\n        }\n        argc--; argv++;\n    }\n    if (argc && argv->a_type == A_SYMBOL)\n    {\n        arrayname = argv->a_w.w_symbol;\n        argc--; argv++;\n    }\n    if (argc && argv->a_type == A_FLOAT)\n    {\n        arraysize = argv->a_w.w_float;\n        gavesize = 1;\n        argc--; argv++;\n    }\n    if (argc)\n    {\n        post(\"warning: array define ignoring extra argument: \");\n        postatom(argc, argv); endpost();\n    }\n    x = (t_glist *)table_donew(arrayname, arraysize, keep, keep && !gavesize,\n        xpix, ypix);\n\n        /* bash the class to \"array define\".  We don't do this earlier in\n        part so that canvas_getcurrent() will work while the glist and\n        garray are being created.  There may be other, unknown side effects. */\n    x->gl_obj.ob_pd = array_define_class;\n    array_define_yrange(x, ylo, yhi);\n    outlet_new(&x->gl_obj, &s_pointer);\n    return (x);\n}\n\nvoid garray_savecontentsto(t_garray *x, t_binbuf *b);\n\nvoid array_define_save(t_gobj *z, t_binbuf *bb)\n{\n    t_glist *x = (t_glist *)z;\n    t_glist *gl = (x->gl_list ? pd_checkglist(&x->gl_list->g_pd) : 0);\n    binbuf_addv(bb, \"ssff\", &s__X, gensym(\"obj\"),\n        (t_float)x->gl_obj.te_xpix, (t_float)x->gl_obj.te_ypix);\n    binbuf_addbinbuf(bb, x->gl_obj.ob_binbuf);\n    binbuf_addsemi(bb);\n\n    if (gl)\n    {\n        garray_savecontentsto((t_garray *)gl->gl_list, bb);\n        obj_saveformat(&x->gl_obj, bb);\n    }\n    else\n        bug(\"array_define_save\");\n}\n\nt_scalar *garray_getscalar(t_garray *x);\n\n    /* send a pointer to the scalar that owns this array to\n    whomever is bound to the given symbol */\nstatic void array_define_send(t_glist *x, t_symbol *s)\n{\n    t_glist *gl = (x->gl_list ? pd_checkglist(&x->gl_list->g_pd) : 0);\n    if (!s->s_thing)\n        pd_error(x, \"array_define_send: %s: no such object\", s->s_name);\n    else if (gl && gl->gl_list && pd_class(&gl->gl_list->g_pd) == garray_class)\n    {\n        t_gpointer gp;\n        gpointer_init(&gp);\n        gpointer_setglist(&gp, gl,\n            garray_getscalar((t_garray *)gl->gl_list));\n        pd_pointer(s->s_thing, &gp);\n        gpointer_unset(&gp);\n    }\n    else bug(\"array_define_send\");\n}\n\nvoid garray_properties(t_garray *x);\n\nstatic void array_define_done_popup(t_glist*x, t_float which, t_float xpos, t_float ypos)\n{\n    int iwhich = (int)which;\n    t_glist *gl = (x->gl_list ? pd_checkglist(&x->gl_list->g_pd) : 0);\n    t_gobj *obj = 0;\n    if (!gl || !gl->gl_list || pd_class(&gl->gl_list->g_pd) != garray_class)\n        return;\n\n    obj = gl->gl_list;\n\n    switch(iwhich) {\n    case 0: /* properties */\n        garray_properties((t_garray *)obj);\n    break;\n    case 1: /* open */\n        typedmess(&(obj->g_pd), gensym(\"arrayviewlistnew\"), 0, 0);\n        break;\n    case 2: /* help */\n        open_via_helppath(class_gethelpname(array_define_class), \"\");\n        break;\n    }\n}\n\nstatic void array_define_bang(t_glist *x)\n{\n    t_glist *gl = (x->gl_list ? pd_checkglist(&x->gl_list->g_pd) : 0);\n    if (gl && gl->gl_list && pd_class(&gl->gl_list->g_pd) == garray_class)\n    {\n        t_gpointer gp;\n        gpointer_init(&gp);\n        gpointer_setglist(&gp, gl,\n            garray_getscalar((t_garray *)gl->gl_list));\n        outlet_pointer(x->gl_obj.ob_outlet, &gp);\n        gpointer_unset(&gp);\n    }\n    else bug(\"array_define_bang\");\n}\n\n    /* just forward any messages to the garray */\nstatic void array_define_anything(t_glist *x,\n    t_symbol *s, int argc, t_atom *argv)\n{\n    t_glist *gl = (x->gl_list ? pd_checkglist(&x->gl_list->g_pd) : 0);\n    if (gl && gl->gl_list && pd_class(&gl->gl_list->g_pd) == garray_class)\n        typedmess(&gl->gl_list->g_pd, s, argc, argv);\n    else bug(\"array_define_anything\");\n}\n\n    /* ignore messages like \"editmode\" */\nstatic void array_define_ignore(t_glist *x,\n    t_symbol *s, int argc, t_atom *argv)\n{\n}\n\n/* ---  array_client - common code for objects that refer to arrays -- */\n\ntypedef struct _array_client\n{\n    t_object tc_obj;\n    t_symbol *tc_sym;\n    t_gpointer tc_gp;\n    t_symbol *tc_struct;\n    t_symbol *tc_field;\n    t_canvas *tc_canvas;\n} t_array_client;\n\n#define x_sym x_tc.tc_sym\n#define x_struct x_tc.tc_struct\n#define x_field x_tc.tc_field\n#define x_gp x_tc.tc_gp\n\n    /* find the array for this object.  Prints an error  message and returns\n        0 on failure. */\nstatic t_array *array_client_getbuf(t_array_client *x, t_glist **glist)\n{\n    if (x->tc_sym)       /* named array object */\n    {\n        t_garray *y = (t_garray *)pd_findbyclass(x->tc_sym, garray_class);\n        if (y)\n        {\n            *glist = garray_getglist(y);\n            return (garray_getarray(y));\n        }\n        else\n        {\n            pd_error(x, \"array: couldn't find named array '%s'\",\n                x->tc_sym->s_name);\n            *glist = 0;\n            return (0);\n        }\n    }\n    else if (x->tc_struct)   /* by pointer */\n    {\n        t_template *template = template_findbyname(x->tc_struct);\n        t_gstub *gs = x->tc_gp.gp_stub;\n        t_word *vec;\n        int onset, type;\n        t_symbol *arraytype;\n        if (!template)\n        {\n            pd_error(x, \"array: couldn't find struct %s\", x->tc_struct->s_name);\n            return (0);\n        }\n        if (!gpointer_check(&x->tc_gp, 0))\n        {\n            pd_error(x, \"array: stale or empty pointer\");\n            return (0);\n        }\n        if (gs->gs_which == GP_ARRAY)\n            vec = x->tc_gp.gp_un.gp_w;\n        else vec = x->tc_gp.gp_un.gp_scalar->sc_vec;\n\n        if (!template_find_field(template,\n            x->tc_field, &onset, &type, &arraytype))\n        {\n            pd_error(x, \"array: no field named %s\", x->tc_field->s_name);\n            return (0);\n        }\n        if (type != DT_ARRAY)\n        {\n            pd_error(x, \"array: field %s not of type array\",\n                x->tc_field->s_name);\n            return (0);\n        }\n        if (gs->gs_which == GP_GLIST)\n            *glist = gs->gs_un.gs_glist;\n        else\n        {\n            t_array *owner_array = gs->gs_un.gs_array;\n            while (owner_array->a_gp.gp_stub->gs_which == GP_ARRAY)\n                owner_array = owner_array->a_gp.gp_stub->gs_un.gs_array;\n            *glist = owner_array->a_gp.gp_stub->gs_un.gs_glist;\n        }\n        return (*(t_array **)(((char *)vec) + onset));\n    }\n    else return (0);    /* shouldn't happen */\n}\n\nstatic void array_client_senditup(t_array_client *x)\n{\n    t_glist *glist = 0;\n    t_array *a = array_client_getbuf(x, &glist);\n    if (glist)\n       array_redraw(a, glist);\n}\n\nstatic void array_client_free(t_array_client *x)\n{\n    gpointer_unset(&x->tc_gp);\n}\n\n/* ----------  array size : get or set size of an array ---------------- */\nstatic t_class *array_size_class;\n\ntypedef struct _array_size\n{\n    t_array_client x_tc;\n} t_array_size;\n#define x_outlet x_tc.tc_obj.ob_outlet\n\nstatic void *array_size_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_array_size *x = (t_array_size *)pd_new(array_size_class);\n    x->x_sym = x->x_struct = x->x_field = 0;\n    gpointer_init(&x->x_gp);\n    while (argc && argv->a_type == A_SYMBOL &&\n        *argv->a_w.w_symbol->s_name == '-')\n    {\n        if (!strcmp(argv->a_w.w_symbol->s_name, \"-s\") &&\n            argc >= 3 && argv[1].a_type == A_SYMBOL &&\n                argv[2].a_type == A_SYMBOL)\n        {\n            x->x_struct = canvas_makebindsym(argv[1].a_w.w_symbol);\n            x->x_field = argv[2].a_w.w_symbol;\n            argc -= 2; argv += 2;\n        }\n        else\n        {\n            pd_error(x, \"array setline: unknown flag ...\");\n            postatom(argc, argv); endpost();\n        }\n        argc--; argv++;\n    }\n    if (argc && argv->a_type == A_SYMBOL)\n    {\n        if (x->x_struct)\n        {\n            pd_error(x, \"array setline: extra names after -s..\");\n            postatom(argc, argv); endpost();\n        }\n        else x->x_sym = argv->a_w.w_symbol;\n        argc--; argv++;\n    }\n    if (argc)\n    {\n        post(\"warning: array setline ignoring extra argument: \");\n        postatom(argc, argv); endpost();\n    }\n    if (x->x_struct)\n        pointerinlet_new(&x->x_tc.tc_obj, &x->x_gp);\n    else symbolinlet_new(&x->x_tc.tc_obj, &x->x_tc.tc_sym);\n    outlet_new(&x->x_tc.tc_obj, &s_float);\n    return (x);\n}\n\nstatic void array_size_bang(t_array_size *x)\n{\n    t_glist *glist;\n    t_array *a = array_client_getbuf(&x->x_tc, &glist);\n    if (a)\n        outlet_float(x->x_outlet, a->a_n);\n}\n\nstatic void array_size_float(t_array_size *x, t_floatarg f)\n{\n    t_glist *glist;\n    t_array *a = array_client_getbuf(&x->x_tc, &glist);\n    if (a)\n    {\n              /* if it's a named array object we have to go back and find the\n              garray (repeating work done in array_client_getbuf()) because\n              the garray might want to adjust.  Maybe array_client_getbuf\n              should have a return slot for the garray if any?  */\n        if (x->x_tc.tc_sym)\n        {\n            t_garray *y = (t_garray *)pd_findbyclass(x->x_tc.tc_sym,\n                garray_class);\n            if (!y)\n            {\n                pd_error(x, \"no such array '%s'\", x->x_tc.tc_sym->s_name);\n                return;\n            }\n            garray_resize(y, f);\n        }\n        else\n        {\n            int n = f;\n            if (n < 1)\n                n = 1;\n             array_resize_and_redraw(a, glist, n);\n        }\n    }\n}\n\n/* ------  range operations - act on a specifiable range in an array ----- */\nstatic t_class *array_sum_class;\n\ntypedef struct _array_rangeop   /* any operation meaningful on a subrange */\n{\n    t_array_client x_tc;\n    t_float x_onset;\n    t_float x_n;\n    t_symbol *x_elemfield;\n    t_symbol *x_elemtemplate;   /* unused - perhaps should at least check it */\n} t_array_rangeop;\n\n    /* generic creator for operations on ranges (array {get,set,sum,random,\n        quantile,search,...}  \"onsetin\" and \"nin\" are true if we should make\n        inlets for onset and n - if no inlet for 'n' we also won't allow\n        it to be specified as an argument.  Everything can take an onset but\n        sometimes we don't need an inlet because it's the inlet itself.  In\n        any case we allow onset to be specified as an argument (even if it's\n        the 'hot inlet') -- for the same reason as in the 'delay' object.\n        Finally we can optionally warn if there are extra arguments; some\n        specific arguments (e.g., search) allow them but most don't. */\nstatic void *array_rangeop_new(t_class *class,\n    t_symbol *s, int *argcp, t_atom **argvp,\n    int onsetin, int nin, int warnextra)\n{\n    int argc = *argcp;\n    t_atom *argv = *argvp;\n    t_array_rangeop *x = (t_array_rangeop *)pd_new(class);\n    x->x_sym = x->x_struct = x->x_field = 0;\n    gpointer_init(&x->x_gp);\n    x->x_elemtemplate = &s_;\n    x->x_elemfield = gensym(\"y\");\n    x->x_onset = 0;\n    x->x_n = -1;\n    if (onsetin)\n        floatinlet_new(&x->x_tc.tc_obj, &x->x_onset);\n    if (nin)\n        floatinlet_new(&x->x_tc.tc_obj, &x->x_n);\n    while (argc && argv->a_type == A_SYMBOL &&\n        *argv->a_w.w_symbol->s_name == '-')\n    {\n        if (!strcmp(argv->a_w.w_symbol->s_name, \"-s\") &&\n            argc >= 3 && argv[1].a_type == A_SYMBOL &&\n                argv[2].a_type == A_SYMBOL)\n        {\n            x->x_struct = canvas_makebindsym(argv[1].a_w.w_symbol);\n            x->x_field = argv[2].a_w.w_symbol;\n            argc -= 2; argv += 2;\n        }\n        else if (!strcmp(argv->a_w.w_symbol->s_name, \"-f\") &&\n            argc >= 3 && argv[1].a_type == A_SYMBOL &&\n                argv[2].a_type == A_SYMBOL)\n        {\n            x->x_elemtemplate = argv[1].a_w.w_symbol;\n            x->x_elemfield = argv[2].a_w.w_symbol;\n            argc -= 2; argv += 2;\n        }\n        else\n        {\n            pd_error(x, \"%s: unknown flag ...\", class_getname(class));\n            postatom(argc, argv); endpost();\n        }\n        argc--; argv++;\n    }\n    if (argc && argv->a_type == A_SYMBOL)\n    {\n        if (x->x_struct)\n        {\n            pd_error(x, \"%s: extra names after -s..\", class_getname(class));\n            postatom(argc, argv); endpost();\n        }\n        else x->x_sym = argv->a_w.w_symbol;\n        argc--; argv++;\n    }\n    if (argc && argv->a_type == A_FLOAT)\n    {\n        x->x_onset = argv->a_w.w_float;\n        argc--; argv++;\n    }\n    if (argc && argv->a_type == A_FLOAT)\n    {\n        x->x_n = argv->a_w.w_float;\n        argc--; argv++;\n    }\n    if (argc && warnextra)\n    {\n        post(\"warning: %s ignoring extra argument: \", class_getname(class));\n        postatom(argc, argv); endpost();\n    }\n    if (x->x_struct)\n        pointerinlet_new(&x->x_tc.tc_obj, &x->x_gp);\n    else symbolinlet_new(&x->x_tc.tc_obj, &x->x_tc.tc_sym);\n    *argcp = argc;\n    *argvp = argv;\n    return (x);\n}\n\nstatic int array_rangeop_getrange(t_array_rangeop *x,\n    char **firstitemp, int *nitemp, int *stridep, int *arrayonsetp)\n{\n    t_glist *glist;\n    t_array *a = array_client_getbuf(&x->x_tc, &glist);\n    int stride, fieldonset, arrayonset, nitem, type;\n    t_symbol *arraytype;\n    t_template *template;\n    if (!a)\n        return (0);\n    template = template_findbyname(a->a_templatesym);\n    if (!template_find_field(template, x->x_elemfield, &fieldonset,\n        &type, &arraytype) || type != DT_FLOAT)\n    {\n        pd_error(x, \"can't find field %s in struct %s\",\n            x->x_elemfield->s_name, a->a_templatesym->s_name);\n        return (0);\n    }\n    stride = a->a_elemsize;\n    arrayonset = x->x_onset;\n    if (arrayonset < 0)\n        arrayonset = 0;\n    else if (arrayonset > a->a_n)\n        arrayonset = a->a_n;\n    if (x->x_n < 0)\n        nitem = a->a_n - arrayonset;\n    else\n    {\n        nitem = x->x_n;\n        if (nitem + arrayonset > a->a_n)\n            nitem = a->a_n - arrayonset;\n    }\n    *firstitemp = a->a_vec+(fieldonset+arrayonset*stride);\n    *nitemp = nitem;\n    *stridep = stride;\n    *arrayonsetp = arrayonset;\n    return (1);\n}\n\n/* --------  specific operations on ranges of arrays -------- */\n\n/* ----------------  array sum -- add them up ------------------- */\nstatic t_class *array_sum_class;\n\n#define t_array_sum t_array_rangeop\n\nstatic void *array_sum_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_array_sum *x = array_rangeop_new(array_sum_class, s, &argc, &argv,\n        0, 1, 1);\n    outlet_new(&x->x_tc.tc_obj, &s_float);\n    return (x);\n}\n\nstatic void array_sum_bang(t_array_rangeop *x)\n{\n    char *itemp, *firstitem;\n    int stride, nitem, arrayonset, i;\n    double sum;\n    if (!array_rangeop_getrange(x, &firstitem, &nitem, &stride, &arrayonset))\n        return;\n    for (i = 0, sum = 0, itemp = firstitem; i < nitem; i++, itemp += stride)\n        sum += *(t_float *)itemp;\n    outlet_float(x->x_outlet, sum);\n}\n\nstatic void array_sum_float(t_array_rangeop *x, t_floatarg f)\n{\n    x->x_onset = f;\n    array_sum_bang(x);\n}\n\n/* ----------------  array get -- output as list ------------------- */\nstatic t_class *array_get_class;\n\n#define t_array_get t_array_rangeop\n\nstatic void *array_get_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_array_get *x = array_rangeop_new(array_get_class, s, &argc, &argv,\n        0, 1, 1);\n    outlet_new(&x->x_tc.tc_obj, &s_float);\n    return (x);\n}\n\nstatic void array_get_bang(t_array_rangeop *x)\n{\n    char *itemp, *firstitem;\n    int stride, nitem, arrayonset, i;\n    t_atom *outv;\n    if (!array_rangeop_getrange(x, &firstitem, &nitem, &stride, &arrayonset))\n        return;\n    ALLOCA(t_atom, outv, nitem, TEXT_NGETBYTE);\n    for (i = 0, itemp = firstitem; i < nitem; i++, itemp += stride)\n        SETFLOAT(&outv[i],  *(t_float *)itemp);\n    outlet_list(x->x_outlet, 0, nitem, outv);\n    FREEA(t_atom, outv, nitem, TEXT_NGETBYTE);\n}\n\nstatic void array_get_float(t_array_rangeop *x, t_floatarg f)\n{\n    x->x_onset = f;\n    array_get_bang(x);\n}\n\n/* --------------  array set -- copy list to array -------------- */\nstatic t_class *array_set_class;\n\n#define t_array_set t_array_rangeop\n\nstatic void *array_set_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_array_set *x = array_rangeop_new(array_set_class, s, &argc, &argv,\n        1, 0, 1);\n    return (x);\n}\n\nstatic void array_set_list(t_array_rangeop *x, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    char *itemp, *firstitem;\n    int stride, nitem, arrayonset, i;\n    if (!array_rangeop_getrange(x, &firstitem, &nitem, &stride, &arrayonset))\n        return;\n    if (nitem > argc)\n        nitem = argc;\n    for (i = 0, itemp = firstitem; i < nitem; i++, itemp += stride)\n        *(t_float *)itemp = atom_getfloatarg(i, argc, argv);\n    array_client_senditup(&x->x_tc);\n}\n\n/* -----  array quantile -- output quantile for input from 0 to 1 ------- */\nstatic t_class *array_quantile_class;\n\n#define t_array_quantile t_array_rangeop\n\nstatic void *array_quantile_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_array_quantile *x = array_rangeop_new(array_quantile_class, s,\n        &argc, &argv, 1, 1, 1);\n    outlet_new(&x->x_tc.tc_obj, &s_float);\n    return (x);\n}\n\nstatic void array_quantile_float(t_array_rangeop *x, t_floatarg f)\n{\n    char *itemp, *firstitem;\n    int stride, nitem, arrayonset, i;\n    double sum;\n    if (!array_rangeop_getrange(x, &firstitem, &nitem, &stride, &arrayonset))\n        return;\n    for (i = 0, sum = 0, itemp = firstitem; i < nitem; i++, itemp += stride)\n        sum += (*(t_float *)itemp > 0? *(t_float *)itemp : 0);\n    sum *= f;\n    for (i = 0, itemp = firstitem; i < (nitem-1); i++, itemp += stride)\n    {\n        sum -= (*(t_float *)itemp > 0? *(t_float *)itemp : 0);\n        if (sum < 0)\n            break;\n    }\n    outlet_float(x->x_outlet, i);\n}\n\n/* ----  array random -- output random value with array as distribution ---- */\nstatic t_class *array_random_class;\n\ntypedef struct _array_random   /* any operation meaningful on a subrange */\n{\n    t_array_rangeop x_r;\n    unsigned int x_state;\n} t_array_random;\n\nstatic void *array_random_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_array_random *x = array_rangeop_new(array_random_class, s,\n        &argc, &argv, 0, 1, 1);\n    static unsigned int random_nextseed = 584926371;\n    random_nextseed = random_nextseed * 435898247 + 938284287;\n    x->x_state = random_nextseed;\n    outlet_new(&x->x_r.x_tc.tc_obj, &s_float);\n    return (x);\n}\n\nstatic void array_random_seed(t_array_random *x, t_floatarg f)\n{\n    x->x_state = f;\n}\n\nstatic void array_random_bang(t_array_random *x)\n{\n    char *firstitem;\n    int stride, nitem, arrayonset;\n\n    if (!array_rangeop_getrange(&x->x_r, &firstitem, &nitem, &stride,\n        &arrayonset))\n            return;\n    x->x_state = x->x_state * 472940017 + 832416023;\n    array_quantile_float(&x->x_r, (1./4294967296.0) * (double)(x->x_state));\n}\n\nstatic void array_random_float(t_array_random *x, t_floatarg f)\n{\n    x->x_r.x_onset = f;\n    array_random_bang(x);\n}\n\n/* ----  array max -- output largest value and its index ------------ */\nstatic t_class *array_max_class;\n\ntypedef struct _array_max\n{\n    t_array_rangeop x_rangeop;\n    t_outlet *x_out1;       /* value */\n    t_outlet *x_out2;       /* index */\n} t_array_max;\n\nstatic void *array_max_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_array_max *x = array_rangeop_new(array_max_class, s, &argc, &argv,\n        0, 1, 1);\n    x->x_out1 = outlet_new(&x->x_rangeop.x_tc.tc_obj, &s_float);\n    x->x_out2 = outlet_new(&x->x_rangeop.x_tc.tc_obj, &s_float);\n    return (x);\n}\n\nstatic void array_max_bang(t_array_max *x)\n{\n    char *itemp, *firstitem;\n    int stride, nitem, arrayonset, i, besti;\n    t_float bestf;\n    if (!array_rangeop_getrange(&x->x_rangeop, &firstitem, &nitem, &stride,\n        &arrayonset))\n            return;\n    for (i = 0, besti = -1, bestf= -1e30, itemp = firstitem;\n        i < nitem; i++, itemp += stride)\n            if (*(t_float *)itemp > bestf)\n                bestf = *(t_float *)itemp, besti = i+arrayonset;\n    outlet_float(x->x_out2, besti);\n    outlet_float(x->x_out1, bestf);\n}\n\nstatic void array_max_float(t_array_max *x, t_floatarg f)\n{\n    x->x_rangeop.x_onset = f;\n    array_max_bang(x);\n}\n\n/* ----  array min -- output largest value and its index ------------ */\nstatic t_class *array_min_class;\n\ntypedef struct _array_min\n{\n    t_array_rangeop x_rangeop;\n    t_outlet *x_out1;       /* value */\n    t_outlet *x_out2;       /* index */\n} t_array_min;\n\nstatic void *array_min_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_array_min *x = array_rangeop_new(array_min_class, s, &argc, &argv,\n        0, 1, 1);\n    x->x_out1 = outlet_new(&x->x_rangeop.x_tc.tc_obj, &s_float);\n    x->x_out2 = outlet_new(&x->x_rangeop.x_tc.tc_obj, &s_float);\n    return (x);\n}\n\nstatic void array_min_bang(t_array_min *x)\n{\n    char *itemp, *firstitem;\n    int stride, nitem, i, arrayonset, besti;\n    t_float bestf;\n    if (!array_rangeop_getrange(&x->x_rangeop, &firstitem, &nitem, &stride,\n        &arrayonset))\n            return;\n    for (i = 0, besti = -1, bestf= 1e30, itemp = firstitem;\n        i < nitem; i++, itemp += stride)\n            if (*(t_float *)itemp < bestf)\n                bestf = *(t_float *)itemp, besti = i+arrayonset;\n    outlet_float(x->x_out2, besti);\n    outlet_float(x->x_out1, bestf);\n}\n\nstatic void array_min_float(t_array_min *x, t_floatarg f)\n{\n    x->x_rangeop.x_onset = f;\n    array_min_bang(x);\n}\n\n/* overall creator for \"array\" objects - dispatch to \"array define\" etc */\nstatic void *arrayobj_new(t_symbol *s, int argc, t_atom *argv)\n{\n    if (!argc || argv[0].a_type != A_SYMBOL)\n        pd_this->pd_newest = array_define_new(s, argc, argv);\n    else\n    {\n        const char *str = argv[0].a_w.w_symbol->s_name;\n        if (!strcmp(str, \"d\") || !strcmp(str, \"define\"))\n            pd_this->pd_newest = array_define_new(s, argc-1, argv+1);\n        else if (!strcmp(str, \"size\"))\n            pd_this->pd_newest = array_size_new(s, argc-1, argv+1);\n        else if (!strcmp(str, \"sum\"))\n            pd_this->pd_newest = array_sum_new(s, argc-1, argv+1);\n        else if (!strcmp(str, \"get\"))\n            pd_this->pd_newest = array_get_new(s, argc-1, argv+1);\n        else if (!strcmp(str, \"set\"))\n            pd_this->pd_newest = array_set_new(s, argc-1, argv+1);\n        else if (!strcmp(str, \"quantile\"))\n            pd_this->pd_newest = array_quantile_new(s, argc-1, argv+1);\n        else if (!strcmp(str, \"random\"))\n            pd_this->pd_newest = array_random_new(s, argc-1, argv+1);\n        else if (!strcmp(str, \"max\"))\n            pd_this->pd_newest = array_max_new(s, argc-1, argv+1);\n        else if (!strcmp(str, \"min\"))\n            pd_this->pd_newest = array_min_new(s, argc-1, argv+1);\n        else\n        {\n            pd_error(0, \"array %s: unknown function\", str);\n            pd_this->pd_newest = 0;\n        }\n    }\n    return (pd_this->pd_newest);\n}\n\nvoid canvas_add_for_class(t_class *c);\n\n/* ---------------- global setup function -------------------- */\n\nvoid x_array_setup(void)\n{\n    array_define_class = class_new(gensym(\"array define\"), 0,\n        (t_method)canvas_free, sizeof(t_canvas), 0, 0);\n    canvas_add_for_class(array_define_class);\n    class_addmethod(array_define_class, (t_method)array_define_send,\n        gensym(\"send\"), A_SYMBOL, 0);\n    class_addbang(array_define_class, array_define_bang);\n    class_addanything(array_define_class, array_define_anything);\n    class_sethelpsymbol(array_define_class, gensym(\"array-object\"));\n    class_setsavefn(array_define_class, array_define_save);\n\n    class_addmethod(array_define_class, (t_method)array_define_done_popup,\n        gensym(\"done-popup\"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);\n\n    class_addmethod(array_define_class, (t_method)array_define_ignore,\n        gensym(\"editmode\"), A_GIMME, 0);\n\n    class_addcreator((t_newmethod)arrayobj_new, gensym(\"array\"), A_GIMME, 0);\n\n    class_addcreator((t_newmethod)table_new, gensym(\"table\"),\n        A_DEFSYM, A_DEFFLOAT, 0);\n\n    array_size_class = class_new(gensym(\"array size\"),\n        (t_newmethod)array_size_new, (t_method)array_client_free,\n            sizeof(t_array_size), 0, A_GIMME, 0);\n    class_addbang(array_size_class, array_size_bang);\n    class_addfloat(array_size_class, array_size_float);\n    class_sethelpsymbol(array_size_class, gensym(\"array-object\"));\n\n    array_sum_class = class_new(gensym(\"array sum\"),\n        (t_newmethod)array_sum_new, (t_method)array_client_free,\n            sizeof(t_array_sum), 0, A_GIMME, 0);\n    class_addbang(array_sum_class, array_sum_bang);\n    class_addfloat(array_sum_class, array_sum_float);\n    class_sethelpsymbol(array_sum_class, gensym(\"array-object\"));\n\n    array_get_class = class_new(gensym(\"array get\"),\n        (t_newmethod)array_get_new, (t_method)array_client_free,\n            sizeof(t_array_get), 0, A_GIMME, 0);\n    class_addbang(array_get_class, array_get_bang);\n    class_addfloat(array_get_class, array_get_float);\n    class_sethelpsymbol(array_get_class, gensym(\"array-object\"));\n\n    array_set_class = class_new(gensym(\"array set\"),\n        (t_newmethod)array_set_new, (t_method)array_client_free,\n            sizeof(t_array_set), 0, A_GIMME, 0);\n    class_addlist(array_set_class, array_set_list);\n    class_sethelpsymbol(array_set_class, gensym(\"array-object\"));\n\n    array_quantile_class = class_new(gensym(\"array quantile\"),\n        (t_newmethod)array_quantile_new, (t_method)array_client_free,\n            sizeof(t_array_quantile), 0, A_GIMME, 0);\n    class_addfloat(array_quantile_class, array_quantile_float);\n    class_sethelpsymbol(array_quantile_class, gensym(\"array-object\"));\n\n    array_random_class = class_new(gensym(\"array random\"),\n        (t_newmethod)array_random_new, (t_method)array_client_free,\n            sizeof(t_array_random), 0, A_GIMME, 0);\n    class_addmethod(array_random_class, (t_method)array_random_seed,\n        gensym(\"seed\"), A_FLOAT, 0);\n    class_addfloat(array_random_class, array_random_float);\n    class_addbang(array_random_class, array_random_bang);\n    class_sethelpsymbol(array_random_class, gensym(\"array-object\"));\n\n    array_max_class = class_new(gensym(\"array max\"),\n        (t_newmethod)array_max_new, (t_method)array_client_free,\n            sizeof(t_array_max), 0, A_GIMME, 0);\n    class_addfloat(array_max_class, array_max_float);\n    class_addbang(array_max_class, array_max_bang);\n    class_sethelpsymbol(array_max_class, gensym(\"array-object\"));\n\n    array_min_class = class_new(gensym(\"array min\"),\n        (t_newmethod)array_min_new, (t_method)array_client_free,\n            sizeof(t_array_min), 0, A_GIMME, 0);\n    class_addfloat(array_min_class, array_min_float);\n    class_addbang(array_min_class, array_min_bang);\n    class_sethelpsymbol(array_min_class, gensym(\"array-object\"));\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/x_connective.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* connective objects */\n\n#include \"m_pd.h\"\n\n#include <string.h>\n#include <stdio.h>\n#include <stdlib.h>\n#ifdef _WIN32\n# include <malloc.h> /* MSVC or mingw on windows */\n#elif defined(__linux__) || defined(__APPLE__) || defined(HAVE_ALLOCA_H)\n# include <alloca.h> /* linux, mac, mingw, cygwin */\n#endif\n\n/* -------------------------- int ------------------------------ */\nstatic t_class *pdint_class;\n\ntypedef struct _pdint\n{\n    t_object x_obj;\n    t_float x_f;\n} t_pdint;\n\nstatic void *pdint_new(t_floatarg f)\n{\n    t_pdint *x = (t_pdint *)pd_new(pdint_class);\n    x->x_f = f;\n    outlet_new(&x->x_obj, &s_float);\n    floatinlet_new(&x->x_obj, &x->x_f);\n    return (x);\n}\n\nstatic void pdint_bang(t_pdint *x)\n{\n    outlet_float(x->x_obj.ob_outlet, (t_float)(int64_t)(x->x_f));\n}\n\nstatic void pdint_float(t_pdint *x, t_float f)\n{\n    outlet_float(x->x_obj.ob_outlet, (t_float)(int64_t)(x->x_f = f));\n}\n\nstatic void pdint_send(t_pdint *x, t_symbol *s)\n{\n    if (s->s_thing)\n        pd_float(s->s_thing, (t_float)(int64_t)x->x_f);\n    else pd_error(x, \"%s: no such object\", s->s_name);\n}\n\nvoid pdint_setup(void)\n{\n    pdint_class = class_new(gensym(\"int\"), (t_newmethod)pdint_new, 0,\n        sizeof(t_pdint), 0, A_DEFFLOAT, 0);\n    class_addcreator((t_newmethod)pdint_new, gensym(\"i\"), A_DEFFLOAT, 0);\n    class_addmethod(pdint_class, (t_method)pdint_send, gensym(\"send\"),\n        A_SYMBOL, 0);\n    class_addbang(pdint_class, pdint_bang);\n    class_addfloat(pdint_class, pdint_float);\n}\n\n/* -------------------------- float ------------------------------ */\nstatic t_class *pdfloat_class;\n\ntypedef struct _pdfloat\n{\n    t_object x_obj;\n    t_float x_f;\n} t_pdfloat;\n\n    /* \"float,\" \"symbol,\" and \"bang\" are special because\n    they're created by short-circuited messages to the \"new\"\n    object which are handled specially in pd_typedmess(). */\n\nstatic void *pdfloat_new(t_pd *dummy, t_float f)\n{\n    t_pdfloat *x = (t_pdfloat *)pd_new(pdfloat_class);\n    x->x_f = f;\n    outlet_new(&x->x_obj, &s_float);\n    floatinlet_new(&x->x_obj, &x->x_f);\n    pd_this->pd_newest = &x->x_obj.ob_pd;\n    return (x);\n}\n\nstatic void *pdfloat_new2(t_floatarg f)\n{\n    return (pdfloat_new(0, f));\n}\n\nstatic void pdfloat_bang(t_pdfloat *x)\n{\n    outlet_float(x->x_obj.ob_outlet, x->x_f);\n}\n\nstatic void pdfloat_float(t_pdfloat *x, t_float f)\n{\n    outlet_float(x->x_obj.ob_outlet, x->x_f = f);\n}\n\n\nstatic void pdfloat_symbol(t_pdfloat *x, t_symbol *s)\n{\n    t_float f = 0.0f;\n    char *str_end = NULL;\n    f = strtod(s->s_name, &str_end);\n    if (f == 0 && s->s_name == str_end)\n        pd_error(x, \"couldn't convert %s to float\", s->s_name);\n    else outlet_float(x->x_obj.ob_outlet, x->x_f = f);\n}\n\nstatic void pdfloat_send(t_pdfloat *x, t_symbol *s)\n{\n    if (s->s_thing)\n        pd_float(s->s_thing, x->x_f);\n    else pd_error(x, \"%s: no such object\", s->s_name);\n}\n\nvoid pdfloat_setup(void)\n{\n    pdfloat_class = class_new(gensym(\"float\"), (t_newmethod)pdfloat_new, 0,\n        sizeof(t_pdfloat), 0, A_FLOAT, 0);\n    class_addcreator((t_newmethod)pdfloat_new2, gensym(\"f\"), A_DEFFLOAT, 0);\n    class_addmethod(pdfloat_class, (t_method)pdfloat_send, gensym(\"send\"),\n        A_SYMBOL, 0);\n    class_addbang(pdfloat_class, pdfloat_bang);\n    class_addfloat(pdfloat_class, (t_method)pdfloat_float);\n    class_addsymbol(pdfloat_class, (t_method)pdfloat_symbol);\n}\n\n/* -------------------------- symbol ------------------------------ */\nstatic t_class *pdsymbol_class;\n\ntypedef struct _pdsymbol\n{\n    t_object x_obj;\n    t_symbol *x_s;\n} t_pdsymbol;\n\nstatic void *pdsymbol_new(t_pd *dummy, t_symbol *s)\n{\n    t_pdsymbol *x = (t_pdsymbol *)pd_new(pdsymbol_class);\n    x->x_s = s;\n    outlet_new(&x->x_obj, &s_symbol);\n    symbolinlet_new(&x->x_obj, &x->x_s);\n    pd_this->pd_newest = &x->x_obj.ob_pd;\n    return (x);\n}\n\nstatic void pdsymbol_bang(t_pdsymbol *x)\n{\n    outlet_symbol(x->x_obj.ob_outlet, x->x_s);\n}\n\nstatic void pdsymbol_symbol(t_pdsymbol *x, t_symbol *s)\n{\n    outlet_symbol(x->x_obj.ob_outlet, x->x_s = s);\n}\n\nstatic void pdsymbol_anything(t_pdsymbol *x, t_symbol *s, int ac, t_atom *av)\n{\n    outlet_symbol(x->x_obj.ob_outlet, x->x_s = s);\n}\n\n    /* For \"list\" message don't just output \"list\"; if empty, we want to\n    bang the symbol and if it starts with a symbol, we output that.\n    Otherwise it's not clear what we should do so we just go for the\n    \"anything\" method.  LATER figure out if there are other places where\n    empty lists aren't equivalent to \"bang\"???  Should Pd's message passer\n    always check and call the more specific method, or should it be the\n    object's responsibility?  Dunno... */\nstatic void pdsymbol_list(t_pdsymbol *x, t_symbol *s, int ac, t_atom *av)\n{\n    if (!ac)\n        pdsymbol_bang(x);\n    else if (av->a_type == A_SYMBOL)\n        pdsymbol_symbol(x, av->a_w.w_symbol);\n    else pdsymbol_anything(x, s, ac, av);\n}\n\nvoid pdsymbol_setup(void)\n{\n    pdsymbol_class = class_new(gensym(\"symbol\"), (t_newmethod)pdsymbol_new, 0,\n        sizeof(t_pdsymbol), 0, A_SYMBOL, 0);\n    class_addbang(pdsymbol_class, pdsymbol_bang);\n    class_addsymbol(pdsymbol_class, pdsymbol_symbol);\n    class_addanything(pdsymbol_class, pdsymbol_anything);\n}\n\n/* -------------------------- bang ------------------------------ */\nstatic t_class *bang_class;\n\ntypedef struct _bang\n{\n    t_object x_obj;\n} t_bang;\n\nstatic void *bang_new(t_pd *dummy)\n{\n    t_bang *x = (t_bang *)pd_new(bang_class);\n    outlet_new(&x->x_obj, &s_bang);\n    pd_this->pd_newest = &x->x_obj.ob_pd;\n    return (x);\n}\n\nstatic void *bang_new2(t_bang f)\n{\n    return (bang_new(0));\n}\n\nstatic void bang_bang(t_bang *x)\n{\n    outlet_bang(x->x_obj.ob_outlet);\n}\n\nvoid bang_setup(void)\n{\n    bang_class = class_new(gensym(\"bang\"), (t_newmethod)bang_new, 0,\n        sizeof(t_bang), 0, 0);\n    class_addcreator((t_newmethod)bang_new2, gensym(\"b\"), 0);\n    class_addbang(bang_class, bang_bang);\n    class_addfloat(bang_class, bang_bang);\n    class_addsymbol(bang_class, bang_bang);\n    class_addlist(bang_class, bang_bang);\n    class_addanything(bang_class, bang_bang);\n}\n\n/* -------------------- send ------------------------------ */\n\nstatic t_class *send_class;\n\ntypedef struct _send\n{\n    t_object x_obj;\n    t_symbol *x_sym;\n} t_send;\n\nstatic void send_bang(t_send *x)\n{\n    if (x->x_sym->s_thing) pd_bang(x->x_sym->s_thing);\n}\n\nstatic void send_float(t_send *x, t_float f)\n{\n    if (x->x_sym->s_thing) pd_float(x->x_sym->s_thing, f);\n}\n\nstatic void send_symbol(t_send *x, t_symbol *s)\n{\n    if (x->x_sym->s_thing) pd_symbol(x->x_sym->s_thing, s);\n}\n\nstatic void send_pointer(t_send *x, t_gpointer *gp)\n{\n    if (x->x_sym->s_thing) pd_pointer(x->x_sym->s_thing, gp);\n}\n\nstatic void send_list(t_send *x, t_symbol *s, int argc, t_atom *argv)\n{\n    if (x->x_sym->s_thing) pd_list(x->x_sym->s_thing, s, argc, argv);\n}\n\nstatic void send_anything(t_send *x, t_symbol *s, int argc, t_atom *argv)\n{\n    if (x->x_sym->s_thing) typedmess(x->x_sym->s_thing, s, argc, argv);\n}\n\nstatic void *send_new(t_symbol *s)\n{\n    t_send *x = (t_send *)pd_new(send_class);\n    if (!*s->s_name)\n        symbolinlet_new(&x->x_obj, &x->x_sym);\n    x->x_sym = s;\n    return (x);\n}\n\nstatic void send_setup(void)\n{\n    send_class = class_new(gensym(\"send\"), (t_newmethod)send_new, 0,\n        sizeof(t_send), 0, A_DEFSYM, 0);\n    class_addcreator((t_newmethod)send_new, gensym(\"s\"), A_DEFSYM, 0);\n    class_addbang(send_class, send_bang);\n    class_addfloat(send_class, send_float);\n    class_addsymbol(send_class, send_symbol);\n    class_addpointer(send_class, send_pointer);\n    class_addlist(send_class, send_list);\n    class_addanything(send_class, send_anything);\n    class_sethelpsymbol(send_class, gensym(\"send-receive\"));\n}\n/* -------------------- receive ------------------------------ */\n\nstatic t_class *receive_class;\n\ntypedef struct _receive\n{\n    t_object x_obj;\n    t_symbol *x_sym;\n} t_receive;\n\nstatic void receive_bang(t_receive *x)\n{\n    outlet_bang(x->x_obj.ob_outlet);\n}\n\nstatic void receive_float(t_receive *x, t_float f)\n{\n    outlet_float(x->x_obj.ob_outlet, f);\n}\n\nstatic void receive_symbol(t_receive *x, t_symbol *s)\n{\n    outlet_symbol(x->x_obj.ob_outlet, s);\n}\n\nstatic void receive_pointer(t_receive *x, t_gpointer *gp)\n{\n    outlet_pointer(x->x_obj.ob_outlet, gp);\n}\n\nstatic void receive_list(t_receive *x, t_symbol *s, int argc, t_atom *argv)\n{\n    outlet_list(x->x_obj.ob_outlet, s, argc, argv);\n}\n\nstatic void receive_anything(t_receive *x, t_symbol *s, int argc, t_atom *argv)\n{\n    outlet_anything(x->x_obj.ob_outlet, s, argc, argv);\n}\n\nstatic void *receive_new(t_symbol *s)\n{\n    t_receive *x = (t_receive *)pd_new(receive_class);\n    x->x_sym = s;\n    pd_bind(&x->x_obj.ob_pd, s);\n    outlet_new(&x->x_obj, 0);\n    return (x);\n}\n\nstatic void receive_free(t_receive *x)\n{\n    pd_unbind(&x->x_obj.ob_pd, x->x_sym);\n}\n\nstatic void receive_setup(void)\n{\n    receive_class = class_new(gensym(\"receive\"), (t_newmethod)receive_new,\n        (t_method)receive_free, sizeof(t_receive), CLASS_NOINLET, A_DEFSYM, 0);\n    class_addcreator((t_newmethod)receive_new, gensym(\"r\"), A_DEFSYM, 0);\n    class_addbang(receive_class, receive_bang);\n    class_addfloat(receive_class, (t_method)receive_float);\n    class_addsymbol(receive_class, receive_symbol);\n    class_addpointer(receive_class, receive_pointer);\n    class_addlist(receive_class, receive_list);\n    class_addanything(receive_class, receive_anything);\n    class_sethelpsymbol(receive_class, gensym(\"send-receive\"));\n}\n\n/* -------------------------- select ------------------------------ */\n\nstatic t_class *sel1_class;\n\ntypedef struct _sel1\n{\n    t_object x_obj;\n    t_atom x_atom;\n    t_outlet *x_outlet1;\n    t_outlet *x_outlet2;\n} t_sel1;\n\nstatic void sel1_float(t_sel1 *x, t_float f)\n{\n    if (x->x_atom.a_type == A_FLOAT && f == x->x_atom.a_w.w_float)\n        outlet_bang(x->x_outlet1);\n    else outlet_float(x->x_outlet2, f);\n}\n\nstatic void sel1_symbol(t_sel1 *x, t_symbol *s)\n{\n    if (x->x_atom.a_type == A_SYMBOL && s == x->x_atom.a_w.w_symbol)\n        outlet_bang(x->x_outlet1);\n    else outlet_symbol(x->x_outlet2, s);\n}\n\nstatic t_class *sel2_class;\n\ntypedef struct _selectelement\n{\n    t_word e_w;\n    t_outlet *e_outlet;\n} t_selectelement;\n\ntypedef struct _sel2\n{\n    t_object x_obj;\n    t_atomtype x_type;\n    t_int x_nelement;\n    t_selectelement *x_vec;\n    t_outlet *x_rejectout;\n} t_sel2;\n\nstatic void sel2_float(t_sel2 *x, t_float f)\n{\n    t_selectelement *e;\n    int nelement;\n    if (x->x_type == A_FLOAT)\n    {\n        for (nelement = (int)x->x_nelement, e = x->x_vec; nelement--; e++)\n            if (e->e_w.w_float == f)\n        {\n            outlet_bang(e->e_outlet);\n            return;\n        }\n    }\n    outlet_float(x->x_rejectout, f);\n}\n\nstatic void sel2_symbol(t_sel2 *x, t_symbol *s)\n{\n    t_selectelement *e;\n    int nelement;\n    if (x->x_type == A_SYMBOL)\n    {\n        for (nelement = (int)x->x_nelement, e = x->x_vec; nelement--; e++)\n            if (e->e_w.w_symbol == s)\n        {\n            outlet_bang(e->e_outlet);\n            return;\n        }\n    }\n    outlet_symbol(x->x_rejectout, s);\n}\n\nstatic void sel2_free(t_sel2 *x)\n{\n    freebytes(x->x_vec, x->x_nelement * sizeof(*x->x_vec));\n}\n\nstatic void *select_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_atom a;\n    if (argc == 0)\n    {\n        argc = 1;\n        SETFLOAT(&a, 0);\n        argv = &a;\n    }\n    if (argc == 1)\n    {\n        t_sel1 *x = (t_sel1 *)pd_new(sel1_class);\n        x->x_atom = *argv;\n        x->x_outlet1 = outlet_new(&x->x_obj, &s_bang);\n        if (argv->a_type == A_FLOAT)\n        {\n            floatinlet_new(&x->x_obj, &x->x_atom.a_w.w_float);\n            x->x_outlet2 = outlet_new(&x->x_obj, &s_float);\n        }\n        else\n        {\n            symbolinlet_new(&x->x_obj, &x->x_atom.a_w.w_symbol);\n            x->x_outlet2 = outlet_new(&x->x_obj, &s_symbol);\n        }\n        return (x);\n    }\n    else\n    {\n        int n;\n        t_selectelement *e;\n        t_sel2 *x = (t_sel2 *)pd_new(sel2_class);\n        x->x_nelement = argc;\n        x->x_vec = (t_selectelement *)getbytes(argc * sizeof(*x->x_vec));\n        x->x_type = argv[0].a_type;\n        for (n = 0, e = x->x_vec; n < argc; n++, e++)\n        {\n            e->e_outlet = outlet_new(&x->x_obj, &s_bang);\n            if ((x->x_type = argv->a_type) == A_FLOAT)\n                e->e_w.w_float = atom_getfloatarg(n, argc, argv);\n            else e->e_w.w_symbol = atom_getsymbolarg(n, argc, argv);\n        }\n        x->x_rejectout = outlet_new(&x->x_obj, &s_float);\n        return (x);\n    }\n\n}\n\nvoid select_setup(void)\n{\n    sel1_class = class_new(gensym(\"select\"), 0, 0,\n        sizeof(t_sel1), 0, 0);\n    class_addfloat(sel1_class, sel1_float);\n    class_addsymbol(sel1_class, sel1_symbol);\n\n    sel2_class = class_new(gensym(\"select\"), 0, (t_method)sel2_free,\n        sizeof(t_sel2), 0, 0);\n    class_addfloat(sel2_class, sel2_float);\n    class_addsymbol(sel2_class, sel2_symbol);\n\n    class_addcreator((t_newmethod)select_new, gensym(\"select\"),  A_GIMME, 0);\n    class_addcreator((t_newmethod)select_new, gensym(\"sel\"),  A_GIMME, 0);\n}\n\n/* -------------------------- route ------------------------------ */\n\nstatic t_class *route_class;\n\ntypedef struct _routeelement\n{\n    t_word e_w;\n    t_outlet *e_outlet;\n} t_routeelement;\n\ntypedef struct _route\n{\n    t_object x_obj;\n    t_atomtype x_type;\n    int x_nelement;\n    t_routeelement *x_vec;\n    t_outlet *x_rejectout;\n} t_route;\n\nstatic void route_anything(t_route *x, t_symbol *sel, int argc, t_atom *argv)\n{\n    t_routeelement *e;\n    int nelement;\n    if (x->x_type == A_SYMBOL)\n    {\n        for (nelement = x->x_nelement, e = x->x_vec; nelement--; e++)\n            if (e->e_w.w_symbol == sel)\n        {\n            if (argc > 0 && argv[0].a_type == A_SYMBOL)\n                outlet_anything(e->e_outlet, argv[0].a_w.w_symbol,\n                    argc-1, argv+1);\n            else outlet_list(e->e_outlet, 0, argc, argv);\n            return;\n        }\n    }\n    outlet_anything(x->x_rejectout, sel, argc, argv);\n}\n\nstatic void route_list(t_route *x, t_symbol *sel, int argc, t_atom *argv)\n{\n    t_routeelement *e;\n    int nelement;\n    if (x->x_type == A_FLOAT)\n    {\n        t_float f;\n        if (!argc || argv->a_type != A_FLOAT)\n            goto rejected;\n        f = atom_getfloat(argv);\n        for (nelement = x->x_nelement, e = x->x_vec; nelement--; e++)\n            if (e->e_w.w_float == f)\n        {\n            if (argc > 1 && argv[1].a_type == A_SYMBOL)\n                outlet_anything(e->e_outlet, argv[1].a_w.w_symbol,\n                    argc-2, argv+2);\n            else outlet_list(e->e_outlet, 0, argc-1, argv+1);\n            return;\n        }\n    }\n    else    /* symbol arguments */\n    {\n        if (argc > 1)       /* 2 or more args: treat as \"list\" */\n        {\n            for (nelement = x->x_nelement, e = x->x_vec; nelement--; e++)\n            {\n                if (e->e_w.w_symbol == &s_list)\n                {\n                    if (argc > 0 && argv[0].a_type == A_SYMBOL)\n                        outlet_anything(e->e_outlet, argv[0].a_w.w_symbol,\n                            argc-1, argv+1);\n                    else outlet_list(e->e_outlet, 0, argc, argv);\n                    return;\n                }\n            }\n        }\n        else if (argc == 0)         /* no args: treat as \"bang\" */\n        {\n            for (nelement = x->x_nelement, e = x->x_vec; nelement--; e++)\n            {\n                if (e->e_w.w_symbol == &s_bang)\n                {\n                    outlet_bang(e->e_outlet);\n                    return;\n                }\n            }\n        }\n        else if (argv[0].a_type == A_FLOAT)    /* one float arg */\n        {\n            for (nelement = x->x_nelement, e = x->x_vec; nelement--; e++)\n            {\n                if (e->e_w.w_symbol == &s_float)\n                {\n                    outlet_float(e->e_outlet, argv[0].a_w.w_float);\n                    return;\n                }\n            }\n        }\n        else if (argv[0].a_type == A_POINTER)    /* one pointer arg */\n        {\n            for (nelement = x->x_nelement, e = x->x_vec; nelement--; e++)\n            {\n                if (e->e_w.w_symbol == &s_pointer)\n                {\n                    outlet_pointer(e->e_outlet, argv[0].a_w.w_gpointer);\n                    return;\n                }\n            }\n        }\n        else                                     /* one symbol arg */\n        {\n            for (nelement = x->x_nelement, e = x->x_vec; nelement--; e++)\n            {\n                if (e->e_w.w_symbol == &s_symbol)\n                {\n                    outlet_symbol(e->e_outlet, argv[0].a_w.w_symbol);\n                    return;\n                }\n            }\n        }\n    }\n rejected:\n    outlet_list(x->x_rejectout, 0, argc, argv);\n}\n\n\nstatic void route_free(t_route *x)\n{\n    freebytes(x->x_vec, x->x_nelement * sizeof(*x->x_vec));\n}\n\nstatic void *route_new(t_symbol *s, int argc, t_atom *argv)\n{\n    int n;\n    t_routeelement *e;\n    t_route *x = (t_route *)pd_new(route_class);\n    t_atom a;\n    if (argc == 0)\n    {\n        argc = 1;\n        SETFLOAT(&a, 0);\n        argv = &a;\n    }\n    x->x_type = argv[0].a_type;\n    x->x_nelement = argc;\n    x->x_vec = (t_routeelement *)getbytes(argc * sizeof(*x->x_vec));\n    for (n = 0, e = x->x_vec; n < argc; n++, e++)\n    {\n        e->e_outlet = outlet_new(&x->x_obj, &s_list);\n        if (x->x_type == A_FLOAT)\n            e->e_w.w_float = atom_getfloatarg(n, argc, argv);\n        else e->e_w.w_symbol = atom_getsymbolarg(n, argc, argv);\n    }\n    if (argc == 1)\n    {\n        if (argv->a_type == A_FLOAT)\n            floatinlet_new(&x->x_obj, &x->x_vec->e_w.w_float);\n        else symbolinlet_new(&x->x_obj, &x->x_vec->e_w.w_symbol);\n    }\n    x->x_rejectout = outlet_new(&x->x_obj, &s_list);\n    return (x);\n}\n\nvoid route_setup(void)\n{\n    route_class = class_new(gensym(\"route\"), (t_newmethod)route_new,\n        (t_method)route_free, sizeof(t_route), 0, A_GIMME, 0);\n    class_addlist(route_class, route_list);\n    class_addanything(route_class, route_anything);\n}\n\n/* -------------------------- pack ------------------------------ */\n\nstatic t_class *pack_class;\n\ntypedef struct _pack\n{\n    t_object x_obj;\n    t_atom *x_vec;          /* input values */\n    t_gpointer *x_gpointer; /* the pointers */\n    int x_n;                /* number of args */\n    int x_nptr;             /* number of pointers */\n} t_pack;\n\nstatic void *pack_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_pack *x = (t_pack *)pd_new(pack_class);\n    t_atom defarg[2], *vec;\n    t_gpointer *gp;\n    int nptr = 0;\n    int i;\n    if (!argc)\n    {\n        argv = defarg;\n        argc = 2;\n        SETFLOAT(&defarg[0], 0);\n        SETFLOAT(&defarg[1], 0);\n    }\n\n    x->x_n = argc;\n    vec = x->x_vec = (t_atom *)getbytes(argc * sizeof(*x->x_vec));\n\n    for (i = 0; i < argc; i++)\n        if (argv[i].a_type == A_SYMBOL && *argv[i].a_w.w_symbol->s_name == 'p')\n            nptr++;\n\n    gp = x->x_gpointer = (t_gpointer *)t_getbytes(nptr * sizeof (*gp));\n    x->x_nptr = nptr;\n\n    for (i = 0; i < argc; i++)\n    {\n        if (argv[i].a_type == A_FLOAT)\n        {\n            vec[i] = argv[i];\n            if (i) floatinlet_new(&x->x_obj, &vec[i].a_w.w_float);\n        }\n        else if (argv[i].a_type == A_SYMBOL)\n        {\n            char c = *argv[i].a_w.w_symbol->s_name;\n            if (c == 's')\n            {\n                SETSYMBOL(&vec[i], &s_symbol);\n                if (i) symbolinlet_new(&x->x_obj, &vec[i].a_w.w_symbol);\n            }\n            else if (c == 'p')\n            {\n                vec[i].a_type = A_POINTER;\n                vec[i].a_w.w_gpointer = gp;\n                gpointer_init(gp);\n                if (i) pointerinlet_new(&x->x_obj, gp);\n                gp++;\n            }\n            else\n            {\n                if (c != 'f') pd_error(x, \"pack: %s: bad type\",\n                    argv[i].a_w.w_symbol->s_name);\n                SETFLOAT(&vec[i], 0);\n                if (i) floatinlet_new(&x->x_obj, &vec[i].a_w.w_float);\n            }\n        }\n    }\n    outlet_new(&x->x_obj, &s_list);\n    return (x);\n}\n\nstatic void pack_bang(t_pack *x)\n{\n    int i;\n    t_atom *outvec = (t_atom *)alloca(x->x_n * sizeof(t_atom));\n    t_gpointer *gpvec, *gp;\n    if (x->x_nptr > 0)\n    {\n        gp = gpvec = (t_gpointer *)alloca(x->x_nptr * sizeof(t_gpointer));\n        for (i = 0; i < x->x_n; i++)\n        {\n            outvec[i] = x->x_vec[i];\n            if (x->x_vec[i].a_type == A_POINTER)\n            {\n                gpointer_copy(x->x_vec[i].a_w.w_gpointer, gp);\n                outvec[i].a_w.w_gpointer = gp;\n                gp++;\n            }\n        }\n    }\n    else\n    {\n        for (i = 0; i < x->x_n; i++)\n            outvec[i] = x->x_vec[i];\n    }\n\n    outlet_list(x->x_obj.ob_outlet, &s_list, x->x_n, outvec);\n\n    for (i = 0; i < x->x_nptr; i++)\n        gpointer_unset(&gpvec[i]);\n}\n\nstatic void pack_pointer(t_pack *x, t_gpointer *gp)\n{\n    if (x->x_vec->a_type == A_POINTER)\n    {\n        gpointer_unset(x->x_gpointer);\n        gpointer_copy(gp, x->x_gpointer);\n        pack_bang(x);\n    }\n    else pd_error(x, \"pack_pointer: wrong type\");\n}\n\nstatic void pack_float(t_pack *x, t_float f)\n{\n    if (x->x_vec->a_type == A_FLOAT)\n    {\n        x->x_vec->a_w.w_float = f;\n        pack_bang(x);\n    }\n    else pd_error(x, \"pack_float: wrong type\");\n}\n\nstatic void pack_symbol(t_pack *x, t_symbol *s)\n{\n    if (x->x_vec->a_type == A_SYMBOL)\n    {\n        x->x_vec->a_w.w_symbol = s;\n        pack_bang(x);\n    }\n    else pd_error(x, \"pack_symbol: wrong type\");\n}\n\n    /* without a list method, pack_anything() would be called */\nstatic void pack_list(t_pack *x, t_symbol *s, int ac, t_atom *av)\n{\n    obj_list(&x->x_obj, 0, ac, av);\n}\n\nstatic void pack_anything(t_pack *x, t_symbol *s, int ac, t_atom *av)\n{\n    t_atom *av2 = (t_atom *)alloca((ac + 1) * sizeof(t_atom));\n    int i;\n    for (i = 0; i < ac; i++)\n        av2[i + 1] = av[i];\n    SETSYMBOL(av2, s);\n    obj_list(&x->x_obj, 0, ac + 1, av2);\n}\n\nstatic void pack_free(t_pack *x)\n{\n    int i;\n    for (i = 0; i < x->x_nptr; i++)\n        gpointer_unset(&x->x_gpointer[i]);\n    freebytes(x->x_vec, x->x_n * sizeof(*x->x_vec));\n    freebytes(x->x_gpointer, x->x_nptr * sizeof(*x->x_gpointer));\n}\n\nstatic void pack_setup(void)\n{\n    pack_class = class_new(gensym(\"pack\"), (t_newmethod)pack_new,\n        (t_method)pack_free, sizeof(t_pack), 0, A_GIMME, 0);\n    class_addbang(pack_class, pack_bang);\n    class_addpointer(pack_class, pack_pointer);\n    class_addfloat(pack_class, pack_float);\n    class_addsymbol(pack_class, pack_symbol);\n    class_addlist(pack_class, pack_list);\n    class_addanything(pack_class, pack_anything);\n}\n\n/* -------------------------- unpack ------------------------------ */\n\nstatic t_class *unpack_class;\n\ntypedef struct unpackout\n{\n    t_atomtype u_type;\n    t_outlet *u_outlet;\n} t_unpackout;\n\ntypedef struct _unpack\n{\n    t_object x_obj;\n    t_int x_n;\n    t_unpackout *x_vec;\n} t_unpack;\n\nstatic void *unpack_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_unpack *x = (t_unpack *)pd_new(unpack_class);\n    t_atom defarg[2], *ap;\n    t_unpackout *u;\n    int i;\n    if (!argc)\n    {\n        argv = defarg;\n        argc = 2;\n        SETFLOAT(&defarg[0], 0);\n        SETFLOAT(&defarg[1], 0);\n    }\n    x->x_n = argc;\n    x->x_vec = (t_unpackout *)getbytes(argc * sizeof(*x->x_vec));\n    for (i = 0, ap = argv, u = x->x_vec; i < argc; u++, ap++, i++)\n    {\n        t_atomtype type = ap->a_type;\n        if (type == A_SYMBOL)\n        {\n            char c = *ap->a_w.w_symbol->s_name;\n            if (c == 's')\n            {\n                u->u_type = A_SYMBOL;\n                u->u_outlet = outlet_new(&x->x_obj, &s_symbol);\n            }\n            else if (c == 'p')\n            {\n                u->u_type =  A_POINTER;\n                u->u_outlet = outlet_new(&x->x_obj, &s_pointer);\n            }\n            else\n            {\n                if (c != 'f') pd_error(x, \"unpack: %s: bad type\",\n                    ap->a_w.w_symbol->s_name);\n                u->u_type = A_FLOAT;\n                u->u_outlet = outlet_new(&x->x_obj, &s_float);\n            }\n        }\n        else\n        {\n            u->u_type =  A_FLOAT;\n            u->u_outlet = outlet_new(&x->x_obj, &s_float);\n        }\n    }\n    return (x);\n}\n\nstatic void unpack_list(t_unpack *x, t_symbol *s, int argc, t_atom *argv)\n{\n    t_atom *ap;\n    t_unpackout *u;\n    int i;\n    if (argc > x->x_n) argc = (int)x->x_n;\n    for (i = argc, u = x->x_vec + i, ap = argv + i; u--, ap--, i--;)\n    {\n        t_atomtype type = u->u_type;\n        if (type != ap->a_type)\n            pd_error(x, \"unpack: type mismatch\");\n        else if (type == A_FLOAT)\n            outlet_float(u->u_outlet, ap->a_w.w_float);\n        else if (type == A_SYMBOL)\n            outlet_symbol(u->u_outlet, ap->a_w.w_symbol);\n        else outlet_pointer(u->u_outlet, ap->a_w.w_gpointer);\n    }\n}\n\nstatic void unpack_anything(t_unpack *x, t_symbol *s, int ac, t_atom *av)\n{\n    t_atom *av2 = (t_atom *)getbytes((ac + 1) * sizeof(t_atom));\n    int i;\n    for (i = 0; i < ac; i++)\n        av2[i + 1] = av[i];\n    SETSYMBOL(av2, s);\n    unpack_list(x, 0, ac+1, av2);\n    freebytes(av2, (ac + 1) * sizeof(t_atom));\n}\n\nstatic void unpack_free(t_unpack *x)\n{\n    freebytes(x->x_vec, x->x_n * sizeof(*x->x_vec));\n}\n\nstatic void unpack_setup(void)\n{\n    unpack_class = class_new(gensym(\"unpack\"), (t_newmethod)unpack_new,\n        (t_method)unpack_free, sizeof(t_unpack), 0, A_GIMME, 0);\n    class_addlist(unpack_class, unpack_list);\n    class_addanything(unpack_class, unpack_anything);\n}\n\n/* -------------------------- trigger ------------------------------ */\n\nstatic t_class *trigger_class;\n#define TR_BANG 0\n#define TR_FLOAT 1\n#define TR_SYMBOL 2\n#define TR_POINTER 3\n#define TR_LIST 4\n#define TR_ANYTHING 5\n\ntypedef struct triggerout\n{\n    int u_type;         /* outlet type from above */\n    t_outlet *u_outlet;\n} t_triggerout;\n\ntypedef struct _trigger\n{\n    t_object x_obj;\n    t_int x_n;\n    t_triggerout *x_vec;\n} t_trigger;\n\nstatic void *trigger_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_trigger *x = (t_trigger *)pd_new(trigger_class);\n    t_atom defarg[2], *ap;\n    t_triggerout *u;\n    int i;\n    if (!argc)\n    {\n        argv = defarg;\n        argc = 2;\n        SETSYMBOL(&defarg[0], &s_bang);\n        SETSYMBOL(&defarg[1], &s_bang);\n    }\n    x->x_n = argc;\n    x->x_vec = (t_triggerout *)getbytes(argc * sizeof(*x->x_vec));\n    for (i = 0, ap = argv, u = x->x_vec; i < argc; u++, ap++, i++)\n    {\n        t_atomtype thistype = ap->a_type;\n        char c;\n        if (thistype == TR_SYMBOL) c = ap->a_w.w_symbol->s_name[0];\n        else if (thistype == TR_FLOAT) c = 'f';\n        else c = 0;\n        if (c == 'p')\n            u->u_type = TR_POINTER,\n                u->u_outlet = outlet_new(&x->x_obj, &s_pointer);\n        else if (c == 'f')\n            u->u_type = TR_FLOAT, u->u_outlet = outlet_new(&x->x_obj, &s_float);\n        else if (c == 'b')\n            u->u_type = TR_BANG, u->u_outlet = outlet_new(&x->x_obj, &s_bang);\n        else if (c == 'l')\n            u->u_type = TR_LIST, u->u_outlet = outlet_new(&x->x_obj, &s_list);\n        else if (c == 's')\n            u->u_type = TR_SYMBOL,\n                u->u_outlet = outlet_new(&x->x_obj, &s_symbol);\n        else if (c == 'a')\n            u->u_type = TR_ANYTHING,\n                u->u_outlet = outlet_new(&x->x_obj, &s_symbol);\n        else\n        {\n            pd_error(x, \"trigger: %s: bad type\", ap->a_w.w_symbol->s_name);\n            u->u_type = TR_FLOAT, u->u_outlet = outlet_new(&x->x_obj, &s_float);\n        }\n    }\n    return (x);\n}\n\nstatic void trigger_list(t_trigger *x, t_symbol *s, int argc, t_atom *argv)\n{\n    t_triggerout *u;\n    int i;\n    for (i = (int)x->x_n, u = x->x_vec + i; u--, i--;)\n    {\n        if (u->u_type == TR_FLOAT)\n            outlet_float(u->u_outlet, (argc ? atom_getfloat(argv) : 0));\n        else if (u->u_type == TR_BANG)\n            outlet_bang(u->u_outlet);\n        else if (u->u_type == TR_SYMBOL)\n            outlet_symbol(u->u_outlet,\n                (argc ? atom_getsymbol(argv) : &s_symbol));\n        else if (u->u_type == TR_POINTER)\n        {\n            if (!argc || argv->a_type != TR_POINTER)\n                pd_error(x, \"trigger: bad pointer\");\n            else outlet_pointer(u->u_outlet, argv->a_w.w_gpointer);\n        }\n        else if (u->u_type == TR_LIST)\n            outlet_list(u->u_outlet, &s_list, argc, argv);\n        else outlet_anything(u->u_outlet, s, argc, argv);\n    }\n}\n\nstatic void trigger_anything(t_trigger *x, t_symbol *s, int argc, t_atom *argv)\n{\n    t_triggerout *u;\n    int i;\n    for (i = (int)x->x_n, u = x->x_vec + i; u--, i--;)\n    {\n        if (u->u_type == TR_BANG)\n            outlet_bang(u->u_outlet);\n        else if (u->u_type == TR_ANYTHING)\n            outlet_anything(u->u_outlet, s, argc, argv);\n        else pd_error(x, \"trigger: generic messages can only be converted to 'b' or 'a'\");\n    }\n}\n\nstatic void trigger_bang(t_trigger *x)\n{\n    trigger_list(x, &s_bang, 0, 0);\n}\n\nstatic void trigger_pointer(t_trigger *x, t_gpointer *gp)\n{\n    t_atom at;\n    SETPOINTER(&at, gp);\n    trigger_list(x, &s_pointer, 1, &at);\n}\n\nstatic void trigger_float(t_trigger *x, t_float f)\n{\n    t_atom at;\n    SETFLOAT(&at, f);\n    trigger_list(x, &s_float, 1, &at);\n}\n\nstatic void trigger_symbol(t_trigger *x, t_symbol *s)\n{\n    t_atom at;\n    SETSYMBOL(&at, s);\n    trigger_list(x, &s_symbol, 1, &at);\n}\n\nstatic void trigger_free(t_trigger *x)\n{\n    freebytes(x->x_vec, x->x_n * sizeof(*x->x_vec));\n}\n\nstatic void trigger_setup(void)\n{\n    trigger_class = class_new(gensym(\"trigger\"), (t_newmethod)trigger_new,\n        (t_method)trigger_free, sizeof(t_trigger), 0, A_GIMME, 0);\n    class_addcreator((t_newmethod)trigger_new, gensym(\"t\"), A_GIMME, 0);\n    class_addlist(trigger_class, trigger_list);\n    class_addbang(trigger_class, trigger_bang);\n    class_addpointer(trigger_class, trigger_pointer);\n    class_addfloat(trigger_class, (t_method)trigger_float);\n    class_addsymbol(trigger_class, trigger_symbol);\n    class_addanything(trigger_class, trigger_anything);\n}\n\n/* -------------------------- spigot ------------------------------ */\nstatic t_class *spigot_class;\n\ntypedef struct _spigot\n{\n    t_object x_obj;\n    t_float x_state;\n} t_spigot;\n\nstatic void *spigot_new(t_floatarg f)\n{\n    t_spigot *x = (t_spigot *)pd_new(spigot_class);\n    floatinlet_new(&x->x_obj, &x->x_state);\n    outlet_new(&x->x_obj, 0);\n    x->x_state = f;\n    return (x);\n}\n\nstatic void spigot_bang(t_spigot *x)\n{\n    if (x->x_state != 0) outlet_bang(x->x_obj.ob_outlet);\n}\n\nstatic void spigot_pointer(t_spigot *x, t_gpointer *gp)\n{\n    if (x->x_state != 0) outlet_pointer(x->x_obj.ob_outlet, gp);\n}\n\nstatic void spigot_float(t_spigot *x, t_float f)\n{\n    if (x->x_state != 0) outlet_float(x->x_obj.ob_outlet, f);\n}\n\nstatic void spigot_symbol(t_spigot *x, t_symbol *s)\n{\n    if (x->x_state != 0) outlet_symbol(x->x_obj.ob_outlet, s);\n}\n\nstatic void spigot_list(t_spigot *x, t_symbol *s, int argc, t_atom *argv)\n{\n    if (x->x_state != 0) outlet_list(x->x_obj.ob_outlet, s, argc, argv);\n}\n\nstatic void spigot_anything(t_spigot *x, t_symbol *s, int argc, t_atom *argv)\n{\n    if (x->x_state != 0) outlet_anything(x->x_obj.ob_outlet, s, argc, argv);\n}\n\nstatic void spigot_setup(void)\n{\n    spigot_class = class_new(gensym(\"spigot\"), (t_newmethod)spigot_new, 0,\n        sizeof(t_spigot), 0, A_DEFFLOAT, 0);\n    class_addbang(spigot_class, spigot_bang);\n    class_addpointer(spigot_class, spigot_pointer);\n    class_addfloat(spigot_class, spigot_float);\n    class_addsymbol(spigot_class, spigot_symbol);\n    class_addlist(spigot_class, spigot_list);\n    class_addanything(spigot_class, spigot_anything);\n}\n\n/* --------------------------- moses ----------------------------- */\nstatic t_class *moses_class;\n\ntypedef struct _moses\n{\n    t_object x_ob;\n    t_outlet *x_out2;\n    t_float x_y;\n} t_moses;\n\nstatic void *moses_new(t_floatarg f)\n{\n    t_moses *x = (t_moses *)pd_new(moses_class);\n    floatinlet_new(&x->x_ob, &x->x_y);\n    outlet_new(&x->x_ob, &s_float);\n    x->x_out2 = outlet_new(&x->x_ob, &s_float);\n    x->x_y = f;\n    return (x);\n}\n\nstatic void moses_float(t_moses *x, t_float f)\n{\n    if (f < x->x_y) outlet_float(x->x_ob.ob_outlet, f);\n    else outlet_float(x->x_out2, f);\n}\n\nstatic void moses_setup(void)\n{\n    moses_class = class_new(gensym(\"moses\"), (t_newmethod)moses_new, 0,\n        sizeof(t_moses), 0, A_DEFFLOAT, 0);\n    class_addfloat(moses_class, moses_float);\n}\n\n/* ----------------------- until --------------------- */\n\nstatic t_class *until_class;\n\ntypedef struct _until\n{\n    t_object x_obj;\n    int x_run;\n    int x_count;\n} t_until;\n\nstatic void *until_new(void)\n{\n    t_until *x = (t_until *)pd_new(until_class);\n    inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym(\"bang\"), gensym(\"bang2\"));\n    outlet_new(&x->x_obj, &s_bang);\n    x->x_run = 0;\n    return (x);\n}\n\nstatic void until_bang(t_until *x)\n{\n    x->x_run = 1;\n    x->x_count = -1;\n    while (x->x_run && x->x_count)\n        x->x_count--, outlet_bang(x->x_obj.ob_outlet);\n}\n\nstatic void until_float(t_until *x, t_float f)\n{\n    if (f < 0)\n        f = 0;\n    x->x_run = 1;\n    x->x_count = f;\n    while (x->x_run && x->x_count)\n        x->x_count--, outlet_bang(x->x_obj.ob_outlet);\n}\n\nstatic void until_bang2(t_until *x)\n{\n    x->x_run = 0;\n}\n\nstatic void until_setup(void)\n{\n    until_class = class_new(gensym(\"until\"), (t_newmethod)until_new, 0,\n        sizeof(t_until), 0, 0);\n    class_addbang(until_class, until_bang);\n    class_addfloat(until_class, until_float);\n    class_addmethod(until_class, (t_method)until_bang2, gensym(\"bang2\"), 0);\n}\n\n/* ----------------------- makefilename --------------------- */\n\nstatic t_class *makefilename_class;\n\ntypedef enum {\n    NONE = 0,\n    INT,\n    FLOAT,\n    STRING,\n    POINTER,\n} t_printtype;\n\ntypedef struct _makefilename\n{\n    t_object x_obj;\n    t_symbol *x_format;\n    t_printtype x_accept;\n} t_makefilename;\n\nstatic const char* _formatscan(const char*str, t_printtype*typ) {\n    int infmt=0;\n    for (; *str; str++) {\n        if (!infmt && *str=='%') {\n            infmt=1;\n            continue;\n        }\n        if (infmt) {\n            if (*str=='%') {\n                infmt=0;\n                continue;\n            }\n            if (strchr(\"-.#0123456789\",*str)!=0)\n                continue;\n            if (*str=='s') {\n                *typ = STRING;\n                return str;\n            }\n            if (strchr(\"fgGeE\",*str)!=0) {\n                *typ = FLOAT;\n                return str;\n            }\n            if (strchr(\"xXdiouc\",*str)!=0) {\n                *typ = INT;\n                return str;\n            }\n           if (strchr(\"p\",*str)!=0) {\n                *typ = POINTER;\n                return str;\n            }\n        }\n    }\n    *typ = NONE;\n    return str;\n}\n\nstatic void makefilename_scanformat(t_makefilename *x)\n{\n    const char *str;\n    t_printtype typ;\n    if (!x->x_format) return;\n    str = x->x_format->s_name;\n    str = _formatscan(str, &typ);\n    x->x_accept = typ;\n    if (str && (NONE != typ)) {\n            /* try again, to see if there's another format specifier (which we forbid) */\n        str = _formatscan(str, &typ);\n        if (NONE != typ) {\n            pd_error(x, \"makefilename: invalid format string '%s' (too many format specifiers)\", x->x_format->s_name);\n            x->x_format = 0;\n            return;\n        }\n    }\n}\n\nstatic void *makefilename_new(t_symbol *s)\n{\n    t_makefilename *x = (t_makefilename *)pd_new(makefilename_class);\n    if (!s || !*s->s_name)\n        s = gensym(\"file.%d\");\n    outlet_new(&x->x_obj, &s_symbol);\n    x->x_format = s;\n    x->x_accept = NONE;\n    makefilename_scanformat(x);\n    return (x);\n}\n\nstatic void makefilename_float(t_makefilename *x, t_floatarg f)\n{\n    char buf[MAXPDSTRING];\n    if(!x->x_format) {\n        pd_error(x, \"makefilename: no format specifier given\");\n        return;\n    }\n    switch(x->x_accept) {\n    case NONE:\n        sprintf(buf, \"%s\",  x->x_format->s_name);\n        break;\n    case INT: case POINTER:\n        sprintf(buf, x->x_format->s_name, (int)f);\n        break;\n    case FLOAT:\n        sprintf(buf, x->x_format->s_name, f);\n        break;\n    case STRING: {\n        char buf2[MAXPDSTRING];\n        sprintf(buf2, \"%g\", f);\n        sprintf(buf, x->x_format->s_name, buf2);\n        break;\n    }\n    default:\n        sprintf(buf, \"%s\", x->x_format->s_name);\n    }\n    if (buf[0]!=0)\n        outlet_symbol(x->x_obj.ob_outlet, gensym(buf));\n}\n\nstatic void makefilename_symbol(t_makefilename *x, t_symbol *s)\n{\n    char buf[MAXPDSTRING];\n    if(!x->x_format) {\n        pd_error(x, \"makefilename: no format specifier given\");\n        return;\n    }\n    switch(x->x_accept) {\n    case STRING: case POINTER:\n        sprintf(buf, x->x_format->s_name, s->s_name);\n        break;\n    case INT:\n        sprintf(buf, x->x_format->s_name, 0);\n        break;\n    case FLOAT:\n        sprintf(buf, x->x_format->s_name, 0.);\n        break;\n    case NONE:\n        sprintf(buf, \"%s\", x->x_format->s_name);\n        break;\n    default:\n        sprintf(buf, \"%s\", x->x_format->s_name);\n    }\n    if (buf[0]!=0)\n        outlet_symbol(x->x_obj.ob_outlet, gensym(buf));\n}\n\nstatic void makefilename_bang(t_makefilename *x)\n{\n    char buf[MAXPDSTRING];\n    if(!x->x_format) {\n        pd_error(x, \"makefilename: no format specifier given\");\n        return;\n    }\n    switch(x->x_accept) {\n    case INT:\n        sprintf(buf, x->x_format->s_name, 0);\n        break;\n    case FLOAT:\n        sprintf(buf, x->x_format->s_name, 0.);\n        break;\n    case NONE:\n        sprintf(buf, \"%s\", x->x_format->s_name);\n        break;\n    default:\n        sprintf(buf, \"%s\", x->x_format->s_name);\n    }\n    if (buf[0]!=0)\n        outlet_symbol(x->x_obj.ob_outlet, gensym(buf));\n}\n\nstatic void makefilename_set(t_makefilename *x, t_symbol *s)\n{\n    x->x_format = s;\n    makefilename_scanformat(x);\n}\n\nstatic void makefilename_setup(void)\n{\n    makefilename_class = class_new(gensym(\"makefilename\"),\n    (t_newmethod)makefilename_new, 0,\n        sizeof(t_makefilename), 0, A_DEFSYM, 0);\n    class_addfloat(makefilename_class, makefilename_float);\n    class_addsymbol(makefilename_class, makefilename_symbol);\n    class_addbang(makefilename_class, makefilename_bang);\n    class_addmethod(makefilename_class, (t_method)makefilename_set,\n        gensym(\"set\"), A_SYMBOL, 0);\n}\n\n/* -------------------------- swap ------------------------------ */\nstatic t_class *swap_class;\n\ntypedef struct _swap\n{\n    t_object x_obj;\n    t_outlet *x_out2;\n    t_float x_f1;\n    t_float x_f2;\n} t_swap;\n\nstatic void *swap_new(t_floatarg f)\n{\n    t_swap *x = (t_swap *)pd_new(swap_class);\n    x->x_f2 = f;\n    x->x_f1 = 0;\n    outlet_new(&x->x_obj, &s_float);\n    x->x_out2 = outlet_new(&x->x_obj, &s_float);\n    floatinlet_new(&x->x_obj, &x->x_f2);\n    return (x);\n}\n\nstatic void swap_bang(t_swap *x)\n{\n    outlet_float(x->x_out2, x->x_f1);\n    outlet_float(x->x_obj.ob_outlet, x->x_f2);\n}\n\nstatic void swap_float(t_swap *x, t_float f)\n{\n    x->x_f1 = f;\n    swap_bang(x);\n}\n\nvoid swap_setup(void)\n{\n    swap_class = class_new(gensym(\"swap\"), (t_newmethod)swap_new, 0,\n        sizeof(t_swap), 0, A_DEFFLOAT, 0);\n    class_addcreator((t_newmethod)swap_new, gensym(\"fswap\"), A_DEFFLOAT, 0);\n    class_addbang(swap_class, swap_bang);\n    class_addfloat(swap_class, swap_float);\n}\n\n/* -------------------------- change ------------------------------ */\nstatic t_class *change_class;\n\ntypedef struct _change\n{\n    t_object x_obj;\n    t_float x_f;\n} t_change;\n\nstatic void *change_new(t_floatarg f)\n{\n    t_change *x = (t_change *)pd_new(change_class);\n    x->x_f = f;\n    outlet_new(&x->x_obj, &s_float);\n    return (x);\n}\n\nstatic void change_bang(t_change *x)\n{\n    outlet_float(x->x_obj.ob_outlet, x->x_f);\n}\n\nstatic void change_float(t_change *x, t_float f)\n{\n    if (f != x->x_f)\n    {\n        x->x_f = f;\n        outlet_float(x->x_obj.ob_outlet, x->x_f);\n    }\n}\n\nstatic void change_set(t_change *x, t_float f)\n{\n    x->x_f = f;\n}\n\nvoid change_setup(void)\n{\n    change_class = class_new(gensym(\"change\"), (t_newmethod)change_new, 0,\n        sizeof(t_change), 0, A_DEFFLOAT, 0);\n    class_addbang(change_class, change_bang);\n    class_addfloat(change_class, change_float);\n    class_addmethod(change_class, (t_method)change_set, gensym(\"set\"),\n        A_DEFFLOAT, 0);\n}\n\n/* -------------------- value ------------------------------ */\n\nstatic t_class *value_class, *vcommon_class;\n\ntypedef struct vcommon\n{\n    t_pd c_pd;\n    int c_refcount;\n    t_float c_f;\n} t_vcommon;\n\ntypedef struct _value\n{\n    t_object x_obj;\n    t_symbol *x_sym;\n    t_float *x_floatstar;\n} t_value;\n\n    /* get a pointer to a named floating-point variable.  The variable\n    belongs to a \"vcommon\" object, which is created if necessary. */\nt_float *value_get(t_symbol *s)\n{\n    t_vcommon *c = (t_vcommon *)pd_findbyclass(s, vcommon_class);\n    if (!c)\n    {\n        c = (t_vcommon *)pd_new(vcommon_class);\n        c->c_f = 0;\n        c->c_refcount = 0;\n        pd_bind(&c->c_pd, s);\n    }\n    c->c_refcount++;\n    return (&c->c_f);\n}\n\n    /* release a variable.  This only frees the \"vcommon\" resource when the\n    last interested party releases it. */\nvoid value_release(t_symbol *s)\n{\n    t_vcommon *c = (t_vcommon *)pd_findbyclass(s, vcommon_class);\n    if (c)\n    {\n        if (!--c->c_refcount)\n        {\n            pd_unbind(&c->c_pd, s);\n            pd_free(&c->c_pd);\n        }\n    }\n    else bug(\"value_release\");\n}\n\n/*\n * value_getfloat -- obtain the float value of a \"value\" object\n *                  return 0 on success, 1 otherwise\n */\nint value_getfloat(t_symbol *s, t_float *f)\n{\n    t_vcommon *c = (t_vcommon *)pd_findbyclass(s, vcommon_class);\n    if (!c)\n        return (1);\n    *f = c->c_f;\n    return (0);\n}\n\n/*\n * value_setfloat -- set the float value of a \"value\" object\n *                  return 0 on success, 1 otherwise\n */\nint value_setfloat(t_symbol *s, t_float f)\n{\n    t_vcommon *c = (t_vcommon *)pd_findbyclass(s, vcommon_class);\n    if (!c)\n        return (1);\n    c->c_f = f;\n    return (0);\n}\n\nstatic void vcommon_float(t_vcommon *x, t_float f)\n{\n    x->c_f = f;\n}\n\nstatic void *value_new(t_symbol *s)\n{\n    t_value *x = (t_value *)pd_new(value_class);\n    if (!*s->s_name)\n        inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym(\"symbol\"), gensym(\"symbol2\"));\n    x->x_sym = s;\n    x->x_floatstar = value_get(s);\n    outlet_new(&x->x_obj, &s_float);\n    return (x);\n}\n\nstatic void value_bang(t_value *x)\n{\n    outlet_float(x->x_obj.ob_outlet, *x->x_floatstar);\n}\n\nstatic void value_float(t_value *x, t_float f)\n{\n    *x->x_floatstar = f;\n}\n\n/* set method */\nstatic void value_symbol2(t_value *x, t_symbol *s)\n{\n    value_release(x->x_sym);\n    x->x_sym = s;\n    x->x_floatstar = value_get(s);\n}\n\nstatic void value_send(t_value *x, t_symbol *s)\n{\n    if (s->s_thing)\n        pd_float(s->s_thing, *x->x_floatstar);\n    else pd_error(x, \"%s: no such object\", s->s_name);\n}\n\nstatic void value_ff(t_value *x)\n{\n    value_release(x->x_sym);\n}\n\nstatic void value_setup(void)\n{\n    value_class = class_new(gensym(\"value\"), (t_newmethod)value_new,\n        (t_method)value_ff,\n        sizeof(t_value), 0, A_DEFSYM, 0);\n    class_addcreator((t_newmethod)value_new, gensym(\"v\"), A_DEFSYM, 0);\n    class_addbang(value_class, value_bang);\n    class_addfloat(value_class, value_float);\n    class_addmethod(value_class, (t_method)value_symbol2, gensym(\"symbol2\"),\n        A_DEFSYM, 0);\n    class_addmethod(value_class, (t_method)value_send, gensym(\"send\"), A_SYMBOL, 0);\n    vcommon_class = class_new(gensym(\"value\"), 0, 0,\n        sizeof(t_vcommon), CLASS_PD, 0);\n    class_addfloat(vcommon_class, vcommon_float);\n}\n\n/* -------------- overall setup routine for this file ----------------- */\n\nvoid x_connective_setup(void)\n{\n    pdint_setup();\n    pdfloat_setup();\n    pdsymbol_setup();\n    bang_setup();\n    send_setup();\n    receive_setup();\n    select_setup();\n    route_setup();\n    pack_setup();\n    unpack_setup();\n    trigger_setup();\n    spigot_setup();\n    moses_setup();\n    until_setup();\n    makefilename_setup();\n    swap_setup();\n    change_setup();\n    value_setup();\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/x_file.c",
    "content": "/* Copyright (c) 2021 IOhannes m zmölnig.\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n/* The \"file\" object. */\n#define _XOPEN_SOURCE 600\n#define _DEFAULT_SOURCE\n\n#include \"m_pd.h\"\n#include \"g_canvas.h\"\n#include \"s_utf8.h\"\n\n#include \"m_private_utils.h\"\n\n#include <string.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n#include <errno.h>\n#ifdef HAVE_UNISTD_H\n# include <unistd.h>\n#endif\n\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n#include <time.h>\n\n#ifdef _WIN32\n# include <windows.h>\n# include <direct.h>\n# include <io.h>\n#else\n# include <glob.h>\n# include <ftw.h>\n#endif\n\n#ifdef _MSC_VER\n# include <BaseTsd.h>\ntypedef unsigned int mode_t;\ntypedef SSIZE_T ssize_t;\n# define wstat _wstat\n#endif\n\n#ifndef S_ISREG\n  #define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)\n#endif\n#ifndef S_ISDIR\n# define S_ISDIR(mode)  (((mode) & S_IFMT) == S_IFDIR)\n#endif\n\n#ifndef X_FILE_DEBUG\n# define X_FILE_DEBUG PD_DEBUG\n#endif\n\n#ifdef _WIN32\nstatic int do_delete_ucs2(wchar_t*pathname) {\n    struct stat sb;\n    if (!wstat(pathname, &sb) && (S_ISDIR(sb.st_mode))) {\n            /* a directory */\n        return !(RemoveDirectoryW(pathname));\n    } else {\n            /* probably a file */\n        return !(DeleteFileW(pathname));\n    }\n}\n\nstatic int sys_stat(const char *pathname, struct stat *statbuf) {\n    uint16_t ucs2buf[MAX_PATH];\n    u8_utf8toucs2(ucs2buf, MAX_PATH, pathname, strlen(pathname));\n    return wstat(ucs2buf, statbuf);\n}\n\nstatic int sys_rename(const char *oldpath, const char *newpath) {\n    uint16_t src[MAX_PATH], dst[MAX_PATH];\n    u8_utf8toucs2(src, MAX_PATH, oldpath, MAX_PATH);\n    u8_utf8toucs2(dst, MAX_PATH, newpath, MAX_PATH);\n    return _wrename(src, dst);\n}\nstatic int sys_mkdir(const char *pathname, mode_t mode) {\n    uint16_t ucs2name[MAX_PATH];\n    (void)mode;\n    u8_utf8toucs2(ucs2name, MAX_PATH, pathname, MAX_PATH);\n    return !(CreateDirectoryW(ucs2name, 0));\n}\nstatic int sys_remove(const char *pathname) {\n    uint16_t ucs2buf[MAXPDSTRING];\n    u8_utf8toucs2(ucs2buf, MAXPDSTRING, pathname, MAXPDSTRING);\n    return do_delete_ucs2(ucs2buf);\n}\nstatic char* sys_getcwd(char *buf) {\n    uint16_t ucs2buf[MAXPDSTRING];\n    memset(ucs2buf, 0, sizeof(ucs2buf));\n    if (!_wgetcwd(ucs2buf, MAXPDSTRING))\n        return 0;\n\n    u8_ucs2toutf8(buf, MAXPDSTRING-1, ucs2buf, -1);\n    buf[MAXPDSTRING-1] = 0;\n    sys_unbashfilename(buf, buf);\n    return buf;\n}\nstatic int sys_chdir(const char *path) {\n    uint16_t ucs2buf[MAXPDSTRING];\n    u8_utf8toucs2(ucs2buf, MAXPDSTRING, path, MAXPDSTRING);\n    return _wchdir(ucs2buf);\n}\n#else\nstatic int sys_stat(const char *pathname, struct stat *statbuf) {\n    return stat(pathname, statbuf);\n}\n\nstatic int sys_rename(const char *oldpath, const char *newpath) {\n    return rename(oldpath, newpath);\n}\nstatic int sys_mkdir(const char *pathname, mode_t mode) {\n    return mkdir(pathname, mode);\n}\nstatic int sys_remove(const char *pathname) {\n    return remove(pathname);\n}\nstatic char* sys_getcwd(char *buf) {\n    return getcwd(buf, MAXPDSTRING);\n}\nstatic int sys_chdir(const char *path) {\n    return chdir(path);\n}\n#endif\n\n    /* expand env vars and ~ at the beginning of a path and make a copy to return */\nstatic char*do_expandpath(const char *from, char *to, int bufsize)\n{\n    if ((strlen(from) == 1 && from[0] == '~') || (strncmp(from,\"~/\", 2) == 0))\n    {\n#ifdef _WIN32\n        const char *home = getenv(\"USERPROFILE\");\n#else\n        const char *home = getenv(\"HOME\");\n#endif\n        if (home)\n        {\n            strncpy(to, home, bufsize);\n            to[bufsize-1] = 0;\n            strncpy(to + strlen(to), from + 1, bufsize - strlen(to));\n            to[bufsize-1] = 0;\n        }\n        else *to = 0;\n    }\n    else\n    {\n        strncpy(to, from, bufsize);\n        to[bufsize-1] = 0;\n    }\n#ifdef _WIN32\n    {\n        char *buf = alloca(bufsize);\n        ExpandEnvironmentStrings(to, buf, bufsize-1);\n        buf[bufsize-1] = 0;\n        strncpy(to, buf, bufsize);\n        to[bufsize-1] = 0;\n    }\n#endif\n    return to;\n}\n\n    /* unbash '\\' to '/', and drop duplicate '/' */\nstatic char*do_pathnormalize(const char *from, char *to) {\n    const char *rp;\n    char *wp, c;\n    sys_unbashfilename(from, to);\n    rp=wp=to;\n    while((*wp++=c=*rp++)) {\n        if('/' == c) {\n            while('/' == *rp++);\n            rp--;\n        }\n    }\n    return to;\n}\n\nstatic char*do_expandunbash(const char *from, char *to, int bufsize) {\n    do_expandpath(from, to, bufsize);\n    to[bufsize-1]=0;\n    sys_unbashfilename(to, to);\n    to[bufsize-1]=0;\n    return to;\n}\n\nstatic int str_endswith(char* str, char* end){\n    size_t strsize = strlen(str), endsize = strlen(end);\n    if(strsize<endsize) return 0;\n    return strcmp(str + strsize - endsize, end) == 0;\n}\n\nstatic t_symbol*do_splitpath(const char*path, int*argc, t_atom**argv) {\n    t_symbol*slashsym = gensym(\"/\");\n    t_atom*outv;\n    int outc=0, outsize=1;\n    char buffer[MAXPDSTRING], *pathname=buffer;\n    sys_unbashfilename(path, buffer);\n    buffer[MAXPDSTRING-1] = 0;\n\n        /* first count the number of path components */\n    while(*pathname)\n        outsize += ('/'==*pathname++);\n    pathname=buffer;\n    outv = (t_atom*)getbytes(outsize * sizeof(*outv));\n\n    if('/' == *pathname)\n        SETSYMBOL(outv+outc, slashsym), outc++;\n\n    while(*pathname) {\n        char*pathsep;\n        while('/' == *pathname)\n            pathname++;\n        pathsep=strchr(pathname, '/');\n        if(!pathsep) {\n            if(*pathname)\n                SETSYMBOL(outv+outc, gensym(pathname)), outc++;\n            break;\n        }\n        *pathsep=0;\n        SETSYMBOL(outv+outc, gensym(pathname)), outc++;\n        pathname=pathsep+1;\n    }\n\n    if(outc != outsize) {\n        t_atom*a=resizebytes(outv, outsize * sizeof(*outv), outc * sizeof(*outv));\n        if(!a) {\n            freebytes(outv, outsize * sizeof(*outv));\n            outsize = outc = 0;\n        }\n        outv = a;\n    }\n    *argc = outc;\n    *argv = outv;\n    return (*pathname)?0:slashsym;\n}\n\n/* joins up all the path-components (using '/' as the path delimiter)\n * the (optional) prefix is prepended to the string\n   (to be used for Windows volumes)\n * the (optional) suffix is present in the result (and appended if required to the string)\n   (to be used for trailing slash)\n * atoms that are A_NULL are skipped\n */\nstatic t_symbol*do_joinpath(t_symbol*prefix, int argc, t_atom*argv, t_symbol*suffix) {\n        /* luckily for us, the path-separator in Pd is always '/' */\n    const char pathseparator = '/';\n    size_t alen, bufsize = 0;\n    char buffer[MAXPDSTRING];\n    t_symbol*result = 0;\n    int needseparator = 0;\n    int i;\n\n    memset(buffer, 0, sizeof(buffer));\n\n    if(prefix) {\n        strcpy(buffer+bufsize, prefix->s_name);\n        bufsize += strlen(prefix->s_name);\n    }\n\n    for(i=0; i<argc; i++) {\n        char sbuf[MAXPDSTRING];\n        const char*abuf=sbuf;\n        t_atom*a = argv+i;\n        switch(a->a_type) {\n        case A_NULL:\n            abuf = 0;\n            break;\n        case A_SYMBOL:\n            abuf = atom_getsymbol(a)->s_name;\n            break;\n        default:\n            atom_string(a, sbuf, MAXPDSTRING);\n            abuf = sbuf;\n            break;\n        }\n        if(!abuf || !(alen = strlen(abuf))) continue;\n        if(pathseparator == abuf[alen-1])\n            needseparator = 0;\n        if(bufsize+alen+needseparator >= sizeof(buffer))\n            break;\n        if(needseparator) {\n            buffer[bufsize]=pathseparator;\n            bufsize++;\n        }\n        needseparator=1;\n\n        strcpy(buffer+bufsize, abuf);\n        bufsize+=alen;\n    }\n\n    if(suffix && (alen=strlen(suffix->s_name)) && (bufsize+alen)<sizeof(buffer)) {\n        strcpy(buffer+bufsize, suffix->s_name);\n        bufsize+=alen;\n    }\n\n    result = gensym(do_pathnormalize(buffer, buffer));\n    return result;\n}\n\n\n#ifdef _WIN32\nstatic const char*do_errmsg(char*buffer, size_t bufsize) {\n    char errcode[10];\n    char*s;\n    wchar_t wbuf[MAXPDSTRING];\n    DWORD err = GetLastError();\n    DWORD count = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,\n        0, err, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), wbuf, MAXPDSTRING, NULL);\n    if (!count || !WideCharToMultiByte(CP_UTF8, 0, wbuf, count+1, buffer, bufsize, 0, 0))\n        *buffer = '\\0';\n    s=buffer + strlen(buffer)-1;\n    while(('\\r' == *s || '\\n' == *s) && s>buffer)\n        *s--=0;\n    snprintf(errcode, sizeof(errcode), \" [%ld]\", err);\n    errcode[sizeof(errcode)-1] = 0;\n    strcat(buffer, errcode);\n    return buffer;\n}\n#else /* !_WIN32 */\nstatic const char*do_errmsg(char*buffer, size_t bufsize) {\n    (void)buffer; (void)bufsize;\n    return strerror(errno);\n}\n#endif /* !_WIN32 */\n\ntypedef struct _file_handler {\n    int fh_fd;\n    int fh_mode; /* 0..read, 1..write */\n} t_file_handler;\n#define x_fd x_fhptr->fh_fd\n#define x_mode x_fhptr->fh_mode\ntypedef struct _file_handle {\n    t_object x_obj;\n    t_file_handler x_fh;\n    t_file_handler*x_fhptr;\n    t_symbol*x_fcname; /* multiple [file handle] object can refer to the same [file define] */\n\n    mode_t x_creationmode; /* default: 0666, 0777 */\n    int x_verbose; /* default: 0 */\n\n    t_canvas*x_canvas;\n    t_outlet*x_dataout;\n    t_outlet*x_infoout;\n\n} t_file_handle;\n\n\nstatic int do_checkpathname(t_file_handle*x, const char*path) {\n    int err_count = 0, warn_count = 0;\n    char buf[4];\n    const char*s;\n\n        /* check for illegal characters */\n    for(s = path; *s; s++) {\n        const char*ill = \"illegal\";\n        int oops = 0;\n        switch(*s) {\n        case ':': case '\\\\':\n            ill = \"reserved\";\n                /* falls through */\n        case '<': case '>':\n        case '|':\n        case '\"':\n                //case '/':\n        case '?': case '*':\n            oops++;\n            break;\n        default:\n            if(*s<32)\n                oops++;\n        }\n\n        if(oops) {\n#ifdef _WIN32\n            if(x->x_verbose)\n                pd_error(x, \"the path \\\"%s\\\" contains the character '%c', which is %s.\", path, *s, ill);\n            err_count++;\n#else\n            if(x->x_verbose)\n                logpost(x, X_FILE_DEBUG, \"cross-platform issue: the path \\\"%s\\\" contains the character '%c', which is %s on MSW.\", path, *s, ill);\n            warn_count++;\n#endif\n            goto fail;\n        }\n    }\n\n        /* check for illegal names */\n    strncpy(buf, path, sizeof(buf));\n    buf[3] = 0;\n    if(buf[2]) {\n        const char* forbidden_names0[] = {\"AUX\", \"CON\", \"NUL\", \"PRN\", 0};\n        const char* forbidden_names1[] = {\"COM\", \"LPT\", 0};\n        const char**ref;\n            /* upper-case the string */\n        int i;\n        for(i=0; i<3; i++) {\n            if (buf[i] >= 'a' && buf[i] <= 'z')\n                buf[i] = buf[i] - ('a' - 'A');\n        }\n            /* AUX, CON, NULL, PRN */\n        for(ref = forbidden_names0; *ref; ref++) {\n            if(!strcmp(*ref, buf)\n                && (!path[3] || '.' == path[3])) {\n#ifdef _WIN32\n                if(x->x_verbose)\n                    pd_error(x, \"the path \\\"%s\\\" contains the reserved name '%s'.\", path, *ref);\n                err_count++;\n#else\n                if(x->x_verbose)\n                    logpost(x, X_FILE_DEBUG, \"cross-platform issue: the path \\\"%s\\\" contains the name '%s' which is reserved on MSW.\", path, *ref);\n                warn_count++;\n#endif\n                goto fail;\n            }\n        }\n            /* COM[1-9], LPT[1-9] */\n        for(ref = forbidden_names1; *ref; ref++) {\n            if(!strcmp(*ref, buf)\n                && path[3] > '0' && path[3] <= '9'\n                && (!path[4] || '.' == path[4])) {\n#ifdef _WIN32\n                if(x->x_verbose)\n                    pd_error(x, \"the path \\\"%s\\\" contains the reserved name '%s[1-9]'.\", path, *ref);\n                err_count++;\n#else\n                if(x->x_verbose)\n                    logpost(x, X_FILE_DEBUG, \"cross-platform issue: the path \\\"%s\\\" contains the name '%s[1-9]' which is reserved on MSW.\", path, *ref);\n                warn_count++;\n#endif\n                goto fail;\n            }\n        }\n    }\n    return 0;\n\n fail:\n    if(err_count>0)\n        return 2;\n    if(warn_count>0)\n        return 1;\n    return 0;\n}\n\nstatic int do_parse_creationmode(t_atom*ap) {\n    const char*s;\n    if(A_FLOAT==ap->a_type)\n        return atom_getfloat(ap);\n    if(A_SYMBOL!=ap->a_type)\n        return -1; /* oopsie */\n    s = atom_getsymbol(ap)->s_name;\n    if(!strncmp(s, \"0o\", 2)) {\n            /* octal mode */\n        char*endptr;\n        long mode = strtol(s+2, &endptr, 8);\n        return (*endptr)?-1:(int)mode;\n    } else if(!strncmp(s, \"0x\", 2)) {\n            /* hex mode: nobody sane uses this... */\n        char*endptr;\n        long mode = strtol(s+2, &endptr, 16);\n        return (*endptr)?-1:(int)mode;\n    } else {\n            /* free form mode: a+rwx,go-w */\n            /* not supported yet */\n        return -1;\n    }\n    return -1;\n}\n\nstatic void do_parse_args(t_file_handle*x, int argc, t_atom*argv) {\n        /*\n         * -q: quiet mode\n         * -v: verbose mode\n         * -m <mode>: creation mode\n         */\n    t_symbol*flag_m = gensym(\"-m\");\n    t_symbol*flag_q = gensym(\"-q\");\n    t_symbol*flag_v = gensym(\"-v\");\n    x->x_fcname = 0;\n    while(argc--) {\n        const t_symbol*flag = atom_getsymbol(argv);\n        if (0);\n        else if (flag == flag_q) {\n            x->x_verbose--;\n        } else if (flag == flag_v) {\n            x->x_verbose++;\n        } else if (flag == flag_m) {\n            int mode;\n            if(!argc) {\n                pd_error(x, \"'-m' requires an argument\");\n                break;\n            }\n            argc--;\n            argv++;\n            mode = do_parse_creationmode(argv);\n            if(mode<0) {\n                char buf[MAXPDSTRING];\n                atom_string(argv, buf, MAXPDSTRING);\n                pd_error(x, \"invalid creation mode '%s'\", buf);\n                break;\n            } else {\n                x->x_creationmode = mode;\n            }\n        } else {\n            int filearg = (!argc);\n            if(filearg) {\n                x->x_fcname = (t_symbol*)flag;\n            } else {\n                pd_error(x, \"unknown flag %s\", flag->s_name);\n            }\n            break;\n        }\n        argv++;\n    }\n    x->x_verbose = x->x_verbose > 0;\n}\n\nstatic t_file_handle* do_file_handle_new(t_class*cls, t_symbol*s, int argc, t_atom*argv, int verbose, mode_t creationmode) {\n    t_file_handle*x = (t_file_handle*)pd_new(cls);\n    (void)s;\n    x->x_fhptr = &x->x_fh;\n    x->x_fd = -1;\n    x->x_canvas = canvas_getcurrent();\n    x->x_creationmode = creationmode;\n    x->x_verbose = verbose;\n\n    x->x_dataout = outlet_new(&x->x_obj, 0);\n    x->x_infoout = outlet_new(&x->x_obj, 0);\n    do_parse_args(x, argc, argv);\n    return x;\n}\n\nstatic int do_file_open(t_file_handle*x, const char* filename, int mode) {\n    char expandbuf[MAXPDSTRING+1];\n    int fd = sys_open(do_expandpath(filename, expandbuf, MAXPDSTRING), mode, x?x->x_creationmode:0666);\n    if(x) {\n        x->x_fd = fd;\n        if(fd<0) {\n            if(x->x_verbose)\n                pd_error(x, \"unable to open '%s': %s\", filename, strerror(errno));\n            if(x->x_infoout)\n                outlet_bang(x->x_infoout);\n        }\n    }\n    return fd;\n}\n\n\nstatic void file_set_verbosity(t_file_handle*x, t_float f) {\n    x->x_verbose = (f>0.5);\n}\nstatic void file_set_creationmode(t_file_handle*x, t_symbol*s, int argc, t_atom*argv) {\n    if(argc!=1) {\n        pd_error(x, \"usage: '%s <mode>'\", s->s_name);\n        return;\n    }\n    x->x_creationmode = do_parse_creationmode(argv);\n}\n\n    /* ================ [file handle] ====================== */\nt_class *file_define_class;\nstatic int file_handle_getdefine(t_file_handle*x) {\n    if(x->x_fcname) {\n        t_file_handle *y = (t_file_handle *)pd_findbyclass(x->x_fcname,\n                                                           file_define_class);\n        if(y) {\n            x->x_fhptr=&y->x_fh;\n            return 1;\n        }\n        return 0;\n    }\n    x->x_fhptr = &x->x_fh;\n    return 1;\n}\n\nstatic void file_handle_close(t_file_handle*x) {\n    if (x->x_fd>=0)\n        sys_close(x->x_fd);\n    x->x_fd = -1;\n}\nstatic int file_handle_checkopen(t_file_handle*x, const char*cmd) {\n    if(x->x_fcname) {\n        if(!file_handle_getdefine(x)) {\n            pd_error(x, \"file handle: couldn't find file-define '%s'\", x->x_fcname->s_name);\n            return 0;\n        }\n    }\n    if(x->x_fd<0) {\n        if(!cmd)cmd=(x->x_mode)?\"write\":\"read\";\n        pd_error(x, \"'%s' without prior 'open'\", cmd);\n        return 0;\n    }\n    return 1;\n}\nstatic void file_handle_do_read(t_file_handle*x, t_float f) {\n    t_atom*outv;\n    unsigned char*buf;\n    ssize_t n, len, outc=f;\n    if(outc<1) {\n        pd_error(x, \"cannot read %d bytes\", (int)outc);\n        return;\n    }\n    ALLOCA(unsigned char, buf, outc, 100);\n    ALLOCA(t_atom, outv, outc, 100);\n    if(buf && outv) {\n        len = read(x->x_fd, buf, outc);\n        for(n=0; n<len; n++) {\n            SETFLOAT(outv+n, (t_float)buf[n]);\n        }\n        if(len>0) {\n            outlet_list(x->x_dataout, gensym(\"list\"), len, outv);\n        } else if (!len) {\n            file_handle_close(x);\n            outlet_bang(x->x_infoout);\n        } else {\n            if(x->x_verbose)\n                pd_error(x, \"read failed: %s\", strerror(errno));\n            file_handle_close(x);\n            outlet_bang(x->x_infoout);\n        }\n    } else {\n        pd_error(x, \"couldn't allocate buffer for %d bytes\", (int)outc);\n    }\n    FREEA(unsigned char, buf, outc, 100);\n    FREEA(t_atom, outv, outc, 100);\n}\nstatic void file_handle_do_write(t_file_handle*x, int argc, t_atom*argv) {\n    unsigned char*buf;\n    size_t len = (argc>0)?argc:0;\n    ALLOCA(unsigned char, buf, argc, 100);\n    if(buf) {\n        ssize_t n;\n        for(n=0; n<argc; n++) {\n            buf[n] = atom_getfloat(argv+n);\n        }\n        n = write(x->x_fd, buf, len);\n        if(n >= 0 && (size_t)n < len) {\n            n = write(x->x_fd, buf+n, len-n);\n        }\n        if (n<0) {\n            pd_error(x, \"write failed: %s\", strerror(errno));\n            file_handle_close(x);\n            outlet_bang(x->x_infoout);\n        }\n    } else {\n        pd_error(x, \"could not allocate %d bytes for writing\", argc);\n    }\n    FREEA(unsigned char, buf, argc, 100);\n}\nstatic void file_handle_list(t_file_handle*x, t_symbol*s, int argc, t_atom*argv) {\n    (void)s;\n    if(!file_handle_checkopen(x, 0))\n        return;\n    if(x->x_mode) {\n            /* write_mode */\n        file_handle_do_write(x, argc, argv);\n        } else {\n            /* read mode */\n        if(1==argc && A_FLOAT==argv->a_type) {\n            file_handle_do_read(x, atom_getfloat(argv));\n        } else {\n            pd_error(x, \"no way to handle 'list' messages while reading file\");\n        }\n    }\n}\nstatic void file_handle_set(t_file_handle*x, t_symbol*s) {\n    if (gensym(\"\") == s)\n        s=0;\n    if (s && x->x_fhptr == &x->x_fh && x->x_fh.fh_fd >= 0) {\n            /* trying to set a name, even though we have an fd open... */\n        pd_error(x, \"file handle: shadowing local file descriptor with '%s'\", s->s_name);\n    } else if (!s && x->x_fhptr != &x->x_fh && x->x_fh.fh_fd >= 0) {\n        logpost(x, X_FILE_DEBUG, \"file handle: unshadowing local file descriptor\");\n    }\n    x->x_fcname = s;\n    file_handle_getdefine(x);\n}\nstatic void file_handle_seek(t_file_handle*x, t_symbol*s, int argc, t_atom*argv) {\n    off_t offset=0;\n    int whence = SEEK_SET;\n    t_atom a[1];\n    switch(argc) {\n    case 0:\n            /* just output the current position */\n        whence=SEEK_CUR;\n        break;\n    case 2: {\n        if (A_SYMBOL!=argv[1].a_type)\n            goto usage;\n        s=atom_getsymbol(argv+1);\n        switch(*s->s_name) {\n        case 'S': case 's': case 0:\n            whence = SEEK_SET;\n            break;\n        case 'E': case 'e':\n            whence = SEEK_END;\n            break;\n        case 'C': case 'c': case 'R': case 'r':\n            whence = SEEK_CUR;\n            break;\n        default:\n            pd_error(x, \"seek mode must be 'set', 'end' or 'current' (resp. 'relative')\");\n            return;\n        }\n    }\n            /* falls through */\n    case 1:\n        if (A_FLOAT!=argv[0].a_type)\n            goto usage;\n        offset = (int)atom_getfloat(argv);\n        break;\n    }\n\n    if(!file_handle_checkopen(x, \"seek\"))\n        return;\n    offset = lseek(x->x_fd, offset, whence);\n    SETFLOAT(a, offset);\n    outlet_anything(x->x_infoout, gensym(\"seek\"), 1, a);\n    return;\n usage:\n    pd_error(x, \"usage: seek [<int:offset> [<symbol:mode>]]\");\n}\nstatic void file_handle_open(t_file_handle*x, t_symbol*file, t_symbol*smode) {\n    int mode = O_RDONLY;\n    if (x->x_fd>=0) {\n        pd_error(x, \"'open' without prior 'close'\");\n        return;\n    }\n    if(!file_handle_getdefine(x)) {\n        pd_error(x, \"file handle: couldn't find file-define '%s'\", x->x_fcname->s_name);\n        return;\n    }\n    if(smode && smode!=&s_) {\n        switch(smode->s_name[0]) {\n        case 'r': /* read */\n            mode = O_RDONLY;\n            break;\n        case 'w': /* write */\n            mode = O_WRONLY;\n            break;\n        case 'a': /* append */\n            mode = O_WRONLY | O_APPEND;\n            break;\n        case 'c': /* create */\n            mode = O_WRONLY | O_TRUNC;\n            break;\n        }\n    }\n    if(mode & O_WRONLY) {\n        mode |= O_CREAT;\n    }\n    if(do_file_open(x, file->s_name, mode)>=0) {\n            /* check if we haven't accidentally opened a directory */\n        struct stat sb;\n        if(fstat(x->x_fd, &sb)) {\n            file_handle_close(x);\n            if(x->x_verbose)\n                pd_error(x, \"unable to stat '%s': %s\", file->s_name, strerror(errno));\n            outlet_bang(x->x_infoout);\n            return;\n        }\n        if(S_ISDIR(sb.st_mode)) {\n            file_handle_close(x);\n            if(x->x_verbose)\n                pd_error(x, \"unable to open directory '%s' as file\", file->s_name);\n            outlet_bang(x->x_infoout);\n            return;\n        }\n        x->x_mode = (mode&O_WRONLY)?1:0;\n    }\n}\n\nstatic void file_handle_free(t_file_handle*x) {\n        /* close our own file handle (if any) */\n    x->x_fhptr = &x->x_fh;\n    file_handle_close(x);\n}\n\n    /* ================ [file stat] ====================== */\nstatic int do_file_stat(t_file_handle*x, const char*filename, struct stat*sb, int*is_symlink) {\n    int result = -1;\n    int fd = -1;\n    char buf[MAXPDSTRING+1];\n    do_expandpath(filename, buf, MAXPDSTRING);\n\n    if(is_symlink) {\n        *is_symlink=0;\n#ifdef S_IFLNK\n        if(!lstat(buf, sb)) {\n            *is_symlink = !!(S_ISLNK(sb->st_mode));\n        }\n#endif\n    }\n    result = sys_stat(buf, sb);\n    if(!result)\n        return result;\n\n    fd = do_file_open(0, filename, 0);\n\n    if(fd >= 0) {\n        result = fstat(fd, sb);\n        sys_close(fd);\n    } else\n        result = -1;\n\n    if(x) {\n        x->x_fd = -1;\n        if(result && x->x_verbose) {\n            pd_error(x, \"could not stat on '%s': %s\", filename, strerror(errno));\n        }\n    }\n    return result;\n}\nstatic void do_dataout_symbol(t_file_handle*x, const char*selector, t_symbol*s) {\n    t_atom ap[1];\n    SETSYMBOL(ap, s);\n    outlet_anything(x->x_dataout, gensym(selector), 1, ap);\n}\nstatic void do_dataout_float(t_file_handle*x, const char*selector, t_float f) {\n    t_atom ap[1];\n    SETFLOAT(ap, f);\n    outlet_anything(x->x_dataout, gensym(selector), 1, ap);\n}\nstatic void do_dataout_time(t_file_handle*x, const char*selector, time_t t) {\n    t_atom ap[7];\n    struct tm *ts = localtime(&t);\n    if(!ts) {\n        pd_error(x, \"unable to convert timestamp %ld\", (long int)t);\n    }\n    SETFLOAT(ap+0, ts->tm_year + 1900);\n    SETFLOAT(ap+1, ts->tm_mon + 1);\n    SETFLOAT(ap+2, ts->tm_mday);\n    SETFLOAT(ap+3, ts->tm_hour);\n    SETFLOAT(ap+4, ts->tm_min);\n    SETFLOAT(ap+5, ts->tm_sec);\n    SETFLOAT(ap+6, ts->tm_isdst);\n    outlet_anything(x->x_dataout, gensym(selector), 7, ap);\n}\nstatic void file_stat_symbol(t_file_handle*x, t_symbol*filename) {\n        /* get all the info for the given file */\n    struct stat sb;\n    t_symbol*s;\n    int is_symlink=0;\n    int readable=0, writable=0, executable=0, owned=-1;\n    char buf[MAXPDSTRING+1];\n\n    if(do_file_stat(x, filename->s_name, &sb, &is_symlink) < 0) {\n        outlet_bang(x->x_infoout);\n        return;\n    }\n\n        /* this is wrong: readable/writable/executable are supposed to report\n         * on the *current* user, not the *owner*\n         */\n    readable = !!(sb.st_mode & 0400);\n    writable = !!(sb.st_mode & 0200);\n    executable = !!(sb.st_mode & 0100);\n#ifdef HAVE_UNISTD_H\n        /* this is the right way */\n    do_expandpath(filename->s_name, buf, MAXPDSTRING);\n\n    readable = !(access(buf, R_OK));\n    writable = !(access(buf, W_OK));\n    executable = !(access(buf, X_OK));\n#ifndef _WIN32\n    owned = (geteuid() == sb.st_uid);\n#endif\n#endif\n\n    switch (sb.st_mode & S_IFMT) {\n    case S_IFREG:\n#ifdef S_IFLNK\n    case S_IFLNK:\n#endif\n        do_dataout_float(x, \"size\", (int)(sb.st_size));\n        break;\n    case S_IFDIR:\n        do_dataout_float(x, \"size\", 0);\n        break;\n    default:\n        do_dataout_float(x, \"size\", -1);\n        break;\n    }\n    do_dataout_float(x, \"readable\", readable);\n    do_dataout_float(x, \"writable\", writable);\n    do_dataout_float(x, \"executable\", executable);\n    do_dataout_float(x, \"owned\", owned);\n\n    do_dataout_float(x, \"isfile\", !!(S_ISREG(sb.st_mode)));\n    do_dataout_float(x, \"isdirectory\", !!(S_ISDIR(sb.st_mode)));\n    do_dataout_float(x, \"issymlink\", is_symlink);\n\n    do_dataout_float(x, \"uid\", (int)(sb.st_uid));\n    do_dataout_float(x, \"gid\", (int)(sb.st_gid));\n    do_dataout_float(x, \"permissions\", (int)(sb.st_mode & 0777));\n    switch (sb.st_mode & S_IFMT) {\n    case S_IFREG:  s = gensym(\"file\");            break;\n    case S_IFDIR:  s = gensym(\"directory\");       break;\n#ifdef S_IFBLK\n    case S_IFBLK:  s = gensym(\"blockdevice\");     break;\n#endif\n#ifdef S_IFCHR\n    case S_IFCHR:  s = gensym(\"characterdevice\"); break;\n#endif\n#ifdef S_IFIFO\n    case S_IFIFO:  s = gensym(\"pipe\");            break;\n#endif\n#ifdef S_IFLNK\n    case S_IFLNK:  s = gensym(\"symlink\");         break;\n#endif\n#ifdef S_IFSOCK\n    case S_IFSOCK: s = gensym(\"socket\");          break;\n#endif\n    default:       s = 0;                         break;\n    }\n    if(s)\n        do_dataout_symbol(x, \"type\", s);\n    else\n        do_dataout_symbol(x, \"type\", gensym(\"unknown\"));\n\n    do_dataout_time(x, \"atime\", sb.st_atime);\n    do_dataout_time(x, \"mtime\", sb.st_mtime);\n}\n\nstatic void file_size_symbol(t_file_handle*x, t_symbol*filename) {\n    struct stat sb;\n    if(do_file_stat(x, filename->s_name, &sb, 0) < 0) {\n        outlet_bang(x->x_infoout);\n    } else {\n        switch (sb.st_mode & S_IFMT) {\n        case S_IFREG:\n#ifdef S_IFLNK\n        case S_IFLNK:\n#endif\n            outlet_float(x->x_dataout, (int)(sb.st_size));\n            break;\n        case S_IFDIR:\n            outlet_float(x->x_dataout, 0);\n            break;\n        default:\n            outlet_float(x->x_dataout, -1);\n            break;\n        }\n    }\n}\nstatic void file_isfile_symbol(t_file_handle*x, t_symbol*filename) {\n    struct stat sb;\n    if(do_file_stat(x, filename->s_name, &sb, 0) < 0) {\n        outlet_bang(x->x_infoout);\n    } else {\n        outlet_float(x->x_dataout, !!(S_ISREG(sb.st_mode)));\n    }\n}\nstatic void file_isdirectory_symbol(t_file_handle*x, t_symbol*filename) {\n    struct stat sb;\n    if(do_file_stat(x, filename->s_name, &sb, 0) < 0) {\n        outlet_bang(x->x_infoout);\n    } else {\n        outlet_float(x->x_dataout, !!(S_ISDIR(sb.st_mode)));\n    }\n}\n\n    /* ================ [file glob] ====================== */\n#ifdef _WIN32\n/* idiosyncrasies:\n * - cases are ignored ('a*' matches 'A.txt' and 'a.txt'), even with wine on ext4\n * - only the filename component is returned (must prefix path separately)\n * - non-ASCII needs special handling\n * - '*?' seems to be illegal (e.g. 'f*?.txt'); '?*' seems to be fine though\n * - \"*\" matches files starting with '.' (including '.', '..', but also .gitignore)\n * - if the pattern includes '*.', it matches a trailing '~'\n * - wildcards do not apply to directory-components (e.g. 'foo/ * /' (without the spaces, they are just due to C-comments constraints))\n *\n * plan:\n * - concat the path and the filename\n * - convert to utf16 (and back again)\n * - replace '*?' with '*' in the pattern\n * - manually filter out:\n *   - matches starting with '.' if the pattern does not start with '.'\n *   - matches ending in '~' if the pattern does not end with '[*?~]'\n * - only (officially) support wildcards in the filename component (not in the paths)\n * - if the pattern ends with '/', strip it, but return only directories\n */\nstatic void file_glob_symbol(t_file_handle*x, t_symbol*spattern) {\n    WIN32_FIND_DATAW FindFileData;\n    HANDLE hFind;\n    uint16_t ucs2pattern[MAXPDSTRING];\n    char pattern[MAXPDSTRING];\n    int nostartdot=0, noendtilde=0, onlydirs=0;\n    char *filepattern, *strin, *strout;\n    int pathpatternlength=0;\n    int matchdot=0;\n\n    do_expandunbash(spattern->s_name, pattern, MAXPDSTRING);\n\n        /* '.' and '..' should only match if the pattern exquisitely asked for them */\n    if(!strcmp(\".\", pattern) || !strcmp(\"./\", pattern)\n        || str_endswith(pattern, \"/.\") || str_endswith(pattern, \"/./\"))\n        matchdot=1;\n    else if(!strcmp(\"..\", pattern) || !strcmp(\"../\", pattern)\n        || str_endswith(pattern, \"/..\") || str_endswith(pattern, \"/../\"))\n        matchdot=2;\n\n    if (matchdot) {\n            /* windows FindFile would return the actual path rather than '.'\n             * (which would confuse our full-path construction)\n             * so we just return the result directly\n             */\n        struct stat sb;\n        if (!do_file_stat(0, pattern, &sb, 0)) {\n            t_atom outv[2];\n            size_t end = strlen(pattern);\n                /* get rid of trailing slash */\n            if('/' == pattern[end-1])\n                pattern[end-1]=0;\n            SETSYMBOL(outv+0, gensym(pattern));\n            SETFLOAT(outv+1, S_ISDIR(sb.st_mode));\n            outlet_list(x->x_dataout, gensym(\"list\"), 2, outv);\n        } else {\n                // this gets triggered if there is no match...\n            outlet_bang(x->x_infoout);\n        }\n        return;\n    }\n\n\n    filepattern=strrchr(pattern, '/');\n    if(filepattern && !filepattern[1]) {\n            /* patterns ends with slashes: filter for dirs, and bash the trailing slashes */\n        onlydirs=1;\n        while('/' == *filepattern && filepattern>pattern) {\n            *filepattern--=0;\n        }\n        filepattern=strrchr(pattern, '/');\n    }\n    if(!filepattern)\n        filepattern=pattern;\n    else {\n        filepattern++;\n        pathpatternlength=filepattern-pattern;\n    }\n    nostartdot=('.' != *filepattern);\n    strin=filepattern;\n    strout=filepattern;\n    while(*strin) {\n        char c = *strin++;\n        *strout++ = c;\n        if('*' == c) {\n            while('?' == *strin || '*' == *strin)\n                strin++;\n        }\n    }\n    *strout=0;\n    if (strout>pattern) {\n        switch(strout[-1]) {\n        case '~':\n        case '*':\n        case '?':\n            noendtilde=0;\n            break;\n        default:\n            noendtilde=1;\n        }\n    }\n    u8_utf8toucs2(ucs2pattern, MAXPDSTRING, pattern, MAXPDSTRING);\n\n    hFind = FindFirstFileW(ucs2pattern, &FindFileData);\n    if (hFind == INVALID_HANDLE_VALUE) {\n            // this gets triggered if there is no match...\n        outlet_bang(x->x_infoout);\n        return;\n    }\n    do {\n        t_symbol*s;\n        t_atom outv[2];\n        int len = 0;\n        int isdir = !!(FILE_ATTRIBUTE_DIRECTORY & FindFileData.dwFileAttributes);\n        if (matchdot!=1 && !wcscmp(L\".\" , FindFileData.cFileName))\n            continue;\n        if (matchdot!=2 && !wcscmp(L\"..\" , FindFileData.cFileName))\n            continue;\n        u8_ucs2toutf8(filepattern, MAXPDSTRING-pathpatternlength, FindFileData.cFileName, MAX_PATH);\n        len = strlen(filepattern);\n\n        if(onlydirs && !isdir)\n            continue;\n        if(nostartdot && '.' == filepattern[0])\n            continue;\n        if(noendtilde && '~' == filepattern[len-1])\n            continue;\n\n        s = gensym(pattern);\n        SETSYMBOL(outv+0, s);\n        SETFLOAT(outv+1, isdir);\n        outlet_list(x->x_dataout, gensym(\"list\"), 2, outv);\n    } while (FindNextFileW(hFind, &FindFileData) != 0);\n    FindClose(hFind);\n}\n#else /* !_WIN32 */\nstatic void file_glob_symbol(t_file_handle*x, t_symbol*spattern) {\n    t_atom outv[2];\n    glob_t gg;\n    int flags = 0;\n    int matchdot=0;\n    char pattern[MAXPDSTRING];\n    size_t patternlen;\n    int onlydirs;\n    do_expandpath(spattern->s_name, pattern, MAXPDSTRING);\n    patternlen=strlen(pattern);\n    onlydirs = ('/' == pattern[patternlen-1]);\n    if(!strcmp(\".\", pattern) || !strcmp(\"./\", pattern)\n        || str_endswith(pattern, \"/.\") || str_endswith(pattern, \"/./\"))\n        matchdot=1;\n    else if(!strcmp(\"..\", pattern) || !strcmp(\"../\", pattern)\n        || str_endswith(pattern, \"/..\") || str_endswith(pattern, \"/../\"))\n        matchdot=2;\n    if(glob(pattern, flags, NULL, &gg)) {\n            // this gets triggered if there is no match...\n        outlet_bang(x->x_infoout);\n    } else {\n        size_t i;\n        for(i=0; i<gg.gl_pathc; i++) {\n            t_symbol *s;\n            char*path = gg.gl_pathv[i];\n            int isdir = 0;\n            struct stat sb;\n            int end;\n            if(!do_file_stat(0, path, &sb, 0)) {\n                isdir = S_ISDIR(sb.st_mode);\n            }\n            if(onlydirs && !isdir)\n                continue;\n            end=strlen(path);\n            if('/' == path[end-1]) {\n                path[end-1]=0;\n            }\n            if (matchdot!=1 && (!strcmp(path, \".\") || str_endswith(path, \"/.\")))\n                continue;\n            if (matchdot!=2 && (!strcmp(path, \"..\") || str_endswith(path, \"/..\")))\n                continue;\n\n            s = gensym(path);\n            SETSYMBOL(outv+0, s);\n            SETFLOAT(outv+1, isdir);\n            outlet_list(x->x_dataout, gensym(\"list\"), 2, outv);\n        }\n    }\n    globfree(&gg);\n}\n#endif /* _WIN32 */\n\n\n    /* ================ [file which] ====================== */\n\nstatic void file_which_symbol(t_file_handle*x, t_symbol*s) {\n        /* LATER we might output directories as well,... */\n    int isdir=0;\n    t_atom outv[2];\n    char dirresult[MAXPDSTRING], *nameresult;\n    int fd = canvas_open(x->x_canvas,\n        s->s_name, \"\",\n        dirresult, &nameresult, MAXPDSTRING,\n        1);\n    if(fd>=0) {\n        sys_close(fd);\n        if(nameresult>dirresult)\n            nameresult[-1]='/';\n        SETSYMBOL(outv+0, gensym(dirresult));\n        SETFLOAT(outv+1, isdir);\n        outlet_list(x->x_dataout, gensym(\"list\"), 2, outv);\n    } else {\n        outlet_symbol(x->x_infoout, s);\n    }\n}\n\n    /* ================ [file patchpath] ====================== */\n\nstatic void file_patchpath_list(t_file_handle*x, t_symbol*s, int argc, t_atom*argv) {\n    t_canvas *c = x->x_canvas;\n    const char*pathname = 0;\n    int i, parentlevel = 0, effectivelevel = 0;\n\n    switch(argc) {\n    default: goto fail;\n    case 0: break;\n    case 1:\n        switch(argv->a_type) {\n        case(A_SYMBOL):\n            pathname = atom_getsymbol(argv)->s_name;\n            break;\n        case (A_FLOAT):\n            parentlevel = (int)atom_getfloat(argv);\n            break;\n        default: goto fail;\n        }\n        break;\n    case 2:\n        if(A_SYMBOL == argv[0].a_type && A_FLOAT == argv[1].a_type) {\n            pathname = atom_getsymbol(argv+0)->s_name;\n            parentlevel = (int)atom_getfloat(argv+1);\n            break;\n        }\n        goto fail;\n    }\n\n    for (i = 0; i < parentlevel; i++)\n    {\n        while (!c->gl_env)  /* back up to containing canvas or abstraction */\n            c = c->gl_owner;\n        if (c->gl_owner)    /* back up one more into an owner if any */\n        {\n            c = c->gl_owner;\n            effectivelevel++;\n        }\n    }\n    if (pathname)\n    {\n        if(sys_isabsolutepath(pathname))\n        {\n            s = gensym(pathname);\n        } else {\n            char buf[MAXPDSTRING];\n            snprintf(buf, MAXPDSTRING, \"%s/%s\",\n                     canvas_getdir(c)->s_name, pathname);\n            buf[MAXPDSTRING-1] = 0;\n            s = gensym(buf);\n        }\n    }\n    else\n        s = canvas_getdir(c);\n\n    outlet_float(x->x_infoout, effectivelevel);\n    outlet_symbol(x->x_dataout, s);\n    return;\n\n fail:\n    pd_error(x, \"bad arguments for message to object 'file patchpath'\");\n}\n\n    /* ================ [file mkdir] ====================== */\nstatic void file_mkdir_symbol(t_file_handle*x, t_symbol*dir) {\n    char pathname[MAXPDSTRING], *path=pathname, *str;\n    struct stat sb;\n\n    do_expandpath(dir->s_name, pathname, MAXPDSTRING);\n    pathname[MAXPDSTRING-1]=0;\n    do_pathnormalize(pathname, pathname);\n\n    if(sys_isabsolutepath(pathname)) {\n        str=strchr(path, '/');\n        if(str)\n            path=str;\n    }\n    path++;\n    while(*path) {\n            /* get to the next path separator */\n        str=strchr(path, '/');\n        if(str) {\n            *str=0;\n        }\n        if(!sys_stat(pathname, &sb) && (S_ISDIR(sb.st_mode))) {\n                // directory exists, skip...\n        } else {\n            if(sys_mkdir(pathname, x?x->x_creationmode:0777)) {\n                char buf[MAXPDSTRING];\n                pd_error(x, \"failed to create '%s': %s\", pathname, do_errmsg(buf, MAXPDSTRING));\n                outlet_bang(x->x_infoout);\n                return;\n            }\n        }\n\n        if(str) {\n            *str='/';\n            path=str+1;\n        }\n        else\n            break;\n    }\n    outlet_symbol(x->x_dataout, gensym(pathname));\n}\n\n    /* ================ [file delete] ====================== */\n#ifdef _WIN32\nstatic int file_do_delete_recursive_ucs2(uint16_t*path) {\n    WIN32_FIND_DATAW FindFileData;\n    HANDLE hFind;\n    uint16_t pattern[MAX_PATH];\n    swprintf(pattern, MAX_PATH, L\"%ls/*\", path);\n    hFind = FindFirstFileW(pattern, &FindFileData);\n    if (hFind == INVALID_HANDLE_VALUE) {\n        return 1;\n    }\n    do {\n        int isdir = !!(FILE_ATTRIBUTE_DIRECTORY & FindFileData.dwFileAttributes);\n        swprintf(pattern, MAX_PATH, L\"%ls/%ls\", path, FindFileData.cFileName);\n        if(!isdir) {\n            DeleteFileW(pattern);\n        } else {\n                /* skip self and parent */\n            if(!wcscmp(L\".\", FindFileData.cFileName))continue;\n            if(!wcscmp(L\"..\", FindFileData.cFileName))continue;\n            file_do_delete_recursive_ucs2(pattern);\n        }\n    } while (FindNextFileW(hFind, &FindFileData) != 0);\n    FindClose(hFind);\n    return do_delete_ucs2(path);\n}\n\nstatic int file_do_delete_recursive(const char*path) {\n    uint16_t ucs2path[MAXPDSTRING];\n    u8_utf8toucs2(ucs2path, MAXPDSTRING, path, MAXPDSTRING);\n    return file_do_delete_recursive_ucs2(ucs2path);\n}\n#else /* !_WIN32 */\nstatic int nftw_cb(const char *path, const struct stat *s, int flag, struct FTW *f) {\n    (void)s;\n    (void)f;\n    (void)flag;\n    return remove(path);\n}\n\nstatic int file_do_delete_recursive(const char*pathname) {\n    return nftw(pathname, nftw_cb, 128, FTW_MOUNT|FTW_PHYS|FTW_DEPTH);\n}\n#endif /* !_WIN32 */\n\n\nstatic void file_delete_symbol(t_file_handle*x, t_symbol*path) {\n    char pathname[MAXPDSTRING];\n\n    do_expandunbash(path->s_name, pathname, MAXPDSTRING);\n\n    if(sys_remove(pathname)) {\n        char buf[MAXPDSTRING];\n        if(x && x->x_verbose)\n            pd_error(x, \"unable to delete '%s': %s\", pathname, do_errmsg(buf, MAXPDSTRING));\n        outlet_bang(x->x_infoout);\n    } else {\n        outlet_symbol(x->x_dataout, gensym(pathname));\n    }\n}\n\nstatic void file_delete_recursive(t_file_handle*x, t_symbol*path) {\n    char pathname[MAXPDSTRING];\n\n    do_expandunbash(path->s_name, pathname, MAXPDSTRING);\n\n    if(file_do_delete_recursive(pathname)) {\n        if(x->x_verbose) {\n            char buf[MAXPDSTRING];\n            pd_error(x, \"unable to recursively delete '%s': %s\", pathname, do_errmsg(buf, MAXPDSTRING));\n        }\n        outlet_bang(x->x_infoout);\n    } else {\n        outlet_symbol(x->x_dataout, gensym(pathname));\n    }\n}\n\n\n    /* ================ [file copy]/[file move] ====================== */\nstatic int file_do_copy(const char*source, const char*destination, int mode) {\n    int result = 0;\n    ssize_t len;\n    char buf[1024];\n    int src, dst;\n    int wflags = O_WRONLY | O_CREAT | O_TRUNC;\n#ifdef _WIN32\n    wflags |= O_BINARY;\n#endif\n    src = sys_open(source, O_RDONLY);\n    if(src<0)\n        return 1;\n    dst = sys_open(destination, wflags, mode);\n    if(dst<0) {\n        struct stat sb;\n            /* check if destination is a directory: if so calculate the new filename */\n        if(!do_file_stat(0, destination, &sb, 0) && (S_ISDIR(sb.st_mode))) {\n            char destfile[MAXPDSTRING];\n            const char*filename=strrchr(source, '/');\n            if(!filename)\n                filename=source;\n            else\n                filename++;\n            snprintf(destfile, MAXPDSTRING, \"%s/%s\", destination, filename);\n            dst = sys_open(destfile, O_WRONLY | O_CREAT | O_TRUNC, mode);\n        }\n    }\n    if(dst<0)\n        return 1;\n\n    while((len=read(src, buf, sizeof(buf)))>0) {\n        ssize_t wlen=write(dst, buf, len);\n            /* TODO: cater for partial writes */\n        if (wlen<1) {\n            result = 1 ;\n        }\n    }\n    sys_close(src);\n    sys_close(dst);\n\n    return (result);\n}\nstatic int file_do_move(const char*source, const char*destination, int mode) {\n    int olderrno=0;\n    int result = sys_rename(source, destination);\n    (void)mode;\n    if(result) {\n        struct stat sb;\n        int isfile=0;\n        int isdir=0;\n\n        olderrno=errno;\n            /* check whether we are trying to move a file to a directory */\n        if(do_file_stat(0, source, &sb, 0) < 0)\n            goto done; /* source is not statable, that's a serious error */\n        isfile = !(S_ISDIR(sb.st_mode));\n        if(do_file_stat(0, destination, &sb, 0) < 0)\n            goto done;  /* destination is not statable (so it doesn't exist and is not a directory either */\n        isdir = (S_ISDIR(sb.st_mode));\n        if(isfile && isdir) {\n            char destfile[MAXPDSTRING];\n            const char*filename=strrchr(source, '/');\n            if(!filename)\n                filename=source;\n            else\n                filename++;\n            snprintf(destfile, MAXPDSTRING, \"%s/%s\", destination, filename);\n            result = sys_rename(source, destfile);\n            olderrno = errno;\n        }\n    }\n\n    if(result && EXDEV == errno) {\n            /* need to manually copy the file to the another filesystem */\n        result = file_do_copy(source, destination, mode);\n        if(!result) {\n            olderrno=0;\n                /* copy succeeded, now get rid of the source file */\n            if(sys_remove(source)) {\n                    /* oops, couldn't delete the source-file...\n                     * we still report this as SUCCESS, as the file has been duplicated.\n                     * LATER we might try to unlink() the file first.\n                     * set the olderrno to print some error in verbose-mode\n                     */\n                olderrno=errno;\n            }\n        }\n    }\n done:\n    errno=olderrno;\n    return result;\n}\nstatic void file_do_copymove(t_file_handle*x,\n    const char*verb, int (*fun)(const char*,const char*,int),\n    t_symbol*s, int argc, t_atom*argv) {\n    struct stat sb;\n    char src[MAXPDSTRING], dst[MAXPDSTRING];\n    if(argc != 2 || A_SYMBOL != argv[0].a_type || A_SYMBOL != argv[1].a_type) {\n        pd_error(x, \"bad arguments for [file %s] - should be 'source:symbol destination:symbol'\", verb);\n        return;\n    }\n    do_expandunbash(atom_getsymbol(argv+0)->s_name, src, MAXPDSTRING);\n    do_expandunbash(atom_getsymbol(argv+1)->s_name, dst, MAXPDSTRING);\n\n    if(!sys_stat(src, &sb)) {\n        if(S_ISDIR(sb.st_mode)) {\n            if(x->x_verbose) {\n                pd_error(x, \"failed to %s '%s': %s\", verb, src, strerror(EISDIR));\n            }\n            outlet_bang(x->x_infoout);\n            return;\n        }\n    }\n\n    errno = 0;\n    if(fun(src, dst, x->x_creationmode?x->x_creationmode:sb.st_mode)) {\n        if(x->x_verbose) {\n            char buf[MAXPDSTRING];\n            pd_error(x, \"failed to %s '%s' to '%s': %s\", verb, src, dst, do_errmsg(buf, MAXPDSTRING));\n        }\n        outlet_bang(x->x_infoout);\n    } else {\n        if(errno && x->x_verbose) {\n            char buf[MAXPDSTRING];\n            pd_error(x, \"troubles (but overall success) to %s '%s' to '%s': %s\", verb, src, dst, do_errmsg(buf, MAXPDSTRING));\n        }\n        outlet_list(x->x_dataout, s, argc, argv);\n    }\n}\n\nstatic void file_copy_list(t_file_handle*x, t_symbol*s, int argc, t_atom*argv) {\n    file_do_copymove(x, \"copy\", file_do_copy, s, argc, argv);\n}\nstatic void file_move_list(t_file_handle*x, t_symbol*s, int argc, t_atom*argv) {\n    file_do_copymove(x, \"move\", file_do_move, s, argc, argv);\n}\n\n/* ================ file cwd  ====================== */\nstatic void file_cwd_bang(t_file_handle*x) {\n    char buf[MAXPDSTRING];\n    if(sys_getcwd(buf)) {\n        outlet_symbol(x->x_dataout, gensym(buf));\n    } else {\n        if(x->x_verbose)\n            pd_error(x, \"could not query current working directory: %s\", do_errmsg(buf, MAXPDSTRING));\n        outlet_bang(x->x_infoout);\n    }\n}\nstatic void file_cwd_symbol(t_file_handle*x, t_symbol*path) {\n    if(!sys_chdir(path->s_name)) {\n        file_cwd_bang(x);\n    } else {\n        if(x->x_verbose) {\n            char buf[MAXPDSTRING];\n            pd_error(x, \"could not change the working directory to '%s': %s\",\n                     path->s_name, do_errmsg(buf, MAXPDSTRING));\n        }\n        outlet_bang(x->x_infoout);\n    }\n}\n\n\n/* ================ file path operations ====================== */\nstatic void file_split_symbol(t_file_handle*x, t_symbol*path) {\n    int outc = 0;\n    t_atom*outv = 0;\n    t_symbol*slashsym = do_splitpath(path->s_name, &outc, &outv);\n\n    if (slashsym)\n        outlet_symbol(x->x_infoout, slashsym);\n    else\n        outlet_bang(x->x_infoout);\n\n    outlet_list(x->x_dataout, gensym(\"list\"), outc, outv);\n    freebytes(outv, outc * sizeof(*outv));\n}\n\nstatic void file_join_list(t_file_handle*x, t_symbol*s, int argc, t_atom*argv) {\n    s = do_joinpath(0, argc, argv, 0);\n    outlet_symbol(x->x_dataout, s);\n}\n\nstatic void file_splitext_symbol(t_file_handle*x, t_symbol*path) {\n    char pathname[MAXPDSTRING];\n    t_atom outv[2];\n    char*str;\n    sys_unbashfilename(path->s_name, pathname);\n    pathname[MAXPDSTRING-1]=0;\n    str=pathname + strlen(pathname)-1;\n    if(str < pathname || '.' != *str)\n    {\n        while(str>=pathname) {\n            char c = *str;\n            switch(c) {\n            case '.':\n                str[0]=0;\n                SETSYMBOL(outv+0, gensym(pathname));\n                SETSYMBOL(outv+1, gensym(str+1));\n                outlet_list(x->x_dataout, gensym(\"list\"), 2, outv);\n                return;\n            case '/':\n                str = pathname;\n                break;\n            default:\n                break;\n            }\n            str--;\n        }\n    }\n    outlet_symbol(x->x_infoout, gensym(pathname));\n}\nstatic void file_splitname_symbol(t_file_handle*x, t_symbol*path) {\n    char pathname[MAXPDSTRING];\n    char*str;\n    sys_unbashfilename(path->s_name, pathname);\n    pathname[MAXPDSTRING-1]=0;\n    str=strrchr(pathname, '/');\n    if(str>pathname) {\n        t_symbol*s;\n        *str++=0;\n        s = gensym(pathname);\n        if(*str) {\n            t_atom outv[2];\n            SETSYMBOL(outv+0, s);\n            SETSYMBOL(outv+1, gensym(str));\n            outlet_list(x->x_dataout, gensym(\"list\"), 2, outv);\n        } else {\n            outlet_symbol(x->x_dataout, s);\n        }\n    } else {\n        outlet_symbol(x->x_infoout, gensym(pathname));\n    }\n}\n\nstatic void file_normalize_symbol(t_file_handle*x, t_symbol*path) {\n    char _path[MAXPDSTRING];\n    char*xpath=do_expandunbash(path->s_name, _path, sizeof(_path));\n    int argc=0;\n    t_atom*argv=0;\n    t_symbol*dot=gensym(\".\"), *dotdot=gensym(\"..\");\n    t_symbol*slash=gensym(\"/\"), *dotslash=gensym(\"./\");\n    const int isabs=sys_isabsolutepath(xpath);\n    t_symbol*suffix=0,*volume=0, *result=0;\n    int check, changed, i;\n\n#ifdef _WIN32\n    if(xpath[0] && ':' == xpath[1]) {\n        char vol[3];\n        vol[0] = xpath[0];\n        vol[1] = xpath[1];\n        vol[2] = 0;\n        volume = gensym(vol);\n        xpath+=2;\n    }\n#endif\n    suffix=do_splitpath(xpath, &argc, &argv);\n\n        /* drop 'empty' elements ('.') */\n    for(i=isabs; i<argc; i++)\n        if(atom_getsymbol(argv+i) == dot) {\n            argv[i].a_type = A_NULL;\n            continue;\n        }\n\n        /* drop any non-'..' followed by a '..' (recursively) */\n    changed=1;\n    while(changed) {\n        t_atom*last=0;\n        t_symbol*s=0;\n        changed = 0;\n        for(i=isabs; i<argc; i++) {\n            if(A_NULL == argv[i].a_type) continue;\n            s = atom_getsymbol(argv+i);\n            if(s == dotdot) {\n                if (last) {\n                        /* we just encountered a 'xxx/..' portion, that cancels itself out */\n                    last->a_type = argv[i].a_type = A_NULL;\n                    changed = 1;\n                }\n                last = 0;\n            } else {\n                last = argv+i;\n            }\n        }\n    }\n\n        /* drop any leading '..' components in absolute mode... */\n    if(isabs) {\n        for(i=isabs; i<argc; i++) {\n            t_atom*a=argv+i;\n            if(A_NULL == a->a_type) continue;\n            if (dotdot == atom_getsymbol(a)) {\n                a->a_type = A_NULL;\n            } else break;\n        }\n    }\n\n        /* an join the leftovers */\n    result = do_joinpath(volume, argc, argv, suffix);\n\n\n        /* find problematic components */\n    check = 0;\n    for(i=0; i<argc; i++) {\n        t_atom*a=argv+i;\n        int chk;\n        if(A_NULL == a->a_type) continue;\n        chk = do_checkpathname(x, atom_getsymbol(a)->s_name);\n        if(chk>check)check=chk;\n    }\n\n        /* free dynamically allocated memory before outputting data */\n    freebytes(argv, argc * sizeof(*argv));\n\n        /* output the error/warning/ok */\n    outlet_float(x->x_infoout, check);\n\n        /* output the normalized path */\n    if(result && *result->s_name) {\n        if(!isabs && slash == result)\n            outlet_symbol(x->x_dataout, dotslash);\n        else\n            outlet_symbol(x->x_dataout, result);\n    } else {\n        if(suffix)\n            outlet_symbol(x->x_dataout, dotslash);\n        else\n            outlet_symbol(x->x_dataout, dot);\n    }\n}\n\n\nstatic void file_isabsolute_symbol(t_file_handle*x, t_symbol*path) {\n    char xpath[MAXPDSTRING];\n    outlet_float(x->x_dataout, sys_isabsolutepath(do_expandunbash(path->s_name, xpath, MAXPDSTRING)));\n}\n\n\n    /* overall creator for \"file\" objects - dispatch to \"file handle\" etc */\nt_class *file_handle_class, *file_which_class, *file_glob_class, *file_patchpath_class;\nt_class *file_stat_class, *file_size_class, *file_isfile_class, *file_isdirectory_class;\nt_class *file_mkdir_class, *file_delete_class, *file_copy_class, *file_move_class;\nt_class *file_split_class,*file_join_class,*file_splitext_class, *file_splitname_class;\nt_class *file_normalize_class, *file_isabsolute_class, *file_cwd_class;\n\n#define FILE_PD_NEW(verb, verbose, creationmode) static t_file_handle* file_##verb##_new(t_symbol*s, int argc, t_atom*argv) \\\n    {                                                                   \\\n        return do_file_handle_new(file_##verb##_class, s, argc, argv, verbose, creationmode); \\\n    }\n\nstatic void file_define_ignore(t_file_handle*x, t_symbol*s, int argc, t_atom*argv) {\n        /* this is a noop (so the object does not bail out if somebody 'send's something to its label) */\n    (void)x;\n    (void)s;\n    (void)argc;\n    (void)argv;\n}\nstatic t_file_handle*file_define_new(t_symbol*s, int argc, t_atom*argv) {\n   t_file_handle*x = (t_file_handle*)pd_new(file_define_class);\n   x->x_fhptr = &x->x_fh;\n   x->x_fh.fh_fd = -1;\n   x->x_canvas = canvas_getcurrent();\n   x->x_creationmode = 0666;\n   x->x_verbose = 0;\n   if(1 == argc && A_SYMBOL == argv->a_type) {\n       x->x_fcname = atom_getsymbol(argv);\n       pd_bind(&x->x_obj.ob_pd, x->x_fcname);\n   } else {\n       pd_error(x, \"%s requires an argument: handle name\", s->s_name);\n   }\n   return x;\n}\nstatic void file_define_free(t_file_handle*x) {\n    file_handle_close(x);\n    if(x->x_fcname)\n        pd_unbind(&x->x_obj.ob_pd, x->x_fcname);\n}\n\nstatic t_file_handle*file_handle_new(t_symbol*s, int argc, t_atom*argv) {\n    t_file_handle*x=do_file_handle_new(file_handle_class, s, argc, argv, 1, 0666);\n    inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym(\"symbol\"), gensym(\"set\"));\n    return x;\n}\n\n\nFILE_PD_NEW(which, 0, 0);\nFILE_PD_NEW(patchpath, 0, 0);\nFILE_PD_NEW(glob, 0, 0);\n\nFILE_PD_NEW(stat, 0, 0);\nFILE_PD_NEW(size, 0, 0);\nFILE_PD_NEW(isfile, 0, 0);\nFILE_PD_NEW(isdirectory, 0, 0);\n\nFILE_PD_NEW(mkdir, 0, 0777);\nFILE_PD_NEW(delete, 0, 0);\nFILE_PD_NEW(copy, 0, 0);\nFILE_PD_NEW(move, 0, 0);\n\nFILE_PD_NEW(cwd, 1, 0);\n\nFILE_PD_NEW(split, 0, 0);\nFILE_PD_NEW(join, 0, 0);\nFILE_PD_NEW(splitext, 0, 0);\nFILE_PD_NEW(splitname, 0, 0);\nFILE_PD_NEW(isabsolute, 0, 0);\nFILE_PD_NEW(normalize, 1, 0);\n\nstatic t_pd *fileobj_new(t_symbol *s, int argc, t_atom*argv)\n{\n    t_file_handle*x = 0;\n    const char*verb=0;\n    if(gensym(\"file\") == s) {\n        if (argc && A_SYMBOL == argv->a_type) {\n            verb = atom_getsymbol(argv)->s_name;\n            argc--;\n            argv++;\n        } else {\n            verb = \"handle\";\n        }\n    } else if (strlen(s->s_name)>5) {\n        verb = s->s_name + 5;\n    }\n    if (!verb || !*verb)\n        x = do_file_handle_new(file_handle_class, gensym(\"file handle\"), argc, argv, 1, 0666);\n    else {\n#define ELIF_FILE_PD_NEW(name, verbose, creationmode) else              \\\n            if (!strcmp(verb, #name))                                   \\\n                x = do_file_handle_new(file_##name##_class, gensym(\"file \"#name), argc, argv, verbose, creationmode)\n\n        if (!strcmp(verb, \"define\"))\n            x = file_define_new(gensym(\"file define\"), argc, argv);\n        else if (!strcmp(verb, \"handle\"))\n            x = file_handle_new(gensym(\"file handle\"), argc, argv);\n        ELIF_FILE_PD_NEW(handle, 1, 0666);\n        ELIF_FILE_PD_NEW(which, 0, 0);\n        ELIF_FILE_PD_NEW(patchpath, 0, 0);\n        ELIF_FILE_PD_NEW(glob, 0, 0);\n        ELIF_FILE_PD_NEW(stat, 0, 0);\n        ELIF_FILE_PD_NEW(size, 0, 0);\n        ELIF_FILE_PD_NEW(isfile, 0, 0);\n        ELIF_FILE_PD_NEW(isdirectory, 0, 0);\n        ELIF_FILE_PD_NEW(mkdir, 0, 0777);\n        ELIF_FILE_PD_NEW(delete, 0, 0);\n        ELIF_FILE_PD_NEW(copy, 0, 0);\n        ELIF_FILE_PD_NEW(move, 0, 0);\n        ELIF_FILE_PD_NEW(cwd, 1, 0);\n        ELIF_FILE_PD_NEW(split, 0, 0);\n        ELIF_FILE_PD_NEW(join, 0, 0);\n        ELIF_FILE_PD_NEW(splitext, 0, 0);\n        ELIF_FILE_PD_NEW(splitname, 0, 0);\n        ELIF_FILE_PD_NEW(isabsolute, 0, 0);\n        ELIF_FILE_PD_NEW(normalize, 1, 0);\n        else {\n            pd_error(0, \"file %s: unknown function\", verb);\n        }\n    }\n    return (t_pd*)x;\n}\n\ntypedef enum _filenew_flag {\n    DEFAULT = 0,\n    VERBOSE = 1<<0,\n    MODE = 1<<1\n} t_filenew_flag;\n\nstatic t_class*file_class_new(const char*name\n    , t_file_handle* (*ctor)(t_symbol*,int,t_atom*), void (*dtor)(t_file_handle*)\n    , void (*symfun)(t_file_handle*, t_symbol*)\n    , t_filenew_flag flag\n    ) {\n    t_class*cls = class_new(gensym(name),\n        (t_newmethod)ctor, (t_method)dtor,\n        sizeof(t_file_handle), 0, A_GIMME, 0);\n    if (flag & VERBOSE)\n        class_addmethod(cls, (t_method)file_set_verbosity, gensym(\"verbose\"), A_FLOAT, 0);\n    if (flag & MODE)\n        class_addmethod(cls, (t_method)file_set_creationmode, gensym(\"creationmode\"), A_GIMME, 0);\n    if(symfun)\n        class_addsymbol(cls, (t_method)symfun);\n\n    class_sethelpsymbol(cls, gensym(\"file\"));\n    return cls;\n}\n\n    /* ---------------- global setup function -------------------- */\nvoid x_file_setup(void)\n{\n    class_addcreator((t_newmethod)fileobj_new, gensym(\"file\"), A_GIMME, 0);\n\n        /* [file define] */\n    file_define_class = class_new(gensym(\"file define\"),\n                                  (t_newmethod)file_define_new, (t_method)file_define_free,\n                                  sizeof(t_file_handle), CLASS_NOINLET, A_GIMME, 0);\n    class_addanything(file_define_class, file_define_ignore);\n    class_sethelpsymbol(file_define_class, gensym(\"file\"));\n\n        /* [file handle] */\n    file_handle_class = file_class_new(\"file handle\", file_handle_new, file_handle_free, 0, MODE|VERBOSE);\n    class_addmethod(file_handle_class, (t_method)file_handle_open,\n        gensym(\"open\"), A_SYMBOL, A_DEFSYM, 0);\n    class_addmethod(file_handle_class, (t_method)file_handle_close,\n        gensym(\"close\"), 0);\n    class_addmethod(file_handle_class, (t_method)file_handle_seek,\n        gensym(\"seek\"), A_GIMME, 0);\n    class_addmethod(file_handle_class, (t_method)file_handle_set,\n        gensym(\"set\"), A_DEFSYMBOL, 0);\n    class_addlist(file_handle_class, file_handle_list);\n\n        /* [file which] */\n    file_which_class = file_class_new(\"file which\", file_which_new, 0, file_which_symbol, VERBOSE);\n\n        /* [file patchpath] */\n    file_patchpath_class = file_class_new(\"file patchpath\", file_patchpath_new, 0, 0, VERBOSE);\n    class_addlist(file_patchpath_class, file_patchpath_list);\n\n        /* [file glob] */\n    file_glob_class = file_class_new(\"file glob\", file_glob_new, 0, file_glob_symbol, VERBOSE);\n\n        /* [file stat] */\n    file_stat_class = file_class_new(\"file stat\", file_stat_new, 0, file_stat_symbol, VERBOSE);\n    file_size_class = file_class_new(\"file size\", file_size_new, 0, file_size_symbol, VERBOSE);\n    file_isfile_class = file_class_new(\"file isfile\", file_isfile_new, 0, file_isfile_symbol, VERBOSE);\n    file_isdirectory_class = file_class_new(\"file isdirectory\", file_isdirectory_new, 0, file_isdirectory_symbol, VERBOSE);\n\n        /* [file mkdir] */\n    file_mkdir_class = file_class_new(\"file mkdir\", file_mkdir_new, 0, file_mkdir_symbol, MODE|VERBOSE);\n\n        /* [file delete] */\n    file_delete_class = file_class_new(\"file delete\", file_delete_new, 0, file_delete_symbol, VERBOSE);\n    class_addmethod(file_delete_class, (t_method)file_delete_recursive,\n        gensym(\"recursive\"), A_SYMBOL, 0);\n\n        /* [file copy] */\n    file_copy_class = file_class_new(\"file copy\", file_copy_new, 0, 0, MODE|VERBOSE);\n    class_addlist(file_copy_class, (t_method)file_copy_list);\n\n        /* [file move] */\n    file_move_class = file_class_new(\"file move\", file_move_new, 0, 0, MODE|VERBOSE);\n    class_addlist(file_move_class, (t_method)file_move_list);\n\n        /* [file cwd] */\n    file_cwd_class = file_class_new(\"file cwd\", file_cwd_new, 0, file_cwd_symbol, VERBOSE);\n    class_addbang(file_cwd_class, (t_method)file_cwd_bang);\n\n        /* file path objects */\n    file_split_class = file_class_new(\"file split\", file_split_new, 0, file_split_symbol, DEFAULT);\n    file_join_class = file_class_new(\"file join\", file_join_new, 0, 0, DEFAULT);\n    class_addlist(file_join_class, (t_method)file_join_list);\n    file_splitext_class = file_class_new(\"file splitext\", file_splitext_new, 0, file_splitext_symbol, DEFAULT);\n    file_splitname_class = file_class_new(\"file splitname\", file_splitname_new, 0, file_splitname_symbol, DEFAULT);\n    file_isabsolute_class = file_class_new(\"file isabsolute\", file_isabsolute_new, 0, file_isabsolute_symbol, DEFAULT);\n    file_normalize_class = file_class_new(\"file normalize\", file_normalize_new, 0, file_normalize_symbol, VERBOSE);\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/x_gui.c",
    "content": "/* Copyright (c) 1997-2000 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* dialogs.  LATER, deal with the situation where the object goes\naway before the panel does... */\n\n#include \"m_pd.h\"\n#include \"g_canvas.h\"\n#include <stdio.h>\n#include <string.h>\n#ifdef HAVE_UNISTD_H\n#include <unistd.h>\n#endif\n#include \"m_private_utils.h\"\n\n/* --------------------- graphics responder  ---------------- */\n\n/* make one of these if you want to put up a dialog window but want to be\nprotected from getting deleted and then having the dialog call you back.  In\nthis design the calling object doesn't have to keep the address of the dialog\nwindow around; instead we keep a list of all open dialogs.  Any object that\nmight have dialogs, when it is deleted, simply checks down the dialog window\nlist and breaks off any dialogs that might later have sent messages to it.\nOnly when the dialog window itself closes do we delete the gfxstub object. */\n\nstatic t_class *gfxstub_class;\n\ntypedef struct _gfxstub\n{\n    t_pd x_pd;\n    t_pd *x_owner;\n    void *x_key;\n    t_symbol *x_sym;\n    struct _gfxstub *x_next;\n} t_gfxstub;\n\nstatic t_gfxstub *gfxstub_list;\n\n    /* create a new one.  the \"key\" is an address by which the owner\n    will identify it later; if the owner only wants one dialog, this\n    could just be a pointer to the owner itself.  The string \"cmd\"\n    is a TK command to create the dialog, with \"%s\" embedded in\n    it so we can provide a name by which the GUI can send us back\n    messages; e.g., \"pdtk_canvas_dofont %s 10\". */\n\nstatic t_symbol*gfxstub_symbol(t_gfxstub *x)\n{\n    char namebuf[80];\n    sprintf(namebuf, \".gfxstub%lx\", (t_int)x);\n    return gensym(namebuf);\n}\n\nvoid gfxstub_new(t_pd *owner, void *key, const char *cmd)\n{\n    char buf[4*MAXPDSTRING];\n    char sprintfbuf[MAXPDSTRING];\n    char *afterpercent;\n    t_int afterpercentlen;\n    t_gfxstub *x;\n    t_symbol *s;\n        /* if any exists with matching key, burn it. */\n    for (x = gfxstub_list; x; x = x->x_next)\n        if (x->x_key == key)\n            gfxstub_deleteforkey(key);\n    if (strlen(cmd) + 50 > 4*MAXPDSTRING)\n    {\n        bug(\"audio dialog too long\");\n        bug(\"%s\", cmd);\n        return;\n    }\n    x = (t_gfxstub *)pd_new(gfxstub_class);\n\n    s = gfxstub_symbol(x);\n    pd_bind(&x->x_pd, s);\n    x->x_owner = owner;\n    x->x_sym = s;\n    x->x_key = key;\n    x->x_next = gfxstub_list;\n    gfxstub_list = x;\n    /* only replace first %s so sprintf() doesn't crash */\n    afterpercent = strchr(cmd, '%') + 2;\n    afterpercentlen = afterpercent - cmd;\n    strncpy(sprintfbuf, cmd, afterpercentlen);\n    sprintfbuf[afterpercentlen] = '\\0';\n    sprintf(buf, sprintfbuf, s->s_name);\n    strncat(buf, afterpercent, (4*MAXPDSTRING) - afterpercentlen);\n    sys_gui(buf);\n}\n\nstatic void gfxstub_offlist(t_gfxstub *x)\n{\n    t_gfxstub *y1, *y2;\n    if (gfxstub_list == x)\n        gfxstub_list = x->x_next;\n    else for (y1 = gfxstub_list; (y2 = y1->x_next); y1 = y2)\n        if (y2 == x)\n    {\n        y1->x_next = y2->x_next;\n        break;\n    }\n}\n\n    /* if the owner disappears, we still may have to stay around until our\n    dialog window signs off.  Anyway we can now tell the GUI to destroy the\n    window.  */\nvoid gfxstub_deleteforkey(void *key)\n{\n    t_gfxstub *y;\n    int didit = 1;\n    while (didit)\n    {\n        didit = 0;\n        for (y = gfxstub_list; y; y = y->x_next)\n        {\n            if (y->x_key == key)\n            {\n                t_symbol *s = gfxstub_symbol(y);\n                pdgui_vmess(\"destroy\", \"s\", s->s_name);\n                y->x_owner = 0;\n                gfxstub_offlist(y);\n                didit = 1;\n                break;\n            }\n        }\n    }\n}\n\n/* --------- pd messages for gfxstub (these come from the GUI) ---------- */\n\n    /* \"cancel\" to request that we close the dialog window. */\nstatic void gfxstub_cancel(t_gfxstub *x)\n{\n    gfxstub_deleteforkey(x->x_key);\n}\n\n    /* \"signoff\" comes from the GUI to say the dialog window closed. */\nstatic void gfxstub_signoff(t_gfxstub *x)\n{\n    gfxstub_offlist(x);\n    pd_free(&x->x_pd);\n}\n\nstatic t_binbuf *gfxstub_binbuf;\n\n    /* a series of \"data\" messages rebuilds a scalar */\nstatic void gfxstub_data(t_gfxstub *x, t_symbol *s, int argc, t_atom *argv)\n{\n    if (!gfxstub_binbuf)\n        gfxstub_binbuf = binbuf_new();\n    binbuf_add(gfxstub_binbuf, argc, argv);\n    binbuf_addsemi(gfxstub_binbuf);\n}\n    /* the \"end\" message terminates rebuilding the scalar */\nstatic void gfxstub_end(t_gfxstub *x)\n{\n    canvas_dataproperties((t_canvas *)x->x_owner,\n        (t_scalar *)x->x_key, gfxstub_binbuf);\n    if(gfxstub_binbuf)\n        binbuf_free(gfxstub_binbuf);\n    gfxstub_binbuf = 0;\n}\n\n    /* anything else is a message from the dialog window to the owner;\n    just forward it. */\nstatic void gfxstub_anything(t_gfxstub *x, t_symbol *s, int argc, t_atom *argv)\n{\n    if (x->x_owner)\n        pd_typedmess(x->x_owner, s, argc, argv);\n}\n\nstatic void gfxstub_free(t_gfxstub *x)\n{\n    pd_unbind(&x->x_pd, x->x_sym);\n}\n\nstatic void gfxstub_setup(void)\n{\n    gfxstub_class = class_new(gensym(\"gfxstub\"), 0, (t_method)gfxstub_free,\n        sizeof(t_gfxstub), CLASS_PD, 0);\n    class_addanything(gfxstub_class, gfxstub_anything);\n    class_addmethod(gfxstub_class, (t_method)gfxstub_signoff,\n        gensym(\"signoff\"), 0);\n    class_addmethod(gfxstub_class, (t_method)gfxstub_data,\n        gensym(\"data\"), A_GIMME, 0);\n    class_addmethod(gfxstub_class, (t_method)gfxstub_end,\n        gensym(\"end\"), 0);\n    class_addmethod(gfxstub_class, (t_method)gfxstub_cancel,\n        gensym(\"cancel\"), 0);\n}\n\n#include <stdarg.h>\n/* pdgui_*mess() are from s_inter_gui.c */\nvoid pdgui_vamess(const char* message, const char* format, va_list args);\nvoid pdgui_endmess(void);\n\nstatic void _pdguistub_vamess(const char*dest, const char*fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    pdgui_vamess(dest, fmt, args);\n    va_end(args);\n}\nvoid pdgui_stub_vnew(t_pd *owner, const char* destination, void *key, const char* fmt, ...)\n{\n    t_symbol*s;\n    t_gfxstub *x;\n    va_list args;\n\n        /* if any exists with matching key, burn it. */\n    for (x = gfxstub_list; x; x = x->x_next)\n        if (x->x_key == key)\n            gfxstub_deleteforkey(key);\n    x = (t_gfxstub *)pd_new(gfxstub_class);\n    s = gfxstub_symbol(x);\n    pd_bind(&x->x_pd, s);\n    x->x_owner = owner;\n    x->x_sym = s;\n    x->x_key = key;\n    x->x_next = gfxstub_list;\n    gfxstub_list = x;\n\n    _pdguistub_vamess(destination, \"s\", s->s_name);\n    va_start(args, fmt);\n    pdgui_vamess(0, fmt, args);\n    va_end(args);\n    pdgui_endmess();\n\n\n}\nvoid pdgui_stub_deleteforkey(void *key)\n{\n    gfxstub_deleteforkey(key);\n}\n\n/* -------------------------- openpanel ------------------------------ */\n\nstatic t_class *openpanel_class;\n\ntypedef struct _openpanel\n{\n    t_object x_obj;\n    t_symbol *x_s;\n    int x_mode; /* 0: file, 1: folder, 2: multiple files */\n} t_openpanel;\n\nstatic void *openpanel_new(t_floatarg mode)\n{\n    char buf[50];\n    int m = (int)mode;\n    t_openpanel *x = (t_openpanel *)pd_new(openpanel_class);\n    x->x_mode = (mode < 0 || mode > 2) ? 0 : mode;\n    sprintf(buf, \"d%lx\", (t_int)x);\n    x->x_s = gensym(buf);\n    pd_bind(&x->x_obj.ob_pd, x->x_s);\n    outlet_new(&x->x_obj, &s_symbol);\n    return (x);\n}\n\nstatic void openpanel_symbol(t_openpanel *x, t_symbol *s)\n{\n    const char *path = (s && s->s_name) ? s->s_name : \"\\\"\\\"\";\n    pdgui_vmess(\"pdtk_openpanel\", \"ssi\",\n        x->x_s->s_name, path, x->x_mode);\n}\n\nstatic void openpanel_bang(t_openpanel *x)\n{\n    openpanel_symbol(x, &s_);\n}\n\nstatic void openpanel_callback(t_openpanel *x, t_symbol *s, int argc, t_atom *argv)\n{\n    if (x->x_mode != 2) /* single file or folder */\n    {\n        if (argc == 1 && argv->a_type == A_SYMBOL)\n            outlet_symbol(x->x_obj.ob_outlet, argv->a_w.w_symbol);\n        else\n            bug(\"openpanel_callback\");\n    }\n    else /* list of files */\n        outlet_list(x->x_obj.ob_outlet, s, argc, argv);\n}\n\nstatic void openpanel_free(t_openpanel *x)\n{\n    pd_unbind(&x->x_obj.ob_pd, x->x_s);\n}\n\nstatic void openpanel_setup(void)\n{\n    openpanel_class = class_new(gensym(\"openpanel\"),\n        (t_newmethod)openpanel_new, (t_method)openpanel_free,\n        sizeof(t_openpanel), 0, A_DEFFLOAT, 0);\n    class_addbang(openpanel_class, openpanel_bang);\n    class_addsymbol(openpanel_class, openpanel_symbol);\n    class_addmethod(openpanel_class, (t_method)openpanel_callback,\n        gensym(\"callback\"), A_GIMME, 0);\n}\n\n/* -------------------------- savepanel ------------------------------ */\n\nstatic t_class *savepanel_class;\n\ntypedef struct _savepanel\n{\n    t_object x_obj;\n    t_canvas *x_canvas;\n    t_symbol *x_s;\n} t_savepanel;\n\nstatic void *savepanel_new(void)\n{\n    char buf[50];\n    t_savepanel *x = (t_savepanel *)pd_new(savepanel_class);\n    sprintf(buf, \"d%lx\", (t_int)x);\n    x->x_s = gensym(buf);\n    x->x_canvas = canvas_getcurrent();\n    pd_bind(&x->x_obj.ob_pd, x->x_s);\n    outlet_new(&x->x_obj, &s_symbol);\n    return (x);\n}\n\nstatic void savepanel_symbol(t_savepanel *x, t_symbol *s)\n{\n    const char *path = (s && s->s_name) ? s->s_name : \"\\\"\\\"\";\n    pdgui_vmess(\"pdtk_savepanel\", \"ss\",\n        x->x_s->s_name, path);\n}\n\nstatic void savepanel_bang(t_savepanel *x)\n{\n    savepanel_symbol(x, &s_);\n}\n\nstatic void savepanel_callback(t_savepanel *x, t_symbol *s)\n{\n    outlet_symbol(x->x_obj.ob_outlet, s);\n}\n\nstatic void savepanel_free(t_savepanel *x)\n{\n    pd_unbind(&x->x_obj.ob_pd, x->x_s);\n}\n\nstatic void savepanel_setup(void)\n{\n    savepanel_class = class_new(gensym(\"savepanel\"),\n        (t_newmethod)savepanel_new, (t_method)savepanel_free,\n        sizeof(t_savepanel), 0, 0);\n    class_addbang(savepanel_class, savepanel_bang);\n    class_addsymbol(savepanel_class, savepanel_symbol);\n    class_addmethod(savepanel_class, (t_method)savepanel_callback,\n        gensym(\"callback\"), A_SYMBOL, 0);\n}\n\n/* ---------------------- key and its relatives ------------------ */\n\nstatic t_class *key_class, *keyup_class, *keyname_class;\n\ntypedef struct _key\n{\n    t_object x_obj;\n} t_key;\n\nstatic void *key_new(void)\n{\n    t_key *x = (t_key *)pd_new(key_class);\n    outlet_new(&x->x_obj, &s_float);\n    pd_bind(&x->x_obj.ob_pd, gensym(\"#key\"));\n    return (x);\n}\n\nstatic void key_float(t_key *x, t_floatarg f)\n{\n    outlet_float(x->x_obj.ob_outlet, f);\n}\n\nstatic void key_free(t_key *x)\n{\n    pd_unbind(&x->x_obj.ob_pd, gensym(\"#key\"));\n}\n\ntypedef struct _keyup\n{\n    t_object x_obj;\n} t_keyup;\n\nstatic void *keyup_new(void)\n{\n    t_keyup *x = (t_keyup *)pd_new(keyup_class);\n    outlet_new(&x->x_obj, &s_float);\n    pd_bind(&x->x_obj.ob_pd, gensym(\"#keyup\"));\n    return (x);\n}\n\nstatic void keyup_float(t_keyup *x, t_floatarg f)\n{\n    outlet_float(x->x_obj.ob_outlet, f);\n}\n\nstatic void keyup_free(t_keyup *x)\n{\n    pd_unbind(&x->x_obj.ob_pd, gensym(\"#keyup\"));\n}\n\ntypedef struct _keyname\n{\n    t_object x_obj;\n    t_outlet *x_outlet1;\n    t_outlet *x_outlet2;\n} t_keyname;\n\nstatic void *keyname_new(void)\n{\n    t_keyname *x = (t_keyname *)pd_new(keyname_class);\n    x->x_outlet1 = outlet_new(&x->x_obj, &s_float);\n    x->x_outlet2 = outlet_new(&x->x_obj, &s_symbol);\n    pd_bind(&x->x_obj.ob_pd, gensym(\"#keyname\"));\n    return (x);\n}\n\nstatic void keyname_list(t_keyname *x, t_symbol *s, int ac, t_atom *av)\n{\n    outlet_symbol(x->x_outlet2, atom_getsymbolarg(1, ac, av));\n    outlet_float(x->x_outlet1, atom_getfloatarg(0, ac, av));\n}\n\nstatic void keyname_free(t_keyname *x)\n{\n    pd_unbind(&x->x_obj.ob_pd, gensym(\"#keyname\"));\n}\n\nstatic void key_setup(void)\n{\n    key_class = class_new(gensym(\"key\"),\n        (t_newmethod)key_new, (t_method)key_free,\n        sizeof(t_key), CLASS_NOINLET, 0);\n    class_addfloat(key_class, key_float);\n    class_sethelpsymbol(key_class, gensym(\"key-input\"));\n\n    keyup_class = class_new(gensym(\"keyup\"),\n        (t_newmethod)keyup_new, (t_method)keyup_free,\n        sizeof(t_keyup), CLASS_NOINLET, 0);\n    class_addfloat(keyup_class, keyup_float);\n    class_sethelpsymbol(keyup_class, gensym(\"key-input\"));\n\n    keyname_class = class_new(gensym(\"keyname\"),\n        (t_newmethod)keyname_new, (t_method)keyname_free,\n        sizeof(t_keyname), CLASS_NOINLET, 0);\n    class_addlist(keyname_class, keyname_list);\n    class_sethelpsymbol(keyname_class, gensym(\"key-input\"));\n}\n\n/* ------------------------ pdcontrol --------------------------------- */\n\nstatic t_class *pdcontrol_class;\n\ntypedef struct _pdcontrol\n{\n    t_object x_obj;\n    t_canvas *x_canvas;\n    t_outlet *x_outlet;\n} t_pdcontrol;\n\nstatic void *pdcontrol_new( void)\n{\n    t_pdcontrol *x = (t_pdcontrol *)pd_new(pdcontrol_class);\n    x->x_canvas = canvas_getcurrent();\n    x->x_outlet = outlet_new(&x->x_obj, 0);\n    return (x);\n}\n\n    /* output containing directory of patch.  optional args:\n    1. a number, zero for this patch, one for the parent, etc.;\n    2. a symbol to concatenate onto the directory; */\n\nstatic void pdcontrol_dir(t_pdcontrol *x, t_symbol *s, t_floatarg f)\n{\n    t_canvas *c = x->x_canvas;\n    int i;\n    for (i = 0; i < (int)f; i++)\n    {\n        while (!c->gl_env)  /* back up to containing canvas or abstraction */\n            c = c->gl_owner;\n        if (c->gl_owner)    /* back up one more into an owner if any */\n            c = c->gl_owner;\n    }\n    if (*s->s_name)\n    {\n        char buf[MAXPDSTRING];\n        snprintf(buf, MAXPDSTRING, \"%s/%s\",\n            canvas_getdir(c)->s_name, s->s_name);\n        buf[MAXPDSTRING-1] = 0;\n        outlet_symbol(x->x_outlet, gensym(buf));\n    }\n    else outlet_symbol(x->x_outlet, canvas_getdir(c));\n}\n\nstatic void pdcontrol_args(t_pdcontrol *x, t_floatarg f)\n{\n    t_canvas *c = x->x_canvas;\n    int i;\n    int argc;\n    t_atom *argv;\n    for (i = 0; i < (int)f; i++)\n    {\n        while (!c->gl_env)  /* back up to containing canvas or abstraction */\n            c = c->gl_owner;\n        if (c->gl_owner)    /* back up one more into an owner if any */\n            c = c->gl_owner;\n    }\n    canvas_setcurrent(c);\n    canvas_getargs(&argc, &argv);\n    canvas_unsetcurrent(c);\n    outlet_list(x->x_outlet, &s_list, argc, argv);\n}\n\nstatic void pdcontrol_browse(t_pdcontrol *x, t_symbol *s)\n{\n    pdgui_vmess(\"::pd_menucommands::menu_openfile\", \"s\", s->s_name);\n}\n\nstatic void pdcontrol_isvisible(t_pdcontrol *x)\n{\n    outlet_float(x->x_outlet, glist_isvisible(x->x_canvas));\n}\n\nstatic void pdcontrol_sendcanvas(t_pdcontrol *x, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    if (argc && argv[0].a_type == A_SYMBOL)\n        typedmess((t_pd *)(x->x_canvas), argv[0].a_w.w_symbol, argc-1, argv+1);\n    else pd_error(x, \"pdcontrol_sendcanvas: first argument not a symbol\");\n}\n\nstatic void pdcontrol_setup(void)\n{\n    pdcontrol_class = class_new(gensym(\"pdcontrol\"),\n        (t_newmethod)pdcontrol_new, 0, sizeof(t_pdcontrol), 0, 0);\n    class_addmethod(pdcontrol_class, (t_method)pdcontrol_dir,\n        gensym(\"dir\"), A_DEFFLOAT, A_DEFSYMBOL, 0);\n    class_addmethod(pdcontrol_class, (t_method)pdcontrol_args,\n        gensym(\"args\"), A_DEFFLOAT, 0);\n    class_addmethod(pdcontrol_class, (t_method)pdcontrol_browse,\n        gensym(\"browse\"), A_SYMBOL, 0);\n    class_addmethod(pdcontrol_class, (t_method)pdcontrol_isvisible,\n        gensym(\"isvisible\"), 0);\n    class_addmethod(pdcontrol_class, (t_method)pdcontrol_sendcanvas,\n        gensym(\"sendcanvas\"), A_GIMME, 0);\n}\n\n/* -------------------------- setup routine ------------------------------ */\n\nvoid x_gui_setup(void)\n{\n    gfxstub_setup();\n    openpanel_setup();\n    savepanel_setup();\n    key_setup();\n    pdcontrol_setup();\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/x_interface.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* interface objects */\n\n#include \"m_pd.h\"\n#include \"s_stuff.h\"\n#include <string.h>\n\n/* -------------------------- print ------------------------------ */\nstatic t_class *print_class;\n\n#define PRINT_LOGLEVEL 2\n\n  /* imported from s_print.c: */\nextern void startlogpost(const void *object, const int level, const char *fmt, ...)\n    ATTRIBUTE_FORMAT_PRINTF(3, 4);\n\n  /* avoid prefixing with \"verbose(PRINT_LOGLEVEL): \"\n  when printing to stderr or via printhook. */\n#define print_startlogpost(object, fmt, ...) do{ \\\n    if (STUFF->st_printhook || sys_printtostderr) \\\n        startpost(fmt, __VA_ARGS__); \\\n    else startlogpost(object, PRINT_LOGLEVEL, fmt, __VA_ARGS__); \\\n} while(0)\n\ntypedef struct _print\n{\n    t_object x_obj;\n    t_symbol *x_sym;\n} t_print;\n\nstatic void *print_new(t_symbol *sel, int argc, t_atom *argv)\n{\n    t_print *x = (t_print *)pd_new(print_class);\n    if (argc == 0)\n        x->x_sym = gensym(\"print\");\n    else if (argc == 1 && argv->a_type == A_SYMBOL)\n    {\n        t_symbol *s = atom_getsymbolarg(0, argc, argv);\n        if (!strcmp(s->s_name, \"-n\"))\n            x->x_sym = &s_;\n        else x->x_sym = s;\n    }\n    else\n    {\n        int bufsize;\n        char *buf;\n        t_binbuf *bb = binbuf_new();\n        binbuf_add(bb, argc, argv);\n        binbuf_gettext(bb, &buf, &bufsize);\n        buf = resizebytes(buf, bufsize, bufsize+1);\n        buf[bufsize] = 0;\n        x->x_sym = gensym(buf);\n        freebytes(buf, bufsize+1);\n        binbuf_free(bb);\n    }\n    return (x);\n}\n\nstatic void print_bang(t_print *x)\n{\n    print_startlogpost(x, \"%s%sbang\", x->x_sym->s_name, (*x->x_sym->s_name ? \": \" : \"\"));\n    endpost();\n}\n\nstatic void print_pointer(t_print *x, t_gpointer *gp)\n{\n    print_startlogpost(x, \"%s%s(pointer)\", x->x_sym->s_name, (*x->x_sym->s_name ? \": \" : \"\"));\n    endpost();\n}\n\nstatic void print_float(t_print *x, t_float f)\n{\n    print_startlogpost(x, \"%s%s%g\", x->x_sym->s_name, (*x->x_sym->s_name ? \": \" : \"\"), f);\n    endpost();\n}\n\nstatic void print_anything(t_print *x, t_symbol *s, int argc, t_atom *argv)\n{\n    int i;\n    print_startlogpost(x, \"%s%s%s\", x->x_sym->s_name, (*x->x_sym->s_name ? \": \" : \"\"),\n        s->s_name);\n    for (i = 0; i < argc; i++)\n    {\n        char buf[MAXPDSTRING];\n        atom_string(argv+i, buf, MAXPDSTRING);\n        print_startlogpost(x, \" %s\", buf);\n    }\n    endpost();\n}\n\nstatic void print_list(t_print *x, t_symbol *s, int argc, t_atom *argv)\n{\n    if (!argc)\n        print_bang(x);\n    else if (argc == 1)\n    {\n        switch (argv->a_type)\n        {\n        case A_FLOAT:\n            print_float(x, argv->a_w.w_float);\n            break;\n        case A_SYMBOL:\n            print_anything(x, &s_symbol, 1, argv);\n            break;\n        case A_POINTER:\n            print_pointer(x, argv->a_w.w_gpointer);\n            break;\n        default:\n            bug(\"print\");\n            break;\n        }\n    }\n    else if (argv->a_type == A_FLOAT)\n    {\n        int i;\n        /* print first (numeric) atom, to avoid a leading space */\n        if (*x->x_sym->s_name)\n            print_startlogpost(x, \"%s: %g\", x->x_sym->s_name, atom_getfloat(argv));\n        else\n            print_startlogpost(x, \"%g\", atom_getfloat(argv));\n        argc--; argv++;\n        for (i = 0; i < argc; i++)\n        {\n            char buf[MAXPDSTRING];\n            atom_string(argv+i, buf, MAXPDSTRING);\n            print_startlogpost(x, \" %s\", buf);\n        }\n        endpost();\n    }\n    else\n        print_anything(x, &s_list, argc, argv);\n}\n\nstatic void print_setup(void)\n{\n    print_class = class_new(gensym(\"print\"), (t_newmethod)print_new, 0,\n        sizeof(t_print), 0, A_GIMME, 0);\n    class_addbang(print_class, print_bang);\n    class_addfloat(print_class, print_float);\n    class_addpointer(print_class, print_pointer);\n    class_addlist(print_class, print_list);\n    class_addanything(print_class, print_anything);\n}\n\n/* ------------------- trace - see message passing traces ------------- */\nint backtracer_settracing(void *x, int tracing);\nextern int backtracer_cantrace;\n\nstatic t_class *trace_class;\n\ntypedef struct _trace\n{\n    t_object x_obj;\n    t_symbol *x_sym;\n    t_float x_f;\n} t_trace;\n\nstatic void *trace_new(t_symbol *s)\n{\n    t_trace *x = (t_trace *)pd_new(trace_class);\n    x->x_sym = s;\n    x->x_f = 0;\n    floatinlet_new(&x->x_obj, &x->x_f);\n    outlet_new(&x->x_obj, &s_anything);\n    return (x);\n}\n\nstatic void trace_anything(t_trace *x, t_symbol *s, int argc, t_atom *argv)\n{\n    int nturns = x->x_f;\n    if (nturns > 0)\n    {\n        if (!backtracer_cantrace)\n        {\n            pd_error(x, \"trace requested but tracing is not enabled\");\n            x->x_f = 0;\n        }\n        else if (backtracer_settracing(x, 1))\n        {\n            outlet_anything(x->x_obj.ob_outlet, s, argc, argv);\n            x->x_f = nturns-1;\n            (void)backtracer_settracing(x, 0);\n        }\n    }\n    else outlet_anything(x->x_obj.ob_outlet, s, argc, argv);\n}\n\nstatic void trace_setup(void)\n{\n    trace_class = class_new(gensym(\"trace\"), (t_newmethod)trace_new, 0,\n        sizeof(t_trace), 0, A_DEFSYM, 0);\n    class_addanything(trace_class, trace_anything);\n}\n\nvoid x_interface_setup(void)\n{\n    print_setup();\n    trace_setup();\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/x_list.c",
    "content": "/* Copyright (c) 1997- Miller Puckette and others.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n#include \"m_pd.h\"\n#include <string.h>\n\n#include \"m_private_utils.h\"\n\n#define LIST_NGETBYTE 100 /* bigger that this we use alloc, not alloca */\n\n/* the \"list\" object family.\n\n    list append - append a list to another\n    list prepend - prepend a list to another\n    list split - first n elements to first outlet, rest to second outlet\n    list trim - trim off \"list\" selector\n    list length - output number of items in list\n    list fromsymbol - \"explode\" a symbol into a list of character codes\n\nNeed to think more about:\n    list foreach - spit out elements of a list one by one (also in reverse?)\n    list reverse - permute elements of a list back to front\n    list cat - build a list by accumulating elements\n\nProbably don't need:\n    list first - output first n elements.\n    list last - output last n elements\n    list nth - nth item in list, counting from zero\n    list array - get items from a named array as a list\n    list pack - synonym for 'pack'\n    list unpack - synonym for 'unpack'\n*/\n\n/* -------------- utility functions: storage, copying  -------------- */\n    /* List element for storage.  Keep an atom and, in case it's a pointer,\n        an associated 'gpointer' to protect against stale pointers. */\ntypedef struct _listelem\n{\n    t_atom l_a;\n    t_gpointer l_p;\n} t_listelem;\n\ntypedef struct _alist\n{\n    t_pd l_pd;          /* object to point inlets to */\n    int l_n;            /* number of items */\n    int l_npointer;     /* number of pointers */\n    t_listelem *l_vec;  /* pointer to items */\n} t_alist;\n\nstatic void atoms_copy(int argc, t_atom *from, t_atom *to)\n{\n    int i;\n    for (i = 0; i < argc; i++)\n        to[i] = from[i];\n}\n\n/* ------------- fake class to divert inlets to ----------------- */\n\nt_class *alist_class;\n\nstatic void alist_init(t_alist *x)\n{\n    x->l_pd = alist_class;\n    x->l_n = x->l_npointer = 0;\n    x->l_vec = 0;\n}\n\nstatic void alist_clear(t_alist *x)\n{\n    int i;\n    for (i = 0; i < x->l_n; i++)\n    {\n        if (x->l_vec[i].l_a.a_type == A_POINTER)\n            gpointer_unset(x->l_vec[i].l_a.a_w.w_gpointer);\n    }\n    if (x->l_vec)\n        freebytes(x->l_vec, x->l_n * sizeof(*x->l_vec));\n}\n\nstatic void alist_copyin(t_alist *x, t_symbol *s, int argc, t_atom *argv,\n    int where)\n{\n    int i, j;\n    for (i = 0, j = where; i < argc; i++, j++)\n    {\n        x->l_vec[j].l_a = argv[i];\n        if (x->l_vec[j].l_a.a_type == A_POINTER)\n        {\n            x->l_npointer++;\n            gpointer_copy(x->l_vec[j].l_a.a_w.w_gpointer, &x->l_vec[j].l_p);\n            x->l_vec[j].l_a.a_w.w_gpointer = &x->l_vec[j].l_p;\n        }\n    }\n}\n\n    /* set contents to a list */\nstatic void alist_list(t_alist *x, t_symbol *s, int argc, t_atom *argv)\n{\n    alist_clear(x);\n    if (!(x->l_vec = (t_listelem *)getbytes(argc * sizeof(*x->l_vec))))\n    {\n        x->l_n = 0;\n        pd_error(0, \"list: out of memory\");\n        return;\n    }\n    x->l_n = argc;\n    x->l_npointer = 0;\n    alist_copyin(x, s, argc, argv, 0);\n}\n\n    /* set contents to an arbitrary non-list message */\nstatic void alist_anything(t_alist *x, t_symbol *s, int argc, t_atom *argv)\n{\n    int i;\n    alist_clear(x);\n    if (!(x->l_vec = (t_listelem *)getbytes((argc+1) * sizeof(*x->l_vec))))\n    {\n        x->l_n = 0;\n        pd_error(0, \"list_alloc: out of memory\");\n        return;\n    }\n    x->l_n = argc+1;\n    x->l_npointer = 0;\n    SETSYMBOL(&x->l_vec[0].l_a, s);\n    for (i = 0; i < argc; i++)\n    {\n        x->l_vec[i+1].l_a = argv[i];\n        if (x->l_vec[i+1].l_a.a_type == A_POINTER)\n        {\n            x->l_npointer++;\n            gpointer_copy(x->l_vec[i+1].l_a.a_w.w_gpointer, &x->l_vec[i+1].l_p);\n            x->l_vec[i+1].l_a.a_w.w_gpointer = &x->l_vec[i+1].l_p;\n        }\n    }\n}\n\nstatic void alist_toatoms(t_alist *x, t_atom *to, int onset, int count)\n{\n    int i;\n    for (i = 0; i < count; i++)\n        to[i] = x->l_vec[onset + i].l_a;\n}\n\n\nstatic void alist_clone(t_alist *x, t_alist *y, int onset, int count)\n{\n    int i;\n    y->l_pd = alist_class;\n    y->l_n = count;\n    y->l_npointer = 0;\n    if (!(y->l_vec = (t_listelem *)getbytes(y->l_n * sizeof(*y->l_vec))))\n    {\n        y->l_n = 0;\n        pd_error(0, \"list_alloc: out of memory\");\n    }\n    else for (i = 0; i < count; i++)\n    {\n        y->l_vec[i].l_a = x->l_vec[onset + i].l_a;\n        if (y->l_vec[i].l_a.a_type == A_POINTER)\n        {\n            gpointer_copy(y->l_vec[i].l_a.a_w.w_gpointer, &y->l_vec[i].l_p);\n            y->l_vec[i].l_a.a_w.w_gpointer = &y->l_vec[i].l_p;\n            y->l_npointer++;\n        }\n    }\n}\n\n    /* function to restore gpointers after the list has moved in memory */\nstatic void alist_restore_gpointers(t_alist *x, int offset, int count)\n{\n    t_listelem *vec = x->l_vec + offset;\n    while (count--)\n    {\n        if (vec->l_a.a_type == A_POINTER)\n            vec->l_a.a_w.w_gpointer = &vec->l_p;\n        vec++;\n    }\n}\n\nstatic void alist_setup(void)\n{\n    alist_class = class_new(gensym(\"list inlet\"),\n        0, 0, sizeof(t_alist), 0, 0);\n    class_addlist(alist_class, alist_list);\n    class_addanything(alist_class, alist_anything);\n}\n\n/* ------------- list append --------------------- */\n\nt_class *list_append_class;\n\ntypedef struct _list_append\n{\n    t_object x_obj;\n    t_alist x_alist;\n} t_list_append;\n\nstatic void *list_append_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_list_append *x = (t_list_append *)pd_new(list_append_class);\n    alist_init(&x->x_alist);\n    alist_list(&x->x_alist, 0, argc, argv);\n    outlet_new(&x->x_obj, &s_list);\n    inlet_new(&x->x_obj, &x->x_alist.l_pd, 0, 0);\n    return (x);\n}\n\nstatic void list_append_list(t_list_append *x, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    t_atom *outv;\n    int outc = x->x_alist.l_n + argc;\n    ALLOCA(t_atom, outv, outc, LIST_NGETBYTE);\n    atoms_copy(argc, argv, outv);\n    if (x->x_alist.l_npointer)\n    {\n        t_alist y;\n        alist_clone(&x->x_alist, &y, 0, x->x_alist.l_n);\n        alist_toatoms(&y, outv+argc, 0, x->x_alist.l_n);\n        outlet_list(x->x_obj.ob_outlet, &s_list, outc, outv);\n        alist_clear(&y);\n    }\n    else\n    {\n        alist_toatoms(&x->x_alist, outv+argc, 0, x->x_alist.l_n);\n        outlet_list(x->x_obj.ob_outlet, &s_list, outc, outv);\n    }\n    FREEA(t_atom, outv, outc, LIST_NGETBYTE);\n}\n\nstatic void list_append_anything(t_list_append *x, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    t_atom *outv;\n    int outc = x->x_alist.l_n + argc + 1;\n    ALLOCA(t_atom, outv, outc, LIST_NGETBYTE);\n    SETSYMBOL(outv, s);\n    atoms_copy(argc, argv, outv + 1);\n    if (x->x_alist.l_npointer)\n    {\n        t_alist y;\n        alist_clone(&x->x_alist, &y, 0, x->x_alist.l_n);\n        alist_toatoms(&y, outv + 1 + argc, 0, x->x_alist.l_n);\n        outlet_list(x->x_obj.ob_outlet, &s_list, outc, outv);\n        alist_clear(&y);\n    }\n    else\n    {\n        alist_toatoms(&x->x_alist, outv + 1 + argc, 0, x->x_alist.l_n);\n        outlet_list(x->x_obj.ob_outlet, &s_list, outc, outv);\n    }\n    FREEA(t_atom, outv, outc, LIST_NGETBYTE);\n}\n\nstatic void list_append_free(t_list_append *x)\n{\n    alist_clear(&x->x_alist);\n}\n\nstatic void list_append_setup(void)\n{\n    list_append_class = class_new(gensym(\"list append\"),\n        (t_newmethod)list_append_new, (t_method)list_append_free,\n        sizeof(t_list_append), 0, A_GIMME, 0);\n    class_addlist(list_append_class, list_append_list);\n    class_addanything(list_append_class, list_append_anything);\n    class_sethelpsymbol(list_append_class, &s_list);\n}\n\n/* ------------- list prepend --------------------- */\n\nt_class *list_prepend_class;\n\ntypedef t_list_append t_list_prepend;\n\nstatic void *list_prepend_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_list_prepend *x = (t_list_prepend *)pd_new(list_prepend_class);\n    alist_init(&x->x_alist);\n    alist_list(&x->x_alist, 0, argc, argv);\n    outlet_new(&x->x_obj, &s_list);\n    inlet_new(&x->x_obj, &x->x_alist.l_pd, 0, 0);\n    return (x);\n}\n\nstatic void list_prepend_list(t_list_prepend *x, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    t_atom *outv;\n    int n, outc = x->x_alist.l_n + argc;\n    ALLOCA(t_atom, outv, outc, LIST_NGETBYTE);\n    atoms_copy(argc, argv, outv + x->x_alist.l_n);\n    if (x->x_alist.l_npointer)\n    {\n        t_alist y;\n        alist_clone(&x->x_alist, &y, 0, x->x_alist.l_n);\n        alist_toatoms(&y, outv, 0, x->x_alist.l_n);\n        outlet_list(x->x_obj.ob_outlet, &s_list, outc, outv);\n        alist_clear(&y);\n    }\n    else\n    {\n        alist_toatoms(&x->x_alist, outv, 0, x->x_alist.l_n);\n        outlet_list(x->x_obj.ob_outlet, &s_list, outc, outv);\n    }\n    FREEA(t_atom, outv, outc, LIST_NGETBYTE);\n}\n\nstatic void list_prepend_anything(t_list_prepend *x, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    t_atom *outv;\n    int n, outc = x->x_alist.l_n + argc + 1;\n    ALLOCA(t_atom, outv, outc, LIST_NGETBYTE);\n    SETSYMBOL(outv + x->x_alist.l_n, s);\n    atoms_copy(argc, argv, outv + x->x_alist.l_n + 1);\n    if (x->x_alist.l_npointer)\n    {\n        t_alist y;\n        alist_clone(&x->x_alist, &y, 0, x->x_alist.l_n);\n        alist_toatoms(&y, outv, 0, x->x_alist.l_n);\n        outlet_list(x->x_obj.ob_outlet, &s_list, outc, outv);\n        alist_clear(&y);\n    }\n    else\n    {\n        alist_toatoms(&x->x_alist, outv, 0, x->x_alist.l_n);\n        outlet_list(x->x_obj.ob_outlet, &s_list, outc, outv);\n    }\n    FREEA(t_atom, outv, outc, LIST_NGETBYTE);\n}\n\nstatic void list_prepend_free(t_list_prepend *x)\n{\n    alist_clear(&x->x_alist);\n}\n\nstatic void list_prepend_setup(void)\n{\n    list_prepend_class = class_new(gensym(\"list prepend\"),\n        (t_newmethod)list_prepend_new, (t_method)list_prepend_free,\n        sizeof(t_list_prepend), 0, A_GIMME, 0);\n    class_addlist(list_prepend_class, list_prepend_list);\n    class_addanything(list_prepend_class, list_prepend_anything);\n    class_sethelpsymbol(list_prepend_class, &s_list);\n}\n\n/* ------------- list store --------------------- */\n\nt_class *list_store_class;\n\ntypedef struct _list_store\n{\n    t_object x_obj;\n    t_alist x_alist;\n    t_outlet *x_out1;\n    t_outlet *x_out2;\n} t_list_store;\n\nstatic void *list_store_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_list_store *x = (t_list_store *)pd_new(list_store_class);\n    alist_init(&x->x_alist);\n    alist_list(&x->x_alist, 0, argc, argv);\n    x->x_out1 = outlet_new(&x->x_obj, &s_list);\n    x->x_out2 = outlet_new(&x->x_obj, &s_bang);\n    inlet_new(&x->x_obj, &x->x_alist.l_pd, 0, 0);\n    return (x);\n}\n\nstatic void list_store_send(t_list_store *x, t_symbol *s)\n{\n    t_atom *vec;\n    int n = x->x_alist.l_n;\n    if (!s->s_thing)\n    {\n        pd_error(x, \"%s: no such object\", s->s_name);\n        return;\n    }\n    ALLOCA(t_atom, vec, n, LIST_NGETBYTE);\n    if (x->x_alist.l_npointer)\n    {\n        t_alist y;\n        alist_clone(&x->x_alist, &y, 0, n);\n        alist_toatoms(&y, vec, 0, n);\n        pd_list(s->s_thing, gensym(\"list\"), n, vec);\n        alist_clear(&y);\n    }\n    else\n    {\n        alist_toatoms(&x->x_alist, vec, 0, n);\n        pd_list(s->s_thing, gensym(\"list\"), n, vec);\n    }\n    FREEA(t_atom, vec, n, LIST_NGETBYTE);\n}\n\nstatic void list_store_list(t_list_store *x, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    t_atom *outv;\n    int n, outc = x->x_alist.l_n + argc;\n    ALLOCA(t_atom, outv, outc, LIST_NGETBYTE);\n    atoms_copy(argc, argv, outv);\n    if (x->x_alist.l_npointer)\n    {\n        t_alist y;\n        alist_clone(&x->x_alist, &y, 0, x->x_alist.l_n);\n        alist_toatoms(&y, outv+argc, 0, x->x_alist.l_n);\n        outlet_list(x->x_out1, &s_list, outc, outv);\n        alist_clear(&y);\n    }\n    else\n    {\n        alist_toatoms(&x->x_alist, outv+argc, 0, x->x_alist.l_n);\n        outlet_list(x->x_out1, &s_list, outc, outv);\n    }\n    FREEA(t_atom, outv, outc, LIST_NGETBYTE);\n}\n\nstatic void list_store_doinsert(t_list_store *x, t_symbol *s,\n    int argc, t_atom *argv, int index)\n{\n    t_listelem *oldptr = x->x_alist.l_vec;\n        /* try to allocate more memory */\n    if (!(x->x_alist.l_vec = (t_listelem *)resizebytes(x->x_alist.l_vec,\n        (x->x_alist.l_n) * sizeof(*x->x_alist.l_vec),\n        (x->x_alist.l_n + argc) * sizeof(*x->x_alist.l_vec))))\n    {\n        x->x_alist.l_n = 0;\n        pd_error(0, \"list: out of memory\");\n        return;\n    }\n        /* fix gpointers in case resizebytes() has moved the alist in memory */\n    if (x->x_alist.l_vec != oldptr && x->x_alist.l_npointer)\n        alist_restore_gpointers(&x->x_alist, 0, x->x_alist.l_n);\n        /* shift existing elements after 'index' to the right */\n    if (index < x->x_alist.l_n)\n    {\n        memmove(x->x_alist.l_vec + index + argc, x->x_alist.l_vec + index,\n            (x->x_alist.l_n - index) * sizeof(*x->x_alist.l_vec));\n            /* fix gpointers because of memmove() */\n        if (x->x_alist.l_npointer)\n            alist_restore_gpointers(&x->x_alist, index + argc, x->x_alist.l_n - index);\n    }\n        /* finally copy new elements */\n    alist_copyin(&x->x_alist, s, argc, argv, index);\n    x->x_alist.l_n += argc;\n}\n\nstatic void list_store_insert(t_list_store *x, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    if (argc > 1)\n    {\n        int index = atom_getfloat(argv);\n        if (index < 0)\n        {\n            pd_error(x, \"list_store_insert: index %d out of range\", index);\n            return;\n        } else if (index > x->x_alist.l_n)\n            index = x->x_alist.l_n;\n        list_store_doinsert(x, s, --argc, ++argv, index);\n    }\n}\n\nstatic void list_store_append(t_list_store *x, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    list_store_doinsert(x, s, argc, argv, x->x_alist.l_n);\n}\n\nstatic void list_store_prepend(t_list_store *x, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    list_store_doinsert(x, s, argc, argv, 0);\n}\n\nstatic void list_store_delete(t_list_store *x, t_floatarg f1, t_floatarg f2)\n{\n    int i, max, index = (int)f1, n = (int)f2;\n    t_listelem *oldptr = x->x_alist.l_vec;\n    if (index < 0 || index >= x->x_alist.l_n)\n    {\n        pd_error(x, \"list_store_delete: index %d out of range\", index);\n        return;\n    }\n    max = x->x_alist.l_n - index;\n    if (!n)\n        n = 1; /* default */\n    else if (n < 0 || n > max)\n        n = max; /* till the end of the list */\n\n        /* unset pointers for elements which are to be deleted */\n    if (x->x_alist.l_npointer)\n    {\n        t_listelem *vec = x->x_alist.l_vec + index;\n        for (i = 0; i < n; i++)\n        {\n            if (vec[i].l_a.a_type == A_POINTER)\n            {\n                gpointer_unset(vec[i].l_a.a_w.w_gpointer);\n                x->x_alist.l_npointer--;\n            }\n        }\n    }\n        /* shift elements (after the deleted elements) to the left */\n    memmove(x->x_alist.l_vec + index, x->x_alist.l_vec + index + n,\n        (x->x_alist.l_n - index) * sizeof(*x->x_alist.l_vec));\n        /* shrink memory */\n    if (!(x->x_alist.l_vec = (t_listelem *)resizebytes(x->x_alist.l_vec,\n        (x->x_alist.l_n) * sizeof(*x->x_alist.l_vec),\n        (x->x_alist.l_n - n) * sizeof(*x->x_alist.l_vec))))\n    {\n        x->x_alist.l_n = 0;\n        pd_error(0, \"list: out of memory\");\n        return;\n    }\n    if (x->x_alist.l_npointer)\n    {\n            /* fix all gpointers in case resizebytes() has moved the alist in memory */\n        if (x->x_alist.l_vec != oldptr)\n            alist_restore_gpointers(&x->x_alist, 0, x->x_alist.l_n - n);\n        else /* only fix gpointers after index (because of of memmove()) */\n            alist_restore_gpointers(&x->x_alist, index, x->x_alist.l_n - index - n);\n    }\n    x->x_alist.l_n -= n;\n}\n\nstatic void list_store_get(t_list_store *x, t_floatarg f1, t_floatarg f2)\n{\n    t_atom *outv;\n    int onset = f1, outc = f2;\n    if (!outc)\n        outc = 1; /* default */\n    else if (outc < 0)\n    {\n        outc = x->x_alist.l_n - onset; /* till the end of the list */\n        if (outc <= 0) /* onset out of range */\n        {\n            outlet_bang(x->x_out2);\n            return;\n        }\n    }\n    if (onset < 0 || (onset + outc > x->x_alist.l_n))\n    {\n        outlet_bang(x->x_out2);\n        return;\n    }\n    ALLOCA(t_atom, outv, outc, LIST_NGETBYTE);\n    if (x->x_alist.l_npointer)\n    {\n        t_alist y;\n        alist_clone(&x->x_alist, &y, onset, outc);\n        alist_toatoms(&y, outv, 0, outc);\n        outlet_list(x->x_out1, &s_list, outc, outv);\n        alist_clear(&y);\n    }\n    else\n    {\n        alist_toatoms(&x->x_alist, outv, onset, outc);\n        outlet_list(x->x_out1, &s_list, outc, outv);\n    }\n    FREEA(t_atom, outv, outc, LIST_NGETBYTE);\n}\n\nstatic void list_store_set(t_list_store *x, t_symbol *s, int argc, t_atom *argv)\n{\n    if (argc > 1)\n    {\n        int n, max, onset = atom_getfloat(argv);\n        if (onset < 0 || onset >= x->x_alist.l_n)\n        {\n            pd_error(x, \"list_store_set: index %d out of range\", onset);\n            return;\n        }\n        argc--; argv++;\n        max = x->x_alist.l_n - onset;\n        n = (argc > max) ? max : argc;\n        alist_copyin(&x->x_alist, s, n, argv, onset);\n    }\n}\n\nstatic void list_store_free(t_list_store *x)\n{\n    alist_clear(&x->x_alist);\n}\n\nstatic void list_store_setup(void)\n{\n    list_store_class = class_new(gensym(\"list store\"),\n        (t_newmethod)list_store_new, (t_method)list_store_free,\n        sizeof(t_list_store), 0, A_GIMME, 0);\n    class_addlist(list_store_class, list_store_list);\n    class_addmethod(list_store_class, (t_method)list_store_send,\n        gensym(\"send\"), A_SYMBOL, 0);\n    class_addmethod(list_store_class, (t_method)list_store_append,\n        gensym(\"append\"), A_GIMME, 0);\n    class_addmethod(list_store_class, (t_method)list_store_prepend,\n        gensym(\"prepend\"), A_GIMME, 0);\n    class_addmethod(list_store_class, (t_method)list_store_insert,\n        gensym(\"insert\"), A_GIMME, 0);\n    class_addmethod(list_store_class, (t_method)list_store_delete,\n        gensym(\"delete\"), A_FLOAT, A_DEFFLOAT, 0);\n    class_addmethod(list_store_class, (t_method)list_store_get,\n        gensym(\"get\"), A_FLOAT, A_DEFFLOAT, 0);\n    class_addmethod(list_store_class, (t_method)list_store_set,\n        gensym(\"set\"), A_GIMME, 0);\n    class_sethelpsymbol(list_store_class, &s_list);\n}\n\n/* ------------- list split --------------------- */\n\nt_class *list_split_class;\n\ntypedef struct _list_split\n{\n    t_object x_obj;\n    t_float x_f;\n    t_outlet *x_out1;\n    t_outlet *x_out2;\n    t_outlet *x_out3;\n} t_list_split;\n\nstatic void *list_split_new(t_floatarg f)\n{\n    t_list_split *x = (t_list_split *)pd_new(list_split_class);\n    x->x_out1 = outlet_new(&x->x_obj, &s_list);\n    x->x_out2 = outlet_new(&x->x_obj, &s_list);\n    x->x_out3 = outlet_new(&x->x_obj, &s_list);\n    floatinlet_new(&x->x_obj, &x->x_f);\n    x->x_f = f;\n    return (x);\n}\n\nstatic void list_split_list(t_list_split *x, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    int n = x->x_f;\n    if (n < 0)\n        n = 0;\n    if (argc >= n)\n    {\n        outlet_list(x->x_out2, &s_list, argc-n, argv+n);\n        outlet_list(x->x_out1, &s_list, n, argv);\n    }\n    else outlet_list(x->x_out3, &s_list, argc, argv);\n}\n\nstatic void list_split_anything(t_list_split *x, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    t_atom *outv;\n    ALLOCA(t_atom, outv, argc+1, LIST_NGETBYTE);\n    SETSYMBOL(outv, s);\n    atoms_copy(argc, argv, outv + 1);\n    list_split_list(x, &s_list, argc+1, outv);\n    FREEA(t_atom, outv, argc+1, LIST_NGETBYTE);\n}\n\nstatic void list_split_setup(void)\n{\n    list_split_class = class_new(gensym(\"list split\"),\n        (t_newmethod)list_split_new, 0,\n        sizeof(t_list_split), 0, A_DEFFLOAT, 0);\n    class_addlist(list_split_class, list_split_list);\n    class_addanything(list_split_class, list_split_anything);\n    class_sethelpsymbol(list_split_class, &s_list);\n}\n\n/* ------------- list trim --------------------- */\n\nt_class *list_trim_class;\n\ntypedef struct _list_trim\n{\n    t_object x_obj;\n} t_list_trim;\n\nstatic void *list_trim_new(void)\n{\n    t_list_trim *x = (t_list_trim *)pd_new(list_trim_class);\n    outlet_new(&x->x_obj, &s_list);\n    return (x);\n}\n\nstatic void list_trim_list(t_list_trim *x, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    if (argc < 1 || argv[0].a_type != A_SYMBOL)\n        outlet_list(x->x_obj.ob_outlet, &s_list, argc, argv);\n    else outlet_anything(x->x_obj.ob_outlet, argv[0].a_w.w_symbol,\n        argc-1, argv+1);\n}\n\nstatic void list_trim_anything(t_list_trim *x, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    outlet_anything(x->x_obj.ob_outlet, s, argc, argv);\n}\n\nstatic void list_trim_setup(void)\n{\n    list_trim_class = class_new(gensym(\"list trim\"),\n        (t_newmethod)list_trim_new, 0,\n        sizeof(t_list_trim), 0, 0);\n    class_addlist(list_trim_class, list_trim_list);\n    class_addanything(list_trim_class, list_trim_anything);\n    class_sethelpsymbol(list_trim_class, &s_list);\n}\n\n/* ------------- list length --------------------- */\n\nt_class *list_length_class;\n\ntypedef struct _list_length\n{\n    t_object x_obj;\n} t_list_length;\n\nstatic void *list_length_new(void)\n{\n    t_list_length *x = (t_list_length *)pd_new(list_length_class);\n    outlet_new(&x->x_obj, &s_float);\n    return (x);\n}\n\nstatic void list_length_list(t_list_length *x, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    outlet_float(x->x_obj.ob_outlet, (t_float)argc);\n}\n\nstatic void list_length_anything(t_list_length *x, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    outlet_float(x->x_obj.ob_outlet, (t_float)argc+1);\n}\n\nstatic void list_length_setup(void)\n{\n    list_length_class = class_new(gensym(\"list length\"),\n        (t_newmethod)list_length_new, 0,\n        sizeof(t_list_length), 0, 0);\n    class_addlist(list_length_class, list_length_list);\n    class_addanything(list_length_class, list_length_anything);\n    class_sethelpsymbol(list_length_class, &s_list);\n}\n\n/* ------------- list fromsymbol --------------------- */\n\nt_class *list_fromsymbol_class;\n\ntypedef struct _list_fromsymbol\n{\n    t_object x_obj;\n} t_list_fromsymbol;\n\nstatic void *list_fromsymbol_new(void)\n{\n    t_list_fromsymbol *x = (t_list_fromsymbol *)pd_new(list_fromsymbol_class);\n    outlet_new(&x->x_obj, &s_list);\n    return (x);\n}\n\nstatic void list_fromsymbol_symbol(t_list_fromsymbol *x, t_symbol *s)\n{\n    t_atom *outv;\n    int n, outc = (int)strlen(s->s_name);\n    ALLOCA(t_atom, outv, outc, LIST_NGETBYTE);\n    for (n = 0; n < outc; n++)\n        SETFLOAT(outv + n, (unsigned char)s->s_name[n]);\n    outlet_list(x->x_obj.ob_outlet, &s_list, outc, outv);\n    FREEA(t_atom, outv, outc, LIST_NGETBYTE);\n}\n\nstatic void list_fromsymbol_setup(void)\n{\n    list_fromsymbol_class = class_new(gensym(\"list fromsymbol\"),\n        (t_newmethod)list_fromsymbol_new, 0, sizeof(t_list_fromsymbol), 0, 0);\n    class_addsymbol(list_fromsymbol_class, list_fromsymbol_symbol);\n    class_sethelpsymbol(list_fromsymbol_class, &s_list);\n}\n\n/* ------------- list tosymbol --------------------- */\n\nt_class *list_tosymbol_class;\n\ntypedef struct _list_tosymbol\n{\n    t_object x_obj;\n} t_list_tosymbol;\n\nstatic void *list_tosymbol_new(void)\n{\n    t_list_tosymbol *x = (t_list_tosymbol *)pd_new(list_tosymbol_class);\n    outlet_new(&x->x_obj, &s_symbol);\n    return (x);\n}\n\nstatic void list_tosymbol_list(t_list_tosymbol *x, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    int i;\n    char *str;\n    ALLOCA(char, str, argc + 1, MAXPDSTRING);\n    for (i = 0; i < argc; i++)\n        str[i] = (char)atom_getfloatarg(i, argc, argv);\n    str[argc] = 0;\n    outlet_symbol(x->x_obj.ob_outlet, gensym(str));\n    FREEA(char, str, argc + 1, MAXPDSTRING);\n}\n\nstatic void list_tosymbol_setup(void)\n{\n    list_tosymbol_class = class_new(gensym(\"list tosymbol\"),\n        (t_newmethod)list_tosymbol_new, 0, sizeof(t_list_tosymbol), 0, 0);\n    class_addlist(list_tosymbol_class, list_tosymbol_list);\n    class_sethelpsymbol(list_tosymbol_class, &s_list);\n}\n\n/* ------------- list ------------------- */\n\nstatic void *list_new(t_pd *dummy, t_symbol *s, int argc, t_atom *argv)\n{\n    if (!argc || argv[0].a_type != A_SYMBOL)\n        pd_this->pd_newest = list_append_new(s, argc, argv);\n    else\n    {\n        t_symbol *s2 = argv[0].a_w.w_symbol;\n        if (s2 == gensym(\"append\"))\n            pd_this->pd_newest = list_append_new(s, argc-1, argv+1);\n        else if (s2 == gensym(\"prepend\"))\n            pd_this->pd_newest = list_prepend_new(s, argc-1, argv+1);\n        else if (s2 == gensym(\"split\"))\n            pd_this->pd_newest =\n                list_split_new(atom_getfloatarg(1, argc, argv));\n        else if (s2 == gensym(\"trim\"))\n            pd_this->pd_newest = list_trim_new();\n        else if (s2 == gensym(\"length\"))\n            pd_this->pd_newest = list_length_new();\n        else if (s2 == gensym(\"fromsymbol\"))\n            pd_this->pd_newest = list_fromsymbol_new();\n        else if (s2 == gensym(\"tosymbol\"))\n            pd_this->pd_newest = list_tosymbol_new();\n        else if (s2 == gensym(\"store\"))\n            pd_this->pd_newest = list_store_new(s, argc-1, argv+1);\n        else\n        {\n            pd_error(0, \"list %s: unknown function\", s2->s_name);\n            pd_this->pd_newest = 0;\n        }\n    }\n    return (pd_this->pd_newest);\n}\n\nvoid x_list_setup(void)\n{\n    alist_setup();\n    list_append_setup();\n    list_prepend_setup();\n    list_store_setup();\n    list_split_setup();\n    list_trim_setup();\n    list_length_setup();\n    list_fromsymbol_setup();\n    list_tosymbol_setup();\n    class_addcreator((t_newmethod)list_new, &s_list, A_GIMME, 0);\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/x_midi.c",
    "content": "/* Copyright (c) 1997-2001 Miller Puckette and others.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* MIDI. */\n\n#include \"m_pd.h\"\nvoid outmidi_noteon(int portno, int channel, int pitch, int velo);\nvoid outmidi_controlchange(int portno, int channel, int ctlno, int value);\nvoid outmidi_programchange(int portno, int channel, int value);\nvoid outmidi_pitchbend(int portno, int channel, int value);\nvoid outmidi_aftertouch(int portno, int channel, int value);\nvoid outmidi_polyaftertouch(int portno, int channel, int pitch, int value);\nvoid outmidi_byte(int portno, int value);\n\nstruct _instancemidi\n{\n    t_symbol *m_midiin_sym;\n    t_symbol *m_sysexin_sym;\n    t_symbol *m_notein_sym;\n    t_symbol *m_ctlin_sym;\n    t_symbol *m_pgmin_sym;\n    t_symbol *m_bendin_sym;\n    t_symbol *m_touchin_sym;\n    t_symbol *m_polytouchin_sym;\n    t_symbol *m_midirealtimein_sym;\n};\n\n/* ----------------------- midiin and sysexin ------------------------- */\n\nstatic t_class *midiin_class, *sysexin_class;\n\ntypedef struct _midiin\n{\n    t_object x_obj;\n    t_outlet *x_outlet1;\n    t_outlet *x_outlet2;\n} t_midiin;\n\nstatic void *midiin_new(void)\n{\n    t_midiin *x = (t_midiin *)pd_new(midiin_class);\n    x->x_outlet1 = outlet_new(&x->x_obj, &s_float);\n    x->x_outlet2 = outlet_new(&x->x_obj, &s_float);\n    pd_bind(&x->x_obj.ob_pd, pd_this->pd_midi->m_midiin_sym);\n    return (x);\n}\n\nstatic void midiin_list(t_midiin *x, t_symbol *s, int ac, t_atom *av)\n{\n    outlet_float(x->x_outlet2, atom_getfloatarg(1, ac, av) + 1);\n    outlet_float(x->x_outlet1, atom_getfloatarg(0, ac, av));\n}\n\nstatic void midiin_free(t_midiin *x)\n{\n    pd_unbind(&x->x_obj.ob_pd, pd_this->pd_midi->m_midiin_sym);\n}\n\nstatic void *sysexin_new(void)\n{\n    t_midiin *x = (t_midiin *)pd_new(sysexin_class);\n    x->x_outlet1 = outlet_new(&x->x_obj, &s_float);\n    x->x_outlet2 = outlet_new(&x->x_obj, &s_float);\n    pd_bind(&x->x_obj.ob_pd, pd_this->pd_midi->m_sysexin_sym);\n    return (x);\n}\n\nstatic void sysexin_free(t_midiin *x)\n{\n    pd_unbind(&x->x_obj.ob_pd, pd_this->pd_midi->m_sysexin_sym);\n}\n\nstatic void midiin_setup(void)\n{\n    midiin_class = class_new(gensym(\"midiin\"), (t_newmethod)midiin_new,\n        (t_method)midiin_free, sizeof(t_midiin),\n            CLASS_NOINLET, A_DEFFLOAT, 0);\n    class_addlist(midiin_class, midiin_list);\n    class_sethelpsymbol(midiin_class, gensym(\"midi\"));\n\n    sysexin_class = class_new(gensym(\"sysexin\"), (t_newmethod)sysexin_new,\n        (t_method)sysexin_free, sizeof(t_midiin),\n            CLASS_NOINLET, A_DEFFLOAT, 0);\n    class_addlist(sysexin_class, midiin_list);\n    class_sethelpsymbol(sysexin_class, gensym(\"midi\"));\n}\n\nvoid inmidi_byte(int portno, int byte)\n{\n    t_atom at[2];\n    if (pd_this->pd_midi->m_midiin_sym->s_thing)\n    {\n        SETFLOAT(at, byte);\n        SETFLOAT(at+1, portno);\n        pd_list(pd_this->pd_midi->m_midiin_sym->s_thing, 0, 2, at);\n    }\n}\n\nvoid inmidi_sysex(int portno, int byte)\n{\n    t_atom at[2];\n    if (pd_this->pd_midi->m_sysexin_sym->s_thing)\n    {\n        SETFLOAT(at, byte);\n        SETFLOAT(at+1, portno);\n        pd_list(pd_this->pd_midi->m_sysexin_sym->s_thing, 0, 2, at);\n    }\n}\n\n/* ----------------------- notein ------------------------- */\n\nstatic t_class *notein_class;\n\ntypedef struct _notein\n{\n    t_object x_obj;\n    t_float x_channel;\n    t_outlet *x_outlet1;\n    t_outlet *x_outlet2;\n    t_outlet *x_outlet3;\n} t_notein;\n\nstatic void *notein_new(t_floatarg f)\n{\n    t_notein *x = (t_notein *)pd_new(notein_class);\n    x->x_channel = f;\n    x->x_outlet1 = outlet_new(&x->x_obj, &s_float);\n    x->x_outlet2 = outlet_new(&x->x_obj, &s_float);\n    if (f == 0) x->x_outlet3 = outlet_new(&x->x_obj, &s_float);\n    pd_bind(&x->x_obj.ob_pd, pd_this->pd_midi->m_notein_sym);\n    return (x);\n}\n\nstatic void notein_list(t_notein *x, t_symbol *s, int argc, t_atom *argv)\n{\n    t_float pitch = atom_getfloatarg(0, argc, argv);\n    t_float velo = atom_getfloatarg(1, argc, argv);\n    t_float channel = atom_getfloatarg(2, argc, argv);\n    if (x->x_channel != 0)\n    {\n        if (channel != x->x_channel) return;\n        outlet_float(x->x_outlet2, velo);\n        outlet_float(x->x_outlet1, pitch);\n    }\n    else\n    {\n        outlet_float(x->x_outlet3, channel);\n        outlet_float(x->x_outlet2, velo);\n        outlet_float(x->x_outlet1, pitch);\n    }\n}\n\nstatic void notein_free(t_notein *x)\n{\n    pd_unbind(&x->x_obj.ob_pd, pd_this->pd_midi->m_notein_sym);\n}\n\nstatic void notein_setup(void)\n{\n    notein_class = class_new(gensym(\"notein\"), (t_newmethod)notein_new,\n        (t_method)notein_free, sizeof(t_notein), CLASS_NOINLET, A_DEFFLOAT, 0);\n    class_addlist(notein_class, notein_list);\n    class_sethelpsymbol(notein_class, gensym(\"midi\"));\n}\n\nvoid inmidi_noteon(int portno, int channel, int pitch, int velo)\n{\n    if (pd_this->pd_midi->m_notein_sym->s_thing)\n    {\n        t_atom at[3];\n        SETFLOAT(at, pitch);\n        SETFLOAT(at+1, velo);\n        SETFLOAT(at+2, (channel + (portno << 4) + 1));\n        pd_list(pd_this->pd_midi->m_notein_sym->s_thing, &s_list, 3, at);\n    }\n}\n\n/* ----------------------- ctlin ------------------------- */\n\nstatic t_class *ctlin_class;\n\ntypedef struct _ctlin\n{\n    t_object x_obj;\n    t_float x_channel;\n    t_float x_ctlno;\n    t_outlet *x_outlet1;\n    t_outlet *x_outlet2;\n    t_outlet *x_outlet3;\n} t_ctlin;\n\nstatic void *ctlin_new(t_symbol *s, int argc, t_atom *argv)\n{\n    int ctlno, channel;\n    t_ctlin *x = (t_ctlin *)pd_new(ctlin_class);\n    if (!argc) ctlno = -1;\n    else ctlno = atom_getfloatarg(0, argc, argv);\n    channel = atom_getfloatarg(1, argc, argv);\n    x->x_channel = channel;\n    x->x_ctlno = ctlno;\n    x->x_outlet1 = outlet_new(&x->x_obj, &s_float);\n    if (!channel)\n    {\n        if (x->x_ctlno < 0) x->x_outlet2 = outlet_new(&x->x_obj, &s_float);\n        x->x_outlet3 = outlet_new(&x->x_obj, &s_float);\n    }\n    pd_bind(&x->x_obj.ob_pd, pd_this->pd_midi->m_ctlin_sym);\n    return (x);\n}\n\nstatic void ctlin_list(t_ctlin *x, t_symbol *s, int argc, t_atom *argv)\n{\n    t_float ctlnumber = atom_getfloatarg(0, argc, argv);\n    t_float value = atom_getfloatarg(1, argc, argv);\n    t_float channel = atom_getfloatarg(2, argc, argv);\n    if (x->x_ctlno >= 0 && x->x_ctlno != ctlnumber) return;\n    if (x->x_channel > 0  && x->x_channel != channel) return;\n    if (x->x_channel == 0) outlet_float(x->x_outlet3, channel);\n    if (x->x_ctlno < 0) outlet_float(x->x_outlet2, ctlnumber);\n    outlet_float(x->x_outlet1, value);\n}\n\nstatic void ctlin_free(t_ctlin *x)\n{\n    pd_unbind(&x->x_obj.ob_pd, pd_this->pd_midi->m_ctlin_sym);\n}\n\nstatic void ctlin_setup(void)\n{\n    ctlin_class = class_new(gensym(\"ctlin\"), (t_newmethod)ctlin_new,\n        (t_method)ctlin_free, sizeof(t_ctlin),\n            CLASS_NOINLET, A_GIMME, 0);\n    class_addlist(ctlin_class, ctlin_list);\n    class_sethelpsymbol(ctlin_class, gensym(\"midi\"));\n}\n\nvoid inmidi_controlchange(int portno, int channel, int ctlnumber, int value)\n{\n    if (pd_this->pd_midi->m_ctlin_sym->s_thing)\n    {\n        t_atom at[3];\n        SETFLOAT(at, ctlnumber);\n        SETFLOAT(at+1, value);\n        SETFLOAT(at+2, (channel + (portno << 4) + 1));\n        pd_list(pd_this->pd_midi->m_ctlin_sym->s_thing, &s_list, 3, at);\n    }\n}\n\n/* ----------------------- pgmin ------------------------- */\n\nstatic t_class *pgmin_class;\n\ntypedef struct _pgmin\n{\n    t_object x_obj;\n    t_float x_channel;\n    t_outlet *x_outlet1;\n    t_outlet *x_outlet2;\n} t_pgmin;\n\nstatic void *pgmin_new(t_floatarg f)\n{\n    t_pgmin *x = (t_pgmin *)pd_new(pgmin_class);\n    x->x_channel = f;\n    x->x_outlet1 = outlet_new(&x->x_obj, &s_float);\n    if (f == 0) x->x_outlet2 = outlet_new(&x->x_obj, &s_float);\n    pd_bind(&x->x_obj.ob_pd, pd_this->pd_midi->m_pgmin_sym);\n    return (x);\n}\n\nstatic void pgmin_list(t_pgmin *x, t_symbol *s, int argc, t_atom *argv)\n{\n    t_float value = atom_getfloatarg(0, argc, argv);\n    t_float channel = atom_getfloatarg(1, argc, argv);\n    if (x->x_channel != 0)\n    {\n        if (channel != x->x_channel) return;\n        outlet_float(x->x_outlet1, value);\n    }\n    else\n    {\n        outlet_float(x->x_outlet2, channel);\n        outlet_float(x->x_outlet1, value);\n    }\n}\n\nstatic void pgmin_free(t_pgmin *x)\n{\n    pd_unbind(&x->x_obj.ob_pd, pd_this->pd_midi->m_pgmin_sym);\n}\n\nstatic void pgmin_setup(void)\n{\n    pgmin_class = class_new(gensym(\"pgmin\"), (t_newmethod)pgmin_new,\n        (t_method)pgmin_free, sizeof(t_pgmin),\n            CLASS_NOINLET, A_DEFFLOAT, 0);\n    class_addlist(pgmin_class, pgmin_list);\n    class_sethelpsymbol(pgmin_class, gensym(\"midi\"));\n}\n\nvoid inmidi_programchange(int portno, int channel, int value)\n{\n    if (pd_this->pd_midi->m_pgmin_sym->s_thing)\n    {\n        t_atom at[2];\n        SETFLOAT(at, value + 1);\n        SETFLOAT(at+1, (channel + (portno << 4) + 1));\n        pd_list(pd_this->pd_midi->m_pgmin_sym->s_thing, &s_list, 2, at);\n    }\n}\n\n/* ----------------------- bendin ------------------------- */\n\nstatic t_class *bendin_class;\n\ntypedef struct _bendin\n{\n    t_object x_obj;\n    t_float x_channel;\n    t_outlet *x_outlet1;\n    t_outlet *x_outlet2;\n} t_bendin;\n\nstatic void *bendin_new(t_floatarg f)\n{\n    t_bendin *x = (t_bendin *)pd_new(bendin_class);\n    x->x_channel = f;\n    x->x_outlet1 = outlet_new(&x->x_obj, &s_float);\n    if (f == 0) x->x_outlet2 = outlet_new(&x->x_obj, &s_float);\n    pd_bind(&x->x_obj.ob_pd, pd_this->pd_midi->m_bendin_sym);\n    return (x);\n}\n\nstatic void bendin_list(t_bendin *x, t_symbol *s, int argc, t_atom *argv)\n{\n    t_float value = atom_getfloatarg(0, argc, argv);\n    t_float channel = atom_getfloatarg(1, argc, argv);\n    if (x->x_channel != 0)\n    {\n        if (channel != x->x_channel) return;\n        outlet_float(x->x_outlet1, value);\n    }\n    else\n    {\n        outlet_float(x->x_outlet2, channel);\n        outlet_float(x->x_outlet1, value);\n    }\n}\n\nstatic void bendin_free(t_bendin *x)\n{\n    pd_unbind(&x->x_obj.ob_pd, pd_this->pd_midi->m_bendin_sym);\n}\n\nstatic void bendin_setup(void)\n{\n    bendin_class = class_new(gensym(\"bendin\"), (t_newmethod)bendin_new,\n        (t_method)bendin_free, sizeof(t_bendin), CLASS_NOINLET, A_DEFFLOAT, 0);\n    class_addlist(bendin_class, bendin_list);\n    class_sethelpsymbol(bendin_class, gensym(\"midi\"));\n}\n\nvoid inmidi_pitchbend(int portno, int channel, int value)\n{\n    if (pd_this->pd_midi->m_bendin_sym->s_thing)\n    {\n        t_atom at[2];\n        SETFLOAT(at, value);\n        SETFLOAT(at+1, (channel + (portno << 4) + 1));\n        pd_list(pd_this->pd_midi->m_bendin_sym->s_thing, &s_list, 2, at);\n    }\n}\n\n/* ----------------------- touchin ------------------------- */\n\nstatic t_class *touchin_class;\n\ntypedef struct _touchin\n{\n    t_object x_obj;\n    t_float x_channel;\n    t_outlet *x_outlet1;\n    t_outlet *x_outlet2;\n} t_touchin;\n\nstatic void *touchin_new(t_floatarg f)\n{\n    t_touchin *x = (t_touchin *)pd_new(touchin_class);\n    x->x_channel = f;\n    x->x_outlet1 = outlet_new(&x->x_obj, &s_float);\n    if (f == 0) x->x_outlet2 = outlet_new(&x->x_obj, &s_float);\n    pd_bind(&x->x_obj.ob_pd, pd_this->pd_midi->m_touchin_sym);\n    return (x);\n}\n\nstatic void touchin_list(t_touchin *x, t_symbol *s, int argc, t_atom *argv)\n{\n    t_float value = atom_getfloatarg(0, argc, argv);\n    t_float channel = atom_getfloatarg(1, argc, argv);\n    if (x->x_channel)\n    {\n        if (channel != x->x_channel) return;\n        outlet_float(x->x_outlet1, value);\n    }\n    else\n    {\n        outlet_float(x->x_outlet2, channel);\n        outlet_float(x->x_outlet1, value);\n    }\n}\n\nstatic void touchin_free(t_touchin *x)\n{\n    pd_unbind(&x->x_obj.ob_pd, pd_this->pd_midi->m_touchin_sym);\n}\n\nstatic void touchin_setup(void)\n{\n    touchin_class = class_new(gensym(\"touchin\"), (t_newmethod)touchin_new,\n        (t_method)touchin_free, sizeof(t_touchin),\n            CLASS_NOINLET, A_DEFFLOAT, 0);\n    class_addlist(touchin_class, touchin_list);\n    class_sethelpsymbol(touchin_class, gensym(\"midi\"));\n}\n\nvoid inmidi_aftertouch(int portno, int channel, int value)\n{\n    if (pd_this->pd_midi->m_touchin_sym->s_thing)\n    {\n        t_atom at[2];\n        SETFLOAT(at, value);\n        SETFLOAT(at+1, (channel + (portno << 4) + 1));\n        pd_list(pd_this->pd_midi->m_touchin_sym->s_thing, &s_list, 2, at);\n    }\n}\n\n/* ----------------------- polytouchin ------------------------- */\n\nstatic t_class *polytouchin_class;\n\ntypedef struct _polytouchin\n{\n    t_object x_obj;\n    t_float x_channel;\n    t_outlet *x_outlet1;\n    t_outlet *x_outlet2;\n    t_outlet *x_outlet3;\n} t_polytouchin;\n\nstatic void *polytouchin_new(t_floatarg f)\n{\n    t_polytouchin *x = (t_polytouchin *)pd_new(polytouchin_class);\n    x->x_channel = f;\n    x->x_outlet1 = outlet_new(&x->x_obj, &s_float);\n    x->x_outlet2 = outlet_new(&x->x_obj, &s_float);\n    if (f == 0) x->x_outlet3 = outlet_new(&x->x_obj, &s_float);\n    pd_bind(&x->x_obj.ob_pd, pd_this->pd_midi->m_polytouchin_sym);\n    return (x);\n}\n\nstatic void polytouchin_list(t_polytouchin *x, t_symbol *s, int argc,\n    t_atom *argv)\n{\n    t_float pitch = atom_getfloatarg(0, argc, argv);\n    t_float value = atom_getfloatarg(1, argc, argv);\n    t_float channel = atom_getfloatarg(2, argc, argv);\n    if (x->x_channel != 0)\n    {\n        if (channel != x->x_channel) return;\n        outlet_float(x->x_outlet2, pitch);\n        outlet_float(x->x_outlet1, value);\n    }\n    else\n    {\n        outlet_float(x->x_outlet3, channel);\n        outlet_float(x->x_outlet2, pitch);\n        outlet_float(x->x_outlet1, value);\n    }\n}\n\nstatic void polytouchin_free(t_polytouchin *x)\n{\n    pd_unbind(&x->x_obj.ob_pd, pd_this->pd_midi->m_polytouchin_sym);\n}\n\nstatic void polytouchin_setup(void)\n{\n    polytouchin_class = class_new(gensym(\"polytouchin\"),\n        (t_newmethod)polytouchin_new, (t_method)polytouchin_free,\n        sizeof(t_polytouchin), CLASS_NOINLET, A_DEFFLOAT, 0);\n    class_addlist(polytouchin_class, polytouchin_list);\n    class_sethelpsymbol(polytouchin_class, gensym(\"midi\"));\n}\n\nvoid inmidi_polyaftertouch(int portno, int channel, int pitch, int value)\n{\n    if (pd_this->pd_midi->m_polytouchin_sym->s_thing)\n    {\n        t_atom at[3];\n        SETFLOAT(at, pitch);\n        SETFLOAT(at+1, value);\n        SETFLOAT(at+2, (channel + (portno << 4) + 1));\n        pd_list(pd_this->pd_midi->m_polytouchin_sym->s_thing, &s_list, 3, at);\n    }\n}\n\n/*----------midirealtimein (midi F8,FA,FB,FC,FE,FF message)-----------------*/\n\nstatic t_class *midirealtimein_class;\n\ntypedef struct _midirealtimein\n{\n    t_object x_obj;\n    t_outlet *x_outlet1;\n    t_outlet *x_outlet2;\n} t_midirealtimein;\n\nstatic void *midirealtimein_new(void)\n{\n    t_midirealtimein *x = (t_midirealtimein *)pd_new(midirealtimein_class);\n    x->x_outlet1 = outlet_new(&x->x_obj, &s_float);\n    x->x_outlet2 = outlet_new(&x->x_obj, &s_float);\n    pd_bind(&x->x_obj.ob_pd, pd_this->pd_midi->m_midirealtimein_sym);\n    return (x);\n}\n\nstatic void midirealtimein_list(t_midirealtimein *x, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    t_float portno = atom_getfloatarg(0, argc, argv);\n    t_float byte = atom_getfloatarg(1, argc, argv);\n\n    outlet_float(x->x_outlet2, portno);\n    outlet_float(x->x_outlet1, byte);\n}\n\nstatic void midirealtimein_free(t_midirealtimein *x)\n{\n    pd_unbind(&x->x_obj.ob_pd, pd_this->pd_midi->m_midirealtimein_sym);\n}\n\nstatic void midirealtimein_setup(void)\n{\n    midirealtimein_class = class_new(gensym(\"midirealtimein\"),\n        (t_newmethod)midirealtimein_new, (t_method)midirealtimein_free,\n            sizeof(t_midirealtimein), CLASS_NOINLET, A_DEFFLOAT, 0);\n    class_addlist(midirealtimein_class, midirealtimein_list);\n        class_sethelpsymbol(midirealtimein_class, gensym(\"midi\"));\n}\n\nvoid inmidi_realtimein(int portno, int SysMsg)\n{\n    if (pd_this->pd_midi->m_midirealtimein_sym->s_thing)\n    {\n        t_atom at[2];\n        SETFLOAT(at, portno);\n        SETFLOAT(at+1, SysMsg);\n        pd_list(pd_this->pd_midi->m_midirealtimein_sym->s_thing, &s_list, 2, at);\n    }\n}\n\n/* -------------------------- midiout -------------------------- */\n\nstatic t_class *midiout_class;\n\nvoid sys_putmidibyte(int portno, int byte);\n\ntypedef struct _midiout\n{\n    t_object x_obj;\n    t_float x_portno;\n} t_midiout;\n\nstatic void *midiout_new(t_floatarg portno)\n{\n    t_midiout *x = (t_midiout *)pd_new(midiout_class);\n    if (portno <= 0) portno = 1;\n    x->x_portno = portno;\n    floatinlet_new(&x->x_obj, &x->x_portno);\n    return (x);\n}\n\nstatic void midiout_float(t_midiout *x, t_floatarg f)\n{\n    outmidi_byte(x->x_portno - 1, f);\n}\n\nstatic void midiout_list(t_midiout *x, t_symbol *s, int ac, t_atom *av)\n{\n    int i;\n    for (i = 0; i < ac; ++i)\n    {\n        if(av[i].a_type == A_FLOAT)\n            outmidi_byte(x->x_portno - 1, av[i].a_w.w_float);\n    }\n}\n\nstatic void midiout_setup(void)\n{\n    midiout_class = class_new(gensym(\"midiout\"), (t_newmethod)midiout_new, 0,\n        sizeof(t_midiout), 0, A_DEFFLOAT, A_DEFFLOAT, 0);\n    class_addfloat(midiout_class, midiout_float);\n    class_addlist(midiout_class, midiout_list);\n    class_sethelpsymbol(midiout_class, gensym(\"midi\"));\n}\n\n/* -------------------------- noteout -------------------------- */\n\nstatic t_class *noteout_class;\n\ntypedef struct _noteout\n{\n    t_object x_obj;\n    t_float x_velo;\n    t_float x_channel;\n} t_noteout;\n\nstatic void *noteout_new(t_floatarg channel)\n{\n    t_noteout *x = (t_noteout *)pd_new(noteout_class);\n    x->x_velo = 0;\n    if (channel < 1) channel = 1;\n    x->x_channel = channel;\n    floatinlet_new(&x->x_obj, &x->x_velo);\n    floatinlet_new(&x->x_obj, &x->x_channel);\n    return (x);\n}\n\nstatic void noteout_float(t_noteout *x, t_float f)\n{\n    int binchan = x->x_channel - 1;\n    if (binchan < 0)\n        binchan = 0;\n    outmidi_noteon((binchan >> 4),\n        (binchan & 15), (int)f, (int)x->x_velo);\n}\n\nstatic void noteout_setup(void)\n{\n    noteout_class = class_new(gensym(\"noteout\"), (t_newmethod)noteout_new, 0,\n        sizeof(t_noteout), 0, A_DEFFLOAT, 0);\n    class_addfloat(noteout_class, noteout_float);\n    class_sethelpsymbol(noteout_class, gensym(\"midi\"));\n}\n\n\n/* -------------------------- ctlout -------------------------- */\n\nstatic t_class *ctlout_class;\n\ntypedef struct _ctlout\n{\n    t_object x_obj;\n    t_float x_ctl;\n    t_float x_channel;\n} t_ctlout;\n\nstatic void *ctlout_new(t_floatarg ctl, t_floatarg channel)\n{\n    t_ctlout *x = (t_ctlout *)pd_new(ctlout_class);\n    x->x_ctl = ctl;\n    if (channel <= 0) channel = 1;\n    x->x_channel = channel;\n    floatinlet_new(&x->x_obj, &x->x_ctl);\n    floatinlet_new(&x->x_obj, &x->x_channel);\n    return (x);\n}\n\nstatic void ctlout_float(t_ctlout *x, t_float f)\n{\n    int binchan = x->x_channel - 1;\n    if (binchan < 0)\n        binchan = 0;\n    outmidi_controlchange((binchan >> 4),\n        (binchan & 15), (int)(x->x_ctl), (int)f);\n}\n\nstatic void ctlout_setup(void)\n{\n    ctlout_class = class_new(gensym(\"ctlout\"), (t_newmethod)ctlout_new, 0,\n        sizeof(t_ctlout), 0, A_DEFFLOAT, A_DEFFLOAT, 0);\n    class_addfloat(ctlout_class, ctlout_float);\n    class_sethelpsymbol(ctlout_class, gensym(\"midi\"));\n}\n\n\n/* -------------------------- pgmout -------------------------- */\n\nstatic t_class *pgmout_class;\n\ntypedef struct _pgmout\n{\n    t_object x_obj;\n    t_float x_channel;\n} t_pgmout;\n\nstatic void *pgmout_new(t_floatarg channel)\n{\n    t_pgmout *x = (t_pgmout *)pd_new(pgmout_class);\n    if (channel <= 0) channel = 1;\n    x->x_channel = channel;\n    floatinlet_new(&x->x_obj, &x->x_channel);\n    return (x);\n}\n\nstatic void pgmout_float(t_pgmout *x, t_floatarg f)\n{\n    int binchan = x->x_channel - 1;\n    int n = f - 1;\n    if (binchan < 0)\n        binchan = 0;\n    if (n < 0) n = 0;\n    else if (n > 127) n = 127;\n    outmidi_programchange((binchan >> 4),\n        (binchan & 15), n);\n}\n\nstatic void pgmout_setup(void)\n{\n    pgmout_class = class_new(gensym(\"pgmout\"), (t_newmethod)pgmout_new, 0,\n        sizeof(t_pgmout), 0, A_DEFFLOAT, 0);\n    class_addfloat(pgmout_class, pgmout_float);\n    class_sethelpsymbol(pgmout_class, gensym(\"midi\"));\n}\n\n\n/* -------------------------- bendout -------------------------- */\n\nstatic t_class *bendout_class;\n\ntypedef struct _bendout\n{\n    t_object x_obj;\n    t_float x_channel;\n} t_bendout;\n\nstatic void *bendout_new(t_floatarg channel)\n{\n    t_bendout *x = (t_bendout *)pd_new(bendout_class);\n    if (channel <= 0) channel = 1;\n    x->x_channel = channel;\n    floatinlet_new(&x->x_obj, &x->x_channel);\n    return (x);\n}\n\nstatic void bendout_float(t_bendout *x, t_float f)\n{\n    int binchan = x->x_channel - 1;\n    int n = (int)f +  8192;\n    if (binchan < 0)\n        binchan = 0;\n    outmidi_pitchbend((binchan >> 4), (binchan & 15), n);\n}\n\nstatic void bendout_setup(void)\n{\n    bendout_class = class_new(gensym(\"bendout\"), (t_newmethod)bendout_new, 0,\n        sizeof(t_bendout), 0, A_DEFFLOAT, 0);\n    class_addfloat(bendout_class, bendout_float);\n    class_sethelpsymbol(bendout_class, gensym(\"midi\"));\n}\n\n/* -------------------------- touch -------------------------- */\n\nstatic t_class *touchout_class;\n\ntypedef struct _touchout\n{\n    t_object x_obj;\n    t_float x_channel;\n} t_touchout;\n\nstatic void *touchout_new(t_floatarg channel)\n{\n    t_touchout *x = (t_touchout *)pd_new(touchout_class);\n    if (channel <= 0) channel = 1;\n    x->x_channel = channel;\n    floatinlet_new(&x->x_obj, &x->x_channel);\n    return (x);\n}\n\nstatic void touchout_float(t_touchout *x, t_float f)\n{\n    int binchan = x->x_channel - 1;\n    if (binchan < 0)\n        binchan = 0;\n    outmidi_aftertouch((binchan >> 4), (binchan & 15), (int)f);\n}\n\nstatic void touchout_setup(void)\n{\n    touchout_class = class_new(gensym(\"touchout\"), (t_newmethod)touchout_new, 0,\n        sizeof(t_touchout), 0, A_DEFFLOAT, 0);\n    class_addfloat(touchout_class, touchout_float);\n    class_sethelpsymbol(touchout_class, gensym(\"midi\"));\n}\n\n/* -------------------------- polytouch -------------------------- */\n\nstatic t_class *polytouchout_class;\n\ntypedef struct _polytouchout\n{\n    t_object x_obj;\n    t_float x_channel;\n    t_float x_pitch;\n} t_polytouchout;\n\nstatic void *polytouchout_new(t_floatarg channel)\n{\n    t_polytouchout *x = (t_polytouchout *)pd_new(polytouchout_class);\n    if (channel <= 0) channel = 1;\n    x->x_channel = channel;\n    x->x_pitch = 0;\n    floatinlet_new(&x->x_obj, &x->x_pitch);\n    floatinlet_new(&x->x_obj, &x->x_channel);\n    return (x);\n}\n\nstatic void polytouchout_float(t_polytouchout *x, t_float n)\n{\n    int binchan = x->x_channel - 1;\n    if (binchan < 0)\n        binchan = 0;\n    outmidi_polyaftertouch((binchan >> 4), (binchan & 15), x->x_pitch, n);\n}\n\nstatic void polytouchout_setup(void)\n{\n    polytouchout_class = class_new(gensym(\"polytouchout\"),\n        (t_newmethod)polytouchout_new, 0,\n        sizeof(t_polytouchout), 0, A_DEFFLOAT, 0);\n    class_addfloat(polytouchout_class, polytouchout_float);\n    class_sethelpsymbol(polytouchout_class, gensym(\"midi\"));\n}\n\n/* -------------------------- makenote -------------------------- */\n\nstatic t_class *makenote_class;\n\ntypedef struct _hang\n{\n    t_clock *h_clock;\n    struct _hang *h_next;\n    t_float h_pitch;\n    struct _makenote *h_owner;\n} t_hang;\n\ntypedef struct _makenote\n{\n    t_object x_obj;\n    t_float x_velo;\n    t_float x_dur;\n    t_outlet *x_pitchout;\n    t_outlet *x_velout;\n    t_hang *x_hang;\n} t_makenote;\n\nstatic void *makenote_new(t_floatarg velo, t_floatarg dur)\n{\n    t_makenote *x = (t_makenote *)pd_new(makenote_class);\n    x->x_velo = velo;\n    x->x_dur = dur;\n    floatinlet_new(&x->x_obj, &x->x_velo);\n    floatinlet_new(&x->x_obj, &x->x_dur);\n    x->x_pitchout = outlet_new(&x->x_obj, &s_float);\n    x->x_velout = outlet_new(&x->x_obj, &s_float);\n    x->x_hang = 0;\n    return (x);\n}\n\nstatic void makenote_tick(t_hang *hang)\n{\n    t_makenote *x = hang->h_owner;\n    t_hang *h2, *h3;\n    outlet_float(x->x_velout, 0);\n    outlet_float(x->x_pitchout, hang->h_pitch);\n    if (x->x_hang == hang) x->x_hang = hang->h_next;\n    else for (h2 = x->x_hang; (h3 = h2->h_next); h2 = h3)\n    {\n        if (h3 == hang)\n        {\n            h2->h_next = h3->h_next;\n            break;\n        }\n    }\n    clock_free(hang->h_clock);\n    freebytes(hang, sizeof(*hang));\n}\n\nstatic void makenote_float(t_makenote *x, t_float f)\n{\n    t_hang *hang;\n    if (!x->x_velo) return;\n    outlet_float(x->x_velout, x->x_velo);\n    outlet_float(x->x_pitchout, f);\n    hang = (t_hang *)getbytes(sizeof *hang);\n    hang->h_next = x->x_hang;\n    x->x_hang = hang;\n    hang->h_pitch = f;\n    hang->h_owner = x;\n    hang->h_clock = clock_new(hang, (t_method)makenote_tick);\n    clock_delay(hang->h_clock, (x->x_dur >= 0 ? x->x_dur : 0));\n}\n\nstatic void makenote_stop(t_makenote *x)\n{\n    t_hang *hang;\n    while ((hang = x->x_hang))\n    {\n        outlet_float(x->x_velout, 0);\n        outlet_float(x->x_pitchout, hang->h_pitch);\n        x->x_hang = hang->h_next;\n        clock_free(hang->h_clock);\n        freebytes(hang, sizeof(*hang));\n    }\n}\n\nstatic void makenote_clear(t_makenote *x)\n{\n    t_hang *hang;\n    while ((hang = x->x_hang))\n    {\n        x->x_hang = hang->h_next;\n        clock_free(hang->h_clock);\n        freebytes(hang, sizeof(*hang));\n    }\n}\n\nstatic void makenote_setup(void)\n{\n    makenote_class = class_new(gensym(\"makenote\"),\n        (t_newmethod)makenote_new, (t_method)makenote_clear,\n        sizeof(t_makenote), 0, A_DEFFLOAT, A_DEFFLOAT, 0);\n    class_addfloat(makenote_class, makenote_float);\n    class_addmethod(makenote_class, (t_method)makenote_stop, gensym(\"stop\"),\n        0);\n    class_addmethod(makenote_class, (t_method)makenote_clear, gensym(\"clear\"),\n        0);\n}\n\n/* -------------------------- stripnote -------------------------- */\n\nstatic t_class *stripnote_class;\n\ntypedef struct _stripnote\n{\n    t_object x_obj;\n    t_float x_velo;\n    t_outlet *x_pitchout;\n    t_outlet *x_velout;\n} t_stripnote;\n\nstatic void *stripnote_new(void)\n{\n    t_stripnote *x = (t_stripnote *)pd_new(stripnote_class);\n    floatinlet_new(&x->x_obj, &x->x_velo);\n    x->x_pitchout = outlet_new(&x->x_obj, &s_float);\n    x->x_velout = outlet_new(&x->x_obj, &s_float);\n    return (x);\n}\n\nstatic void stripnote_float(t_stripnote *x, t_float f)\n{\n    if (!x->x_velo) return;\n    outlet_float(x->x_velout, x->x_velo);\n    outlet_float(x->x_pitchout, f);\n}\n\nstatic void stripnote_setup(void)\n{\n    stripnote_class = class_new(gensym(\"stripnote\"),\n        (t_newmethod)stripnote_new, 0, sizeof(t_stripnote), 0, 0);\n    class_addfloat(stripnote_class, stripnote_float);\n}\n\n/* -------------------------- poly -------------------------- */\n\nstatic t_class *poly_class;\n\ntypedef struct voice\n{\n    t_float v_pitch;\n    int v_used;\n    unsigned long v_serial;\n} t_voice;\n\ntypedef struct poly\n{\n    t_object x_obj;\n    int x_n;\n    t_voice *x_vec;\n    t_float x_vel;\n    t_outlet *x_pitchout;\n    t_outlet *x_velout;\n    unsigned long x_serial;\n    int x_steal;\n} t_poly;\n\nstatic void *poly_new(t_float fnvoice, t_float fsteal)\n{\n    int i, n = fnvoice;\n    t_poly *x = (t_poly *)pd_new(poly_class);\n    t_voice *v;\n    if (n < 1) n = 1;\n    x->x_n = n;\n    x->x_vec = (t_voice *)getbytes(n * sizeof(*x->x_vec));\n    for (v = x->x_vec, i = n; i--; v++)\n        v->v_pitch = v->v_used = v->v_serial = 0;\n    x->x_vel = 0;\n    x->x_steal = (fsteal != 0);\n    floatinlet_new(&x->x_obj, &x->x_vel);\n    outlet_new(&x->x_obj, &s_float);\n    x->x_pitchout = outlet_new(&x->x_obj, &s_float);\n    x->x_velout = outlet_new(&x->x_obj, &s_float);\n    x->x_serial = 0;\n    return (x);\n}\n\nstatic void poly_float(t_poly *x, t_float f)\n{\n    int i;\n    t_voice *v;\n    t_voice *firston, *firstoff;\n    unsigned int serialon, serialoff, onindex = 0, offindex = 0;\n    if (x->x_vel > 0)\n    {\n            /* note on.  Look for a vacant voice */\n        for (v = x->x_vec, i = 0, firston = firstoff = 0,\n            serialon = serialoff = 0xffffffff; i < x->x_n; v++, i++)\n        {\n            if (v->v_used && v->v_serial < serialon)\n                    firston = v, serialon = (unsigned int)v->v_serial, onindex = i;\n            else if (!v->v_used && v->v_serial < serialoff)\n                    firstoff = v, serialoff = (unsigned int)v->v_serial, offindex = i;\n        }\n        if (firstoff)\n        {\n            outlet_float(x->x_velout, x->x_vel);\n            outlet_float(x->x_pitchout, firstoff->v_pitch = f);\n            outlet_float(x->x_obj.ob_outlet, offindex+1);\n            firstoff->v_used = 1;\n            firstoff->v_serial = x->x_serial++;\n        }\n            /* if none, steal one */\n        else if (firston && x->x_steal)\n        {\n            outlet_float(x->x_velout, 0);\n            outlet_float(x->x_pitchout, firston->v_pitch);\n            outlet_float(x->x_obj.ob_outlet, onindex+1);\n            outlet_float(x->x_velout, x->x_vel);\n            outlet_float(x->x_pitchout, firston->v_pitch = f);\n            outlet_float(x->x_obj.ob_outlet, onindex+1);\n            firston->v_serial = x->x_serial++;\n        }\n    }\n    else    /* note off. Turn off oldest match */\n    {\n        for (v = x->x_vec, i = 0, firston = 0, serialon = 0xffffffff;\n            i < x->x_n; v++, i++)\n                if (v->v_used && v->v_pitch == f && v->v_serial < serialon)\n                    firston = v, serialon = (unsigned int)v->v_serial, onindex = i;\n        if (firston)\n        {\n            firston->v_used = 0;\n            firston->v_serial = x->x_serial++;\n            outlet_float(x->x_velout, 0);\n            outlet_float(x->x_pitchout, firston->v_pitch);\n            outlet_float(x->x_obj.ob_outlet, onindex+1);\n        }\n    }\n}\n\nstatic void poly_stop(t_poly *x)\n{\n    int i;\n    t_voice *v;\n    for (i = 0, v = x->x_vec; i < x->x_n; i++, v++)\n        if (v->v_used)\n    {\n        outlet_float(x->x_velout, 0L);\n        outlet_float(x->x_pitchout, v->v_pitch);\n        outlet_float(x->x_obj.ob_outlet, i+1);\n        v->v_used = 0;\n        v->v_serial = x->x_serial++;\n    }\n}\n\nstatic void poly_clear(t_poly *x)\n{\n    int i;\n    t_voice *v;\n    for (v = x->x_vec, i = x->x_n; i--; v++) v->v_used = v->v_serial = 0;\n}\n\nstatic void poly_free(t_poly *x)\n{\n    freebytes(x->x_vec, x->x_n * sizeof (*x->x_vec));\n}\n\nstatic void poly_setup(void)\n{\n    poly_class = class_new(gensym(\"poly\"),\n        (t_newmethod)poly_new, (t_method)poly_free,\n        sizeof(t_poly), 0, A_DEFFLOAT, A_DEFFLOAT, 0);\n    class_addfloat(poly_class, poly_float);\n    class_addmethod(poly_class, (t_method)poly_stop, gensym(\"stop\"), 0);\n    class_addmethod(poly_class, (t_method)poly_clear, gensym(\"clear\"), 0);\n}\n\n/* -------------------------- bag -------------------------- */\n\nstatic t_class *bag_class;\n\ntypedef struct _bagelem\n{\n    struct _bagelem *e_next;\n    t_float e_value;\n} t_bagelem;\n\ntypedef struct _bag\n{\n    t_object x_obj;\n    t_float x_velo;\n    t_bagelem *x_first;\n} t_bag;\n\nstatic void *bag_new(void)\n{\n    t_bag *x = (t_bag *)pd_new(bag_class);\n    x->x_velo = 0;\n    floatinlet_new(&x->x_obj, &x->x_velo);\n    outlet_new(&x->x_obj, &s_float);\n    x->x_first = 0;\n    return (x);\n}\n\nstatic void bag_float(t_bag *x, t_float f)\n{\n    t_bagelem *bagelem, *e2, *e3;\n    if (x->x_velo != 0)\n    {\n        bagelem = (t_bagelem *)getbytes(sizeof *bagelem);\n        bagelem->e_next = 0;\n        bagelem->e_value = f;\n        if (!x->x_first) x->x_first = bagelem;\n        else    /* LATER replace with a faster algorithm */\n        {\n            for (e2 = x->x_first; (e3 = e2->e_next); e2 = e3)\n                ;\n            e2->e_next = bagelem;\n        }\n    }\n    else\n    {\n        if (!x->x_first) return;\n        if (x->x_first->e_value == f)\n        {\n            bagelem = x->x_first;\n            x->x_first = x->x_first->e_next;\n            freebytes(bagelem, sizeof(*bagelem));\n            return;\n        }\n        for (e2 = x->x_first; (e3 = e2->e_next); e2 = e3)\n            if (e3->e_value == f)\n        {\n            e2->e_next = e3->e_next;\n            freebytes(e3, sizeof(*e3));\n            return;\n        }\n    }\n}\n\nstatic void bag_flush(t_bag *x)\n{\n    t_bagelem *bagelem;\n    while ((bagelem = x->x_first))\n    {\n        outlet_float(x->x_obj.ob_outlet, bagelem->e_value);\n        x->x_first = bagelem->e_next;\n        freebytes(bagelem, sizeof(*bagelem));\n    }\n}\n\nstatic void bag_clear(t_bag *x)\n{\n    t_bagelem *bagelem;\n    while ((bagelem = x->x_first))\n    {\n        x->x_first = bagelem->e_next;\n        freebytes(bagelem, sizeof(*bagelem));\n    }\n}\n\nstatic void bag_setup(void)\n{\n    bag_class = class_new(gensym(\"bag\"),\n        (t_newmethod)bag_new, (t_method)bag_clear,\n        sizeof(t_bag), 0, 0);\n    class_addfloat(bag_class, bag_float);\n    class_addmethod(bag_class, (t_method)bag_flush, gensym(\"flush\"), 0);\n    class_addmethod(bag_class, (t_method)bag_clear, gensym(\"clear\"), 0);\n}\n\nvoid x_midi_setup(void)\n{\n    midiin_setup();\n    midirealtimein_setup();\n    notein_setup();\n    ctlin_setup();\n    pgmin_setup();\n    bendin_setup();\n    touchin_setup();\n    polytouchin_setup();\n    midiout_setup();\n    noteout_setup();\n    ctlout_setup();\n    pgmout_setup();\n    bendout_setup();\n    touchout_setup();\n    polytouchout_setup();\n    makenote_setup();\n    stripnote_setup();\n    poly_setup();\n    bag_setup();\n}\n\nvoid x_midi_newpdinstance(void)\n{\n    pd_this->pd_midi = getbytes(sizeof(t_instancemidi));\n    pd_this->pd_midi->m_midiin_sym = gensym(\"#midiin\");\n    pd_this->pd_midi->m_sysexin_sym = gensym(\"#sysexin\");\n    pd_this->pd_midi->m_notein_sym = gensym(\"#notein\");\n    pd_this->pd_midi->m_ctlin_sym = gensym(\"#ctlin\");\n    pd_this->pd_midi->m_pgmin_sym = gensym(\"#pgmin\");\n    pd_this->pd_midi->m_bendin_sym = gensym(\"#bendin\");\n    pd_this->pd_midi->m_touchin_sym = gensym(\"#touchin\");\n    pd_this->pd_midi->m_polytouchin_sym = gensym(\"#polytouchin\");\n    pd_this->pd_midi->m_midirealtimein_sym = gensym(\"#midirealtimein\");\n}\n\nvoid x_midi_freepdinstance(void)\n{\n    freebytes(pd_this->pd_midi, sizeof(t_instancemidi));\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/x_misc.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* misc. */\n\n#include \"m_pd.h\"\n#include \"s_stuff.h\"\n#include \"g_canvas.h\"\n#include <string.h>\n#ifdef _WIN32\n#include <wtypes.h>\n#include <time.h>\n#else\n#include <sys/types.h>\n#include <sys/time.h>\n#include <sys/times.h>\n#include <sys/param.h>\n#include <unistd.h>\n#endif /* _WIN32 */\n\n#if defined (__APPLE__) || defined (__FreeBSD__)\n#define CLOCKHZ CLK_TCK\n#endif\n#if defined (__linux__) || defined (__CYGWIN__) || defined (ANDROID)\n#define CLOCKHZ sysconf(_SC_CLK_TCK)\n#endif\n#if defined (__FreeBSD_kernel__) || defined(__GNU__) || defined(__OpenBSD__) \\\n    || defined(_WIN32)\n#include <time.h>\n#define CLOCKHZ CLOCKS_PER_SEC\n#endif\n\n#include \"m_private_utils.h\"\n\n/* -------------------------- random ------------------------------ */\n/* this is strictly homebrew and untested. */\n\nstatic t_class *random_class;\n\ntypedef struct _random\n{\n    t_object x_obj;\n    t_float x_f;\n    unsigned int x_state;\n} t_random;\n\n\nstatic int makeseed(void)\n{\n    static unsigned int random_nextseed = 1489853723;\n    random_nextseed = random_nextseed * 435898247 + 938284287;\n    return (random_nextseed & 0x7fffffff);\n}\n\nstatic void *random_new(t_floatarg f)\n{\n    t_random *x = (t_random *)pd_new(random_class);\n    x->x_f = f;\n    x->x_state = makeseed();\n    floatinlet_new(&x->x_obj, &x->x_f);\n    outlet_new(&x->x_obj, &s_float);\n    return (x);\n}\n\nstatic void random_bang(t_random *x)\n{\n    int n = x->x_f, nval;\n    int range = (n < 1 ? 1 : n);\n    unsigned int randval = x->x_state;\n    x->x_state = randval = randval * 472940017 + 832416023;\n    nval = ((double)range) * ((double)randval)\n        * (1./4294967296.);\n    if (nval >= range) nval = range-1;\n    outlet_float(x->x_obj.ob_outlet, nval);\n}\n\nstatic void random_seed(t_random *x, t_float f, t_float glob)\n{\n    x->x_state = f;\n}\n\nstatic void random_setup(void)\n{\n    random_class = class_new(gensym(\"random\"), (t_newmethod)random_new, 0,\n        sizeof(t_random), 0, A_DEFFLOAT, 0);\n    class_addbang(random_class, random_bang);\n    class_addmethod(random_class, (t_method)random_seed,\n        gensym(\"seed\"), A_FLOAT, 0);\n}\n\n\n/* -------------------------- loadbang ------------------------------ */\nstatic t_class *loadbang_class;\n\ntypedef struct _loadbang\n{\n    t_object x_obj;\n} t_loadbang;\n\nstatic void *loadbang_new(void)\n{\n    t_loadbang *x = (t_loadbang *)pd_new(loadbang_class);\n    outlet_new(&x->x_obj, &s_bang);\n    return (x);\n}\n\nstatic void loadbang_loadbang(t_loadbang *x, t_floatarg action)\n{\n    if (action == LB_LOAD)\n        outlet_bang(x->x_obj.ob_outlet);\n}\n\nstatic void loadbang_setup(void)\n{\n    loadbang_class = class_new(gensym(\"loadbang\"), (t_newmethod)loadbang_new, 0,\n        sizeof(t_loadbang), CLASS_NOINLET, 0);\n    class_addmethod(loadbang_class, (t_method)loadbang_loadbang,\n        gensym(\"loadbang\"), A_DEFFLOAT, 0);\n}\n\n/* ------------- namecanvas (delete this later) --------------------- */\nstatic t_class *namecanvas_class;\n\ntypedef struct _namecanvas\n{\n    t_object x_obj;\n    t_symbol *x_sym;\n    t_pd *x_owner;\n} t_namecanvas;\n\nstatic void *namecanvas_new(t_symbol *s)\n{\n    t_namecanvas *x = (t_namecanvas *)pd_new(namecanvas_class);\n    x->x_owner = (t_pd *)canvas_getcurrent();\n    x->x_sym = s;\n    if (*s->s_name) pd_bind(x->x_owner, s);\n    return (x);\n}\n\nstatic void namecanvas_free(t_namecanvas *x)\n{\n    if (*x->x_sym->s_name) pd_unbind(x->x_owner, x->x_sym);\n}\n\nstatic void namecanvas_setup(void)\n{\n    namecanvas_class = class_new(gensym(\"namecanvas\"),\n        (t_newmethod)namecanvas_new, (t_method)namecanvas_free,\n            sizeof(t_namecanvas), CLASS_NOINLET, A_DEFSYM, 0);\n}\n\n/* -------------------------- cputime ------------------------------ */\n#ifdef CLOCKHZ\n\nstatic t_class *cputime_class;\n\ntypedef struct _cputime\n{\n    t_object x_obj;\n#ifdef _WIN32\n    LARGE_INTEGER x_kerneltime;\n    LARGE_INTEGER x_usertime;\n    int x_warned;\n#else\n    struct tms x_setcputime;\n#endif /* _WIN32 */\n} t_cputime;\n\nstatic void cputime_bang(t_cputime *x)\n{\n#ifdef _WIN32\n    FILETIME ignorethis, ignorethat;\n    BOOL retval;\n    retval = GetProcessTimes(GetCurrentProcess(), &ignorethis, &ignorethat,\n        (FILETIME *)&x->x_kerneltime, (FILETIME *)&x->x_usertime);\n    if (!retval)\n    {\n        if (!x->x_warned)\n            post(\"cputime is apparently not supported on your platform\");\n        x->x_warned = 1;\n        x->x_kerneltime.QuadPart = 0;\n        x->x_usertime.QuadPart = 0;\n    }\n#else\n    times(&x->x_setcputime);\n#endif /* _WIN32 */\n}\n\nstatic void cputime_bang2(t_cputime *x)\n{\n#ifndef _WIN32\n    t_float elapsedcpu;\n    struct tms newcputime;\n    times(&newcputime);\n    elapsedcpu = 1000 * (\n        newcputime.tms_utime + newcputime.tms_stime -\n            x->x_setcputime.tms_utime - x->x_setcputime.tms_stime) / CLOCKHZ;\n    outlet_float(x->x_obj.ob_outlet, elapsedcpu);\n#else\n    t_float elapsedcpu;\n    FILETIME ignorethis, ignorethat;\n    LARGE_INTEGER usertime, kerneltime;\n    BOOL retval;\n\n    retval = GetProcessTimes(GetCurrentProcess(), &ignorethis, &ignorethat,\n        (FILETIME *)&kerneltime, (FILETIME *)&usertime);\n    if (retval)\n        elapsedcpu = 0.0001 *\n            ((kerneltime.QuadPart - x->x_kerneltime.QuadPart) +\n                (usertime.QuadPart - x->x_usertime.QuadPart));\n    else elapsedcpu = 0;\n    outlet_float(x->x_obj.ob_outlet, elapsedcpu);\n#endif /* NOT _WIN32 */\n}\n\nstatic void *cputime_new(void)\n{\n    t_cputime *x = (t_cputime *)pd_new(cputime_class);\n    outlet_new(&x->x_obj, gensym(\"float\"));\n\n    inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym(\"bang\"), gensym(\"bang2\"));\n#ifdef _WIN32\n    x->x_warned = 0;\n#endif\n    cputime_bang(x);\n    return (x);\n}\n\nstatic void cputime_setup(void)\n{\n    cputime_class = class_new(gensym(\"cputime\"), (t_newmethod)cputime_new, 0,\n        sizeof(t_cputime), 0, 0);\n    class_addbang(cputime_class, cputime_bang);\n    class_addmethod(cputime_class, (t_method)cputime_bang2, gensym(\"bang2\"), 0);\n}\n#endif /* CLOCKHZ */\n\n/* -------------------------- realtime ------------------------------ */\n\nstatic t_class *realtime_class;\n\ntypedef struct _realtime\n{\n    t_object x_obj;\n    double x_setrealtime;\n} t_realtime;\n\nstatic void realtime_bang(t_realtime *x)\n{\n    x->x_setrealtime = sys_getrealtime();\n}\n\nstatic void realtime_bang2(t_realtime *x)\n{\n    outlet_float(x->x_obj.ob_outlet,\n        (sys_getrealtime() - x->x_setrealtime) * 1000.);\n}\n\nstatic void *realtime_new(void)\n{\n    t_realtime *x = (t_realtime *)pd_new(realtime_class);\n    outlet_new(&x->x_obj, gensym(\"float\"));\n    inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym(\"bang\"), gensym(\"bang2\"));\n    realtime_bang(x);\n    return (x);\n}\n\nstatic void realtime_setup(void)\n{\n    realtime_class = class_new(gensym(\"realtime\"), (t_newmethod)realtime_new, 0,\n        sizeof(t_realtime), 0, 0);\n    class_addbang(realtime_class, realtime_bang);\n    class_addmethod(realtime_class, (t_method)realtime_bang2, gensym(\"bang2\"),\n        0);\n}\n\n/* ---------- oscparse - parse simple OSC messages ----------------- */\n\nstatic t_class *oscparse_class;\n\ntypedef struct _oscparse\n{\n    t_object x_obj;\n} t_oscparse;\n\n#define ROUNDUPTO4(x) (((x) + 3) & (~3))\n\n#define READINT(x)  ((((int)(((x)  )->a_w.w_float)) & 0xff) << 24) | \\\n                    ((((int)(((x)+1)->a_w.w_float)) & 0xff) << 16) | \\\n                    ((((int)(((x)+2)->a_w.w_float)) & 0xff) << 8) | \\\n                    ((((int)(((x)+3)->a_w.w_float)) & 0xff) << 0)\n\nstatic t_symbol *grabstring(int argc, t_atom *argv, int *ip, int slash)\n{\n    char buf[MAXPDSTRING];\n    int first, nchar;\n    if (slash)\n        while (*ip < argc && argv[*ip].a_w.w_float == '/')\n            (*ip)++;\n    for (nchar = 0; nchar < MAXPDSTRING-1 && *ip < argc; nchar++, (*ip)++)\n    {\n        char c = argv[*ip].a_w.w_float;\n        if (c == 0 || (slash && c == '/'))\n            break;\n        buf[nchar] = c;\n    }\n    buf[nchar] = 0;\n    if (!slash)\n        *ip = ROUNDUPTO4(*ip+1);\n    if (*ip > argc)\n        *ip = argc;\n    return (gensym(buf));\n}\n\nstatic void oscparse_list(t_oscparse *x, t_symbol *s, int argc, t_atom *argv)\n{\n    int i, j, j2, k, outc = 1, blob = 0, typeonset, dataonset, nfield;\n    t_atom *outv;\n    if (!argc)\n        return;\n    for (i = 0; i < argc; i++)\n        if (argv[i].a_type != A_FLOAT)\n    {\n        pd_error(x, \"oscparse: takes numbers only\");\n        return;\n    }\n    if (argv[0].a_w.w_float == '#') /* it's a bundle */\n    {\n        if (argv[1].a_w.w_float != 'b' || argc < 16)\n        {\n            pd_error(x, \"oscparse: malformed bundle\");\n            return;\n        }\n            /* we ignore the timetag since there's no correct way to\n            convert it to Pd logical time that I can think of.  LATER\n            consider at least outputting timetag differentially converted\n            into Pd time units. */\n        for (i = 16; i < argc-4; )\n        {\n            int msize = READINT(argv+i);\n            if (msize <= 0 || msize & 3)\n            {\n                pd_error(x, \"oscparse: bad bundle element size\");\n                return;\n            }\n            oscparse_list(x, 0, msize, argv+i+4);\n            i += msize+4;\n        }\n        return;\n    }\n    else if (argv[0].a_w.w_float != '/')\n    {\n        pd_error(x, \"oscparse: not an OSC message (no leading slash)\");\n        return;\n    }\n    for (i = 1; i < argc && argv[i].a_w.w_float != 0; i++)\n        if (argv[i].a_w.w_float == '/')\n            outc++;\n    i = ROUNDUPTO4(i+1);\n    if (argv[i].a_w.w_float != ',' || (i+1) >= argc)\n    {\n        pd_error(x, \"oscparse: malformed type string (char %d, index %d)\",\n            (int)(argv[i].a_w.w_float), i);\n        return;\n    }\n    typeonset = ++i;\n    for (; i < argc && argv[i].a_w.w_float != 0; i++)\n        if (argv[i].a_w.w_float == 'b')\n            blob = 1;\n    nfield = i - typeonset;\n    if (blob)\n        outc += argc - typeonset;\n    else outc += nfield;\n    outv = (t_atom *)alloca(outc * sizeof(t_atom));\n    dataonset = ROUNDUPTO4(i + 1);\n    /* post(\"outc %d, typeonset %d, dataonset %d, nfield %d\", outc, typeonset,\n        dataonset, nfield); */\n    for (i = j = 0; i < typeonset-1 && argv[i].a_w.w_float != 0 &&\n        j < outc; j++)\n            SETSYMBOL(outv+j, grabstring(argc, argv, &i, 1));\n    for (i = typeonset, k = dataonset; i < typeonset + nfield; i++)\n    {\n        union\n        {\n            float z_f;\n            uint32_t z_i;\n        } z;\n        t_float f;\n        int blobsize;\n        switch ((int)(argv[i].a_w.w_float))\n        {\n        case 'f':\n            if (k > argc - 4)\n                goto tooshort;\n            z.z_i = READINT(argv+k);\n            f = z.z_f;\n            if (PD_BADFLOAT(f))\n                f = 0;\n            if (j >= outc)\n            {\n                bug(\"oscparse 1: %d >=%d\", j, outc);\n                return;\n            }\n            SETFLOAT(outv+j, f);\n            j++; k += 4;\n            break;\n        case 'i':\n            if (k > argc - 4)\n                goto tooshort;\n            if (j >= outc)\n            {\n                bug(\"oscparse 2\");\n                return;\n            }\n            SETFLOAT(outv+j, READINT(argv+k));\n            j++; k += 4;\n            break;\n        case 's':\n            if (j >= outc)\n            {\n                bug(\"oscparse 3\");\n                return;\n            }\n            SETSYMBOL(outv+j, grabstring(argc, argv, &k, 0));\n            j++;\n            break;\n        case 'b':\n            if (k > argc - 4)\n                goto tooshort;\n            blobsize = READINT(argv+k);\n            k += 4;\n            if (blobsize < 0 || blobsize > argc - k)\n                goto tooshort;\n            if (j + blobsize + 1 > outc)\n            {\n                bug(\"oscparse 4\");\n                return;\n            }\n            if (k + blobsize > argc)\n                goto tooshort;\n            SETFLOAT(outv+j, blobsize);\n            j++;\n            for (j2 = 0; j2 < blobsize; j++, j2++, k++)\n                SETFLOAT(outv+j, argv[k].a_w.w_float);\n            k = ROUNDUPTO4(k);\n            break;\n        default:\n            pd_error(x, \"oscparse: unknown tag '%c' (%d)\",\n                (int)(argv[i].a_w.w_float), (int)(argv[i].a_w.w_float));\n        }\n    }\n    outlet_list(x->x_obj.ob_outlet, 0, j, outv);\n    return;\ntooshort:\n    pd_error(x, \"oscparse: OSC message ended prematurely\");\n}\n\nstatic t_oscparse *oscparse_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_oscparse *x = (t_oscparse *)pd_new(oscparse_class);\n    outlet_new(&x->x_obj, gensym(\"list\"));\n    return (x);\n}\n\nvoid oscparse_setup(void)\n{\n    oscparse_class = class_new(gensym(\"oscparse\"), (t_newmethod)oscparse_new,\n        0, sizeof(t_oscparse), 0, A_GIMME, 0);\n    class_addlist(oscparse_class, oscparse_list);\n    class_sethelpsymbol(oscparse_class, gensym(\"osc-format-parse\"));\n}\n\n/* --------- oscformat - format simple OSC messages -------------- */\nstatic t_class *oscformat_class;\n\ntypedef struct _oscformat\n{\n    t_object x_obj;\n    char *x_pathbuf;\n    size_t x_pathsize;\n    t_symbol *x_format;\n} t_oscformat;\n\nstatic void oscformat_set(t_oscformat *x, t_symbol *s, int argc, t_atom *argv)\n{\n    char buf[MAXPDSTRING];\n    int i;\n    size_t newsize;\n    *x->x_pathbuf = 0;\n    buf[0] = '/';\n    for (i = 0; i < argc; i++)\n    {\n        char *where = (argv[i].a_type == A_SYMBOL &&\n            *argv[i].a_w.w_symbol->s_name == '/' ? buf : buf+1);\n        atom_string(&argv[i], where, MAXPDSTRING-1);\n        if ((newsize = strlen(buf) + strlen(x->x_pathbuf) + 1) > x->x_pathsize)\n        {\n            x->x_pathbuf = resizebytes(x->x_pathbuf, x->x_pathsize, newsize);\n            x->x_pathsize = newsize;\n        }\n        strcat(x->x_pathbuf, buf);\n    }\n}\n\nstatic void oscformat_format(t_oscformat *x, t_symbol *s)\n{\n    const char *sp;\n    for (sp = s->s_name; *sp; sp++)\n    {\n        if (*sp != 'f' && *sp != 'i' && *sp != 's' && *sp != 'b')\n        {\n            pd_error(x,\n                \"oscformat '%s' may only contain 'f', 'i'. 's', and/or 'b'\",\n                    sp);\n            return;\n        }\n    }\n    x->x_format = s;\n}\n\n#define WRITEINT(msg, i)    SETFLOAT((msg),   (((i) >> 24) & 0xff)); \\\n                            SETFLOAT((msg)+1, (((i) >> 16) & 0xff)); \\\n                            SETFLOAT((msg)+2, (((i) >>  8) & 0xff)); \\\n                            SETFLOAT((msg)+3, (((i)      ) & 0xff))\n\nstatic void putstring(t_atom *msg, int *ip, const char *s)\n{\n    const char *sp = s;\n    do\n    {\n        SETFLOAT(&msg[*ip], *sp & 0xff);\n        (*ip)++;\n    }\n    while (*sp++);\n    while (*ip & 3)\n    {\n        SETFLOAT(&msg[*ip], 0);\n        (*ip)++;\n    }\n}\n\nstatic void oscformat_list(t_oscformat *x, t_symbol *s, int argc, t_atom *argv)\n{\n    int typeindex = 0, j, msgindex, msgsize, datastart, ndata;\n    t_atom *msg;\n    const char *sp, *formatp = x->x_format->s_name;\n    char typecode;\n        /* pass 1: go through args to find overall message size */\n    for (j = ndata = 0, sp = formatp, msgindex = 0; j < argc;)\n    {\n        if (*sp)\n            typecode = *sp++;\n        else if (argv[j].a_type == A_SYMBOL)\n            typecode = 's';\n        else typecode = 'f';\n        if (typecode == 's')\n        {\n            if (argv[j].a_type == A_SYMBOL)\n                msgindex += ROUNDUPTO4(strlen(argv[j].a_w.w_symbol->s_name) + 1);\n            else\n            {\n                pd_error(x, \"oscformat: expected symbol for argument %d\", j+1);\n                return;\n            }\n        }\n        else if (typecode == 'b')\n        {\n            int blobsize = 0x7fffffff, blobindex;\n                /* check if we have a nonnegative size field */\n            if (argv[j].a_type == A_FLOAT &&\n                (int)(argv[j].a_w.w_float) >= 0)\n                    blobsize = (int)(argv[j].a_w.w_float);\n            if (blobsize > argc - j - 1)\n                blobsize = argc - j - 1;    /* if no or bad size, eat it all */\n            msgindex += 4 + ROUNDUPTO4(blobsize);\n            j += blobsize;\n        }\n        else msgindex += 4;\n        j++;\n        ndata++;\n    }\n    datastart = (int)(ROUNDUPTO4(strlen(x->x_pathbuf)+1) + ROUNDUPTO4(ndata + 2));\n    msgsize = datastart + msgindex;\n    msg = (t_atom *)alloca(msgsize * sizeof(t_atom));\n    putstring(msg, &typeindex, x->x_pathbuf);\n    SETFLOAT(&msg[typeindex], ',');\n    typeindex++;\n        /* pass 2: fill in types and data portion of packet */\n    for (j = 0, sp = formatp, msgindex = datastart; j < argc;)\n    {\n        if (*sp)\n            typecode = *sp++;\n        else if (argv[j].a_type == A_SYMBOL)\n            typecode = 's';\n        else typecode = 'f';\n        SETFLOAT(&msg[typeindex], typecode & 0xff);\n        typeindex++;\n        if (typecode == 'f')\n        {\n            union\n            {\n                float z_f;\n                uint32_t z_i;\n            } z;\n            z.z_f = atom_getfloat(&argv[j]);\n            WRITEINT(msg+msgindex, z.z_i);\n            msgindex += 4;\n        }\n        else if (typecode == 'i')\n        {\n            int dat = atom_getfloat(&argv[j]);\n            WRITEINT(msg+msgindex, dat);\n            msgindex += 4;\n        }\n        else if (typecode == 's')\n            putstring(msg, &msgindex, argv[j].a_w.w_symbol->s_name);\n        else if (typecode == 'b')\n        {\n            int blobsize = 0x7fffffff, blobindex;\n            if (argv[j].a_type == A_FLOAT &&\n                (int)(argv[j].a_w.w_float) >= 0)\n                    blobsize = (int)(argv[j].a_w.w_float);\n            if (blobsize > argc - j - 1)\n                blobsize = argc - j - 1;\n            WRITEINT(msg+msgindex, blobsize);\n            msgindex += 4;\n            for (blobindex = 0; blobindex < blobsize; blobindex++)\n                SETFLOAT(msg+msgindex+blobindex,\n                    (argv[j+1+blobindex].a_type == A_FLOAT ?\n                        argv[j+1+blobindex].a_w.w_float :\n                        (argv[j+1+blobindex].a_type == A_SYMBOL ?\n                            argv[j+1+blobindex].a_w.w_symbol->s_name[0] & 0xff :\n                            0)));\n            j += blobsize;\n            while (blobsize & 3)\n                SETFLOAT(msg+msgindex+blobsize, 0), blobsize++;\n            msgindex += blobsize;\n        }\n        j++;\n    }\n    SETFLOAT(&msg[typeindex], 0);\n    typeindex++;\n    while (typeindex & 3)\n        SETFLOAT(&msg[typeindex], 0), typeindex++;\n    if (typeindex != datastart || msgindex != msgsize)\n        bug(\"oscformat: typeindex %d, datastart %d, msgindex %d, msgsize %d\",\n            typeindex, datastart, msgindex, msgsize);\n    /* else post(\"datastart %d, msgsize %d\", datastart, msgsize); */\n    outlet_list(x->x_obj.ob_outlet, 0, msgsize, msg);\n}\n\nstatic void oscformat_free(t_oscformat *x)\n{\n    freebytes(x->x_pathbuf, x->x_pathsize);\n}\n\nstatic void *oscformat_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_oscformat *x = (t_oscformat *)pd_new(oscformat_class);\n    outlet_new(&x->x_obj, gensym(\"list\"));\n    x->x_pathbuf = getbytes(1);\n    x->x_pathsize = 1;\n    *x->x_pathbuf = 0;\n    x->x_format = &s_;\n    if (argc > 1 && argv[0].a_type == A_SYMBOL &&\n        argv[1].a_type == A_SYMBOL &&\n            !strcmp(argv[0].a_w.w_symbol->s_name, \"-f\"))\n    {\n        oscformat_format(x, argv[1].a_w.w_symbol);\n        argc -= 2;\n        argv += 2;\n    }\n    oscformat_set(x, 0, argc, argv);\n    return (x);\n}\n\nvoid oscformat_setup(void)\n{\n    oscformat_class = class_new(gensym(\"oscformat\"), (t_newmethod)oscformat_new,\n        (t_method)oscformat_free, sizeof(t_oscformat), 0, A_GIMME, 0);\n    class_addmethod(oscformat_class, (t_method)oscformat_set,\n        gensym(\"set\"), A_GIMME, 0);\n    class_addmethod(oscformat_class, (t_method)oscformat_format,\n        gensym(\"format\"), A_DEFSYM, 0);\n    class_addlist(oscformat_class, oscformat_list);\n    class_sethelpsymbol(oscformat_class, gensym(\"osc-format-parse\"));\n}\n\n\n/* ---------- fudiparse - parse bytelists to to FUDI messages ----------------- */\n\nstatic t_class *fudiparse_class;\n\ntypedef struct _fudiparse {\n  t_object  x_obj;\n  t_outlet *x_msgout;\n  char     *x_bytes;\n  size_t    x_numbytes;\n} t_fudiparse;\n\nstatic void fudiparse_binbufout(t_fudiparse *x, t_binbuf *b)\n{\n  int msg, natom = binbuf_getnatom(b);\n  t_atom *at = binbuf_getvec(b);\n  for (msg = 0; msg < natom;) {\n    int emsg;\n    for (emsg = msg; emsg < natom && at[emsg].a_type != A_COMMA\n           && at[emsg].a_type != A_SEMI; emsg++)\n      ;\n    if (emsg > msg) {\n      int i;\n      /* check for illegal atoms */\n      for (i = msg; i < emsg; i++)\n        if (at[i].a_type == A_DOLLAR || at[i].a_type == A_DOLLSYM) {\n          pd_error(x, \"fudiparse: got dollar sign in message\");\n          goto nodice;\n        }\n\n      if (at[msg].a_type == A_FLOAT) {\n        if (emsg > msg + 1)\n          outlet_list(x->x_msgout, 0, emsg-msg, at + msg);\n        else outlet_float(x->x_msgout, at[msg].a_w.w_float);\n      }\n      else if (at[msg].a_type == A_SYMBOL) {\n        outlet_anything(x->x_msgout, at[msg].a_w.w_symbol,\n                        emsg-msg-1, at + msg + 1);\n      }\n    }\n  nodice:\n    msg = emsg + 1;\n  }\n}\nstatic void fudiparse_list(t_fudiparse *x, t_symbol*s, int argc, t_atom*argv) {\n  size_t len = argc;\n  t_binbuf* bbuf = binbuf_new();\n  char*cbuf;\n  if((size_t)argc > x->x_numbytes) {\n    freebytes(x->x_bytes, x->x_numbytes);\n    x->x_numbytes = argc;\n    x->x_bytes = getbytes(x->x_numbytes);\n  }\n  cbuf = x->x_bytes;\n\n  while(argc--) {\n    char b = atom_getfloat(argv++);\n    *cbuf++ = b;\n  }\n  binbuf_text(bbuf, x->x_bytes, len);\n\n  fudiparse_binbufout(x, bbuf);\n\n  binbuf_free(bbuf);\n}\n\nstatic void fudiparse_free(t_fudiparse *x) {\n  freebytes(x->x_bytes, x->x_numbytes);\n  x->x_bytes = NULL;\n  x->x_numbytes = 0;\n}\n\nstatic void *fudiparse_new(void) {\n  t_fudiparse *x = (t_fudiparse *)pd_new(fudiparse_class);\n  x->x_msgout = outlet_new(&x->x_obj, 0);\n  x->x_numbytes = 1024;\n  x->x_bytes = getbytes(x->x_numbytes);\n  return (void *)x;\n}\n\nvoid fudiparse_setup(void) {\n  fudiparse_class = class_new(gensym(\"fudiparse\"),\n                              (t_newmethod)fudiparse_new,\n                              (t_method)fudiparse_free,\n                              sizeof(t_fudiparse), CLASS_DEFAULT,\n                              0);\n  class_addlist(fudiparse_class, fudiparse_list);\n  class_sethelpsymbol(fudiparse_class, gensym(\"fudi-format-parse\"));\n}\n/* --------- fudiformat - format Pd (FUDI) messages to bytelists ------------ */\n\nstatic t_class *fudiformat_class;\n\ntypedef struct _fudiformat {\n  t_object  x_obj;\n  t_outlet *x_msgout;\n  t_atom   *x_atoms;\n  size_t    x_numatoms;\n  int       x_udp;\n} t_fudiformat;\n\nstatic void fudiformat_any(t_fudiformat *x, t_symbol*s, int argc, t_atom*argv) {\n  char *buf;\n  int length;\n  int i;\n  t_atom at;\n  t_binbuf*bbuf = binbuf_new();\n  SETSYMBOL(&at, s);\n  binbuf_add(bbuf, 1, &at);\n\n  binbuf_add(bbuf, argc, argv);\n\n  if(!x->x_udp) {\n    SETSEMI(&at);\n    binbuf_add(bbuf, 1, &at);\n  }\n  binbuf_gettext(bbuf, &buf, &length);\n  binbuf_free(bbuf);\n\n  if((size_t)length>x->x_numatoms) {\n    freebytes(x->x_atoms, sizeof(*x->x_atoms) * x->x_numatoms);\n    x->x_numatoms = length;\n    x->x_atoms = getbytes(sizeof(*x->x_atoms) * x->x_numatoms);\n  }\n\n  for(i=0; i<length; i++) {\n    SETFLOAT(x->x_atoms+i, buf[i]);\n  }\n  freebytes(buf, length);\n  outlet_list(x->x_msgout, 0, length, x->x_atoms);\n}\n\nstatic void fudiformat_free(t_fudiformat *x) {\n  freebytes(x->x_atoms, sizeof(*x->x_atoms) * x->x_numatoms);\n  x->x_atoms = NULL;\n  x->x_numatoms = 0;\n}\n\nstatic void *fudiformat_new(t_symbol*s) {\n  t_fudiformat *x = (t_fudiformat *)pd_new(fudiformat_class);\n  x->x_msgout = outlet_new(&x->x_obj, 0);\n  x->x_numatoms = 1024;\n  x->x_atoms = getbytes(sizeof(*x->x_atoms) * x->x_numatoms);\n  if (gensym(\"-u\") == s)\n    x->x_udp = 1;\n  else if (gensym(\"-t\") == s)\n    x->x_udp = 0;\n  else if (gensym(\"\") != s) {\n    pd_error(x, \"fudiformat: unsupported mode '%s'\", s->s_name);\n  }\n\n  return (void *)x;\n}\n\nstatic void fudiformat_setup(void) {\n  fudiformat_class = class_new(gensym(\"fudiformat\"),\n                               (t_newmethod)fudiformat_new,\n                               (t_method)fudiformat_free,\n                               sizeof(t_fudiformat), CLASS_DEFAULT,\n                               A_DEFSYMBOL, 0);\n  class_addanything(fudiformat_class, fudiformat_any);\n  class_sethelpsymbol(fudiformat_class, gensym(\"fudi-format-parse\"));\n}\n\n\n\nvoid x_misc_setup(void)\n{\n    random_setup();\n    loadbang_setup();\n    namecanvas_setup();\n#ifdef CLOCKHZ\n    cputime_setup();\n#endif /* CLOCKHZ */\n    realtime_setup();\n    oscparse_setup();\n    oscformat_setup();\n    fudiparse_setup();\n    fudiformat_setup();\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/x_net.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* network */\n\n#include \"m_pd.h\"\n#include \"s_stuff.h\"\n#include \"s_net.h\"\n\n#include <string.h>\n\n#include \"m_private_utils.h\"\n\n/* print addrinfo lists for debugging */\n/* #define PRINT_ADDRINFO */\n\n/* ----------------------------- helpers ------------------------- */\n\nvoid socketreceiver_free(t_socketreceiver *x);\n\nstatic void outlet_sockaddr(t_outlet *o, const struct sockaddr *sa)\n{\n    char addrstr[INET6_ADDRSTRLEN];\n    unsigned short port = sockaddr_get_port(sa);\n    if (sockaddr_get_addrstr(sa, addrstr, INET6_ADDRSTRLEN))\n    {\n        t_atom ap[2];\n        SETSYMBOL(&ap[0], gensym(addrstr));\n        SETFLOAT(&ap[1], (t_float)port);\n        outlet_list(o, NULL, 2, ap);\n    }\n}\n\n/* ----------------------------- net ------------------------- */\n\nstatic t_class *netsend_class;\n\ntypedef struct _netsend\n{\n    t_object x_obj;\n    t_outlet *x_msgout;\n    t_outlet *x_connectout;\n    t_outlet *x_fromout;\n    int x_sockfd;\n    int x_protocol;\n    int x_bin;\n    t_socketreceiver *x_receiver;\n    struct sockaddr_storage x_server;\n    t_float x_timeout; /* TCP connect timeout in seconds */\n} t_netsend;\n\nstatic t_class *netreceive_class;\n\ntypedef struct _netreceive\n{\n    t_netsend x_ns;\n    int x_nconnections;\n    int x_sockfd;\n    int *x_connections;\n    int x_old;\n    t_socketreceiver **x_receivers;\n} t_netreceive;\n\nstatic void netsend_disconnect(t_netsend *x);\nstatic void netreceive_notify(t_netreceive *x, int fd);\n\n/* ----------------------------- netsend ------------------------- */\n\nstatic void *netsend_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_netsend *x = (t_netsend *)pd_new(netsend_class);\n    outlet_new(&x->x_obj, &s_float);\n    x->x_protocol = SOCK_STREAM;\n    x->x_bin = 0;\n    if (argc && argv->a_type == A_FLOAT)\n    {\n        x->x_protocol = (argv->a_w.w_float != 0 ? SOCK_DGRAM : SOCK_STREAM);\n        argc = 0;\n    }\n    else while (argc && argv->a_type == A_SYMBOL &&\n        *argv->a_w.w_symbol->s_name == '-')\n    {\n        if (!strcmp(argv->a_w.w_symbol->s_name, \"-b\"))\n            x->x_bin = 1;\n        else if (!strcmp(argv->a_w.w_symbol->s_name, \"-u\"))\n            x->x_protocol = SOCK_DGRAM;\n        else\n        {\n            pd_error(x, \"netsend: unknown flag ...\");\n            postatom(argc, argv); endpost();\n        }\n        argc--; argv++;\n    }\n    if (argc)\n    {\n        pd_error(x, \"netsend: extra arguments ignored:\");\n        postatom(argc, argv); endpost();\n    }\n    x->x_sockfd = -1;\n    x->x_receiver = NULL;\n    x->x_msgout = outlet_new(&x->x_obj, &s_anything);\n    x->x_connectout = NULL;\n    x->x_fromout = NULL;\n    x->x_timeout = 10;\n    memset(&x->x_server, 0, sizeof(struct sockaddr_storage));\n    return (x);\n}\n\nstatic void netsend_readbin(t_netsend *x, int fd)\n{\n    unsigned char *inbuf = sys_getrecvbuf(0);\n    int ret = 0, readbytes = 0, i;\n    struct sockaddr_storage fromaddr = {0};\n    socklen_t fromaddrlen = sizeof(struct sockaddr_storage);\n    if (!x->x_msgout)\n    {\n        bug(\"netsend_readbin\");\n        return;\n    }\n    while (1)\n    {\n        if (x->x_protocol == SOCK_DGRAM)\n            ret = (int)recvfrom(fd, inbuf, NET_MAXPACKETSIZE, 0,\n                (struct sockaddr *)&fromaddr, &fromaddrlen);\n        else\n            ret = (int)recv(fd, inbuf, NET_MAXPACKETSIZE, 0);\n        if (ret <= 0)\n        {\n            if (ret < 0)\n            {\n                /* socket_errno_udp() ignores some error codes */\n                if (x->x_protocol == SOCK_DGRAM && !socket_errno_udp())\n                    return;\n                sys_sockerror(\"recv (bin)\");\n            }\n            if (x->x_obj.ob_pd == netreceive_class)\n            {\n                    /* never close UDP socket because we can't really notify it */\n                if (x->x_protocol != SOCK_DGRAM)\n                {\n                    sys_rmpollfn(fd);\n                    sys_closesocket(fd);\n                    netreceive_notify((t_netreceive *)x, fd);\n                }\n            }\n            else /* properly shutdown netsend */\n                netsend_disconnect(x);\n            return;\n        }\n        if (x->x_protocol == SOCK_DGRAM)\n        {\n            t_atom *ap;\n            if (x->x_fromout)\n                outlet_sockaddr(x->x_fromout, (const struct sockaddr *)&fromaddr);\n                /* handle too large UDP packets */\n            if (ret > NET_MAXPACKETSIZE)\n            {\n                post(\"warning: incoming UDP packet truncated from %d to %d bytes.\",\n                    ret, NET_MAXPACKETSIZE);\n                ret = NET_MAXPACKETSIZE;\n            }\n            ap = (t_atom *)alloca(ret * sizeof(t_atom));\n            for (i = 0; i < ret; i++)\n                SETFLOAT(ap+i, inbuf[i]);\n            outlet_list(x->x_msgout, 0, ret, ap);\n            readbytes += ret;\n            /* throttle */\n            if (readbytes >= NET_MAXPACKETSIZE)\n                return;\n            /* check for pending UDP packets */\n            if (socket_bytes_available(fd) <= 0)\n                return;\n        }\n        else\n        {\n            if (x->x_fromout &&\n                !getpeername(fd, (struct sockaddr *)&fromaddr, &fromaddrlen))\n                    outlet_sockaddr(x->x_fromout, (const struct sockaddr *)&fromaddr);\n            for (i = 0; i < ret; i++)\n                outlet_float(x->x_msgout, inbuf[i]);\n            return;\n        }\n    }\n}\n\nstatic void netsend_read(void *z, t_binbuf *b)\n{\n    t_netsend *x = (t_netsend *)z;\n    int msg, natom = binbuf_getnatom(b);\n    t_atom *at = binbuf_getvec(b);\n    for (msg = 0; msg < natom;)\n    {\n        int emsg;\n        for (emsg = msg; emsg < natom && at[emsg].a_type != A_COMMA\n             && at[emsg].a_type != A_SEMI; emsg++)\n            ;\n        if (emsg > msg)\n        {\n            int i;\n            for (i = msg; i < emsg; i++)\n                if (at[i].a_type == A_DOLLAR || at[i].a_type == A_DOLLSYM)\n                {\n                    pd_error(x, \"netreceive: got dollar sign in message\");\n                    goto nodice;\n                }\n            if (at[msg].a_type == A_FLOAT)\n            {\n                if (emsg > msg + 1)\n                    outlet_list(x->x_msgout, 0, emsg-msg, at + msg);\n                else outlet_float(x->x_msgout, at[msg].a_w.w_float);\n            }\n            else if (at[msg].a_type == A_SYMBOL)\n                outlet_anything(x->x_msgout, at[msg].a_w.w_symbol,\n                    emsg-msg-1, at + msg + 1);\n        }\n    nodice:\n        msg = emsg + 1;\n    }\n}\n\nstatic void netsend_notify(void *z, int fd)\n{\n    t_netsend *x = (t_netsend *)z;\n    if (x->x_sockfd >= 0)\n    {\n        /* sys_rmpollfn() and sys_closesocket() are\n           already called in socketreceiver_read */\n        x->x_sockfd = -1;\n        if (x->x_receiver)\n            socketreceiver_free(x->x_receiver);\n        x->x_receiver = NULL;\n        memset(&x->x_server, 0, sizeof(struct sockaddr_storage));\n        outlet_float(x->x_obj.ob_outlet, 0);\n    }\n}\n\nstatic void netsend_connect(t_netsend *x, t_symbol *s, int argc, t_atom *argv)\n{\n    int portno, sportno, sockfd, multicast = 0, status;\n    struct addrinfo *ailist = NULL, *ai;\n    const char *hostname = NULL;\n    char hostbuf[256];\n\n    /* check argument types */\n    if ((argc < 2) ||\n        argv[0].a_type != A_SYMBOL ||\n        argv[1].a_type != A_FLOAT ||\n        ((argc > 2) && argv[2].a_type != A_FLOAT))\n    {\n        pd_error(0, \"netsend: bad connect arguments\");\n        return;\n    }\n    hostname = argv[0].a_w.w_symbol->s_name;\n    portno = (int)argv[1].a_w.w_float;\n    sportno = (argc > 2 ? (int)argv[2].a_w.w_float : 0);\n    if (x->x_sockfd >= 0)\n    {\n        pd_error(0, \"netsend: already connected\");\n        return;\n    }\n\n    /* get addrinfo list using hostname & port */\n    status = addrinfo_get_list(&ailist, hostname, portno, x->x_protocol);\n    if (status != 0)\n    {\n        pd_error(x, \"netsend: bad host or port? %s (%d)\",\n            gai_strerror(status), status);\n        return;\n    }\n    addrinfo_sort_list(&ailist, addrinfo_ipv4_first); /* IPv4 first! */\n#ifdef PRINT_ADDRINFO\n    addrinfo_print_list(ailist);\n#endif\n    /* try each addr until we find one that works */\n    for (ai = ailist; ai != NULL; ai = ai->ai_next)\n    {\n        /* create a socket */\n        sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);\n#if 0\n        fprintf(stderr, \"send socket %d\\n\", sockfd);\n#endif\n        if (sockfd < 0)\n            continue;\n\n#if 0\n        if (socket_set_boolopt(sockfd, SOL_SOCKET, SO_SNDBUF, 0) < 0)\n            post(\"netsend: setsockopt (SO_RCVBUF) failed\");\n#endif\n\n        /* for stream (TCP) sockets, specify \"nodelay\" */\n        if (x->x_protocol == SOCK_STREAM)\n        {\n            if (socket_set_boolopt(sockfd, IPPROTO_TCP, TCP_NODELAY, 1) < 0)\n                post(\"netsend: setsockopt (TCP_NODELAY) failed\");\n        }\n        else /* datagram (UDP) broadcasting */\n        {\n            if (socket_set_boolopt(sockfd, SOL_SOCKET, SO_BROADCAST, 1) < 0)\n                post(\"netsend: setsockopt (SO_BROADCAST) failed\");\n            multicast = sockaddr_is_multicast(ai->ai_addr);\n        }\n        /* if this is an IPv6 address, also listen to IPv4 adapters */\n        if (ai->ai_family == AF_INET6 &&\n            socket_set_boolopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, 0) < 0)\n        {\n            /* post(\"netreceive: setsockopt (IPV6_V6ONLY) failed\"); */\n        }\n\n        sockaddr_get_addrstr(ai->ai_addr, hostbuf, sizeof(hostbuf));\n\n        /* bind optional src listening port */\n        if (sportno != 0)\n        {\n            int bound = 0;\n            struct addrinfo *sailist = NULL, *sai;\n            logpost(NULL, PD_VERBOSE, \"connecting to %s %d, src port %d\", hostbuf, portno, sportno);\n            status = addrinfo_get_list(&sailist, NULL, sportno, x->x_protocol);\n            if (status != 0)\n            {\n                pd_error(x, \"netsend: could not set src port: %s (%d)\",\n                    gai_strerror(status), status);\n                freeaddrinfo(sailist);\n                goto connect_fail;\n            }\n            addrinfo_sort_list(&sailist, addrinfo_ipv6_first); /* IPv6 first! */\n            for (sai = sailist; sai != NULL; sai = sai->ai_next)\n            {\n                /* if this is an IPv6 address, also listen to IPv4 adapters\n                   (if not supported, fall back to IPv4) */\n                if (sai->ai_family == AF_INET6 &&\n                        socket_set_boolopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, 0) < 0)\n                {\n                    /* post(\"netreceive: setsockopt (IPV6_V6ONLY) failed\"); */\n                    continue;\n                }\n                if (bind(sockfd, sai->ai_addr, sai->ai_addrlen) < 0)\n                    continue;\n                bound = 1;\n                break;\n            }\n            freeaddrinfo(sailist);\n            if (!bound)\n            {\n                sys_sockerror(\"setting source port\");\n                goto connect_fail;\n            }\n        }\n        else if (hostname && multicast)\n            logpost(NULL, PD_VERBOSE, \"connecting to %s %d (multicast)\", hostbuf, portno);\n        else\n            logpost(NULL, PD_VERBOSE, \"connecting to %s %d\", hostbuf, portno);\n\n        if (x->x_protocol == SOCK_STREAM)\n        {\n            if (socket_connect(sockfd, ai->ai_addr, ai->ai_addrlen,\n                                    x->x_timeout) < 0)\n            {\n                sys_sockerror(\"connecting stream socket\");\n                sys_closesocket(sockfd);\n                freeaddrinfo(ailist);\n                /* output 0 on connection failure so the user\n                   can easily retry in a loop */\n                outlet_float(x->x_obj.ob_outlet, 0);\n                return;\n            }\n        }\n\n        /* this addr worked */\n        memcpy(&x->x_server, ai->ai_addr, ai->ai_addrlen);\n        break;\n    }\n    freeaddrinfo(ailist);\n\n    /* confirm that socket worked */\n    if (sockfd < 0)\n    {\n        int err = socket_errno();\n        char buf[MAXPDSTRING];\n        socket_strerror(err, buf, sizeof(buf));\n        pd_error(x, \"netsend: connect failed: %s (%d)\", buf, err);\n        return;\n    }\n\n    x->x_sockfd = sockfd;\n    if (x->x_msgout) /* add polling function for return messages */\n    {\n        if (x->x_bin)\n            sys_addpollfn(x->x_sockfd, (t_fdpollfn)netsend_readbin, x);\n        else\n        {\n            t_socketreceiver *y =\n              socketreceiver_new((void *)x, netsend_notify, netsend_read,\n                                 x->x_protocol == SOCK_DGRAM);\n            sys_addpollfn(x->x_sockfd, (t_fdpollfn)socketreceiver_read, y);\n            x->x_receiver = y;\n        }\n    }\n    outlet_float(x->x_obj.ob_outlet, 1);\n    return;\nconnect_fail:\n    freeaddrinfo(ailist);\n    if (sockfd > 0)\n        sys_closesocket(sockfd);\n}\n\nstatic void netsend_disconnect(t_netsend *x)\n{\n    if (x->x_sockfd >= 0)\n    {\n        sys_rmpollfn(x->x_sockfd);\n        sys_closesocket(x->x_sockfd);\n        x->x_sockfd = -1;\n        if (x->x_receiver)\n            socketreceiver_free(x->x_receiver);\n        x->x_receiver = NULL;\n        memset(&x->x_server, 0, sizeof(struct sockaddr_storage));\n        outlet_float(x->x_obj.ob_outlet, 0);\n    }\n}\n\nstatic int netsend_dosend(t_netsend *x, int sockfd, int argc, t_atom *argv)\n{\n    char *buf, *bp;\n    int length, sent, fail = 0;\n    t_binbuf *b = 0;\n    if (x->x_bin)\n    {\n        int i;\n        buf = alloca(argc);\n        for (i = 0; i < argc; i++)\n            ((unsigned char *)buf)[i] = atom_getfloatarg(i, argc, argv);\n        length = argc;\n    }\n    else\n    {\n        t_atom at;\n        b = binbuf_new();\n        binbuf_add(b, argc, argv);\n        SETSEMI(&at);\n        binbuf_add(b, 1, &at);\n        binbuf_gettext(b, &buf, &length);\n    }\n    for (bp = buf, sent = 0; sent < length;)\n    {\n        static double lastwarntime;\n        static double pleasewarn;\n        double timebefore = sys_getrealtime(), timeafter;\n        int late;\n\n        int res = 0;\n        if (x->x_protocol == SOCK_DGRAM)\n        {\n            socklen_t addrlen = (x->x_server.ss_family == AF_INET6 ?\n                sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));\n            res = (int)sendto(sockfd, bp, length-sent, 0,\n                (struct sockaddr *)&x->x_server, addrlen);\n        }\n        else\n            res = (int)send(sockfd, bp, length-sent, 0);\n\n        timeafter = sys_getrealtime();\n        late = (timeafter - timebefore > 0.005);\n        if (late || pleasewarn)\n        {\n            if (timeafter > lastwarntime + 2)\n            {\n                logpost(NULL, PD_DEBUG, \"netsend/netreceive: blocked %d msec\",\n                     (int)(1000 * ((timeafter - timebefore) +\n                     pleasewarn)));\n                pleasewarn = 0;\n                lastwarntime = timeafter;\n            }\n            else if (late) pleasewarn += timeafter - timebefore;\n        }\n        if (res <= 0)\n        {\n            sys_sockerror(\"send\");\n            fail = 1;\n            break;\n        }\n        else\n        {\n            sent += res;\n            bp += res;\n        }\n    }\n    done:\n    if (!x->x_bin)\n    {\n        t_freebytes(buf, length);\n        binbuf_free(b);\n    }\n    return (fail);\n}\n\nstatic void netsend_send(t_netsend *x, t_symbol *s, int argc, t_atom *argv)\n{\n    if (x->x_sockfd >= 0)\n    {\n        if (netsend_dosend(x, x->x_sockfd, argc, argv))\n            netsend_disconnect(x);\n    }\n}\n\nstatic void netsend_timeout(t_netsend *x, t_float timeout)\n{\n    if (timeout >= 0)\n        x->x_timeout = timeout * 0.001;\n}\n\nstatic void netsend_free(t_netsend *x)\n{\n    netsend_disconnect(x);\n}\n\nstatic void netsend_setup(void)\n{\n    netsend_class = class_new(gensym(\"netsend\"), (t_newmethod)netsend_new,\n        (t_method)netsend_free,\n        sizeof(t_netsend), 0, A_GIMME, 0);\n    class_addmethod(netsend_class, (t_method)netsend_connect,\n        gensym(\"connect\"), A_GIMME, 0);\n    class_addmethod(netsend_class, (t_method)netsend_disconnect,\n        gensym(\"disconnect\"), 0);\n    class_addmethod(netsend_class, (t_method)netsend_send,\n        gensym(\"send\"), A_GIMME, 0);\n    class_addlist(netsend_class, (t_method)netsend_send);\n    class_addmethod(netsend_class, (t_method)netsend_timeout,\n        gensym(\"timeout\"), A_DEFFLOAT, 0);\n    class_sethelpsymbol(netsend_class, gensym(\"netsend-receive\"));\n}\n\n/* ----------------------------- netreceive ------------------------- */\n\nstatic void netreceive_notify(t_netreceive *x, int fd)\n{\n    int i;\n    for (i = 0; i < x->x_nconnections; i++)\n    {\n        if (x->x_connections[i] == fd)\n        {\n            memmove(x->x_connections+i, x->x_connections+(i+1),\n                sizeof(int) * (x->x_nconnections - (i+1)));\n            x->x_connections = (int *)t_resizebytes(x->x_connections,\n                x->x_nconnections * sizeof(int),\n                    (x->x_nconnections-1) * sizeof(int));\n\n            if (x->x_receivers[i])\n                socketreceiver_free(x->x_receivers[i]);\n            memmove(x->x_receivers+i, x->x_receivers+(i+1),\n                sizeof(t_socketreceiver*) * (x->x_nconnections - (i+1)));\n\n            x->x_receivers = (t_socketreceiver **)t_resizebytes(x->x_receivers,\n                x->x_nconnections * sizeof(t_socketreceiver*),\n                    (x->x_nconnections-1) * sizeof(t_socketreceiver*));\n            x->x_nconnections--;\n        }\n    }\n    if (x->x_ns.x_connectout)\n    {\n        outlet_float(x->x_ns.x_connectout, x->x_nconnections);\n    }\n    else\n        bug(\"netreceive_notify\");\n}\n\n    /* socketreceiver from sockaddr_in */\nstatic void netreceive_fromaddr(void *z, const void *fromaddr)\n{\n    t_netreceive *x = (t_netreceive *)z;\n    if (x->x_ns.x_fromout)\n        outlet_sockaddr(x->x_ns.x_fromout, (const struct sockaddr *)fromaddr);\n}\n\nstatic void netreceive_connectpoll(t_netreceive *x)\n{\n    int fd = accept(x->x_ns.x_sockfd, 0, 0);\n    if (fd < 0) post(\"netreceive: accept failed\");\n    else\n    {\n        int nconnections = x->x_nconnections+1;\n\n        x->x_connections = (int *)t_resizebytes(x->x_connections,\n            x->x_nconnections * sizeof(int), nconnections * sizeof(int));\n        x->x_connections[x->x_nconnections] = fd;\n        x->x_receivers = (t_socketreceiver **)t_resizebytes(x->x_receivers,\n            x->x_nconnections * sizeof(t_socketreceiver*),\n            nconnections * sizeof(t_socketreceiver*));\n        x->x_receivers[x->x_nconnections] = NULL;\n        if (x->x_ns.x_bin)\n            sys_addpollfn(fd, (t_fdpollfn)netsend_readbin, x);\n        else\n        {\n            t_socketreceiver *y = socketreceiver_new((void *)x,\n            (t_socketnotifier)netreceive_notify,\n                (x->x_ns.x_msgout ? netsend_read : 0), 0);\n            if (x->x_ns.x_fromout)\n                socketreceiver_set_fromaddrfn(y,\n                    (t_socketfromaddrfn)netreceive_fromaddr);\n            sys_addpollfn(fd, (t_fdpollfn)socketreceiver_read, y);\n            x->x_receivers[x->x_nconnections] = y;\n        }\n        outlet_float(x->x_ns.x_connectout, (x->x_nconnections = nconnections));\n    }\n}\n\nstatic void netreceive_closeall(t_netreceive *x)\n{\n    int i;\n    for (i = 0; i < x->x_nconnections; i++)\n    {\n        sys_rmpollfn(x->x_connections[i]);\n        sys_closesocket(x->x_connections[i]);\n        if (x->x_receivers[i])\n        {\n            socketreceiver_free(x->x_receivers[i]);\n            x->x_receivers[i] = NULL;\n        }\n    }\n    x->x_connections = (int *)t_resizebytes(x->x_connections,\n        x->x_nconnections * sizeof(int), 0);\n    x->x_receivers = (t_socketreceiver**)t_resizebytes(x->x_receivers,\n                x->x_nconnections * sizeof(t_socketreceiver*), 0);\n    x->x_nconnections = 0;\n    if (x->x_ns.x_sockfd >= 0)\n    {\n        sys_rmpollfn(x->x_ns.x_sockfd);\n        sys_closesocket(x->x_ns.x_sockfd);\n    }\n    x->x_ns.x_sockfd = -1;\n    if (x->x_ns.x_receiver)\n        socketreceiver_free(x->x_ns.x_receiver);\n    x->x_ns.x_receiver = NULL;\n    if (x->x_ns.x_connectout)\n        outlet_float(x->x_ns.x_connectout, x->x_nconnections);\n}\n\nstatic void netreceive_listen(t_netreceive *x, t_symbol *s, int argc, t_atom *argv)\n{\n    int portno = 0, sockfd, status, protocol = x->x_ns.x_protocol, multicast = 0;\n    struct addrinfo *ailist = NULL, *ai;\n    const char *hostname = NULL; /* allowed or UDP multicast hostname */\n\n    netreceive_closeall(x);\n\n    if (argc && argv->a_type == A_FLOAT)\n        portno = argv->a_w.w_float, argc--, argv++;\n    if (argc && argv->a_type == A_SYMBOL)\n    {\n        hostname = argv->a_w.w_symbol->s_name;\n        argv++; argc--;\n    }\n    if (argc)\n    {\n        pd_error(x, \"netreceive: extra arguments ignored:\");\n        postatom(argc, argv); endpost();\n    }\n    if (portno <= 0)\n        return;\n    status = addrinfo_get_list(&ailist, hostname, portno, protocol);\n    if (status != 0)\n    {\n        pd_error(x, \"netreceive: bad host or port? %s (%d)\",\n            gai_strerror(status), status);\n        return;\n    }\n    if (hostname)\n    {\n        /* If we specify a hostname or IP address we can only listen to a single adapter.\n         * For host names, we prefer IPv4 for now. LATER we might create several sockets */\n        addrinfo_sort_list(&ailist, addrinfo_ipv4_first);\n    }\n    else\n    {\n        /* For the \"any\" address we want to prefer IPv6, so we can create a dual stack socket */\n        addrinfo_sort_list(&ailist, addrinfo_ipv6_first);\n    }\n#ifdef PRINT_ADDRINFO\n    addrinfo_print_list(ailist);\n#endif\n    /* try each addr until we find one that works */\n    for (ai = ailist; ai != NULL; ai = ai->ai_next)\n    {\n        /* create a socket */\n        sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);\n        if (sockfd < 0)\n            continue;\n    #if 0\n        fprintf(stderr, \"receive socket %d\\n\", sockfd);\n    #endif\n\n    #if 1\n        /* ask OS to allow another Pd to reopen this port after we close it */\n        if (socket_set_boolopt(sockfd, SOL_SOCKET, SO_REUSEADDR, 1) < 0)\n            post(\"netreceive: setsockopt (SO_REUSEADDR) failed\");\n    #endif\n    #if 0\n        intarg = 0;\n        if (socket_set_boolopt(sockfd, SOL_SOCKET, SO_RCVBUF, 0) < 0)\n            post(\"netreceive: setsockopt (SO_RCVBUF) failed\");\n    #endif\n        if (protocol == SOCK_STREAM)\n        {\n            /* stream (TCP) sockets are set NODELAY */\n            if (socket_set_boolopt(sockfd, IPPROTO_TCP, TCP_NODELAY, 1) < 0)\n                post(\"netreceive: setsockopt (TCP_NODELAY) failed\");\n        }\n        else if (protocol == SOCK_DGRAM && ai->ai_family == AF_INET)\n        {\n            /* enable IPv4 UDP broadcasting */\n            if (socket_set_boolopt(sockfd, SOL_SOCKET, SO_BROADCAST, 1) < 0)\n                post(\"netreceive: setsockopt (SO_BROADCAST) failed\");\n        }\n        /* if this is the IPv6 \"any\" address, also listen to IPv4 adapters\n           (if not supported, fall back to IPv4) */\n        if (!hostname && ai->ai_family == AF_INET6 &&\n                socket_set_boolopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, 0) < 0)\n        {\n            /* post(\"netreceive: setsockopt (IPV6_V6ONLY) failed\"); */\n            sys_closesocket(sockfd);\n            sockfd = -1;\n            continue;\n        }\n        multicast = sockaddr_is_multicast(ai->ai_addr);\n#if 1\n        if (multicast)\n        {\n            /* binding to the multicast address doesn't work on Windows and on Linux\n               it doesn't seem to work for IPv6 multicast addresses, so we bind to\n               the \"any\" address instead */\n            struct addrinfo *any;\n            int status = addrinfo_get_list(&any,\n                (ai->ai_family == AF_INET6) ? \"::\" : \"0.0.0.0\", portno, protocol);\n            if (status != 0)\n            {\n                pd_error(x,\n                    \"netreceive: getting \\\"any\\\" address for multicast failed %s (%d)\",\n                    gai_strerror(status), status);\n                sys_closesocket(sockfd);\n                return;\n            }\n            /* name the socket */\n            status = bind(sockfd, any->ai_addr, any->ai_addrlen);\n            freeaddrinfo(any);\n            if (status < 0)\n            {\n                sys_closesocket(sockfd);\n                sockfd = -1;\n                continue;\n            }\n        }\n        else\n#endif\n        {\n            /* name the socket */\n            if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0)\n            {\n                sys_closesocket(sockfd);\n                sockfd = -1;\n                continue;\n            }\n        }\n        /* join multicast group */\n        if (multicast && socket_join_multicast_group(sockfd, ai->ai_addr) < 0)\n        {\n            int err = socket_errno();\n            char buf[MAXPDSTRING];\n            socket_strerror(err, buf, sizeof(buf));\n            pd_error(x,\n                \"netreceive: joining multicast group %s failed: %s (%d)\",\n                hostname, buf, err);\n        }\n        /* this addr worked */\n        if (hostname)\n        {\n            char hostbuf[256];\n            sockaddr_get_addrstr(ai->ai_addr,\n                hostbuf, sizeof(hostbuf));\n            logpost(NULL, PD_VERBOSE, \"listening on %s %d%s\", hostbuf, portno,\n                (multicast ? \" (multicast)\" : \"\"));\n        }\n        else\n            logpost(NULL, PD_VERBOSE, \"listening on %d\", portno);\n        break;\n    }\n    freeaddrinfo(ailist);\n\n    /* confirm that socket/bind worked */\n    if (sockfd < 0)\n    {\n        int err = socket_errno();\n        char buf[MAXPDSTRING];\n        socket_strerror(err, buf, sizeof(buf));\n        pd_error(x, \"netreceive: listen failed: %s (%d)\",\n            buf, err);\n        return;\n    }\n    x->x_ns.x_sockfd = sockfd;\n\n    if (protocol == SOCK_DGRAM) /* datagram protocol */\n    {\n        if (x->x_ns.x_bin)\n            sys_addpollfn(x->x_ns.x_sockfd, (t_fdpollfn)netsend_readbin, x);\n        else\n        {\n                /* a UDP receiver doesn't get notifications! */\n            t_socketreceiver *y = socketreceiver_new(x, 0,\n                    (x->x_ns.x_msgout ? netsend_read : 0), 1);\n            if (x->x_ns.x_fromout)\n                socketreceiver_set_fromaddrfn(y,\n                    (t_socketfromaddrfn)netreceive_fromaddr);\n            sys_addpollfn(x->x_ns.x_sockfd, (t_fdpollfn)socketreceiver_read, y);\n            x->x_ns.x_connectout = 0;\n            x->x_ns.x_receiver = y;\n        }\n    }\n    else /* streaming protocol */\n    {\n        if (listen(x->x_ns.x_sockfd, 5) < 0)\n        {\n            sys_sockerror(\"listen\");\n            sys_closesocket(x->x_ns.x_sockfd);\n            x->x_ns.x_sockfd = -1;\n        }\n        else\n        {\n            sys_addpollfn(x->x_ns.x_sockfd,\n                (t_fdpollfn)netreceive_connectpoll,x);\n        }\n    }\n}\n\nstatic void netreceive_send(t_netreceive *x,\n    t_symbol *s, int argc, t_atom *argv)\n{\n    int i;\n    if (x->x_ns.x_protocol != SOCK_STREAM)\n    {\n        pd_error(x, \"netreceive: 'send' only works for TCP\");\n        return;\n    }\n    for (i = 0; i < x->x_nconnections; i++)\n    {\n        if (netsend_dosend(&x->x_ns, x->x_connections[i], argc, argv))\n            pd_error(x, \"netreceive: send message failed\");\n                /* should we now close the connection? */\n    }\n}\n\nstatic void *netreceive_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_netreceive *x = (t_netreceive *)pd_new(netreceive_class);\n    int from = 0;\n    x->x_ns.x_protocol = SOCK_STREAM;\n    x->x_old = 0;\n    x->x_ns.x_bin = 0;\n    x->x_nconnections = 0;\n    x->x_connections = (int *)t_getbytes(0);\n    x->x_receivers = (t_socketreceiver **)t_getbytes(0);\n    x->x_ns.x_sockfd = -1;\n    if (argc && argv->a_type == A_FLOAT)\n    {\n        /* port argument is later passed to netreceive_listen */\n        x->x_ns.x_protocol = (atom_getfloatarg(1, argc, argv) != 0 ?\n            SOCK_DGRAM : SOCK_STREAM);\n        x->x_old = (!strcmp(atom_getsymbolarg(2, argc, argv)->s_name, \"old\"));\n        argc = 1; /* don't pass other arguments */\n    }\n    else\n    {\n        while (argc && argv->a_type == A_SYMBOL &&\n            *argv->a_w.w_symbol->s_name == '-')\n        {\n            if (!strcmp(argv->a_w.w_symbol->s_name, \"-b\"))\n                x->x_ns.x_bin = 1;\n            else if (!strcmp(argv->a_w.w_symbol->s_name, \"-u\"))\n                x->x_ns.x_protocol = SOCK_DGRAM;\n            else if (!strcmp(argv->a_w.w_symbol->s_name, \"-f\"))\n                from = 1;\n            else\n            {\n                pd_error(x, \"netreceive: unknown flag ...\");\n                postatom(argc, argv); endpost();\n            }\n            argc--; argv++;\n        }\n    }\n    if (x->x_old)\n    {\n        /* old style, nonsecure version */\n        x->x_ns.x_msgout = 0;\n    }\n    else x->x_ns.x_msgout = outlet_new(&x->x_ns.x_obj, &s_anything);\n    if (x->x_ns.x_protocol == SOCK_STREAM)\n        x->x_ns.x_connectout = outlet_new(&x->x_ns.x_obj, &s_float);\n    else\n        x->x_ns.x_connectout = 0;\n    if (from)\n        x->x_ns.x_fromout = outlet_new(&x->x_ns.x_obj, &s_symbol);\n    else\n        x->x_ns.x_fromout = NULL;\n        /* create a socket */\n    netreceive_listen(x, 0, argc, argv); /* pass arguments */\n\n    return (x);\n}\n\nstatic void netreceive_free(t_netreceive *x)\n{\n    netreceive_closeall(x);\n}\n\nstatic void netreceive_setup(void)\n{\n    netreceive_class = class_new(gensym(\"netreceive\"),\n        (t_newmethod)netreceive_new, (t_method)netreceive_free,\n        sizeof(t_netreceive), 0, A_GIMME, 0);\n    class_addmethod(netreceive_class, (t_method)netreceive_listen,\n        gensym(\"listen\"), A_GIMME, 0);\n    class_addmethod(netreceive_class, (t_method)netreceive_send,\n        gensym(\"send\"), A_GIMME, 0);\n    class_addlist(netreceive_class, (t_method)netreceive_send);\n    class_sethelpsymbol(netreceive_class, gensym(\"netsend-receive\"));\n}\n\nvoid x_net_setup(void)\n{\n    netsend_setup();\n    netreceive_setup();\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/x_scalar.c",
    "content": "/* Copyright (c) 1997-2013 Miller Puckette and others.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* The \"scalar\" object. */\n\n#include \"m_pd.h\"\n#include \"g_canvas.h\"\n#include <string.h>\n#include <stdio.h>\n#ifdef HAVE_UNISTD_H\n#include <unistd.h>\n#endif\n#ifdef _WIN32\n#include <io.h>\n#endif\n\nt_class *scalar_define_class;\n\nstatic void *scalar_define_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_atom a[9];\n    t_canvas *x, *z = canvas_getcurrent();\n    t_symbol *templatesym = &s_float, *asym = gensym(\"#A\");\n    t_template *template;\n    t_scalar *sc;\n    int keep = 0;\n    while (argc && argv->a_type == A_SYMBOL &&\n        *argv->a_w.w_symbol->s_name == '-')\n    {\n        if (!strcmp(argv->a_w.w_symbol->s_name, \"-k\"))\n            keep = 1;\n        else\n        {\n            pd_error(0, \"scalar define: unknown flag ...\");\n            postatom(argc, argv);\n        }\n        argc--; argv++;\n    }\n    if (argc && argv->a_type == A_SYMBOL)\n    {\n        templatesym = argv->a_w.w_symbol;\n        argc--; argv++;\n    }\n    if (argc)\n    {\n        post(\"warning: scalar define ignoring extra argument: \");\n        postatom(argc, argv);\n    }\n\n        /* make a canvas... */\n    SETFLOAT(a+0, GLIST_DEFCANVASXLOC); /* xpos */\n    SETFLOAT(a+1, GLIST_DEFCANVASYLOC); /* ypos */\n    SETFLOAT(a+2, 600); /* width */\n    SETFLOAT(a+3, 400); /* height */\n    SETSYMBOL(a+4, s);\n    SETFLOAT(a+5, 0);\n    x = canvas_new(0, 0, 6, a);\n\n    x->gl_owner = z;\n    x->gl_private = 0;\n        /* put a scalar in it */\n    template = template_findbyname(canvas_makebindsym(templatesym));\n    if (!template)\n    {\n        pd_error(x, \"scalar define: couldn't find template %s\",\n            templatesym->s_name);\n        goto noscalar;\n    }\n    sc = scalar_new(x, canvas_makebindsym(templatesym));\n    if (!sc)\n    {\n        pd_error(x, \"%s: couldn't create scalar\", templatesym->s_name);\n        goto noscalar;\n    }\n    sc->sc_gobj.g_next = 0;\n    x->gl_list = &sc->sc_gobj;\n    x->gl_private = keep;\n           /* bashily unbind #A -- this would create garbage if #A were\n           multiply bound but we believe in this context it's at most\n           bound to whichever text_define or array was created most recently */\n    asym->s_thing = 0;\n        /* and now bind #A to us to receive following messages in the\n        saved file or copy buffer */\n    pd_bind(&x->gl_obj.ob_pd, asym);\nnoscalar:\n    pd_this->pd_newest = &x->gl_pd;     /* mimic action of canvas_pop() */\n    pd_popsym(&x->gl_pd);\n    x->gl_loading = 0;\n\n        /* bash the class to \"scalar define\" -- see comment in x_array,c */\n    x->gl_obj.ob_pd = scalar_define_class;\n    outlet_new(&x->gl_obj, &s_pointer);\n    return (x);\n}\n\n    /* send a pointer to the scalar to whomever is bound to the symbol */\nstatic void scalar_define_send(t_glist *x, t_symbol *s)\n{\n    if (!s->s_thing)\n        pd_error(x, \"scalar_define_send: %s: no such object\", s->s_name);\n    else if (x->gl_list && pd_class(&x->gl_list->g_pd) == scalar_class)\n    {\n        t_gpointer gp;\n        gpointer_init(&gp);\n        gpointer_setglist(&gp, x, (t_scalar *)&x->gl_list->g_pd);\n        pd_pointer(s->s_thing, &gp);\n        gpointer_unset(&gp);\n    }\n    else bug(\"scalar_define_send\");\n}\n\nstatic void scalar_define_bang(t_glist *x)\n{\n    if (x->gl_list && pd_class(&x->gl_list->g_pd) == scalar_class)\n    {\n        t_gpointer gp;\n        gpointer_init(&gp);\n        gpointer_setglist(&gp, x, (t_scalar *)&x->gl_list->g_pd);\n        outlet_pointer(x->gl_obj.ob_outlet, &gp);\n        gpointer_unset(&gp);\n    }\n    else bug(\"scalar_define_bang\");\n}\n\n    /* set to a list, used to restore from scalar_define_save()s below */\nstatic void scalar_define_set(t_glist *x, t_symbol *s, int argc, t_atom *argv)\n{\n    if (x->gl_list && pd_class(&x->gl_list->g_pd) == scalar_class)\n    {\n        t_binbuf *b = binbuf_new();\n        int nextmsg = 0, natoms;\n        t_atom *vec;\n        glist_clear(x);\n        binbuf_restore(b, argc, argv);\n        natoms = binbuf_getnatom(b);\n        vec = binbuf_getvec(b);\n        canvas_readscalar(x, natoms, vec, &nextmsg, 0);\n        binbuf_free(b);\n    }\n    else bug(\"scalar_define_set\");\n}\n\n    /* save to a binbuf (for file save or copy) */\nstatic void scalar_define_save(t_gobj *z, t_binbuf *bb)\n{\n    t_glist *x = (t_glist *)z;\n    binbuf_addv(bb, \"ssff\", &s__X, gensym(\"obj\"),\n        (t_float)x->gl_obj.te_xpix, (t_float)x->gl_obj.te_ypix);\n    binbuf_addbinbuf(bb, x->gl_obj.ob_binbuf);\n    binbuf_addsemi(bb);\n    if (x->gl_private && x->gl_list &&\n        pd_class(&x->gl_list->g_pd) == scalar_class)\n    {\n        t_binbuf *b2 = binbuf_new();\n        t_scalar *sc = (t_scalar *)(x->gl_list);\n        binbuf_addv(bb, \"ss\", gensym(\"#A\"), gensym(\"set\"));\n        canvas_writescalar(sc->sc_template, sc->sc_vec, b2, 0);\n        binbuf_addbinbuf(bb, b2);\n        binbuf_addsemi(bb);\n        binbuf_free(b2);\n    }\n}\n\n/* overall creator for \"scalar\" objects - dispatch to \"scalar define\" etc */\nstatic void *scalarobj_new(t_symbol *s, int argc, t_atom *argv)\n{\n    if (!argc || argv[0].a_type != A_SYMBOL)\n        pd_this->pd_newest = scalar_define_new(s, argc, argv);\n    else\n    {\n        const char *str = argv[0].a_w.w_symbol->s_name;\n        if (!strcmp(str, \"d\") || !strcmp(str, \"define\"))\n            pd_this->pd_newest = scalar_define_new(s, argc-1, argv+1);\n        else\n        {\n            pd_error(0, \"scalar %s: unknown function\", str);\n            pd_this->pd_newest = 0;\n        }\n    }\n    return (pd_this->pd_newest);\n}\n\nvoid canvas_add_for_class(t_class *c);\n\n/* ---------------- global setup function -------------------- */\n\nvoid x_scalar_setup(void)\n{\n    scalar_define_class = class_new(gensym(\"scalar define\"), 0,\n        (t_method)canvas_free, sizeof(t_canvas), 0, 0);\n    canvas_add_for_class(scalar_define_class);\n    class_addmethod(scalar_define_class, (t_method)scalar_define_send,\n        gensym(\"send\"), A_SYMBOL, 0);\n    class_addbang(scalar_define_class, (t_method)scalar_define_bang);\n    class_addmethod(scalar_define_class, (t_method)scalar_define_set,\n        gensym(\"set\"), A_GIMME, 0);\n    class_sethelpsymbol(scalar_define_class, gensym(\"scalar-object\"));\n    class_setsavefn(scalar_define_class, scalar_define_save);\n\n    class_addcreator((t_newmethod)scalarobj_new, gensym(\"scalar\"), A_GIMME, 0);\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/x_text.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette and others.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* historically this file defined the qlist and textfile objects - at the\nmoment it also defines \"text\" but it may later be better to split this off. */\n\n#include \"m_pd.h\"\n#include \"g_canvas.h\"    /* just for glist_getfont, bother */\n#include <string.h>\n#include <stdio.h>\n#define __USE_GNU     /* needed so stdlib will define qsort_r */\n#include <stdlib.h>\n#ifdef HAVE_UNISTD_H\n#include <unistd.h>\n#endif\n#ifdef _WIN32\n#include <io.h>\n#endif\nstatic t_class *text_define_class;\n\n#include \"m_private_utils.h\"\n\n#define TEXT_NGETBYTE 100 /* bigger that this we use alloc, not alloca */\n\n/* --- unified qsort_r --- */\n\n#if !HAVE_QSORT_R_ARG_LAST && !HAVE_QSORT_R_COMPAR_LAST\n# undef STUPID_SORT\n# define STUPID_SORT 1\n#endif\n\n/* GNU and BSD have incompatible implementations of qsort_r\n * so we define a few macros to fix that.\n * STUPID_SORT uses the same calling convention as GNU\n */\n#if HAVE_QSORT_R_COMPAR_LAST\n        /* BSD variant of qsort_r */\n# define QSORT_R(base, nmemb, size, compar, arg) \\\n    qsort_r((base), (nmemb), (size), (arg), (compar))\n# define TEXT_SORTCOMPARE(z1, z2, zkeyinfo) text_sortcompare(zkeyinfo, z1, z2)\n#else\n        /* GNU variant of qsort_r */\n# define QSORT_R(base, nmemb, size, compar, arg) \\\n    qsort_r((base), (nmemb), (size), (compar), (arg))\n# define TEXT_SORTCOMPARE(z1, z2, zkeyinfo) text_sortcompare(z1, z2, zkeyinfo)\n#endif /* QSORT_R variants */\n\n/* --- common code for text define, textfile, and qlist for storing text -- */\n\ntypedef struct _textbuf\n{\n    t_object b_ob;\n    t_binbuf *b_binbuf;\n    t_canvas *b_canvas;\n    t_guiconnect *b_guiconnect;\n    t_symbol *b_sym;\n} t_textbuf;\n\nstatic void textbuf_init(t_textbuf *x, t_symbol *sym)\n{\n    x->b_binbuf = binbuf_new();\n    x->b_canvas = canvas_getcurrent();\n    x->b_sym = sym;\n}\n\nstatic void textbuf_senditup(t_textbuf *x)\n{\n    int ntxt;\n    char *txt, *buf;\n    if (!x->b_guiconnect)\n        return;\n\n#if 0\n    binbuf_gettext(x->b_binbuf, &txt, &ntxt);\n    buf = getbytes(ntxt+2);\n    memcpy(buf, txt, ntxt);\n    buf[ntxt] = buf[ntxt+1] = 0;\n        /* append a trailing newline, but only if there isn't one there already */\n    if ('\\n' != buf[ntxt-1]) buf[ntxt] = '\\n';\n\n    pdgui_vmess(\"pdtk_textwindow_clear\", \"^\", x);\n    pdgui_vmess(\"pdtk_textwindow_append\", \"^s\", x, buf);\n\n    freebytes(txt, ntxt);\n    freebytes(buf, ntxt+2);\n#else\n        /* send the binbuf directly and let the GUI figure out when to do\n         * linebreaks and how to escape special character $1*/\n    pdgui_vmess(\"pdtk_textwindow_clear\", \"^\", x);\n    pdgui_vmess(\"pdtk_textwindow_appendatoms\", \"^A\",\n        x, binbuf_getnatom(x->b_binbuf), binbuf_getvec(x->b_binbuf));\n#endif\n\n    pdgui_vmess(\"pdtk_textwindow_setdirty\", \"^i\", x, 0);\n}\n\nstatic void textbuf_open(t_textbuf *x)\n{\n    if (x->b_guiconnect)\n    {\n        char textid[128];\n        sprintf(textid, \".x%lx.text\", x);\n        pdgui_vmess(\"wm\", \"r^\", \"deiconify\", x);\n        pdgui_vmess(\"raise\", \"^\", x);\n        pdgui_vmess(\"focus\", \"s\", textid);\n    }\n    else\n    {\n        char buf[40];\n        sprintf(buf, \"%dx%d\", 600, 340);\n        pdgui_vmess(\"pdtk_textwindow_open\", \"^r si\",\n                  x, buf,\n                  x->b_sym->s_name,\n                  sys_hostfontsize(glist_getfont(x->b_canvas), glist_getzoom(x->b_canvas)));\n        sprintf(buf, \".x%lx\", x);\n        x->b_guiconnect = guiconnect_new(&x->b_ob.ob_pd, gensym(buf));\n        textbuf_senditup(x);\n    }\n}\n\nstatic void textbuf_close(t_textbuf *x)\n{\n    if (x->b_guiconnect)\n    {\n        pdgui_vmess(\"pdtk_textwindow_doclose\", \"^\", x);\n        guiconnect_notarget(x->b_guiconnect, 1000);\n        x->b_guiconnect = 0;\n    }\n}\n\nstatic void textbuf_addline(t_textbuf *b, t_symbol *s, int argc, t_atom *argv)\n{\n    t_binbuf *z = binbuf_new();\n    binbuf_restore(z, argc, argv);\n    binbuf_add(b->b_binbuf, binbuf_getnatom(z), binbuf_getvec(z));\n    binbuf_free(z);\n}\n\nstatic void textbuf_read(t_textbuf *x, t_symbol *s, int argc, t_atom *argv)\n{\n    int cr = 0;\n    t_symbol *filename;\n    while (argc && argv->a_type == A_SYMBOL &&\n        *argv->a_w.w_symbol->s_name == '-')\n    {\n        if (!strcmp(argv->a_w.w_symbol->s_name, \"-c\"))\n            cr = 1;\n        else\n        {\n            pd_error(x, \"text read: unknown flag ...\");\n            postatom(argc, argv); endpost();\n        }\n        argc--; argv++;\n    }\n    if (argc && argv->a_type == A_SYMBOL)\n    {\n        filename = argv->a_w.w_symbol;\n        argc--; argv++;\n    }\n    else\n    {\n        pd_error(x, \"text read: no file name given\");\n        return;\n    }\n    if (argc)\n    {\n        post(\"warning: text define ignoring extra argument: \");\n        postatom(argc, argv); endpost();\n    }\n    if (binbuf_read_via_canvas(x->b_binbuf, filename->s_name, x->b_canvas, cr))\n            pd_error(x, \"%s: read failed\", filename->s_name);\n    textbuf_senditup(x);\n}\n\nstatic void textbuf_write(t_textbuf *x, t_symbol *s, int argc, t_atom *argv)\n{\n    int cr = 0;\n    t_symbol *filename;\n    char buf[MAXPDSTRING];\n    while (argc && argv->a_type == A_SYMBOL &&\n        *argv->a_w.w_symbol->s_name == '-')\n    {\n        if (!strcmp(argv->a_w.w_symbol->s_name, \"-c\"))\n            cr = 1;\n        else\n        {\n            pd_error(x, \"text write: unknown flag ...\");\n            postatom(argc, argv); endpost();\n        }\n        argc--; argv++;\n    }\n    if (argc && argv->a_type == A_SYMBOL)\n    {\n        filename = argv->a_w.w_symbol;\n        argc--; argv++;\n    }\n    else\n    {\n        pd_error(x, \"text write: no file name given\");\n        return;\n    }\n    if (argc)\n    {\n        post(\"warning: text define ignoring extra argument: \");\n        postatom(argc, argv); endpost();\n    }\n    canvas_makefilename(x->b_canvas, filename->s_name,\n        buf, MAXPDSTRING);\n    if (binbuf_write(x->b_binbuf, buf, \"\", cr))\n            pd_error(x, \"%s: write failed\", filename->s_name);\n}\n\nstatic void textbuf_free(t_textbuf *x)\n{\n    t_pd *x2;\n    if (x->b_binbuf)\n        binbuf_free(x->b_binbuf);\n    if (x->b_guiconnect)\n    {\n        pdgui_vmess(\"destroy\", \"^\", x);\n        guiconnect_notarget(x->b_guiconnect, 1000);\n    }\n        /* just in case we're still bound to #A from loading... */\n    while ((x2 = pd_findbyclass(gensym(\"#A\"), text_define_class)))\n        pd_unbind(x2, gensym(\"#A\"));\n}\n\n    /* random helper function to find the nth line in a text buffer */\nstatic int text_nthline(int n, t_atom *vec, int line, int *startp, int *endp)\n{\n    int i, cnt = 0;\n    for (i = 0; i < n; i++)\n    {\n        if (cnt == line)\n        {\n            int j = i;\n            while (j < n && vec[j].a_type != A_SEMI &&\n                vec[j].a_type != A_COMMA)\n                    j++;\n            *startp = i;\n            *endp = j;\n            return (1);\n        }\n        else if (vec[i].a_type == A_SEMI || vec[i].a_type == A_COMMA)\n            cnt++;\n    }\n    return (0);\n}\n\n/* text_define object - text buffer, accessible by other accessor objects */\n\ntypedef struct _text_define\n{\n    t_textbuf x_textbuf;\n    t_outlet *x_out;\n    t_outlet *x_notifyout;\n    t_symbol *x_bindsym;\n    t_scalar *x_scalar;     /* faux scalar (struct text-scalar) to point to */\n    t_gpointer x_gp;        /* pointer to it */\n    t_canvas *x_canvas;     /* owning canvas whose stub we use for x_gp */\n    unsigned char x_keep;   /* whether to embed contents in patch on save */\n} t_text_define;\n\n#define x_ob x_textbuf.b_ob\n#define x_binbuf x_textbuf.b_binbuf\n#define x_canvas x_textbuf.b_canvas\n\nstatic void *text_define_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_text_define *x = (t_text_define *)pd_new(text_define_class);\n    t_symbol *asym = gensym(\"#A\");\n    x->x_keep = 0;\n    x->x_bindsym = &s_;\n    while (argc && argv->a_type == A_SYMBOL &&\n        *argv->a_w.w_symbol->s_name == '-')\n    {\n        if (!strcmp(argv->a_w.w_symbol->s_name, \"-k\"))\n            x->x_keep = 1;\n        else\n        {\n            pd_error(x, \"text define: unknown flag ...\");\n            postatom(argc, argv); endpost();\n        }\n        argc--; argv++;\n    }\n    if (argc && argv->a_type == A_SYMBOL)\n    {\n        pd_bind(&x->x_ob.ob_pd, argv->a_w.w_symbol);\n        x->x_bindsym = argv->a_w.w_symbol;\n        argc--; argv++;\n    }\n    if (argc)\n    {\n        post(\"warning: text define ignoring extra argument: \");\n        postatom(argc, argv); endpost();\n    }\n    textbuf_init(&x->x_textbuf, *x->x_bindsym->s_name ? x->x_bindsym :\n        gensym(\"text\"));\n        /* set up a scalar and a pointer to it that we can output */\n    x->x_scalar = scalar_new(canvas_getcurrent(), gensym(\"pd-text\"));\n    binbuf_free(x->x_scalar->sc_vec[2].w_binbuf);\n    x->x_scalar->sc_vec[2].w_binbuf = x->x_binbuf;\n    x->x_out = outlet_new(&x->x_ob, &s_pointer);\n    x->x_notifyout = outlet_new(&x->x_ob, 0);\n    gpointer_init(&x->x_gp);\n    x->x_canvas = canvas_getcurrent();\n           /* bashily unbind #A -- this would create garbage if #A were\n           multiply bound but we believe in this context it's at most\n           bound to whichever text_define or array was created most recently */\n    asym->s_thing = 0;\n        /* and now bind #A to us to receive following messages in the\n        saved file or copy buffer */\n    pd_bind(&x->x_ob.ob_pd, asym);\n    return (x);\n}\n\nstatic void text_define_clear(t_text_define *x)\n{\n    binbuf_clear(x->x_binbuf);\n    textbuf_senditup(&x->x_textbuf);\n}\n\n    /* from g_traversal.c - maybe put in a header? */\nt_binbuf *pointertobinbuf(t_pd *x, t_gpointer *gp, t_symbol *s,\n    const char *fname);\n\n    /* these are unused; they copy text from this object to and from a text\n        field in a scalar. */\nstatic void text_define_frompointer(t_text_define *x, t_gpointer *gp,\n    t_symbol *s)\n{\n    t_binbuf *b = pointertobinbuf(&x->x_textbuf.b_ob.ob_pd,\n        gp, s, \"text_frompointer\");\n    if (b)\n    {\n        binbuf_clear(x->x_textbuf.b_binbuf);\n        binbuf_add(x->x_textbuf.b_binbuf, binbuf_getnatom(b), binbuf_getvec(b));\n    }\n}\n\nstatic void text_define_topointer(t_text_define *x, t_gpointer *gp, t_symbol *s)\n{\n    t_binbuf *b = pointertobinbuf(&x->x_textbuf.b_ob.ob_pd,\n        gp, s, \"text_topointer\");\n    if (b)\n    {\n        t_gstub *gs = gp->gp_stub;\n        binbuf_clear(b);\n        binbuf_add(b, binbuf_getnatom(x->x_textbuf.b_binbuf),\n            binbuf_getvec(x->x_textbuf.b_binbuf));\n        if (gs->gs_which == GP_GLIST)\n            scalar_redraw(gp->gp_un.gp_scalar, gs->gs_un.gs_glist);\n        else\n        {\n            t_array *owner_array = gs->gs_un.gs_array;\n            while (owner_array->a_gp.gp_stub->gs_which == GP_ARRAY)\n                owner_array = owner_array->a_gp.gp_stub->gs_un.gs_array;\n            scalar_redraw(owner_array->a_gp.gp_un.gp_scalar,\n                owner_array->a_gp.gp_stub->gs_un.gs_glist);\n        }\n    }\n}\n\n    /* bang: output a pointer to a struct containing this text */\nvoid text_define_bang(t_text_define *x)\n{\n    gpointer_setglist(&x->x_gp, x->x_canvas, x->x_scalar);\n    outlet_pointer(x->x_out, &x->x_gp);\n}\n\n    /* set from a list */\nvoid text_define_set(t_text_define *x, t_symbol *s, int argc, t_atom *argv)\n{\n    binbuf_clear(x->x_binbuf);\n    binbuf_restore(x->x_binbuf, argc, argv);\n    textbuf_senditup(&x->x_textbuf);\n}\n\nstatic void text_define_save(t_gobj *z, t_binbuf *bb)\n{\n    t_text_define *x = (t_text_define *)z;\n    binbuf_addv(bb, \"ssff\", &s__X, gensym(\"obj\"),\n        (float)x->x_ob.te_xpix, (float)x->x_ob.te_ypix);\n    binbuf_addbinbuf(bb, x->x_ob.ob_binbuf);\n    binbuf_addsemi(bb);\n    if (x->x_keep)\n    {\n        binbuf_addv(bb, \"ss\", gensym(\"#A\"), gensym(\"set\"));\n        binbuf_addbinbuf(bb, x->x_binbuf);\n        binbuf_addsemi(bb);\n    }\n    obj_saveformat(&x->x_ob, bb);\n}\n\n    /* send a pointer to the scalar that owns this text to\n    whomever is bound to the given symbol */\nstatic void text_define_send(t_text_define *x, t_symbol *s)\n{\n    if (!s->s_thing)\n        pd_error(x, \"text_define_send: %s: no such object\", s->s_name);\n    else\n    {\n        gpointer_setglist(&x->x_gp, x->x_canvas, x->x_scalar);\n        pd_pointer(s->s_thing, &x->x_gp);\n    }\n}\n\ntypedef struct _keyinfo\n{\n    int ki_forward; /* one if forward, -1 if reversed */\n    int ki_onset;   /* number of fields to skip over */\n} t_keyinfo;\n\nstatic int TEXT_SORTCOMPARE(const void *z1, const void *z2, void *zkeyinfo)\n{\n    const t_atom *a1 = *(t_atom **)z1, *a2 = *(t_atom **)z2;\n    t_keyinfo *k = (t_keyinfo *)zkeyinfo;\n    int count;\n        /* advance first line by key onset and react if we run out early */\n    for (count = k->ki_onset; count--; a1++)\n    {\n        if (a1->a_type == A_SEMI || a1->a_type == A_COMMA)\n        {\n                /* if second line runs out early too consider them equal */\n            for (count = k->ki_onset; count--; a2++)\n                if (a2->a_type == A_SEMI || a2->a_type == A_COMMA)\n                    goto equal;\n            return (-k->ki_forward);\n        }\n    }\n    for (count = k->ki_onset; count--; a2++)\n        if (a2->a_type == A_SEMI || a2->a_type == A_COMMA)\n            return (-k->ki_forward);\n        /* compare remaining fields */\n    for (; ; a1++, a2++)\n    {\n        if (a1->a_type == A_SEMI || a1->a_type == A_COMMA)\n        {\n                /* hit end of first line */\n            if (a2->a_type == A_SEMI || a2->a_type == A_COMMA)\n                 goto equal;\n            else return (-k->ki_forward);\n        }\n        else if (a2->a_type == A_SEMI || a2->a_type == A_COMMA)\n            return (k->ki_forward); /* hit end of second line */\n\n            /* otherwise if they're different return something, and\n            if not proceed to next field */\n        else if (a1->a_type == A_FLOAT)\n        {\n            if (a2->a_type == A_FLOAT)\n            {\n                if (a1->a_w.w_float < a2->a_w.w_float)\n                    return (-k->ki_forward);\n                else if (a1->a_w.w_float > a2->a_w.w_float)\n                    return (k->ki_forward);\n            }\n            else return (-k->ki_forward);\n        }\n        else if (a1->a_type == A_SYMBOL)\n        {\n            if (a2->a_type == A_SYMBOL)\n            {\n                int z = strcmp(a1->a_w.w_symbol->s_name,\n                    a2->a_w.w_symbol->s_name);\n                if (z)\n                    return (z * k->ki_forward);\n            }\n            else return (k->ki_forward);\n        }\n    }\nequal:\n    /* ran out of both lines at same time, so we're \"equal\".\n    in this case compare pointers so that \"equal\" lines (which\n    might not be identical because of a nonzero onset) stay in the\n    same order as before. */\n    if (a1 < a2)\n        return (-1);\n    else return (1);\n}\n\n\n/* 'qsort_r' is a GNU extension and 'qsort_s' is part of C11.\n * Both are not available in Emscripten, Android or older MSVC versions.\n * 'stupid_sortcompare' is thread-safe but not reentrant.\n */\n\n#if STUPID_SORT\nstatic PERTHREAD void *stupid_zkeyinfo;\nstatic int stupid_sortcompare(const void *z1, const void *z2)\n{\n    return (text_sortcompare(z1, z2, stupid_zkeyinfo));\n}\n#endif\n\n    /* sort the contents */\nstatic void text_define_sort(t_text_define *x, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    int nlines = 0, unique = 0,  natom = binbuf_getnatom(x->x_binbuf), i,\n        thisline, startline;\n    t_atom *vec = binbuf_getvec(x->x_binbuf), **sortbuf, *a1, *a2;\n    t_binbuf *newb;\n    t_keyinfo k;\n    k.ki_forward = 1;\n    k.ki_onset = 0;\n    while (argc && argv->a_type == A_SYMBOL &&\n        *argv->a_w.w_symbol->s_name == '-')\n    {\n        if (!strcmp(argv->a_w.w_symbol->s_name, \"-u\"))\n            unique = 1;\n        else if (!strcmp(argv->a_w.w_symbol->s_name, \"-r\"))\n            k.ki_forward = -1;\n        else if (!strcmp(argv->a_w.w_symbol->s_name, \"-k\")  && argc > 1\n            && argv[1].a_type == A_FLOAT)\n        {\n            if ((k.ki_onset = argv[1].a_w.w_float) < 0)\n                k.ki_onset = 0;\n            argc--; argv++;\n        }\n        else\n        {\n            pd_error(x, \"text define sort: unknown flag ...\");\n            postatom(argc, argv); endpost();\n        }\n        argc--; argv++;\n    }\n    if (argc)\n    {\n        post(\"warning: text define sort ignoring extra argument: \");\n        postatom(argc, argv); endpost();\n    }\n    if (!natom)\n        return;\n            /* last thing in buffer should be a terminator */\n    if (vec[natom-1].a_type != A_SEMI &&\n        vec[natom-1].a_type != A_COMMA)\n            binbuf_addsemi(x->x_binbuf),\n                vec = binbuf_getvec(x->x_binbuf),\n                    natom = binbuf_getnatom(x->x_binbuf),\n                        nlines++;\n    for (i = nlines = 0; i < natom; i++)\n        if (vec[i].a_type == A_SEMI || vec[i].a_type == A_COMMA)\n            nlines++;\n    sortbuf = (t_atom **)getbytes(nlines * sizeof(*sortbuf));\n    for (i = thisline = 0, startline = 1; i < natom; i++)\n    {\n        if (startline)\n        {\n            if (thisline >= nlines)\n                bug(\"text_define_sort\");\n            sortbuf[thisline++] = vec+i;\n        }\n        startline = (vec[i].a_type == A_SEMI || vec[i].a_type == A_COMMA);\n    }\n#if STUPID_SORT\n    stupid_zkeyinfo = &k;\n    qsort(sortbuf, nlines, sizeof(*sortbuf), stupid_sortcompare);\n#else\n    QSORT_R(sortbuf, nlines, sizeof(*sortbuf), text_sortcompare, &k);\n#endif /* STUPID_SORT */\n    newb = binbuf_new();\n    for (thisline = 0; thisline < nlines; thisline++)\n    {\n        if (unique && thisline > 0)   /* check for duplicates */\n        {\n            for (a1 = sortbuf[thisline-1], a2 = sortbuf[thisline]; ; a1++, a2++)\n            {\n                if (a1->a_type == A_SEMI || a1->a_type == A_COMMA)\n                {\n                    if (a1->a_type == a2->a_type)\n                        goto skipit; /* duplicate line, don't copy */\n                    else goto doit;\n                }\n                else if (a1->a_type != a2->a_type ||\n                    (a1->a_type == A_FLOAT &&\n                        a1->a_w.w_float != a2->a_w.w_float) ||\n                    (a1->a_type == A_SYMBOL &&\n                        a1->a_w.w_symbol != a2->a_w.w_symbol))\n                            goto doit;\n            }\n        }\n    doit:\n        for (i = 0, a1 = sortbuf[thisline];\n            a1->a_type != A_SEMI && a1->a_type != A_COMMA; i++, a1++)\n                ;\n        binbuf_add(newb, i+1, sortbuf[thisline]);\n    skipit: ;\n    }\n    binbuf_free(x->x_binbuf);\n    x->x_scalar->sc_vec[2].w_binbuf = x->x_binbuf = newb;\n    freebytes(sortbuf, nlines * sizeof(*sortbuf));\n    textbuf_senditup(&x->x_textbuf);\n}\n\n    /* notification from GUI that we've been updated */\nstatic void text_define_notify(t_text_define *x)\n{\n    outlet_anything(x->x_notifyout, gensym(\"updated\"), 0, 0);\n    textbuf_senditup(&x->x_textbuf);\n}\n\nstatic void text_define_free(t_text_define *x)\n{\n    x->x_binbuf = 0; /* prevent double deletion */\n    textbuf_free(&x->x_textbuf);\n    if (x->x_bindsym != &s_)\n        pd_unbind(&x->x_ob.ob_pd, x->x_bindsym);\n    gpointer_unset(&x->x_gp);\n    /* deleting the scalar will automatically free the binbuf */\n    pd_free(&x->x_scalar->sc_gobj.g_pd);\n    x->x_canvas->gl_valid = ++glist_valid; /* invalidate pointers */\n}\n\n/* ---  text_client - common code for objects that refer to text buffers -- */\n\ntypedef struct _text_client\n{\n    t_object tc_obj;\n    t_symbol *tc_sym;\n    t_gpointer tc_gp;\n    t_symbol *tc_struct;\n    t_symbol *tc_field;\n} t_text_client;\n\n    /* parse buffer-finding arguments */\nstatic void text_client_argparse(t_text_client *x, int *argcp, t_atom **argvp,\n    char *name)\n{\n    int argc = *argcp;\n    t_atom *argv = *argvp;\n    x->tc_sym = x->tc_struct = x->tc_field = 0;\n    gpointer_init(&x->tc_gp);\n    if (argc && argv->a_type == A_SYMBOL &&\n        !strcmp(argv->a_w.w_symbol->s_name, \"-s\"))\n    {\n        if (argc < 3 || argv[1].a_type != A_SYMBOL ||\n            argv[2].a_type != A_SYMBOL)\n                pd_error(x, \"%s: '-s' needs a struct and field name\", name);\n        else\n        {\n            x->tc_struct = canvas_makebindsym(argv[1].a_w.w_symbol);\n            x->tc_field = argv[2].a_w.w_symbol;\n            argc -= 3; argv += 3;\n        }\n    }\n    else if (argc && argv->a_type == A_SYMBOL)\n    {\n        x->tc_sym = argv->a_w.w_symbol;\n        argc--; argv++;\n    }\n    *argcp = argc;\n    *argvp = argv;\n}\n\n    /* find the binbuf for this object.  This should be reusable for other\n    objects.  Prints an error  message and returns 0 on failure. */\nstatic t_binbuf *text_client_getbuf(t_text_client *x)\n{\n    if (x->tc_sym)       /* named text object */\n    {\n        t_textbuf *y = (t_textbuf *)pd_findbyclass(x->tc_sym,\n            text_define_class);\n        if (y)\n            return (y->b_binbuf);\n        else\n        {\n            pd_error(x, \"text: couldn't find text buffer '%s'\",\n                x->tc_sym->s_name);\n            return (0);\n        }\n    }\n    else if (x->tc_struct)   /* by pointer */\n    {\n        t_template *template = template_findbyname(x->tc_struct);\n        t_gstub *gs = x->tc_gp.gp_stub;\n        t_word *vec;\n        int onset, type;\n        t_symbol *arraytype;\n        if (!template)\n        {\n            pd_error(x, \"text: couldn't find struct %s\", x->tc_struct->s_name);\n            return (0);\n        }\n        if (!gpointer_check(&x->tc_gp, 0))\n        {\n            pd_error(x, \"text: stale or empty pointer\");\n            return (0);\n        }\n        if (gs->gs_which == GP_ARRAY)\n            vec = x->tc_gp.gp_un.gp_w;\n        else vec = x->tc_gp.gp_un.gp_scalar->sc_vec;\n\n        if (!template_find_field(template,\n            x->tc_field, &onset, &type, &arraytype))\n        {\n            pd_error(x, \"text: no field named %s\", x->tc_field->s_name);\n            return (0);\n        }\n        if (type != DT_TEXT)\n        {\n            pd_error(x, \"text: field %s not of type text\", x->tc_field->s_name);\n            return (0);\n        }\n        return (*(t_binbuf **)(((char *)vec) + onset));\n    }\n    else return (0);    /* shouldn't happen */\n}\n\nstatic  void text_client_senditup(t_text_client *x)\n{\n    if (x->tc_sym)       /* named text object */\n    {\n        t_textbuf *y = (t_textbuf *)pd_findbyclass(x->tc_sym,\n            text_define_class);\n        if (y)\n            textbuf_senditup(y);\n        else bug(\"text_client_senditup\");\n    }\n    else if (x->tc_struct)   /* by pointer */\n    {\n        t_template *template = template_findbyname(x->tc_struct);\n        t_gstub *gs = x->tc_gp.gp_stub;\n        if (!template)\n        {\n            pd_error(x, \"text: couldn't find struct %s\", x->tc_struct->s_name);\n            return;\n        }\n        if (!gpointer_check(&x->tc_gp, 0))\n        {\n            pd_error(x, \"text: stale or empty pointer\");\n            return;\n        }\n        if (gs->gs_which == GP_GLIST)\n            scalar_redraw(x->tc_gp.gp_un.gp_scalar, gs->gs_un.gs_glist);\n        else\n        {\n            t_array *owner_array = gs->gs_un.gs_array;\n            while (owner_array->a_gp.gp_stub->gs_which == GP_ARRAY)\n                owner_array = owner_array->a_gp.gp_stub->gs_un.gs_array;\n            scalar_redraw(owner_array->a_gp.gp_un.gp_scalar,\n                owner_array->a_gp.gp_stub->gs_un.gs_glist);\n        }\n    }\n}\n\nstatic void text_client_free(t_text_client *x)\n{\n    gpointer_unset(&x->tc_gp);\n}\n\n/* ------- text_get object - output all or part of nth lines ----------- */\nt_class *text_get_class;\n\ntypedef struct _text_get\n{\n    t_text_client x_tc;\n    t_outlet *x_out1;       /* list */\n    t_outlet *x_out2;       /* 1 if comma terminated, 0 if semi, 2 if none */\n    t_float x_f1;           /* field number, -1 for whole line */\n    t_float x_f2;           /* number of fields */\n} t_text_get;\n\n#define x_obj x_tc.tc_obj\n#define x_sym x_tc.tc_sym\n#define x_gp x_tc.tc_gp\n#define x_struct x_tc.tc_struct\n#define x_field x_tc.tc_field\n\nstatic void *text_get_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_text_get *x = (t_text_get *)pd_new(text_get_class);\n    x->x_out1 = outlet_new(&x->x_obj, &s_list);\n    x->x_out2 = outlet_new(&x->x_obj, &s_float);\n    floatinlet_new(&x->x_obj, &x->x_f1);\n    floatinlet_new(&x->x_obj, &x->x_f2);\n    x->x_f1 = -1;\n    x->x_f2 = 1;\n    text_client_argparse(&x->x_tc, &argc, &argv, \"text get\");\n    if (argc)\n    {\n        if (argv->a_type == A_FLOAT)\n            x->x_f1 = argv->a_w.w_float;\n        else\n        {\n            post(\"text get: can't understand field number\");\n            postatom(argc, argv); endpost();\n        }\n        argc--; argv++;\n    }\n    if (argc)\n    {\n        if (argv->a_type == A_FLOAT)\n            x->x_f2 = argv->a_w.w_float;\n        else\n        {\n            post(\"text get: can't understand field count\");\n            postatom(argc, argv); endpost();\n        }\n        argc--; argv++;\n    }\n    if (argc)\n    {\n        post(\"warning: text get ignoring extra argument: \");\n        postatom(argc, argv); endpost();\n    }\n    if (x->x_struct)\n        pointerinlet_new(&x->x_obj, &x->x_gp);\n    else symbolinlet_new(&x->x_obj, &x->x_tc.tc_sym);\n    return (x);\n}\n\nstatic void text_get_float(t_text_get *x, t_floatarg f)\n{\n    t_binbuf *b = text_client_getbuf(&x->x_tc);\n    int start, end, n, startfield, nfield;\n    t_atom *vec;\n    if (!b)\n       return;\n    vec = binbuf_getvec(b);\n    n = binbuf_getnatom(b);\n    startfield = x->x_f1;\n    nfield = x->x_f2;\n    if (text_nthline(n, vec, f, &start, &end))\n    {\n        int outc = end - start, k;\n        t_atom *outv;\n        if (x->x_f1 < 0)    /* negative start field for whole line */\n        {\n                /* tell us what terminated the line (semi or comma) */\n            outlet_float(x->x_out2, (end < n && vec[end].a_type == A_COMMA));\n            ALLOCA(t_atom, outv, outc, TEXT_NGETBYTE);\n            for (k = 0; k < outc; k++)\n                outv[k] = vec[start+k];\n            outlet_list(x->x_out1, 0, outc, outv);\n            FREEA(t_atom, outv, outc, TEXT_NGETBYTE);\n        }\n        else if (startfield + nfield > outc)\n            pd_error(x, \"text get: field request (%d %d) out of range\",\n                startfield, nfield);\n        else if (nfield < 0)\n            pd_error(x, \"text get: bad field count (%d)\", nfield);\n        else\n        {\n            ALLOCA(t_atom, outv, nfield, TEXT_NGETBYTE);\n            for (k = 0; k < nfield; k++)\n                outv[k] = vec[(start+startfield)+k];\n            outlet_list(x->x_out1, 0, nfield, outv);\n            FREEA(t_atom, outv, nfield, TEXT_NGETBYTE);\n        }\n    }\n    else if (x->x_f1 < 0)   /* whole line but out of range: empty list and 2 */\n    {\n        outlet_float(x->x_out2, 2);         /* 2 for out of range */\n        outlet_list(x->x_out1, 0, 0, 0);    /* ... and empty list */\n    }\n}\n\n/* --------- text_set object - replace all or part of nth line ----------- */\ntypedef struct _text_set\n{\n    t_text_client x_tc;\n    t_float x_f1;           /* line number */\n    t_float x_f2;           /* field number, -1 for whole line */\n} t_text_set;\n\nt_class *text_set_class;\n\nstatic void *text_set_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_text_set *x = (t_text_set *)pd_new(text_set_class);\n    floatinlet_new(&x->x_obj, &x->x_f1);\n    floatinlet_new(&x->x_obj, &x->x_f2);\n    x->x_f1 = 0;\n    x->x_f2 = -1;\n    text_client_argparse(&x->x_tc, &argc, &argv, \"text set\");\n    if (argc)\n    {\n        if (argv->a_type == A_FLOAT)\n            x->x_f1 = argv->a_w.w_float;\n        else\n        {\n            post(\"text set: can't understand line number\");\n            postatom(argc, argv); endpost();\n        }\n        argc--; argv++;\n    }\n    if (argc)\n    {\n        if (argv->a_type == A_FLOAT)\n            x->x_f2 = argv->a_w.w_float;\n        else\n        {\n            post(\"text set: can't understand field number\");\n            postatom(argc, argv); endpost();\n        }\n        argc--; argv++;\n    }\n    if (argc)\n    {\n        post(\"warning: text set ignoring extra argument: \");\n        postatom(argc, argv); endpost();\n    }\n    if (x->x_struct)\n        pointerinlet_new(&x->x_obj, &x->x_gp);\n    else symbolinlet_new(&x->x_obj, &x->x_tc.tc_sym);\n    return (x);\n}\n\nstatic void text_set_list(t_text_set *x,\n    t_symbol *s, int argc, t_atom *argv)\n{\n    t_binbuf *b = text_client_getbuf(&x->x_tc);\n    int start, end, n, fieldno = x->x_f2, i,\n            /* check for overflow in this conversion: */\n        lineno = (x->x_f1 > (double)0x7fffffff ? 0x7fffffff : (int)x->x_f1);\n    t_atom *vec;\n    if (!b)\n       return;\n    vec = binbuf_getvec(b);\n    n = binbuf_getnatom(b);\n    if (lineno < 0)\n    {\n        pd_error(x, \"text set: line number (%d) < 0\", lineno);\n        return;\n    }\n    if (text_nthline(n, vec, lineno, &start, &end))\n    {\n        if (fieldno < 0)\n        {\n            if (end - start != argc)  /* grow or shrink */\n            {\n                int oldn = n;\n                n = n + (argc - (end-start));\n                if (n > oldn)\n                    (void)binbuf_resize(b, n);\n                vec = binbuf_getvec(b);\n                memmove(&vec[start + argc], &vec[end],\n                    sizeof(*vec) * (oldn - end));\n                if (n < oldn)\n                {\n                    (void)binbuf_resize(b, n);\n                    vec = binbuf_getvec(b);\n                }\n            }\n        }\n        else\n        {\n            if (fieldno >= end - start)\n            {\n                pd_error(x, \"text set: field number (%d) past end of line\",\n                    fieldno);\n                return;\n            }\n            if (fieldno + argc > end - start)\n                argc = (end - start) - fieldno;\n            start += fieldno;\n        }\n    }\n    else if (fieldno < 0)  /* if line number too high just append to end */\n    {\n        int addsemi = (n && vec[n-1].a_type != A_SEMI &&\n            vec[n-1].a_type != A_COMMA), newsize = n + addsemi + argc + 1;\n        (void)binbuf_resize(b, newsize);\n        vec = binbuf_getvec(b);\n        if (addsemi)\n            SETSEMI(&vec[n]);\n        SETSEMI(&vec[newsize-1]);\n        start = n+addsemi;\n    }\n    else\n    {\n        post(\"text set: %d: line number out of range\", lineno);\n        return;\n    }\n    for (i = 0; i < argc; i++)\n    {\n        if (argv[i].a_type == A_POINTER)\n            SETSYMBOL(&vec[start+i], gensym(\"(pointer)\"));\n        else vec[start+i] = argv[i];\n    }\n    text_client_senditup(&x->x_tc);\n}\n\n/* --------- text_insert object - insert a line ----------- */\ntypedef struct _text_insert\n{\n    t_text_client x_tc;\n    t_float x_f1;           /* line number */\n} t_text_insert;\n\nt_class *text_insert_class;\n\nstatic void *text_insert_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_text_insert *x = (t_text_insert *)pd_new(text_insert_class);\n    floatinlet_new(&x->x_obj, &x->x_f1);\n    x->x_f1 = 0;\n    text_client_argparse(&x->x_tc, &argc, &argv, \"text insert\");\n    if (argc)\n    {\n        if (argv->a_type == A_FLOAT)\n            x->x_f1 = argv->a_w.w_float;\n        else\n        {\n            post(\"text insert: can't understand line number\");\n            postatom(argc, argv); endpost();\n        }\n        argc--; argv++;\n    }\n    if (argc)\n    {\n        post(\"warning: text insert ignoring extra argument: \");\n        postatom(argc, argv); endpost();\n    }\n    if (x->x_struct)\n        pointerinlet_new(&x->x_obj, &x->x_gp);\n    else symbolinlet_new(&x->x_obj, &x->x_tc.tc_sym);\n    return (x);\n}\n\nstatic void text_insert_list(t_text_insert *x,\n    t_symbol *s, int argc, t_atom *argv)\n{\n    t_binbuf *b = text_client_getbuf(&x->x_tc);\n    int start, end, n, nwas, i,\n         lineno = (x->x_f1 > (double)0x7fffffff ? 0x7fffffff : (int)x->x_f1);\n\n    t_atom *vec;\n    if (!b)\n       return;\n    if (lineno < 0)\n    {\n        pd_error(x, \"text insert: line number (%d) < 0\", lineno);\n        return;\n    }\n    nwas = binbuf_getnatom(b);\n    if (!text_nthline(nwas, binbuf_getvec(b), lineno, &start, &end))\n        start = nwas;\n    (void)binbuf_resize(b, (n = nwas + argc + 1));\n    vec = binbuf_getvec(b);\n    if (start < n)\n        memmove(&vec[start+(argc+1)], &vec[start], sizeof(*vec) * (nwas-start));\n    for (i = 0; i < argc; i++)\n    {\n        if (argv[i].a_type == A_POINTER)\n            SETSYMBOL(&vec[start+i], gensym(\"(pointer)\"));\n        else vec[start+i] = argv[i];\n    }\n    SETSEMI(&vec[start+argc]);\n    text_client_senditup(&x->x_tc);\n}\n\n/* --------- text_delete object - delete nth line ----------- */\ntypedef struct _text_delete\n{\n    t_text_client x_tc;\n} t_text_delete;\n\nt_class *text_delete_class;\n\nstatic void *text_delete_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_text_delete *x = (t_text_delete *)pd_new(text_delete_class);\n    text_client_argparse(&x->x_tc, &argc, &argv, \"text delete\");\n    if (argc)\n    {\n        post(\"warning: text delete ignoring extra argument: \");\n        postatom(argc, argv); endpost();\n    }\n    if (x->x_struct)\n        pointerinlet_new(&x->x_obj, &x->x_gp);\n    else symbolinlet_new(&x->x_obj, &x->x_tc.tc_sym);\n    return (x);\n}\n\nstatic void text_delete_float(t_text_delete *x, t_floatarg f)\n{\n    t_binbuf *b = text_client_getbuf(&x->x_tc);\n    int start, end, n,\n         lineno = (f > (double)0x7fffffff ? 0x7fffffff : f);\n    t_atom *vec;\n    if (!b)\n       return;\n    vec = binbuf_getvec(b);\n    n = binbuf_getnatom(b);\n    if (lineno < 0)\n        binbuf_clear(b);\n    else if (text_nthline(n, vec, lineno, &start, &end))\n    {\n        if (end < n)\n            end++;\n        memmove(&vec[start], &vec[end], sizeof(*vec) * (n - end));\n        (void)binbuf_resize(b, n - (end - start));\n    }\n    else\n    {\n        post(\"text delete: %d: line number out of range\", lineno);\n        return;\n    }\n    text_client_senditup(&x->x_tc);\n}\n\n/* ---------------- text_size object - output number of lines -------------- */\nt_class *text_size_class;\n\ntypedef struct _text_size\n{\n    t_text_client x_tc;\n    t_outlet *x_out1;       /* float */\n} t_text_size;\n\nstatic void *text_size_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_text_size *x = (t_text_size *)pd_new(text_size_class);\n    x->x_out1 = outlet_new(&x->x_obj, &s_float);\n    text_client_argparse(&x->x_tc, &argc, &argv, \"text size\");\n    if (argc)\n    {\n        post(\"warning: text size ignoring extra argument: \");\n        postatom(argc, argv); endpost();\n    }\n    if (x->x_struct)\n        pointerinlet_new(&x->x_obj, &x->x_gp);\n    else symbolinlet_new(&x->x_obj, &x->x_tc.tc_sym);\n    return (x);\n}\n\nstatic void text_size_bang(t_text_size *x)\n{\n    t_binbuf *b = text_client_getbuf(&x->x_tc);\n    int n, i, cnt = 0;\n    t_atom *vec;\n    if (!b)\n       return;\n    vec = binbuf_getvec(b);\n    n = binbuf_getnatom(b);\n    for (i = 0; i < n; i++)\n    {\n        if (vec[i].a_type == A_SEMI || vec[i].a_type == A_COMMA)\n            cnt++;\n    }\n    if (n && vec[n-1].a_type != A_SEMI && vec[n-1].a_type != A_COMMA)\n        cnt++;\n    outlet_float(x->x_out1, cnt);\n}\n\nstatic void text_size_float(t_text_size *x, t_floatarg f)\n{\n    t_binbuf *b = text_client_getbuf(&x->x_tc);\n    int start, end, n;\n    t_atom *vec;\n    if (!b)\n       return;\n    vec = binbuf_getvec(b);\n    n = binbuf_getnatom(b);\n    if (text_nthline(n, vec, f, &start, &end))\n        outlet_float(x->x_out1, end-start);\n    else outlet_float(x->x_out1, -1);\n}\n\n/* ---------------- text_tolist object - output text as a list ----------- */\nt_class *text_tolist_class;\n\n#define t_text_tolist t_text_client\n\nstatic void *text_tolist_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_text_tolist *x = (t_text_tolist *)pd_new(text_tolist_class);\n    outlet_new(&x->tc_obj, &s_list);\n    text_client_argparse(x, &argc, &argv, \"text tolist\");\n    if (argc)\n    {\n        post(\"warning: text tolist ignoring extra argument: \");\n        postatom(argc, argv); endpost();\n    }\n    if (x->tc_struct)\n        pointerinlet_new(&x->tc_obj, &x->tc_gp);\n    else symbolinlet_new(&x->tc_obj, &x->tc_sym);\n    return (x);\n}\n\nstatic void text_tolist_bang(t_text_tolist *x)\n{\n    t_binbuf *b = text_client_getbuf(x), *b2;\n    if (!b)\n       return;\n    b2 = binbuf_new();\n    binbuf_addbinbuf(b2, b);\n    outlet_list(x->tc_obj.ob_outlet, 0, binbuf_getnatom(b2), binbuf_getvec(b2));\n    binbuf_free(b2);\n}\n\n/* ------------- text_fromlist object - set text from a list -------- */\nt_class *text_fromlist_class;\n\n#define t_text_fromlist t_text_client\n\nstatic void *text_fromlist_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_text_fromlist *x = (t_text_fromlist *)pd_new(text_fromlist_class);\n    text_client_argparse(x, &argc, &argv, \"text fromlist\");\n    if (argc)\n    {\n        post(\"warning: text fromlist ignoring extra argument: \");\n        postatom(argc, argv); endpost();\n    }\n    if (x->tc_struct)\n        pointerinlet_new(&x->tc_obj, &x->tc_gp);\n    else symbolinlet_new(&x->tc_obj, &x->tc_sym);\n    return (x);\n}\n\nstatic void text_fromlist_list(t_text_fromlist *x,\n    t_symbol *s, int argc, t_atom *argv)\n{\n    t_binbuf *b = text_client_getbuf(x);\n    if (!b)\n       return;\n    binbuf_clear(b);\n    binbuf_restore(b, argc, argv);\n    text_client_senditup(x);\n}\n\n/* ---- text_search object - output index of line(s) matching criteria ---- */\n\nt_class *text_search_class;\n\n    /* relations we can test when searching: */\n#define KB_EQ 0     /*  equal */\n#define KB_GT 1     /* > (etc..) */\n#define KB_GE 2\n#define KB_LT 3\n#define KB_LE 4\n#define KB_NEAR 5   /* anything matches but closer is better */\n\ntypedef struct _key\n{\n    int k_field;\n    int k_binop;\n} t_key;\n\ntypedef struct _text_search\n{\n    t_text_client x_tc;\n    t_outlet *x_out1;       /* line indices */\n    int x_nkeys;\n    int x_onset;        /* first line to include in search */\n    int x_range;        /* max number of lines to search */\n    t_key *x_keyvec;\n} t_text_search;\n\nstatic void *text_search_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_text_search *x = (t_text_search *)pd_new(text_search_class);\n    int i, key, nkey, nextop;\n    x->x_out1 = outlet_new(&x->x_obj, &s_list);\n    text_client_argparse(&x->x_tc, &argc, &argv, \"text search\");\n    for (i = nkey = 0; i < argc; i++)\n        if (argv[i].a_type == A_FLOAT)\n            nkey++;\n    if (nkey == 0)\n        nkey = 1;\n    x->x_nkeys = nkey;\n    x->x_onset = 0;\n    x->x_range = 0x7fffffff;\n    x->x_keyvec = (t_key *)getbytes(nkey * sizeof(*x->x_keyvec));\n    if (!argc)\n        x->x_keyvec[0].k_field = 0, x->x_keyvec[0].k_binop = KB_EQ;\n    else for (i = key = 0, nextop = -1; i < argc; i++)\n    {\n        if (argv[i].a_type == A_FLOAT)\n        {\n            x->x_keyvec[key].k_field =\n                (argv[i].a_w.w_float > 0 ? argv[i].a_w.w_float : 0);\n            x->x_keyvec[key].k_binop = (nextop >= 0 ? nextop : KB_EQ);\n            nextop = -1;\n            key++;\n        }\n        else\n        {\n            const char *s = argv[i].a_w.w_symbol->s_name;\n            if (nextop >= 0)\n                pd_error(x,\n                    \"text search: extra operation argument ignored: %s\", s);\n            else if (!strcmp(argv[i].a_w.w_symbol->s_name, \">\"))\n                nextop = KB_GT;\n            else if (!strcmp(argv[i].a_w.w_symbol->s_name, \">=\"))\n                nextop = KB_GE;\n            else if (!strcmp(argv[i].a_w.w_symbol->s_name, \"<\"))\n                nextop = KB_LT;\n            else if (!strcmp(argv[i].a_w.w_symbol->s_name, \"<=\"))\n                nextop = KB_LE;\n            else if (!strcmp(argv[i].a_w.w_symbol->s_name, \"near\"))\n                nextop = KB_NEAR;\n            else pd_error(x,\n                    \"text search: unknown operation argument: %s\", s);\n        }\n    }\n    if (x->x_struct)\n        pointerinlet_new(&x->x_obj, &x->x_gp);\n    else symbolinlet_new(&x->x_obj, &x->x_tc.tc_sym);\n    return (x);\n}\n\nstatic void text_search_list(t_text_search *x,\n    t_symbol *s, int argc, t_atom *argv)\n{\n    t_binbuf *b = text_client_getbuf(&x->x_tc);\n    int i, n, lineno, bestline = -1, beststart=-1, bestn, thisstart,\n        nkeys = x->x_nkeys, failed = 0;\n    t_atom *vec;\n    if (!b)\n       return;\n    if (argc < nkeys)\n    {\n        pd_error(x, \"need %d keys, only got %d in list\",\n            nkeys, argc);\n    }\n    vec = binbuf_getvec(b);\n    n = binbuf_getnatom(b);\n    if (nkeys < 1)\n        bug(\"text_search\");\n    for (i = lineno = thisstart = 0; i < n; i++)\n    {\n        if (vec[i].a_type == A_SEMI || vec[i].a_type == A_COMMA || i == n-1)\n        {\n            int thisn, j, field, binop;\n            if (lineno < x->x_onset)\n                goto nomatch;\n            if (lineno >= x->x_onset + x->x_range)\n                break;\n            thisn = i - thisstart;\n            field = x->x_keyvec[0].k_field;\n            binop = x->x_keyvec[0].k_binop;\n                /* do we match? */\n            for (j = 0; j < argc; )\n            {\n                if (field >= thisn ||\n                    vec[thisstart+field].a_type != argv[j].a_type)\n                        goto nomatch;\n                if (argv[j].a_type == A_FLOAT)      /* arg is a float */\n                {\n                    switch (binop)\n                    {\n                        case KB_EQ:\n                            if (vec[thisstart+field].a_w.w_float !=\n                                argv[j].a_w.w_float)\n                                    goto nomatch;\n                        break;\n                        case KB_GT:\n                            if (vec[thisstart+field].a_w.w_float <=\n                                argv[j].a_w.w_float)\n                                    goto nomatch;\n                        break;\n                        case KB_GE:\n                            if (vec[thisstart+field].a_w.w_float <\n                                argv[j].a_w.w_float)\n                                    goto nomatch;\n                        break;\n                        case KB_LT:\n                            if (vec[thisstart+field].a_w.w_float >=\n                                argv[j].a_w.w_float)\n                                    goto nomatch;\n                        break;\n                        case KB_LE:\n                            if (vec[thisstart+field].a_w.w_float >\n                                argv[j].a_w.w_float)\n                                    goto nomatch;\n                        break;\n                            /* the other possibility ('near') never fails */\n                    }\n                }\n                else                                /* arg is a symbol */\n                {\n                    if (binop != KB_EQ)\n                    {\n                        if (!failed)\n                        {\n                            pd_error(x,\n                    \"text search (%s): only exact matches allowed for symbols\",\n                                argv[j].a_w.w_symbol->s_name);\n                            failed = 1;\n                        }\n                        goto nomatch;\n                    }\n                    if (vec[thisstart+field].a_w.w_symbol !=\n                        argv[j].a_w.w_symbol)\n                            goto nomatch;\n                }\n                if (++j >= nkeys)    /* if at last key just increment field */\n                    field++;\n                else field = x->x_keyvec[j].k_field,    /* else next key */\n                        binop = x->x_keyvec[j].k_binop;\n            }\n                /* the line matches.  Now, if there is a previous match, are\n                we better than it? */\n            if (bestline >= 0)\n            {\n                field = x->x_keyvec[0].k_field;\n                binop = x->x_keyvec[0].k_binop;\n                for (j = 0; j < argc; )\n                {\n                    if (field >= thisn\n                        || vec[thisstart+field].a_type != argv[j].a_type)\n                            bug(\"text search 2\");\n                    if (argv[j].a_type == A_FLOAT)      /* arg is a float */\n                    {\n                        float thisv = vec[thisstart+field].a_w.w_float,\n                            bestv = (beststart >= 0 ?\n                                vec[beststart+field].a_w.w_float : -1e20);\n                        switch (binop)\n                        {\n                            case KB_GT:\n                            case KB_GE:\n                                if (thisv < bestv)\n                                    goto replace;\n                                else if (thisv > bestv)\n                                    goto nomatch;\n                            break;\n                            case KB_LT:\n                            case KB_LE:\n                                if (thisv > bestv)\n                                    goto replace;\n                                else if (thisv < bestv)\n                                    goto nomatch;\n                            break;\n                            case KB_NEAR:\n                                if (thisv >= argv[j].a_w.w_float &&\n                                    bestv >= argv[j].a_w.w_float)\n                                {\n                                    if (thisv < bestv)\n                                        goto replace;\n                                    else if (thisv > bestv)\n                                        goto nomatch;\n                                }\n                                else if (thisv <= argv[j].a_w.w_float &&\n                                    bestv <= argv[j].a_w.w_float)\n                                {\n                                    if (thisv > bestv)\n                                        goto replace;\n                                    else if (thisv < bestv)\n                                        goto nomatch;\n                                }\n                                else\n                                {\n                                    float d1 = thisv - argv[j].a_w.w_float,\n                                        d2 = bestv - argv[j].a_w.w_float;\n                                    if (d1 < 0)\n                                        d1 = -d1;\n                                    if (d2 < 0)\n                                        d2 = -d2;\n\n                                    if (d1 < d2)\n                                        goto replace;\n                                    else if (d1 > d2)\n                                        goto nomatch;\n                                }\n                            break;\n                                /* the other possibility ('=') never decides */\n                        }\n                    }\n                    if (++j >= nkeys)    /* last key - increment field */\n                        field++;\n                    else field = x->x_keyvec[j].k_field,    /* else next key */\n                            binop = x->x_keyvec[j].k_binop;\n                }\n                goto nomatch;   /* a tie - keep the old one */\n            replace:\n                bestline = lineno, beststart = thisstart, bestn = thisn;\n            }\n                /* no previous match so we're best */\n            else bestline = lineno, beststart = thisstart, bestn = thisn;\n        nomatch:\n            lineno++;\n            thisstart = i+1;\n        }\n    }\n    outlet_float(x->x_out1, bestline);\n}\n\nstatic void text_search_range(t_text_search *x, t_floatarg onset,\n    t_floatarg range)\n{\n    x->x_onset = (onset >= 0x7fffffff ? 0x7ffffff : (onset < 0 ? 0 : onset));\n    x->x_range = (range >= 0x7fffffff ? 0x7ffffff : (range < 0 ? 0 : range));\n}\n\n/* ---------------- text_sequence object - sequencer ----------- */\nt_class *text_sequence_class;\n\ntypedef struct _text_sequence\n{\n    t_text_client x_tc;\n    t_outlet *x_mainout;    /* outlet for lists, zero if \"global\" */\n    t_outlet *x_waitout;    /* outlet for wait times, zero if we never wait */\n    t_outlet *x_endout;    /* bang when hit end */\n    int x_onset;\n    int x_argc;\n    t_atom *x_argv;\n    t_symbol *x_waitsym;    /* symbol to initiate wait, zero if none */\n    int x_waitargc;         /* how many leading numbers to use for waiting */\n    t_clock *x_clock;       /* callback for auto mode */\n    t_float x_nextdelay;\n    t_symbol *x_lastto;     /* destination symbol if we're after a comma */\n    unsigned char x_eaten;  /* true if we've eaten leading numbers already */\n    unsigned char x_loop;   /* true if we can send multiple lines */\n    unsigned char x_auto;   /* set timer when we hit single-number time list */\n} t_text_sequence;\n\nstatic void text_sequence_tick(t_text_sequence *x);\nstatic void text_sequence_tempo(t_text_sequence *x,\n    t_symbol *unitname, t_floatarg tempo);\nvoid parsetimeunits(void *x, t_float amount, t_symbol *unitname,\n    t_float *unit, int *samps); /* time unit parsing from x_time.c */\n\nstatic void *text_sequence_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_text_sequence *x = (t_text_sequence *)pd_new(text_sequence_class);\n    int global = 0;\n    text_client_argparse(&x->x_tc, &argc, &argv, \"text sequence\");\n    x->x_waitsym = 0;\n    x->x_waitargc = 0;\n    x->x_eaten = 0;\n    x->x_loop = 0;\n    x->x_lastto = 0;\n    x->x_clock = clock_new(x, (t_method)text_sequence_tick);\n    while (argc && argv->a_type == A_SYMBOL &&\n        *argv->a_w.w_symbol->s_name == '-')\n    {\n        if (!strcmp(argv->a_w.w_symbol->s_name, \"-w\") && argc >= 2)\n        {\n            if (argv[1].a_type == A_SYMBOL)\n            {\n                x->x_waitsym = argv[1].a_w.w_symbol;\n                x->x_waitargc = 0;\n            }\n            else\n            {\n                x->x_waitsym = 0;\n                if ((x->x_waitargc = argv[1].a_w.w_float) < 0)\n                    x->x_waitargc = 0;\n            }\n            argc -= 1; argv += 1;\n        }\n        else if (!strcmp(argv->a_w.w_symbol->s_name, \"-g\"))\n            global = 1;\n        else if (!strcmp(argv->a_w.w_symbol->s_name, \"-t\") && argc >= 3)\n        {\n            text_sequence_tempo(x, atom_getsymbolarg(2, argc, argv),\n                atom_getfloatarg(1, argc, argv));\n             argc -= 2; argv += 2;\n        }\n        else\n        {\n            pd_error(x, \"text sequence: unknown flag '%s'...\",\n                argv->a_w.w_symbol->s_name);\n        }\n        argc--; argv++;\n    }\n    if (argc)\n    {\n        post(\"warning: text sequence ignoring extra argument: \");\n        postatom(argc, argv); endpost();\n    }\n    if (x->x_tc.tc_struct)\n        pointerinlet_new(&x->x_tc.tc_obj, &x->x_tc.tc_gp);\n    else symbolinlet_new(&x->x_tc.tc_obj, &x->x_tc.tc_sym);\n    x->x_argc = 0;\n    x->x_argv = (t_atom *)getbytes(0);\n    x->x_onset = 0x7fffffff;\n    x->x_mainout = (!global ? outlet_new(&x->x_obj, &s_list) : 0);\n    x->x_waitout = (global || x->x_waitsym || x->x_waitargc ?\n        outlet_new(&x->x_obj, &s_list) : 0);\n    x->x_endout = outlet_new(&x->x_obj, &s_bang);\n    if (global)\n    {\n        if (x->x_waitargc)\n            pd_error(x,\n       \"warning: text sequence: numeric 'w' argument ignored if '-g' given\");\n        x->x_waitargc = 0x40000000;\n    }\n    return (x);\n}\n\nstatic void text_sequence_doit(t_text_sequence *x, int argc, t_atom *argv)\n{\n    t_binbuf *b = text_client_getbuf(&x->x_tc);\n    int n, i, onset, nfield, wait, eatsemi = 1, gotcomma = 0;\n    t_atom *vec, *outvec, *ap;\n    if (!b)\n        goto nosequence;\n    vec = binbuf_getvec(b);\n    n = binbuf_getnatom(b);\n    if (x->x_onset >= n)\n    {\n    nosequence:\n        x->x_onset = 0x7fffffff;\n        x->x_loop = x->x_auto = 0;\n        outlet_bang(x->x_endout);\n        return;\n    }\n    onset = x->x_onset;\n\n        /* test if leading numbers, or a leading symbol equal to our\n        \"wait symbol\", are directing us to wait */\n    if (!x->x_lastto && (\n        (vec[onset].a_type == A_FLOAT && x->x_waitargc && !x->x_eaten) ||\n            (vec[onset].a_type == A_SYMBOL &&\n                vec[onset].a_w.w_symbol == x->x_waitsym)))\n    {\n        if (vec[onset].a_type == A_FLOAT)\n        {\n            for (i = onset; i < n && i < onset + x->x_waitargc &&\n                vec[i].a_type == A_FLOAT; i++)\n                    ;\n            x->x_eaten = 1;\n            eatsemi = 0;\n        }\n        else\n        {\n            for (i = onset; i < n && vec[i].a_type != A_SEMI &&\n                vec[i].a_type != A_COMMA; i++)\n                    ;\n            x->x_eaten = 1;\n            onset++;    /* symbol isn't part of wait list */\n        }\n        wait = 1;\n    }\n    else    /* message to send */\n    {\n        for (i = onset; i < n && vec[i].a_type != A_SEMI &&\n            vec[i].a_type != A_COMMA; i++)\n                ;\n        wait = 0;\n        x->x_eaten = 0;\n        if (i < n && vec[i].a_type == A_COMMA)\n            gotcomma = 1;\n    }\n    nfield = i - onset;\n    i += eatsemi;\n    if (i >= n)\n        i = 0x7fffffff;\n    x->x_onset = i;\n        /* generate output list, realizing dolar sign atoms.  Allocate one\n        extra atom in case we want to prepend a symbol later */\n    ALLOCA(t_atom, outvec, nfield+1, TEXT_NGETBYTE);\n    for (i = 0, ap = vec+onset; i < nfield; i++, ap++)\n    {\n        int type = ap->a_type;\n        if (type == A_FLOAT || type == A_SYMBOL)\n            outvec[i] = *ap;\n        else if (type == A_DOLLAR)\n        {\n            int atno = ap->a_w.w_index-1;\n            if (atno < 0 || atno >= argc)\n            {\n                pd_error(x, \"argument $%d out of range\", atno+1);\n                SETFLOAT(outvec+i, 0);\n            }\n            else outvec[i] = argv[atno];\n        }\n        else if (type == A_DOLLSYM)\n        {\n            t_symbol *s =\n                binbuf_realizedollsym(ap->a_w.w_symbol, argc, argv, 0);\n            if (s)\n                SETSYMBOL(outvec+i, s);\n            else\n            {\n                pd_error(0, \"$%s: not enough arguments supplied\",\n                    ap->a_w.w_symbol->s_name);\n                SETSYMBOL(outvec+i, &s_symbol);\n            }\n        }\n        else bug(\"text sequence\");\n    }\n    if (wait)\n    {\n        x->x_loop = 0;\n        x->x_lastto = 0;\n        if (x->x_auto && nfield == 1 && outvec[0].a_type == A_FLOAT)\n            x->x_nextdelay = outvec[0].a_w.w_float;\n        else if (!x->x_waitout)\n            bug(\"text sequence 3\");\n        else\n        {\n            x->x_auto = 0;\n            outlet_list(x->x_waitout, 0, nfield, outvec);\n        }\n    }\n    else if (x->x_mainout)\n    {\n        int n2 = nfield;\n        if (x->x_lastto)\n        {\n            memmove(outvec+1, outvec, nfield * sizeof(*outvec));\n            SETSYMBOL(outvec, x->x_lastto);\n            n2++;\n        }\n        if (!gotcomma)\n            x->x_lastto = 0;\n        else if (!x->x_lastto && nfield && outvec->a_type == A_SYMBOL)\n            x->x_lastto = outvec->a_w.w_symbol;\n        outlet_list(x->x_mainout, 0, n2, outvec);\n    }\n    else if (nfield)\n    {\n        t_symbol *tosym = x->x_lastto;\n        t_pd *to = 0;\n        t_atom *vecleft = outvec;\n        int nleft = nfield;\n        if (!tosym)\n        {\n            if (outvec[0].a_type != A_SYMBOL)\n                bug(\"text sequence 2\");\n            else tosym = outvec[0].a_w.w_symbol;\n            vecleft++;\n            nleft--;\n        }\n        if (tosym)\n        {\n            if (!(to = tosym->s_thing))\n                pd_error(x, \"%s: no such object\", tosym->s_name);\n        }\n        x->x_lastto = (gotcomma ? tosym : 0);\n        if (to)\n        {\n            if (nleft > 0 && vecleft[0].a_type == A_SYMBOL)\n                typedmess(to, vecleft->a_w.w_symbol, nleft-1, vecleft+1);\n            else pd_list(to, 0, nleft, vecleft);\n        }\n    }\n    FREEA(t_atom, outvec, nfield+1, TEXT_NGETBYTE);\n}\n\nstatic void text_sequence_list(t_text_sequence *x, t_symbol *s, int argc,\n    t_atom *argv)\n{\n    x->x_loop = 1;\n    while (x->x_loop)\n    {\n        if (argc)\n            text_sequence_doit(x, argc, argv);\n        else text_sequence_doit(x, x->x_argc, x->x_argv);\n    }\n}\n\nstatic void text_sequence_stop(t_text_sequence *x)\n{\n    x->x_loop = 0;\n    if (x->x_auto)\n    {\n        clock_unset(x->x_clock);\n        x->x_auto = 0;\n    }\n}\n\nstatic void text_sequence_tick(t_text_sequence *x)  /* clock callback */\n{\n    x->x_lastto = 0;\n    while (x->x_auto)\n    {\n        x->x_loop = 1;\n        while (x->x_loop)\n            text_sequence_doit(x, x->x_argc, x->x_argv);\n        if (x->x_nextdelay > 0)\n            break;\n    }\n    if (x->x_auto)\n        clock_delay(x->x_clock, x->x_nextdelay);\n}\n\nstatic void text_sequence_auto(t_text_sequence *x)\n{\n    x->x_lastto = 0;\n    if (x->x_auto)\n        clock_unset(x->x_clock);\n    x->x_auto = 1;\n    text_sequence_tick(x);\n}\n\nstatic void text_sequence_step(t_text_sequence *x)\n{\n    text_sequence_stop(x);\n    text_sequence_doit(x, x->x_argc, x->x_argv);\n}\n\nstatic void text_sequence_line(t_text_sequence *x, t_floatarg f)\n{\n    t_binbuf *b = text_client_getbuf(&x->x_tc);\n    int n, start, end;\n    t_atom *vec;\n    if (!b)\n       return;\n    x->x_lastto = 0;\n    vec = binbuf_getvec(b);\n    n = binbuf_getnatom(b);\n    if (!text_nthline(n, vec, f, &start, &end))\n    {\n        pd_error(x, \"text sequence: line number %d out of range\", (int)f);\n        x->x_onset = 0x7fffffff;\n    }\n    else x->x_onset = start;\n    x->x_eaten = 0;\n}\n\nstatic void text_sequence_args(t_text_sequence *x, t_symbol *s,\n    int argc, t_atom *argv)\n{\n    int i;\n    x->x_argv = t_resizebytes(x->x_argv,\n        x->x_argc * sizeof(t_atom), argc * sizeof(t_atom));\n    for (i = 0; i < argc; i++)\n        x->x_argv[i] = argv[i];\n    x->x_argc = argc;\n}\n\nstatic void text_sequence_tempo(t_text_sequence *x,\n    t_symbol *unitname, t_floatarg tempo)\n{\n    t_float unit;\n    int samps;\n    parsetimeunits(x, tempo, unitname, &unit, &samps);\n    clock_setunit(x->x_clock, unit, samps);\n}\n\nstatic void text_sequence_free(t_text_sequence *x)\n{\n    t_freebytes(x->x_argv, sizeof(t_atom) * x->x_argc);\n    clock_free(x->x_clock);\n    text_client_free(&x->x_tc);\n}\n\n/* --- overall creator for \"text\" objects: dispatch to \"text define\" etc --- */\nstatic void *text_new(t_symbol *s, int argc, t_atom *argv)\n{\n    if (!argc || argv[0].a_type != A_SYMBOL)\n        pd_this->pd_newest = text_define_new(s, argc, argv);\n    else\n    {\n        const char *str = argv[0].a_w.w_symbol->s_name;\n        if (!strcmp(str, \"d\") || !strcmp(str, \"define\"))\n            pd_this->pd_newest = text_define_new(s, argc-1, argv+1);\n        else if (!strcmp(str, \"get\"))\n            pd_this->pd_newest = text_get_new(s, argc-1, argv+1);\n        else if (!strcmp(str, \"set\"))\n            pd_this->pd_newest = text_set_new(s, argc-1, argv+1);\n        else if (!strcmp(str, \"insert\"))\n            pd_this->pd_newest = text_insert_new(s, argc-1, argv+1);\n        else if (!strcmp(str, \"delete\"))\n            pd_this->pd_newest = text_delete_new(s, argc-1, argv+1);\n        else if (!strcmp(str, \"size\"))\n            pd_this->pd_newest = text_size_new(s, argc-1, argv+1);\n        else if (!strcmp(str, \"tolist\"))\n            pd_this->pd_newest = text_tolist_new(s, argc-1, argv+1);\n        else if (!strcmp(str, \"fromlist\"))\n            pd_this->pd_newest = text_fromlist_new(s, argc-1, argv+1);\n        else if (!strcmp(str, \"search\"))\n            pd_this->pd_newest = text_search_new(s, argc-1, argv+1);\n        else if (!strcmp(str, \"sequence\"))\n            pd_this->pd_newest = text_sequence_new(s, argc-1, argv+1);\n        else\n        {\n            pd_error(0, \"list %s: unknown function\", str);\n            pd_this->pd_newest = 0;\n        }\n    }\n    return (pd_this->pd_newest);\n}\n\n/*  the qlist and textfile objects, as of 0.44, are 'derived' from\n* the text object above.  Maybe later it will be desirable to add new\n* functionality to textfile; qlist is an ancient holdover (1987) and\n* is probably best left alone.\n*/\n\ntypedef struct _qlist\n{\n    t_textbuf x_textbuf;\n    t_outlet *x_bangout;\n    int x_onset;                /* playback position */\n    t_clock *x_clock;\n    t_float x_tempo;\n    double x_whenclockset;\n    t_float x_clockdelay;\n    int x_rewound;          /* we've been rewound since last start */\n    int x_innext;           /* we're currently inside the \"next\" routine */\n} t_qlist;\n#define x_ob x_textbuf.b_ob\n#define x_binbuf x_textbuf.b_binbuf\n#define x_canvas x_textbuf.b_canvas\n\nstatic void qlist_tick(t_qlist *x);\n\nstatic t_class *qlist_class;\n\nstatic void *qlist_new(void)\n{\n    t_qlist *x = (t_qlist *)pd_new(qlist_class);\n    textbuf_init(&x->x_textbuf, gensym(\"qlist\"));\n    x->x_clock = clock_new(x, (t_method)qlist_tick);\n    outlet_new(&x->x_ob, &s_list);\n    x->x_bangout = outlet_new(&x->x_ob, &s_bang);\n    x->x_onset = 0x7fffffff;\n    x->x_tempo = 1;\n    x->x_whenclockset = 0;\n    x->x_clockdelay = 0;\n    x->x_rewound = x->x_innext = 0;\n    return (x);\n}\n\nstatic void qlist_rewind(t_qlist *x)\n{\n    x->x_onset = 0;\n    if (x->x_clock) clock_unset(x->x_clock);\n    x->x_whenclockset = 0;\n    x->x_rewound = 1;\n}\n\nstatic void qlist_donext(t_qlist *x, int drop, int automatic)\n{\n    t_pd *target = 0;\n    if (x->x_innext)\n    {\n        pd_error(x, \"qlist sent 'next' from within itself\");\n        return;\n    }\n    x->x_innext = 1;\n    while (1)\n    {\n        int argc = binbuf_getnatom(x->x_binbuf),\n            count, onset = x->x_onset, onset2, wasrewound;\n        t_atom *argv = binbuf_getvec(x->x_binbuf);\n        t_atom *ap = argv + onset, *ap2;\n        if (onset >= argc) goto end;\n        while (ap->a_type == A_SEMI || ap->a_type == A_COMMA)\n        {\n            if (ap->a_type == A_SEMI) target = 0;\n            onset++, ap++;\n            if (onset >= argc) goto end;\n        }\n\n        if (!target && ap->a_type == A_FLOAT)\n        {\n            ap2 = ap + 1;\n            onset2 = onset + 1;\n            while (onset2 < argc && ap2->a_type == A_FLOAT)\n                onset2++, ap2++;\n            x->x_onset = onset2;\n            if (automatic)\n            {\n                clock_delay(x->x_clock,\n                    x->x_clockdelay = ap->a_w.w_float * x->x_tempo);\n                x->x_whenclockset = clock_getsystime();\n            }\n            else outlet_list(x->x_ob.ob_outlet, 0, onset2-onset, ap);\n            x->x_innext = 0;\n            return;\n        }\n        ap2 = ap + 1;\n        onset2 = onset + 1;\n        while (onset2 < argc &&\n            (ap2->a_type == A_FLOAT || ap2->a_type == A_SYMBOL))\n                onset2++, ap2++;\n        x->x_onset = onset2;\n        count = onset2 - onset;\n        if (!target)\n        {\n            if (ap->a_type != A_SYMBOL) continue;\n            else if (!(target = ap->a_w.w_symbol->s_thing))\n            {\n                pd_error(x, \"qlist: %s: no such object\",\n                    ap->a_w.w_symbol->s_name);\n                continue;\n            }\n            ap++;\n            onset++;\n            count--;\n            if (!count)\n            {\n                x->x_onset = onset2;\n                continue;\n            }\n        }\n        wasrewound = x->x_rewound;\n        x->x_rewound = 0;\n        if (!drop)\n        {\n            if (ap->a_type == A_FLOAT)\n                typedmess(target, &s_list, count, ap);\n            else if (ap->a_type == A_SYMBOL)\n                typedmess(target, ap->a_w.w_symbol, count-1, ap+1);\n        }\n        if (x->x_rewound)\n        {\n            x->x_innext = 0;\n            return;\n        }\n        x->x_rewound = wasrewound;\n    }  /* while (1); never falls through */\n\nend:\n    x->x_onset = 0x7fffffff;\n    x->x_whenclockset = 0;\n    x->x_innext = 0;\n    outlet_bang(x->x_bangout);\n}\n\nstatic void qlist_next(t_qlist *x, t_floatarg drop)\n{\n    qlist_donext(x, drop != 0, 0);\n}\n\nstatic void qlist_bang(t_qlist *x)\n{\n    qlist_rewind(x);\n        /* if we're restarted reentrantly from a \"next\" message set ourselves\n        up to do this non-reentrantly after a delay of 0 */\n    if (x->x_innext)\n    {\n        x->x_whenclockset = clock_getsystime();\n        x->x_clockdelay = 0;\n        clock_delay(x->x_clock, 0);\n    }\n    else qlist_donext(x, 0, 1);\n}\n\nstatic void qlist_tick(t_qlist *x)\n{\n    x->x_whenclockset = 0;\n    qlist_donext(x, 0, 1);\n}\n\nstatic void qlist_add(t_qlist *x, t_symbol *s, int argc, t_atom *argv)\n{\n    t_atom a;\n    SETSEMI(&a);\n    binbuf_add(x->x_binbuf, argc, argv);\n    binbuf_add(x->x_binbuf, 1, &a);\n}\n\nstatic void qlist_add2(t_qlist *x, t_symbol *s, int argc, t_atom *argv)\n{\n    binbuf_add(x->x_binbuf, argc, argv);\n}\n\nstatic void qlist_clear(t_qlist *x)\n{\n    qlist_rewind(x);\n    binbuf_clear(x->x_binbuf);\n}\n\nstatic void qlist_set(t_qlist *x, t_symbol *s, int argc, t_atom *argv)\n{\n    qlist_clear(x);\n    qlist_add(x, s, argc, argv);\n}\n\nstatic void qlist_read(t_qlist *x, t_symbol *filename, t_symbol *format)\n{\n    int cr = 0;\n    if (!strcmp(format->s_name, \"cr\"))\n        cr = 1;\n    else if (*format->s_name)\n        pd_error(x, \"qlist_read: unknown flag: %s\", format->s_name);\n\n    if (binbuf_read_via_canvas(x->x_binbuf, filename->s_name, x->x_canvas, cr))\n            pd_error(x, \"%s: read failed\", filename->s_name);\n    x->x_onset = 0x7fffffff;\n    x->x_rewound = 1;\n}\n\nstatic void qlist_write(t_qlist *x, t_symbol *filename, t_symbol *format)\n{\n    int cr = 0;\n    char buf[MAXPDSTRING];\n    canvas_makefilename(x->x_canvas, filename->s_name,\n        buf, MAXPDSTRING);\n    if (!strcmp(format->s_name, \"cr\"))\n        cr = 1;\n    else if (*format->s_name)\n        pd_error(x, \"qlist_read: unknown flag: %s\", format->s_name);\n    if (binbuf_write(x->x_binbuf, buf, \"\", cr))\n            pd_error(x, \"%s: write failed\", filename->s_name);\n}\n\nstatic void qlist_print(t_qlist *x)\n{\n    post(\"--------- textfile or qlist contents: -----------\");\n    binbuf_print(x->x_binbuf);\n}\n\nstatic void qlist_tempo(t_qlist *x, t_float f)\n{\n    t_float newtempo;\n    if (f < 1e-20) f = 1e-20;\n    else if (f > 1e20) f = 1e20;\n    newtempo = 1./f;\n    if (x->x_whenclockset != 0)\n    {\n        t_float elapsed = clock_gettimesince(x->x_whenclockset);\n        t_float left = x->x_clockdelay - elapsed;\n        if (left < 0) left = 0;\n        left *= newtempo / x->x_tempo;\n        clock_delay(x->x_clock, left);\n    }\n    x->x_tempo = newtempo;\n}\n\nstatic void qlist_free(t_qlist *x)\n{\n    textbuf_free(&x->x_textbuf);\n    clock_free(x->x_clock);\n}\n\n/* -------------------- textfile ------------------------------- */\n\n/* has the same struct as qlist (so we can reuse some of its\n* methods) but \"sequencing\" here only relies on 'binbuf' and 'onset'\n* fields.\n*/\n\nstatic t_class *textfile_class;\n\nstatic void *textfile_new(void)\n{\n    t_qlist *x = (t_qlist *)pd_new(textfile_class);\n    textbuf_init(&x->x_textbuf, gensym(\"textfile\"));\n    outlet_new(&x->x_ob, &s_list);\n    x->x_bangout = outlet_new(&x->x_ob, &s_bang);\n    x->x_onset = 0x7fffffff;\n    x->x_rewound = 0;\n    x->x_tempo = 1;\n    x->x_whenclockset = 0;\n    x->x_clockdelay = 0;\n    x->x_clock = NULL;\n    return (x);\n}\n\nstatic void textfile_bang(t_qlist *x)\n{\n    int argc = binbuf_getnatom(x->x_binbuf),\n        onset = x->x_onset, onset2;\n    t_atom *argv = binbuf_getvec(x->x_binbuf);\n    t_atom *ap = argv + onset, *ap2;\n    while (onset < argc &&\n        (ap->a_type == A_SEMI || ap->a_type == A_COMMA))\n            onset++, ap++;\n    onset2 = onset;\n    ap2 = ap;\n    while (onset2 < argc &&\n        (ap2->a_type != A_SEMI && ap2->a_type != A_COMMA))\n            onset2++, ap2++;\n    if (onset2 > onset)\n    {\n        x->x_onset = onset2;\n        if (ap->a_type == A_SYMBOL)\n            outlet_anything(x->x_ob.ob_outlet, ap->a_w.w_symbol,\n                onset2-onset-1, ap+1);\n        else outlet_list(x->x_ob.ob_outlet, 0, onset2-onset, ap);\n    }\n    else\n    {\n        x->x_onset = 0x7fffffff;\n        outlet_bang(x->x_bangout);\n    }\n}\n\nstatic void textfile_rewind(t_qlist *x)\n{\n    x->x_onset = 0;\n}\n\n/* ---------------- global setup function -------------------- */\n\nstatic t_pd *text_templatecanvas;\nstatic char text_templatefile[] = \"\\\ncanvas 0 0 458 153 10;\\n\\\n#X obj 43 31 struct text float x float y text t;\\n\\\n\";\n\n/* create invisible, built-in canvas to supply template containing one text\nfield named 't'.  I don't know how to make this not break\npre-0.45 patches using templates named 'text'... perhaps this is a minor\nenough incompatibility that I'll just get away with it. */\n\nvoid text_template_init(void)\n{\n    t_binbuf *b;\n    b = binbuf_new();\n\n    glob_setfilename(0, gensym(\"_text_template\"), gensym(\".\"));\n    binbuf_text(b, text_templatefile, strlen(text_templatefile));\n    binbuf_eval(b, &pd_canvasmaker, 0, 0);\n    vmess(s__X.s_thing, gensym(\"pop\"), \"i\", 0);\n\n    glob_setfilename(0, &s_, &s_);\n    binbuf_free(b);\n}\n\nvoid x_qlist_setup(void)\n{\n    text_template_init();\n    text_define_class = class_new(gensym(\"text define\"),\n        (t_newmethod)text_define_new,\n        (t_method)text_define_free, sizeof(t_text_define), 0, A_GIMME, 0);\n    class_addmethod(text_define_class, (t_method)textbuf_open,\n        gensym(\"click\"), 0);\n    class_addmethod(text_define_class, (t_method)textbuf_close,\n        gensym(\"close\"), 0);\n    class_addmethod(text_define_class, (t_method)textbuf_addline,\n        gensym(\"addline\"), A_GIMME, 0);\n    class_addmethod(text_define_class, (t_method)text_define_notify,\n        gensym(\"notify\"), 0, 0);\n    class_addmethod(text_define_class, (t_method)text_define_set,\n        gensym(\"set\"), A_GIMME, 0);\n    class_addmethod(text_define_class, (t_method)text_define_clear,\n        gensym(\"clear\"), 0);\n    class_addmethod(text_define_class, (t_method)textbuf_write,\n        gensym(\"write\"), A_GIMME, 0);\n    class_addmethod(text_define_class, (t_method)textbuf_read,\n        gensym(\"read\"), A_GIMME, 0);\n    class_addmethod(text_define_class, (t_method)text_define_send,\n        gensym(\"send\"), A_SYMBOL, 0);\n    class_addmethod(text_define_class, (t_method)text_define_sort,\n        gensym(\"sort\"), A_GIMME, 0);\n    class_setsavefn(text_define_class, text_define_save);\n    class_addbang(text_define_class, text_define_bang);\n    class_sethelpsymbol(text_define_class, gensym(\"text-object\"));\n\n    class_addcreator((t_newmethod)text_new, gensym(\"text\"), A_GIMME, 0);\n\n    text_get_class = class_new(gensym(\"text get\"),\n        (t_newmethod)text_get_new, (t_method)text_client_free,\n            sizeof(t_text_get), 0, A_GIMME, 0);\n    class_addfloat(text_get_class, text_get_float);\n    class_sethelpsymbol(text_get_class, gensym(\"text-object\"));\n\n    text_set_class = class_new(gensym(\"text set\"),\n        (t_newmethod)text_set_new, (t_method)text_client_free,\n            sizeof(t_text_set), 0, A_GIMME, 0);\n    class_addlist(text_set_class, text_set_list);\n    class_sethelpsymbol(text_set_class, gensym(\"text-object\"));\n\n    text_insert_class = class_new(gensym(\"text insert\"),\n        (t_newmethod)text_insert_new, (t_method)text_client_free,\n            sizeof(t_text_insert), 0, A_GIMME, 0);\n    class_addlist(text_insert_class, text_insert_list);\n    class_sethelpsymbol(text_insert_class, gensym(\"text-object\"));\n\n    text_delete_class = class_new(gensym(\"text delete\"),\n        (t_newmethod)text_delete_new, (t_method)text_client_free,\n            sizeof(t_text_delete), 0, A_GIMME, 0);\n    class_addfloat(text_delete_class, text_delete_float);\n    class_sethelpsymbol(text_delete_class, gensym(\"text-object\"));\n\n    text_size_class = class_new(gensym(\"text size\"),\n        (t_newmethod)text_size_new, (t_method)text_client_free,\n            sizeof(t_text_size), 0, A_GIMME, 0);\n    class_addbang(text_size_class, text_size_bang);\n    class_addfloat(text_size_class, text_size_float);\n    class_sethelpsymbol(text_size_class, gensym(\"text-object\"));\n\n    text_tolist_class = class_new(gensym(\"text tolist\"),\n        (t_newmethod)text_tolist_new, (t_method)text_client_free,\n            sizeof(t_text_tolist), 0, A_GIMME, 0);\n    class_addbang(text_tolist_class, text_tolist_bang);\n    class_sethelpsymbol(text_tolist_class, gensym(\"text-object\"));\n\n    text_fromlist_class = class_new(gensym(\"text fromlist\"),\n        (t_newmethod)text_fromlist_new, (t_method)text_client_free,\n            sizeof(t_text_fromlist), 0, A_GIMME, 0);\n    class_addlist(text_fromlist_class, text_fromlist_list);\n    class_sethelpsymbol(text_fromlist_class, gensym(\"text-object\"));\n\n    text_search_class = class_new(gensym(\"text search\"),\n        (t_newmethod)text_search_new, (t_method)text_client_free,\n            sizeof(t_text_search), 0, A_GIMME, 0);\n    class_addlist(text_search_class, text_search_list);\n    class_addmethod(text_search_class, (t_method)text_search_range,\n        gensym(\"range\"), A_FLOAT, A_FLOAT, 0);\n    class_sethelpsymbol(text_search_class, gensym(\"text-object\"));\n\n    text_sequence_class = class_new(gensym(\"text sequence\"),\n        (t_newmethod)text_sequence_new, (t_method)text_sequence_free,\n            sizeof(t_text_sequence), 0, A_GIMME, 0);\n    class_addmethod(text_sequence_class, (t_method)text_sequence_step,\n        gensym(\"step\"), 0);\n    class_addmethod(text_sequence_class, (t_method)text_sequence_line,\n        gensym(\"line\"), A_FLOAT, 0);\n    class_addmethod(text_sequence_class, (t_method)text_sequence_auto,\n        gensym(\"auto\"), 0);\n    class_addmethod(text_sequence_class, (t_method)text_sequence_stop,\n        gensym(\"stop\"), 0);\n    class_addmethod(text_sequence_class, (t_method)text_sequence_args,\n        gensym(\"args\"), A_GIMME, 0);\n    class_addmethod(text_sequence_class, (t_method)text_sequence_tempo,\n        gensym(\"tempo\"), A_FLOAT, A_SYMBOL, 0);\n    class_addlist(text_sequence_class, text_sequence_list);\n    class_sethelpsymbol(text_sequence_class, gensym(\"text-object\"));\n\n    qlist_class = class_new(gensym(\"qlist\"), (t_newmethod)qlist_new,\n        (t_method)qlist_free, sizeof(t_qlist), 0, 0);\n    class_addmethod(qlist_class, (t_method)qlist_rewind, gensym(\"rewind\"), 0);\n    class_addmethod(qlist_class, (t_method)qlist_next,\n        gensym(\"next\"), A_DEFFLOAT, 0);\n    class_addmethod(qlist_class, (t_method)qlist_set, gensym(\"set\"),\n        A_GIMME, 0);\n    class_addmethod(qlist_class, (t_method)qlist_clear, gensym(\"clear\"), 0);\n    class_addmethod(qlist_class, (t_method)qlist_add, gensym(\"add\"),\n        A_GIMME, 0);\n    class_addmethod(qlist_class, (t_method)qlist_add2, gensym(\"add2\"),\n        A_GIMME, 0);\n    class_addmethod(qlist_class, (t_method)qlist_add, gensym(\"append\"),\n        A_GIMME, 0);\n    class_addmethod(qlist_class, (t_method)qlist_read, gensym(\"read\"),\n        A_SYMBOL, A_DEFSYM, 0);\n    class_addmethod(qlist_class, (t_method)qlist_write, gensym(\"write\"),\n        A_SYMBOL, A_DEFSYM, 0);\n    class_addmethod(qlist_class, (t_method)textbuf_open, gensym(\"click\"), 0);\n    class_addmethod(qlist_class, (t_method)textbuf_close, gensym(\"close\"), 0);\n    class_addmethod(qlist_class, (t_method)textbuf_addline,\n        gensym(\"addline\"), A_GIMME, 0);\n    class_addmethod(qlist_class, (t_method)textbuf_senditup,\n        gensym(\"notify\"), 0, 0);\n    class_addmethod(qlist_class, (t_method)qlist_print, gensym(\"print\"),\n        A_DEFSYM, 0);\n    class_addmethod(qlist_class, (t_method)qlist_tempo,\n        gensym(\"tempo\"), A_FLOAT, 0);\n    class_addbang(qlist_class, qlist_bang);\n\n    textfile_class = class_new(gensym(\"textfile\"), (t_newmethod)textfile_new,\n        (t_method)textbuf_free, sizeof(t_qlist), 0, 0);\n    class_addmethod(textfile_class, (t_method)textfile_rewind, gensym(\"rewind\"),\n        0);\n    class_addmethod(textfile_class, (t_method)qlist_set, gensym(\"set\"),\n        A_GIMME, 0);\n    class_addmethod(textfile_class, (t_method)qlist_clear, gensym(\"clear\"), 0);\n    class_addmethod(textfile_class, (t_method)qlist_add, gensym(\"add\"),\n        A_GIMME, 0);\n    class_addmethod(textfile_class, (t_method)qlist_add2, gensym(\"add2\"),\n        A_GIMME, 0);\n    class_addmethod(textfile_class, (t_method)qlist_add, gensym(\"append\"),\n        A_GIMME, 0);\n    class_addmethod(textfile_class, (t_method)qlist_read, gensym(\"read\"),\n        A_SYMBOL, A_DEFSYM, 0);\n    class_addmethod(textfile_class, (t_method)qlist_write, gensym(\"write\"),\n        A_SYMBOL, A_DEFSYM, 0);\n    class_addmethod(textfile_class, (t_method)textbuf_open, gensym(\"click\"), 0);\n    class_addmethod(textfile_class, (t_method)textbuf_close, gensym(\"close\"),\n        0);\n    class_addmethod(textfile_class, (t_method)textbuf_addline,\n        gensym(\"addline\"), A_GIMME, 0);\n    class_addmethod(textfile_class, (t_method)textbuf_senditup,\n        gensym(\"notify\"), 0, 0);\n    class_addmethod(textfile_class, (t_method)qlist_print, gensym(\"print\"),\n        A_DEFSYM, 0);\n    class_addbang(textfile_class, textfile_bang);\n}\n\n/* public interface to get text buffers by name */\n\nt_binbuf *text_getbufbyname(t_symbol *s)\n{\n    t_text_define *y = (t_text_define *)pd_findbyclass(s, text_define_class);\n    if (y)\n        return (y->x_textbuf.b_binbuf);\n    else return (0);\n}\n\n    /* notify text object that binbuf was modified */\nvoid text_notifybyname(t_symbol *s)\n{\n    t_text_define *y = (t_text_define *)pd_findbyclass(s, text_define_class);\n    if (y)\n        text_define_notify(y);\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/x_time.c",
    "content": "/* Copyright (c) 1997-1999 Miller Puckette.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* clock objects */\n\n#include \"m_pd.h\"\n#include <stdio.h>\n#include <string.h>\n\n    /* parse a time unit such as \"5 msec\", \"60 perminute\", or \"1 sample\" to\n    a form usable by clock_setunit)( and clock_gettimesincewithunits().\n    This brute-force search through symbols really ought not to be done on\n    the fly for incoming 'tempo' messages, hmm...  This isn't public because\n    its interface might want to change - but it's used in x_text.c as well\n    as here. */\nvoid parsetimeunits(void *x, t_float amount, t_symbol *unitname,\n    t_float *unit, int *samps)\n{\n    const char *s = unitname->s_name;\n    if (amount <= 0)\n        amount = 1;\n    if (s[0] == 'p' && s[1] == 'e' && s[2] == 'r')  /* starts with 'per' */\n    {\n        const char *s2 = s+3;\n        if (!strcmp(s2, \"millisecond\") || !strcmp(s2, \"msec\"))  /* msec */\n            *samps = 0, *unit = 1./amount;\n        else if (!strncmp(s2, \"sec\", 3))        /* seconds */\n            *samps = 0, *unit = 1000./amount;\n        else if (!strncmp(s2, \"min\", 3))        /* minutes */\n            *samps = 0, *unit = 60000./amount;\n        else if (!strncmp(s2, \"sam\", 3))        /* samples */\n            *samps = 1, *unit = 1./amount;\n        else goto fail;\n    }\n    else\n    {\n            /* empty string defaults to msec */\n        if (!strcmp(s, \"millisecond\") || !strcmp(s, \"msec\"))\n            *samps = 0, *unit = amount;\n        else if (!strncmp(s, \"sec\", 3))\n            *samps = 0, *unit = 1000.*amount;\n        else if (!strncmp(s, \"min\", 3))\n            *samps = 0, *unit = 60000.*amount;\n        else if (!strncmp(s, \"sam\", 3))\n            *samps = 1, *unit = amount;\n        else\n        {\n        fail:\n                /* empty time unit specification defaults to 1 msec for\n                back compatibility, since it's possible someone threw a\n                float argument to timer which had previously been ignored. */\n            if (*s)\n                pd_error(x, \"%s: unknown time unit\", s);\n            else pd_error(x,\n                \"tempo setting needs time unit ('sec', 'samp', 'permin', etc.\");\n            *unit = 1;\n            *samps = 0;\n        }\n    }\n}\n\n/* -------------------------- delay ------------------------------ */\nstatic t_class *delay_class;\n\ntypedef struct _delay\n{\n    t_object x_obj;\n    t_clock *x_clock;\n    double x_deltime;\n} t_delay;\n\nstatic void delay_ft1(t_delay *x, t_floatarg g)\n{\n    if (g < 0) g = 0;\n    x->x_deltime = g;\n}\n\nstatic void delay_tick(t_delay *x)\n{\n    outlet_bang(x->x_obj.ob_outlet);\n}\n\nstatic void delay_bang(t_delay *x)\n{\n    clock_delay(x->x_clock, x->x_deltime);\n}\n\nstatic void delay_stop(t_delay *x)\n{\n    clock_unset(x->x_clock);\n}\n\nstatic void delay_float(t_delay *x, t_float f)\n{\n    delay_ft1(x, f);\n    delay_bang(x);\n}\n\nstatic void delay_tempo(t_delay *x, t_symbol *unitname, t_floatarg tempo)\n{\n    t_float unit;\n    int samps;\n    parsetimeunits(x, tempo, unitname, &unit, &samps);\n    clock_setunit(x->x_clock, unit, samps);\n}\n\nstatic void delay_free(t_delay *x)\n{\n    clock_free(x->x_clock);\n}\n\nstatic void *delay_new(t_symbol *unitname, t_floatarg f, t_floatarg tempo)\n{\n    t_delay *x = (t_delay *)pd_new(delay_class);\n    delay_ft1(x, f);\n    x->x_clock = clock_new(x, (t_method)delay_tick);\n    outlet_new(&x->x_obj, gensym(\"bang\"));\n    inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym(\"float\"), gensym(\"ft1\"));\n    if (tempo != 0)\n        delay_tempo(x, unitname, tempo);\n    return (x);\n}\n\nstatic void delay_setup(void)\n{\n    delay_class = class_new(gensym(\"delay\"), (t_newmethod)delay_new,\n        (t_method)delay_free, sizeof(t_delay), 0,\n            A_DEFFLOAT, A_DEFFLOAT, A_DEFSYM, 0);\n    class_addcreator((t_newmethod)delay_new, gensym(\"del\"),\n        A_DEFFLOAT, A_DEFFLOAT, A_DEFSYM, 0);\n    class_addbang(delay_class, delay_bang);\n    class_addmethod(delay_class, (t_method)delay_stop, gensym(\"stop\"), 0);\n    class_addmethod(delay_class, (t_method)delay_ft1,\n        gensym(\"ft1\"), A_FLOAT, 0);\n    class_addmethod(delay_class, (t_method)delay_tempo,\n        gensym(\"tempo\"), A_FLOAT, A_SYMBOL, 0);\n    class_addfloat(delay_class, (t_method)delay_float);\n}\n\n/* -------------------------- metro ------------------------------ */\nstatic t_class *metro_class;\n\ntypedef struct _metro\n{\n    t_object x_obj;\n    t_clock *x_clock;\n    double x_deltime;\n    int x_hit;\n} t_metro;\n\nstatic void metro_ft1(t_metro *x, t_floatarg g)\n{\n    if (g <= 0) /* as of 0.45, we're willing to try any positive time value */\n        g = 1;  /* but default to 1 (arbitrary and probably not so good) */\n    x->x_deltime = g;\n}\n\nstatic void metro_tick(t_metro *x)\n{\n    x->x_hit = 0;\n    outlet_bang(x->x_obj.ob_outlet);\n    if (!x->x_hit) clock_delay(x->x_clock, x->x_deltime);\n}\n\nstatic void metro_float(t_metro *x, t_float f)\n{\n    if (f != 0) metro_tick(x);\n    else clock_unset(x->x_clock);\n    x->x_hit = 1;\n}\n\nstatic void metro_bang(t_metro *x)\n{\n    metro_float(x, 1);\n}\n\nstatic void metro_stop(t_metro *x)\n{\n    metro_float(x, 0);\n}\n\nstatic void metro_tempo(t_metro *x, t_symbol *unitname, t_floatarg tempo)\n{\n    t_float unit;\n    int samps;\n    parsetimeunits(x, tempo, unitname, &unit, &samps);\n    clock_setunit(x->x_clock, unit, samps);\n}\n\nstatic void metro_free(t_metro *x)\n{\n    clock_free(x->x_clock);\n}\n\nstatic void *metro_new(t_symbol *unitname, t_floatarg f, t_floatarg tempo)\n{\n    t_metro *x = (t_metro *)pd_new(metro_class);\n    metro_ft1(x, f);\n    x->x_hit = 0;\n    x->x_clock = clock_new(x, (t_method)metro_tick);\n    outlet_new(&x->x_obj, gensym(\"bang\"));\n    inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym(\"float\"), gensym(\"ft1\"));\n    if (tempo != 0)\n        metro_tempo(x, unitname, tempo);\n    return (x);\n}\n\nstatic void metro_setup(void)\n{\n    metro_class = class_new(gensym(\"metro\"), (t_newmethod)metro_new,\n        (t_method)metro_free, sizeof(t_metro), 0,\n            A_DEFFLOAT, A_DEFFLOAT, A_DEFSYM, 0);\n    class_addbang(metro_class, metro_bang);\n    class_addmethod(metro_class, (t_method)metro_stop, gensym(\"stop\"), 0);\n    class_addmethod(metro_class, (t_method)metro_ft1, gensym(\"ft1\"),\n        A_FLOAT, 0);\n    class_addmethod(metro_class, (t_method)metro_tempo,\n        gensym(\"tempo\"), A_FLOAT, A_SYMBOL, 0);\n    class_addfloat(metro_class, (t_method)metro_float);\n}\n\n/* -------------------------- line ------------------------------ */\n#define DEFAULTLINEGRAIN 20\nstatic t_class *line_class;\n\ntypedef struct _line\n{\n    t_object x_obj;\n    t_clock *x_clock;\n    double x_targettime;\n    t_float x_targetval;\n    double x_prevtime;\n    t_float x_setval;\n    int x_gotinlet;\n    t_float x_grain;\n    double x_1overtimediff;\n    double x_in1val;\n} t_line;\n\nstatic void line_tick(t_line *x)\n{\n    double timenow = clock_getsystime();\n    double msectogo = - clock_gettimesince(x->x_targettime);\n    if (msectogo < 1E-9)\n    {\n        outlet_float(x->x_obj.ob_outlet, x->x_targetval);\n    }\n    else\n    {\n        outlet_float(x->x_obj.ob_outlet,\n            x->x_setval + x->x_1overtimediff * (timenow - x->x_prevtime)\n                * (x->x_targetval - x->x_setval));\n        if (x->x_grain <= 0)\n            x->x_grain = DEFAULTLINEGRAIN;\n        clock_delay(x->x_clock,\n            (x->x_grain > msectogo ? msectogo : x->x_grain));\n    }\n}\n\nstatic void line_float(t_line *x, t_float f)\n{\n    double timenow = clock_getsystime();\n    if (x->x_gotinlet && x->x_in1val > 0)\n    {\n        if (timenow > x->x_targettime) x->x_setval = x->x_targetval;\n        else x->x_setval = x->x_setval + x->x_1overtimediff *\n            (timenow - x->x_prevtime)\n            * (x->x_targetval - x->x_setval);\n        x->x_prevtime = timenow;\n        x->x_targettime = clock_getsystimeafter(x->x_in1val);\n        x->x_targetval = f;\n        line_tick(x);\n        x->x_gotinlet = 0;\n        x->x_1overtimediff = 1./ (x->x_targettime - timenow);\n        if (x->x_grain <= 0)\n            x->x_grain = DEFAULTLINEGRAIN;\n        clock_delay(x->x_clock,\n            (x->x_grain > x->x_in1val ? x->x_in1val : x->x_grain));\n\n    }\n    else\n    {\n        clock_unset(x->x_clock);\n        x->x_targetval = x->x_setval = f;\n        outlet_float(x->x_obj.ob_outlet, f);\n    }\n    x->x_gotinlet = 0;\n}\n\nstatic void line_ft1(t_line *x, t_floatarg g)\n{\n    x->x_in1val = g;\n    x->x_gotinlet = 1;\n}\n\nstatic void line_stop(t_line *x)\n{\n    if (pd_compatibilitylevel >= 48)\n    {\n        if (clock_getsystime() >= x->x_targettime)\n            x->x_setval = x->x_targetval;\n        else x->x_setval += x->x_1overtimediff *\n            (clock_getsystime() - x->x_prevtime) *\n                (x->x_targetval - x->x_setval);\n    }\n    x->x_targetval = x->x_setval;\n    clock_unset(x->x_clock);\n}\n\nstatic void line_set(t_line *x, t_floatarg f)\n{\n    clock_unset(x->x_clock);\n    x->x_targetval = x->x_setval = f;\n}\n\nstatic void line_free(t_line *x)\n{\n    clock_free(x->x_clock);\n}\n\nstatic void *line_new(t_floatarg f, t_floatarg grain)\n{\n    t_line *x = (t_line *)pd_new(line_class);\n    x->x_targetval = x->x_setval = f;\n    x->x_gotinlet = 0;\n    x->x_1overtimediff = 1;\n    x->x_clock = clock_new(x, (t_method)line_tick);\n    x->x_targettime = x->x_prevtime = clock_getsystime();\n    x->x_grain = grain;\n    outlet_new(&x->x_obj, gensym(\"float\"));\n    inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym(\"float\"), gensym(\"ft1\"));\n    floatinlet_new(&x->x_obj, &x->x_grain);\n    return (x);\n}\n\nstatic void line_setup(void)\n{\n    line_class = class_new(gensym(\"line\"), (t_newmethod)line_new,\n        (t_method)line_free, sizeof(t_line), 0, A_DEFFLOAT, A_DEFFLOAT, 0);\n    class_addmethod(line_class, (t_method)line_ft1,\n        gensym(\"ft1\"), A_FLOAT, 0);\n    class_addmethod(line_class, (t_method)line_stop,\n        gensym(\"stop\"), 0);\n    class_addmethod(line_class, (t_method)line_set,\n        gensym(\"set\"), A_FLOAT, 0);\n    class_addfloat(line_class, (t_method)line_float);\n}\n\n/* -------------------------- timer ------------------------------ */\nstatic t_class *timer_class;\n\ntypedef struct _timer\n{\n    t_object x_obj;\n    double x_settime;\n    double x_moreelapsed;\n    t_float x_unit;\n    int x_samps;\n} t_timer;\n\nstatic void timer_bang(t_timer *x)\n{\n    x->x_settime = clock_getsystime();\n    x->x_moreelapsed = 0;\n}\n\nstatic void timer_bang2(t_timer *x)\n{\n    outlet_float(x->x_obj.ob_outlet,\n        clock_gettimesincewithunits(x->x_settime, x->x_unit, x->x_samps)\n            + x->x_moreelapsed);\n}\n\nstatic void timer_tempo(t_timer *x, t_symbol *unitname, t_floatarg tempo)\n{\n    x->x_moreelapsed +=  clock_gettimesincewithunits(x->x_settime,\n        x->x_unit, x->x_samps);\n    x->x_settime = clock_getsystime();\n    parsetimeunits(x, tempo, unitname, &x->x_unit, &x->x_samps);\n}\n\nstatic void *timer_new(t_symbol *unitname, t_floatarg tempo)\n{\n    t_timer *x = (t_timer *)pd_new(timer_class);\n    x->x_unit = 1;\n    x->x_samps = 0;\n    timer_bang(x);\n    outlet_new(&x->x_obj, gensym(\"float\"));\n    inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym(\"bang\"), gensym(\"bang2\"));\n    if (tempo != 0)\n        timer_tempo(x, unitname, tempo);\n    return (x);\n}\n\nstatic void timer_setup(void)\n{\n    timer_class = class_new(gensym(\"timer\"), (t_newmethod)timer_new, 0,\n        sizeof(t_timer), 0, A_DEFFLOAT, A_DEFSYM, 0);\n    class_addbang(timer_class, timer_bang);\n    class_addmethod(timer_class, (t_method)timer_bang2, gensym(\"bang2\"), 0);\n    class_addmethod(timer_class, (t_method)timer_tempo,\n        gensym(\"tempo\"), A_FLOAT, A_SYMBOL, 0);\n}\n\n\n/* -------------------------- pipe -------------------------- */\n\nstatic t_class *pipe_class;\n\ntypedef struct _hang\n{\n    t_clock *h_clock;\n    struct _hang *h_next;\n    struct _pipe *h_owner;\n    t_gpointer *h_gp;\n    union word h_vec[1];        /* not the actual number. */\n} t_hang;\n\ntypedef struct pipeout\n{\n    t_atom p_atom;\n    t_outlet *p_outlet;\n} t_pipeout;\n\ntypedef struct _pipe\n{\n    t_object x_obj;\n    int x_n;\n    int x_nptr;\n    t_float x_deltime;\n    t_pipeout *x_vec;\n    t_gpointer *x_gp;\n    t_hang *x_hang;\n} t_pipe;\n\nstatic void *pipe_new(t_symbol *s, int argc, t_atom *argv)\n{\n    t_pipe *x = (t_pipe *)pd_new(pipe_class);\n    t_atom defarg, *ap;\n    t_pipeout *vec, *vp;\n    t_gpointer *gp;\n    int nptr = 0;\n    int i;\n    t_float deltime;\n    if (argc)\n    {\n        if (argv[argc-1].a_type != A_FLOAT)\n        {\n            char stupid[80];\n            atom_string(&argv[argc-1], stupid, 79);\n            pd_error(x, \"pipe: %s: bad time delay value\", stupid);\n            deltime = 0;\n        }\n        else deltime = argv[argc-1].a_w.w_float;\n        argc--;\n    }\n    else deltime = 0;\n    if (!argc)\n    {\n        argv = &defarg;\n        argc = 1;\n        SETFLOAT(&defarg, 0);\n    }\n    x->x_n = argc;\n    vec = x->x_vec = (t_pipeout *)getbytes(argc * sizeof(*x->x_vec));\n\n    for (i = argc, ap = argv; i--; ap++)\n        if (ap->a_type == A_SYMBOL && *ap->a_w.w_symbol->s_name == 'p')\n            nptr++;\n\n    gp = x->x_gp = (t_gpointer *)t_getbytes(nptr * sizeof (*gp));\n    x->x_nptr = nptr;\n\n    for (i = 0, vp = vec, ap = argv; i < argc; i++, ap++, vp++)\n    {\n        if (ap->a_type == A_FLOAT)\n        {\n            vp->p_atom = *ap;\n            vp->p_outlet = outlet_new(&x->x_obj, &s_float);\n            if (i) floatinlet_new(&x->x_obj, &vp->p_atom.a_w.w_float);\n        }\n        else if (ap->a_type == A_SYMBOL)\n        {\n            char c = *ap->a_w.w_symbol->s_name;\n            if (c == 's')\n            {\n                SETSYMBOL(&vp->p_atom, &s_symbol);\n                vp->p_outlet = outlet_new(&x->x_obj, &s_symbol);\n                if (i) symbolinlet_new(&x->x_obj, &vp->p_atom.a_w.w_symbol);\n            }\n            else if (c == 'p')\n            {\n                vp->p_atom.a_type = A_POINTER;\n                vp->p_atom.a_w.w_gpointer = gp;\n                gpointer_init(gp);\n                vp->p_outlet = outlet_new(&x->x_obj, &s_pointer);\n                if (i) pointerinlet_new(&x->x_obj, gp);\n                gp++;\n            }\n            else\n            {\n                if (c != 'f') pd_error(x, \"pipe: %s: bad type\",\n                    ap->a_w.w_symbol->s_name);\n                SETFLOAT(&vp->p_atom, 0);\n                vp->p_outlet = outlet_new(&x->x_obj, &s_float);\n                if (i) floatinlet_new(&x->x_obj, &vp->p_atom.a_w.w_float);\n            }\n        }\n    }\n    floatinlet_new(&x->x_obj, &x->x_deltime);\n    x->x_hang = 0;\n    x->x_deltime = deltime;\n    return (x);\n}\n\nstatic void hang_free(t_hang *h)\n{\n    t_pipe *x = h->h_owner;\n    t_gpointer *gp;\n    int i;\n    for (gp = h->h_gp, i = x->x_nptr; i--; gp++)\n        gpointer_unset(gp);\n    freebytes(h->h_gp, x->x_nptr * sizeof(*h->h_gp));\n    clock_free(h->h_clock);\n    freebytes(h, sizeof(*h) + (x->x_n - 1) * sizeof(*h->h_vec));\n}\n\nstatic void hang_tick(t_hang *h)\n{\n    t_pipe *x = h->h_owner;\n    t_hang *h2, *h3;\n    t_pipeout *p;\n    int i;\n    union word *w;\n    if (x->x_hang == h) x->x_hang = h->h_next;\n    else for (h2 = x->x_hang; (h3 = h2->h_next); h2 = h3)\n    {\n        if (h3 == h)\n        {\n            h2->h_next = h3->h_next;\n            break;\n        }\n    }\n    for (i = x->x_n, p = x->x_vec + (x->x_n - 1), w = h->h_vec + (x->x_n - 1);\n        i--; p--, w--)\n    {\n        switch (p->p_atom.a_type)\n        {\n        case A_FLOAT: outlet_float(p->p_outlet, w->w_float); break;\n        case A_SYMBOL: outlet_symbol(p->p_outlet, w->w_symbol); break;\n        case A_POINTER:\n            if (gpointer_check(w->w_gpointer, 1))\n                outlet_pointer(p->p_outlet, w->w_gpointer);\n            else pd_error(x, \"pipe: stale pointer\");\n            break;\n        default: break;\n        }\n    }\n    hang_free(h);\n}\n\nstatic void pipe_list(t_pipe *x, t_symbol *s, int ac, t_atom *av)\n{\n    t_hang *h = (t_hang *)\n        getbytes(sizeof(*h) + (x->x_n - 1) * sizeof(*h->h_vec));\n    t_gpointer *gp, *gp2;\n    t_pipeout *p;\n    int i, n = x->x_n;\n    t_atom *ap;\n    t_word *w;\n    h->h_gp = (t_gpointer *)getbytes(x->x_nptr * sizeof(t_gpointer));\n    if (ac > n)\n    {\n        if (av[n].a_type == A_FLOAT)\n            x->x_deltime = av[n].a_w.w_float;\n        else pd_error(x, \"pipe: symbol or pointer in time inlet\");\n        ac = n;\n    }\n    for (i = 0, gp = x->x_gp, p = x->x_vec, ap = av; i < ac;\n        i++, p++, ap++)\n    {\n        switch (p->p_atom.a_type)\n        {\n        case A_FLOAT: p->p_atom.a_w.w_float = atom_getfloat(ap); break;\n        case A_SYMBOL: p->p_atom.a_w.w_symbol = atom_getsymbol(ap); break;\n        case A_POINTER:\n            gpointer_unset(gp);\n            if (ap->a_type != A_POINTER)\n                pd_error(x, \"pipe: bad pointer\");\n            else\n            {\n                *gp = *(ap->a_w.w_gpointer);\n                if (gp->gp_stub) gp->gp_stub->gs_refcount++;\n            }\n            gp++;\n        default: break;\n        }\n    }\n    for (i = 0, gp = x->x_gp, gp2 = h->h_gp, p = x->x_vec, w = h->h_vec;\n        i < n; i++, p++, w++)\n    {\n        if (p->p_atom.a_type == A_POINTER)\n        {\n            if (gp->gp_stub) gp->gp_stub->gs_refcount++;\n            w->w_gpointer = gp2;\n            *gp2++ = *gp++;\n        }\n        else *w = p->p_atom.a_w;\n    }\n    h->h_next = x->x_hang;\n    x->x_hang = h;\n    h->h_owner = x;\n    h->h_clock = clock_new(h, (t_method)hang_tick);\n    clock_delay(h->h_clock, (x->x_deltime >= 0 ? x->x_deltime : 0));\n}\n\nstatic void pipe_flush(t_pipe *x)\n{\n    while (x->x_hang) hang_tick(x->x_hang);\n}\n\nstatic void pipe_clear(t_pipe *x)\n{\n    t_hang *hang;\n    while ((hang = x->x_hang))\n    {\n        x->x_hang = hang->h_next;\n        hang_free(hang);\n    }\n}\n\nstatic void pipe_free(t_pipe *x)\n{\n    pipe_clear(x);\n    freebytes(x->x_vec, x->x_n * sizeof(*x->x_vec));\n    freebytes(x->x_gp, x->x_nptr * sizeof(*x->x_gp));\n\n}\n\nstatic void pipe_setup(void)\n{\n    pipe_class = class_new(gensym(\"pipe\"),\n        (t_newmethod)pipe_new, (t_method)pipe_free,\n        sizeof(t_pipe), 0, A_GIMME, 0);\n    class_addlist(pipe_class, pipe_list);\n    class_addmethod(pipe_class, (t_method)pipe_flush, gensym(\"flush\"), 0);\n    class_addmethod(pipe_class, (t_method)pipe_clear, gensym(\"clear\"), 0);\n}\n\nvoid x_time_setup(void)\n{\n    delay_setup();\n    metro_setup();\n    line_setup();\n    timer_setup();\n    pipe_setup();\n}\n"
  },
  {
    "path": "libs/libpd/pure-data/src/x_vexp.c",
    "content": "/* Copyright (c) IRCAM.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* \"expr\" was written by Shahrokh Yadegari c. 1989. -msp */\n/* \"expr~\" and \"fexpr~\" conversion by Shahrokh Yadegari c. 1999,2000 */\n\n\n/*\n * Feb 2002 -   added access to variables\n *              multiple expression support\n *              new short hand forms for fexpr~\n *                      now $y or $y1 = $y1[-1] and $y2 = $y2[-1]\n *              --sdy\n *\n * July 2002\n *              fixed bugs introduced in last changes in store and ET_EQ\n *              --sdy\n *\n * Oct 2015\n *              $x[-1] was not equal $x1[-1], not accessing the previous block\n *              (bug fix by Dan Ellis)\n *\n *  July 2017,  Version 0.55\n *      - The arrays now redraw after a store into one of their members\n *              - ex_if() (the \"if()\" function is reworked to only evaluate\n *                either the left or the right args depending on the truth\n *                value of the condition. However, if the condition is a\n *                vector, both the left and the right are evaluated regardless.\n *              - priority of ',' and '=' was switched to fix the bug of using\n *                store \"=\" in functions with multiple arguments, which caused\n *                an error during execution.\n *              - The number of inlet and outlets (MAX_VARS) is now set at 100\n *\n *  Jan 2018, Version 0.56\n *              -fexpr~ now accepts a float in its first input\n *              -Added avg() and Avg() back to the list of functions\n *\n * Oct 2020, Version 0.57\n *\t\t- fixed a bug in fact()\n *\t\t- fixed the bad lvalue bug - \"4 + 5 = 3\" was not caught before\n *\t\t- fact() (factorial) now calculates and returns its value in double\n *\t\t- Added mtof(), mtof(), dbtorms(), rmstodb(), powtodb(), dbtopow()\n */\n\n/*\n * vexp.c -- a variable expression evaluator\n *\n * This modules implements an expression evaluator using the\n * operator-precedence parsing.  It transforms an infix expression\n * to a prefix stack ready to be evaluated.  The expression sysntax\n * is close to that of C.  There are a few operators that are not\n * supported and functions are also recognized.  Strings can be\n * passed to functions when they are quoted in '\"'s. \"[]\" are implemented\n * as an easy way of accessing the content of tables, and the syntax\n * table_name[index].\n * Variables (inlets) are specified with the following syntax: $x#,\n * where x is either i(integers), f(floats), and s(strings); and #\n * is a digit that corresponds to the inlet number.  The string variables\n * can be used as strings when they are quoted and can also be used as\n * table names when they are followed by \"[]\".\n *\n * signal vectors have been added to this implementation:\n * $v# denotes a signal vector\n * $x#[index]  is the value of a sample at the index of a the signal vector\n * $x# is the shorthand for $x#[0]\n * $y[index]   is the value of the sample output at the index of a the\n *             signal output\n * \"index\" for $x#[index] has to have this range (0 <= index < vectorsize)\n * \"index\" for $y[index]  has to have this range (0 <  index < vectorsize)\n */\n\n#include <string.h>\n#include <stdlib.h>\n#include <ctype.h>\n#include \"x_vexp.h\"\n#include <errno.h>\n#ifdef MSP\n#undef isdigit\n#define isdigit(x)      (x >= '0' && x <= '9')\n#endif\n\n#if defined _MSC_VER && (_MSC_VER < 1800)\n#define strtof(a, b) _atoldbl(a, *b)\n#endif\n\n\nchar *atoif(char *s, long int *value, long int *type);\n\nstatic struct ex_ex *ex_lex(struct expr *expr, long int *n);\nstruct ex_ex *ex_match(struct ex_ex *eptr, long int op);\nstruct ex_ex *ex_parse(struct expr *expr, struct ex_ex *iptr,\n                                        struct ex_ex *optr, long int *argc);\nstatic int ex_checklval (struct ex_ex *eptr);\nstruct ex_ex *ex_eval(struct expr *expr, struct ex_ex *eptr,\n                                                struct ex_ex *optr, int i);\n\nint expr_donew(struct expr *exprr, int ac, t_atom *av);\nstruct ex_ex *eval_func(struct expr *expr,struct ex_ex *eptr,\n                                                struct ex_ex *optr, int i);\nstruct ex_ex *eval_tab(struct expr *expr, struct ex_ex *eptr,\n                                                struct ex_ex *optr, int i);\nstruct ex_ex *eval_var(struct expr *expr, struct ex_ex *eptr,\n                                                struct ex_ex *optr, int i);\nstruct ex_ex *eval_store(struct expr *expr, struct ex_ex *eptr,\n                                                struct ex_ex *optr, int i);\nstruct ex_ex *eval_sigidx(struct expr *expr, struct ex_ex *eptr,\n                                                struct ex_ex *optr, int i);\nstatic int cal_sigidx(struct ex_ex *optr,       /* The output value */\n           int i, t_float rem_i,      /* integer and fractinal part of index */\n           int idx,                  /* index of current fexpr~ processing */\n           int vsize,                                       /* vector size */\n           t_float *curvec, t_float *prevec);   /* current and previous table */\nt_ex_func *find_func(char *s);\nvoid ex_dzdetect(struct expr *expr);\n\n#define MAX_ARGS        10\nextern t_ex_func ex_funcs[];\n\nstruct ex_ex nullex = { 0 };\n\nvoid set_tokens (char *s);\nint getoken (struct expr *expr, struct ex_ex *eptr);\nvoid ex_print (struct ex_ex *eptr);\n#ifdef MSP\nvoid atom_string(t_atom *a, char *buf, unsigned int bufsize);\n\nvoid atom_string(t_atom *a, char *buf, unsigned int bufsize)\n{\n    char tbuf[30];\n    switch(a->a_type)\n    {\n    case A_SEMI: strcpy(buf, \";\"); break;\n    case A_COMMA: strcpy(buf, \",\"); break;\n#ifdef PD\n    case A_POINTER:\n        strcpy(buf, \"(pointer)\");\n        break;\n#endif\n    case A_FLOAT:\n        sprintf(tbuf, \"%g\", a->a_w.w_float);\n        if (strlen(tbuf) < bufsize-1) strcpy(buf, tbuf);\n        else if (a->a_w.w_float < 0) strcpy(buf, \"-\");\n        else  strcat(buf, \"+\");\n        break;\n    case A_LONG:\n        sprintf(tbuf, \"%d\", a->a_w.w_long);\n        if (strlen(tbuf) < bufsize-1) strcpy(buf, tbuf);\n        else if (a->a_w.w_float < 0) strcpy(buf, \"-\");\n        else  strcat(buf, \"+\");\n        break;\n    case A_SYMBOL:\n    {\n        char *sp;\n        unsigned int len;\n        int quote;\n        for (sp = a->a_w.w_symbol->s_name, len = 0, quote = 0; *sp; sp++, len++)\n            if (*sp == ';' || *sp == ',' || *sp == '\\\\' ||\n                (*sp == '$' && sp == a->a_w.w_symbol->s_name && sp[1] >= '0'\n                    && sp[1] <= '9'))\n                quote = 1;\n        if (quote)\n        {\n            char *bp = buf, *ep = buf + (bufsize-2);\n            sp = a->a_w.w_symbol->s_name;\n            while (bp < ep && *sp)\n            {\n                if (*sp == ';' || *sp == ',' || *sp == '\\\\' ||\n                    (*sp == '$' && bp == buf && sp[1] >= '0' && sp[1] <= '9'))\n                        *bp++ = '\\\\';\n                *bp++ = *sp++;\n            }\n            if (*sp) *bp++ = '*';\n            *bp = 0;\n            /* post(\"quote %s -> %s\", a->a_w.w_symbol->s_name, buf); */\n        }\n        else\n        {\n            if (len < bufsize-1) strcpy(buf, a->a_w.w_symbol->s_name);\n            else\n            {\n                strncpy(buf, a->a_w.w_symbol->s_name, bufsize - 2);\n                strcpy(buf + (bufsize - 2), \"*\");\n            }\n        }\n    }\n        break;\n#ifdef PD\n    case A_DOLLAR:\n        sprintf(buf, \"$%d\", a->a_w.w_index);\n        break;\n    case A_DOLLSYM:\n        sprintf(buf, \"$%s\", a->a_w.w_symbol->s_name);\n                break;\n#else /* MAX */\n case A_DOLLAR:\n        sprintf(buf, \"$%s\", a->a_w.w_symbol->s_name);\n        break;\n#endif\n    default:\n        post(\"atom_string bug\");\n    }\n}\n#endif /* MSP */\n/*\n * expr_donew -- create a new \"expr\" object.\n *           returns 1 on failure, 0 on success.\n */\nint\nexpr_donew(struct expr *expr, int ac, t_atom *av)\n{\n        struct ex_ex *list;\n        struct ex_ex *ret;\n        long max_node = 0;              /* maximum number of nodes needed */\n        char *exp_string;\n        int exp_strlen;\n        t_binbuf *b;\n        int i;\n\n        memset(expr->exp_var, 0, MAX_VARS * sizeof (*expr->exp_var));\n#ifdef PD\n        b = binbuf_new();\n        binbuf_add(b, ac, av);\n        binbuf_gettext(b, &exp_string, &exp_strlen);\n        binbuf_free(b);\n#else /* MSP */\n {\n    char *buf = getbytes(0), *newbuf;\n    int length = 0;\n    char string[250];\n    t_atom *ap;\n    int indx;\n\n    for (ap = av, indx = 0; indx < ac; indx++, ap = ++av) {\n        int newlength;\n\n        if ((ap->a_type == A_SEMI || ap->a_type == A_COMMA) &&\n                length && buf[length-1] == ' ')\n                        length--;\n        atom_string(ap, string, 250);\n        newlength = length + strlen(string) + 1;\n        if (!(newbuf = t_resizebytes(buf, length, newlength)))\n                                break;\n        buf = newbuf;\n        strcpy(buf + length, string);\n        length = newlength;\n        if (ap->a_type == A_SEMI)\n                        buf[length-1] = '\\n';\n        else\n                        buf[length-1] = ' ';\n    }\n\n    if (length && buf[length-1] == ' ') {\n        if (newbuf = t_resizebytes(buf, length, length-1))\n        {\n            buf = newbuf;\n            length--;\n        }\n    }\n    exp_string = buf;\n    exp_strlen  = length;\n }\n#endif\n        exp_string = (char *)t_resizebytes(exp_string, exp_strlen,exp_strlen+1);\n        exp_string[exp_strlen] = 0;\n        expr->exp_string = exp_string;\n        expr->exp_str = exp_string;\n        expr->exp_nexpr = 0;\n        ret = (struct ex_ex *) 0;\n        /*\n         * if ret == 0 it means that we have no expression\n         * so we let the pass go through to build a single null stack\n         */\n        while (*expr->exp_str || !ret) {\n                list = ex_lex(expr, &max_node);\n                if (!list) {            /* syntax error */\n                        goto error;\n                }\n                expr->exp_stack[expr->exp_nexpr] =\n                  (struct ex_ex *)fts_malloc(max_node * sizeof (struct ex_ex));\n                                if (!expr->exp_stack[expr->exp_nexpr]) {\n                                        post_error( (fts_object_t *) expr,\n                                                \"expr: malloc for expr nodes failed\\n\");\n                                        goto error;\n                                }\n                                expr->exp_stack[expr->exp_nexpr][max_node-1].ex_type=0;\n                expr->exp_nexpr++;\n                ret = ex_match(list, (long)0);\n                if (expr->exp_nexpr  > MAX_VARS)\n                    /* we cannot exceed MAX_VARS '$' variables */\n                {\n                        post_error((fts_object_t *) expr,\n                            \"expr: too many variables (maximum %d allowed)\",\n                                MAX_VARS);\n                        goto error;\n                }\n                if (!ret)               /* syntax error */\n                        goto error;\n                ret = ex_parse(expr,\n                        list, expr->exp_stack[expr->exp_nexpr - 1], (long *)0);\n                if (!ret || ex_checklval(expr->exp_stack[expr->exp_nexpr - 1]))\n                        goto error;\n                fts_free(list);\n        }\n        *ret = nullex;\n        t_freebytes(exp_string, exp_strlen+1);\n        return (0);\nerror:\n        for (i = 0; i < expr->exp_nexpr; i++) {\n                fts_free(expr->exp_stack[i]);\n                expr->exp_stack[i] = 0;\n        }\n        expr->exp_nexpr = 0;\n        if (list)\n                fts_free(list);\n        t_freebytes(exp_string, exp_strlen+1);\n        return (1);\n}\n\n/*\n * ex_lex -- This routine is a bit more than a lexical parser since it will\n *           also do some syntax checking.  It reads the string s and will\n *           return a linked list of struct ex_ex.\n *           It will also put the number of the nodes in *n.\n */\nstruct ex_ex *\nex_lex(struct expr *expr, long int *n)\n{\n        struct ex_ex *list_arr;\n        struct ex_ex *exptr;\n        long non = 0;           /* number of nodes */\n        long maxnode = 0;\n\n        list_arr = (struct ex_ex *)fts_malloc(sizeof (struct ex_ex) * MINODES);\n        if (! list_arr) {\n                post(\"ex_lex: no mem\\n\");\n                return ((struct ex_ex *)0);\n        }\n        exptr = list_arr;\n        maxnode = MINODES;\n\n        while (8)\n        {\n                if (non >= maxnode) {\n                        maxnode += MINODES;\n\n                        list_arr = fts_realloc((void *)list_arr,\n                                        sizeof (struct ex_ex) * maxnode);\n                        if (!list_arr) {\n                                post(\"ex_lex: no mem\\n\");\n                                return ((struct ex_ex *)0);\n                        }\n                        exptr = &(list_arr)[non];\n                }\n\n                if (getoken(expr, exptr)) {\n                        fts_free(list_arr);\n                        return ((struct ex_ex *)0);\n                }\n                non++;\n\n                if (!exptr->ex_type)\n                        break;\n\n                exptr++;\n        }\n        *n = non;\n\n        return list_arr;\n}\n\n/*\n * ex_match -- this routine walks through the eptr and matches the\n *             perentheses and brackets, it also converts the function\n *             names to a pointer to the describing structure of the\n *             specified function\n */\n/* operator to match */\nstruct ex_ex *\nex_match(struct ex_ex *eptr, long int op)\n{\n        int firstone = 1;\n        struct ex_ex *ret;\n        t_ex_func *fun;\n\n        for (; 8; eptr++, firstone = 0) {\n                switch (eptr->ex_type) {\n                case 0:\n                        if (!op)\n                                return (eptr);\n                        post(\"expr syntax error: an open %s not matched\\n\",\n                            op == OP_RP ? \"parenthesis\" : \"bracket\");\n                        return (exNULL);\n                case ET_INT:\n                case ET_FLT:\n                case ET_II:\n                case ET_FI:\n                case ET_SI:\n                case ET_VI:\n                case ET_SYM:\n                case ET_VSYM:\n                        continue;\n                case ET_YO:\n                        if (eptr[1].ex_type != ET_OP || eptr[1].ex_op != OP_LB)\n                                eptr->ex_type = ET_YOM1;\n                        continue;\n                case ET_XI:\n                        if (eptr[1].ex_type != ET_OP || eptr[1].ex_op != OP_LB)\n                                eptr->ex_type = ET_XI0;\n                        continue;\n                                /*\n                                 * tables, functions, parenthesis, and brackets, are marked as\n                                 * operations, and they are assigned their proper operation\n                                 * in this function. Thus, if we arrive to any of these in this\n                                 * type tokens at this location, we must have had some error\n                                 */\n                case ET_TBL:\n                case ET_FUNC:\n                case ET_LP:\n                case ET_LB:\n                        post(\"ex_match: unexpected type, %ld\\n\", eptr->ex_type);\n                        return (exNULL);\n                case ET_OP:\n                        if (op == eptr->ex_op)\n                                return (eptr);\n                        /*\n                         * if we are looking for a right peranthesis\n                         * or a right bracket and find the other kind,\n                         * it has to be a syntax error\n                         */\n                        if ((eptr->ex_op == OP_RP && op == OP_RB) ||\n                            (eptr->ex_op == OP_RB && op == OP_RP)) {\n                                post(\"expr syntax error: prenthesis or brackets not matched\\n\");\n                                return (exNULL);\n                        }\n                        /*\n                         * Up to now we have marked the unary minuses as\n                         * subrtacts.  Any minus that is the first one in\n                         * chain or is preceded by anything except ')' and\n                         * ']' is a unary minus.\n                         */\n                        if (eptr->ex_op == OP_SUB) {\n                                ret = eptr - 1;\n                                if (firstone ||  (ret->ex_type == ET_OP &&\n                                    ret->ex_op != OP_RB && ret->ex_op != OP_RP))\n                                        eptr->ex_op = OP_UMINUS;\n                        } else if (eptr->ex_op == OP_LP) {\n                                ret = ex_match(eptr + 1, OP_RP);\n                                if (!ret)\n                                        return (ret);\n                                eptr->ex_type = ET_LP;\n                                eptr->ex_ptr = (char *) ret;\n                                eptr = ret;\n                        } else if (eptr->ex_op == OP_LB) {\n                                ret = ex_match(eptr + 1, OP_RB);\n                                if (!ret)\n                                        return (ret);\n                                /*\n                                 * this is a special case handling\n                                 * for $1, $2 processing in Pd\n                                 *\n                                 * Pure data translates $#[x] (e.g. $1[x]) to 0[x]\n                                 * for abstracting patches so that later\n                                 * in the instantiation of the abstraction\n                                 * the $# is replaced with the proper argument\n                                 * of the abstraction\n                                 * so we change 0[x] to a special table pointing to null\n                                 * and catch errors in execution time\n                                 */\n                                if (!firstone && (eptr - 1)->ex_type == ET_INT &&\n                                    ((eptr - 1)->ex_int == 0)) {\n                                        (eptr - 1)->ex_type = ET_TBL;\n                                        (eptr - 1)->ex_ptr = (char *)0;\n                                }\n                                eptr->ex_type = ET_LB;\n                                eptr->ex_ptr = (char *) ret;\n                                eptr = ret;\n                        }\n                        continue;\n                case ET_STR:\n                        if (eptr[1].ex_op == OP_LB) {\n                                char *tmp;\n\n                                eptr->ex_type = ET_TBL;\n                                tmp = eptr->ex_ptr;\n                                if (ex_getsym(tmp, (t_symbol **)&(eptr->ex_ptr))) {\n                                        post(\"expr: syntax error: problms with ex_getsym\\n\");\n                                        return (exNULL);\n                                }\n                                fts_free((void *)tmp);\n                        } else if (eptr[1].ex_op == OP_LP) {\n                                fun = find_func(eptr->ex_ptr);\n                                if (!fun) {\n                                        post(\n                                         \"expr: error: function %s not found\\n\",\n                                                                eptr->ex_ptr);\n                                        return (exNULL);\n                                }\n                                eptr->ex_type = ET_FUNC;\n                                eptr->ex_ptr = (char *) fun;\n                        } else {\n                                char *tmp;\n\n                                if (eptr[1].ex_type && eptr[1].ex_type!=ET_OP){\n                                        post(\"expr: syntax error: bad string '%s'\\n\", eptr->ex_ptr);\n                                        return (exNULL);\n                                }\n                                /* it is a variable */\n                                eptr->ex_type = ET_VAR;\n                                tmp = eptr->ex_ptr;\n                                if (ex_getsym(tmp,\n                                                (t_symbol **)&(eptr->ex_ptr))) {\n                                        post(\"expr: variable '%s' not found\",tmp);\n                                        return (exNULL);\n                                }\n                        }\n                        continue;\n                default:\n                        post(\"ex_match: bad type\\n\");\n                        return (exNULL);\n                }\n        }\n        /* NOTREACHED */\n}\n\n/*\n * ex_parse -- This function is called when we have already done some\n *             parsing on the expression, and we have already matched\n *             our brackets and parenthesis.  The main job of this\n *             function is to convert the infix expression to the\n *             prefix form.\n *             First we find the operator with the lowest precedence and\n *             put it on the stack ('optr', it is really just an array), then\n *             we call ourself (ex_parse()), on its arguments (unary operators\n *             only have one operator.)\n *             When \"argc\" is set it means that we are parsing the arguments\n *             of a function and we will increment *argc anytime we find\n *             a segment that can qualify as an argument (counting commas).\n *\n *             returns 0 on syntax error\n */\n/* number of argument separated by comma */\nstruct ex_ex *\nex_parse(struct expr *x, struct ex_ex *iptr, struct ex_ex *optr, long int *argc)\n{\n        struct ex_ex *eptr;\n        struct ex_ex *lowpre = 0;       /* pointer to the lowest precedence */\n        struct ex_ex savex = { 0 };\n        struct ex_ex *tmpex;\n        long pre = HI_PRE;\n        long count;\n\n        if (!iptr) {\n                post(\"ex_parse: input is null, iptr = 0x%lx\\n\", iptr);\n                return (exNULL);\n        }\n        if (!iptr->ex_type)\n                return (exNULL);\n\n        /*\n         * the following loop finds the lowest precedence operator in the\n         * the input token list, comma is explicitly checked here since\n         * that is a special operator and is only legal in functions\n         */\n        for (eptr = iptr, count = 0; eptr->ex_type; eptr++, count++)\n                switch (eptr->ex_type) {\n                case ET_SYM:\n                case ET_VSYM:\n                        if (!argc) {\n                                post(\"expr: syntax error: symbols allowed for functions only\\n\");\n                                ex_print(eptr);\n                                return (exNULL);\n                        }   /* falls through */\n                case ET_INT:\n                case ET_FLT:\n                case ET_II:\n                case ET_FI:\n                case ET_XI0:\n                case ET_YOM1:\n                case ET_VI:\n                case ET_VAR:\n                        if (!count && !eptr[1].ex_type) {\n                                *optr = *eptr;\n                                                                tmpex = optr;\n                                                                tmpex->ex_end = ++optr;\n                                return (optr);\n                        }\n                        break;\n                case ET_XI:\n                case ET_YO:\n                case ET_SI:\n                case ET_TBL:\n                        if (eptr[1].ex_type != ET_LB) {\n                                post(\"expr: syntax error: brackets missing\\n\");\n                                ex_print(eptr);\n                                return (exNULL);\n                        }\n                        /* if this table is the only token, parse the table */\n                        if (!count &&\n                            !((struct ex_ex *) eptr[1].ex_ptr)[1].ex_type) {\n                                savex = *((struct ex_ex *) eptr[1].ex_ptr);\n                                *((struct ex_ex *) eptr[1].ex_ptr) = nullex;\n                                *optr = *eptr;\n                                lowpre = ex_parse(x, &eptr[2], optr + 1, (long *)0);\n                                *((struct ex_ex *) eptr[1].ex_ptr) = savex;\n                                                                optr->ex_end = lowpre;\n                                return(lowpre);\n                        }\n                        eptr = (struct ex_ex *) eptr[1].ex_ptr;\n                        break;\n                case ET_OP:\n                        if (eptr->ex_op == OP_COMMA) {\n                                if (!argc || !count || !eptr[1].ex_type) {\n                                        post(\"expr: syntax error: illegal comma\\n\");\n                                        ex_print(eptr[1].ex_type ? eptr : iptr);\n                                        return (exNULL);\n                                }\n                        }\n                        if (!eptr[1].ex_type) {\n                                post(\"expr: syntax error: missing operand\\n\");\n                                ex_print(iptr);\n                                return (exNULL);\n                        }\n                        if ((eptr->ex_op & PRE_MASK) <= pre) {\n                                pre = eptr->ex_op & PRE_MASK;\n                                lowpre = eptr;\n                        }\n                        break;\n                case ET_FUNC:\n                        if (eptr[1].ex_type != ET_LP) {\n                                post(\"expr: ex_parse: no parenthesis\\n\");\n                                return (exNULL);\n                        }\n                        /* if this function is the only token, parse it */\n                        if (!count &&\n                            !((struct ex_ex *) eptr[1].ex_ptr)[1].ex_type) {\n                                long ac;\n\n                                if (eptr[1].ex_ptr == (char *) &eptr[2]) {\n                                        post(\"expr: syntax error: missing argument\\n\");\n                                        ex_print(eptr);\n                                        return (exNULL);\n                                }\n                                ac = 0;\n                                savex = *((struct ex_ex *) eptr[1].ex_ptr);\n                                *((struct ex_ex *) eptr[1].ex_ptr) = nullex;\n                                *optr = *eptr;\n                                lowpre = ex_parse(x, &eptr[2], optr + 1, &ac);\n                                if (!lowpre)\n                                        return (exNULL);\n                                ac++;\n                                if (ac !=\n                                    ((t_ex_func *)eptr->ex_ptr)->f_argc){\n                                        post(\"expr: syntax error: function '%s' needs %ld arguments\\n\",\n                                            ((t_ex_func *)eptr->ex_ptr)->f_name,\n                                            ((t_ex_func *)eptr->ex_ptr)->f_argc);\n                                        return (exNULL);\n                                }\n                                *((struct ex_ex *) eptr[1].ex_ptr) = savex;\n                                                                optr->ex_end = lowpre;\n                                return (lowpre);\n                        }\n                        eptr = (struct ex_ex *) eptr[1].ex_ptr;\n                        break;\n                case ET_LP:\n                case ET_LB:\n                        if (!count &&\n                            !((struct ex_ex *) eptr->ex_ptr)[1].ex_type) {\n                                if (eptr->ex_ptr == (char *)(&eptr[1])) {\n                                        post(\"expr: syntax error: empty '%s'\\n\",\n                                            eptr->ex_type==ET_LP?\"()\":\"[]\");\n                                        ex_print(eptr);\n                                        return (exNULL);\n                                }\n                                savex = *((struct ex_ex *) eptr->ex_ptr);\n                                *((struct ex_ex *) eptr->ex_ptr) = nullex;\n                                lowpre = ex_parse(x, &eptr[1], optr, (long *)0);\n                                *((struct ex_ex *) eptr->ex_ptr) = savex;\n                                                                optr->ex_end = lowpre;\n                                return (lowpre);\n                        }\n                        eptr = (struct ex_ex *)eptr->ex_ptr;\n                        break;\n                case ET_STR:\n                default:\n                        ex_print(eptr);\n                        post(\"expr: ex_parse: type = 0x%lx\\n\", eptr->ex_type);\n                        return (exNULL);\n                }\n\n        if (pre == HI_PRE) {\n                post(\"expr: syntax error: missing operation\\n\");\n                ex_print(iptr);\n                return (exNULL);\n        }\n        if (count < 2) {\n                post(\"expr: syntax error: mission operand\\n\");\n                ex_print(iptr);\n                return (exNULL);\n        }\n        if (count == 2) {\n                if (lowpre != iptr) {\n                        post(\"expr: ex_parse: unary operator should be first\\n\");\n                        return (exNULL);\n                }\n                if (!unary_op(lowpre->ex_op)) {\n                        post(\"expr: syntax error: not a uniary operator\\n\");\n                        ex_print(iptr);\n                        return (exNULL);\n                }\n                *optr = *lowpre;\n                eptr = ex_parse(x, &lowpre[1], optr + 1, argc);\n                                optr->ex_end = eptr;\n                return (eptr);\n        }\n        /* this is the case of using unary operator as a binary opetator */\n        if (count == 3 && unary_op(lowpre->ex_op)) {\n                post(\"expr: syntax error, missing operand before unary operator\\n\");\n                ex_print(iptr);\n                return (exNULL);\n        }\n        if (lowpre == iptr) {\n                post(\"expr: syntax error: mission operand\\n\");\n                ex_print(iptr);\n                return (exNULL);\n        }\n        savex = *lowpre;\n        *lowpre = nullex;\n        if (savex.ex_op != OP_COMMA) {\n            *optr = savex;\n                eptr = ex_parse(x, iptr, optr + 1, argc);\n                } else {\n            (*argc)++;\n                eptr = ex_parse(x, iptr, optr, argc);\n                }\n        if (eptr) {\n                eptr = ex_parse(x, &lowpre[1], eptr, argc);\n                *lowpre = savex;\n        }\n                optr->ex_end = eptr;\n        return (eptr);\n}\n\n/*\n * ex_checklval -- check the left value for all stores ('=')\n *                 all left values should either be a variable or a table\n *                 return 1 if syntax error\n *                 return 0 on success\n */\n\nstatic int\nex_checklval(struct ex_ex *eptr)\n{\n        struct ex_ex *extmp;\n\n        extmp = eptr->ex_end;\n        while (eptr->ex_type && eptr != extmp) {\n\t\t\t\tif (eptr->ex_type == ET_OP && eptr->ex_op == OP_STORE) {\n                        if (eptr[1].ex_type != ET_VAR &&\n                            eptr[1].ex_type != ET_SI &&\n                            eptr[1].ex_type != ET_TBL) {\n                                post(\"Bad left value: \");\n                                ex_print(eptr);\n                               return (1);\n                         }\n\t\t\t\t}\n\t\t\t\teptr++;\n        }\n        return (0);\n}\n\n\n/*\n * this is the divide zero check for a non divide operator\n */\n#define DZC(ARG1,OPR,ARG2)      (ARG1 OPR ARG2)\n\n#define EVAL(OPR);                                                      \\\neptr = ex_eval(expr, ex_eval(expr, eptr, &left, idx), &right, idx);     \\\nswitch (left.ex_type) {                                                 \\\ncase ET_INT:                                                            \\\n        switch(right.ex_type) {                                         \\\n        case ET_INT:                                                    \\\n                if (optr->ex_type == ET_VEC) {                          \\\n                        op = optr->ex_vec;                              \\\n                        scalar = (t_float)DZC(left.ex_int, OPR, right.ex_int); \\\n                        for (j = 0; j < expr->exp_vsize; j++)           \\\n                                *op++ = scalar;                         \\\n                } else {                                                \\\n                        optr->ex_type = ET_INT;                         \\\n                        optr->ex_int = DZC(left.ex_int, OPR, right.ex_int); \\\n                }                                                       \\\n                break;                                                  \\\n        case ET_FLT:                                                    \\\n                if (optr->ex_type == ET_VEC) {                          \\\n                        op = optr->ex_vec;                              \\\n                        scalar = DZC(((t_float)left.ex_int), OPR, right.ex_flt);\\\n                        for (j = 0; j < expr->exp_vsize; j++)           \\\n                                *op++ = scalar;                         \\\n                } else {                                                \\\n                        optr->ex_type = ET_FLT;                         \\\n                        optr->ex_flt = DZC(((t_float)left.ex_int), OPR,   \\\n                                                        right.ex_flt);  \\\n                }                                                       \\\n                break;                                                  \\\n        case ET_VEC:                                                    \\\n        case ET_VI:                                                     \\\n                if (optr->ex_type != ET_VEC) {                          \\\n                        if (optr->ex_type == ET_VI) {                   \\\n                                post(\"expr~: Int. error %d\", __LINE__); \\\n                                abort();                                \\\n                        }                                               \\\n                        optr->ex_type = ET_VEC;                         \\\n                        optr->ex_vec = (t_float *)                      \\\n                          fts_malloc(sizeof (t_float)*expr->exp_vsize); \\\n                }                                                       \\\n                scalar = left.ex_int;                                   \\\n                rp = right.ex_vec;                                      \\\n                op = optr->ex_vec;                                      \\\n                for (i = 0; i < expr->exp_vsize; i++) {                 \\\n                        *op++ = DZC (scalar, OPR, *rp);                 \\\n                        rp++;                                           \\\n                }                                                       \\\n                break;                                                  \\\n        case ET_SYM:                                                    \\\n        default:                                                        \\\n                post_error((fts_object_t *) expr,                       \\\n                      \"expr: ex_eval(%d): bad right type %ld\\n\",        \\\n                                              __LINE__, right.ex_type); \\\n                nullret = 1;                                            \\\n        }                                                               \\\n        break;                                                          \\\ncase ET_FLT:                                                            \\\n        switch(right.ex_type) {                                         \\\n        case ET_INT:                                                    \\\n                if (optr->ex_type == ET_VEC) {                          \\\n                        op = optr->ex_vec;                              \\\n                        scalar = DZC((t_float) left.ex_flt, OPR, right.ex_int); \\\n                        for (j = 0; j < expr->exp_vsize; j++)           \\\n                                *op++ = scalar;                         \\\n                } else {                                                \\\n                        optr->ex_type = ET_FLT;                         \\\n                        optr->ex_flt = DZC(left.ex_flt, OPR, right.ex_int); \\\n                }                                                       \\\n                break;                                                  \\\n        case ET_FLT:                                                    \\\n                if (optr->ex_type == ET_VEC) {                          \\\n                        op = optr->ex_vec;                              \\\n                        scalar = DZC(left.ex_flt, OPR, right.ex_flt);   \\\n                        for (j = 0; j < expr->exp_vsize; j++)           \\\n                                *op++ = scalar;                         \\\n                } else {                                                \\\n                        optr->ex_type = ET_FLT;                         \\\n                        optr->ex_flt= DZC(left.ex_flt, OPR, right.ex_flt); \\\n                }                                                       \\\n                break;                                                  \\\n        case ET_VEC:                                                    \\\n        case ET_VI:                                                     \\\n                if (optr->ex_type != ET_VEC) {                          \\\n                        if (optr->ex_type == ET_VI) {                   \\\n                                post(\"expr~: Int. error %d\", __LINE__); \\\n                                abort();                                \\\n                        }                                               \\\n                        optr->ex_type = ET_VEC;                         \\\n                        optr->ex_vec = (t_float *)                      \\\n                          fts_malloc(sizeof (t_float)*expr->exp_vsize); \\\n                }                                                       \\\n                scalar = left.ex_flt;                                   \\\n                rp = right.ex_vec;                                      \\\n                op = optr->ex_vec;                                      \\\n                for (i = 0; i < expr->exp_vsize; i++) {                 \\\n                        *op++ = DZC(scalar, OPR, *rp);                  \\\n                        rp++;                                           \\\n                }                                                       \\\n                break;                                                  \\\n        case ET_SYM:                                                    \\\n        default:                                                        \\\n                post_error((fts_object_t *) expr,                       \\\n                      \"expr: ex_eval(%d): bad right type %ld\\n\",        \\\n                                              __LINE__, right.ex_type); \\\n                nullret = 1;                                            \\\n        }                                                               \\\n        break;                                                          \\\ncase ET_VEC:                                                            \\\ncase ET_VI:                                                             \\\n        if (optr->ex_type != ET_VEC) {                                  \\\n                if (optr->ex_type == ET_VI) {                           \\\n                        post(\"expr~: Int. error %d\", __LINE__);         \\\n                        abort();                                        \\\n                }                                                       \\\n                optr->ex_type = ET_VEC;                                 \\\n                optr->ex_vec = (t_float *)                              \\\n                  fts_malloc(sizeof (t_float)*expr->exp_vsize);         \\\n        }                                                               \\\n        op = optr->ex_vec;                                              \\\n        lp = left.ex_vec;                                               \\\n        switch(right.ex_type) {                                         \\\n        case ET_INT:                                                    \\\n                scalar = right.ex_int;                                  \\\n                for (i = 0; i < expr->exp_vsize; i++) {                 \\\n                        *op++ = DZC(*lp, OPR, scalar);                  \\\n                        lp++;                                           \\\n                }                                                       \\\n                break;                                                  \\\n        case ET_FLT:                                                    \\\n                scalar = right.ex_flt;                                  \\\n                for (i = 0; i < expr->exp_vsize; i++) {                 \\\n                        *op++ = DZC(*lp, OPR, scalar);                  \\\n                        lp++;                                           \\\n                }                                                       \\\n                break;                                                  \\\n        case ET_VEC:                                                    \\\n        case ET_VI:                                                     \\\n                rp = right.ex_vec;                                      \\\n                for (i = 0; i < expr->exp_vsize; i++) {                 \\\n                        /*                                              \\\n                         * on a RISC processor one could copy           \\\n                         * 8 times in each round to get a considerable  \\\n                         * improvement                                  \\\n                         */                                             \\\n                        *op++ = DZC(*lp, OPR, *rp);                     \\\n                        rp++; lp++;                                     \\\n                }                                                       \\\n                break;                                                  \\\n        case ET_SYM:                                                    \\\n        default:                                                        \\\n                post_error((fts_object_t *) expr,                       \\\n                      \"expr: ex_eval(%d): bad right type %ld\\n\",        \\\n                                              __LINE__, right.ex_type); \\\n                nullret = 1;                                            \\\n        }                                                               \\\n        break;                                                          \\\ncase ET_SYM:                                                            \\\ndefault:                                                                \\\n                post_error((fts_object_t *) expr,                       \\\n                      \"expr: ex_eval(%d): bad left type %ld\\n\",         \\\n                                              __LINE__, left.ex_type);  \\\n}                                                                       \\\nbreak;\n\n/*\n * evaluate a unary operator, TYPE is applied to float operands\n */\n#define EVAL_UNARY(OPR, TYPE)                                           \\\n        eptr = ex_eval(expr, eptr, &left, idx);                         \\\n        switch(left.ex_type) {                                          \\\n        case ET_INT:                                                    \\\n                if (optr->ex_type == ET_VEC) {                          \\\n                        ex_mkvector(optr->ex_vec,(t_float)(OPR left.ex_int),\\\n                                                        expr->exp_vsize);\\\n                        break;                                          \\\n                }                                                       \\\n                optr->ex_type = ET_INT;                                 \\\n                optr->ex_int = OPR left.ex_int;                         \\\n                break;                                                  \\\n        case ET_FLT:                                                    \\\n                if (optr->ex_type == ET_VEC) {                          \\\n                        ex_mkvector(optr->ex_vec, OPR (TYPE left.ex_flt),\\\n                                                        expr->exp_vsize);\\\n                        break;                                          \\\n                }                                                       \\\n                optr->ex_type = ET_FLT;                                 \\\n                optr->ex_flt = OPR (TYPE left.ex_flt);                  \\\n                break;                                                  \\\n        case ET_VI:                                                     \\\n        case ET_VEC:                                                    \\\n                j = expr->exp_vsize;                                    \\\n                if (optr->ex_type != ET_VEC) {                          \\\n                        optr->ex_type = ET_VEC;                         \\\n                        optr->ex_vec = (t_float *)                      \\\n                          fts_malloc(sizeof (t_float)*expr->exp_vsize); \\\n                }                                                       \\\n                op = optr->ex_vec;                                      \\\n                lp = left.ex_vec;                                       \\\n                j = expr->exp_vsize;                                    \\\n                for (i = 0; i < j; i++)                                 \\\n                        *op++ = OPR (TYPE *lp++);                       \\\n                break;                                                  \\\n        default:                                                        \\\n                post_error((fts_object_t *) expr,                       \\\n                        \"expr: ex_eval(%d): bad left type %ld\\n\",       \\\n                                              __LINE__, left.ex_type);  \\\n                nullret++;                                              \\\n        }                                                               \\\n        break;\n\nvoid\nex_mkvector(t_float *fp, t_float x, int size)\n{\n        while (size--)\n                *fp++ = x;\n}\n\n/*\n * ex_dzdetect -- divide by zero detected\n */\nvoid\nex_dzdetect(struct expr *expr)\n{\n        char *etype;\n\n        if ((!expr->exp_error) & EE_DZ) {\n                if (IS_EXPR(expr))\n                        etype = \"expr\";\n                else if (IS_EXPR_TILDE(expr))\n                        etype = \"expr~\";\n                else if (IS_FEXPR_TILDE(expr))\n                        etype = \"fexpr~\";\n                else {\n                        post (\"expr -- ex_dzdetect internal error\");\n                        etype = \"\";\n                }\n                post (\"%s divide by zero detected\", etype);\n                expr->exp_error |= EE_DZ;\n        }\n}\n\n\n/*\n * ex_eval -- evaluate the array of prefix expression\n *            ex_eval returns the pointer to the first unevaluated node\n *            in the array.  This is a recursive routine.\n */\n\n/* SDY - potential memory leak\nall the returns in this function need to be changed so that the code\nends up at the end to check for newly allocated right and left vectors which\nneed to be freed\n\nlook into the variable nullret\n*/\nstruct ex_ex *\nex_eval(struct expr *expr, struct ex_ex *eptr, struct ex_ex *optr, int idx)\n/* the expr object data pointer */\n/* the operation stack */\n/* the result pointer */\n/* the sample number processed for fexpr~ */\n{\n        int i, j;\n        t_float *lp, *rp, *op; /* left, right, and out pointer to vectors */\n        t_float scalar;\n        int nullret = 0;                /* did we have an error */\n        struct ex_ex left = { 0 }, right = { 0 };       /* left and right operands */\n\n        left.ex_type = 0;\n        left.ex_int = 0;\n        right.ex_type = 0;\n        right.ex_int = 0;\n\n        if (!eptr)\n                return (exNULL);\n        switch (eptr->ex_type) {\n        case ET_INT:\n                if (optr->ex_type == ET_VEC)\n                        ex_mkvector(optr->ex_vec, (t_float) eptr->ex_int,\n                                                                expr->exp_vsize);\n                else\n                        *optr = *eptr;\n                return (++eptr);\n\n        case ET_FLT:\n\n                if (optr->ex_type == ET_VEC)\n                        ex_mkvector(optr->ex_vec, eptr->ex_flt, expr->exp_vsize);\n                else\n                        *optr = *eptr;\n                return (++eptr);\n\n        case ET_SYM:\n                if (optr->ex_type == ET_VEC) {\n                        post_error((fts_object_t *) expr,\n                              \"expr: ex_eval: cannot turn string to vector\\n\");\n                        return (exNULL);\n                }\n                *optr = *eptr;\n                return (++eptr);\n        case ET_II:\n                if (eptr->ex_int == -1) {\n                        post_error((fts_object_t *) expr,\n                            \"expr: ex_eval: inlet number not set\\n\");\n                        return (exNULL);\n                }\n                if (optr->ex_type == ET_VEC) {\n                        ex_mkvector(optr->ex_vec,\n                               (t_float)expr->exp_var[eptr->ex_int].ex_int,\n                                                                expr->exp_vsize);\n                } else {\n                        optr->ex_type = ET_INT;\n                        optr->ex_int = expr->exp_var[eptr->ex_int].ex_int;\n                }\n                return (++eptr);\n        case ET_FI:\n                if (eptr->ex_int == -1) {\n                        post_error((fts_object_t *) expr,\n                            \"expr: ex_eval: inlet number not set\\n\");\n                        return (exNULL);\n                }\n                if (optr->ex_type == ET_VEC) {\n                        ex_mkvector(optr->ex_vec,\n                             expr->exp_var[eptr->ex_int].ex_flt, expr->exp_vsize);\n                } else {\n                        optr->ex_type = ET_FLT;\n                        optr->ex_flt = expr->exp_var[eptr->ex_int].ex_flt;\n                }\n                return (++eptr);\n\n        case ET_VSYM:\n                if (optr->ex_type == ET_VEC) {\n                        post_error((fts_object_t *) expr,\n                            \"expr: IntErr. vsym in for vec out\\n\");\n                        return (exNULL);\n                }\n                if (eptr->ex_int == -1) {\n                        post_error((fts_object_t *) expr,\n                                \"expr: ex_eval: inlet number not set\\n\");\n                        return (exNULL);\n                }\n                optr->ex_type = ET_SYM;\n                optr->ex_ptr = expr->exp_var[eptr->ex_int].ex_ptr;\n                return(++eptr);\n\n        case ET_VI:\n                if (optr->ex_type != ET_VEC)\n                        *optr = expr->exp_var[eptr->ex_int];\n                else if (optr->ex_vec != expr->exp_var[eptr->ex_int].ex_vec)\n                        memcpy(optr->ex_vec, expr->exp_var[eptr->ex_int].ex_vec,\n                                        expr->exp_vsize * sizeof (t_float));\n                return(++eptr);\n        case ET_VEC:\n                if (optr->ex_type != ET_VEC) {\n                        optr->ex_type = ET_VEC;\n                        optr->ex_vec = eptr->ex_vec;\n                        eptr->ex_type = ET_INT;\n                        eptr->ex_int = 0;\n                } else if (optr->ex_vec != eptr->ex_vec) {\n                        memcpy(optr->ex_vec, eptr->ex_vec,\n                                        expr->exp_vsize * sizeof (t_float));\n                        fts_free(eptr->ex_vec);\n                } else { /* this should not happen */\n                        post(\"expr int. error, optr->ex_vec = %d\",optr->ex_vec);\n                        abort();\n                }\n                return(++eptr);\n        case ET_XI0:\n                /* short hand for $x?[0] */\n\n                /* SDY delete the following check */\n                if (!IS_FEXPR_TILDE(expr) || optr->ex_type==ET_VEC) {\n                        post(\"%d:exp->exp_flags = %d\", __LINE__,expr->exp_flags);\n                        abort();\n                }\n                optr->ex_type = ET_FLT;\n                optr->ex_flt = expr->exp_var[eptr->ex_int].ex_vec[idx];\n                return(++eptr);\n        case ET_YOM1:\n                /*\n                 * short hand for $y?[-1]\n                 * if we are calculating the first sample of the vector\n                 * we need to look at the previous results buffer\n                 */\n                optr->ex_type = ET_FLT;\n                if (idx == 0)\n                        optr->ex_flt =\n                        expr->exp_p_res[eptr->ex_int][expr->exp_vsize - 1];\n                else\n                        optr->ex_flt=expr->exp_tmpres[eptr->ex_int][idx-1];\n                return(++eptr);\n\n        case ET_YO:\n        case ET_XI:\n                /* SDY delete the following */\n                if (!IS_FEXPR_TILDE(expr) || optr->ex_type==ET_VEC) {\n                        post(\"%d:expr->exp_flags = %d\", __LINE__,expr->exp_flags);\n                        abort();\n                }\n                return (eval_sigidx(expr, eptr, optr, idx));\n\n        case ET_TBL:\n        case ET_SI:\n                return (eval_tab(expr, eptr, optr, idx));\n        case ET_FUNC:\n                return (eval_func(expr, eptr, optr, idx));\n        case ET_VAR:\n                return (eval_var(expr, eptr, optr, idx));\n        case ET_OP:\n                break;\n        case ET_STR:\n        case ET_LP:\n        case ET_LB:\n        default:\n                post_error((fts_object_t *) expr,\n                        \"expr: ex_eval: unexpected type %d\\n\",\n                            (int)eptr->ex_type);\n                return (exNULL);\n        }\n        if (!eptr[1].ex_type) {\n                post_error((fts_object_t *) expr,\n                                        \"expr: ex_eval: not enough nodes 1\\n\");\n                return (exNULL);\n        }\n        if (!unary_op(eptr->ex_op) && !eptr[2].ex_type) {\n                post_error((fts_object_t *) expr,\n                                        \"expr: ex_eval: not enough nodes 2\\n\");\n                return (exNULL);\n        }\n\n        switch((eptr++)->ex_op) {\n        case OP_STORE:\n                return (eval_store(expr, eptr, optr, idx));\n        case OP_NOT:\n                EVAL_UNARY(!, +);\n        case OP_NEG:\n                EVAL_UNARY(~, (long));\n        case OP_UMINUS:\n                EVAL_UNARY(-, +);\n        case OP_MUL:\n                EVAL(*);\n        case OP_ADD:\n                EVAL(+);\n        case OP_SUB:\n                EVAL(-);\n        case OP_LT:\n                EVAL(<);\n        case OP_LE:\n                EVAL(<=);\n        case OP_GT:\n                EVAL(>);\n        case OP_GE:\n                EVAL(>=);\n        case OP_EQ:\n                EVAL(==);\n        case OP_NE:\n                EVAL(!=);\n/*\n * following operators convert their argument to integer\n */\n#undef DZC\n#define DZC(ARG1,OPR,ARG2)      (((int)ARG1) OPR ((int)ARG2))\n        case OP_SL:\n                EVAL(<<);\n        case OP_SR:\n                EVAL(>>);\n        case OP_AND:\n                EVAL(&);\n        case OP_XOR:\n                EVAL(^);\n        case OP_OR:\n                EVAL(|);\n        case OP_LAND:\n                EVAL(&&);\n        case OP_LOR:\n                EVAL(||);\n/*\n * for modulo we need to convert to integer and check for divide by zero\n */\n#undef DZC\n#define DZC(ARG1,OPR,ARG2)      ((((int)ARG2)?(((int)ARG1) OPR ((int)ARG2)) \\\n                                                        : (ex_dzdetect(expr),0)))\n        case OP_MOD:\n                EVAL(%);\n/*\n * define the divide by zero check for divide\n */\n#undef DZC\n#define DZC(ARG1,OPR,ARG2)      (((ARG2)?(ARG1 OPR ARG2):(ex_dzdetect(expr),0)))\n        case OP_DIV:\n                EVAL(/);\n        case OP_LP:\n        case OP_RP:\n        case OP_LB:\n        case OP_RB:\n        case OP_COMMA:\n        case OP_SEMI:\n        default:\n                post_error((fts_object_t *) expr,\n                    \"expr: ex_print: bad op 0x%x\\n\", (unsigned)eptr->ex_op);\n                return (exNULL);\n        }\n\n\n        /*\n         * the left and right nodes could have been transformed to vectors\n         * down the chain\n         */\n        if (left.ex_type == ET_VEC)\n                fts_free(left.ex_vec);\n        if (right.ex_type == ET_VEC)\n                fts_free(right.ex_vec);\n        if (nullret)\n                return (exNULL);\n        else\n                return (eptr);\n}\n\nextern struct ex_ex * ex_if(t_expr *expr,  struct ex_ex *eptr,\n                                struct ex_ex *optr,struct ex_ex *argv, int idx);\n\n/*\n * eval_func --  evaluate a function, call ex_eval() on all the arguments\n *               so that all of them are terminal nodes. The call the\n *               appropriate function\n */\nstruct ex_ex *\neval_func(struct expr *expr, struct ex_ex *eptr, struct ex_ex *optr, int idx)\n/* the expr object data pointer */\n/* the operation stack */\n/* the result pointer */\n{\n        int i;\n        struct ex_ex args[MAX_ARGS];\n        t_ex_func *f;\n\n        f = (t_ex_func *)(eptr++)->ex_ptr;\n        if (!f || !f->f_name) {\n                return (exNULL);\n        }\n        if (f->f_argc > MAX_ARGS) {\n                post_error((fts_object_t *) expr, \"expr: eval_func: asking too many arguments\\n\");\n                return (exNULL);\n        }\n\n                /*\n                 * We treat the \"if\" function differently to be able to evaluate\n                 * the args selectively based on the truth value of the \"condition\"\n                 */\n                if (f->f_func != (void (*)) ex_if) {\n                        for (i = 0; i < f->f_argc; i++) {\n                args[i].ex_type = 0;\n                args[i].ex_int = 0;\n                eptr = ex_eval(expr, eptr, &args[i], idx);\n                        }\n                (*f->f_func)(expr, f->f_argc, args, optr);\n        } else {\n                        for (i = 0; i < f->f_argc; i++) {\n                args[i].ex_type = 0;\n                args[i].ex_int = 0;\n                        }\n                eptr = ex_if(expr, eptr, optr, args, idx);\n                }\n        for (i = 0; i < f->f_argc; i++) {\n                if (args[i].ex_type == ET_VEC)\n                        fts_free(args[i].ex_vec);\n        }\n        return (eptr);\n}\n\n\n/*\n * eval_store --  evaluate the '=' operator,\n *                make sure the first operator is a legal left operator\n *                and call ex_eval on the right operator\n */\nstruct ex_ex *\neval_store(struct expr *expr, struct ex_ex *eptr, struct ex_ex *optr, int idx)\n/* the expr object data pointer */\n/* the operation stack */\n/* the result pointer */\n{\n        struct ex_ex arg = { 0 };\n        struct ex_ex rval = { 0 };\n        struct ex_ex *retp;\n        char *tbl = (char *) 0;\n        char *var = (char *) 0;\n        int badleft = 0;\n        int notable = 0;\n\n        arg.ex_type = ET_INT;\n        arg.ex_int = 0;\n\n        switch (eptr->ex_type) {\n        case ET_VAR:\n                var = (char *) eptr->ex_ptr;\n                eptr = ex_eval(expr, ++eptr, &arg, idx);\n                if (max_ex_var_store(expr, (t_symbol *)var, &arg, optr))\n                        retp = exNULL;\n                else\n                        retp = eptr;\n\n                if (arg.ex_type == ET_VEC)\n                        fts_free(arg.ex_vec);\n                return(retp);\n        case ET_TBL:\n                tbl = (char *) eptr->ex_ptr;\n                break;\n        case ET_SI:\n                if (!expr->exp_var[eptr->ex_int].ex_ptr) {\n                        if (!(expr->exp_error & EE_NOTABLE)) {\n                                post(\"expr: syntax error: no string for inlet %d\",\n                                                                                                                eptr->ex_int + 1);\n                                post(\"expr: No more table errors will be reported\");\n                                post(\"expr: till the next reset\");\n                                expr->exp_error |= EE_NOTABLE;\n                        }\n                        badleft++;\n                        post(\"Bad left value: \");\n                        /* report Error */\n                        ex_print(eptr);\n                        retp = exNULL;\n                        return (retp);\n                } else {\n                        tbl = (char *) expr->exp_var[eptr->ex_int].ex_ptr;\n                }\n                break;\n        default:\n                post(\"Bad left value: \");\n                /* report Error */\n                ex_print(eptr);\n                retp = exNULL;\n                return (retp);\n        }\n        arg.ex_type = 0;\n        arg.ex_int = 0;\n        /* evaluate the index of the table */\n        if (!(eptr = ex_eval(expr, ++eptr, &arg, idx)))\n                return (eptr);\n\n        /* evaluate the right index of the table */\n        if (!(eptr = ex_eval(expr, eptr, &rval, idx)))\n                return (eptr);\n        optr->ex_type = ET_INT;\n        optr->ex_int = 0;\n        if (!notable || badleft)\n                (void)max_ex_tab_store(expr, (t_symbol *)tbl, &arg, &rval, optr);\n        if (arg.ex_type == ET_VEC)\n                fts_free(arg.ex_vec);\n        return (eptr);\n}\n\n/*\n * eval_tab -- evaluate a table operation\n */\nstruct ex_ex *\neval_tab(struct expr *expr, struct ex_ex *eptr, struct ex_ex *optr, int idx)\n/* the expr object data pointer */\n/* the operation stack */\n/* the result pointer */\n{\n        struct ex_ex arg = { 0 };\n        char *tbl = (char *) 0;\n        int notable = 0;\n\n        if (eptr->ex_type == ET_SI) {\n                if (!expr->exp_var[eptr->ex_int].ex_ptr) {\n                        if (!(expr->exp_error & EE_NOTABLE)) {\n                                post(\"expr: syntax error: no string for inlet %d\",\n                                                                                                                        eptr->ex_int + 1);\n                                post(\"expr: No more table errors will be reported\");\n                                post(\"expr: till the next reset\");\n                                expr->exp_error |= EE_NOTABLE;\n                        }\n                        notable++;\n                } else\n                        tbl = (char *) expr->exp_var[eptr->ex_int].ex_ptr;\n        } else if (eptr->ex_type == ET_TBL) {\n                tbl = (char *) eptr->ex_ptr;\n                if (!tbl) {\n                        post(\"expr: abstraction argument for table not set\");\n                        notable++;\n                }\n\n        } else {\n                post_error((fts_object_t *) expr,\"expr: eval_tbl: bad type %ld\\n\",eptr->ex_type);\n                notable++;\n\n        }\n        arg.ex_type = 0;\n        arg.ex_int = 0;\n        if (!(eptr = ex_eval(expr, ++eptr, &arg, idx)))\n                return (eptr);\n\n        optr->ex_type = ET_INT;\n        optr->ex_int = 0;\n        if (!notable)\n                (void)max_ex_tab(expr, (t_symbol *)tbl, &arg, optr);\n        if (arg.ex_type == ET_VEC)\n                fts_free(arg.ex_vec);\n        return (eptr);\n}\n\n/*\n * eval_var -- evaluate a variable\n */\nstruct ex_ex *\neval_var(struct expr *expr, struct ex_ex *eptr, struct ex_ex *optr, int idx)\n/* the expr object data pointer */\n/* the operation stack */\n/* the result pointer */\n{\n        char *var = (char *) 0;\n        int novar = 0;\n\n        if (eptr->ex_type == ET_SI) {\n                if (!expr->exp_var[eptr->ex_int].ex_ptr) {\n                                if (!(expr->exp_error & EE_NOVAR)) {\n                                        post(\"expr: syntax error: no string for inlet %d\", eptr->ex_int + 1);\n                                        post(\"expr: no more table errors will be reported\");\n                                        post(\"expr: till the next reset\");\n                                        expr->exp_error |= EE_NOVAR;\n                                }\n                                novar++;\n                } else\n                        var = (char *) expr->exp_var[eptr->ex_int].ex_ptr;\n        } else if (eptr->ex_type == ET_VAR)\n                var = (char *) eptr->ex_ptr;\n        else {\n                post_error((fts_object_t *) expr, \"expr: eval_tbl: bad type %ld\\n\", eptr->ex_type);\n                novar++;\n\n        }\n\n        optr->ex_type = ET_INT;\n        optr->ex_int = 0;\n        if (!novar)\n                (void)max_ex_var(expr, (t_symbol *)var, optr, idx);\n        return (++eptr);\n}\n\n/*\n * eval_sigidx -- evaluate the value of an indexed signal for fexpr~\n */\nstruct ex_ex *\neval_sigidx(struct expr *expr, struct ex_ex *eptr, struct ex_ex *optr, int idx)\n/* the expr object data pointer */\n/* the operation stack */\n/* the result pointer */\n/* the index */\n{\n        struct ex_ex arg = { 0 };\n        struct ex_ex *reteptr;\n        int i = 0;\n        t_float fi = 0,         /* index in float */\n              rem_i = 0;        /* remains of the float */\n\n        arg.ex_type = 0;\n        arg.ex_int = 0;\n        reteptr = ex_eval(expr, eptr + 1, &arg, idx);\n        if (arg.ex_type == ET_FLT) {\n                fi = arg.ex_flt;                /* float index */\n                i = (int) arg.ex_flt;           /* integer index */\n                rem_i =  arg.ex_flt - i;        /* remains of integer */\n        } else if (arg.ex_type == ET_INT) {\n                fi = arg.ex_int;                /* float index */\n                i = (int) arg.ex_int;           /* integer index */\n                rem_i = 0;\n        } else {\n                post(\"eval_sigidx: bad res type (%d)\", arg.ex_type);\n        }\n        optr->ex_type = ET_FLT;\n        /*\n         * indexing an input vector\n         */\n        if (eptr->ex_type == ET_XI) {\n                if (fi > 0) {\n                        if (!(expr->exp_error & EE_BI_INPUT)) {\n                                expr->exp_error |= EE_BI_INPUT;\n                          post(\"expr: input vector index > 0, (vector x%d[%f])\",\n                                               eptr->ex_int + 1, i + rem_i);\n                                post(\"fexpr~: index assumed to be = 0\");\n                                post(\"fexpr~: no error report till next reset\");\n                                ex_print(eptr);\n                        }\n                        /* just replace it with zero */\n                        i = 0;\n                        rem_i = 0;\n                }\n                if (cal_sigidx(optr, i, rem_i, idx, expr->exp_vsize,\n                                        expr->exp_var[eptr->ex_int].ex_vec,\n                                                expr->exp_p_var[eptr->ex_int])) {\n                        if (!(expr->exp_error & EE_BI_INPUT)) {\n                                expr->exp_error |= EE_BI_INPUT;\n                                post(\"expr: input vector index <  -VectorSize, (vector x%d[%f])\", eptr->ex_int + 1, fi);\n                                ex_print(eptr);\n                                post(\"fexpr~: index assumed to be = -%d\",\n                                        expr->exp_vsize);\n                                post(\"fexpr~: no error report till next reset\");\n                        }\n                }\n\n        /*\n         * indexing an output vector\n         */\n        } else if (eptr->ex_type == ET_YO) {\n                /* for output vectors index of zero is not legal */\n                if (fi >= 0) {\n                        if (!(expr->exp_error & EE_BI_OUTPUT)) {\n                                expr->exp_error |= EE_BI_OUTPUT;\n                                post(\"fexpr~: bad output index, (%f)\", fi);\n                                ex_print(eptr);\n                                post(\"fexpr~: no error report till next reset\");\n                                post(\"fexpr~: index assumed to be = -1\");\n                        }\n                        i = -1;\n                }\n                if (eptr->ex_int >= expr->exp_nexpr) {\n                        post(\"fexpr~: $y%d illegal: not that many exprs\",\n                                                                eptr->ex_int);\n                        optr->ex_flt = 0;\n                        return (reteptr);\n                }\n                if (cal_sigidx(optr, i, rem_i, idx, expr->exp_vsize,\n                             expr->exp_tmpres[eptr->ex_int],\n                                                expr->exp_p_res[eptr->ex_int])) {\n                        if (!(expr->exp_error & EE_BI_OUTPUT)) {\n                                expr->exp_error |= EE_BI_OUTPUT;\n                                post(\"fexpr~: bad output index, (%f)\", fi);\n                                ex_print(eptr);\n                                post(\"fexpr~: index assumed to be = -%d\",\n                                        expr->exp_vsize);\n                        }\n                }\n        } else {\n                optr->ex_flt = 0;\n                post(\"fexpr~:eval_sigidx: internal error - unknown vector (%d)\",\n                                                                eptr->ex_type);\n        }\n        return (reteptr);\n}\n\n/*\n * cal_sigidx -- given two tables (one current one previous) calculate an\n *               evaluation of a float index into the vectors by linear\n *               interpolation\n *              return 0 on success, 1 on failure (index out of bound)\n */\nstatic int\ncal_sigidx(struct ex_ex *optr,  /* The output value */\n           int i, t_float rem_i,/* integer and fractinal part of index */\n           int idx,             /* index of current fexpr~ processing */\n           int vsize,           /* vector size */\n           t_float *curvec, t_float *prevec)        /* current and previous table */\n{\n        int n;\n\n        n = i + idx;\n        if (n > 0) {\n                /* from the curvec */\n                if (rem_i)\n                        optr->ex_flt = curvec[n] +\n                                        rem_i * (curvec[n] - curvec[n - 1]);\n                else\n                        optr->ex_flt = curvec[n];\n                return (0);\n        }\n        if (n == 0) {\n                /*\n                 * this is the case that the remaining float\n                 * is between two tables\n                 */\n                if (rem_i)\n                        optr->ex_flt = *curvec +\n                                        rem_i * (*curvec - prevec[vsize - 1]);\n                else\n                        optr->ex_flt = *curvec;\n                return (0);\n        }\n        /* find the index in the saved buffer */\n        n = vsize + n;\n        if (n > 0) {\n                if (rem_i)\n                        optr->ex_flt = prevec[n] +\n                                        rem_i * (prevec[n] - prevec[n - 1]);\n                else\n                        optr->ex_flt = prevec[n];\n                return (0);\n        }\n        /* out of bound */\n        optr->ex_flt = *prevec;\n        return (1);\n}\n\n/*\n * getoken -- return 1 on syntax error otherwise 0\n */\nint\ngetoken(struct expr *expr, struct ex_ex *eptr)\n{\n        char *p;\n        long i;\n\n\n        if (!expr->exp_str) {\n                post(\"expr: getoken: expression string not set\\n\");\n                return (0);\n        }\nretry:\n        if (!*expr->exp_str) {\n                eptr->ex_type = 0;\n                eptr->ex_int = 0;\n                return (0);\n        }\n        if (*expr->exp_str == ';') {\n                expr->exp_str++;\n                eptr->ex_type = 0;\n                eptr->ex_int = 0;\n                return (0);\n        }\n        eptr->ex_type = ET_OP;\n        switch (*expr->exp_str++) {\n        case '\\\\':\n        case ' ':\n        case '\\t':\n                goto retry;\n        case ';':\n                post(\"expr: syntax error: ';' not implemented\\n\");\n                return (1);\n        case ',':\n                eptr->ex_op = OP_COMMA;\n                break;\n        case '(':\n                eptr->ex_op = OP_LP;\n                break;\n        case ')':\n                eptr->ex_op = OP_RP;\n                break;\n        case ']':\n                eptr->ex_op = OP_RB;\n                break;\n        case '~':\n                eptr->ex_op = OP_NEG;\n                break;\n                /* we will take care of unary minus later */\n        case '*':\n                eptr->ex_op = OP_MUL;\n                break;\n        case '/':\n                eptr->ex_op = OP_DIV;\n                break;\n        case '%':\n                eptr->ex_op = OP_MOD;\n                break;\n        case '+':\n                eptr->ex_op = OP_ADD;\n                break;\n        case '-':\n                eptr->ex_op = OP_SUB;\n                break;\n        case '^':\n                eptr->ex_op = OP_XOR;\n                break;\n        case '[':\n                eptr->ex_op = OP_LB;\n                break;\n        case '!':\n                if (*expr->exp_str == '=') {\n                        eptr->ex_op = OP_NE;\n                        expr->exp_str++;\n                } else\n                        eptr->ex_op = OP_NOT;\n                break;\n        case '<':\n                switch (*expr->exp_str) {\n                case '<':\n                        eptr->ex_op = OP_SL;\n                        expr->exp_str++;\n                        break;\n                case '=':\n                        eptr->ex_op = OP_LE;\n                        expr->exp_str++;\n                        break;\n                default:\n                        eptr->ex_op = OP_LT;\n                        break;\n                }\n                break;\n        case '>':\n                switch (*expr->exp_str) {\n                case '>':\n                        eptr->ex_op = OP_SR;\n                        expr->exp_str++;\n                        break;\n                case '=':\n                        eptr->ex_op = OP_GE;\n                        expr->exp_str++;\n                        break;\n                default:\n                        eptr->ex_op = OP_GT;\n                        break;\n                }\n                break;\n        case '=':\n                if (*expr->exp_str != '=')\n                        eptr->ex_op = OP_STORE;\n                else {\n                        expr->exp_str++;\n                        eptr->ex_op = OP_EQ;\n                }\n                break;\n\n        case '&':\n                if (*expr->exp_str == '&') {\n                        expr->exp_str++;\n                        eptr->ex_op = OP_LAND;\n                } else\n                        eptr->ex_op = OP_AND;\n                break;\n\n        case '|':\n                if (*expr->exp_str == '|') {\n                        expr->exp_str++;\n                        eptr->ex_op = OP_LOR;\n                } else\n                        eptr->ex_op = OP_OR;\n                break;\n        case '$':\n                switch (*expr->exp_str++) {\n                case 'I':\n                case 'i':\n                        eptr->ex_type = ET_II;\n                        break;\n                case 'F':\n                case 'f':\n                        eptr->ex_type = ET_FI;\n                        break;\n                case 'S':\n                case 's':\n                        eptr->ex_type = ET_SI;\n                        break;\n                case 'V':\n                case 'v':\n                        if (IS_EXPR_TILDE(expr)) {\n                                eptr->ex_type = ET_VI;\n                                break;\n                        }\n                        post(\"$v? works only for expr~\");\n                        post(\"expr: syntax error: %s\\n\", &expr->exp_str[-2]);\n                        return (1);\n                case 'X':\n                case 'x':\n                        if (IS_FEXPR_TILDE(expr)) {\n                                eptr->ex_type = ET_XI;\n                                if (isdigit((int)(*expr->exp_str)))\n                                        break;\n                                /* for $x[] is a shorhand for $x1[] */\n                                /* eptr->ex_int = 0; */\n                                                                eptr->ex_op = 0;\n                                                                expr->exp_var[eptr->ex_op].ex_type = eptr->ex_type;\n                                goto noinletnum;\n                        }\n                        post(\"$x? works only for fexpr~\");\n                        post(\"expr: syntax error: %s\\n\", &expr->exp_str[-2]);\n                        return (1);\n                case 'y':\n                case 'Y':\n                        if (IS_FEXPR_TILDE(expr)) {\n                                eptr->ex_type = ET_YO;\n                                /*$y takes no number */\n                                if (isdigit((int)(*expr->exp_str)))\n                                        break;\n                                /* for $y[] is a shorhand for $y1[] */\n                                /* eptr->ex_int = 0; */\n                                                                eptr->ex_op = 0;\n                                                                expr->exp_var[eptr->ex_op].ex_type = eptr->ex_type;\n                                goto noinletnum;\n                        }\n                        post(\"$y works only for fexpr~\");\n                                /* falls through */\n                                /*\n                                 * allow $# for abstraction argument substitution\n                                 *  $1+1 is translated to 0+1 and in abstraction substitution\n                                 *  the value is replaced with the new string\n                                 */\n                                case '0':\n                                case '1':\n                                case '2':\n                                case '3':\n                                case '4':\n                                case '5':\n                                case '6':\n                                case '7':\n                                case '8':\n                                case '9':\n                                        p = atoif(--expr->exp_str, &eptr->ex_op, &i);\n                        if (!p)\n                        return (1);\n                                        if (i != ET_INT) {\n                                                post(\"expr: syntax error: %s\\n\", expr->exp_str);\n                                                return (1);\n                                        }\n                        expr->exp_str = p;\n                                        eptr->ex_type = ET_INT;\n                                        eptr->ex_int = 0;\n                        return (0);\n                default:\n                        post(\"expr: syntax error: %s\\n\", &expr->exp_str[-2]);\n                        return (1);\n                }\n                p = atoif(expr->exp_str, &eptr->ex_op, &i);\n                if (!p) {\n                        post(\"expr: syntax error: %s\\n\", &expr->exp_str[-2]);\n                        return (1);\n                }\n                if (i != ET_INT) {\n                        post(\"expr: syntax error: %s\\n\", expr->exp_str);\n                        return (1);\n                }\n                /*\n                 * make the user inlets one based rather than zero based\n                 * therefore we decrement the number that user has supplied\n                 */\n                if (!eptr->ex_op || (eptr->ex_op)-- > MAX_VARS) {\n                 post(\"expr: syntax error: inlet or outlet out of range: %s\\n\",\n                                                             expr->exp_str);\n                        return (1);\n                }\n\n                /*\n                 * until we can change the input type of inlets on\n                 * the fly (at pd_new()\n                 * time) the first input to expr~ is always a vector\n                 * and $f1 or $i1 is\n                 * illegal for fexpr~\n                 */\n                if (eptr->ex_op == 0 &&\n                   (IS_FEXPR_TILDE(expr) ||  IS_EXPR_TILDE(expr)) &&\n                   (eptr->ex_type==ET_II || eptr->ex_type==ET_FI ||\n                                                        eptr->ex_type==ET_SI)) {\n                       post(\"first inlet of expr~/fexpr~ can only be a vector\");\n                       return (1);\n                }\n                /* record the inlet or outlet type and check for consistency */\n                if (eptr->ex_type == ET_YO ) {\n                        /* it is an outlet  for fexpr~*/\n                        /* no need to do anything */\n                        ;\n                } else if (!expr->exp_var[eptr->ex_op].ex_type)\n                        expr->exp_var[eptr->ex_op].ex_type = eptr->ex_type;\n                else if (expr->exp_var[eptr->ex_op].ex_type != eptr->ex_type) {\n                        post(\"expr: syntax error: inlets can only have one type: %s\\n\", expr->exp_str);\n                        return (1);\n                }\n                expr->exp_str = p;\nnoinletnum:\n                break;\n        case '\"':\n                {\n                        struct ex_ex ex = { 0 };\n\n                        p = expr->exp_str;\n                        if (!*expr->exp_str || *expr->exp_str == '\"') {\n                                post(\"expr: syntax error: empty symbol: %s\\n\", --expr->exp_str);\n                                return (1);\n                        }\n                        if (getoken(expr, &ex))\n                                return (1);\n                        switch (ex.ex_type) {\n                        case ET_STR:\n                                if (ex_getsym(ex.ex_ptr, (t_symbol **)&(eptr->ex_ptr))) {\n                                        post(\"expr: syntax error: getoken: problms with ex_getsym\\n\");\n                                        return (1);\n                                }\n                                eptr->ex_type = ET_SYM;\n                                break;\n                        case ET_SI:\n                                *eptr = ex;\n                                eptr->ex_type = ET_VSYM;\n                                break;\n                        default:\n                                post(\"expr: syntax error: bad symbol name: %s\\n\", p);\n                                return (1);\n                        }\n                        if (*expr->exp_str++ != '\"') {\n                                post(\"expr: syntax error: missing '\\\"'\\n\");\n                                return (1);\n                        }\n                        break;\n                }\n        case '.':\n        case '0':\n        case '1':\n        case '2':\n        case '3':\n        case '4':\n        case '5':\n        case '6':\n        case '7':\n        case '8':\n        case '9':\n                p = atoif(--expr->exp_str, &eptr->ex_int, &eptr->ex_type);\n                if (!p)\n                        return (1);\n                expr->exp_str = p;\n                break;\n\n        default:\n                /*\n                 * has to be a string, it should either be a\n                 * function or a table\n                 */\n                p = --expr->exp_str;\n                for (i = 0; name_ok(*p); i++)\n                        p++;\n                if (!i) {\n                        post(\"expr: syntax error: %s\\n\", expr->exp_str);\n                        return (1);\n                }\n                eptr->ex_ptr = (char *)fts_malloc(i + 1);\n                strncpy(eptr->ex_ptr, expr->exp_str, (int) i);\n                (eptr->ex_ptr)[i] = 0;\n                expr->exp_str = p;\n                /*\n                 * we mark this as a string and later we will change this\n                 * to either a function or a table\n                 */\n                eptr->ex_type = ET_STR;\n                break;\n        }\n        return (0);\n}\n\n/*\n * atoif -- ascii to float or integer (strtof() understand exponential notations  ad strtod() understand hex numbers)\n */\nchar *\natoif(char *s, long int *value, long int *type)\n{\n    const char*s0 = s;\n    char *p;\n        long lval;\n        float fval;\n\n        lval = strtod(s, &p);\n        fval = strtof(s, &p);\n        if (lval != (int) fval) {\n                *type = ET_FLT;\n                *((t_float *) value) = fval;\n                return (p);\n        }\n        while (s != p) {\n                if (*s == 'x' || *s == 'X')\n                                break;\n                if (*s == '.' || *s == 'e' || *s == 'E') {\n                        *type = ET_FLT;\n                        *((t_float *) value) = fval;\n                        return (p);\n                }\n                s++;\n        }\n        if(s0 == p)\n            return 0;\n\n        *type = ET_INT;\n        *((t_int *) value) = lval;\n        return (p);\n}\n\n/*\n * find_func -- returns a pointer to the found function structure\n *              otherwise it returns 0\n */\nt_ex_func *\nfind_func(char *s)\n{\n        t_ex_func *f;\n\n        for (f = ex_funcs; f->f_name; f++)\n                if (!strcmp(f->f_name, s))\n                        return (f);\n        return ((t_ex_func *) 0);\n}\n\n\n/*\n * ex_print -- print an expression array\n */\n\nvoid\nex_print(struct ex_ex *eptr)\n{\n                struct ex_ex *extmp;\n\n                extmp = eptr->ex_end;\n        while (eptr->ex_type && eptr != extmp) {\n                switch (eptr->ex_type) {\n                case ET_INT:\n                        post(\"%ld \", eptr->ex_int);\n                        break;\n                case ET_FLT:\n                        post(\"%f \", eptr->ex_flt);\n                        break;\n                case ET_STR:\n                        post(\"%s \", eptr->ex_ptr);\n                        break;\n                case ET_TBL:\n                        if (!eptr->ex_ptr) { /* special case of $# processing */\n                                post(\"%s \", \"$$\");\n                            break;\n                        }\n                            /* falls through */\n                case ET_VAR:\n                        post(\"%s \", ex_symname((fts_symbol_t )eptr->ex_ptr));\n                        break;\n                case ET_SYM:\n                        post(\"\\\"%s\\\" \", ex_symname((fts_symbol_t )eptr->ex_ptr));\n                        break;\n                case ET_VSYM:\n                        post(\"\\\"$s%ld\\\" \", eptr->ex_int + 1);\n                        break;\n                case ET_FUNC:\n                        post(\"%s \",\n                            ((t_ex_func *)eptr->ex_ptr)->f_name);\n                        break;\n                case ET_LP:\n                        post(\"%c\", '(');\n                        break;\n                        /* CHANGE\n                 case ET_RP:\n                         post(\"%c \", ')');\n                         break;\n                         */\n                case ET_LB:\n                        post(\"%c\", '[');\n                        break;\n                        /* CHANGE\n                case ET_RB:\n                         post(\"%c \", ']');\n                         break;\n                         */\n                case ET_II:\n                        post(\"$i%ld \", eptr->ex_int + 1);\n                        break;\n                case ET_FI:\n                        post(\"$f%ld \", eptr->ex_int + 1);\n                        break;\n                case ET_SI:\n                        post(\"$s%lx \", eptr->ex_ptr);\n                        break;\n                case ET_VI:\n                        post(\"$v%lx \", eptr->ex_vec);\n                        break;\n                case ET_VEC:\n                        post(\"vec = %ld \", eptr->ex_vec);\n                        break;\n                case ET_YOM1:\n                case ET_YO:\n                        post(\"$y%d\", eptr->ex_int + 1);\n                        break;\n                case ET_XI:\n                case ET_XI0:\n                        post(\"$x%d\", eptr->ex_int + 1);\n                        break;\n                case ET_OP:\n                        switch (eptr->ex_op) {\n                        case OP_LP:\n                                post(\"%c\", '(');\n                                break;\n                        case OP_RP:\n                                post(\"%c \", ')');\n                                break;\n                        case OP_LB:\n                                post(\"%c\", '[');\n                                break;\n                        case OP_RB:\n                                post(\"%c \", ']');\n                                break;\n                        case OP_NOT:\n                                post(\"%c\", '!');\n                                break;\n                        case OP_NEG:\n                                post(\"%c\", '~');\n                                break;\n                        case OP_UMINUS:\n                                post(\"%c\", '-');\n                                break;\n                        case OP_MUL:\n                                post(\"%c\", '*');\n                                break;\n                        case OP_DIV:\n                                post(\"%c\", '/');\n                                break;\n                        case OP_MOD:\n                                post(\"%c\", '%');\n                                break;\n                        case OP_ADD:\n                                post(\"%c\", '+');\n                                break;\n                        case OP_SUB:\n                                post(\"%c\", '-');\n                                break;\n                        case OP_SL:\n                                post(\"%s\", \"<<\");\n                                break;\n                        case OP_SR:\n                                post(\"%s\", \">>\");\n                                break;\n                        case OP_LT:\n                                post(\"%c\", '<');\n                                break;\n                        case OP_LE:\n                                post(\"%s\", \"<=\");\n                                break;\n                        case OP_GT:\n                                post(\"%c\", '>');\n                                break;\n                        case OP_GE:\n                                post(\"%s\", \">=\");\n                                break;\n                        case OP_EQ:\n                                post(\"%s\", \"==\");\n                                break;\n                        case OP_STORE:\n                                post(\"%s\", \"=\");\n                                break;\n                        case OP_NE:\n                                post(\"%s\", \"!=\");\n                                break;\n                        case OP_AND:\n                                post(\"%c\", '&');\n                                break;\n                        case OP_XOR:\n                                post(\"%c\", '^');\n                                break;\n                        case OP_OR:\n                                post(\"%c\", '|');\n                                break;\n                        case OP_LAND:\n                                post(\"%s\", \"&&\");\n                                break;\n                        case OP_LOR:\n                                post(\"%s\", \"||\");\n                                break;\n                        case OP_COMMA:\n                                post(\"%c\", ',');\n                                break;\n                        case OP_SEMI:\n                                post(\"%c\", ';');\n                                break;\n                        default:\n                                post(\"expr: ex_print: bad op 0x%lx\\n\", eptr->ex_op);\n                        }\n                        break;\n                default:\n                        post(\"expr: ex_print: bad type 0x%lx\\n\", eptr->ex_type);\n                }\n                eptr++;\n        }\n        post(\"\\n\");\n}\n\n#ifdef _WIN32\nvoid ABORT(void) {bug(\"expr\");}\n#endif\n"
  },
  {
    "path": "libs/libpd/pure-data/src/x_vexp.h",
    "content": "/* Copyright (c) IRCAM.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* \"expr\" was written by Shahrokh Yadegari c. 1989. -msp */\n/* \"expr~\" and \"fexpr~\" conversion by Shahrokh Yadegari c. 1999,2000 */\n\n#define MSP\n#ifdef PD\n#undef MSP\n#endif\n\n#ifdef PD\n#include \"m_pd.h\"\n#else /* MSP */\n#include \"ext.h\"\n#include \"z_dsp.h\"\ntypedef float t_float;      // t_float is from m_pd.h\n#endif\n\n#define fts_malloc malloc\n#define fts_calloc calloc\n#define fts_free free\n#define fts_realloc realloc\n#define fts_atom_t t_atom\n#define fts_object_t t_object\ntypedef t_symbol *fts_symbol_t;\n\n#ifdef MSP\n#define t_atom          Atom\n#define t_symbol        Symbol\n#define pd_new(x)       newobject(x);\n#define pd_free(x)      freeobject(x);\n#define t_outlet        void\n#define t_binbuf        void\ntypedef t_class         *t_pd;\ntypedef float           t_floatarg;\n\n#include <stdlib.h>\n#include <stdio.h>\n#include <stdarg.h>\n#include <string.h>\n#include <errno.h>\n\nvoid pd_error(void *object, char *fmt, ...);\n\n#endif /* MSP */\n\n#define post_error pd_error\n#define fts_is_floatg(x) ((x)->a_type == A_FLOAT)\n\n#define fts_new_symbol_copy gensym\n\n#define fts_symbol_name(x) ((x)->s_name)\n\n/*\n * Currently the maximum number of variables (inlets) that are supported\n * is 10.\n */\n\n#define MAX_VARS        100\n#define MINODES         10 /* was 200 */\n\n/* terminal defines */\n\n/*\n * operations\n * (x<<16|y) x defines the level of precedence,\n * the lower the number the lower the precedence\n * separators are defines as operators just for convenience\n */\n\n#define OP_SEMI         ((long)(1<<16|1))               /* ; */\n#define OP_COMMA        ((long)(2<<16|2))               /* , */\n#define OP_STORE        ((long)(3<<16|28))             /* = */\n#define OP_LOR          ((long)(4<<16|3))               /* || */\n#define OP_LAND         ((long)(5<<16|4))               /* && */\n#define OP_OR           ((long)(6<<16|5))               /* | */\n#define OP_XOR          ((long)(7<<16|6))               /* ^ */\n#define OP_AND          ((long)(8<<16|7))               /* & */\n#define OP_NE           ((long)(9<<16|8))               /* != */\n#define OP_EQ           ((long)(9<<16|9))               /* == */\n#define OP_GE           ((long)(10<<16|10))              /* >= */\n#define OP_GT           ((long)(10<<16|11))              /* > */\n#define OP_LE           ((long)(10<<16|12))              /* <= */\n#define OP_LT           ((long)(10<<16|13))              /* < */\n#define OP_SR           ((long)(11<<16|14))             /* >> */\n#define OP_SL           ((long)(11<<16|15))             /* << */\n#define OP_SUB          ((long)(12<<16|16))             /* - */\n#define OP_ADD          ((long)(12<<16|17))             /* + */\n#define OP_MOD          ((long)(13<<16|18))             /* % */\n#define OP_DIV          ((long)(13<<16|19))             /* / */\n#define OP_MUL          ((long)(13<<16|20))             /* * */\n#define OP_UMINUS       ((long)(14<<16|21))             /* - unary minus */\n#define OP_NEG          ((long)(14<<16|22))             /* ~ one complement */\n#define OP_NOT          ((long)(14<<16|23))             /* ! */\n#define OP_RB           ((long)(15<<16|24))             /* ] */\n#define OP_LB           ((long)(15<<16|25))             /* [ */\n#define OP_RP           ((long)(15<<16|26))             /* ) */\n#define OP_LP           ((long)(15<<16|27))             /* ( */\n#define HI_PRE          ((long)(100<<16))       /* infinite precedence */\n#define PRE_MASK        ((long)0xffff0000)      /* precedence level mask */\n\n#define name_ok(c)      (((c)=='_') || ((c)>='a' && (c)<='z') || \\\n                        ((c)>='A' && (c)<='Z') || ((c) >= '0' && (c) <= '9'))\n#define unary_op(x)     ((x) == OP_NOT || (x) == OP_NEG || (x) == OP_UMINUS)\n\nstruct ex_ex {\n        union {\n                long v_int;\n                t_float v_flt;\n                t_float *v_vec;         /* this is an for allocated vector */\n                long op;\n                char *ptr;\n        } ex_cont;              /* content */\n#define ex_int          ex_cont.v_int\n#define ex_flt          ex_cont.v_flt\n#define ex_vec          ex_cont.v_vec\n#define ex_op           ex_cont.op\n#define ex_ptr          ex_cont.ptr\n        long ex_type;           /* type of the node */\n                struct ex_ex *ex_end;   /* the node after the end of this expression */\n};\n#define exNULL  ((struct ex_ex *)0)\n\n/* defines for ex_type */\n#define ET_INT          1               /* an int */\n#define ET_FLT          2               /* a float */\n#define ET_OP           3               /* operator */\n#define ET_STR          4               /* string */\n#define ET_TBL          5               /* a table, the content is a pointer */\n#define ET_FUNC         6               /* a function */\n#define ET_SYM          7               /* symbol (\"string\") */\n#define ET_VSYM         8               /* variable symbol (\"$s?\") */\n                                /* we treat parenthesis and brackets */\n                                /* special to keep a pointer to their */\n                                /* match in the content */\n#define ET_LP           9               /* left parenthesis */\n#define ET_LB           10              /* left bracket */\n#define ET_II           11              /* and integer inlet */\n#define ET_FI           12              /* float inlet */\n#define ET_SI           13              /* string inlet */\n#define ET_VI           14              /* signal inlet */\n#define ET_VEC          15              /* allocated signal vector */\n                                /* special types for fexpr~ */\n#define ET_YO           16              /* vector output for fexpr~ */\n#define ET_YOM1         17              /* shorthand for $y?[-1] */\n#define ET_XI           18              /* vector input for fexpr~ */\n#define ET_XI0          20              /* shorthand for $x?[0] */\n#define ET_VAR          21              /* variable */\n\n/* defines for ex_flags */\n#define EF_TYPE_MASK    0x07    /* first three bits define the type of expr */\n#define EF_EXPR         0x01    /* expr  - control in and out */\n#define EF_EXPR_TILDE   0x02    /* expr~ signal and control in, signal out */\n#define EF_FEXPR_TILDE  0x04    /* fexpr~ filter expression */\n\n#define EF_STOP         0x08    /* is it stopped used for expr~ and fexpr~ */\n#define EF_VERBOSE      0x10    /* verbose mode */\n\n#define IS_EXPR(x)        ((((x)->exp_flags&EF_TYPE_MASK)|EF_EXPR) == EF_EXPR)\n#define IS_EXPR_TILDE(x)  \\\n                 ((((x)->exp_flags&EF_TYPE_MASK)|EF_EXPR_TILDE)==EF_EXPR_TILDE)\n#define IS_FEXPR_TILDE(x) \\\n               ((((x)->exp_flags&EF_TYPE_MASK)|EF_FEXPR_TILDE)==EF_FEXPR_TILDE)\n\n#define SET_EXPR(x)     (x)->exp_flags |= EF_EXPR; \\\n                        (x)->exp_flags &= ~EF_EXPR_TILDE;  \\\n                        (x)->exp_flags &= ~EF_FEXPR_TILDE;\n\n#define SET_EXPR_TILDE(x)       (x)->exp_flags &= ~EF_EXPR; \\\n                                (x)->exp_flags |= EF_EXPR_TILDE;  \\\n                                (x)->exp_flags &= ~EF_FEXPR_TILDE;\n\n#define SET_FEXPR_TILDE(x)      (x)->exp_flags &= ~EF_EXPR; \\\n                                (x)->exp_flags &= ~EF_EXPR_TILDE;  \\\n                                (x)->exp_flags |= EF_FEXPR_TILDE;\n\n/*\n * defines for expr_error\n */\n#define EE_DZ           0x01    /* divide by zero error */\n#define EE_BI_OUTPUT    0x02    /* Bad output index */\n#define EE_BI_INPUT     0x04    /* Bad input index */\n#define EE_NOTABLE      0x08    /* NO TABLE */\n#define EE_NOVAR        0x10    /* NO VARIABLE */\n\ntypedef struct expr {\n#ifdef PD\n        t_object exp_ob;\n#else /* MSP */\n        t_pxobject exp_ob;\n#endif\n        int     exp_flags;              /* are we expr~, fexpr~, or expr */\n        int     exp_error;              /* reported errors */\n        int     exp_nexpr;              /* number of expressions */\n        char    *exp_string;            /* the full expression string */\n        char    *exp_str;               /* current parsing position */\n        t_outlet *exp_outlet[MAX_VARS];\n#ifdef PD\n        struct _exprproxy *exp_proxy;\n#else /* MAX */\n        void *exp_proxy[MAX_VARS];\n        long exp_proxy_id;\n#endif\n        struct ex_ex *exp_stack[MAX_VARS];\n        struct ex_ex exp_var[MAX_VARS];\n        struct ex_ex exp_res[MAX_VARS]; /* the evluation result */\n        t_float *exp_p_var[MAX_VARS];\n        t_float *exp_p_res[MAX_VARS];   /* the previous evaluation result */\n        t_float *exp_tmpres[MAX_VARS];  /* temporty result for fexpr~ */\n        int exp_vsize;                  /* the size of the signal vector */\n        int exp_nivec;                  /* # of vector inlets */\n        t_float exp_f;          /* control value to be transformed to signal */\n} t_expr;\n\ntypedef struct ex_funcs {\n        char *f_name;                                   /* function name */\n        void (*f_func)(t_expr *, long, struct ex_ex *, struct ex_ex *);\n          /* the real function performing the function (void, no return!!!) */\n        long f_argc;                            /* number of arguments */\n} t_ex_func;\n\n/* function prototypes for pd-related functions called within vexp.h */\n\nextern int\nmax_ex_tab_store(struct expr *expr, t_symbol *s, struct ex_ex *arg,\n                                                                        struct ex_ex *rval, struct ex_ex *optr);\nextern int\nmax_ex_tab(struct expr *expr, t_symbol *s, struct ex_ex *arg,\n                                                                                                                struct ex_ex *optr);\nextern int max_ex_var(struct expr *expr, t_symbol *s, struct ex_ex *optr,\n                                                                                                                                        int idx);\nextern int max_ex_var_store(struct expr *, t_symbol *, struct ex_ex *, struct ex_ex *);\nextern int ex_getsym(char *p, t_symbol **s);\nextern const char *ex_symname(t_symbol *s);\nvoid ex_mkvector(t_float *fp, t_float x, int size);\nextern void ex_size(t_expr *expr, long int argc, struct ex_ex *argv,\n                                                        struct ex_ex *optr);\nextern void ex_sum(t_expr *expr, long int argc, struct ex_ex *argv,                                                                     struct ex_ex *optr);\nextern void ex_Sum(t_expr *expr, long int argc, struct ex_ex *argv,                                                                     struct ex_ex *optr);\nextern void ex_avg(t_expr *expr, long int argc, struct ex_ex *argv,                                                                     struct ex_ex *optr);\nextern void ex_Avg(t_expr *expr, long int argc, struct ex_ex *argv,                                                                     struct ex_ex *optr);\nextern void ex_store(t_expr *expr, long int argc, struct ex_ex *argv,                                                                   struct ex_ex *optr);\n\nint value_getonly(t_symbol *s, t_float *f);\n\n\n/* These pragmas are only used for MSVC, not MinGW or Cygwin <hans@at.or.at> */\n#ifdef _MSC_VER\n#pragma warning (disable: 4305 4244)\n#endif\n\n#ifdef _WIN32\n#define abort ABORT\nvoid ABORT(void);\n#endif\n"
  },
  {
    "path": "libs/libpd/pure-data/src/x_vexp_fun.c",
    "content": "/* Copyright (c) IRCAM.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* \"expr\" was written by Shahrokh Yadegari c. 1989. -msp\n *\n * Nov. 2001 --sdy\n *      conversion for expr~\n *\n * Jan, 2002 --sdy\n *      added fmod()\n *\n * May  2002\n *      added floor and ceil for expr -- Orm Finnendahl\n *\n * July 2002 --sdy\n *      added the following math functions:\n *              cbrt - cube root\n *              erf - error function\n *              erfc - complementary error function\n *              expm1 - exponential minus 1,\n *              log1p - logarithm of 1 plus\n *              isinf - is the value infinite,\n *              finite - is the value finite\n *              isnan -- is the resut a nan (Not a number)\n *              copysign - copy sign of a number\n *              ldexp  -  multiply floating-point number by integral power of 2\n *              imodf - get signed integral value from floating-point number\n *              modf - get signed fractional value from floating-point number\n *              drem - floating-point remainder function\n *\n *      The following are done but not popular enough in math libss\n *      to be included yet\n *              hypoth - Euclidean distance function\n *              trunc\n *              round\n *              nearbyint -\n *  November 2015\n *       - drem() is now obsolete but it is kept here so that other\n *         patches do not break\n *       - added remainder() - floating-point remainder function\n *       - fixed the bug that unary operators could be used as\n *         binary ones (10 ~ 1)\n *       - fixed ceil() and floor() which should have only one argument\n *       - added copysign  (the previous one \"copysig\" which was\n *         defined with one argument was kept for compatibility)\n *       - fixed sum(\"table\"), and Sum(\"table\", x, y)\n *       - deleted avg(), Avg() as they can be simple expressions\n *       - deleted store as this can be achieved by the '=' operator\n *  July 2017 --sdy\n *\n *      - ex_if() is reworked to only evaluate either the left or the right arg\n *\tOctober 2020 --sdy\n *\t\t- fact() (factorial) now calculates and returns its value in double\n *\t\t- Added mtof(), mtof(), dbtorms(), rmstodb(), powtodb(), dbtopow()\n *\n */\n\n\n\n/*\n * vexp_func.c -- this file include all the functions for vexp.\n *                the first two arguments to the function are the number\n *                of argument and an array of arguments (argc, argv)\n *                the last argument is a pointer to a struct ex_ex for\n *                the result.  Up do this point, the content of the\n *                struct ex_ex that these functions receive are either\n *                ET_INT (long), ET_FLT (t_float), or ET_SYM (char **, it is\n *                char ** and not char * since NewHandle of Mac returns\n *                a char ** for relocatability.)  The common practice in\n *                these functions is that they figure out the type of their\n *                result according to the type of the arguments. In general\n *                the ET_SYM is used an ET_INT when we expect a value.\n *                It is the users responsibility not to pass strings to the\n *                function.\n */\n\n#include <stdlib.h>\n#include <string.h>\n\n#include <math.h>\n\n#include \"x_vexp.h\"\n\nstruct ex_ex *ex_eval(struct expr *expr, struct ex_ex *eptr,\n                                                struct ex_ex *optr, int i);\n\n/* forward declarations */\n\nstatic void ex_min(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_max(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_toint(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_rint(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_tofloat(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_pow(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_exp(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_log(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_ln(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_sin(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_cos(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_asin(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_acos(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_tan(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_atan(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_sinh(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_cosh(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_asinh(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_acosh(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_tanh(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_atanh(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_atan2(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_sqrt(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_fact(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_random(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_abs(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_fmod(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_ceil(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_floor(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr);\nstruct ex_ex * ex_if(t_expr *expr, struct ex_ex *argv, struct ex_ex *optr,\n                                     struct ex_ex *args, int idx);\nstatic void ex_ldexp(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_imodf(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_modf(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_mtof(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_ftom(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_dbtorms(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_rmstodb(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_dbtopow(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_powtodb(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr);\n#if !defined(_MSC_VER) || (_MSC_VER >= 1700)\nstatic void ex_cbrt(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_erf(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_erfc(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_expm1(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_log1p(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_isinf(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_finite(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_isnan(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_copysign(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_drem(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_remainder(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_round(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_trunc(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr);\nstatic void ex_nearbyint(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr);\n\n#endif\n#ifdef notdef\n/* the following will be added once they are more popular in math libraries */\nstatic void ex_hypoth(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr);\n#endif\n\n\nt_ex_func ex_funcs[] = {\n        {\"min\",         ex_min,         2},\n        {\"max\",         ex_max,         2},\n        {\"int\",         ex_toint,       1},\n        {\"rint\",        ex_rint,        1},\n        {\"float\",       ex_tofloat,     1},\n        {\"fmod\",        ex_fmod,        2},\n        {\"floor\",       ex_floor,       1},\n        {\"ceil\",        ex_ceil,        1},\n        {\"pow\",         ex_pow,         2},\n        {\"sqrt\",        ex_sqrt,        1},\n        {\"exp\",         ex_exp,         1},\n        {\"log10\",       ex_log,         1},\n        {\"ln\",          ex_ln,          1},\n        {\"log\",         ex_ln,          1},\n        {\"sin\",         ex_sin,         1},\n        {\"cos\",         ex_cos,         1},\n        {\"tan\",         ex_tan,         1},\n        {\"asin\",        ex_asin,        1},\n        {\"acos\",        ex_acos,        1},\n        {\"atan\",        ex_atan,        1},\n        {\"atan2\",       ex_atan2,       2},\n        {\"sinh\",        ex_sinh,        1},\n        {\"cosh\",        ex_cosh,        1},\n        {\"tanh\",        ex_tanh,        1},\n        {\"fact\",        ex_fact,        1},\n        {\"random\",      ex_random,      2},     /* random number */\n        {\"abs\",         ex_abs,         1},\n        {\"if\",          (void (*))ex_if,          3},\n        {\"ldexp\",       ex_ldexp,       2},\n        {\"imodf\",       ex_imodf,       1},\n        {\"modf\",        ex_modf,        1},\n\t\t{\"mtof\",\t\tex_mtof,\t\t1},\n\t\t{\"ftom\",\t\tex_ftom,\t\t1},\n\t\t{\"dbtorms\",\t\tex_dbtorms,\t\t1},\n\t\t{\"rmstodb\",\t\tex_rmstodb,\t\t1},\n\t\t{\"dbtopow\",\t\tex_dbtopow,\t\t1},\n\t\t{\"powtodb\",\t\tex_powtodb,\t\t1},\n#if !defined(_MSC_VER) || (_MSC_VER >= 1700)\n        {\"asinh\",       ex_asinh,       1},\n        {\"acosh\",       ex_acosh,       1},\n        {\"atanh\",       ex_atanh,       1},     /* hyperbolic atan */\n        {\"isnan\",       ex_isnan,       1},\n        {\"cbrt\",        ex_cbrt,        1},\n        {\"round\",       ex_round,       1},\n        {\"trunc\",       ex_trunc,       1},\n        {\"erf\",         ex_erf,         1},\n        {\"erfc\",        ex_erfc,        1},\n        {\"expm1\",       ex_expm1,       1},\n        {\"log1p\",       ex_log1p,       1},\n        {\"finite\",      ex_finite,      1},\n        {\"nearbyint\",   ex_nearbyint,   1},\n        {\"copysign\",    ex_copysign,    2},\n        {\"isinf\",       ex_isinf,       1},\n        {\"remainder\",   ex_remainder,           2},\n#endif\n#ifdef PD\n        {\"size\",        ex_size,        1},\n        {\"sum\",         ex_sum,         1},\n        {\"Sum\",         ex_Sum,         3},\n        {\"avg\",         ex_avg,         1},\n        {\"Avg\",         ex_Avg,         3},\n#endif\n#ifdef notdef\n/* the following will be added once they are more popular in math libraries */\n        {\"hypoth\",      ex_hypoth,      1},\n#endif\n        {0,             0,              0}\n};\n\n/*\n * FUN_EVAL --  do type checking, evaluate a function,\n *              if fltret is set return float\n *              otherwise return value based on regular typechecking,\n */\n#define FUNC_EVAL(left, right, func, leftfuncast, rightfuncast, optr, fltret) \\\nswitch (left->ex_type) {                                                \\\ncase ET_INT:                                                            \\\n        switch(right->ex_type) {                                        \\\n        case ET_INT:                                                    \\\n                if (optr->ex_type == ET_VEC) {                          \\\n                        op = optr->ex_vec;                              \\\n                        scalar = (t_float)func(leftfuncast left->ex_int,  \\\n                                        rightfuncast right->ex_int);    \\\n                        j = e->exp_vsize;                               \\\n                        while (j--)                                     \\\n                                *op++ = scalar;                         \\\n                } else {                                                \\\n                        if (fltret) {                                   \\\n                                optr->ex_type = ET_FLT;                 \\\n                                optr->ex_flt = (t_float)func(leftfuncast  \\\n                                left->ex_int, rightfuncast right->ex_int); \\\n                        } else {                                        \\\n                                optr->ex_type = ET_INT;                 \\\n                                optr->ex_int = (int)func(leftfuncast    \\\n                                left->ex_int, rightfuncast right->ex_int); \\\n                        }                                               \\\n                }                                                       \\\n                break;                                                  \\\n        case ET_FLT:                                                    \\\n                if (optr->ex_type == ET_VEC) {                          \\\n                        op = optr->ex_vec;                              \\\n                        scalar = (t_float)func(leftfuncast left->ex_int,  \\\n                                        rightfuncast right->ex_flt);    \\\n                        j = e->exp_vsize;                               \\\n                        while (j--)                                     \\\n                                *op++ = scalar;                         \\\n                } else {                                                \\\n                        optr->ex_type = ET_FLT;                         \\\n                        optr->ex_flt = (t_float)func(leftfuncast left->ex_int,  \\\n                                        rightfuncast right->ex_flt);    \\\n                }                                                       \\\n                break;                                                  \\\n        case ET_VEC:                                                    \\\n        case ET_VI:                                                     \\\n                if (optr->ex_type != ET_VEC) {                          \\\n                        if (optr->ex_type == ET_VI) {                   \\\n                                post(\"expr~: Int. error %d\", __LINE__); \\\n                                abort();                                \\\n                        }                                               \\\n                        optr->ex_type = ET_VEC;                         \\\n                        optr->ex_vec = (t_float *)                      \\\n                          fts_malloc(sizeof (t_float)*e->exp_vsize);    \\\n                }                                                       \\\n                scalar = left->ex_int;                                  \\\n                rp = right->ex_vec;                                     \\\n                op = optr->ex_vec;                                      \\\n                j = e->exp_vsize;                                       \\\n                while (j--) {                                           \\\n                        *op++ = (t_float)func(leftfuncast scalar,         \\\n                                                rightfuncast *rp);      \\\n                        rp++;                                           \\\n                }                                                       \\\n                break;                                                  \\\n        case ET_SYM:                                                    \\\n        default:                                                        \\\n                post_error((fts_object_t *) e,                          \\\n                      \"expr: FUNC_EVAL(%d): bad right type %ld\\n\",      \\\n                                              __LINE__, right->ex_type);\\\n        }                                                               \\\n        break;                                                          \\\ncase ET_FLT:                                                            \\\n        switch(right->ex_type) {                                        \\\n        case ET_INT:                                                    \\\n                if (optr->ex_type == ET_VEC) {                          \\\n                        op = optr->ex_vec;                              \\\n                        scalar = (t_float)func(leftfuncast left->ex_flt,  \\\n                                        rightfuncast right->ex_int);    \\\n                        j = e->exp_vsize;                               \\\n                        while (j--)                                     \\\n                                *op++ = scalar;                         \\\n                } else {                                                \\\n                        optr->ex_type = ET_FLT;                         \\\n                        optr->ex_flt = (t_float)func(leftfuncast left->ex_flt,  \\\n                                        rightfuncast right->ex_int);    \\\n                }                                                       \\\n                break;                                                  \\\n        case ET_FLT:                                                    \\\n                if (optr->ex_type == ET_VEC) {                          \\\n                        op = optr->ex_vec;                              \\\n                        scalar = (t_float)func(leftfuncast left->ex_flt,  \\\n                                        rightfuncast right->ex_flt);    \\\n                        j = e->exp_vsize;                               \\\n                        while (j--)                                     \\\n                                *op++ = scalar;                         \\\n                } else {                                                \\\n                        optr->ex_type = ET_FLT;                         \\\n                        optr->ex_flt = (t_float)func(leftfuncast left->ex_flt,  \\\n                                        rightfuncast right->ex_flt);    \\\n                }                                                       \\\n                break;                                                  \\\n        case ET_VEC:                                                    \\\n        case ET_VI:                                                     \\\n                if (optr->ex_type != ET_VEC) {                          \\\n                        if (optr->ex_type == ET_VI) {                   \\\n                                post(\"expr~: Int. error %d\", __LINE__); \\\n                                abort();                                \\\n                        }                                               \\\n                        optr->ex_type = ET_VEC;                         \\\n                        optr->ex_vec = (t_float *)                      \\\n                          fts_malloc(sizeof (t_float) * e->exp_vsize);\\\n                }                                                       \\\n                scalar = left->ex_flt;                                  \\\n                rp = right->ex_vec;                                     \\\n                op = optr->ex_vec;                                      \\\n                j = e->exp_vsize;                                       \\\n                while (j--) {                                           \\\n                        *op++ = (t_float)func(leftfuncast scalar,         \\\n                                                rightfuncast *rp);      \\\n                        rp++;                                           \\\n                }                                                       \\\n                break;                                                  \\\n        case ET_SYM:                                                    \\\n        default:                                                        \\\n                post_error((fts_object_t *) e,                  \\\n                      \"expr: FUNC_EVAL(%d): bad right type %ld\\n\",      \\\n                                              __LINE__, right->ex_type);\\\n        }                                                               \\\n        break;                                                          \\\ncase ET_VEC:                                                            \\\ncase ET_VI:                                                             \\\n        if (optr->ex_type != ET_VEC) {                                  \\\n                if (optr->ex_type == ET_VI) {                           \\\n                        post(\"expr~: Int. error %d\", __LINE__);         \\\n                        abort();                                        \\\n                }                                                       \\\n                optr->ex_type = ET_VEC;                                 \\\n                optr->ex_vec = (t_float *)                              \\\n                  fts_malloc(sizeof (t_float) * e->exp_vsize);  \\\n        }                                                               \\\n        op = optr->ex_vec;                                              \\\n        lp = left->ex_vec;                                              \\\n        switch(right->ex_type) {                                        \\\n        case ET_INT:                                                    \\\n                scalar = right->ex_int;                                 \\\n                j = e->exp_vsize;                                       \\\n                while (j--) {                                           \\\n                        *op++ = (t_float)func(leftfuncast *lp,            \\\n                                                rightfuncast scalar);   \\\n                        lp++;                                           \\\n                }                                                       \\\n                break;                                                  \\\n        case ET_FLT:                                                    \\\n                scalar = right->ex_flt;                                 \\\n                j = e->exp_vsize;                                       \\\n                while (j--) {                                           \\\n                        *op++ = (t_float)func(leftfuncast *lp,            \\\n                                                rightfuncast scalar);   \\\n                        lp++;                                           \\\n                }                                                       \\\n                break;                                                  \\\n        case ET_VEC:                                                    \\\n        case ET_VI:                                                     \\\n                rp = right->ex_vec;                                     \\\n                j = e->exp_vsize;                                       \\\n                while (j--) {                                           \\\n                        /*                                              \\\n                         * on a RISC processor one could copy           \\\n                         * 8 times in each round to get a considerable  \\\n                         * improvement                                  \\\n                         */                                             \\\n                        *op++ = (t_float)func(leftfuncast *lp,            \\\n                                                rightfuncast *rp);      \\\n                        rp++; lp++;                                     \\\n                }                                                       \\\n                break;                                                  \\\n        case ET_SYM:                                                    \\\n        default:                                                        \\\n                post_error((fts_object_t *) e,                  \\\n                      \"expr: FUNC_EVAL(%d): bad right type %ld\\n\",      \\\n                                              __LINE__, right->ex_type);\\\n        }                                                               \\\n        break;                                                          \\\ncase ET_SYM:                                                            \\\ndefault:                                                                \\\n                post_error((fts_object_t *) e,                  \\\n                      \"expr: FUNC_EVAL(%d): bad left type %ld\\n\",               \\\n                                              __LINE__, left->ex_type); \\\n}\n\n/*\n * FUNC_EVAL_UNARY - evaluate a unary function,\n *              if fltret is set return t_float\n *              otherwise return value based on regular typechecking,\n */\n#define FUNC_EVAL_UNARY(left, func, leftcast, optr, fltret)             \\\nswitch(left->ex_type) {                                         \\\ncase ET_INT:                                                    \\\n        if (optr->ex_type == ET_VEC) {                          \\\n                ex_mkvector(optr->ex_vec,                       \\\n                (t_float)(func (leftcast left->ex_int)), e->exp_vsize);\\\n                break;                                          \\\n        }                                                       \\\n        if (fltret) {                                           \\\n                optr->ex_type = ET_FLT;                         \\\n                optr->ex_flt = (t_float) func(leftcast left->ex_int); \\\n                break;                                          \\\n        }                                                       \\\n        optr->ex_type = ET_INT;                                 \\\n        optr->ex_int = (int) func(leftcast left->ex_int);       \\\n        break;                                                  \\\ncase ET_FLT:                                                    \\\n        if (optr->ex_type == ET_VEC) {                          \\\n                ex_mkvector(optr->ex_vec,                       \\\n                (t_float)(func (leftcast left->ex_flt)), e->exp_vsize);\\\n                break;                                          \\\n        }                                                       \\\n        optr->ex_type = ET_FLT;                                 \\\n        optr->ex_flt = (t_float) func(leftcast left->ex_flt);     \\\n        break;                                                  \\\ncase ET_VI:                                                     \\\ncase ET_VEC:                                                    \\\n        if (optr->ex_type != ET_VEC) {                          \\\n                optr->ex_type = ET_VEC;                         \\\n                optr->ex_vec = (t_float *)                      \\\n                  fts_malloc(sizeof (t_float)*e->exp_vsize);    \\\n        }                                                       \\\n        op = optr->ex_vec;                                      \\\n        lp = left->ex_vec;                                      \\\n        j = e->exp_vsize;                                       \\\n        while (j--)                                             \\\n                *op++ = (t_float)(func (leftcast *lp++));         \\\n        break;                                                  \\\ndefault:                                                        \\\n        post_error((fts_object_t *) e,                          \\\n                \"expr: FUNV_EVAL_UNARY(%d): bad left type %ld\\n\",\\\n                                      __LINE__, left->ex_type); \\\n}\n\n#undef min\n#undef max\n#define min(x,y)        (x > y ? y : x)\n#define max(x,y)        (x > y ? x : y)\n\n#define FUNC_DEF(ex_func, func, castleft, castright, fltret);                   \\\nstatic void                                                             \\\nex_func(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\\\n{                                                                       \\\n        struct ex_ex *left, *right;                                     \\\n        t_float *op; /* output pointer */                                 \\\n        t_float *lp, *rp;         /* left and right vector pointers */    \\\n        t_float scalar;                                                   \\\n        int j;                                                          \\\n                                                                        \\\n        left = argv++;                                                  \\\n        right = argv;                                                   \\\n        FUNC_EVAL(left, right, func, castleft, castright, optr, fltret); \\\n}\n\n\n#define FUNC_DEF_UNARY(ex_func, func, cast, fltret);                    \\\nstatic void                                                             \\\nex_func(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\\\n{                                                                       \\\n        struct ex_ex *left;                                             \\\n        t_float *op; /* output pointer */                                 \\\n        t_float *lp, *rp;         /* left and right vector pointers */    \\\n        t_float scalar;                                                   \\\n        int j;                                                          \\\n                                                                        \\\n        left = argv++;                                                  \\\n                                                                        \\\n        FUNC_EVAL_UNARY(left, func, cast, optr, fltret);                \\\n}\n\n/*\n * ex_min -- if any of the arguments are or the output are vectors, a vector\n *           of floats is generated otherwise the type of the result is the\n *           type of the smaller value\n */\nstatic void\nex_min(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\n{\n        struct ex_ex *left, *right;\n        t_float *op; /* output pointer */\n        t_float *lp, *rp;         /* left and right vector pointers */\n        t_float scalar;\n        int j;\n\n        left = argv++;\n        right = argv;\n\n        FUNC_EVAL(left, right, min, (double), (double), optr, 0);\n}\n\n/*\n * ex_max -- if any of the arguments are or the output are vectors, a vector\n *           of floats is generated otherwise the type of the result is the\n *           type of the larger value\n */\nstatic void\nex_max(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\n{\n        struct ex_ex *left, *right;\n        t_float *op; /* output pointer */\n        t_float *lp, *rp;         /* left and right vector pointers */\n        t_float scalar;\n        int j;\n\n        left = argv++;\n        right = argv;\n\n        FUNC_EVAL(left, right, max, (double), (double), optr, 0);\n}\n\n/*\n * ex_toint -- convert to integer\n */\nstatic void\nex_toint(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\n{\n        struct ex_ex *left;\n        t_float *op; /* output pointer */\n        t_float *lp, *rp;         /* left and right vector pointers */\n        t_float scalar;\n        int j;\n\n        left = argv++;\n\n#define toint(x)        ((int)(x))\n                FUNC_EVAL_UNARY(left, toint, (int), optr, 0);\n        }\n\n#if defined _MSC_VER && (_MSC_VER < 1800)\n/* rint is not available for Visual Studio Version < Visual Studio 2013 */\nstatic double rint(double x)\n{\n        return (floor(x + 0.5));\n}\n#endif\n\n/*\n * ex_rint -- rint() round to the nearest int according to the common\n *            rounding mechanism\n */\nstatic void\nex_rint(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\n{\n        struct ex_ex *left;\n        t_float *op; /* output pointer */\n        t_float *lp, *rp;         /* left and right vector pointers */\n        t_float scalar;\n        int j;\n\n        left = argv++;\n\n\n        FUNC_EVAL_UNARY(left, rint, (double), optr, 1);\n}\n\n/*\n * ex_tofloat -- convert to t_float\n */\nstatic void\nex_tofloat(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\n{\n        struct ex_ex *left;\n        t_float *op; /* output pointer */\n        t_float *lp, *rp;         /* left and right vector pointers */\n        t_float scalar;\n        int j;\n\n        left = argv++;\n\n#define tofloat(x)      ((t_float)(x))\n        FUNC_EVAL_UNARY(left, tofloat, (t_float), optr, 1);\n}\n\n\n/*\n * ex_pow -- the power of\n */\nstatic void\nex_pow(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\n{\n        struct ex_ex *left, *right;\n        t_float *op; /* output pointer */\n        t_float *lp, *rp;         /* left and right vector pointers */\n        t_float scalar;\n        int j;\n\n        left = argv++;\n        right = argv;\n        FUNC_EVAL(left, right, pow, (double), (double), optr, 1);\n}\n\n/*\n * ex_sqrt -- square root\n */\nstatic void\nex_sqrt(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\n{\n        struct ex_ex *left;\n        t_float *op; /* output pointer */\n        t_float *lp, *rp;         /* left and right vector pointers */\n        t_float scalar;\n        int j;\n\n        left = argv++;\n\n        FUNC_EVAL_UNARY(left, sqrt, (double), optr, 1);\n}\n\n/*\n * ex_exp -- e to the power of\n */\nstatic void\nex_exp(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\n{\n        struct ex_ex *left;\n        t_float *op; /* output pointer */\n        t_float *lp, *rp;         /* left and right vector pointers */\n        t_float scalar;\n        int j;\n\n        left = argv++;\n\n        FUNC_EVAL_UNARY(left, exp, (double), optr, 1);\n}\n\n/*\n * ex_log -- 10 based logarithm\n */\nstatic void\nex_log(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\n{\n        struct ex_ex *left;\n        t_float *op; /* output pointer */\n        t_float *lp, *rp;         /* left and right vector pointers */\n        t_float scalar;\n        int j;\n\n        left = argv++;\n\n        FUNC_EVAL_UNARY(left, log10, (double), optr, 1);\n}\n\n/*\n * ex_ln -- natural log\n */\nstatic void\nex_ln(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\n{\n        struct ex_ex *left;\n        t_float *op; /* output pointer */\n        t_float *lp, *rp;         /* left and right vector pointers */\n        t_float scalar;\n        int j;\n\n        left = argv++;\n\n        FUNC_EVAL_UNARY(left, log, (double), optr, 1);\n}\n\nstatic void\nex_sin(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\n{\n        struct ex_ex *left;\n        t_float *op; /* output pointer */\n        t_float *lp, *rp;         /* left and right vector pointers */\n        t_float scalar;\n        int j;\n\n        left = argv++;\n\n        FUNC_EVAL_UNARY(left, sin, (double), optr, 1);\n}\n\nstatic void\nex_cos(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\n{\n        struct ex_ex *left;\n        t_float *op; /* output pointer */\n        t_float *lp, *rp;         /* left and right vector pointers */\n        t_float scalar;\n        int j;\n\n        left = argv++;\n\n        FUNC_EVAL_UNARY(left, cos, (double), optr, 1);\n}\n\n\nstatic void\nex_tan(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\n{\n        struct ex_ex *left;\n        t_float *op; /* output pointer */\n        t_float *lp, *rp;         /* left and right vector pointers */\n        t_float scalar;\n        int j;\n\n        left = argv++;\n\n        FUNC_EVAL_UNARY(left, tan, (double), optr, 1);\n}\n\nstatic void\nex_asin(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\n{\n        struct ex_ex *left;\n        t_float *op; /* output pointer */\n        t_float *lp, *rp;         /* left and right vector pointers */\n        t_float scalar;\n        int j;\n\n        left = argv++;\n\n        FUNC_EVAL_UNARY(left, asin, (double), optr, 1);\n}\n\nstatic void\nex_acos(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\n{\n        struct ex_ex *left;\n        t_float *op; /* output pointer */\n        t_float *lp, *rp;         /* left and right vector pointers */\n        t_float scalar;\n        int j;\n\n        left = argv++;\n\n        FUNC_EVAL_UNARY(left, acos, (double), optr, 1);\n}\n\n\nstatic void\nex_atan(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\n{\n        struct ex_ex *left;\n        t_float *op; /* output pointer */\n        t_float *lp, *rp;         /* left and right vector pointers */\n        t_float scalar;\n        int j;\n\n        left = argv++;\n\n        FUNC_EVAL_UNARY(left, atan, (double), optr, 1);\n}\n\n/*\n *ex_atan2 --\n */\nstatic void\nex_atan2(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\n{\n        struct ex_ex *left, *right;\n        t_float *op; /* output pointer */\n        t_float *lp, *rp;         /* left and right vector pointers */\n        t_float scalar;\n        int j;\n\n        left = argv++;\n        right = argv;\n        FUNC_EVAL(left, right, atan2, (double), (double), optr, 1);\n}\n\n/*\n * ex_fmod -- floating point modulo\n */\nstatic void\nex_fmod(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\n{\n        struct ex_ex *left, *right;\n        t_float *op; /* output pointer */\n        t_float *lp, *rp;         /* left and right vector pointers */\n        t_float scalar;\n        int j;\n\n        left = argv++;\n        right = argv;\n        FUNC_EVAL(left, right, fmod, (double), (double), optr, 1);\n}\n\n\n/*\n * ex_floor -- floor\n */\nstatic void\nex_floor(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\n{\n        struct ex_ex *left;\n        t_float *op; /* output pointer */\n        t_float *lp, *rp;         /* left and right vector pointers */\n        t_float scalar;\n        int j;\n\n        left = argv++;\n        FUNC_EVAL_UNARY(left, floor, (double), optr, 1);\n}\n\n\n/*\n * ex_ceil -- ceil\n */\nstatic void\nex_ceil(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\n{\n        struct ex_ex *left;\n        t_float *op; /* output pointer */\n        t_float *lp, *rp;         /* left and right vector pointers */\n        t_float scalar;\n        int j;\n\n        left = argv++;\n        FUNC_EVAL_UNARY(left, ceil, (double), optr, 1);\n}\n\nstatic void\nex_sinh(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\n{\n        struct ex_ex *left;\n        t_float *op; /* output pointer */\n        t_float *lp, *rp;         /* left and right vector pointers */\n        t_float scalar;\n        int j;\n\n        left = argv++;\n\n        FUNC_EVAL_UNARY(left, sinh, (double), optr, 1);\n}\n\nstatic void\nex_cosh(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\n{\n        struct ex_ex *left;\n        t_float *op; /* output pointer */\n        t_float *lp, *rp;         /* left and right vector pointers */\n        t_float scalar;\n        int j;\n\n        left = argv++;\n\n        FUNC_EVAL_UNARY(left, cosh, (double), optr, 1);\n}\n\n\nstatic void\nex_tanh(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\n{\n        struct ex_ex *left;\n        t_float *op; /* output pointer */\n        t_float *lp, *rp;         /* left and right vector pointers */\n        t_float scalar;\n        int j;\n\n        left = argv++;\n\n        FUNC_EVAL_UNARY(left, tanh, (double), optr, 1);\n}\n\n\n#if !defined(_MSC_VER) || (_MSC_VER >= 1700)\nstatic void\nex_asinh(t_expr *e, long argc, struct ex_ex *argv, struct ex_ex *optr)\n{\n        struct ex_ex *left;\n        t_float *op; /* output pointer */\n        t_float *lp, *rp;         /* left and right vector pointers */\n        t_float scalar;\n        int j;\n\n        left = argv++;\n\n        FUNC_EVAL_UNARY(left, asinh, (double), optr, 1);\n}\n\nstatic void\nex_acosh(t_expr *e, long argc, struct ex_ex *argv, struct ex_ex *optr)\n{\n        struct ex_ex *left;\n        t_float *op; /* output pointer */\n        t_float *lp, *rp;         /* left and right vector pointers */\n        t_float scalar;\n        int j;\n\n        left = argv++;\n\n        FUNC_EVAL_UNARY(left, acosh, (double), optr, 1);\n}\n\nstatic void\nex_atanh(t_expr *e, long argc, struct ex_ex *argv, struct ex_ex *optr)\n{\n        struct ex_ex *left;\n        t_float *op; /* output pointer */\n        t_float *lp, *rp;         /* left and right vector pointers */\n        t_float scalar;\n        int j;\n\n        left = argv++;\n\n        FUNC_EVAL_UNARY(left, atanh, (double), optr, 1);\n}\n#endif\n\nstatic double\nex_dofact(int i)\n{\n        float ret = 0;\n\n        if (i > 0)\n                ret = 1;\n        else\n                return (1);\n\n        do {\n                ret *= i;\n        } while (--i);\n\n        return(ret);\n}\n\nstatic void\nex_fact(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\n{\n        struct ex_ex *left;\n        t_float *op; /* output pointer */\n        t_float *lp, *rp;         /* left and right vector pointers */\n        t_float scalar;\n        int j;\n\n        left = argv++;\n\n        FUNC_EVAL_UNARY(left, ex_dofact,  (int), optr, 1);\n}\n\nstatic int\nex_dorandom(int i1, int i2)\n{\n                int i;\n                int j;\n\n                j = rand() & 0x7fffL;\n                i = i1 + (int)((((float)(i2 - i1)) * (float)j) / pow (2, 15));\n                return (i);\n}\n/*\n * ex_random -- return a random number\n */\nstatic void\nex_random(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\n{\n        struct ex_ex *left, *right;\n        t_float *op; /* output pointer */\n        t_float *lp, *rp;         /* left and right vector pointers */\n        t_float scalar;\n        int j;\n\n        left = argv++;\n        right = argv;\n        FUNC_EVAL(left, right, ex_dorandom, (int), (int), optr, 0);\n}\n\n\nstatic void\nex_abs(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\n{\n        struct ex_ex *left;\n        t_float *op; /* output pointer */\n        t_float *lp, *rp;         /* left and right vector pointers */\n        t_float scalar;\n        int j;\n\n        left = argv++;\n\n        FUNC_EVAL_UNARY(left, fabs, (double), optr, 0);\n}\n\n/*\n * ex_if -- if function\n */\nstruct ex_ex *\nex_if(t_expr *e, struct ex_ex *eptr, struct ex_ex *optr, struct ex_ex *argv, int idx)\n{\n        struct ex_ex *left, *right, *cond, *res;\n        t_float *op; /* output pointer */\n        t_float *lp, *rp;         /* left and right vector pointers */\n        t_float *cp;              /* condition pointer */\n        t_float leftvalue, rightvalue;\n        int j;\n                int condtrue = 0;\n\n                // evaluate the condition\n                eptr = ex_eval(e, eptr, argv, idx);\n        cond = argv++;\n                // only either the left or right will be evaluated depending\n                // on the truth value of the condition\n                // However, if the condition is a vector, both args will be evaluated\n\n        switch (cond->ex_type) {\n        case ET_VEC:\n        case ET_VI:\n                if (optr->ex_type != ET_VEC) {\n                        if (optr->ex_type == ET_VI) {\n                                /* SDY remove this test */\n                                post(\"expr~: Int. error %d\", __LINE__);\n                                return (eptr);\n                        }\n                        optr->ex_type = ET_VEC;\n                        optr->ex_vec = (t_float *)\n                                  fts_malloc(sizeof (t_float) * e->exp_vsize);\n                                                if (!optr->ex_vec) {\n                                                        post(\"expr:if: no mem\");\n                                                        /* pass over the left and right args */\n                                                        return(cond->ex_end->ex_end);\n                                                }\n                }\n                                /*\n                                 * if the condition is a vector\n                                 * the left and the right args both will get processed\n                                 */\n                                eptr = ex_eval(e, eptr, argv, idx);\n                                left = argv++;\n                                eptr = ex_eval(e, eptr, argv, idx);\n                                right = argv;\n                op = optr->ex_vec;\n                j = e->exp_vsize;\n                cp = cond->ex_vec;\n                switch (left->ex_type) {\n                case ET_INT:\n                        leftvalue = left->ex_int;\n                        switch (right->ex_type) {\n                        case ET_INT:\n                                rightvalue = right->ex_int;\n                                while (j--) {\n                                        if (*cp++)\n                                                *op++ = leftvalue;\n                                        else\n                                                *op++ = rightvalue;\n                                }\n                        return (eptr);\n                        case ET_FLT:\n                                rightvalue = right->ex_flt;\n                                while (j--) {\n                                        if (*cp++)\n                                                *op++ = leftvalue;\n                                        else\n                                                *op++ = rightvalue;\n                                }\n                                return (eptr);\n                        case ET_VEC:\n                        case ET_VI:\n                                rp = right->ex_vec;\n                                while (j--) {\n                                        if (*cp++)\n                                                *op++ = leftvalue;\n                                        else\n                                                *op++ = *rp;\n                                        rp++;\n                                }\n                                return (eptr);\n                        case ET_SYM:\n                        default:\n                                post_error((fts_object_t *) e,\n                              \"expr: FUNC_EVAL(%d): bad right type %ld\\n\",\n                                                      __LINE__, right->ex_type);\n                                return (eptr);\n                        }\n                case ET_FLT:\n                        leftvalue = left->ex_flt;\n                        switch (right->ex_type) {\n                        case ET_INT:\n                                rightvalue = right->ex_int;\n                                while (j--) {\n                                        if (*cp++)\n                                                *op++ = leftvalue;\n                                        else\n                                                *op++ = rightvalue;\n                                }\n                                return (eptr);\n                        case ET_FLT:\n                                rightvalue = right->ex_flt;\n                                while (j--) {\n                                        if (*cp++)\n                                                *op++ = leftvalue;\n                                        else\n                                                *op++ = rightvalue;\n                                }\n                                return (eptr);\n                        case ET_VEC:\n                        case ET_VI:\n                                rp = right->ex_vec;\n                                while (j--) {\n                                        if (*cp++)\n                                                *op++ = leftvalue;\n                                        else\n                                                *op++ = *rp;\n                                        rp++;\n                                }\n                                return (eptr);\n                        case ET_SYM:\n                        default:\n                                post_error((fts_object_t *) e,\n                              \"expr: FUNC_EVAL(%d): bad right type %ld\\n\",\n                                                      __LINE__, right->ex_type);\n                                return (eptr);\n                        }\n                case ET_VEC:\n                case ET_VI:\n                        lp = left->ex_vec;\n                        switch (right->ex_type) {\n                        case ET_INT:\n                                rightvalue = right->ex_int;\n                                while (j--) {\n                                        if (*cp++)\n                                                *op++ = *lp;\n                                        else\n                                                *op++ = rightvalue;\n                                        lp++;\n                                }\n                                return (eptr);\n                        case ET_FLT:\n                                rightvalue = right->ex_flt;\n                                while (j--) {\n                                        if (*cp++)\n                                                *op++ = *lp;\n                                        else\n                                                *op++ = rightvalue;\n                                        lp++;\n                                }\n                                return (eptr);\n                        case ET_VEC:\n                        case ET_VI:\n                                rp = right->ex_vec;\n                                while (j--) {\n                                        if (*cp++)\n                                                *op++ = *lp;\n                                        else\n                                                *op++ = *rp;\n                                        lp++; rp++;\n                                }\n                                return (eptr);\n                        case ET_SYM:\n                        default:\n                                post_error((fts_object_t *) e,\n                              \"expr: FUNC_EVAL(%d): bad right type %ld\\n\",\n                                                      __LINE__, right->ex_type);\n                                return (eptr);\n                        }\n                case ET_SYM:\n                default:\n                        post_error((fts_object_t *) e,\n                      \"expr: FUNC_EVAL(%d): bad left type %ld\\n\",\n                                              __LINE__, left->ex_type);\n                        return (eptr);\n                }\n        case ET_INT:\n                if (cond->ex_int)\n                        condtrue = 1;\n                else\n                        condtrue = 0;\n                break;\n        case ET_FLT:\n                if (cond->ex_flt)\n                        condtrue = 1;\n                else\n                        condtrue = 0;\n                break;\n        case ET_SYM:\n        default:\n                post_error((fts_object_t *) e,\n              \"expr: FUNC_EVAL(%d): bad condition type %ld\\n\",\n                                      __LINE__, cond->ex_type);\n                return (eptr);\n        }\n                if (condtrue) {\n                        eptr = ex_eval(e, eptr, argv, idx);\n                        res = argv++;\n                        if (!eptr)\n                                return (exNULL);\n                        eptr = eptr->ex_end; /* no right processing */\n\n                } else {\n                        if (!eptr)\n                                return (exNULL);\n                        eptr = eptr->ex_end; /* no left rocessing */\n                        eptr = ex_eval(e, eptr, argv, idx);\n                        res = argv++;\n                }\n        switch(res->ex_type) {\n        case ET_INT:\n                if (optr->ex_type == ET_VEC) {\n                        ex_mkvector(optr->ex_vec, (t_float)res->ex_int,\n                                                                e->exp_vsize);\n                        return (eptr);\n                }\n                *optr = *res;\n                return (eptr);\n        case ET_FLT:\n                if (optr->ex_type == ET_VEC) {\n                        ex_mkvector(optr->ex_vec, (t_float)res->ex_flt,\n                                                                e->exp_vsize);\n                        return (eptr);\n                }\n                *optr = *res;\n                return (eptr);\n        case ET_VEC:\n        case ET_VI:\n                if (optr->ex_type != ET_VEC) {\n                        if (optr->ex_type == ET_VI) {\n                                /* SDY remove this test */\n                                post(\"expr~: Int. error %d\", __LINE__);\n                                return (eptr);\n                        }\n                        optr->ex_type = ET_VEC;\n                        optr->ex_vec = (t_float *)\n                                  fts_malloc(sizeof (t_float) * e->exp_vsize);\n                                                if (!optr->ex_vec) {\n                                                        post(\"expr:if: no mem\");\n                            return (eptr);\n                                                }\n                }\n                memcpy(optr->ex_vec, res->ex_vec, e->exp_vsize*sizeof(t_float));\n                return (eptr);\n        case ET_SYM:\n        default:\n                post_error((fts_object_t *) e,\n              \"expr: FUNC_EVAL(%d): bad res type %ld\\n\",\n                                      __LINE__, res->ex_type);\n                return (eptr);\n        }\n\n}\n\n/*\n * ex_imodf -   extract signed integral value from floating-point number\n */\nstatic double\nimodf(double x)\n{\n        double  xx;\n\n        modf(x, &xx);\n        return (xx);\n}\nFUNC_DEF_UNARY(ex_imodf, imodf, (double), 1);\n\n/*\n * ex_modf - extract signed  fractional value from floating-point number\n *\n *  using fracmodf because fmodf() is already defined in a .h file\n */\nstatic double\nfracmodf(double x)\n{\n        double  xx;\n\n        return(modf(x, &xx));\n}\nFUNC_DEF_UNARY(ex_modf, fracmodf, (double), 1);\n\n\nFUNC_DEF_UNARY(ex_mtof, mtof, (double), 1);\nFUNC_DEF_UNARY(ex_ftom, ftom, (double), 1);\nFUNC_DEF_UNARY(ex_dbtorms, dbtorms, (double), 1);\nFUNC_DEF_UNARY(ex_rmstodb, rmstodb, (double), 1);\nFUNC_DEF_UNARY(ex_dbtopow, dbtopow, (double), 1);\nFUNC_DEF_UNARY(ex_powtodb, powtodb, (double), 1);\n\n/*\n * ex_ldexp  -  multiply floating-point number by integral power of 2\n */\nFUNC_DEF(ex_ldexp, ldexp, (double), (int), 1);\n\n#if !defined(_MSC_VER) || (_MSC_VER >= 1700)\n/*\n * ex_cbrt - cube root\n */\nFUNC_DEF_UNARY(ex_cbrt, cbrt, (double), 1);\n\n/*\n * ex_erf - error function\n */\nFUNC_DEF_UNARY(ex_erf, erf, (double), 1);\n\n/*\n * ex_erfc - complementary error function\n */\nFUNC_DEF_UNARY(ex_erfc, erfc, (double), 1);\n\n/*\n * ex_expm1 - exponential minus 1,\n */\nFUNC_DEF_UNARY(ex_expm1, expm1, (double), 1);\n\n/*\n * ex_log1p - logarithm of 1 plus\n */\nFUNC_DEF_UNARY(ex_log1p, log1p, (double), 1);\n\n/*\n * ex_isinf - is the value infinite,\n */\nFUNC_DEF_UNARY(ex_isinf, isinf, (double), 0);\n\n/*\n * ex_finite - is the value finite\n */\nFUNC_DEF_UNARY(ex_finite, isfinite, (double), 0);\n\n/*\n * ex_isnan -- is the resut a nan (Not a number)\n */\nFUNC_DEF_UNARY(ex_isnan, isnan, (double), 0);\n\n/*\n * ex_copysign - copy sign of a number\n */\nFUNC_DEF(ex_copysign, copysign, (double), (double), 1);\n\n/*\n * drem() is now obsolute\n * ex_drem - floating-point remainder function\n */\nFUNC_DEF(ex_drem, remainder, (double), (double), 1);\n\n/*\n * ex_remainder - floating-point remainder function\n */\nFUNC_DEF(ex_remainder, remainder, (double), (double), 1);\n\n/*\n * ex_round -  round to nearest integer, away from zero\n */\nFUNC_DEF_UNARY(ex_round, round, (double), 1);\n\n/*\n * ex_trunc -  round to integer, towards zero\n */\nFUNC_DEF_UNARY(ex_trunc, trunc, (double), 1);\n\n/*\n * ex_nearbyint -  round to nearest integer\n */\nFUNC_DEF_UNARY(ex_nearbyint, nearbyint, (double), 1);\n\n#endif\n\n#ifdef notdef\n/* the following will be added once they are more popular in math libraries */\n/*\n * ex_hypoth - Euclidean distance function\n */\nFUNC_DEF(ex_hypoth, hypoth, (double), (double), 1);\n\n#endif\n"
  },
  {
    "path": "libs/libpd/pure-data/src/x_vexp_if.c",
    "content": "/* Copyright (c) IRCAM.\n* For information on usage and redistribution, and for a DISCLAIMER OF ALL\n* WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.  */\n\n/* \"expr\" was written by Shahrokh Yadegari c. 1989. -msp */\n/* \"expr~\" and \"fexpr~\" conversion by Shahrokh Yadegari c. 1999,2000 */\n\n/*\n * Feb 2002 - added access to variables\n *            multiple expression support\n *            new short hand forms for fexpr~\n *              now $y or $y1 = $y1[-1] and $y2 = $y2[-1]\n * --sdy\n *\n *\n *  version 0.50 - March 2016\n *  version 0.55 - July 2017\n *  version 0.56 - January 2018\n *  version 0.57 - October 2020\n */\n\n#include <stdio.h>\n#include <string.h>\n#include <stdlib.h>\n\n#include \"x_vexp.h\"\n\nstatic char *exp_version = \"0.57\";\n\nextern struct ex_ex *ex_eval(struct expr *expr, struct ex_ex *eptr,\n                                                struct ex_ex *optr, int n);\n\n#ifdef PD\nstatic t_class *expr_class;\nstatic t_class *expr_tilde_class;\nstatic t_class *fexpr_tilde_class;\n#else /* MSP */\nvoid *expr_tilde_class;\n#endif\n\n\n/*------------------------- expr class -------------------------------------*/\n\nextern int expr_donew(struct expr *expr, int ac, t_atom *av);\n\n/*#define EXPR_DEBUG*/\n\nstatic void expr_bang(t_expr *x);\nt_int *expr_perform(t_int *w);\n\n\nstatic void\nexpr_list(t_expr *x, t_symbol *s, int argc, const fts_atom_t *argv)\n{\n        int i;\n\n        if (argc > MAX_VARS) argc = MAX_VARS;\n\n        for (i = 0; i < argc; i++)\n        {\n                if (argv[i].a_type == A_FLOAT)\n                {\n                        if (x->exp_var[i].ex_type == ET_FI)\n                                x->exp_var[i].ex_flt = argv[i].a_w.w_float;\n                        else if (x->exp_var[i].ex_type == ET_II)\n                                x->exp_var[i].ex_int = argv[i].a_w.w_float;\n                        else if (x->exp_var[i].ex_type)\n                            pd_error(x, \"expr: type mismatch\");\n                }\n                else if (argv[i].a_type == A_SYMBOL)\n                {\n                        if (x->exp_var[i].ex_type == ET_SI)\n                                x->exp_var[i].ex_ptr = (char *)argv[i].a_w.w_symbol;\n                        else if (x->exp_var[i].ex_type)\n                            pd_error(x, \"expr: type mismatch\");\n                }\n        }\n        expr_bang(x);\n}\n\nstatic void\nexpr_flt(t_expr *x, t_float f, int in)\n{\n        if (in >= MAX_VARS)\n                return;\n\n        if (x->exp_var[in].ex_type == ET_FI)\n                x->exp_var[in].ex_flt = f;\n        else if (x->exp_var[in].ex_type == ET_II)\n                x->exp_var[in].ex_int = f;\n}\n\nstatic t_class *exprproxy_class;\n\ntypedef struct _exprproxy {\n        t_pd p_pd;\n        int p_index;\n        t_expr *p_owner;\n        struct _exprproxy *p_next;\n} t_exprproxy;\n\nt_exprproxy *exprproxy_new(t_expr *owner, int indx);\nvoid exprproxy_float(t_exprproxy *p, t_floatarg f);\n\nt_exprproxy *\nexprproxy_new(t_expr *owner, int indx)\n{\n        t_exprproxy *x = (t_exprproxy *)pd_new(exprproxy_class);\n        x->p_owner = owner;\n        x->p_index = indx;\n        x->p_next = owner->exp_proxy;\n        owner->exp_proxy = x;\n        return (x);\n}\n\nvoid\nexprproxy_float(t_exprproxy *p, t_floatarg f)\n{\n        t_expr *x = p->p_owner;\n        int in = p->p_index;\n\n        if (in >= MAX_VARS)\n                return;\n\n        if (x->exp_var[in].ex_type == ET_FI)\n                x->exp_var[in].ex_flt = f;\n        else if (x->exp_var[in].ex_type == ET_II)\n                x->exp_var[in].ex_int = f;\n}\n\n/* method definitions */\nstatic void\nexpr_ff(t_expr *x)\n{\n        t_exprproxy *y;\n        int i;\n\n        y = x->exp_proxy;\n        while (y)\n        {\n                x->exp_proxy = y->p_next;\n#ifdef PD\n                pd_free(&y->p_pd);\n#else /*MSP */\n        /* SDY find out what needs to be called for MSP */\n\n#endif\n                y = x->exp_proxy;\n        }\n        for (i = 0 ; i < x->exp_nexpr; i++)\n                if (x->exp_stack[i])\n                        fts_free(x->exp_stack[i]);\n/*\n * SDY free all the allocated buffers here for expr~ and fexpr~\n * check to see if there are others\n */\n        for (i = 0; i < MAX_VARS; i++) {\n                if (x->exp_p_var[i])\n                        fts_free(x->exp_p_var[i]);\n                if (x->exp_p_res[i])\n                        fts_free(x->exp_p_res[i]);\n                if (x->exp_tmpres[i])\n                        fts_free(x->exp_tmpres[i]);\n        }\n\n\n}\n\nstatic void\nexpr_bang(t_expr *x)\n{\n        int i;\n\n#ifdef EXPR_DEBUG\n        {\n                struct ex_ex *eptr;\n\n                for (i = 0, eptr = x->exp_var;  ; eptr++, i++)\n                {\n                        if (!eptr->ex_type)\n                                break;\n                        switch (eptr->ex_type)\n                        {\n                        case ET_II:\n                                fprintf(stderr,\"ET_II: %d \\n\", eptr->ex_int);\n                                break;\n\n                        case ET_FI:\n                                fprintf(stderr,\"ET_FT: %f \\n\", eptr->ex_flt);\n                                break;\n\n                        default:\n                                fprintf(stderr,\"oups\\n\");\n                        }\n                }\n        }\n#endif\n        /* banging a signal or filter object means nothing */\n        if (!IS_EXPR(x))\n                return;\n\n        for (i = x->exp_nexpr - 1; i > -1 ; i--) {\n                if (!ex_eval(x, x->exp_stack[i], &x->exp_res[i], 0)) {\n                        /*fprintf(stderr,\"expr_bang(error evaluation)\\n\"); */\n                /*  SDY now that we have multiple ones, on error we should\n                 * continue\n                        return;\n                 */\n                }\n                switch(x->exp_res[i].ex_type) {\n                case ET_INT:\n                        outlet_float(x->exp_outlet[i],\n                                        (t_float) x->exp_res[i].ex_int);\n                        break;\n\n                case ET_FLT:\n                        outlet_float(x->exp_outlet[i],  x->exp_res[i].ex_flt);\n                        break;\n\n                case ET_SYM:\n                        /* CHANGE this will have to be taken care of */\n\n                default:\n                        post(\"expr: bang: unrecognized result %ld\\n\", x->exp_res[i].ex_type);\n                }\n        }\n}\n\nstatic t_expr *\n#ifdef PD\nexpr_new(t_symbol *s, int ac, t_atom *av)\n#else /* MSP */\nNexpr_new(t_symbol *s, int ac, t_atom *av)\n#endif\n{\n        struct expr *x;\n        int i, ninlet;\n        struct ex_ex *eptr;\n        t_atom fakearg;\n        int dsp_index;  /* keeping track of the dsp inlets */\n\n\n/*\n * SDY - we may need to call dsp_setup() in this function\n */\n\n        if (!ac)\n        {\n                ac = 1;\n                av = &fakearg;\n                SETFLOAT(&fakearg, 0);\n        }\n\n#ifdef PD\n        /*\n         * figure out if we are expr, expr~, or fexpr~\n         */\n        if (!strcmp(\"expr\", s->s_name)) {\n                x = (t_expr *)pd_new(expr_class);\n                SET_EXPR(x);\n        } else if (!strcmp(\"expr~\", s->s_name)) {\n                x = (t_expr *)pd_new(expr_tilde_class);\n                SET_EXPR_TILDE(x);\n        } else if (!strcmp(\"fexpr~\", s->s_name)) {\n                x = (t_expr *)pd_new(fexpr_tilde_class);\n                SET_FEXPR_TILDE(x);\n        } else {\n                post(\"expr_new: bad object name '%s'\", s->s_name);\n                /* assume expr */\n                x = (t_expr *)pd_new(expr_class);\n                SET_EXPR(x);\n        }\n#else /* MSP */\n        /* for now assume an expr~ */\n        x = (t_expr *)pd_new(expr_tilde_class);\n        SET_EXPR_TILDE(x);\n#endif\n\n        /*\n         * initialize the newly allocated object\n         */\n        x->exp_proxy = 0;\n        x->exp_nivec = 0;\n        x->exp_nexpr = 0;\n        x->exp_error = 0;\n        for (i = 0; i < MAX_VARS; i++) {\n                x->exp_stack[i] = (struct ex_ex *)0;\n                x->exp_outlet[i] = (t_outlet *)0;\n                x->exp_res[i].ex_type = 0;\n                x->exp_res[i].ex_int = 0;\n                x->exp_p_res[i] = (t_float *)0;\n                x->exp_var[i].ex_type = 0;\n                x->exp_var[i].ex_int = 0;\n                x->exp_p_var[i] = (t_float *)0;\n                x->exp_tmpres[i] = (t_float *)0;\n                x->exp_vsize = 0;\n        }\n        x->exp_f = 0; /* save the control value to be transformed to signal */\n\n\n        if (expr_donew(x, ac, av))\n        {\n                pd_error(x, \"expr: syntax error\");\n/*\nSDY the following coredumps why?\n                pd_free(&x->exp_ob.ob_pd);\n*/\n                return (0);\n        }\n\n        ninlet = 1;\n        for (i = 0, eptr = x->exp_var; i < MAX_VARS ; i++, eptr++)\n                if (eptr->ex_type) {\n                        ninlet = i + 1;\n                }\n\n        /*\n         * create the new inlets\n         */\n        for (i = 1, eptr = x->exp_var + 1, dsp_index=1; i<ninlet ; i++, eptr++)\n        {\n                t_exprproxy *p;\n                switch (eptr->ex_type)\n                {\n                case 0:\n                        /* nothing is using this inlet */\n                        if (i < ninlet)\n#ifdef PD\n                                floatinlet_new(&x->exp_ob, &eptr->ex_flt);\n#else /* MSP */\n                                inlet_new(&x->exp_ob, \"float\");\n#endif\n                        break;\n\n                case ET_II:\n                case ET_FI:\n                        p = exprproxy_new(x, i);\n#ifdef PD\n                        inlet_new(&x->exp_ob, &p->p_pd, &s_float, &s_float);\n#else /* MSP */\n                        inlet_new(&x->exp_ob, \"float\");\n#endif\n                        break;\n\n                case ET_SI:\n#ifdef PD\n                        symbolinlet_new(&x->exp_ob, (t_symbol **)&eptr->ex_ptr);\n#else /* MSP */\n                        inlet_new(&x->exp_ob, \"symbol\");\n#endif\n                        break;\n\n                case ET_XI:\n                case ET_VI:\n                        if (!IS_EXPR(x)) {\n                                dsp_index++;\n#ifdef PD\n                                inlet_new(&x->exp_ob, &x->exp_ob.ob_pd,\n                                                        &s_signal, &s_signal);\n#else /* MSP */\n                                inlet_new(&x->exp_ob, \"signal\");\n#endif\n                                break;\n                        } else\n                                post(\"expr: internal error expr_new\");\n                                /* falls through */\n                default:\n                        pd_error(x, \"expr: bad type (%lx) inlet = %d\\n\",\n                                            eptr->ex_type, i + 1);\n                        break;\n                }\n        }\n        if (IS_EXPR(x)) {\n                for (i = 0; i < x->exp_nexpr; i++)\n                        x->exp_outlet[i] = outlet_new(&x->exp_ob, 0);\n        } else {\n                for (i = 0; i < x->exp_nexpr; i++)\n                        x->exp_outlet[i] = outlet_new(&x->exp_ob,\n                                                        gensym(\"signal\"));\n                x->exp_nivec = dsp_index;\n        }\n        /*\n         * for now assume a 64 sample size block but this may change once\n         * expr_dsp is called\n         */\n        x->exp_vsize = 64;\n        for (i = 0; i < x->exp_nexpr; i++) {\n                x->exp_p_res[i] = fts_calloc(x->exp_vsize, sizeof (t_float));\n                x->exp_tmpres[i] = fts_calloc(x->exp_vsize, sizeof (t_float));\n        }\n        for (i = 0; i < MAX_VARS; i++)\n                x->exp_p_var[i] = fts_calloc(x->exp_vsize, sizeof (t_float));\n\n        return (x);\n}\n\nt_int *\nexpr_perform(t_int *w)\n{\n        int i, j;\n        t_expr *x = (t_expr *)w[1];\n        struct ex_ex res;\n        int n;\n\n        /* sanity check */\n        if (IS_EXPR(x)) {\n                post(\"expr_perform: bad x->exp_flags = %d\", x->exp_flags);\n                abort();\n        }\n\n        if (x->exp_flags & EF_STOP) {\n                for (i = 0; i < x->exp_nexpr; i++)\n                        memset(x->exp_res[i].ex_vec, 0,\n                                        x->exp_vsize * sizeof (t_float));\n                return (w + 2);\n        }\n\n        if (IS_EXPR_TILDE(x)) {\n                /*\n                 * if we have only one expression, we can right on\n                 * on the output directly, otherwise we have to copy\n                 * the data because, outputs could be the same buffer as\n                 * inputs\n                 */\n                if ( x->exp_nexpr == 1)\n                        ex_eval(x, x->exp_stack[0], &x->exp_res[0], 0);\n                else {\n                        res.ex_type = ET_VEC;\n                        for (i = 0; i < x->exp_nexpr; i++) {\n                                res.ex_vec = x->exp_tmpres[i];\n                                ex_eval(x, x->exp_stack[i], &res, 0);\n                        }\n                        n = x->exp_vsize * sizeof(t_float);\n                        for (i = 0; i < x->exp_nexpr; i++)\n                                memcpy(x->exp_res[i].ex_vec, x->exp_tmpres[i],\n                                                                        n);\n                }\n                return (w + 2);\n        }\n\n        if (!IS_FEXPR_TILDE(x)) {\n                post(\"expr_perform: bad x->exp_flags = %d - expecting fexpr\",\n                                                                x->exp_flags);\n                return (w + 2);\n        }\n        /*\n         * since the output buffer could be the same as one of the inputs\n         * we need to keep the output in  a different buffer\n         */\n        for (i = 0; i < x->exp_vsize; i++) for (j = 0; j < x->exp_nexpr; j++) {\n                res.ex_type = 0;\n                res.ex_int = 0;\n                ex_eval(x, x->exp_stack[j], &res, i);\n                switch (res.ex_type) {\n                case ET_INT:\n                        x->exp_tmpres[j][i] = (t_float) res.ex_int;\n                        break;\n                case ET_FLT:\n                        x->exp_tmpres[j][i] = res.ex_flt;\n                        break;\n                default:\n                        post(\"expr_perform: bad result type %d\", res.ex_type);\n                }\n        }\n        /*\n         * copy inputs and results to the save buffers\n         * inputs need to be copied first as the output buffer can be\n         * same as an input buffer\n         */\n        n = x->exp_vsize * sizeof(t_float);\n        for (i = 0; i < MAX_VARS; i++)\n                if (x->exp_var[i].ex_type == ET_XI)\n                        memcpy(x->exp_p_var[i], x->exp_var[i].ex_vec, n);\n        for (i = 0; i < x->exp_nexpr; i++) {\n                memcpy(x->exp_p_res[i], x->exp_tmpres[i], n);\n                memcpy(x->exp_res[i].ex_vec, x->exp_tmpres[i], n);\n        }\n        return (w + 2);\n}\n\nstatic void\nexpr_dsp(t_expr *x, t_signal **sp)\n{\n        int i, nv;\n        int newsize;\n\n        x->exp_error = 0;               /* reset all errors */\n        newsize = (x->exp_vsize !=  sp[0]->s_n);\n        x->exp_vsize = sp[0]->s_n;      /* record the vector size */\n        for (i = 0; i < x->exp_nexpr; i++) {\n                x->exp_res[i].ex_type = ET_VEC;\n                x->exp_res[i].ex_vec =  sp[x->exp_nivec + i]->s_vec;\n        }\n        for (i = 0, nv = 0; i < MAX_VARS; i++)\n                /*\n                 * the first inlet is always a signal\n                 *\n                 * SDY  We are warning the user till this limitation\n                 * is taken away from pd\n                 */\n                if (!i || x->exp_var[i].ex_type == ET_VI ||\n                                        x->exp_var[i].ex_type == ET_XI) {\n                        if (nv >= x->exp_nivec) {\n                          post(\"expr_dsp int. err nv = %d, x->exp_nive = %d\",\n                                                          nv,  x->exp_nivec);\n                          abort();\n                        }\n                        x->exp_var[i].ex_vec  = sp[nv]->s_vec;\n                        nv++;\n                }\n        /* we always have one inlet but we may not use it */\n        if (nv != x->exp_nivec && (nv != 0 ||  x->exp_nivec != 1)) {\n                post(\"expr_dsp internal error 2 nv = %d, x->exp_nive = %d\",\n                                                          nv,  x->exp_nivec);\n                abort();\n        }\n\n        dsp_add(expr_perform, 1, (t_int *) x);\n\n        /*\n         * The buffer are now being allocated for expr~ and fexpr~\n         * because if we have more than one expression we need the\n         * temporary buffers, The save buffers are not really needed\n        if (!IS_FEXPR_TILDE(x))\n                return;\n         */\n        /*\n         * if we have already allocated the buffers and we have a\n         * new size free all the buffers\n         */\n        if (x->exp_p_res[0]) {\n                if (!newsize)\n                        return;\n                /*\n                 * if new size, reallocate all the previous buffers for fexpr~\n                 */\n                for (i = 0; i < x->exp_nexpr; i++) {\n                        fts_free(x->exp_p_res[i]);\n                        fts_free(x->exp_tmpres[i]);\n                }\n                for (i = 0; i < MAX_VARS; i++)\n                        fts_free(x->exp_p_var[i]);\n\n        }\n        for (i = 0; i < x->exp_nexpr; i++) {\n                x->exp_p_res[i] = fts_calloc(x->exp_vsize, sizeof (t_float));\n                x->exp_tmpres[i] = fts_calloc(x->exp_vsize, sizeof (t_float));\n        }\n        for (i = 0; i < MAX_VARS; i++)\n                x->exp_p_var[i] = fts_calloc(x->exp_vsize, sizeof (t_float));\n}\n\n/*\n * expr_verbose -- toggle the verbose switch\n */\nstatic void\nexpr_verbose(t_expr *x)\n{\n        if (x->exp_flags & EF_VERBOSE) {\n                x->exp_flags &= ~EF_VERBOSE;\n                post (\"verbose off\");\n        } else {\n                x->exp_flags |= EF_VERBOSE;\n                post (\"verbose on\");\n        }\n}\n\n\nstatic void\nexpr_version(t_expr *x)\n{\n        post( \"expr, expr~, fexpr~ version %s\", exp_version);\n}\n\n/*\n * expr_start -- turn on expr processing for now only used for fexpr~\n */\nstatic void\nexpr_start(t_expr *x)\n{\n        x->exp_flags &= ~EF_STOP;\n}\n\n/*\n * expr_stop -- turn on expr processing for now only used for fexpr~\n */\nstatic void\nexpr_stop(t_expr *x)\n{\n        x->exp_flags |= EF_STOP;\n}\nstatic void\nfexpr_set_usage(void)\n{\n        post(\"fexpr~: set val ...\");\n        post(\"fexpr~: set {xy}[#] val ...\");\n}\n\n/*\n * fexpr_tilde_set -- set previous values of the buffers\n *              set val val ... - sets the first elements of output buffers\n *              set x val ...   - sets the elements of the first input buffer\n *              set x# val ...  - sets the elements of the #th input buffers\n *              set y val ...   - sets the elements of the first output buffer\n *              set y# val ...  - sets the elements of the #th output buffers\n */\nstatic void\nfexpr_tilde_set(t_expr *x, t_symbol *s, int argc, t_atom *argv)\n{\n        t_symbol *sx;\n        int vecno;\n        int i, nargs;\n\n        if (!argc)\n                return;\n        sx = atom_getsymbolarg(0, argc, argv);\n        switch(sx->s_name[0]) {\n        case 'x':\n                if (!sx->s_name[1])\n                        vecno = 0;\n                else {\n                        vecno = atoi(sx->s_name + 1);\n                        if (!vecno) {\n                                post(\"fexpr~.set: bad set x vector number\");\n                                fexpr_set_usage();\n                                return;\n                        }\n                        if (vecno >= MAX_VARS) {\n                                post(\"fexpr~.set: no more than %d inlets\",\n                                                                      MAX_VARS);\n                                return;\n                        }\n                        vecno--;\n                }\n                if (x->exp_var[vecno].ex_type != ET_XI) {\n                        post(\"fexpr~-set: no signal at inlet %d\", vecno + 1);\n                        return;\n                }\n                nargs = argc - 1;\n                if (!nargs) {\n                        post(\"fexpr~-set: no argument to set\");\n                        return;\n                }\n                if (nargs > x->exp_vsize) {\n                   post(\"fexpr~.set: %d set values larger than vector size(%d)\",\n                                                        nargs,  x->exp_vsize);\n                   post(\"fexpr~.set: only the first %d values will be set\",\n                                                                x->exp_vsize);\n                   nargs = x->exp_vsize;\n                }\n                for (i = 0; i < nargs; i++) {\n                        x->exp_p_var[vecno][x->exp_vsize - i - 1] =\n                                        atom_getfloatarg(i + 1, argc, argv);\n                }\n                return;\n        case 'y':\n                if (!sx->s_name[1])\n                        vecno = 0;\n                else {\n                        vecno = atoi(sx->s_name + 1);\n                        if (!vecno) {\n                                post(\"fexpr~.set: bad set y vector number\");\n                                fexpr_set_usage();\n                                return;\n                        }\n                        vecno--;\n                }\n                if (vecno >= x->exp_nexpr) {\n                        post(\"fexpr~.set: only %d outlets\", x->exp_nexpr);\n                        return;\n                }\n                nargs = argc - 1;\n                if (!nargs) {\n                        post(\"fexpr~-set: no argument to set\");\n                        return;\n                }\n                if (nargs > x->exp_vsize) {\n                   post(\"fexpr~-set: %d set values larger than vector size(%d)\",\n                                                        nargs,  x->exp_vsize);\n                   post(\"fexpr~.set: only the first %d values will be set\",\n                                                                x->exp_vsize);\n                   nargs = x->exp_vsize;\n                }\n                for (i = 0; i < nargs; i++) {\n                        x->exp_p_res[vecno][x->exp_vsize - i - 1] =\n                                        atom_getfloatarg(i + 1, argc, argv);\n                }\n                return;\n        case 0:\n                if (argc >  x->exp_nexpr) {\n                        post(\"fexpr~.set: only %d outlets available\",\n                                                                x->exp_nexpr);\n                        post(\"fexpr~.set: the extra set values are ignored\");\n                }\n                for (i = 0; i < x->exp_nexpr && i < argc; i++)\n                        x->exp_p_res[i][x->exp_vsize - 1] =\n                                        atom_getfloatarg(i, argc, argv);\n                return;\n        default:\n                fexpr_set_usage();\n                return;\n        }\n        return;\n}\n\n/*\n * fexpr_tilde_clear - clear the past buffers\n */\nstatic void\nfexpr_tilde_clear(t_expr *x, t_symbol *s, int argc, t_atom *argv)\n{\n        t_symbol *sx;\n        int vecno;\n        int i, nargs;\n\n        /*\n         *  if no argument clear all input and output buffers\n         */\n        if (!argc) {\n                for (i = 0; i < x->exp_nexpr; i++)\n                        memset(x->exp_p_res[i], 0, x->exp_vsize*sizeof(t_float));\n                for (i = 0; i < MAX_VARS; i++)\n                        if (x->exp_var[i].ex_type == ET_XI)\n                                memset(x->exp_p_var[i], 0,\n                                                x->exp_vsize*sizeof(t_float));\n                return;\n        }\n        if (argc > 1) {\n                post(\"fexpr~ usage: 'clear' or 'clear {xy}[#]'\");\n                return;\n        }\n\n        sx = atom_getsymbolarg(0, argc, argv);\n        switch(sx->s_name[0]) {\n        case 'x':\n                if (!sx->s_name[1])\n                        vecno = 0;\n                else {\n                        vecno = atoi(sx->s_name + 1);\n                        if (!vecno) {\n                                post(\"fexpr~.clear: bad clear x vector number\");\n                                return;\n                        }\n                        if (vecno >= MAX_VARS) {\n                                post(\"fexpr~.clear: no more than %d inlets\",\n                                                                      MAX_VARS);\n                                return;\n                        }\n                        vecno--;\n                }\n                if (x->exp_var[vecno].ex_type != ET_XI) {\n                        post(\"fexpr~-clear: no signal at inlet %d\", vecno + 1);\n                        return;\n                }\n                memset(x->exp_p_var[vecno], 0, x->exp_vsize*sizeof(t_float));\n                return;\n        case 'y':\n                if (!sx->s_name[1])\n                        vecno = 0;\n                else {\n                        vecno = atoi(sx->s_name + 1);\n                        if (!vecno) {\n                                post(\"fexpr~.clear: bad clear y vector number\");\n                                return;\n                        }\n                        vecno--;\n                }\n                if (vecno >= x->exp_nexpr) {\n                        post(\"fexpr~.clear: only %d outlets\", x->exp_nexpr);\n                        return;\n                }\n                memset(x->exp_p_res[vecno], 0, x->exp_vsize*sizeof(t_float));\n                return;\n                return;\n        default:\n                post(\"fexpr~ usage: 'clear' or 'clear {xy}[#]'\");\n                return;\n        }\n        return;\n}\n\n#ifdef PD\n\nvoid\nexpr_setup(void)\n{\n        /*\n         * expr initialization\n         */\n        expr_class = class_new(gensym(\"expr\"), (t_newmethod)expr_new,\n            (t_method)expr_ff, sizeof(t_expr), 0, A_GIMME, 0);\n        class_addlist(expr_class, expr_list);\n        exprproxy_class = class_new(gensym(\"exprproxy\"), 0,\n                                        0, sizeof(t_exprproxy), CLASS_PD, 0);\n        class_addfloat(exprproxy_class, exprproxy_float);\n        class_addmethod(expr_class,(t_method)expr_version,\n                                                        gensym(\"version\"), 0);\n\n        /*\n         * expr~ initialization\n         */\n        expr_tilde_class = class_new(gensym(\"expr~\"), (t_newmethod)expr_new,\n            (t_method)expr_ff, sizeof(t_expr), 0, A_GIMME, 0);\n        class_addmethod(expr_tilde_class, nullfn, gensym(\"signal\"), 0);\n        CLASS_MAINSIGNALIN(expr_tilde_class, t_expr, exp_f);\n        class_addmethod(expr_tilde_class,(t_method)expr_dsp, gensym(\"dsp\"),\n                                                        A_CANT, 0);\n        class_sethelpsymbol(expr_tilde_class, gensym(\"expr\"));\n        class_addmethod(expr_tilde_class,(t_method)expr_version,\n                                                        gensym(\"version\"), 0);\n        /*\n         * fexpr~ initialization\n         */\n        fexpr_tilde_class = class_new(gensym(\"fexpr~\"), (t_newmethod)expr_new,\n            (t_method)expr_ff, sizeof(t_expr), 0, A_GIMME, 0);\n        class_addmethod(fexpr_tilde_class, nullfn, gensym(\"signal\"), 0);\n        CLASS_MAINSIGNALIN(fexpr_tilde_class, t_expr, exp_f);\n        class_addmethod(fexpr_tilde_class,(t_method)expr_start,\n                                                        gensym(\"start\"), 0);\n        class_addmethod(fexpr_tilde_class,(t_method)expr_stop,\n                                                        gensym(\"stop\"), 0);\n\n        class_addmethod(fexpr_tilde_class,(t_method)expr_dsp,gensym(\"dsp\"),\n                                                        A_CANT, 0);\n        class_addmethod(fexpr_tilde_class, (t_method)fexpr_tilde_set,\n                        gensym(\"set\"), A_GIMME, 0);\n        class_addmethod(fexpr_tilde_class, (t_method)fexpr_tilde_clear,\n                        gensym(\"clear\"), A_GIMME, 0);\n        class_addmethod(fexpr_tilde_class,(t_method)expr_verbose,\n                                                        gensym(\"verbose\"), 0);\n        class_addmethod(fexpr_tilde_class,(t_method)expr_version,\n                                                        gensym(\"version\"), 0);\n        class_sethelpsymbol(fexpr_tilde_class, gensym(\"expr\"));\n\n}\n\nvoid\nexpr_tilde_setup(void)\n{\n        expr_setup();\n}\n\nvoid\nfexpr_tilde_setup(void)\n{\n        expr_setup();\n}\n#else /* MSP */\nvoid\nmain(void)\n{\n        setup((t_messlist **)&expr_tilde_class, (method)Nexpr_new,\n                (method)expr_ff, (short)sizeof(t_expr), 0L, A_GIMME, 0);\n        addmess((method)expr_dsp, \"dsp\", A_CANT, 0); // dsp method\n        dsp_initclass();\n}\n#endif\n\n\n/* -- the following functions use Pd internals and so are in the \"if\" file. */\n\n\nint\nex_getsym(char *p, fts_symbol_t *s)\n{\n        *s = gensym(p);\n        return (0);\n}\n\nconst char *\nex_symname(fts_symbol_t s)\n{\n        if (!s)\n            return (0);\n        return (fts_symbol_name(s));\n}\n\n/*\n * max_ex_tab -- evaluate this table access\n *               eptr is the name of the table and arg is the index we\n *               have to put the result in optr\n *               return 1 on error and 0 otherwise\n *\n * Arguments:\n *  the expr object\n *  table\n *  the argument\n *  the result pointer\n */\nint\nmax_ex_tab(struct expr *expr, fts_symbol_t s, struct ex_ex *arg,\n    struct ex_ex *optr)\n{\n#ifdef PD\n        t_garray *garray;\n        int size;\n        long indx;\n        t_word *wvec;\n\n        if (!s || !(garray = (t_garray *)pd_findbyclass(s, garray_class)) ||\n            !garray_getfloatwords(garray, &size, &wvec))\n        {\n                optr->ex_type = ET_FLT;\n                optr->ex_flt = 0;\n                pd_error(expr, \"no such table '%s'\", ex_symname(s));\n                return (1);\n        }\n        optr->ex_type = ET_FLT;\n\n        switch (arg->ex_type) {\n        case ET_INT:\n                indx = arg->ex_int;\n                break;\n        case ET_FLT:\n                /* strange interpolation code deleted here -msp */\n                indx = arg->ex_flt;\n                break;\n\n        default:        /* do something with strings */\n                pd_error(expr, \"expr: bad argument for table '%s'\\n\", fts_symbol_name(s));\n                indx = 0;\n        }\n        if (indx < 0) indx = 0;\n        else if (indx >= size) indx = size - 1;\n        optr->ex_flt = wvec[indx].w_float;\n#else /* MSP */\n        /*\n         * table lookup not done for MSP yet\n         */\n        post(\"max_ex_tab: not complete for MSP yet!\");\n        optr->ex_type = ET_FLT;\n        optr->ex_flt = 0;\n#endif\n        return (0);\n}\n\n/*\n * max_ex_tab_store -- store a value in a table\n *                                              tbl[arg->value] = rval.value\n *               eptr is the name of the table and arg is the index we\n *               have to put the result in optr\n *               return 1 on error and 0 otherwise\n *\n * Arguments:\n *  the expr object\n *  table\n *  the argument\n *  value to be stored\n *  the result pointer\n */\nint\nmax_ex_tab_store(struct expr *expr, t_symbol *s, struct ex_ex *arg,\n                                                                        struct ex_ex *rval, struct ex_ex *optr)\n{\n#ifdef PD\n        t_garray *garray;\n        int size;\n        long indx;\n        t_word *wvec;\n\n        if (!s || !(garray = (t_garray *)pd_findbyclass(s, garray_class)) ||\n                !garray_getfloatwords(garray, &size, &wvec)) {\n                optr->ex_type = ET_FLT;\n                optr->ex_flt = 0;\n                if (s)\n                    pd_error(expr, \"no such table to store '%s'\", s->s_name);\n                else\n                    pd_error(expr, \"cannot store in unnamed table\");\n                return (1);\n        }\n        optr->ex_type = ET_FLT;\n\n        switch (arg->ex_type) {\n        case ET_INT:\n                indx = arg->ex_int;\n                break;\n        case ET_FLT:\n                /* strange interpolation code deleted here -msp */\n                indx = arg->ex_flt;\n                break;\n\n        default:        /* do something with strings */\n                pd_error(expr, \"expr: bad argument for table store '%s'\\n\",\n                        fts_symbol_name(s));\n                indx = 0;\n        }\n        if (indx < 0)\n                indx = 0;\n        else if (indx >= size)\n                indx = size - 1;\n        *optr = *rval;\n        switch (rval->ex_type) {\n        case ET_INT:\n                wvec[indx].w_float = rval->ex_int;\n                                break;\n        case ET_FLT:\n                wvec[indx].w_float = rval->ex_flt;\n                                break;\n        default:\n                pd_error(expr, \"expr:bad right value type '%ld'\", rval->ex_type);\n                optr->ex_type = ET_FLT;\n                optr->ex_flt = 0;\n                return (1);\n        }\n                garray_redraw(garray);\n                return(0);\n\n#else /* MSP */\n        /*\n         * table lookup not done for MSP yet\n         */\n        post(\"max_ex_tab: not complete for MSP yet!\");\n        optr->ex_type = ET_FLT;\n        optr->ex_flt = 0;\n#endif\n        return (0);\n}\n\nint\nmax_ex_var(struct expr *expr, t_symbol *var, struct ex_ex *optr, int idx)\n{\n        optr->ex_type = ET_FLT;\n                if (!strcmp(var->s_name, \"sys_idx\")) {\n                        optr->ex_flt = idx;\n                        return (0);\n                }\n        if (value_getfloat(var, &(optr->ex_flt))) {\n                optr->ex_type = ET_FLT;\n                optr->ex_flt = 0;\n                pd_error(expr, \"no such var '%s'\", var->s_name);\n                return (1);\n        }\n        return (0);\n}\n\n#ifdef PD /* this goes to the end of this file as the following functions\n           * should be defined in the expr object in MSP\n           */\n#define ISTABLE(sym, garray, size, vec)                               \\\nif (!sym || !(garray = (t_garray *)pd_findbyclass(sym, garray_class)) || \\\n                !garray_getfloatwords(garray, &size, &vec))  {          \\\n        optr->ex_type = ET_FLT;                                         \\\n        optr->ex_int = 0;                                               \\\n        pd_error(0, \"no such table '%s'\", sym?(sym->s_name):\"(null)\");                       \\\n        return;                                                         \\\n}\n\n/*\n * ex_size -- find the size of a table\n */\nvoid\nex_size(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\n{\n        t_symbol *s;\n        t_garray *garray;\n        int size;\n        t_word *wvec;\n\n        if (argv->ex_type != ET_SYM)\n        {\n                post(\"expr: size: need a table name\\n\");\n                optr->ex_type = ET_INT;\n                optr->ex_int = 0;\n                return;\n        }\n\n        s = (fts_symbol_t ) argv->ex_ptr;\n\n        ISTABLE(s, garray, size, wvec);\n\n        optr->ex_type = ET_INT;\n        optr->ex_int = size;\n}\n\n/*\n * ex_sum -- calculate the sum of all elements of a table\n */\n\nvoid\nex_sum(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\n{\n        t_symbol *s;\n        t_garray *garray;\n        int size;\n        t_word *wvec;\n        t_float sum;\n        int indx;\n\n        if (argv->ex_type != ET_SYM)\n        {\n                post(\"expr: sum: need a table name\\n\");\n                optr->ex_type = ET_INT;\n                optr->ex_int = 0;\n                return;\n        }\n\n        s = (fts_symbol_t ) argv->ex_ptr;\n\n        ISTABLE(s, garray, size, wvec);\n\n        for (indx = 0, sum = 0; indx < size; indx++)\n                sum += wvec[indx].w_float;\n\n        optr->ex_type = ET_FLT;\n        optr->ex_flt = sum;\n}\n\n\n/*\n * ex_Sum -- calculate the sum of table with the given boundaries\n */\n\nvoid\nex_Sum(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\n{\n        t_symbol *s;\n        t_garray *garray;\n        int size;\n        t_word *wvec;\n        t_float sum;\n        long indx, n1, n2;\n\n        if (argv->ex_type != ET_SYM)\n        {\n                post(\"expr: sum: need a table name\\n\");\n                optr->ex_type = ET_INT;\n                optr->ex_int = 0;\n                return;\n        }\n\n        s = (fts_symbol_t ) argv->ex_ptr;\n\n        ISTABLE(s, garray, size, wvec);\n\n                switch((++argv)->ex_type) {\n                case ET_INT:\n                n1 = argv->ex_int;\n                        break;\n                case ET_FLT:\n                n1 = argv->ex_flt;\n                        break;\n                default:\n                        post(\"expr: Sum: boundaries have to be fix values\\n\");\n                        optr->ex_type = ET_INT;\n                        optr->ex_int = 0;\n                        return;\n                }\n                if (n1 < 0)\n                        n1 = 0;\n\n                switch((++argv)->ex_type) {\n                case ET_INT:\n                n2 = argv->ex_int;\n                        break;\n                case ET_FLT:\n                n2 = argv->ex_flt;\n                        break;\n                default:\n                        post(\"expr: Sum: boundaries have to be fix values\\n\");\n                        optr->ex_type = ET_INT;\n                        optr->ex_int = 0;\n                        return;\n                }\n                if (n2 > size)\n                        n2 = size;\n\n        for (indx = n1, sum = 0; indx <= n2; indx++)\n                        if (indx >= 0 && indx < size)\n                                sum += wvec[indx].w_float;\n\n        optr->ex_type = ET_FLT;\n        optr->ex_flt = sum;\n}\n\n/*\n * ex_avg -- calculate the average of a table\n */\n\nvoid\nex_avg(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\n{\n        t_symbol *s;\n        t_garray *garray;\n        int size;\n        t_word *wvec;\n        t_float sum;\n        int indx;\n\n        if (argv->ex_type != ET_SYM)\n        {\n                post(\"expr: avg: need a table name\\n\");\n                optr->ex_type = ET_INT;\n                optr->ex_int = 0;\n                return;\n        }\n\n        s = (fts_symbol_t ) argv->ex_ptr;\n\n        ISTABLE(s, garray, size, wvec);\n\n        for (indx = 0, sum = 0; indx < size; indx++)\n                sum += wvec[indx].w_float;\n\n        optr->ex_type = ET_FLT;\n        optr->ex_flt = sum / size;\n}\n\n\n/*\n * ex_Avg -- calculate the average of table with the given boundaries\n */\n\nvoid\nex_Avg(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\n{\n        t_symbol *s;\n        t_garray *garray;\n        int size;\n        t_word *wvec;\n        t_float sum;\n        long indx, n1, n2;\n\n        if (argv->ex_type != ET_SYM)\n        {\n                post(\"expr: sum: need a table name\\n\");\n                optr->ex_type = ET_INT;\n                optr->ex_int = 0;\n                return;\n        }\n\n        s = (fts_symbol_t ) argv->ex_ptr;\n\n        ISTABLE(s, garray, size, wvec);\n\n                switch((++argv)->ex_type) {\n                case ET_INT:\n                n1 = argv->ex_int;\n                        break;\n                case ET_FLT:\n                n1 = argv->ex_flt;\n                        break;\n                default:\n                        post(\"expr: Avg: boundaries have to be fix values\\n\");\n                        optr->ex_type = ET_INT;\n                        optr->ex_int = 0;\n                        return;\n                }\n                if (n1 < 0)\n                        n1 = 0;\n\n                switch((++argv)->ex_type) {\n                case ET_INT:\n                n2 = argv->ex_int;\n                        break;\n                case ET_FLT:\n                n2 = argv->ex_flt;\n                        break;\n                default:\n                        post(\"expr: Avg: boundaries have to be fix values\\n\");\n                        optr->ex_type = ET_INT;\n                        optr->ex_int = 0;\n                        return;\n                }\n                if (n2 >= size)\n                        n2 = size - 1;\n\n        for (indx = n1, sum = 0; indx <= n2; indx++)\n                if (indx >= 0 && indx < size)\n                        sum += wvec[indx].w_float;\n\n        optr->ex_type = ET_FLT;\n        optr->ex_flt = sum / (n2 - n1 + 1);\n}\n\n/*\n * max_ex_store --- store a value in a variable or table\n */\nint\nmax_ex_var_store(struct expr *expr, t_symbol * var, struct ex_ex *eptr, struct ex_ex *optr)\n{\n                t_float value = 0.;\n\n                *optr = *eptr;\n                switch (eptr->ex_type) {\n                case ET_INT:\n                        value = eptr->ex_int;\n                        break;\n                case ET_FLT:\n                        value = eptr->ex_flt;\n                        break;\n                default:\n                        post(\"do not know yet\\n\");\n                }\n\n        if (value_setfloat(var, value)) {\n                optr->ex_flt = 0;\n                pd_error(expr, \"no such var '%s'\", var->s_name);\n                return (1);\n        }\n        return (0);\n}\n\n/*\n * ex_store -- store a value in a table\n *             if the index is greater the size of the table,\n *             we will make a modulo the size of the table\n */\n\nvoid\nex_store(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\n{\n/* SDY - look into this function */\n#if 0\n        fts_symbol_t s;\n        fts_integer_vector_t *tw = 0;\n\n        if (argv->ex_type != ET_SYM)\n        {\n                post(\"expr: store: need a table name\\n\");\n        }\n\n        s = (fts_symbol_t ) (argv++)->ex_ptr;\n\n        tw = table_integer_vector_get_by_name(s);\n\n        if (! tw)\n        {\n                optr->ex_type = ET_INT;\n                optr->ex_int = 0;\n                post(\"expr: store: no such table %s\\n\", fts_symbol_name(s));\n                return;\n        }\n\n        if (argv->ex_type != ET_INT || argv[1].ex_type != ET_INT)\n        {\n                post(\"expr: store: arguments have to be integer\\n\");\n                optr->ex_type = ET_INT;\n                optr->ex_int = 0;\n        }\n\n        fts_integer_vector_set_element(tw, argv->ex_int < 0 ? 0 : argv->ex_int % fts_integer_vector_get_size(tw), argv[1].ex_int);\n        *optr = argv[1];\n#endif\n}\n\n#else /* MSP */\n\nvoid\npd_error(void *object, char *fmt, ...)\n{\n    va_list ap;\n    t_int arg[8];\n    int i;\n    static int saidit = 0;\n    va_start(ap, fmt);\n/* SDY\n    vsprintf(error_string, fmt, ap);\n*/ post(fmt, ap);\n        va_end(ap);\n/* SDY\n    fprintf(stderr, \"error: %s\\n\", error_string);\n    error_object = object;\n*/\n    if (!saidit)\n    {\n        post(\"... you might be able to track this down from the Find menu.\");\n        saidit = 1;\n    }\n}\n#endif\n"
  },
  {
    "path": "pdExample/addons.make",
    "content": "ofxPd\n"
  },
  {
    "path": "pdExample/bin/data/.gitkeep",
    "content": ""
  },
  {
    "path": "pdExample/bin/data/pd/abs/test_abs.pd",
    "content": "#N canvas 80 290 336 208 10;\n#X text 14 32 this is a test abstraction ... does it load?;\n#X obj 37 85 inlet;\n#X obj 37 155 print PD;\n#X msg 37 119 test_abs: Hello World!;\n#X connect 1 0 3 0;\n#X connect 3 0 2 0;\n"
  },
  {
    "path": "pdExample/bin/data/pd/instance.pd",
    "content": "#N canvas 0 22 364 323 10;\n#X obj 39 45 r \\$0-instance;\n#X obj 175 294 print PD;\n#X obj 244 179 \\$0;\n#X obj 39 120 random 100;\n#X obj 39 76 route bang;\n#X msg 244 207 instance \\$1;\n#X obj 244 152 loadbang;\n#X obj 175 235 list prepend;\n#X msg 39 150 hello world w/ rand num of \\$1;\n#X obj 175 264 list trim;\n#X connect 0 0 4 0;\n#X connect 2 0 5 0;\n#X connect 3 0 8 0;\n#X connect 4 0 3 0;\n#X connect 4 1 7 0;\n#X connect 5 0 7 1;\n#X connect 6 0 2 0;\n#X connect 7 0 9 0;\n#X connect 8 0 7 0;\n#X connect 9 0 1 0;\n"
  },
  {
    "path": "pdExample/bin/data/pd/test.pd",
    "content": "#N canvas 486 364 409 355 10;\n#X obj 272 270 dac~;\n#N canvas 369 98 675 256 test 0;\n#N canvas 0 22 450 300 (subpatch) 0;\n#X array array1 10 float 3;\n#A 0 0.0857145 0.328572 0.500001 0.57143 0.514287 0.47143 0.357144\n0.285715 0.057143 0;\n#X coords 0 1 9 -1 200 140 1;\n#X restore 425 58 graph;\n#X obj 133 169 tabread array1;\n#X obj 133 87 until;\n#X obj 133 109 f;\n#X obj 162 109 + 1;\n#X obj 190 109 sel 0;\n#X obj 162 131 mod 10;\n#X obj 114 23 inlet;\n#X msg 12 170 FINISH ARRAY TEST;\n#X msg 248 171 START ARRAY TEST;\n#X obj 114 53 t b b b;\n#X obj 12 202 print PD;\n#X obj 248 206 print PD;\n#X obj 133 204 print PD array1;\n#X connect 1 0 13 0;\n#X connect 2 0 3 0;\n#X connect 3 0 4 0;\n#X connect 3 0 1 0;\n#X connect 4 0 6 0;\n#X connect 5 0 2 1;\n#X connect 6 0 3 1;\n#X connect 6 0 5 0;\n#X connect 7 0 10 0;\n#X connect 8 0 11 0;\n#X connect 9 0 12 0;\n#X connect 10 0 8 0;\n#X connect 10 1 2 0;\n#X connect 10 2 9 0;\n#X restore 44 238 pd test array;\n#N canvas 0 22 949 263 test 0;\n#X obj 149 202 noteout 1;\n#X obj 249 18 inlet;\n#X obj 249 50 t b b b b b b b b;\n#X obj 265 203 ctlout 1;\n#X obj 351 203 pgmout 1;\n#X msg 351 174 100;\n#X obj 426 203 bendout 1;\n#X obj 504 204 touchout 1;\n#X obj 592 205 polytouchout 1;\n#X msg 426 174 2000;\n#X msg 30 172 START MIDI TEST;\n#X msg 801 180 MIDI TEST FINISHED;\n#X obj 30 201 print PD;\n#X obj 801 206 print PD;\n#X obj 705 206 midiout;\n#X obj 705 180 unpack f f;\n#X msg 705 155 239 0;\n#X text 398 231 note: bendout values are -8192 - 8192;\n#X obj 705 123 t b b;\n#X text 250 226 note: val ctl;\n#X text 588 151 note: val note;\n#X text 693 227 note: byte port;\n#X msg 592 178 100 64;\n#X msg 149 172 60 64;\n#X msg 265 173 100 64;\n#X msg 504 177 100;\n#X connect 1 0 2 0;\n#X connect 2 0 18 0;\n#X connect 2 1 22 0;\n#X connect 2 2 25 0;\n#X connect 2 3 9 0;\n#X connect 2 4 5 0;\n#X connect 2 5 24 0;\n#X connect 2 6 23 0;\n#X connect 2 7 10 0;\n#X connect 5 0 4 0;\n#X connect 9 0 6 0;\n#X connect 10 0 12 0;\n#X connect 11 0 13 0;\n#X connect 15 0 14 0;\n#X connect 15 1 14 1;\n#X connect 16 0 15 0;\n#X connect 18 0 11 0;\n#X connect 18 1 16 0;\n#X connect 22 0 8 0;\n#X connect 23 0 0 0;\n#X connect 24 0 3 0;\n#X connect 25 0 7 0;\n#X restore 61 207 pd test midi;\n#N canvas 0 22 161 223 sines 0;\n#X obj 53 110 osc~ 400;\n#X obj 53 84 +~ 400;\n#X obj 53 34 osc~ 1;\n#X obj 53 61 *~ 150;\n#X obj 53 137 *~ 0.2;\n#X obj 53 167 outlet~;\n#X connect 0 0 4 0;\n#X connect 1 0 0 0;\n#X connect 2 0 3 0;\n#X connect 3 0 1 0;\n#X connect 4 0 5 0;\n#X restore 272 193 pd sines;\n#N canvas 508 242 260 393 tone 0;\n#X obj 99 244 line~;\n#X obj 3 296 *~;\n#X obj 3 18 r tone;\n#X obj 3 123 mtof;\n#X obj 118 145 t b b;\n#X obj 118 120 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144\n-1 -1;\n#X msg 99 192 1 50;\n#X obj 153 174 delay 100;\n#X msg 69 53 pitch 60;\n#X obj 135 289 env~;\n#X obj 135 313 change;\n#X obj 135 339 s env;\n#X obj 3 52 list trim;\n#X obj 3 86 route pitch bang;\n#X obj 3 329 *~ 0.4;\n#X obj 3 159 phasor~ 200;\n#X obj 3 360 outlet~;\n#X msg 153 205 0 500;\n#X obj 3 251 clip~ -1 1;\n#X obj 3 201 -~ 0.5;\n#X obj 3 225 *~ 2;\n#X text 11 183 make saw;\n#X connect 0 0 1 1;\n#X connect 0 0 9 0;\n#X connect 1 0 14 0;\n#X connect 2 0 12 0;\n#X connect 3 0 15 0;\n#X connect 4 0 7 0;\n#X connect 4 1 6 0;\n#X connect 5 0 4 0;\n#X connect 6 0 0 0;\n#X connect 7 0 17 0;\n#X connect 8 0 13 0;\n#X connect 9 0 10 0;\n#X connect 10 0 11 0;\n#X connect 12 0 13 0;\n#X connect 13 0 3 0;\n#X connect 13 1 4 0;\n#X connect 14 0 16 0;\n#X connect 15 0 19 0;\n#X connect 17 0 0 0;\n#X connect 18 0 1 0;\n#X connect 19 0 20 0;\n#X connect 20 0 18 0;\n#X restore 340 194 pd tone;\n#X obj 203 20 r \\$0-fromOF;\n#X obj 203 52 print PD dollar zero;\n#X obj 27 22 r fromOF;\n#X obj 98 53 print PD;\n#X msg 27 109 bang;\n#N canvas 0 22 334 183 patch 0;\n#X obj 38 73 \\$0;\n#X msg 38 103 PATCH OPENED: \\$1;\n#X obj 38 42 loadbang;\n#X obj 179 73 \\$0;\n#X msg 179 103 PATCH CLOSED: \\$1;\n#X text 177 38 [closebang];\n#X obj 38 136 print PD;\n#X obj 179 136 print PD;\n#X connect 0 0 1 0;\n#X connect 1 0 6 0;\n#X connect 2 0 0 0;\n#X connect 3 0 4 0;\n#X connect 4 0 7 0;\n#X restore 279 105 pd patch open close;\n#X obj 27 52 list trim;\n#X obj 27 81 route test;\n#N canvas 0 22 907 194 midi 0;\n#X obj 23 21 notein;\n#X obj 23 55 pack f f f;\n#X obj 134 55 pack f f f;\n#X obj 134 21 ctlin;\n#X obj 244 21 pgmin;\n#X obj 244 55 pack f f;\n#X obj 338 21 bendin;\n#X obj 338 55 pack f f;\n#X obj 435 21 touchin;\n#X obj 435 55 pack f f;\n#X obj 560 22 polytouchin;\n#X obj 701 57 pack f f;\n#X obj 701 23 midiin;\n#X obj 338 169 print PD MIDI;\n#X obj 560 56 pack f f f;\n#X text 283 1 note: bendin values are 0 - 16383;\n#X obj 798 56 pack f f;\n#X obj 798 22 sysexin;\n#X msg 23 84 notein \\$3 \\$1 \\$2;\n#X msg 134 84 ctlin \\$3 \\$2 \\$1;\n#X msg 244 84 pgm \\$2 \\$1;\n#X msg 338 84 bendin \\$2 \\$1;\n#X msg 435 84 touchin \\$2 \\$1;\n#X msg 560 85 polytouchin \\$3 \\$2 \\$1;\n#X msg 701 86 midiin \\$2 \\$1;\n#X msg 798 85 sysexin \\$2 \\$1;\n#X connect 0 0 1 0;\n#X connect 0 1 1 1;\n#X connect 0 2 1 2;\n#X connect 1 0 18 0;\n#X connect 2 0 19 0;\n#X connect 3 0 2 0;\n#X connect 3 1 2 1;\n#X connect 3 2 2 2;\n#X connect 4 0 5 0;\n#X connect 4 1 5 1;\n#X connect 5 0 20 0;\n#X connect 6 0 7 0;\n#X connect 6 1 7 1;\n#X connect 7 0 21 0;\n#X connect 8 0 9 0;\n#X connect 8 1 9 1;\n#X connect 9 0 22 0;\n#X connect 10 0 14 0;\n#X connect 10 1 14 1;\n#X connect 10 2 14 2;\n#X connect 11 0 24 0;\n#X connect 12 0 11 0;\n#X connect 12 1 11 1;\n#X connect 14 0 23 0;\n#X connect 16 0 25 0;\n#X connect 17 0 16 0;\n#X connect 17 1 16 1;\n#X connect 18 0 13 0;\n#X connect 19 0 13 0;\n#X connect 20 0 13 0;\n#X connect 21 0 13 0;\n#X connect 22 0 13 0;\n#X connect 23 0 13 0;\n#X connect 24 0 13 0;\n#X connect 25 0 13 0;\n#X restore 279 134 pd midi in;\n#N canvas 0 22 788 194 test 0;\n#X obj 185 160 s toOF;\n#X obj 185 120 f 100;\n#X obj 237 120 symbol kaaa;\n#X obj 139 120 bang;\n#X obj 324 120 list 100 2.3 test 1 2 3;\n#X msg 484 120 \\; toOF kaa 1 2.3 test;\n#X obj 266 24 inlet;\n#X obj 266 57 t b b b b b b b;\n#X msg 26 121 START MSG TEST;\n#X msg 650 118 MSG TEST FINISH;\n#X obj 26 150 print PD;\n#X obj 650 147 print PD;\n#X connect 1 0 0 0;\n#X connect 2 0 0 0;\n#X connect 3 0 0 0;\n#X connect 4 0 0 0;\n#X connect 6 0 7 0;\n#X connect 7 0 9 0;\n#X connect 7 1 5 0;\n#X connect 7 2 4 0;\n#X connect 7 3 2 0;\n#X connect 7 4 1 0;\n#X connect 7 5 3 0;\n#X connect 7 6 8 0;\n#X connect 8 0 10 0;\n#X connect 9 0 11 0;\n#X restore 78 177 pd test message;\n#N canvas 554 232 235 238 delay 0;\n#X obj 35 22 inlet~;\n#X obj 35 197 outlet~;\n#X obj 35 167 delread~ \\$0-delay1;\n#X obj 35 55 delwrite~ \\$0-delay1 5000;\n#X msg 35 137 1000;\n#X obj 35 107 loadbang;\n#X connect 0 0 3 0;\n#X connect 2 0 1 0;\n#X connect 4 0 2 0;\n#X connect 5 0 4 0;\n#X restore 205 192 pd delay;\n#X obj 205 166 adc~;\n#N canvas 742 363 375 145 license 0;\n#X text 8 15 Copyright (c) 2011 Dan Wilcox <danomatika@gmail.com>;\n#X text 10 64 For information on usage and redistribution \\, and for\na DISCLAIMER OF ALL WARRANTIES \\, see the file \\, \"LICENSE.txt \\, \"\nin this distribution.;\n#X text 9 40 BSD Simplified License;\n#X text 10 116 See https://github.com/danomatika/ofxPd for documentation\n;\n#X restore 28 316 pd license;\n#X text 255 317 Dan Wilcox 2011 BSD;\n#X obj 27 139 t b b b b;\n#X obj 27 270 test_abs;\n#N canvas 666 63 404 247 scope~ 0;\n#X obj 26 28 inlet~ audio;\n#X obj 75 154 metro 100;\n#X msg 75 125 1;\n#X obj 56 59 clip~ -1 1;\n#X obj 75 95 loadbang;\n#N canvas 0 22 450 300 (subpatch) 0;\n#X array scope 512 float 2;\n#X coords 0 1 512 -1 200 140 1;\n#X restore 168 27 graph;\n#X obj 56 190 tabwrite~ scope;\n#X obj 25 222 outlet~;\n#X connect 0 0 3 0;\n#X connect 0 0 7 0;\n#X connect 1 0 6 0;\n#X connect 2 0 1 0;\n#X connect 3 0 6 0;\n#X connect 4 0 2 0;\n#X restore 272 240 pd scope~;\n#X connect 3 0 21 0;\n#X connect 4 0 21 0;\n#X connect 5 0 6 0;\n#X connect 7 0 8 0;\n#X connect 7 0 11 0;\n#X connect 9 0 19 0;\n#X connect 11 0 12 0;\n#X connect 12 0 9 0;\n#X connect 15 0 21 0;\n#X connect 16 0 15 0;\n#X connect 19 0 20 0;\n#X connect 19 1 1 0;\n#X connect 19 2 2 0;\n#X connect 19 3 14 0;\n#X connect 21 0 0 0;\n#X connect 21 0 0 1;\n"
  },
  {
    "path": "pdExample/src/main.cpp",
    "content": "/*\n * Copyright (c) 2011 Dan Wilcox <danomatika@gmail.com>\n *\n * BSD Simplified License.\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.\n *\n * See https://github.com/danomatika/ofxPd for documentation\n *\n */\n#include \"ofMain.h\"\n#include \"ofApp.h\"\n\n//========================================================================\nint main() {\n\tofSetupOpenGL(200, 200, OF_WINDOW);\n\tofRunApp(new ofApp());\n}\n"
  },
  {
    "path": "pdExample/src/ofApp.cpp",
    "content": "/*\n * Copyright (c) 2011 Dan Wilcox <danomatika@gmail.com>\n *\n * BSD Simplified License.\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.\n *\n * See https://github.com/danomatika/ofxPd for documentation\n *\n */\n#include \"ofApp.h\"\n\n//--------------------------------------------------------------\nvoid ofApp::setup() {\n\n\tofSetFrameRate(60);\n\tofSetVerticalSync(true);\n\tofSetBackgroundColor(100, 100, 100);\n\t//ofSetLogLevel(\"Pd\", OF_LOG_VERBOSE); // see verbose info inside\n\n\t// double check where we are ...\n\tstd::cout << ofFilePath::getCurrentWorkingDirectory() << std::endl;\n\n\t// the number of libpd ticks per buffer,\n\t// used to compute the audio buffer len: tpb * blocksize (always 64)\n\t#ifdef TARGET_LINUX_ARM\n\t\t// longer latency for Raspberry PI\n\t\tint ticksPerBuffer = 32; // 32 * 64 = buffer len of 2048\n\t\tint numInputs = 0; // no built in mic\n\t#else\n\t\tint ticksPerBuffer = 8; // 8 * 64 = buffer len of 512\n\t\tint numInputs = 1;\n\t#endif\n\n\t// setup OF sound stream\n\tofSoundStreamSettings settings;\n\tsettings.numInputChannels = 1;\n\tsettings.numOutputChannels = 2;\n\tsettings.sampleRate = 44100;\n\tsettings.bufferSize = ofxPd::blockSize() * ticksPerBuffer;\n\tsettings.setInListener(this);\n\tsettings.setOutListener(this);\n\tofSoundStreamSetup(settings);\n\n\t// setup Pd\n\t//\n\t// set 4th arg to true for queued message passing using an internal ringbuffer,\n\t// this is useful if you need to control where and when the message callbacks\n\t// happen (ie. within a GUI thread)\n\t//\n\t// note: you won't see any message prints until update() is called since\n\t// the queued messages are processed there, this is normal\n\t//\n\tif(!pd.init(2, numInputs, 44100, ticksPerBuffer, false)) {\n\t\tOF_EXIT_APP(1);\n\t}\n\n\tmidiChan = 1; // midi channels are 1-16\n\n\t// subscribe to receive source names\n\tpd.subscribe(\"toOF\");\n\tpd.subscribe(\"env\");\n\n\t// add message receiver, required if you want to receive messages\n\tpd.addReceiver(*this); // automatically receives from all subscribed sources\n\tpd.ignoreSource(*this, \"env\");     // don't receive from \"env\"\n\t//pd.ignoreSource(*this);          // ignore all sources\n\t//pd.receiveSource(*this, \"toOF\"); // receive only from \"toOF\"\n\n\t// add midi receiver, required if you want to receive midi messages\n\tpd.addMidiReceiver(*this); // automatically receives from all channels\n\t//pd.ignoreMidiChannel(*this, 1);  // ignore midi channel 1\n\t//pd.ignoreMidiChannel(*this);     // ignore all channels\n\t//pd.receiveMidiChannel(*this, 1); // receive only from channel 1\n\n\t// add the data/pd folder to the search path\n\tpd.addToSearchPath(\"pd/abs\");\n\n\t// audio processing on\n\tpd.start();\n\n\t// -----------------------------------------------------\n\tstd::cout << std::endl << \"BEGIN Patch Test\" << std::endl;\n\n\t// open patch\n\tPatch patch = pd.openPatch(\"pd/test.pd\");\n\tstd::cout << patch << std::endl;\n\n\t// close patch\n\tpd.closePatch(patch);\n\tstd::cout << patch << std::endl;\n\n\t// open patch again\n\tpatch = pd.openPatch(patch);\n\tstd::cout << patch << std::endl;\n\t\n\tstd::cout << \"FINISH Patch Test\" << std::endl;\n\n\t// -----------------------------------------------------\n\tstd::cout << std::endl << \"BEGIN Message Test\" << std::endl;\n\n\t// test basic atoms\n\tpd.sendBang(\"fromOF\");\n\tpd.sendFloat(\"fromOF\", 100);\n\tpd.sendSymbol(\"fromOF\", \"test string\");\n\n\t// stream interface\n\tpd << Bang(\"fromOF\")\n\t   << Float(\"fromOF\", 100)\n\t   << Symbol(\"fromOF\", \"test string\");\n\n\t// send a list\n\tpd.startMessage();\n\tpd.addFloat(1.23);\n\tpd.addSymbol(\"a symbol\");\n\tpd.finishList(\"fromOF\");\n\n\t// send a message to the $0 receiver ie. $0-fromOF\n\tpd.startMessage();\n\tpd.addFloat(1.23);\n\tpd.addSymbol(\"a symbol\");\n\tpd.finishList(patch.dollarZeroStr()+\"-fromOF\");\n\n\t// send a list using the List object\n\tList testList;\n\ttestList.addFloat(1.23);\n\ttestList.addSymbol(\"sent from a List object\");\n\tpd.sendList(\"fromOF\", testList);\n\tpd.sendMessage(\"fromOF\", \"msg\", testList);\n\n\t// stream interface for list\n\tpd << StartMessage() << 1.23 << \"sent from a streamed list\" << FinishList(\"fromOF\");\n\n\tstd::cout << \"FINISH Message Test\" << std::endl;\n\n\t// -----------------------------------------------------\n\tstd::cout << std::endl << \"BEGIN MIDI Test\" << std::endl;\n\n\t// send functions\n\tpd.sendNoteOn(midiChan, 60);\n\tpd.sendControlChange(midiChan, 0, 64);\n\tpd.sendProgramChange(midiChan, 100); // note: pgm num range is 1 - 128\n\tpd.sendPitchBend(midiChan, 2000);    // note: ofxPd uses -8192 - 8192 while [bendin] returns 0 - 16383,\n\t\t\t\t\t\t\t\t\t\t // so sending a val of 2000 gives 10192 in pd\n\tpd.sendAftertouch(midiChan, 100);\n\tpd.sendPolyAftertouch(midiChan, 64, 100);\n\tpd.sendMidiByte(0, 239);    // note: pd adds +2 to the port number from [midiin], [sysexin], & [realtimein]\n\tpd.sendSysex(0, 239);       // so sending to port 0 gives port 2 in pd\n\tpd.sendSysRealTime(0, 239);\n\n\t// stream\n\tpd << NoteOn(midiChan, 60) << ControlChange(midiChan, 100, 64)\n\t   << ProgramChange(midiChan, 100) << PitchBend(midiChan, 2000)\n\t   << Aftertouch(midiChan, 100) << PolyAftertouch(midiChan, 64, 100)\n\t   << StartMidi(0) << 239 << Finish()\n\t   << StartSysex(0) << 239 << Finish()\n\t   << StartSysRealTime(0) << 239 << Finish();\n\n\tstd::cout << \"FINISH MIDI Test\" << std::endl;\n\n\t// -----------------------------------------------------\n\tstd::cout << std::endl << \"BEGIN Array Test\" << std::endl;\n\n\t// array check length\n\tstd::cout << \"array1 len: \" << pd.arraySize(\"array1\") << std::endl;\n\n\t// read array\n\tstd::vector<float> array1;\n\tpd.readArray(\"array1\", array1);\t// sets array to correct size\n\tstd::cout << \"array1 \";\n\tfor(int i = 0; i < array1.size(); ++i) {\n\t\tstd::cout << array1[i] << \" \";\n\t}\n\tstd::cout << std::endl;\n\n\t// write array\n\tfor(int i = 0; i < array1.size(); ++i) {\n\t\tarray1[i] = i;\n\t}\n\tpd.writeArray(\"array1\", array1);\n\n\t// ready array\n\tpd.readArray(\"array1\", array1);\n\tstd::cout << \"array1 \";\n\tfor(int i = 0; i < array1.size(); ++i) {\n\t\tstd::cout << array1[i] << \" \";\n\t}\n\tstd::cout << std::endl;\n\n\t// clear array\n\tpd.clearArray(\"array1\", 10);\n\n\t// ready array\n\tpd.readArray(\"array1\", array1);\n\tstd::cout << \"array1 \";\n\tfor(int i = 0; i < array1.size(); ++i) {\n\t\tstd::cout << array1[i] << \" \";\n\t}\n\tstd::cout << std::endl;\n\n\tstd::cout << \"FINISH Array Test\" << std::endl;\n\n\t// -----------------------------------------------------\n\tstd::cout << std::endl << \"BEGIN PD Test\" << std::endl;\n\n\tpd.sendSymbol(\"fromOF\", \"test\");\n\n\tstd::cout << \"FINISH PD Test\" << std::endl;\n\n\t// -----------------------------------------------------\n\tstd::cout << std::endl << \"BEGIN Instance Test\" << std::endl;\n\n\t// open 10 instances\n\tfor(int i = 0; i < 10; ++i) {\n\t\tPatch p = pd.openPatch(\"pd/instance.pd\");\n\t\tinstances.push_back(p);\n\t}\n\n\t// send a hello bang to each instance individually using the dollarZero\n\t// to [r $0-instance] which should print the instance dollarZero unique id\n\t// and a unique random number\n\tfor(int i = 0; i < instances.size(); ++i) {\n\t\tpd.sendBang(instances[i].dollarZeroStr()+\"-instance\");\n\t}\n\n\t// send a random float between 0 and 100\n\tfor(int i = 0; i < instances.size(); ++i) {\n\t\tpd.sendFloat(instances[i].dollarZeroStr()+\"-instance\", int(ofRandom(0, 100)));\n\t}\n\n\t// send a symbol\n\tfor(int i = 0; i < instances.size(); ++i) {\n\t\tpd.sendSymbol(instances[i].dollarZeroStr()+\"-instance\", \"howdy dude\");\n\t}\n\n\t// close all instances\n\tfor(int i = 0; i < instances.size(); ++i) {\n\t\tpd.closePatch(instances[i]);\n\t}\n\tinstances.clear();\n\n\tstd::cout << \"FINISH Instance Test\" << std::endl;\n\n\t// -----------------------------------------------------\n\t// play a tone by sending a list\n\t// [list tone pitch 72 (\n\tpd.startMessage();\n\tpd.addSymbol(\"pitch\");\n\tpd.addFloat(72);\n\tpd.finishList(\"tone\");\n\tpd.sendBang(\"tone\");\n}\n\n//--------------------------------------------------------------\nvoid ofApp::update() {\n\t\n\t// since this is a test and we don't know if init() was called with\n\t// queued = true or not, we check it here\n\tif(pd.isQueued()) {\n\t\t// process any received messages, if you're using the queue and *do not*\n\t\t// call these, you won't receive any messages or midi!\n\t\tpd.receiveMessages();\n\t\tpd.receiveMidi();\n\t}\n\n\t// update scope array from pd\n\tpd.readArray(\"scope\", scopeArray);\n}\n\n//--------------------------------------------------------------\nvoid ofApp::draw() {\n\n\t// draw scope\n\tofSetColor(0, 255, 0);\n\tofSetRectMode(OF_RECTMODE_CENTER);\n\tfloat x = 0, y = ofGetHeight()/2;\n\tfloat w = ofGetWidth() / (float) scopeArray.size(), h = ofGetHeight()/2;\n\tfor(int i = 0; i < scopeArray.size()-1; ++i) {\n\t\tofDrawLine(x, y+scopeArray[i]*h, x+w, y+scopeArray[i+1]*h);\n\t\tx += w;\n\t}\n}\n\n//--------------------------------------------------------------\nvoid ofApp::exit() {\n\n\t// cleanup\n\tofSoundStreamStop();\n}\n\n//--------------------------------------------------------------\nvoid ofApp::playTone(int pitch) {\n\tpd << StartMessage() << \"pitch\" << pitch << FinishList(\"tone\") << Bang(\"tone\");\n}\n\n//--------------------------------------------------------------\nvoid ofApp::keyPressed (int key) {\n\n\tswitch(key) {\n\t\t\n\t\t// musical keyboard\n\t\tcase 'a':\n\t\t\tplayTone(60);\n\t\t\tbreak;\n\t\tcase 'w':\n\t\t\tplayTone(61);\n\t\t\tbreak;\n\t\tcase 's':\n\t\t\tplayTone(62);\n\t\t\tbreak;\n\t\tcase 'e':\n\t\t\tplayTone(63);\n\t\t\tbreak;\n\t\tcase 'd':\n\t\t\tplayTone(64);\n\t\t\tbreak;\n\t\tcase 'f':\n\t\t\tplayTone(65);\n\t\t\tbreak;\n\t\tcase 't':\n\t\t\tplayTone(66);\n\t\t\tbreak;\n\t\tcase 'g':\n\t\t\tplayTone(67);\n\t\t\tbreak;\n\t\tcase 'y':\n\t\t\tplayTone(68);\n\t\t\tbreak;\n\t\tcase 'h':\n\t\t\tplayTone(69);\n\t\t\tbreak;\n\t\tcase 'u':\n\t\t\tplayTone(70);\n\t\t\tbreak;\n\t\tcase 'j':\n\t\t\tplayTone(71);\n\t\t\tbreak;\n\t\tcase 'k':\n\t\t\tplayTone(72);\n\t\t\tbreak;\n\n\t\tcase ' ':\n\t\t\tif(pd.isReceivingSource(*this, \"env\")) {\n\t\t\t\tpd.ignoreSource(*this, \"env\");\n\t\t\t\tstd::cout << \"ignoring env\" << std::endl;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tpd.receiveSource(*this, \"env\");\n\t\t\t\tstd::cout << \"receiving from env\" << std::endl;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tbreak;\n\t}\n}\n\n//--------------------------------------------------------------\nvoid ofApp::audioIn(ofSoundBuffer &buffer) {\n\tpd.audioIn(buffer);\n}\n\n//--------------------------------------------------------------\nvoid ofApp::audioOut(ofSoundBuffer &buffer) {\n\tpd.audioOut(buffer);\n}\n\n//--------------------------------------------------------------\nvoid ofApp::print(const std::string &message) {\n\tstd::cout << message << std::endl;\n}\n\n//--------------------------------------------------------------\nvoid ofApp::receiveBang(const std::string &dest) {\n\tstd::cout << \"OF: bang \" << dest << std::endl;\n}\n\nvoid ofApp::receiveFloat(const std::string &dest, float value) {\n\tstd::cout << \"OF: float \" << dest << \": \" << value << std::endl;\n}\n\nvoid ofApp::receiveSymbol(const std::string &dest, const std::string &symbol) {\n\tstd::cout << \"OF: symbol \" << dest << \": \" << symbol << std::endl;\n}\n\nvoid ofApp::receiveList(const std::string &dest, const pd::List &list) {\n\tstd::cout << \"OF: list \" << dest << \": \";\n\n\t// step through the list\n\tfor(int i = 0; i < list.len(); ++i) {\n\t\tif(list.isFloat(i))\n\t\t\tstd::cout << list.getFloat(i) << \" \";\n\t\telse if(list.isSymbol(i))\n\t\t\tstd::cout << list.getSymbol(i) << \" \";\n\t}\n\n\t// you can also use the built in toString function or simply stream it out\n\t// std::cout << list.toString();\n\t// std::cout << list;\n\n\t// print an OSC-style type string\n\tstd::cout << list.types() << std::endl;\n}\n\nvoid ofApp::receiveMessage(const std::string &dest, const std::string &msg, const pd::List &list) {\n\tstd::cout << \"OF: message \" << dest << \": \" << msg << \" \" << list.toString() << list.types() << std::endl;\n}\n\n//--------------------------------------------------------------\nvoid ofApp::receiveNoteOn(const int channel, const int pitch, const int velocity) {\n\tstd::cout << \"OF MIDI: note on: \" << channel << \" \" << pitch << \" \" << velocity << std::endl;\n}\n\nvoid ofApp::receiveControlChange(const int channel, const int controller, const int value) {\n\tstd::cout << \"OF MIDI: control change: \" << channel << \" \" << controller << \" \" << value << std::endl;\n}\n\n// note: pgm nums are 1-128 to match pd\nvoid ofApp::receiveProgramChange(const int channel, const int value) {\n\tstd::cout << \"OF MIDI: program change: \" << channel << \" \" << value << std::endl;\n}\n\nvoid ofApp::receivePitchBend(const int channel, const int value) {\n\tstd::cout << \"OF MIDI: pitch bend: \" << channel << \" \" << value << std::endl;\n}\n\nvoid ofApp::receiveAftertouch(const int channel, const int value) {\n\tstd::cout << \"OF MIDI: aftertouch: \" << channel << \" \" << value << std::endl;\n}\n\nvoid ofApp::receivePolyAftertouch(const int channel, const int pitch, const int value) {\n\tstd::cout << \"OF MIDI: poly aftertouch: \" << channel << \" \" << pitch << \" \" << value << std::endl;\n}\n\n// note: pd adds +2 to the port num, so sending to port 3 in pd to [midiout],\n//       shows up at port 1 in ofxPd\nvoid ofApp::receiveMidiByte(const int port, const int byte) {\n\tstd::cout << \"OF MIDI: midi byte: \" << port << \" \" << byte << std::endl;\n}\n"
  },
  {
    "path": "pdExample/src/ofApp.h",
    "content": "/*\n * Copyright (c) 2011 Dan Wilcox <danomatika@gmail.com>\n *\n * BSD Simplified License.\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.\n *\n * See https://github.com/danomatika/ofxPd for documentation\n *\n */\n#pragma once\n\n#include \"ofMain.h\"\n\n#include \"ofxPd.h\"\n\n// a namespace for the Pd types\nusing namespace pd;\n\n// inherit pd receivers to receive message and midi events\nclass ofApp : public ofBaseApp, public PdReceiver, public PdMidiReceiver {\n\n\tpublic:\n\n\t\t// main\n\t\tvoid setup();\n\t\tvoid update();\n\t\tvoid draw();\n\t\tvoid exit();\n\n\t\t// do something\n\t\tvoid playTone(int pitch);\n\t\t\n\t\t// input callbacks\n\t\tvoid keyPressed(int key);\n\t\t\n\t\t// audio callbacks\n\t\tvoid audioIn(ofSoundBuffer &buffer);\n\t\tvoid audioOut(ofSoundBuffer &buffer);\n\t\t\n\t\t// pd message receiver callbacks\n\t\tvoid print(const std::string &message);\n\t\t\n\t\tvoid receiveBang(const std::string &dest);\n\t\tvoid receiveFloat(const std::string &dest, float value);\n\t\tvoid receiveSymbol(const std::string &dest, const std::string &symbol);\n\t\tvoid receiveList(const std::string &dest, const pd::List &list);\n\t\tvoid receiveMessage(const std::string &dest, const std::string &msg, const pd::List &list);\n\t\t\n\t\t// pd midi receiver callbacks\n\t\tvoid receiveNoteOn(const int channel, const int pitch, const int velocity);\n\t\tvoid receiveControlChange(const int channel, const int controller, const int value);\n\t\tvoid receiveProgramChange(const int channel, const int value);\n\t\tvoid receivePitchBend(const int channel, const int value);\n\t\tvoid receiveAftertouch(const int channel, const int value);\n\t\tvoid receivePolyAftertouch(const int channel, const int pitch, const int value);\n\t\t\n\t\tvoid receiveMidiByte(const int port, const int byte);\n\t\t\n\t\tofxPd pd;\n\t\tstd::vector<float> scopeArray;\n\t\tstd::vector<Patch> instances;\n\t\t\n\t\tint midiChan;\n};\n"
  },
  {
    "path": "pdExampleIOS/addons.make",
    "content": "ofxPd\n"
  },
  {
    "path": "pdExampleIOS/bin/data/.gitkeep",
    "content": ""
  },
  {
    "path": "pdExampleIOS/bin/data/pd/abs/test_abs.pd",
    "content": "#N canvas 80 290 336 208 10;\n#X text 14 32 this is a test abstraction ... does it load?;\n#X obj 37 85 inlet;\n#X obj 37 155 print PD;\n#X msg 37 119 test_abs: Hello World!;\n#X connect 1 0 3 0;\n#X connect 3 0 2 0;\n"
  },
  {
    "path": "pdExampleIOS/bin/data/pd/instance.pd",
    "content": "#N canvas 0 22 364 323 10;\n#X obj 39 45 r \\$0-instance;\n#X obj 175 294 print PD;\n#X obj 244 179 \\$0;\n#X obj 39 120 random 100;\n#X obj 39 76 route bang;\n#X msg 244 207 instance \\$1;\n#X obj 244 152 loadbang;\n#X obj 175 235 list prepend;\n#X msg 39 150 hello world w/ rand num of \\$1;\n#X obj 175 264 list trim;\n#X connect 0 0 4 0;\n#X connect 2 0 5 0;\n#X connect 3 0 8 0;\n#X connect 4 0 3 0;\n#X connect 4 1 7 0;\n#X connect 5 0 7 1;\n#X connect 6 0 2 0;\n#X connect 7 0 9 0;\n#X connect 8 0 7 0;\n#X connect 9 0 1 0;\n"
  },
  {
    "path": "pdExampleIOS/bin/data/pd/test.pd",
    "content": "#N canvas 711 355 409 355 10;\n#X obj 272 270 dac~;\n#N canvas 369 98 675 256 test 0;\n#N canvas 0 22 450 300 (subpatch) 0;\n#X array array1 10 float 3;\n#A 0 0.0857145 0.328572 0.500001 0.57143 0.514287 0.47143 0.357144\n0.285715 0.057143 0;\n#X coords 0 1 9 -1 200 140 1;\n#X restore 425 58 graph;\n#X obj 133 169 tabread array1;\n#X obj 133 87 until;\n#X obj 133 109 f;\n#X obj 162 109 + 1;\n#X obj 190 109 sel 0;\n#X obj 162 131 mod 10;\n#X obj 114 23 inlet;\n#X msg 12 170 FINISH ARRAY TEST;\n#X msg 248 171 START ARRAY TEST;\n#X obj 114 53 t b b b;\n#X obj 12 202 print PD;\n#X obj 248 206 print PD;\n#X obj 133 204 print PD array1;\n#X connect 1 0 13 0;\n#X connect 2 0 3 0;\n#X connect 3 0 4 0;\n#X connect 3 0 1 0;\n#X connect 4 0 6 0;\n#X connect 5 0 2 1;\n#X connect 6 0 3 1;\n#X connect 6 0 5 0;\n#X connect 7 0 10 0;\n#X connect 8 0 11 0;\n#X connect 9 0 12 0;\n#X connect 10 0 8 0;\n#X connect 10 1 2 0;\n#X connect 10 2 9 0;\n#X restore 44 238 pd test array;\n#N canvas 0 22 949 263 test 0;\n#X obj 149 202 noteout 1;\n#X obj 249 18 inlet;\n#X obj 249 50 t b b b b b b b b;\n#X obj 265 203 ctlout 1;\n#X obj 351 203 pgmout 1;\n#X msg 351 174 100;\n#X obj 426 203 bendout 1;\n#X obj 504 204 touchout 1;\n#X obj 592 205 polytouchout 1;\n#X msg 426 174 2000;\n#X msg 30 172 START MIDI TEST;\n#X msg 801 180 MIDI TEST FINISHED;\n#X obj 30 201 print PD;\n#X obj 801 206 print PD;\n#X obj 705 206 midiout;\n#X obj 705 180 unpack f f;\n#X msg 705 155 239 0;\n#X text 398 231 note: bendout values are -8192 - 8192;\n#X obj 705 123 t b b;\n#X text 250 226 note: val ctl;\n#X text 588 151 note: val note;\n#X text 693 227 note: byte port;\n#X msg 592 178 100 64;\n#X msg 149 172 60 64;\n#X msg 265 173 100 64;\n#X msg 504 177 100;\n#X connect 1 0 2 0;\n#X connect 2 0 18 0;\n#X connect 2 1 22 0;\n#X connect 2 2 25 0;\n#X connect 2 3 9 0;\n#X connect 2 4 5 0;\n#X connect 2 5 24 0;\n#X connect 2 6 23 0;\n#X connect 2 7 10 0;\n#X connect 5 0 4 0;\n#X connect 9 0 6 0;\n#X connect 10 0 12 0;\n#X connect 11 0 13 0;\n#X connect 15 0 14 0;\n#X connect 15 1 14 1;\n#X connect 16 0 15 0;\n#X connect 18 0 11 0;\n#X connect 18 1 16 0;\n#X connect 22 0 8 0;\n#X connect 23 0 0 0;\n#X connect 24 0 3 0;\n#X connect 25 0 7 0;\n#X restore 61 207 pd test midi;\n#N canvas 0 22 161 223 sines 0;\n#X obj 53 110 osc~ 400;\n#X obj 53 84 +~ 400;\n#X obj 53 34 osc~ 1;\n#X obj 53 61 *~ 150;\n#X obj 53 137 *~ 0.2;\n#X obj 53 167 outlet~;\n#X connect 0 0 4 0;\n#X connect 1 0 0 0;\n#X connect 2 0 3 0;\n#X connect 3 0 1 0;\n#X connect 4 0 5 0;\n#X restore 272 193 pd sines;\n#N canvas 508 242 260 393 tone 0;\n#X obj 99 244 line~;\n#X obj 3 296 *~;\n#X obj 3 18 r tone;\n#X obj 3 123 mtof;\n#X obj 118 145 t b b;\n#X obj 118 120 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144\n-1 -1;\n#X msg 99 192 1 50;\n#X obj 153 174 delay 100;\n#X msg 69 53 pitch 60;\n#X obj 135 289 env~;\n#X obj 135 313 change;\n#X obj 135 339 s env;\n#X obj 3 52 list trim;\n#X obj 3 86 route pitch bang;\n#X obj 3 329 *~ 0.4;\n#X obj 3 159 phasor~ 200;\n#X obj 3 360 outlet~;\n#X msg 153 205 0 500;\n#X obj 3 251 clip~ -1 1;\n#X obj 3 201 -~ 0.5;\n#X obj 3 225 *~ 2;\n#X text 11 183 make saw;\n#X connect 0 0 1 1;\n#X connect 0 0 9 0;\n#X connect 1 0 14 0;\n#X connect 2 0 12 0;\n#X connect 3 0 15 0;\n#X connect 4 0 7 0;\n#X connect 4 1 6 0;\n#X connect 5 0 4 0;\n#X connect 6 0 0 0;\n#X connect 7 0 17 0;\n#X connect 8 0 13 0;\n#X connect 9 0 10 0;\n#X connect 10 0 11 0;\n#X connect 12 0 13 0;\n#X connect 13 0 3 0;\n#X connect 13 1 4 0;\n#X connect 14 0 16 0;\n#X connect 15 0 19 0;\n#X connect 17 0 0 0;\n#X connect 18 0 1 0;\n#X connect 19 0 20 0;\n#X connect 20 0 18 0;\n#X restore 340 194 pd tone;\n#X obj 203 20 r \\$0-fromOF;\n#X obj 203 52 print PD dollar zero;\n#X obj 27 22 r fromOF;\n#X obj 98 53 print PD;\n#X msg 27 109 bang;\n#N canvas 0 22 334 183 patch 0;\n#X obj 38 73 \\$0;\n#X msg 38 103 PATCH OPENED: \\$1;\n#X obj 38 42 loadbang;\n#X obj 179 73 \\$0;\n#X msg 179 103 PATCH CLOSED: \\$1;\n#X text 177 38 [closebang];\n#X obj 38 136 print PD;\n#X obj 179 136 print PD;\n#X connect 0 0 1 0;\n#X connect 1 0 6 0;\n#X connect 2 0 0 0;\n#X connect 3 0 4 0;\n#X connect 4 0 7 0;\n#X restore 279 105 pd patch open close;\n#X obj 27 52 list trim;\n#X obj 27 81 route test;\n#N canvas 0 22 907 194 midi 0;\n#X obj 23 21 notein;\n#X obj 23 55 pack f f f;\n#X obj 134 55 pack f f f;\n#X obj 134 21 ctlin;\n#X obj 244 21 pgmin;\n#X obj 244 55 pack f f;\n#X obj 338 21 bendin;\n#X obj 338 55 pack f f;\n#X obj 435 21 touchin;\n#X obj 435 55 pack f f;\n#X obj 560 22 polytouchin;\n#X obj 701 57 pack f f;\n#X obj 701 23 midiin;\n#X obj 338 169 print PD MIDI;\n#X obj 560 56 pack f f f;\n#X text 283 1 note: bendin values are 0 - 16383;\n#X obj 798 56 pack f f;\n#X obj 798 22 sysexin;\n#X msg 23 84 notein \\$3 \\$1 \\$2;\n#X msg 134 84 ctlin \\$3 \\$2 \\$1;\n#X msg 244 84 pgm \\$2 \\$1;\n#X msg 338 84 bendin \\$2 \\$1;\n#X msg 435 84 touchin \\$2 \\$1;\n#X msg 560 85 polytouchin \\$3 \\$2 \\$1;\n#X msg 701 86 midiin \\$2 \\$1;\n#X msg 798 85 sysexin \\$2 \\$1;\n#X connect 0 0 1 0;\n#X connect 0 1 1 1;\n#X connect 0 2 1 2;\n#X connect 1 0 18 0;\n#X connect 2 0 19 0;\n#X connect 3 0 2 0;\n#X connect 3 1 2 1;\n#X connect 3 2 2 2;\n#X connect 4 0 5 0;\n#X connect 4 1 5 1;\n#X connect 5 0 20 0;\n#X connect 6 0 7 0;\n#X connect 6 1 7 1;\n#X connect 7 0 21 0;\n#X connect 8 0 9 0;\n#X connect 8 1 9 1;\n#X connect 9 0 22 0;\n#X connect 10 0 14 0;\n#X connect 10 1 14 1;\n#X connect 10 2 14 2;\n#X connect 11 0 24 0;\n#X connect 12 0 11 0;\n#X connect 12 1 11 1;\n#X connect 14 0 23 0;\n#X connect 16 0 25 0;\n#X connect 17 0 16 0;\n#X connect 17 1 16 1;\n#X connect 18 0 13 0;\n#X connect 19 0 13 0;\n#X connect 20 0 13 0;\n#X connect 21 0 13 0;\n#X connect 22 0 13 0;\n#X connect 23 0 13 0;\n#X connect 24 0 13 0;\n#X connect 25 0 13 0;\n#X restore 279 134 pd midi in;\n#N canvas 0 22 788 194 test 0;\n#X obj 185 160 s toOF;\n#X obj 185 120 f 100;\n#X obj 237 120 symbol kaaa;\n#X obj 139 120 bang;\n#X obj 324 120 list 100 2.3 test 1 2 3;\n#X msg 484 120 \\; toOF kaa 1 2.3 test;\n#X obj 266 24 inlet;\n#X obj 266 57 t b b b b b b b;\n#X msg 26 121 START MSG TEST;\n#X msg 650 118 MSG TEST FINISH;\n#X obj 26 150 print PD;\n#X obj 650 147 print PD;\n#X connect 1 0 0 0;\n#X connect 2 0 0 0;\n#X connect 3 0 0 0;\n#X connect 4 0 0 0;\n#X connect 6 0 7 0;\n#X connect 7 0 9 0;\n#X connect 7 1 5 0;\n#X connect 7 2 4 0;\n#X connect 7 3 2 0;\n#X connect 7 4 1 0;\n#X connect 7 5 3 0;\n#X connect 7 6 8 0;\n#X connect 8 0 10 0;\n#X connect 9 0 11 0;\n#X restore 78 177 pd test message;\n#N canvas 554 232 235 238 delay 0;\n#X obj 35 22 inlet~;\n#X obj 35 197 outlet~;\n#X obj 35 167 delread~ \\$0-delay1;\n#X obj 35 55 delwrite~ \\$0-delay1 5000;\n#X msg 35 137 1000;\n#X obj 35 107 loadbang;\n#X connect 0 0 3 0;\n#X connect 2 0 1 0;\n#X connect 4 0 2 0;\n#X connect 5 0 4 0;\n#X restore 205 192 pd delay;\n#X obj 205 166 adc~;\n#N canvas 742 363 375 145 license 0;\n#X text 8 15 Copyright (c) 2011 Dan Wilcox <danomatika@gmail.com>;\n#X text 10 64 For information on usage and redistribution \\, and for\na DISCLAIMER OF ALL WARRANTIES \\, see the file \\, \"LICENSE.txt \\, \"\nin this distribution.;\n#X text 9 40 BSD Simplified License;\n#X text 10 116 See https://github.com/danomatika/ofxPd for documentation\n;\n#X restore 28 316 pd license;\n#X text 255 317 Dan Wilcox 2011 BSD;\n#X obj 27 139 t b b b b;\n#X obj 27 270 test_abs;\n#N canvas 666 63 404 247 scope~ 0;\n#X obj 26 28 inlet~ audio;\n#X obj 75 154 metro 100;\n#X msg 75 125 1;\n#X obj 56 59 clip~ -1 1;\n#X obj 75 95 loadbang;\n#N canvas 0 22 450 300 (subpatch) 0;\n#X array scope 512 float 2;\n#X coords 0 1 512 -1 200 140 1;\n#X restore 168 27 graph;\n#X obj 56 190 tabwrite~ scope;\n#X obj 25 222 outlet~;\n#X connect 0 0 3 0;\n#X connect 0 0 7 0;\n#X connect 1 0 6 0;\n#X connect 2 0 1 0;\n#X connect 3 0 6 0;\n#X connect 4 0 2 0;\n#X restore 272 240 pd scope~;\n#X obj 163 237 loadbang;\n#X obj 162 269 metro 1000;\n#X obj 197 302 + 1;\n#X obj 160 300 f 0;\n#X obj 164 329 s toOF;\n#X connect 3 0 21 0;\n#X connect 4 0 21 0;\n#X connect 5 0 6 0;\n#X connect 7 0 8 0;\n#X connect 7 0 11 0;\n#X connect 9 0 19 0;\n#X connect 11 0 12 0;\n#X connect 12 0 9 0;\n#X connect 15 0 21 0;\n#X connect 16 0 15 0;\n#X connect 19 0 20 0;\n#X connect 19 1 1 0;\n#X connect 19 2 2 0;\n#X connect 19 3 14 0;\n#X connect 21 0 0 0;\n#X connect 21 0 0 1;\n#X connect 22 0 23 0;\n#X connect 23 0 25 0;\n#X connect 24 0 25 1;\n#X connect 25 0 24 0;\n#X connect 25 0 26 0;\n"
  },
  {
    "path": "pdExampleIOS/src/main.mm",
    "content": "/*\n * Copyright (c) 2011 Dan Wilcox <danomatika@gmail.com>\n *\n * BSD Simplified License.\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.\n *\n * See https://github.com/danomatika/ofxPd for documentation\n *\n */\n#include \"ofMain.h\"\n#include \"ofApp.h\"\n\n// quick fix until 64bit OF on iOS,\n// from https://github.com/openframeworks/openFrameworks/issues/3178\n//#if OF_VERSION_MAJOR == 0 && OF_VERSION_MINOR <= 8\n//extern \"C\" {\n//    size_t fwrite$UNIX2003(const void *a, size_t b, size_t c, FILE *d) {\n//        return fwrite(a, b, c, d);\n//    }\n//    char* strerror$UNIX2003(int errnum) {\n//        return strerror(errnum);\n//    }\n//    time_t mktime$UNIX2003(struct tm * a) {\n//        return mktime(a);\n//    }\n//    double strtod$UNIX2003(const char * a, char ** b) {\n//        return strtod(a, b);\n//    }\n//}\n//#endif\n\n//========================================================================\nint main() {\n\n\t//  here are the most commonly used iOS window settings.\n    //------------------------------------------------------\n    ofiOSWindowSettings settings;\n    settings.enableRetina = false; // enables retina resolution if the device supports it.\n    settings.enableDepth = false; // enables depth buffer for 3d drawing.\n    settings.enableAntiAliasing = false; // enables anti-aliasing which smooths out graphics on the screen.\n    settings.numOfAntiAliasingSamples = 0; // number of samples used for anti-aliasing.\n    settings.enableHardwareOrientation = true; // enables native view orientation.\n    settings.enableHardwareOrientationAnimation = true; // enables native orientation changes to be animated.\n    //settings.glesVersion = OFXIOS_RENDERER_ES1; // type of renderer to use, ES1, ES2, etc.\n    //settings.setupOrientation = OF_ORIENTATION_90_LEFT; // set default orientation for setup\n\n    ofCreateWindow(settings);\n\n\tofRunApp(new ofApp);\n}\n"
  },
  {
    "path": "pdExampleIOS/src/ofApp.h",
    "content": "/*\n * Copyright (c) 2011 Dan Wilcox <danomatika@gmail.com>\n *\n * BSD Simplified License.\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.\n *\n * See https://github.com/danomatika/ofxPd for documentation\n *\n */\n#pragma once\n\n#include \"ofMain.h\"\n#include \"ofxiOS.h\"\n#include \"ofxiOSExtras.h\"\n\n#include \"ofxPd.h\"\n\n// a namespace for the Pd types\nusing namespace pd;\n\n// derive from Pd receiver classes to receive message and midi events\nclass ofApp : public ofxiOSApp, public PdReceiver, public PdMidiReceiver {\n\n\tpublic:\n\n\t\t// main\n\t\tvoid setup();\n\t\tvoid update();\n\t\tvoid draw();\n\t\tvoid exit();\n\n\t\t// input callbacks\n\t\tvoid keyPressed(int key);\n\t\n\t\tvoid touchDown(ofTouchEventArgs &touch);\n\t\tvoid touchMoved(ofTouchEventArgs &touch);\n\t\tvoid touchUp(ofTouchEventArgs &touch);\n\t\tvoid touchDoubleTap(ofTouchEventArgs &touch);\n\t\tvoid touchCancelled(ofTouchEventArgs &touch);\n\n\t\tvoid lostFocus();\n\t\tvoid gotFocus();\n\t\tvoid gotMemoryWarning();\n\t\tvoid deviceOrientationChanged(int newOrientation);\n\t\t\n\t\t// audio callbacks\n\t\tvoid audioIn(ofSoundBuffer &buffer);\n\t\tvoid audioOut(ofSoundBuffer &buffer);\n\t\t\n\t\t// pd message receiver callbacks\n\t\tvoid print(const std::string &message);\n\t\t\n\t\tvoid receiveBang(const std::string &dest);\n\t\tvoid receiveFloat(const std::string &dest, float value);\n\t\tvoid receiveSymbol(const std::string &dest, const std::string &symbol);\n\t\tvoid receiveList(const std::string &dest, const pd::List &list);\n\t\tvoid receiveMessage(const std::string &dest, const std::string &msg, const pd::List &list);\n\t\t\n\t\t// pd midi receiver callbacks\n\t\tvoid receiveNoteOn(const int channel, const int pitch, const int velocity);\n\t\tvoid receiveControlChange(const int channel, const int controller, const int value);\n\t\tvoid receiveProgramChange(const int channel, const int value);\n\t\tvoid receivePitchBend(const int channel, const int value);\n\t\tvoid receiveAftertouch(const int channel, const int value);\n\t\tvoid receivePolyAftertouch(const int channel, const int pitch, const int value);\n\t\t\n\t\tvoid receiveMidiByte(const int port, const int byte);\n\t\n\t\t// do something\n\t\tvoid playTone(int pitch);\n\t\n\t\t// sets the preferred sample rate, returns the *actual* samplerate\n\t\t// which may be different ie. iPhone 6S only wants 48k\n\t\tfloat setAVSessionSampleRate(float preferredSampleRate);\n\t\t\n\t\tofxPd pd;\n\t\tstd::vector<float> scopeArray;\n\t\tstd::vector<pd::Patch> instances;\n\t\t\n        int midiChan;\n};\n"
  },
  {
    "path": "pdExampleIOS/src/ofApp.mm",
    "content": "/*\n * Copyright (c) 2011 Dan Wilcox <danomatika@gmail.com>\n *\n * BSD Simplified License.\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.\n *\n * See https://github.com/danomatika/ofxPd for documentation\n *\n */\n#include \"ofApp.h\"\n\n#import <AVFoundation/AVFoundation.h>\n\n//--------------------------------------------------------------\nvoid ofApp::setup() {\n\n\tofSetFrameRate(60);\n\tofSetVerticalSync(true);\n\tofBackground(127, 127, 127);\n\t//ofSetLogLevel(\"Pd\", OF_LOG_VERBOSE); // see verbose info inside\n\n\t// double check where we are ...\n\tstd::cout << ofFilePath::getCurrentWorkingDirectory() << std::endl;\n\n\t// register touch events\n\tofRegisterTouchEvents(this);\n\t\n\t// iOSAlerts will be sent to this\n\tofxiOSAlerts.addListener(this);\n\t\n\t// try to set the preferred iOS sample rate, but get the actual sample rate\n\t// being used by the AVSession since newer devices like the iPhone 6S only\n\t// want specific values (ie 48000 instead of 44100)\n\tfloat sampleRate = setAVSessionSampleRate(44100);\n\t\n\t// the number if libpd ticks per buffer,\n\t// used to compute the audio buffer len: tpb * blocksize (always 64)\n\tint ticksPerBuffer = 8; // 8 * 64 = buffer len of 512\n\n\t// setup OF sound stream using the current *actual* samplerate\n\tofSoundStreamSettings settings;\n\tsettings.numInputChannels = 1;\n\tsettings.numOutputChannels = 2;\n\tsettings.sampleRate = sampleRate;\n\tsettings.bufferSize = ofxPd::blockSize() * ticksPerBuffer;\n\tsettings.setInListener(this);\n\tsettings.setOutListener(this);\n\tofSoundStreamSetup(settings);\n\n\t// setup Pd\n\t//\n\t// set 4th arg to true for queued message passing using an internal ringbuffer,\n\t// this is useful if you need to control where and when the message callbacks\n\t// happen (ie. within a GUI thread)\n\t//\n\t// note: you won't see any message prints until update() is called since\n\t// the queued messages are processed there, this is normal\n\t//\n\tif(!pd.init(2, 1, sampleRate, ticksPerBuffer-1, false)) {\n\t\tOF_EXIT_APP(1);\n\t}\n\n\tmidiChan = 1; // midi channels are 1-16\n\n\t// subscribe to receive source names\n\tpd.subscribe(\"toOF\");\n\tpd.subscribe(\"env\");\n\n\t// add message receiver, required if you want to receive messages\n\tpd.addReceiver(*this);   // automatically receives from all subscribed sources\n\tpd.ignoreSource(*this, \"env\");     // don't receive from \"env\"\n\t//pd.ignoreSource(*this);          // ignore all sources\n\t//pd.receiveSource(*this, \"toOF\"); // receive only from \"toOF\"\n\n\t// add midi receiver, required if you want to receive midi messages\n\tpd.addMidiReceiver(*this);  // automatically receives from all channels\n\t//pd.ignoreMidiChannel(*this, 1);  // ignore midi channel 1\n\t//pd.ignoreMidiChannel(*this);     // ignore all channels\n\t//pd.receiveMidiChannel(*this, 1); // receive only from channel 1\n\n\t// add the data/pd folder to the search path\n\tpd.addToSearchPath(\"pd/abs\");\n\n\t// audio processing on\n\tpd.start();\n\n\t// -----------------------------------------------------\n\tstd::cout << std::endl << \"BEGIN Patch Test\" << std::endl;\n\n\t// open patch\n\tPatch patch = pd.openPatch(\"pd/test.pd\");\n\tstd::cout << patch << std::endl;\n\n\t// close patch\n\tpd.closePatch(patch);\n\tstd::cout << patch << std::endl;\n\n\t// open patch again\n\tpatch = pd.openPatch(patch);\n\tstd::cout << patch << std::endl;\n\n\tstd::cout << \"FINISH Patch Test\" << std::endl;\n\n\t// -----------------------------------------------------\n\tstd::cout << std::endl << \"BEGIN Message Test\" << std::endl;\n\n\t// test basic atoms\n\tpd.sendBang(\"fromOF\");\n\tpd.sendFloat(\"fromOF\", 100);\n\tpd.sendSymbol(\"fromOF\", \"test string\");\n\n\t// stream interface\n\tpd << Bang(\"fromOF\")\n\t   << Float(\"fromOF\", 100)\n\t   << Symbol(\"fromOF\", \"test string\");\n\n\t// send a list\n\tpd.startMessage();\n\tpd.addFloat(1.23);\n\tpd.addSymbol(\"a symbol\");\n\tpd.finishList(\"fromOF\");\n\n\t// send a message to the $0 receiver ie $0-fromOF\n\tpd.startMessage();\n\tpd.addFloat(1.23);\n\tpd.addSymbol(\"a symbol\");\n\tpd.finishList(patch.dollarZeroStr()+\"-fromOF\");\n\n\t// send a list using the List object\n\tpd::List testList;\n\ttestList.addFloat(1.23);\n\ttestList.addSymbol(\"sent from a List object\");\n\tpd.sendList(\"fromOF\", testList);\n\tpd.sendMessage(\"fromOF\", \"msg\", testList);\n\n\t// stream interface for list\n\tpd << StartMessage() << 1.23 << \"sent from a streamed list\" << FinishList(\"fromOF\");\n\n\tstd::cout << \"FINISH Message Test\" << std::endl;\n\n\t// -----------------------------------------------------\n\tstd::cout << std::endl << \"BEGIN MIDI Test\" << std::endl;\n\n\t// send functions\n\tpd.sendNoteOn(midiChan, 60);\n\tpd.sendControlChange(midiChan, 0, 64);\n\tpd.sendProgramChange(midiChan, 100); // note: pgm num range is 1 - 128\n\tpd.sendPitchBend(midiChan, 2000);    // note: ofxPd uses -8192 - 8192 while [bendin] returns 0 - 16383,\n\t\t\t\t\t\t\t\t\t\t // so sending a val of 2000 gives 10192 in pd\n\tpd.sendAftertouch(midiChan, 100);\n\tpd.sendPolyAftertouch(midiChan, 64, 100);\n\tpd.sendMidiByte(0, 239);    // note: pd adds +2 to the port number from [midiin], [sysexin], & [realtimein]\n\tpd.sendSysex(0, 239);       // so sending to port 0 gives port 2 in pd\n\tpd.sendSysRealTime(0, 239);\n\n\t// stream\n\tpd << NoteOn(midiChan, 60) << ControlChange(midiChan, 100, 64)\n\t   << ProgramChange(midiChan, 100) << PitchBend(midiChan, 2000)\n\t   << Aftertouch(midiChan, 100) << PolyAftertouch(midiChan, 64, 100)\n\t   << StartMidi(0) << 239 << Finish()\n\t   << StartSysex(0) << 239 << Finish()\n\t   << StartSysRealTime(0) << 239 << Finish();\n\n\tstd::cout << \"FINISH MIDI Test\" << std::endl;\n\n\t// -----------------------------------------------------\n\tstd::cout << std::endl << \"BEGIN Array Test\" << std::endl;\n\n\t// array check length\n\tstd::cout << \"array1 len: \" << pd.arraySize(\"array1\") << std::endl;\n\n\t// read array\n\tstd::vector<float> array1;\n\tpd.readArray(\"array1\", array1);\t// sets array to correct size\n\tstd::cout << \"array1 \";\n\tfor(int i = 0; i < array1.size(); ++i) {\n\t\tstd::cout << array1[i] << \" \";\n\t}\n\tstd::cout << std::endl;\n\n\t// write array\n\tfor(int i = 0; i < array1.size(); ++i)\n\t\tarray1[i] = i;\n\tpd.writeArray(\"array1\", array1);\n\n\t// ready array\n\tpd.readArray(\"array1\", array1);\n\tstd::cout << \"array1 \";\n\tfor(int i = 0; i < array1.size(); ++i) {\n\t\tstd::cout << array1[i] << \" \";\n\t}\n\tstd::cout << std::endl;\n\n\t// clear array\n\tpd.clearArray(\"array1\", 10);\n\n\t// ready array\n\tpd.readArray(\"array1\", array1);\n\tstd::cout << \"array1 \";\n\tfor(int i = 0; i < array1.size(); ++i) {\n\t\tstd::cout << array1[i] << \" \";\n\t}\n\tstd::cout << std::endl;\n\n\tstd::cout << \"FINISH Array Test\" << std::endl;\n\n\t// -----------------------------------------------------\n\tstd::cout << std::endl << \"BEGIN PD Test\" << std::endl;\n\n\tpd.sendSymbol(\"fromOF\", \"test\");\n\n\tstd::cout << \"FINISH PD Test\" << std::endl;\n\n\t// -----------------------------------------------------\n\tstd::cout << std::endl << \"BEGIN Instance Test\" << std::endl;\n\n\t// open 10 instances\n\tfor(int i = 0; i < 10; ++i) {\n\t\tPatch p = pd.openPatch(\"pd/instance.pd\");\n\t\tinstances.push_back(p);\n\t}\n\n\t// send a hello bang to each instance individually using the dollarZero\n\t// to [r $0-instance] which should print the instance dollarZero unique id\n\t// and a unique random number\n\tfor(int i = 0; i < instances.size(); ++i) {\n\t\tpd.sendBang(instances[i].dollarZeroStr()+\"-instance\");\n\t}\n\n\t// send a random float between 0 and 100\n\tfor(int i = 0; i < instances.size(); ++i) {\n\t\tpd.sendFloat(instances[i].dollarZeroStr()+\"-instance\", int(ofRandom(0, 100)));\n\t}\n\n\t// send a symbol\n\tfor(int i = 0; i < instances.size(); ++i) {\n\t\tpd.sendSymbol(instances[i].dollarZeroStr()+\"-instance\", \"howdy dude\");\n\t}\n\n\t// close all instances\n\tfor(int i = 0; i < instances.size(); ++i) {\n\t\tpd.closePatch(instances[i]);\n\t}\n\tinstances.clear();\n\n\tstd::cout << \"FINISH Instance Test\" << std::endl;\n\n\t// -----------------------------------------------------\n\t// play a tone by sending a list\n\t// [list tone pitch 72 (\n\tpd.startMessage();\n\tpd.addSymbol(\"pitch\");\n\tpd.addFloat(72);\n\tpd.finishList(\"tone\");\n\tpd.sendBang(\"tone\");\n}\n\n//--------------------------------------------------------------\nvoid ofApp::update() {\n\tofBackground(100, 100, 100);\n\t\n\t// since this is a test and we don't know if init() was called with\n\t// queued = true or not, we check it here\n\tif(pd.isQueued()) {\n\t\t// process any received messages, if you're using the queue and *do not*\n\t\t// call these, you won't receive any messages or midi!\n\t\tpd.receiveMessages();\n\t\tpd.receiveMidi();\n\t}\n\n\t// update scope array from pd\n\tpd.readArray(\"scope\", scopeArray);\n}\n\n//--------------------------------------------------------------\nvoid ofApp::draw() {\n\n\t// draw scope\n\tofSetColor(0, 255, 0);\n\tofSetRectMode(OF_RECTMODE_CENTER);\n\tfloat x = 0, y = ofGetHeight()/2;\n\tfloat w = ofGetWidth() / (float) scopeArray.size(), h = ofGetHeight()/2;\n\tfor(int i = 0; i < scopeArray.size()-1; ++i) {\n\t\tofDrawLine(x, y+scopeArray[i]*h, x+w, y+scopeArray[i+1]*h);\n\t\tx += w;\n\t}\n}\n\n//--------------------------------------------------------------\nvoid ofApp::exit() {\n\n\t// cleanup\n\tofSoundStreamStop();\n}\n\n//--------------------------------------------------------------\nvoid ofApp::keyPressed (int key) {\n\n\tswitch(key) {\n\n\t\t// musical keyboard if you have a usb keyboard\n\t\tcase 'a':\n\t\t\tplayTone(60);\n\t\t\tbreak;\n\t\tcase 'w':\n\t\t\tplayTone(61);\n\t\t\tbreak;\n\t\tcase 's':\n\t\t\tplayTone(62);\n\t\t\tbreak;\n\t\tcase 'e':\n\t\t\tplayTone(63);\n\t\t\tbreak;\n\t\tcase 'd':\n\t\t\tplayTone(64);\n\t\t\tbreak;\n\t\tcase 'f':\n\t\t\tplayTone(65);\n\t\t\tbreak;\n\t\tcase 't':\n\t\t\tplayTone(66);\n\t\t\tbreak;\n\t\tcase 'g':\n\t\t\tplayTone(67);\n\t\t\tbreak;\n\t\tcase 'y':\n\t\t\tplayTone(68);\n\t\t\tbreak;\n\t\tcase 'h':\n\t\t\tplayTone(69);\n\t\t\tbreak;\n\t\tcase 'u':\n\t\t\tplayTone(70);\n\t\t\tbreak;\n\t\tcase 'j':\n\t\t\tplayTone(71);\n\t\t\tbreak;\n\t\tcase 'k':\n\t\t\tplayTone(72);\n\t\t\tbreak;\n\n\t\tcase ' ':\n\t\t\tif(pd.isReceivingSource(*this, \"env\")) {\n\t\t\t\tpd.ignoreSource(*this, \"env\");\n\t\t\t\tstd::cout << \"ignoring env\" << std::endl;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tpd.receiveSource(*this, \"env\");\n\t\t\t\tstd::cout << \"receiving from env\" << std::endl;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tbreak;\n\t}\n}\n\n//--------------------------------------------------------------\nvoid ofApp::touchDown(ofTouchEventArgs &touch) {\n\t// y pos changes pitch\n\tint pitch = (-1 * (touch.y/ofGetHeight()) + 1) * 127;\n\tplayTone(pitch);\n}\n\n//--------------------------------------------------------------\nvoid ofApp::touchMoved(ofTouchEventArgs &touch) {}\n\n//--------------------------------------------------------------\nvoid ofApp::touchUp(ofTouchEventArgs &touch) {}\n\n//--------------------------------------------------------------\nvoid ofApp::touchDoubleTap(ofTouchEventArgs &touch) {}\n\n//--------------------------------------------------------------\nvoid ofApp::touchCancelled(ofTouchEventArgs &args) {}\n\n//--------------------------------------------------------------\nvoid ofApp::lostFocus() {}\n\n//--------------------------------------------------------------\nvoid ofApp::gotFocus() {}\n\n//--------------------------------------------------------------\nvoid ofApp::gotMemoryWarning() {}\n\n//--------------------------------------------------------------\nvoid ofApp::deviceOrientationChanged(int newOrientation) {}\n\n//--------------------------------------------------------------\nvoid ofApp::audioIn(ofSoundBuffer &buffer) {\n\tpd.audioIn(buffer);\n}\n\n//--------------------------------------------------------------\nvoid ofApp::audioOut(ofSoundBuffer &buffer) {\n\tpd.audioOut(buffer);\n}\n\n//--------------------------------------------------------------\nvoid ofApp::print(const std::string &message) {\n\tstd::cout << message << std::endl;\n}\n\n//--------------------------------------------------------------\nvoid ofApp::receiveBang(const std::string &dest) {\n\tstd::cout << \"OF: bang \" << dest << std::endl;\n}\n\nvoid ofApp::receiveFloat(const std::string &dest, float value) {\n\tstd::cout << \"OF: float \" << dest << \": \" << value << std::endl;\n}\n\nvoid ofApp::receiveSymbol(const std::string &dest, const std::string &symbol) {\n\tstd::cout << \"OF: symbol \" << dest << \": \" << symbol << std::endl;\n}\n\nvoid ofApp::receiveList(const std::string &dest, const pd::List &list) {\n\tstd::cout << \"OF: list \" << dest << \": \";\n\n\t// step through the list\n\tfor(int i = 0; i < list.len(); ++i) {\n\t\tif(list.isFloat(i))\n\t\t\tstd::cout << list.getFloat(i) << \" \";\n\t\telse if(list.isSymbol(i))\n\t\t\tstd::cout << list.getSymbol(i) << \" \";\n\t}\n\n\t// you can also use the built in toString function or simply stream it out\n\t// std::cout << list.toString();\n\t// std::cout << list;\n\n\t// print an OSC-style type string\n\tstd::cout << list.types() << std::endl;\n}\n\nvoid ofApp::receiveMessage(const std::string &dest, const std::string &msg, const pd::List &list) {\n\tstd::cout << \"OF: message \" << dest << \": \" << msg << \" \" << list.toString() << list.types() << std::endl;\n}\n\n//--------------------------------------------------------------\nvoid ofApp::receiveNoteOn(const int channel, const int pitch, const int velocity) {\n\tstd::cout << \"OF MIDI: note on: \" << channel << \" \" << pitch << \" \" << velocity << std::endl;\n}\n\nvoid ofApp::receiveControlChange(const int channel, const int controller, const int value) {\n\tstd::cout << \"OF MIDI: control change: \" << channel << \" \" << controller << \" \" << value << std::endl;\n}\n\n// note: pgm nums are 1-128 to match pd\nvoid ofApp::receiveProgramChange(const int channel, const int value) {\n\tstd::cout << \"OF MIDI: program change: \" << channel << \" \" << value << std::endl;\n}\n\nvoid ofApp::receivePitchBend(const int channel, const int value) {\n\tstd::cout << \"OF MIDI: pitch bend: \" << channel << \" \" << value << std::endl;\n}\n\nvoid ofApp::receiveAftertouch(const int channel, const int value) {\n\tstd::cout << \"OF MIDI: aftertouch: \" << channel << \" \" << value << std::endl;\n}\n\nvoid ofApp::receivePolyAftertouch(const int channel, const int pitch, const int value) {\n\tstd::cout << \"OF MIDI: poly aftertouch: \" << channel << \" \" << pitch << \" \" << value << std::endl;\n}\n\n// note: pd adds +2 to the port num, so sending to port 3 in pd to [midiout],\n//       shows up at port 1 in ofxPd\nvoid ofApp::receiveMidiByte(const int port, const int byte) {\n\tstd::cout << \"OF MIDI: midi byte: \" << port << \" \" << byte << std::endl;\n}\n\n//--------------------------------------------------------------\nvoid ofApp::playTone(int pitch) {\n\tpd << StartMessage() << \"pitch\" << pitch << FinishList(\"tone\") << Bang(\"tone\");\n}\n\n//--------------------------------------------------------------\n// set the samplerate the Apple approved way since newer devices\n// like the iPhone 6S only allow certain sample rates,\n// the following code may not be needed once this functionality is\n// incorporated into the ofxiOSSoundStream\n// thanks to Seth aka cerupcat\nfloat ofApp::setAVSessionSampleRate(float preferredSampleRate) {\n\t\n\tNSError *audioSessionError = nil;\n\tAVAudioSession *session = [AVAudioSession sharedInstance];\n\n\t// disable active\n\t[session setActive:NO error:&audioSessionError];\n\tif (audioSessionError) {\n\t\tNSLog(@\"Error %ld, %@\", (long)audioSessionError.code, audioSessionError.localizedDescription);\n\t}\n\n\t// set category\n\t[session setCategory:AVAudioSessionCategoryPlayAndRecord\n\t         withOptions:(AVAudioSessionCategoryOptionAllowBluetooth | AVAudioSessionCategoryOptionMixWithOthers | AVAudioSessionCategoryOptionDefaultToSpeaker)\n\t               error:&audioSessionError];\n\tif(audioSessionError) {\n\t\tNSLog(@\"Error %ld, %@\", (long)audioSessionError.code, audioSessionError.localizedDescription);\n\t}\n\n\t// try to set the preferred sample rate\n\t[session setPreferredSampleRate:preferredSampleRate error:&audioSessionError];\n\tif(audioSessionError) {\n\t\tNSLog(@\"Error %ld, %@\", (long)audioSessionError.code, audioSessionError.localizedDescription);\n\t}\n\n\t// *** Activate the audio session before asking for the \"current\" values ***\n\t[session setActive:YES error:&audioSessionError];\n\tif (audioSessionError) {\n\t\tNSLog(@\"Error %ld, %@\", (long)audioSessionError.code, audioSessionError.localizedDescription);\n\t}\n\tofLogNotice() << \"AVSession samplerate: \" << session.sampleRate << \", I/O buffer duration: \" << session.IOBufferDuration;\n\n\t// our actual samplerate, might be different aka 48k on iPhone 6S\n\treturn session.sampleRate;\n}\n"
  },
  {
    "path": "pdMultiExample/addons.make",
    "content": "ofxPd\n"
  },
  {
    "path": "pdMultiExample/bin/data/.gitkeep",
    "content": ""
  },
  {
    "path": "pdMultiExample/bin/data/test.pd",
    "content": "#N canvas 406 290 351 173 10;\n#X obj 39 35 loadbang;\n#X obj 143 114 dac~;\n#X obj 39 88 f 0;\n#X obj 70 88 + 1;\n#X obj 143 35 r \\$0-frequency;\n#X obj 143 60 osc~;\n#X obj 205 114 print \\$0-frequency;\n#X obj 205 76 loadbang;\n#X obj 143 88 *~ 0.5;\n#X obj 39 60 metro 1000;\n#X obj 39 114 print \\$0-count;\n#X connect 0 0 9 0;\n#X connect 2 0 3 0;\n#X connect 2 0 10 0;\n#X connect 3 0 2 1;\n#X connect 4 0 5 0;\n#X connect 4 0 6 0;\n#X connect 5 0 8 0;\n#X connect 7 0 6 0;\n#X connect 8 0 1 0;\n#X connect 8 0 1 1;\n#X connect 9 0 2 0;\n"
  },
  {
    "path": "pdMultiExample/src/main.cpp",
    "content": "/*\n * Copyright (c) 2015 Dan Wilcox <danomatika@gmail.com>\n *\n * BSD Simplified License.\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.\n *\n * See https://github.com/danomatika/ofxPd for documentation\n *\n */\n#include \"ofMain.h\"\n#include \"ofApp.h\"\n\n//========================================================================\nint main() {\n\tofSetupOpenGL(200, 200, OF_WINDOW);\n\tofRunApp(new ofApp());\n}\n"
  },
  {
    "path": "pdMultiExample/src/ofApp.cpp",
    "content": "/*\n * Copyright (c) 2015 Dan Wilcox <danomatika@gmail.com>\n *\n * BSD Simplified License.\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.\n *\n * See https://github.com/danomatika/ofxPd for documentation\n *\n */\n#include \"ofApp.h\"\n\n//--------------------------------------------------------------\nvoid ofApp::setup() {\n\n\tofSetFrameRate(60);\n\tofSetVerticalSync(true);\n\t//ofSetLogLevel(\"Pd\", OF_LOG_VERBOSE); // see verbose info inside\n\n\t// the number of libpd ticks per buffer,\n\t// used to compute the audio buffer len: tpb * blocksize (always 64)\n\t#ifdef TARGET_LINUX_ARM\n\t\t// longer latency for Raspberry PI\n\t\tint ticksPerBuffer = 32; // 32 * 64 = buffer len of 2048\n\t\tint numInputs = 0; // no built in mic\n\t#else\n\t\tint ticksPerBuffer = 8; // 8 * 64 = buffer len of 512\n\t\tint numInputs = 1;\n\t#endif\n\tint numOutputs = 2;\n\n\t// allocate instance output buffers\n\tint outputBufferFrames = ticksPerBuffer * ofxPd::blockSize();\n\toutputBuffer1.allocate(outputBufferFrames, numOutputs);\n\toutputBuffer2.allocate(outputBufferFrames, numOutputs);\n\n\t// setup OF sound stream\n\tofSoundStreamSettings settings;\n\tsettings.numInputChannels = numInputs;\n\tsettings.numOutputChannels = numOutputs;\n\tsettings.sampleRate = 44100;\n\tsettings.bufferSize = ofxPd::blockSize() * ticksPerBuffer;\n\tsettings.setInListener(this);\n\tsettings.setOutListener(this);\n\tofSoundStreamSetup(settings);\n\n\t// allocate pd instance handles\n\tofLog() << ofxPd::numInstances() - 1 << \" instance(s)\"; // - main instance\n\tofLog() << \"instance 1: \" << ofToHex(pd1.instancePtr());\n\tofLog() << \"instance 2: \" << ofToHex(pd2.instancePtr());\n\n\t// setup Pd\n\t//\n\t// set 4th arg to true for queued message passing using an internal ringbuffer,\n\t// this is useful if you need to control where and when the message callbacks\n\t// happen (ie. within a GUI thread)\n\t//\n\t// note: you won't see any message prints until update() is called since\n\t// the queued messages are processed there, this is normal\n\tif(!pd1.init(numOutputs, numInputs, 44100, ticksPerBuffer, false)) {\n\t\tofExit(1);\n\t}\n\tpd1.setReceiver(this);\n\n\t// audio processing on\n\tpd1.start();\n\n\t// open patch\n\tpd1.openPatch(\"test.pd\");\n\n\t// start the osc~ via sending [; 1003-frequency 440(\n\t// 1003 refers to the first $0 which is used within the test patch for the\n\t// receiver name: [r $0-1003]\n\tpd1 << StartMessage() << 440.0f << FinishMessage(\"1003-frequency\", \"float\");\n\n\t// now do the same for instance 2\n\tif(!pd2.init(numOutputs, numInputs, 44100, ticksPerBuffer, false)) {\n\t\tofExit(1);\n\t}\n\tpd2.setReceiver(this);\n\tpd2.start();\n\tpd2.openPatch(\"test.pd\");\n\n\t// start the osc~ via sending [; 1000-frequency 880(\n\tpd2 << StartMessage() << 880.0f << FinishMessage(\"1003-frequency\", \"float\");\n\n\t// check if we are really using multiple instances\n\tif(pd1.instancePtr() == pd2.instancePtr()) {\n\t\tofLogError() << \"Both instances are the same.\";\n\t\tofLogError() << \"Is this example compiled with PDINSTANCE and PDTHREADS set?\";\n\t\tofExit();\n\t}\n}\n\n//--------------------------------------------------------------\nvoid ofApp::update() {\n\tofBackground(100, 100, 100);\n\t\n\t// since this is a test and we don't know if init() was called with\n\t// queued = true or not, we check it here\n\tif(pd1.isQueued()) {\n\t\t// process any received messages, if you're using the queue\n\t\tpd1.receiveMessages();\n\t}\n\tif(pd2.isQueued()) {\n\t\tpd2.receiveMessages();\n\t}\n}\n\n//--------------------------------------------------------------\nvoid ofApp::draw() {}\n\n//--------------------------------------------------------------\nvoid ofApp::exit() {\n\n\t// cleanup\n\tofSoundStreamStop();\n\n\tpd1.clear();\n\tpd2.clear();\n}\n\n//--------------------------------------------------------------\nvoid ofApp::audioIn(ofSoundBuffer& buffer) {\n\n\t// process audio input for instance 1\n\tpd1.audioIn(buffer);\n\n\t// process audio input for instance 2\n\tpd2.audioIn(buffer);\n}\n\n//--------------------------------------------------------------\nvoid ofApp::audioOut(ofSoundBuffer& buffer) {\n\n\t// process audio output for instance 1\n\tpd1.audioOut(outputBuffer1);\n\n\t// process audio output for instance 2\n\tpd2.audioOut(outputBuffer2);\n\n\t// mix the two instance output buffers together\n\tfor(int i = 0; i < outputBuffer1.size() && i < buffer.size(); i++) {\n\t\tbuffer[i] = (outputBuffer1[i] + outputBuffer2[i]) * 0.5f; // simple mix\n\t}\n}\n\n//--------------------------------------------------------------\nvoid ofApp::print(const std::string &message) {\n\tofLog() << message;\n}\n"
  },
  {
    "path": "pdMultiExample/src/ofApp.h",
    "content": "/*\n * Copyright (c) 2015 Dan Wilcox <danomatika@gmail.com>\n *\n * BSD Simplified License.\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.\n *\n * See https://github.com/danomatika/ofxPd for documentation\n *\n */\n#pragma once\n\n#include \"ofMain.h\"\n\n#include \"ofxPd.h\"\n\n// a namespace for the Pd types\nusing namespace pd;\n\n// This example demonstrates how to use the libpd multiple instance support with\n// the ofxPd C++ wrapper. When compiled with -DPDINSTANCE and -DPDTHREADS set in\n// *both* CFLAGS and CXXFLAGS, each ofxPd instance is separate with it's own\n// internal state and message receivers. If the defines are not set, each ofxPd\n// instance refers to the same libpd main instance.\n//\n// This example is adapted from the libpd pdtest_multi C example which is\n// originally by Miller Puckette.\n//\n// *Do not* use this as your first approach to parallelize CPU hogging patches.\n// It's highly suggested that you attempt to streamline your patches first before\n// using this multiple instance support.\n//\nclass ofApp : public ofBaseApp, public PdReceiver {\n\n\tpublic:\n\n\t\t// main\n\t\tvoid setup();\n\t\tvoid update();\n\t\tvoid draw();\n\t\tvoid exit();\n\t\t\n\t\t// audio callbacks\n\t\tvoid audioIn(ofSoundBuffer& buffer);\n\t\tvoid audioOut(ofSoundBuffer& buffer);\n\n\t\t// pd message receiver callbacks\n\t\tvoid print(const std::string &message);\n\t\n\t\t// pd instances\n\t\tofxPd pd1, pd2;\n\n\t\tofSoundBuffer outputBuffer1; //< interleaved audio output buffer for instance 1\n\t\tofSoundBuffer outputBuffer2; //< interleaved audio output buffer for instance 2\n};\n"
  },
  {
    "path": "pitchShifter/LICENSE.txt",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<http://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<http://www.gnu.org/philosophy/why-not-lgpl.html>.\n\n"
  },
  {
    "path": "pitchShifter/README.md",
    "content": "PitchShifter\n============\n\n![image](https://raw.github.com/danomatika/ofxPd/master/examplePitchShifter/screenshot.png)\n\nCopyright (c) [Dan Wilcox](danomatika.com) 2011, 2013\n\nGNU General Public License 3\n\nFor information on usage and redistribution, and for a DISCLAIMER OF ALL\nWARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.\n\nSee https://github.com/ofxPd/examplePitchShifter for documentation\n\nDescription\n-----------\n\nPitchShifter is a simple live audio pitch shifter example application for desktop & iOS.\n\nMade for [Golan Levin](http://flong.com) & the [CMU Studio for Creative Inquiry](http://studiofrocreativeinquiry.com)\n\nIt uses ofxPd & OpenFrameworks.\n\n[ofxPd](https://github.com/danomatika/ofxPd) is an Open Frameworks addon for running an instance of the Pure Data audio environment within an OpenFrameworks application. Audio, messages, and [MIDI](http://en.wikipedia.org/wiki/Musical_Instrument_Digital_Interface) events can be passed to and from pure data patches and the library is thread safe.\n\n[Pure Data](http://pure-data.info/) is a graphical patching environment for audio and multimedia (note: the gui and graphics features are not within the scope of this addon) \n\n[OpenFrameworks](http://www.openframeworks.cc/) is a cross platform open source toolkit for creative coding in C++\n\n"
  },
  {
    "path": "pitchShifter/addons.make",
    "content": "ofxPd\n"
  },
  {
    "path": "pitchShifter/bin/data/.gitkeep",
    "content": ""
  },
  {
    "path": "pitchShifter/bin/data/pd/_main.pd",
    "content": "#N canvas 590 417 508 403 10;\n#X declare -path rc;\n#X obj 29 25 adc~ 1;\n#X obj 28 338 dac~ 1 2;\n#X obj 326 18 declare -path rc;\n#X obj 28 239 e_pshift~;\n#X obj 214 20 loadbang;\n#N canvas 666 63 404 247 scope~ 0;\n#X obj 26 28 inlet~ audio;\n#X obj 75 154 metro 100;\n#X msg 75 125 1;\n#X obj 56 59 clip~ -1 1;\n#X obj 75 95 loadbang;\n#N canvas 0 22 450 300 (subpatch) 0;\n#X array scope 512 float 2;\n#X coords 0 1 512 -1 200 140 1;\n#X restore 168 27 graph;\n#X obj 56 190 tabwrite~ scope;\n#X obj 26 222 outlet~;\n#X connect 0 0 3 0;\n#X connect 0 0 7 0;\n#X connect 1 0 6 0;\n#X connect 2 0 1 0;\n#X connect 3 0 6 0;\n#X connect 4 0 2 0;\n#X restore 28 268 pd scope~;\n#X obj 132 21 r TO_PD;\n#X obj 132 47 list trim;\n#X obj 132 86 route inGain outGain mix;\n#X obj 28 207 *~ 1;\n#X obj 132 120 clip 0 1;\n#X obj 179 150 clip 0 1;\n#X obj 28 303 *~ 1;\n#X obj 260 146 - 1;\n#X obj 260 173 abs;\n#X msg 233 226 wet \\$1;\n#X msg 260 201 dry \\$1;\n#X obj 233 119 t f f;\n#X msg 214 47 transpose 0 \\, inGain 0.5 \\, outGain 0.5 \\, mix 1;\n#X obj 319 252 t a;\n#X text 179 301 Dan Wilcox 2012 GPL 3;\n#X text 178 317 danomatika.com | robotcowboy.com;\n#X connect 0 0 9 0;\n#X connect 3 0 5 0;\n#X connect 4 0 18 0;\n#X connect 5 0 12 0;\n#X connect 6 0 7 0;\n#X connect 7 0 8 0;\n#X connect 8 0 10 0;\n#X connect 8 1 11 0;\n#X connect 8 2 17 0;\n#X connect 8 3 19 0;\n#X connect 9 0 3 0;\n#X connect 10 0 9 1;\n#X connect 11 0 12 1;\n#X connect 12 0 1 1;\n#X connect 12 0 1 0;\n#X connect 13 0 14 0;\n#X connect 14 0 16 0;\n#X connect 15 0 19 0;\n#X connect 16 0 19 0;\n#X connect 17 0 15 0;\n#X connect 17 1 13 0;\n#X connect 18 0 8 0;\n#X connect 19 0 3 1;\n"
  },
  {
    "path": "pitchShifter/bin/data/pd/rc/e_pshift~-help.pd",
    "content": "#N canvas 1295 97 584 398 10;\n#X text 305 337 danomatika.com | robotcowboy.com;\n#X text 306 352 https://github.com/danomatika/rc-patches;\n#X text 306 322 2008 \\, 2012 Dan Wilcox GPL v3;\n#X obj 44 364 dac~;\n#X obj 43 152 osc~ 440;\n#X obj 44 180 *~ 0.5;\n#X obj 95 293 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0\n1;\n#X obj 193 295 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0\n1;\n#X text 165 271 pitch shifted audio;\n#X text 62 271 normal audio;\n#X msg 260 81 transpose \\$1;\n#X floatatom 260 52 5 0 0 0 - - -;\n#X msg 353 101 window \\$1;\n#X floatatom 353 75 5 0 0 0 - - -;\n#X msg 422 135 delay \\$1;\n#X floatatom 422 111 5 0 0 0 - - -;\n#X text 302 51 semitones;\n#X text 393 75 msec;\n#X text 461 113 msec;\n#X text 287 141 control messages;\n#X msg 203 111 onoff \\$1;\n#X obj 203 84 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0\n1;\n#X text 39 65 Creation args: name;\n#X text 26 22 rc-pshift~: pitch shifter \\, make your voice sound weird!\n;\n#X msg 180 154 reset;\n#X msg 450 194 dry 0.5;\n#X msg 408 168 wet 0.34;\n#X text 501 173 wet/dry mix;\n#X text 135 192 audio in;\n#X obj 141 212 e_pshift~;\n#X obj 142 319 u_spigot~;\n#X obj 44 318 u_spigot~;\n#X connect 4 0 5 0;\n#X connect 5 0 29 0;\n#X connect 5 0 31 0;\n#X connect 6 0 31 1;\n#X connect 7 0 30 1;\n#X connect 10 0 29 1;\n#X connect 11 0 10 0;\n#X connect 12 0 29 1;\n#X connect 13 0 12 0;\n#X connect 14 0 29 1;\n#X connect 15 0 14 0;\n#X connect 20 0 29 1;\n#X connect 21 0 20 0;\n#X connect 24 0 29 1;\n#X connect 25 0 29 1;\n#X connect 26 0 29 1;\n#X connect 29 0 30 0;\n#X connect 30 0 3 1;\n#X connect 31 0 3 0;\n"
  },
  {
    "path": "pitchShifter/bin/data/pd/rc/e_pshift~.pd",
    "content": "#N canvas 590 926 392 188 10;\n#X obj 20 20 inlet~;\n#X obj 20 149 outlet~;\n#X obj 50 54 delwrite~ \\$0-del 5000;\n#X text 122 105 from G09.pitchshift;\n#N canvas 387 113 432 456 guts 0;\n#X obj 83 274 *~;\n#X obj 206 211 line~;\n#X obj 206 185 pack 0 200;\n#X obj 20 84 exp;\n#X obj 20 175 /;\n#X obj 58 120 * 0.001;\n#X obj 315 281 line~;\n#X obj 315 256 pack 0 200;\n#X obj 84 320 +~;\n#X obj 20 326 cos~;\n#X obj 21 370 *~;\n#X obj 20 403 +~;\n#X obj 107 233 wrap~;\n#X obj 252 276 *~;\n#X obj 252 322 +~;\n#X obj 189 349 cos~;\n#X obj 189 376 *~;\n#X obj 58 143 t b f;\n#X obj 107 206 +~ 0.5;\n#X obj 20 274 -~ 0.5;\n#X obj 20 300 *~ 0.5;\n#X obj 189 275 -~ 0.5;\n#X obj 189 321 *~ 0.5;\n#X obj 20 112 - 1;\n#X obj 20 58 * 0.05776;\n#X obj 20 138 * -1;\n#X obj 20 206 phasor~;\n#X obj 315 232 max 1.5;\n#X text 381 197 (msec);\n#X obj 206 82 max 1;\n#X obj 252 351 vd~ \\$0-del;\n#X obj 84 346 vd~ \\$0-del;\n#X obj 20 31 r \\$0-transpose;\n#X obj 206 52 r \\$0-window;\n#X obj 315 198 r \\$0-delay;\n#X text 112 31 halftones;\n#X obj 20 430 outlet~;\n#X text 280 52 (msec);\n#X obj 300 140 switch~;\n#X obj 300 106 r \\$0-onoff;\n#X connect 0 0 8 0;\n#X connect 1 0 0 1;\n#X connect 1 0 13 1;\n#X connect 2 0 1 0;\n#X connect 3 0 23 0;\n#X connect 4 0 26 0;\n#X connect 5 0 17 0;\n#X connect 6 0 8 1;\n#X connect 6 0 14 1;\n#X connect 7 0 6 0;\n#X connect 8 0 31 0;\n#X connect 9 0 10 0;\n#X connect 10 0 11 0;\n#X connect 11 0 36 0;\n#X connect 12 0 13 0;\n#X connect 12 0 21 0;\n#X connect 13 0 14 0;\n#X connect 14 0 30 0;\n#X connect 15 0 16 0;\n#X connect 16 0 11 1;\n#X connect 17 0 4 0;\n#X connect 17 1 4 1;\n#X connect 18 0 12 0;\n#X connect 19 0 20 0;\n#X connect 20 0 9 0;\n#X connect 21 0 22 0;\n#X connect 22 0 15 0;\n#X connect 23 0 25 0;\n#X connect 24 0 3 0;\n#X connect 25 0 4 0;\n#X connect 26 0 0 0;\n#X connect 26 0 19 0;\n#X connect 26 0 18 0;\n#X connect 27 0 7 0;\n#X connect 29 0 5 0;\n#X connect 29 0 2 0;\n#X connect 30 0 16 1;\n#X connect 31 0 10 1;\n#X connect 32 0 24 0;\n#X connect 33 0 29 0;\n#X connect 34 0 27 0;\n#X connect 39 0 38 0;\n#X restore 53 88 pd guts;\n#N canvas 261 237 459 426 mix 0;\n#X obj 348 304 *~;\n#X obj 79 306 *~;\n#X obj 79 344 +~;\n#X obj 348 65 inlet~;\n#X obj 80 29 inlet~;\n#X obj 79 375 outlet~;\n#X obj 363 186 r \\$0-wet;\n#X obj 94 173 r \\$0-dry;\n#X obj 94 234 f;\n#X obj 171 31 r \\$0-onoff;\n#X obj 94 261 spigot;\n#X msg 171 264 1;\n#X obj 363 266 spigot;\n#X obj 363 235 f;\n#X msg 228 262 0;\n#X obj 171 76 sel 0;\n#X msg 265 173 bang;\n#X obj 265 93 t b b;\n#X msg 292 118 1;\n#X obj 171 114 t b b;\n#X msg 198 143 0;\n#X text 404 67 wet;\n#X text 123 28 dry;\n#X obj 235 58 loadbang;\n#X text 167 358 turn mix on and off;\n#X text 169 375 turns dry to 1 when off;\n#X connect 0 0 2 1;\n#X connect 1 0 2 0;\n#X connect 2 0 5 0;\n#X connect 3 0 0 0;\n#X connect 4 0 1 0;\n#X connect 6 0 13 0;\n#X connect 7 0 8 0;\n#X connect 8 0 10 0;\n#X connect 9 0 15 0;\n#X connect 10 0 1 1;\n#X connect 11 0 1 1;\n#X connect 12 0 0 1;\n#X connect 13 0 12 0;\n#X connect 14 0 0 1;\n#X connect 15 0 19 0;\n#X connect 15 1 17 0;\n#X connect 16 0 13 0;\n#X connect 16 0 8 0;\n#X connect 17 0 16 0;\n#X connect 17 1 18 0;\n#X connect 18 0 12 1;\n#X connect 18 0 10 1;\n#X connect 19 0 14 0;\n#X connect 19 0 11 0;\n#X connect 19 1 20 0;\n#X connect 20 0 12 1;\n#X connect 20 0 10 1;\n#X connect 23 0 19 0;\n#X restore 20 118 pd mix;\n#N canvas 4 51 450 300 messages 0;\n#X obj 42 21 inlet;\n#X obj 42 256 s \\$0-transpose;\n#X obj 82 232 s \\$0-window;\n#X obj 123 205 s \\$0-delay;\n#X obj 164 177 s \\$0-onoff;\n#N canvas 0 22 597 369 defaults 0;\n#X obj 104 234 s \\$0-transpose;\n#X obj 154 211 s \\$0-window;\n#X obj 204 187 s \\$0-delay;\n#X obj 254 162 s \\$0-onoff;\n#X obj 154 25 loadbang;\n#X msg 154 87 100;\n#X msg 204 86 1.5;\n#X msg 104 83 0;\n#X obj 104 24 inlet;\n#X obj 351 123 s \\$0-wet;\n#X obj 329 163 s \\$0-dry;\n#X msg 351 88 1;\n#X msg 254 87 1;\n#X msg 293 85 0;\n#X connect 4 0 5 0;\n#X connect 4 0 6 0;\n#X connect 4 0 7 0;\n#X connect 4 0 12 0;\n#X connect 4 0 11 0;\n#X connect 4 0 13 0;\n#X connect 5 0 1 0;\n#X connect 6 0 2 0;\n#X connect 7 0 0 0;\n#X connect 8 0 7 0;\n#X connect 8 0 5 0;\n#X connect 8 0 6 0;\n#X connect 8 0 12 0;\n#X connect 8 0 11 0;\n#X connect 8 0 13 0;\n#X connect 11 0 9 0;\n#X connect 12 0 3 0;\n#X connect 13 0 10 0;\n#X restore 204 143 pd defaults;\n#X msg 204 108 bang;\n#X obj 42 55 route transpose window delay onoff reset wet dry;\n#X obj 245 112 s \\$0-wet;\n#X obj 286 85 s \\$0-dry;\n#X connect 0 0 7 0;\n#X connect 6 0 5 0;\n#X connect 7 0 1 0;\n#X connect 7 1 2 0;\n#X connect 7 2 3 0;\n#X connect 7 3 4 0;\n#X connect 7 4 6 0;\n#X connect 7 5 8 0;\n#X connect 7 6 9 0;\n#X restore 206 54 pd messages;\n#X obj 206 18 inlet;\n#X text 123 136 danomatika.com | robotcowboy.com;\n#X text 124 151 https://github.com/danomatika/rc-patches;\n#X text 123 121 2008 \\, 2012 Dan Wilcox GPL v3;\n#X connect 0 0 5 0;\n#X connect 0 0 2 0;\n#X connect 4 0 5 1;\n#X connect 5 0 1 0;\n#X connect 7 0 6 0;\n"
  },
  {
    "path": "pitchShifter/bin/data/pd/rc/u_spigot~-help.pd",
    "content": "#N canvas 696 405 404 250 10;\n#X obj 112 114 tgl 15 0 empty empty empty 0 -6 0 10 -262144 -1 -1 0\n1;\n#X obj 61 54 osc~ 440;\n#X obj 61 177 dac~;\n#X obj 61 88 *~ 0.1;\n#X text 139 112 signal through on/off;\n#X text 141 134 any nonzero float treated as 1;\n#X text 137 191 danomatika.com | robotcowboy.com;\n#X text 138 206 https://github.com/danomatika/rc-patches;\n#X text 138 176 2012 Dan Wilcox GPL v3;\n#X text 21 19 u_spigot~: a convenience patch to toggle a signal;\n#X text 170 45 includes 5 ms declick;\n#X obj 61 140 u_spigot~;\n#X connect 0 0 11 1;\n#X connect 1 0 3 0;\n#X connect 3 0 11 0;\n#X connect 11 0 2 0;\n#X connect 11 0 2 1;\n"
  },
  {
    "path": "pitchShifter/bin/data/pd/rc/u_spigot~.pd",
    "content": "#N canvas 0 22 489 282 10;\n#X text 214 224 danomatika.com | robotcowboy.com;\n#X text 215 239 https://github.com/danomatika/rc-patches;\n#X text 215 209 2012 Dan Wilcox GPL v3;\n#X obj 30 235 outlet~;\n#X obj 31 65 inlet~;\n#X obj 45 97 inlet;\n#X text 80 60 audio signal in;\n#X text 87 237 audio signal out;\n#X obj 45 120 sel 0;\n#X text 87 94 left inlet: onoff toggle \\, any nonzero is treated as\n1;\n#X obj 45 181 vline~;\n#X obj 30 208 *~;\n#X msg 45 143 0 5;\n#X msg 78 143 1 5;\n#X text 23 20 spigot with declick for signals;\n#X connect 4 0 11 0;\n#X connect 5 0 8 0;\n#X connect 8 0 12 0;\n#X connect 8 1 13 0;\n#X connect 10 0 11 1;\n#X connect 11 0 3 0;\n#X connect 12 0 10 0;\n#X connect 13 0 10 0;\n"
  },
  {
    "path": "pitchShifter/src/main.cpp",
    "content": "/*\n * Copyright (c) 2012 Dan Wilcox <danomatika@gmail.com>\n * for Golan Levin & the CMU Studio for Creative Inquiry\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n *\n * See https://github.com/danomatika/ofxPd/examplePitchShifter for documentation\n *\n */\n#include \"ofMain.h\"\n#include \"ofApp.h\"\n\n//========================================================================\nint main() {\n\tofSetupOpenGL(1024, 768, OF_WINDOW);\n\tofRunApp(new ofApp());\n}\n"
  },
  {
    "path": "pitchShifter/src/ofApp.cpp",
    "content": "/*\n * Copyright (c) 2012 Dan Wilcox <danomatika@gmail.com>\n * for Golan Levin & the CMU Studio for Creative Inquiry\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n *\n * See https://github.com/danomatika/ofxPd/examplePitchShifter for documentation\n *\n */\n#include \"ofApp.h\"\n\n//--------------------------------------------------------------\nvoid ofApp::setup() {\n\n\tofSetFrameRate(60);\n\tofSetVerticalSync(true);\n\t\n\t// the number of libpd ticks per buffer,\n\t// used to compute the audio buffer len: tpb * blocksize (always 64)\n\t#ifdef TARGET_LINUX_ARM\n\t\t// longer latency for Raspberry PI\n\t\tint ticksPerBuffer = 32; // 32 * 64 = buffer len of 2048\n\t\t\n\t\t// you'll need a USB mic on the Raspberry PI and may need to set the audio device,\n\t\t// otherwise this app won't really do anything without an incoming audio signal ...\n\t\tint numInputs = 0; // no built in mic, change this if you have a USB mic\n\t#else\n\t\tint ticksPerBuffer = 8; // 8 * 64 = buffer len of 512\n\t\tint numInputs = 1;\n\t#endif\n\n\t// setup OF sound stream\n\tofSoundStreamSettings settings;\n\tsettings.numInputChannels = numInputs;\n\tsettings.numOutputChannels = 2;\n\tsettings.sampleRate = 44100;\n\tsettings.bufferSize = ofxPd::blockSize() * ticksPerBuffer;\n\tsettings.setInListener(this);\n\tsettings.setOutListener(this);\n\tofSoundStreamSetup(settings);\n\t\n\t// setup pd\n\tif(!pd.init(2, numInputs, 44100, ticksPerBuffer)) {\n\t\tOF_EXIT_APP(1);\n\t}\n\tpd.subscribe(\"mix\");\n\tpd.subscribe(\"transpose\");\n\tpd.subscribe(\"inputGain\");\n\tpd.subscribe(\"outputGain\");\n\tpd.addToSearchPath(\"pd\");\n\tpd.start();\n\n\t// open patch\n\tPatch patch = pd.openPatch(\"pd/_main.pd\");\n\tstd::cout << patch << std::endl;\n\n\t// setup GUI\n\tint x = -12, width = 100, step = 75;\n\tx += step;\n\ttransposeSlider.setup(x, 34, width, 700, -12, 12, 0, true, true); x += width + step*2;\n\tmixSlider.setup(x, 34, width, 700, 0, 1, 1.0, true, true); x += width + step*2;\n\tinGainSlider.setup(x, 34, width, 700, 0, 1, 0.5, true, true); x += width + step*2;\n\toutGainSlider.setup(x, 34, 100, 700, 0, 1, 0.25, true, true);\n\ttransposeSlider.setLabelString(\"Transpose\");\n\tmixSlider.setLabelString(\"Wet/Dry Mix\");\n\tinGainSlider.setLabelString(\"Input Gain\");\n\toutGainSlider.setLabelString(\"Output Gain\");\n}\n\n//--------------------------------------------------------------\nvoid ofApp::update() {\n\tofBackground(0, 0, 0);\n\t\n\t// update scope array from pd\n\tpd.readArray(\"scope\", scopeArray);\n\t\n\t// udpate pd from gui\n\tpd << StartMessage() << \"transpose\" << transposeSlider.getValue() << FinishList(\"TO_PD\");\n\tpd << StartMessage() << \"mix\" << mixSlider.getValue() << FinishList(\"TO_PD\");\n\tpd << StartMessage() << \"inGain\" << inGainSlider.getValue() << FinishList(\"TO_PD\");\n\tpd << StartMessage() << \"outGain\" << outGainSlider.getValue() << FinishList(\"TO_PD\");\n}\n\n//--------------------------------------------------------------\nvoid ofApp::draw() {\n\n\t// draw scope\n\tofSetColor(0, 255, 0, 127);\n\tofSetRectMode(OF_RECTMODE_CENTER);\n\tofSetLineWidth(2.0);\n\tfloat x = 1, y = ofGetHeight()/2;\n\tfloat w = ofGetWidth() / (float) scopeArray.size(), h = ofGetHeight()/2;\n\tfor(int i = 0; i < scopeArray.size()-1; ++i) {\n\t\tofDrawLine(x, y+scopeArray[i]*h, x+w, y+scopeArray[i+1]*h);\n\t\tx += w;\n\t}\n\tofSetLineWidth(1.0);\n}\n\n//--------------------------------------------------------------\nvoid ofApp::exit() {\n\n\t// cleanup\n\tofSoundStreamStop();\n}\n\n//--------------------------------------------------------------\nvoid ofApp::keyPressed(int key) {}\n\n//--------------------------------------------------------------\nvoid ofApp::audioIn(ofSoundBuffer &buffer) {\n\tpd.audioIn(buffer);\n}\n\n//--------------------------------------------------------------\nvoid ofApp::audioOut(ofSoundBuffer &buffer) {\n\tpd.audioOut(buffer);\n}\n\n//--------------------------------------------------------------\nvoid ofApp::print(const std::string &message) {\n\tstd::cout << message << std::endl;\n}\n"
  },
  {
    "path": "pitchShifter/src/ofApp.h",
    "content": "/*\n * Copyright (c) 2012 Dan Wilcox <danomatika@gmail.com>\n * for Golan Levin & the CMU Studio for Creative Inquiry\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n *\n * See https://github.com/danomatika/ofxPd/examplePitchShifter for documentation\n *\n */\n#pragma once\n\n#include \"ofMain.h\"\n\n#include \"ofxPd.h\"\n#include \"ofxSimpleSlider.h\"\n\n// a namespace for the Pd types\nusing namespace pd;\n\nclass ofApp : public ofBaseApp, public PdReceiver {\n\n\tpublic:\n\n\t\t// main\n\t\tvoid setup();\n\t\tvoid update();\n\t\tvoid draw();\n\t\tvoid exit();\n\t\t\n\t\t// input callbacks\n\t\tvoid keyPressed(int key);\n\t\t\n\t\t// audio callbacks\n\t\tvoid audioIn(ofSoundBuffer &buffer);\n\t\tvoid audioOut(ofSoundBuffer &buffer);\n\t\t\n\t\t// pd message receiver callbacks\n\t\tvoid print(const std::string &message);\n\t\t\n\t\tofxPd pd; //< pd instance\n\t\tstd::vector<float> scopeArray;\n\n\t\t// gui\n\t\tofxSimpleSlider mixSlider, transposeSlider, inGainSlider, outGainSlider;\n};\n"
  },
  {
    "path": "pitchShifter/src/ofxSimpleSlider.cpp",
    "content": "/*\n *  ofxSimpleSlider.cpp\n *  Created by Golan Levin on 2/24/12.\n *  Updated by Dan Wilcox 2012.\n */\n#include \"ofxSimpleSlider.h\"\n\n//----------------------------------------------------\nofxSimpleSlider::ofxSimpleSlider() {\n\tbWasSetup = false;\n}\n\n//----------------------------------------------------\nofxSimpleSlider::~ofxSimpleSlider() {\n\tclear();\n}\n\n//----------------------------------------------------\nvoid ofxSimpleSlider::setup(float inx, float iny, float inw, float inh,\n\t                        float loVal, float hiVal, float initialValue,\n\t                        bool bVert, bool bDrawNum) {\n\tx = inx;\n\ty = iny; \n\twidth = inw; \n\theight = inh;\n\tbox.set(x,y, width, height); \n\tnumberDisplayPrecision = 2;\n\t\n\tbVertical = bVert;\n\tbDrawNumber = bDrawNum;\n\tbHasFocus = false;\n\n\tlowValue = loVal;\n\thighValue = hiVal;\n\tpercent = ofMap(initialValue, lowValue, highValue, 0, 1);\n\tpercent = ofClamp(percent, 0, 1);\n\t\n\tlabelString = \"\"; \n\t\n\tif(!bWasSetup) {\n\t\tofAddListener(ofEvents().draw, this, &ofxSimpleSlider::draw);\n\t\tofAddListener(ofEvents().mouseMoved, this, &ofxSimpleSlider::mouseMoved);\n\t\tofAddListener(ofEvents().mousePressed, this, &ofxSimpleSlider::mousePressed);\n\t\tofAddListener(ofEvents().mouseReleased, this, &ofxSimpleSlider::mouseReleased);\n\t\tofAddListener(ofEvents().mouseDragged, this, &ofxSimpleSlider::mouseDragged);\n\t\tbWasSetup = true;\n\t}\n}\n\n//----------------------------------------------------\nvoid ofxSimpleSlider::clear() {\n\tif(bWasSetup) {\n\t\tofRemoveListener(ofEvents().draw, this, &ofxSimpleSlider::draw);\n\t\tofRemoveListener(ofEvents().mouseMoved, this, &ofxSimpleSlider::mouseMoved);\n\t\tofRemoveListener(ofEvents().mousePressed, this, &ofxSimpleSlider::mousePressed);\n\t\tofRemoveListener(ofEvents().mouseReleased, this, &ofxSimpleSlider::mouseReleased);\n\t\tofRemoveListener(ofEvents().mouseDragged, this, &ofxSimpleSlider::mouseDragged);\n\t}\n\tbWasSetup = false;\n}\n\n//----------------------------------------------------\nvoid ofxSimpleSlider::setLabelString(std::string str) {\n\tlabelString = str;\n}\n\n//----------------------------------------------------\nvoid ofxSimpleSlider::draw(ofEventArgs &event) {\n\t\n\tofEnableAlphaBlending();\n\tofDisableSmoothing();\n\tofPushMatrix();\n\tofTranslate(x, y, 0);\n\t\n\t// Use different alphas if we're actively maniupulating me.\n\tfloat sliderAlpha = (bHasFocus ? 128 : 64);\n\tfloat spineAlpha  = (bHasFocus ? 192 : 128);\n\tfloat thumbAlpha  = (bHasFocus ? 255 : 160);\n\t\n\t// draw box outline\n\tofNoFill();\n\tofSetLineWidth(1.0);\n\tofSetColor(200, 200, 200, sliderAlpha);\n\tofSetRectMode(OF_RECTMODE_CORNER);\n\tofDrawRectangle(0, 0, width, height);\n\t\n\t// draw spine\n\tofSetLineWidth(1.0);\n\tofSetColor(255, 255, 255, spineAlpha);\n\tif(bVertical) {\n\t\tofDrawLine(width/2, 0, width/2, height);\n\t} else {\n\t\tofDrawLine(0, height/2, width, height/2);\n\t}\n\t\n\t// draw thumb\n\tofSetLineWidth(5.0);\n\tofSetColor(255, 255, 255, thumbAlpha);\n\tif(bVertical) {\n\t\tfloat thumbY = ofMap(percent, 0, 1, height, 0, true);\n\t\tofDrawLine(0, thumbY, width, thumbY);\n\t} else {\n\t\tfloat thumbX = ofMap(percent, 0, 1, 0, width, true);\n\t\tofDrawLine(thumbX, 0, thumbX, height);\n\t}\n\t\n\t// draw numeric value\n\tif(bHasFocus) {\n\t\tofSetColor(255);\n\t} else {\n\t\tofSetColor(200);\n\t}\n\tif(bVertical) {\n\t\tofDrawBitmapString(ofToString(getValue(), numberDisplayPrecision), width+5, height);\n\t\tofDrawBitmapString(labelString, width+5, height-14);\n\t} else {\n\t\tofDrawBitmapString(ofToString(getValue(), numberDisplayPrecision), width+5, height/2 + 4);\n\t\tfloat labelStringWidth = labelString.size();\n\t\tofDrawBitmapString(labelString, 0-labelStringWidth*8-5, height/2 + 4);\n\t}\t\n\n\tofPopMatrix();\n\tofSetLineWidth(1.0);\n\tofDisableAlphaBlending();\n}\n\n//----------------------------------------------------\nfloat ofxSimpleSlider::getValue() {\n\t// THIS IS THE MAIN WAY YOU GET THE VALUE FROM THE SLIDER!\n\tfloat out = ofMap(percent, 0, 1, lowValue, highValue, true);\n\treturn out;\n}\n\n//----------------------------------------------------\n// Probably not used very much. \nfloat ofxSimpleSlider::getLowValue() {\n\treturn lowValue;\n}\nfloat ofxSimpleSlider::getHighValue() {\n\treturn highValue;\n}\nfloat ofxSimpleSlider::getPercent() {\n\treturn percent;\n}\n\n//----------------------------------------------------\n// Probably not used very much. \nvoid ofxSimpleSlider::setLowValue(float lv) {\n\tlowValue = lv;\n}\nvoid ofxSimpleSlider::setHighValue(float hv) {\n\thighValue = hv; \n}\nvoid ofxSimpleSlider::setPercent(float p) {\n\t// Set the slider's percentage from the outside.\n\tp = ofClamp(p, 0, 1);\n\tpercent\t= p;\n}\nvoid ofxSimpleSlider::setNumberDisplayPrecision(int prec) {\n\tnumberDisplayPrecision = prec;\n}\n\t\t\n//----------------------------------------------------\nvoid ofxSimpleSlider::mouseMoved(ofMouseEventArgs &event) {\n\tbHasFocus = false;\n}\nvoid ofxSimpleSlider::mouseDragged(ofMouseEventArgs &event) {\n\tif(bHasFocus) {\n\t\tupdatePercentFromMouse(event.x, event.y);\n\t}\n}\nvoid ofxSimpleSlider::mousePressed(ofMouseEventArgs &event) {\n\tbHasFocus = false;\n\tif(box.inside(event.x, event.y)) {\n\t\tbHasFocus = true;\n\t\tupdatePercentFromMouse(event.x, event.y);\n\t}\n}\nvoid ofxSimpleSlider::mouseReleased(ofMouseEventArgs &event) {\n\tif(bHasFocus) {\n\t\tif(box.inside(event.x, event.y)) {\n\t\t\tupdatePercentFromMouse(event.x, event.y);\n\t\t}\n\t}\n\tbHasFocus = false;\n}\n\n//----------------------------------------------------\nvoid ofxSimpleSlider::updatePercentFromMouse(int mx, int my) {\n\t// Given the mouse value, compute the percentage.\n\tif(bVertical) {\n\t\tpercent = ofMap(my, y, y+height, 1, 0, true);\n\t} else {\n\t\tpercent = ofMap(mx, x, x+width,  0, 1, true);\n\t}\n}\n"
  },
  {
    "path": "pitchShifter/src/ofxSimpleSlider.h",
    "content": "/*\n *  ofxSimpleSlider.h\n *  Created by Golan Levin on 2/24/12.\n *  Updated by Dan Wilcox 2012.\n */\n#pragma once\n\n#include \"ofMain.h\"\n\nclass ofxSimpleSlider {\n\n\tpublic:\n\t\t\t\n\t\tofxSimpleSlider();\n\t\tvirtual ~ofxSimpleSlider();\n\n\t\tvoid setup(float inx, float iny, float inw, float inh,\n\t\t\t       float loVal, float hiVal, float initialPercent,\n\t\t\t       bool bVert, bool bDrawNum);\n\t\tvoid clear();\n\n\t\tvoid draw(ofEventArgs &event);\n\t\tvoid mouseMoved(ofMouseEventArgs &event);\n\t\tvoid mouseDragged(ofMouseEventArgs &event);\n\t\tvoid mousePressed(ofMouseEventArgs &event);\n\t\tvoid mouseReleased(ofMouseEventArgs &event);\n\t\n\t\tfloat getValue();\n\t\tfloat getLowValue();\n\t\tfloat getHighValue();\n\t\tfloat getPercent();\n\t\t\n\t\tvoid setLowValue(float lv);\n\t\tvoid setHighValue(float hv);\n\t\tvoid setPercent(float p);\n\t\tvoid setNumberDisplayPrecision(int prec);\n\t\tvoid setLabelString(std::string str);\n\t\tvoid updatePercentFromMouse(int mx, int my);\n\t\n\tprotected:\n\t\t\n\t\tfloat x;\n\t\tfloat y; \n\t\tfloat width; \n\t\tfloat height;\n\t\tofRectangle box; \n\t\tint numberDisplayPrecision;\n\t\n\t\tbool bVertical;\n\t\tbool bDrawNumber;\n\t\tbool bHasFocus; \n\t\n\t\tfloat lowValue;\n\t\tfloat highValue;\n\t\tfloat percent;\n\t\n\t\tstd::string labelString;\n\t\n\tprivate:\n\t\n\t\tbool bWasSetup;\n};\n"
  },
  {
    "path": "scripts/update_libpd.sh",
    "content": "#! /bin/sh\n\n# exit on error\nset -e\n\nVER=master\nSRC=libpd\nDEST=../libs/libpd\n\n###\n\ncd \"$(dirname $0)\"\n\n# get source\ngit clone --depth 1 https://github.com/libpd/libpd.git\ncd $SRC\ngit checkout $VER\ngit submodule init\ngit submodule update\ncd -\n\n# remove uneeded makefiles, etc in src\nfind $SRC/pure-data -name \"makefile*\" -delete\nfind $SRC/pure-data -name \"Makefile.am\" -delete\nfind $SRC/pure-data -name \"GNUmakefile.am\" -delete\nfind $SRC/pure-data -name \"*.pd\" -delete\nrm -f $SRC/pure-data/src/CHANGELOG.txt\nrm -f $SRC/pure-data/src/notes.txt\nrm -f $SRC/pure-data/src/pd.ico\nrm $SRC/pure-data/src/pd.rc\n\n# remove unneeded audio apis\nrm $SRC/pure-data/src/s_audio_alsa*\nrm $SRC/pure-data/src/s_audio_audiounit.c\nrm $SRC/pure-data/src/s_audio_esd.c\nrm $SRC/pure-data/src/s_audio_jack.c\nrm $SRC/pure-data/src/s_audio_mmio.c\nrm $SRC/pure-data/src/s_audio_oss.c\nrm $SRC/pure-data/src/s_audio_pa.c\nrm $SRC/pure-data/src/s_audio_paring.*\n\n# remove unneeded midi apis\nrm $SRC/pure-data/src/s_midi_alsa.c\nrm $SRC/pure-data/src/s_midi_dummy.c\nrm -f $SRC/pure-data/src/s_midi_mmio.c\nrm $SRC/pure-data/src/s_midi_oss.c\nrm $SRC/pure-data/src/s_midi_pm.c\nrm $SRC/pure-data/src/s_midi.c\n\n# remove uneeded fft library interfaces\nrm $SRC/pure-data/src/d_fft_fftw.c\n\n# remove libpd stuff included with pure-data\nrm $SRC/pure-data/src/s_libpdmidi.c\nrm $SRC/pure-data/src/x_libpdreceive.*\nrm $SRC/pure-data/src/z_*.*\n\n# remove some other stuff we don't need ...\nrm $SRC/pure-data/src/s_entry.c\nrm $SRC/pure-data/src/s_file.c\nrm $SRC/pure-data/src/s_watchdog.c\nrm $SRC/pure-data/src/u_pdreceive.c\nrm $SRC/pure-data/src/u_pdsend.c\nrm -f $SRC/pure-data/.dir-locals.el\nrm -f $SRC/pure-data/src/.dir-locals.el\n\n# copy sources\nmkdir -p $DEST/pure-data\ncp -Rv $SRC/cpp $DEST/\ncp -Rv $SRC/pure-data/src $DEST/pure-data\ncp -Rv $SRC/pure-data/extra $DEST/pure-data\ncp -Rv $SRC/libpd_wrapper $DEST/\n\n# copy libs\nmkdir -p $DEST/libs\ncp -Rv $SRC/libs/mingw64 $DEST/libs/\n\n# cleanup\nrm -rf $SRC\n"
  },
  {
    "path": "src/ofxPd.cpp",
    "content": "/*\n * Copyright (c) 2011-2022 Dan Wilcox <danomatika@gmail.com>\n *\n * BSD Simplified License.\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.\n *\n * See https://github.com/danomatika/ofxPd for documentation\n *\n * This project uses libpd, copyrighted by Miller Puckette and others using the\n * \"Standard Improved BSD License\". See the file \"LICENSE.txt\" in src/pd.\n *\n * See http://gitorious.org/pdlib/pages/Libpd for documentation\n *\n */\n\n// include before PdBase.hpp to fix conflict between boost & libpd's s_ define\n#include \"ofFileUtils.h\"\n\n#include \"ofxPd.h\"\n\n#include <algorithm>\n#include \"ofUtils.h\"\n#include \"ofLog.h\"\n\n// needed for libpd audio passing\n#ifndef USEAPI_DUMMY\n\t#define USEAPI_DUMMY\n#endif\n\nusing namespace std;\nusing namespace pd;\n\n//------------------------------------------------------------------------------\nofxPd::ofxPd() : PdBase() {\n\tinBuffer = NULL;\n\tcomputing = false;\n\tclear();\n}\n\n//------------------------------------------------------------------------------\nofxPd::~ofxPd() {\n    clear();\n}\n\n//------------------------------------------------------------------------------\nbool ofxPd::init(const int numOutChannels, const int numInChannels, \n                 const int sampleRate, const int ticksPerBuffer, bool queued) {\n\t\n\t// init pd\n\tif(!PdBase::init(numInChannels, numOutChannels, sampleRate, queued)) {\n\t\tofLogError(\"Pd\") << \"could not init\";\n\t\tclear();\n\t\treturn false;\n\t}\n\t\n\tticks = ticksPerBuffer;\n\tbsize = ticksPerBuffer*blockSize();\n\tsrate = sampleRate;\n\tinChannels = numInChannels;\n\toutChannels = numOutChannels;\n\n\t// allocate buffers\n\tinBuffer = new float[numInChannels * bsize];\n\n\tofLogVerbose(\"Pd\") <<\"inited\";\n\tofLogVerbose(\"Pd\") <<\" samplerate: \" << sampleRate;\n\tofLogVerbose(\"Pd\") <<\" channels in: \" << numInChannels;\n\tofLogVerbose(\"Pd\") <<\" channels out: \" << numOutChannels;\n\tofLogVerbose(\"Pd\") <<\" ticks: \" << ticksPerBuffer;\n\tofLogVerbose(\"Pd\") <<\" block size: \" << blockSize();\n\tofLogVerbose(\"Pd\") <<\" calc buffer size: \" << bsize;\n\tofLogVerbose(\"Pd\") <<\" queued: \" << (queued ? \"yes\" : \"no\");\n\n\treturn true;\n}\n\nvoid ofxPd::clear() {\n\n\tif(inBuffer != NULL) {\n\t\tdelete[] inBuffer;\n\t\tinBuffer = NULL;\n\t}\n\tunsubscribeAll();\n\n\tchannels.clear();\n\n\t// add default global channel\n\tChannel c;\n\tchannels.insert(make_pair(-1, c));\n\t\n\tticks = 0;\n\tbsize = 0;\n\tsrate = 0;\n\tinChannels = 0;\n\toutChannels = 0;\n}\n\n//------------------------------------------------------------------------------\nvoid ofxPd::addToSearchPath(const std::string &path) {\n\tstring fullpath = ofFilePath::getAbsolutePath(ofToDataPath(path));\n\tofLogVerbose(\"Pd\") << \"adding search path: \" + fullpath;\n\tPdBase::addToSearchPath(fullpath.c_str());\n}\n\nvoid ofxPd::clearSearchPath() {\n\tofLogVerbose(\"Pd\") << \"clearing search paths\";\n\tPdBase::clearSearchPath();\n}\n\n//------------------------------------------------------------------------------\nPatch ofxPd::openPatch(const std::string &patch) {\n\n\tstring fullpath = ofFilePath::getAbsolutePath(ofToDataPath(patch));\n\tstring file = ofFilePath::getFileName(fullpath);\n\tstring folder = ofFilePath::getEnclosingDirectory(fullpath);\n\t\n\t// trim the trailing slash Poco::Path always adds ... blarg\n\tif(folder.size() > 0 && folder.at(folder.size()-1) == '/') {\n\t\tfolder.erase(folder.end()-1);\n\t}\n\t\n\tofLogVerbose(\"Pd\") << \"opening patch: \"+file+\" path: \"+folder;\n\n\t// [; pd open file folder(\n\tPatch p = PdBase::openPatch(file.c_str(), folder.c_str());\n \tif(!p.isValid()) {\n\t\tofLogError(\"Pd\") << \"opening patch \\\"\"+file+\"\\\" failed\";\n\t}\n\t\n\treturn p;\n}\n\npd::Patch ofxPd::openPatch(pd::Patch &patch) {\n\tofLogVerbose(\"Pd\") << \"opening patch: \"+patch.filename()+\" path: \"+patch.path();\n\tPatch p = PdBase::openPatch(patch);\n\tif(!p.isValid()) {\n\t\tofLogError(\"Pd\") << \"opening patch \\\"\"+patch.filename()+\"\\\" failed\";\n\t}\n\treturn p;\n}\n\nvoid ofxPd::closePatch(const std::string &patch) {\n\tofLogVerbose(\"Pd\") << \"closing path: \"+patch;\n\tPdBase::closePatch(patch);\n}\n\nvoid ofxPd::closePatch(Patch &patch) {\n\tofLogVerbose(\"Pd\") << \"closing patch: \"+patch.filename();\n\tPdBase::closePatch(patch);\n}\t\n\n//------------------------------------------------------------------------------\nvoid ofxPd::computeAudio(bool state) {\n\tif(state) {\n\t\tofLogVerbose(\"Pd\") << \"audio processing on\";\n\t}\n\telse {\n\t\tofLogVerbose(\"Pd\") << \"audio processing off\";\n\t}\n\tcomputing = state;\n\n\t// [; pd dsp $1(\n\tPdBase::computeAudio(state);\n}\nvoid ofxPd::start() {\n\t// [; pd dsp 1(\n\tcomputeAudio(true);\n}\n\nvoid ofxPd::stop() {\t\n\t// [; pd dsp 0(\n\tcomputeAudio(false);\n}\n\n//------------------------------------------------------------------------------\nvoid ofxPd::subscribe(const std::string &source) {\n\tif(exists(source)) {\n\t\tofLogWarning(\"Pd\") << \"subscribe: ignoring duplicate source\";\n\t\treturn;\n\t}\n\tPdBase::subscribe(source);\n\tSource s;\n\tsources.insert(pair<string,Source>(source, s));\n}\n\nvoid ofxPd::unsubscribe(const std::string &source) {\n\tmap<string,Source>::iterator iter;\n\titer = sources.find(source);\n\tif(iter == sources.end()) {\n\t\tofLogWarning(\"Pd\") << \"unsubscribe: ignoring unknown source\";\n\t\treturn;\n\t}\n\tPdBase::unsubscribe(source);\n\tsources.erase(iter);\n}\n\nbool ofxPd::exists(const std::string &source) {\n\tif(sources.find(source) != sources.end()) {\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nvoid ofxPd::unsubscribeAll() {\n\n    PdBase::unsubscribeAll();\n\tsources.clear();\n\n\t// add default global source\n\tSource s;\n\tsources.insert(make_pair(\"\", s));\n}\n\n//------------------------------------------------------------------------------\nvoid ofxPd::addReceiver(PdReceiver &receiver) {\n\t\n\tpair<set<PdReceiver *>::iterator, bool> ret;\n\tret = receivers.insert(&receiver);\n\tif(!ret.second) {\n\t\tofLogWarning(\"Pd\") << \"addReceiver: ignoring duplicate receiver\";\n\t\treturn;\n\t}\n\n\t// set PdBase receiver on adding first reciever\n\tif(receivers.size() == 1) {\n\t\tPdBase::setReceiver(this);\n\t}\n\n\t// receive from all sources by default\n\treceiveSource(receiver);\n}\n\nvoid ofxPd::removeReceiver(PdReceiver &receiver) {\n\t\n\t// exists?\n\tset<PdReceiver *>::iterator r_iter;\n\tr_iter = receivers.find(&receiver);\n\tif(r_iter == receivers.end()) {\n\t\tofLogWarning(\"Pd\") << \"removeReceiver: ignoring unknown receiver\";\n\t\treturn;\n\t}\n\treceivers.erase(r_iter);\n\t\n\t// clear PdBase receiver on removing last reciever\n\tif(receivers.size() == 0) {\n\t\tPdBase::setReceiver(NULL);\n\t}\n\n\t// remove from all sources\n\tignoreSource(receiver);\n}\n\nbool ofxPd::receiverExists(PdReceiver &receiver) {\n\tif(receivers.find(&receiver) != receivers.end())\n\t\treturn true;\n\treturn false;\n}\n\nvoid ofxPd::clearReceivers() {\n\n\treceivers.clear();\n\t\n\tmap<string,Source>::iterator iter;\n\tfor(iter = sources.begin(); iter != sources.end(); ++iter) {\n\t\titer->second.receivers.clear();\n\t}\n\t\n\tPdBase::setReceiver(NULL);\n}\n\n//------------------------------------------------------------------------------\nint ofxPd::ticksPerBuffer() {\n\treturn ticks;\n}\n\nint ofxPd::bufferSize() {\n\treturn bsize;\n}\n\nint ofxPd::sampleRate() {\n\treturn srate;\n}\n\nint ofxPd::numInChannels() {\n\treturn inChannels;\n}\n\nint ofxPd::numOutChannels() {\n\treturn outChannels;\n}\n\nbool ofxPd::isComputingAudio() {\n\treturn computing;\n}\n\n//------------------------------------------------------------------------------\nvoid ofxPd::receiveSource(PdReceiver &receiver, const std::string &source) {\n\n\tif(!receiverExists(receiver)) {\n\t\tofLogWarning(\"Pd\") << \"receive: unknown receiver, call addReceiver first\";\n\t\treturn;\n\t}\n\t\n\tif(!exists(source)) {\n\t\tofLogWarning(\"Pd\") << \"receive: unknown source, call subscribe first\";\n\t\treturn;\n\t}\n\t\n\t// global source (all sources)\n\tmap<string,Source>::iterator g_iter;\n\tg_iter = sources.find(\"\");\n\t\n\t// subscribe to specific source\n\tif(source != \"\") {\n\t\n\t\t// make sure global source is ignored\n\t\tif(g_iter->second.receiverExists(&receiver)) {\n\t\t\tg_iter->second.removeReceiver(&receiver);\n\t\t}\n\t\t\n\t\t// receive from specific source\n\t\tmap<string,Source>::iterator s_iter;\n\t\ts_iter = sources.find(source);\n\t\ts_iter->second.addReceiver(&receiver);\n\t}\n\telse {\n\t\t// make sure all sources are ignored\n\t\tignoreSource(receiver);\n\t\n\t\t// receive from the global source\n\t\tg_iter->second.addReceiver(&receiver);\n\t}\n}\n\nvoid ofxPd::ignoreSource(PdReceiver &receiver, const std::string &source) {\n\n\tif(!receiverExists(receiver)) {\n\t\tofLogWarning(\"Pd\") << \"ignore: ignoring unknown receiver\";\n\t\treturn;\n\t}\n\n\tif(!exists(source)) {\n\t\tofLogWarning(\"Pd\") << \"ignore: ignoring unknown source\";\n\t\treturn;\n\t}\n\t\n\tmap<string,Source>::iterator s_iter;\n\t\n\t// unsubscribe from specific source\n\tif(source != \"\") {\n\t\n\t\t// global source (all sources)\n\t\tmap<string,Source>::iterator g_iter;\n\t\tg_iter = sources.find(\"\");\n\t\n\t\t// negation from global\n\t\tif(g_iter->second.receiverExists(&receiver)) {\n\t\t\t\n\t\t\t// remove from global\n\t\t\tg_iter->second.removeReceiver(&receiver);\n\t\t\t\n\t\t\t// add to *all* other sources\n\t\t\tfor(s_iter = sources.begin(); s_iter != sources.end(); ++s_iter) {\n\t\t\t\tif(s_iter != g_iter) {\n\t\t\t\t\ts_iter->second.addReceiver(&receiver);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t// remove from source\n\t\ts_iter = sources.find(source);\n\t\ts_iter->second.removeReceiver(&receiver);\n\t}\n\telse {\t// ignore all sources\t\n\t\tfor(s_iter = sources.begin(); s_iter != sources.end(); ++s_iter) {\n\t\t\ts_iter->second.removeReceiver(&receiver);\n\t\t}\n\t}\n}\n\nbool ofxPd::isReceivingSource(PdReceiver &receiver, const std::string &source) {\n\tmap<string,Source>::iterator s_iter;\n\ts_iter = sources.find(source);\n\tif(s_iter != sources.end() && s_iter->second.receiverExists(&receiver)) {\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//------------------------------------------------------------------------------\nvoid ofxPd::addMidiReceiver(PdMidiReceiver &receiver) {\n\n\tpair<set<PdMidiReceiver*>::iterator, bool> ret;\n\tret = midiReceivers.insert(&receiver);\n\tif(!ret.second) {\n\t\tofLogWarning(\"Pd\") << \"addMidiReceiver: ignoring duplicate receiver\";\n\t\treturn;\n\t}\n\n\t// set PdBase receiver on adding first reciever\n\tif(midiReceivers.size() == 1) {\n\t\tPdBase::setMidiReceiver(this);\n\t}\n\n\t// receive from all channels by default\n\treceiveMidiChannel(receiver);\n}\n\nvoid ofxPd::removeMidiReceiver(PdMidiReceiver &receiver) {\n\n\t// exists?\n\tset<PdMidiReceiver*>::iterator r_iter;\n\tr_iter = midiReceivers.find(&receiver);\n\tif(r_iter == midiReceivers.end()) {\n\t\tofLogWarning(\"Pd\") << \"removeMidiReceiver: ignoring unknown receiver\";\n\t\treturn;\n\t}\n\tmidiReceivers.erase(r_iter);\n\t\n\t// clear PdBase receiver on removing last reciever\n\tif(receivers.size() == 0) {\n\t\tPdBase::setMidiReceiver(NULL);\n\t}\n\n\t// remove from all sources\n\tignoreMidiChannel(receiver);\n}\n\nbool ofxPd::midiReceiverExists(PdMidiReceiver &receiver) {\n\tif(midiReceivers.find(&receiver) != midiReceivers.end()) {\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nvoid ofxPd::clearMidiReceivers() {\n\n\tmidiReceivers.clear();\n\t\n\tmap<int,Channel>::iterator iter;\n\tfor(iter = channels.begin(); iter != channels.end(); ++iter) {\n\t\titer->second.receivers.clear();\n\t}\n\n\tPdBase::setMidiReceiver(NULL);\n}\n\n//------------------------------------------------------------------------------\nvoid ofxPd::receiveMidiChannel(PdMidiReceiver &receiver, int channel) {\n\n\tif(!midiReceiverExists(receiver)) {\n\t\tofLogWarning(\"Pd\") << \"receiveMidi: unknown receiver, call addMidiReceiver first\";\n\t\treturn;\n\t}\n\n\t// handle bad channel numbers\n\tif(channel < 0) {\n\t\tchannel = 0;\n\t}\n\n\t// insert channel if it dosen't exist yet\n\tif(channels.find(channel) == channels.end()) {\n\t\tChannel c;\n\t\tchannels.insert(pair<int,Channel>(channel, c));\n\t}\n\n\t// global channel (all channels)\n\tmap<int,Channel>::iterator g_iter;\n\tg_iter = channels.find(0);\n\t\n\t// subscribe to specific channel\n\tif(channel != 0) {\n\t\n\t\t// make sure global channel is ignored\n\t\tif(g_iter->second.midiReceiverExists(&receiver)) {\n\t\t\tg_iter->second.removeMidiReceiver(&receiver);\n\t\t}\n\t\t\n\t\t// receive from specific channel\n\t\tmap<int,Channel>::iterator c_iter;\n\t\tc_iter = channels.find(channel);\n\t\tc_iter->second.addMidiReceiver(&receiver);\n\t}\n\telse {\n\t\t// make sure all channels are ignored\n\t\tignoreMidiChannel(receiver);\n\n\t\t// receive from the global channel\n\t\tg_iter->second.addMidiReceiver(&receiver);\n\t}\n}\n\nvoid ofxPd::ignoreMidiChannel(PdMidiReceiver &receiver, int channel) {\n\n\tif(!midiReceiverExists(receiver)) {\n\t\tofLogWarning(\"Pd\") << \"ignoreMidi: ignoring unknown receiver\";\n\t\treturn;\n\t} \n\n\t// handle bad channel numbers\n\tif(channel < 0) {\n\t\tchannel = 0;\n\t}\n\n\t// insert channel if it dosen't exist yet\n\tif(channels.find(channel) == channels.end()) {\n\t\tChannel c;\n\t\tchannels.insert(pair<int,Channel>(channel, c));\n\t}\n\n\tmap<int,Channel>::iterator c_iter;\n\t\n\t// unsubscribe from specific channel\n\tif(channel != 0) {\n\t\n\t\t// global channel (all channels)\n\t\tmap<int,Channel>::iterator g_iter;\n\t\tg_iter = channels.find(0);\n\t\n\t\t// negation from global\n\t\tif(g_iter->second.midiReceiverExists(&receiver)) {\n\t\t\t\n\t\t\t// remove from global\n\t\t\tg_iter->second.removeMidiReceiver(&receiver);\n\t\t\t\n\t\t\t// add to *all* other channels\n\t\t\tfor(c_iter = channels.begin(); c_iter != channels.end(); ++c_iter) {\n\t\t\t\tif(c_iter != g_iter) {\n\t\t\t\t\tc_iter->second.addMidiReceiver(&receiver);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t// remove from channel\n\t\tc_iter = channels.find(channel);\n\t\tc_iter->second.removeMidiReceiver(&receiver);\n\t}\n\telse {\t// ignore all sources\t\n\t\tfor(c_iter = channels.begin(); c_iter != channels.end(); ++c_iter) {\n\t\t\tc_iter->second.removeMidiReceiver(&receiver);\n\t\t}\n\t}\n}\n\nbool ofxPd::isReceivingMidiChannel(PdMidiReceiver &receiver, int channel) {\n\n\t// handle bad channel numbers\n\tif(channel < 0) {\n\t\tchannel = 0;\n\t}\n\n\tmap<int,Channel>::iterator c_iter;\n\tc_iter = channels.find(channel);\n\tif(c_iter != channels.end() && c_iter->second.midiReceiverExists(&receiver))\n\t\treturn true;\n\treturn false;\n}\n\n//------------------------------------------------------------------------------\nvoid ofxPd::sendNoteOn(const int channel, const int pitch, const int velocity) {\n\tPdBase::sendNoteOn(channel-1, pitch, velocity);\n}\n\nvoid ofxPd::sendControlChange(const int channel, const int control, const int value) {\n\tPdBase::sendControlChange(channel-1, control, value);\n}\n\nvoid ofxPd::sendProgramChange(const int channel, int program) {\n\tPdBase::sendProgramChange(channel-1, program-1);\n}\n\nvoid ofxPd::sendPitchBend(const int channel, const int value) {\n\tPdBase::sendPitchBend(channel-1, value);\n}\n\nvoid ofxPd::sendAftertouch(const int channel, const int value) {\n\tPdBase::sendAftertouch(channel-1, value);\n}\n\nvoid ofxPd::sendPolyAftertouch(const int channel, int pitch, int value) {\n\tPdBase::sendPolyAftertouch(channel-1, pitch, value);\n}\n\n//------------------------------------------------------------------------------\nvoid ofxPd::audioIn(float *input, int bufferSize, int nChannels) {\n\ttry {\n\t\tif(inBuffer != NULL) {\n\t\t\tif(bufferSize != bsize || nChannels != inChannels) {\n\t\t\t\tticks = bufferSize/blockSize();\n\t\t\t\tbsize = bufferSize;\n\t\t\t\tinChannels = nChannels;\n\t\t\t\tofLogVerbose(\"Pd\") << \"buffer size or num input channels updated\";\n\t\t\t\tinit(outChannels, inChannels, srate, ticks, isQueued());\n\t\t\t\tPdBase::computeAudio(computing);\n\t\t\t}\n\t\t\tmemcpy(inBuffer, input, bufferSize*nChannels*sizeof(float));\n\t\t}\n\t}\n\tcatch (...) {\n\t\tofLogError(\"Pd\") << \"could not copy input buffer\";\n\t}\n}\n\nvoid ofxPd::audioOut(float *output, int bufferSize, int nChannels) {\n\tif(inBuffer != NULL) {\n\t\tif(bufferSize != bsize || nChannels != outChannels) {\n\t\t\tticks = bufferSize/blockSize();\n\t\t\tbsize = bufferSize;\n\t\t\toutChannels = nChannels;\n\t\t\tofLogVerbose(\"Pd\") << \"buffer size or num output channels updated\";\n\t\t\tinit(outChannels, inChannels, srate, ticks, isQueued());\n\t\t\tPdBase::computeAudio(computing);\n\t\t}\n\t\tif(!PdBase::processFloat(ticks, inBuffer, output)) {\n\t\t\tofLogError(\"Pd\") << \"could not process output buffer\";\n\t\t}\n\t}\n}\n\nvoid ofxPd::audioIn(ofSoundBuffer &buffer) {\n\taudioIn(buffer.getBuffer().data(), buffer.getNumFrames(), buffer.getNumChannels());\n}\n\nvoid ofxPd::audioOut(ofSoundBuffer &buffer) {\n\taudioOut(buffer.getBuffer().data(), buffer.getNumFrames(), buffer.getNumChannels());\n}\n\n/* ***** PROTECTED ***** */\n\n//------------------------------------------------------------------------------\nvoid ofxPd::print(const std::string &message) {\n\n\tofLogVerbose(\"Pd\") << \"print: \" << message;\n\n\t// broadcast\n\tset<PdReceiver *>::iterator iter;\n\tfor(iter = receivers.begin(); iter != receivers.end(); ++iter) {\n\t\t(*iter)->print(message);\n\t}\n}\n\nvoid ofxPd::receiveBang(const std::string &dest) {\n\n\tofLogVerbose(\"Pd\") << \"bang: \" << dest;\n\t\n\tset<PdReceiver *>::iterator r_iter;\n\tset<PdReceiver *> *r_set;\n\n\t// send to global receivers\n\tmap<string,Source>::iterator g_iter;\n\tg_iter = sources.find(\"\");\n\tr_set = &g_iter->second.receivers;\n\tfor(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) {\n\t\t(*r_iter)->receiveBang(dest);\n\t}\n\t\n\t// send to subscribed receivers\n\tmap<string,Source>::iterator s_iter;\n\ts_iter = sources.find(dest);\n\tr_set = &s_iter->second.receivers;\n\tfor(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) {\n\t\t(*r_iter)->receiveBang(dest);\n\t}\n}\n\nvoid ofxPd::receiveFloat(const std::string &dest, float value) {\n\n\tofLogVerbose(\"Pd\") << \"float: \" << dest << \" \" << value;\n\n\tset<PdReceiver *>::iterator r_iter;\n\tset<PdReceiver *> *r_set;\n\n\t// send to global receivers\n\tmap<string,Source>::iterator g_iter;\n\tg_iter = sources.find(\"\");\n\tr_set = &g_iter->second.receivers;\n\tfor(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) {\n\t\t(*r_iter)->receiveFloat(dest, value);\n\t}\n\n\t// send to subscribed receivers\n\tmap<string,Source>::iterator s_iter;\n\ts_iter = sources.find(dest);\n\tr_set = &s_iter->second.receivers;\n\tfor(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) {\n\t\t(*r_iter)->receiveFloat(dest, value);\n\t}\n}\n\nvoid ofxPd::receiveSymbol(const std::string &dest, const std::string &symbol) {\n\n\tofLogVerbose(\"Pd\") << \"symbol: \" << dest << \" \" << symbol;\n\n\tset<PdReceiver *>::iterator r_iter;\n\tset<PdReceiver *> *r_set;\n\n\t// send to global receivers\n\tmap<string,Source>::iterator g_iter;\n\tg_iter = sources.find(\"\");\n\tr_set = &g_iter->second.receivers;\n\tfor(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) {\n\t\t(*r_iter)->receiveSymbol(dest, (string) symbol);\n\t}\n\n\t// send to subscribed receivers\n\tmap<string,Source>::iterator s_iter;\n\ts_iter = sources.find(dest);\n\tr_set = &s_iter->second.receivers;\n\tfor(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) {\n\t\t(*r_iter)->receiveSymbol(dest, (string) symbol);\n\t}\n}\n\nvoid ofxPd::receiveList(const std::string &dest, const List &list) {\n\n\tofLogVerbose(\"Pd\") << \"list: \" << dest << \" \" << list.toString();\n\n\tset<PdReceiver *>::iterator r_iter;\n\tset<PdReceiver *> *r_set;\n\n\t// send to global receivers\n\tmap<string,Source>::iterator g_iter;\n\tg_iter = sources.find(\"\");\n\tr_set = &g_iter->second.receivers;\n\tfor(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) {\n\t\t(*r_iter)->receiveList(dest, list);\n\t}\n\n\t// send to subscribed receivers\n\tmap<string,Source>::iterator s_iter;\n\ts_iter = sources.find(dest);\n\tr_set = &s_iter->second.receivers;\n\tfor(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) {\n\t\t(*r_iter)->receiveList(dest, list);\n\t}\n}\n\nvoid ofxPd::receiveMessage(const std::string &dest, const std::string &msg, const List &list) {\n\n\tofLogVerbose(\"Pd\") << \"message: \" << dest << \" \" << msg << \" \" << list.toString();\n\n\tset<PdReceiver *>::iterator r_iter;\n\tset<PdReceiver *> *r_set;\n\n\t// send to global receivers\n\tmap<string,Source>::iterator g_iter;\n\tg_iter = sources.find(\"\");\n\tr_set = &g_iter->second.receivers;\n\tfor(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) {\n\t\t(*r_iter)->receiveMessage(dest, msg, list);\n\t}\n\n\t// send to subscribed receivers\n\tmap<string,Source>::iterator s_iter;\n\ts_iter = sources.find(dest);\n\tr_set = &s_iter->second.receivers;\n\tfor(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) {\n\t\t(*r_iter)->receiveMessage(dest, msg, list);\n\t}\n}\n\n//----------------------------------------------------------\nvoid ofxPd::receiveNoteOn(const int channel, const int pitch, const int velocity) {\n\n\tofLogVerbose(\"Pd\") << \"note on: \" << channel+1 << \" \" << pitch << \" \" << velocity;\n\n\tset<PdMidiReceiver *>::iterator r_iter;\n\tset<PdMidiReceiver *> *r_set;\n\n\t// send to global receivers\n\tmap<int,Channel>::iterator g_iter;\n\tg_iter = channels.find(0);\n\tr_set = &g_iter->second.receivers;\n\tfor(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) {\n\t\t(*r_iter)->receiveNoteOn(channel+1, pitch, velocity);\n\t}\n\n\t// send to subscribed receivers\n\tmap<int,Channel>::iterator c_iter;\n\tc_iter = channels.find(channel);\n\tif(c_iter != channels.end()) {\n\t\tr_set = &c_iter->second.receivers;\n\t\tfor(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) {\n\t\t\t(*r_iter)->receiveNoteOn(channel+1, pitch, velocity);\n\t\t}\n\t}\n}\n\nvoid ofxPd::receiveControlChange(const int channel, const int controller, const int value) {\n\n\tofLogVerbose(\"Pd\") << \"control change: \" << channel+1 << \" \" << controller << \" \" << value;\n\n\tset<PdMidiReceiver *>::iterator r_iter;\n\tset<PdMidiReceiver *> *r_set;\n \n\t// send to global receivers\n\tmap<int,Channel>::iterator g_iter;\n\tg_iter = channels.find(0);\n\tr_set = &g_iter->second.receivers;\n\tfor(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) {\n\t\t(*r_iter)->receiveControlChange(channel+1, controller, value);\n\t}\n\n\t// send to subscribed receivers\n\tmap<int,Channel>::iterator c_iter;\n\tc_iter = channels.find(channel);\n\tif(c_iter != channels.end()) {\n\t\tr_set = &c_iter->second.receivers;\n\t\tfor(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) {\n\t\t\t(*r_iter)->receiveControlChange(channel+1, controller, value);\n\t\t}\n\t}\n}\n\nvoid ofxPd::receiveProgramChange(const int channel, const int value) {\n\n\tofLogVerbose(\"Pd\") << \"program change: \" << channel+1 << \" \" << value+1;\n\n    set<PdMidiReceiver *>::iterator r_iter;\n\tset<PdMidiReceiver *> *r_set;\n\n\t// send to global receivers\n\tmap<int,Channel>::iterator g_iter;\n\tg_iter = channels.find(0);\n\tr_set = &g_iter->second.receivers;\n\tfor(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) {\n\t\t(*r_iter)->receiveProgramChange(channel+1, value+1);\n\t}\n\n\t// send to subscribed receivers\n\tmap<int,Channel>::iterator c_iter;\n\tc_iter = channels.find(channel);\n\tif(c_iter != channels.end()) {\n\t\tr_set = &c_iter->second.receivers;\n\t\tfor(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) {\n\t\t\t(*r_iter)->receiveProgramChange(channel+1, value+1);\n\t\t}\n\t}\n}\n\nvoid ofxPd::receivePitchBend(const int channel, const int value) {\n\n\tofLogVerbose(\"Pd\") << \"pitch bend: \" << channel+1 << value;\n\n    set<PdMidiReceiver *>::iterator r_iter;\n\tset<PdMidiReceiver *> *r_set;\n\n\t// send to global receivers\n\tmap<int,Channel>::iterator g_iter;\n\tg_iter = channels.find(0);\n\tr_set = &g_iter->second.receivers;\n\tfor(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) {\n\t\t(*r_iter)->receivePitchBend(channel+1, value);\n\t}\n\n\t// send to subscribed receivers\n\tmap<int,Channel>::iterator c_iter;\n\tc_iter = channels.find(channel);\n\tif(c_iter != channels.end()) {\n\t\tr_set = &c_iter->second.receivers;\n\t\tfor(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) {\n\t\t\t(*r_iter)->receivePitchBend(channel+1, value);\n\t\t}\n\t}\n}\n\nvoid ofxPd::receiveAftertouch(const int channel, const int value) {\n\n\tofLogVerbose(\"Pd\") << \"aftertouch: \" << channel+1 << value;\n\n\tset<PdMidiReceiver *>::iterator r_iter;\n\tset<PdMidiReceiver *> *r_set;\n\n\t// send to global receivers\n\tmap<int,Channel>::iterator g_iter;\n\tg_iter = channels.find(0);\n\tr_set = &g_iter->second.receivers;\n\tfor(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) {\n\t\t(*r_iter)->receiveAftertouch(channel+1, value);\n\t}\n\n\t// send to subscribed receivers\n\tmap<int,Channel>::iterator c_iter;\n\tc_iter = channels.find(channel);\n\tif(c_iter != channels.end()) {\n\t\tr_set = &c_iter->second.receivers;\n\t\tfor(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) {\n\t\t\t(*r_iter)->receiveAftertouch(channel+1, value);\n\t\t}\n\t}\n}\n\nvoid ofxPd::receivePolyAftertouch(const int channel, const int pitch, const int value) {\n\n\tofLogVerbose(\"Pd\") << \"poly aftertouch: \" << channel+1 << \" \" << pitch << \" \" << value;\n\n    set<PdMidiReceiver *>::iterator r_iter;\n\tset<PdMidiReceiver *> *r_set;\n\n\t// send to global receivers\n\tmap<int,Channel>::iterator g_iter;\n\tg_iter = channels.find(0);\n\tr_set = &g_iter->second.receivers;\n\tfor(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) {\n\t\t(*r_iter)->receivePolyAftertouch(channel+1, pitch, value);\n\t}\n\n\t// send to subscribed receivers\n\tmap<int,Channel>::iterator c_iter;\n\tc_iter = channels.find(channel);\n\tif(c_iter != channels.end()) {\n\t\tr_set = &c_iter->second.receivers;\n\t\tfor(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) {\n\t\t\t(*r_iter)->receivePolyAftertouch(channel+1, pitch, value);\n\t\t}\n\t}\n}\n\nvoid ofxPd::receiveMidiByte(const int port, const int byte) {\n\n\tofLogVerbose(\"Pd\") << \"midi byte: \" << port << \" \" << byte;\n\n\tset<PdMidiReceiver *> &r_set = midiReceivers;\n\tset<PdMidiReceiver *>::iterator iter;\n\tfor(iter = r_set.begin(); iter != r_set.end(); ++iter) {\n\t\t(*iter)->receiveMidiByte(port, byte);\n\t}\n}\n"
  },
  {
    "path": "src/ofxPd.h",
    "content": "/*\n * Copyright (c) 2011-2022 Dan Wilcox <danomatika@gmail.com>\n *\n * BSD Simplified License.\n * For information on usage and redistribution, and for a DISCLAIMER OF ALL\n * WARRANTIES, see the file, \"LICENSE.txt,\" in this distribution.\n *\n * See https://github.com/danomatika/ofxPd for documentation\n *\n * This project uses libpd, copyrighted by Miller Puckette and others using the\n * \"Standard Improved BSD License\". See the file \"LICENSE.txt\" in src/pd.\n *\n * See http://gitorious.org/pdlib/pages/Libpd for documentation\n *\n */\n#pragma once\n\n#include <map>\n#include <set>\n\n#include \"PdBase.hpp\"\n#include \"ofSoundBuffer.h\"\n\n///\n/// a Pure Data instance\n///\n/// references: https://github.com/libpd/libpd/wiki\n///\n/// also: see PdBase.h in src/pd/cpp for some functions which are not wrapped by\n///       ofxPd and PdTypes.h for small Pd C++ Objects\n///\n/// differences from libpd C api and/or C++ wrapper:\n///     - midi channels are 1-16 to match pd ranges\n///     - pgm values are 1-128 to match [pgmin]/[pgmout] in pd\n///     - init() takes numOutChannels first to match ofSoundStream\n///\n/// note: as of ofxPd 1.9.0 & libpd 0.13, ofxPd supports multiple instances if\n///       compiled with PDINSTANCE defined, in which case each ofxPd instance can\n///       act separately with it's own PdReceiver and PdMidiReceiver\n///\nclass ofxPd : public pd::PdBase, protected pd::PdReceiver, protected pd::PdMidiReceiver {\n\n\tpublic :\n\n\t\tofxPd();\n\t\tvirtual ~ofxPd();\n\n\t/// \\section Initializing Pd\n\n\t\t/// initialize audio resources\n\t\t///\n\t\t/// set the audio latency by setting the libpd ticks per buffer:\n\t\t/// ticks per buffer * lib pd block size (always 64)\n\t\t///\n\t\t/// ie 4 ticks per buffer * 64 = buffer len of 512\n\t\t///\n\t\t/// you can call this again after loading patches & setting receivers\n\t\t/// in order to update the audio settings\n\t\t///\n\t\t/// the lower the number of ticks, the faster the audio processing\n\t\t/// if you experience audio dropouts (audible clicks), increase the\n\t\t/// ticks per buffer\n\t\t///\n\t\t/// set queued = true to use the built in ringbuffers for message and\n\t\t/// midi event passing, you will then need to call receiveMessages() and\n\t\t/// receiveMidi() in order to pass messages from the ringbuffers to your\n\t\t/// PdReceiver and PdMidiReceiver implementations\n\t\t///\n\t\t/// the queued ringbuffers are useful when you need to receive events\n\t\t/// on a gui thread and don't want to use locking (aka a mutex)\n\t\t///\n\t\tbool init(const int numOutChannels, const int numInChannels,\n\t\t          const int sampleRate, const int ticksPerBuffer=32,\n\t\t\t\t  bool queued=false);\n\n\t\t/// clear resources, here for future proofing, currently does nothing\n\t\tvoid clear();\n\n\t/// \\section Adding Search Paths\n\n\t\t/// add to the pd search path\n\t\t/// takes an absolute or relative path (in data folder)\n\t\t///\n\t\t/// note: fails silently if path not found\n\t\t///\n\t\tvoid addToSearchPath(const std::string &path);\n\n\t\t/// clear the current pd search path\n\t\tvoid clearSearchPath();\n\n\t/// \\section Opening Patches\n\n\t\t/// the pd::Patch class is a wrapper around a unique pointer to an open\n\t\t/// instance, giving you access to the $0 value for that instance\n\t\t///\n\t\t/// if you are going to use multiple instances of a patch, you will need\n\t\t/// need to keep a Patch object in order to differentiate between them\n\t\t///\n\t\t/// see src/pd/cpp/PdTypes.hpp for more info\n\n\t\t/// open a patch file, takes an absolute or relative path (in data folder)\n\t\t/// returns a Patch object\n\t\tpd::Patch openPatch(const std::string &patch);\n\n\t\t/// open a patch file using the filename and path of an existing patch\n\t\t///\n\t\t/// // open a patch, don't need pd::Patch instance object\n\t\t/// pd.openPatch(\"apatch.pd\", \"/some/path\");\n\t\t///\n\t\t/// set the filename within the patch object or use a previously opened\n\t\t/// object and create a new instance\n\t\t///\n\t\t/// // open an instance of \"somefile.pd\", save the instance in a pd::Patch\n\t\t/// Patch p2(\"somefile.pd\", \"/some/path\");    // set file and path\n\t\t/// pd.openPatch(p2);\n\t\t///\n\t\t/// // open a new instance of \"somefile.pd\"\n\t\t/// Patch p3 = pd.openPatch(p2);\n\t\t///\n\t\t/// p2 and p3 now refer to 2 different instances of \"somefile.pd\" and \n\t\t/// p2.dollarZero() & p3.dollarZero() should now return different ids\n\t\t///\n\t\tpd::Patch openPatch(pd::Patch &patch);\n\n\t\t/// close a patch file, takes the patch's basename (filename without extension),\n\t\t/// use this function if you've only opened 1 instance of the given patch\n\t\tvoid closePatch(const std::string &patch);\n\n\t\t/// close a patch file using a Patch object, clears the given Patch object\n\t\t/// does not affect other open instances of the same patch\n\t\tvoid closePatch(pd::Patch &patch);\n\n\t/// \\section Audio Processing Control\n\n\t\t/// start/stop audio processing\n\t\t///\n\t\t/// note: in general, once started, you won't need to turn off audio processing\n\t\t///\n\t\t/// shortcuts for [; pd dsp 1( & [; pd dsp 0(\n\t\t///\n\t\tvoid computeAudio(bool state);\n\t\tvoid start();\n\t\tvoid stop();\n\n\t//// \\section Message Receiving\n\n\t\t/// subscribe/unsubscribe to source names from libpd\n\t\t///\n\t\t/// aka the pd receive name\n\t\t///\n\t\t/// [r source]\n\t\t/// |\n\t\t///\n\t\t/// note: the global source (aka \"\") exists by default\n\t\t///\n\t\tvoid subscribe(const std::string &source);\n\t\tvoid unsubscribe(const std::string &source);\n\t\tbool exists(const std::string &source);\n\t\tvoid unsubscribeAll(); ///< receivers will be unsubscribed from *all* sources\n\n\t\t/// process the internal message queue if using the ringbuffer:\n\t\t///\n\t\t/// internally, libpd will use a ringbuffer to pass messages & midi without\n\t\t/// needing to require locking (mutexes) if you call init() with queued = true\n\t\t///\n\t\t/// call these in your update() loop in order to receive waiting messages\n\t\t/// or midi data which are then sent to your PdReceiver & PdMidiReceiver\n\t\t///\n\t\t/// warning: if you call init() with queued = true and *do not* call these\n\t\t///          functions, you will *not* receive any messages!\n\t\t///\n\t\t/// void receiveMessages(); -> calls PdReceiver\n\t\t/// void receiveMidi();     -> calls PdMidiReceiver\n\n\t\t/// add/remove incoming event receiver\n\t\t///\n\t\t/// receivers automatically receive from *all* subscribed sources\n\t\t/// as well as print events\n\t\t///\n\t\t/// see receive/ignore for specific source receiving control\n\t\t///\n\t\tvoid addReceiver(pd::PdReceiver &receiver);\n\t\tvoid removeReceiver(pd::PdReceiver &receiver);\n\t\tbool receiverExists(pd::PdReceiver &receiver);\n\t\tvoid clearReceivers(); ///< also unsubscribes all receivers\n\n\t\t/// set a receiver to receive/ignore a subscribed source from libpd\n\t\t///\n\t\t/// receive/ignore using a source name or \"\" for all sources,\n\t\t/// make sure to add the receiver and source first\n\t\t///\n\t\t/// note: the global source (aka \"\") is added by default\n\t\t/// note: ignoring the global source ignores *all* sources,\n\t\t///       so the receiver will not receive any message events,\n\t\t///\t\t  but still get print events\n\t\t///\n\t\t/// also: use negation if you want to plug into all sources but one:\n\t\t///\n\t\t/// pd.receive(receiver);          // receive from *all*\n\t\t/// pd.ignore(receiver, \"source\"); // ignore \"source\"\n\t\t///\n\t\tvoid receiveSource(pd::PdReceiver &receiver, const std::string &source=\"\");\n\t\tvoid ignoreSource(pd::PdReceiver &receiver, const std::string &source=\"\");\n\t\tbool isReceivingSource(pd::PdReceiver &receiver, const std::string &source=\"\");\n\n\t/// \\section Midi Receiving\n\n\t\t/// add/remove incoming midi event receiver\n\t\t///\n\t\t/// receivers automatically receive from *all* incoming midi channels\n\t\t///\n\t\t/// see receive/ignore for specific source receiving control\n\t\t///\n\t\tvoid addMidiReceiver(pd::PdMidiReceiver &receiver);\n\t\tvoid removeMidiReceiver(pd::PdMidiReceiver &receiver);\n\t\tbool midiReceiverExists(pd::PdMidiReceiver &receiver);\n\t\tvoid clearMidiReceivers();\n\n\t\t/// set a receiver to receive/ignore an incoming midi channel\n\t\t///\n\t\t/// receive/ignore a specific midi channel or 0 for all channels,\n\t\t/// make sure to add the receiver first\n\t\t///\n\t\t/// note: midi bytes are sent to all receivers\n\t\t/// note: the global channel (aka 0) is added by default\n\t\t/// note: ignoring the global channel ignores *all* channels,\n\t\t///       so the receiver will not receive any midi events except for\n\t\t///       midi bytes\n\t\t///\n\t\t/// also: use negation if you want to plug into all channels but one:\n\t\t///\n\t\t/// pd.receiveMidi(midiReceiver);   // receive from *all* channels\n\t\t/// pd.ignoreMidi(midiReceiver, 2); // ignore channel 2\n\t\t///\n\t\tvoid receiveMidiChannel(pd::PdMidiReceiver &receiver, int channel=0);\n\t\tvoid ignoreMidiChannel(pd::PdMidiReceiver &receiver, int channel=0);\n\t\tbool isReceivingMidiChannel(pd::PdMidiReceiver &receiver, int channel=0);\n\n\t/// \\section Sending Functions\n\n\t\t/// messages\n\t\t///\n\t\t/// pd.sendBang(\"test\");\n\t\t/// pd.sendFloat(\"test\", 1.23);\n\t\t/// pd.sendSymbol(\"test\", \"hello\");\n\t\t///\n\t\t/// see PdBase.h for function declarations\n\n\t\t/// compound messages\n\t\t///\n\t\t/// pd.startMessage();\n\t\t/// pd.addSymbol(\"hello\");\n\t\t/// pd.addFloat(1.23);\n\t\t/// pd.finishList(\"test\");  // \"test\" is the receiver name in pd\n\t\t///\n\t\t/// sends [list hello 1.23( -> [r test],\n\t\t/// you will need to use the [list trim] object on the receiving end\n\t\t///\n\t\t/// finishMsg sends a typed message -> [; test msg1 hello 1.23(\n\t\t///\n\t\t/// pd.startMessage();\n\t\t/// pd.addSymbol(\"hello\");\n\t\t/// pd.addFloat(1.23);\n\t\t/// pd.finishMessage(\"test\", \"msg1\");\n\t\t///\n\t\t/// see PdBase.h for function declarations\n\n\t\t/// compound messages using the Pd List type\n\t\t///\n\t\t/// pd::List list;\n\t\t/// list.addSymbol(\"hello\");\n\t\t/// list.addFloat(1.23);\n\t\t/// pd.sendList(\"test\", list);\n\t\t///\n\t\t/// sends [list hello 1.23( -> [r test]\n\t\t///\n\t\t/// clear the list:\n\t\t///\n\t\t/// list.clear();\n\t\t///\n\t\t/// stream operators work as well:\n\t\t///\n\t\t/// list << \"hello\" << 1.23;\n\t\t/// pd.sendMessage(\"test\", \"msg1\", list);\n\t\t///\n\t\t/// sends a typed message -> [; test msg1 hello 1.23(\n\t\t///\n\t\t/// see PdBase.h for function declarations\n\n\t\t/// midi\n\t\t///\n\t\t/// send midi messages, any out of range messages will be silently ignored\n\t\t///\n\t\t/// number ranges:\n\t\t/// channel     1 - 16 * dev# (dev #0: 1-16, dev #1: 17-32, etc)\n\t\t/// pitch       0 - 127\n\t\t/// velocity    0 - 127\n\t\t/// control value   0 - 127\n\t\t/// program value   1 - 128\n\t\t/// bend value     -8192 - 8191\n\t\t/// touch value     0 - 127\n\t\t///\n\t\t/// note, in pd:\n\t\t/// [bendin] takes 0 - 16383 while [bendout] returns -8192 - 8192\n\t\t/// [pgmin] and [pgmout] are 1 - 128\n\t\t///\n\t\tvoid sendNoteOn(const int channel, const int pitch, const int velocity=64);\n\t\tvoid sendControlChange(const int channel, const int controller, const int value);\n\t\tvoid sendProgramChange(const int channel, const int value);\n\t\tvoid sendPitchBend(const int channel, const int value);\n\t\tvoid sendAftertouch(const int channel, const int value);\n\t\tvoid sendPolyAftertouch(const int channel, const int pitch, const int value);\n\n\t/// \\section Sending Stream Interface\n\n\t\t/// single messages\n\t\t///\n\t\t/// pd << Bang(\"test\"); /// \"test\" is the receiver name in pd\n\t\t/// pd << Float(\"test\", 100);\n\t\t/// pd << Symbol(\"test\", \"a symbol\");\n\t\t///\n\t\t/// compound messages\n\t\t///\n\t\t/// pd << StartMessage() << 100 << 1.2 << \"a symbol\" << FinishList(\"test\");\n\t\t///\n\t\t/// midi\n\t\t///\n\t\t/// pd << NoteOn(64) << NoteOn(64, 60) << NoteOn(64, 60, 1);\n\t\t/// pd << ControlChange(100, 64) << ProgramChange(100, 1) << PitchBend(2000, 1);\n\t\t/// pd << Aftertouch(127, 1) << PolyAftertouch(64, 127, 1);\n\t\t///\n\t\t/// compound raw midi byte stream\n\t\t///\n\t\t/// pd << StartMidi() << 0xEF << 0x45 << Finish();\n\t\t/// pd << StartSysex() << 0xE7 << 0x45 << 0x56 << 0x17 << Finish();\n\t\t///\n\t\t/// see PdBase.h for function declarations\n\n\t/// \\section Array Access\n\n\t\t/// get array size\n\t\t///\n\t\t/// int s = pd.arraySize(\"array1\");\n\t\t///\n\t\t/// resize an array\n\t\t///\n\t\t/// pd.resizeArray(\"array1\", 100);\n\t\t///\n\t\t/// read an array into a float vector\n\t\t///\n\t\t/// vector<float> array1;\n\t\t/// readArray(\"array1\", array1);\n\t\t///\n\t\t/// write a float vector to an array\n\t\t///\n\t\t/// writeArray(\"array1\", array1);\n\t    ///\n\t    /// clear array and set to a specific value\n\t\t///\n\t\t/// clearArray(\"array1\", 0);\n\t\t///\n\t\t/// see PdBase.h for function declarations\n\n\t/// \\section Utils\n\n\t\t/// has this pd instance been initialized?\n\t\t/// bool isInited();\n\t\t///\n\t\t/// is the global pd instance using the ringbuffer queue\n\t\t/// for message padding?\n\t\t/// bool isQueued();\n\t\t///\n\t\t/// get the blocksize of pd (sample length per channel)\n\t\t/// static int blockSize();\n\t\t///\n\t\t/// get/set the max length of messages and lists, default: 32\n\t\t/// void setMaxMsgLength(unsigned int len);\n\t\t/// unsigned int maxMsgLength();\n\t\t///\n\t\t/// get the pd instance pointer\n\t\t/// returns main instance when libpd is not compiled with PDINSTANCE\n\t\t/// t_pdinstance *instancePtr();\n\t\t///\n\t\t/// get the number of pd instances, including the main instance\n\t\t/// returns number or 1 when libpd is not compiled with PDINSTANCE\n\t\t/// static int numInstances();\n\t\t///\n\t\t/// see PdBase.h for function declarations\n\t\n\t\t/// get the current ticks per buffer,\n\t\t/// updated if the buffer size changes in audioIn or audioOut\n\t\tint ticksPerBuffer();\n\n\t\t/// get the current buffer size: ticks per buffer * blockSize(),\n\t\t/// updated if the buffer size changes in audioIn or audioOut\n\t\tint bufferSize();\n\t\n\t\t/// get the current sample rate\n\t\tint sampleRate();\n\t\n\t\t/// get the number of input channels,\n\t\t/// updated if the number changes in audioIn()\n\t\tint numInChannels();\n\t\n\t\t/// get the number of output channels,\n\t\t/// updated if the number changes in audioOut()\n\t\tint numOutChannels();\n\t\n\t\t/// check if ofxPd is currently computing audio\n\t\tbool isComputingAudio();\n\n\t/// \\section Audio Processing Callbacks\n\n\t\t/// audio settings will be re-inited if the buffersize or number of\n\t\t/// channels changes, will produce a verbose print for debugging as well\n\t\t///\n\t\t/// note: the libpd processing is done in the audioOut callback\n\n\t\t/// raw buffer input callback\n\t\tvirtual void audioIn(float *input, int bufferSize, int nChannels);\n\n\t\t/// raw buffer output callback\n\t\tvirtual void audioOut(float *output, int bufferSize, int nChannels);\n\n\t\t/// newer-style input callback which uses ofSoundBuffer\n\t\tvoid audioIn(ofSoundBuffer &buffer);\n\n\t\t/// newer-style output callback which uses ofSoundBuffer\n\t\tvoid audioOut(ofSoundBuffer &buffer);\n\n\tprotected:\n\n\t\t/// message callbacks\n\t\tvoid print(const std::string &message);\n\t\tvoid receiveBang(const std::string &dest);\n\t\tvoid receiveFloat(const std::string &dest, float value);\n\t\tvoid receiveSymbol(const std::string &dest, const std::string &symbol);\n\t\tvoid receiveList(const std::string &dest, const pd::List &list);\n\t\tvoid receiveMessage(const std::string &dest, const std::string &msg, const pd::List &list);\n\n\t\t/// midi callbacks\n\t\tvoid receiveNoteOn(const int channel, const int pitch, const int velocity);\n\t\tvoid receiveControlChange(const int channel, const int controller, const int value);\n\t\tvoid receiveProgramChange(const int channel, const int value);\n\t\tvoid receivePitchBend(const int channel, const int value);\n\t\tvoid receiveAftertouch(const int channel, const int value);\n\t\tvoid receivePolyAftertouch(const int channel, const int pitch, const int value);\n\t\tvoid receiveMidiByte(const int port, const int byte);\n\n\tprivate:\n\n\t\tint ticks; ///< number of ticks per buffer\n\t\tint bsize; ///< current buffer size aka tbp * blocksize\n\t\tint srate; ///< current sample rate\n\t\tint inChannels, outChannels; ///< current num of input & output channels\n\t\tbool computing; ///< is compute audio on?\n\t\n\t\tfloat *inBuffer; ///< interleaved input audio buffer\n\n\t\t/// a receiving source's pointer and receivers\n\t\tstruct Source {\n\n\t\t\tstd::set<pd::PdReceiver *> receivers; ///< receivers\n\n\t\t\t// helper functions\n\t\t\tvoid addReceiver(pd::PdReceiver *receiver) {\n\t\t\t\treceivers.insert(receiver);\n\t\t\t}\n\n\t\t\tvoid removeReceiver(pd::PdReceiver *receiver) {\n\t\t\t\tstd::set<pd::PdReceiver *>::iterator iter;\n\t\t\t\titer = receivers.find(receiver);\n\t\t\t\tif(iter != receivers.end())\n\t\t\t\t\treceivers.erase(iter);\n\t\t\t}\n\n\t\t\tbool receiverExists(pd::PdReceiver *receiver) {\n\t\t\t\tif(receivers.find(receiver) != receivers.end())\n\t\t\t\t\treturn true;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t};\n\n\t\tstd::set<pd::PdReceiver *> receivers;  ///< the receivers\n\t\tstd::map<std::string,Source> sources; ///< subscribed sources\n\t\t                                      ///< first object always global\n\n\t\t/// a receiving midi channel's receivers\n\t\tstruct Channel {\n\n\t\t\tstd::set<pd::PdMidiReceiver *> receivers; ///< receivers\n\n\t\t\t// helper functions\n\t\t\tvoid addMidiReceiver(pd::PdMidiReceiver *receiver) {\n\t\t\t\treceivers.insert(receiver);\n\t\t\t}\n\n\t\t\tvoid removeMidiReceiver(pd::PdMidiReceiver *receiver) {\n\t\t\t\tstd::set<pd::PdMidiReceiver *>::iterator iter;\n\t\t\t\titer = receivers.find(receiver);\n\t\t\t\tif(iter != receivers.end())\n\t\t\t\t\treceivers.erase(iter);\n\t\t\t}\n\n\t\t\tbool midiReceiverExists(pd::PdMidiReceiver *receiver) {\n\t\t\t\tif(receivers.find(receiver) != receivers.end())\n\t\t\t\t\treturn true;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t};\n\n\t\tstd::set<pd::PdMidiReceiver *> midiReceivers; ///< the midi receivers\n\t\tstd::map<int,Channel> channels;               ///< subscribed channels\n\t\t                                              ///< first object always global\n};\n"
  }
]