Repository: danomatika/ofxPd Branch: master Commit: 374561865bfc Files: 171 Total size: 2.9 MB Directory structure: gitextract_4rjwthh6/ ├── .gitignore ├── CHANGES.txt ├── LICENSE.txt ├── README.md ├── addon_config.mk ├── doc/ │ ├── interface_sketch.txt │ └── logo/ │ ├── libpd.pd │ ├── logo.pd │ └── openFrameworks.pd ├── libs/ │ └── libpd/ │ ├── cpp/ │ │ ├── LICENSE.txt │ │ ├── PdBase.hpp │ │ ├── PdMidiReceiver.hpp │ │ ├── PdReceiver.hpp │ │ └── PdTypes.hpp │ ├── libpd_wrapper/ │ │ ├── s_libpdmidi.c │ │ ├── util/ │ │ │ ├── ringbuffer.c │ │ │ ├── ringbuffer.h │ │ │ ├── z_print_util.c │ │ │ ├── z_print_util.h │ │ │ ├── z_queued.c │ │ │ └── z_queued.h │ │ ├── x_libpdreceive.c │ │ ├── x_libpdreceive.h │ │ ├── z_hooks.c │ │ ├── z_hooks.h │ │ ├── z_libpd.c │ │ └── z_libpd.h │ └── pure-data/ │ ├── extra/ │ │ ├── README.txt │ │ ├── bob~/ │ │ │ ├── README.txt │ │ │ └── bob~.c │ │ ├── bonk~/ │ │ │ ├── bonk~.c │ │ │ └── templates.txt │ │ ├── choice/ │ │ │ └── choice.c │ │ ├── fiddle~/ │ │ │ └── fiddle~.c │ │ ├── loop~/ │ │ │ └── loop~.c │ │ ├── lrshift~/ │ │ │ └── lrshift~.c │ │ ├── pd~/ │ │ │ ├── binarymsg.c │ │ │ ├── notes.txt │ │ │ ├── pdsched.c │ │ │ └── pd~.c │ │ ├── pique/ │ │ │ └── pique.c │ │ ├── sigmund~/ │ │ │ └── sigmund~.c │ │ └── stdout/ │ │ └── stdout.c │ └── src/ │ ├── d_arithmetic.c │ ├── d_array.c │ ├── d_ctl.c │ ├── d_dac.c │ ├── d_delay.c │ ├── d_fft.c │ ├── d_fft_fftsg.c │ ├── d_filter.c │ ├── d_global.c │ ├── d_math.c │ ├── d_misc.c │ ├── d_osc.c │ ├── d_resample.c │ ├── d_soundfile.c │ ├── d_soundfile.h │ ├── d_soundfile_aiff.c │ ├── d_soundfile_caf.c │ ├── d_soundfile_next.c │ ├── d_soundfile_wave.c │ ├── d_ugen.c │ ├── g_all_guis.c │ ├── g_all_guis.h │ ├── g_array.c │ ├── g_bang.c │ ├── g_canvas.c │ ├── g_canvas.h │ ├── g_clone.c │ ├── g_editor.c │ ├── g_editor_extras.c │ ├── g_graph.c │ ├── g_guiconnect.c │ ├── g_io.c │ ├── g_mycanvas.c │ ├── g_numbox.c │ ├── g_radio.c │ ├── g_readwrite.c │ ├── g_rtext.c │ ├── g_scalar.c │ ├── g_slider.c │ ├── g_template.c │ ├── g_text.c │ ├── g_toggle.c │ ├── g_traversal.c │ ├── g_undo.c │ ├── g_undo.h │ ├── g_vumeter.c │ ├── m_atom.c │ ├── m_binbuf.c │ ├── m_class.c │ ├── m_conf.c │ ├── m_glob.c │ ├── m_imp.h │ ├── m_memory.c │ ├── m_obj.c │ ├── m_pd.c │ ├── m_pd.h │ ├── m_private_utils.h │ ├── m_sched.c │ ├── s_audio.c │ ├── s_audio_dummy.c │ ├── s_inter.c │ ├── s_inter_gui.c │ ├── s_loader.c │ ├── s_main.c │ ├── s_net.c │ ├── s_net.h │ ├── s_path.c │ ├── s_print.c │ ├── s_stuff.h │ ├── s_utf8.c │ ├── s_utf8.h │ ├── x_acoustics.c │ ├── x_arithmetic.c │ ├── x_array.c │ ├── x_connective.c │ ├── x_file.c │ ├── x_gui.c │ ├── x_interface.c │ ├── x_list.c │ ├── x_midi.c │ ├── x_misc.c │ ├── x_net.c │ ├── x_scalar.c │ ├── x_text.c │ ├── x_time.c │ ├── x_vexp.c │ ├── x_vexp.h │ ├── x_vexp_fun.c │ └── x_vexp_if.c ├── pdExample/ │ ├── addons.make │ ├── bin/ │ │ └── data/ │ │ ├── .gitkeep │ │ └── pd/ │ │ ├── abs/ │ │ │ └── test_abs.pd │ │ ├── instance.pd │ │ └── test.pd │ └── src/ │ ├── main.cpp │ ├── ofApp.cpp │ └── ofApp.h ├── pdExampleIOS/ │ ├── addons.make │ ├── bin/ │ │ └── data/ │ │ ├── .gitkeep │ │ └── pd/ │ │ ├── abs/ │ │ │ └── test_abs.pd │ │ ├── instance.pd │ │ └── test.pd │ └── src/ │ ├── main.mm │ ├── ofApp.h │ └── ofApp.mm ├── pdMultiExample/ │ ├── addons.make │ ├── bin/ │ │ └── data/ │ │ ├── .gitkeep │ │ └── test.pd │ └── src/ │ ├── main.cpp │ ├── ofApp.cpp │ └── ofApp.h ├── pitchShifter/ │ ├── LICENSE.txt │ ├── README.md │ ├── addons.make │ ├── bin/ │ │ └── data/ │ │ ├── .gitkeep │ │ └── pd/ │ │ ├── _main.pd │ │ └── rc/ │ │ ├── e_pshift~-help.pd │ │ ├── e_pshift~.pd │ │ ├── u_spigot~-help.pd │ │ └── u_spigot~.pd │ └── src/ │ ├── main.cpp │ ├── ofApp.cpp │ ├── ofApp.h │ ├── ofxSimpleSlider.cpp │ └── ofxSimpleSlider.h ├── scripts/ │ └── update_libpd.sh └── src/ ├── ofxPd.cpp └── ofxPd.h ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # built *.app *.a # osx .DS_Store # win *.exe *.dll # xcode *.pbxuser *.perspectivev3 *.mode1v3 xcuserdata build # codeblocks *.layout *.depend *.cbTemp # example project files pdExample/Makefile pdExample/*.xcodeproj pdExample/config.make pdExample/*xcconfig pdExample/*.plist pdExample/of.entitlements pdMultiExample/Makefile pdMultiExample/*.xcodeproj pdMultiExample/config.make pdMultiExample/*xcconfig pdMultiExample/*.plist pdMultiExample/of.entitlements pdExampleIOS/Makefile pdExampleIOS/*.xcodeproj pdExampleIOS/config.make pdExampleIOS/*xcconfig pdExampleIOS/*.plist pdExampleIOS/*.pch pdExampleIOS/bin/data/Default*.png pdExampleIOS/bin/data/Icon*.png pdExampleIOS/mediaAssets/ pdExampleIOS/of.entitlements pitchShifter/Makefile pitchShifter/*.xcodeproj pitchShifter/config.make pitchShifter/*xcconfig pitchShifter/*.plist pitchShifter/of.entitlements ================================================ FILE: CHANGES.txt ================================================ TBA * fixed pdMultiExample not using newer ofSoundBuffer audioIn and audioOut functions (reported by Theo Watson) 1.10.1: 2024 Apr 10 * fixed endian detection issues on Linux by defining HAVE_ENDIAN_H * defined _DARWIN_UNLIMITED_SELECT to increased max number of file descriptors on macOS 1.10.0: 2023 Nov 16 * updated to libpd 0.14.0 (pd 0.54-1) note: some files have been added or removed, so regenerate your projects * updated for OF 0.12 warning: the ProjectGenerator may not set the correct C flags for Xcode projects, see the readme on how to manually set them * added ofSoundBuffer audioIn() and audioOut() functions (suggested by Nejrup) * use ofSetBackgroundColor instead of ofSetBackground in pdExample (Jonathan Frank) * updated thumbnail to pd vanilla screenshot * various formatting updates and typo fixes * removed references to Codeblocks in readme build requirements 1.9.1: 2022 Dec 16 * updated to libpd 0.13.2 (pd 0.53-1) 1.9.0: 2022 Nov 05 * updated to libpd 0.13.0 (pd 0.53-0) note: some files have been added or removed, so regenerate your projects * added support for multiple ofxPd instances when compiling with PDINSTANCE note: projects which use libpd_*_instance functions must be updated to use separate ofxPd instances 1.8.1: 2021 Jan 08 * updated to libpd 0.12.1 for pd~ support 1.8.0: 2020 Dec 30 * updated to libpd 0.12.0 (pd 0.51-4) note: some files have been added, so regenerate your projects * added prelim support for Visual Studio (not fully tested, 64 bit only) * added info about PDINSTANCE and PDTHREADS defines to readme * added PDINSTANCE and PDTHREADS defines addon_config.mk (uncomment to use) * added info about NSMicrophoneUsageDescription to readme * readded [pd~] sources as they build on windows again * fixed pdMultiExample not building with PDINSTANCE and PDTHREADS defines (reported by Antoine Rousseau) * append ADDONS_CFLAGS to avoid problem with OF ProjectGenerator on Android * include ofFileUtils.h before ofxPd.h to conflict between boost & libpd's s_ define (Antoine Rousseau) * clarifications & screenshots for MinGW build instructions (moebiussurfing) * clarify windows libpd.dll instructions * update pdMultiExample 1.7.1: 2018 Sep 13 * updated to libpd 0.11.0 (pd 0.48-2) 1.7.0: 2018 Jul 24 * updated for OF 0.10.0 * updated to latest libpd pre-0.11 (pd 0.48-1), note: some files have been removed, so regenerate your projects * fixed macOS 10.13 SDK dispatch.h error * fixed compile error due to leftover Poco/Mutex include (reported by Dan Moore) * fixed iOS duplicate symbols link error by setting -fcommon (reported by ukelady) 1.6.1: 2017 Mar 17 * updated to latest libpd 0.10 (pd 0.47-1) * updated readme section on externs * fixed bad C++11 preprocessor check (reported by Mateus Knelsen) 1.6.0: 2016 May 31 some libpd source files changed, update your projects * added HAVE_LIBDL define for dynamic external loading on desktop * updated to libpd 0.9.0 & pd version 0.47-0: - new clone object - expr is now BSD licensed - HAVE_ALLOCA_H no longer required * updated for OF 0.9.0 * removed Poco mutex as we can now use the PdBase std::mutex with C++11 1.5.3: 2015 Nov 30 * updated to libpd 0.8.4 * ofxPd now reinits itself if the buffer size or number of channels changes in audioIn()/audioOut() * pdMultiExample updates 1.5.2: 2015 Jun 16 * updated latest libpd which includes big fix for ringbuffer race condition 1.5.1: 2015 Apr 28 * updated to libpd & pd version 0.46-6 (includes new bob~ external) * fixed examples not building correctly due to remaining polling interface code * fixed build issue due to missing expr~ and d_fft_fftsg.c sources * old polling interface removed since it's no longer part of PdBase, use queued=true in init() & receiveMessages()/receiveMidi() 1.5.0: 2015 Feb 22 some libpd source files changed, update your projects * updated to latest libpd & pd version 0.46-4 * included extra externals are now loaded by default (expr, bonk~, fiddle~, etc) * updated addons_config.mk to work with upcoming OF 0.9.0 project generator * renamed examples and removed example project files (use the project generator) 1.4.0: 2013 Aug 13 * added PitchShifter example * general updates for OF 0.8.0: * moved libpd source into libs/libpd to match ofxAddonsTemplate * split example into seperate desktop & ios projects (easier to generate) * regenerated project files * updated to latest libpd * built-in pd externals now work (bonk~, choice, fiddle~, loop~, lrshift~, pique~, sigmund~, stdout) 1.3.0: 2012 Nov 7 * added ability to open a new patch instance with an existing patch object * updated to latest libpd * changed logging to use "Pd" logging module * fixed Windows hang on exit due to stuck mutex lock in ofxPd::clear() * removed [expr~] due to licensing, should be left up to individual developers 1.2.0: 2012 Jul 30 * added support for PdBase event polling in ofxPd 1.1.0: 2012 Mar 22 * updated to latest libpd * List::asFloat() & List::asSymbol() are now List::getFloat() & List::getSymbol() * bugfixes for Visual Studio 2010 (thanks Laurence Muller) 1.0.0: 2012 Jan 18 streamlined api * added List building & sending * wrapped Pd receiver and types within a "pd" namespace * reworked api to better match libpd Java api - channel is now first arg in midi functions - port is now first arg in raw midi byte functions - pgm num range is now 1-128 to match [pgmin]/[pgmout] (was 0-127) - destination names are now set when finishing compound messages - renamed "sysRT" functions to "sysRt" - renamed "getArrayLen" to "getArraySize" - renamed "*Source" functions to "subscribe" functions - renamed "*subscribe" functions to "receive/ignore" functions - renamed "ofxPdListener" class to "PdReceiver" - renamed "*Listener" functions to "*Receiver" functions - renamed Receiver functions (bangReceived, floatReceived, etc) to match Java Receiver (receiveBang, receiveFloat, etc) - split midi receiving into PdMidiReceiver base class - renamed "dspOn/dspOff" to "start/stop" - added computeAudio function - receivers now receive from all subscribed sources by default - renamed ofxPdListener and ofxPdTypes source files to PdReceiver and PdTypes - renamed "getArraySize" function to "arraySize" - renamed "setMaxMsgLength" function to "setMaxMessageLen" - renamed "getMaxMsgLength" function to "maxMessgeLen" - renamed "getBlockSize" function to "blockSize" - renamed "sendMsg" & "finishMsg" functions to "sendMessage" & finishMessage" - renamed midi funcs/objects to match java api (ie Note->NoteOn, Ctl->ControlChange, etc) - renamed "receivedPrint" function to "print" * added receiveMidi/ignoreMidi functions to filter midi channel events for PdMidiReceivers * added isInited function * separated base libpd wrapper into PdBase class, now extended by ofxPd - added setReceiver and setMidiReceiver functions - added audio processing (processRaw, processShort, etc) functions - Pd class source files (PdTypes, PdReceiver, etc) have been moved to src/pd/cpp * now checks if HAVE_UNISTD_H & USEAPI_DUMMY are defined before defining * PdBase instance calls now wrapped by a PdContext singleton (global instance until multiple context support is added to libpd) 0.3.0: 2011 Sep 22 * added max message length setting * updated for OF 0062 * updated to newest libpd * merged win codeblocks branch * cleaned Xcode projects, removed OF 0062 project 0.2.0: 2011 Aug 7 * fleshed out entire libpd api: - array access - Patch object - stream interface and type structs - receiving control functions - path handling in addToSearchPath() - ticksPerBuffer added to init() - etc * sound in and out working * added support for Linux and Codeblocks project (thanks Damian Stewart) * added iOS example project * added libpd source update script * added pd external libs tutorial to readme * now using BSD license * updated for OF 007 * simplified some api names to match corresponding pd names (midi) * midi channels are now numbered 1-16 * now thread safe on all platforms (thanks Damian Stewart) * reorganized examples into AppCore class * swapped num in and out channel arg order in init() to match ofSoundStream * moved libpd sources to src folder * updated example: - search path abs test - realtime scope array - keyboard piano - delay * bugfix 0062 hangs on exit * bugfix soundstream hang on exit * bugfix listener bugs and print handling * removed need for libpd static lib * removed 'pd' function name prefix 0.1.0: 2011 Jan 24 initial version * added Xcode OF 007 beta project file * added libpd as a static lib * added message sending functions * added message receiving functions * audio out working * added event listeners (thanks Marek Bereza) ================================================ FILE: LICENSE.txt ================================================ Copyright (c) 2011-2023 Dan Wilcox All rights reserved. The following terms (the "Standard Improved BSD License") apply to all files associated with the software unless explicitly disclaimed in individual files: Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: README.md ================================================ ofxPd =====

Copyright (c) [Dan Wilcox](danomatika.com) 2011-2023 BSD Simplified License. For information on usage and redistribution, and for a DISCLAIMER OF ALL WARRANTIES, see the file, "LICENSE.txt," in this distribution. See https://github.com/danomatika/ofxPd for documentation as well as the [OF forum post on ofxPd](http://forum.openframeworks.cc/t/ofxpd/6492) This 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). Description ----------- 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. [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) [openFrameworks](http://www.openframeworks.cc) is a cross platform open source toolkit for creative coding in C++ Build Requirements ------------------ To 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. [OF github repository](https://github.com/openframeworks/openFrameworks) On macOS, you will need to install Xcode. On Linux, you can use the Makefile. On 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. Installation ------------ Place ofxPd within a folder in the apps folder of the OF dir tree: openframeworks/addons/ofxPd The easiest way to do this is via cloning with git: cd openframeworks/addons/ git clone git://github.com/danomatika/ofxPd.git ### Which version to use? The master branch of ofxPd will work with the current stable version of openFrameworks and can be considered *relatively* stable. Previous 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. If you want to use ofxPd with a previous version of openFrameworks, checkout the corresponding version tag after cloning: git clone git://github.com/danomatika/ofxPd.git cd ofxPd git checkout 1.5.3 Running the Example Projects ---------------------------- The example projects are in the `pdExample` & `pdExampleIOS` folders. Project 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. To (re)generate project files for an *existing* project: * Click the "Import" button in the ProjectGenerator * Navigate to the project's parent folder ie. "ofxPd", select the base folder for the example project ie. "pdExample", and click the Open button * Click the "Update" button If everything went Ok, you should now be able to open the generated project and build/run the example. ### Notes for iOS Projects * 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: 1. Click on the blue project at the top of the Xcode file tree (ie. something like "pdExampleIOS" 2. Click the "Info" tab in the top/middle 3. Under "Custom iOS Target Properties", hover over the last key in the list and click the "+" button 4. Add the following: - Key: NSMicrophoneUsageDescription - Type: string - Value: a description string for the app like, ie. "This app needs to use the microphone for bla bla..." * If you use the OF release zips from openFrameworks.cc, you need the iOS zip *not* the macOS zip * Make sure that "iOS (Xcode)" is selected in the PG's "Platforms" box pdMultiExample & Multiple Instance Support ------------------------------------------ This special example demonstrates ofxPd's multiple-instance support, where you can run multiple separate instances of libpd concurrently. To 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. ### Makefile For Makefile builds, these are set in `pdMultiExample/config.make`. ### Project Generator For 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. ### Xcode The defines can be added manually to the Xcode projects build settings: `Other C Flags` & `Other C++ Flags`. PitchShifter ------------ PitchShifter 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. How to Create a New ofxPd Project --------------------------------- _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._ To 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: openFrameworks/addons/ofxPd/pdExample/ => openFrameworks/apps/myApps/pdExample/ It must be 3 levels down in the openFrameworks folder structure. Then after renaming: openFrameworks/apps/myApps/myPdProject/ ### For Xcode: Rename the project in Xcode (do not rename the .xcodeproj file in Finder!): Xcode Menu->Project->Rename Adding ofxPd to an Existing Project ----------------------------------- _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._ If you want to add ofxPd to another project, you need to make sure you include the src folder: openFrameworks/addons/ofxPd/src You will also need to include some additional C flags for building the libpd source: -DPD -DUSEAPI_DUMMY -DPD_INTERNAL -DHAVE_UNISTD_H -DHAVE_ALLOCA_H -DLIBPD_EXTRA _Note: **-DLIBPD_EXTRA** is optional if you do not need/use the externals in `libpd/pure-data/extra`_ If you want to build ofxPd with the libpd experimental libpd multi-instance support (ie. for pdMultiExample), add these C flags as well: -DPDINSTANCE -DPDTHREADS ### For Xcode: Additional C flags are needed per-platform: * macOS: `-DHAVE_LIBDL -DHAVE_MACHINE_ENDIAN_H -D_DARWIN_C_SOURCE` * iOS: `-fcommon -DHAVE_MACHINE_ENDIAN_H -D_DARWIN_C_SOURCE` * Create a new group "ofxPd" * Drag these directories from ofxPd into this new group: ofxPd/src * Add a search path to: `../../../addons/ofxPd/libs/libpd/pure-data/src` under Targets->YourApp->Build->Header Search Paths (make sure "All" is selected) * Under Targets->YourApp->Build->**Other C Flags** (make sure "All" is selected), add
-DPD -DUSEAPI_DUMMY -DPD_INTERNAL -DHAVE_UNISTD_H -DHAVE_ALLOCA_H -DLIBPD_EXTRA
and the additional C flags noted above * _Note: Make sure you use Other **C** Flags! Other **C++** Flags will **not** work since libpd is written in C._ * Under **Other C++ Flags**, add
-DHAVE_UNISTD_H=1
### For Linux (Makefiles): Edit addons.make in your project folder and add the following line to the end of the file:
ofxPd
Using ofxPd with Visual Studio ------------------------------ ### Visual Studio only As 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. _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._ ### Build libpd.dll with MinGW & use with Visual Studio In 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. You can check if there is a pre-compiled libpd for ofxPd available here: If so, skip to the "Adding libpd" section, otherwise follow the steps below to set up a build environment and build libpd. #### Building libpd with Msys2/MinGW The steps for 64 bit are basically: 1. Set up Msys2/MinGW: see https://github.com/libpd/libpd#windows * _Make sure to follow all steps in the Msys2 setup instructions, ie. updating packages after install_ 2. Open an Msys2 shell (64 bit) 3. Build libpd: `make` 4. Install libpd to a temp folder: `make install prefix=build/libpd` #### Adding libpd to a Visual Studio project Replace the libpd source code in ofxPd with the libpd headers and library files: 1. Delete the ofxPd `ofxPd/libs/libpd` folder 2. Copy `libpd/build/libpd` into `ofxPd/libs` To 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. Add libpd.lib to link stage of the Visual Studio project: ![VS Linker properties](doc/windows_vs_linker.png) * Set "x64" target * Project -> Properties * Make sure Active configuration & platform are set (you will need to do this for both Debug & Release builds) * Configuration Properties -> Linker -> Input * Additional Dependencies -> click on right hand drop down, choose Edit... * Add the path libpd.lib: `$(OF_ROOT)\addons\ofxPd\libs\libpd\lib\libpd.lib` Add the runtime libraries to the project's `bin` folder: * `libpd/build/libpd/lib/libpd.dll` * `libpd/libs/mingw64/libwinpthread-1.dll` _Note: You will need to re-add libpd.lib to the VS link stage whenever you regenerate the project with the OF ProjectGenerator._ ![VS project layout](doc/windows_vs_pdExample.png) For 32 bit: * Open an Msys2 shell (32 bit) * Build libpd using `make` * Set the "Win32" target in your VS project before setting the libpd.lib path * Copy pthread from the "mingw32" folder: `libpd/libs/mingw32/libwinpthread-1.dll` _Screenshots provided by @moebiussurfing._ ### Contributing a libpd Build for Windows If you have successfully built a new version of libpd for Windows, please consider contributing a copy for others to use. Make a zip file with the following layout from your ofxPd directory: ~~~ bin/pd.dll bin/libwinpthread-1.dll libs/libpd/lib/pd.dll libs/libpd/lib/libpd.lib libs/libpd/lib/libpd.def libs/libpd/include/ <-- libpd headers ~~~ Name the zip using the following format: "libpd-VER-ARCH-VS####.zip". For example, "libpd-0.12-prerelease-x64-VS2017.zip" is a 64 bit build of libpd 0.12 (pre-release) using Visual Studio 2017. Create 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/). Notes ----- ### Audio Interfacing & Debugging Audio Issues libpd 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). ### Sample Rate The 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. For 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. ### Running App in the Background on iOS If 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. ### Disabling Automatic Screen Locking on iOS You 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: [[UIApplication sharedApplication] setIdleTimerDisabled:YES]; Bugs & Errors ------------- ### OF 0.12.0 and Xcode: 'ext.h' file not found _Note: This issue is fixed in the OF 0.12.1 ProjectGenerator nightly builds._ The 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: 1. Click on the project in the left-hand Project Navigator 2. Under Targets->YourApp->Build->**Other C Flags** (make sure "All" is selected) 3. If the entry is empty or is missing the required flags, set them ### iOS app crashes immediately with something about "Microphone Description" As 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. Since 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. See the steps listed in the "Running the Example Projects" section. ### Xcode: Expected value in expression dispatch.h The 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": ~~~ -DHAVE_UNISTD_H=1 ~~~ ### Pitch is off on the iPhone 6S The 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. The 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. ### File "tr1/memory" not found in Xcode You 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: * Choose the _parent folder_ of your project folder * Set the name of the project * Add ofxPd as an add-on * Hit generate Also 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. ### Unknown type `t_float`, etc The 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. ### Undefined basic_ostream in XCode If 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: Undefined symbols: "std::basic_ostream ... you need to change the Base SDK to 10.6: Project > Edit Project Settings ### RtAudio Hang on Exit in 0062 RtAudio 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. ### "verbose" redefinition in Win Codeblocks Currently, 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`. Note: This change hasn't been tested while using the ofVideoGrabber yet ... there is a slight chance it may cause a crash, be warned. ### "undefined reference to SetDllDirectory" in Win Codeblocks Newer 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. Adding Pure Data external libraries to ofxPd -------------------------------------------- ofxPd 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. ### Adding external source files The source files for externals included with Pd-extended can be found in the Pure Data Git repositories: . Other externals may be found elsewhere, including on GitHub. For 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): git clone https://git.puredata.info/cgit/svn2git/libraries/zexy.git Once 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. Note: 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. ### Calling the external setup function In 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: ~~~ if(!pd.init(numOutChannels, numInChannels, sampleRate, ticksPerBuffer)) { OF_EXIT_APP(1); } // load libs zexy_setup(); ... ~~~ If all goes well, you should see some sort of print from the library as it initializes: ~~~ [zexy] part of zexy-2.2.3 (compiled: Aug 7 2011) Copyright (l) 1999-2008 IOhannes m zmölnig, forum::für::umläute & IEM [&&~] part of zexy-2.2.3 (compiled: Aug 7 2011) Copyright (l) 1999-2008 IOhannes m zmölnig, forum::für::umläute & IEM [.] part of zexy-2.2.3 (compiled: Aug 7 2011) Copyright (l) 1999-2008 IOhannes m zmölnig, forum::für::umläute & IEM ... ~~~ For 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: ~~~ 'zexy_setup' was not declared in this scope ~~~ In 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: ~~~ #pragma once extern "C" { void zexy_setup(); } ~~~ The `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. ### External library licensing on iOS Apple'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. GPL 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. Developing ofxPd ---------------- You can help develop ofxPd on GitHub: [https://github.com/danomatika/ofxPd](https://github.com/danomatika/ofxPd) Create an account, clone or fork the repo, then request a push/merge. If you find any bugs or suggestions please log them to GitHub as well. ================================================ FILE: addon_config.mk ================================================ # All variables and this file are optional, if they are not present the PG and the # makefiles will try to parse the correct values from the file system. # # Variables that specify exclusions can use % as a wildcard to specify that anything in # that position will match. A partial path can also be specified to, for example, exclude # a whole folder from the parsed paths from the file system # # Variables can be specified using = or += # = will clear the contents of that variable both specified from the file or the ones parsed # from the file system # += will add the values to the previous ones in the file or the ones parsed from the file # system # # The PG can be used to detect errors in this file, just create a new project with this addon # and the PG will write to the console the kind of error and in which line it is meta: ADDON_NAME = ofxPd ADDON_DESCRIPTION = Addon for running the Pure Data audio engine in an OF app ADDON_AUTHOR = Dan Wilcox ADDON_TAGS = "sound" "audio" "computer music" ADDON_URL = http://github.com/danomatika/ofxPd common: # required for libpd ADDON_CFLAGS = -DPD -DUSEAPI_DUMMY -DPD_INTERNAL -DHAVE_UNISTD_H -DHAVE_ALLOCA_H -DLIBPD_EXTRA # uncomment this for multiple instance support, ie. for pdMultiExample #ADDON_CFLAGS += -DPDINSTANCE -DPDTHREADS # this is included directly in pd~.c, don't build twice ADDON_SOURCES_EXCLUDE = libs/libpd/pure-data/extra/pd~/binarymsg.c linux64: ADDON_LIBS_EXCLUDE = libs/libpd/libs # support dynamic loading ADDON_CFLAGS += -DHAVE_LIBDL -DHAVE_ENDIAN_H linux: ADDON_LIBS_EXCLUDE = libs/libpd/libs # support dynamic loading ADDON_CFLAGS += -DHAVE_LIBDL -DHAVE_ENDIAN_H linuxarmv6l: ADDON_LIBS_EXCLUDE = libs/libpd/libs # support dynamic loading ADDON_CFLAGS += -DHAVE_LIBDL -DHAVE_ENDIAN_H linuxarmv7l: ADDON_LIBS_EXCLUDE = libs/libpd/libs # support dynamic loading ADDON_CFLAGS += -DHAVE_LIBDL -DHAVE_ENDIAN_H msys2: # support dynamic loading ADDON_CFLAGS += -DHAVE_LIBDL # this assumes 64 bit builds only at this point... ADDON_DLLS_TO_COPY = libs/libpd/libs/mingw64/libwinpthread-1.dll vs: # support dynamic loading ADDON_CFLAGS += -DHAVE_LIBDL # this assumes 64 bit builds only at this point... ADDON_DLLS_TO_COPY = libs/libpd/libs/mingw64/libwinpthread-1.dll android/armeabi: ADDON_LIBS_EXCLUDE = libs/libpd/libs # support dynamic loading ADDON_CFLAGS += -DHAVE_LIBDL -DHAVE_ENDIAN_H android/armeabi-v7a: ADDON_LIBS_EXCLUDE = libs/libpd/libs # support dynamic loading ADDON_CFLAGS += -DHAVE_LIBDL -DHAVE_ENDIAN_H osx: ADDON_LIBS_EXCLUDE = libs/libpd/libs # support dynamic loading ADDON_CFLAGS += -DHAVE_LIBDL -DHAVE_MACHINE_ENDIAN_H -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT # fix dispatch.h error with macOS SDK 10.13+ ADDON_CPPFLAGS += -DHAVE_UNISTD_H=1 ios: ADDON_LIBS_EXCLUDE = libs/libpd/libs # set No common blocks option to avoid duplicate symbols link error ADDON_CFLAGS += -fcommon -DHAVE_MACHINE_ENDIAN_H -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT # fix dispatch.h error with macOS SDK 10.13+ ADDON_CPPFLAGS += -DHAVE_UNISTD_H=1 ================================================ FILE: doc/interface_sketch.txt ================================================ TYPES // message primitives Bang(string recvName) Float(string recvName, float value) Symbol(string recvName, string symbol) // compound message class List { public: unsigned int len(); // number of items string types(); // OSC style type string ie "fssfffs" // check type bool isFloat(int index=-1) bool isSymbol(int index=-1) // get as type float asFloat(int index=-1) string asSymbol(int index=-1) protected: List(string dest, int length, t_atom *items) addFloat(Float& f); addSymbol(Symbol& s); private: string types; unsigned int length; t_atom *items; }; // compound messages StartList(string recvName) StartMsg(string recvName, string symName) // midi Note(int pitch, int velocity, int channel=0) ControlChange(int controller, int value, int channel=0) ProgramChange(int program, int channel=0) PitchBend(int value, int channel=0) Aftertouch(int value, int channel=0) PolyAftertouch(int pitch, int value, int channel=0) // compound raw midi StartMidi(int port=0) StartSysex(int port=0) StartSysexRealtime(int port=0) // finish any compound message Finish() SENDING // MESSAGES // single messages pd.sendBang("test"); pd.sendFloat("test", 100); pd.sendSymbol("test", "symbol1"); pd << Bang("test") << Float("test", 100) << Symbol("test", "symbol1"); // compound messages // send a list // // [ 100 292.99 c string ( // | // [ s test ( // pd.startList("test"); pd.addFloat(100); pd.addFloat(292.99); pd.addSymbol("c"); pd.addSymbol("string"); pd.finish(); pd << StartMessage("test") << 100 << 292.99 << 'c' << "string" << Finish(); // send a typed message // // [; pd dsp 1 ( // pd.startMsg("pd", "dsp"); pd.addFloat(1); pd.finish(); pd << StartMsg("pd", "dsp") << 1 << Finish(); // MIDI // midi channels should be 1-16? ... same as patching // note pd.sendNote(60); pd.sendNote(60, 100); pd.sendNote(60, 100, 0); pd << Note(60) << Note(60, 100) << Note(60, 100, 0); // controls pd << ControlChange(7, 100) << ControlChange(7, 100, 0); // 7 - Volume pd << ProgamChange(40) << ProgramChange(40, 0); // 40 - Violin pd << PitchBend(8000) << PitchBend(8000, 0); // value -8192 to 8192 pd << Aftertouch(100) << Aftertouch(100, 0); pd << PolyAftertouch(60, 100) << PolyAftertouch(60, 100, 0); // raw midi // Noteon chan 0, Middle C, velocity pd << StartMidi() << 0x90 << 0x3c << 0x40 << FinishBytes(); pd << StartSysex() << 0xF0 << 0x7D << 0xF7 << FinishBytes(); pd << StartSysexRealtime() << 0xF0 << 0x7D << 0xF7 << FinishBytes(); RECEIVING public ofxPdListener { public: void printReceived(const std::string& message) {} void bangReceived(string dest) {} void floatReceived(string dest, float value) {} void symbolReceived(string dest, string symbol) {} void listReceived(string dest, const List& list) {} void messageReceived(string dest, string msg, const List& list) {} void noteReceived(int channel, int pitch, int velocity) {} void controlChangeReceived(int channel, int controller, int val) {} void programChangeReceived(int channel, int program) {} void pitchbendReceived(int channel, int val) {} void aftertouchReceived(int channel, int val) {} void polyAftertouchReceived(int channel, int pitch, int val) {} void midiByteReceived(int port, int byte) {} void sysexReceived(int port, int byte) {} void sysexRealtimeReceived(int port, int byte) {} }; pd.addListener(listener); // add listener to global space pd.addListener(listener, "source1"); pd.addSource("source1"); // add "test" source pd.subscribe(listener, "source1"); // subscribe listener to "test", // adds both if not already added, // takes listener out of global space pd.addSource("source2"); pd.subscribe(listener, "source2"); pd.unsubscribe(listener, "source2"); pd.unsubscribe(listener, "source1"); // unsubscribe listener to "test' pd.unsubscribe(listener); // unsubscribes listener from all sources, global pd.removeListener(listener); // removes listener from global space ofxPdMsg msg; msg.makeBang(); pd.sendMsg(msg); // bang, empty message msg.makeFloat(float); pd.sendMsg(msg); // float msg.clear(); msg. List list; list << 100 << 292.99 << 'c' << "string"; pd.sendList("test", list); pd.sendMsg("test", "pd", list); pd.clear(); ofxPdBundle bundle; bundle.add(msg); ofxPdMidiMsg midi; msp << StartMidi() << 0x90 << 0x3c << 0x40 << FinishBytes(); pd.sendMidi(midi); ================================================ FILE: doc/logo/libpd.pd ================================================ #N canvas 0 22 450 300 10; #X obj 137 96 inlet; #X obj 142 154 outlet; #X obj 277 141 outlet~; #X obj 273 94 inlet~; ================================================ FILE: doc/logo/logo.pd ================================================ #N canvas 0 22 460 266 12; #X obj 278 125 libpd; #X obj 154 125 openFrameworks; #X connect 0 0 1 1; #X connect 1 1 0 0; #X coords 0 -1 1 1 271 71 1 100 100; ================================================ FILE: doc/logo/openFrameworks.pd ================================================ #N canvas 0 22 450 300 10; #X obj 80 100 outlet; #X obj 81 36 inlet; #X obj 218 33 inlet; #X obj 217 98 outlet; ================================================ FILE: libs/libpd/cpp/LICENSE.txt ================================================ Copyright (c) 2012-2022 Dan Wilcox All rights reserved. The following terms (the "Standard Improved BSD License") apply to all files associated with the software unless explicitly disclaimed in individual files: Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: libs/libpd/cpp/PdBase.hpp ================================================ /* * Copyright (c) 2012-2022 Dan Wilcox * * BSD Simplified License. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. * * See https://github.com/libpd/libpd for documentation * * This file was originally written for the ofxPd openFrameworks addon: * https://github.com/danomatika/ofxPd * */ #pragma once #include "z_libpd.h" #include "z_queued.h" #include "z_print_util.h" #include #include "PdTypes.hpp" #include "PdReceiver.hpp" #include "PdMidiReceiver.hpp" // needed for libpd audio passing #ifndef USEAPI_DUMMY #define USEAPI_DUMMY #endif #ifndef HAVE_UNISTD_H #define HAVE_UNISTD_H #endif #ifdef PDINSTANCE #define PDBASE_SETINSTANCE libpd_set_instance(instance); #else #define PDBASE_SETINSTANCE #endif typedef struct _atom t_atom; namespace pd { /// a Pure Data instance /// /// use this class directly or extend it and any of its virtual functions /// /// by default, each PdBase instance refers to the single main libpd /// instance so it is recommended to use only one PdBase at a time /// /// as of version 0.13, libpd supports multiple instances if compiled with /// PDINSTANCE defined, in which case each PdBase instance can act separately /// with it's own PdReceiver and PdMidiReceiver /// class PdBase { public: PdBase() { bMsgInProgress = false; maxMsgLen = 32; curMsgLen = 0; msgType = MSG; midiPort = 0; receiver = NULL; midiReceiver = NULL; bInited = false; bQueued = false; libpd_init(); #ifdef PDINSTANCE instance = libpd_new_instance(); #endif libpd_set_instancedata(this, NULL); } virtual ~PdBase() { PDBASE_SETINSTANCE libpd_set_instancedata(NULL, NULL); #ifdef PDINSTANCE libpd_set_instance(instance); libpd_free_instance(instance); #endif } /// \section Initializing Pd /// initialize resources and set up the audio processing /// /// set the audio latency by setting the libpd ticks per buffer: /// ticks per buffer * lib pd block size (always 64) /// /// ie 4 ticks per buffer * 64 = buffer len of 512 /// /// you can call this again after loading patches & setting receivers /// in order to update the audio settings /// /// the lower the number of ticks, the faster the audio processing /// if you experience audio dropouts (audible clicks), increase the /// ticks per buffer /// /// set queued = true to use the built in ringbuffers for message and /// midi event passing, you will then need to call receiveMessages() and /// receiveMidi() in order to pass messages from the ringbuffers to your /// PdReceiver and PdMidiReceiver implementations /// /// the queued ringbuffers are useful when you need to receive events /// on a gui thread and don't want to use locking /// /// return true if inited successfully /// /// note: must be called before processing /// virtual bool init(const int numInChannels, const int numOutChannels, const int sampleRate, bool queued=false) { PDBASE_SETINSTANCE // attach callbacks bQueued = queued; if(queued) { libpd_queued_init(); libpd_set_queued_printhook(libpd_print_concatenator); libpd_set_concatenated_printhook(_print); libpd_set_queued_banghook(_bang); libpd_set_queued_floathook(_float); libpd_set_queued_symbolhook(_symbol); libpd_set_queued_listhook(_list); libpd_set_queued_messagehook(_message); libpd_set_queued_noteonhook(_noteon); libpd_set_queued_controlchangehook(_controlchange); libpd_set_queued_programchangehook(_programchange); libpd_set_queued_pitchbendhook(_pitchbend); libpd_set_queued_aftertouchhook(_aftertouch); libpd_set_queued_polyaftertouchhook(_polyaftertouch); libpd_set_queued_midibytehook(_midibyte); } else { libpd_set_printhook(libpd_print_concatenator); libpd_set_concatenated_printhook(_print); libpd_set_banghook(_bang); libpd_set_floathook(_float); libpd_set_symbolhook(_symbol); libpd_set_listhook(_list); libpd_set_messagehook(_message); libpd_set_noteonhook(_noteon); libpd_set_controlchangehook(_controlchange); libpd_set_programchangehook(_programchange); libpd_set_pitchbendhook(_pitchbend); libpd_set_aftertouchhook(_aftertouch); libpd_set_polyaftertouchhook(_polyaftertouch); libpd_set_midibytehook(_midibyte); } // init audio if(libpd_init_audio(numInChannels, numOutChannels, sampleRate) != 0) { return false; } bInited = true; return bInited; } /// clear resources virtual void clear() { PDBASE_SETINSTANCE // detach callbacks if(bInited) { computeAudio(false); if(bQueued) { libpd_set_queued_printhook(NULL); libpd_set_concatenated_printhook(NULL); libpd_set_queued_banghook(NULL); libpd_set_queued_floathook(NULL); libpd_set_queued_symbolhook(NULL); libpd_set_queued_listhook(NULL); libpd_set_queued_messagehook(NULL); libpd_set_queued_noteonhook(NULL); libpd_set_queued_controlchangehook(NULL); libpd_set_queued_programchangehook(NULL); libpd_set_queued_pitchbendhook(NULL); libpd_set_queued_aftertouchhook(NULL); libpd_set_queued_polyaftertouchhook(NULL); libpd_set_queued_midibytehook(NULL); libpd_queued_release(); } else { libpd_set_printhook(NULL); libpd_set_concatenated_printhook(NULL); libpd_set_banghook(NULL); libpd_set_floathook(NULL); libpd_set_symbolhook(NULL); libpd_set_listhook(NULL); libpd_set_messagehook(NULL); libpd_set_noteonhook(NULL); libpd_set_controlchangehook(NULL); libpd_set_programchangehook(NULL); libpd_set_pitchbendhook(NULL); libpd_set_aftertouchhook(NULL); libpd_set_polyaftertouchhook(NULL); libpd_set_midibytehook(NULL); } } bInited = false; bQueued = false; bMsgInProgress = false; curMsgLen = 0; msgType = MSG; midiPort = 0; unsubscribeAll(); } /// \section Adding Search Paths /// add to the pd search path /// takes an absolute or relative path (in data folder) /// /// note: fails silently if path not found /// virtual void addToSearchPath(const std::string &path) { PDBASE_SETINSTANCE libpd_add_to_search_path(path.c_str()); } /// clear the current pd search path virtual void clearSearchPath() { PDBASE_SETINSTANCE libpd_clear_search_path(); } /// \section Opening Patches /// open a patch file (aka somefile.pd) at a specified parent dir path /// returns a pd::Patch object /// /// use pd::Patch::isValid() to check if a patch was opened successfully: /// /// pd::Patch p1 = pd.openPatch("somefile.pd", "/some/dir/path/"); /// if(!p1.isValid()) { /// std::cout << "aww ... p1 couldn't be opened" << std::endl; /// } virtual pd::Patch openPatch(const std::string &patch, const std::string &path) { PDBASE_SETINSTANCE // [; pd open file folder( void *handle = libpd_openfile(patch.c_str(), path.c_str()); if(handle == NULL) { return Patch(); // return empty Patch } int dollarZero = libpd_getdollarzero(handle); return Patch(handle, dollarZero, patch, path); } /// open a patch file using the filename and path of an existing patch /// /// set the filename within the patch object or use a previously opened /// object /// /// // open an instance of "somefile.pd" /// pd::Patch p2("somefile.pd", "/some/path"); // set file and path /// pd.openPatch(p2); /// /// // open a new instance of "somefile.pd" /// pd::Patch p3 = pd.openPatch(p2); /// /// // p2 and p3 refer to 2 different instances of "somefile.pd" /// virtual pd::Patch openPatch(pd::Patch &patch) { return openPatch(patch.filename(), patch.path()); } /// close a patch file /// takes only the patch's basename (filename without extension) virtual void closePatch(const std::string &patch) { PDBASE_SETINSTANCE // [; pd-name menuclose 1( std::string patchname = (std::string)"pd-" + patch; libpd_start_message(1); libpd_add_float(1); libpd_finish_message(patchname.c_str(), "menuclose"); } /// close a patch file, takes a patch object /// note: clears the given Patch object virtual void closePatch(pd::Patch &patch) { if(!patch.isValid()) { return; } PDBASE_SETINSTANCE libpd_closefile(patch.handle()); patch.clear(); } /// \section Audio Processing /// /// one of these must be called for audio dsp and message io to occur /// /// inBuffer must be an array of the right size and never null /// use inBuffer = new type[0] if no input is desired /// /// outBuffer must be an array of size outBufferSize from openAudio call /// /// note: raw does not interlace the buffers /// /// process float buffers for a given number of ticks /// returns false on error bool processFloat(int ticks, const float *inBuffer, float *outBuffer) { PDBASE_SETINSTANCE return libpd_process_float(ticks, inBuffer, outBuffer) == 0; } /// process short buffers for a given number of ticks /// returns false on error bool processShort(int ticks, const short *inBuffer, short *outBuffer) { PDBASE_SETINSTANCE return libpd_process_short(ticks, inBuffer, outBuffer) == 0; } /// process double buffers for a given number of ticks /// returns false on error bool processDouble(int ticks, const double *inBuffer, double *outBuffer) { PDBASE_SETINSTANCE return libpd_process_double(ticks, inBuffer, outBuffer) == 0; } /// process one pd tick, writes raw float data to/from buffers /// returns false on error bool processRaw(const float *inBuffer, float *outBuffer) { PDBASE_SETINSTANCE return libpd_process_raw(inBuffer, outBuffer) == 0; } /// process one pd tick, writes raw short data to/from buffers /// returns false on error bool processRawShort(const short *inBuffer, short *outBuffer) { PDBASE_SETINSTANCE return libpd_process_raw_short(inBuffer, outBuffer) == 0; } /// process one pd tick, writes raw double data to/from buffers /// returns false on error bool processRawDouble(const double *inBuffer, double *outBuffer) { PDBASE_SETINSTANCE return libpd_process_raw_double(inBuffer, outBuffer) == 0; } /// \section Audio Processing Control /// start/stop audio processing /// /// in general, once started, you won't need to turn off audio /// /// shortcut for [; pd dsp 1( & [; pd dsp 0( /// virtual void computeAudio(bool state) { PDBASE_SETINSTANCE // [; pd dsp $1( libpd_start_message(1); libpd_add_float((float) state); libpd_finish_message("pd", "dsp"); } /// \section Message Receiving /// subscribe to messages sent by a pd send source /// /// aka this like a virtual pd receive object /// /// [r source] /// | /// virtual void subscribe(const std::string &source) { if(exists(source)) { std::cerr << "Pd: unsubscribe: ignoring duplicate source" << std::endl; return; } PDBASE_SETINSTANCE void *pointer = libpd_bind(source.c_str()); if(pointer != NULL) { sources.insert(std::pair(source, pointer)); } } /// unsubscribe from messages sent by a pd send source virtual void unsubscribe(const std::string &source) { std::map::iterator iter; iter = sources.find(source); if(iter == sources.end()) { std::cerr << "Pd: unsubscribe: ignoring unknown source" << std::endl; return; } PDBASE_SETINSTANCE libpd_unbind(iter->second); sources.erase(iter); } /// is a pd send source subscribed? virtual bool exists(const std::string &source) { PDBASE_SETINSTANCE if(sources.find(source) != sources.end()) { return true; } return false; } //// receivers will be unsubscribed from *all* pd send sources virtual void unsubscribeAll() { PDBASE_SETINSTANCE std::map::iterator iter; for(iter = sources.begin(); iter != sources.end(); ++iter) { libpd_unbind(iter->second); } sources.clear(); } /// \section Receiving from the Message Queues /// /// process the internal message queue if using the ringbuffer /// /// internally, libpd will use a ringbuffer to pass messages & midi without /// needing to require locking if you call init() with queued = true /// /// call these in a loop somewhere in order to receive waiting messages /// or midi data which are then sent to your PdReceiver & PdMidiReceiver /// /// *do not* use if inited with queued = false /// process waiting messages virtual void receiveMessages() { PDBASE_SETINSTANCE libpd_queued_receive_pd_messages(); } /// process waiting midi messages virtual void receiveMidi() { PDBASE_SETINSTANCE libpd_queued_receive_midi_messages(); } /// \section Event Receiving via Callbacks /// set the incoming event receiver, disables the event queue /// /// automatically receives from all currently subscribed sources /// /// set this to NULL to disable callback receiving and re-enable the /// event queue /// void setReceiver(pd::PdReceiver *receiver) { this->receiver = receiver; } /// \section Midi Receiving via Callbacks /// set the incoming midi event receiver, disables the midi queue /// /// automatically receives from all midi channels /// /// set this to NULL to disable midi events and re-enable the midi queue /// void setMidiReceiver(pd::PdMidiReceiver *midiReceiver) { this->midiReceiver = midiReceiver; } /// \section Send Functions /// send a bang message virtual void sendBang(const std::string &dest) { PDBASE_SETINSTANCE libpd_bang(dest.c_str()); } /// send a float virtual void sendFloat(const std::string &dest, float value) { PDBASE_SETINSTANCE libpd_float(dest.c_str(), value); } /// send a symbol virtual void sendSymbol(const std::string &dest, const std::string &symbol) { PDBASE_SETINSTANCE libpd_symbol(dest.c_str(), symbol.c_str()); } /// \section Sending Compound Messages /// /// pd.startMessage(); /// pd.addSymbol("hello"); /// pd.addFloat(1.23); /// pd.finishList("test"); // "test" is the receiver name in pd /// /// sends [list hello 1.23( -> [r test], /// you will need to use the [list trim] object on the receiving end /// /// finishMsg sends a typed message -> [; test msg1 hello 1.23( /// /// pd.startMessage(); /// pd.addSymbol("hello"); /// pd.addFloat(1.23); /// pd.finishMessage("test", "msg1"); /// /// start a compound list or message virtual void startMessage() { if(bMsgInProgress) { std::cerr << "Pd: cannot start message, message in progress" << std::endl; return; } PDBASE_SETINSTANCE if(libpd_start_message(maxMsgLen) == 0) { bMsgInProgress = true; msgType = MSG; } } /// add a float to the current compound list or message virtual void addFloat(const float num) { if(!bMsgInProgress) { std::cerr << "Pd: cannot add float, message not in progress" << std::endl; return; } if(msgType != MSG) { std::cerr << "Pd: cannot add float, midi byte stream in progress" << std::endl; return; } if(curMsgLen+1 >= maxMsgLen) { std::cerr << "Pd: cannot add float, max message len of " << maxMsgLen << " reached" << std::endl; return; } PDBASE_SETINSTANCE libpd_add_float(num); curMsgLen++; } /// add a symbol to the current compound list or message virtual void addSymbol(const std::string &symbol) { if(!bMsgInProgress) { std::cerr << "Pd: cannot add symbol, message not in progress" << std::endl; return; } if(msgType != MSG) { std::cerr << "Pd: cannot add symbol, midi byte stream in progress" << std::endl; return; } if(curMsgLen+1 >= maxMsgLen) { std::cerr << "Pd: cannot add symbol, max message len of " << maxMsgLen << " reached" << std::endl; return; } PDBASE_SETINSTANCE libpd_add_symbol(symbol.c_str()); curMsgLen++; } /// finish and send as a list virtual void finishList(const std::string &dest) { if(!bMsgInProgress) { std::cerr << "Pd: cannot finish list, " << "message not in progress" << std::endl; return; } if(msgType != MSG) { std::cerr << "Pd: cannot finish list, " << "midi byte stream in progress" << std::endl; return; } PDBASE_SETINSTANCE libpd_finish_list(dest.c_str()); bMsgInProgress = false; curMsgLen = 0; } /// finish and send as a list with a specific message name virtual void finishMessage(const std::string &dest, const std::string &msg) { if(!bMsgInProgress) { std::cerr << "Pd: cannot finish message, " << "message not in progress" << std::endl; return; } if(msgType != MSG) { std::cerr << "Pd: cannot finish message, " << "midi byte stream in progress" << std::endl; return; } PDBASE_SETINSTANCE libpd_finish_message(dest.c_str(), msg.c_str()); bMsgInProgress = false; curMsgLen = 0; } /// send a list using the pd::List type /// /// pd::List list; /// list.addSymbol("hello"); /// list.addFloat(1.23); /// pd.sendList("test", list); /// /// sends [list hello 1.23( -> [r test] /// /// stream operators work as well: /// /// list << "hello" << 1.23; /// pd.sendList("test", list); /// virtual void sendList(const std::string &dest, const pd::List &list) { if(bMsgInProgress) { std::cerr << "Pd: cannot send list, message in progress" << std::endl; return; } PDBASE_SETINSTANCE libpd_start_message(list.len()); bMsgInProgress = true; // step through list for(int i = 0; i < (int)list.len(); ++i) { if(list.isFloat(i)) addFloat(list.getFloat(i)); else if(list.isSymbol(i)) addSymbol(list.getSymbol(i)); } finishList(dest); } /// send a message using the pd::List type /// /// pd::List list; /// list.addSymbol("hello"); /// list.addFloat(1.23); /// pd.sendMessage("test", "msg1", list); /// /// sends a typed message -> [; test msg1 hello 1.23( /// /// stream operators work as well: /// // list << "hello" << 1.23; /// pd.sendMessage("test", "msg1", list); /// virtual void sendMessage(const std::string &dest, const std::string &msg, const pd::List &list = pd::List()) { if(bMsgInProgress) { std::cerr << "Pd: cannot send message, message in progress" << std::endl; return; } PDBASE_SETINSTANCE libpd_start_message(list.len()); bMsgInProgress = true; // step through list for(int i = 0; i < (int)list.len(); ++i) { if(list.isFloat(i)) addFloat(list.getFloat(i)); else if(list.isSymbol(i)) addSymbol(list.getSymbol(i)); } finishMessage(dest, msg); } /// \section Sending MIDI /// /// any out of range messages will be silently ignored /// /// number ranges: /// * channel 0 - 15 * dev# (dev #0: 0-15, dev #1: 16-31, etc) /// * pitch 0 - 127 /// * velocity 0 - 127 /// * controller value 0 - 127 /// * program value 0 - 127 /// * bend value -8192 - 8191 /// * touch value 0 - 127 /// /// send a MIDI note on /// /// pd does not use note off MIDI messages, so send a note on with vel = 0 /// virtual void sendNoteOn(const int channel, const int pitch, const int velocity=64) { PDBASE_SETINSTANCE libpd_noteon(channel, pitch, velocity); } /// send a MIDI control change virtual void sendControlChange(const int channel, const int controller, const int value) { PDBASE_SETINSTANCE libpd_controlchange(channel, controller, value); } /// send a MIDI program change virtual void sendProgramChange(const int channel, const int value) { PDBASE_SETINSTANCE libpd_programchange(channel, value); } /// send a MIDI pitch bend /// /// in pd: [bendin] takes 0 - 16383 while [bendout] returns -8192 - 8192 /// virtual void sendPitchBend(const int channel, const int value) { PDBASE_SETINSTANCE libpd_pitchbend(channel, value); } /// send a MIDI aftertouch virtual void sendAftertouch(const int channel, const int value) { PDBASE_SETINSTANCE libpd_aftertouch(channel, value); } /// send a MIDI poly aftertouch virtual void sendPolyAftertouch(const int channel, const int pitch, const int value) { PDBASE_SETINSTANCE libpd_polyaftertouch(channel, pitch, value); } /// send a raw MIDI byte /// /// value is a raw midi byte value 0 - 255 /// port is the raw portmidi port #, similar to a channel /// /// for some reason, [midiin], [sysexin] & [realtimein] add 2 to the /// port num, so sending to port 1 in PdBase returns port 3 in pd /// /// however, [midiout], [sysexout], & [realtimeout] do not add to the /// port num, so sending port 1 to [midiout] returns port 1 in PdBase /// virtual void sendMidiByte(const int port, const int value) { PDBASE_SETINSTANCE libpd_midibyte(port, value); } /// send a raw MIDI sysex byte virtual void sendSysex(const int port, const int value) { PDBASE_SETINSTANCE libpd_sysex(port, value); } /// send a raw MIDI realtime byte virtual void sendSysRealTime(const int port, const int value) { PDBASE_SETINSTANCE libpd_sysrealtime(port, value); } /// \section Stream Interface /// /// single messages /// /// pd << Bang("test"); /// "test" is the receiver name in pd /// pd << Float("test", 100); /// pd << Symbol("test", "a symbol"); /// /// send a bang message PdBase& operator<<(const pd::Bang &var) { if(bMsgInProgress) { std::cerr << "Pd: cannot send Bang, message in progress" << std::endl; return *this; } sendBang(var.dest.c_str()); return *this; } /// send a float message PdBase& operator<<(const pd::Float &var) { if(bMsgInProgress) { std::cerr << "Pd: cannot send Float, message in progress" << std::endl; return *this; } sendFloat(var.dest.c_str(), var.num); return *this; } /// send a symbol message PdBase& operator<<(const pd::Symbol &var) { if(bMsgInProgress) { std::cerr << "Pd: cannot send Symbol, message in progress" << std::endl; return *this; } sendSymbol(var.dest.c_str(), var.symbol.c_str()); return *this; } /// \section Stream Interface for Compound Messages /// /// pd << pd::StartMessage() << 100 << 1.2 << "a symbol" << pd::FinishList("test"); /// /// start a compound message PdBase& operator<<(const pd::StartMessage &) { startMessage(); return *this; } /// finish a compound message and send it as a list PdBase& operator<<(const pd::FinishList &var) { finishList(var.dest); return *this; } /// finish a compound message and send it as a message PdBase& operator<<(const pd::FinishMessage &var) { finishMessage(var.dest, var.msg); return *this; } // add a boolean as a float to the compound message PdBase& operator<<(const bool var) { addFloat((float) var); return *this; } // add an integer as a float to the compound message PdBase& operator<<(const int var) { switch(msgType) { case MSG: addFloat((float) var); break; case MIDI: sendMidiByte(midiPort, var); break; case SYSEX: sendSysex(midiPort, var); break; case SYSRT: sendSysRealTime(midiPort, var); break; } return *this; } // add a float to the compound message PdBase& operator<<(const float var) { addFloat((float) var); return *this; } // add a double as a float to the compound message PdBase& operator<<(const double var) { addFloat((float) var); return *this; } // add a character as a symbol to the compound message PdBase& operator<<(const char var) { std::string s; s = var; addSymbol(s); return *this; } // add a C-string char buffer as a symbol to the compound message PdBase& operator<<(const char *var) { addSymbol((std::string)var); return *this; } // add a string as a symbol to the compound message PdBase& operator<<(const std::string &var) { addSymbol(var); return *this; } /// \section Stream Interface for MIDI /// /// pd << pd::NoteOn(64) << NoteOn(64, 60) << pd::NoteOn(64, 60, 1); /// pd << pd::ControlChange(100, 64) << pd::ProgramChange(100, 1); /// pd << pd::Aftertouch(127, 1) << pd::PolyAftertouch(64, 127, 1); /// pd << pd::PitchBend(2000, 1); /// /// send a MIDI note on PdBase& operator<<(const pd::NoteOn &var) { sendNoteOn(var.channel, var.pitch, var.velocity); return *this; } /// send a MIDI control change PdBase& operator<<(const pd::ControlChange &var) { sendControlChange(var.channel, var.controller, var.value); return *this; } /// send a MIDI program change PdBase& operator<<(const pd::ProgramChange &var) { sendProgramChange(var.channel, var.value); return *this; } /// send a MIDI pitch bend PdBase& operator<<(const pd::PitchBend &var) { sendPitchBend(var.channel, var.value); return *this; } /// send a MIDI aftertouch PdBase& operator<<(const pd::Aftertouch &var) { sendAftertouch(var.channel, var.value); return *this; } /// send a MIDI poly aftertouch PdBase& operator<<(const pd::PolyAftertouch &var) { sendPolyAftertouch(var.channel, var.pitch, var.value); return *this; } /// \section Stream Interface for Raw Bytes /// /// pd << pd::StartMidi() << 0xEF << 0x45 << pd::Finish(); /// pd << pd::StartSysex() << 0xE7 << 0x45 << 0x56 << 0x17 << pd::Finish(); /// /// start a raw byte MIDI message PdBase& operator<<(const pd::StartMidi &var) { if(bMsgInProgress) { std::cerr << "Pd: cannot start MidiByte stream, " << "message in progress" << std::endl; return *this; } bMsgInProgress = true; msgType = MIDI; midiPort = var.port; return *this; } /// start a raw byte MIDI sysex message PdBase& operator<<(const pd::StartSysex &var) { if(bMsgInProgress) { std::cerr << "Pd: cannot start Sysex stream, " << "message in progress" << std::endl; return *this; } bMsgInProgress = true; msgType = SYSEX; midiPort = var.port; return *this; } /// start a raw byte MIDI realtime message PdBase& operator<<(const pd::StartSysRealTime &var) { if(bMsgInProgress) { std::cerr << "Pd: cannot start SysRealRime stream, " << "message in progress" << std::endl; return *this; } bMsgInProgress = true; msgType = SYSRT; midiPort = var.port; return *this; } /// finish and send a raw byte MIDI message PdBase& operator<<(const pd::Finish &) { if(!bMsgInProgress) { std::cerr << "Pd: cannot finish midi byte stream, " << "stream not in progress" << std::endl; return *this; } if(msgType == MSG) { std::cerr << "Pd: cannot finish midi byte stream, " << "message in progress" << std::endl; return *this; } bMsgInProgress = false; curMsgLen = 0; return *this; } /// is a message or byte stream currently in progress? bool isMessageInProgress() { return bMsgInProgress; } /// \section Array Access /// get the size of a pd array /// returns 0 if array not found int arraySize(const std::string &name) { PDBASE_SETINSTANCE int len = libpd_arraysize(name.c_str()); if(len < 0) { std::cerr << "Pd: cannot get size of unknown array \"" << name << "\"" << std::endl; return 0; } return len; } /// (re)size a pd array /// sizes <= 0 are clipped to 1 /// returns true on success, false on failure bool resizeArray(const std::string &name, long size) { PDBASE_SETINSTANCE int ret = libpd_resize_array(name.c_str(), size); if(ret < 0) { std::cerr << "Pd: cannot resize unknown array \"" << name << "\"" << std::endl; return false; } return true; } /// read from a pd array /// /// resizes given vector to readLen, checks readLen and offset /// /// returns true on success, false on failure /// /// calling without setting readLen and offset reads the whole array: /// /// std::vector array1; /// readArray("array1", array1); /// virtual bool readArray(const std::string &name, std::vector &dest, int readLen=-1, int offset=0) { PDBASE_SETINSTANCE int len = libpd_arraysize(name.c_str()); if(len < 0) { std::cerr << "Pd: cannot read unknown array \"" << name << "\"" << std::endl; return false; } // full array len? if(readLen < 0) { readLen = len; } // check read len else if(readLen > len) { std::cerr << "Pd: given read len " << readLen << " > len " << len << " of array \"" << name << "\"" << std::endl; return false; } // check offset if(offset + readLen > len) { std::cerr << "Pd: given read len and offset > len " << readLen << " of array \"" << name << "\"" << std::endl; return false; } // resize if necessary if(dest.size() != (std::size_t)readLen) { dest.resize(readLen, 0); } if(libpd_read_array(&dest[0], name.c_str(), offset, readLen) < 0) { std::cerr << "Pd: libpd_read_array failed for array \"" << name << "\"" << std::endl; return false; } return true; } /// write to a pd array /// /// calling without setting writeLen and offset writes the whole array: /// /// writeArray("array1", array1); /// virtual bool writeArray(const std::string &name, std::vector &source, int writeLen=-1, int offset=0) { PDBASE_SETINSTANCE int len = libpd_arraysize(name.c_str()); if(len < 0) { std::cerr << "Pd: cannot write to unknown array \"" << name << "\"" << std::endl; return false; } // full array len? if(writeLen < 0) { writeLen = len; } // check write len else if(writeLen > len) { std::cerr << "Pd: given write len " << writeLen << " > len " << len << " of array \"" << name << "\"" << std::endl; return false; } // check offset if(offset+writeLen > len) { std::cerr << "Pd: given write len and offset > len " << writeLen << " of array \"" << name << "\"" << std::endl; return false; } if(libpd_write_array(name.c_str(), offset, &source[0], writeLen) < 0) { std::cerr << "Pd: libpd_write_array failed for array \"" << name << "\"" << std::endl; return false; } return true; } /// clear array and set to a specific value virtual void clearArray(const std::string &name, int value=0) { PDBASE_SETINSTANCE int len = libpd_arraysize(name.c_str()); if(len < 0) { std::cerr << "Pd: cannot clear unknown array \"" << name << "\"" << std::endl; return; } std::vector array; array.resize(len, value); if(libpd_write_array(name.c_str(), 0, &array[0], len) < 0) { std::cerr << "Pd: libpd_write_array failed while clearing array \"" << name << "\"" << std::endl; } } /// \section Utils /// has the global pd instance been initialized? bool isInited() { return bInited; } /// is the global pd instance using the ringbuffer queue /// for message padding? bool isQueued() { return bQueued; } /// get the blocksize of pd (sample length per channel) static int blockSize() { return libpd_blocksize(); } /// set the max length of messages and lists, default: 32 void setMaxMessageLen(unsigned int len) { maxMsgLen = len; } /// get the max length of messages and lists unsigned int maxMessageLen() { return maxMsgLen; } /// get the pd instance pointer /// returns main instance when libpd is not compiled with PDINSTANCE t_pdinstance *instancePtr() { #ifdef PDINSTANCE return instance; #endif return libpd_main_instance(); } /// get the number of pd instances, including the main instance /// returns number or 1 when libpd is not compiled with PDINSTANCE static int numInstances() { return libpd_num_instances(); } protected: /// compound message status enum MsgType { MSG, MIDI, SYSEX, SYSRT }; /// \section Variables bool bMsgInProgress; ///< is a compound message being constructed? int maxMsgLen; ///< maximum allowed message length int curMsgLen; ///< the length of the current message /// compound message status PdBase::MsgType msgType; int midiPort; ///< target midi port std::map sources; ///< subscribed sources pd::PdReceiver *receiver; ///< the message receiver pd::PdMidiReceiver *midiReceiver; ///< the midi receiver #ifdef PDINSTANCE t_pdinstance *instance; ///< instance pointer #endif protected: bool bInited; ///< is this pd instance inited? bool bQueued; ///< is this instance using the libpd_queued ringbuffer? // libpd static callback functions static void _print(const char *s) { PdBase *base = (PdBase *)libpd_get_instancedata(); if(base->receiver) { base->receiver->print((std::string)s); } } static void _bang(const char *source) { PdBase *base = (PdBase *)libpd_get_instancedata(); if(base->receiver) { base->receiver->receiveBang((std::string)source); } } static void _float(const char *source, float value) { PdBase *base = (PdBase *)libpd_get_instancedata(); if(base->receiver) { base->receiver->receiveFloat((std::string)source, value); } } static void _symbol(const char *source, const char *symbol) { PdBase *base = (PdBase *)libpd_get_instancedata(); if(base->receiver) { base->receiver->receiveSymbol((std::string)source, (std::string)symbol); } } static void _list(const char *source, int argc, t_atom *argv) { PdBase *base = (PdBase *)libpd_get_instancedata(); pd::List list; for(int i = 0; i < argc; i++) { t_atom a = argv[i]; if(a.a_type == A_FLOAT) { float f = a.a_w.w_float; list.addFloat(f); } else if(a.a_type == A_SYMBOL) { const char *s = a.a_w.w_symbol->s_name; list.addSymbol((std::string)s); } } if(base->receiver) { base->receiver->receiveList((std::string)source, list); } } static void _message(const char *source, const char *symbol, int argc, t_atom *argv) { PdBase *base = (PdBase *)libpd_get_instancedata(); pd::List list; for(int i = 0; i < argc; i++) { t_atom a = argv[i]; if(a.a_type == A_FLOAT) { float f = a.a_w.w_float; list.addFloat(f); } else if(a.a_type == A_SYMBOL) { const char *s = a.a_w.w_symbol->s_name; list.addSymbol((std::string)s); } } if(base->receiver) { base->receiver->receiveMessage((std::string)source, (std::string)symbol, list); } } static void _noteon(int channel, int pitch, int velocity) { PdBase *base = (PdBase *)libpd_get_instancedata(); if(base->midiReceiver) { base->midiReceiver->receiveNoteOn(channel, pitch, velocity); } } static void _controlchange(int channel, int controller, int value) { PdBase *base = (PdBase *)libpd_get_instancedata(); if(base->midiReceiver) { base->midiReceiver->receiveControlChange(channel, controller, value); } } static void _programchange(int channel, int value) { PdBase *base = (PdBase *)libpd_get_instancedata(); if(base->midiReceiver) { base->midiReceiver->receiveProgramChange(channel, value); } } static void _pitchbend(int channel, int value) { PdBase *base = (PdBase *)libpd_get_instancedata(); if(base->midiReceiver) { base->midiReceiver->receivePitchBend(channel, value); } } static void _aftertouch(int channel, int value) { PdBase *base = (PdBase *)libpd_get_instancedata(); if(base->midiReceiver) { base->midiReceiver->receiveAftertouch(channel, value); } } static void _polyaftertouch(int channel, int pitch, int value) { PdBase *base = (PdBase *)libpd_get_instancedata(); if(base->midiReceiver) { base->midiReceiver->receivePolyAftertouch(channel, pitch, value); } } static void _midibyte(int port, int byte) { PdBase *base = (PdBase *)libpd_get_instancedata(); if(base->midiReceiver) { base->midiReceiver->receiveMidiByte(port, byte); } } }; } // namespace ================================================ FILE: libs/libpd/cpp/PdMidiReceiver.hpp ================================================ /* * Copyright (c) 2012-2022 Dan Wilcox * * BSD Simplified License. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. * * See https://github.com/libpd/libpd for documentation * * This file was originally written for the ofxPd openFrameworks addon: * https://github.com/danomatika/ofxPd * */ #pragma once namespace pd { /// a pd MIDI receiver base class class PdMidiReceiver { public: virtual ~PdMidiReceiver() {} /// receive a MIDI note on virtual void receiveNoteOn(const int /*channel*/, const int /*pitch*/, const int /*velocity*/) {} /// receive a MIDI control change virtual void receiveControlChange(const int /*channel*/, const int /*controller*/, const int /*value*/) {} /// receive a MIDI program change, /// note: pgm value is 1-128 virtual void receiveProgramChange(const int /*channel*/, const int /*value*/) {} /// receive a MIDI pitch bend virtual void receivePitchBend(const int /*channel*/, const int /*value*/) {} /// receive a MIDI aftertouch message virtual void receiveAftertouch(const int /*channel*/, const int /*value*/) {} /// receive a MIDI poly aftertouch message virtual void receivePolyAftertouch(const int /*channel*/, const int /*pitch*/, const int /*value*/) {} /// receive a raw MIDI byte (sysex, realtime, etc) virtual void receiveMidiByte(const int /*port*/, const int /*byte*/) {} }; } // namespace ================================================ FILE: libs/libpd/cpp/PdReceiver.hpp ================================================ /* * Copyright (c) 2012-2022 Dan Wilcox * * BSD Simplified License. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. * * See https://github.com/libpd/libpd for documentation * * This file was originally written for the ofxPd openFrameworks addon: * https://github.com/danomatika/ofxPd * */ #pragma once #include "PdTypes.hpp" namespace pd { /// a pd message receiver base class class PdReceiver { public: virtual ~PdReceiver() {} /// receive a print virtual void print(const std::string &/*message*/) {} /// receive a bang virtual void receiveBang(const std::string &/*dest*/) {} /// receive a float virtual void receiveFloat(const std::string &/*dest*/, float /*num*/) {} /// receive a symbol virtual void receiveSymbol(const std::string &/*dest*/, const std::string &/*symbol*/) {} /// receive a list virtual void receiveList(const std::string &/*dest*/, const pd::List &/*list*/) {} /// receive a named message ie. sent from a message box like: /// [; dest msg arg1 arg2 arg3( virtual void receiveMessage(const std::string &/*dest*/, const std::string &/*msg*/, const pd::List &/*list*/) {} }; } // namespace ================================================ FILE: libs/libpd/cpp/PdTypes.hpp ================================================ /* * Copyright (c) 2012-2022 Dan Wilcox * * BSD Simplified License. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. * * See https://github.com/libpd/libpd for documentation * * This file was originally written for the ofxPd openFrameworks addon: * https://github.com/danomatika/ofxPd * */ #pragma once #include #include #include #include namespace pd { /// \section Pd Patch /// a pd patch /// /// if you use the copy constructor/operator, keep in mind the libpd void* /// pointer patch handle is copied and problems can arise if one object is used /// to close a patch that other copies may be referring to class Patch { public: Patch() : _handle(NULL), _dollarZero(0), _dollarZeroStr("0"), _filename(""), _path("") {} Patch(const std::string &filename, const std::string &path) : _handle(NULL), _dollarZero(0), _dollarZeroStr("0"), _filename(filename), _path(path) {} Patch(void *handle, int dollarZero, const std::string &filename, const std::string &path) : _handle(handle), _dollarZero(dollarZero), _filename(filename), _path(path) { std::stringstream itoa; itoa << dollarZero; _dollarZeroStr = itoa.str(); } /// get the raw pointer to the patch instance void* handle() const {return _handle;} /// get the unqiue instance $0 ID int dollarZero() const {return _dollarZero;} /// get the patch filename std::string filename() const {return _filename;} /// get the parent dir path for the file std::string path() const {return _path;} /// get the unique instance $0 ID as a string std::string dollarZeroStr() const {return _dollarZeroStr;} /// is the patch pointer valid? bool isValid() const {return _handle != NULL;} /// clear patch pointer and dollar zero (does not close patch!) /// /// note: does not clear filename and path so the object can be reused // for opening multiple instances void clear() { _handle = NULL; _dollarZero = 0; _dollarZeroStr = "0"; } /// copy constructor Patch(const Patch &from) { _handle = from._handle; _dollarZero = from._dollarZero; _dollarZeroStr = from._dollarZeroStr; _filename = from._filename; _path = from._path; } /// copy operator void operator=(const Patch &from) { _handle = from._handle; _dollarZero = from._dollarZero; _dollarZeroStr = from._dollarZeroStr; _filename = from._filename; _path = from._path; } /// print info to ostream friend std::ostream& operator<<(std::ostream &os, const Patch &from) { return os << "Patch: \"" << from.filename() << "\" $0: " << from.dollarZeroStr() << " valid: " << from.isValid(); } private: void *_handle; ///< patch handle pointer int _dollarZero; ///< the unique $0 patch ID std::string _dollarZeroStr; ///< $0 as a string std::string _filename; ///< filename std::string _path; ///< full path to parent folder }; /// \section Pd stream interface message objects /// bang event struct Bang { const std::string dest; ///< dest receiver name explicit Bang(const std::string &dest) : dest(dest) {} }; /// float value struct Float { const std::string dest; ///< dest receiver name const float num; ///< the float value Float(const std::string &dest, const float num) : dest(dest), num(num) {} }; /// symbol value struct Symbol { const std::string dest; ///< dest receiver name const std::string symbol; ///< the symbol value Symbol(const std::string &dest, const std::string &symbol) : dest(dest), symbol(symbol) {} }; /// a compound message containing floats and symbols class List { public: List() {} /// \section Read /// check if index is a float type bool isFloat(const unsigned int index) const { if(index < objects.size()) if(objects[index].type == List::FLOAT) return true; return false; } /// check if index is a symbol type bool isSymbol(const unsigned int index) const { if(index < objects.size()) if(objects[index].type == List::SYMBOL) return true; return false; } /// get index as a float float getFloat(const unsigned int index) const { if(!isFloat(index)) { std::cerr << "Pd: List object " << index << " is not a float" << std::endl; return 0; } return objects[index].value; } /// get index as a symbol std::string getSymbol(const unsigned int index) const { if(!isSymbol(index)) { std::cerr << "Pd: List object " << index << " is not a symbol" << std::endl; return ""; } return objects[index].symbol; } /// \section Write /// /// add elements to the list /// /// List list; /// list.addSymbol("hello"); /// list.addFloat(1.23); /// /// add a float to the list void addFloat(const float num) { MsgObject o; o.type = List::FLOAT; o.value = num; objects.push_back(o); typeString += 'f'; } /// add a symbol to the list void addSymbol(const std::string &symbol) { MsgObject o; o.type = List::SYMBOL; o.symbol = symbol; objects.push_back(o); typeString += 's'; } /// \section Write Stream Interface /// /// list << "hello" << 1.23; /// /// add a float to the message List& operator<<(const bool var) { addFloat((float) var); return *this; } /// add a float to the message List& operator<<(const int var) { addFloat((float) var); return *this; } /// add a float to the message List& operator<<(const float var) { addFloat((float) var); return *this; } /// add a float to the message List& operator<<(const double var) { addFloat((float) var); return *this; } /// add a symbol to the message List& operator<<(const char var) { std::string s; s = var; addSymbol(s); return *this; } /// add a symbol to the message List& operator<<(const char *var) { addSymbol((std::string) var); return *this; } /// add a symbol to the message List& operator<<(const std::string &var) { addSymbol((std::string) var); return *this; } /// \section Util /// return number of items unsigned int len() const {return (unsigned int) objects.size();} /// return OSC style type string ie "fsfs" const std::string& types() const {return typeString;} /// clear all objects void clear() { typeString = ""; objects.clear(); } /// get list as a string std::string toString() const { std::string line; std::stringstream itoa; for(int i = 0; i < (int)objects.size(); ++i) { if(isFloat(i)) { itoa << getFloat(i); line += itoa.str(); itoa.str(""); } else { line += getSymbol(i); } if(i < (int)objects.size()-1) { line += " "; } } return line; } /// print to ostream friend std::ostream& operator<<(std::ostream &os, const List &from) { return os << from.toString(); } private: std::string typeString; ///< OSC style type string // object type enum MsgType { FLOAT, SYMBOL }; // object wrapper struct MsgObject { MsgType type; float value; std::string symbol; }; std::vector objects; ///< list objects }; /// start a compound message struct StartMessage { explicit StartMessage() {} }; /// finish a compound message as a list struct FinishList { const std::string dest; ///< dest receiver name explicit FinishList(const std::string &dest) : dest(dest) {} }; /// finish a compound message as a typed message struct FinishMessage { const std::string dest; ///< dest receiver name const std::string msg; ///< target msg at the dest FinishMessage(const std::string &dest, const std::string &msg) : dest(dest), msg(msg) {} }; /// \section Pd stream interface midi objects /// ref: http://www.gweep.net/~prefect/eng/reference/protocol/midispec.html /// send a note on event (set vel = 0 for noteoff) struct NoteOn { const int channel; ///< channel (0 - 15 * dev#) const int pitch; ///< pitch (0 - 127) const int velocity; ///< velocity (0 - 127) NoteOn(const int channel, const int pitch, const int velocity=64) : channel(channel), pitch(pitch), velocity(velocity) {} }; /// change a control value aka send a CC message struct ControlChange { const int channel; ///< channel (0 - 15 * dev#) const int controller; ///< controller (0 - 127) const int value; ///< value (0 - 127) ControlChange(const int channel, const int controller, const int value) : channel(channel), controller(controller), value(value) {} }; /// change a program value (ie an instrument) struct ProgramChange { const int channel; ///< channel (0 - 15 * dev#) const int value; ///< value (0 - 127) ProgramChange(const int channel, const int value) : channel(channel), value(value) {} }; /// change the pitch bend value struct PitchBend { const int channel; ///< channel (0 - 15 * dev#) const int value; ///< value (-8192 - 8192) PitchBend(const int channel, const int value) : channel(channel), value(value) {} }; /// change an aftertouch value struct Aftertouch { const int channel; ///< channel (0 - 15 * dev#) const int value; ///< value (0 - 127) Aftertouch(const int channel, const int value) : channel(channel), value(value) {} }; /// change a poly aftertouch value struct PolyAftertouch { const int channel; ///< channel (0 - 15 * dev#) const int pitch; ///< pitch (0 - 127) const int value; ///< value (0 - 127) PolyAftertouch(const int channel, const int pitch, const int value) : channel(channel), pitch(pitch), value(value) {} }; /// a raw midi byte struct MidiByte { const int port; ///< raw portmidi port ///< see http://en.wikipedia.org/wiki/PortMidi const unsigned char byte; ///< the raw midi byte value MidiByte(const int port, unsigned char byte) : port(port), byte(byte) {} }; /// start a raw midi byte stream struct StartMidi { const int port; ///< raw portmidi port explicit StartMidi(const int port=0) : port(port) {} }; /// start a raw sysex byte stream struct StartSysex { const int port; ///< raw portmidi port explicit StartSysex(const int port=0) : port(port) {} }; /// start a sys realtime byte stream struct StartSysRealTime { const int port; ///< raw portmidi port explicit StartSysRealTime(const int port=0) : port(port) {} }; /// finish a midi byte stream struct Finish { explicit Finish() {} }; } // namespace ================================================ FILE: libs/libpd/libpd_wrapper/s_libpdmidi.c ================================================ /* Copyright (c) 1997-2010 Miller Puckette and others. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ #include "m_pd.h" #include "z_libpd.h" #include "z_hooks.h" #define CLAMP(x, low, high) ((x > high) ? high : ((x < low) ? low : x)) #define CLAMP4BIT(x) CLAMP(x, 0, 0x0f) #define CLAMP7BIT(x) CLAMP(x, 0, 0x7f) #define CLAMP8BIT(x) CLAMP(x, 0, 0xff) #define CLAMP12BIT(x) CLAMP(x, 0, 0x0fff) #define CLAMP14BIT(x) CLAMP(x, 0, 0x3fff) #define CHANNEL ((CLAMP12BIT(port) << 4) | CLAMP4BIT(channel)) void outmidi_noteon(int port, int channel, int pitch, int velo) { t_libpdimp *imp = LIBPDSTUFF; if (imp && imp->i_hooks.h_noteonhook) imp->i_hooks.h_noteonhook(CHANNEL, CLAMP7BIT(pitch), CLAMP7BIT(velo)); } void outmidi_controlchange(int port, int channel, int ctl, int value) { t_libpdimp *imp = LIBPDSTUFF; if (imp && imp->i_hooks.h_controlchangehook) imp->i_hooks.h_controlchangehook(CHANNEL, CLAMP7BIT(ctl), CLAMP7BIT(value)); } void outmidi_programchange(int port, int channel, int value) { t_libpdimp *imp = LIBPDSTUFF; if (imp && imp->i_hooks.h_programchangehook) imp->i_hooks.h_programchangehook(CHANNEL, CLAMP7BIT(value)); } void outmidi_pitchbend(int port, int channel, int value) { t_libpdimp *imp = LIBPDSTUFF; if (imp && imp->i_hooks.h_pitchbendhook) imp->i_hooks.h_pitchbendhook(CHANNEL, CLAMP14BIT(value) - 8192); // remove offset } void outmidi_aftertouch(int port, int channel, int value) { t_libpdimp *imp = LIBPDSTUFF; if (imp && imp->i_hooks.h_aftertouchhook) imp->i_hooks.h_aftertouchhook(CHANNEL, CLAMP7BIT(value)); } void outmidi_polyaftertouch(int port, int channel, int pitch, int value) { t_libpdimp *imp = LIBPDSTUFF; if (imp && imp->i_hooks.h_polyaftertouchhook) imp->i_hooks.h_polyaftertouchhook(CHANNEL, CLAMP7BIT(pitch), CLAMP7BIT(value)); } void outmidi_byte(int port, int value) { t_libpdimp *imp = LIBPDSTUFF; if (imp && imp->i_hooks.h_midibytehook) imp->i_hooks.h_midibytehook(CLAMP12BIT(port), CLAMP8BIT(value)); } /* tell Pd GUI that our list of MIDI APIs is empty */ #include void sys_get_midi_apis(char *buf) {strcpy(buf, "{}");} // the rest is not relevant to libpd void sys_listmididevs(void) {} void sys_get_midi_params(int *pnmidiindev, int *pmidiindev, int *pnmidioutdev, int *pmidioutdev) {*pnmidiindev = *pnmidioutdev = 0;} void sys_open_midi(int nmidiindev, int *midiindev, int nmidioutdev, int *midioutdev, int enable) {} void sys_close_midi() {} void sys_reopen_midi(void) {} void sys_initmidiqueue(void) {} void sys_pollmidiqueue(void) {} void sys_setmiditimediff(double inbuftime, double outbuftime) {} void glob_midi_setapi(void *dummy, t_floatarg f) {} void glob_midi_properties(t_pd *dummy, t_floatarg flongform) {} void glob_midi_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv) {} int sys_mididevnametonumber(int output, const char *name) { return 0; } void sys_mididevnumbertoname(int output, int devno, char *name, int namesize) {} void sys_set_midi_api(int api) {} void sys_gui_midipreferences(void) {} int sys_midiapi; ================================================ FILE: libs/libpd/libpd_wrapper/util/ringbuffer.c ================================================ /* * Copyright (c) 2012 Peter Brinkmann (peter.brinkmann@gmail.com) * * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. * * See https://github.com/libpd/libpd/wiki for documentation * */ #include "ringbuffer.h" #include #include #include #if __STDC_VERSION__ >= 201112L // use stdatomic if C11 is available #include #define SYNC_FETCH(ptr) atomic_fetch_or((_Atomic int *)ptr, 0) #define SYNC_COMPARE_AND_SWAP(ptr, oldval, newval) \ atomic_compare_exchange_strong((_Atomic int *)ptr, &oldval, newval) #else // use platform specfics #ifdef __APPLE__ // apple atomics #include #define SYNC_FETCH(ptr) OSAtomicOr32Barrier(0, (volatile uint32_t *)ptr) #define SYNC_COMPARE_AND_SWAP(ptr, oldval, newval) \ OSAtomicCompareAndSwap32Barrier(oldval, newval, ptr) #elif defined(_WIN32) || defined(_WIN64) // win api atomics #include #define SYNC_FETCH(ptr) InterlockedOr(ptr, 0) #define SYNC_COMPARE_AND_SWAP(ptr, oldval, newval) \ InterlockedCompareExchange(ptr, newval, oldval) #else // gcc atomics #define SYNC_FETCH(ptr) __sync_fetch_and_or(ptr, 0) #define SYNC_COMPARE_AND_SWAP(ptr, oldval, newval) \ __sync_val_compare_and_swap(ptr, oldval, newval) #endif #endif ring_buffer *rb_create(int size) { if (size & 0xff) return NULL; // size must be a multiple of 256 ring_buffer *buffer = malloc(sizeof(ring_buffer)); if (!buffer) return NULL; buffer->buf_ptr = calloc(size, sizeof(char)); if (!buffer->buf_ptr) { free(buffer); return NULL; } buffer->size = size; buffer->write_idx = 0; buffer->read_idx = 0; return buffer; } void rb_free(ring_buffer *buffer) { free(buffer->buf_ptr); free(buffer); } int rb_available_to_write(ring_buffer *buffer) { if (buffer) { // note: the largest possible result is buffer->size - 1 because // we adopt the convention that read_idx == write_idx means that the // buffer is empty int read_idx = SYNC_FETCH(&(buffer->read_idx)); int write_idx = SYNC_FETCH(&(buffer->write_idx)); return (buffer->size + read_idx - write_idx - 1) % buffer->size; } else { return 0; } } int rb_available_to_read(ring_buffer *buffer) { if (buffer) { int read_idx = SYNC_FETCH(&(buffer->read_idx)); int write_idx = SYNC_FETCH(&(buffer->write_idx)); return (buffer->size + write_idx - read_idx) % buffer->size; } else { return 0; } } int rb_write_to_buffer(ring_buffer *buffer, int n, ...) { if (!buffer) return -1; int write_idx = buffer->write_idx; // no need for sync in writer thread int available = rb_available_to_write(buffer); va_list args; va_start(args, n); int i; for (i = 0; i < n; ++i) { const char* src = va_arg(args, const char*); int len = va_arg(args, int); available -= len; if (len < 0 || available < 0) return -1; if (write_idx + len <= buffer->size) { memcpy(buffer->buf_ptr + write_idx, src, len); } else { int d = buffer->size - write_idx; memcpy(buffer->buf_ptr + write_idx, src, d); memcpy(buffer->buf_ptr, src + d, len - d); } write_idx = (write_idx + len) % buffer->size; } va_end(args); SYNC_COMPARE_AND_SWAP(&(buffer->write_idx), buffer->write_idx, write_idx); // includes memory barrier return 0; } int rb_write_value_to_buffer(ring_buffer *buffer, int value, int n) { if (!buffer) return -1; int write_idx = buffer->write_idx; // No need for sync in writer thread. int available = rb_available_to_write(buffer); available -= n; if (n < 0 || available < 0) return -1; if (write_idx + n <= buffer->size) { memset(buffer->buf_ptr + write_idx, value, n); } else { int d = buffer->size - write_idx; memset(buffer->buf_ptr + write_idx, value, d); memset(buffer->buf_ptr, value, n - d); } write_idx = (write_idx + n) % buffer->size; SYNC_COMPARE_AND_SWAP(&(buffer->write_idx), buffer->write_idx, write_idx); // includes memory barrier return 0; } int rb_read_from_buffer(ring_buffer *buffer, char *dest, int len) { if (len == 0) return 0; if (!buffer || len < 0 || len > rb_available_to_read(buffer)) return -1; // note that rb_available_to_read also serves as a memory barrier, and so any // writes to buffer->buf_ptr that precede the update of buffer->write_idx are // visible to us now int read_idx = buffer->read_idx; // no need for sync in reader thread if (read_idx + len <= buffer->size) { memcpy(dest, buffer->buf_ptr + read_idx, len); } else { int d = buffer->size - read_idx; memcpy(dest, buffer->buf_ptr + read_idx, d); memcpy(dest + d, buffer->buf_ptr, len - d); } SYNC_COMPARE_AND_SWAP(&(buffer->read_idx), buffer->read_idx, (read_idx + len) % buffer->size); // includes memory barrier return 0; } // simply reset the indices void rb_clear_buffer(ring_buffer *buffer) { if (buffer) { SYNC_COMPARE_AND_SWAP(&(buffer->read_idx), buffer->read_idx, 0); SYNC_COMPARE_AND_SWAP(&(buffer->write_idx), buffer->write_idx, 0); } } ================================================ FILE: libs/libpd/libpd_wrapper/util/ringbuffer.h ================================================ /* * Copyright (c) 2012 Peter Brinkmann (peter.brinkmann@gmail.com) * * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. * * See https://github.com/libpd/libpd/wiki for documentation * */ #ifndef __Z_RING_BUFFER_H__ #define __Z_RING_BUFFER_H__ /// simple lock-free ring buffer implementation for one writer thread /// and one consumer thread typedef struct ring_buffer { int size; char *buf_ptr; int write_idx; int read_idx; } ring_buffer; /// create a ring buffer, size must be a multiple of 256 /// returns NULL on failure ring_buffer *rb_create(int size); /// free a ring buffer void rb_free(ring_buffer *buffer); /// get the number of bytes that can currently be written /// this is safe to call from any thread int rb_available_to_write(ring_buffer *buffer); /// get the number of bytes that can currently be read /// this is safe to called from any thread int rb_available_to_read(ring_buffer *buffer); /// write bytes from n sources to the ring buffer (if the ring buffer has /// enough space), varargs are pairs of type (const char*, int) giving a pointer /// to a buffer and the number of bytes to be copied /// note: call this from a single writer thread only /// returns 0 on success int rb_write_to_buffer(ring_buffer *buffer, int n, ...); /// writes single byte value n times to the ring buffer (if the ring buffer has /// enough space) /// note: call this from a single writer thread only /// returns 0 on success int rb_write_value_to_buffer(ring_buffer *buffer, int value, int n); /// read given number of bytes from the ring buffer to dest (if the ring /// buffer has enough data) /// note: call this from a single reader thread only /// returns 0 on success int rb_read_from_buffer(ring_buffer *buffer, char *dest, int len); /// clears the contents of the ring buffer /// this is safe to call from any thread void rb_clear_buffer(ring_buffer *buffer); #endif ================================================ FILE: libs/libpd/libpd_wrapper/util/z_print_util.c ================================================ /* * Copyright (c) 2013 Dan Wilcox (danomatika@gmail.com) & * Peter Brinkmann (peter.brinkmann@gmail.com) * Copyright (c) 2022 libpd team * * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. * * See https://github.com/libpd/libpd/wiki for documentation * */ #include "z_print_util.h" #include #include #include "../z_hooks.h" #define PRINT_LINE_SIZE 2048 typedef struct _print_util { t_libpd_printhook concat_printhook; char concat_buf[PRINT_LINE_SIZE]; /* line buffer */ int concat_len; /* current line len */ } print_util; void libpd_set_concatenated_printhook(const t_libpd_printhook hook) { t_libpdimp *imp = LIBPDSTUFF; if (hook) { if (!imp->i_print_util) { imp->i_print_util = calloc(1, sizeof(print_util)); } ((print_util *)imp->i_print_util)->concat_printhook = hook; } else { if (imp->i_print_util) { free(imp->i_print_util); imp->i_print_util = NULL; } } } void libpd_print_concatenator(const char *s) { print_util *util = (print_util *)LIBPDSTUFF->i_print_util; if (!util) return; util->concat_buf[util->concat_len] = '\0'; int len = (int)strlen(s); while (util->concat_len + len >= PRINT_LINE_SIZE) { int d = PRINT_LINE_SIZE - 1 - util->concat_len; strncat(util->concat_buf, s, d); util->concat_printhook(util->concat_buf); s += d; len -= d; util->concat_len = 0; util->concat_buf[0] = '\0'; } strncat(util->concat_buf, s, len); util->concat_len += len; if (util->concat_len > 0 && util->concat_buf[util->concat_len - 1] == '\n') { util->concat_buf[util->concat_len - 1] = '\0'; util->concat_printhook(util->concat_buf); util->concat_len = 0; } } ================================================ FILE: libs/libpd/libpd_wrapper/util/z_print_util.h ================================================ /* * Copyright (c) 2013 Dan Wilcox (danomatika@gmail.com) & * Peter Brinkmann (peter.brinkmann@gmail.com) * Copyright (c) 2022 libpd team * * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. * * See https://github.com/libpd/libpd/wiki for documentation * */ #ifndef __Z_PRINT_UTIL_H__ #define __Z_PRINT_UTIL_H__ #include "z_libpd.h" #ifdef __cplusplus extern "C" { #endif /// assign the pointer to your print line handler /// concatenates print messages into single lines before returning them to the /// print hook: /// ex: line "hello 123\n" is received in 1 part -> "hello 123" /// for comparison, the default behavior may receive messages in chunks: /// ex: line "hello 123" could be sent in 3 parts -> "hello", " ", "123\n" /// call with NULL pointer to free internal buffer /// note: do not call before libpd_init() EXTERN void libpd_set_concatenated_printhook(const t_libpd_printhook hook); /// assign this function pointer to libpd_printhook or libpd_queued_printhook, /// depending on whether you're using queued messages, to intercept and /// concatenate print messages: /// libpd_set_printhook(libpd_print_concatenator); /// libpd_set_concatenated_printhook(your_print_handler); /// note: the char pointer argument is only good for the duration of the print /// callback; if you intend to use the argument after the callback has /// returned, you need to make a defensive copy EXTERN void libpd_print_concatenator(const char *s); #ifdef __cplusplus } #endif #endif ================================================ FILE: libs/libpd/libpd_wrapper/util/z_queued.c ================================================ /* * Copyright (c) 2012 Peter Brinkmann (peter.brinkmann@gmail.com) * Copyright (c) 2022 libpd team * * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. * * See https://github.com/libpd/libpd/wiki for documentation * */ #include "z_queued.h" #include #include #include "../z_hooks.h" #include "ringbuffer.h" #define BUFFER_SIZE 16384 typedef struct _queued_stuff { t_libpdhooks hooks; t_libpd_printhook printhook; ring_buffer *pd_receive_buffer; ring_buffer *midi_receive_buffer; char temp_buffer[BUFFER_SIZE]; } queued_stuff; #define QUEUEDSTUFF ((queued_stuff *)(LIBPDSTUFF->i_queued)) typedef struct _pd_params { enum { LIBPD_PRINT, LIBPD_BANG, LIBPD_FLOAT, LIBPD_SYMBOL, LIBPD_LIST, LIBPD_MESSAGE, } type; const char *src; t_float x; const char *sym; int argc; } pd_params; typedef struct _midi_params { enum { LIBPD_NOTEON, LIBPD_CONTROLCHANGE, LIBPD_PROGRAMCHANGE, LIBPD_PITCHBEND, LIBPD_AFTERTOUCH, LIBPD_POLYAFTERTOUCH, LIBPD_MIDIBYTE } type; int midi1; int midi2; int midi3; } midi_params; #define S_PD_PARAMS sizeof(pd_params) #define S_MIDI_PARAMS sizeof(midi_params) #define S_ATOM sizeof(t_atom) static void receive_print(pd_params *p, char **buffer) { if (QUEUEDSTUFF->printhook) { QUEUEDSTUFF->printhook(*buffer); } *buffer += p->argc; } static void receive_bang(pd_params *p, char **buffer) { if (QUEUEDSTUFF->hooks.h_banghook) { QUEUEDSTUFF->hooks.h_banghook(p->src); } } static void receive_float(pd_params *p, char **buffer) { if (QUEUEDSTUFF->hooks.h_floathook) { QUEUEDSTUFF->hooks.h_floathook(p->src, (float)p->x); } else if (QUEUEDSTUFF->hooks.h_doublehook) { QUEUEDSTUFF->hooks.h_doublehook(p->src, (double)p->x); } } static void receive_symbol(pd_params *p, char **buffer) { if (QUEUEDSTUFF->hooks.h_symbolhook) { QUEUEDSTUFF->hooks.h_symbolhook(p->src, p->sym); } } static void receive_list(pd_params *p, char **buffer) { if (QUEUEDSTUFF->hooks.h_listhook) { QUEUEDSTUFF->hooks.h_listhook(p->src, p->argc, (t_atom *) *buffer); } *buffer += p->argc * S_ATOM; } static void receive_message(pd_params *p, char **buffer) { if (QUEUEDSTUFF->hooks.h_messagehook) { QUEUEDSTUFF->hooks.h_messagehook(p->src, p->sym, p->argc, (t_atom *) *buffer); } *buffer += p->argc * S_ATOM; } #define LIBPD_WORD_ALIGN 8 static void internal_printhook(const char *s) { static char padding[LIBPD_WORD_ALIGN]; queued_stuff *queued = QUEUEDSTUFF; int len = (int) strlen(s) + 1; // remember terminating null char int rest = len % LIBPD_WORD_ALIGN; if (rest) rest = LIBPD_WORD_ALIGN - rest; int total = len + rest; if (rb_available_to_write(queued->pd_receive_buffer) >= S_PD_PARAMS + total) { pd_params p = {LIBPD_PRINT, NULL, 0.0f, NULL, total}; rb_write_to_buffer(queued->pd_receive_buffer, 3, (const char *)&p, S_PD_PARAMS, s, len, padding, rest); } } static void internal_banghook(const char *src) { queued_stuff *queued = QUEUEDSTUFF; if (rb_available_to_write(queued->pd_receive_buffer) >= S_PD_PARAMS) { pd_params p = {LIBPD_BANG, src, 0.0f, NULL, 0}; rb_write_to_buffer(queued->pd_receive_buffer, 1, (const char *)&p, S_PD_PARAMS); } } static void internal_floathook(const char *src, float x) { queued_stuff *queued = QUEUEDSTUFF; if (rb_available_to_write(queued->pd_receive_buffer) >= S_PD_PARAMS) { pd_params p = {LIBPD_FLOAT, src, x, NULL, 0}; rb_write_to_buffer(queued->pd_receive_buffer, 1, (const char *)&p, S_PD_PARAMS); } } static void internal_doublehook(const char *src, double x) { queued_stuff *queued = QUEUEDSTUFF; if (rb_available_to_write(queued->pd_receive_buffer) >= S_PD_PARAMS) { pd_params p = {LIBPD_FLOAT, src, (t_float)x, NULL, 0}; rb_write_to_buffer(queued->pd_receive_buffer, 1, (const char *)&p, S_PD_PARAMS); } } static void internal_symbolhook(const char *src, const char *sym) { queued_stuff *queued = QUEUEDSTUFF; if (rb_available_to_write(queued->pd_receive_buffer) >= S_PD_PARAMS) { pd_params p = {LIBPD_SYMBOL, src, 0.0f, sym, 0}; rb_write_to_buffer(queued->pd_receive_buffer, 1, (const char *)&p, S_PD_PARAMS); } } static void internal_listhook(const char *src, int argc, t_atom *argv) { queued_stuff *queued = QUEUEDSTUFF; int n = argc * S_ATOM; if (rb_available_to_write(queued->pd_receive_buffer) >= S_PD_PARAMS + n) { pd_params p = {LIBPD_LIST, src, 0.0f, NULL, argc}; rb_write_to_buffer(queued->pd_receive_buffer, 2, (const char *)&p, S_PD_PARAMS, (const char *)argv, n); } } static void internal_messagehook(const char *src, const char* sym, int argc, t_atom *argv) { queued_stuff *queued = QUEUEDSTUFF; int n = argc * S_ATOM; if (rb_available_to_write(queued->pd_receive_buffer) >= S_PD_PARAMS + n) { pd_params p = {LIBPD_MESSAGE, src, 0.0f, sym, argc}; rb_write_to_buffer(queued->pd_receive_buffer, 2, (const char *)&p, S_PD_PARAMS, (const char *)argv, n); } } static void receive_noteon(midi_params *p, char **buffer) { if (QUEUEDSTUFF->hooks.h_noteonhook) { QUEUEDSTUFF->hooks.h_noteonhook(p->midi1, p->midi2, p->midi3); } } static void receive_controlchange(midi_params *p, char **buffer) { if (QUEUEDSTUFF->hooks.h_controlchangehook) { QUEUEDSTUFF->hooks.h_controlchangehook(p->midi1, p->midi2, p->midi3); } } static void receive_programchange(midi_params *p, char **buffer) { if (QUEUEDSTUFF->hooks.h_programchangehook) { QUEUEDSTUFF->hooks.h_programchangehook(p->midi1, p->midi2); } } static void receive_pitchbend(midi_params *p, char **buffer) { if (QUEUEDSTUFF->hooks.h_pitchbendhook) { QUEUEDSTUFF->hooks.h_pitchbendhook(p->midi1, p->midi2); } } static void receive_aftertouch(midi_params *p, char **buffer) { if (QUEUEDSTUFF->hooks.h_aftertouchhook) { QUEUEDSTUFF->hooks.h_aftertouchhook(p->midi1, p->midi2); } } static void receive_polyaftertouch(midi_params *p, char **buffer) { if (QUEUEDSTUFF->hooks.h_polyaftertouchhook) { QUEUEDSTUFF->hooks.h_polyaftertouchhook(p->midi1, p->midi2, p->midi3); } } static void receive_midibyte(midi_params *p, char **buffer) { if (QUEUEDSTUFF->hooks.h_midibytehook) { QUEUEDSTUFF->hooks.h_midibytehook(p->midi1, p->midi2); } } static void internal_noteonhook(int channel, int pitch, int velocity) { queued_stuff *queued = QUEUEDSTUFF; if (rb_available_to_write(queued->midi_receive_buffer) >= S_MIDI_PARAMS) { midi_params p = {LIBPD_NOTEON, channel, pitch, velocity}; rb_write_to_buffer(queued->midi_receive_buffer, 1, (const char *)&p, S_MIDI_PARAMS); } } static void internal_controlchangehook(int channel, int controller, int value) { queued_stuff *queued = QUEUEDSTUFF; if (rb_available_to_write(queued->midi_receive_buffer) >= S_MIDI_PARAMS) { midi_params p = {LIBPD_CONTROLCHANGE, channel, controller, value}; rb_write_to_buffer(queued->midi_receive_buffer, 1, (const char *)&p, S_MIDI_PARAMS); } } static void internal_programchangehook(int channel, int value) { queued_stuff *queued = QUEUEDSTUFF; if (rb_available_to_write(queued->midi_receive_buffer) >= S_MIDI_PARAMS) { midi_params p = {LIBPD_PROGRAMCHANGE, channel, value, 0}; rb_write_to_buffer(queued->midi_receive_buffer, 1, (const char *)&p, S_MIDI_PARAMS); } } static void internal_pitchbendhook(int channel, int value) { queued_stuff *queued = QUEUEDSTUFF; if (rb_available_to_write(queued->midi_receive_buffer) >= S_MIDI_PARAMS) { midi_params p = {LIBPD_PITCHBEND, channel, value, 0}; rb_write_to_buffer(queued->midi_receive_buffer, 1, (const char *)&p, S_MIDI_PARAMS); } } static void internal_aftertouchhook(int channel, int value) { queued_stuff *queued = QUEUEDSTUFF; if (rb_available_to_write(queued->midi_receive_buffer) >= S_MIDI_PARAMS) { midi_params p = {LIBPD_AFTERTOUCH, channel, value, 0}; rb_write_to_buffer(queued->midi_receive_buffer, 1, (const char *)&p, S_MIDI_PARAMS); } } static void internal_polyaftertouchhook(int channel, int pitch, int value) { queued_stuff *queued = QUEUEDSTUFF; if (rb_available_to_write(queued->midi_receive_buffer) >= S_MIDI_PARAMS) { midi_params p = {LIBPD_POLYAFTERTOUCH, channel, pitch, value}; rb_write_to_buffer(queued->midi_receive_buffer, 1, (const char *)&p, S_MIDI_PARAMS); } } static void internal_midibytehook(int port, int byte) { queued_stuff *queued = QUEUEDSTUFF; if (rb_available_to_write(queued->midi_receive_buffer) >= S_MIDI_PARAMS) { midi_params p = {LIBPD_MIDIBYTE, port, byte, 0}; rb_write_to_buffer(queued->midi_receive_buffer, 1, (const char *)&p, S_MIDI_PARAMS); } } void libpd_set_queued_printhook(const t_libpd_printhook hook) { QUEUEDSTUFF->printhook = hook; } void libpd_set_queued_banghook(const t_libpd_banghook hook) { QUEUEDSTUFF->hooks.h_banghook = hook; } void libpd_set_queued_floathook(const t_libpd_floathook hook) { QUEUEDSTUFF->hooks.h_floathook = hook; QUEUEDSTUFF->hooks.h_doublehook = NULL; } void libpd_set_queued_doublehook(const t_libpd_doublehook hook) { QUEUEDSTUFF->hooks.h_floathook = NULL; QUEUEDSTUFF->hooks.h_doublehook = hook; } void libpd_set_queued_symbolhook(const t_libpd_symbolhook hook) { QUEUEDSTUFF->hooks.h_symbolhook = hook; } void libpd_set_queued_listhook(const t_libpd_listhook hook) { QUEUEDSTUFF->hooks.h_listhook = hook; } void libpd_set_queued_messagehook(const t_libpd_messagehook hook) { QUEUEDSTUFF->hooks.h_messagehook = hook; } void libpd_set_queued_noteonhook(const t_libpd_noteonhook hook) { QUEUEDSTUFF->hooks.h_noteonhook = hook; } void libpd_set_queued_controlchangehook(const t_libpd_controlchangehook hook) { QUEUEDSTUFF->hooks.h_controlchangehook = hook; } void libpd_set_queued_programchangehook(const t_libpd_programchangehook hook) { QUEUEDSTUFF->hooks.h_programchangehook = hook; } void libpd_set_queued_pitchbendhook(const t_libpd_pitchbendhook hook) { QUEUEDSTUFF->hooks.h_pitchbendhook = hook; } void libpd_set_queued_aftertouchhook(const t_libpd_aftertouchhook hook) { QUEUEDSTUFF->hooks.h_aftertouchhook = hook; } void libpd_set_queued_polyaftertouchhook(const t_libpd_polyaftertouchhook hook) { QUEUEDSTUFF->hooks.h_polyaftertouchhook = hook; } void libpd_set_queued_midibytehook(const t_libpd_midibytehook hook) { QUEUEDSTUFF->hooks.h_midibytehook = hook; } static void queued_stuff_free(void *p) { queued_stuff *queued = (queued_stuff *)p; if (queued->pd_receive_buffer) rb_free(queued->pd_receive_buffer); if (queued->midi_receive_buffer) rb_free(queued->midi_receive_buffer); } int libpd_queued_init() { int ret = libpd_init(); libpd_set_printhook(internal_printhook); libpd_set_banghook(internal_banghook); libpd_set_doublehook(internal_doublehook); libpd_set_symbolhook(internal_symbolhook); libpd_set_listhook(internal_listhook); libpd_set_messagehook(internal_messagehook); libpd_set_noteonhook(internal_noteonhook); libpd_set_controlchangehook(internal_controlchangehook); libpd_set_programchangehook(internal_programchangehook); libpd_set_pitchbendhook(internal_pitchbendhook); libpd_set_aftertouchhook(internal_aftertouchhook); libpd_set_polyaftertouchhook(internal_polyaftertouchhook); libpd_set_midibytehook(internal_midibytehook); t_libpdimp *imp = LIBPDSTUFF; if (!imp->i_queued) { queued_stuff *queued = (queued_stuff *)calloc(1, sizeof(queued_stuff)); if(!queued) goto cleanup; queued->pd_receive_buffer = rb_create(BUFFER_SIZE); if (!queued->pd_receive_buffer) goto cleanup; queued->midi_receive_buffer = rb_create(BUFFER_SIZE); if (!queued->midi_receive_buffer) goto cleanup; imp->i_queued = (void *)queued; imp->i_queued_freehook = queued_stuff_free; } return ret; cleanup: libpd_queued_release(); return -2; } void libpd_queued_release() { t_libpdimp *imp = LIBPDSTUFF; if (imp->i_queued) { queued_stuff_free(imp->i_queued); imp->i_queued = NULL; imp->i_queued_freehook = NULL; } } void libpd_queued_receive_pd_messages() { queued_stuff *queued = QUEUEDSTUFF; size_t available = rb_available_to_read(queued->pd_receive_buffer); if (!available) return; rb_read_from_buffer(queued->pd_receive_buffer, queued->temp_buffer, (int)available); char *end = queued->temp_buffer + available; char *buffer = queued->temp_buffer; while (buffer < end) { pd_params *p = (pd_params *)buffer; buffer += S_PD_PARAMS; switch (p->type) { case LIBPD_PRINT: { receive_print(p, &buffer); break; } case LIBPD_BANG: { receive_bang(p, &buffer); break; } case LIBPD_FLOAT: { receive_float(p, &buffer); break; } case LIBPD_SYMBOL: { receive_symbol(p, &buffer); break; } case LIBPD_LIST: { receive_list(p, &buffer); break; } case LIBPD_MESSAGE: { receive_message(p, &buffer); break; } default: break; } } } void libpd_queued_receive_midi_messages() { queued_stuff *queued = QUEUEDSTUFF; size_t available = rb_available_to_read(queued->midi_receive_buffer); if (!available) return; rb_read_from_buffer(queued->midi_receive_buffer, queued->temp_buffer, (int)available); char *end = queued->temp_buffer + available; char *buffer = queued->temp_buffer; while (buffer < end) { midi_params *p = (midi_params *)buffer; buffer += S_MIDI_PARAMS; switch (p->type) { case LIBPD_NOTEON: { receive_noteon(p, &buffer); break; } case LIBPD_CONTROLCHANGE: { receive_controlchange(p, &buffer); break; } case LIBPD_PROGRAMCHANGE: { receive_programchange(p, &buffer); break; } case LIBPD_PITCHBEND: { receive_pitchbend(p, &buffer); break; } case LIBPD_AFTERTOUCH: { receive_aftertouch(p, &buffer); break; } case LIBPD_POLYAFTERTOUCH: { receive_polyaftertouch(p, &buffer); break; } case LIBPD_MIDIBYTE: { receive_midibyte(p, &buffer); break; } default: break; } } } ================================================ FILE: libs/libpd/libpd_wrapper/util/z_queued.h ================================================ /* * Copyright (c) 2012 Peter Brinkmann (peter.brinkmann@gmail.com) * Copyright (c) 2022 libpd team * * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. * * See https://github.com/libpd/libpd/wiki for documentation * */ #ifndef __Z_QUEUED_H__ #define __Z_QUEUED_H__ #include "z_libpd.h" #ifdef __cplusplus extern "C" { #endif /// set the queued print receiver hook, NULL by default /// note: do not call this before libpd_queued_init() or while DSP is running EXTERN void libpd_set_queued_printhook(const t_libpd_printhook hook); /// set the queued bang receiver hook, NULL by default /// note: do not call this before libpd_queued_init() or while DSP is running EXTERN void libpd_set_queued_banghook(const t_libpd_banghook hook); /// set the queued float receiver hook, NULL by default /// note: do not call this before libpd_queued_init() or while DSP is running /// note: you can either have a queued float receiver hook, or a queued /// double receiver hook (see below), but not both. /// calling this, will automatically unset the queued double receiver /// hook EXTERN void libpd_set_queued_floathook(const t_libpd_floathook hook); /// set the queued double receiver hook, NULL by default /// note: do not call this before libpd_queued_init() or while DSP is running /// note: you can either have a queued double receiver hook, or a queued /// float receiver hook (see above), but not both. /// calling this, will automatically unset the queued float receiver /// hook EXTERN void libpd_set_queued_doublehook(const t_libpd_doublehook hook); /// set the queued symbol receiver hook, NULL by default /// note: do not call this before libpd_queued_init() or while DSP is running EXTERN void libpd_set_queued_symbolhook(const t_libpd_symbolhook hook); /// set the queued list receiver hook, NULL by default /// note: do not call this before libpd_queued_init() or while DSP is running EXTERN void libpd_set_queued_listhook(const t_libpd_listhook hook); /// set the queued typed message receiver hook, NULL by default /// note: do not call this before libpd_queued_init() or while DSP is running EXTERN void libpd_set_queued_messagehook(const t_libpd_messagehook hook); /// set the queued MIDI note on hook, NULL by default /// note: do not call this before libpd_queued_init() or while DSP is running EXTERN void libpd_set_queued_noteonhook(const t_libpd_noteonhook hook); /// set the queued MIDI control change hook, NULL by default /// note: do not call this before libpd_queued_init() or while DSP is running EXTERN void libpd_set_queued_controlchangehook(const t_libpd_controlchangehook hook); /// set the queued MIDI program change hook, NULL by default /// note: do not call this before libpd_queued_init() or while DSP is running EXTERN void libpd_set_queued_programchangehook(const t_libpd_programchangehook hook); /// set the queued MIDI pitch bend hook, NULL by default /// note: do not call this before libpd_queued_init() or while DSP is running EXTERN void libpd_set_queued_pitchbendhook(const t_libpd_pitchbendhook hook); /// set the queued MIDI after touch hook, NULL by default /// note: do not call this before libpd_queued_init() or while DSP is running EXTERN void libpd_set_queued_aftertouchhook(const t_libpd_aftertouchhook hook); /// set the queued MIDI poly after touch hook, NULL by default /// note: do not call this before libpd_queued_init() or while DSP is running EXTERN void libpd_set_queued_polyaftertouchhook(const t_libpd_polyaftertouchhook hook); /// set the queued raw MIDI byte hook, NULL by default /// note: do not call this before libpd_queued_init() or while DSP is running EXTERN void libpd_set_queued_midibytehook(const t_libpd_midibytehook hook); /// initialize libpd and the queued ringbuffers, safe to call more than once /// returns 0 on success, -1 if libpd was already initialized, or -2 if ring /// buffer allocation failed /// /// with a single instance, use in place of libpd_init() /// /// with multiple instances, call once for each instance *before* setting hooks: /// t_pdinstance *pd1 = libpd_new_instance(); /// libpd_set_instance(pd1); /// libpd_queued_init(); /// libpd_set_queued_printhook(pdprint); /// ... /// EXTERN int libpd_queued_init(); /// free the queued ringbuffers /// with multiple instances, call before freeing each instance: /// libpd_set_instance(pd1); /// libpd_queued_release(); /// libpd_free_instance(pd1); EXTERN void libpd_queued_release(); /// process and dispatch received messages in message ringbuffer EXTERN void libpd_queued_receive_pd_messages(); /// process and dispatch receive midi messages in MIDI message ringbuffer EXTERN void libpd_queued_receive_midi_messages(); #ifdef __cplusplus } #endif #endif ================================================ FILE: libs/libpd/libpd_wrapper/x_libpdreceive.c ================================================ /* * Copyright (c) 2010 Peter Brinkmann (peter.brinkmann@gmail.com) * Copyright (c) 2012-2021 libpd team * * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. * * See https://github.com/libpd/libpd/wiki for documentation * */ #include "x_libpdreceive.h" #include "z_libpd.h" #include "z_hooks.h" static t_class *libpdrec_class; typedef struct _libpdrec { t_object x_obj; t_symbol *x_sym; t_libpdhooks *x_hooks; } t_libpdrec; static void libpdrecbang(t_libpdrec *x) { if (x->x_hooks->h_banghook) (*x->x_hooks->h_banghook)(x->x_sym->s_name); } static void libpdrecfloat(t_libpdrec *x, t_float f) { if (x->x_hooks->h_floathook) (*x->x_hooks->h_floathook)(x->x_sym->s_name, f); else if (x->x_hooks->h_doublehook) (*x->x_hooks->h_doublehook)(x->x_sym->s_name, f); } static void libpdrecsymbol(t_libpdrec *x, t_symbol *s) { if (x->x_hooks->h_symbolhook) (*x->x_hooks->h_symbolhook)(x->x_sym->s_name, s->s_name); } static void libpdrecpointer(t_libpdrec *x, t_gpointer *gp) { // just ignore pointers for now... } static void libpdreclist(t_libpdrec *x, t_symbol *s, int argc, t_atom *argv) { if (x->x_hooks->h_listhook) (*x->x_hooks->h_listhook)(x->x_sym->s_name, argc, argv); } static void libpdrecanything(t_libpdrec *x, t_symbol *s, int argc, t_atom *argv) { if (x->x_hooks->h_messagehook) (*x->x_hooks->h_messagehook)(x->x_sym->s_name, s->s_name, argc, argv); } static void libpdreceive_free(t_libpdrec *x) { pd_unbind(&x->x_obj.ob_pd, x->x_sym); } static void *libpdreceive_donew(t_symbol *s) { t_libpdrec *x; x = (t_libpdrec *)pd_new(libpdrec_class); x->x_sym = s; x->x_hooks = &LIBPDSTUFF->i_hooks; pd_bind(&x->x_obj.ob_pd, s); return x; } // this is exposed in the libpd API so must set the lock void *libpdreceive_new(t_symbol *s) { t_libpdrec *x; sys_lock(); x = (t_libpdrec *)libpdreceive_donew(s); sys_unlock(); return x; } void libpdreceive_setup(void) { sys_lock(); libpdrec_class = class_new(gensym("libpd_receive"), (t_newmethod)libpdreceive_donew, (t_method)libpdreceive_free, sizeof(t_libpdrec), CLASS_DEFAULT, A_DEFSYM, 0); class_addbang(libpdrec_class, libpdrecbang); class_addfloat(libpdrec_class, libpdrecfloat); class_addsymbol(libpdrec_class, libpdrecsymbol); class_addpointer(libpdrec_class, libpdrecpointer); class_addlist(libpdrec_class, libpdreclist); class_addanything(libpdrec_class, libpdrecanything); sys_unlock(); } ================================================ FILE: libs/libpd/libpd_wrapper/x_libpdreceive.h ================================================ /* * Copyright (c) 2010 Peter Brinkmann (peter.brinkmann@gmail.com) * Copyright (c) 2012-2021 libpd team * * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. * * See https://github.com/libpd/libpd/wiki for documentation * */ #ifndef __X_LIBPDREC_H__ #define __X_LIBPDREC_H__ #include "m_pd.h" // internal "virtual" libpd receive object which forwards events to hooks // do *not* include this file in a user-facing header // set up the libpd source receiver class void libpdreceive_setup(void); // create a new libpd source receiver with a given name symbol void *libpdreceive_new(t_symbol *); #endif ================================================ FILE: libs/libpd/libpd_wrapper/z_hooks.c ================================================ /* * Copyright (c) 2013 Dan Wilcox (danomatika@gmail.com) * Copyright (c) 2013-2021 libpd team * * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. * * See https://github.com/libpd/libpd/wiki for documentation * */ #include "z_hooks.h" #include /* instance */ t_libpdimp libpd_mainimp = {NULL}; t_libpdimp* libpdimp_new(void) { t_libpdimp *imp = calloc(1, sizeof(t_libpdimp)); return imp; } void libpdimp_free(t_libpdimp *imp) { if (imp == &libpd_mainimp) return; if (imp->i_queued) imp->i_queued_freehook(imp->i_queued); if (imp->i_print_util) free(imp->i_print_util); if (imp->i_data && imp->i_data_freehook) imp->i_data_freehook(imp->i_data); free(imp); } ================================================ FILE: libs/libpd/libpd_wrapper/z_hooks.h ================================================ /* * Copyright (c) 2013 Dan Wilcox (danomatika@gmail.com) * Copyright (c) 2013-2021 libpd team * * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. * * See https://github.com/libpd/libpd/wiki for documentation * */ #ifndef __Z_HOOKS_H__ #define __Z_HOOKS_H__ #include "z_libpd.h" #include "s_stuff.h" // internal hooks, etc // do *not* include this file in a user-facing header /* hooks */ typedef struct _libpdhooks { // messages // no h_printhook as libpd_set_printhook() sets internal STUFF->st_printhook t_libpd_banghook h_banghook; t_libpd_floathook h_floathook; t_libpd_doublehook h_doublehook; t_libpd_symbolhook h_symbolhook; t_libpd_listhook h_listhook; t_libpd_messagehook h_messagehook; // MIDI t_libpd_noteonhook h_noteonhook; t_libpd_controlchangehook h_controlchangehook; t_libpd_programchangehook h_programchangehook; t_libpd_pitchbendhook h_pitchbendhook; t_libpd_aftertouchhook h_aftertouchhook; t_libpd_polyaftertouchhook h_polyaftertouchhook; t_libpd_midibytehook h_midibytehook; } t_libpdhooks; /* instance */ /// libpd per-instance implementation data typedef struct _libpdimp { t_libpdhooks i_hooks; /* event hooks */ void *i_queued; /* queued data, default NULL */ void *i_print_util; /* print util data, default NULL */ void *i_data; /* user data, default NULL */ t_libpd_freehook i_queued_freehook; /* i_queued free, default NULL */ t_libpd_freehook i_data_freehook; /* i_data free, default NULL */ } t_libpdimp; /// main instance implementation data, always valid extern t_libpdimp libpd_mainimp; /// alloc new instance implementation data t_libpdimp* libpdimp_new(void); /// free instance implementation data /// does nothing if imp is libpd_mainimp void libpdimp_free(t_libpdimp *imp); /// get current instance implementation data #ifdef PDINSTANCE #define LIBPDSTUFF ((t_libpdimp *)(STUFF->st_impdata)) #else #define LIBPDSTUFF ((t_libpdimp *)&libpd_mainimp) #endif #endif ================================================ FILE: libs/libpd/libpd_wrapper/z_libpd.c ================================================ /* * Copyright (c) 2010 Peter Brinkmann (peter.brinkmann@gmail.com) * Copyright (c) 2012-2021 libpd team * * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. * * See https://github.com/libpd/libpd/wiki for documentation * */ #include #include #include #include #include #ifndef LIBPD_NO_NUMERIC # include #endif #include "z_libpd.h" #include "x_libpdreceive.h" #include "z_hooks.h" #include "m_imp.h" #include "g_all_guis.h" // pd_init() doesn't call socket_init() which is needed on windows for // libpd_start_gui() to work #if (defined(_WIN32) || defined(_WIN64)) && PD_MINOR_VERSION > 50 # include "s_net.h" # define SOCKET_INIT socket_init(); #else # define SOCKET_INIT #endif #if PD_MINOR_VERSION < 46 # define HAVE_SCHED_TICK_ARG #endif #ifdef HAVE_SCHED_TICK_ARG # define SCHED_TICK(x) sched_tick(x) #else # define SCHED_TICK(x) sched_tick() #endif // forward declares void pd_init(void); int sys_startgui(const char *libdir); void sys_stopgui(void); int sys_pollgui(void); // (optional) setup functions for built-in "extra" externals #ifdef LIBPD_EXTRA void bob_tilde_setup(void); void bonk_tilde_setup(void); void choice_setup(void); void fiddle_tilde_setup(void); void loop_tilde_setup(void); void lrshift_tilde_setup(void); void pd_tilde_setup(void); void pique_setup(void); void sigmund_tilde_setup(void); void stdout_setup(void); #endif static PERTHREAD t_atom *s_argv = NULL; static PERTHREAD t_atom *s_curr = NULL; static PERTHREAD int s_argm = 0; static PERTHREAD int s_argc = 0; static void *get_object(const char *s) { t_pd *x = gensym(s)->s_thing; return x; } // note: could we use pd_this instead? static int s_initialized = 0; // this is called instead of sys_main() to start things int libpd_init(void) { if (s_initialized) return -1; // only allow init once (for now) s_initialized = 1; signal(SIGFPE, SIG_IGN); libpd_start_message(32); // allocate array for message assembly sys_externalschedlib = 0; sys_printtostderr = 0; sys_usestdpath = 0; // don't use pd_extrapath, only sys_searchpath sys_debuglevel = 0; sys_noloadbang = 0; sys_hipriority = 0; sys_nmidiin = 0; sys_nmidiout = 0; #ifdef HAVE_SCHED_TICK_ARG sys_time = 0; #endif pd_init(); STUFF->st_soundin = NULL; STUFF->st_soundout = NULL; STUFF->st_schedblocksize = DEFDACBLKSIZE; STUFF->st_impdata = &libpd_mainimp; sys_init_fdpoll(); libpdreceive_setup(); STUFF->st_searchpath = NULL; sys_libdir = gensym(""); SOCKET_INIT post("pd %d.%d.%d%s", PD_MAJOR_VERSION, PD_MINOR_VERSION, PD_BUGFIX_VERSION, PD_TEST_VERSION); #ifdef LIBPD_EXTRA bob_tilde_setup(); bonk_tilde_setup(); choice_setup(); fiddle_tilde_setup(); loop_tilde_setup(); lrshift_tilde_setup(); pd_tilde_setup(); pique_setup(); sigmund_tilde_setup(); stdout_setup(); #endif #ifndef LIBPD_NO_NUMERIC setlocale(LC_NUMERIC, "C"); #endif return 0; } void libpd_clear_search_path(void) { sys_lock(); namelist_free(STUFF->st_searchpath); STUFF->st_searchpath = NULL; sys_unlock(); } void libpd_add_to_search_path(const char *path) { sys_lock(); STUFF->st_searchpath = namelist_append(STUFF->st_searchpath, path, 0); sys_unlock(); } void *libpd_openfile(const char *name, const char *dir) { void *retval; sys_lock(); pd_globallock(); retval = (void *)glob_evalfile(NULL, gensym(name), gensym(dir)); pd_globalunlock(); sys_unlock(); return retval; } void libpd_closefile(void *p) { sys_lock(); pd_free((t_pd *)p); sys_unlock(); } int libpd_getdollarzero(void *p) { sys_lock(); pd_pushsym((t_pd *)p); int dzero = canvas_getdollarzero(); pd_popsym((t_pd *)p); sys_unlock(); return dzero; } int libpd_blocksize(void) { return DEFDACBLKSIZE; } int libpd_init_audio(int inChannels, int outChannels, int sampleRate) { t_audiosettings as; as.a_indevvec[0] = as.a_outdevvec[0] = DEFAULTAUDIODEV; as.a_nindev = as.a_noutdev = as.a_nchindev = as.a_nchoutdev = 1; as.a_chindevvec[0] = inChannels; as.a_choutdevvec[0] = outChannels; as.a_srate = sampleRate; as.a_blocksize = DEFDACBLKSIZE; as.a_callback = 0; as.a_advance = -1; as.a_api = API_DUMMY; sys_lock(); sys_set_audio_settings(&as); sched_set_using_audio(SCHED_AUDIO_CALLBACK); sys_reopen_audio(); sys_unlock(); return 0; } static const t_sample sample_to_short = SHRT_MAX, short_to_sample = 1.0 / (t_sample) SHRT_MAX; #define PROCESS(_x, _y) \ int i, j, k; \ t_sample *p0, *p1; \ sys_lock(); \ sys_pollgui(); \ for (i = 0; i < ticks; i++) { \ for (j = 0, p0 = STUFF->st_soundin; j < DEFDACBLKSIZE; j++, p0++) { \ for (k = 0, p1 = p0; k < STUFF->st_inchannels; k++, p1 += DEFDACBLKSIZE) \ { \ *p1 = *inBuffer++ _x; \ } \ } \ memset(STUFF->st_soundout, 0, \ STUFF->st_outchannels*DEFDACBLKSIZE*sizeof(t_sample)); \ SCHED_TICK(pd_this->pd_systime + STUFF->st_time_per_dsp_tick); \ for (j = 0, p0 = STUFF->st_soundout; j < DEFDACBLKSIZE; j++, p0++) { \ for (k = 0, p1 = p0; k < STUFF->st_outchannels; k++, p1 += DEFDACBLKSIZE) \ { \ *outBuffer++ = *p1 _y; \ } \ } \ } \ sys_unlock(); \ return 0; int libpd_process_short(const int ticks, const short *inBuffer, short *outBuffer) { PROCESS(* short_to_sample, * sample_to_short) } int libpd_process_float(const int ticks, const float *inBuffer, float *outBuffer) { PROCESS(,) } int libpd_process_double(const int ticks, const double *inBuffer, double *outBuffer) { PROCESS(,) } #define PROCESS_RAW(_x, _y) \ size_t n_in = STUFF->st_inchannels * DEFDACBLKSIZE; \ size_t n_out = STUFF->st_outchannels * DEFDACBLKSIZE; \ t_sample *p; \ size_t i; \ sys_lock(); \ sys_pollgui(); \ for (p = STUFF->st_soundin, i = 0; i < n_in; i++) { \ *p++ = *inBuffer++ _x; \ } \ memset(STUFF->st_soundout, 0, n_out * sizeof(t_sample)); \ SCHED_TICK(pd_this->pd_systime + STUFF->st_time_per_dsp_tick); \ for (p = STUFF->st_soundout, i = 0; i < n_out; i++) { \ *outBuffer++ = *p++ _y; \ } \ sys_unlock(); \ return 0; int libpd_process_raw(const float *inBuffer, float *outBuffer) { PROCESS_RAW(,) } int libpd_process_raw_short(const short *inBuffer, short *outBuffer) { PROCESS_RAW(* short_to_sample, * sample_to_short) } int libpd_process_raw_double(const double *inBuffer, double *outBuffer) { PROCESS_RAW(,) } #define GETARRAY \ t_garray *garray = (t_garray *) pd_findbyclass(gensym(name), garray_class); \ if (!garray) {sys_unlock(); return -1;} \ int libpd_arraysize(const char *name) { int retval; sys_lock(); GETARRAY retval = garray_npoints(garray); sys_unlock(); return retval; } int libpd_resize_array(const char *name, long size) { sys_lock(); GETARRAY garray_resize_long(garray, size); sys_unlock(); return 0; } #define MEMCPY(_x, _y) \ GETARRAY \ if (n < 0 || offset < 0 || offset + n > garray_npoints(garray)) return -2; \ t_word *vec = ((t_word *) garray_vec(garray)) + offset; \ int i; \ for (i = 0; i < n; i++) _x = _y; int libpd_read_array(float *dest, const char *name, int offset, int n) { sys_lock(); MEMCPY(*dest++, (vec++)->w_float) sys_unlock(); return 0; } int libpd_write_array(const char *name, int offset, const float *src, int n) { sys_lock(); MEMCPY((vec++)->w_float, *src++) sys_unlock(); return 0; } int libpd_read_array_double(double *dest, const char *name, int offset, int n) { sys_lock(); MEMCPY(*dest++, (vec++)->w_float) sys_unlock(); return 0; } int libpd_write_array_double(const char *name, int offset, const double *src, int n) { sys_lock(); MEMCPY((vec++)->w_float, *src++) sys_unlock(); return 0; } int libpd_bang(const char *recv) { void *obj; sys_lock(); obj = get_object(recv); if (obj == NULL) { sys_unlock(); return -1; } pd_bang(obj); sys_unlock(); return 0; } static int libpd_dofloat(const char *recv, t_float x) { void *obj; sys_lock(); obj = get_object(recv); if (obj == NULL) { sys_unlock(); return -1; } pd_float(obj, x); sys_unlock(); return 0; } int libpd_float(const char *recv, float x) { return libpd_dofloat(recv, x); } int libpd_double(const char *recv, double x) { return libpd_dofloat(recv, x); } int libpd_symbol(const char *recv, const char *symbol) { void *obj; sys_lock(); obj = get_object(recv); if (obj == NULL) { sys_unlock(); return -1; } pd_symbol(obj, gensym(symbol)); sys_unlock(); return 0; } int libpd_start_message(int maxlen) { if (maxlen > s_argm) { t_atom *v = realloc(s_argv, maxlen * sizeof(t_atom)); if (v) { s_argv = v; s_argm = maxlen; } else { return -1; } } s_argc = 0; s_curr = s_argv; return 0; } #define ADD_ARG(f) f(s_curr, x); s_curr++; s_argc++; void libpd_add_float(float x) { ADD_ARG(SETFLOAT); } void libpd_add_double(double x) { ADD_ARG(SETFLOAT); } void libpd_add_symbol(const char *symbol) { t_symbol *x; sys_lock(); x = gensym(symbol); sys_unlock(); ADD_ARG(SETSYMBOL); } int libpd_finish_list(const char *recv) { return libpd_list(recv, s_argc, s_argv); } int libpd_finish_message(const char *recv, const char *msg) { return libpd_message(recv, msg, s_argc, s_argv); } void libpd_set_float(t_atom *a, float x) { SETFLOAT(a, x); } void libpd_set_double(t_atom *v, double x) { SETFLOAT(v, x); } void libpd_set_symbol(t_atom *a, const char *symbol) { SETSYMBOL(a, gensym(symbol)); } int libpd_list(const char *recv, int argc, t_atom *argv) { t_pd *obj; sys_lock(); obj = get_object(recv); if (obj == NULL) { sys_unlock(); return -1; } pd_list(obj, &s_list, argc, argv); sys_unlock(); return 0; } int libpd_message(const char *recv, const char *msg, int argc, t_atom *argv) { t_pd *obj; sys_lock(); obj = get_object(recv); if (obj == NULL) { sys_unlock(); return -1; } pd_typedmess(obj, gensym(msg), argc, argv); sys_unlock(); return 0; } void *libpd_bind(const char *recv) { t_symbol *x; sys_lock(); x = gensym(recv); sys_unlock(); return libpdreceive_new(x); } void libpd_unbind(void *p) { sys_lock(); pd_free((t_pd *)p); sys_unlock(); } int libpd_exists(const char *recv) { int retval; sys_lock(); retval = (get_object(recv) != NULL); sys_unlock(); return retval; } // when setting hooks, use mainimp if pd is not yet inited #define IMP (s_initialized ? LIBPDSTUFF : &libpd_mainimp) void libpd_set_printhook(const t_libpd_printhook hook) { if (!s_initialized) // set default hook sys_printhook = (t_printhook)hook; else // set instance hook STUFF->st_printhook = (t_printhook)hook; } void libpd_set_banghook(const t_libpd_banghook hook) { IMP->i_hooks.h_banghook = hook; } void libpd_set_floathook(const t_libpd_floathook hook) { IMP->i_hooks.h_floathook = hook; IMP->i_hooks.h_doublehook = NULL; } void libpd_set_doublehook(const t_libpd_doublehook hook) { IMP->i_hooks.h_floathook = NULL; IMP->i_hooks.h_doublehook = hook; } void libpd_set_symbolhook(const t_libpd_symbolhook hook) { IMP->i_hooks.h_symbolhook = hook; } void libpd_set_listhook(const t_libpd_listhook hook) { IMP->i_hooks.h_listhook = hook; } void libpd_set_messagehook(const t_libpd_messagehook hook) { IMP->i_hooks.h_messagehook = hook; } int libpd_is_float(t_atom *a) { return (a)->a_type == A_FLOAT; } int libpd_is_symbol(t_atom *a) { return (a)->a_type == A_SYMBOL; } float libpd_get_float(t_atom *a) { return (a)->a_w.w_float; } double libpd_get_double(t_atom *a) { return (a)->a_w.w_float; } const char *libpd_get_symbol(t_atom *a) { return (a)->a_w.w_symbol->s_name; } t_atom *libpd_next_atom(t_atom *a) { return a + 1; } #define CHECK_CHANNEL if (channel < 0) return -1; #define CHECK_PORT if (port < 0 || port > 0x0fff) return -1; #define CHECK_RANGE_7BIT(v) if (v < 0 || v > 0x7f) return -1; #define CHECK_RANGE_8BIT(v) if (v < 0 || v > 0xff) return -1; #define PORT (channel >> 4) #define CHANNEL (channel & 0x0f) int libpd_noteon(int channel, int pitch, int velocity) { CHECK_CHANNEL CHECK_RANGE_7BIT(pitch) CHECK_RANGE_7BIT(velocity) sys_lock(); inmidi_noteon(PORT, CHANNEL, pitch, velocity); sys_unlock(); return 0; } int libpd_controlchange(int channel, int controller, int value) { CHECK_CHANNEL CHECK_RANGE_7BIT(controller) CHECK_RANGE_7BIT(value) sys_lock(); inmidi_controlchange(PORT, CHANNEL, controller, value); sys_unlock(); return 0; } int libpd_programchange(int channel, int value) { CHECK_CHANNEL CHECK_RANGE_7BIT(value) sys_lock(); inmidi_programchange(PORT, CHANNEL, value); sys_unlock(); return 0; } // note: for consistency with Pd, we center the output of [pitchin] at 8192 int libpd_pitchbend(int channel, int value) { CHECK_CHANNEL if (value < -8192 || value > 8191) return -1; sys_lock(); inmidi_pitchbend(PORT, CHANNEL, value + 8192); sys_unlock(); return 0; } int libpd_aftertouch(int channel, int value) { CHECK_CHANNEL CHECK_RANGE_7BIT(value) sys_lock(); inmidi_aftertouch(PORT, CHANNEL, value); sys_unlock(); return 0; } int libpd_polyaftertouch(int channel, int pitch, int value) { CHECK_CHANNEL CHECK_RANGE_7BIT(pitch) CHECK_RANGE_7BIT(value) sys_lock(); inmidi_polyaftertouch(PORT, CHANNEL, pitch, value); sys_unlock(); return 0; } int libpd_midibyte(int port, int byte) { CHECK_PORT CHECK_RANGE_8BIT(byte) sys_lock(); inmidi_byte(port, byte); sys_unlock(); return 0; } int libpd_sysex(int port, int byte) { CHECK_PORT CHECK_RANGE_8BIT(byte) sys_lock(); inmidi_sysex(port, byte); sys_unlock(); return 0; } int libpd_sysrealtime(int port, int byte) { CHECK_PORT CHECK_RANGE_8BIT(byte) sys_lock(); inmidi_realtimein(port, byte); sys_unlock(); return 0; } void libpd_set_noteonhook(const t_libpd_noteonhook hook) { IMP->i_hooks.h_noteonhook = hook; } void libpd_set_controlchangehook(const t_libpd_controlchangehook hook) { IMP->i_hooks.h_controlchangehook = hook; } void libpd_set_programchangehook(const t_libpd_programchangehook hook) { IMP->i_hooks.h_programchangehook = hook; } void libpd_set_pitchbendhook(const t_libpd_pitchbendhook hook) { IMP->i_hooks.h_pitchbendhook = hook; } void libpd_set_aftertouchhook(const t_libpd_aftertouchhook hook) { IMP->i_hooks.h_aftertouchhook = hook; } void libpd_set_polyaftertouchhook(const t_libpd_polyaftertouchhook hook) { IMP->i_hooks.h_polyaftertouchhook = hook; } void libpd_set_midibytehook(const t_libpd_midibytehook hook) { IMP->i_hooks.h_midibytehook = hook; } int libpd_start_gui(const char *path) { int retval; sys_lock(); retval = sys_startgui(path); sys_unlock(); return retval; } void libpd_stop_gui(void) { sys_lock(); sys_stopgui(); sys_unlock(); } int libpd_poll_gui(void) { int retval; sys_lock(); retval = sys_pollgui(); sys_unlock(); return (retval); } t_pdinstance *libpd_new_instance(void) { #ifdef PDINSTANCE t_pdinstance *pd = pdinstance_new(); pd->pd_stuff->st_impdata = libpdimp_new(); return pd; #else return NULL; #endif } void libpd_set_instance(t_pdinstance *pd) { #ifdef PDINSTANCE pd_setinstance(pd); #endif } void libpd_free_instance(t_pdinstance *pd) { #ifdef PDINSTANCE if (pd == &pd_maininstance) return; libpdimp_free(pd->pd_stuff->st_impdata); pdinstance_free(pd); #endif } t_pdinstance *libpd_this_instance(void) { return pd_this; } t_pdinstance *libpd_main_instance(void) { return &pd_maininstance; } int libpd_num_instances(void) { #ifdef PDINSTANCE return pd_ninstances; #else return 1; #endif } void libpd_set_instancedata(void *data, t_libpd_freehook freehook) { LIBPDSTUFF->i_data = data; LIBPDSTUFF->i_data_freehook = freehook; } void* libpd_get_instancedata() { return LIBPDSTUFF->i_data; } void libpd_set_verbose(int verbose) { if (verbose < 0) verbose = 0; sys_verbose = verbose; } int libpd_get_verbose(void) { return sys_verbose; } // dummy routines needed because we don't use s_file.c void glob_loadpreferences(t_pd *dummy, t_symbol *s) {} void glob_savepreferences(t_pd *dummy, t_symbol *s) {} void glob_forgetpreferences(t_pd *dummy) {} void sys_loadpreferences(const char *filename, int startingup) {} int sys_oktoloadfiles(int done) {return 1;} void sys_savepreferences(const char *filename) {} // used in s_path.c ================================================ FILE: libs/libpd/libpd_wrapper/z_libpd.h ================================================ /* * Copyright (c) 2010 Peter Brinkmann (peter.brinkmann@gmail.com) * Copyright (c) 2012-2021 libpd team * * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. * * See https://github.com/libpd/libpd/wiki for documentation * */ #ifndef __Z_LIBPD_H__ #define __Z_LIBPD_H__ #ifdef __cplusplus extern "C" { #endif #include "m_pd.h" /* initializing pd */ /// initialize libpd; it is safe to call this more than once /// returns 0 on success or -1 if libpd was already initialized /// note: sets SIGFPE handler to keep bad pd patches from crashing due to divide /// by 0, set any custom handling after calling this function EXTERN int libpd_init(void); /// clear the libpd search path for abstractions and externals /// note: this is called by libpd_init() EXTERN void libpd_clear_search_path(void); /// add a path to the libpd search paths /// relative paths are relative to the current working directory /// unlike desktop pd, *no* search paths are set by default (ie. extra) EXTERN void libpd_add_to_search_path(const char *path); /* opening patches */ /// open a patch by filename and parent dir path /// returns an opaque patch handle pointer or NULL on failure EXTERN void *libpd_openfile(const char *name, const char *dir); /// close a patch by patch handle pointer EXTERN void libpd_closefile(void *p); /// get the $0 id of the patch handle pointer /// returns $0 value or 0 if the patch is non-existent EXTERN int libpd_getdollarzero(void *p); /* audio processing */ /// return pd's fixed block size: the number of sample frames per 1 pd tick EXTERN int libpd_blocksize(void); /// initialize audio rendering /// returns 0 on success EXTERN int libpd_init_audio(int inChannels, int outChannels, int sampleRate); /// process interleaved float samples from inBuffer -> libpd -> outBuffer /// buffer sizes are based on # of ticks and channels where: /// size = ticks * libpd_blocksize() * (in/out)channels /// returns 0 on success EXTERN int libpd_process_float(const int ticks, const float *inBuffer, float *outBuffer); /// process interleaved short samples from inBuffer -> libpd -> outBuffer /// buffer sizes are based on # of ticks and channels where: /// size = ticks * libpd_blocksize() * (in/out)channels /// float samples are converted to short by multiplying by 32767 and casting, /// so any values received from pd patches beyond -1 to 1 will result in garbage /// note: for efficiency, does *not* clip input /// returns 0 on success EXTERN int libpd_process_short(const int ticks, const short *inBuffer, short *outBuffer); /// process interleaved double samples from inBuffer -> libpd -> outBuffer /// buffer sizes are based on # of ticks and channels where: /// size = ticks * libpd_blocksize() * (in/out)channels /// note: only full-precision when compiled with PD_FLOATSIZE=64 /// returns 0 on success EXTERN int libpd_process_double(const int ticks, const double *inBuffer, double *outBuffer); /// process non-interleaved float samples from inBuffer -> libpd -> outBuffer /// copies buffer contents to/from libpd without striping /// buffer sizes are based on a single tick and # of channels where: /// size = libpd_blocksize() * (in/out)channels /// returns 0 on success EXTERN int libpd_process_raw(const float *inBuffer, float *outBuffer); /// process non-interleaved short samples from inBuffer -> libpd -> outBuffer /// copies buffer contents to/from libpd without striping /// buffer sizes are based on a single tick and # of channels where: /// size = libpd_blocksize() * (in/out)channels /// float samples are converted to short by multiplying by 32767 and casting, /// so any values received from pd patches beyond -1 to 1 will result in garbage /// note: for efficiency, does *not* clip input /// returns 0 on success EXTERN int libpd_process_raw_short(const short *inBuffer, short *outBuffer); /// process non-interleaved double samples from inBuffer -> libpd -> outBuffer /// copies buffer contents to/from libpd without striping /// buffer sizes are based on a single tick and # of channels where: /// size = libpd_blocksize() * (in/out)channels /// note: only full-precision when compiled with PD_FLOATSIZE=64 /// returns 0 on success EXTERN int libpd_process_raw_double(const double *inBuffer, double *outBuffer); /* array access */ /// get the size of an array by name /// returns size or negative error code if non-existent EXTERN int libpd_arraysize(const char *name); /// (re)size an array by name; sizes <= 0 are clipped to 1 /// returns 0 on success or negative error code if non-existent EXTERN int libpd_resize_array(const char *name, long size); /// read n values from named src array and write into dest starting at an offset /// note: performs no bounds checking on dest /// returns 0 on success or a negative error code if the array is non-existent /// or offset + n exceeds range of array EXTERN int libpd_read_array(float *dest, const char *name, int offset, int n); /// read n values from src and write into named dest array starting at an offset /// note: performs no bounds checking on src /// returns 0 on success or a negative error code if the array is non-existent /// or offset + n exceeds range of array EXTERN int libpd_write_array(const char *name, int offset, const float *src, int n); /// read n values from named src array and write into dest starting at an offset /// note: performs no bounds checking on dest /// note: only full-precision when compiled with PD_FLOATSIZE=64 /// returns 0 on success or a negative error code if the array is non-existent /// or offset + n exceeds range of array /// double-precision variant of libpd_read_array() EXTERN int libpd_read_array_double(double *dest, const char *src, int offset, int n); /// read n values from src and write into named dest array starting at an offset /// note: performs no bounds checking on src /// note: only full-precision when compiled with PD_FLOATSIZE=64 /// returns 0 on success or a negative error code if the array is non-existent /// or offset + n exceeds range of array /// double-precision variant of libpd_write_array() EXTERN int libpd_write_array_double(const char *dest, int offset, const double *src, int n); /* sending messages to pd */ /// send a bang to a destination receiver /// ex: libpd_bang("foo") will send a bang to [s foo] on the next tick /// returns 0 on success or -1 if receiver name is non-existent EXTERN int libpd_bang(const char *recv); /// send a float to a destination receiver /// ex: libpd_float("foo", 1) will send a 1.0 to [s foo] on the next tick /// returns 0 on success or -1 if receiver name is non-existent EXTERN int libpd_float(const char *recv, float x); /// send a double to a destination receiver /// ex: libpd_double("foo", 1.1) will send a 1.1 to [s foo] on the next tick /// note: only full-precision when compiled with PD_FLOATSIZE=64 /// returns 0 on success or -1 if receiver name is non-existent EXTERN int libpd_double(const char *recv, double x); /// send a symbol to a destination receiver /// ex: libpd_symbol("foo", "bar") will send "bar" to [s foo] on the next tick /// returns 0 on success or -1 if receiver name is non-existent EXTERN int libpd_symbol(const char *recv, const char *symbol); /* sending compound messages: sequenced function calls */ /// start composition of a new list or typed message of up to max element length /// messages can be of a smaller length as max length is only an upper bound /// note: no cleanup is required for unfinished messages /// returns 0 on success or nonzero if the length is too large EXTERN int libpd_start_message(int maxlen); /// add a float to the current message in progress EXTERN void libpd_add_float(float x); /// add a double to the current message in progress /// note: only full-precision when compiled with PD_FLOATSIZE=64 EXTERN void libpd_add_double(double x); /// add a symbol to the current message in progress EXTERN void libpd_add_symbol(const char *symbol); /// finish current message and send as a list to a destination receiver /// returns 0 on success or -1 if receiver name is non-existent /// ex: send [list 1 2 bar( to [s foo] on the next tick with: /// libpd_start_message(3); /// libpd_add_float(1); /// libpd_add_float(2); /// libpd_add_symbol("bar"); /// libpd_finish_list("foo"); EXTERN int libpd_finish_list(const char *recv); /// finish current message and send as a typed message to a destination receiver /// note: typed message handling currently only supports up to 4 elements /// internally, additional elements may be ignored /// returns 0 on success or -1 if receiver name is non-existent /// ex: send [; pd dsp 1( on the next tick with: /// libpd_start_message(1); /// libpd_add_float(1); /// libpd_finish_message("pd", "dsp"); EXTERN int libpd_finish_message(const char *recv, const char *msg); /* sending compound messages: atom array */ /// write a float value to the given atom EXTERN void libpd_set_float(t_atom *a, float x); /// write a double value to the given atom /// note: only full-precision when compiled with PD_FLOATSIZE=64 EXTERN void libpd_set_double(t_atom *v, double x); /// write a symbol value to the given atom EXTERN void libpd_set_symbol(t_atom *a, const char *symbol); /// send an atom array of a given length as a list to a destination receiver /// returns 0 on success or -1 if receiver name is non-existent /// ex: send [list 1 2 bar( to [r foo] on the next tick with: /// t_atom v[3]; /// libpd_set_float(v, 1); /// libpd_set_float(v + 1, 2); /// libpd_set_symbol(v + 2, "bar"); /// libpd_list("foo", 3, v); EXTERN int libpd_list(const char *recv, int argc, t_atom *argv); /// send a atom array of a given length as a typed message to a destination /// receiver, returns 0 on success or -1 if receiver name is non-existent /// ex: send [; pd dsp 1( on the next tick with: /// t_atom v[1]; /// libpd_set_float(v, 1); /// libpd_message("pd", "dsp", 1, v); EXTERN int libpd_message(const char *recv, const char *msg, int argc, t_atom *argv); /* receiving messages from pd */ /// subscribe to messages sent to a source receiver /// ex: libpd_bind("foo") adds a "virtual" [r foo] which forwards messages to /// the libpd message hooks /// returns an opaque receiver pointer or NULL on failure EXTERN void *libpd_bind(const char *recv); /// unsubscribe and free a source receiver object created by libpd_bind() EXTERN void libpd_unbind(void *p); /// check if a source receiver object exists with a given name /// returns 1 if the receiver exists, otherwise 0 EXTERN int libpd_exists(const char *recv); /// print receive hook signature, s is the string to be printed /// note: default behavior returns individual words and spaces: /// line "hello 123" is received in 4 parts -> "hello", " ", "123\n" typedef void (*t_libpd_printhook)(const char *s); /// bang receive hook signature, recv is the source receiver name typedef void (*t_libpd_banghook)(const char *recv); /// float receive hook signature, recv is the source receiver name typedef void (*t_libpd_floathook)(const char *recv, float x); /// double receive hook signature, recv is the source receiver name /// note: only full-precision when compiled with PD_FLOATSIZE=64 typedef void (*t_libpd_doublehook)(const char *recv, double x); /// symbol receive hook signature, recv is the source receiver name typedef void (*t_libpd_symbolhook)(const char *recv, const char *symbol); /// list receive hook signature, recv is the source receiver name /// argc is the list length and vector argv contains the list elements /// which can be accessed using the atom accessor functions, ex: /// int i; /// for (i = 0; i < argc; i++) { /// t_atom *a = &argv[n]; /// if (libpd_is_float(a)) { /// float x = libpd_get_float(a); /// // do something with float x /// } else if (libpd_is_symbol(a)) { /// char *s = libpd_get_symbol(a); /// // do something with c string s /// } /// } /// note: check for both float and symbol types as atom may also be a pointer typedef void (*t_libpd_listhook)(const char *recv, int argc, t_atom *argv); /// typed message hook signature, recv is the source receiver name and msg is /// the typed message name: a message like [; foo bar 1 2 a b( will trigger a /// function call like libpd_messagehook("foo", "bar", 4, argv) /// argc is the list length and vector argv contains the /// list elements which can be accessed using the atom accessor functions, ex: /// int i; /// for (i = 0; i < argc; i++) { /// t_atom *a = &argv[n]; /// if (libpd_is_float(a)) { /// float x = libpd_get_float(a); /// // do something with float x /// } else if (libpd_is_symbol(a)) { /// char *s = libpd_get_symbol(a); /// // do something with c string s /// } /// } /// note: check for both float and symbol types as atom may also be a pointer typedef void (*t_libpd_messagehook)(const char *recv, const char *msg, int argc, t_atom *argv); /// set the print receiver hook, prints to stdout by default /// note: do not call this while DSP is running EXTERN void libpd_set_printhook(const t_libpd_printhook hook); /// set the bang receiver hook, NULL by default /// note: do not call this while DSP is running EXTERN void libpd_set_banghook(const t_libpd_banghook hook); /// set the float receiver hook, NULL by default /// note: avoid calling this while DSP is running /// note: you can either have a float receiver hook, or a double receiver /// hook (see below), but not both. /// calling this, will automatically unset the double receiver hook /// note: only full-precision when compiled with PD_FLOATSIZE=64 EXTERN void libpd_set_floathook(const t_libpd_floathook hook); /// set the double receiver hook, NULL by default /// note: avoid calling this while DSP is running /// note: you can either have a double receiver hook, or a float receiver /// hook (see above), but not both. /// calling this, will automatically unset the float receiver hook /// note: only full-precision when compiled with PD_FLOATSIZE=64 EXTERN void libpd_set_doublehook(const t_libpd_doublehook hook); /// set the symbol receiver hook, NULL by default /// note: do not call this while DSP is running EXTERN void libpd_set_symbolhook(const t_libpd_symbolhook hook); /// set the list receiver hook, NULL by default /// note: do not call this while DSP is running EXTERN void libpd_set_listhook(const t_libpd_listhook hook); /// set the message receiver hook, NULL by default /// note: do not call this while DSP is running EXTERN void libpd_set_messagehook(const t_libpd_messagehook hook); /// check if an atom is a float type: 0 or 1 /// note: no NULL check is performed EXTERN int libpd_is_float(t_atom *a); /// check if an atom is a symbol type: 0 or 1 /// note: no NULL check is performed EXTERN int libpd_is_symbol(t_atom *a); /// get the float value of an atom /// note: no NULL or type checks are performed EXTERN float libpd_get_float(t_atom *a); /// returns the double value of an atom /// note: no NULL or type checks are performed /// note: only full-precision when compiled with PD_FLOATSIZE=64 EXTERN double libpd_get_double(t_atom *a); /// returns the symbol value of an atom /// note: no NULL or type checks are performed EXTERN const char *libpd_get_symbol(t_atom *a); /// increment to the next atom in an atom vector /// returns next atom or NULL, assuming the atom vector is NULL-terminated EXTERN t_atom *libpd_next_atom(t_atom *a); /* sending MIDI messages to pd */ /// send a MIDI note on message to [notein] objects /// channel is 0-indexed, pitch is 0-127, and velocity is 0-127 /// channels encode MIDI ports via: libpd_channel = pd_channel + 16 * pd_port /// note: there is no note off message, send a note on with velocity = 0 instead /// returns 0 on success or -1 if an argument is out of range EXTERN int libpd_noteon(int channel, int pitch, int velocity); /// send a MIDI control change message to [ctlin] objects /// channel is 0-indexed, controller is 0-127, and value is 0-127 /// channels encode MIDI ports via: libpd_channel = pd_channel + 16 * pd_port /// returns 0 on success or -1 if an argument is out of range EXTERN int libpd_controlchange(int channel, int controller, int value); /// send a MIDI program change message to [pgmin] objects /// channel is 0-indexed and value is 0-127 /// channels encode MIDI ports via: libpd_channel = pd_channel + 16 * pd_port /// returns 0 on success or -1 if an argument is out of range EXTERN int libpd_programchange(int channel, int value); /// send a MIDI pitch bend message to [bendin] objects /// channel is 0-indexed and value is -8192-8192 /// channels encode MIDI ports via: libpd_channel = pd_channel + 16 * pd_port /// note: [bendin] outputs 0-16383 while [bendout] accepts -8192-8192 /// returns 0 on success or -1 if an argument is out of range EXTERN int libpd_pitchbend(int channel, int value); /// send a MIDI after touch message to [touchin] objects /// channel is 0-indexed and value is 0-127 /// channels encode MIDI ports via: libpd_channel = pd_channel + 16 * pd_port /// returns 0 on success or -1 if an argument is out of range EXTERN int libpd_aftertouch(int channel, int value); /// send a MIDI poly after touch message to [polytouchin] objects /// channel is 0-indexed, pitch is 0-127, and value is 0-127 /// channels encode MIDI ports via: libpd_channel = pd_channel + 16 * pd_port /// returns 0 on success or -1 if an argument is out of range EXTERN int libpd_polyaftertouch(int channel, int pitch, int value); /// send a raw MIDI byte to [midiin] objects /// port is 0-indexed and byte is 0-256 /// returns 0 on success or -1 if an argument is out of range EXTERN int libpd_midibyte(int port, int byte); /// send a raw MIDI byte to [sysexin] objects /// port is 0-indexed and byte is 0-256 /// returns 0 on success or -1 if an argument is out of range EXTERN int libpd_sysex(int port, int byte); /// send a raw MIDI byte to [realtimein] objects /// port is 0-indexed and byte is 0-256 /// returns 0 on success or -1 if an argument is out of range EXTERN int libpd_sysrealtime(int port, int byte); /* receiving MIDI messages from pd */ /// MIDI note on receive hook signature /// channel is 0-indexed, pitch is 0-127, and value is 0-127 /// channels encode MIDI ports via: libpd_channel = pd_channel + 16 * pd_port /// note: there is no note off message, note on w/ velocity = 0 is used instead /// note: out of range values from pd are clamped typedef void (*t_libpd_noteonhook)(int channel, int pitch, int velocity); /// MIDI control change receive hook signature /// channel is 0-indexed, controller is 0-127, and value is 0-127 /// channels encode MIDI ports via: libpd_channel = pd_channel + 16 * pd_port /// note: out of range values from pd are clamped typedef void (*t_libpd_controlchangehook)(int channel, int controller, int value); /// MIDI program change receive hook signature /// channel is 0-indexed and value is 0-127 /// channels encode MIDI ports via: libpd_channel = pd_channel + 16 * pd_port /// note: out of range values from pd are clamped typedef void (*t_libpd_programchangehook)(int channel, int value); /// MIDI pitch bend receive hook signature /// channel is 0-indexed and value is -8192-8192 /// channels encode MIDI ports via: libpd_channel = pd_channel + 16 * pd_port /// note: [bendin] outputs 0-16383 while [bendout] accepts -8192-8192 /// note: out of range values from pd are clamped typedef void (*t_libpd_pitchbendhook)(int channel, int value); /// MIDI after touch receive hook signature /// channel is 0-indexed and value is 0-127 /// channels encode MIDI ports via: libpd_channel = pd_channel + 16 * pd_port /// note: out of range values from pd are clamped typedef void (*t_libpd_aftertouchhook)(int channel, int value); /// MIDI poly after touch receive hook signature /// channel is 0-indexed, pitch is 0-127, and value is 0-127 /// channels encode MIDI ports via: libpd_channel = pd_channel + 16 * pd_port /// note: out of range values from pd are clamped typedef void (*t_libpd_polyaftertouchhook)(int channel, int pitch, int value); /// raw MIDI byte receive hook signature /// port is 0-indexed and byte is 0-256 /// note: out of range values from pd are clamped typedef void (*t_libpd_midibytehook)(int port, int byte); /// set the MIDI note on hook to receive from [noteout] objects, NULL by default /// note: do not call this while DSP is running EXTERN void libpd_set_noteonhook(const t_libpd_noteonhook hook); /// set the MIDI control change hook to receive from [ctlout] objects, /// NULL by default /// note: do not call this while DSP is running EXTERN void libpd_set_controlchangehook(const t_libpd_controlchangehook hook); /// set the MIDI program change hook to receive from [pgmout] objects, /// NULL by default /// note: do not call this while DSP is running EXTERN void libpd_set_programchangehook(const t_libpd_programchangehook hook); /// set the MIDI pitch bend hook to receive from [bendout] objects, /// NULL by default /// note: do not call this while DSP is running EXTERN void libpd_set_pitchbendhook(const t_libpd_pitchbendhook hook); /// set the MIDI after touch hook to receive from [touchout] objects, /// NULL by default /// note: do not call this while DSP is running EXTERN void libpd_set_aftertouchhook(const t_libpd_aftertouchhook hook); /// set the MIDI poly after touch hook to receive from [polytouchout] objects, /// NULL by default /// note: do not call this while DSP is running EXTERN void libpd_set_polyaftertouchhook(const t_libpd_polyaftertouchhook hook); /// set the raw MIDI byte hook to receive from [midiout] objects, /// NULL by default /// note: do not call this while DSP is running EXTERN void libpd_set_midibytehook(const t_libpd_midibytehook hook); /* GUI */ /// open the current patches within a pd vanilla GUI /// requires the path to pd's main folder that contains bin/, tcl/, etc /// for a macOS .app bundle: /path/to/Pd-#.#-#.app/Contents/Resources /// returns 0 on success EXTERN int libpd_start_gui(const char *path); /// stop the pd vanilla GUI EXTERN void libpd_stop_gui(void); /// manually update and handle any GUI messages /// this is called automatically when using a libpd_process function, /// note: this also facilitates network message processing, etc so it can be /// useful to call repeatedly when idle for more throughput /// returns 1 if the poll found something, in which case it might be desirable /// to poll again, up to some reasonable limit EXTERN int libpd_poll_gui(void); /* multiple instances */ /// create a new pd instance and set as current /// note: use this in place of pdinstance_new() /// returns new instance or NULL when libpd is not compiled with PDINSTANCE EXTERN t_pdinstance *libpd_new_instance(void); /// set the current pd instance /// subsequent libpd calls will affect this instance only /// note: use this in place of pd_setinstance() /// does nothing when libpd is not compiled with PDINSTANCE EXTERN void libpd_set_instance(t_pdinstance *pd); /// free a pd instance and set main instance as current /// note: use this in place of pdinstance_free() /// does nothing when libpd is not compiled with PDINSTANCE EXTERN void libpd_free_instance(t_pdinstance *pd); /// get the current pd instance EXTERN t_pdinstance *libpd_this_instance(void); /// get the main pd instance, always valid EXTERN t_pdinstance *libpd_main_instance(void); /// get the number of pd instances, including the main instance /// returns number or 1 when libpd is not compiled with PDINSTANCE EXTERN int libpd_num_instances(void); /// per-instance data free hook signature typedef void (*t_libpd_freehook)(void *data); /// set per-instance user data and optional free hook /// note: if non-NULL, freehook is called by libpd_free_instance() EXTERN void libpd_set_instancedata(void *data, t_libpd_freehook freehook); /// get per-instance user data EXTERN void* libpd_get_instancedata(void); /* log level */ /// set verbose print state: 0 or 1 EXTERN void libpd_set_verbose(int verbose); /// get the verbose print state: 0 or 1 EXTERN int libpd_get_verbose(void); #ifdef __cplusplus } #endif #endif ================================================ FILE: libs/libpd/pure-data/extra/README.txt ================================================ This is the README file for the "extras" library, which contains Pd objects which are too specialized or otherwise non-canonical for inclusion into Pd proper. This library is part of the regular ("vanilla") Pd distribution. It is all open source; see LICENSE.txt in the Pd distribution for details. contents: generally useful externs: [sigmund~] - pitch and sinusoidal peak analysis [bonk~] - percussion detector [lrshift~] - left or right shift an audio vector (probably should be standard) abstractions: [hilbert~] - Hilbert transform for SSB modulation [complex-mod~] - ring modulation for complex (real+imaginary) audio signals [rev1~], etc. - reverberators [output~] - stereo output abstraction for convenience externs aimed at particular tasks: [pd~] - embed one Pd inside another one [stdout] - send messages to standard out (useful in pd~ sub-process) [bob~] - Moog ladder filter simulation using a Runge-Kutte ODE solver [choice] - find the best fit of a vector to a set of example vectors [loop~] - sample looper obsolete: [pique] - fft-based peak finder (use [sigmund~] instead) [fiddle~] - pitch tracker (use [sigmund~] instead) The [sigmund~], [bonk~], and [fiddle~] objects have also been compiled for Max/MSP by Ted Apel; see his website for details. ================================================ FILE: libs/libpd/pure-data/extra/bob~/README.txt ================================================ The bob~ object. BSD licensed; Copyright notice is in bob~ source code. Imitates a Moog resonant filter by Runge-Kutte numerical integration of a differential equation approximately describing the dynamics of the circuit. Useful references: Tim Stilson Analyzing the Moog VCF with Considerations for Digital Implementation https://ccrma.stanford.edu/~stilti/papers/moogvcf.ps.gz (sections 1 and 2 are a reasonably good introduction but the model they use is highly idealized.) Timothy E. Stinchcombe Analysis of the Moog Transistor Ladder and Derivative Filters (long, but a very thorough description of how the filter works including its nonlinearities) Antti Huovilainen Non-linear digital implementation of the moog ladder filter (comes close to giving a differential equation for a reasonably realistic model of the filter). Th differential equations are: y1' = k * (S(x - r * y4) - S(y1)) y2' = k * (S(y1) - S(y2)) y3' = k * (S(y2) - S(y3)) y4' = k * (S(y3) - S(y4)) where k controls the cutoff frequency, r is feedback (<= 4 for stability), and S(x) is a saturation function. ================================================ FILE: libs/libpd/pure-data/extra/bob~/bob~.c ================================================ /* bob~ - use a differential equation solver to imitate an analogue circuit */ /* copyright 2015 Miller Puckette - BSD license */ #include "m_pd.h" #include #define DIM 4 #define FLOAT double /* if CALCERROR is defined we compute an error estaimate to verify the filter, outputting it from a second outlet on demand. This doubles the computation time, so it's only compiled in for testing. */ /* #define CALCERROR */ typedef struct _params { FLOAT p_input; FLOAT p_cutoff; FLOAT p_resonance; FLOAT p_saturation; FLOAT p_derivativeswere[DIM]; } t_params; /* imitate the (tanh) clipping function of a transistor pair. We hope/assume the C compiler is smart enough to inline this so use a function instead of a #define. */ #if 0 static FLOAT clip(FLOAT value, FLOAT saturation, FLOAT saturationinverse) { return (saturation * tanh(value * saturationinverse)); } #else /* cheaper way - to 4th order, tanh is x - x*x*x/3; this cubic's plateaus are at +/- 1 so clip to 1 and evaluate the cubic. This is pretty coarse - for instance if you clip a sinusoid this way you can sometimes hear the discontinuity in 4th derivative at the clip point */ static FLOAT clip(FLOAT value, FLOAT saturation, FLOAT saturationinverse) { float v2 = (value*saturationinverse > 1 ? 1 : (value*saturationinverse < -1 ? -1: value*saturationinverse)); return (saturation * (v2 - (1./3.) * v2 * v2 * v2)); } #endif static void calc_derivatives(FLOAT *dstate, FLOAT *state, t_params *params) { FLOAT k = ((float)(2*3.14159)) * params->p_cutoff; FLOAT sat = params->p_saturation, satinv = 1./sat; FLOAT satstate0 = clip(state[0], sat, satinv); FLOAT satstate1 = clip(state[1], sat, satinv); FLOAT satstate2 = clip(state[2], sat, satinv); dstate[0] = k * (clip(params->p_input - params->p_resonance * state[3], sat, satinv) - satstate0); dstate[1] = k * (satstate0 - satstate1); dstate[2] = k * (satstate1 - satstate2); dstate[3] = k * (satstate2 - clip(state[3], sat, satinv)); } static void solver_euler(FLOAT *state, FLOAT *errorestimate, FLOAT stepsize, t_params *params) { FLOAT cumerror = 0; int i; FLOAT derivatives[DIM]; calc_derivatives(derivatives, state, params); *errorestimate = 0; for (i = 0; i < DIM; i++) { state[i] += stepsize * derivatives[i]; *errorestimate += (derivatives[i] > params->p_derivativeswere[i] ? derivatives[i] - params->p_derivativeswere[i] : params->p_derivativeswere[i] - derivatives[i]); } for (i = 0; i < DIM; i++) params->p_derivativeswere[i] = derivatives[i]; } static void solver_rungekutte(FLOAT *state, FLOAT *errorestimate, FLOAT stepsize, t_params *params) { FLOAT cumerror = 0; int i; FLOAT deriv1[DIM], deriv2[DIM], deriv3[DIM], deriv4[DIM], tempstate[DIM]; FLOAT oldstate[DIM], backstate[DIM]; #if CALCERROR for (i = 0; i < DIM; i++) oldstate[i] = state[i]; #endif *errorestimate = 0; calc_derivatives(deriv1, state, params); for (i = 0; i < DIM; i++) tempstate[i] = state[i] + 0.5 * stepsize * deriv1[i]; calc_derivatives(deriv2, tempstate, params); for (i = 0; i < DIM; i++) tempstate[i] = state[i] + 0.5 * stepsize * deriv2[i]; calc_derivatives(deriv3, tempstate, params); for (i = 0; i < DIM; i++) tempstate[i] = state[i] + stepsize * deriv3[i]; calc_derivatives(deriv4, tempstate, params); for (i = 0; i < DIM; i++) state[i] += (1./6.) * stepsize * (deriv1[i] + 2 * deriv2[i] + 2 * deriv3[i] + deriv4[i]); #if CALCERROR calc_derivatives(deriv1, state, params); for (i = 0; i < DIM; i++) tempstate[i] = state[i] - 0.5 * stepsize * deriv1[i]; calc_derivatives(deriv2, tempstate, params); for (i = 0; i < DIM; i++) tempstate[i] = state[i] - 0.5 * stepsize * deriv2[i]; calc_derivatives(deriv3, tempstate, params); for (i = 0; i < DIM; i++) tempstate[i] = state[i] - stepsize * deriv3[i]; calc_derivatives(deriv4, tempstate, params); for (i = 0; i < DIM; i++) { backstate[i] = state[i ]- (1./6.) * stepsize * (deriv1[i] + 2 * deriv2[i] + 2 * deriv3[i] + deriv4[i]); *errorestimate += (backstate[i] > oldstate[i] ? backstate[i] - oldstate[i] : oldstate[i] - backstate[i]); } #endif } typedef struct _bob { t_object x_obj; t_float x_f; t_outlet *x_out1; /* signal output */ #ifdef CALCERROR t_outlet *x_out2; /* error estimate */ FLOAT x_cumerror; #endif t_params x_params; FLOAT x_state[DIM]; FLOAT x_sr; int x_oversample; int x_errorcount; } t_bob; static t_class *bob_class; static void bob_saturation(t_bob *x, t_float saturation) { if (saturation <= 1e-3) saturation = 1e-3; x->x_params.p_saturation = saturation; } static void bob_oversample(t_bob *x, t_float oversample) { if (oversample <= 1) oversample = 1; x->x_oversample = oversample; } static void bob_clear(t_bob *x) { int i; for (i = 0; i < DIM; i++) x->x_state[i] = x->x_params.p_derivativeswere[i] = 0; } static void bob_error(t_bob *x) { #ifdef CALCERROR outlet_float(x->x_out2, (x->x_errorcount ? x->x_cumerror/x->x_errorcount : 0)); x->x_cumerror = 0; x->x_errorcount = 0; #else post("error estimate unavailable (not compiled in)"); #endif } static void bob_print(t_bob *x) { int i; for (i = 0; i < DIM; i++) post("state %d: %f", i, x->x_state[i]); post("saturation %f", x->x_params.p_saturation); post("oversample %d", x->x_oversample); } static void *bob_new( void) { t_bob *x = (t_bob *)pd_new(bob_class); x->x_out1 = outlet_new(&x->x_obj, gensym("signal")); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); x->x_f = 0; bob_clear(x); bob_saturation(x, 3); bob_oversample(x, 2); #ifdef CALCERROR x->x_cumerror = 0; x->x_errorcount = 0; x->x_out2 = outlet_new(&x->x_obj, gensym("float")); #endif return (x); } static t_int *bob_perform(t_int *w) { t_bob *x = (t_bob *)(w[1]); t_float *in1 = (t_float *)(w[2]); t_float *cutoffin = (t_float *)(w[3]); t_float *resonancein = (t_float *)(w[4]); t_float *out = (t_float *)(w[5]); /* bug fix: output is last state variable, not first */ FLOAT *outstate = (pd_compatibilitylevel > 51? &x->x_state[3] : &x->x_state[0]); int n = (int)(w[6]), i, j; FLOAT stepsize = 1./(x->x_oversample * x->x_sr); FLOAT errorestimate; for (i = 0; i < n; i++) { x->x_params.p_input = *in1++; x->x_params.p_cutoff = *cutoffin++; if ((x->x_params.p_resonance = *resonancein++) < 0) x->x_params.p_resonance = 0; for (j = 0; j < x->x_oversample; j++) solver_rungekutte(x->x_state, &errorestimate, stepsize, &x->x_params); *out++ = *outstate; #if CALCERROR x->x_cumerror += errorestimate; x->x_errorcount++; #endif } return (w+7); } static void bob_dsp(t_bob *x, t_signal **sp) { x->x_sr = sp[0]->s_sr; dsp_add(bob_perform, 6, x, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec, (t_int)sp[0]->s_n); } void bob_tilde_setup(void) { int i; bob_class = class_new(gensym("bob~"), (t_newmethod)bob_new, 0, sizeof(t_bob), 0, 0); class_addmethod(bob_class, (t_method)bob_saturation, gensym("saturation"), A_FLOAT, 0); class_addmethod(bob_class, (t_method)bob_oversample, gensym("oversample"), A_FLOAT, 0); class_addmethod(bob_class, (t_method)bob_clear, gensym("clear"), 0); class_addmethod(bob_class, (t_method)bob_print, gensym("print"), 0); class_addmethod(bob_class, (t_method)bob_error, gensym("error"), 0); class_addmethod(bob_class, (t_method)bob_dsp, gensym("dsp"), A_CANT, 0); CLASS_MAINSIGNALIN(bob_class, t_bob, x_f); } ================================================ FILE: libs/libpd/pure-data/extra/bonk~/bonk~.c ================================================ /* ########################################################################### # bonk~ - a Max/MSP external # by miller puckette and ted apel # http://crca.ucsd.edu/~msp/ # Max/MSP port by barry threw # http://www.barrythrew.com # me@barrythrew.com # San Francisco, CA # (c) 2008 # for Kesumo - http://www.kesumo.com ########################################################################### // bonk~ detects attacks in an audio signal ########################################################################### This software is copyrighted by Miller Puckette and others. The following terms (the "Standard Improved BSD License") apply to all files associated with the software unless explicitly disclaimed in individual files: Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* dolist: decay and other times in msec */ #include #include #include /* These pragmas are only used for MSVC, not MinGW or Cygwin */ #ifdef _MSC_VER #pragma warning (disable: 4305 4244) #endif #ifdef MSP #include "ext.h" #include "z_dsp.h" #include "math.h" #include "ext_support.h" #include "ext_proto.h" #include "ext_obex.h" typedef double t_floatarg; /* from m_pd.h */ typedef float t_float; /* from m_pd.h */ #define flog log #define fexp exp #define fsqrt sqrt #define t_resizebytes(a, b, c) t_resizebytes((char *)(a), (b), (c)) void *bonk_class; #define getbytes t_getbytes #define freebytes t_freebytes #endif /* MSP */ #ifdef PD #include "m_pd.h" static t_class *bonk_class; #endif #ifdef _WIN32 # include /* MSVC or mingw on windows */ #elif defined(__linux__) || defined(__APPLE__) # include /* linux, mac, mingw, cygwin */ #else # include /* BSDs for example */ #endif /* ------------------------ bonk~ ----------------------------- */ #define DEFNPOINTS 256 #define MAXCHANNELS 8 #define MINPOINTS 64 #define DEFPERIOD 128 #define DEFNFILTERS 11 #define DEFHALFTONES 6 #define DEFOVERLAP 1 #define DEFFIRSTBIN 1 #define DEFMINBANDWIDTH 1.5 #define DEFHITHRESH 5 #define DEFLOTHRESH 2.5 #define DEFMASKTIME 4 #define DEFMASKDECAY 0.7 #define DEFDEBOUNCEDECAY 0 #define DEFMINVEL 7 #define DEFATTACKBINS 1 #define MAXATTACKWAIT 4 typedef struct _filterkernel { int k_filterpoints; int k_hoppoints; int k_skippoints; int k_nhops; t_float k_centerfreq; /* center frequency, bins */ t_float k_bandwidth; /* bandwidth, bins */ t_float *k_stuff; } t_filterkernel; typedef struct _filterbank { int b_nfilters; /* number of filters in bank */ int b_npoints; /* input vector size */ t_float b_halftones; /* filter bandwidth in halftones */ t_float b_overlap; /* overlap; default 1 for 1/2-power pts */ t_float b_firstbin; /* freq of first filter in bins, default 1 */ t_float b_minbandwidth; /* minimum bandwidth, default 1.5 */ t_filterkernel *b_vec; /* filter kernels */ int b_refcount; /* number of bonk~ objects using this */ struct _filterbank *b_next; /* next in linked list */ } t_filterbank; #if 0 /* this is the design for 1.0: */ static t_filterkernel bonk_filterkernels[] = {{256, 2, .01562}, {256, 4, .01562}, {256, 6, .01562}, {180, 6, .02222}, {128, 6, .01803}, {90, 6, .02222}, {64, 6, .02362}, {46, 6, .02773}, {32, 6, .03227}, {22, 6, .03932}, {16, 6, .04489}}; #endif #if 0 /* here's the 1.1 rev: */ static t_filterkernel bonk_filterkernels[] = {{256, 1, .01562, 0}, {256, 3, .01562, 0}, {256, 5, .01562, 0}, {212, 6, .01886, 0}, {150, 6, .01885, 0}, {106, 6, .02179, 0}, {76, 6, .0236, 0}, {54, 6, .02634, 0}, {38, 6, .03047, 0}, {26, 6, .03667, 0}, {18, 6, .04458, 0}}; #define NFILTERS \ ((int)(sizeof(bonk_filterkernels) / sizeof(bonk_filterkernels[0]))) #endif #if 0 /* and 1.2 */ #define NFILTERS 11 static t_filterkernel bonk_filterkernels[NFILTERS]; #endif /* and 1.3 */ #define MAXNFILTERS 200 #define MASKHIST 8 static t_filterbank *bonk_filterbanklist; typedef struct _hist { t_float h_power; t_float h_before; t_float h_outpower; int h_countup; t_float h_mask[MASKHIST]; } t_hist; typedef struct template { t_float t_amp[MAXNFILTERS]; } t_template; typedef struct _insig { t_hist g_hist[MAXNFILTERS]; /* history for each filter */ #ifdef PD t_outlet *g_outlet; /* outlet for raw data */ #endif #ifdef MSP void *g_outlet; /* outlet for raw data */ #endif t_float *g_inbuf; /* buffered input samples */ t_sample *g_invec; /* new input samples */ } t_insig; typedef struct _bonk { #ifdef PD t_object x_obj; t_outlet *x_cookedout; t_clock *x_clock; t_canvas *x_canvas; /* ptr to current canvas --fbar */ #endif /* PD */ #ifdef MSP t_pxobject x_obj; void *obex; void *x_cookedout; void *x_clock; #endif /* MSP */ /* parameters */ int x_npoints; /* number of points in input buffer */ int x_period; /* number of input samples between analyses */ int x_nfilters; /* number of filters requested */ t_float x_halftones; /* nominal halftones between filters */ t_float x_overlap; t_float x_firstbin; t_float x_minbandwidth; t_float x_hithresh; /* threshold for total growth to trigger */ t_float x_lothresh; /* threshold for total growth to re-arm */ t_float x_minvel; /* minimum velocity we output */ t_float x_maskdecay; int x_masktime; int x_useloudness; /* use loudness spectra instead of power */ t_float x_debouncedecay; t_float x_debouncevel; double x_learndebounce; /* debounce time (in "learn" mode only) */ int x_attackbins; /* number of bins to wait for attack */ t_filterbank *x_filterbank; t_hist x_hist[MAXNFILTERS]; t_template *x_template; t_insig *x_insig; int x_ninsig; int x_ntemplate; int x_infill; int x_countdown; int x_willattack; int x_attacked; int x_debug; int x_learn; int x_learncount; /* countup for "learn" mode */ int x_spew; /* if true, always generate output! */ int x_maskphase; /* phase, 0 to MASKHIST-1, for mask history */ t_float x_sr; /* current sample rate in Hz. */ int x_hit; /* next "tick" called because of a hit, not a poll */ } t_bonk; #ifdef MSP static void *bonk_new(t_symbol *s, long ac, t_atom *av); static void bonk_tick(t_bonk *x); static void bonk_doit(t_bonk *x); static void bonk_perform_generic(t_bonk *x, int n); static t_int *bonk_perform(t_int *w); static void bonk_perform64(t_bonk *x, t_object *dsp64, double **ins, long numins, double **outs, long numouts, long sampleframes, long flags, void *userparam); static void bonk_dsp(t_bonk *x, t_signal **sp); static void bonk_dsp64(t_bonk *x, t_object *dsp64, short *count, double samplerate, long maxvectorsize, long flags); void bonk_assist(t_bonk *x, void *b, long m, long a, char *s); static void bonk_free(t_bonk *x); void bonk_setup(void); int main(); static void bonk_thresh(t_bonk *x, t_floatarg f1, t_floatarg f2); static void bonk_print(t_bonk *x, t_floatarg f); static void bonk_bang(t_bonk *x); static void bonk_write(t_bonk *x, t_symbol *s); static void bonk_dowrite(t_bonk *x, t_symbol *s); static void bonk_writefile(t_bonk *x, char *filename, short path); static void bonk_read(t_bonk *x, t_symbol *s); static void bonk_doread(t_bonk *x, t_symbol *s); static void bonk_openfile(t_bonk *x, char *filename, short path); void bonk_minvel_set(t_bonk *x, void *attr, long ac, t_atom *av); void bonk_lothresh_set(t_bonk *x, void *attr, long ac, t_atom *av); void bonk_hithresh_set(t_bonk *x, void *attr, long ac, t_atom *av); void bonk_masktime_set(t_bonk *x, void *attr, long ac, t_atom *av); void bonk_maskdecay_set(t_bonk *x, void *attr, long ac, t_atom *av); void bonk_debouncedecay_set(t_bonk *x, void *attr, long ac, t_atom *av); void bonk_debug_set(t_bonk *x, void *attr, long ac, t_atom *av); void bonk_spew_set(t_bonk *x, void *attr, long ac, t_atom *av); void bonk_useloudness_set(t_bonk *x, void *attr, long ac, t_atom *av); void bonk_attackbins_set(t_bonk *x, void *attr, long ac, t_atom *av); void bonk_learn_set(t_bonk *x, void *attr, long ac, t_atom *av); t_float qrsqrt(t_float f); double clock_getsystime(); double clock_gettimesince(double prevsystime); char *strcpy(char *s1, const char *s2); #define SETFLOAT A_SETFLOAT #endif static void bonk_tick(t_bonk *x); #define HALFWIDTH 0.75 /* half peak bandwidth at half power point in bins */ #define SLIDE 0.25 /* relative slide between filter subwindows */ static t_filterbank *bonk_newfilterbank(int npoints, int nfilters, t_float halftones, t_float overlap, t_float firstbin, t_float minbandwidth) { int i, j; t_float cf, bw, h, relspace; t_filterbank *b = (t_filterbank *)getbytes(sizeof(*b)); b->b_npoints = npoints; b->b_nfilters = nfilters; b->b_halftones = halftones; b->b_overlap = overlap; b->b_firstbin = firstbin; b->b_minbandwidth = minbandwidth; b->b_refcount = 0; b->b_next = bonk_filterbanklist; bonk_filterbanklist = b; b->b_vec = (t_filterkernel *)getbytes(nfilters * sizeof(*b->b_vec)); h = exp((log(2.)/12.)*halftones); /* specced interval between filters */ relspace = (h - 1)/(h + 1); /* nominal spacing-per-f for fbank */ if (minbandwidth < 2*HALFWIDTH) minbandwidth = 2*HALFWIDTH; if (firstbin < minbandwidth/(2*HALFWIDTH)) firstbin = minbandwidth/(2*HALFWIDTH); cf = firstbin; bw = cf * relspace * overlap; if (bw < (0.5*minbandwidth)) bw = (0.5*minbandwidth); for (i = 0; i < nfilters; i++) { t_float *fp, newcf, newbw; t_float normalizer = 0; int filterpoints, skippoints, hoppoints, nhops; filterpoints = npoints * HALFWIDTH/bw; if (cf > npoints/2) { post("bonk~: only using %d filters (ran past Nyquist)", i+1); break; } if (filterpoints < 4) { post("bonk~: only using %d filters (kernels got too short)", i+1); break; } else if (filterpoints > npoints) filterpoints = npoints; hoppoints = SLIDE * npoints * HALFWIDTH/bw; nhops = 1. + (npoints-filterpoints)/(t_float)hoppoints; skippoints = 0.5 * (npoints-filterpoints - (nhops-1) * hoppoints); b->b_vec[i].k_stuff = (t_float *)getbytes(2 * sizeof(t_float) * filterpoints); b->b_vec[i].k_filterpoints = filterpoints; b->b_vec[i].k_nhops = nhops; b->b_vec[i].k_hoppoints = hoppoints; b->b_vec[i].k_skippoints = skippoints; b->b_vec[i].k_centerfreq = cf; b->b_vec[i].k_bandwidth = bw; for (fp = b->b_vec[i].k_stuff, j = 0; j < filterpoints; j++, fp+= 2) { t_float phase = j * cf * (2*3.141592653589793 / npoints); t_float wphase = j * (2*3.141592653589793 / filterpoints); t_float window = sin(0.5*wphase); fp[0] = window * cos(phase); fp[1] = window * sin(phase); normalizer += window; } normalizer = 1/(normalizer * sqrt(nhops)); for (fp = b->b_vec[i].k_stuff, j = 0; j < filterpoints; j++, fp+= 2) fp[0] *= normalizer, fp[1] *= normalizer; #if 0 post("i %d cf %.2f bw %.2f nhops %d, hop %d, skip %d, npoints %d", i, cf, bw, nhops, hoppoints, skippoints, filterpoints); #endif newcf = (cf + bw/overlap)/(1 - relspace); newbw = newcf * overlap * relspace; if (newbw < 0.5*minbandwidth) { newbw = 0.5*minbandwidth; newcf = cf + minbandwidth / overlap; } cf = newcf; bw = newbw; } for (; i < nfilters; i++) b->b_vec[i].k_stuff = 0, b->b_vec[i].k_filterpoints = 0; return (b); } static void bonk_freefilterbank(t_filterbank *b) { t_filterbank *b2, *b3; int i; if (bonk_filterbanklist == b) bonk_filterbanklist = b->b_next; else for (b2 = bonk_filterbanklist; (b3 = b2->b_next); b2 = b3) if (b3 == b) { b2->b_next = b3->b_next; break; } for (i = 0; i < b->b_nfilters; i++) if (b->b_vec[i].k_stuff) freebytes(b->b_vec[i].k_stuff, b->b_vec[i].k_filterpoints * sizeof(t_float)); freebytes(b->b_vec, b->b_nfilters * sizeof(*b->b_vec)); freebytes(b, sizeof(*b)); } static void bonk_donew(t_bonk *x, int npoints, int period, int nsig, int nfilters, t_float halftones, t_float overlap, t_float firstbin, t_float minbandwidth, t_float samplerate) { int i, j; t_hist *h; t_float *fp; t_insig *g; t_filterbank *fb; for (j = 0, g = x->x_insig; j < nsig; j++, g++) { for (i = 0, h = g->g_hist; i--; h++) { h->h_power = h->h_before = 0, h->h_countup = 0; for (j = 0; j < MASKHIST; j++) h->h_mask[j] = 0; } /* we ought to check for failure to allocate memory here */ g->g_inbuf = (t_float *)getbytes(npoints * sizeof(t_float)); for (i = npoints, fp = g->g_inbuf; i--; fp++) *fp = 0; } if (!period) period = npoints/2; x->x_npoints = npoints; x->x_period = period; x->x_ninsig = nsig; x->x_nfilters = (nfilters > MAXNFILTERS ? MAXNFILTERS : nfilters); x->x_halftones = halftones; x->x_template = (t_template *)getbytes(0); x->x_ntemplate = 0; x->x_infill = 0; x->x_countdown = 0; x->x_willattack = 0; x->x_attacked = 0; x->x_maskphase = 0; x->x_debug = 0; x->x_hithresh = DEFHITHRESH; x->x_lothresh = DEFLOTHRESH; x->x_masktime = DEFMASKTIME; x->x_maskdecay = DEFMASKDECAY; x->x_learn = 0; x->x_learndebounce = clock_getsystime(); x->x_learncount = 0; x->x_debouncedecay = DEFDEBOUNCEDECAY; x->x_minvel = DEFMINVEL; x->x_useloudness = 0; x->x_debouncevel = 0; x->x_attackbins = DEFATTACKBINS; x->x_sr = samplerate; x->x_filterbank = 0; x->x_hit = 0; for (fb = bonk_filterbanklist; fb; fb = fb->b_next) if (fb->b_nfilters == x->x_nfilters && fb->b_halftones == x->x_halftones && fb->b_firstbin == firstbin && fb->b_overlap == overlap && fb->b_npoints == x->x_npoints && fb->b_minbandwidth == minbandwidth) { fb->b_refcount++; x->x_filterbank = fb; break; } if (!x->x_filterbank) x->x_filterbank = bonk_newfilterbank(npoints, nfilters, halftones, overlap, firstbin, minbandwidth), x->x_filterbank->b_refcount++; } static void bonk_tick(t_bonk *x) { t_atom at[MAXNFILTERS], *ap, at2[3]; int i, j, k, n; t_hist *h; t_float *pp, vel = 0., temperature = 0.; t_float *fp; t_template *tp; int nfit, ninsig = x->x_ninsig, ntemplate = x->x_ntemplate, nfilters = x->x_nfilters; t_insig *gp; #ifdef _MSC_VER t_float powerout[MAXNFILTERS*MAXCHANNELS]; #else t_float *powerout = alloca(x->x_nfilters * x->x_ninsig * sizeof(*powerout)); #endif for (i = ninsig, pp = powerout, gp = x->x_insig; i--; gp++) { for (j = 0, h = gp->g_hist; j < nfilters; j++, h++, pp++) { t_float power = h->h_outpower; t_float intensity = *pp = (power > 0. ? 100. * qrsqrt(qrsqrt(power)) : 0.); vel += intensity; temperature += intensity * (t_float)j; } } if (vel > 0) temperature /= vel; else temperature = 0; vel *= 0.5 / ninsig; /* fudge factor */ if (x->x_hit) { /* if hit nonzero it's a clock callback. if in "learn" mode update the template list; in any event match the hit to known templates. */ if (vel < x->x_debouncevel) { if (x->x_debug) post("bounce cancelled: vel %f debounce %f", vel, x->x_debouncevel); return; } if (vel < x->x_minvel) { if (x->x_debug) post("low velocity cancelled: vel %f, minvel %f", vel, x->x_minvel); return; } x->x_debouncevel = vel; if (x->x_learn) { double lasttime = x->x_learndebounce; double msec = clock_gettimesince(lasttime); if ((!ntemplate) || (msec > 200)) { int countup = x->x_learncount; /* normalize to 100 */ t_float norm; for (i = nfilters * ninsig, norm = 0, pp = powerout; i--; pp++) norm += *pp * *pp; if (norm < 1.0e-15) norm = 1.0e-15; norm = 100. * qrsqrt(norm); /* check if this is the first strike for a new template */ if (!countup) { int oldn = ntemplate; x->x_ntemplate = ntemplate = oldn + ninsig; x->x_template = (t_template *)t_resizebytes(x->x_template, oldn * sizeof(x->x_template[0]), ntemplate * sizeof(x->x_template[0])); for (i = ninsig, pp = powerout; i--; oldn++) for (j = nfilters, fp = x->x_template[oldn].t_amp; j--; pp++, fp++) *fp = *pp * norm; } else { int oldn = ntemplate - ninsig; if (oldn < 0) post("bonk_tick bug"); for (i = ninsig, pp = powerout; i--; oldn++) { for (j = nfilters, fp = x->x_template[oldn].t_amp; j--; pp++, fp++) *fp = (countup * *fp + *pp * norm) /(countup + 1.0); } } countup++; if (countup == x->x_learn) countup = 0; x->x_learncount = countup; } else return; } x->x_learndebounce = clock_getsystime(); if (ntemplate) { t_float bestfit = -1e30; int templatecount; nfit = -1; for (i = 0, templatecount = 0, tp = x->x_template; templatecount < ntemplate; i++) { t_float dotprod = 0; for (k = 0, pp = powerout; k < ninsig && templatecount < ntemplate; k++, tp++, templatecount++) { for (j = nfilters, fp = tp->t_amp; j--; fp++, pp++) { if (*fp < 0 || *pp < 0) post("bonk_tick bug 2"); dotprod += *fp * *pp; } } if (dotprod > bestfit) { bestfit = dotprod; nfit = i; } } if (nfit < 0) post("bonk_tick bug"); } else nfit = 0; } else nfit = -1; /* hit is zero; this is the "bang" method. */ x->x_attacked = 1; if (x->x_debug) post("bonk out: number %d, vel %f, temperature %f", nfit, vel, temperature); SETFLOAT(at2, nfit); SETFLOAT(at2+1, vel); SETFLOAT(at2+2, temperature); outlet_list(x->x_cookedout, 0, 3, at2); for (n = 0, gp = x->x_insig + (ninsig-1), pp = powerout + nfilters * (ninsig-1); n < ninsig; n++, gp--, pp -= nfilters) { t_float *pp2; for (i = 0, ap = at, pp2 = pp; i < nfilters; i++, ap++, pp2++) { ap->a_type = A_FLOAT; ap->a_w.w_float = *pp2; } outlet_list(gp->g_outlet, 0, nfilters, at); } } static void bonk_doit(t_bonk *x) { int i, j, ch, n; t_filterkernel *k; t_hist *h; t_float growth = 0, *fp1, *fp3, *fp4, hithresh, lothresh; int ninsig = x->x_ninsig, nfilters = x->x_nfilters, maskphase = x->x_maskphase, nextphase, oldmaskphase; t_insig *gp; nextphase = maskphase + 1; if (nextphase >= MASKHIST) nextphase = 0; x->x_maskphase = nextphase; oldmaskphase = nextphase - x->x_attackbins; if (oldmaskphase < 0) oldmaskphase += MASKHIST; if (x->x_useloudness) hithresh = qrsqrt(qrsqrt(x->x_hithresh)), lothresh = qrsqrt(qrsqrt(x->x_lothresh)); else hithresh = x->x_hithresh, lothresh = x->x_lothresh; for (ch = 0, gp = x->x_insig; ch < ninsig; ch++, gp++) { for (i = 0, k = x->x_filterbank->b_vec, h = gp->g_hist; i < nfilters; i++, k++, h++) { t_float power = 0, maskpow = h->h_mask[maskphase]; t_float *inbuf= gp->g_inbuf + k->k_skippoints; int countup = h->h_countup; int filterpoints = k->k_filterpoints; /* if the user asked for more filters that fit under the Nyquist frequency, some filters won't actually be filled in so we skip running them. */ if (!filterpoints) { h->h_countup = 0; h->h_mask[nextphase] = 0; h->h_power = 0; continue; } /* run the filter repeatedly, sliding it forward by hoppoints, for nhop times */ for (fp1 = inbuf, n = 0; n < k->k_nhops; fp1 += k->k_hoppoints, n++) { t_float rsum = 0, isum = 0; for (fp3 = fp1, fp4 = k->k_stuff, j = filterpoints; j--;) { t_float g = *fp3++; rsum += g * *fp4++; isum += g * *fp4++; } power += rsum * rsum + isum * isum; } if (!x->x_willattack) h->h_before = maskpow; if (power > h->h_mask[oldmaskphase]) { if (x->x_useloudness) growth += qrsqrt(qrsqrt( power/(h->h_mask[oldmaskphase] + 1.0e-15))) - 1.; else growth += power/(h->h_mask[oldmaskphase] + 1.0e-15) - 1.; } if (!x->x_willattack && countup >= x->x_masktime) maskpow *= x->x_maskdecay; if (power > maskpow) { maskpow = power; countup = 0; } countup++; h->h_countup = countup; h->h_mask[nextphase] = maskpow; h->h_power = power; } } if (x->x_willattack) { if (x->x_willattack > MAXATTACKWAIT || growth < x->x_lothresh) { /* if haven't yet, and if not in spew mode, report a hit */ if (!x->x_spew && !x->x_attacked) { for (ch = 0, gp = x->x_insig; ch < ninsig; ch++, gp++) for (i = nfilters, h = gp->g_hist; i--; h++) h->h_outpower = h->h_mask[nextphase]; x->x_hit = 1; clock_delay(x->x_clock, 0); } } if (growth < x->x_lothresh) x->x_willattack = 0; else x->x_willattack++; } else if (growth > x->x_hithresh) { if (x->x_debug) post("attack: growth = %f", growth); x->x_willattack = 1; x->x_attacked = 0; for (ch = 0, gp = x->x_insig; ch < ninsig; ch++, gp++) for (i = nfilters, h = gp->g_hist; i--; h++) h->h_mask[nextphase] = h->h_power, h->h_countup = 0; } /* if in "spew" mode just always output */ if (x->x_spew) { for (ch = 0, gp = x->x_insig; ch < ninsig; ch++, gp++) for (i = nfilters, h = gp->g_hist; i--; h++) h->h_outpower = h->h_power; x->x_hit = 0; clock_delay(x->x_clock, 0); } x->x_debouncevel *= x->x_debouncedecay; } static void bonk_perform_generic(t_bonk *x, int n) { int onset = 0; if (x->x_countdown >= n) x->x_countdown -= n; else { int i, j, ninsig = x->x_ninsig; t_insig *gp; if (x->x_countdown > 0) { n -= x->x_countdown; onset += x->x_countdown; x->x_countdown = 0; } while (n > 0) { int infill = x->x_infill; int m = (n < (x->x_npoints - infill) ? n : (x->x_npoints - infill)); for (i = 0, gp = x->x_insig; i < ninsig; i++, gp++) { t_float *fp = gp->g_inbuf + infill; t_sample *in1 = gp->g_invec + onset; for (j = 0; j < m; j++) *fp++ = *in1++; } infill += m; x->x_infill = infill; if (infill == x->x_npoints) { bonk_doit(x); /* shift or clear the input buffer and update counters */ if (x->x_period > x->x_npoints) x->x_countdown = x->x_period - x->x_npoints; else x->x_countdown = 0; if (x->x_period < x->x_npoints) { int overlap = x->x_npoints - x->x_period; t_float *fp1, *fp2; for (n = 0, gp = x->x_insig; n < ninsig; n++, gp++) for (i = overlap, fp1 = gp->g_inbuf, fp2 = fp1 + x->x_period; i--;) *fp1++ = *fp2++; x->x_infill = overlap; } else x->x_infill = 0; } n -= m; onset += m; } } } static t_int *bonk_perform(t_int *w) { t_bonk *x = (t_bonk *)(w[1]); int n = (int)(w[2]); bonk_perform_generic(x, n); return (w+3); } static void bonk_dsp(t_bonk *x, t_signal **sp) { int i, n = sp[0]->s_n, ninsig = x->x_ninsig; t_insig *gp; x->x_sr = sp[0]->s_sr; for (i = 0, gp = x->x_insig; i < ninsig; i++, gp++) gp->g_invec = (*(sp++))->s_vec; dsp_add(bonk_perform, 2, x, (t_int)n); } static void bonk_thresh(t_bonk *x, t_floatarg f1, t_floatarg f2) { if (f1 > f2) post("bonk: warning: low threshold greater than hi threshold"); x->x_lothresh = (f1 <= 0 ? 0.0001 : f1); x->x_hithresh = (f2 <= 0 ? 0.0001 : f2); } #ifdef PD static void bonk_mask(t_bonk *x, t_floatarg f1, t_floatarg f2) { int ticks = f1; if (ticks < 0) ticks = 0; if (f2 < 0) f2 = 0; else if (f2 > 1) f2 = 1; x->x_masktime = ticks; x->x_maskdecay = f2; } static void bonk_debounce(t_bonk *x, t_floatarg f1) { if (f1 < 0) f1 = 0; else if (f1 > 1) f1 = 1; x->x_debouncedecay = f1; } static void bonk_minvel(t_bonk *x, t_floatarg f) { if (f < 0) f = 0; x->x_minvel = f; } static void bonk_debug(t_bonk *x, t_floatarg f) { x->x_debug = (f != 0); } static void bonk_spew(t_bonk *x, t_floatarg f) { x->x_spew = (f != 0); } static void bonk_useloudness(t_bonk *x, t_floatarg f) { x->x_useloudness = (f != 0); } static void bonk_attackbins(t_bonk *x, t_floatarg f) { if (f < 1) f = 1; else if (f > MASKHIST) f = MASKHIST; x->x_attackbins = f; } static void bonk_learn(t_bonk *x, t_floatarg f) { int n = f; if (n < 0) n = 0; if (n) { x->x_template = (t_template *)t_resizebytes(x->x_template, x->x_ntemplate * sizeof(x->x_template[0]), 0); x->x_ntemplate = 0; } x->x_learn = n; x->x_learncount = 0; } #endif static void bonk_print(t_bonk *x, t_floatarg f) { int i; post("thresh %f %f", x->x_lothresh, x->x_hithresh); post("mask %d %f", x->x_masktime, x->x_maskdecay); post("attack-frames %d", x->x_attackbins); post("debounce %f", x->x_debouncedecay); post("minvel %f", x->x_minvel); post("spew %d", x->x_spew); post("useloudness %d", x->x_useloudness); #if 0 /* LATER rewrite without hard-coded 11 filters */ if (x->x_ntemplate) { post("templates:"); for (i = 0; i < x->x_ntemplate; i++) post( "%2d %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f", i, x->x_template[i].t_amp[0], x->x_template[i].t_amp[1], x->x_template[i].t_amp[2], x->x_template[i].t_amp[3], x->x_template[i].t_amp[4], x->x_template[i].t_amp[5], x->x_template[i].t_amp[6], x->x_template[i].t_amp[7], x->x_template[i].t_amp[8], x->x_template[i].t_amp[9], x->x_template[i].t_amp[10]); } else post("no templates"); #endif post("number of templates %d", x->x_ntemplate); if (x->x_learn) post("learn mode"); if (f != 0) { int j, ninsig = x->x_ninsig; t_insig *gp; for (j = 0, gp = x->x_insig; j < ninsig; j++, gp++) { t_hist *h; if (ninsig > 1) post("input %d:", j+1); for (i = x->x_nfilters, h = gp->g_hist; i--; h++) post("pow %f mask %f before %f count %d", h->h_power, h->h_mask[x->x_maskphase], h->h_before, h->h_countup); } post("bin size %.2f Hz ... filters:", x->x_sr/x->x_npoints); for (j = 0; j < x->x_nfilters; j++) post("\ %2d cf %.2f(%.2f bins) bw %.2f(%.2f) nhops %d hop %d skip %d npoints %d", j, x->x_filterbank->b_vec[j].k_centerfreq*x->x_sr/x->x_npoints, x->x_filterbank->b_vec[j].k_centerfreq, x->x_filterbank->b_vec[j].k_bandwidth*x->x_sr/x->x_npoints, x->x_filterbank->b_vec[j].k_bandwidth, x->x_filterbank->b_vec[j].k_nhops, x->x_filterbank->b_vec[j].k_hoppoints, x->x_filterbank->b_vec[j].k_skippoints, x->x_filterbank->b_vec[j].k_filterpoints); } if (x->x_debug) post("debug mode"); } static void bonk_forget(t_bonk *x) { int ntemplate = x->x_ntemplate, newn = ntemplate - x->x_ninsig; if (newn < 0) newn = 0; x->x_template = (t_template *)t_resizebytes(x->x_template, x->x_ntemplate * sizeof(x->x_template[0]), newn * sizeof(x->x_template[0])); x->x_ntemplate = newn; x->x_learncount = 0; } static void bonk_bang(t_bonk *x) { int i, ch; t_insig *gp; x->x_hit = 0; for (ch = 0, gp = x->x_insig; ch < x->x_ninsig; ch++, gp++) { t_hist *h; for (i = 0, h = gp->g_hist; i < x->x_nfilters; i++, h++) h->h_outpower = h->h_power; } bonk_tick(x); } #ifdef PD static void bonk_read(t_bonk *x, t_symbol *s) { float vec[MAXNFILTERS]; int i, ntemplate = 0, remaining; float *fp; t_float *fp2; /* fbar: canvas_open code taken from g_array.c */ FILE *fd; char buf[MAXPDSTRING], *bufptr; int filedesc; if ((filedesc = canvas_open(x->x_canvas, s->s_name, "", buf, &bufptr, MAXPDSTRING, 0)) < 0 || !(fd = fdopen(filedesc, "r"))) { post("%s: open failed", s->s_name); return; } x->x_template = (t_template *)t_resizebytes(x->x_template, x->x_ntemplate * sizeof(t_template), 0); while (1) { for (i = x->x_nfilters, fp = vec; i--; fp++) if (fscanf(fd, "%f", fp) < 1) goto nomore; x->x_template = (t_template *)t_resizebytes(x->x_template, ntemplate * sizeof(t_template), (ntemplate + 1) * sizeof(t_template)); for (i = x->x_nfilters, fp = vec, fp2 = x->x_template[ntemplate].t_amp; i--;) *fp2++ = *fp++; ntemplate++; } nomore: if ((remaining = (ntemplate % x->x_ninsig))) { post("bonk_read: %d templates not a multiple of %d; dropping extras"); x->x_template = (t_template *)t_resizebytes(x->x_template, ntemplate * sizeof(t_template), (ntemplate - remaining) * sizeof(t_template)); ntemplate = ntemplate - remaining; } post("bonk: read %d templates\n", ntemplate); x->x_ntemplate = ntemplate; fclose(fd); } #endif #ifdef MSP static void bonk_perform64(t_bonk *x, t_object *dsp64, double **ins, long numins, double **outs, long numouts, long sampleframes, long flags, void *userparam) { int n = sampleframes; int i = sampleframes, ninsig = x->x_ninsig; t_insig *gp; for (i = 0, gp = x->x_insig; i < ninsig; i++, gp++) gp->g_invec = ins[0]; bonk_perform_generic(x, n); } static void bonk_dsp64(t_bonk *x, t_object *dsp64, short *count, double samplerate, long maxvectorsize, long flags) { x->x_sr = samplerate; object_method(dsp64, gensym("dsp_add64"), x, bonk_perform64, 0, NULL); } static void bonk_read(t_bonk *x, t_symbol *s) { defer(x, (method)bonk_doread, s, 0, NULL); } static void bonk_doread(t_bonk *x, t_symbol *s) { t_fourcc filetype = 'TEXT', outtype; char filename[512]; short path; if (s == gensym("")) { if (open_dialog(filename, &path, &outtype, &filetype, 1)) return; } else { strcpy(filename, s->s_name); if (locatefile_extended(filename, &path, &outtype, &filetype, 1)) { object_error((t_object *) x, "%s: not found", s->s_name); return; } } // we have a file bonk_openfile(x, filename, path); } static void bonk_openfile(t_bonk *x, char *filename, short path) { t_float vec[MAXNFILTERS]; int i, ntemplate = 0, remaining; t_float *fp, *fp2; t_filehandle fh; char **texthandle; char *tokptr; t_ptr_size size; if (path_opensysfile(filename, path, &fh, READ_PERM)) { object_error((t_object *) x, "error opening %s", filename); return; } sysfile_geteof(fh, &size); texthandle = sysmem_newhandleclear(size + 1); sysfile_readtextfile(fh, texthandle, 0, TEXT_LB_NATIVE); sysfile_close(fh); x->x_template = (t_template *)t_resizebytes(x->x_template, x->x_ntemplate * sizeof(t_template), 0); tokptr = strtok(*texthandle, " \n"); while(tokptr != NULL) { for (i = x->x_nfilters, fp = vec; i--; fp++) { if (sscanf(tokptr, "%f", fp) < 1) goto nomore; tokptr = strtok(NULL, " \n"); } x->x_template = (t_template *)t_resizebytes(x->x_template, ntemplate * sizeof(t_template), (ntemplate + 1) * sizeof(t_template)); for (i = x->x_nfilters, fp = vec, fp2 = x->x_template[ntemplate].t_amp; i--;) *fp2++ = *fp++; ntemplate++; } nomore: if ((remaining = (ntemplate % x->x_ninsig))) { post("bonk_read: %d templates not a multiple of %d; dropping extras"); x->x_template = (t_template *)t_resizebytes(x->x_template, ntemplate * sizeof(t_template), (ntemplate - remaining) * sizeof(t_template)); ntemplate = ntemplate - remaining; } sysmem_freehandle(texthandle); post("bonk: read %d templates\n", ntemplate); x->x_ntemplate = ntemplate; } #endif #ifdef PD static void bonk_write(t_bonk *x, t_symbol *s) { FILE *fd; char buf[MAXPDSTRING]; /* fbar */ int i, ntemplate = x->x_ntemplate; t_template *tp = x->x_template; t_float *fp; /* fbar: canvas-code as in g_array.c */ canvas_makefilename(x->x_canvas, s->s_name, buf, MAXPDSTRING); sys_bashfilename(buf, buf); if (!(fd = fopen(buf, "w"))) { post("%s: couldn't create", s->s_name); return; } for (; ntemplate--; tp++) { for (i = x->x_nfilters, fp = tp->t_amp; i--; fp++) fprintf(fd, "%6.2f ", *fp); fprintf(fd, "\n"); } post("bonk: wrote %d templates\n", x->x_ntemplate); fclose(fd); } #endif #ifdef MSP static void bonk_write(t_bonk *x, t_symbol *s) { defer(x, (method)bonk_dowrite, s, 0, NULL); } static void bonk_dowrite(t_bonk *x, t_symbol *s) { t_fourcc filetype = 'TEXT', outtype; char filename[MAX_FILENAME_CHARS]; short path; if (s == gensym("")) { sprintf(filename, "bonk_template.txt"); saveas_promptset("Save template as..."); if (saveasdialog_extended(filename, &path, &outtype, &filetype, 0)) return; } else { strcpy(filename, s->s_name); path = path_getdefault(); } bonk_writefile(x, filename, path); } void bonk_writefile(t_bonk *x, char *filename, short path) { int i, ntemplate = x->x_ntemplate; t_template *tp = x->x_template; t_float *fp; long err; t_ptr_size buflen; t_filehandle fh; char buf[20]; err = path_createsysfile(filename, path, 'TEXT', &fh); if (err) return; for (; ntemplate--; tp++) { for (i = x->x_nfilters, fp = tp->t_amp; i--; fp++) { snprintf(buf, 20, "%6.2f ", *fp); buflen = strlen(buf); sysfile_write(fh, &buflen, buf); } buflen = 1; sysfile_write(fh, &buflen, "\n"); } sysfile_close(fh); } #endif static void bonk_free(t_bonk *x) { int i, ninsig = x->x_ninsig; t_insig *gp = x->x_insig; #ifdef MSP dsp_free((t_pxobject *)x); #endif for (i = 0, gp = x->x_insig; i < ninsig; i++, gp++) freebytes(gp->g_inbuf, x->x_npoints * sizeof(t_float)); freebytes(x->x_insig, ninsig * sizeof(*x->x_insig)); clock_free(x->x_clock); if (!--(x->x_filterbank->b_refcount)) bonk_freefilterbank(x->x_filterbank); freebytes(x->x_template, x->x_ntemplate * sizeof(x->x_template[0])); } /* -------------------------- Pd glue ------------------------- */ #ifdef PD static void *bonk_new(t_symbol *s, int argc, t_atom *argv) { t_bonk *x = (t_bonk *)pd_new(bonk_class); int nsig = 1, period = DEFPERIOD, npts = DEFNPOINTS, nfilters = DEFNFILTERS, j; t_float halftones = DEFHALFTONES, overlap = DEFOVERLAP, firstbin = DEFFIRSTBIN, minbandwidth = DEFMINBANDWIDTH; t_insig *g; x->x_canvas = canvas_getcurrent(); /* fbar: bind current canvas to x */ if (argc > 0 && argv[0].a_type == A_FLOAT) { /* old style args for compatibility */ period = atom_getfloatarg(0, argc, argv); nsig = atom_getfloatarg(1, argc, argv); } else while (argc > 0) { t_symbol *firstarg = atom_getsymbolarg(0, argc, argv); if (!strcmp(firstarg->s_name, "-npts") && argc > 1) { npts = atom_getfloatarg(1, argc, argv); argc -= 2; argv += 2; } else if (!strcmp(firstarg->s_name, "-hop") && argc > 1) { period = atom_getfloatarg(1, argc, argv); argc -= 2; argv += 2; } else if (!strcmp(firstarg->s_name, "-nsigs") && argc > 1) { nsig = atom_getfloatarg(1, argc, argv); argc -= 2; argv += 2; } else if (!strcmp(firstarg->s_name, "-nfilters") && argc > 1) { nfilters = atom_getfloatarg(1, argc, argv); argc -= 2; argv += 2; } else if (!strcmp(firstarg->s_name, "-halftones") && argc > 1) { halftones = atom_getfloatarg(1, argc, argv); argc -= 2; argv += 2; } else if (!strcmp(firstarg->s_name, "-overlap") && argc > 1) { overlap = atom_getfloatarg(1, argc, argv); argc -= 2; argv += 2; } else if (!strcmp(firstarg->s_name, "-firstbin") && argc > 1) { firstbin = atom_getfloatarg(1, argc, argv); argc -= 2; argv += 2; } else if (!strcmp(firstarg->s_name, "-minbandwidth") && argc > 1) { minbandwidth = atom_getfloatarg(1, argc, argv); argc -= 2; argv += 2; } else if (!strcmp(firstarg->s_name, "-spew") && argc > 1) { x->x_spew = (atom_getfloatarg(1, argc, argv) != 0); argc -= 2; argv += 2; } else { pd_error(x, "usage is: bonk [-npts #] [-hop #] [-nsigs #] [-nfilters #] [-halftones #]"); post( "... [-overlap #] [-firstbin #] [-spew #]"); argc = 0; } } x->x_npoints = (npts >= MINPOINTS ? npts : DEFNPOINTS); x->x_period = (period >= 1 ? period : npts/2); x->x_nfilters = (nfilters >= 1 ? nfilters : DEFNFILTERS); if (halftones < 0.01) halftones = DEFHALFTONES; else if (halftones > 12) halftones = 12; if (nsig < 1) nsig = 1; else if (nsig > MAXCHANNELS) nsig = MAXCHANNELS; if (firstbin < 0.5) firstbin = 0.5; if (overlap < 1) overlap = 1; x->x_clock = clock_new(x, (t_method)bonk_tick); x->x_insig = (t_insig *)getbytes(nsig * sizeof(*x->x_insig)); for (j = 0, g = x->x_insig; j < nsig; j++, g++) { g->g_outlet = outlet_new(&x->x_obj, gensym("list")); if (j) inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); } x->x_cookedout = outlet_new(&x->x_obj, gensym("list")); bonk_donew(x, npts, period, nsig, nfilters, halftones, overlap, firstbin, minbandwidth, sys_getsr()); return (x); } void bonk_tilde_setup(void) { bonk_class = class_new(gensym("bonk~"), (t_newmethod)bonk_new, (t_method)bonk_free, sizeof(t_bonk), 0, A_GIMME, 0); class_addmethod(bonk_class, nullfn, gensym("signal"), 0); class_addmethod(bonk_class, (t_method)bonk_dsp, gensym("dsp"), A_CANT, 0); class_addbang(bonk_class, bonk_bang); class_addmethod(bonk_class, (t_method)bonk_learn, gensym("learn"), A_FLOAT, 0); class_addmethod(bonk_class, (t_method)bonk_forget, gensym("forget"), 0); class_addmethod(bonk_class, (t_method)bonk_thresh, gensym("thresh"), A_FLOAT, A_FLOAT, 0); class_addmethod(bonk_class, (t_method)bonk_mask, gensym("mask"), A_FLOAT, A_FLOAT, 0); class_addmethod(bonk_class, (t_method)bonk_debounce, gensym("debounce"), A_FLOAT, 0); class_addmethod(bonk_class, (t_method)bonk_minvel, gensym("minvel"), A_FLOAT, 0); class_addmethod(bonk_class, (t_method)bonk_print, gensym("print"), A_DEFFLOAT, 0); class_addmethod(bonk_class, (t_method)bonk_debug, gensym("debug"), A_DEFFLOAT, 0); class_addmethod(bonk_class, (t_method)bonk_spew, gensym("spew"), A_DEFFLOAT, 0); class_addmethod(bonk_class, (t_method)bonk_useloudness, gensym("useloudness"), A_DEFFLOAT, 0); class_addmethod(bonk_class, (t_method)bonk_attackbins, gensym("attack-bins"), A_DEFFLOAT, 0); class_addmethod(bonk_class, (t_method)bonk_attackbins, gensym("attack-frames"), A_DEFFLOAT, 0); class_addmethod(bonk_class, (t_method)bonk_read, gensym("read"), A_SYMBOL, 0); class_addmethod(bonk_class, (t_method)bonk_write, gensym("write"), A_SYMBOL, 0); post("bonk version 1.5"); } #endif /* -------------------------- MSP glue ------------------------- */ #ifdef MSP int main() { t_class *c; t_object *attr; long attrflags = 0; t_symbol *sym_long = gensym("long"), *sym_float32 = gensym("float32"); c = class_new("bonk~", (method)bonk_new, (method)bonk_free, sizeof(t_bonk), (method)0L, A_GIMME, 0); class_obexoffset_set(c, calcoffset(t_bonk, obex)); attr = attr_offset_new("npoints", sym_long, attrflags, (method)0L, (method)0L, calcoffset(t_bonk, x_npoints)); class_addattr(c, attr); attr = attr_offset_new("hop", sym_long, attrflags, (method)0L, (method)0L, calcoffset(t_bonk, x_period)); class_addattr(c, attr); attr = attr_offset_new("nfilters", sym_long, attrflags, (method)0L, (method)0L, calcoffset(t_bonk, x_nfilters)); class_addattr(c, attr); attr = attr_offset_new("halftones", sym_float32, attrflags, (method)0L, (method)0L, calcoffset(t_bonk, x_halftones)); class_addattr(c, attr); attr = attr_offset_new("overlap", sym_float32, attrflags, (method)0L, (method)0L, calcoffset(t_bonk, x_overlap)); class_addattr(c, attr); attr = attr_offset_new("firstbin", sym_float32, attrflags, (method)0L, (method)0L, calcoffset(t_bonk, x_firstbin)); class_addattr(c, attr); attr = attr_offset_new("minbandwidth", sym_float32, attrflags, (method)0L, (method)0L, calcoffset(t_bonk, x_minbandwidth)); class_addattr(c, attr); attr = attr_offset_new("minvel", sym_float32, attrflags, (method)0L, (method)bonk_minvel_set, calcoffset(t_bonk, x_minvel)); class_addattr(c, attr); attr = attr_offset_new("lothresh", sym_float32, attrflags, (method)0L, (method)bonk_lothresh_set, calcoffset(t_bonk, x_lothresh)); class_addattr(c, attr); attr = attr_offset_new("hithresh", sym_float32, attrflags, (method)0L, (method)bonk_hithresh_set, calcoffset(t_bonk, x_hithresh)); class_addattr(c, attr); attr = attr_offset_new("masktime", sym_long, attrflags, (method)0L, (method)bonk_masktime_set, calcoffset(t_bonk, x_masktime)); class_addattr(c, attr); attr = attr_offset_new("maskdecay", sym_float32, attrflags, (method)0L, (method)bonk_maskdecay_set, calcoffset(t_bonk, x_maskdecay)); class_addattr(c, attr); attr = attr_offset_new("debouncedecay", sym_float32, attrflags, (method)0L, (method)bonk_debouncedecay_set, calcoffset(t_bonk, x_debouncedecay)); class_addattr(c, attr); attr = attr_offset_new("debug", sym_long, attrflags, (method)0L, (method)bonk_debug_set, calcoffset(t_bonk, x_debug)); class_addattr(c, attr); attr = attr_offset_new("spew", sym_long, attrflags, (method)0L, (method)bonk_spew_set, calcoffset(t_bonk, x_spew)); class_addattr(c, attr); attr = attr_offset_new("useloudness", sym_long, attrflags, (method)0L, (method)bonk_useloudness_set, calcoffset(t_bonk, x_useloudness)); class_addattr(c, attr); attr = attr_offset_new("attackframes", sym_long, attrflags, (method)0L, (method)bonk_attackbins_set, calcoffset(t_bonk, x_attackbins)); class_addattr(c, attr); attr = attr_offset_new("learn", sym_long, attrflags, (method)0L, (method)bonk_learn_set, calcoffset(t_bonk, x_learn)); class_addattr(c, attr); class_addmethod(c, (method)bonk_dsp, "dsp", A_CANT, 0); class_addmethod(c, (method)bonk_dsp64, "dsp64", A_CANT, 0); class_addmethod(c, (method)bonk_bang, "bang", A_CANT, 0); class_addmethod(c, (method)bonk_forget, "forget", 0); class_addmethod(c, (method)bonk_thresh, "thresh", A_FLOAT, A_FLOAT, 0); class_addmethod(c, (method)bonk_print, "print", A_DEFFLOAT, 0); class_addmethod(c, (method)bonk_read, "read", A_DEFSYM, 0); class_addmethod(c, (method)bonk_write, "write", A_DEFSYM, 0); class_addmethod(c, (method)bonk_assist, "assist", A_CANT, 0); class_addmethod(c, (method)object_obex_dumpout, "dumpout", A_CANT, 0); class_addmethod(c, (method)object_obex_quickref, "quickref", A_CANT, 0); class_dspinit(c); class_register(CLASS_BOX, c); bonk_class = c; post("bonk~ v1.5"); return (0); } static void *bonk_new(t_symbol *s, long ac, t_atom *av) { short j; t_bonk *x; if ((x = (t_bonk *)object_alloc(bonk_class))) { t_insig *g; x->x_npoints = DEFNPOINTS; x->x_period = DEFPERIOD; x->x_nfilters = DEFNFILTERS; x->x_halftones = DEFHALFTONES; x->x_firstbin = DEFFIRSTBIN; x->x_minbandwidth = DEFMINBANDWIDTH; x->x_overlap = DEFOVERLAP; x->x_ninsig = 1; x->x_hithresh = DEFHITHRESH; x->x_lothresh = DEFLOTHRESH; x->x_masktime = DEFMASKTIME; x->x_maskdecay = DEFMASKDECAY; x->x_debouncedecay = DEFDEBOUNCEDECAY; x->x_minvel = DEFMINVEL; x->x_attackbins = DEFATTACKBINS; if (!x->x_period) x->x_period = x->x_npoints/2; x->x_template = (t_template *)getbytes(0); x->x_ntemplate = 0; x->x_infill = 0; x->x_countdown = 0; x->x_willattack = 0; x->x_attacked = 0; x->x_maskphase = 0; x->x_debug = 0; x->x_learn = 0; x->x_learndebounce = clock_getsystime(); x->x_learncount = 0; x->x_useloudness = 0; x->x_debouncevel = 0; x->x_sr = sys_getsr(); if (ac) { switch (av[0].a_type) { case A_LONG: x->x_ninsig = av[0].a_w.w_long; break; } } if (x->x_ninsig < 1) x->x_ninsig = 1; if (x->x_ninsig > MAXCHANNELS) x->x_ninsig = MAXCHANNELS; attr_args_process(x, ac, av); x->x_insig = (t_insig *)getbytes(x->x_ninsig * sizeof(*x->x_insig)); dsp_setup((t_pxobject *)x, x->x_ninsig); object_obex_store(x, gensym("dumpout"), outlet_new(x, NULL)); x->x_cookedout = listout((t_object *)x); for (j = 0, g = x->x_insig + x->x_ninsig-1; j < x->x_ninsig; j++, g--) { g->g_outlet = listout((t_object *)x); } x->x_clock = clock_new(x, (method)bonk_tick); bonk_donew(x, x->x_npoints, x->x_period, x->x_ninsig, x->x_nfilters, x->x_halftones, x->x_overlap, x->x_firstbin, x->x_minbandwidth, sys_getsr()); } return (x); } /* Attribute setters. */ void bonk_minvel_set(t_bonk *x, void *attr, long ac, t_atom *av) { if (ac && av) { t_float f = atom_getfloat(av); if (f < 0) f = 0; x->x_minvel = f; } } void bonk_lothresh_set(t_bonk *x, void *attr, long ac, t_atom *av) { if (ac && av) { t_float f = atom_getfloat(av); if (f > x->x_hithresh) post("bonk: warning: low threshold greater than hi threshold"); x->x_lothresh = (f <= 0 ? 0.0001 : f); } } void bonk_hithresh_set(t_bonk *x, void *attr, long ac, t_atom *av) { if (ac && av) { t_float f = atom_getfloat(av); if (f < x->x_lothresh) post("bonk: warning: low threshold greater than hi threshold"); x->x_hithresh = (f <= 0 ? 0.0001 : f); } } void bonk_masktime_set(t_bonk *x, void *attr, long ac, t_atom *av) { if (ac && av) { int n = atom_getlong(av); x->x_masktime = (n < 0) ? 0 : n; } } void bonk_maskdecay_set(t_bonk *x, void *attr, long ac, t_atom *av) { if (ac && av) { t_float f = atom_getfloat(av); f = (f < 0) ? 0 : f; f = (f > 1) ? 1 : f; x->x_maskdecay = f; } } void bonk_debouncedecay_set(t_bonk *x, void *attr, long ac, t_atom *av) { if (ac && av) { t_float f = atom_getfloat(av); f = (f < 0) ? 0 : f; f = (f > 1) ? 1 : f; x->x_debouncedecay = f; } } void bonk_debug_set(t_bonk *x, void *attr, long ac, t_atom *av) { if (ac && av) { int n = atom_getlong(av); x->x_debug = (n != 0); } } void bonk_spew_set(t_bonk *x, void *attr, long ac, t_atom *av) { if (ac && av) { int n = atom_getlong(av); x->x_spew = (n != 0); } } void bonk_useloudness_set(t_bonk *x, void *attr, long ac, t_atom *av) { if (ac && av) { int n = atom_getlong(av); x->x_useloudness = (n != 0); } } void bonk_attackbins_set(t_bonk *x, void *attr, long ac, t_atom *av) { if (ac && av) { int n = atom_getlong(av); n = (n < 1) ? 1 : n; n = (n > MASKHIST) ? MASKHIST : n; x->x_attackbins = n; } } void bonk_learn_set(t_bonk *x, void *attr, long ac, t_atom *av) { if (ac && av) { int n = atom_getlong(av); if (n != 0) { x->x_template = (t_template *)t_resizebytes(x->x_template, x->x_ntemplate * sizeof(x->x_template[0]), 0); x->x_ntemplate = 0; } x->x_learn = n; x->x_learncount = 0; } } /* end attr setters */ void bonk_assist(t_bonk *x, void *b, long m, long a, char *s) { } /* get current system time */ double clock_getsystime() { return gettime(); } /* elapsed time in milliseconds since the given system time */ double clock_gettimesince(double prevsystime) { return ((gettime() - prevsystime)); } t_float qrsqrt(t_float f) { return 1/sqrt(f); } #endif /* MSP */ ================================================ FILE: libs/libpd/pure-data/extra/bonk~/templates.txt ================================================ 10.47 9.65 14.95 23.77 28.32 38.84 53.21 41.20 31.25 21.70 16.48 6.52 13.93 27.82 58.05 24.11 35.26 35.98 37.78 22.54 13.56 10.75 30.45 28.86 29.42 21.94 29.92 35.70 38.49 32.01 28.19 27.38 22.10 66.77 46.27 28.82 25.95 22.84 20.61 20.33 14.18 6.86 8.92 7.37 ================================================ FILE: libs/libpd/pure-data/extra/choice/choice.c ================================================ /* choice -- match incoming list against a collection of stored templates. */ /* Copyright 1999 Miller Puckette. Permission is granted to use this software for any purpose provided you keep this copyright notice intact. THE AUTHOR AND HIS EMPLOYERS MAKE NO WARRANTY, EXPRESS OR IMPLIED, IN CONNECTION WITH THIS SOFTWARE. This file is downloadable from http://www.crca.ucsd.edu/~msp . */ #include "m_pd.h" #include static t_class *choice_class; #define DIMENSION 10 typedef struct _elem { t_float e_age; t_float e_weight[DIMENSION]; } t_elem; typedef struct _choice { t_object x_obj; t_elem *x_vec; int x_n; int x_nonrepeat; } t_choice; static void *choice_new(t_float fnonrepeat) { t_choice *x = (t_choice *)pd_new(choice_class); outlet_new(&x->x_obj, gensym("float")); x->x_vec = (t_elem *)getbytes(0); x->x_n = 0; x->x_nonrepeat = (fnonrepeat != 0); return (x); } static void choice_clear(t_choice *x) { x->x_vec = (t_elem *)resizebytes(x->x_vec, x->x_n * sizeof(t_elem), 0); x->x_n = 0; } static void choice_print(t_choice *x) { int j; for (j = 0; j < x->x_n; j++) { t_elem *e = x->x_vec + j; t_float *w = e->e_weight; post("%2d age %2d \ w %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f", j, (int)(e->e_age), w[0], w[1], w[2], w[3], w[4], w[5], w[6], w[7], w[8], w[9]); } } static void choice_add(t_choice *x, t_symbol *s, int argc, t_atom *argv) { int oldn = x->x_n, newn = oldn + 1, i; t_elem *e; t_float sum, normal; x->x_vec = (t_elem *)resizebytes(x->x_vec, oldn * sizeof(t_elem), newn * sizeof(t_elem)); x->x_n = newn; e = x->x_vec + oldn; e->e_age = 2; for (i = 0, sum = 0; i < DIMENSION; i++) { t_float f = atom_getfloatarg(i, argc, argv); e->e_weight[i] = f; sum += f*f; } normal = (t_float)(sum > 0 ? 1./sqrt(sum) : 1); for (i = 0; i < DIMENSION; i++) e->e_weight[i] *= normal; } static void choice_list(t_choice *x, t_symbol *s, int argc, t_atom *argv) { int i, j; t_float bestsum = 0; int bestindex = -1; t_float invec[DIMENSION]; for (i = 0; i < DIMENSION; i++) invec[i] = atom_getfloatarg(i, argc, argv); for (j = 0; j < x->x_n; j++) { t_elem *e = x->x_vec + j; t_float sum; for (i = 0, sum = 0; i < DIMENSION; i++) sum += e->e_weight[i] * invec[i]; if (x->x_nonrepeat) sum *= (t_float)(log(e->e_age)); if (sum > bestsum) { bestsum = sum; sum = 1; bestindex = j; } } if (bestindex >= 0) { for (j = 0; j < x->x_n; j++) x->x_vec[j].e_age += 1.; x->x_vec[bestindex].e_age = 1; } outlet_float(x->x_obj.ob_outlet, (t_float)bestindex); } static void choice_free(t_choice *x) { freebytes(x->x_vec, x->x_n * sizeof(t_elem)); } void choice_setup(void) { choice_class = class_new(gensym("choice"), (t_newmethod)choice_new, (t_method)choice_free, sizeof(t_choice), 0, A_DEFFLOAT, 0); class_addmethod(choice_class, (t_method)choice_add, gensym("add"), A_GIMME, 0); class_addmethod(choice_class, (t_method)choice_clear, gensym("clear"), 0); class_addmethod(choice_class, (t_method)choice_print, gensym("print"), 0); class_addlist(choice_class, choice_list); } ================================================ FILE: libs/libpd/pure-data/extra/fiddle~/fiddle~.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette and Ted Apel. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* * Fiddle is a pitch tracker hardwired to have hop size ("H") equal to * half its window size ("N"). * * This version should compile for Max "0.26," JMAX, Pd, or Max/MSP. * * The "lastanalysis" field holds the shifted FT of the previous H * samples. The buffer contains in effect points 1/2, 3/2, ..., (N-1)/2 * of the DTFT of a real vector of length N, half of whose points are zero, * i.e., only the first H points are used. Put another way, we get the * the odd-numbered points of the FFT of the H points, zero padded to 4*H in * length. The integer points 0, 1, ..., H-1 * are found by interpolating these others, using the fact that the * half-integer points are band-limited (they only have positive frequencies.) * To facilitate the interpolation the "lastanalysis" buffer contains * FILTSIZE extra points (1/2-FILTSIZE, ..., -1/2) at the beginning and * FILTSIZE again at the end ((N+1)/2, ..., FILTSIZE+(N-1)/2). The buffer * therefore has N+4*FILTSIZE floating-point numbers in it. * * after doing this I found out that you can just do a real FFT * of the H new points, zero-padded to contain N points, and using a similar * but simpler interpolation scheme you can still get 2N points of the DTFT * of the N points. Jean Laroche is a big fat hen. * */ /* These pragmas are only used for MSVC, not MinGW or Cygwin */ #ifdef _MSC_VER #pragma warning (disable: 4305 4244) #endif /* this #ifdef does nothing, but its there... */ #ifdef _WIN32 #define flog log #define fexp exp #define fsqrt sqrt #else #define flog log #define fexp exp #define fsqrt sqrt #endif char fiddle_version[] = "fiddle version 1.1 TEST4"; #ifdef JMAX #include "fts.h" #include #include typedef float t_float; typedef float t_floatarg; typedef fts_symbol_t t_symbol; static void *getbytes(size_t nbytes) { void *ret; if (nbytes < 1) nbytes = 1; ret = (void *)malloc(nbytes); return (ret); } static void *resizebytes(void *old, size_t oldsize, size_t newsize) { void *ret; if (newsize < 1) newsize = 1; ret = (void *)realloc((char *)old, newsize); return (ret); } static void freebytes(void *fatso, size_t nbytes) { free(fatso); } #define CLASSNAME "fiddle" #define OUTLETpower 5 #define OUTLETmicropitch1 4 #define OUTLETmicropitch2 3 #define OUTLETmicropitch3 2 #define OUTLETattack 1 #define OUTLETpitch 0 static fts_symbol_t *dsp_symbol = 0; #define error post #endif /* FTS */ #ifdef MAX26 #define t_floatarg double #include "m_extern.h" #include "d_graph.h" #include "d_ugen.h" #endif /* MAX26 */ #ifdef PD #include "m_pd.h" #endif /* PD */ #ifdef MSP #define flog log #define fexp exp #define fsqrt sqrt #endif /* MSP */ #ifdef MSP #include "ext.h" #include "z_dsp.h" #include "fft_mayer.proto.h" typedef float t_float; typedef double t_floatarg; #endif /* MSP */ #include #define MINBIN 3 #define DEFAMPLO 40 #define DEFAMPHI 50 #define DEFATTACKTIME 100 #define DEFATTACKTHRESH 10 #define DEFVIBTIME 50 #define DEFVIBDEPTH 0.5 #define GLISS 0.7f #define DBFUDGE 30.8f #define MINFREQINBINS 5 /* minimum frequency in bins for reliable output */ #define MAXNPITCH 3 #define MAXHIST 3 /* find N hottest peaks in histogram */ #define MAXPOINTS 8192 #define MINPOINTS 128 #define DEFAULTPOINTS 1024 #define HISTORY 20 #define MAXPEAK 100 /* maximum number of peaks */ #define DEFNPEAK 20 /* default number of peaks */ #define MAXNPEAK (MAXLOWPEAK + MAXSTRONGPEAK) #define MINBW (0.03f) /* consider BW >= 0.03 FFT bins */ #define BINPEROCT 48 /* bins per octave */ #define BPERO_OVER_LOG2 69.24936196f /* BINSPEROCT/log(2) */ #define FACTORTOBINS (t_float)(4/0.0145453) /* 4 / (pow(2.,1/48.) - 1) */ #define BINGUARD 10 /* extra bins to throw in front */ #define PARTIALDEVIANCE 0.023f /* acceptable partial detuning in % */ #define LOGTODB 4.34294481903f /* 20/log(10) */ #define KNOCKTHRESH 10.f /* don't know how to describe this */ static t_float sigfiddle_partialonset[] = { 0, 48, 76.0782000346154967102, 96, 111.45254855459339269887, 124.07820003461549671089, 134.75303625876499715823, 144, 152.15640006923099342109, 159.45254855459339269887, 166.05271769459026829915, 172.07820003461549671088, 177.62110647077242370064, 182.75303625876499715892, 187.53074858920888940907, 192, }; #define NPARTIALONSET ((int)(sizeof(sigfiddle_partialonset)/sizeof(t_float))) static int sigfiddle_intpartialonset[] = { 0, 48, 76, 96, 111, 124, 135, 144, 152, 159, 166, 172, 178, 183, 188, 192, }; /* these coefficients, which come from the "upsamp" subdirectory, are a filter kernel for upsampling by a factor of two, assuming the sound to be upsampled has no energy above half the Nyquist, i.e., that it's already 2x oversampled compared to the theoretically possible sample rate. I got these by trial and error. */ #define FILT1 ((t_float)(.5 * 1.227054)) #define FILT2 ((t_float)(.5 * -0.302385)) #define FILT3 ((t_float)(.5 * 0.095326)) #define FILT4 ((t_float)(.5 * -0.022748)) #define FILT5 ((t_float)(.5 * 0.002533)) #define FILTSIZE 5 typedef struct peakout /* a peak for output */ { t_float po_freq; /* frequency in hz */ t_float po_amp; /* amplitude */ } t_peakout; typedef struct peak /* a peak for analysis */ { t_float p_freq; /* frequency in bins */ t_float p_width; /* peak width in bins */ t_float p_pow; /* peak power */ t_float p_loudness; /* 4th root of power */ t_float *p_fp; /* pointer back to spectrum */ } t_peak; typedef struct histopeak { t_float h_pitch; /* estimated pitch */ t_float h_value; /* value of peak */ t_float h_loud; /* combined strength of found partials */ int h_index; /* index of bin holding peak */ int h_used; /* true if an x_hist entry points here */ } t_histopeak; typedef struct pitchhist /* struct for keeping history by pitch */ { t_float h_pitch; /* pitch to output */ t_float h_amps[HISTORY]; /* past amplitudes */ t_float h_pitches[HISTORY]; /* past pitches */ t_float h_noted; /* last pitch output */ int h_age; /* number of frames pitch has been there */ t_histopeak *h_wherefrom; /* new histogram peak to incorporate */ void *h_outlet; } t_pitchhist; typedef struct sigfiddle /* instance struct */ { #ifdef JMAX fts_object_t x_h; /* object header */ fts_alarm_t x_clock; /* callback for timeouts */ #endif #ifdef MAX26 t_head x_h; /* header for tilde objects */ t_sig *x_io[IN1+OUT0]; /* number of signal inputs and outputs */ void *x_clock; /* a "clock" object */ #endif #ifdef PD t_object x_ob; /* object header */ t_clock *x_clock; /* callback for timeouts */ #endif #ifdef MSP t_pxobject x_obj; void *x_clock; long x_downsample; /* downsample feature because of MSP's large sig vector sizes */ #endif t_float *x_inbuf; /* buffer to analyze, npoints/2 elems */ t_float *x_lastanalysis; /* FT of last buffer (see main comment) */ t_float *x_spiral; /* 1/4-wave complex exponential */ t_peakout *x_peakbuf; /* spectral peaks for output */ int x_npeakout; /* number of spectral peaks to output */ int x_npeakanal; /* number of spectral peaks to analyze */ int x_phase; /* number of points since last output */ int x_histphase; /* phase into amplitude history vector */ int x_hop; /* period of output, npoints/2 */ t_float x_sr; /* sample rate */ t_pitchhist x_hist[MAXNPITCH]; /* history of current pitches */ int x_nprint; /* how many periods to print */ int x_npitch; /* number of simultaneous pitches */ t_float x_dbs[HISTORY]; /* DB history, indexed by "histphase" */ t_float x_peaked; /* peak since last attack */ int x_dbage; /* number of bins DB has met threshold */ int x_auto; /* true if generating continuous output */ /* parameters */ t_float x_amplo; t_float x_amphi; int x_attacktime; int x_attackbins; t_float x_attackthresh; int x_vibtime; int x_vibbins; t_float x_vibdepth; t_float x_npartial; /* outlets & clock */ void *x_envout; int x_attackvalue; void *x_attackout; void *x_noteout; void *x_peakout; } t_sigfiddle; #if CHECKER t_float fiddle_checker[1024]; #endif #ifdef MSP /* Mac compiler requires prototypes for everything */ int sigfiddle_ilog2(int n); t_float fiddle_mtof(t_float f); t_float fiddle_ftom(t_float f); void sigfiddle_doit(t_sigfiddle *x); void sigfiddle_debug(t_sigfiddle *x); void sigfiddle_print(t_sigfiddle *x); void sigfiddle_assist(t_sigfiddle *x, void *b, long m, long a, char *s); void sigfiddle_amprange(t_sigfiddle *x, double amplo, double amphi); void sigfiddle_reattack(t_sigfiddle *x, t_floatarg attacktime, t_floatarg attackthresh); void sigfiddle_vibrato(t_sigfiddle *x, t_floatarg vibtime, t_floatarg vibdepth); void sigfiddle_npartial(t_sigfiddle *x, double npartial); void sigfiddle_auto(t_sigfiddle *x, t_floatarg f); void sigfiddle_setnpoints(t_sigfiddle *x, t_floatarg f); int sigfiddle_doinit(t_sigfiddle *x, long npoints, long npitch, long npeakanal, long npeakout); static t_int *fiddle_perform(t_int *w); void sigfiddle_dsp(t_sigfiddle *x, t_signal **sp); void sigfiddle_tick(t_sigfiddle *x); void sigfiddle_bang(t_sigfiddle *x); void sigfiddle_ff(t_sigfiddle *x); void *sigfiddle_new(long npoints, long npitch, long npeakanal, long npeakout); void msp_fft(t_float *buf, long np, long inv); t_float msp_ffttemp[MAXPOINTS*2]; int errno; #endif int sigfiddle_ilog2(int n) { int ret = -1; while (n) { n >>= 1; ret++; } return (ret); } t_float fiddle_mtof(t_float f) { return (8.17579891564 * exp(.0577622650 * f)); } t_float fiddle_ftom(t_float f) { return (17.3123405046 * log(.12231220585 * f)); } #define ftom fiddle_ftom #define mtof fiddle_mtof void sigfiddle_doit(t_sigfiddle *x) { #ifdef MSP /* prevents interrupt-level stack overflow crash with Netscape. */ static t_float spect1[4*MAXPOINTS]; static t_float spect2[MAXPOINTS + 4*FILTSIZE]; #else t_float spect1[4*MAXPOINTS]; t_float spect2[MAXPOINTS + 4*FILTSIZE]; #endif #if CHECKER t_float checker3[4*MAXPOINTS]; #endif t_peak peaklist[MAXPEAK + 1], *pk1; t_peakout *pk2; t_histopeak histvec[MAXHIST], *hp1; int i, j, k, hop = x->x_hop, n = 2*hop, npeak, npitch, logn = sigfiddle_ilog2(n), newphase, oldphase; t_float *fp, *fp1, *fp2, *fp3, total_power, total_loudness, total_db; t_float maxbin = BINPEROCT * (logn-2), *histogram = spect2 + BINGUARD; t_pitchhist *phist; t_float hzperbin = x->x_sr / (2.0f * n); int npeakout = x->x_npeakout, npeakanal = x->x_npeakanal; int npeaktot = (npeakout > npeakanal ? npeakout : npeakanal); oldphase = x->x_histphase; newphase = x->x_histphase + 1; if (newphase == HISTORY) newphase = 0; x->x_histphase = newphase; /* * multiply the H points by a 1/4-wave complex exponential, * and take FFT of the result. */ for (i = 0, fp1 = x->x_inbuf, fp2 = x->x_spiral, fp3 = spect1; i < hop; i++, fp1++, fp2 += 2, fp3 += 2) fp3[0] = fp1[0] * fp2[0], fp3[1] = fp1[0] * fp2[1]; #ifdef MAX26 fft(spect1, hop, 0); #endif #ifdef PD pd_fft(spect1, hop, 0); #endif #ifdef JMAX fts_cfft_inplc((complex *)spect1, hop); #endif #ifdef MSP msp_fft(spect1,hop,0); #endif /* * now redistribute the points to get in effect the odd-numbered * points of the FFT of the H points, zero padded to 4*H in length. */ for (i = 0, fp1 = spect1, fp2 = spect2 + (2*FILTSIZE); i < (hop>>1); i++, fp1 += 2, fp2 += 4) fp2[0] = fp1[0], fp2[1] = fp1[1]; for (i = 0, fp1 = spect1 + n - 2, fp2 = spect2 + (2*FILTSIZE+2); i < (hop>>1); i++, fp1 -= 2, fp2 += 4) fp2[0] = fp1[0], fp2[1] = -fp1[1]; for (i = 0, fp1 = spect2 + (2*FILTSIZE), fp2 = spect2 + (2*FILTSIZE-2); ix_lastanalysis + 2*FILTSIZE, fp3 = spect2 + 2*FILTSIZE; i < (hop>>1); i++) { t_float re, im; re= FILT1 * ( fp2[ -2] -fp2[ 1] +fp3[ -2] -fp3[ 1]) + FILT2 * ( fp2[ -3] -fp2[ 2] +fp3[ -3] -fp3[ 2]) + FILT3 * (-fp2[ -6] +fp2[ 5] -fp3[ -6] +fp3[ 5]) + FILT4 * (-fp2[ -7] +fp2[ 6] -fp3[ -7] +fp3[ 6]) + FILT5 * ( fp2[-10] -fp2[ 9] +fp3[-10] -fp3[ 9]); im= FILT1 * ( fp2[ -1] +fp2[ 0] +fp3[ -1] +fp3[ 0]) + FILT2 * (-fp2[ -4] -fp2[ 3] -fp3[ -4] -fp3[ 3]) + FILT3 * (-fp2[ -5] -fp2[ 4] -fp3[ -5] -fp3[ 4]) + FILT4 * ( fp2[ -8] +fp2[ 7] +fp3[ -8] +fp3[ 7]) + FILT5 * ( fp2[ -9] +fp2[ 8] +fp3[ -9] +fp3[ 8]); fp1[0] = 0.7071f * (re + im); fp1[1] = 0.7071f * (im - re); fp1[4] = fp2[0] + fp3[1]; fp1[5] = fp2[1] - fp3[0]; fp1 += 8, fp2 += 2, fp3 += 2; re= FILT1 * ( fp2[ -2] -fp2[ 1] -fp3[ -2] +fp3[ 1]) + FILT2 * ( fp2[ -3] -fp2[ 2] -fp3[ -3] +fp3[ 2]) + FILT3 * (-fp2[ -6] +fp2[ 5] +fp3[ -6] -fp3[ 5]) + FILT4 * (-fp2[ -7] +fp2[ 6] +fp3[ -7] -fp3[ 6]) + FILT5 * ( fp2[-10] -fp2[ 9] -fp3[-10] +fp3[ 9]); im= FILT1 * ( fp2[ -1] +fp2[ 0] -fp3[ -1] -fp3[ 0]) + FILT2 * (-fp2[ -4] -fp2[ 3] +fp3[ -4] +fp3[ 3]) + FILT3 * (-fp2[ -5] -fp2[ 4] +fp3[ -5] +fp3[ 4]) + FILT4 * ( fp2[ -8] +fp2[ 7] -fp3[ -8] -fp3[ 7]) + FILT5 * ( fp2[ -9] +fp2[ 8] -fp3[ -9] -fp3[ 8]); fp1[0] = 0.7071f * (re + im); fp1[1] = 0.7071f * (im - re); fp1[4] = fp2[0] - fp3[1]; fp1[5] = fp2[1] + fp3[0]; fp1 += 8, fp2 += 2, fp3 += 2; } #if 0 if (x->x_nprint) { for (i = 0, fp = spect1; i < 16; i++, fp+= 4) post("spect %d %f %f --> %f", i, fp[0], fp[1], sqrt(fp[0] * fp[0] + fp[1] * fp[1])); } #endif /* copy new spectrum out */ for (i = 0, fp1 = spect2, fp2 = x->x_lastanalysis; i < n + 4*FILTSIZE; i++) *fp2++ = *fp1++; for (i = 0; i < MINBIN; i++) spect1[4*i + 2] = spect1[4*i + 3] = 0; /* starting at bin MINBIN, compute hanning windowed power spectrum */ for (i = MINBIN, fp1 = spect1+4*MINBIN, total_power = 0; i < n-2; i++, fp1 += 4) { t_float re = fp1[0] - 0.5 * (fp1[-8] + fp1[8]); t_float im = fp1[1] - 0.5 * (fp1[-7] + fp1[9]); fp1[3] = (total_power += (fp1[2] = re * re + im * im)); } if (total_power > 1e-9f) { total_db = (100.f - DBFUDGE) + LOGTODB * log(total_power/n); total_loudness = fsqrt(fsqrt(total_power)); if (total_db < 0) total_db = 0; } else total_db = total_loudness = 0; /* store new db in history vector */ x->x_dbs[newphase] = total_db; if (total_db < x->x_amplo) goto nopow; #if 1 if (x->x_nprint) post("power %f", total_power); #endif #if CHECKER /* verify that our FFT resampling thing is putting out good results */ for (i = 0; i < hop; i++) { checker3[2*i] = fiddle_checker[i]; checker3[2*i + 1] = 0; checker3[n + 2*i] = fiddle_checker[i] = x->x_inbuf[i]; checker3[n + 2*i + 1] = 0; } for (i = 2*n; i < 4*n; i++) checker3[i] = 0; fft(checker3, 2*n, 0); if (x->x_nprint) { for (i = 0, fp = checker3; i < 16; i++, fp += 2) post("spect %d %f %f --> %f", i, fp[0], fp[1], sqrt(fp[0] * fp[0] + fp[1] * fp[1])); } #endif npeak = 0; /* search for peaks */ for (i = MINBIN, fp = spect1+4*MINBIN, pk1 = peaklist; i < n-2 && npeak < npeaktot; i++, fp += 4) { t_float height = fp[2], h1 = fp[-2], h2 = fp[6]; t_float totalfreq, pfreq, f1, f2, m, var, stdev; if (height < h1 || height < h2 || h1 < 0.00001f*total_power || h2 < 0.00001f*total_power) continue; /* use an informal phase vocoder to estimate the frequency. Do this for the two adjacent bins too. */ pfreq= ((fp[-8] - fp[8]) * (2.0f * fp[0] - fp[8] - fp[-8]) + (fp[-7] - fp[9]) * (2.0f * fp[1] - fp[9] - fp[-7])) / (2.0f * height); f1= ((fp[-12] - fp[4]) * (2.0f * fp[-4] - fp[4] - fp[-12]) + (fp[-11] - fp[5]) * (2.0f * fp[-3] - fp[5] - fp[-11])) / (2.0f * h1) - 1; f2= ((fp[-4] - fp[12]) * (2.0f * fp[4] - fp[12] - fp[-4]) + (fp[-3] - fp[13]) * (2.0f * fp[5] - fp[13] - fp[-3])) / (2.0f * h2) + 1; /* get sample mean and variance of the three */ m = 0.333333f * (pfreq + f1 + f2); var = 0.5f * ((pfreq-m)*(pfreq-m) + (f1-m)*(f1-m) + (f2-m)*(f2-m)); totalfreq = i + m; if (var * total_power > KNOCKTHRESH * height || var < 1e-30) { #if 0 if (x->x_nprint) post("cancel: %.2f hz, index %.1f, power %.5f, stdev=%.2f", totalfreq * hzperbin, BPERO_OVER_LOG2 * log(totalfreq) - 96, height, sqrt(var)); #endif continue; } stdev = fsqrt(var); if (totalfreq < 4) { if (x->x_nprint) post("oops: was %d, freq %f, m %f, stdev %f h %f", i, totalfreq, m, stdev, height); totalfreq = 4; } pk1->p_width = stdev; pk1->p_pow = height; pk1->p_loudness = fsqrt(fsqrt(height)); pk1->p_fp = fp; pk1->p_freq = totalfreq; npeak++; #if 1 if (x->x_nprint) { post("peak: %.2f hz. index %.1f, power %.5f, stdev=%.2f", pk1->p_freq * hzperbin, BPERO_OVER_LOG2 * log(pk1->p_freq) - 96, height, stdev); } #endif pk1++; } /* prepare the raw peaks for output */ for (i = 0, pk1 = peaklist, pk2 = x->x_peakbuf; i < npeak; i++, pk1++, pk2++) { t_float loudness = pk1->p_loudness; if (i >= npeakout) break; pk2->po_freq = hzperbin * pk1->p_freq; pk2->po_amp = (2. / (t_float)n) * (loudness * loudness); } for (; i < npeakout; i++, pk2++) pk2->po_amp = pk2->po_freq = 0; /* now, working back into spect2, make a sort of "liklihood" * spectrum. Proceeding in 48ths of an octave, from 2 to * n/2 (in bins), the likelihood of each pitch range is contributed * to by every peak in peaklist that's an integer multiple of it * in frequency. */ if (npeak > npeakanal) npeak = npeakanal; /* max # peaks to analyze */ for (i = 0, fp1 = histogram; i < maxbin; i++) *fp1++ = 0; for (i = 0, pk1 = peaklist; i < npeak; i++, pk1++) { t_float pit = BPERO_OVER_LOG2 * flog(pk1->p_freq) - 96.0; t_float binbandwidth = FACTORTOBINS * pk1->p_width/pk1->p_freq; t_float putbandwidth = (binbandwidth < 2 ? 2 : binbandwidth); t_float weightbandwidth = (binbandwidth < 1.0 ? 1.0 : binbandwidth); /* t_float weightamp = 1.0f + 3.0f * pk1->p_pow / pow; */ t_float weightamp = 4. * pk1->p_loudness / total_loudness; for (j = 0, fp2 = sigfiddle_partialonset; j < NPARTIALONSET; j++, fp2++) { t_float bin = pit - *fp2; if (bin < maxbin) { t_float para, pphase, score = 30.0 * weightamp / ((j+x->x_npartial) * weightbandwidth); int firstbin = bin + 0.5f - 0.5f * putbandwidth; int lastbin = bin + 0.5f + 0.5f * putbandwidth; int ibw = lastbin - firstbin; if (firstbin < -BINGUARD) break; para = 1.0f / (putbandwidth * putbandwidth); for (k = 0, fp3 = histogram + firstbin, pphase = firstbin-bin; k <= ibw; k++, fp3++, pphase += 1.0f) { *fp3 += score * (1.0f - para * pphase * pphase); } } } } #if 1 if (x->x_nprint) { for (i = 0; i < 6*5; i++) { t_float fhz = hzperbin * exp ((8*i + 96) * (1./BPERO_OVER_LOG2)); if (!(i % 6)) post("-- bin %d pitch %f freq %f----", 8*i, ftom(fhz), fhz);; post("%3d %3d %3d %3d %3d %3d %3d %3d", (int)(histogram[8*i]), (int)(histogram[8*i+1]), (int)(histogram[8*i+2]), (int)(histogram[8*i+3]), (int)(histogram[8*i+4]), (int)(histogram[8*i+5]), (int)(histogram[8*i+6]), (int)(histogram[8*i+7])); } } #endif /* * Next we find up to NPITCH strongest peaks in the histogram. * if a peak is related to a stronger one via an interval in * the sigfiddle_partialonset array, we suppress it. */ for (npitch = 0; npitch < x->x_npitch; npitch++) { int indx; t_float best; if (npitch) { for (best = 0, indx = -1, j=1; j < maxbin-1; j++) { if (histogram[j] > best && histogram[j] > histogram[j-1] && histogram[j] > histogram[j+1]) { for (k = 0; k < npitch; k++) if (histvec[k].h_index == j) goto peaknogood; for (k = 0; k < NPARTIALONSET; k++) { if (j - sigfiddle_intpartialonset[k] < 0) break; if (histogram[j - sigfiddle_intpartialonset[k]] > histogram[j]) goto peaknogood; } for (k = 0; k < NPARTIALONSET; k++) { if (j + sigfiddle_intpartialonset[k] >= maxbin) break; if (histogram[j + sigfiddle_intpartialonset[k]] > histogram[j]) goto peaknogood; } indx = j; best = histogram[j]; } peaknogood: ; } } else { for (best = 0, indx = -1, j=0; j < maxbin; j++) if (histogram[j] > best) indx = j, best = histogram[j]; } if (indx < 0) break; histvec[npitch].h_value = best; histvec[npitch].h_index = indx; } #if 1 if (x->x_nprint) { for (i = 0; i < npitch; i++) { post("index %d freq %f --> value %f", histvec[i].h_index, exp((1./BPERO_OVER_LOG2) * (histvec[i].h_index + 96)), histvec[i].h_value); post("next %f , prev %f", exp((1./BPERO_OVER_LOG2) * (histvec[i].h_index + 97)), exp((1./BPERO_OVER_LOG2) * (histvec[i].h_index + 95)) ); } } #endif /* for each histogram peak, we now search back through the * FFT peaks. A peak is a pitch if either there are several * harmonics that match it, or else if (a) the fundamental is * present, and (b) the sum of the powers of the contributing peaks * is at least 1/100 of the total power. * * A peak is a contributor if its frequency is within 25 cents of * a partial from 1 to 16. * * Finally, we have to be at least 5 bins in frequency, which * corresponds to 2-1/5 periods fitting in the analysis window. */ for (i = 0; i < npitch; i++) { t_float cumpow = 0, cumstrength = 0, freqnum = 0, freqden = 0; int npartials = 0, nbelow8 = 0; /* guessed-at frequency in bins */ t_float putfreq = fexp((1.0 / BPERO_OVER_LOG2) * (histvec[i].h_index + 96.0f)); for (j = 0; j < npeak; j++) { t_float fpnum = peaklist[j].p_freq/putfreq; int pnum = fpnum + 0.5f; t_float fipnum = pnum; t_float deviation; if (pnum > 16 || pnum < 1) continue; deviation = 1.0f - fpnum/fipnum; if (deviation > -PARTIALDEVIANCE && deviation < PARTIALDEVIANCE) { /* * we figure this is a partial since it's within 1/4 of * a halftone of a multiple of the putative frequency. */ t_float stdev, weight; npartials++; if (pnum < 8) nbelow8++; cumpow += peaklist[j].p_pow; cumstrength += fsqrt(fsqrt(peaklist[j].p_pow)); stdev = (peaklist[j].p_width > MINBW ? peaklist[j].p_width : MINBW); weight = 1.0f / ((stdev*fipnum) * (stdev*fipnum)); freqden += weight; freqnum += weight * peaklist[j].p_freq/fipnum; #if 1 if (x->x_nprint) { post("peak %d partial %d f=%f w=%f", j, pnum, peaklist[j].p_freq/fipnum, weight); } #endif } #if 1 else if (x->x_nprint) post("peak %d partial %d dev %f", j, pnum, deviation); #endif } if ((nbelow8 < 4 || npartials < 7) && cumpow < 0.01f * total_power) histvec[i].h_value = 0; else { t_float pitchpow = (cumstrength * cumstrength) * (cumstrength * cumstrength); t_float freqinbins = freqnum/freqden; /* check for minimum output frequency */ if (freqinbins < MINFREQINBINS) histvec[i].h_value = 0; else { /* we passed all tests... save the values we got */ histvec[i].h_pitch = ftom(hzperbin * freqnum/freqden); histvec[i].h_loud = (100.0f -DBFUDGE) + (LOGTODB) * log(pitchpow/n); } } } #if 1 if (x->x_nprint) { for (i = 0; i < npitch; i++) { if (histvec[i].h_value > 0) post("index %d pit %f loud %f", histvec[i].h_index, histvec[i].h_pitch, histvec[i].h_loud); else post("-- cancelled --"); } } #endif /* now try to find continuous pitch tracks that match the new * pitches. First mark each peak unmatched. */ for (i = 0, hp1 = histvec; i < npitch; i++, hp1++) hp1->h_used = 0; /* for each old pitch, try to match a new one to it. */ for (i = 0, phist = x->x_hist; i < x->x_npitch; i++, phist++) { t_float thispitch = phist->h_pitches[oldphase]; phist->h_pitch = 0; /* no output, thanks */ phist->h_wherefrom = 0; if (thispitch == 0.0f) continue; for (j = 0, hp1 = histvec; j < npitch; j++, hp1++) if ((hp1->h_value > 0) && hp1->h_pitch > thispitch - GLISS && hp1->h_pitch < thispitch + GLISS) { phist->h_wherefrom = hp1; hp1->h_used = 1; } } for (i = 0, hp1 = histvec; i < npitch; i++, hp1++) if ((hp1->h_value > 0) && !hp1->h_used) { for (j = 0, phist = x->x_hist; j < x->x_npitch; j++, phist++) if (!phist->h_wherefrom) { phist->h_wherefrom = hp1; phist->h_age = 0; phist->h_noted = 0; hp1->h_used = 1; goto happy; } break; happy: ; } /* copy the pitch info into the history vector */ for (i = 0, phist = x->x_hist; i < x->x_npitch; i++, phist++) { if (phist->h_wherefrom) { phist->h_amps[newphase] = phist->h_wherefrom->h_loud; phist->h_pitches[newphase] = phist->h_wherefrom->h_pitch; (phist->h_age)++; } else { phist->h_age = 0; phist->h_amps[newphase] = phist->h_pitches[newphase] = 0; } } #if 1 if (x->x_nprint) { post("vibrato %d %f", x->x_vibbins, x->x_vibdepth); for (i = 0, phist = x->x_hist; i < x->x_npitch; i++, phist++) { post("noted %f, age %d", phist->h_noted, phist->h_age); #ifndef I860 post("values %f %f %f %f %f", phist->h_pitches[newphase], phist->h_pitches[(newphase + HISTORY-1)%HISTORY], phist->h_pitches[(newphase + HISTORY-2)%HISTORY], phist->h_pitches[(newphase + HISTORY-3)%HISTORY], phist->h_pitches[(newphase + HISTORY-4)%HISTORY]); #endif } } #endif /* look for envelope attacks */ x->x_attackvalue = 0; if (x->x_peaked) { if (total_db > x->x_amphi) { int binlook = newphase - x->x_attackbins; if (binlook < 0) binlook += HISTORY; if (total_db > x->x_dbs[binlook] + x->x_attackthresh) { x->x_attackvalue = 1; x->x_peaked = 0; } } } else { int binlook = newphase - x->x_attackbins; if (binlook < 0) binlook += HISTORY; if (x->x_dbs[binlook] > x->x_amphi && x->x_dbs[binlook] > total_db) x->x_peaked = 1; } /* for each current frequency track, test for a new note using a * stability criterion. Later perhaps we should also do as in * pitch~ and check for unstable notes a posteriori when * there's a new attack with no note found since the last onset; * but what's an attack &/or onset when we're polyphonic? */ for (i = 0, phist = x->x_hist; i < x->x_npitch; i++, phist++) { /* * if we've found a pitch but we've now strayed from it turn * it off. */ if (phist->h_noted) { if (phist->h_pitches[newphase] > phist->h_noted + x->x_vibdepth || phist->h_pitches[newphase] < phist->h_noted - x->x_vibdepth) phist->h_noted = 0; } else { if (phist->h_wherefrom && phist->h_age >= x->x_vibbins) { t_float centroid = 0; int not = 0; for (j = 0, k = newphase; j < x->x_vibbins; j++) { centroid += phist->h_pitches[k]; k--; if (k < 0) k = HISTORY-1; } centroid /= x->x_vibbins; for (j = 0, k = newphase; j < x->x_vibbins; j++) { /* calculate deviation from norm */ t_float dev = centroid - phist->h_pitches[k]; k--; if (k < 0) k = HISTORY-1; if (dev > x->x_vibdepth || -dev > x->x_vibdepth) not = 1; } if (!not) { phist->h_pitch = phist->h_noted = centroid; } } } } return; nopow: for (i = 0; i < x->x_npitch; i++) { x->x_hist[i].h_pitch = x->x_hist[i].h_noted = x->x_hist[i].h_pitches[newphase] = x->x_hist[i].h_amps[newphase] = 0; x->x_hist[i].h_age = 0; } x->x_peaked = 1; x->x_dbage = 0; } void sigfiddle_debug(t_sigfiddle *x) { x->x_nprint = 1; } void sigfiddle_print(t_sigfiddle *x) { post("npoints %d,", 2 * x->x_hop); post("amp-range %f %f,", x->x_amplo, x->x_amphi); post("reattack %d %f,", x->x_attacktime, x->x_attackthresh); post("vibrato %d %f", x->x_vibtime, x->x_vibdepth); post("npartial %f", x->x_npartial); post("auto %d", x->x_auto); } void sigfiddle_amprange(t_sigfiddle *x, t_floatarg amplo, t_floatarg amphi) { if (amplo < 0) amplo = 0; if (amphi < amplo) amphi = amplo + 1; x->x_amplo = amplo; x->x_amphi = amphi; } void sigfiddle_reattack(t_sigfiddle *x, t_floatarg attacktime, t_floatarg attackthresh) { if (attacktime < 0) attacktime = 0; if (attackthresh <= 0) attackthresh = 1000; x->x_attacktime = attacktime; x->x_attackthresh = attackthresh; x->x_attackbins = (x->x_sr * 0.001 * attacktime) / x->x_hop; if (x->x_attackbins >= HISTORY) x->x_attackbins = HISTORY - 1; } void sigfiddle_vibrato(t_sigfiddle *x, t_floatarg vibtime, t_floatarg vibdepth) { if (vibtime < 0) vibtime = 0; if (vibdepth <= 0) vibdepth = 1000; x->x_vibtime = vibtime; x->x_vibdepth = vibdepth; x->x_vibbins = (x->x_sr * 0.001 * vibtime) / x->x_hop; if (x->x_vibbins >= HISTORY) x->x_vibbins = HISTORY - 1; if (x->x_vibbins < 1) x->x_vibbins = 1; } void sigfiddle_npartial(t_sigfiddle *x, t_floatarg npartial) { if (npartial < 0.1) npartial = 0.1; x->x_npartial = npartial; } void sigfiddle_auto(t_sigfiddle *x, t_floatarg f) { x->x_auto = (f != 0); } static void sigfiddle_freebird(t_sigfiddle *x) { if (x->x_inbuf) { freebytes(x->x_inbuf, sizeof(t_float) * x->x_hop); x->x_inbuf = 0; } if (x->x_lastanalysis) { freebytes(x->x_lastanalysis, sizeof(t_float) * (2 * x->x_hop + 4 * FILTSIZE)); x->x_lastanalysis = 0; } if (x->x_spiral) { freebytes(x->x_spiral, sizeof(t_float) * 2 * x->x_hop); x->x_spiral = 0; } x->x_hop = 0; } int sigfiddle_setnpoints(t_sigfiddle *x, t_floatarg fnpoints) { int i, npoints = fnpoints; sigfiddle_freebird(x); if (npoints < MINPOINTS || npoints > MAXPOINTS) { pd_error(0, "fiddle~: npoints out of range; using %d", npoints = DEFAULTPOINTS); } if (npoints != (1 << sigfiddle_ilog2(npoints))) { pd_error(0, "fiddle~: npoints not a power of 2; using %d", npoints = (1 << sigfiddle_ilog2(npoints))); } x->x_hop = npoints >> 1; if (!(x->x_inbuf = (t_float *)getbytes(sizeof(t_float) * x->x_hop))) goto fail; if (!(x->x_lastanalysis = (t_float *)getbytes( sizeof(t_float) * (2 * x->x_hop + 4 * FILTSIZE)))) goto fail; if (!(x->x_spiral = (t_float *)getbytes(sizeof(t_float) * 2 * x->x_hop))) goto fail; for (i = 0; i < x->x_hop; i++) x->x_inbuf[i] = 0; for (i = 0; i < npoints + 4 * FILTSIZE; i++) x->x_lastanalysis[i] = 0; for (i = 0; i < x->x_hop; i++) x->x_spiral[2*i] = cos((3.14159*i)/(npoints)), x->x_spiral[2*i+1] = -sin((3.14159*i)/(npoints)); x->x_phase = 0; return (1); fail: sigfiddle_freebird(x); return (0); } int sigfiddle_doinit(t_sigfiddle *x, long npoints, long npitch, long npeakanal, long npeakout) { t_float *buf1, *buf2, *buf3; t_peakout *buf4; int i; if (!npeakanal && !npeakout) npeakanal = DEFNPEAK, npeakout = 0; if (npeakanal < 0) npeakanal = 0; else if (npeakanal > MAXPEAK) npeakanal = MAXPEAK; if (npeakout < 0) npeakout = 0; else if (npeakout > MAXPEAK) npeakout = MAXPEAK; if (npitch <= 0) npitch = 0; else if (npitch > MAXNPITCH) npitch = MAXNPITCH; if (npeakanal && !npitch) npitch = 1; if (!npoints) npoints = DEFAULTPOINTS; if (!sigfiddle_setnpoints(x, npoints)) { pd_error(0, "fiddle~: out of memory"); return (0); } if (!(buf4 = (t_peakout *)getbytes(sizeof(*buf4) * npeakout))) { sigfiddle_freebird(x); pd_error(0, "fiddle~: out of memory"); return (0); } for (i = 0; i < npeakout; i++) buf4[i].po_freq = buf4[i].po_amp = 0; x->x_peakbuf = buf4; x->x_npeakout = (int)npeakout; x->x_npeakanal = (int)npeakanal; x->x_phase = 0; x->x_histphase = 0; x->x_sr = 44100; /* this and the next are filled in later */ for (i = 0; i < MAXNPITCH; i++) { int j; x->x_hist[i].h_pitch = x->x_hist[i].h_noted = 0; x->x_hist[i].h_age = 0; x->x_hist[i].h_wherefrom = 0; x->x_hist[i].h_outlet = 0; for (j = 0; j < HISTORY; j++) x->x_hist[i].h_amps[j] = x->x_hist[i].h_pitches[j] = 0; } x->x_nprint = 0; x->x_npitch = (int)npitch; for (i = 0; i < HISTORY; i++) x->x_dbs[i] = 0; x->x_dbage = 0; x->x_peaked = 0; x->x_auto = 1; x->x_amplo = DEFAMPLO; x->x_amphi = DEFAMPHI; x->x_attacktime = DEFATTACKTIME; x->x_attackbins = 1; /* real value calculated afterward */ x->x_attackthresh = DEFATTACKTHRESH; x->x_vibtime = DEFVIBTIME; x->x_vibbins = 1; /* real value calculated afterward */ x->x_vibdepth = DEFVIBDEPTH; x->x_npartial = 7; x->x_attackvalue = 0; return (1); } /* formalities for JMAX */ #ifdef JMAX void sigfiddle_debug13(fts_object_t *o, int winlet, fts_symbol_t s, int ac, const fts_atom_t *at) { t_sigfiddle *x = (t_sigfiddle *)o; sigfiddle_debug(x); } void sigfiddle_print13(fts_object_t *o, int winlet, fts_symbol_t s, int ac, const fts_atom_t *at) { t_sigfiddle *x = (t_sigfiddle *)o; sigfiddle_print(x); } void sigfiddle_amprange13(fts_object_t *o, int winlet, fts_symbol_t s, int ac, const fts_atom_t *at) { t_sigfiddle *x = (t_sigfiddle *)o; t_float lo = (t_float) fts_get_float_arg(ac, at, 0, 0); t_float hi = (t_float) fts_get_float_arg(ac, at, 1, 0); sigfiddle_amprange(x, lo, hi); } void sigfiddle_reattack13(fts_object_t *o, int winlet, fts_symbol_t s, int ac, const fts_atom_t *at) { t_sigfiddle *x = (t_sigfiddle *)o; long msec = fts_get_float_arg(ac, at, 0, 0); t_float db = (t_float) fts_get_float_arg(ac, at, 1, 0); sigfiddle_reattack(x, msec, db); } void sigfiddle_vibrato13(fts_object_t *o, int winlet, fts_symbol_t s, int ac, const fts_atom_t *at) { t_sigfiddle *x = (t_sigfiddle *)o; long msec = fts_get_float_arg(ac, at, 0, 0); t_float halftones = (t_float) fts_get_float_arg(ac, at, 1, 0); sigfiddle_vibrato(x, msec, halftones); } void sigfiddle_npartial13(fts_object_t *o, int winlet, fts_symbol_t s, int ac, const fts_atom_t *at) { t_sigfiddle *x = (t_sigfiddle *)o; t_float npartial = (t_float) fts_get_float_arg(ac, at, 0, 0); sigfiddle_npartial(x, npartial); } void ftl_sigfiddle(fts_word_t *a) { t_sigfiddle *x = (t_sigfiddle *)fts_word_get_long(a); t_float *in = (t_float *)fts_word_get_long(a + 1); long n_tick = fts_word_get_long(a + 2); int count; t_float *fp, *fp2; for (count = 0, fp = x->x_inbuf + x->x_phase; count < n_tick; count++) *fp++ = *in++; if (fp == x->x_inbuf + x->x_hop) { sigfiddle_doit(x); x->x_phase = 0; fts_alarm_set_delay(&x->x_clock, 0L); /* output bang */ fts_alarm_arm(&x->x_clock); if (x->x_nprint) x->x_nprint--; } else x->x_phase += n_tick; } void sigfiddle_put(fts_object_t *o, int winlet, fts_symbol_t *s, int ac, const fts_atom_t *at) { t_sigfiddle *x = (t_sigfiddle *)o; fts_dsp_descr_t *dsp = (fts_dsp_descr_t *)fts_get_long_arg(ac, at, 0, 0); fts_atom_t a[3]; x->x_sr = fts_dsp_get_input_srate(dsp, 0); sigfiddle_reattack(x, x->x_attacktime, x->x_attackthresh); sigfiddle_vibrato(x, x->x_vibtime, x->x_vibdepth); fts_set_long(a, (long)x); fts_set_symbol(a+1, fts_dsp_get_input_name(dsp, 0)); fts_set_long(a+2, fts_dsp_get_input_size(dsp, 0)); dsp_add_funcall(dsp_symbol, 3, a); } void sigfiddle_tick(fts_alarm_t *alarm, void *p) { fts_object_t *o = (fts_object_t *)p; t_sigfiddle *x = (t_sigfiddle *)p; int i; t_pitchhist *ph; fts_outlet_float(o, OUTLETpower, x->x_dbs[x->x_histphase]); for (i = 0, ph = x->x_hist; i < x->x_npitch; i++, ph++) { fts_atom_t at[2]; fts_set_float(at, ph->h_pitches[x->x_histphase]); fts_set_float(at+1, ph->h_amps[x->x_histphase]); fts_outlet_list(o, OUTLETmicropitch3 - i, 2, at); } if (x->x_attackvalue) fts_outlet_bang(o, OUTLETattack); for (i = 0, ph = x->x_hist; i < x->x_npitch; i++, ph++) if (ph->h_pitch) fts_outlet_float(o, OUTLETpitch, ph->h_pitch); } static void sigfiddle_delete(fts_object_t *o, int winlet, fts_symbol_t *s, int ac, const fts_atom_t *at) { t_sigfiddle *x = (t_sigfiddle *)o; fts_free(x->x_inbuf); fts_free(x->x_lastanalysis); fts_free(x->x_spiral); dsp_list_remove(o); } static void sigfiddle_init(fts_object_t *o, int winlet, fts_symbol_t *s, int ac, const fts_atom_t *at) { t_sigfiddle *x = (t_sigfiddle *)o; t_float *buf1, *buf2, *buf3; int i, hop; long npoints = fts_get_long_arg(ac, at, 1, 0); long npitch = fts_get_long_arg(ac, at, 2, 0); long npeakanal = fts_get_long_arg(ac, at, 3, 0); long npeakout = fts_get_long_arg(ac, at, 4, 0); if (!sigfiddle_doinit(x, npoints, npitch, npeakanal, npeakout)) { post("fiddle~: initialization failed"); return; } hop = npoints>>1; if (fts_fft_declaresize(hop) != fts_Success) post("fiddle~: bad FFT size"); fts_alarm_init(&(x->x_clock), 0, sigfiddle_tick, x); dsp_list_insert(o); } static fts_status_t sigfiddle_instantiate(fts_class_t *cl, int ac, const fts_atom_t *at) { int i; fts_type_t a[5]; fts_class_init(cl, sizeof(t_sigfiddle), 1, 6, 0); /* 1 inlet + 6 outlets */ /* the system methods */ a[0] = fts_Symbol; a[1] = fts_Long | fts_OptArg; a[2] = fts_Long | fts_OptArg; fts_method_define(cl, fts_SystemInlet, fts_s_init, sigfiddle_init, 3, a); fts_method_define(cl, fts_SystemInlet, fts_s_delete, sigfiddle_delete, 0, a); a[0] = fts_Object; fts_method_define(cl, fts_SystemInlet, fts_s_put, sigfiddle_put, 1, a); /* class' own methods */ fts_method_define(cl, 0, fts_new_symbol("print"), sigfiddle_print13, 0, a); fts_method_define(cl, 0, fts_new_symbol("debug"), sigfiddle_debug13, 0, a); fts_method_define(cl, 0, fts_new_symbol("amp-range"), sigfiddle_amprange13, 0, a); fts_method_define(cl, 0, fts_new_symbol("reattack"), sigfiddle_reattack13, 0, a); fts_method_define(cl, 0, fts_new_symbol("vibrato"), sigfiddle_vibrato13, 0, a); fts_method_define(cl, 0, fts_new_symbol("npartial"), sigfiddle_npartial13, 0, a); /* classes signal inlets */ dsp_sig_inlet(cl, 0); /* declare signal input #0 */ /* classes outlets */ a[0] = fts_Float; fts_outlet_type_define(cl, OUTLETpitch, fts_s_float, 1, a); /* declare outlet #0 */ fts_outlet_type_define(cl, OUTLETattack, fts_s_bang, 0, a); /* declare outlet #1 */ a[0] = fts_VarArgs; fts_outlet_type_define(cl, OUTLETmicropitch1, fts_s_list, 1, a); /* declare outlet #2 */ fts_outlet_type_define(cl, OUTLETmicropitch2, fts_s_list, 1, a); /* declare outlet #3 */ fts_outlet_type_define(cl, OUTLETmicropitch3, fts_s_list, 1, a); /* declare outlet #4 */ a[0] = fts_Float; fts_outlet_type_define(cl, OUTLETpower, fts_s_float, 1, a); /* declare outlet #5 */ dsp_symbol = fts_new_symbol("fiddle"); dsp_declare_function(dsp_symbol, ftl_sigfiddle); /* DSP properties */ fts_class_put_prop(cl, fts_s_dsp_is_sink, fts_true); return(fts_Success); } void fiddle_config(void) { sys_log(fiddle_version); fts_metaclass_create(fts_new_symbol(CLASSNAME), sigfiddle_instantiate, fts_always_equiv); } fts_module_t fiddle_module = {"fiddle", "sonic meat fiddle", fiddle_config, 0}; #endif /* JMAX */ #ifdef PD static t_int *fiddle_perform(t_int *w) { t_float *in = (t_float *)(w[1]); t_sigfiddle *x = (t_sigfiddle *)(w[2]); int n = (int)(w[3]); int count; t_float *fp; if (!x->x_hop) goto nono; for (count = 0, fp = x->x_inbuf + x->x_phase; count < n; count++) *fp++ = *in++; if (fp == x->x_inbuf + x->x_hop) { sigfiddle_doit(x); x->x_phase = 0; if (x->x_auto) clock_delay(x->x_clock, 0L); if (x->x_nprint) x->x_nprint--; } else x->x_phase += n; nono: return (w+4); } void sigfiddle_dsp(t_sigfiddle *x, t_signal **sp) { x->x_sr = sp[0]->s_sr; sigfiddle_reattack(x, x->x_attacktime, x->x_attackthresh); sigfiddle_vibrato(x, x->x_vibtime, x->x_vibdepth); dsp_add(fiddle_perform, 3, sp[0]->s_vec, x, (t_int)sp[0]->s_n); } /* This is the callback function for the clock, but also acts as the "bang" method; you can leave "auto" on to get this called automatically (the default) or turn auto off and bang it yourself. */ void sigfiddle_bang(t_sigfiddle *x) { int i; t_pitchhist *ph; if (x->x_npeakout) { int npeakout = x->x_npeakout; t_peakout *po; for (i = 0, po = x->x_peakbuf; i < npeakout; i++, po++) { t_atom at[3]; SETFLOAT(at, i+1); SETFLOAT(at+1, po->po_freq); SETFLOAT(at+2, po->po_amp); outlet_list(x->x_peakout, 0, 3, at); } } outlet_float(x->x_envout, x->x_dbs[x->x_histphase]); for (i = 0, ph = x->x_hist; i < x->x_npitch; i++, ph++) { t_atom at[2]; SETFLOAT(at, ph->h_pitches[x->x_histphase]); SETFLOAT(at+1, ph->h_amps[x->x_histphase]); outlet_list(ph->h_outlet, 0, 2, at); } if (x->x_attackvalue) outlet_bang(x->x_attackout); for (i = 0, ph = x->x_hist; i < x->x_npitch; i++, ph++) if (ph->h_pitch) outlet_float(x->x_noteout, ph->h_pitch); } void sigfiddle_ff(t_sigfiddle *x) /* cleanup on free */ { if (x->x_inbuf) { freebytes(x->x_inbuf, sizeof(t_float) * x->x_hop); freebytes(x->x_lastanalysis, sizeof(t_float) * (2*x->x_hop + 4 * FILTSIZE)); freebytes(x->x_spiral, sizeof(t_float) * 2*x->x_hop); freebytes(x->x_peakbuf, sizeof(*x->x_peakbuf) * x->x_npeakout); clock_free(x->x_clock); } } static t_class *sigfiddle_class; void *sigfiddle_new(t_floatarg npoints, t_floatarg npitch, t_floatarg fnpeakanal, t_floatarg fnpeakout) { t_sigfiddle *x = (t_sigfiddle *)pd_new(sigfiddle_class); int i; int npeakanal = fnpeakanal, npeakout = fnpeakout; if (!sigfiddle_doinit(x, npoints, npitch, npeakanal, npeakout)) { x->x_inbuf = 0; /* prevent the free routine from cleaning up */ pd_free(&x->x_ob.ob_pd); return (0); } x->x_noteout = outlet_new(&x->x_ob, gensym("float")); x->x_attackout = outlet_new(&x->x_ob, gensym("bang")); for (i = 0; i < x->x_npitch; i++) x->x_hist[i].h_outlet = outlet_new(&x->x_ob, gensym("list")); x->x_envout = outlet_new(&x->x_ob, gensym("float")); if (x->x_npeakout) x->x_peakout = outlet_new(&x->x_ob, gensym("list")); else x->x_peakout = 0; x->x_clock = clock_new(&x->x_ob.ob_pd, (t_method)sigfiddle_bang); return (x); } void fiddle_tilde_setup(void) { sigfiddle_class = class_new(gensym("fiddle~"), (t_newmethod)sigfiddle_new, (t_method)sigfiddle_ff, sizeof(t_sigfiddle), 0, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0); class_addmethod(sigfiddle_class, (t_method)sigfiddle_dsp, gensym("dsp"), 0); class_addmethod(sigfiddle_class, (t_method)sigfiddle_debug, gensym("debug"), 0); class_addmethod(sigfiddle_class, (t_method)sigfiddle_setnpoints, gensym("npoints"), A_FLOAT, 0); class_addmethod(sigfiddle_class, (t_method)sigfiddle_amprange, gensym("amp-range"), A_FLOAT, A_FLOAT, 0); class_addmethod(sigfiddle_class, (t_method)sigfiddle_reattack, gensym("reattack"), A_FLOAT, A_FLOAT, 0); class_addmethod(sigfiddle_class, (t_method)sigfiddle_vibrato, gensym("vibrato"), A_FLOAT, A_FLOAT, 0); class_addmethod(sigfiddle_class, (t_method)sigfiddle_npartial, gensym("npartial"), A_FLOAT, 0); class_addmethod(sigfiddle_class, (t_method)sigfiddle_auto, gensym("auto"), A_FLOAT, 0); class_addmethod(sigfiddle_class, (t_method)sigfiddle_print, gensym("print"), 0); class_addmethod(sigfiddle_class, nullfn, gensym("signal"), 0); class_addbang(sigfiddle_class, sigfiddle_bang); class_addcreator((t_newmethod)sigfiddle_new, gensym("fiddle"), A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0); post(fiddle_version); } void fiddle_setup(void) { fiddle_tilde_setup(); } #endif /* PD */ #ifdef MAX26 void cu_fiddle(t_float *in1, t_sigfiddle *x, int n) { int count; t_float *fp, *fp2; for (count = 0, fp = x->x_inbuf + x->x_phase; count < n; count++) *fp++ = *in1++; if (fp == x->x_inbuf + x->x_hop) { sigfiddle_doit(x); x->x_phase = 0; if (x->x_auto) clock_delay(x->x_clock, 0L); if (x->x_nprint) x->x_nprint--; } else x->x_phase += n; } void sigfiddle_put(t_sigfiddle *x, long whether) { if (whether) { u_stdout(x); x->x_sr = x->x_io[0]->s_sr; sigfiddle_reattack(x, x->x_attacktime, x->x_attackthresh); sigfiddle_vibrato(x, x->x_vibtime, x->x_vibdepth); dspchain_addc(cu_fiddle, 3, x->x_io[0]->s_shit, x, x->x_io[0]->s_n); } } void sigfiddle_tick(t_sigfiddle *x) /* callback function for the clock */ { int i; t_pitchhist *ph; outlet_float(x->x_envout, x->x_dbs[x->x_histphase]); for (i = 0, ph = x->x_hist; i < x->x_npitch; i++, ph++) { t_atom at[2]; SETFLOAT(at, ph->h_pitches[x->x_histphase]); SETFLOAT(at+1, ph->h_amps[x->x_histphase]); outlet_list(ph->h_outlet, NIL, 2, at); } if (x->x_attackvalue) outlet_bang(x->x_attackout); for (i = 0, ph = x->x_hist; i < x->x_npitch; i++, ph++) if (ph->h_pitch) outlet_float(x->x_noteout, ph->h_pitch); } void sigfiddle_ff(t_sigfiddle *x) /* cleanup on free */ { if (x->x_inbuf) { freebytes(x->x_inbuf, sizeof(t_float) * x->x_hop); freebytes(x->x_lastanalysis, sizeof(t_float) * (2*x->x_hop + 4 * FILTSIZE)); freebytes(x->x_spiral, sizeof(t_float) * 2*x->x_hop); clock_free(x->x_clock); u_clean(x); } } t_externclass *sigfiddle_class; void *sigfiddle_new(long npoints, long npitch, long npeakanal, long npeakout) { t_sigfiddle *x = (t_sigfiddle *)obj_new(&sigfiddle_class, 0); int i; if (!sigfiddle_doinit(x, npoints, npitch, npeakanal, npeakout)) { x->x_inbuf = 0; /* prevent the free routine from cleaning up */ obj_free(x); return (0); } u_setup(x, IN1, OUT0); x->x_envout = outlet_new(x, gensym("float")); for (i = 0; i < x->x_npitch; i++) x->x_hist[i].h_outlet = outlet_new(x, gensym("list")); x->x_attackout = outlet_new(x, gensym("bang")); x->x_noteout = outlet_new(x, gensym("float")); x->x_clock = clock_new(x, sigfiddle_tick); return (x); } void fiddle_setup() { c_extern(&sigfiddle_class, sigfiddle_new, sigfiddle_ff, gensym("fiddle"), sizeof(t_sigfiddle), 0, A_DEFLONG, A_DEFLONG, A_DEFLONG, A_DEFLONG, 0); c_addmess(sigfiddle_put, gensym("put"), A_CANT, 0); c_addmess(sigfiddle_debug, gensym("debug"), 0); c_addmess(sigfiddle_amprange, gensym("amp-range"), A_FLOAT, A_FLOAT, 0); c_addmess(sigfiddle_reattack, gensym("reattack"), A_FLOAT, A_FLOAT, 0); c_addmess(sigfiddle_vibrato, gensym("vibrato"), A_LONG, A_FLOAT, 0); c_addmess(sigfiddle_npartial, gensym("npartial"), A_FLOAT, 0); c_addmess(sigfiddle_print, gensym("print"), 0); u_inletmethod(0); /* one signal input */ #ifdef MAX post(fiddle_version); #endif } #endif /* MAX26 */ /************* Beginning of MSP Code ******************************/ #ifdef MSP static t_int *fiddle_perform(t_int *w) { t_float *in = (t_float *)(w[1]); t_sigfiddle *x = (t_sigfiddle *)(w[2]); int n = (int)(w[3]); int count,inc = x->x_downsample; t_float *fp; if (x->x_obj.z_disabled) goto skip; for (count = 0, fp = x->x_inbuf + x->x_phase; count < n; count+=inc) { *fp++ = *in; in += inc; } if (fp == x->x_inbuf + x->x_hop) { sigfiddle_doit(x); x->x_phase = 0; if (x->x_auto) clock_delay(x->x_clock, 0L); if (x->x_nprint) x->x_nprint--; } else x->x_phase += n; skip: return (w+4); } void sigfiddle_dsp(t_sigfiddle *x, t_signal **sp) { if (sp[0]->s_n > x->x_hop) { x->x_downsample = sp[0]->s_n / x->x_hop; post("* warning: fiddle~: will downsample input by %ld",x->x_downsample); x->x_sr = sp[0]->s_sr / x->x_downsample; } else { x->x_downsample = 1; x->x_sr = sp[0]->s_sr; } sigfiddle_reattack(x, x->x_attacktime, x->x_attackthresh); sigfiddle_vibrato(x, x->x_vibtime, x->x_vibdepth); dsp_add(fiddle_perform, 3, sp[0]->s_vec, x, (t_int)sp[0]->s_n); } void sigfiddle_tick(t_sigfiddle *x) /* callback function for the clock MSP*/ { int i; t_pitchhist *ph; if (x->x_npeakout) { int npeakout = x->x_npeakout; t_peakout *po; for (i = 0, po = x->x_peakbuf; i < npeakout; i++, po++) { t_atom at[3]; SETINT(at, i+1); SETFLOAT(at+1, po->po_freq); SETFLOAT(at+2, po->po_amp); outlet_list(x->x_peakout, 0, 3, at); } } outlet_float(x->x_envout, x->x_dbs[x->x_histphase]); for (i = 0, ph = x->x_hist; i < x->x_npitch; i++, ph++) { t_atom at[2]; SETFLOAT(at, ph->h_pitches[x->x_histphase]); SETFLOAT(at+1, ph->h_amps[x->x_histphase]); outlet_list(ph->h_outlet, 0, 2, at); } if (x->x_attackvalue) outlet_bang(x->x_attackout); for (i = 0, ph = x->x_hist; i < x->x_npitch; i++, ph++) if (ph->h_pitch) outlet_float(x->x_noteout, ph->h_pitch); } void sigfiddle_bang(t_sigfiddle *x) { int i; t_pitchhist *ph; if (x->x_npeakout) { int npeakout = x->x_npeakout; t_peakout *po; for (i = 0, po = x->x_peakbuf; i < npeakout; i++, po++) { t_atom at[3]; SETLONG(at, i+1); SETFLOAT(at+1, po->po_freq); SETFLOAT(at+2, po->po_amp); outlet_list(x->x_peakout, 0, 3, at); } } outlet_float(x->x_envout, x->x_dbs[x->x_histphase]); for (i = 0, ph = x->x_hist; i < x->x_npitch; i++, ph++) { t_atom at[2]; SETFLOAT(at, ph->h_pitches[x->x_histphase]); SETFLOAT(at+1, ph->h_amps[x->x_histphase]); outlet_list(ph->h_outlet, 0, 2, at); } if (x->x_attackvalue) outlet_bang(x->x_attackout); for (i = 0, ph = x->x_hist; i < x->x_npitch; i++, ph++) if (ph->h_pitch) outlet_float(x->x_noteout, ph->h_pitch); } void sigfiddle_ff(t_sigfiddle *x) /* cleanup on free MSP */ { if (x->x_inbuf) { t_freebytes(x->x_inbuf, sizeof(t_float) * x->x_hop); t_freebytes(x->x_lastanalysis, sizeof(t_float) * (2*x->x_hop + 4 * FILTSIZE)); t_freebytes(x->x_spiral, sizeof(t_float) * 2*x->x_hop); t_freebytes(x->x_peakbuf, sizeof(*x->x_peakbuf) * x->x_npeakout); } dsp_free((t_pxobject *)x); } void *sigfiddle_class; void *sigfiddle_new(long npoints, long npitch, long npeakanal, long npeakout) { t_sigfiddle *x = (t_sigfiddle *)newobject(sigfiddle_class); int i; if (!sigfiddle_doinit(x, npoints, npitch, npeakanal, npeakout)) { x->x_inbuf = 0; /* prevent the free routine from cleaning up */ return (0); } dsp_setup((t_pxobject *)x,1); x->x_clock = clock_new(x, (method)sigfiddle_tick); if (x->x_npeakout) x->x_peakout = listout((t_object *)x); else x->x_peakout = 0; x->x_envout = floatout((t_object *)x); for (i = 0; i < x->x_npitch; i++) x->x_hist[i].h_outlet = listout((t_object *)x); x->x_attackout = bangout((t_object *)x); x->x_noteout = floatout((t_object *)x); return (x); } void main() { setup(&sigfiddle_class, sigfiddle_new, (method)sigfiddle_ff, (short)sizeof(t_sigfiddle), 0L, A_DEFLONG, A_DEFLONG, A_DEFLONG, A_DEFLONG, 0); addmess((method)sigfiddle_dsp, "dsp", A_CANT, 0); addmess((method)sigfiddle_debug, "debug", 0); addmess((method)sigfiddle_setnpoints, "npoints", A_FLOAT, 0); addmess((method)sigfiddle_amprange, "amp-range", A_FLOAT, A_FLOAT, 0); addmess((method)sigfiddle_reattack, "reattack", A_FLOAT, A_FLOAT, 0); addmess((method)sigfiddle_vibrato, "vibrato", A_FLOAT, A_FLOAT, 0); addmess((method)sigfiddle_npartial, "npartial", A_FLOAT, 0); addmess((method)sigfiddle_auto, "auto", A_FLOAT, 0); addmess((method)sigfiddle_print, "print", 0); addmess((method)sigfiddle_assist, "assist", A_CANT, 0); addbang((method)sigfiddle_bang); dsp_initclass(); rescopy('STR#',3748); post(fiddle_version); } void sigfiddle_assist(t_sigfiddle *x, void *b, long m, long a, char *s) { assist_string(3748,m,a,1,2,s); } void msp_fft(t_float *buf, long np, long inv) { t_float *src,*real,*rp,*imag,*ip; long i; /* // because this fft algorithm uses separate real and imaginary // buffers // we must split the real and imaginary parts into two buffers, // then do the opposite on output // a more ambitious person would either do an in-place conversion // or rewrite the fft algorithm */ real = rp = msp_ffttemp; imag = ip = real + MAXPOINTS; src = buf; for (i = 0; i < np; i++) { *rp++ = *src++; *ip++ = *src++; } if (inv) ifft(np,real,imag); else fft(np,real,imag); rp = real; ip = imag; src = buf; for (i = 0; i < np; i++) { *src++ = *rp++; *src++ = *ip++; } } #endif /* MSP */ ================================================ FILE: libs/libpd/pure-data/extra/loop~/loop~.c ================================================ /* loop~ -- loop generator for sampling */ /* Copyright 1997-1999 Miller Puckette. Permission is granted to use this software for any purpose provided you keep this copyright notice intact. THE AUTHOR AND HIS EMPLOYERS MAKE NO WARRANTY, EXPRESS OR IMPLIED, IN CONNECTION WITH THIS SOFTWARE. This file is downloadable from http://www.crca.ucsd.edu/~msp . */ #ifdef PD #include "m_pd.h" #else #define t_sample float #define t_float float #endif typedef struct _loopctl { double l_phase; t_sample l_invwindow; t_sample l_window; int l_resync; } t_loopctl; static void loopctl_run(t_loopctl *x, t_sample *transposein, t_sample *windowin, t_sample *rawout, t_sample *windowout, int n) { t_sample window, invwindow; double phase = x->l_phase; if (x->l_resync) { window = *windowin; if (window < 0) { if (window > -1) window = -1; invwindow = -1/window; } else { if (window < 1) window = 1; invwindow = 1/window; } x->l_resync = 0; } else { window = x->l_window; phase = x->l_phase; invwindow = x->l_invwindow; } while (n--) { double phaseinc = invwindow * *transposein++; double newphase; t_sample nwind = *windowin++; if (phaseinc >= 1 || phaseinc < 0) phaseinc = 0; newphase = phase + phaseinc; if (newphase >= 1) { window = nwind; if (window < 0) { if (window > -1) window = -1; invwindow = -1/window; } else { if (window < 1) window = 1; invwindow = 1/window; } newphase -= 1.; } phase = newphase; *rawout++ = (t_sample)phase; *windowout++ = window; } x->l_invwindow = invwindow; x->l_window = window; x->l_phase = phase; } static void loopctl_init(t_loopctl *x) { x->l_window = 1; x->l_invwindow = 1; x->l_phase = 0; } static void loopctl_set(t_loopctl *x, t_float val) { if (val < 0 || val > 1) val = 0; x->l_phase = val; x->l_resync = 1; } #ifdef PD typedef struct _loop { t_object x_obj; t_float x_f; t_loopctl x_loopctl; } t_loop; static t_class *loop_class; static void *loop_new(void) { t_loop *x = (t_loop *)pd_new(loop_class); loopctl_init(&x->x_loopctl); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); outlet_new(&x->x_obj, gensym("signal")); outlet_new(&x->x_obj, gensym("signal")); return (x); } static t_int *loop_perform(t_int *w) { t_loopctl *ctl = (t_loopctl *)(w[1]); t_sample *in1 = (t_sample *)(w[2]); t_sample *in2 = (t_sample *)(w[3]); t_sample *out1 = (t_sample *)(w[4]); t_sample *out2 = (t_sample *)(w[5]); int n = (int)(w[6]); loopctl_run(ctl, in1, in2, out1, out2, n); return (w+7); } static void loop_dsp(t_loop *x, t_signal **sp) { dsp_add(loop_perform, 6, &x->x_loopctl, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec, (t_int)sp[0]->s_n); } static void loop_set(t_loop *x, t_floatarg val) { loopctl_set(&x->x_loopctl, val); } static void loop_bang(t_loop *x) { loopctl_set(&x->x_loopctl, 0); } void loop_tilde_setup(void) { loop_class = class_new(gensym("loop~"), (t_newmethod)loop_new, 0, sizeof(t_loop), 0, 0); class_addmethod(loop_class, (t_method)loop_dsp, gensym("dsp"), A_CANT, 0); CLASS_MAINSIGNALIN(loop_class, t_loop, x_f); class_addmethod(loop_class, (t_method)loop_set, gensym("set"), A_DEFFLOAT, 0); class_addbang(loop_class, loop_bang); } #endif /* PD */ ================================================ FILE: libs/libpd/pure-data/extra/lrshift~/lrshift~.c ================================================ #include "m_pd.h" /* ------------------------ lrshift~ ----------------------------- */ static t_class *lrshift_tilde_class; typedef struct _lrshift_tilde { t_object x_obj; int x_n; t_float x_f; } t_lrshift_tilde; static t_int *leftshift_perform(t_int *w) { t_sample *in = (t_sample *)(w[1]); t_sample *out= (t_sample *)(w[2]); int n = (int)(w[3]); int shift = (int)(w[4]); in += shift; n -= shift; while (n--) *out++ = *in++; while (shift--) *out++ = 0; return (w+5); } static t_int *rightshift_perform(t_int *w) { t_sample *in = (t_sample *)(w[1]); t_sample *out= (t_sample *)(w[2]); int n = (int)(w[3]); int shift = (int)(w[4]); n -= shift; in -= shift; while (n--) *--out = *--in; while (shift--) *--out = 0; return (w+5); } static void lrshift_tilde_dsp(t_lrshift_tilde *x, t_signal **sp) { int n = sp[0]->s_length, i; int shift = x->x_n; if (shift > n) shift = n; if (shift < -n) shift = -n; signal_setmultiout(&sp[1], sp[0]->s_nchans); for (i = 0; i < sp[0]->s_nchans; i++) { if (shift < 0) dsp_add(rightshift_perform, 4, sp[0]->s_vec + i*n + n, sp[1]->s_vec + i*n + n, (t_int)n, (t_int)(-shift)); else dsp_add(leftshift_perform, 4, sp[0]->s_vec + i*n, sp[1]->s_vec + i*n, (t_int)n, (t_int)shift); } } static void *lrshift_tilde_new(t_floatarg f) { t_lrshift_tilde *x = (t_lrshift_tilde *)pd_new(lrshift_tilde_class); x->x_n = f; x->x_f = 0; outlet_new(&x->x_obj, gensym("signal")); return (x); } void lrshift_tilde_setup(void) { lrshift_tilde_class = class_new(gensym("lrshift~"), (t_newmethod)lrshift_tilde_new, 0, sizeof(t_lrshift_tilde), CLASS_MULTICHANNEL, A_DEFFLOAT, 0); CLASS_MAINSIGNALIN(lrshift_tilde_class, t_lrshift_tilde, x_f); class_addmethod(lrshift_tilde_class, (t_method)lrshift_tilde_dsp, gensym("dsp"), 0); } ================================================ FILE: libs/libpd/pure-data/extra/pd~/binarymsg.c ================================================ #ifdef MAX #define A_PDFLOAT 1 #define A_PDSYMBOL 2 #define A_PDSEMI 4 #else #define A_PDFLOAT A_FLOAT #define A_PDSYMBOL A_SYMBOL #define A_PDSEMI A_SEMI #endif static void pd_tilde_putfloat(float f, FILE *fd) { putc(A_PDFLOAT, fd); fwrite(&f, sizeof(f), 1, fd); } static void pd_tilde_putsymbol(t_symbol *s, FILE *fd) { const char *sp = s->s_name; putc(A_PDSYMBOL, fd); do putc(*sp, fd); while (*sp++); } static void pd_tilde_putsemi(FILE *fd) { putc(A_PDSEMI, fd); } static int pd_tilde_getatom(t_atom *ap, FILE *fd) { char buf[MAXPDSTRING]; while (1) { int type = getc(fd), fill; float f; switch (type) { case EOF: return (0); case A_PDSEMI: SETSEMI(ap); return (1); case A_PDFLOAT: if (fread(&f, sizeof(f), 1, fd) >= 1) { SETFLOAT(ap, f); return (1); } else return (0); case A_PDSYMBOL: for (fill = 0; fill < MAXPDSTRING; fill++) { int c = getc(fd); if (c == EOF) return (0); else buf[fill] = c; if (!c) { SETSYMBOL(ap, gensym(buf)); return (1); } } return (0); } } } ================================================ FILE: libs/libpd/pure-data/extra/pd~/notes.txt ================================================ pd -schedlib `pwd`/pdsched dolist: pd~ to delay starting subproc until asked figure out about setting nchannels from command line fix maximum nchannels in and out ================================================ FILE: libs/libpd/pure-data/extra/pd~/pdsched.c ================================================ /* Copyright 2008 Miller Puckette. Berkeley license; see the file LICENSE.txt in this distribution. */ /* A plug-in scheduler that turns Pd into a filter that inputs and outputs audio and messages. */ /* todo: fix schedlib code to use extent2 figure out about if (sys_externalschedlib) { return; } in s_audio.c make buffer size ynamically growable */ #include "m_pd.h" #include "s_stuff.h" #include "m_imp.h" #include #ifdef _WIN32 #include #include #endif #include "binarymsg.c" #if PD_WATCHDOG void glob_watchdog(t_pd *dummy); static void pollwatchdog( void) { static int sched_diddsp, sched_nextpingtime; sched_diddsp++; if (!sys_havegui() && sys_hipriority && (sched_diddsp - sched_nextpingtime > 0)) { glob_watchdog(0); /* ping every 2 seconds */ sched_nextpingtime = sched_diddsp + 2 * (int)(STUFF->st_dacsr /(double)STUFF->st_schedblocksize); } } #else /* ! PD_WATCHDOG */ static void pollwatchdog( void) { /* dummy */ } #endif /* PD_WATCHDOG */ static t_class *pd_ambinary_class; #define BUFSIZE 65536 static char *ascii_inbuf; static int readasciimessage(t_binbuf *b) { int fill = 0, c; binbuf_clear(b); while ((c = getchar()) != EOF) { if (c == ';') { binbuf_text(b, ascii_inbuf, fill); return (1); } else if (fill < BUFSIZE) ascii_inbuf[fill++] = c; else if (fill == BUFSIZE) fprintf(stderr, "pd-extern: input buffer overflow\n"); } return (0); } static int readbinmessage(t_binbuf *b) { binbuf_clear(b); while (1) { t_atom at; if (!pd_tilde_getatom(&at, stdin)) { fprintf(stderr, "pd-extern: EOF on input\n"); return (0); } else if (at.a_type == A_SEMI) return (1); else binbuf_add(b, 1, &at); } } int pd_extern_sched(char *flags) { int i, j, chin, chout, fill = 0, c, useascii = 0; t_binbuf *b = binbuf_new(); t_audiosettings as; sys_get_audio_settings(&as); as.a_api = API_NONE; sys_set_audio_settings(&as); chin = (as.a_nindev < 1 ? 0 : as.a_chindevvec[0]); chout = (as.a_noutdev < 1 ? 0 : as.a_choutdevvec[0]); if (!flags || flags[0] != 'a') { /* signal to stdout object to do binary by attaching an object to an obscure symbol name */ pd_ambinary_class = class_new(gensym("pd~"), 0, 0, sizeof(t_pd), CLASS_PD, 0); pd_bind(&pd_ambinary_class, gensym("#pd_binary_stdio")); /* On Windows, set stdin and out to "binary" mode */ #ifdef _WIN32 setmode(fileno(stdout),O_BINARY); setmode(fileno(stdin),O_BINARY); #endif } else { if (!(ascii_inbuf = getbytes(BUFSIZE))) return (1); useascii = 1; } /* fprintf(stderr, "Pd plug-in scheduler called, chans %d %d, sr %d\n", chin, chout, (int)rate); */ sys_setchsr(chin, chout, as.a_srate); while (useascii ? readasciimessage(b) : readbinmessage(b) ) { t_atom *ap = binbuf_getvec(b); int n = binbuf_getnatom(b); if (n > 0 && ap[0].a_type == A_FLOAT) { /* a list -- take it as incoming signals. */ int chan, nchan = n/DEFDACBLKSIZE; t_sample *fp; for (i = chan = 0, fp = STUFF->st_soundin; chan < nchan; chan++) for (j = 0; j < DEFDACBLKSIZE; j++) *fp++ = atom_getfloat(ap++); for (; chan < chin; chan++) for (j = 0; j < DEFDACBLKSIZE; j++) *fp++ = 0; sched_tick(); sys_pollgui(); pollwatchdog(); if (useascii) printf(";\n"); else putchar(A_SEMI); for (i = chout*DEFDACBLKSIZE, fp = STUFF->st_soundout; i--; fp++) { if (useascii) printf("%g\n", *fp); else pd_tilde_putfloat(*fp, stdout); *fp = 0; } if (useascii) printf(";\n"); else putchar(A_SEMI); fflush(stdout); } else if (n > 1 && ap[0].a_type == A_SYMBOL) { t_pd *whom = ap[0].a_w.w_symbol->s_thing; if (!whom) pd_error(0, "%s: no such object", ap[0].a_w.w_symbol->s_name); else if (ap[1].a_type == A_SYMBOL) typedmess(whom, ap[1].a_w.w_symbol, n-2, ap+2); else pd_list(whom, 0, n-1, ap+1); } } binbuf_free(b); return (0); } ================================================ FILE: libs/libpd/pure-data/extra/pd~/pd~.c ================================================ /* pd~.c - embed a Pd process within Pd or Max. Copyright 2008 Miller Puckette BSD license; see README.txt in this distribution for details. */ /* this is here so that we don't have to do anything in the MACOS tool chain to define MSP. I'm sure it's easy but life's too short to waste learning XCode. */ #if !defined(PD) && !defined(MSP) #define MSP #endif #ifdef _WIN32 #include #include #include #include typedef int socklen_t; #else #include #include #include #include #include #include #include #include #endif #include #include #include #ifdef _MSC_VER #pragma warning (disable: 4305 4244) #define snprintf _snprintf #define stat _stat #endif #ifdef _WIN32 # define PIPE(p) _pipe(p, 65536, O_BINARY | O_NOINHERIT) #else # define PIPE(p) pipe(p) #endif #ifdef MSP #include "ext.h" #include "z_dsp.h" #include "math.h" #include "ext_mess.h" #include "ext_support.h" #include "ext_proto.h" #include "ext_obex.h" typedef float t_float; typedef float t_pdsample; #define PD_FLOATSIZE 32 #define w_symbol w_sym #define A_SYMBOL A_SYM #define PDERROR error( void *pd_tilde_class; #define MAXPDSTRING 4096 #define DEFDACBLKSIZE 64 typedef struct _binbuf { int b_n; t_atom *b_vec; } t_binbuf; #define SETSEMI(atom) ((atom)->a_type = A_SEMI, (atom)->a_w.w_long = 0) #define SETCOMMA(atom) ((atom)->a_type = A_COMMA, (atom)->a_w.w_long = 0) #define SETFLOAT(atom, f) ((atom)->a_type = A_FLOAT, (atom)->a_w.w_float = (f)) #define SETSYMBOL(atom, s) ((atom)->a_type = A_SYMBOL, \ (atom)->a_w.w_symbol = (s)) void critical_enter(int z); void critical_exit(int z); #else /* not MSP */ #define t_pdsample t_sample #endif /* MSP */ #ifdef PD #include "m_pd.h" #include "s_stuff.h" static t_class *pd_tilde_class; #define PDERROR pd_error(x, #endif #if defined(__x86_64__) || defined(_M_X64) # define ARCHEXT "amd64" #elif defined(__i386__) || defined(_M_IX86) # define ARCHEXT "i386" #elif defined(__arm__) # define ARCHEXT "arm" #elif defined(__aarch64__) # define ARCHEXT "arm64" #elif defined(__ppc__) # define ARCHEXT "ppc" #endif #ifdef ARCHEXT #define ARCHDLLEXT(prefix) prefix ARCHEXT , #else #define ARCHDLLEXT(prefix) #endif static const char *pd_tilde_dllextent[] = { #if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__GNU__) ARCHDLLEXT(".l_") ".pd_linux", #elif defined(__APPLE__) ARCHDLLEXT(".d_") ".d_fat", ".pd_darwin", #elif defined(_WIN32) || defined(__CYGWIN__) ARCHDLLEXT(".m_") #endif /* and some generic extensions */ #if defined(_WIN32) || defined(__CYGWIN__) ".dll", #else ".so", #endif 0}; static const char**get_dllextent() { #if PD const char**dllextent = sys_get_dllextensions(); if(dllextent && *dllextent) return dllextent; #endif return pd_tilde_dllextent; } #include "binarymsg.c" /* ------------------------ pd_tilde~ ----------------------------- */ #define MSGBUFSIZE 65536 typedef struct _pd_tilde { #ifdef PD t_object x_obj; t_clock *x_clock; t_outlet *x_outlet1; /* for messages back from subproc */ t_canvas *x_canvas; #endif /* PD */ #ifdef MSP t_pxobject x_obj; void *x_outlet1; void *x_clock; int x_clockisset; t_pdsample *x_sampbuf; #endif /* MSP */ FILE *x_infd; FILE *x_outfd; t_binbuf *x_binbuf; int x_childpid; int x_ninsig; int x_noutsig; int x_fifo; int x_binary; t_float x_sr; t_symbol *x_pddir; t_symbol *x_schedlibdir; t_pdsample **x_insig; t_pdsample **x_outsig; int x_blksize; } t_pd_tilde; #ifdef MSP static void *pd_tilde_new(t_symbol *s, long ac, t_atom *av); static void pd_tilde_dsp64(t_pd_tilde *x, t_object *dsp64, short *count, double samplerate, long maxvectorsize, long flags); void pd_tilde_assist(t_pd_tilde *x, void *b, long m, long a, char *s); static void pd_tilde_free(t_pd_tilde *x); void pd_tilde_setup(void); void ext_main( void *r); void pd_tilde_minvel_set(t_pd_tilde *x, void *attr, long ac, t_atom *av); #define binbuf_new pdbinbuf_new #undef binbuf_free #define binbuf_free pdbinbuf_free #define binbuf_clear pdbinbuf_clear #define binbuf_resize pdbinbuf_resize #define binbuf_add pdbinbuf_add #define binbuf_text pdbinbuf_text #define binbuf_getnatom pdbinbuf_getnatom #define binbuf_getvec pdbinbuf_getvec t_binbuf *binbuf_new(void) { t_binbuf *x = (t_binbuf *)t_getbytes(sizeof(*x)); x->b_n = 0; x->b_vec = t_getbytes(0); return (x); } void binbuf_free(t_binbuf *x) { t_freebytes(x->b_vec, x->b_n * sizeof(*x->b_vec)); t_freebytes(x, sizeof(*x)); } void binbuf_clear(t_binbuf *x) { x->b_vec = t_resizebytes((char *)x->b_vec, x->b_n * sizeof(*x->b_vec), 0); x->b_n = 0; } int binbuf_resize(t_binbuf *x, int newsize) { t_atom *new = (t_atom *)t_resizebytes((char *)x->b_vec, x->b_n * sizeof(*x->b_vec), newsize * sizeof(*x->b_vec)); if (new) x->b_vec = new, x->b_n = newsize; return (new != 0); } void binbuf_add(t_binbuf *x, int argc, const t_atom *argv) { int previoussize = x->b_n; int newsize = previoussize + argc, i; t_atom *ap; if (!binbuf_resize(x, newsize)) { PDERROR "binbuf_addmessage: out of space"); return; } for (ap = x->b_vec + previoussize, i = argc; i--; ap++) *ap = *(argv++); } /* convert text to a binbuf */ void binbuf_text(t_binbuf *x, const char *text, size_t size) { char buf[MAXPDSTRING+1], *bufp, *ebuf = buf+MAXPDSTRING; const char *textp = text, *etext = text+size; t_atom *ap; int nalloc = 16, natom = 0; binbuf_clear(x); if (!binbuf_resize(x, nalloc)) return; ap = x->b_vec; while (1) { /* skip leading space */ while ((textp != etext) && (*textp == ' ' || *textp == '\n' || *textp == '\r' || *textp == '\t')) textp++; if (textp == etext) break; if (*textp == ';') SETSEMI(ap), textp++; else if (*textp == ',') SETCOMMA(ap), textp++; else { /* it's an atom other than a comma or semi */ char c; int floatstate = 0, slash = 0, lastslash = 0, dollar = 0; bufp = buf; do { c = *bufp = *textp++; lastslash = slash; slash = (c == '\\'); if (floatstate >= 0) { int digit = (c >= '0' && c <= '9'), dot = (c == '.'), minus = (c == '-'), plusminus = (minus || (c == '+')), expon = (c == 'e' || c == 'E'); if (floatstate == 0) /* beginning */ { if (minus) floatstate = 1; else if (digit) floatstate = 2; else if (dot) floatstate = 3; else floatstate = -1; } else if (floatstate == 1) /* got minus */ { if (digit) floatstate = 2; else if (dot) floatstate = 3; else floatstate = -1; } else if (floatstate == 2) /* got digits */ { if (dot) floatstate = 4; else if (expon) floatstate = 6; else if (!digit) floatstate = -1; } else if (floatstate == 3) /* got '.' without digits */ { if (digit) floatstate = 5; else floatstate = -1; } else if (floatstate == 4) /* got '.' after digits */ { if (digit) floatstate = 5; else if (expon) floatstate = 6; else floatstate = -1; } else if (floatstate == 5) /* got digits after . */ { if (expon) floatstate = 6; else if (!digit) floatstate = -1; } else if (floatstate == 6) /* got 'e' */ { if (plusminus) floatstate = 7; else if (digit) floatstate = 8; else floatstate = -1; } else if (floatstate == 7) /* got plus or minus */ { if (digit) floatstate = 8; else floatstate = -1; } else if (floatstate == 8) /* got digits */ { if (!digit) floatstate = -1; } } if (!lastslash && c == '$' && (textp != etext && textp[0] >= '0' && textp[0] <= '9')) dollar = 1; if (!slash) bufp++; else if (lastslash) { bufp++; slash = 0; } } while (textp != etext && bufp != ebuf && (slash || (*textp != ' ' && *textp != '\n' && *textp != '\r' && *textp != '\t' &&*textp != ',' && *textp != ';'))); *bufp = 0; if (floatstate == 2 || floatstate == 4 || floatstate == 5 || floatstate == 8) SETFLOAT(ap, atof(buf)); /* LATER try to figure out how to mix "$" and "\$" correctly; here, the backslashes were already stripped so we assume all "$" chars are real dollars. In fact, we only know at least one was. */ else if (dollar) { if (buf[0] != '$') dollar = 0; for (bufp = buf+1; *bufp; bufp++) if (*bufp < '0' || *bufp > '9') dollar = 0; SETFLOAT(ap, 0); } else SETSYMBOL(ap, gensym(buf)); } ap++; natom++; if (natom == nalloc) { if (!binbuf_resize(x, nalloc*2)) break; nalloc = nalloc * 2; ap = x->b_vec + natom; } if (textp == etext) break; } /* reallocate the vector to exactly the right size */ binbuf_resize(x, natom); } int binbuf_getnatom(const t_binbuf *x) { return (x->b_n); } t_atom *binbuf_getvec(const t_binbuf *x) { return (x->b_vec); } void sys_bashfilename(char *tmpbuf, char *outbuf) { strcpy(outbuf, tmpbuf); } #if 0 /* stuff for debugging */ static FILE *barf_fd; void barf(const char *s) { if (!barf_fd) barf_fd = fopen("/tmp/foo.txt", "w"); if (barf_fd) { fprintf(barf_fd, "%s\n", s); fflush(barf_fd); } } void barf1(const char *s, int n1) { char buf2[1000]; sprintf(buf2, s, n1); barf(buf2); } void barf2(const char *s, int n1, int n2) { char buf2[1000]; sprintf(buf2, s, n1, n2); barf(buf2); } #ifdef getc /* if debugging is on, copy all getc() output to /tmp/foo.txt */ #undef getc #endif int barf_getc(FILE *fd) { int poodle = getc(fd); #if 0 if (poodle < 0) barf("barf_getc oops"); else { if (!barf_fd) barf_fd = fopen("/tmp/foo.txt", "w"); if (barf_fd) putc(poodle, barf_fd); } #endif return (poodle); } #define getc barf_getc #endif /* debugging */ #endif /* MAX */ static void pd_tilde_close(t_pd_tilde *x) { #ifdef _WIN32 int termstat; #endif FILE *infd = x->x_infd, *outfd = x->x_outfd; x->x_infd = x->x_outfd = 0; if (outfd) fclose(outfd); if (infd) fclose(infd); if (x->x_childpid > 0) #ifdef _WIN32 _cwait(&termstat, x->x_childpid, WAIT_CHILD); #else waitpid(x->x_childpid, 0, 0); #endif binbuf_clear(x->x_binbuf); x->x_infd = x->x_outfd = 0; x->x_childpid = -1; } static int pd_tilde_readmessages(t_pd_tilde *x, FILE *infd) { t_atom at; if (x->x_binary) { int nonempty = 0; while (1) { if (!pd_tilde_getatom(&at, infd)) return 0; if (!nonempty && at.a_type == A_SEMI) break; nonempty = (at.a_type != A_SEMI); binbuf_add(x->x_binbuf, 1, &at); } } else /* ASCII */ { t_binbuf *tmpb = binbuf_new(); while (1) { char msgbuf[MAXPDSTRING]; int c, infill = 0, n; t_atom *vec; while (isspace((c = getc(infd))) && c != EOF) ; if (c == EOF) return 0; do msgbuf[infill++] = c; while (!isspace((c = getc(infd))) && c != ';' && c != EOF && infill < MAXPDSTRING-1) ; if (c == ';' && infill < MAXPDSTRING-1) msgbuf[infill++] = c; binbuf_text(tmpb, msgbuf, infill); n = binbuf_getnatom(tmpb); vec = binbuf_getvec(tmpb); binbuf_add(x->x_binbuf, n, vec); if (!n) { post("bug: pd~"); break; /* shouldn't happen */ } if (vec[0].a_type == A_SEMI) break; } binbuf_free(tmpb); } #ifdef MAX /* in Max, since control and DSP may be asynchronous, we might already be set. If so, re-setting can just delay things further. */ if (!x->x_clockisset) { x->x_clockisset = 1; clock_delay(x->x_clock, 0); } #else clock_delay(x->x_clock, 0); #endif return (1); } #define FIXEDARG 13 #define MAXARG 100 #ifdef _WIN32 #define EXTENT ".exe" #else #define EXTENT "" #endif /* only call this if we're not already running (x->x_infd = 0, etc.) */ static void pd_tilde_dostart(t_pd_tilde *x, const char *pddir, const char *schedlibdir, const char *patchdir_c, int argc, t_atom *argv, int ninsig, int noutsig, int fifo, t_float samplerate) { int i, pid, pipe1[2], pipe2[2]; FILE *infd, *outfd; char cmdbuf[MAXPDSTRING], pdexecbuf[MAXPDSTRING], schedbuf[MAXPDSTRING], tmpbuf[MAXPDSTRING], patchdir[MAXPDSTRING]; char *execargv[FIXEDARG+MAXARG+1], ninsigstr[20], noutsigstr[20], sampleratestr[40]; const char**dllextent; struct stat statbuf; x->x_childpid = -1; if (argc > MAXARG) { post("pd~: args truncated to %d items", MAXARG); argc = MAXARG; } sprintf(ninsigstr, "%d", ninsig); sprintf(noutsigstr, "%d", noutsig); sprintf(sampleratestr, "%f", (float)samplerate); snprintf(tmpbuf, MAXPDSTRING, "%s/bin/pd" EXTENT, pddir); sys_bashfilename(tmpbuf, pdexecbuf); if (stat(pdexecbuf, &statbuf) < 0) { snprintf(tmpbuf, MAXPDSTRING, "%s/../../../bin/pd" EXTENT, pddir); sys_bashfilename(tmpbuf, pdexecbuf); if (stat(pdexecbuf, &statbuf) < 0) { snprintf(tmpbuf, MAXPDSTRING, "%s/pd" EXTENT, pddir); sys_bashfilename(tmpbuf, pdexecbuf); if (stat(pdexecbuf, &statbuf) < 0) { PDERROR "pd~: can't stat %s", pdexecbuf); goto fail1; } } } /* check that the scheduler dynamic linkable exists w either suffix */ for(dllextent=get_dllextent(); *dllextent; dllextent++) { snprintf(tmpbuf, MAXPDSTRING, "%s/pdsched%s", schedlibdir, *dllextent); sys_bashfilename(tmpbuf, schedbuf); if (stat(schedbuf, &statbuf) >= 0) goto gotone; } PDERROR "pd~: can't stat %s", schedbuf); goto fail1; gotone: /* but the sub-process wants the scheduler name without the suffix */ snprintf(tmpbuf, MAXPDSTRING, "%s/pdsched", schedlibdir); sys_bashfilename(tmpbuf, schedbuf); /* was: snprintf(cmdbuf, MAXPDSTRING, "'%s' -schedlib '%s'/pdsched -path '%s' -inchannels %d -outchannels %d -r \ %g %s\n", pdexecbuf, schedlibdir, patchdir, ninsig, noutsig, samplerate, pdargs); */ snprintf(cmdbuf, MAXPDSTRING, "%s", pdexecbuf); #ifdef _WIN32 /* _spawnv wants the command without quotes as in cmdbuf above; but in the argument vector paths must be quoted if they contain whitespace */ if (strchr(pdexecbuf, ' ') && *pdexecbuf != '"' && *pdexecbuf != '\'') { if (snprintf(tmpbuf, MAXPDSTRING, "\"%s\"", pdexecbuf) >= 0) snprintf(pdexecbuf, MAXPDSTRING, "%s", tmpbuf); } if (strchr(schedbuf, ' ') && *schedbuf != '"' && *schedbuf != '\'') { if (snprintf(tmpbuf, MAXPDSTRING, "\"%s\"", schedbuf) >= 0) snprintf(schedbuf, MAXPDSTRING, "%s", tmpbuf); } if (strchr(patchdir_c, ' ') && *patchdir_c != '"' && *patchdir_c != '\'') snprintf(patchdir, MAXPDSTRING, "\"%s\"", patchdir_c); else #endif /* _WIN32 */ snprintf(patchdir, MAXPDSTRING, "%s", patchdir_c); execargv[0] = pdexecbuf; execargv[1] = "-schedlib"; execargv[2] = schedbuf; execargv[3] = "-extraflags"; execargv[4] = (x->x_binary ? "b" : "a"); execargv[5] = "-path"; execargv[6] = patchdir; execargv[7] = "-inchannels"; execargv[8] = ninsigstr; execargv[9] = "-outchannels"; execargv[10] = noutsigstr; execargv[11] = "-r"; execargv[12] = sampleratestr; /* convert atom arguments to strings (temporarily allocating space) */ for (i = 0; i < argc; i++) { #ifdef PD if (argv[i].a_type == A_SYMBOL) snprintf(tmpbuf, MAXPDSTRING, "%s", argv[i].a_w.w_symbol->s_name); else if (argv[i].a_type == A_FLOAT) sprintf(tmpbuf, "%f", (float)argv[i].a_w.w_float); #endif #ifdef MSP /* because Mac pathnames sometimes have an evil preceding colon character, we test for and silently eat them */ if (argv[i].a_type == A_SYM) strncpy(tmpbuf, (*argv[i].a_w.w_sym->s_name == ':'? argv[i].a_w.w_sym->s_name+1 : argv[i].a_w.w_sym->s_name), MAXPDSTRING-3); else if (argv[i].a_type == A_LONG) sprintf(tmpbuf, "%ld", (long)argv[i].a_w.w_long); else if (argv[i].a_type == A_FLOAT) sprintf(tmpbuf, "%f", (float)argv[i].a_w.w_float); #endif #ifdef _WIN32 /* and now, for Windows (whether Max or Pd), spaces need quotes */ if (strchr(tmpbuf, ' ') && *tmpbuf != '"' && *tmpbuf != '\'') { char nutherbuf[MAXPDSTRING]; snprintf(nutherbuf, MAXPDSTRING, "\"%s\"", tmpbuf); snprintf(tmpbuf, MAXPDSTRING, "%s", nutherbuf); } #endif /* _WIN32 */ execargv[FIXEDARG+i] = malloc(strlen(tmpbuf) + 1); strcpy(execargv[FIXEDARG+i], tmpbuf); } execargv[argc+FIXEDARG] = 0; #if 0 for (i = 0; i < argc+FIXEDARG; i++) post("arg %d = %s", i, execargv[i]); #endif if (PIPE(pipe1) < 0) { PDERROR "pd~: can't create pipe"); goto fail1; } if (PIPE(pipe2) < 0) { PDERROR "pd~: can't create pipe"); goto fail2; } #ifdef _WIN32 { int stdinwas = _dup(0), stdoutwas = _dup(1); if (pipe2[1] == 0) pipe2[1] = _dup(pipe2[1]); if (pipe1[0] != 0) _dup2(pipe1[0], 0); if (pipe2[1] != 1) _dup2(pipe2[1], 1); pid = _spawnv(P_NOWAIT, cmdbuf, (const char * const *)execargv); if (pid < 0) { post("%s: couldn't start subprocess (%s)\n", execargv[0], strerror(errno)); goto fail1; } _dup2(stdinwas, 0); _dup2(stdoutwas, 1); _close(stdinwas); _close(stdoutwas); } #else /* _WIN32 */ if ((pid = fork()) < 0) { PDERROR "pd~: can't fork"); goto fail3; } else if (pid == 0) { /* child process */ /* the first dup2 below would bash pipe2[1] if it happens to be zero so in that case renumber it */ if (pipe2[1] == 0) pipe2[1] = dup(pipe2[1]); if (pipe1[0] != 0) { dup2(pipe1[0], 0); close(pipe1[0]); } if (pipe2[1] != 1) { dup2(pipe2[1], 1); close(pipe2[1]); } if (pipe1[1] >= 2) close(pipe1[1]); if (pipe2[0] >= 2) close(pipe2[0]); execv(cmdbuf, execargv); _exit(1); } for (i=FIXEDARG; execargv[i]; i++) free(execargv[i]); #endif /* _WIN32 */ /* done with fork/exec or spawn; parent continues here */ close(pipe1[0]); close(pipe2[1]); #ifndef _WIN32 /* this was done in windows via the O_NOINHERIT flag */ fcntl(pipe1[1], F_SETFD, FD_CLOEXEC); fcntl(pipe2[0], F_SETFD, FD_CLOEXEC); #endif outfd = fdopen(pipe1[1], "w"); infd = fdopen(pipe2[0], "r"); x->x_childpid = pid; for (i = 0; i < fifo; i++) if (x->x_binary) { pd_tilde_putsemi(outfd); pd_tilde_putfloat(0, outfd); pd_tilde_putsemi(outfd); } else fprintf(outfd, "%s", ";\n0;\n"); fflush(outfd); binbuf_clear(x->x_binbuf); pd_tilde_readmessages(x, infd); x->x_outfd = outfd; x->x_infd = infd; return; #ifndef _WIN32 fail3: close(pipe2[0]); close(pipe2[1]); if (x->x_childpid > 0) waitpid(x->x_childpid, 0, 0); #endif fail2: close(pipe1[0]); close(pipe1[1]); fail1: x->x_infd = x->x_outfd = 0; x->x_childpid = -1; post("pd~ startup failed"); return; } /* #define TOSSIN */ #ifdef TOSSIN #include void pd_empty(int infd) { char buf[10000]; fd_set readset, writeset, exceptset; FD_ZERO(&writeset); FD_ZERO(&readset); FD_ZERO(&exceptset); FD_SET(infd, &readset); if (select(infd+1, &readset, &writeset, &exceptset, 0) >= 0 && FD_ISSET(infd, &readset)) read(infd, buf, 10000); } #endif static int nperfed = 0; static void pd_tilde_doperf(t_pd_tilde *x) { int n = x->x_blksize, i, j, nsigs, numbuffill = 0, c; char numbuf[80]; if (n > DEFDACBLKSIZE) n = DEFDACBLKSIZE; #ifdef MAX critical_enter(0); #endif if (!x->x_infd) goto zeroit; if (x->x_binary) { pd_tilde_putsemi(x->x_outfd); if (!x->x_ninsig) pd_tilde_putfloat(0, x->x_outfd); else for (i = 0; i < x->x_ninsig; i++) { t_pdsample *fp = x->x_insig[i]; for (j = 0; j < n; j++) pd_tilde_putfloat(*fp++, x->x_outfd); for (; j < DEFDACBLKSIZE; j++) pd_tilde_putfloat(0, x->x_outfd); } pd_tilde_putsemi(x->x_outfd); } else { fprintf(x->x_outfd, ";\n"); if (!x->x_ninsig) fprintf(x->x_outfd, "0\n"); else for (i = 0; i < x->x_ninsig; i++) { t_pdsample *fp = x->x_insig[i]; for (j = 0; j < n; j++) fprintf(x->x_outfd, "%g\n", *fp++); for (; j < DEFDACBLKSIZE; j++) fprintf(x->x_outfd, "0\n"); } fprintf(x->x_outfd, ";\n"); } fflush(x->x_outfd); nsigs = j = 0; if (x->x_binary) { while (1) { t_atom at; if (!pd_tilde_getatom(&at, x->x_infd)) { if (errno) PDERROR "pd~: %s", strerror(errno)); else PDERROR "pd~: subprocess exited"); pd_tilde_close(x); goto zeroit; } if (at.a_type == A_SEMI) break; else if (at.a_type == A_FLOAT) { if (nsigs < x->x_noutsig) x->x_outsig[nsigs][j] = at.a_w.w_float; if (++j >= DEFDACBLKSIZE) j = 0, nsigs++; } else PDERROR "pd~: subprocess returned malformed audio"); } } else { while (1) { while (1) { c = getc(x->x_infd); if (c == EOF) { if (errno) PDERROR "pd~: %s", strerror(errno)); else PDERROR "pd~: subprocess exited"); pd_tilde_close(x); goto zeroit; } else if (!isspace(c) && c != ';') { if (numbuffill < (80-1)) numbuf[numbuffill++] = c; } else { t_pdsample z; if (numbuffill) { numbuf[numbuffill] = 0; #if PD_FLOATSIZE == 32 if (sscanf(numbuf, "%f", &z) < 1) #else if (sscanf(numbuf, "%lf", &z) < 1) #endif continue; if (nsigs < x->x_noutsig) x->x_outsig[nsigs][j] = z; if (++j >= DEFDACBLKSIZE) j = 0, nsigs++; } numbuffill = 0; break; } } /* message terminated */ if (c == ';') break; } } if (nsigs < x->x_noutsig) post("pd~: short audio signals (sigs %d, fragment %d)", nsigs, j); for (; nsigs < x->x_noutsig; nsigs++, j = 0) { for (; j < x->x_blksize; j++) x->x_outsig[nsigs][j] = 0; } if (!pd_tilde_readmessages(x, x->x_infd)) { if (errno) PDERROR "pd~: %s", strerror(errno)); else PDERROR "pd~: subprocess exited"); pd_tilde_close(x); } #ifdef MAX critical_exit(0); #endif return; zeroit: #ifdef MAX critical_exit(0); #endif for (i = 0; i < x->x_noutsig; i++) { for (j = 0; j < x->x_blksize; j++) x->x_outsig[i][j] = 0; } } static void pd_tilde_pdtilde(t_pd_tilde *x, t_symbol *s, int argc, t_atom *argv) { t_symbol *sel = ((argc > 0 && argv->a_type == A_SYMBOL) ? argv->a_w.w_symbol : gensym("?")), *schedlibdir; const char *patchdir; if (sel == gensym("start")) { char pdargstring[MAXPDSTRING]; #ifdef MAX critical_enter(0); #endif if (x->x_infd) pd_tilde_close(x); #ifdef MAX critical_exit(0); #endif pdargstring[0] = 0; argc--; argv++; #ifdef PD patchdir = canvas_getdir(x->x_canvas)->s_name; #endif #ifdef MSP patchdir = "."; #endif schedlibdir = x->x_schedlibdir; if (schedlibdir == gensym(".") && x->x_pddir != gensym(".")) { const char *pds = x->x_pddir->s_name; char scheddirstring[MAXPDSTRING]; int l = strlen(pds); if (l >= 4 && (!strcmp(pds+l-3, "bin") || !strcmp(pds+l-4, "bin/"))) snprintf(scheddirstring, MAXPDSTRING, "%s/../extra/pd~", pds); else snprintf(scheddirstring, MAXPDSTRING, "%s/extra/pd~", pds); schedlibdir = gensym(scheddirstring); } pd_tilde_dostart(x, x->x_pddir->s_name, schedlibdir->s_name, patchdir, argc, argv, x->x_ninsig, x->x_noutsig, x->x_fifo, x->x_sr); } else if (sel == gensym("stop")) { #ifdef MAX critical_enter(0); #endif if (x->x_infd) pd_tilde_close(x); #ifdef MAX critical_exit(0); #endif } else if (sel == gensym("pddir")) { if ((argc > 1) && argv[1].a_type == A_SYMBOL) { t_symbol *sym = argv[1].a_w.w_symbol; #ifdef MSP if (sym->s_name[0] == ':') sym = gensym(s->s_name+1); #endif x->x_pddir = sym; } else PDERROR "pd~ pddir: needs symbol argument"); } else PDERROR "pd~: unknown control message: %s", sel->s_name); } static void pd_tilde_free(t_pd_tilde *x) { pd_tilde_close(x); clock_free(x->x_clock); t_freebytes(x->x_insig, x->x_ninsig * sizeof(*x->x_insig)); t_freebytes(x->x_outsig, x->x_noutsig * sizeof(*x->x_outsig)); #ifdef MSP if (x->x_sampbuf) free(x->x_sampbuf); dsp_free((t_pxobject *)x); #endif } /* -------------------------- Pd glue ------------------------- */ #ifdef PD static t_int *pd_tilde_perform(t_int *w) { t_pd_tilde *x = (t_pd_tilde *)(w[1]); pd_tilde_doperf(x); return (w+2); } static void pd_tilde_dsp(t_pd_tilde *x, t_signal **sp) { int i; t_pdsample **g; x->x_blksize = (x->x_ninsig || x->x_noutsig ? sp[0]->s_n : 1); for (i = 0, g = x->x_insig; i < x->x_ninsig; i++, g++) *g = (*(sp++))->s_vec; /* if there were no input signals Pd still provided us with one, which we ignore: */ if (!x->x_ninsig) sp++; for (i = 0, g = x->x_outsig; i < x->x_noutsig; i++, g++) *g = (*(sp++))->s_vec; dsp_add(pd_tilde_perform, 1, x); } static void pd_tilde_tick(t_pd_tilde *x) { int messstart = 0, i, n; t_atom *vec; /* binbuf_print(b); */ n = binbuf_getnatom(x->x_binbuf); vec = binbuf_getvec(x->x_binbuf); for (i = 0; i < n; i++) { if (vec[i].a_type == A_SEMI) { if (i > messstart && vec[messstart].a_type == A_SYMBOL) outlet_anything(x->x_outlet1, vec[messstart].a_w.w_symbol, i-(messstart+1), vec+(messstart+1)); else if (i > messstart) outlet_list(x->x_outlet1, 0, i-messstart, vec+messstart); messstart = i+1; } } binbuf_clear(x->x_binbuf); } static void pd_tilde_anything(t_pd_tilde *x, t_symbol *s, int argc, t_atom *argv) { char msgbuf[MAXPDSTRING]; if (!x->x_outfd) return; if (x->x_binary) { pd_tilde_putsymbol(s, x->x_outfd); for (; argc--; argv++) { if (argv->a_type == A_FLOAT) pd_tilde_putfloat(argv->a_w.w_float, x->x_outfd); else if (argv->a_type == A_SYMBOL) pd_tilde_putsymbol(argv->a_w.w_symbol, x->x_outfd); } putc(A_SEMI, x->x_outfd); } else { fprintf(x->x_outfd, "%s ", s->s_name); while (argc--) { atom_string(argv++, msgbuf, MAXPDSTRING); fprintf(x->x_outfd, "%s ", msgbuf); } fprintf(x->x_outfd, ";\n"); } } static void *pd_tilde_new(t_symbol *s, int argc, t_atom *argv) { t_pd_tilde *x = (t_pd_tilde *)pd_new(pd_tilde_class); int ninsig = 2, noutsig = 2, j, fifo = 5, binary = 1; t_float sr = sys_getsr(); t_pdsample **g; t_symbol *pddir = sys_libdir, *scheddir = gensym(class_gethelpdir(pd_tilde_class)); while (argc > 0) { t_symbol *firstarg = atom_getsymbolarg(0, argc, argv); if (!strcmp(firstarg->s_name, "-sr") && argc > 1) { sr = atom_getfloatarg(1, argc, argv); argc -= 2; argv += 2; } else if (!strcmp(firstarg->s_name, "-ninsig") && argc > 1) { ninsig = atom_getfloatarg(1, argc, argv); argc -= 2; argv += 2; } else if (!strcmp(firstarg->s_name, "-noutsig") && argc > 1) { noutsig = atom_getfloatarg(1, argc, argv); argc -= 2; argv += 2; } else if (!strcmp(firstarg->s_name, "-fifo") && argc > 1) { fifo = atom_getfloatarg(1, argc, argv); argc -= 2; argv += 2; } else if (!strcmp(firstarg->s_name, "-pddir") && argc > 1) { pddir = atom_getsymbolarg(1, argc, argv); argc -= 2; argv += 2; } else if (!strcmp(firstarg->s_name, "-scheddir") && argc > 1) { scheddir = atom_getsymbolarg(1, argc, argv); argc -= 2; argv += 2; } else if (!strcmp(firstarg->s_name, "-ascii")) { binary = 0; argc--; argv++; } else break; } if (argc) { pd_error(x, "usage: pd~ [-sr #] [-ninsig #] [-noutsig #] [-fifo #] [-pddir <>]"); post( "... [-scheddir <>]"); } x->x_clock = clock_new(x, (t_method)pd_tilde_tick); x->x_insig = (t_pdsample **)t_getbytes(ninsig * sizeof(*x->x_insig)); x->x_outsig = (t_pdsample **)t_getbytes(noutsig * sizeof(*x->x_outsig)); x->x_ninsig = ninsig; x->x_noutsig = noutsig; x->x_blksize = DEFDACBLKSIZE; x->x_fifo = fifo; x->x_sr = sr; x->x_pddir = pddir; x->x_schedlibdir = scheddir; x->x_infd = 0; x->x_outfd = 0; x->x_childpid = -1; x->x_canvas = canvas_getcurrent(); x->x_binbuf = binbuf_new(); x->x_binary = binary; for (j = 1, g = x->x_insig; j < ninsig; j++, g++) inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); x->x_outlet1 = outlet_new(&x->x_obj, 0); for (j = 0, g = x->x_outsig; j < noutsig; j++, g++) outlet_new(&x->x_obj, &s_signal); #ifndef _WIN32 signal(SIGPIPE, SIG_IGN); #endif return (x); } void pd_tilde_setup(void) { pd_tilde_class = class_new(gensym("pd~"), (t_newmethod)pd_tilde_new, (t_method)pd_tilde_free, sizeof(t_pd_tilde), 0, A_GIMME, 0); class_addmethod(pd_tilde_class, nullfn, gensym("signal"), 0); class_addmethod(pd_tilde_class, (t_method)pd_tilde_dsp, gensym("dsp"), A_CANT, 0); class_addmethod(pd_tilde_class, (t_method)pd_tilde_pdtilde, gensym("pd~"), A_GIMME, 0); class_addanything(pd_tilde_class, pd_tilde_anything); post("pd~ version 0.54"); } #endif /* -------------------------- Max/MSP glue ------------------------- */ #ifdef MSP static void pd_tilde_perf64(t_pd_tilde *x, t_object *dsp64, double **ins, long numins, double **outs, long numouts, long sampleframes, long flags, void *userparam) { int i, j, nin = (numins < x->x_ninsig ? numins : x->x_ninsig), nout = (numouts < x->x_noutsig ? numouts : x->x_noutsig); if (sampleframes > DEFDACBLKSIZE) sampleframes = DEFDACBLKSIZE; /* LATER iterate through them */ x->x_blksize = sampleframes; for (i = 0; i < nin; i++) for (j = 0; j < sampleframes; j++) x->x_insig[i][j] = ins[i][j]; for (; i < x->x_ninsig; i++) for (j = 0; j < sampleframes; j++) x->x_insig[i][j] = 0; pd_tilde_doperf(x); for (i = 0; i < nout; i++) for (j = 0; j < sampleframes; j++) outs[i][j] = x->x_outsig[i][j]; for (; i < numouts; i++) for (j = 0; j < sampleframes; j++) outs[i][j] = 0; nperfed++; } static void pd_tilde_dsp64(t_pd_tilde *x, t_object *dsp64, short *count, double samplerate, long maxvectorsize, long flags) { int i; t_pdsample *sp; if (x->x_sampbuf) free(x->x_sampbuf); x->x_sampbuf = malloc((x->x_ninsig + x->x_noutsig) * DEFDACBLKSIZE * sizeof(t_pdsample)); for (i = 0, sp = x->x_sampbuf; i < x->x_ninsig; i++) x->x_insig[i] = sp, sp += DEFDACBLKSIZE; for (i = 0; i < x->x_noutsig; i++) x->x_outsig[i] = sp, sp += DEFDACBLKSIZE; object_method(dsp64, gensym("dsp_add64"), x, pd_tilde_perf64, 0, 0); } static void pd_tilde_tick(t_pd_tilde *x) { int messstart = 0, i, n = 0; t_atom *vec; t_binbuf *b; critical_enter(0); b = x->x_binbuf; x->x_binbuf = binbuf_new(); critical_exit(0); n = binbuf_getnatom(b); vec = binbuf_getvec(b); for (i = 0; i < n; i++) { if (vec[i].a_type == A_SEMI) { if (i > messstart) { if (vec[messstart].a_type == A_SYM) outlet_anything(x->x_outlet1, vec[messstart].a_w.w_sym, i-messstart-1, vec+(messstart+1)); else if (vec[messstart].a_type == A_FLOAT && i == messstart+1) outlet_float(x->x_outlet1, vec[messstart].a_w.w_float); else if (vec[messstart].a_type == A_LONG && i == messstart+1) outlet_int(x->x_outlet1, vec[messstart].a_w.w_long); else outlet_list(x->x_outlet1, gensym("list"), i-messstart, vec+(messstart)); } messstart = i+1; } } binbuf_free(b); x->x_clockisset = 0; } static void pd_tilde_anything(t_pd_tilde *x, t_symbol *s, long ac, t_atom *av) { if (!x->x_outfd) return; if (x->x_binary) { critical_enter(0); pd_tilde_putsymbol(s, x->x_outfd); while (ac--) { switch (av->a_type) { case A_FLOAT: pd_tilde_putfloat(av->a_w.w_float, x->x_outfd); break; case A_LONG: pd_tilde_putfloat(av->a_w.w_long, x->x_outfd); break; case A_SYM: pd_tilde_putsymbol(av->a_w.w_sym, x->x_outfd); break; } av++; } pd_tilde_putsemi(x->x_outfd); critical_exit(0); } else { char msgbuf[MAXPDSTRING], *sp, *ep = msgbuf+MAXPDSTRING; msgbuf[0] = 0; strncpy(msgbuf, s->s_name, MAXPDSTRING); msgbuf[MAXPDSTRING-1] = 0; sp = msgbuf + strlen(msgbuf); while (ac--) { if (sp < ep-1) sp[0] = ' ', sp[1] = 0, sp++; if (sp < ep - 80) { if (av->a_type == A_SYM && strlen(av->a_w.w_sym->s_name) < ep - sp-20) strcpy(sp, av->a_w.w_sym->s_name); else if (av->a_type == A_LONG) sprintf(sp, "%ld" , (long)av->a_w.w_long); else if (av->a_type == A_FLOAT) sprintf(sp, "%g" , (t_float)av->a_w.w_float); } sp += strlen(sp); av++; } critical_enter(0); fprintf(x->x_outfd, "%s;\n", msgbuf); critical_exit(0); } } void ext_main( void *r) { t_class *c; c = class_new("pd~", (method)pd_tilde_new, (method)pd_tilde_free, sizeof(t_pd_tilde), (method)0L, A_GIMME, 0); class_addmethod(c, (method)pd_tilde_dsp64, "dsp64", A_CANT, 0); class_addmethod(c, (method)pd_tilde_assist, "assist", A_CANT, 0); class_addmethod(c, (method)pd_tilde_pdtilde, "pd~", A_GIMME, 0); class_addmethod(c, (method)pd_tilde_anything, "anything", A_GIMME, 0); class_dspinit(c); class_register(CLASS_BOX, c); pd_tilde_class = c; post("pd~ version 0.54"); } static void *pd_tilde_new(t_symbol *s, long ac, t_atom *av) { int ninsig = 2, noutsig = 2, fifo = 5, binary = 1, j; t_float sr = sys_getsr(); t_symbol *pddir = gensym("."), *scheddir = gensym("."); t_pd_tilde *x; if ((x = (t_pd_tilde *)object_alloc(pd_tilde_class))) { while (ac > 0 && av[0].a_type == A_SYM) { const char *flag = av[0].a_w.w_sym->s_name; if (!strcmp(flag, "-sr") && ac > 1) { sr = (av[1].a_type == A_FLOAT ? av[1].a_w.w_float : (av[1].a_type == A_LONG ? av[1].a_w.w_long : 0)); ac -= 2; av += 2; } else if (!strcmp(flag, "-ninsig") && ac > 1) { ninsig = (av[1].a_type == A_FLOAT ? av[1].a_w.w_float : (av[1].a_type == A_LONG ? av[1].a_w.w_long : 0)); ac -= 2; av += 2; } else if (!strcmp(flag, "-noutsig") && ac > 1) { noutsig = (av[1].a_type == A_FLOAT ? av[1].a_w.w_float : (av[1].a_type == A_LONG ? av[1].a_w.w_long : 0)); ac -= 2; av += 2; } else if (!strcmp(flag, "-fifo") && ac > 1) { fifo = (av[1].a_type == A_FLOAT ? av[1].a_w.w_float : (av[1].a_type == A_LONG ? av[1].a_w.w_long : 0)); ac -= 2; av += 2; } else if (!strcmp(flag, "-pddir") && ac > 1) { pddir = (av[1].a_type == A_SYM ? av[1].a_w.w_sym : gensym(".")); ac -= 2; av += 2; } else if (!strcmp(flag, "-scheddir") && ac > 1) { scheddir = (av[1].a_type == A_SYM ? av[1].a_w.w_sym : gensym(".")); ac -= 2; av += 2; } else if (!strcmp(flag, "-ascii")) { binary = 0; ac--; av++; } else break; } if (ac) post("pd~: warning: ignoring extra arguments"); dsp_setup((t_pxobject *)x, ninsig); x->x_outlet1 = outlet_new(&x->x_obj, 0); for (j = 0; j < noutsig; j++) outlet_new((t_pxobject *)x, "signal"); x->x_clock = clock_new(x, (method)pd_tilde_tick); x->x_insig = (t_pdsample **)t_getbytes(ninsig * sizeof(*x->x_insig)); x->x_outsig = (t_pdsample **)t_getbytes(noutsig * sizeof(*x->x_outsig)); x->x_ninsig = ninsig; x->x_noutsig = noutsig; x->x_fifo = fifo; x->x_sr = sr; x->x_pddir = pddir; x->x_schedlibdir = scheddir; x->x_infd = 0; x->x_outfd = 0; x->x_childpid = -1; x->x_binbuf = binbuf_new(); x->x_clockisset = 0; x->x_binary = binary; x->x_sampbuf = 0; } return (x); } void pd_tilde_assist(t_pd_tilde *x, void *b, long m, long a, char *s) { } #endif /* MSP */ ================================================ FILE: libs/libpd/pure-data/extra/pique/pique.c ================================================ /* Copyright (c) 1999 Miller Puckette. The contents of this file are free for any use, but BOTH THE AUTHOR AND UCSD DISCLAIM ALL WARRANTIES related to it. Although not written in Java, this still should not be used to control any machinery containing a sharp blade or combustible materiel, or as part of any life support system or weapon. */ #include "m_pd.h" #include #include /* These pragmas are only used for MSVC, not MinGW or Cygwin */ #ifdef _MSC_VER #pragma warning( disable : 4244 ) #pragma warning( disable : 4305 ) #endif static t_class *pique_class; typedef struct _pique { t_object x_obj; int x_n; t_float x_errthresh; t_float *x_freq; t_float *x_amp; t_float *x_ampre; t_float *x_ampim; } t_pique; static void *pique_new(t_floatarg f) { int n = f; t_pique *x = (t_pique *)pd_new(pique_class); if (n < 1) n = 100; x->x_n = n; x->x_errthresh = 0; x->x_freq = t_getbytes(n * sizeof(*x->x_freq)); x->x_amp = t_getbytes(n * sizeof(*x->x_amp)); x->x_ampre = t_getbytes(n * sizeof(*x->x_ampre)); x->x_ampim = t_getbytes(n * sizeof(*x->x_ampim)); outlet_new(&x->x_obj, &s_list); return (x); } static t_float hanning(t_float pidetune, t_float sinpidetune) { t_float pi = 3.141592653589793; if (pidetune < 0.01 && pidetune > -0.01) return (1); else if (pidetune > 3.14 && pidetune < 3.143) return (0.5); else if (pidetune < -3.14 && pidetune > -3.143) return (0.5); else return (sinpidetune/pidetune - 0.5 * (sinpidetune/(pidetune+pi) + sinpidetune/(pidetune-pi))); } static t_float peakerror(t_word *fpreal, t_word *fpimag, t_float pidetune, t_float norm, t_float peakreal, t_float peakimag) { t_float sinpidetune = sin(pidetune); t_float cospidetune = cos(pidetune); t_float windowshould = hanning(pidetune, sinpidetune); t_float realshould = windowshould * ( peakreal * cospidetune + peakimag * sinpidetune); t_float imagshould = windowshould * ( peakimag * cospidetune - peakreal * sinpidetune); t_float realgot = norm * (fpreal[0].w_float - 0.5 * (fpreal[1].w_float + fpreal[-1].w_float)); t_float imaggot = norm * (fpimag[0].w_float - 0.5 * (fpimag[1].w_float + fpimag[-1].w_float)); t_float realdev = realshould - realgot, imagdev = imagshould - imaggot; /* post("real %f->%f; imag %f->%f", realshould, realgot, imagshould, imaggot); */ return (realdev * realdev + imagdev * imagdev); } static void pique_doit(int npts, t_word *fpreal, t_word *fpimag, int npeak, int *nfound, t_float *fpfreq, t_float *fpamp, t_float *fpampre, t_float *fpampim, t_float errthresh) { t_float srate = sys_getsr(); /* not sure how to get this correctly */ t_float oneovern = 1.0/ (t_float)npts; t_float fperbin = srate * oneovern; t_float pow1, pow2 = 0, pow3 = 0, pow4 = 0, pow5 = 0; t_float re1, re2 = 0, re3 = fpreal->w_float; t_float im1, im2 = 0, im3 = 0, powthresh, relativeerror; int count, peakcount = 0, n2 = (npts >> 1); t_float *fp1, *fp2; t_word *wp1, *wp2; for (count = n2, wp1 = fpreal, wp2 = fpimag, powthresh = 0; count--; wp1++, wp2++) powthresh += (wp1->w_float) * (wp1->w_float) + (wp2->w_float) * (wp2->w_float) ; powthresh *= 0.00001; for (count = 1; count < n2; count++) { t_float windreal, windimag, pi = 3.141592653589793; t_float detune, pidetune, sinpidetune, cospidetune, ampcorrect, freqout, ampout, ampoutreal, ampoutimag; t_float rpeak, rpeaknext, rpeakprev; t_float ipeak, ipeaknext, ipeakprev; t_float errleft, errright; fpreal++; fpimag++; re1 = re2; re2 = re3; re3 = fpreal->w_float; im1 = im2; im2 = im3; im3 = fpimag->w_float; if (count < 2) continue; pow1 = pow2; pow2 = pow3; pow3 = pow4; pow4 = pow5; /* get Hanning-windowed spectrum by convolution */ windreal = re2 - 0.5 * (re1 + re3); windimag = im2 - 0.5 * (im1 + im3); pow5 = windreal * windreal + windimag * windimag; /* if (count < 30) post("power %f", pow5); */ if (count < 5) continue; /* check for a peak. The actual bin is count-3. */ if (pow3 <= pow2 || pow3 <= pow4 || pow3 <= pow1 || pow3 <= pow5 || pow3 < powthresh) continue; /* go back for the raw FFT values around the peak. */ rpeak = fpreal[-3].w_float; rpeaknext = fpreal[-2].w_float; rpeakprev = fpreal[-4].w_float; ipeak = fpimag[-3].w_float; ipeaknext = fpimag[-2].w_float; ipeakprev = fpimag[-4].w_float; /* recalculate Hanning-windowed spectrum by convolution */ windreal = rpeak - 0.5 * (rpeaknext + rpeakprev); windimag = ipeak - 0.5 * (ipeaknext + ipeakprev); detune = ((rpeakprev - rpeaknext) * (2.0 * rpeak - rpeakprev - rpeaknext) + (ipeakprev - ipeaknext) * (2.0 * ipeak - ipeakprev - ipeaknext)) / (4.0 * pow3); /* if (count < 30) post("detune %f", detune); */ if (detune > 0.7 || detune < -0.7) continue; /* the frequency is the sum of the bin frequency and detuning */ freqout = fperbin * ((t_float)(count-3) + detune); pidetune = pi * detune; sinpidetune = sin(pidetune); cospidetune = cos(pidetune); ampcorrect = 1.0 / hanning(pidetune, sinpidetune); /* Multiply by 2 to get real-sinusoid peak amplitude and divide by N to normalize FFT */ ampcorrect *= 2. * oneovern; /* amplitude is peak height, corrected for Hanning window shape */ ampout = ampcorrect * sqrt(pow3); ampoutreal = ampcorrect * (windreal * cospidetune - windimag * sinpidetune); ampoutimag = ampcorrect * (windreal * sinpidetune + windimag * cospidetune); if (errthresh > 0) { /* post("peak %f %f", freqout, ampout); */ errleft = peakerror(fpreal-4, fpimag-4, pidetune+pi, 2. * oneovern, ampoutreal, ampoutimag); errright = peakerror(fpreal-2, fpimag-2, pidetune-pi, 2. * oneovern, ampoutreal, ampoutimag); relativeerror = (errleft + errright)/(ampout * ampout); if (relativeerror > errthresh) continue; } /* post("power %f, error %f, relative %f", pow3, errleft + errright, relativeerror); */ *fpfreq++ = freqout; *fpamp++ = ampout; *fpampre++ = ampoutreal; *fpampim++ = ampoutimag; if (++peakcount == npeak) break; } *nfound = peakcount; } static void pique_list(t_pique *x, t_symbol *s, int argc, t_atom *argv) { int npts = atom_getfloatarg(0, argc, argv); t_symbol *symreal = atom_getsymbolarg(1, argc, argv); t_symbol *symimag = atom_getsymbolarg(2, argc, argv); int npeak = atom_getfloatarg(3, argc, argv); int n; t_garray *a; t_word *fpreal, *fpimag; if (npts < 8 || npeak < 1) pd_error(0, "pique: bad npoints or npeak"); if (npeak > x->x_n) npeak = x->x_n; if (!(a = (t_garray *)pd_findbyclass(symreal, garray_class)) || !garray_getfloatwords(a, &n, &fpreal) || n < npts) pd_error(0, "%s: missing or bad array", symreal->s_name); else if (!(a = (t_garray *)pd_findbyclass(symimag, garray_class)) || !garray_getfloatwords(a, &n, &fpimag) || n < npts) pd_error(0, "%s: missing or bad array", symimag->s_name); else { int nfound, i; t_float *fpfreq = x->x_freq; t_float *fpamp = x->x_amp; t_float *fpampre = x->x_ampre; t_float *fpampim = x->x_ampim; pique_doit(npts, fpreal, fpimag, npeak, &nfound, fpfreq, fpamp, fpampre, fpampim, x->x_errthresh); for (i = 0; i < nfound; i++, fpamp++, fpfreq++, fpampre++, fpampim++) { t_atom at[5]; SETFLOAT(at, (t_float)i); SETFLOAT(at+1, *fpfreq); SETFLOAT(at+2, *fpamp); SETFLOAT(at+3, *fpampre); SETFLOAT(at+4, *fpampim); outlet_list(x->x_obj.ob_outlet, &s_list, 5, at); } } } static void pique_errthresh(t_pique *x, t_floatarg f) { x->x_errthresh = f; } static void pique_free(t_pique *x) { int n = x->x_n; t_freebytes(x->x_freq, n * sizeof(*x->x_freq)); t_freebytes(x->x_amp, n * sizeof(*x->x_amp)); t_freebytes(x->x_ampre, n * sizeof(*x->x_ampre)); t_freebytes(x->x_ampim, n * sizeof(*x->x_ampim)); } void pique_setup(void) { pique_class = class_new(gensym("pique"), (t_newmethod)pique_new, (t_method)pique_free, sizeof(t_pique),0, A_DEFFLOAT, 0); class_addlist(pique_class, pique_list); class_addmethod(pique_class, (t_method)pique_errthresh, gensym("errthresh"), A_FLOAT, 0); post("pique 0.1 for PD version 23"); } ================================================ FILE: libs/libpd/pure-data/extra/sigmund~/sigmund~.c ================================================ /* Copyright (c) 2005 Miller Puckette. BSD licensed. No warranties. */ /* fix parameter settings not to report pitch if evidence too scanty? note-on detection triggered by falling envelope (a posteriori) reentrancy bug setting loud flag (other parameters too?) tweaked freqs still not stable enough implement block ("-b") mode */ #ifdef PD #include "m_pd.h" #endif #ifdef MSP #include "ext.h" #include "z_dsp.h" #include "ext_support.h" #include "ext_proto.h" #include "ext_obex.h" typedef double t_floatarg; typedef float t_float; #define t_resizebytes(a, b, c) t_resizebytes((char *)(a), (b), (c)) #endif /* From here to the next "#ifdef PD" or "#ifdef Max" should be extractable and usable in other contexts. The one external requirement is a real single-precision FFT, invoked as in the Mayer one: */ /* this routine is passed a buffer of npoints values, and returns the N/2+1 real parts of the DFT (frequency zero through Nyquist), followed by the N/2-1 imaginary points, in order of decreasing frequency. Pd 0.41, for example, defines this in the file d_fft_mayer.c or d_fft_fftsg.c. */ #include #include #include #ifdef _WIN32 # include /* MSVC or mingw on windows */ #elif defined(__linux__) || defined(__APPLE__) # include /* linux, mac, mingw, cygwin */ #endif #include #ifdef _MSC_VER #pragma warning( disable : 4244 ) #pragma warning( disable : 4305 ) #endif #ifndef HAVE_ALLOCA /* can work without alloca() but we never need it */ #define HAVE_ALLOCA 1 #endif /* limit stack allocation to ~400kB (enough for 16384 points). * usually the stack size is at least 1 MB */ #define ALLOCA_MAXBYTES 400000 #if HAVE_ALLOCA #define BUF_ALLOCA(n) ((n) < ALLOCA_MAXBYTES ? \ alloca(n) : getbytes(n)) #define BUF_FREEA(x, n) ( \ (((n) < ALLOCA_MAXBYTES) || (freebytes((x), (n)), 0))) #else #define BUF_ALLOCA(n) (getbytes(n)) #define BUF_FREEA(x, n) (freebytes((x), (n))) #endif typedef struct peak { t_float p_freq; t_float p_amp; t_float p_ampreal; t_float p_ampimag; t_float p_pit; t_float p_db; t_float p_salience; t_float p_tmp; } t_peak; typedef struct _pitchpt { t_float p_weight; t_float p_loudness; t_float p_evenness; t_float p_unused; } t_pitchpt; /********************** service routines **************************/ /* these three are adapted from elsewhere in Pd but included here for completeness */ static unsigned int sigmund_ilog2(int n) { int ret = -1; while (n) { n >>= 1; ret++; } return (ret); } static t_float sigmund_ftom(t_float f) { return (f > 0 ? 17.3123405046 * log(.12231220585 * f) : -1500); } #define LOGTEN 2.302585092994 static t_float sigmund_powtodb(t_float f) { if (f <= 0) return (0); else { t_float val = 100 + 10./LOGTEN * log(f); return (val < 0 ? 0 : val); } } /* parameters for von Hann window (change these to get Hamming if desired) */ #define W_ALPHA 0.5 #define W_BETA 0.5 #define NEGBINS 4 /* number of bins of negative frequency we'll need */ #define PI 3.141592653589793 #define LOG2 0.693147180559945 #define LOG10 2.302585092994046 static t_float sinx(t_float theta, t_float sintheta) { if (theta > -0.003 && theta < 0.003) return (1); else return (sintheta/theta); } static t_float window_hann_mag(t_float pidetune, t_float sinpidetune) { return (W_ALPHA * sinx(pidetune, sinpidetune) - 0.5 * W_BETA * (sinx(pidetune+PI, sinpidetune) + sinx(pidetune-PI, sinpidetune))); } static t_float window_mag(t_float pidetune, t_float cospidetune) { return (sinx(pidetune + (PI/2), cospidetune) + sinx(pidetune - (PI/2), -cospidetune)); } /*********** Routines to analyze a window into sinusoidal peaks *************/ static int sigmund_cmp_freq(const void *p1, const void *p2) { if ((*(t_peak **)p1)->p_freq > (*(t_peak **)p2)->p_freq) return (1); else if ((*(t_peak **)p1)->p_freq < (*(t_peak **)p2)->p_freq) return (-1); else return (0); } static void sigmund_tweak(int npts, t_float *ftreal, t_float *ftimag, int npeak, t_peak *peaks, t_float fperbin, int loud) { t_peak **peakptrs = (t_peak **)alloca(sizeof (*peakptrs) * (npeak+1)); t_peak negpeak; int peaki, j, k; t_float ampreal[3], ampimag[3]; t_float binperf = 1./fperbin; t_float phaseperbin = (npts-0.5)/npts, oneovern = 1./npts; if (npeak < 1) return; for (peaki = 0; peaki < npeak; peaki++) peakptrs[peaki+1] = &peaks[peaki]; qsort(peakptrs+1, npeak, sizeof (*peakptrs), sigmund_cmp_freq); peakptrs[0] = &negpeak; negpeak.p_ampreal = peakptrs[1]->p_ampreal; negpeak.p_ampimag = -peakptrs[1]->p_ampimag; negpeak.p_freq = -peakptrs[1]->p_freq; for (peaki = 1; peaki <= npeak; peaki++) { int cbin = peakptrs[peaki]->p_freq*binperf + 0.5; int nsub = (peaki == npeak ? 1:2); t_float windreal, windimag, windpower, detune, pidetune, sinpidetune, cospidetune, ampcorrect, ampout, ampoutreal, ampoutimag, freqout; /* post("3 nsub %d amp %f freq %f", nsub, peakptrs[peaki]->p_amp, peakptrs[peaki]->p_freq); */ if (cbin < 0 || cbin > 2*npts - 3) continue; for (j = 0; j < 3; j++) ampreal[j] = ftreal[cbin+2*j-2], ampimag[j] = ftimag[cbin+2*j-2]; /* post("a %f %f", ampreal[1], ampimag[1]); */ for (j = 0; j < nsub; j++) { t_peak *neighbor = peakptrs[(peaki-1) + 2*j]; t_float neighborreal = npts * neighbor->p_ampreal; t_float neighborimag = npts * neighbor->p_ampimag; for (k = 0; k < 3; k++) { t_float freqdiff = (0.5*PI) * ((cbin + 2*k-2) -binperf * neighbor->p_freq); t_float sx = sinx(freqdiff, sin(freqdiff)); t_float phasere = cos(freqdiff * phaseperbin); t_float phaseim = sin(freqdiff * phaseperbin); ampreal[k] -= sx * (phasere * neighborreal - phaseim * neighborimag); ampimag[k] -= sx * (phaseim * neighborreal + phasere * neighborimag); } /* post("b %f %f", ampreal[1], ampimag[1]); */ } windreal = W_ALPHA * ampreal[1] - (0.5 * W_BETA) * (ampreal[0] + ampreal[2]); windimag = W_ALPHA * ampimag[1] - (0.5 * W_BETA) * (ampimag[0] + ampimag[2]); windpower = windreal * windreal + windimag * windimag; detune = ( W_BETA*(ampreal[0] - ampreal[2]) * (2.0*W_ALPHA * ampreal[1] - W_BETA * (ampreal[0] + ampreal[2])) + W_BETA*(ampimag[0] - ampimag[2]) * (2.0*W_ALPHA * ampimag[1] - W_BETA * (ampimag[0] + ampimag[2])) ) / (4.0 * windpower); if (detune > 0.5) detune = 0.5; else if (detune < -0.5) detune = -0.5; /* if (loud > 0) post("tweak: windpower %f, bin %d, detune %f", windpower, cbin, detune); */ pidetune = PI * detune; sinpidetune = sin(pidetune); cospidetune = cos(pidetune); ampcorrect = 1.0 / window_hann_mag(pidetune, sinpidetune); ampout = oneovern * ampcorrect *sqrt(windpower); ampoutreal = oneovern * ampcorrect * (windreal * cospidetune - windimag * sinpidetune); ampoutimag = oneovern * ampcorrect * (windreal * sinpidetune + windimag * cospidetune); freqout = (cbin + 2*detune) * fperbin; /* if (loud > 1) post("amp %f, freq %f", ampout, freqout); */ peakptrs[peaki]->p_freq = freqout; peakptrs[peaki]->p_amp = ampout; peakptrs[peaki]->p_ampreal = ampoutreal; peakptrs[peaki]->p_ampimag = ampoutimag; } } static void sigmund_remask(int maxbin, int bestindex, t_float powmask, t_float maxpower, t_float *maskbuf) { int bin; int bin1 = (bestindex > 52 ? bestindex-50:2); int bin2 = (maxbin < bestindex + 50 ? bestindex + 50 : maxbin); for (bin = bin1; bin < bin2; bin++) { t_float bindiff = bin - bestindex; t_float mymask; mymask = powmask/ (1. + bindiff * bindiff * bindiff * bindiff); if (bindiff < 2 && bindiff > -2) mymask = 2*maxpower; if (mymask > maskbuf[bin]) maskbuf[bin] = mymask; } } #define PEAKMASKFACTOR 1. #define PEAKTHRESHFACTOR 0.6 static void sigmund_getrawpeaks(int npts, t_float *insamps, int npeak, t_peak *peakv, int *nfound, t_float *power, t_float srate, int loud, t_float hifreq) { t_float oneovern = 1.0/ (t_float)npts; t_float fperbin = 0.5 * srate * oneovern, totalpower = 0; int npts2 = 2*npts, i, bin, bufsize = sizeof (t_float ) * (2*NEGBINS + 6*npts); int peakcount = 0; t_float *fp1, *fp2; t_float *rawreal, *rawimag, *maskbuf, *powbuf; t_float *bigbuf = (t_float *)BUF_ALLOCA(bufsize); int maxbin = hifreq/fperbin; if (maxbin > npts - NEGBINS) maxbin = npts - NEGBINS; /* if (loud) post("tweak %d", tweak); */ maskbuf = bigbuf + npts2; powbuf = maskbuf + npts; rawreal = powbuf + npts+NEGBINS; rawimag = rawreal+npts+NEGBINS; for (i = 0; i < npts; i++) maskbuf[i] = 0; for (i = 0; i < npts; i++) bigbuf[i] = insamps[i]; for (i = npts; i < 2*npts; i++) bigbuf[i] = 0; mayer_realfft(npts2, bigbuf); for (i = 0; i < npts; i++) rawreal[i] = bigbuf[i]; for (i = 1; i < npts-1; i++) rawimag[i] = bigbuf[npts2-i]; rawreal[-1] = rawreal[1]; rawreal[-2] = rawreal[2]; rawreal[-3] = rawreal[3]; rawreal[-4] = rawreal[4]; rawimag[0] = rawimag[npts-1] = 0; rawimag[-1] = -rawimag[1]; rawimag[-2] = -rawimag[2]; rawimag[-3] = -rawimag[3]; rawimag[-4] = -rawimag[4]; #if 1 for (i = 0, fp1 = rawreal, fp2 = rawimag; i < maxbin; i++, fp1++, fp2++) { t_float x1 = fp1[1] - fp1[-1], x2 = fp2[1] - fp2[-1], p = powbuf[i] = x1*x1+x2*x2; if (i >= 2) totalpower += p; } powbuf[maxbin] = powbuf[maxbin+1] = 0; *power = 0.5 * totalpower *oneovern * oneovern; #endif for (peakcount = 0; peakcount < npeak; peakcount++) { t_float pow1, maxpower = 0, windreal, windimag, windpower, detune, pidetune, sinpidetune, cospidetune, ampcorrect, ampout, ampoutreal, ampoutimag, freqout, powmask; int bestindex = -1; for (bin = 2, fp1 = rawreal+2, fp2 = rawimag+2; bin < maxbin; bin++, fp1++, fp2++) { pow1 = powbuf[bin]; if (pow1 > maxpower && pow1 > maskbuf[bin]) { t_float thresh = PEAKTHRESHFACTOR * (powbuf[bin-2]+powbuf[bin+2]); if (pow1 > thresh) maxpower = pow1, bestindex = bin; } } if (totalpower <= 0 || maxpower < 1e-10*totalpower || bestindex < 0) break; fp1 = rawreal+bestindex; fp2 = rawimag+bestindex; powmask = maxpower * PEAKMASKFACTOR; /* if (loud > 2) post("maxpower %f, powmask %f, param1 %f", maxpower, powmask, param1); */ sigmund_remask(maxbin, bestindex, powmask, maxpower, maskbuf); /* if (loud > 1) post("best index %d, total power %f", bestindex, totalpower); */ windreal = fp1[1] - fp1[-1]; windimag = fp2[1] - fp2[-1]; windpower = windreal * windreal + windimag * windimag; detune = ((fp1[1] * fp1[1] - fp1[-1]*fp1[-1]) + (fp2[1] * fp2[1] - fp2[-1]*fp2[-1])) / (2 * windpower); if (detune > 0.5) detune = 0.5; else if (detune < -0.5) detune = -0.5; /* if (loud > 1) post("windpower %f, index %d, detune %f", windpower, bestindex, detune); */ pidetune = PI * detune; sinpidetune = sin(pidetune); cospidetune = cos(pidetune); ampcorrect = 1.0 / window_mag(pidetune, cospidetune); ampout = ampcorrect *sqrt(windpower); ampoutreal = ampcorrect * (windreal * cospidetune - windimag * sinpidetune); ampoutimag = ampcorrect * (windreal * sinpidetune + windimag * cospidetune); /* the frequency is the sum of the bin frequency and detuning */ peakv[peakcount].p_freq = (freqout = (bestindex + 2*detune)) * fperbin; peakv[peakcount].p_amp = oneovern * ampout; peakv[peakcount].p_ampreal = oneovern * ampoutreal; peakv[peakcount].p_ampimag = oneovern * ampoutimag; } sigmund_tweak(npts, rawreal, rawimag, peakcount, peakv, fperbin, loud); sigmund_tweak(npts, rawreal, rawimag, peakcount, peakv, fperbin, loud); for (i = 0; i < peakcount; i++) { peakv[i].p_pit = sigmund_ftom(peakv[i].p_freq); peakv[i].p_db = sigmund_powtodb(peakv[i].p_amp); } *nfound = peakcount; BUF_FREEA(bigbuf, bufsize); } /*************** Routines for finding fundamental pitch *************/ #define PITCHNPEAK 12 #define NHARMONICS 16 #define STEPSPEROCTAVE 48 #define PITCHSTEPS(npts) (STEPSPEROCTAVE * sigmund_ilog2((npts))) static void sigmund_getpitch(int npeak, t_peak *peakv, t_float *freqp, t_float *qualityp, t_float *evennessp, t_float npts, t_float srate, t_float *harmonicweights, t_float amppowerlaw, t_float qualitythreshold, t_pitchpt *ppt, int compat, int verbose) { t_float fperbin = 0.5 * srate / npts, fbestbin; int npit = PITCHSTEPS(npts), i, j, k, nsalient, bestbin; t_float bestweight, sumamp, sumloudness, sumfreq, sumweight, freq = 0; t_peak *bigpeaks[PITCHNPEAK]; if (npeak < 1) goto done; for (i = 0; i < npit; i++) ppt[i].p_weight = ppt[i].p_loudness = ppt[i].p_evenness = 0; for (i = 0; i < npeak; i++) { peakv[i].p_tmp = 0; peakv[i].p_salience = peakv[i].p_db; } for (nsalient = 0; nsalient < PITCHNPEAK; nsalient++) { t_peak *bestpeak = 0; t_float bestsalience = -1e20; for (j = 0; j < npeak; j++) if (peakv[j].p_tmp == 0 && peakv[j].p_salience > bestsalience) { bestsalience = peakv[j].p_salience; bestpeak = &peakv[j]; } if (!bestpeak) break; bigpeaks[nsalient] = bestpeak; bestpeak->p_tmp = 1; /* post("peak f=%f a=%f", bestpeak.p_freq, bestpeak.p_amp); */ } sumloudness = 0; for (i = 0; i < nsalient; i++) { t_peak *thispeak = bigpeaks[i]; t_float weightindex = (STEPSPEROCTAVE/LOG2) * log(thispeak->p_freq/(2.*fperbin)); t_float loudness = pow(thispeak->p_amp, amppowerlaw); /* post("index %f, uncertainty %f", weightindex, pitchuncertainty); */ for (j = 0; j < NHARMONICS; j++) { int loindex = weightindex - (STEPSPEROCTAVE/LOG2) * log(j + 1.) - 0.5; int hiindex = loindex + 2; if (hiindex < 0) break; if (hiindex >= npit) continue; if (loindex < 0) loindex = 0; for (k = loindex; k <= hiindex; k++) { ppt[k].p_weight += loudness * harmonicweights[j]; ppt[k].p_loudness += loudness; if (j&1) ppt[k].p_evenness += loudness; } } sumloudness += loudness; } bestbin = -1; bestweight = -1e20; for (i = 0; i < npit; i++) if (ppt[i].p_weight > bestweight) bestweight = ppt[i].p_weight, bestbin = i; if (bestbin < 0 || bestbin >= npit - 1 || sumloudness <= 0 || ppt[bestbin].p_loudness <= 0 || (compat && bestweight < sumloudness * 0.4) || (!compat && ppt[bestbin].p_loudness < sumloudness * qualitythreshold)) { *qualityp = 0; *evennessp = 0; goto done; } *qualityp = ppt[bestbin].p_loudness / sumloudness; *evennessp = ppt[bestbin].p_evenness / ppt[bestbin].p_loudness; /* first guess by parabolic peak fitting */ fbestbin = bestbin + (ppt[bestbin+1].p_weight - ppt[bestbin-1].p_weight) / (ppt[bestbin+1].p_weight + ppt[bestbin].p_weight + ppt[bestbin-1].p_weight); freq = 2*fperbin * exp((LOG2/STEPSPEROCTAVE)*fbestbin); for (sumamp = sumweight = sumfreq = 0, i = 0; i < nsalient; i++) { t_peak *thispeak = bigpeaks[i]; t_float thisamp = thispeak->p_amp; t_float thisfreq = thispeak->p_freq; t_float harmonic = thisfreq/freq; t_float intpart = (int)(0.5 + harmonic); t_float inharm = harmonic - intpart; #if 0 if (loud) post("freq %f intpart %f inharm %f", freq, intpart, inharm); #endif if (intpart >= 1 && intpart <= 16 && inharm < 0.015 * intpart && inharm > - (0.015 * intpart)) { t_float weight = thisamp * intpart; sumweight += weight; sumfreq += weight*thisfreq/intpart; #if 0 if (loud) post("weight %f freq %f", weight, thisfreq); #endif } } if (sumweight > 0) freq = sumfreq / sumweight; done: if (!(freq >= 0 || freq <= 0)) { /* post("freq nan cancelled"); */ freq = 0; } *freqp = freq; } /*************** gather peak lists into sinusoidal tracks *************/ static void sigmund_peaktrack(int ninpeak, t_peak *inpeakv, int noutpeak, t_peak *outpeakv, float maxerror, int loud) { int incnt, outcnt; for (outcnt = 0; outcnt < noutpeak; outcnt++) outpeakv[outcnt].p_tmp = -1; /* first pass. Match each "in" peak with the closest previous "out" peak, but no two to the same one. */ for (incnt = 0; incnt < ninpeak; incnt++) { t_float besterror = 1e20; int bestcnt = -1; inpeakv[incnt].p_tmp = -1; for (outcnt = 0; outcnt < noutpeak; outcnt++) { t_float thiserror; if (outpeakv[outcnt].p_amp == 0) continue; thiserror = inpeakv[incnt].p_freq - outpeakv[outcnt].p_freq; if (thiserror < 0) thiserror = -thiserror; if (thiserror < besterror) { besterror = thiserror; bestcnt = outcnt; } } if (bestcnt >= 0 && besterror < maxerror && outpeakv[bestcnt].p_tmp < 0) { outpeakv[bestcnt] = inpeakv[incnt]; inpeakv[incnt].p_tmp = 0; outpeakv[bestcnt].p_tmp = 0; } } /* second pass. Unmatched "in" peaks assigned to free "out" peaks */ for (incnt = 0; incnt < ninpeak; incnt++) if (inpeakv[incnt].p_tmp < 0) { for (outcnt = 0; outcnt < noutpeak; outcnt++) if (outpeakv[outcnt].p_tmp < 0) { outpeakv[outcnt] = inpeakv[incnt]; inpeakv[incnt].p_tmp = 0; outpeakv[outcnt].p_tmp = 1; break; } } for (outcnt = 0; outcnt < noutpeak; outcnt++) if (outpeakv[outcnt].p_tmp == -1) outpeakv[outcnt].p_amp = 0; } /**************** parse continuous pitch into note starts ***************/ #define NHISTPOINT 100 typedef struct _histpoint { t_float h_freq; t_float h_power; } t_histpoint; typedef struct _notefinder { t_float n_age; t_float n_hifreq; t_float n_lofreq; int n_peaked; t_histpoint n_hist[NHISTPOINT]; int n_histphase; } t_notefinder; static void notefinder_init(t_notefinder *x) { int i; x->n_peaked = x->n_age = 0; x->n_hifreq = x->n_lofreq = 0; x->n_histphase = 0; for (i = 0; i < NHISTPOINT; i++) x->n_hist[i].h_freq =x->n_hist[i].h_power = 0; } static void notefinder_doit(t_notefinder *x, t_float freq, t_float power, t_float *note, t_float vibrato, int stableperiod, t_float powerthresh, t_float growththresh, int loud) { /* calculate frequency ratio between allowable vibrato extremes (equal to twice the vibrato deviation from center) */ t_float vibmultiple = exp((2*LOG2/12) * vibrato); int oldhistphase, i, k; if (stableperiod > NHISTPOINT - 1) stableperiod = NHISTPOINT - 1; else if (stableperiod < 1) stableperiod = 1; if (++x->n_histphase == NHISTPOINT) x->n_histphase = 0; x->n_hist[x->n_histphase].h_freq = freq; x->n_hist[x->n_histphase].h_power = power; x->n_age++; *note = 0; #if 0 if (loud) { post("stable %d, age %d, vibmultiple %f, powerthresh %f, hifreq %f", stableperiod, (int)x->n_age ,vibmultiple, powerthresh, x->n_hifreq); post("histfreq %f %f %f %f", x->n_hist[x->n_histphase].h_freq, x->n_hist[(x->n_histphase+NHISTPOINT-1)%NHISTPOINT].h_freq, x->n_hist[(x->n_histphase+NHISTPOINT-2)%NHISTPOINT].h_freq, x->n_hist[(x->n_histphase+NHISTPOINT-3)%NHISTPOINT].h_freq); post("power %f %f %f %f", x->n_hist[x->n_histphase].h_power, x->n_hist[(x->n_histphase+NHISTPOINT-1)%NHISTPOINT].h_power, x->n_hist[(x->n_histphase+NHISTPOINT-2)%NHISTPOINT].h_power, x->n_hist[(x->n_histphase+NHISTPOINT-3)%NHISTPOINT].h_power); for (i = 0, k = x->n_histphase; i < stableperiod; i++) { post("pit %5.1f pow %f", sigmund_ftom(x->n_hist[k].h_freq), x->n_hist[k].h_power); if (--k < 0) k = NHISTPOINT - 1; } } #endif /* look for shorter notes than "stableperiod" in length. The amplitude must rise and then fall while the pitch holds steady. */ if (x->n_hifreq <= 0 && x->n_age > stableperiod) { t_float maxpow = 0, freqatmaxpow = 0, localhifreq = -1e20, locallofreq = 1e20; int startphase = x->n_histphase - stableperiod + 1; if (startphase < 0) startphase += NHISTPOINT; for (i = 0, k = startphase; i < stableperiod; i++) { if (x->n_hist[k].h_freq <= 0) break; if (x->n_hist[k].h_power > maxpow) maxpow = x->n_hist[k].h_power, freqatmaxpow = x->n_hist[k].h_freq; if (x->n_hist[k].h_freq > localhifreq) localhifreq = x->n_hist[k].h_freq; if (x->n_hist[k].h_freq < locallofreq) locallofreq = x->n_hist[k].h_freq; if (localhifreq > locallofreq * vibmultiple) break; if (maxpow > power * growththresh && maxpow > x->n_hist[startphase].h_power * growththresh && localhifreq < vibmultiple * locallofreq && freqatmaxpow > 0 && maxpow > powerthresh) { x->n_hifreq = x->n_lofreq = *note = freqatmaxpow; x->n_age = 0; x->n_peaked = 0; /* post("got short note"); */ return; } if (++k >= NHISTPOINT) k = 0; } } if (x->n_hifreq > 0) { /* test if we're within "vibrato" range, and if so update range */ if (freq * vibmultiple >= x->n_hifreq && x->n_lofreq * vibmultiple >= freq) { if (freq > x->n_hifreq) x->n_hifreq = freq; if (freq < x->n_lofreq) x->n_lofreq = freq; } else if (x->n_hifreq > 0 && x->n_age > stableperiod) { /* if we've been out of range at least 1/2 the last "stableperiod+1" analyses, clear the note */ int nbad = 0; for (i = 0, k = x->n_histphase; i < stableperiod + 1; i++) { if (--k < 0) k = NHISTPOINT - 1; if (x->n_hist[k].h_freq * vibmultiple <= x->n_hifreq || x->n_lofreq * vibmultiple <= x->n_hist[k].h_freq) nbad++; } if (2 * nbad >= stableperiod + 1) { x->n_hifreq = x->n_lofreq = 0; x->n_age = 0; } } } oldhistphase = x->n_histphase - stableperiod; if (oldhistphase < 0) oldhistphase += NHISTPOINT; /* look for envelope attacks */ if (x->n_hifreq > 0 && x->n_peaked) { if (freq > 0 && power > powerthresh && power > x->n_hist[oldhistphase].h_power * exp((LOG10*0.1)*growththresh)) { /* clear it and fall through for new stable-note test */ x->n_peaked = 0; x->n_hifreq = x->n_lofreq = 0; x->n_age = 0; } } else if (!x->n_peaked) { if (x->n_hist[oldhistphase].h_power > powerthresh && x->n_hist[oldhistphase].h_power > power) x->n_peaked = 1; } /* test for a new note using a stability criterion. */ if (freq >= 0 && (x->n_hifreq <= 0 || freq > x->n_hifreq || freq < x->n_lofreq)) { t_float testfhi = freq, testflo = freq, maxpow = x->n_hist[x->n_histphase].h_power; for (i = 0, k = x->n_histphase; i < stableperiod-1; i++) { if (--k < 0) k = NHISTPOINT - 1; if (x->n_hist[k].h_freq > testfhi) testfhi = x->n_hist[k].h_freq; if (x->n_hist[k].h_freq < testflo) testflo = x->n_hist[k].h_freq; if (x->n_hist[k].h_power > maxpow) maxpow = x->n_hist[k].h_power; } #if 0 if (loud) post("freq %.2g testfhi %.2g testflo %.2g maxpow %.2g", freq, testfhi, testflo, maxpow); #endif if (testflo > 0 && testfhi <= vibmultiple * testflo && maxpow > powerthresh) { /* report new note */ t_float sumf = 0, sumw = 0, thisw; for (i = 0, k = x->n_histphase; i < stableperiod; i++) { thisw = x->n_hist[k].h_power; sumw += thisw; sumf += thisw*x->n_hist[k].h_freq; if (--k < 0) k = NHISTPOINT - 1; } x->n_hifreq = x->n_lofreq = *note = (sumw > 0 ? sumf/sumw : 0); #if 0 /* debugging printout */ for (i = 0; i < stableperiod; i++) { int k3 = x->n_histphase - i; if (k3 < 0) k3 += NHISTPOINT; startpost("%5.1f ", sigmund_ftom(x->n_hist[k3].h_freq)); } post(""); #endif x->n_age = 0; x->n_peaked = 0; return; } } *note = 0; return; } /**************** object structure for Pd and Max. *********************/ /* From here onward, the code is specific to eithr Pd, Max, or both. If neither "PD 'nor "MSP" is defined, none of this is compiled, so that the whole file can be included in other, non-PD and non-Max projects. */ #if (defined(PD) || defined (MSP)) #define NHIST 100 #define MODE_STREAM 1 #define MODE_BLOCK 2 /* unimplemented */ #define MODE_TABLE 3 #define NPOINTS_DEF 1024 #define NPOINTS_MIN 128 #define NPOINTS_MAX 4194304 #define HOP_DEF 512 #define NPEAK_DEF 20 #define VIBRATO_DEF 1 #define STABLETIME_DEF 50 #define MINPOWER_DEF 50 #define GROWTH_DEF 7 #define AMPPOWERLAW_DEF 0.5 #define NHARMONICS_DEF 6 #define QUALITY_DEF 0.4 #define OUT_PITCH 0 #define OUT_ENV 1 #define OUT_NOTE 2 #define OUT_PEAKS 3 #define OUT_TRACKS 4 #define OUT_SPECTRUM 5 #define OUT_QUALITY 6 #define OUT_EVENNESS 7 typedef struct _varout { #ifdef PD t_outlet *v_outlet; #endif /* PD */ #ifdef MSP void *v_outlet; #endif /* MSP */ int v_what; } t_varout; typedef struct _sigmund { #ifdef PD t_object x_obj; t_clock *x_clock; t_float x_f; /* for main signal inlet */ #endif /* PD */ #ifdef MSP t_pxobject x_obj; void *obex; void *x_clock; t_sample *x_inbuf2; /* extra input buffer to eat clock/DSP jitter */ #endif /* MSP */ t_varout *x_varoutv; int x_nvarout; t_float x_sr; /* sample rate */ int x_mode; /* MODE_STREAM, etc. */ int x_npts; /* number of points in analysis window */ int x_npeak; /* number of peaks to find */ int x_loud; /* debug level */ t_sample *x_inbuf; /* input buffer */ t_pitchpt *x_pitchbuf; /* input buffer */ int x_infill; /* number of points filled */ int x_countdown; /* countdown to start filling buffer */ int x_hop; /* samples between analyses */ t_float x_maxfreq; /* highest-frequency peak to report */ t_float x_vibrato; /* vibrato depth in half tones */ t_float x_stabletime; /* period of stability needed for note */ t_float x_growth; /* growth to set off a new note */ t_float x_minpower; /* minimum power, in DB, for a note */ t_float x_hweights[NHARMONICS]; /* harmonic weights for pitch detection */ t_float x_nharmonics; /* harmonic droppoff point to calculate hweights */ t_float x_octavebias; /* octave-up or down bias to calculate hweights */ t_float x_amppowerlaw; /* power to raise amplitudes to get pitch weight */ t_float x_quality; /* required quality to report pitch */ t_float x_param1; /* three parameters for temporary use */ t_float x_param2; t_float x_param3; t_notefinder x_notefinder; /* note parsing state */ t_peak *x_trackv; /* peak tracking state */ int x_ntrack; /* number of peaks tracked */ unsigned int x_dopitch:1; /* which things to calculate */ unsigned int x_donote:1; unsigned int x_dotracks:1; } t_sigmund; static void sigmund_nharmonics(t_sigmund *x, t_floatarg nharmonics, t_floatarg octavebias); static void sigmund_preinit(t_sigmund *x) { x->x_npts = NPOINTS_DEF; sigmund_nharmonics(x, NHARMONICS_DEF, 0); x->x_amppowerlaw = AMPPOWERLAW_DEF; x->x_quality = QUALITY_DEF; x->x_param1 = 0; x->x_param2 = 0; x->x_param3 = 0; x->x_hop = HOP_DEF; x->x_mode = MODE_STREAM; x->x_npeak = NPEAK_DEF; x->x_vibrato = VIBRATO_DEF; x->x_stabletime = STABLETIME_DEF; x->x_growth = GROWTH_DEF; x->x_minpower = MINPOWER_DEF; x->x_maxfreq = 1000000; x->x_loud = 0; x->x_sr = 1; x->x_nvarout = 0; x->x_varoutv = (t_varout *)getbytes(0); x->x_trackv = 0; x->x_ntrack = 0; x->x_dopitch = x->x_donote = x->x_dotracks = 0; x->x_inbuf = 0; x->x_pitchbuf = (t_pitchpt *)getbytes(PITCHSTEPS(x->x_npts)); #ifdef MSP x->x_inbuf2 = 0; #endif } static void sigmund_npts(t_sigmund *x, t_floatarg f) { int nwas = x->x_npts, npts = f; /* check parameter ranges */ if (npts < NPOINTS_MIN) post("sigmund~: minimum points %d", NPOINTS_MIN), npts = NPOINTS_MIN; if (npts > NPOINTS_MAX) post("sigmund~: maximum points %d", NPOINTS_MAX), npts = NPOINTS_MAX; if (npts != (1 << sigmund_ilog2(npts))) post("sigmund~: adjusting analysis size to %d points", (npts = (1 << sigmund_ilog2(npts)))); if (npts != nwas) x->x_countdown = x->x_infill = 0; if (x->x_mode == MODE_STREAM) { if (x->x_inbuf) { x->x_inbuf = (t_sample *)t_resizebytes(x->x_inbuf, sizeof(*x->x_inbuf) * nwas, sizeof(*x->x_inbuf) * npts); #ifdef MSP x->x_inbuf2 = (t_sample *)t_resizebytes(x->x_inbuf2, sizeof(*x->x_inbuf2) * nwas, sizeof(*x->x_inbuf2) * npts); #endif } else { x->x_inbuf = (t_sample *)getbytes(sizeof(*x->x_inbuf) * npts); memset((char *)(x->x_inbuf), 0, sizeof(*x->x_inbuf) * npts); #ifdef MSP x->x_inbuf2 = (t_sample *)getbytes(sizeof(*x->x_inbuf2) * npts); memset((char *)(x->x_inbuf2), 0, sizeof(*x->x_inbuf2) * npts); #endif } } else x->x_inbuf = 0; x->x_pitchbuf = (t_pitchpt *)resizebytes(x->x_pitchbuf, PITCHSTEPS(nwas) * sizeof(t_pitchpt), PITCHSTEPS(npts) * sizeof(t_pitchpt)); x->x_npts = npts; } static void sigmund_hop(t_sigmund *x, t_floatarg f) { int hop = f; if (hop < 0) { pd_error(0, "sigmund~: ignoring negative hopsize %d", hop); return; } x->x_hop = hop; if (0 == hop) return; /* check parameter ranges */ if (x->x_hop != (1 << sigmund_ilog2(x->x_hop))) post("sigmund~: adjusting analysis size to %d points", (x->x_hop = (1 << sigmund_ilog2(x->x_hop)))); } static void sigmund_npeak(t_sigmund *x, t_floatarg f) { if (f < 1) f = 1; x->x_npeak = f; } static void sigmund_maxfreq(t_sigmund *x, t_floatarg f) { x->x_maxfreq = f; } static void sigmund_vibrato(t_sigmund *x, t_floatarg f) { if (f < 0) f = 0; x->x_vibrato = f; } static void sigmund_stabletime(t_sigmund *x, t_floatarg f) { if (f < 0) f = 0; x->x_stabletime = f; } static void sigmund_growth(t_sigmund *x, t_floatarg f) { if (f < 0) f = 0; x->x_growth = f; } static void sigmund_minpower(t_sigmund *x, t_floatarg f) { if (f < 0) f = 0; x->x_minpower = f; } static void sigmund_nharmonics(t_sigmund *x, t_floatarg nharmonics, t_floatarg octavebias) { int i; t_float evenbias, oddbias; if (nharmonics < 0) nharmonics = 0; if (octavebias < -100) octavebias = -100; else if (octavebias > 100) octavebias = 100; evenbias = (octavebias > 0 ? 1 : 1 + octavebias/100); oddbias = (octavebias < 0 ? 1 : 1 - octavebias/100); for (i = 0; i < NHARMONICS; i++) x->x_hweights[i] = (nharmonics <= 0 ? (i==0) : (nharmonics / (nharmonics + i)) * ((i & 1) ? oddbias : evenbias)); x->x_nharmonics = nharmonics; x->x_octavebias = octavebias; } static void sigmund_amppowerlaw(t_sigmund *x, t_floatarg f) { if (f <= 0.01) f = 0.01; else if (f > 10) f = 10; x->x_amppowerlaw = f; } static void sigmund_quality(t_sigmund *x, t_floatarg f) { if (f <= 0) f = 0; else if (f > 1) f = 1; x->x_quality = f; } static void sigmund_outspectrum(t_sigmund *x, t_outlet *outlet, t_float basepit) { /* "&0xffff" below silences spurious compiler warning */ int nsteps = PITCHSTEPS(x->x_npts) & 0xffff, i; t_atom *at = (t_atom *)alloca(sizeof(t_atom) * (nsteps + 1)); SETFLOAT(at, basepit); for (i = 0; i < nsteps; i++) SETFLOAT(at + (i+1), x->x_pitchbuf[i].p_weight); outlet_list(outlet, 0, nsteps+1, at); } static void sigmund_doit(t_sigmund *x, int npts, t_float *arraypoints, int debug, t_float srate) { t_peak *peakv = (t_peak *)alloca(sizeof(t_peak) * x->x_npeak); int nfound, i, cnt; t_float freq = 0, quality = 0, evenness = 0, power, note = 0; sigmund_getrawpeaks(npts, arraypoints, x->x_npeak, peakv, &nfound, &power, srate, debug, x->x_maxfreq); if (x->x_dopitch) sigmund_getpitch(nfound, peakv, &freq, &quality, &evenness, npts, srate, x->x_hweights, x->x_amppowerlaw, x->x_quality, x->x_pitchbuf, (pd_compatibilitylevel < 54), debug); if (x->x_donote) notefinder_doit(&x->x_notefinder, freq, power, ¬e, x->x_vibrato, 1 + x->x_stabletime * 0.001 * srate / (t_float)x->x_hop, exp(LOG10*0.1*(x->x_minpower - 100)), x->x_growth, debug); if (x->x_dotracks) sigmund_peaktrack(nfound, peakv, x->x_ntrack, x->x_trackv, 2* srate / npts, debug); for (cnt = x->x_nvarout; cnt--;) { t_varout *v = &x->x_varoutv[cnt]; switch (v->v_what) { case OUT_PITCH: outlet_float(v->v_outlet, sigmund_ftom(freq)); break; case OUT_EVENNESS: outlet_float(v->v_outlet, evenness); break; case OUT_QUALITY: outlet_float(v->v_outlet, quality); break; case OUT_SPECTRUM: sigmund_outspectrum(x, v->v_outlet, sigmund_ftom(2 * srate / npts)); break; case OUT_ENV: outlet_float(v->v_outlet, sigmund_powtodb(power)); break; case OUT_NOTE: if (note > 0) outlet_float(v->v_outlet, sigmund_ftom(note)); break; case OUT_PEAKS: for (i = 0; i < nfound; i++) { t_atom at[5]; SETFLOAT(at, (t_float)i); SETFLOAT(at+1, peakv[i].p_freq); SETFLOAT(at+2, 2*peakv[i].p_amp); SETFLOAT(at+3, 2*peakv[i].p_ampreal); SETFLOAT(at+4, 2*peakv[i].p_ampimag); outlet_list(v->v_outlet, 0, 5, at); } break; case OUT_TRACKS: for (i = 0; i < x->x_ntrack; i++) { t_atom at[4]; SETFLOAT(at, (t_float)i); SETFLOAT(at+1, x->x_trackv[i].p_freq); SETFLOAT(at+2, 2*x->x_trackv[i].p_amp); SETFLOAT(at+3, x->x_trackv[i].p_tmp); outlet_list(v->v_outlet, 0, 4, at); } break; } } } static t_int *sigmund_perform(t_int *w); static void sigmund_dsp(t_sigmund *x, t_signal **sp) { if (x->x_mode == MODE_STREAM) { if (x->x_hop % sp[0]->s_n) post("sigmund~: adjusting hop size to %d", (x->x_hop = sp[0]->s_n * (x->x_hop / sp[0]->s_n))); if (x->x_infill % sp[0]->s_n) { if (x->x_inbuf) { int i; t_sample*inbuf = x->x_inbuf; for(i=0; ix_npts; i++) *inbuf++ = 0.; } x->x_infill = 0; } x->x_sr = sp[0]->s_sr; dsp_add(sigmund_perform, 3, x, sp[0]->s_vec, (t_int)sp[0]->s_n); } } static void sigmund_print(t_sigmund *x) { int i; post("sigmund~ version 0.08 settings:"); post("npts %d", (int)x->x_npts); post("hop %d", (int)x->x_hop); post("npeak %d", (int)x->x_npeak); post("maxfreq %g", x->x_maxfreq); post("vibrato %g", x->x_vibrato); post("stabletime %g", x->x_stabletime); post("growth %g", x->x_growth); post("minpower %g", x->x_minpower); post("amppowerlaw %g", x->x_amppowerlaw); post("quality %g", x->x_quality); if (x->x_nharmonics >= 0) post("nharmonics %f %f", x->x_nharmonics, x->x_octavebias), post("resulting harmonic weights:"); else post("harmonic weights specified individually:"); post("%5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f", x->x_hweights[0], x->x_hweights[1], x->x_hweights[2], x->x_hweights[3], x->x_hweights[4], x->x_hweights[5], x->x_hweights[6], x->x_hweights[7]); post("%5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f", x->x_hweights[8], x->x_hweights[9], x->x_hweights[10], x->x_hweights[11], x->x_hweights[12], x->x_hweights[13], x->x_hweights[14], x->x_hweights[15]); if (x->x_sr > 0) post("minimum possible pitch at sample rate %f is %f", x->x_sr, sigmund_ftom(2 * x->x_sr / x->x_npts)); x->x_loud = 1; } static void sigmund_free(t_sigmund *x) { if (x->x_inbuf) { freebytes(x->x_inbuf, x->x_npts * sizeof(*x->x_inbuf)); #ifdef MSP freebytes(x->x_inbuf2, x->x_npts * sizeof(*x->x_inbuf2)); #endif } freebytes(x->x_pitchbuf, PITCHSTEPS(x->x_npts) * sizeof(*x->x_inbuf)); if (x->x_trackv) freebytes(x->x_trackv, x->x_ntrack * sizeof(*x->x_trackv)); freebytes(x->x_varoutv, x->x_nvarout * sizeof(t_varout)); clock_free(x->x_clock); } #endif /* PD or MSP */ /*************************** Glue for Pd ************************/ #ifdef PD static t_class *sigmund_class; static void sigmund_tick(t_sigmund *x); static void sigmund_clear(t_sigmund *x); static void sigmund_npts(t_sigmund *x, t_floatarg f); static void sigmund_hop(t_sigmund *x, t_floatarg f); static void sigmund_npeak(t_sigmund *x, t_floatarg f); static void sigmund_maxfreq(t_sigmund *x, t_floatarg f); static void sigmund_vibrato(t_sigmund *x, t_floatarg f); static void sigmund_stabletime(t_sigmund *x, t_floatarg f); static void sigmund_growth(t_sigmund *x, t_floatarg f); static void sigmund_minpower(t_sigmund *x, t_floatarg f); static void sigmund_amppowerlaw(t_sigmund *x, t_floatarg f); static void sigmund_quality(t_sigmund *x, t_floatarg f); static void sigmund_tick(t_sigmund *x) { if (x->x_infill == x->x_npts) { sigmund_doit(x, x->x_npts, x->x_inbuf, x->x_loud, x->x_sr); if (x->x_hop >= x->x_npts) { x->x_infill = 0; x->x_countdown = x->x_hop - x->x_npts; } else { memmove(x->x_inbuf, x->x_inbuf + x->x_hop, (x->x_infill = x->x_npts - x->x_hop) * sizeof(*x->x_inbuf)); x->x_countdown = 0; } if (x->x_loud) x->x_loud--; } } static t_int *sigmund_perform(t_int *w) { t_sigmund *x = (t_sigmund *)(w[1]); t_sample *in = (t_sample *)(w[2]); int n = (int)(w[3]); if (x->x_hop % n) return (w+4); if (x->x_countdown > 0) x->x_countdown -= n; else if (x->x_infill != x->x_npts) { int j; t_float *fp = x->x_inbuf + x->x_infill; for (j = 0; j < n; j++) *fp++ = *in++; x->x_infill += n; if (x->x_infill == x->x_npts) clock_delay(x->x_clock, 0); } return (w+4); } static void *sigmund_new(t_symbol *s, int argc, t_atom *argv) { t_sigmund *x = (t_sigmund *)pd_new(sigmund_class); sigmund_preinit(x); while (argc > 0) { t_symbol *firstarg = atom_getsymbolarg(0, argc, argv); if (!strcmp(firstarg->s_name, "-t")) { x->x_mode = MODE_TABLE; argc--, argv++; } else if (!strcmp(firstarg->s_name, "-s")) { x->x_mode = MODE_STREAM; argc--, argv++; } #if 0 else if (!strcmp(firstarg->s_name, "-b")) { x->x_mode = MODE_BLOCK; argc--, argv++; } #endif else if (!strcmp(firstarg->s_name, "-npts") && argc > 1) { x->x_npts = atom_getfloatarg(1, argc, argv); argc -= 2; argv += 2; } else if (!strcmp(firstarg->s_name, "-hop") && argc > 1) { sigmund_hop(x, atom_getfloatarg(1, argc, argv)); argc -= 2; argv += 2; } else if (!strcmp(firstarg->s_name, "-npeak") && argc > 1) { sigmund_npeak(x, atom_getfloatarg(1, argc, argv)); argc -= 2; argv += 2; } else if (!strcmp(firstarg->s_name, "-maxfreq") && argc > 1) { sigmund_maxfreq(x, atom_getfloatarg(1, argc, argv)); argc -= 2; argv += 2; } else if (!strcmp(firstarg->s_name, "-vibrato") && argc > 1) { sigmund_vibrato(x, atom_getfloatarg(1, argc, argv)); argc -= 2; argv += 2; } else if (!strcmp(firstarg->s_name, "-stabletime") && argc > 1) { sigmund_stabletime(x, atom_getfloatarg(1, argc, argv)); argc -= 2; argv += 2; } else if (!strcmp(firstarg->s_name, "-growth") && argc > 1) { sigmund_growth(x, atom_getfloatarg(1, argc, argv)); argc -= 2; argv += 2; } else if (!strcmp(firstarg->s_name, "-minpower") && argc > 1) { sigmund_minpower(x, atom_getfloatarg(1, argc, argv)); argc -= 2; argv += 2; } else if (!strcmp(firstarg->s_name, "-nharmonics") && argc > 2) { sigmund_nharmonics(x, atom_getfloatarg(1, argc, argv), atom_getfloatarg(2, argc, argv)); argc -= 3; argv += 3; } else if (!strcmp(firstarg->s_name, "-amppowerlaw") && argc > 1) { sigmund_amppowerlaw(x, atom_getfloatarg(1, argc, argv)); argc -= 2; argv += 2; } else if (!strcmp(firstarg->s_name, "-quality") && argc > 1) { sigmund_quality(x, atom_getfloatarg(1, argc, argv)); argc -= 2; argv += 2; } else if (!strcmp(firstarg->s_name, "pitch")) { int n2 = x->x_nvarout+1; x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv, x->x_nvarout*sizeof(t_varout), n2*sizeof(t_varout)); x->x_varoutv[x->x_nvarout].v_outlet = outlet_new(&x->x_obj, &s_float); x->x_varoutv[x->x_nvarout].v_what = OUT_PITCH; x->x_nvarout = n2; x->x_dopitch = 1; argc--, argv++; } else if (!strcmp(firstarg->s_name, "quality")) { int n2 = x->x_nvarout+1; x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv, x->x_nvarout*sizeof(t_varout), n2*sizeof(t_varout)); x->x_varoutv[x->x_nvarout].v_outlet = outlet_new(&x->x_obj, &s_float); x->x_varoutv[x->x_nvarout].v_what = OUT_QUALITY; x->x_nvarout = n2; x->x_dopitch = 1; argc--, argv++; } else if (!strcmp(firstarg->s_name, "evenness")) { int n2 = x->x_nvarout+1; x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv, x->x_nvarout*sizeof(t_varout), n2*sizeof(t_varout)); x->x_varoutv[x->x_nvarout].v_outlet = outlet_new(&x->x_obj, &s_float); x->x_varoutv[x->x_nvarout].v_what = OUT_EVENNESS; x->x_nvarout = n2; x->x_dopitch = 1; argc--, argv++; } else if (!strcmp(firstarg->s_name, "spectrum")) { int n2 = x->x_nvarout+1; x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv, x->x_nvarout*sizeof(t_varout), n2*sizeof(t_varout)); x->x_varoutv[x->x_nvarout].v_outlet = outlet_new(&x->x_obj, &s_float); x->x_varoutv[x->x_nvarout].v_what = OUT_SPECTRUM; x->x_nvarout = n2; x->x_dopitch = 1; argc--, argv++; } else if (!strcmp(firstarg->s_name, "env")) { int n2 = x->x_nvarout+1; x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv, x->x_nvarout*sizeof(t_varout), n2*sizeof(t_varout)); x->x_varoutv[x->x_nvarout].v_outlet = outlet_new(&x->x_obj, &s_float); x->x_varoutv[x->x_nvarout].v_what = OUT_ENV; x->x_nvarout = n2; argc--, argv++; } else if (!strcmp(firstarg->s_name, "note") || !strcmp(firstarg->s_name, "notes")) { int n2 = x->x_nvarout+1; x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv, x->x_nvarout*sizeof(t_varout), n2*sizeof(t_varout)); x->x_varoutv[x->x_nvarout].v_outlet = outlet_new(&x->x_obj, &s_float); x->x_varoutv[x->x_nvarout].v_what = OUT_NOTE; x->x_nvarout = n2; x->x_dopitch = x->x_donote = 1; argc--, argv++; } else if (!strcmp(firstarg->s_name, "peaks")) { int n2 = x->x_nvarout+1; x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv, x->x_nvarout*sizeof(t_varout), n2*sizeof(t_varout)); x->x_varoutv[x->x_nvarout].v_outlet = outlet_new(&x->x_obj, &s_list); x->x_varoutv[x->x_nvarout].v_what = OUT_PEAKS; x->x_nvarout = n2; argc--, argv++; } else if (!strcmp(firstarg->s_name, "tracks")) { int n2 = x->x_nvarout+1; x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv, x->x_nvarout*sizeof(t_varout), n2*sizeof(t_varout)); x->x_varoutv[x->x_nvarout].v_outlet = outlet_new(&x->x_obj, &s_list); x->x_varoutv[x->x_nvarout].v_what = OUT_TRACKS; x->x_nvarout = n2; x->x_dotracks = 1; argc--, argv++; } else { pd_error(x, "sigmund~: %s: unknown flag or argument missing", firstarg->s_name); argc--, argv++; } } if (!x->x_nvarout) { x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv, 0, 2*sizeof(t_varout)); x->x_varoutv[0].v_outlet = outlet_new(&x->x_obj, &s_float); x->x_varoutv[0].v_what = OUT_PITCH; x->x_varoutv[1].v_outlet = outlet_new(&x->x_obj, &s_float); x->x_varoutv[1].v_what = OUT_ENV; x->x_nvarout = 2; x->x_dopitch = 1; } if (x->x_dotracks) { x->x_ntrack = x->x_npeak; x->x_trackv = (t_peak *)getbytes(x->x_ntrack * sizeof(*x->x_trackv)); } x->x_clock = clock_new(&x->x_obj.ob_pd, (t_method)sigmund_tick); x->x_infill = 0; x->x_countdown = 0; sigmund_npts(x, x->x_npts); notefinder_init(&x->x_notefinder); sigmund_clear(x); return (x); } static void sigmund_list(t_sigmund *x, t_symbol *s, int argc, t_atom *argv) { t_symbol *syminput = atom_getsymbolarg(0, argc, argv); int npts = atom_getfloatarg(1, argc, argv); int onset = atom_getfloatarg(2, argc, argv); t_float srate = atom_getfloatarg(3, argc, argv); int loud = atom_getfloatarg(4, argc, argv); int arraysize, totstorage, nfound, i, bufsize; t_garray *a; t_float *arraypoints, pit; t_word *wordarray = 0; if (argc < 4) { post( "sigmund~: array-name, npts, array-onset, samplerate, [optional debug flag]"); return; } if (npts < 64 || npts != (1 << ilog2(npts))) { pd_error(0, "sigmund~: bad npoints"); return; } if (onset < 0) { pd_error(0, "sigmund~: negative onset"); return; } if (srate <= 0) { pd_error(0, "sigmund~: bad samplerate"); return; } bufsize = sizeof(t_float)*npts; arraypoints = (t_float *)getbytes(bufsize); if (!(a = (t_garray *)pd_findbyclass(syminput, garray_class)) || !garray_getfloatwords(a, &arraysize, &wordarray) || arraysize < onset + npts) { pd_error(0, "sigmund~: '%s' array missing or too small", syminput->s_name); goto cleanup; } if (arraysize < npts) { pd_error(0, "sigmund~: too few points in array"); goto cleanup; } for (i = 0; i < npts; i++) arraypoints[i] = wordarray[i+onset].w_float; sigmund_doit(x, npts, arraypoints, loud, srate); cleanup: freebytes(arraypoints, bufsize); } static void sigmund_clear(t_sigmund *x) { if (x->x_trackv) memset(x->x_trackv, 0, x->x_ntrack * sizeof(*x->x_trackv)); x->x_infill = x->x_countdown = 0; } static void sigmund_harmonicweights(t_sigmund *x, t_symbol *s, int argc, t_atom *argv) { int i; for (i = 0; i < NHARMONICS; i++) x->x_hweights[i] = atom_getfloatarg(i, argc, argv); x->x_nharmonics = x->x_octavebias = -1; } /* these are for testing; their meanings vary... */ static void sigmund_param1(t_sigmund *x, t_floatarg f) { x->x_param1 = f; } static void sigmund_param2(t_sigmund *x, t_floatarg f) { x->x_param2 = f; } static void sigmund_param3(t_sigmund *x, t_floatarg f) { x->x_param3 = f; } static void sigmund_printnext(t_sigmund *x, t_float f) { x->x_loud = f; } void sigmund_tilde_setup(void) { sigmund_class = class_new(gensym("sigmund~"), (t_newmethod)sigmund_new, (t_method)sigmund_free, sizeof(t_sigmund), 0, A_GIMME, 0); class_addlist(sigmund_class, sigmund_list); class_addmethod(sigmund_class, (t_method)sigmund_dsp, gensym("dsp"), A_CANT, 0); CLASS_MAINSIGNALIN(sigmund_class, t_sigmund, x_f); class_addmethod(sigmund_class, (t_method)sigmund_param1, gensym("param1"), A_FLOAT, 0); class_addmethod(sigmund_class, (t_method)sigmund_param2, gensym("param2"), A_FLOAT, 0); class_addmethod(sigmund_class, (t_method)sigmund_param3, gensym("param3"), A_FLOAT, 0); class_addmethod(sigmund_class, (t_method)sigmund_npts, gensym("npts"), A_FLOAT, 0); class_addmethod(sigmund_class, (t_method)sigmund_hop, gensym("hop"), A_FLOAT, 0); class_addmethod(sigmund_class, (t_method)sigmund_maxfreq, gensym("maxfreq"), A_FLOAT, 0); class_addmethod(sigmund_class, (t_method)sigmund_npeak, gensym("npeak"), A_FLOAT, 0); class_addmethod(sigmund_class, (t_method)sigmund_vibrato, gensym("vibrato"), A_FLOAT, 0); class_addmethod(sigmund_class, (t_method)sigmund_stabletime, gensym("stabletime"), A_FLOAT, 0); class_addmethod(sigmund_class, (t_method)sigmund_growth, gensym("growth"), A_FLOAT, 0); class_addmethod(sigmund_class, (t_method)sigmund_minpower, gensym("minpower"), A_FLOAT, 0); class_addmethod(sigmund_class, (t_method)sigmund_nharmonics, gensym("nharmonics"), A_FLOAT, A_DEFFLOAT, 0); class_addmethod(sigmund_class, (t_method)sigmund_harmonicweights, gensym("harmonicweights"), A_GIMME, 0); class_addmethod(sigmund_class, (t_method)sigmund_amppowerlaw, gensym("amppowerlaw"), A_FLOAT, 0); class_addmethod(sigmund_class, (t_method)sigmund_quality, gensym("quality"), A_FLOAT, 0); class_addmethod(sigmund_class, (t_method)sigmund_clear, gensym("clear"), 0); class_addmethod(sigmund_class, (t_method)sigmund_print, gensym("print"), 0); class_addmethod(sigmund_class, (t_method)sigmund_printnext, gensym("printnext"), A_FLOAT, 0); } #endif /* PD */ /************************ Max/MSP glue **********************************/ /* -------------------------- MSP glue ------------------------- */ #ifdef MSP static void *sigmund_class; /* Max/MSP has laxer sync between DSP and "tick"s - so in the perf routine we keep a circular buffer that is rectified into inbuf only when the tick comes. */ static void sigmund_tick(t_sigmund *x) { int i, j, npts = x->x_npts; if (!x->x_inbuf) return; for (i = x->x_infill, j = 0; i < npts; i++, j++) x->x_inbuf[j] = x->x_inbuf2[i]; for (i = 0; j < npts; i++, j++) x->x_inbuf[j] = x->x_inbuf2[i]; sigmund_doit(x, x->x_npts, x->x_inbuf, x->x_loud, x->x_sr); x->x_loud = 0; } static t_int *sigmund_perform(t_int *w) { t_sigmund *x = (t_sigmund *)(w[1]); t_float *in = (t_float *)(w[2]); int n = (int)(w[3]), j; int infill = x->x_infill; t_float *fp = x->x_inbuf2 + infill; if (x->x_obj.z_disabled) /* return if in muted MSP subpatch -Rd */ return (w+4); if (infill < 0 || infill >= x->x_npts) infill = 0; /* for some reason this sometimes happens: */ if (!x->x_inbuf2) return (w+4); for (j = 0; j < n; j++) { *fp++ = *in++; if (++infill == x->x_npts) infill = 0, fp = x->x_inbuf2; } x->x_infill = infill; if (x->x_countdown <= 0) { x->x_countdown = x->x_hop; clock_delay(x->x_clock, 0); } x->x_countdown -= n; return (w+4); } static void *sigmund_new(t_symbol *s, long ac, t_atom *av) { t_sigmund *x; t_varout *g; int i, j; if (!(x = (t_sigmund *)object_alloc(sigmund_class))) return (0); sigmund_preinit(x); attr_args_process(x, ac, av); dsp_setup((t_pxobject *)x, 1); object_obex_store(x, gensym("dumpout"), outlet_new(x, NULL)); for (i = 0; i < ac; i++) if (av[i].a_type == A_SYM) { char *s = av[i].a_w.w_sym->s_name; if (!strcmp(s, "pitch")) { int n2 = x->x_nvarout+1; x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv, x->x_nvarout*sizeof(t_varout), n2*sizeof(t_varout)); x->x_varoutv[x->x_nvarout].v_what = OUT_PITCH; x->x_nvarout = n2; x->x_dopitch = 1; } else if (!strcmp(s, "spectrum")) { int n2 = x->x_nvarout+1; x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv, x->x_nvarout*sizeof(t_varout), n2*sizeof(t_varout)); x->x_varoutv[x->x_nvarout].v_what = OUT_SPECTRUM; x->x_nvarout = n2; x->x_dopitch = 1; } else if (!strcmp(s, "quality")) { int n2 = x->x_nvarout+1; x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv, x->x_nvarout*sizeof(t_varout), n2*sizeof(t_varout)); x->x_varoutv[x->x_nvarout].v_what = OUT_QUALITY; x->x_nvarout = n2; x->x_dopitch = 1; } else if (!strcmp(s, "evenness")) { int n2 = x->x_nvarout+1; x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv, x->x_nvarout*sizeof(t_varout), n2*sizeof(t_varout)); x->x_varoutv[x->x_nvarout].v_what = OUT_EVENNESS; x->x_nvarout = n2; x->x_dopitch = 1; } else if (!strcmp(s, "env")) { int n2 = x->x_nvarout+1; x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv, x->x_nvarout*sizeof(t_varout), n2*sizeof(t_varout)); x->x_varoutv[x->x_nvarout].v_what = OUT_ENV; x->x_nvarout = n2; } else if (!strcmp(s, "note") || !strcmp(s, "notes")) { int n2 = x->x_nvarout+1; x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv, x->x_nvarout*sizeof(t_varout), n2*sizeof(t_varout)); x->x_varoutv[x->x_nvarout].v_what = OUT_NOTE; x->x_nvarout = n2; x->x_dopitch = x->x_donote = 1; } else if (!strcmp(s, "peaks")) { int n2 = x->x_nvarout+1; x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv, x->x_nvarout*sizeof(t_varout), n2*sizeof(t_varout)); x->x_varoutv[x->x_nvarout].v_what = OUT_PEAKS; x->x_nvarout = n2; } else if (!strcmp(s, "tracks")) { int n2 = x->x_nvarout+1; x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv, x->x_nvarout*sizeof(t_varout), n2*sizeof(t_varout)); x->x_varoutv[x->x_nvarout].v_what = OUT_TRACKS; x->x_nvarout = n2; x->x_dotracks = 1; } else if (s[0] != '@') post("sigmund~: ignoring unknown argument '%s'" ,s); } if (!x->x_nvarout) { x->x_varoutv = (t_varout *)t_resizebytes(x->x_varoutv, 0, 2*sizeof(t_varout)); x->x_varoutv[0].v_what = OUT_PITCH; x->x_varoutv[1].v_what = OUT_ENV; x->x_nvarout = 2; x->x_dopitch = 1; } for (j = 0, g = x->x_varoutv + x->x_nvarout-1; j < x->x_nvarout; j++, g--) g->v_outlet = ((g->v_what == OUT_PITCH || g->v_what == OUT_ENV || g->v_what == OUT_NOTE) ? floatout((t_object *)x) : listout((t_object *)x)); if (x->x_dotracks) { x->x_ntrack = x->x_npeak; x->x_trackv = (t_peak *)getbytes(x->x_ntrack * sizeof(*x->x_trackv)); } x->x_clock = clock_new(x, (method)sigmund_tick); x->x_infill = 0; x->x_countdown = 0; sigmund_npts(x, x->x_npts); notefinder_init(&x->x_notefinder); return (x); } /* Attribute setters. */ void sigmund_npts_set(t_sigmund *x, void *attr, long ac, t_atom *av) { if (ac && av) sigmund_npts(x, atom_getfloat(av)); } void sigmund_hop_set(t_sigmund *x, void *attr, long ac, t_atom *av) { if (ac && av) sigmund_hop(x, atom_getfloat(av)); } void sigmund_npeak_set(t_sigmund *x, void *attr, long ac, t_atom *av) { if (ac && av) sigmund_npeak(x, atom_getfloat(av)); } void sigmund_maxfreq_set(t_sigmund *x, void *attr, long ac, t_atom *av) { if (ac && av) sigmund_maxfreq(x, atom_getfloat(av)); } void sigmund_vibrato_set(t_sigmund *x, void *attr, long ac, t_atom *av) { if (ac && av) sigmund_vibrato(x, atom_getfloat(av)); } void sigmund_stabletime_set(t_sigmund *x, void *attr, long ac, t_atom *av) { if (ac && av) sigmund_stabletime(x, atom_getfloat(av)); } void sigmund_growth_set(t_sigmund *x, void *attr, long ac, t_atom *av) { if (ac && av) sigmund_growth(x, atom_getfloat(av)); } void sigmund_minpower_set(t_sigmund *x, void *attr, long ac, t_atom *av) { if (ac && av) sigmund_minpower(x, atom_getfloat(av)); } /* end attr setters */ void sigmund_assist(t_sigmund *x, void *b, long m, long a, char *s) { } int main() { t_class *c; long attrflags = 0; t_symbol *sym_long = gensym("long"), *sym_float32 = gensym("float32"); c = class_new("sigmund~", (method)sigmund_new, (method)sigmund_free, sizeof(t_sigmund), (method)0L, A_GIMME, 0); class_obexoffset_set(c, calcoffset(t_sigmund, obex)); class_addattr(c, attr_offset_new("npts", sym_long, attrflags, (method)0L, (method)sigmund_npts_set, calcoffset(t_sigmund, x_npts))); class_addattr(c ,attr_offset_new("hop", sym_long, attrflags, (method)0L, (method)sigmund_hop_set, calcoffset(t_sigmund, x_hop))); class_addattr(c ,attr_offset_new("maxfreq", sym_float32, attrflags, (method)0L, (method)sigmund_maxfreq_set, calcoffset(t_sigmund, x_maxfreq))); class_addattr(c ,attr_offset_new("npeak", sym_long, attrflags, (method)0L, (method)sigmund_npeak_set, calcoffset(t_sigmund, x_npeak))); class_addattr(c ,attr_offset_new("vibrato", sym_float32, attrflags, (method)0L, (method)sigmund_vibrato_set, calcoffset(t_sigmund, x_vibrato))); class_addattr(c ,attr_offset_new("stabletime", sym_float32, attrflags, (method)0L, (method)sigmund_stabletime_set, calcoffset(t_sigmund, x_stabletime))); class_addattr(c ,attr_offset_new("growth", sym_float32, attrflags, (method)0L, (method)sigmund_growth_set, calcoffset(t_sigmund, x_growth))); class_addattr(c ,attr_offset_new("minpower", sym_float32, attrflags, (method)0L, (method)sigmund_minpower_set, calcoffset(t_sigmund, x_minpower))); class_addmethod(c, (method)sigmund_dsp, "dsp", A_CANT, 0); class_addmethod(c, (method)sigmund_print, "print", 0); class_addmethod(c, (method)sigmund_print, "printnext", A_DEFFLOAT, 0); class_addmethod(c, (method)sigmund_assist, "assist", A_CANT, 0); class_addmethod(c, (method)object_obex_dumpout, "dumpout", A_CANT, 0); class_addmethod(c, (method)object_obex_quickref, "quickref", A_CANT, 0); class_dspinit(c); class_register(CLASS_BOX, c); sigmund_class = c; post("sigmund~ version 0.08"); return (0); } #endif /* MSP */ ================================================ FILE: libs/libpd/pure-data/extra/stdout/stdout.c ================================================ /* stdout -- write messages to standard output. Copyright 2008 Miller Puckette BSD license; see README.txt in this distribution for details. */ #include "m_pd.h" #include #include static t_class *stdout_class; #define MODE_DEFAULT 0 /* default, FUDI style */ #define MODE_CR 1 /* newline-terminate messages and omit semicolons */ #define MODE_BIN 2 /* binary messages supplied bytewise from patch */ #define MODE_PDTILDE 3 /* binary atoms for subprocess of pd~ object */ typedef struct _stdout { t_object x_obj; int x_mode; /* 0=FUDI; 1=printf (no terminating semicolon); -1=binary */ int x_flush; /* fflush() stdout after each message */ } t_stdout; static void *stdout_new(t_symbol*s, int argc, t_atom*argv) { t_stdout *x = (t_stdout *)pd_new(stdout_class); /* for some reason in MS windows we have to flush standard out here - otherwise these outputs get out of order from the ones from pdsched.c over in ../pd~. I'm guessing mingw (with its different C runtime support) will handle this correctly and so am only ifdeffing this on Microsoft C compiler. It's an efficiency hit, possibly a serious one. */ #ifdef _MSC_VER x->x_flush = 1; #endif while(argc--) { s = atom_getsymbol(argv++); if (gensym("-cr") == s) { /* No-semicolon mode */ x->x_mode = MODE_CR; } else if ((gensym("-b") == s) || (gensym("-binary") == s)) { /* Binary mode: no extra characters (semicolons, CR,...) is appended */ x->x_mode = MODE_BIN; } else if ((gensym("-f") == s) || (gensym("-flush") == s)) { x->x_flush = 1; } else if ((gensym("-nf") == s) || (gensym("-noflush") == s)) { x->x_flush = 0; } else if (gensym("") != s) { /* unknown mode; ignore it */ } } if (gensym("#pd_binary_stdio")->s_thing) x->x_mode = MODE_PDTILDE; return (x); } static void stdout_binary(t_stdout *x, int argc, t_atom *argv) { #define BUFSIZE 65535 char buf[BUFSIZE]; int i; if (argc>BUFSIZE) argc = BUFSIZE; for (i=0; i=BUFSIZE?(BUFSIZE-1):i] = 0; fwrite(buf, 1, argc, stdout); if (x->x_flush || !argc) fflush(stdout); } static void pd_tilde_putfloat(float f, FILE *fd) { putc(A_FLOAT, fd); fwrite(&f, sizeof(f), 1, fd); } static void pd_tilde_putsymbol(t_symbol *s, FILE *fd) { const char *sp = s->s_name; putc(A_SYMBOL, fd); do putc(*sp, fd); while (*sp++); } static void stdout_anything(t_stdout *x, t_symbol *s, int argc, t_atom *argv) { char msgbuf[MAXPDSTRING], *sp, *ep = msgbuf+MAXPDSTRING; if (x->x_mode == MODE_BIN) { if ((gensym("list") == s) || (gensym("float") == s) || (gensym("bang") == s)) stdout_binary(x, argc, argv); else pd_error(x, "stdout: only 'list' messages allowed in binary mode (got '%s')", s->s_name); return; } else if (x->x_mode == MODE_PDTILDE) { pd_tilde_putsymbol(s, stdout); for (; argc--; argv++) { if (argv->a_type == A_FLOAT) pd_tilde_putfloat(argv->a_w.w_float, stdout); else if (argv->a_type == A_SYMBOL) pd_tilde_putsymbol(argv->a_w.w_symbol, stdout); } putc(A_SEMI, stdout); if (x->x_flush) fflush(stdout); return; } msgbuf[0] = 0; strncpy(msgbuf, s->s_name, MAXPDSTRING); msgbuf[MAXPDSTRING-1] = 0; sp = msgbuf + strlen(msgbuf); while (argc--) { if (sp < ep-1) sp[0] = ' ', sp[1] = 0, sp++; atom_string(argv++, sp, (unsigned int)(ep-sp)); sp += strlen(sp); } switch(x->x_mode) { case MODE_CR: printf("%s\n", msgbuf); break; default: printf("%s;\n", msgbuf); } if (x->x_flush) fflush(stdout); } static void stdout_free(t_stdout *x) { fflush(stdout); } void stdout_setup(void) { stdout_class = class_new(gensym("stdout"), (t_newmethod)stdout_new, (t_method)stdout_free, sizeof(t_stdout), 0, A_GIMME, 0); class_addanything(stdout_class, stdout_anything); } ================================================ FILE: libs/libpd/pure-data/src/d_arithmetic.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* arithmetic binops (+, -, *, /). If no creation argument is given, there are two signal inlets for vector/vector operation; otherwise it's vector/scalar and the second inlet takes a float to reset the value. */ #include "m_pd.h" #include /* needed for log~ */ /* -------------- convenience routines for multichannel binops ----- */ /* add a binary operation (such as "+") to the DSP chain, in which inputs may be of different sizes (but the output should have the same size as the larger input). Whichever is shorter gets re-used as many times as necessary to match the longer one; so one can add signals with different numbers of channels. Two functions are passed, a general one and another that is called if the block size is a multiple of 8. */ static void dsp_add_multi(t_sample *vec1, int n1, t_sample *vec2, int n2, t_sample *outvec, t_perfroutine func, t_perfroutine func8) { int i; if (n1 > n2) for (i = (n1+n2-1)/n2; i--; ) { t_int blocksize = (n2 < n1 - i*n2 ? n2 : n1 - i*n2); dsp_add(((blocksize & 7) || !func8 ? func : func8), 4, vec1 + i * n2, vec2, outvec + i * n2, blocksize); } else for (i = (n1+n2-1)/n1; i--; ) { t_int blocksize = (n1 < n2 - i*n1 ? n1 : n2 - i*n1); dsp_add(((blocksize & 7) || !func8 ? func : func8), 4, vec1, vec2 + i*n1, outvec + i*n1, blocksize); } } /* more generally, add a binary operation to the DSP chain, that may be vector or scalar in either of its two inputs. The caller supplies three versions: vector-vector, vector-scalar, and scalar-vector. In the scalar-vector case the "perf" routine gets the vector argument first (on the DSP chain) - this way, if the operation is commutative the same function can be supplied for perf_vs and perf_vs_reverse. If perf_vv8, etc, are nonzero, they are called if the vector size is a multiple of 8. */ static void any_binop_dsp(t_signal **sp, t_perfroutine perf_vv, t_perfroutine perf_vv8, t_perfroutine perf_vs, t_perfroutine perf_vs8, t_perfroutine perf_vs_reverse, t_perfroutine perf_vs8_reverse) { int bign0 = sp[0]->s_length * sp[0]->s_nchans, bign1 = sp[1]->s_length * sp[1]->s_nchans, outchans; if (bign1 > bign0) outchans = sp[1]->s_nchans; else if (bign0 > 1) outchans = sp[0]->s_nchans; else outchans = 1; signal_setmultiout(&sp[2], outchans); if (bign0 > 1) /* first input is a vector */ { if (bign1 > 1) /* general case: both inputs are vectors */ dsp_add_multi(sp[0]->s_vec, bign0, sp[1]->s_vec, bign1, sp[2]->s_vec, perf_vv, perf_vv8); /* add a scalar to a vector */ else dsp_add(((bign0 & 7) || !perf_vs8 ? perf_vs : perf_vs8), 4, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, (t_int)bign0); } else /* first input is scalar */ { if (bign1 > 1) /* second input is a vector: use reverse scalar version */ dsp_add(((bign0 & 7) || !perf_vs8_reverse ? perf_vs_reverse : perf_vs8_reverse), 4, sp[1]->s_vec, sp[0]->s_vec, sp[2]->s_vec, (t_int)bign1); else { /* operate on two scalars to make a vector, needing two ops */ dsp_add(perf_vs, 4, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, (t_int)1); dsp_add_scalarcopy(sp[2]->s_vec, sp[2]->s_vec, (t_int)sp[2]->s_length); } } } /* vector-scalar version (as in "+~ 1") - here we don't deal with scalar left inputs - the class may not be declared CLASS_NOPROMOTELEFT. */ static void any_binop_scalar_dsp(t_signal **sp, t_sample *g, t_perfroutine perf, t_perfroutine perf8) { t_int bign = sp[0]->s_length * sp[0]->s_nchans; signal_setmultiout(&sp[1], sp[0]->s_nchans); dsp_add(((bign & 7) || !perf8 ? perf : perf8), 4, sp[0]->s_vec, g, sp[1]->s_vec, bign); } /* ----------------------------- plus ----------------------------- */ static t_class *plus_class, *scalarplus_class; typedef struct _plus { t_object x_obj; t_float x_f; } t_plus; typedef struct _scalarplus { t_object x_obj; t_float x_f; t_float x_g; /* inlet value */ } t_scalarplus; static void *plus_new(t_symbol *s, int argc, t_atom *argv) { if (argc > 1) post("+~: extra arguments ignored"); if (argc) /* argument implies we'll do a scalar add as in "+~ 1" */ { t_scalarplus *x = (t_scalarplus *)pd_new(scalarplus_class); floatinlet_new(&x->x_obj, &x->x_g); x->x_g = atom_getfloatarg(0, argc, argv); outlet_new(&x->x_obj, &s_signal); x->x_f = 0; return (x); } else { t_plus *x = (t_plus *)pd_new(plus_class); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); outlet_new(&x->x_obj, &s_signal); x->x_f = 0; return (x); } } t_int *scalarplus_perform(t_int *w) { t_sample *in = (t_sample *)(w[1]); t_float f = *(t_float *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); while (n--) *out++ = *in++ + f; return (w+5); } t_int *scalarplus_perf8(t_int *w) { t_sample *in = (t_sample *)(w[1]); t_float g = *(t_float *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); for (; n; n -= 8, in += 8, out += 8) { t_sample f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3]; t_sample f4 = in[4], f5 = in[5], f6 = in[6], f7 = in[7]; out[0] = f0 + g; out[1] = f1 + g; out[2] = f2 + g; out[3] = f3 + g; out[4] = f4 + g; out[5] = f5 + g; out[6] = f6 + g; out[7] = f7 + g; } return (w+5); } static void plus_dsp(t_plus *x, t_signal **sp) { any_binop_dsp(sp, plus_perform, plus_perf8, scalarplus_perform, scalarplus_perf8, scalarplus_perform, scalarplus_perf8); } static void scalarplus_dsp(t_scalarplus *x, t_signal **sp) { any_binop_scalar_dsp(sp, &x->x_g, scalarplus_perform, scalarplus_perf8); } static void plus_setup(void) { plus_class = class_new(gensym("+~"), (t_newmethod)plus_new, 0, sizeof(t_plus), CLASS_MULTICHANNEL | CLASS_NOPROMOTESIG | CLASS_NOPROMOTELEFT, A_GIMME, 0); class_addmethod(plus_class, (t_method)plus_dsp, gensym("dsp"), A_CANT, 0); CLASS_MAINSIGNALIN(plus_class, t_plus, x_f); class_sethelpsymbol(plus_class, gensym("binops-tilde")); scalarplus_class = class_new(gensym("+~"), 0, 0, sizeof(t_scalarplus), CLASS_MULTICHANNEL, 0); CLASS_MAINSIGNALIN(scalarplus_class, t_scalarplus, x_f); class_addmethod(scalarplus_class, (t_method)scalarplus_dsp, gensym("dsp"), A_CANT, 0); class_sethelpsymbol(scalarplus_class, gensym("binops-tilde")); } /* ----------------------------- minus ----------------------------- */ static t_class *minus_class, *scalarminus_class; typedef struct _minus { t_object x_obj; t_float x_f; } t_minus; typedef struct _scalarminus { t_object x_obj; t_float x_f; t_float x_g; } t_scalarminus; static void *minus_new(t_symbol *s, int argc, t_atom *argv) { if (argc > 1) post("-~: extra arguments ignored"); if (argc) { t_scalarminus *x = (t_scalarminus *)pd_new(scalarminus_class); floatinlet_new(&x->x_obj, &x->x_g); x->x_g = atom_getfloatarg(0, argc, argv); outlet_new(&x->x_obj, &s_signal); x->x_f = 0; return (x); } else { t_minus *x = (t_minus *)pd_new(minus_class); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); outlet_new(&x->x_obj, &s_signal); x->x_f = 0; return (x); } } t_int *minus_perform(t_int *w) { t_sample *in1 = (t_sample *)(w[1]); t_sample *in2 = (t_sample *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); while (n--) *out++ = *in1++ - *in2++; return (w+5); } t_int *minus_perf8(t_int *w) { t_sample *in1 = (t_sample *)(w[1]); t_sample *in2 = (t_sample *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); for (; n; n -= 8, in1 += 8, in2 += 8, out += 8) { t_sample f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3]; t_sample f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7]; t_sample g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3]; t_sample g4 = in2[4], g5 = in2[5], g6 = in2[6], g7 = in2[7]; out[0] = f0 - g0; out[1] = f1 - g1; out[2] = f2 - g2; out[3] = f3 - g3; out[4] = f4 - g4; out[5] = f5 - g5; out[6] = f6 - g6; out[7] = f7 - g7; } return (w+5); } t_int *scalarminus_perform(t_int *w) { t_sample *in = (t_sample *)(w[1]); t_float f = *(t_float *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); while (n--) *out++ = *in++ - f; return (w+5); } t_int *scalarminus_perf8(t_int *w) { t_sample *in = (t_sample *)(w[1]); t_float g = *(t_float *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); for (; n; n -= 8, in += 8, out += 8) { t_sample f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3]; t_sample f4 = in[4], f5 = in[5], f6 = in[6], f7 = in[7]; out[0] = f0 - g; out[1] = f1 - g; out[2] = f2 - g; out[3] = f3 - g; out[4] = f4 - g; out[5] = f5 - g; out[6] = f6 - g; out[7] = f7 - g; } return (w+5); } t_int *reversescalarminus_perform(t_int *w) { t_sample *in = (t_sample *)(w[1]); t_float f = *(t_float *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); while (n--) *out++ = f - *in++; return (w+5); } t_int *reversescalarminus_perf8(t_int *w) { t_sample *in = (t_sample *)(w[1]); t_float g = *(t_float *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); for (; n; n -= 8, in += 8, out += 8) { t_sample f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3]; t_sample f4 = in[4], f5 = in[5], f6 = in[6], f7 = in[7]; out[0] = g - f0; out[1] = g - f1; out[2] = g - f2; out[3] = g - f3; out[4] = g - f4; out[5] = g - f5; out[6] = g - f6; out[7] = g - f7; } return (w+5); } static void minus_dsp(t_minus *x, t_signal **sp) { any_binop_dsp(sp, minus_perform, minus_perf8, scalarminus_perform, scalarminus_perf8, reversescalarminus_perform, reversescalarminus_perf8); } static void scalarminus_dsp(t_scalarminus *x, t_signal **sp) { any_binop_scalar_dsp(sp, &x->x_g, scalarminus_perform, scalarminus_perf8); } static void minus_setup(void) { minus_class = class_new(gensym("-~"), (t_newmethod)minus_new, 0, sizeof(t_minus), CLASS_MULTICHANNEL | CLASS_NOPROMOTESIG | CLASS_NOPROMOTELEFT, A_GIMME, 0); CLASS_MAINSIGNALIN(minus_class, t_minus, x_f); class_addmethod(minus_class, (t_method)minus_dsp, gensym("dsp"), A_CANT, 0); class_sethelpsymbol(minus_class, gensym("binops-tilde")); scalarminus_class = class_new(gensym("-~"), 0, 0, sizeof(t_scalarminus), CLASS_MULTICHANNEL, 0); CLASS_MAINSIGNALIN(scalarminus_class, t_scalarminus, x_f); class_addmethod(scalarminus_class, (t_method)scalarminus_dsp, gensym("dsp"), A_CANT, 0); class_sethelpsymbol(scalarminus_class, gensym("binops-tilde")); } /* ----------------------------- times ----------------------------- */ static t_class *times_class, *scalartimes_class; typedef struct _times { t_object x_obj; t_float x_f; } t_times; typedef struct _scalartimes { t_object x_obj; t_float x_f; t_float x_g; } t_scalartimes; static void *times_new(t_symbol *s, int argc, t_atom *argv) { if (argc > 1) post("*~: extra arguments ignored"); if (argc) { t_scalartimes *x = (t_scalartimes *)pd_new(scalartimes_class); floatinlet_new(&x->x_obj, &x->x_g); x->x_g = atom_getfloatarg(0, argc, argv); outlet_new(&x->x_obj, &s_signal); x->x_f = 0; return (x); } else { t_times *x = (t_times *)pd_new(times_class); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); outlet_new(&x->x_obj, &s_signal); x->x_f = 0; return (x); } } t_int *times_perform(t_int *w) { t_sample *in1 = (t_sample *)(w[1]); t_sample *in2 = (t_sample *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); while (n--) *out++ = *in1++ * *in2++; return (w+5); } t_int *times_perf8(t_int *w) { t_sample *in1 = (t_sample *)(w[1]); t_sample *in2 = (t_sample *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); for (; n; n -= 8, in1 += 8, in2 += 8, out += 8) { t_sample f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3]; t_sample f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7]; t_sample g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3]; t_sample g4 = in2[4], g5 = in2[5], g6 = in2[6], g7 = in2[7]; out[0] = f0 * g0; out[1] = f1 * g1; out[2] = f2 * g2; out[3] = f3 * g3; out[4] = f4 * g4; out[5] = f5 * g5; out[6] = f6 * g6; out[7] = f7 * g7; } return (w+5); } t_int *scalartimes_perform(t_int *w) { t_sample *in = (t_sample *)(w[1]); t_float f = *(t_float *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); while (n--) *out++ = *in++ * f; return (w+5); } t_int *scalartimes_perf8(t_int *w) { t_sample *in = (t_sample *)(w[1]); t_float g = *(t_float *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); for (; n; n -= 8, in += 8, out += 8) { t_sample f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3]; t_sample f4 = in[4], f5 = in[5], f6 = in[6], f7 = in[7]; out[0] = f0 * g; out[1] = f1 * g; out[2] = f2 * g; out[3] = f3 * g; out[4] = f4 * g; out[5] = f5 * g; out[6] = f6 * g; out[7] = f7 * g; } return (w+5); } static void times_dsp(t_times *x, t_signal **sp) { any_binop_dsp(sp, times_perform, times_perf8, scalartimes_perform, scalartimes_perf8, scalartimes_perform, scalartimes_perf8); } static void scalartimes_dsp(t_scalartimes *x, t_signal **sp) { any_binop_scalar_dsp(sp, &x->x_g, scalartimes_perform, scalartimes_perf8); } static void times_setup(void) { times_class = class_new(gensym("*~"), (t_newmethod)times_new, 0, sizeof(t_times), CLASS_MULTICHANNEL | CLASS_NOPROMOTESIG | CLASS_NOPROMOTELEFT, A_GIMME, 0); CLASS_MAINSIGNALIN(times_class, t_times, x_f); class_addmethod(times_class, (t_method)times_dsp, gensym("dsp"), A_CANT, 0); class_sethelpsymbol(times_class, gensym("binops-tilde")); scalartimes_class = class_new(gensym("*~"), 0, 0, sizeof(t_scalartimes), CLASS_MULTICHANNEL, 0); CLASS_MAINSIGNALIN(scalartimes_class, t_scalartimes, x_f); class_addmethod(scalartimes_class, (t_method)scalartimes_dsp, gensym("dsp"), A_CANT, 0); class_sethelpsymbol(scalartimes_class, gensym("binops-tilde")); } /* ----------------------------- over ----------------------------- */ static t_class *over_class, *scalarover_class; typedef struct _over { t_object x_obj; t_float x_f; } t_over; typedef struct _scalarover { t_object x_obj; t_float x_f; t_float x_g; } t_scalarover; static void *over_new(t_symbol *s, int argc, t_atom *argv) { if (argc > 1) post("/~: extra arguments ignored"); if (argc) { t_scalarover *x = (t_scalarover *)pd_new(scalarover_class); floatinlet_new(&x->x_obj, &x->x_g); x->x_g = atom_getfloatarg(0, argc, argv); outlet_new(&x->x_obj, &s_signal); x->x_f = 0; return (x); } else { t_over *x = (t_over *)pd_new(over_class); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); outlet_new(&x->x_obj, &s_signal); x->x_f = 0; return (x); } } t_int *over_perform(t_int *w) { t_sample *in1 = (t_sample *)(w[1]); t_sample *in2 = (t_sample *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); while (n--) { t_sample f = *in1++, g = *in2++; *out++ = (g ? f / g : 0); } return (w+5); } t_int *over_perf8(t_int *w) { t_sample *in1 = (t_sample *)(w[1]); t_sample *in2 = (t_sample *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); for (; n; n -= 8, in1 += 8, in2 += 8, out += 8) { t_sample f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3]; t_sample f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7]; t_sample g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3]; t_sample g4 = in2[4], g5 = in2[5], g6 = in2[6], g7 = in2[7]; out[0] = (g0? f0 / g0 : 0); out[1] = (g1? f1 / g1 : 0); out[2] = (g2? f2 / g2 : 0); out[3] = (g3? f3 / g3 : 0); out[4] = (g4? f4 / g4 : 0); out[5] = (g5? f5 / g5 : 0); out[6] = (g6? f6 / g6 : 0); out[7] = (g7? f7 / g7 : 0); } return (w+5); } t_int *scalarover_perform(t_int *w) { t_sample *in = (t_sample *)(w[1]); t_float f = *(t_float *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); if(f) f = 1./f; while (n--) *out++ = *in++ * f; return (w+5); } t_int *scalarover_perf8(t_int *w) { t_sample *in = (t_sample *)(w[1]); t_float g = *(t_float *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); if (g) g = 1.f / g; for (; n; n -= 8, in += 8, out += 8) { t_sample f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3]; t_sample f4 = in[4], f5 = in[5], f6 = in[6], f7 = in[7]; out[0] = f0 * g; out[1] = f1 * g; out[2] = f2 * g; out[3] = f3 * g; out[4] = f4 * g; out[5] = f5 * g; out[6] = f6 * g; out[7] = f7 * g; } return (w+5); } t_int *reversescalarover_perform(t_int *w) { t_sample *in = (t_sample *)(w[1]); t_float f = *(t_float *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); while (n--) { t_sample g = *in++; *out++ = (g != 0 ? f/g : 0); } return (w+5); } t_int *reversescalarover_perf8(t_int *w) { t_sample *in = (t_sample *)(w[1]); t_float g = *(t_float *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); for (; n; n -= 8, in += 8, out += 8) { t_sample f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3]; t_sample f4 = in[4], f5 = in[5], f6 = in[6], f7 = in[7]; out[0] = (f0 != 0 ? g/f0 : 0); out[1] = (f1 != 0 ? g/f1 : 0); out[2] = (f2 != 0 ? g/f2 : 0); out[3] = (f3 != 0 ? g/f3 : 0); out[4] = (f4 != 0 ? g/f4 : 0); out[5] = (f5 != 0 ? g/f5 : 0); out[6] = (f6 != 0 ? g/f6 : 0); out[7] = (f7 != 0 ? g/f7 : 0); } return (w+5); } static void over_dsp(t_over *x, t_signal **sp) { any_binop_dsp(sp, over_perform, over_perf8, scalarover_perform, scalarover_perf8, reversescalarover_perform, reversescalarover_perf8); } static void scalarover_dsp(t_scalarover *x, t_signal **sp) { any_binop_scalar_dsp(sp, &x->x_g, scalarover_perform, scalarover_perf8); } static void over_setup(void) { over_class = class_new(gensym("/~"), (t_newmethod)over_new, 0, sizeof(t_over), CLASS_MULTICHANNEL | CLASS_NOPROMOTESIG | CLASS_NOPROMOTELEFT, A_GIMME, 0); CLASS_MAINSIGNALIN(over_class, t_over, x_f); class_addmethod(over_class, (t_method)over_dsp, gensym("dsp"), A_CANT, 0); class_sethelpsymbol(over_class, gensym("binops-tilde")); scalarover_class = class_new(gensym("/~"), 0, 0, sizeof(t_scalarover), CLASS_MULTICHANNEL, 0); CLASS_MAINSIGNALIN(scalarover_class, t_scalarover, x_f); class_addmethod(scalarover_class, (t_method)scalarover_dsp, gensym("dsp"), A_CANT, 0); class_sethelpsymbol(scalarover_class, gensym("binops-tilde")); } /* ----------------------------- max ----------------------------- */ static t_class *max_class, *scalarmax_class; typedef struct _max { t_object x_obj; t_float x_f; } t_max; typedef struct _scalarmax { t_object x_obj; t_float x_f; t_float x_g; } t_scalarmax; static void *max_new(t_symbol *s, int argc, t_atom *argv) { if (argc > 1) post("max~: extra arguments ignored"); if (argc) { t_scalarmax *x = (t_scalarmax *)pd_new(scalarmax_class); floatinlet_new(&x->x_obj, &x->x_g); x->x_g = atom_getfloatarg(0, argc, argv); outlet_new(&x->x_obj, &s_signal); x->x_f = 0; return (x); } else { t_max *x = (t_max *)pd_new(max_class); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); outlet_new(&x->x_obj, &s_signal); x->x_f = 0; return (x); } } t_int *max_perform(t_int *w) { t_sample *in1 = (t_sample *)(w[1]); t_sample *in2 = (t_sample *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); while (n--) { t_sample f = *in1++, g = *in2++; *out++ = (f > g ? f : g); } return (w+5); } t_int *max_perf8(t_int *w) { t_sample *in1 = (t_sample *)(w[1]); t_sample *in2 = (t_sample *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); for (; n; n -= 8, in1 += 8, in2 += 8, out += 8) { t_sample f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3]; t_sample f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7]; t_sample g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3]; t_sample g4 = in2[4], g5 = in2[5], g6 = in2[6], g7 = in2[7]; out[0] = (f0 > g0 ? f0 : g0); out[1] = (f1 > g1 ? f1 : g1); out[2] = (f2 > g2 ? f2 : g2); out[3] = (f3 > g3 ? f3 : g3); out[4] = (f4 > g4 ? f4 : g4); out[5] = (f5 > g5 ? f5 : g5); out[6] = (f6 > g6 ? f6 : g6); out[7] = (f7 > g7 ? f7 : g7); } return (w+5); } t_int *scalarmax_perform(t_int *w) { t_sample *in = (t_sample *)(w[1]); t_float f = *(t_float *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); while (n--) { t_sample g = *in++; *out++ = (f > g ? f : g); } return (w+5); } t_int *scalarmax_perf8(t_int *w) { t_sample *in = (t_sample *)(w[1]); t_float g = *(t_float *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); for (; n; n -= 8, in += 8, out += 8) { t_sample f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3]; t_sample f4 = in[4], f5 = in[5], f6 = in[6], f7 = in[7]; out[0] = (f0 > g ? f0 : g); out[1] = (f1 > g ? f1 : g); out[2] = (f2 > g ? f2 : g); out[3] = (f3 > g ? f3 : g); out[4] = (f4 > g ? f4 : g); out[5] = (f5 > g ? f5 : g); out[6] = (f6 > g ? f6 : g); out[7] = (f7 > g ? f7 : g); } return (w+5); } static void max_dsp(t_max *x, t_signal **sp) { any_binop_dsp(sp, max_perform, max_perf8, scalarmax_perform, scalarmax_perf8, scalarmax_perform, scalarmax_perf8); } static void scalarmax_dsp(t_scalarmax *x, t_signal **sp) { any_binop_scalar_dsp(sp, &x->x_g, scalarmax_perform, scalarmax_perf8); } static void max_setup(void) { max_class = class_new(gensym("max~"), (t_newmethod)max_new, 0, sizeof(t_max), CLASS_MULTICHANNEL | CLASS_NOPROMOTESIG | CLASS_NOPROMOTELEFT, A_GIMME, 0); CLASS_MAINSIGNALIN(max_class, t_max, x_f); class_addmethod(max_class, (t_method)max_dsp, gensym("dsp"), A_CANT, 0); class_sethelpsymbol(max_class, gensym("binops-tilde")); scalarmax_class = class_new(gensym("max~"), 0, 0, sizeof(t_scalarmax), CLASS_MULTICHANNEL, 0); CLASS_MAINSIGNALIN(scalarmax_class, t_scalarmax, x_f); class_addmethod(scalarmax_class, (t_method)scalarmax_dsp, gensym("dsp"), A_CANT, 0); class_sethelpsymbol(scalarmax_class, gensym("binops-tilde")); } /* ----------------------------- min ----------------------------- */ static t_class *min_class, *scalarmin_class; typedef struct _min { t_object x_obj; t_float x_f; } t_min; typedef struct _scalarmin { t_object x_obj; t_float x_g; t_float x_f; } t_scalarmin; static void *min_new(t_symbol *s, int argc, t_atom *argv) { if (argc > 1) post("min~: extra arguments ignored"); if (argc) { t_scalarmin *x = (t_scalarmin *)pd_new(scalarmin_class); floatinlet_new(&x->x_obj, &x->x_g); x->x_g = atom_getfloatarg(0, argc, argv); outlet_new(&x->x_obj, &s_signal); x->x_f = 0; return (x); } else { t_min *x = (t_min *)pd_new(min_class); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); outlet_new(&x->x_obj, &s_signal); x->x_f = 0; return (x); } } t_int *min_perform(t_int *w) { t_sample *in1 = (t_sample *)(w[1]); t_sample *in2 = (t_sample *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); while (n--) { t_sample f = *in1++, g = *in2++; *out++ = (f < g ? f : g); } return (w+5); } t_int *min_perf8(t_int *w) { t_sample *in1 = (t_sample *)(w[1]); t_sample *in2 = (t_sample *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); for (; n; n -= 8, in1 += 8, in2 += 8, out += 8) { t_sample f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3]; t_sample f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7]; t_sample g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3]; t_sample g4 = in2[4], g5 = in2[5], g6 = in2[6], g7 = in2[7]; out[0] = (f0 < g0 ? f0 : g0); out[1] = (f1 < g1 ? f1 : g1); out[2] = (f2 < g2 ? f2 : g2); out[3] = (f3 < g3 ? f3 : g3); out[4] = (f4 < g4 ? f4 : g4); out[5] = (f5 < g5 ? f5 : g5); out[6] = (f6 < g6 ? f6 : g6); out[7] = (f7 < g7 ? f7 : g7); } return (w+5); } t_int *scalarmin_perform(t_int *w) { t_sample *in = (t_sample *)(w[1]); t_float f = *(t_float *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); while (n--) { t_sample g = *in++; *out++ = (f < g ? f : g); } return (w+5); } t_int *scalarmin_perf8(t_int *w) { t_sample *in = (t_sample *)(w[1]); t_float g = *(t_float *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); for (; n; n -= 8, in += 8, out += 8) { t_sample f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3]; t_sample f4 = in[4], f5 = in[5], f6 = in[6], f7 = in[7]; out[0] = (f0 < g ? f0 : g); out[1] = (f1 < g ? f1 : g); out[2] = (f2 < g ? f2 : g); out[3] = (f3 < g ? f3 : g); out[4] = (f4 < g ? f4 : g); out[5] = (f5 < g ? f5 : g); out[6] = (f6 < g ? f6 : g); out[7] = (f7 < g ? f7 : g); } return (w+5); } static void min_dsp(t_min *x, t_signal **sp) { any_binop_dsp(sp, min_perform, min_perf8, scalarmin_perform, scalarmin_perf8, scalarmin_perform, scalarmin_perf8); } static void scalarmin_dsp(t_scalarmin *x, t_signal **sp) { any_binop_scalar_dsp(sp, &x->x_g, scalarmin_perform, scalarmin_perf8); } static void min_setup(void) { min_class = class_new(gensym("min~"), (t_newmethod)min_new, 0, sizeof(t_min), CLASS_MULTICHANNEL | CLASS_NOPROMOTESIG | CLASS_NOPROMOTELEFT, A_GIMME, 0); CLASS_MAINSIGNALIN(min_class, t_min, x_f); class_addmethod(min_class, (t_method)min_dsp, gensym("dsp"), A_CANT, 0); class_sethelpsymbol(min_class, gensym("binops-tilde")); scalarmin_class = class_new(gensym("min~"), 0, 0, sizeof(t_scalarmin), CLASS_MULTICHANNEL, 0); CLASS_MAINSIGNALIN(scalarmin_class, t_scalarmin, x_f); class_addmethod(scalarmin_class, (t_method)scalarmin_dsp, gensym("dsp"), A_CANT, 0); class_sethelpsymbol(scalarmin_class, gensym("binops-tilde")); } /* ----------------------------- log ----------------------------- */ static t_class *log_tilde_class, *scalarlog_tilde_class; typedef struct _log_tilde { t_object x_obj; t_float x_f; } t_log_tilde; typedef struct _scalarlog_tilde { t_object x_obj; t_float x_f; t_float x_g; } t_scalarlog_tilde; static void *log_tilde_new(t_symbol *s, int argc, t_atom *argv) { if (argc > 1) post("log~: extra arguments ignored"); if (argc) { t_scalarlog_tilde *x = (t_scalarlog_tilde *)pd_new(scalarlog_tilde_class); floatinlet_new(&x->x_obj, &x->x_g); x->x_g = atom_getfloatarg(0, argc, argv); outlet_new(&x->x_obj, &s_signal); x->x_f = 0; return (x); } else { t_log_tilde *x = (t_log_tilde *)pd_new(log_tilde_class); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); outlet_new(&x->x_obj, &s_signal); x->x_f = 0; return (x); } } t_int *log_tilde_perform(t_int *w) { t_sample *in1 = (t_sample *)(w[1]); t_sample *in2 = (t_sample *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); while (n--) { t_sample f = *in1++, g = *in2++; if (f <= 0) *out = -1000; /* rather than blow up, output a number << 0 */ else if (g == 1 || g <= 0) *out = log(f); else *out = log(f)/log(g); out++; } return (w+5); } t_int *log_tilde_perform_scalar(t_int *w) { t_sample *in1 = (t_sample *)(w[1]); t_sample base = *(t_sample *)(w[2]), multiplier = ((base > 0 && base != 1) ? 1./log(base) : 1); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); while (n--) { t_sample f = *in1++; if (f <= 0) *out = -1000; /* rather than blow up, output a number << 0 */ else *out = log(f) * multiplier; out++; } return (w+5); } /* nobody sane will ever ask for log(scalar) to a signal base but ok... */ t_int *log_tilde_perform_reversescalar(t_int *w) { t_sample *in1 = (t_sample *)(w[1]); t_sample scalarin = *(t_sample *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); while (n--) { t_sample base = *in1++; if (base <= 1) *out = -1000; /* rather than blow up, output a number << 0 */ else if (scalarin <- 0) *out = -1000; else *out = log(scalarin) / log(base); out++; } return (w+5); } static void log_tilde_dsp(t_log_tilde *x, t_signal **sp) { any_binop_dsp(sp, log_tilde_perform, log_tilde_perform, log_tilde_perform_scalar, log_tilde_perform_scalar, log_tilde_perform_reversescalar, log_tilde_perform_reversescalar); } static void scalarlog_tilde_dsp(t_scalarlog_tilde *x, t_signal **sp) { any_binop_scalar_dsp(sp, &x->x_g, log_tilde_perform_scalar, log_tilde_perform_scalar); } static void log_tilde_setup(void) { log_tilde_class = class_new(gensym("log~"), (t_newmethod)log_tilde_new, 0, sizeof(t_log_tilde), CLASS_MULTICHANNEL | CLASS_NOPROMOTESIG | CLASS_NOPROMOTELEFT, A_GIMME, 0); CLASS_MAINSIGNALIN(log_tilde_class, t_log_tilde, x_f); class_addmethod(log_tilde_class, (t_method)log_tilde_dsp, gensym("dsp"), A_CANT, 0); class_sethelpsymbol(log_tilde_class, gensym("binops-tilde")); scalarlog_tilde_class = class_new(gensym("log~"), 0, 0, sizeof(t_scalarlog_tilde), CLASS_MULTICHANNEL, 0); CLASS_MAINSIGNALIN(scalarlog_tilde_class, t_scalarlog_tilde, x_f); class_addmethod(scalarlog_tilde_class, (t_method)scalarlog_tilde_dsp, gensym("dsp"), A_CANT, 0); class_sethelpsymbol(scalarlog_tilde_class, gensym("binops-tilde")); } /* ----------------------------- pow ----------------------------- */ static t_class *pow_tilde_class, *scalarpow_tilde_class; typedef struct _pow_tilde { t_object x_obj; t_float x_f; } t_pow_tilde; typedef struct _scalarpow_tilde { t_object x_obj; t_float x_f; t_float x_g; } t_scalarpow_tilde; static void *pow_tilde_new(t_symbol *s, int argc, t_atom *argv) { if (argc > 1) post("pow~: extra arguments ignored"); if (argc) { t_scalarpow_tilde *x = (t_scalarpow_tilde *)pd_new(scalarpow_tilde_class); floatinlet_new(&x->x_obj, &x->x_g); x->x_g = atom_getfloatarg(0, argc, argv); outlet_new(&x->x_obj, &s_signal); x->x_f = 0; return (x); } else { t_pow_tilde *x = (t_pow_tilde *)pd_new(pow_tilde_class); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); outlet_new(&x->x_obj, &s_signal); x->x_f = 0; return (x); } } t_int *pow_tilde_perform(t_int *w) { t_sample *in1 = (t_sample *)(w[1]); t_sample *in2 = (t_sample *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); while (n--) { t_sample f = *in1++, g = *in2++; *out++ = (f == 0 && g < 0) || (f < 0 && (g - (int)g) != 0) ? 0 : pow(f, g); } return (w+5); } t_int *pow_tilde_perform_scalar(t_int *w) { t_sample *in1 = (t_sample *)(w[1]); t_sample g = *(t_sample *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); while (n--) { t_sample f = *in1++; *out++ = (f == 0 && g < 0) || (f < 0 && (g - (int)g) != 0) ? 0 : pow(f, g); } return (w+5); } t_int *pow_tilde_perform_reversescalar(t_int *w) { t_sample *in1 = (t_sample *)(w[1]); t_sample f = *(t_sample *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); while (n--) { t_sample g = *in1++; *out++ = (f == 0 && g < 0) || (f < 0 && (g - (int)g) != 0) ? 0 : pow(f, g); } return (w+5); } static void pow_tilde_dsp(t_pow_tilde *x, t_signal **sp) { any_binop_dsp(sp, pow_tilde_perform, pow_tilde_perform, pow_tilde_perform_scalar, pow_tilde_perform_scalar, pow_tilde_perform_reversescalar, pow_tilde_perform_reversescalar); } static void scalarpow_tilde_dsp(t_scalarpow_tilde *x, t_signal **sp) { any_binop_scalar_dsp(sp, &x->x_g, pow_tilde_perform_scalar, pow_tilde_perform_scalar); } static void pow_tilde_setup(void) { pow_tilde_class = class_new(gensym("pow~"), (t_newmethod)pow_tilde_new, 0, sizeof(t_pow_tilde), CLASS_MULTICHANNEL | CLASS_NOPROMOTESIG | CLASS_NOPROMOTELEFT, A_GIMME, 0); CLASS_MAINSIGNALIN(pow_tilde_class, t_pow_tilde, x_f); class_addmethod(pow_tilde_class, (t_method)pow_tilde_dsp, gensym("dsp"), A_CANT, 0); class_sethelpsymbol(pow_tilde_class, gensym("binops-tilde")); scalarpow_tilde_class = class_new(gensym("pow~"), 0, 0, sizeof(t_scalarpow_tilde), CLASS_MULTICHANNEL, 0); CLASS_MAINSIGNALIN(scalarpow_tilde_class, t_scalarpow_tilde, x_f); class_addmethod(scalarpow_tilde_class, (t_method)scalarpow_tilde_dsp, gensym("dsp"), A_CANT, 0); class_sethelpsymbol(scalarpow_tilde_class, gensym("binops-tilde")); } /* ----------------------- global setup routine ---------------- */ void d_arithmetic_setup(void) { plus_setup(); minus_setup(); times_setup(); over_setup(); max_setup(); min_setup(); log_tilde_setup(); pow_tilde_setup(); } ================================================ FILE: libs/libpd/pure-data/src/d_array.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette and others. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* reading and writing arrays */ /* LATER consider adding methods to set gpointer */ #include "m_pd.h" #include "g_canvas.h" /* common struct for reading or writing to an array at DSP time. */ typedef struct _dsparray { t_symbol *d_symbol; t_gpointer d_gp; int d_phase; /* used for tabwrite~ and tabplay~ */ void *d_owner; /* for pd_error() */ } t_dsparray; typedef struct _arrayvec { int v_n; t_dsparray *v_vec; } t_arrayvec; /* LATER consider exporting this and using it for tabosc4~ too */ static int dsparray_get_array(t_dsparray *d, int *npoints, t_word **vec, int recover) { t_garray *a; if (gpointer_check(&d->d_gp, 0)) { *vec = (t_word *)d->d_gp.gp_stub->gs_un.gs_array->a_vec; *npoints = d->d_gp.gp_stub->gs_un.gs_array->a_n; return 1; } else if (recover || d->d_gp.gp_stub) /* when the pointer is invalid: if "recover" is true or if the array has already been successfully acquired, then try re-acquiring it */ { if (!(a = (t_garray *)pd_findbyclass(d->d_symbol, garray_class))) { if (d->d_owner && *d->d_symbol->s_name) pd_error(d->d_owner, "%s: no such array", d->d_symbol->s_name); gpointer_unset(&d->d_gp); return 0; } else if (!garray_getfloatwords(a, npoints, vec)) { if (d->d_owner) pd_error(d->d_owner, "%s: bad template", d->d_symbol->s_name); gpointer_unset(&d->d_gp); return 0; } else { gpointer_setarray(&d->d_gp, garray_getarray(a), *vec); return 1; } } return 0; } static void arrayvec_testvec(t_arrayvec *v) { int i, vecsize; t_word *vec; for (i = 0; i < v->v_n; i++) { if (*v->v_vec[i].d_symbol->s_name) dsparray_get_array(&v->v_vec[i], &vecsize, &vec, 1); } } static void arrayvec_set(t_arrayvec *v, int argc, t_atom *argv) { int i; for (i = 0; i < v->v_n && i < argc; i++) { gpointer_unset(&v->v_vec[i].d_gp); /* reset the pointer */ if (argv[i].a_type != A_SYMBOL) pd_error(v->v_vec[i].d_owner, "expected symbolic array name, got number instead"), v->v_vec[i].d_symbol = &s_; else { v->v_vec[i].d_phase = 0x7fffffff; v->v_vec[i].d_symbol = argv[i].a_w.w_symbol; } } if (pd_getdspstate()) arrayvec_testvec(v); } static void arrayvec_init(t_arrayvec *v, void *x, int rawargc, t_atom *rawargv) { int i, argc; t_atom a, *argv; if (rawargc == 0) { argc = 1; SETSYMBOL(&a, &s_); argv = &a; } else argc = rawargc, argv=rawargv; v->v_vec = (t_dsparray *)getbytes(argc * sizeof(*v->v_vec)); v->v_n = argc; for (i = 0; i < v->v_n; i++) { v->v_vec[i].d_owner = x; v->v_vec[i].d_phase = 0x7fffffff; gpointer_init(&v->v_vec[i].d_gp); } arrayvec_set(v, argc, argv); } static void arrayvec_free(t_arrayvec *v) { int i; for (i = 0; i < v->v_n; i++) gpointer_unset(&v->v_vec[i].d_gp); freebytes(v->v_vec, v->v_n * sizeof(*v->v_vec)); } /* ------------------------- tabwrite~ -------------------------- */ static t_class *tabwrite_tilde_class; typedef struct _tabwrite_tilde { t_object x_obj; t_arrayvec x_v; t_float x_f; } t_tabwrite_tilde; static void *tabwrite_tilde_new(t_symbol *s, int argc, t_atom *argv) { t_tabwrite_tilde *x = (t_tabwrite_tilde *)pd_new(tabwrite_tilde_class); arrayvec_init(&x->x_v, x, argc, argv); x->x_f = 0; return (x); } static void tabwrite_tilde_redraw(t_symbol *arraysym) { t_garray *a = (t_garray *)pd_findbyclass(arraysym, garray_class); if (!a) bug("tabwrite_tilde_redraw"); else garray_redraw(a); } static t_int *tabwrite_tilde_perform(t_int *w) { t_dsparray *d = (t_dsparray *)(w[1]); t_sample *in = (t_sample *)(w[2]); int n = (int)(w[3]), phase = d->d_phase, endphase; t_word *buf; if (!dsparray_get_array(d, &endphase, &buf, 0)) goto noop; if (phase < endphase) { int nxfer = endphase - phase; t_word *wp = buf + phase; if (nxfer > n) nxfer = n; phase += nxfer; while (nxfer--) { t_sample f = *in++; if (PD_BIGORSMALL(f)) f = 0; (wp++)->w_float = f; } if (phase >= endphase) { tabwrite_tilde_redraw(d->d_symbol); phase = 0x7fffffff; } d->d_phase = phase; } else d->d_phase = 0x7fffffff; noop: return (w+4); } static void tabwrite_tilde_set(t_tabwrite_tilde *x, t_symbol *s, int argc, t_atom *argv) { arrayvec_set(&x->x_v, argc, argv); } static void tabwrite_tilde_dsp(t_tabwrite_tilde *x, t_signal **sp) { int i, nchans = (sp[0]->s_nchans < x->x_v.v_n ? sp[0]->s_nchans : x->x_v.v_n); arrayvec_testvec(&x->x_v); for (i = 0; i < nchans; i++) dsp_add(tabwrite_tilde_perform, 3, x->x_v.v_vec+i, sp[0]->s_vec + i * sp[0]->s_length, (t_int)sp[0]->s_length); } static void tabwrite_tilde_start(t_tabwrite_tilde *x, t_floatarg f) { int i; for (i = 0; i < x->x_v.v_n; i++) x->x_v.v_vec[i].d_phase = (f > 0 ? f : 0); } static void tabwrite_tilde_bang(t_tabwrite_tilde *x) { tabwrite_tilde_start(x, 0); } static void tabwrite_tilde_stop(t_tabwrite_tilde *x) { int i; for (i = 0; i < x->x_v.v_n; i++) if (x->x_v.v_vec[i].d_phase != 0x7fffffff) { tabwrite_tilde_redraw(x->x_v.v_vec[i].d_symbol); x->x_v.v_vec[i].d_phase = 0x7fffffff; } } static void tabwrite_tilde_free(t_tabwrite_tilde *x) { arrayvec_free(&x->x_v); } static void tabwrite_tilde_setup(void) { tabwrite_tilde_class = class_new(gensym("tabwrite~"), (t_newmethod)tabwrite_tilde_new, (t_method)tabwrite_tilde_free, sizeof(t_tabwrite_tilde), CLASS_MULTICHANNEL, A_GIMME, 0); CLASS_MAINSIGNALIN(tabwrite_tilde_class, t_tabwrite_tilde, x_f); class_addmethod(tabwrite_tilde_class, (t_method)tabwrite_tilde_dsp, gensym("dsp"), A_CANT, 0); class_addmethod(tabwrite_tilde_class, (t_method)tabwrite_tilde_set, gensym("set"), A_GIMME, 0); class_addmethod(tabwrite_tilde_class, (t_method)tabwrite_tilde_stop, gensym("stop"), 0); class_addmethod(tabwrite_tilde_class, (t_method)tabwrite_tilde_start, gensym("start"), A_DEFFLOAT, 0); class_addbang(tabwrite_tilde_class, tabwrite_tilde_bang); } /* ------------ tabplay~ - non-transposing sample playback --------------- */ static t_class *tabplay_tilde_class; typedef struct _tabplay_tilde { t_object x_obj; t_outlet *x_bangout; int x_limit; t_clock *x_clock; t_arrayvec x_v; } t_tabplay_tilde; static void tabplay_tilde_tick(t_tabplay_tilde *x); static void *tabplay_tilde_new(t_symbol *s, int argc, t_atom *argv) { t_tabplay_tilde *x = (t_tabplay_tilde *)pd_new(tabplay_tilde_class); x->x_clock = clock_new(x, (t_method)tabplay_tilde_tick); outlet_new(&x->x_obj, &s_signal); x->x_bangout = outlet_new(&x->x_obj, &s_bang); arrayvec_init(&x->x_v, x, argc, argv); x->x_limit = 0; return (x); } static t_int *tabplay_tilde_perform(t_int *w) { t_tabplay_tilde *x = (t_tabplay_tilde *)(w[1]); t_dsparray *d = (t_dsparray *)(w[2]); t_sample *out = (t_sample *)(w[3]); t_word *wp; int n = (int)(w[4]), phase = d->d_phase, endphase, nxfer, n3; t_word *buf; if (!dsparray_get_array(d, &endphase, &buf, 0) || phase >= endphase) goto zero; if (endphase > x->x_limit) endphase = x->x_limit; nxfer = endphase - phase; wp = buf + phase; if (nxfer > n) nxfer = n; n3 = n - nxfer; phase += nxfer; while (nxfer--) *out++ = (wp++)->w_float; if (phase >= endphase) { int i, playing = 0; d->d_phase = 0x7fffffff; /* set the clock when all channels have run out */ for (i = 0; i < x->x_v.v_n; i++) if (x->x_v.v_vec[i].d_phase < 0x7fffffff) playing = 1; if (!playing) clock_delay(x->x_clock, 0); while (n3--) *out++ = 0; } else d->d_phase = phase; return (w+5); zero: while (n--) *out++ = 0; return (w+5); } static void tabplay_tilde_set(t_tabplay_tilde *x, t_symbol *s, int argc, t_atom *argv) { arrayvec_set(&x->x_v, argc, argv); } static void tabplay_tilde_dsp(t_tabplay_tilde *x, t_signal **sp) { int i; signal_setmultiout(&sp[0], x->x_v.v_n); arrayvec_testvec(&x->x_v); for (i = 0; i < x->x_v.v_n; i++) dsp_add(tabplay_tilde_perform, 4, x, &x->x_v.v_vec[i], sp[0]->s_vec + i * sp[0]->s_length, (t_int)sp[0]->s_length); } static void tabplay_tilde_list(t_tabplay_tilde *x, t_symbol *s, int argc, t_atom *argv) { long start = atom_getfloatarg(0, argc, argv); long length = atom_getfloatarg(1, argc, argv); int i; if (start < 0) start = 0; if (length <= 0) x->x_limit = 0x7fffffff; else x->x_limit = (int)(start + length); for (i = 0; i < x->x_v.v_n; i++) x->x_v.v_vec[i].d_phase = (int)start; } static void tabplay_tilde_stop(t_tabplay_tilde *x) { int i; for (i = 0; i < x->x_v.v_n; i++) x->x_v.v_vec[i].d_phase = 0x7fffffff; } static void tabplay_tilde_tick(t_tabplay_tilde *x) { outlet_bang(x->x_bangout); } static void tabplay_tilde_free(t_tabplay_tilde *x) { clock_free(x->x_clock); arrayvec_free(&x->x_v); } static void tabplay_tilde_setup(void) { tabplay_tilde_class = class_new(gensym("tabplay~"), (t_newmethod)tabplay_tilde_new, (t_method)tabplay_tilde_free, sizeof(t_tabplay_tilde), CLASS_MULTICHANNEL, A_GIMME, 0); class_addmethod(tabplay_tilde_class, (t_method)tabplay_tilde_dsp, gensym("dsp"), A_CANT, 0); class_addmethod(tabplay_tilde_class, (t_method)tabplay_tilde_stop, gensym("stop"), 0); class_addmethod(tabplay_tilde_class, (t_method)tabplay_tilde_set, gensym("set"), A_GIMME, 0); class_addlist(tabplay_tilde_class, tabplay_tilde_list); } /******************** tabread~ ***********************/ static t_class *tabread_tilde_class; typedef struct _tabread_tilde { t_object x_obj; t_arrayvec x_v; t_float x_f; } t_tabread_tilde; static void *tabread_tilde_new(t_symbol *s, int argc, t_atom *argv) { t_tabread_tilde *x = (t_tabread_tilde *)pd_new(tabread_tilde_class); arrayvec_init(&x->x_v, x, argc, argv); outlet_new(&x->x_obj, gensym("signal")); x->x_f = 0; return (x); } static t_int *tabread_tilde_perform(t_int *w) { t_dsparray *d = (t_dsparray *)(w[1]); t_sample *in = (t_sample *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]), i, maxindex; t_word *buf; if (!dsparray_get_array(d, &maxindex, &buf, 0)) goto zero; maxindex -= 1; for (i = 0; i < n; i++) { int index = *in++; if (index < 0) index = 0; else if (index > maxindex) index = maxindex; *out++ = buf[index].w_float; } return (w+5); zero: while (n--) *out++ = 0; return (w+5); } static void tabread_tilde_set(t_tabread_tilde *x, t_symbol *s, int argc, t_atom *argv) { arrayvec_set(&x->x_v, argc, argv); } static void tabread_tilde_dsp(t_tabread_tilde *x, t_signal **sp) { int i; signal_setmultiout(&sp[1], x->x_v.v_n); arrayvec_testvec(&x->x_v); for (i = 0; i < x->x_v.v_n; i++) dsp_add(tabread_tilde_perform, 4, &x->x_v.v_vec[i], sp[0]->s_vec + (i%(sp[0]->s_nchans)) * sp[0]->s_length, sp[1]->s_vec + i * sp[0]->s_length, (t_int)sp[0]->s_length); } static void tabread_tilde_free(t_tabread_tilde *x) { arrayvec_free(&x->x_v); } static void tabread_tilde_setup(void) { tabread_tilde_class = class_new(gensym("tabread~"), (t_newmethod)tabread_tilde_new, (t_method)tabread_tilde_free, sizeof(t_tabread_tilde), CLASS_MULTICHANNEL, A_GIMME, 0); CLASS_MAINSIGNALIN(tabread_tilde_class, t_tabread_tilde, x_f); class_addmethod(tabread_tilde_class, (t_method)tabread_tilde_dsp, gensym("dsp"), A_CANT, 0); class_addmethod(tabread_tilde_class, (t_method)tabread_tilde_set, gensym("set"), A_GIMME, 0); } /******************** tabread4~ ***********************/ static t_class *tabread4_tilde_class; typedef struct _tabread4_tilde { t_object x_obj; t_arrayvec x_v; t_float x_f; t_float x_onset; } t_tabread4_tilde; static void *tabread4_tilde_new(t_symbol *s, int argc, t_atom *argv) { t_tabread4_tilde *x = (t_tabread4_tilde *)pd_new(tabread4_tilde_class); arrayvec_init(&x->x_v, x, argc, argv); outlet_new(&x->x_obj, gensym("signal")); floatinlet_new(&x->x_obj, &x->x_onset); x->x_f = x->x_onset = 0; return (x); } static t_int *tabread4_tilde_perform(t_int *w) { t_dsparray *d = (t_dsparray *)(w[1]); double onset = *(t_float *)(w[2]); t_sample *in = (t_sample *)(w[3]); t_sample *out = (t_sample *)(w[4]); int n = (int)(w[5]); int maxindex, i; t_word *buf, *wp; const t_sample one_over_six = 1./6.; if (!dsparray_get_array(d, &maxindex, &buf, 0)) goto zero; maxindex -= 3; if (maxindex < 0) goto zero; if (!buf || maxindex < 1) goto zero; for (i = 0; i < n; i++) { double findex = *in++ + onset; int index = findex; t_sample frac, a, b, c, d, cminusb; if (index < 1) index = 1, frac = 0; else if (index > maxindex) index = maxindex, frac = 1; else frac = findex - index; wp = buf + index; a = wp[-1].w_float; b = wp[0].w_float; c = wp[1].w_float; d = wp[2].w_float; cminusb = c-b; *out++ = b + frac * ( cminusb - one_over_six * ((t_sample)1.-frac) * ( (d - a - (t_sample)3.0 * cminusb) * frac + (d + a*(t_sample)2.0 - b*(t_sample)3.0) ) ); } return (w+6); zero: while (n--) *out++ = 0; return (w+6); } static void tabread4_tilde_set(t_tabread4_tilde *x, t_symbol *s, int argc, t_atom *argv) { arrayvec_set(&x->x_v, argc, argv); } static void tabread4_tilde_dsp(t_tabread4_tilde *x, t_signal **sp) { int i; signal_setmultiout(&sp[1], x->x_v.v_n); arrayvec_testvec(&x->x_v); for (i = 0; i < x->x_v.v_n; i++) dsp_add(tabread4_tilde_perform, 5, &x->x_v.v_vec[i], &x->x_onset, sp[0]->s_vec + (i%(sp[0]->s_nchans)) * sp[0]->s_length, sp[1]->s_vec + i * sp[0]->s_length, (t_int)sp[0]->s_length); } static void tabread4_tilde_free(t_tabread4_tilde *x) { arrayvec_free(&x->x_v); } static void tabread4_tilde_setup(void) { tabread4_tilde_class = class_new(gensym("tabread4~"), (t_newmethod)tabread4_tilde_new, (t_method)tabread4_tilde_free, sizeof(t_tabread4_tilde), CLASS_MULTICHANNEL, A_GIMME, 0); CLASS_MAINSIGNALIN(tabread4_tilde_class, t_tabread4_tilde, x_f); class_addmethod(tabread4_tilde_class, (t_method)tabread4_tilde_dsp, gensym("dsp"), A_CANT, 0); class_addmethod(tabread4_tilde_class, (t_method)tabread4_tilde_set, gensym("set"), A_GIMME, 0); } /* ------------------------ tabsend~ ------------------------- */ static t_class *tabsend_class; typedef struct _tabsend { t_object x_obj; t_arrayvec x_v; int x_graphperiod; t_float x_f; } t_tabsend; static void tabsend_tick(t_tabsend *x); static void *tabsend_new(t_symbol *s, int argc, t_atom *argv) { t_tabsend *x = (t_tabsend *)pd_new(tabsend_class); arrayvec_init(&x->x_v, x, argc, argv); x->x_graphperiod = 1; x->x_f = 0; return (x); } static t_int *tabsend_perform(t_int *w) { t_tabsend *x = (t_tabsend *)(w[1]); t_dsparray *d = (t_dsparray *)(w[2]); t_sample *in = (t_sample *)(w[3]); int n = (int)w[4], maxindex; t_word *dest; int phase = d->d_phase; if (!dsparray_get_array(d, &maxindex, &dest, 0)) goto bad; if (n > maxindex) n = maxindex; while (n--) { t_sample f = *in++; if (PD_BIGORSMALL(f)) f = 0; (dest++)->w_float = f; } if (phase++ >= x->x_graphperiod) { tabwrite_tilde_redraw(d->d_symbol); phase = 0; } d->d_phase = phase; bad: return (w+5); } static void tabsend_set(t_tabsend *x, t_symbol *s, int argc, t_atom *argv) { arrayvec_set(&x->x_v, argc, argv); } static void tabsend_dsp(t_tabsend *x, t_signal **sp) { int i, nchans = (sp[0]->s_nchans < x->x_v.v_n ? sp[0]->s_nchans : x->x_v.v_n); int length = sp[0]->s_length; int tickspersec = sp[0]->s_sr/length; if (tickspersec < 1) tickspersec = 1; x->x_graphperiod = tickspersec; arrayvec_testvec(&x->x_v); for (i = 0; i < nchans; i++) dsp_add(tabsend_perform, 4, x, x->x_v.v_vec+i, sp[0]->s_vec + i * sp[0]->s_length, (t_int)sp[0]->s_length); } static void tabsend_free(t_tabsend *x) { arrayvec_free(&x->x_v); } static void tabsend_setup(void) { tabsend_class = class_new(gensym("tabsend~"), (t_newmethod)tabsend_new, (t_method)tabsend_free, sizeof(t_tabsend), CLASS_MULTICHANNEL, A_GIMME, 0); CLASS_MAINSIGNALIN(tabsend_class, t_tabsend, x_f); class_addmethod(tabsend_class, (t_method)tabsend_dsp, gensym("dsp"), A_CANT, 0); class_addmethod(tabsend_class, (t_method)tabsend_set, gensym("set"), A_GIMME, 0); class_sethelpsymbol(tabsend_class, gensym("tabsend-receive~")); } /* ------------------------ tabreceive~ ------------------------- */ static t_class *tabreceive_class; typedef struct _tabreceive { t_object x_obj; t_arrayvec x_v; } t_tabreceive; static void *tabreceive_new(t_symbol *s, int argc, t_atom *argv) { t_tabreceive *x = (t_tabreceive *)pd_new(tabreceive_class); outlet_new(&x->x_obj, &s_signal); arrayvec_init(&x->x_v, x, argc, argv); return (x); } static t_int *tabreceive_perform(t_int *w) { t_dsparray *d = (t_dsparray *)(w[1]); t_sample *out = (t_sample *)(w[2]); int n = (int)w[3], maxindex; t_word *from; if (dsparray_get_array(d, &maxindex, &from, 0)) { t_int vecsize = maxindex; if (vecsize > n) vecsize = n; while (vecsize--) *out++ = (from++)->w_float; vecsize = n - maxindex; if (vecsize > 0) while (vecsize--) *out++ = 0; } else while (n--) *out++ = 0; return (w+4); } static void tabreceive_set(t_tabreceive *x, t_symbol *s, int argc, t_atom *argv) { arrayvec_set(&x->x_v, argc, argv); } static void tabreceive_dsp(t_tabreceive *x, t_signal **sp) { int i; signal_setmultiout(&sp[0], x->x_v.v_n); arrayvec_testvec(&x->x_v); for (i = 0; i < x->x_v.v_n; i++) dsp_add(tabreceive_perform, 3, &x->x_v.v_vec[i], sp[0]->s_vec + i * sp[0]->s_length, (t_int)sp[0]->s_length); } static void tabreceive_free(t_tabreceive *x) { arrayvec_free(&x->x_v); } static void tabreceive_setup(void) { tabreceive_class = class_new(gensym("tabreceive~"), (t_newmethod)tabreceive_new, (t_method)tabreceive_free, sizeof(t_tabreceive), CLASS_MULTICHANNEL, A_GIMME, 0); class_addmethod(tabreceive_class, (t_method)tabreceive_dsp, gensym("dsp"), A_CANT, 0); class_addmethod(tabreceive_class, (t_method)tabreceive_set, gensym("set"), A_GIMME, 0); class_sethelpsymbol(tabreceive_class, gensym("tabsend-receive~")); } /* ---------- tabread: control, non-interpolating ------------------------ */ static t_class *tabread_class; typedef struct _tabread { t_object x_obj; t_symbol *x_arrayname; } t_tabread; static void tabread_float(t_tabread *x, t_float f) { t_garray *a; int npoints; t_word *vec; if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class))) pd_error(x, "%s: no such array", x->x_arrayname->s_name); else if (!garray_getfloatwords(a, &npoints, &vec)) pd_error(x, "%s: bad template for tabread", x->x_arrayname->s_name); else { int n = f; if (n < 0) n = 0; else if (n >= npoints) n = npoints - 1; outlet_float(x->x_obj.ob_outlet, (npoints ? vec[n].w_float : 0)); } } static void tabread_set(t_tabread *x, t_symbol *s) { x->x_arrayname = s; } static void *tabread_new(t_symbol *s) { t_tabread *x = (t_tabread *)pd_new(tabread_class); x->x_arrayname = s; outlet_new(&x->x_obj, &s_float); return (x); } static void tabread_setup(void) { tabread_class = class_new(gensym("tabread"), (t_newmethod)tabread_new, 0, sizeof(t_tabread), 0, A_DEFSYM, 0); class_addfloat(tabread_class, (t_method)tabread_float); class_addmethod(tabread_class, (t_method)tabread_set, gensym("set"), A_SYMBOL, 0); } /* ---------- tabread4: control, 4-point interpolation --------------- */ static t_class *tabread4_class; typedef struct _tabread4 { t_object x_obj; t_symbol *x_arrayname; } t_tabread4; static void tabread4_float(t_tabread4 *x, t_float f) { t_garray *a; int npoints; t_word *vec; if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class))) pd_error(x, "%s: no such array", x->x_arrayname->s_name); else if (!garray_getfloatwords(a, &npoints, &vec)) pd_error(x, "%s: bad template for tabread4", x->x_arrayname->s_name); else if (npoints < 4) outlet_float(x->x_obj.ob_outlet, 0); else if (f <= 1) outlet_float(x->x_obj.ob_outlet, vec[1].w_float); else if (f >= npoints - 2) outlet_float(x->x_obj.ob_outlet, vec[npoints - 2].w_float); else { int n = f; float a, b, c, d, cminusb, frac; t_word *wp; if (n >= npoints - 2) n = npoints - 3; wp = vec + n; frac = f - n; a = wp[-1].w_float; b = wp[0].w_float; c = wp[1].w_float; d = wp[2].w_float; cminusb = c-b; outlet_float(x->x_obj.ob_outlet, b + frac * ( cminusb - 0.1666667f * (1.-frac) * ( (d - a - 3.0f * cminusb) * frac + (d + 2.0f*a - 3.0f*b)))); } } static void tabread4_set(t_tabread4 *x, t_symbol *s) { x->x_arrayname = s; } static void *tabread4_new(t_symbol *s) { t_tabread4 *x = (t_tabread4 *)pd_new(tabread4_class); x->x_arrayname = s; outlet_new(&x->x_obj, &s_float); return (x); } static void tabread4_setup(void) { tabread4_class = class_new(gensym("tabread4"), (t_newmethod)tabread4_new, 0, sizeof(t_tabread4), 0, A_DEFSYM, 0); class_addfloat(tabread4_class, (t_method)tabread4_float); class_addmethod(tabread4_class, (t_method)tabread4_set, gensym("set"), A_SYMBOL, 0); } /* ------------------ tabwrite: control ------------------------ */ static t_class *tabwrite_class; typedef struct _tabwrite { t_object x_obj; t_symbol *x_arrayname; t_float x_ft1; } t_tabwrite; static void tabwrite_float(t_tabwrite *x, t_float f) { int vecsize; t_garray *a; t_word *vec; if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class))) pd_error(x, "%s: no such array", x->x_arrayname->s_name); else if (!garray_getfloatwords(a, &vecsize, &vec)) pd_error(x, "%s: bad template for tabwrite", x->x_arrayname->s_name); else { int n = x->x_ft1; if (n < 0) n = 0; else if (n >= vecsize) n = vecsize-1; vec[n].w_float = f; garray_redraw(a); } } static void tabwrite_set(t_tabwrite *x, t_symbol *s) { x->x_arrayname = s; } static void *tabwrite_new(t_symbol *s) { t_tabwrite *x = (t_tabwrite *)pd_new(tabwrite_class); x->x_ft1 = 0; x->x_arrayname = s; floatinlet_new(&x->x_obj, &x->x_ft1); return (x); } void tabwrite_setup(void) { tabwrite_class = class_new(gensym("tabwrite"), (t_newmethod)tabwrite_new, 0, sizeof(t_tabwrite), 0, A_DEFSYM, 0); class_addfloat(tabwrite_class, (t_method)tabwrite_float); class_addmethod(tabwrite_class, (t_method)tabwrite_set, gensym("set"), A_SYMBOL, 0); } /* ------------------------ global setup routine ------------------------- */ void d_array_setup(void) { tabwrite_tilde_setup(); tabplay_tilde_setup(); tabread_tilde_setup(); tabread4_tilde_setup(); tabsend_setup(); tabreceive_setup(); tabread_setup(); tabread4_setup(); tabwrite_setup(); } ================================================ FILE: libs/libpd/pure-data/src/d_ctl.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* sig~ and line~ control-to-signal converters; snapshot~ signal-to-control converter. */ #include "m_pd.h" #include "math.h" /* -------------------------- sig~ ------------------------------ */ static t_class *sig_tilde_class; typedef struct _sig { t_object x_obj; t_float x_f; } t_sig; static void sig_tilde_float(t_sig *x, t_float f) { x->x_f = f; } static void sig_tilde_dsp(t_sig *x, t_signal **sp) { dsp_add_scalarcopy(&x->x_f, sp[0]->s_vec, (t_int)sp[0]->s_n); } static void *sig_tilde_new(t_floatarg f) { t_sig *x = (t_sig *)pd_new(sig_tilde_class); x->x_f = f; outlet_new(&x->x_obj, gensym("signal")); return (x); } static void sig_tilde_setup(void) { sig_tilde_class = class_new(gensym("sig~"), (t_newmethod)sig_tilde_new, 0, sizeof(t_sig), 0, A_DEFFLOAT, 0); class_addfloat(sig_tilde_class, (t_method)sig_tilde_float); class_addmethod(sig_tilde_class, (t_method)sig_tilde_dsp, gensym("dsp"), A_CANT, 0); } /* -------------------------- line~ ------------------------------ */ static t_class *line_tilde_class; typedef struct _line { t_object x_obj; t_sample x_target; /* target value of ramp */ t_sample x_value; /* current value of ramp at block-borders */ t_sample x_biginc; t_sample x_inc; t_float x_1overn; t_float x_dspticktomsec; t_float x_inletvalue; t_float x_inletwas; int x_ticksleft; int x_retarget; } t_line; static t_int *line_tilde_perform(t_int *w) { t_line *x = (t_line *)(w[1]); t_sample *out = (t_sample *)(w[2]); int n = (int)(w[3]); t_sample f = x->x_value; if (PD_BIGORSMALL(f)) x->x_value = f = 0; if (x->x_retarget) { int nticks = x->x_inletwas * x->x_dspticktomsec; if (!nticks) nticks = 1; x->x_ticksleft = nticks; x->x_biginc = (x->x_target - x->x_value)/(t_float)nticks; x->x_inc = x->x_1overn * x->x_biginc; x->x_retarget = 0; } if (x->x_ticksleft) { t_sample f = x->x_value; while (n--) *out++ = f, f += x->x_inc; x->x_value += x->x_biginc; x->x_ticksleft--; } else { t_sample g = x->x_value = x->x_target; while (n--) *out++ = g; } return (w+4); } /* TB: vectorized version */ static t_int *line_tilde_perf8(t_int *w) { t_line *x = (t_line *)(w[1]); t_sample *out = (t_sample *)(w[2]); int n = (int)(w[3]); t_sample f = x->x_value; if (PD_BIGORSMALL(f)) x->x_value = f = 0; if (x->x_retarget) { int nticks = x->x_inletwas * x->x_dspticktomsec; if (!nticks) nticks = 1; x->x_ticksleft = nticks; x->x_biginc = (x->x_target - x->x_value)/(t_sample)nticks; x->x_inc = x->x_1overn * x->x_biginc; x->x_retarget = 0; } if (x->x_ticksleft) { t_sample f = x->x_value; while (n--) *out++ = f, f += x->x_inc; x->x_value += x->x_biginc; x->x_ticksleft--; } else { t_sample f = x->x_value = x->x_target; for (; n; n -= 8, out += 8) { out[0] = f; out[1] = f; out[2] = f; out[3] = f; out[4] = f; out[5] = f; out[6] = f; out[7] = f; } } return (w+4); } static void line_tilde_float(t_line *x, t_float f) { if (x->x_inletvalue <= 0) { x->x_target = x->x_value = f; x->x_ticksleft = x->x_retarget = 0; } else { x->x_target = f; x->x_retarget = 1; x->x_inletwas = x->x_inletvalue; x->x_inletvalue = 0; } } static void line_tilde_stop(t_line *x) { x->x_target = x->x_value; x->x_ticksleft = x->x_retarget = 0; } static void line_tilde_dsp(t_line *x, t_signal **sp) { if(sp[0]->s_n&7) dsp_add(line_tilde_perform, 3, x, sp[0]->s_vec, (t_int)sp[0]->s_n); else dsp_add(line_tilde_perf8, 3, x, sp[0]->s_vec, (t_int)sp[0]->s_n); x->x_1overn = 1./sp[0]->s_n; x->x_dspticktomsec = sp[0]->s_sr / (1000 * sp[0]->s_n); } static void *line_tilde_new(void) { t_line *x = (t_line *)pd_new(line_tilde_class); outlet_new(&x->x_obj, gensym("signal")); floatinlet_new(&x->x_obj, &x->x_inletvalue); x->x_ticksleft = x->x_retarget = 0; x->x_value = x->x_target = x->x_inletvalue = x->x_inletwas = 0; return (x); } static void line_tilde_setup(void) { line_tilde_class = class_new(gensym("line~"), line_tilde_new, 0, sizeof(t_line), 0, 0); class_addfloat(line_tilde_class, (t_method)line_tilde_float); class_addmethod(line_tilde_class, (t_method)line_tilde_dsp, gensym("dsp"), A_CANT, 0); class_addmethod(line_tilde_class, (t_method)line_tilde_stop, gensym("stop"), 0); } /* -------------------------- vline~ ------------------------------ */ static t_class *vline_tilde_class; #include "s_stuff.h" /* for DEFDACBLKSIZE; this should be in m_pd.h */ typedef struct _vseg { double s_targettime; double s_starttime; t_sample s_target; struct _vseg *s_next; } t_vseg; typedef struct _vline { t_object x_obj; double x_value; double x_inc; double x_referencetime; double x_lastlogicaltime; double x_nextblocktime; double x_samppermsec; double x_msecpersamp; double x_targettime; t_sample x_target; t_float x_inlet1; t_float x_inlet2; t_vseg *x_list; } t_vline; static t_int *vline_tilde_perform(t_int *w) { t_vline *x = (t_vline *)(w[1]); t_sample *out = (t_sample *)(w[2]); int n = (int)(w[3]), i; double f = x->x_value; double inc = x->x_inc; double msecpersamp = x->x_msecpersamp; double timenow, logicaltimenow = clock_gettimesince(x->x_referencetime); t_vseg *s = x->x_list; if (logicaltimenow != x->x_lastlogicaltime) { int sampstotime = (n > DEFDACBLKSIZE ? n : DEFDACBLKSIZE); x->x_lastlogicaltime = logicaltimenow; x->x_nextblocktime = logicaltimenow - sampstotime * msecpersamp; } timenow = x->x_nextblocktime; x->x_nextblocktime = timenow + n * msecpersamp; for (i = 0; i < n; i++) { double timenext = timenow + msecpersamp; checknext: if (s) { /* has starttime elapsed? If so update value and increment */ if (s->s_starttime < timenext) { if (x->x_targettime <= timenext) f = x->x_target, inc = 0; /* if zero-length segment bash output value */ if (s->s_targettime <= s->s_starttime) { f = s->s_target; inc = 0; } else { double incpermsec = (s->s_target - f)/ (s->s_targettime - s->s_starttime); f = f + incpermsec * (timenext - s->s_starttime); inc = incpermsec * msecpersamp; } x->x_inc = inc; x->x_target = s->s_target; x->x_targettime = s->s_targettime; x->x_list = s->s_next; t_freebytes(s, sizeof(*s)); s = x->x_list; goto checknext; } } if (x->x_targettime <= timenext) f = x->x_target, inc = x->x_inc = 0, x->x_targettime = 1e20; *out++ = f; f = f + inc; timenow = timenext; } x->x_value = f; return (w+4); } static void vline_tilde_stop(t_vline *x) { t_vseg *s1, *s2; for (s1 = x->x_list; s1; s1 = s2) s2 = s1->s_next, t_freebytes(s1, sizeof(*s1)); x->x_list = 0; x->x_inc = 0; x->x_inlet1 = x->x_inlet2 = 0; x->x_target = x->x_value; x->x_targettime = 1e20; } static void vline_tilde_float(t_vline *x, t_float f) { double timenow = clock_gettimesince(x->x_referencetime); t_float inlet1 = (x->x_inlet1 < 0 ? 0 : x->x_inlet1); t_float inlet2 = x->x_inlet2; double starttime = timenow + inlet2; t_vseg *s1, *s2, *deletefrom = 0, *snew; if (PD_BIGORSMALL(f)) f = 0; /* negative delay input means stop and jump immediately to new value */ if (inlet2 < 0) { x->x_value = f; vline_tilde_stop(x); return; } snew = (t_vseg *)t_getbytes(sizeof(*snew)); /* check if we supplant the first item in the list. We supplant an item by having an earlier starttime, or an equal starttime unless the equal one was instantaneous and the new one isn't (in which case we'll do a jump-and-slide starting at that time.) */ if (!x->x_list || x->x_list->s_starttime > starttime || (x->x_list->s_starttime == starttime && (x->x_list->s_targettime > x->x_list->s_starttime || inlet1 <= 0))) { deletefrom = x->x_list; x->x_list = snew; } else { for (s1 = x->x_list; (s2 = s1->s_next); s1 = s2) { if (s2->s_starttime > starttime || (s2->s_starttime == starttime && (s2->s_targettime > s2->s_starttime || inlet1 <= 0))) { deletefrom = s2; s1->s_next = snew; goto didit; } } s1->s_next = snew; deletefrom = 0; didit: ; } while (deletefrom) { s1 = deletefrom->s_next; t_freebytes(deletefrom, sizeof(*deletefrom)); deletefrom = s1; } snew->s_next = 0; snew->s_target = f; snew->s_starttime = starttime; snew->s_targettime = starttime + inlet1; x->x_inlet1 = x->x_inlet2 = 0; } static void vline_tilde_dsp(t_vline *x, t_signal **sp) { dsp_add(vline_tilde_perform, 3, x, sp[0]->s_vec, (t_int)sp[0]->s_n); x->x_samppermsec = ((double)(sp[0]->s_sr)) / 1000; x->x_msecpersamp = ((double)1000) / sp[0]->s_sr; } static void *vline_tilde_new(void) { t_vline *x = (t_vline *)pd_new(vline_tilde_class); outlet_new(&x->x_obj, gensym("signal")); floatinlet_new(&x->x_obj, &x->x_inlet1); floatinlet_new(&x->x_obj, &x->x_inlet2); x->x_inlet1 = x->x_inlet2 = 0; x->x_value = x->x_inc = 0; x->x_referencetime = x->x_lastlogicaltime = x->x_nextblocktime = clock_getlogicaltime(); x->x_list = 0; x->x_samppermsec = 0; x->x_targettime = 1e20; return (x); } static void vline_tilde_setup(void) { vline_tilde_class = class_new(gensym("vline~"), vline_tilde_new, (t_method)vline_tilde_stop, sizeof(t_vline), 0, 0); class_addfloat(vline_tilde_class, (t_method)vline_tilde_float); class_addmethod(vline_tilde_class, (t_method)vline_tilde_dsp, gensym("dsp"), A_CANT, 0); class_addmethod(vline_tilde_class, (t_method)vline_tilde_stop, gensym("stop"), 0); } /* -------------------------- snapshot~ ------------------------------ */ static t_class *snapshot_tilde_class; typedef struct _snapshot { t_object x_obj; t_sample x_value; t_float x_f; } t_snapshot; static void *snapshot_tilde_new(void) { t_snapshot *x = (t_snapshot *)pd_new(snapshot_tilde_class); x->x_value = 0; outlet_new(&x->x_obj, &s_float); x->x_f = 0; return (x); } static t_int *snapshot_tilde_perform(t_int *w) { t_sample *in = (t_sample *)(w[1]); t_sample *out = (t_sample *)(w[2]); *out = *in; return (w+3); } static void snapshot_tilde_dsp(t_snapshot *x, t_signal **sp) { dsp_add(snapshot_tilde_perform, 2, sp[0]->s_vec + (sp[0]->s_n-1), &x->x_value); } static void snapshot_tilde_bang(t_snapshot *x) { outlet_float(x->x_obj.ob_outlet, x->x_value); } static void snapshot_tilde_set(t_snapshot *x, t_floatarg f) { x->x_value = f; } static void snapshot_tilde_setup(void) { snapshot_tilde_class = class_new(gensym("snapshot~"), snapshot_tilde_new, 0, sizeof(t_snapshot), 0, 0); CLASS_MAINSIGNALIN(snapshot_tilde_class, t_snapshot, x_f); class_addmethod(snapshot_tilde_class, (t_method)snapshot_tilde_dsp, gensym("dsp"), A_CANT, 0); class_addmethod(snapshot_tilde_class, (t_method)snapshot_tilde_set, gensym("set"), A_DEFFLOAT, 0); class_addbang(snapshot_tilde_class, snapshot_tilde_bang); } /* -------------------------- vsnapshot~ ------------------------------ */ static t_class *vsnapshot_tilde_class; typedef struct _vsnapshot { t_object x_obj; int x_n; int x_gotone; t_sample *x_vec; t_float x_f; t_float x_sampspermsec; double x_time; } t_vsnapshot; static void *vsnapshot_tilde_new(void) { t_vsnapshot *x = (t_vsnapshot *)pd_new(vsnapshot_tilde_class); outlet_new(&x->x_obj, &s_float); x->x_f = 0; x->x_n = 0; x->x_vec = 0; x->x_gotone = 0; return (x); } static t_int *vsnapshot_tilde_perform(t_int *w) { t_sample *in = (t_sample *)(w[1]); t_vsnapshot *x = (t_vsnapshot *)(w[2]); t_sample *out = x->x_vec; int n = x->x_n, i; for (i = 0; i < n; i++) out[i] = in[i]; x->x_time = clock_getlogicaltime(); x->x_gotone = 1; return (w+3); } static void vsnapshot_tilde_dsp(t_vsnapshot *x, t_signal **sp) { int n = sp[0]->s_n; if (n != x->x_n) { if (x->x_vec) t_freebytes(x->x_vec, x->x_n * sizeof(t_sample)); x->x_vec = (t_sample *)getbytes(n * sizeof(t_sample)); x->x_gotone = 0; x->x_n = n; } x->x_sampspermsec = sp[0]->s_sr / 1000; dsp_add(vsnapshot_tilde_perform, 2, sp[0]->s_vec, x); } static void vsnapshot_tilde_bang(t_vsnapshot *x) { t_sample val; if (x->x_gotone) { int indx = clock_gettimesince(x->x_time) * x->x_sampspermsec; if (indx < 0) indx = 0; else if (indx >= x->x_n) indx = x->x_n - 1; val = x->x_vec[indx]; } else val = 0; outlet_float(x->x_obj.ob_outlet, val); } static void vsnapshot_tilde_ff(t_vsnapshot *x) { if (x->x_vec) t_freebytes(x->x_vec, x->x_n * sizeof(t_sample)); } static void vsnapshot_tilde_setup(void) { vsnapshot_tilde_class = class_new(gensym("vsnapshot~"), vsnapshot_tilde_new, (t_method)vsnapshot_tilde_ff, sizeof(t_vsnapshot), 0, 0); CLASS_MAINSIGNALIN(vsnapshot_tilde_class, t_vsnapshot, x_f); class_addmethod(vsnapshot_tilde_class, (t_method)vsnapshot_tilde_dsp, gensym("dsp"), A_CANT, 0); class_addbang(vsnapshot_tilde_class, vsnapshot_tilde_bang); } /* ---------------- env~ - simple envelope follower. ----------------- */ #define MAXOVERLAP 32 #define INITVSTAKEN 64 typedef struct sigenv { t_object x_obj; /* header */ void *x_outlet; /* a "float" outlet */ void *x_clock; /* a "clock" object */ t_sample *x_buf; /* a Hanning window */ int x_phase; /* number of points since last output */ int x_period; /* requested period of output */ int x_realperiod; /* period rounded up to vecsize multiple */ int x_npoints; /* analysis window size in samples */ t_float x_result; /* result to output */ t_sample x_sumbuf[MAXOVERLAP]; /* summing buffer */ t_float x_f; int x_allocforvs; /* extra buffer for DSP vector size */ } t_sigenv; t_class *env_tilde_class; static void env_tilde_tick(t_sigenv *x); static void *env_tilde_new(t_floatarg fnpoints, t_floatarg fperiod) { int npoints = fnpoints; int period = fperiod; t_sigenv *x; t_sample *buf; int i; if (npoints < 1) npoints = 1024; if (period < 1) period = npoints/2; if (period < npoints / MAXOVERLAP + 1) period = npoints / MAXOVERLAP + 1; if (!(buf = getbytes(sizeof(t_sample) * (npoints + INITVSTAKEN)))) { pd_error(0, "env: couldn't allocate buffer"); return (0); } x = (t_sigenv *)pd_new(env_tilde_class); x->x_buf = buf; x->x_npoints = npoints; x->x_phase = 0; x->x_period = period; for (i = 0; i < MAXOVERLAP; i++) x->x_sumbuf[i] = 0; for (i = 0; i < npoints; i++) buf[i] = (1. - cos((2 * 3.14159 * i) / npoints))/npoints; for (; i < npoints+INITVSTAKEN; i++) buf[i] = 0; x->x_clock = clock_new(x, (t_method)env_tilde_tick); x->x_outlet = outlet_new(&x->x_obj, gensym("float")); x->x_f = 0; x->x_allocforvs = INITVSTAKEN; return (x); } static t_int *env_tilde_perform(t_int *w) { t_sigenv *x = (t_sigenv *)(w[1]); t_sample *in = (t_sample *)(w[2]); int n = (int)(w[3]); int count; t_sample *sump; in += n; for (count = x->x_phase, sump = x->x_sumbuf; count < x->x_npoints; count += x->x_realperiod, sump++) { t_sample *hp = x->x_buf + count; t_sample *fp = in; t_sample sum = *sump; int i; for (i = 0; i < n; i++) { fp--; sum += *hp++ * (*fp * *fp); } *sump = sum; } sump[0] = 0; x->x_phase -= n; if (x->x_phase < 0) { x->x_result = x->x_sumbuf[0]; for (count = x->x_realperiod, sump = x->x_sumbuf; count < x->x_npoints; count += x->x_realperiod, sump++) sump[0] = sump[1]; sump[0] = 0; x->x_phase = x->x_realperiod - n; clock_delay(x->x_clock, 0L); } return (w+4); } static void env_tilde_dsp(t_sigenv *x, t_signal **sp) { if (x->x_period % sp[0]->s_n) x->x_realperiod = x->x_period + sp[0]->s_n - (x->x_period % sp[0]->s_n); else x->x_realperiod = x->x_period; if (sp[0]->s_n > x->x_allocforvs) { void *xx = resizebytes(x->x_buf, (x->x_npoints + x->x_allocforvs) * sizeof(t_sample), (x->x_npoints + sp[0]->s_n) * sizeof(t_sample)); if (!xx) { pd_error(0, "env~: out of memory"); return; } x->x_buf = (t_sample *)xx; x->x_allocforvs = sp[0]->s_n; } dsp_add(env_tilde_perform, 3, x, sp[0]->s_vec, (t_int)sp[0]->s_n); } static void env_tilde_tick(t_sigenv *x) /* callback function for the clock */ { outlet_float(x->x_outlet, powtodb(x->x_result)); } static void env_tilde_ff(t_sigenv *x) /* cleanup on free */ { clock_free(x->x_clock); freebytes(x->x_buf, (x->x_npoints + x->x_allocforvs) * sizeof(*x->x_buf)); } void env_tilde_setup(void) { env_tilde_class = class_new(gensym("env~"), (t_newmethod)env_tilde_new, (t_method)env_tilde_ff, sizeof(t_sigenv), 0, A_DEFFLOAT, A_DEFFLOAT, 0); CLASS_MAINSIGNALIN(env_tilde_class, t_sigenv, x_f); class_addmethod(env_tilde_class, (t_method)env_tilde_dsp, gensym("dsp"), A_CANT, 0); } /* --------------------- threshold~ ----------------------------- */ static t_class *threshold_tilde_class; typedef struct _threshold_tilde { t_object x_obj; t_outlet *x_outlet1; /* bang out for high thresh */ t_outlet *x_outlet2; /* bang out for low thresh */ t_clock *x_clock; /* wakeup for message output */ t_float x_f; /* scalar inlet */ int x_state; /* 1 = high, 0 = low */ t_float x_hithresh; /* value of high threshold */ t_float x_lothresh; /* value of low threshold */ t_float x_deadwait; /* msec remaining in dead period */ t_float x_msecpertick; /* msec per DSP tick */ t_float x_hideadtime; /* hi dead time in msec */ t_float x_lodeadtime; /* lo dead time in msec */ } t_threshold_tilde; static void threshold_tilde_tick(t_threshold_tilde *x); static void threshold_tilde_set(t_threshold_tilde *x, t_floatarg hithresh, t_floatarg hideadtime, t_floatarg lothresh, t_floatarg lodeadtime); static t_threshold_tilde *threshold_tilde_new(t_floatarg hithresh, t_floatarg hideadtime, t_floatarg lothresh, t_floatarg lodeadtime) { t_threshold_tilde *x = (t_threshold_tilde *) pd_new(threshold_tilde_class); x->x_state = 0; /* low state */ x->x_deadwait = 0; /* no dead time */ x->x_clock = clock_new(x, (t_method)threshold_tilde_tick); x->x_outlet1 = outlet_new(&x->x_obj, &s_bang); x->x_outlet2 = outlet_new(&x->x_obj, &s_bang); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("ft1")); x->x_msecpertick = 0.; x->x_f = 0; threshold_tilde_set(x, hithresh, hideadtime, lothresh, lodeadtime); return (x); } /* "set" message to specify thresholds and dead times */ static void threshold_tilde_set(t_threshold_tilde *x, t_floatarg hithresh, t_floatarg hideadtime, t_floatarg lothresh, t_floatarg lodeadtime) { if (lothresh > hithresh) lothresh = hithresh; x->x_hithresh = hithresh; x->x_hideadtime = hideadtime; x->x_lothresh = lothresh; x->x_lodeadtime = lodeadtime; } /* number in inlet sets state -- note incompatible with JMAX which used "int" message for this, impossible here because of auto signal conversion */ static void threshold_tilde_ft1(t_threshold_tilde *x, t_floatarg f) { x->x_state = (f != 0); x->x_deadwait = 0; } static void threshold_tilde_tick(t_threshold_tilde *x) { if (x->x_state) outlet_bang(x->x_outlet1); else outlet_bang(x->x_outlet2); } static t_int *threshold_tilde_perform(t_int *w) { t_sample *in1 = (t_sample *)(w[1]); t_threshold_tilde *x = (t_threshold_tilde *)(w[2]); int n = (int)w[3]; if (x->x_deadwait > 0) x->x_deadwait -= x->x_msecpertick; else if (x->x_state) { /* we're high; look for low sample */ for (; n--; in1++) { if (*in1 < x->x_lothresh) { clock_delay(x->x_clock, 0L); x->x_state = 0; x->x_deadwait = x->x_lodeadtime; goto done; } } } else { /* we're low; look for high sample */ for (; n--; in1++) { if (*in1 >= x->x_hithresh) { clock_delay(x->x_clock, 0L); x->x_state = 1; x->x_deadwait = x->x_hideadtime; goto done; } } } done: return (w+4); } void threshold_tilde_dsp(t_threshold_tilde *x, t_signal **sp) { x->x_msecpertick = 1000. * sp[0]->s_n / sp[0]->s_sr; dsp_add(threshold_tilde_perform, 3, sp[0]->s_vec, x, (t_int)sp[0]->s_n); } static void threshold_tilde_ff(t_threshold_tilde *x) { clock_free(x->x_clock); } static void threshold_tilde_setup(void) { threshold_tilde_class = class_new(gensym("threshold~"), (t_newmethod)threshold_tilde_new, (t_method)threshold_tilde_ff, sizeof(t_threshold_tilde), 0, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0); CLASS_MAINSIGNALIN(threshold_tilde_class, t_threshold_tilde, x_f); class_addmethod(threshold_tilde_class, (t_method)threshold_tilde_set, gensym("set"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); class_addmethod(threshold_tilde_class, (t_method)threshold_tilde_ft1, gensym("ft1"), A_FLOAT, 0); class_addmethod(threshold_tilde_class, (t_method)threshold_tilde_dsp, gensym("dsp"), A_CANT, 0); } /* ------------------------ global setup routine ------------------------- */ void d_ctl_setup(void) { sig_tilde_setup(); line_tilde_setup(); vline_tilde_setup(); snapshot_tilde_setup(); vsnapshot_tilde_setup(); env_tilde_setup(); threshold_tilde_setup(); } ================================================ FILE: libs/libpd/pure-data/src/d_dac.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* The dac~ and adc~ routines. */ #include "m_pd.h" #include "s_stuff.h" #include /* ----------------------------- dac~ --------------------------- */ static t_class *dac_class; typedef struct _dac { t_object x_obj; t_int x_n; t_int *x_vec; t_float x_f; } t_dac; static void *dac_new(t_symbol *s, int argc, t_atom *argv) { t_dac *x = (t_dac *)pd_new(dac_class); t_atom defarg[2]; int i; if (!argc) { argv = defarg; argc = 2; SETFLOAT(&defarg[0], 1); SETFLOAT(&defarg[1], 2); } x->x_n = argc; x->x_vec = (t_int *)getbytes(argc * sizeof(*x->x_vec)); for (i = 0; i < argc; i++) x->x_vec[i] = atom_getfloatarg(i, argc, argv); for (i = 1; i < argc; i++) inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); x->x_f = 0; return (x); } static void dac_dsp(t_dac *x, t_signal **sp) { t_int i, j; for (i = 0; i < x->x_n; i++) { int ch = (int)(x->x_vec[i] - 1); if (sp[i]->s_length != DEFDACBLKSIZE) pd_error(x, "dac~: input vector size (%d) doesn't match Pd vector size (%d)", sp[i]->s_length, DEFDACBLKSIZE); else for (j = 0; j < sp[i]->s_nchans; j++) { if (ch + j >= 0 && ch + j < sys_get_outchannels()) dsp_add(plus_perform, 4, STUFF->st_soundout + DEFDACBLKSIZE * (ch + j), sp[i]->s_vec + j * sp[i]->s_length, STUFF->st_soundout + DEFDACBLKSIZE * (ch + j), (t_int)DEFDACBLKSIZE); } } } static void dac_set(t_dac *x, t_symbol *s, int argc, t_atom *argv) { int i; for (i = 0; i < argc && i < x->x_n; i++) x->x_vec[i] = atom_getfloatarg(i, argc, argv); canvas_update_dsp(); } static void dac_free(t_dac *x) { freebytes(x->x_vec, x->x_n * sizeof(*x->x_vec)); } static void dac_setup(void) { dac_class = class_new(gensym("dac~"), (t_newmethod)dac_new, (t_method)dac_free, sizeof(t_dac), CLASS_MULTICHANNEL, A_GIMME, 0); CLASS_MAINSIGNALIN(dac_class, t_dac, x_f); class_addmethod(dac_class, (t_method)dac_dsp, gensym("dsp"), A_CANT, 0); class_addmethod(dac_class, (t_method)dac_set, gensym("set"), A_GIMME, 0); class_sethelpsymbol(dac_class, gensym("adc~_dac~")); } /* ----------------------------- adc~ --------------------------- */ static t_class *adc_class; typedef struct _adc { t_object x_obj; int x_n; int *x_vec; int x_multi; } t_adc; static void *adc_new(t_symbol *s, int argc, t_atom *argv) { t_adc *x = (t_adc *)pd_new(adc_class); t_atom defarg[2]; int i, firstchan; if (!argc) { argv = defarg; argc = 2; SETFLOAT(&defarg[0], 1); SETFLOAT(&defarg[1], 2); } if (argc > 0 && argv[0].a_type == A_SYMBOL && !strcmp(argv[0].a_w.w_symbol->s_name, "-m")) { /* multichannel version: -m [nchans] [start channel] */ x->x_multi = 1; if ((x->x_n = atom_getfloatarg(1, argc, argv)) < 1) x->x_n = 2; if ((firstchan = atom_getfloatarg(2, argc, argv)) < 1) firstchan = 1; x->x_vec = (int *)getbytes(x->x_n * sizeof(*x->x_vec)); for (i = 0; i < x->x_n; i++) x->x_vec[i] = firstchan+i; outlet_new(&x->x_obj, &s_signal); } else { x->x_multi = 0; x->x_n = argc; x->x_vec = (int *)getbytes(argc * sizeof(*x->x_vec)); for (i = 0; i < argc; i++) x->x_vec[i] = atom_getfloatarg(i, argc, argv); for (i = 0; i < x->x_n; i++) outlet_new(&x->x_obj, &s_signal); } return (x); } static void adc_dsp(t_adc *x, t_signal **sp) { int i; if (x->x_multi) signal_setmultiout(sp, x->x_n); else for (i = 0; i < x->x_n; i++) signal_setmultiout(&sp[i], 1); if (sp[0]->s_length != DEFDACBLKSIZE) { pd_error(0, "adc~: local vector size %d doesn't match system (%d)", sp[0]->s_length, DEFDACBLKSIZE); return; } for (i = 0; i < x->x_n; i++) { int ch = x->x_vec[i] - 1; t_sample *out = (x->x_multi? sp[0]->s_vec + sp[0]->s_length * i: sp[i]->s_vec); if (ch >= 0 && ch < sys_get_inchannels()) dsp_add_copy(STUFF->st_soundin + DEFDACBLKSIZE*ch, out, DEFDACBLKSIZE); else dsp_add_zero(out, DEFDACBLKSIZE); } } static void adc_set(t_adc *x, t_symbol *s, int argc, t_atom *argv) { int i; if (x->x_multi) { int nchannels = atom_getfloatarg(0, argc, argv), startchannel = atom_getfloatarg(1, argc, argv); if (nchannels < 1) nchannels = 2; if (startchannel < 1) startchannel = 1; x->x_vec = (int *)t_resizebytes(x->x_vec, x->x_n * sizeof(*x->x_vec), nchannels * sizeof(*x->x_vec)); for (i = 0; i < nchannels; i++) x->x_vec[i] = startchannel + i; x->x_n = nchannels; } else for (i = 0; i < argc && i < x->x_n; i++) x->x_vec[i] = atom_getfloatarg(i, argc, argv); canvas_update_dsp(); } static void adc_free(t_adc *x) { freebytes(x->x_vec, x->x_n * sizeof(*x->x_vec)); } static void adc_setup(void) { adc_class = class_new(gensym("adc~"), (t_newmethod)adc_new, (t_method)adc_free, sizeof(t_adc), 0, A_GIMME, 0); class_addmethod(adc_class, (t_method)adc_dsp, gensym("dsp"), A_CANT, 0); class_addmethod(adc_class, (t_method)adc_set, gensym("set"), A_GIMME, 0); class_sethelpsymbol(adc_class, gensym("adc~_dac~")); } void d_dac_setup(void) { dac_setup(); adc_setup(); } ================================================ FILE: libs/libpd/pure-data/src/d_delay.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* send~, delread~, throw~, catch~ */ #include "m_pd.h" #include extern int ugen_getsortno(void); /* ----------------------------- delwrite~ ----------------------------- */ static t_class *sigdelwrite_class; typedef struct delwritectl { int c_n; t_sample *c_vec; int c_phase; } t_delwritectl; typedef struct _sigdelwrite { t_object x_obj; t_symbol *x_sym; t_float x_deltime; /* delay in msec (added by Mathieu Bouchard) */ t_delwritectl x_cspace; int x_sortno; /* DSP sort number at which this was last put on chain */ int x_rsortno; /* DSP sort # for first delread or write in chain */ int x_vecsize; /* vector size for delread~ to use */ t_float x_sr; t_float x_f; } t_sigdelwrite; #define XTRASAMPS 4 #define SAMPBLK 4 static void sigdelwrite_update(t_sigdelwrite *x) /* added by Mathieu Bouchard */ { int nsamps = x->x_deltime * x->x_sr * (t_float)(0.001f); if (nsamps < 1) nsamps = 1; nsamps += ((- nsamps) & (SAMPBLK - 1)); nsamps += x->x_vecsize; if (x->x_cspace.c_n != nsamps) { x->x_cspace.c_vec = (t_sample *)resizebytes(x->x_cspace.c_vec, (x->x_cspace.c_n + XTRASAMPS) * sizeof(t_sample), (nsamps + XTRASAMPS) * sizeof(t_sample)); x->x_cspace.c_n = nsamps; x->x_cspace.c_phase = XTRASAMPS; #if 0 post("delay line resized to %d samples", nsamps); #endif } } static void sigdelwrite_clear (t_sigdelwrite *x) /* added by Orm Finnendahl */ { if (x->x_cspace.c_n > 0) memset(x->x_cspace.c_vec, 0, sizeof(t_sample)*(x->x_cspace.c_n + XTRASAMPS)); } /* routine to check that all delwrites/delreads/vds have same vecsize */ static void sigdelwrite_check(t_sigdelwrite *x, int vecsize, t_float sr) { /* the first object in the DSP chain sets the vecsize */ if (x->x_rsortno != ugen_getsortno()) { x->x_vecsize = vecsize; x->x_sr = sr; x->x_rsortno = ugen_getsortno(); } #if 1 /* Subsequent objects are only allowed to increase the vector size/samplerate */ else { if (vecsize > x->x_vecsize) x->x_vecsize = vecsize; if (sr > x->x_sr) x->x_sr = sr; } #else /* LATER this should really check sample rate and blocking, once that is supported. Probably we don't actually care about vecsize. For now just suppress this check. */ else if (vecsize != x->x_vecsize) pd_error(x, "delread/delwrite/vd vector size mismatch"); #endif } static void *sigdelwrite_new(t_symbol *s, t_floatarg msec) { t_sigdelwrite *x = (t_sigdelwrite *)pd_new(sigdelwrite_class); if (!*s->s_name) s = gensym("delwrite~"); pd_bind(&x->x_obj.ob_pd, s); x->x_sym = s; x->x_deltime = msec; x->x_cspace.c_n = 0; x->x_cspace.c_vec = getbytes(XTRASAMPS * sizeof(t_sample)); x->x_sortno = 0; x->x_vecsize = 0; x->x_sr = 0; x->x_f = 0; return (x); } static t_int *sigdelwrite_perform(t_int *w) { t_sample *in = (t_sample *)(w[1]); t_delwritectl *c = (t_delwritectl *)(w[2]); int n = (int)(w[3]); int phase = c->c_phase, nsamps = c->c_n; t_sample *vp = c->c_vec, *bp = vp + phase, *ep = vp + (c->c_n + XTRASAMPS); phase += n; while (n--) { t_sample f = *in++; if (PD_BIGORSMALL(f)) f = 0; *bp++ = f; if (bp == ep) { vp[0] = ep[-4]; vp[1] = ep[-3]; vp[2] = ep[-2]; vp[3] = ep[-1]; bp = vp + XTRASAMPS; phase -= nsamps; } } c->c_phase = phase; return (w+4); } static void sigdelwrite_dsp(t_sigdelwrite *x, t_signal **sp) { dsp_add(sigdelwrite_perform, 3, sp[0]->s_vec, &x->x_cspace, (t_int)sp[0]->s_n); x->x_sortno = ugen_getsortno(); sigdelwrite_check(x, sp[0]->s_n, sp[0]->s_sr); sigdelwrite_update(x); } static void sigdelwrite_free(t_sigdelwrite *x) { pd_unbind(&x->x_obj.ob_pd, x->x_sym); freebytes(x->x_cspace.c_vec, (x->x_cspace.c_n + XTRASAMPS) * sizeof(t_sample)); } static void sigdelwrite_setup(void) { sigdelwrite_class = class_new(gensym("delwrite~"), (t_newmethod)sigdelwrite_new, (t_method)sigdelwrite_free, sizeof(t_sigdelwrite), 0, A_DEFSYM, A_DEFFLOAT, 0); CLASS_MAINSIGNALIN(sigdelwrite_class, t_sigdelwrite, x_f); class_addmethod(sigdelwrite_class, (t_method)sigdelwrite_dsp, gensym("dsp"), A_CANT, 0); class_addmethod(sigdelwrite_class, (t_method)sigdelwrite_clear, gensym("clear"), 0); class_sethelpsymbol(sigdelwrite_class, gensym("delay-tilde-objects")); } /* ----------------------------- delread~ ----------------------------- */ static t_class *sigdelread_class; typedef struct _sigdelread { t_object x_obj; t_symbol *x_sym; t_float x_deltime; /* delay in msec */ int x_delsamps; /* delay in samples */ t_float x_sr; /* samples per msec */ t_float x_n; /* vector size */ int x_zerodel; /* 0 or vecsize depending on read/write order */ } t_sigdelread; static void sigdelread_float(t_sigdelread *x, t_float f); static void *sigdelread_new(t_symbol *s, t_floatarg f) { t_sigdelread *x = (t_sigdelread *)pd_new(sigdelread_class); x->x_sym = s; x->x_sr = 1; x->x_n = 1; x->x_zerodel = 0; sigdelread_float(x, f); outlet_new(&x->x_obj, &s_signal); return (x); } static void sigdelread_float(t_sigdelread *x, t_float f) { t_sigdelwrite *delwriter = (t_sigdelwrite *)pd_findbyclass(x->x_sym, sigdelwrite_class); x->x_deltime = f; if (delwriter) { x->x_delsamps = (int)(0.5 + x->x_sr * x->x_deltime) + x->x_n - x->x_zerodel; if (x->x_delsamps < x->x_n) x->x_delsamps = x->x_n; else if (x->x_delsamps > delwriter->x_cspace.c_n) x->x_delsamps = delwriter->x_cspace.c_n; } } static t_int *sigdelread_perform(t_int *w) { t_sample *out = (t_sample *)(w[1]); t_delwritectl *c = (t_delwritectl *)(w[2]); int delsamps = *(int *)(w[3]); int n = (int)(w[4]); int phase = c->c_phase - delsamps, nsamps = c->c_n; t_sample *vp = c->c_vec, *bp, *ep = vp + (c->c_n + XTRASAMPS); if (phase < 0) phase += nsamps; bp = vp + phase; while (n--) { *out++ = *bp++; if (bp == ep) bp -= nsamps; } return (w+5); } static void sigdelread_dsp(t_sigdelread *x, t_signal **sp) { t_sigdelwrite *delwriter = (t_sigdelwrite *)pd_findbyclass(x->x_sym, sigdelwrite_class); x->x_sr = sp[0]->s_sr * 0.001; x->x_n = sp[0]->s_n; if (delwriter) { sigdelwrite_check(delwriter, sp[0]->s_n, sp[0]->s_sr); sigdelwrite_update(delwriter); x->x_zerodel = (delwriter->x_sortno == ugen_getsortno() ? 0 : delwriter->x_vecsize); sigdelread_float(x, x->x_deltime); dsp_add(sigdelread_perform, 4, sp[0]->s_vec, &delwriter->x_cspace, &x->x_delsamps, (t_int)sp[0]->s_n); /* check block size - but only if delwriter has been initialized */ if (delwriter->x_cspace.c_n > 0 && sp[0]->s_n > delwriter->x_cspace.c_n) pd_error(x, "delread~ %s: blocksize larger than delwrite~ buffer", x->x_sym->s_name); } else if (*x->x_sym->s_name) pd_error(x, "delread~: %s: no such delwrite~",x->x_sym->s_name); } static void sigdelread_setup(void) { sigdelread_class = class_new(gensym("delread~"), (t_newmethod)sigdelread_new, 0, sizeof(t_sigdelread), 0, A_DEFSYM, A_DEFFLOAT, 0); class_addmethod(sigdelread_class, (t_method)sigdelread_dsp, gensym("dsp"), A_CANT, 0); class_addfloat(sigdelread_class, (t_method)sigdelread_float); class_sethelpsymbol(sigdelread_class, gensym("delay-tilde-objects")); } /* ----------------------------- vd~ / delread4~ ----------------------------- */ static t_class *sigvd_class; typedef struct _sigvd { t_object x_obj; t_symbol *x_sym; t_float x_sr; /* samples per msec */ int x_zerodel; /* 0 or vecsize depending on read/write order */ t_float x_f; } t_sigvd; static void *sigvd_new(t_symbol *s) { t_sigvd *x = (t_sigvd *)pd_new(sigvd_class); x->x_sym = s; x->x_sr = 1; x->x_zerodel = 0; outlet_new(&x->x_obj, &s_signal); x->x_f = 0; return (x); } static t_int *sigvd_perform(t_int *w) { t_sample *in = (t_sample *)(w[1]); t_sample *out = (t_sample *)(w[2]); t_delwritectl *ctl = (t_delwritectl *)(w[3]); t_sigvd *x = (t_sigvd *)(w[4]); int n = (int)(w[5]); int nsamps = ctl->c_n; t_sample limit = nsamps - n; t_sample fn = n-1; t_sample *vp = ctl->c_vec, *bp, *wp = vp + ctl->c_phase; t_sample zerodel = x->x_zerodel; if (limit < 0) /* blocksize is larger than delread~ buffer size */ { while (n--) *out++ = 0; return (w+6); } while (n--) { t_sample delsamps = x->x_sr * *in++ - zerodel, frac; int idelsamps; t_sample a, b, c, d, cminusb; if (!(delsamps >= 1.00001f)) /* too small or NAN */ delsamps = 1.00001f; if (delsamps > limit) /* too big */ delsamps = limit; delsamps += fn; fn = fn - 1.0f; idelsamps = delsamps; frac = delsamps - (t_sample)idelsamps; bp = wp - idelsamps; if (bp < vp + XTRASAMPS) bp += nsamps; d = bp[-3]; c = bp[-2]; b = bp[-1]; a = bp[0]; cminusb = c-b; *out++ = b + frac * ( cminusb - 0.1666667f * (1.-frac) * ( (d - a - 3.0f * cminusb) * frac + (d + 2.0f*a - 3.0f*b) ) ); } return (w+6); } static void sigvd_dsp(t_sigvd *x, t_signal **sp) { t_sigdelwrite *delwriter = (t_sigdelwrite *)pd_findbyclass(x->x_sym, sigdelwrite_class); x->x_sr = sp[0]->s_sr * 0.001; if (delwriter) { sigdelwrite_check(delwriter, sp[0]->s_n, sp[0]->s_sr); sigdelwrite_update(delwriter); x->x_zerodel = (delwriter->x_sortno == ugen_getsortno() ? 0 : delwriter->x_vecsize); dsp_add(sigvd_perform, 5, sp[0]->s_vec, sp[1]->s_vec, &delwriter->x_cspace, x, (t_int)sp[0]->s_n); /* check block size - but only if delwriter has been initialized */ if (delwriter->x_cspace.c_n > 0 && sp[0]->s_n > delwriter->x_cspace.c_n) pd_error(x, "delread4~ %s: blocksize larger than delwrite~ buffer", x->x_sym->s_name); } else if (*x->x_sym->s_name) pd_error(x, "delread4~: %s: no such delwrite~",x->x_sym->s_name); } static void sigvd_setup(void) { sigvd_class = class_new(gensym("delread4~"), (t_newmethod)sigvd_new, 0, sizeof(t_sigvd), 0, A_DEFSYM, 0); class_addcreator((t_newmethod)sigvd_new, gensym("vd~"), A_DEFSYM, 0); class_addmethod(sigvd_class, (t_method)sigvd_dsp, gensym("dsp"), A_CANT, 0); CLASS_MAINSIGNALIN(sigvd_class, t_sigvd, x_f); class_sethelpsymbol(sigvd_class, gensym("delay-tilde-objects")); } /* ----------------------- global setup routine ---------------- */ void d_delay_setup(void) { sigdelwrite_setup(); sigdelread_setup(); sigvd_setup(); } ================================================ FILE: libs/libpd/pure-data/src/d_fft.c ================================================ /* Copyright (c) 1997- Miller Puckette and others. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ #include "m_pd.h" /* This file interfaces to one of the Mayer, Ooura, or fftw FFT packages to implement the "fft~", etc, Pd objects. If using Mayer, also compile d_fft_mayer.c; if ooura, use d_fft_fftsg.c instead; if fftw, use d_fft_fftw.c and also link in the fftw library. You can only have one of these three linked in. The configure script can be used to select which one. */ /* ------------------ initialization and cleanup -------------------------- */ void mayer_init( void); void mayer_term( void); static void fftclass_cleanup(t_class *c) { mayer_term(); } /* ---------------- utility functions for DSP chains ---------------------- */ /* swap two arrays */ static t_int *sigfft_swap(t_int *w) { t_sample *in1 = (t_sample *)(w[1]); t_sample *in2 = (t_sample *)(w[2]); int n = (int)w[3]; for (;n--; in1++, in2++) { t_sample f = *in1; *in1 = *in2; *in2 = f; } return (w+4); } /* take array1 (supply a pointer to beginning) and copy it, into decreasing addresses, into array 2 (supply a pointer one past the end), and negate the sign. */ static t_int *sigrfft_flip(t_int *w) { t_sample *in = (t_sample *)(w[1]); t_sample *out = (t_sample *)(w[2]); int n = (int)w[3]; while (n--) *(--out) = - *in++; return (w+4); } /* ------------------------ fft~ and ifft~ -------------------------------- */ static t_class *sigfft_class, *sigifft_class; typedef struct fft { t_object x_obj; t_float x_f; } t_sigfft; static void *sigfft_new(void) { t_sigfft *x = (t_sigfft *)pd_new(sigfft_class); outlet_new(&x->x_obj, gensym("signal")); outlet_new(&x->x_obj, gensym("signal")); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); x->x_f = 0; return (x); } static void *sigifft_new(void) { t_sigfft *x = (t_sigfft *)pd_new(sigifft_class); outlet_new(&x->x_obj, gensym("signal")); outlet_new(&x->x_obj, gensym("signal")); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); x->x_f = 0; return (x); } static t_int *sigfft_perform(t_int *w) { t_sample *in1 = (t_sample *)(w[1]); t_sample *in2 = (t_sample *)(w[2]); int n = (int)w[3]; mayer_fft(n, in1, in2); return (w+4); } static t_int *sigifft_perform(t_int *w) { t_sample *in1 = (t_sample *)(w[1]); t_sample *in2 = (t_sample *)(w[2]); int n = (int)w[3]; mayer_ifft(n, in1, in2); return (w+4); } static void sigfft_dspx(t_sigfft *x, t_signal **sp, t_int *(*f)(t_int *w)) { int length = sp[0]->s_length, nchans = (sp[0]->s_nchans < sp[1]->s_nchans ? sp[0]->s_nchans : sp[1]->s_nchans), ch; if (sp[0]->s_nchans != sp[1]->s_nchans) pd_error(x, "FFT inputs have different channel counts - ignoring extras"); signal_setmultiout(&sp[2], nchans); signal_setmultiout(&sp[3], nchans); if (length < 4 || (length != (1 << ilog2(length)))) { if (length < 4) pd_error(x, "fft: minimum 4 points"); else pd_error(x, "fft: blocksize (%d) not a power of 2", length); dsp_add_zero(sp[2]->s_vec, length * nchans); dsp_add_zero(sp[3]->s_vec, length * nchans); return; } for (ch = 0; ch < nchans; ch++) { t_sample *in1 = sp[0]->s_vec + ch * length; t_sample *in2 = sp[1]->s_vec + ch * length; t_sample *out1 = sp[2]->s_vec + ch * length; t_sample *out2 = sp[3]->s_vec + ch * length; if (out1 == in2 && out2 == in1) dsp_add(sigfft_swap, 3, out1, out2, (t_int)length); else if (out1 == in2) { dsp_add(copy_perform, 3, in2, out2, (t_int)length); dsp_add(copy_perform, 3, in1, out1, (t_int)length); } else { if (out1 != in1) dsp_add(copy_perform, 3, in1, out1, (t_int)length); if (out2 != in2) dsp_add(copy_perform, 3, in2, out2, (t_int)length); } dsp_add(f, 3, out1, out2, (t_int)length); } } static void sigfft_dsp(t_sigfft *x, t_signal **sp) { sigfft_dspx(x, sp, sigfft_perform); } static void sigifft_dsp(t_sigfft *x, t_signal **sp) { sigfft_dspx(x, sp, sigifft_perform); } static void sigfft_setup(void) { sigfft_class = class_new(gensym("fft~"), sigfft_new, 0, sizeof(t_sigfft), CLASS_MULTICHANNEL, 0); class_setfreefn(sigfft_class, fftclass_cleanup); CLASS_MAINSIGNALIN(sigfft_class, t_sigfft, x_f); class_addmethod(sigfft_class, (t_method)sigfft_dsp, gensym("dsp"), A_CANT, 0); mayer_init(); sigifft_class = class_new(gensym("ifft~"), sigifft_new, 0, sizeof(t_sigfft), CLASS_MULTICHANNEL, 0); class_setfreefn(sigifft_class, fftclass_cleanup); CLASS_MAINSIGNALIN(sigifft_class, t_sigfft, x_f); class_addmethod(sigifft_class, (t_method)sigifft_dsp, gensym("dsp"), A_CANT, 0); class_sethelpsymbol(sigifft_class, gensym("fft~")); mayer_init(); } /* ----------------------- rfft~ -------------------------------- */ static t_class *sigrfft_class; typedef struct rfft { t_object x_obj; t_float x_f; } t_sigrfft; static void *sigrfft_new(void) { t_sigrfft *x = (t_sigrfft *)pd_new(sigrfft_class); outlet_new(&x->x_obj, gensym("signal")); outlet_new(&x->x_obj, gensym("signal")); x->x_f = 0; return (x); } static t_int *sigrfft_perform(t_int *w) { t_sample *in = (t_sample *)(w[1]); int n = (int)w[2]; mayer_realfft(n, in); return (w+3); } static void sigrfft_dsp(t_sigrfft *x, t_signal **sp) { int length = sp[0]->s_length, n2 = (length>>1), ch; int nchans = sp[0]->s_nchans; signal_setmultiout(&sp[1], nchans); signal_setmultiout(&sp[2], nchans); if (length < 4 || (length != (1 << ilog2(length)))) { if (length < 4) pd_error(x, "fft: minimum 4 points"); else pd_error(x, "fft: blocksize (%d) not a power of 2", length); dsp_add_zero(sp[1]->s_vec, length * nchans); dsp_add_zero(sp[2]->s_vec, length * nchans); return; } for (ch = 0; ch < nchans; ch++) { t_sample *in1 = sp[0]->s_vec + ch * length; t_sample *out1 = sp[1]->s_vec + ch * length; t_sample *out2 = sp[2]->s_vec + ch * length; if (in1 != out1) dsp_add(copy_perform, 3, in1, out1, (t_int)length); dsp_add(sigrfft_perform, 2, out1, (t_int)length); dsp_add(sigrfft_flip, 3, out1 + (n2+1), out2 + n2, (t_int)(n2-1)); dsp_add_zero(out1 + (n2+1), ((n2-1)&(~7))); dsp_add_zero(out1 + (n2+1) + ((n2-1)&(~7)), ((n2-1)&7)); dsp_add_zero(out2 + n2, n2); dsp_add_zero(out2, 1); } } static void sigrfft_setup(void) { sigrfft_class = class_new(gensym("rfft~"), sigrfft_new, 0, sizeof(t_sigrfft), CLASS_MULTICHANNEL, 0); class_setfreefn(sigrfft_class, fftclass_cleanup); CLASS_MAINSIGNALIN(sigrfft_class, t_sigrfft, x_f); class_addmethod(sigrfft_class, (t_method)sigrfft_dsp, gensym("dsp"), A_CANT, 0); class_sethelpsymbol(sigrfft_class, gensym("fft~")); mayer_init(); } /* ----------------------- rifft~ -------------------------------- */ static t_class *sigrifft_class; typedef struct rifft { t_object x_obj; t_float x_f; } t_sigrifft; static void *sigrifft_new(void) { t_sigrifft *x = (t_sigrifft *)pd_new(sigrifft_class); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); outlet_new(&x->x_obj, gensym("signal")); x->x_f = 0; return (x); } static t_int *sigrifft_perform(t_int *w) { t_sample *in = (t_sample *)(w[1]); int n = (int)w[2]; mayer_realifft(n, in); return (w+3); } static void sigrifft_dsp(t_sigrifft *x, t_signal **sp) { int length = sp[0]->s_length, n2 = (length>>1), nchans = (sp[0]->s_nchans < sp[1]->s_nchans ? sp[0]->s_nchans : sp[1]->s_nchans), ch; if (sp[0]->s_nchans != sp[1]->s_nchans) pd_error(x, "rifft~ inputs have different channel counts - ignoring extras"); signal_setmultiout(&sp[2], nchans); if (length < 4 || (length != (1 << ilog2(length)))) { if (length < 4) pd_error(x, "fft: minimum 4 points"); else pd_error(x, "fft: blocksize (%d) not a power of 2", length); dsp_add_zero(sp[2]->s_vec, length * nchans); return; } for (ch = 0; ch < nchans; ch++) { t_sample *in1 = sp[0]->s_vec + ch * length; t_sample *in2 = sp[1]->s_vec + ch * length; t_sample *out1 = sp[2]->s_vec + ch * length; if (in2 == out1) { dsp_add(sigrfft_flip, 3, out1+1, out1 + length, (t_int)(n2-1)); dsp_add(copy_perform, 3, in1, out1, (t_int)(n2+1)); } else { if (in1 != out1) dsp_add(copy_perform, 3, in1, out1, (t_int)(n2+1)); dsp_add(sigrfft_flip, 3, in2+1, out1 + length, (t_int)(n2-1)); } dsp_add(sigrifft_perform, 2, out1, (t_int)length); } } static void sigrifft_setup(void) { sigrifft_class = class_new(gensym("rifft~"), sigrifft_new, 0, sizeof(t_sigrifft), CLASS_MULTICHANNEL, 0); class_setfreefn(sigrifft_class, fftclass_cleanup); CLASS_MAINSIGNALIN(sigrifft_class, t_sigrifft, x_f); class_addmethod(sigrifft_class, (t_method)sigrifft_dsp, gensym("dsp"), A_CANT, 0); class_sethelpsymbol(sigrifft_class, gensym("fft~")); mayer_init(); } /* ----------------------- framp~ -------------------------------- */ static t_class *sigframp_class; typedef struct framp { t_object x_obj; t_float x_f; } t_sigframp; static void *sigframp_new(void) { t_sigframp *x = (t_sigframp *)pd_new(sigframp_class); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); outlet_new(&x->x_obj, gensym("signal")); outlet_new(&x->x_obj, gensym("signal")); /* q8_rsqrt() triggers init_rsqrt() as a side-effect */ q8_rsqrt(-1.); x->x_f = 0; return (x); } static t_int *sigframp_perform(t_int *w) { t_sample *inreal = (t_sample *)(w[1]); t_sample *inimag = (t_sample *)(w[2]); t_sample *outfreq = (t_sample *)(w[3]); t_sample *outamp = (t_sample *)(w[4]); t_sample lastreal = 0, currentreal = inreal[0], nextreal = inreal[1]; t_sample lastimag = 0, currentimag = inimag[0], nextimag = inimag[1]; int n = (int)w[5]; int m = n + 1; t_sample fbin = 1, oneovern2 = 1.f/((t_sample)n * (t_sample)n); inreal += 2; inimag += 2; *outamp++ = *outfreq++ = 0; n -= 2; while (n--) { t_sample re, im, pow, freq; lastreal = currentreal; currentreal = nextreal; nextreal = *inreal++; lastimag = currentimag; currentimag = nextimag; nextimag = *inimag++; re = currentreal - 0.5f * (lastreal + nextreal); im = currentimag - 0.5f * (lastimag + nextimag); pow = re * re + im * im; if (pow > 1e-19) { t_sample detune = ((lastreal - nextreal) * re + (lastimag - nextimag) * im) / (2.0f * pow); if (detune > 2 || detune < -2) freq = pow = 0; else freq = fbin + detune; } else freq = pow = 0; *outfreq++ = freq; *outamp++ = oneovern2 * pow; fbin += 1.0f; } while (m--) *outamp++ = *outfreq++ = 0; return (w+6); } t_int *sigsqrt_perform(t_int *w); static void sigframp_dsp(t_sigframp *x, t_signal **sp) { int n = sp[0]->s_n, n2 = (n>>1); if (n < 4) { pd_error(0, "framp: minimum 4 points"); return; } dsp_add(sigframp_perform, 5, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec, (t_int)n2); dsp_add(sigsqrt_perform, 3, sp[3]->s_vec, sp[3]->s_vec, (t_int)n2); } static void sigframp_setup(void) { sigframp_class = class_new(gensym("framp~"), sigframp_new, 0, sizeof(t_sigframp), 0, 0); class_setfreefn(sigframp_class, fftclass_cleanup); CLASS_MAINSIGNALIN(sigframp_class, t_sigframp, x_f); class_addmethod(sigframp_class, (t_method)sigframp_dsp, gensym("dsp"), A_CANT, 0); mayer_init(); } /* ------------------------ global setup routine ------------------------- */ void d_fft_setup(void) { sigfft_setup(); sigrfft_setup(); sigrifft_setup(); sigframp_setup(); } ================================================ FILE: libs/libpd/pure-data/src/d_fft_fftsg.c ================================================ /****************** begin Pd-specific prologue ***********************/ /* This is the file, "fftsg.c" from the OOURA FFT package. The copyright notice from Ooura's README is: Copyright: Copyright(C) 1996-2001 Takuya OOURA email: ooura@mmm.t.u-tokyo.ac.jp download: http://momonga.t.u-tokyo.ac.jp/~ooura/fft.html You may use, copy, modify this code for any purpose and without fee. You may distribute this ORIGINAL package. After the following prologue, the code is essentially Ooura's original, which I believe the above notice permits me to redistribute; the only change is to replace 'double' with a macro, 'FFTFLT', so that it can optionally be run in single precision for greater speed. See Ooura's website for another, more permissive-sounding copyright notice. -MSP */ /* ---------- Pd interface to OOURA FFT; imitate Mayer API ---------- */ #include "m_pd.h" #include "m_imp.h" #include "m_private_utils.h" #define FFTFLT double void cdft(int, int, FFTFLT *, int *, FFTFLT *); void rdft(int, int, FFTFLT *, int *, FFTFLT *); int ilog2(int n); static PERTHREAD int ooura_maxn; static PERTHREAD int *ooura_bitrev; static PERTHREAD int ooura_bitrevsize; static PERTHREAD FFTFLT *ooura_costab; static PERTHREAD FFTFLT *ooura_buffer; static int ooura_init( int n) { n = (1 << ilog2(n)); if (n < 4) return (0); if (n > ooura_maxn) { if (ooura_maxn) { t_freebytes(ooura_bitrev, ooura_bitrevsize); t_freebytes(ooura_costab, ooura_maxn * sizeof(FFTFLT) / 2); t_freebytes(ooura_buffer, ooura_maxn * sizeof(FFTFLT)); } ooura_bitrevsize = sizeof(int) * (2 + (1 << (ilog2(n)/2))); ooura_bitrev = (int *)t_getbytes(ooura_bitrevsize); ooura_bitrev[0] = 0; if (!ooura_bitrev) { pd_error(0, "out of memory allocating FFT buffer"); ooura_maxn = 0; return (0); } ooura_costab = (FFTFLT *)t_getbytes(n * sizeof(FFTFLT)/2); if (!ooura_costab) { pd_error(0, "out of memory allocating FFT buffer"); t_freebytes(ooura_bitrev, ooura_bitrevsize); ooura_maxn = 0; return (0); } ooura_buffer = (FFTFLT *)t_getbytes(n * sizeof(FFTFLT)); if (!ooura_buffer) { pd_error(0, "out of memory allocating FFT buffer"); t_freebytes(ooura_bitrev, ooura_bitrevsize); t_freebytes(ooura_costab, n * sizeof(FFTFLT) / 2); ooura_maxn = 0; return (0); } ooura_maxn = n; ooura_bitrev[0] = 0; } return (1); } static void ooura_term( void) { if (!ooura_maxn) return; t_freebytes(ooura_bitrev, ooura_bitrevsize); t_freebytes(ooura_costab, ooura_maxn * sizeof(FFTFLT)/2); t_freebytes(ooura_buffer, ooura_maxn * sizeof(FFTFLT)); ooura_maxn = 0; ooura_bitrev = 0; ooura_bitrevsize = 0; ooura_costab = 0; } /* -------- initialization and cleanup -------- */ static PERTHREAD int mayer_refcount = 0; void mayer_init( void) { mayer_refcount++; } void mayer_term( void) { if (--mayer_refcount == 0) /* clean up */ ooura_term(); } /* -------- public routines -------- */ EXTERN void mayer_fht(t_sample *fz, int n) { post("FHT: not yet implemented"); } EXTERN void mayer_dofft(t_sample *fz1, t_sample *fz2, int n, int sgn) { FFTFLT *buf, *fp3; int i; t_sample *fp1, *fp2; if (!ooura_init(2*n)) return; buf = ooura_buffer; for (i = 0, fp1 = fz1, fp2 = fz2, fp3 = buf; i < n; i++) { fp3[0] = *fp1++; fp3[1] = *fp2++; fp3 += 2; } cdft(2*n, sgn, buf, ooura_bitrev, ooura_costab); for (i = 0, fp1 = fz1, fp2 = fz2, fp3 = buf; i < n; i++) { *fp1++ = fp3[0]; *fp2++ = fp3[1]; fp3 += 2; } } EXTERN void mayer_fft(int n, t_sample *fz1, t_sample *fz2) { mayer_dofft(fz1, fz2, n, -1); } EXTERN void mayer_ifft(int n, t_sample *fz1, t_sample *fz2) { mayer_dofft(fz1, fz2, n, 1); } EXTERN void mayer_realfft(int n, t_sample *fz) { FFTFLT *buf, *fp3; int i, nover2 = n/2; t_sample *fp1, *fp2; if (!ooura_init(n)) return; buf = ooura_buffer; for (i = 0, fp1 = fz, fp3 = buf; i < n; i++, fp1++, fp3++) buf[i] = fz[i]; rdft(n, 1, buf, ooura_bitrev, ooura_costab); fz[0] = buf[0]; fz[nover2] = buf[1]; for (i = 1, fp1 = fz+1, fp2 = fz+(n-1), fp3 = buf+2; i < nover2; i++, fp1++, fp2--, fp3 += 2) *fp1 = fp3[0], *fp2 = fp3[1]; } EXTERN void mayer_realifft(int n, t_sample *fz) { FFTFLT *buf, *fp3; int i, nover2 = n/2; t_sample *fp1, *fp2; if (!ooura_init(n)) return; buf = ooura_buffer; buf[0] = fz[0]; buf[1] = fz[nover2]; for (i = 1, fp1 = fz+1, fp2 = fz+(n-1), fp3 = buf+2; i < nover2; i++, fp1++, fp2--, fp3 += 2) fp3[0] = *fp1, fp3[1] = *fp2; rdft(n, -1, buf, ooura_bitrev, ooura_costab); for (i = 0, fp1 = fz, fp3 = buf; i < n; i++, fp1++, fp3++) fz[i] = 2*buf[i]; } /* ancient ISPW-like version, used in fiddle~ and perhaps other externs here and there. */ void pd_fft(t_float *buf, int npoints, int inverse) { FFTFLT *buf2 = (FFTFLT *)alloca(2 * npoints * sizeof(FFTFLT)), *bp2; t_float *fp; int i; if (!ooura_init(2*npoints)) return; for (i = 0, bp2 = buf2, fp = buf; i < 2 * npoints; i++, bp2++, fp++) *bp2 = *fp; cdft(2*npoints, (inverse ? 1 : -1), buf2, ooura_bitrev, ooura_costab); for (i = 0, bp2 = buf2, fp = buf; i < 2 * npoints; i++, bp2++, fp++) *fp = *bp2; } /****************** end Pd-specific prologue ***********************/ /* Fast Fourier/Cosine/Sine Transform dimension :one data length :power of 2 decimation :frequency radix :split-radix data :inplace table :use functions cdft: Complex Discrete Fourier Transform rdft: Real Discrete Fourier Transform ddct: Discrete Cosine Transform ddst: Discrete Sine Transform dfct: Cosine Transform of RDFT (Real Symmetric DFT) dfst: Sine Transform of RDFT (Real Anti-symmetric DFT) function prototypes void cdft(int, int, FFTFLT *, int *, FFTFLT *); void rdft(int, int, FFTFLT *, int *, FFTFLT *); void ddct(int, int, FFTFLT *, int *, FFTFLT *); void ddst(int, int, FFTFLT *, int *, FFTFLT *); void dfct(int, FFTFLT *, FFTFLT *, int *, FFTFLT *); void dfst(int, FFTFLT *, FFTFLT *, int *, FFTFLT *); macro definitions USE_CDFT_PTHREADS : default=not defined CDFT_THREADS_BEGIN_N : must be >= 512, default=8192 CDFT_4THREADS_BEGIN_N : must be >= 512, default=65536 USE_CDFT_WINTHREADS : default=not defined CDFT_THREADS_BEGIN_N : must be >= 512, default=32768 CDFT_4THREADS_BEGIN_N : must be >= 512, default=524288 -------- Complex DFT (Discrete Fourier Transform) -------- [definition] X[k] = sum_j=0^n-1 x[j]*exp(2*pi*i*j*k/n), 0<=k X[k] = sum_j=0^n-1 x[j]*exp(-2*pi*i*j*k/n), 0<=k ip[0] = 0; // first time only cdft(2*n, 1, a, ip, w); ip[0] = 0; // first time only cdft(2*n, -1, a, ip, w); [parameters] 2*n :data length (int) n >= 1, n = power of 2 a[0...2*n-1] :input/output data (FFTFLT *) input data a[2*j] = Re(x[j]), a[2*j+1] = Im(x[j]), 0<=j= 2+sqrt(n) strictly, length of ip >= 2+(1<<(int)(log(n+0.5)/log(2))/2). ip[0],ip[1] are pointers of the cos/sin table. w[0...n/2-1] :cos/sin table (FFTFLT *) w[],ip[] are initialized if ip[0] == 0. [remark] Inverse of cdft(2*n, -1, a, ip, w); is cdft(2*n, 1, a, ip, w); for (j = 0; j <= 2 * n - 1; j++) { a[j] *= 1.0 / n; } . -------- Real DFT / Inverse of Real DFT -------- [definition] RDFT R[k] = sum_j=0^n-1 a[j]*cos(2*pi*j*k/n), 0<=k<=n/2 I[k] = sum_j=0^n-1 a[j]*sin(2*pi*j*k/n), 0 IRDFT (excluding scale) a[k] = (R[0] + R[n/2]*cos(pi*k))/2 + sum_j=1^n/2-1 R[j]*cos(2*pi*j*k/n) + sum_j=1^n/2-1 I[j]*sin(2*pi*j*k/n), 0<=k ip[0] = 0; // first time only rdft(n, 1, a, ip, w); ip[0] = 0; // first time only rdft(n, -1, a, ip, w); [parameters] n :data length (int) n >= 2, n = power of 2 a[0...n-1] :input/output data (FFTFLT *) output data a[2*k] = R[k], 0<=k input data a[2*j] = R[j], 0<=j= 2+sqrt(n/2) strictly, length of ip >= 2+(1<<(int)(log(n/2+0.5)/log(2))/2). ip[0],ip[1] are pointers of the cos/sin table. w[0...n/2-1] :cos/sin table (FFTFLT *) w[],ip[] are initialized if ip[0] == 0. [remark] Inverse of rdft(n, 1, a, ip, w); is rdft(n, -1, a, ip, w); for (j = 0; j <= n - 1; j++) { a[j] *= 2.0 / n; } . -------- DCT (Discrete Cosine Transform) / Inverse of DCT -------- [definition] IDCT (excluding scale) C[k] = sum_j=0^n-1 a[j]*cos(pi*j*(k+1/2)/n), 0<=k DCT C[k] = sum_j=0^n-1 a[j]*cos(pi*(j+1/2)*k/n), 0<=k ip[0] = 0; // first time only ddct(n, 1, a, ip, w); ip[0] = 0; // first time only ddct(n, -1, a, ip, w); [parameters] n :data length (int) n >= 2, n = power of 2 a[0...n-1] :input/output data (FFTFLT *) output data a[k] = C[k], 0<=k= 2+sqrt(n/2) strictly, length of ip >= 2+(1<<(int)(log(n/2+0.5)/log(2))/2). ip[0],ip[1] are pointers of the cos/sin table. w[0...n*5/4-1] :cos/sin table (FFTFLT *) w[],ip[] are initialized if ip[0] == 0. [remark] Inverse of ddct(n, -1, a, ip, w); is a[0] *= 0.5; ddct(n, 1, a, ip, w); for (j = 0; j <= n - 1; j++) { a[j] *= 2.0 / n; } . -------- DST (Discrete Sine Transform) / Inverse of DST -------- [definition] IDST (excluding scale) S[k] = sum_j=1^n A[j]*sin(pi*j*(k+1/2)/n), 0<=k DST S[k] = sum_j=0^n-1 a[j]*sin(pi*(j+1/2)*k/n), 0 ip[0] = 0; // first time only ddst(n, 1, a, ip, w); ip[0] = 0; // first time only ddst(n, -1, a, ip, w); [parameters] n :data length (int) n >= 2, n = power of 2 a[0...n-1] :input/output data (FFTFLT *) input data a[j] = A[j], 0 output data a[k] = S[k], 0= 2+sqrt(n/2) strictly, length of ip >= 2+(1<<(int)(log(n/2+0.5)/log(2))/2). ip[0],ip[1] are pointers of the cos/sin table. w[0...n*5/4-1] :cos/sin table (FFTFLT *) w[],ip[] are initialized if ip[0] == 0. [remark] Inverse of ddst(n, -1, a, ip, w); is a[0] *= 0.5; ddst(n, 1, a, ip, w); for (j = 0; j <= n - 1; j++) { a[j] *= 2.0 / n; } . -------- Cosine Transform of RDFT (Real Symmetric DFT) -------- [definition] C[k] = sum_j=0^n a[j]*cos(pi*j*k/n), 0<=k<=n [usage] ip[0] = 0; // first time only dfct(n, a, t, ip, w); [parameters] n :data length - 1 (int) n >= 2, n = power of 2 a[0...n] :input/output data (FFTFLT *) output data a[k] = C[k], 0<=k<=n t[0...n/2] :work area (FFTFLT *) ip[0...*] :work area for bit reversal (int *) length of ip >= 2+sqrt(n/4) strictly, length of ip >= 2+(1<<(int)(log(n/4+0.5)/log(2))/2). ip[0],ip[1] are pointers of the cos/sin table. w[0...n*5/8-1] :cos/sin table (FFTFLT *) w[],ip[] are initialized if ip[0] == 0. [remark] Inverse of a[0] *= 0.5; a[n] *= 0.5; dfct(n, a, t, ip, w); is a[0] *= 0.5; a[n] *= 0.5; dfct(n, a, t, ip, w); for (j = 0; j <= n; j++) { a[j] *= 2.0 / n; } . -------- Sine Transform of RDFT (Real Anti-symmetric DFT) -------- [definition] S[k] = sum_j=1^n-1 a[j]*sin(pi*j*k/n), 0= 2, n = power of 2 a[0...n-1] :input/output data (FFTFLT *) output data a[k] = S[k], 0= 2+sqrt(n/4) strictly, length of ip >= 2+(1<<(int)(log(n/4+0.5)/log(2))/2). ip[0],ip[1] are pointers of the cos/sin table. w[0...n*5/8-1] :cos/sin table (FFTFLT *) w[],ip[] are initialized if ip[0] == 0. [remark] Inverse of dfst(n, a, t, ip, w); is dfst(n, a, t, ip, w); for (j = 1; j <= n - 1; j++) { a[j] *= 2.0 / n; } . Appendix : The cos/sin table is recalculated when the larger table required. w[] and ip[] are compatible with all routines. */ void cdft(int n, int isgn, FFTFLT *a, int *ip, FFTFLT *w) { void makewt(int nw, int *ip, FFTFLT *w); void cftfsub(int n, FFTFLT *a, int *ip, int nw, FFTFLT *w); void cftbsub(int n, FFTFLT *a, int *ip, int nw, FFTFLT *w); int nw; nw = ip[0]; if (n > (nw << 2)) { nw = n >> 2; makewt(nw, ip, w); } if (isgn >= 0) { cftfsub(n, a, ip, nw, w); } else { cftbsub(n, a, ip, nw, w); } } void rdft(int n, int isgn, FFTFLT *a, int *ip, FFTFLT *w) { void makewt(int nw, int *ip, FFTFLT *w); void makect(int nc, int *ip, FFTFLT *c); void cftfsub(int n, FFTFLT *a, int *ip, int nw, FFTFLT *w); void cftbsub(int n, FFTFLT *a, int *ip, int nw, FFTFLT *w); void rftfsub(int n, FFTFLT *a, int nc, FFTFLT *c); void rftbsub(int n, FFTFLT *a, int nc, FFTFLT *c); int nw, nc; FFTFLT xi; nw = ip[0]; if (n > (nw << 2)) { nw = n >> 2; makewt(nw, ip, w); } nc = ip[1]; if (n > (nc << 2)) { nc = n >> 2; makect(nc, ip, w + nw); } if (isgn >= 0) { if (n > 4) { cftfsub(n, a, ip, nw, w); rftfsub(n, a, nc, w + nw); } else if (n == 4) { cftfsub(n, a, ip, nw, w); } xi = a[0] - a[1]; a[0] += a[1]; a[1] = xi; } else { a[1] = 0.5 * (a[0] - a[1]); a[0] -= a[1]; if (n > 4) { rftbsub(n, a, nc, w + nw); cftbsub(n, a, ip, nw, w); } else if (n == 4) { cftbsub(n, a, ip, nw, w); } } } void ddct(int n, int isgn, FFTFLT *a, int *ip, FFTFLT *w) { void makewt(int nw, int *ip, FFTFLT *w); void makect(int nc, int *ip, FFTFLT *c); void cftfsub(int n, FFTFLT *a, int *ip, int nw, FFTFLT *w); void cftbsub(int n, FFTFLT *a, int *ip, int nw, FFTFLT *w); void rftfsub(int n, FFTFLT *a, int nc, FFTFLT *c); void rftbsub(int n, FFTFLT *a, int nc, FFTFLT *c); void dctsub(int n, FFTFLT *a, int nc, FFTFLT *c); int j, nw, nc; FFTFLT xr; nw = ip[0]; if (n > (nw << 2)) { nw = n >> 2; makewt(nw, ip, w); } nc = ip[1]; if (n > nc) { nc = n; makect(nc, ip, w + nw); } if (isgn < 0) { xr = a[n - 1]; for (j = n - 2; j >= 2; j -= 2) { a[j + 1] = a[j] - a[j - 1]; a[j] += a[j - 1]; } a[1] = a[0] - xr; a[0] += xr; if (n > 4) { rftbsub(n, a, nc, w + nw); cftbsub(n, a, ip, nw, w); } else if (n == 4) { cftbsub(n, a, ip, nw, w); } } dctsub(n, a, nc, w + nw); if (isgn >= 0) { if (n > 4) { cftfsub(n, a, ip, nw, w); rftfsub(n, a, nc, w + nw); } else if (n == 4) { cftfsub(n, a, ip, nw, w); } xr = a[0] - a[1]; a[0] += a[1]; for (j = 2; j < n; j += 2) { a[j - 1] = a[j] - a[j + 1]; a[j] += a[j + 1]; } a[n - 1] = xr; } } void ddst(int n, int isgn, FFTFLT *a, int *ip, FFTFLT *w) { void makewt(int nw, int *ip, FFTFLT *w); void makect(int nc, int *ip, FFTFLT *c); void cftfsub(int n, FFTFLT *a, int *ip, int nw, FFTFLT *w); void cftbsub(int n, FFTFLT *a, int *ip, int nw, FFTFLT *w); void rftfsub(int n, FFTFLT *a, int nc, FFTFLT *c); void rftbsub(int n, FFTFLT *a, int nc, FFTFLT *c); void dstsub(int n, FFTFLT *a, int nc, FFTFLT *c); int j, nw, nc; FFTFLT xr; nw = ip[0]; if (n > (nw << 2)) { nw = n >> 2; makewt(nw, ip, w); } nc = ip[1]; if (n > nc) { nc = n; makect(nc, ip, w + nw); } if (isgn < 0) { xr = a[n - 1]; for (j = n - 2; j >= 2; j -= 2) { a[j + 1] = -a[j] - a[j - 1]; a[j] -= a[j - 1]; } a[1] = a[0] + xr; a[0] -= xr; if (n > 4) { rftbsub(n, a, nc, w + nw); cftbsub(n, a, ip, nw, w); } else if (n == 4) { cftbsub(n, a, ip, nw, w); } } dstsub(n, a, nc, w + nw); if (isgn >= 0) { if (n > 4) { cftfsub(n, a, ip, nw, w); rftfsub(n, a, nc, w + nw); } else if (n == 4) { cftfsub(n, a, ip, nw, w); } xr = a[0] - a[1]; a[0] += a[1]; for (j = 2; j < n; j += 2) { a[j - 1] = -a[j] - a[j + 1]; a[j] -= a[j + 1]; } a[n - 1] = -xr; } } void dfct(int n, FFTFLT *a, FFTFLT *t, int *ip, FFTFLT *w) { void makewt(int nw, int *ip, FFTFLT *w); void makect(int nc, int *ip, FFTFLT *c); void cftfsub(int n, FFTFLT *a, int *ip, int nw, FFTFLT *w); void rftfsub(int n, FFTFLT *a, int nc, FFTFLT *c); void dctsub(int n, FFTFLT *a, int nc, FFTFLT *c); int j, k, l, m, mh, nw, nc; FFTFLT xr, xi, yr, yi; nw = ip[0]; if (n > (nw << 3)) { nw = n >> 3; makewt(nw, ip, w); } nc = ip[1]; if (n > (nc << 1)) { nc = n >> 1; makect(nc, ip, w + nw); } m = n >> 1; yi = a[m]; xi = a[0] + a[n]; a[0] -= a[n]; t[0] = xi - yi; t[m] = xi + yi; if (n > 2) { mh = m >> 1; for (j = 1; j < mh; j++) { k = m - j; xr = a[j] - a[n - j]; xi = a[j] + a[n - j]; yr = a[k] - a[n - k]; yi = a[k] + a[n - k]; a[j] = xr; a[k] = yr; t[j] = xi - yi; t[k] = xi + yi; } t[mh] = a[mh] + a[n - mh]; a[mh] -= a[n - mh]; dctsub(m, a, nc, w + nw); if (m > 4) { cftfsub(m, a, ip, nw, w); rftfsub(m, a, nc, w + nw); } else if (m == 4) { cftfsub(m, a, ip, nw, w); } a[n - 1] = a[0] - a[1]; a[1] = a[0] + a[1]; for (j = m - 2; j >= 2; j -= 2) { a[2 * j + 1] = a[j] + a[j + 1]; a[2 * j - 1] = a[j] - a[j + 1]; } l = 2; m = mh; while (m >= 2) { dctsub(m, t, nc, w + nw); if (m > 4) { cftfsub(m, t, ip, nw, w); rftfsub(m, t, nc, w + nw); } else if (m == 4) { cftfsub(m, t, ip, nw, w); } a[n - l] = t[0] - t[1]; a[l] = t[0] + t[1]; k = 0; for (j = 2; j < m; j += 2) { k += l << 2; a[k - l] = t[j] - t[j + 1]; a[k + l] = t[j] + t[j + 1]; } l <<= 1; mh = m >> 1; for (j = 0; j < mh; j++) { k = m - j; t[j] = t[m + k] - t[m + j]; t[k] = t[m + k] + t[m + j]; } t[mh] = t[m + mh]; m = mh; } a[l] = t[0]; a[n] = t[2] - t[1]; a[0] = t[2] + t[1]; } else { a[1] = a[0]; a[2] = t[0]; a[0] = t[1]; } } void dfst(int n, FFTFLT *a, FFTFLT *t, int *ip, FFTFLT *w) { void makewt(int nw, int *ip, FFTFLT *w); void makect(int nc, int *ip, FFTFLT *c); void cftfsub(int n, FFTFLT *a, int *ip, int nw, FFTFLT *w); void rftfsub(int n, FFTFLT *a, int nc, FFTFLT *c); void dstsub(int n, FFTFLT *a, int nc, FFTFLT *c); int j, k, l, m, mh, nw, nc; FFTFLT xr, xi, yr, yi; nw = ip[0]; if (n > (nw << 3)) { nw = n >> 3; makewt(nw, ip, w); } nc = ip[1]; if (n > (nc << 1)) { nc = n >> 1; makect(nc, ip, w + nw); } if (n > 2) { m = n >> 1; mh = m >> 1; for (j = 1; j < mh; j++) { k = m - j; xr = a[j] + a[n - j]; xi = a[j] - a[n - j]; yr = a[k] + a[n - k]; yi = a[k] - a[n - k]; a[j] = xr; a[k] = yr; t[j] = xi + yi; t[k] = xi - yi; } t[0] = a[mh] - a[n - mh]; a[mh] += a[n - mh]; a[0] = a[m]; dstsub(m, a, nc, w + nw); if (m > 4) { cftfsub(m, a, ip, nw, w); rftfsub(m, a, nc, w + nw); } else if (m == 4) { cftfsub(m, a, ip, nw, w); } a[n - 1] = a[1] - a[0]; a[1] = a[0] + a[1]; for (j = m - 2; j >= 2; j -= 2) { a[2 * j + 1] = a[j] - a[j + 1]; a[2 * j - 1] = -a[j] - a[j + 1]; } l = 2; m = mh; while (m >= 2) { dstsub(m, t, nc, w + nw); if (m > 4) { cftfsub(m, t, ip, nw, w); rftfsub(m, t, nc, w + nw); } else if (m == 4) { cftfsub(m, t, ip, nw, w); } a[n - l] = t[1] - t[0]; a[l] = t[0] + t[1]; k = 0; for (j = 2; j < m; j += 2) { k += l << 2; a[k - l] = -t[j] - t[j + 1]; a[k + l] = t[j] - t[j + 1]; } l <<= 1; mh = m >> 1; for (j = 1; j < mh; j++) { k = m - j; t[j] = t[m + k] + t[m + j]; t[k] = t[m + k] - t[m + j]; } t[0] = t[m + mh]; m = mh; } a[l] = t[0]; } a[0] = 0; } /* -------- initializing routines -------- */ #include void makewt(int nw, int *ip, FFTFLT *w) { void makeipt(int nw, int *ip); int j, nwh, nw0, nw1; FFTFLT delta, wn4r, wk1r, wk1i, wk3r, wk3i; ip[0] = nw; ip[1] = 1; if (nw > 2) { nwh = nw >> 1; delta = atan(1.0) / nwh; wn4r = cos(delta * nwh); w[0] = 1; w[1] = wn4r; if (nwh == 4) { w[2] = cos(delta * 2); w[3] = sin(delta * 2); } else if (nwh > 4) { makeipt(nw, ip); w[2] = 0.5 / cos(delta * 2); w[3] = 0.5 / cos(delta * 6); for (j = 4; j < nwh; j += 4) { w[j] = cos(delta * j); w[j + 1] = sin(delta * j); w[j + 2] = cos(3 * delta * j); w[j + 3] = -sin(3 * delta * j); } } nw0 = 0; while (nwh > 2) { nw1 = nw0 + nwh; nwh >>= 1; w[nw1] = 1; w[nw1 + 1] = wn4r; if (nwh == 4) { wk1r = w[nw0 + 4]; wk1i = w[nw0 + 5]; w[nw1 + 2] = wk1r; w[nw1 + 3] = wk1i; } else if (nwh > 4) { wk1r = w[nw0 + 4]; wk3r = w[nw0 + 6]; w[nw1 + 2] = 0.5 / wk1r; w[nw1 + 3] = 0.5 / wk3r; for (j = 4; j < nwh; j += 4) { wk1r = w[nw0 + 2 * j]; wk1i = w[nw0 + 2 * j + 1]; wk3r = w[nw0 + 2 * j + 2]; wk3i = w[nw0 + 2 * j + 3]; w[nw1 + j] = wk1r; w[nw1 + j + 1] = wk1i; w[nw1 + j + 2] = wk3r; w[nw1 + j + 3] = wk3i; } } nw0 = nw1; } } } void makeipt(int nw, int *ip) { int j, l, m, m2, p, q; ip[2] = 0; ip[3] = 16; m = 2; for (l = nw; l > 32; l >>= 2) { m2 = m << 1; q = m2 << 3; for (j = m; j < m2; j++) { p = ip[j] << 2; ip[m + j] = p; ip[m2 + j] = p + q; } m = m2; } } void makect(int nc, int *ip, FFTFLT *c) { int j, nch; FFTFLT delta; ip[1] = nc; if (nc > 1) { nch = nc >> 1; delta = atan(1.0) / nch; c[0] = cos(delta * nch); c[nch] = 0.5 * c[0]; for (j = 1; j < nch; j++) { c[j] = 0.5 * cos(delta * j); c[nc - j] = 0.5 * sin(delta * j); } } } /* -------- child routines -------- */ #ifdef USE_CDFT_PTHREADS #define USE_CDFT_THREADS #ifndef CDFT_THREADS_BEGIN_N #define CDFT_THREADS_BEGIN_N 8192 #endif #ifndef CDFT_4THREADS_BEGIN_N #define CDFT_4THREADS_BEGIN_N 65536 #endif #include #include #include #define cdft_thread_t pthread_t #define cdft_thread_create(thp,func,argp) { \ if (pthread_create(thp, NULL, func, (void *) argp) != 0) { \ fprintf(stderr, "cdft thread error\n"); \ exit(1); \ } \ } #define cdft_thread_wait(th) { \ if (pthread_join(th, NULL) != 0) { \ fprintf(stderr, "cdft thread error\n"); \ exit(1); \ } \ } #endif /* USE_CDFT_PTHREADS */ #ifdef USE_CDFT_WINTHREADS #define USE_CDFT_THREADS #ifndef CDFT_THREADS_BEGIN_N #define CDFT_THREADS_BEGIN_N 32768 #endif #ifndef CDFT_4THREADS_BEGIN_N #define CDFT_4THREADS_BEGIN_N 524288 #endif #include #include #include #define cdft_thread_t HANDLE #define cdft_thread_create(thp,func,argp) { \ DWORD thid; \ *(thp) = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) func, (LPVOID) argp, 0, &thid); \ if (*(thp) == 0) { \ fprintf(stderr, "cdft thread error\n"); \ exit(1); \ } \ } #define cdft_thread_wait(th) { \ WaitForSingleObject(th, INFINITE); \ CloseHandle(th); \ } #endif /* USE_CDFT_WINTHREADS */ void cftfsub(int n, FFTFLT *a, int *ip, int nw, FFTFLT *w) { void bitrv2(int n, int *ip, FFTFLT *a); void bitrv216(FFTFLT *a); void bitrv208(FFTFLT *a); void cftf1st(int n, FFTFLT *a, FFTFLT *w); void cftrec4(int n, FFTFLT *a, int nw, FFTFLT *w); void cftleaf(int n, int isplt, FFTFLT *a, int nw, FFTFLT *w); void cftfx41(int n, FFTFLT *a, int nw, FFTFLT *w); void cftf161(FFTFLT *a, FFTFLT *w); void cftf081(FFTFLT *a, FFTFLT *w); void cftf040(FFTFLT *a); void cftx020(FFTFLT *a); #ifdef USE_CDFT_THREADS void cftrec4_th(int n, FFTFLT *a, int nw, FFTFLT *w); #endif /* USE_CDFT_THREADS */ if (n > 8) { if (n > 32) { cftf1st(n, a, &w[nw - (n >> 2)]); #ifdef USE_CDFT_THREADS if (n > CDFT_THREADS_BEGIN_N) { cftrec4_th(n, a, nw, w); } else #endif /* USE_CDFT_THREADS */ if (n > 512) { cftrec4(n, a, nw, w); } else if (n > 128) { cftleaf(n, 1, a, nw, w); } else { cftfx41(n, a, nw, w); } bitrv2(n, ip, a); } else if (n == 32) { cftf161(a, &w[nw - 8]); bitrv216(a); } else { cftf081(a, w); bitrv208(a); } } else if (n == 8) { cftf040(a); } else if (n == 4) { cftx020(a); } } void cftbsub(int n, FFTFLT *a, int *ip, int nw, FFTFLT *w) { void bitrv2conj(int n, int *ip, FFTFLT *a); void bitrv216neg(FFTFLT *a); void bitrv208neg(FFTFLT *a); void cftb1st(int n, FFTFLT *a, FFTFLT *w); void cftrec4(int n, FFTFLT *a, int nw, FFTFLT *w); void cftleaf(int n, int isplt, FFTFLT *a, int nw, FFTFLT *w); void cftfx41(int n, FFTFLT *a, int nw, FFTFLT *w); void cftf161(FFTFLT *a, FFTFLT *w); void cftf081(FFTFLT *a, FFTFLT *w); void cftb040(FFTFLT *a); void cftx020(FFTFLT *a); #ifdef USE_CDFT_THREADS void cftrec4_th(int n, FFTFLT *a, int nw, FFTFLT *w); #endif /* USE_CDFT_THREADS */ if (n > 8) { if (n > 32) { cftb1st(n, a, &w[nw - (n >> 2)]); #ifdef USE_CDFT_THREADS if (n > CDFT_THREADS_BEGIN_N) { cftrec4_th(n, a, nw, w); } else #endif /* USE_CDFT_THREADS */ if (n > 512) { cftrec4(n, a, nw, w); } else if (n > 128) { cftleaf(n, 1, a, nw, w); } else { cftfx41(n, a, nw, w); } bitrv2conj(n, ip, a); } else if (n == 32) { cftf161(a, &w[nw - 8]); bitrv216neg(a); } else { cftf081(a, w); bitrv208neg(a); } } else if (n == 8) { cftb040(a); } else if (n == 4) { cftx020(a); } } void bitrv2(int n, int *ip, FFTFLT *a) { int j, j1, k, k1, l, m, nh, nm; FFTFLT xr, xi, yr, yi; m = 1; for (l = n >> 2; l > 8; l >>= 2) { m <<= 1; } nh = n >> 1; nm = 4 * m; if (l == 8) { for (k = 0; k < m; k++) { for (j = 0; j < k; j++) { j1 = 4 * j + 2 * ip[m + k]; k1 = 4 * k + 2 * ip[m + j]; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 += 2 * nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 -= nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 += 2 * nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nh; k1 += 2; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nm; k1 -= 2 * nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nm; k1 += nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nm; k1 -= 2 * nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += 2; k1 += nh; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 += 2 * nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 -= nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 += 2 * nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nh; k1 -= 2; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nm; k1 -= 2 * nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nm; k1 += nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nm; k1 -= 2 * nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; } k1 = 4 * k + 2 * ip[m + k]; j1 = k1 + 2; k1 += nh; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 += 2 * nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 -= nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= 2; k1 -= nh; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nh + 2; k1 += nh + 2; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nh - nm; k1 += 2 * nm - 2; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; } } else { for (k = 0; k < m; k++) { for (j = 0; j < k; j++) { j1 = 4 * j + ip[m + k]; k1 = 4 * k + ip[m + j]; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 += nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nh; k1 += 2; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nm; k1 -= nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += 2; k1 += nh; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 += nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nh; k1 -= 2; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nm; k1 -= nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; } k1 = 4 * k + ip[m + k]; j1 = k1 + 2; k1 += nh; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 += nm; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; } } } void bitrv2conj(int n, int *ip, FFTFLT *a) { int j, j1, k, k1, l, m, nh, nm; FFTFLT xr, xi, yr, yi; m = 1; for (l = n >> 2; l > 8; l >>= 2) { m <<= 1; } nh = n >> 1; nm = 4 * m; if (l == 8) { for (k = 0; k < m; k++) { for (j = 0; j < k; j++) { j1 = 4 * j + 2 * ip[m + k]; k1 = 4 * k + 2 * ip[m + j]; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 += 2 * nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 -= nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 += 2 * nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nh; k1 += 2; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nm; k1 -= 2 * nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nm; k1 += nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nm; k1 -= 2 * nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += 2; k1 += nh; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 += 2 * nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 -= nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 += 2 * nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nh; k1 -= 2; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nm; k1 -= 2 * nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nm; k1 += nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nm; k1 -= 2 * nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; } k1 = 4 * k + 2 * ip[m + k]; j1 = k1 + 2; k1 += nh; a[j1 - 1] = -a[j1 - 1]; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; a[k1 + 3] = -a[k1 + 3]; j1 += nm; k1 += 2 * nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 -= nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= 2; k1 -= nh; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nh + 2; k1 += nh + 2; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nh - nm; k1 += 2 * nm - 2; a[j1 - 1] = -a[j1 - 1]; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; a[k1 + 3] = -a[k1 + 3]; } } else { for (k = 0; k < m; k++) { for (j = 0; j < k; j++) { j1 = 4 * j + ip[m + k]; k1 = 4 * k + ip[m + j]; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 += nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nh; k1 += 2; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nm; k1 -= nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += 2; k1 += nh; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += nm; k1 += nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nh; k1 -= 2; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 -= nm; k1 -= nm; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; } k1 = 4 * k + ip[m + k]; j1 = k1 + 2; k1 += nh; a[j1 - 1] = -a[j1 - 1]; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; a[k1 + 3] = -a[k1 + 3]; j1 += nm; k1 += nm; a[j1 - 1] = -a[j1 - 1]; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; a[k1 + 3] = -a[k1 + 3]; } } } void bitrv216(FFTFLT *a) { FFTFLT x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i, x5r, x5i, x7r, x7i, x8r, x8i, x10r, x10i, x11r, x11i, x12r, x12i, x13r, x13i, x14r, x14i; x1r = a[2]; x1i = a[3]; x2r = a[4]; x2i = a[5]; x3r = a[6]; x3i = a[7]; x4r = a[8]; x4i = a[9]; x5r = a[10]; x5i = a[11]; x7r = a[14]; x7i = a[15]; x8r = a[16]; x8i = a[17]; x10r = a[20]; x10i = a[21]; x11r = a[22]; x11i = a[23]; x12r = a[24]; x12i = a[25]; x13r = a[26]; x13i = a[27]; x14r = a[28]; x14i = a[29]; a[2] = x8r; a[3] = x8i; a[4] = x4r; a[5] = x4i; a[6] = x12r; a[7] = x12i; a[8] = x2r; a[9] = x2i; a[10] = x10r; a[11] = x10i; a[14] = x14r; a[15] = x14i; a[16] = x1r; a[17] = x1i; a[20] = x5r; a[21] = x5i; a[22] = x13r; a[23] = x13i; a[24] = x3r; a[25] = x3i; a[26] = x11r; a[27] = x11i; a[28] = x7r; a[29] = x7i; } void bitrv216neg(FFTFLT *a) { FFTFLT x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i, x5r, x5i, x6r, x6i, x7r, x7i, x8r, x8i, x9r, x9i, x10r, x10i, x11r, x11i, x12r, x12i, x13r, x13i, x14r, x14i, x15r, x15i; x1r = a[2]; x1i = a[3]; x2r = a[4]; x2i = a[5]; x3r = a[6]; x3i = a[7]; x4r = a[8]; x4i = a[9]; x5r = a[10]; x5i = a[11]; x6r = a[12]; x6i = a[13]; x7r = a[14]; x7i = a[15]; x8r = a[16]; x8i = a[17]; x9r = a[18]; x9i = a[19]; x10r = a[20]; x10i = a[21]; x11r = a[22]; x11i = a[23]; x12r = a[24]; x12i = a[25]; x13r = a[26]; x13i = a[27]; x14r = a[28]; x14i = a[29]; x15r = a[30]; x15i = a[31]; a[2] = x15r; a[3] = x15i; a[4] = x7r; a[5] = x7i; a[6] = x11r; a[7] = x11i; a[8] = x3r; a[9] = x3i; a[10] = x13r; a[11] = x13i; a[12] = x5r; a[13] = x5i; a[14] = x9r; a[15] = x9i; a[16] = x1r; a[17] = x1i; a[18] = x14r; a[19] = x14i; a[20] = x6r; a[21] = x6i; a[22] = x10r; a[23] = x10i; a[24] = x2r; a[25] = x2i; a[26] = x12r; a[27] = x12i; a[28] = x4r; a[29] = x4i; a[30] = x8r; a[31] = x8i; } void bitrv208(FFTFLT *a) { FFTFLT x1r, x1i, x3r, x3i, x4r, x4i, x6r, x6i; x1r = a[2]; x1i = a[3]; x3r = a[6]; x3i = a[7]; x4r = a[8]; x4i = a[9]; x6r = a[12]; x6i = a[13]; a[2] = x4r; a[3] = x4i; a[6] = x6r; a[7] = x6i; a[8] = x1r; a[9] = x1i; a[12] = x3r; a[13] = x3i; } void bitrv208neg(FFTFLT *a) { FFTFLT x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i, x5r, x5i, x6r, x6i, x7r, x7i; x1r = a[2]; x1i = a[3]; x2r = a[4]; x2i = a[5]; x3r = a[6]; x3i = a[7]; x4r = a[8]; x4i = a[9]; x5r = a[10]; x5i = a[11]; x6r = a[12]; x6i = a[13]; x7r = a[14]; x7i = a[15]; a[2] = x7r; a[3] = x7i; a[4] = x3r; a[5] = x3i; a[6] = x5r; a[7] = x5i; a[8] = x1r; a[9] = x1i; a[10] = x6r; a[11] = x6i; a[12] = x2r; a[13] = x2i; a[14] = x4r; a[15] = x4i; } void cftf1st(int n, FFTFLT *a, FFTFLT *w) { int j, j0, j1, j2, j3, k, m, mh; FFTFLT wn4r, csc1, csc3, wk1r, wk1i, wk3r, wk3i, wd1r, wd1i, wd3r, wd3i; FFTFLT x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i; mh = n >> 3; m = 2 * mh; j1 = m; j2 = j1 + m; j3 = j2 + m; x0r = a[0] + a[j2]; x0i = a[1] + a[j2 + 1]; x1r = a[0] - a[j2]; x1i = a[1] - a[j2 + 1]; x2r = a[j1] + a[j3]; x2i = a[j1 + 1] + a[j3 + 1]; x3r = a[j1] - a[j3]; x3i = a[j1 + 1] - a[j3 + 1]; a[0] = x0r + x2r; a[1] = x0i + x2i; a[j1] = x0r - x2r; a[j1 + 1] = x0i - x2i; a[j2] = x1r - x3i; a[j2 + 1] = x1i + x3r; a[j3] = x1r + x3i; a[j3 + 1] = x1i - x3r; wn4r = w[1]; csc1 = w[2]; csc3 = w[3]; wd1r = 1; wd1i = 0; wd3r = 1; wd3i = 0; k = 0; for (j = 2; j < mh - 2; j += 4) { k += 4; wk1r = csc1 * (wd1r + w[k]); wk1i = csc1 * (wd1i + w[k + 1]); wk3r = csc3 * (wd3r + w[k + 2]); wk3i = csc3 * (wd3i + w[k + 3]); wd1r = w[k]; wd1i = w[k + 1]; wd3r = w[k + 2]; wd3i = w[k + 3]; j1 = j + m; j2 = j1 + m; j3 = j2 + m; x0r = a[j] + a[j2]; x0i = a[j + 1] + a[j2 + 1]; x1r = a[j] - a[j2]; x1i = a[j + 1] - a[j2 + 1]; y0r = a[j + 2] + a[j2 + 2]; y0i = a[j + 3] + a[j2 + 3]; y1r = a[j + 2] - a[j2 + 2]; y1i = a[j + 3] - a[j2 + 3]; x2r = a[j1] + a[j3]; x2i = a[j1 + 1] + a[j3 + 1]; x3r = a[j1] - a[j3]; x3i = a[j1 + 1] - a[j3 + 1]; y2r = a[j1 + 2] + a[j3 + 2]; y2i = a[j1 + 3] + a[j3 + 3]; y3r = a[j1 + 2] - a[j3 + 2]; y3i = a[j1 + 3] - a[j3 + 3]; a[j] = x0r + x2r; a[j + 1] = x0i + x2i; a[j + 2] = y0r + y2r; a[j + 3] = y0i + y2i; a[j1] = x0r - x2r; a[j1 + 1] = x0i - x2i; a[j1 + 2] = y0r - y2r; a[j1 + 3] = y0i - y2i; x0r = x1r - x3i; x0i = x1i + x3r; a[j2] = wk1r * x0r - wk1i * x0i; a[j2 + 1] = wk1r * x0i + wk1i * x0r; x0r = y1r - y3i; x0i = y1i + y3r; a[j2 + 2] = wd1r * x0r - wd1i * x0i; a[j2 + 3] = wd1r * x0i + wd1i * x0r; x0r = x1r + x3i; x0i = x1i - x3r; a[j3] = wk3r * x0r + wk3i * x0i; a[j3 + 1] = wk3r * x0i - wk3i * x0r; x0r = y1r + y3i; x0i = y1i - y3r; a[j3 + 2] = wd3r * x0r + wd3i * x0i; a[j3 + 3] = wd3r * x0i - wd3i * x0r; j0 = m - j; j1 = j0 + m; j2 = j1 + m; j3 = j2 + m; x0r = a[j0] + a[j2]; x0i = a[j0 + 1] + a[j2 + 1]; x1r = a[j0] - a[j2]; x1i = a[j0 + 1] - a[j2 + 1]; y0r = a[j0 - 2] + a[j2 - 2]; y0i = a[j0 - 1] + a[j2 - 1]; y1r = a[j0 - 2] - a[j2 - 2]; y1i = a[j0 - 1] - a[j2 - 1]; x2r = a[j1] + a[j3]; x2i = a[j1 + 1] + a[j3 + 1]; x3r = a[j1] - a[j3]; x3i = a[j1 + 1] - a[j3 + 1]; y2r = a[j1 - 2] + a[j3 - 2]; y2i = a[j1 - 1] + a[j3 - 1]; y3r = a[j1 - 2] - a[j3 - 2]; y3i = a[j1 - 1] - a[j3 - 1]; a[j0] = x0r + x2r; a[j0 + 1] = x0i + x2i; a[j0 - 2] = y0r + y2r; a[j0 - 1] = y0i + y2i; a[j1] = x0r - x2r; a[j1 + 1] = x0i - x2i; a[j1 - 2] = y0r - y2r; a[j1 - 1] = y0i - y2i; x0r = x1r - x3i; x0i = x1i + x3r; a[j2] = wk1i * x0r - wk1r * x0i; a[j2 + 1] = wk1i * x0i + wk1r * x0r; x0r = y1r - y3i; x0i = y1i + y3r; a[j2 - 2] = wd1i * x0r - wd1r * x0i; a[j2 - 1] = wd1i * x0i + wd1r * x0r; x0r = x1r + x3i; x0i = x1i - x3r; a[j3] = wk3i * x0r + wk3r * x0i; a[j3 + 1] = wk3i * x0i - wk3r * x0r; x0r = y1r + y3i; x0i = y1i - y3r; a[j3 - 2] = wd3i * x0r + wd3r * x0i; a[j3 - 1] = wd3i * x0i - wd3r * x0r; } wk1r = csc1 * (wd1r + wn4r); wk1i = csc1 * (wd1i + wn4r); wk3r = csc3 * (wd3r - wn4r); wk3i = csc3 * (wd3i - wn4r); j0 = mh; j1 = j0 + m; j2 = j1 + m; j3 = j2 + m; x0r = a[j0 - 2] + a[j2 - 2]; x0i = a[j0 - 1] + a[j2 - 1]; x1r = a[j0 - 2] - a[j2 - 2]; x1i = a[j0 - 1] - a[j2 - 1]; x2r = a[j1 - 2] + a[j3 - 2]; x2i = a[j1 - 1] + a[j3 - 1]; x3r = a[j1 - 2] - a[j3 - 2]; x3i = a[j1 - 1] - a[j3 - 1]; a[j0 - 2] = x0r + x2r; a[j0 - 1] = x0i + x2i; a[j1 - 2] = x0r - x2r; a[j1 - 1] = x0i - x2i; x0r = x1r - x3i; x0i = x1i + x3r; a[j2 - 2] = wk1r * x0r - wk1i * x0i; a[j2 - 1] = wk1r * x0i + wk1i * x0r; x0r = x1r + x3i; x0i = x1i - x3r; a[j3 - 2] = wk3r * x0r + wk3i * x0i; a[j3 - 1] = wk3r * x0i - wk3i * x0r; x0r = a[j0] + a[j2]; x0i = a[j0 + 1] + a[j2 + 1]; x1r = a[j0] - a[j2]; x1i = a[j0 + 1] - a[j2 + 1]; x2r = a[j1] + a[j3]; x2i = a[j1 + 1] + a[j3 + 1]; x3r = a[j1] - a[j3]; x3i = a[j1 + 1] - a[j3 + 1]; a[j0] = x0r + x2r; a[j0 + 1] = x0i + x2i; a[j1] = x0r - x2r; a[j1 + 1] = x0i - x2i; x0r = x1r - x3i; x0i = x1i + x3r; a[j2] = wn4r * (x0r - x0i); a[j2 + 1] = wn4r * (x0i + x0r); x0r = x1r + x3i; x0i = x1i - x3r; a[j3] = -wn4r * (x0r + x0i); a[j3 + 1] = -wn4r * (x0i - x0r); x0r = a[j0 + 2] + a[j2 + 2]; x0i = a[j0 + 3] + a[j2 + 3]; x1r = a[j0 + 2] - a[j2 + 2]; x1i = a[j0 + 3] - a[j2 + 3]; x2r = a[j1 + 2] + a[j3 + 2]; x2i = a[j1 + 3] + a[j3 + 3]; x3r = a[j1 + 2] - a[j3 + 2]; x3i = a[j1 + 3] - a[j3 + 3]; a[j0 + 2] = x0r + x2r; a[j0 + 3] = x0i + x2i; a[j1 + 2] = x0r - x2r; a[j1 + 3] = x0i - x2i; x0r = x1r - x3i; x0i = x1i + x3r; a[j2 + 2] = wk1i * x0r - wk1r * x0i; a[j2 + 3] = wk1i * x0i + wk1r * x0r; x0r = x1r + x3i; x0i = x1i - x3r; a[j3 + 2] = wk3i * x0r + wk3r * x0i; a[j3 + 3] = wk3i * x0i - wk3r * x0r; } void cftb1st(int n, FFTFLT *a, FFTFLT *w) { int j, j0, j1, j2, j3, k, m, mh; FFTFLT wn4r, csc1, csc3, wk1r, wk1i, wk3r, wk3i, wd1r, wd1i, wd3r, wd3i; FFTFLT x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i; mh = n >> 3; m = 2 * mh; j1 = m; j2 = j1 + m; j3 = j2 + m; x0r = a[0] + a[j2]; x0i = -a[1] - a[j2 + 1]; x1r = a[0] - a[j2]; x1i = -a[1] + a[j2 + 1]; x2r = a[j1] + a[j3]; x2i = a[j1 + 1] + a[j3 + 1]; x3r = a[j1] - a[j3]; x3i = a[j1 + 1] - a[j3 + 1]; a[0] = x0r + x2r; a[1] = x0i - x2i; a[j1] = x0r - x2r; a[j1 + 1] = x0i + x2i; a[j2] = x1r + x3i; a[j2 + 1] = x1i + x3r; a[j3] = x1r - x3i; a[j3 + 1] = x1i - x3r; wn4r = w[1]; csc1 = w[2]; csc3 = w[3]; wd1r = 1; wd1i = 0; wd3r = 1; wd3i = 0; k = 0; for (j = 2; j < mh - 2; j += 4) { k += 4; wk1r = csc1 * (wd1r + w[k]); wk1i = csc1 * (wd1i + w[k + 1]); wk3r = csc3 * (wd3r + w[k + 2]); wk3i = csc3 * (wd3i + w[k + 3]); wd1r = w[k]; wd1i = w[k + 1]; wd3r = w[k + 2]; wd3i = w[k + 3]; j1 = j + m; j2 = j1 + m; j3 = j2 + m; x0r = a[j] + a[j2]; x0i = -a[j + 1] - a[j2 + 1]; x1r = a[j] - a[j2]; x1i = -a[j + 1] + a[j2 + 1]; y0r = a[j + 2] + a[j2 + 2]; y0i = -a[j + 3] - a[j2 + 3]; y1r = a[j + 2] - a[j2 + 2]; y1i = -a[j + 3] + a[j2 + 3]; x2r = a[j1] + a[j3]; x2i = a[j1 + 1] + a[j3 + 1]; x3r = a[j1] - a[j3]; x3i = a[j1 + 1] - a[j3 + 1]; y2r = a[j1 + 2] + a[j3 + 2]; y2i = a[j1 + 3] + a[j3 + 3]; y3r = a[j1 + 2] - a[j3 + 2]; y3i = a[j1 + 3] - a[j3 + 3]; a[j] = x0r + x2r; a[j + 1] = x0i - x2i; a[j + 2] = y0r + y2r; a[j + 3] = y0i - y2i; a[j1] = x0r - x2r; a[j1 + 1] = x0i + x2i; a[j1 + 2] = y0r - y2r; a[j1 + 3] = y0i + y2i; x0r = x1r + x3i; x0i = x1i + x3r; a[j2] = wk1r * x0r - wk1i * x0i; a[j2 + 1] = wk1r * x0i + wk1i * x0r; x0r = y1r + y3i; x0i = y1i + y3r; a[j2 + 2] = wd1r * x0r - wd1i * x0i; a[j2 + 3] = wd1r * x0i + wd1i * x0r; x0r = x1r - x3i; x0i = x1i - x3r; a[j3] = wk3r * x0r + wk3i * x0i; a[j3 + 1] = wk3r * x0i - wk3i * x0r; x0r = y1r - y3i; x0i = y1i - y3r; a[j3 + 2] = wd3r * x0r + wd3i * x0i; a[j3 + 3] = wd3r * x0i - wd3i * x0r; j0 = m - j; j1 = j0 + m; j2 = j1 + m; j3 = j2 + m; x0r = a[j0] + a[j2]; x0i = -a[j0 + 1] - a[j2 + 1]; x1r = a[j0] - a[j2]; x1i = -a[j0 + 1] + a[j2 + 1]; y0r = a[j0 - 2] + a[j2 - 2]; y0i = -a[j0 - 1] - a[j2 - 1]; y1r = a[j0 - 2] - a[j2 - 2]; y1i = -a[j0 - 1] + a[j2 - 1]; x2r = a[j1] + a[j3]; x2i = a[j1 + 1] + a[j3 + 1]; x3r = a[j1] - a[j3]; x3i = a[j1 + 1] - a[j3 + 1]; y2r = a[j1 - 2] + a[j3 - 2]; y2i = a[j1 - 1] + a[j3 - 1]; y3r = a[j1 - 2] - a[j3 - 2]; y3i = a[j1 - 1] - a[j3 - 1]; a[j0] = x0r + x2r; a[j0 + 1] = x0i - x2i; a[j0 - 2] = y0r + y2r; a[j0 - 1] = y0i - y2i; a[j1] = x0r - x2r; a[j1 + 1] = x0i + x2i; a[j1 - 2] = y0r - y2r; a[j1 - 1] = y0i + y2i; x0r = x1r + x3i; x0i = x1i + x3r; a[j2] = wk1i * x0r - wk1r * x0i; a[j2 + 1] = wk1i * x0i + wk1r * x0r; x0r = y1r + y3i; x0i = y1i + y3r; a[j2 - 2] = wd1i * x0r - wd1r * x0i; a[j2 - 1] = wd1i * x0i + wd1r * x0r; x0r = x1r - x3i; x0i = x1i - x3r; a[j3] = wk3i * x0r + wk3r * x0i; a[j3 + 1] = wk3i * x0i - wk3r * x0r; x0r = y1r - y3i; x0i = y1i - y3r; a[j3 - 2] = wd3i * x0r + wd3r * x0i; a[j3 - 1] = wd3i * x0i - wd3r * x0r; } wk1r = csc1 * (wd1r + wn4r); wk1i = csc1 * (wd1i + wn4r); wk3r = csc3 * (wd3r - wn4r); wk3i = csc3 * (wd3i - wn4r); j0 = mh; j1 = j0 + m; j2 = j1 + m; j3 = j2 + m; x0r = a[j0 - 2] + a[j2 - 2]; x0i = -a[j0 - 1] - a[j2 - 1]; x1r = a[j0 - 2] - a[j2 - 2]; x1i = -a[j0 - 1] + a[j2 - 1]; x2r = a[j1 - 2] + a[j3 - 2]; x2i = a[j1 - 1] + a[j3 - 1]; x3r = a[j1 - 2] - a[j3 - 2]; x3i = a[j1 - 1] - a[j3 - 1]; a[j0 - 2] = x0r + x2r; a[j0 - 1] = x0i - x2i; a[j1 - 2] = x0r - x2r; a[j1 - 1] = x0i + x2i; x0r = x1r + x3i; x0i = x1i + x3r; a[j2 - 2] = wk1r * x0r - wk1i * x0i; a[j2 - 1] = wk1r * x0i + wk1i * x0r; x0r = x1r - x3i; x0i = x1i - x3r; a[j3 - 2] = wk3r * x0r + wk3i * x0i; a[j3 - 1] = wk3r * x0i - wk3i * x0r; x0r = a[j0] + a[j2]; x0i = -a[j0 + 1] - a[j2 + 1]; x1r = a[j0] - a[j2]; x1i = -a[j0 + 1] + a[j2 + 1]; x2r = a[j1] + a[j3]; x2i = a[j1 + 1] + a[j3 + 1]; x3r = a[j1] - a[j3]; x3i = a[j1 + 1] - a[j3 + 1]; a[j0] = x0r + x2r; a[j0 + 1] = x0i - x2i; a[j1] = x0r - x2r; a[j1 + 1] = x0i + x2i; x0r = x1r + x3i; x0i = x1i + x3r; a[j2] = wn4r * (x0r - x0i); a[j2 + 1] = wn4r * (x0i + x0r); x0r = x1r - x3i; x0i = x1i - x3r; a[j3] = -wn4r * (x0r + x0i); a[j3 + 1] = -wn4r * (x0i - x0r); x0r = a[j0 + 2] + a[j2 + 2]; x0i = -a[j0 + 3] - a[j2 + 3]; x1r = a[j0 + 2] - a[j2 + 2]; x1i = -a[j0 + 3] + a[j2 + 3]; x2r = a[j1 + 2] + a[j3 + 2]; x2i = a[j1 + 3] + a[j3 + 3]; x3r = a[j1 + 2] - a[j3 + 2]; x3i = a[j1 + 3] - a[j3 + 3]; a[j0 + 2] = x0r + x2r; a[j0 + 3] = x0i - x2i; a[j1 + 2] = x0r - x2r; a[j1 + 3] = x0i + x2i; x0r = x1r + x3i; x0i = x1i + x3r; a[j2 + 2] = wk1i * x0r - wk1r * x0i; a[j2 + 3] = wk1i * x0i + wk1r * x0r; x0r = x1r - x3i; x0i = x1i - x3r; a[j3 + 2] = wk3i * x0r + wk3r * x0i; a[j3 + 3] = wk3i * x0i - wk3r * x0r; } #ifdef USE_CDFT_THREADS struct cdft_arg_st { int n0; int n; FFTFLT *a; int nw; FFTFLT *w; }; typedef struct cdft_arg_st cdft_arg_t; void cftrec4_th(int n, FFTFLT *a, int nw, FFTFLT *w) { void *cftrec1_th(void *p); void *cftrec2_th(void *p); int i, idiv4, m, nthread; cdft_thread_t th[4]; cdft_arg_t ag[4]; nthread = 2; idiv4 = 0; m = n >> 1; if (n > CDFT_4THREADS_BEGIN_N) { nthread = 4; idiv4 = 1; m >>= 1; } for (i = 0; i < nthread; i++) { ag[i].n0 = n; ag[i].n = m; ag[i].a = &a[i * m]; ag[i].nw = nw; ag[i].w = w; if (i != idiv4) { cdft_thread_create(&th[i], cftrec1_th, &ag[i]); } else { cdft_thread_create(&th[i], cftrec2_th, &ag[i]); } } for (i = 0; i < nthread; i++) { cdft_thread_wait(th[i]); } } void *cftrec1_th(void *p) { int cfttree(int n, int j, int k, FFTFLT *a, int nw, FFTFLT *w); void cftleaf(int n, int isplt, FFTFLT *a, int nw, FFTFLT *w); void cftmdl1(int n, FFTFLT *a, FFTFLT *w); int isplt, j, k, m, n, n0, nw; FFTFLT *a, *w; n0 = ((cdft_arg_t *) p)->n0; n = ((cdft_arg_t *) p)->n; a = ((cdft_arg_t *) p)->a; nw = ((cdft_arg_t *) p)->nw; w = ((cdft_arg_t *) p)->w; m = n0; while (m > 512) { m >>= 2; cftmdl1(m, &a[n - m], &w[nw - (m >> 1)]); } cftleaf(m, 1, &a[n - m], nw, w); k = 0; for (j = n - m; j > 0; j -= m) { k++; isplt = cfttree(m, j, k, a, nw, w); cftleaf(m, isplt, &a[j - m], nw, w); } return (void *) 0; } void *cftrec2_th(void *p) { int cfttree(int n, int j, int k, FFTFLT *a, int nw, FFTFLT *w); void cftleaf(int n, int isplt, FFTFLT *a, int nw, FFTFLT *w); void cftmdl2(int n, FFTFLT *a, FFTFLT *w); int isplt, j, k, m, n, n0, nw; FFTFLT *a, *w; n0 = ((cdft_arg_t *) p)->n0; n = ((cdft_arg_t *) p)->n; a = ((cdft_arg_t *) p)->a; nw = ((cdft_arg_t *) p)->nw; w = ((cdft_arg_t *) p)->w; k = 1; m = n0; while (m > 512) { m >>= 2; k <<= 2; cftmdl2(m, &a[n - m], &w[nw - m]); } cftleaf(m, 0, &a[n - m], nw, w); k >>= 1; for (j = n - m; j > 0; j -= m) { k++; isplt = cfttree(m, j, k, a, nw, w); cftleaf(m, isplt, &a[j - m], nw, w); } return (void *) 0; } #endif /* USE_CDFT_THREADS */ void cftrec4(int n, FFTFLT *a, int nw, FFTFLT *w) { int cfttree(int n, int j, int k, FFTFLT *a, int nw, FFTFLT *w); void cftleaf(int n, int isplt, FFTFLT *a, int nw, FFTFLT *w); void cftmdl1(int n, FFTFLT *a, FFTFLT *w); int isplt, j, k, m; m = n; while (m > 512) { m >>= 2; cftmdl1(m, &a[n - m], &w[nw - (m >> 1)]); } cftleaf(m, 1, &a[n - m], nw, w); k = 0; for (j = n - m; j > 0; j -= m) { k++; isplt = cfttree(m, j, k, a, nw, w); cftleaf(m, isplt, &a[j - m], nw, w); } } int cfttree(int n, int j, int k, FFTFLT *a, int nw, FFTFLT *w) { void cftmdl1(int n, FFTFLT *a, FFTFLT *w); void cftmdl2(int n, FFTFLT *a, FFTFLT *w); int i, isplt, m; if ((k & 3) != 0) { isplt = k & 1; if (isplt != 0) { cftmdl1(n, &a[j - n], &w[nw - (n >> 1)]); } else { cftmdl2(n, &a[j - n], &w[nw - n]); } } else { m = n; for (i = k; (i & 3) == 0; i >>= 2) { m <<= 2; } isplt = i & 1; if (isplt != 0) { while (m > 128) { cftmdl1(m, &a[j - m], &w[nw - (m >> 1)]); m >>= 2; } } else { while (m > 128) { cftmdl2(m, &a[j - m], &w[nw - m]); m >>= 2; } } } return isplt; } void cftleaf(int n, int isplt, FFTFLT *a, int nw, FFTFLT *w) { void cftmdl1(int n, FFTFLT *a, FFTFLT *w); void cftmdl2(int n, FFTFLT *a, FFTFLT *w); void cftf161(FFTFLT *a, FFTFLT *w); void cftf162(FFTFLT *a, FFTFLT *w); void cftf081(FFTFLT *a, FFTFLT *w); void cftf082(FFTFLT *a, FFTFLT *w); if (n == 512) { cftmdl1(128, a, &w[nw - 64]); cftf161(a, &w[nw - 8]); cftf162(&a[32], &w[nw - 32]); cftf161(&a[64], &w[nw - 8]); cftf161(&a[96], &w[nw - 8]); cftmdl2(128, &a[128], &w[nw - 128]); cftf161(&a[128], &w[nw - 8]); cftf162(&a[160], &w[nw - 32]); cftf161(&a[192], &w[nw - 8]); cftf162(&a[224], &w[nw - 32]); cftmdl1(128, &a[256], &w[nw - 64]); cftf161(&a[256], &w[nw - 8]); cftf162(&a[288], &w[nw - 32]); cftf161(&a[320], &w[nw - 8]); cftf161(&a[352], &w[nw - 8]); if (isplt != 0) { cftmdl1(128, &a[384], &w[nw - 64]); cftf161(&a[480], &w[nw - 8]); } else { cftmdl2(128, &a[384], &w[nw - 128]); cftf162(&a[480], &w[nw - 32]); } cftf161(&a[384], &w[nw - 8]); cftf162(&a[416], &w[nw - 32]); cftf161(&a[448], &w[nw - 8]); } else { cftmdl1(64, a, &w[nw - 32]); cftf081(a, &w[nw - 8]); cftf082(&a[16], &w[nw - 8]); cftf081(&a[32], &w[nw - 8]); cftf081(&a[48], &w[nw - 8]); cftmdl2(64, &a[64], &w[nw - 64]); cftf081(&a[64], &w[nw - 8]); cftf082(&a[80], &w[nw - 8]); cftf081(&a[96], &w[nw - 8]); cftf082(&a[112], &w[nw - 8]); cftmdl1(64, &a[128], &w[nw - 32]); cftf081(&a[128], &w[nw - 8]); cftf082(&a[144], &w[nw - 8]); cftf081(&a[160], &w[nw - 8]); cftf081(&a[176], &w[nw - 8]); if (isplt != 0) { cftmdl1(64, &a[192], &w[nw - 32]); cftf081(&a[240], &w[nw - 8]); } else { cftmdl2(64, &a[192], &w[nw - 64]); cftf082(&a[240], &w[nw - 8]); } cftf081(&a[192], &w[nw - 8]); cftf082(&a[208], &w[nw - 8]); cftf081(&a[224], &w[nw - 8]); } } void cftmdl1(int n, FFTFLT *a, FFTFLT *w) { int j, j0, j1, j2, j3, k, m, mh; FFTFLT wn4r, wk1r, wk1i, wk3r, wk3i; FFTFLT x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; mh = n >> 3; m = 2 * mh; j1 = m; j2 = j1 + m; j3 = j2 + m; x0r = a[0] + a[j2]; x0i = a[1] + a[j2 + 1]; x1r = a[0] - a[j2]; x1i = a[1] - a[j2 + 1]; x2r = a[j1] + a[j3]; x2i = a[j1 + 1] + a[j3 + 1]; x3r = a[j1] - a[j3]; x3i = a[j1 + 1] - a[j3 + 1]; a[0] = x0r + x2r; a[1] = x0i + x2i; a[j1] = x0r - x2r; a[j1 + 1] = x0i - x2i; a[j2] = x1r - x3i; a[j2 + 1] = x1i + x3r; a[j3] = x1r + x3i; a[j3 + 1] = x1i - x3r; wn4r = w[1]; k = 0; for (j = 2; j < mh; j += 2) { k += 4; wk1r = w[k]; wk1i = w[k + 1]; wk3r = w[k + 2]; wk3i = w[k + 3]; j1 = j + m; j2 = j1 + m; j3 = j2 + m; x0r = a[j] + a[j2]; x0i = a[j + 1] + a[j2 + 1]; x1r = a[j] - a[j2]; x1i = a[j + 1] - a[j2 + 1]; x2r = a[j1] + a[j3]; x2i = a[j1 + 1] + a[j3 + 1]; x3r = a[j1] - a[j3]; x3i = a[j1 + 1] - a[j3 + 1]; a[j] = x0r + x2r; a[j + 1] = x0i + x2i; a[j1] = x0r - x2r; a[j1 + 1] = x0i - x2i; x0r = x1r - x3i; x0i = x1i + x3r; a[j2] = wk1r * x0r - wk1i * x0i; a[j2 + 1] = wk1r * x0i + wk1i * x0r; x0r = x1r + x3i; x0i = x1i - x3r; a[j3] = wk3r * x0r + wk3i * x0i; a[j3 + 1] = wk3r * x0i - wk3i * x0r; j0 = m - j; j1 = j0 + m; j2 = j1 + m; j3 = j2 + m; x0r = a[j0] + a[j2]; x0i = a[j0 + 1] + a[j2 + 1]; x1r = a[j0] - a[j2]; x1i = a[j0 + 1] - a[j2 + 1]; x2r = a[j1] + a[j3]; x2i = a[j1 + 1] + a[j3 + 1]; x3r = a[j1] - a[j3]; x3i = a[j1 + 1] - a[j3 + 1]; a[j0] = x0r + x2r; a[j0 + 1] = x0i + x2i; a[j1] = x0r - x2r; a[j1 + 1] = x0i - x2i; x0r = x1r - x3i; x0i = x1i + x3r; a[j2] = wk1i * x0r - wk1r * x0i; a[j2 + 1] = wk1i * x0i + wk1r * x0r; x0r = x1r + x3i; x0i = x1i - x3r; a[j3] = wk3i * x0r + wk3r * x0i; a[j3 + 1] = wk3i * x0i - wk3r * x0r; } j0 = mh; j1 = j0 + m; j2 = j1 + m; j3 = j2 + m; x0r = a[j0] + a[j2]; x0i = a[j0 + 1] + a[j2 + 1]; x1r = a[j0] - a[j2]; x1i = a[j0 + 1] - a[j2 + 1]; x2r = a[j1] + a[j3]; x2i = a[j1 + 1] + a[j3 + 1]; x3r = a[j1] - a[j3]; x3i = a[j1 + 1] - a[j3 + 1]; a[j0] = x0r + x2r; a[j0 + 1] = x0i + x2i; a[j1] = x0r - x2r; a[j1 + 1] = x0i - x2i; x0r = x1r - x3i; x0i = x1i + x3r; a[j2] = wn4r * (x0r - x0i); a[j2 + 1] = wn4r * (x0i + x0r); x0r = x1r + x3i; x0i = x1i - x3r; a[j3] = -wn4r * (x0r + x0i); a[j3 + 1] = -wn4r * (x0i - x0r); } void cftmdl2(int n, FFTFLT *a, FFTFLT *w) { int j, j0, j1, j2, j3, k, kr, m, mh; FFTFLT wn4r, wk1r, wk1i, wk3r, wk3i, wd1r, wd1i, wd3r, wd3i; FFTFLT x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, y0r, y0i, y2r, y2i; mh = n >> 3; m = 2 * mh; wn4r = w[1]; j1 = m; j2 = j1 + m; j3 = j2 + m; x0r = a[0] - a[j2 + 1]; x0i = a[1] + a[j2]; x1r = a[0] + a[j2 + 1]; x1i = a[1] - a[j2]; x2r = a[j1] - a[j3 + 1]; x2i = a[j1 + 1] + a[j3]; x3r = a[j1] + a[j3 + 1]; x3i = a[j1 + 1] - a[j3]; y0r = wn4r * (x2r - x2i); y0i = wn4r * (x2i + x2r); a[0] = x0r + y0r; a[1] = x0i + y0i; a[j1] = x0r - y0r; a[j1 + 1] = x0i - y0i; y0r = wn4r * (x3r - x3i); y0i = wn4r * (x3i + x3r); a[j2] = x1r - y0i; a[j2 + 1] = x1i + y0r; a[j3] = x1r + y0i; a[j3 + 1] = x1i - y0r; k = 0; kr = 2 * m; for (j = 2; j < mh; j += 2) { k += 4; wk1r = w[k]; wk1i = w[k + 1]; wk3r = w[k + 2]; wk3i = w[k + 3]; kr -= 4; wd1i = w[kr]; wd1r = w[kr + 1]; wd3i = w[kr + 2]; wd3r = w[kr + 3]; j1 = j + m; j2 = j1 + m; j3 = j2 + m; x0r = a[j] - a[j2 + 1]; x0i = a[j + 1] + a[j2]; x1r = a[j] + a[j2 + 1]; x1i = a[j + 1] - a[j2]; x2r = a[j1] - a[j3 + 1]; x2i = a[j1 + 1] + a[j3]; x3r = a[j1] + a[j3 + 1]; x3i = a[j1 + 1] - a[j3]; y0r = wk1r * x0r - wk1i * x0i; y0i = wk1r * x0i + wk1i * x0r; y2r = wd1r * x2r - wd1i * x2i; y2i = wd1r * x2i + wd1i * x2r; a[j] = y0r + y2r; a[j + 1] = y0i + y2i; a[j1] = y0r - y2r; a[j1 + 1] = y0i - y2i; y0r = wk3r * x1r + wk3i * x1i; y0i = wk3r * x1i - wk3i * x1r; y2r = wd3r * x3r + wd3i * x3i; y2i = wd3r * x3i - wd3i * x3r; a[j2] = y0r + y2r; a[j2 + 1] = y0i + y2i; a[j3] = y0r - y2r; a[j3 + 1] = y0i - y2i; j0 = m - j; j1 = j0 + m; j2 = j1 + m; j3 = j2 + m; x0r = a[j0] - a[j2 + 1]; x0i = a[j0 + 1] + a[j2]; x1r = a[j0] + a[j2 + 1]; x1i = a[j0 + 1] - a[j2]; x2r = a[j1] - a[j3 + 1]; x2i = a[j1 + 1] + a[j3]; x3r = a[j1] + a[j3 + 1]; x3i = a[j1 + 1] - a[j3]; y0r = wd1i * x0r - wd1r * x0i; y0i = wd1i * x0i + wd1r * x0r; y2r = wk1i * x2r - wk1r * x2i; y2i = wk1i * x2i + wk1r * x2r; a[j0] = y0r + y2r; a[j0 + 1] = y0i + y2i; a[j1] = y0r - y2r; a[j1 + 1] = y0i - y2i; y0r = wd3i * x1r + wd3r * x1i; y0i = wd3i * x1i - wd3r * x1r; y2r = wk3i * x3r + wk3r * x3i; y2i = wk3i * x3i - wk3r * x3r; a[j2] = y0r + y2r; a[j2 + 1] = y0i + y2i; a[j3] = y0r - y2r; a[j3 + 1] = y0i - y2i; } wk1r = w[m]; wk1i = w[m + 1]; j0 = mh; j1 = j0 + m; j2 = j1 + m; j3 = j2 + m; x0r = a[j0] - a[j2 + 1]; x0i = a[j0 + 1] + a[j2]; x1r = a[j0] + a[j2 + 1]; x1i = a[j0 + 1] - a[j2]; x2r = a[j1] - a[j3 + 1]; x2i = a[j1 + 1] + a[j3]; x3r = a[j1] + a[j3 + 1]; x3i = a[j1 + 1] - a[j3]; y0r = wk1r * x0r - wk1i * x0i; y0i = wk1r * x0i + wk1i * x0r; y2r = wk1i * x2r - wk1r * x2i; y2i = wk1i * x2i + wk1r * x2r; a[j0] = y0r + y2r; a[j0 + 1] = y0i + y2i; a[j1] = y0r - y2r; a[j1 + 1] = y0i - y2i; y0r = wk1i * x1r - wk1r * x1i; y0i = wk1i * x1i + wk1r * x1r; y2r = wk1r * x3r - wk1i * x3i; y2i = wk1r * x3i + wk1i * x3r; a[j2] = y0r - y2r; a[j2 + 1] = y0i - y2i; a[j3] = y0r + y2r; a[j3 + 1] = y0i + y2i; } void cftfx41(int n, FFTFLT *a, int nw, FFTFLT *w) { void cftf161(FFTFLT *a, FFTFLT *w); void cftf162(FFTFLT *a, FFTFLT *w); void cftf081(FFTFLT *a, FFTFLT *w); void cftf082(FFTFLT *a, FFTFLT *w); if (n == 128) { cftf161(a, &w[nw - 8]); cftf162(&a[32], &w[nw - 32]); cftf161(&a[64], &w[nw - 8]); cftf161(&a[96], &w[nw - 8]); } else { cftf081(a, &w[nw - 8]); cftf082(&a[16], &w[nw - 8]); cftf081(&a[32], &w[nw - 8]); cftf081(&a[48], &w[nw - 8]); } } void cftf161(FFTFLT *a, FFTFLT *w) { FFTFLT wn4r, wk1r, wk1i, x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i, y8r, y8i, y9r, y9i, y10r, y10i, y11r, y11i, y12r, y12i, y13r, y13i, y14r, y14i, y15r, y15i; wn4r = w[1]; wk1r = w[2]; wk1i = w[3]; x0r = a[0] + a[16]; x0i = a[1] + a[17]; x1r = a[0] - a[16]; x1i = a[1] - a[17]; x2r = a[8] + a[24]; x2i = a[9] + a[25]; x3r = a[8] - a[24]; x3i = a[9] - a[25]; y0r = x0r + x2r; y0i = x0i + x2i; y4r = x0r - x2r; y4i = x0i - x2i; y8r = x1r - x3i; y8i = x1i + x3r; y12r = x1r + x3i; y12i = x1i - x3r; x0r = a[2] + a[18]; x0i = a[3] + a[19]; x1r = a[2] - a[18]; x1i = a[3] - a[19]; x2r = a[10] + a[26]; x2i = a[11] + a[27]; x3r = a[10] - a[26]; x3i = a[11] - a[27]; y1r = x0r + x2r; y1i = x0i + x2i; y5r = x0r - x2r; y5i = x0i - x2i; x0r = x1r - x3i; x0i = x1i + x3r; y9r = wk1r * x0r - wk1i * x0i; y9i = wk1r * x0i + wk1i * x0r; x0r = x1r + x3i; x0i = x1i - x3r; y13r = wk1i * x0r - wk1r * x0i; y13i = wk1i * x0i + wk1r * x0r; x0r = a[4] + a[20]; x0i = a[5] + a[21]; x1r = a[4] - a[20]; x1i = a[5] - a[21]; x2r = a[12] + a[28]; x2i = a[13] + a[29]; x3r = a[12] - a[28]; x3i = a[13] - a[29]; y2r = x0r + x2r; y2i = x0i + x2i; y6r = x0r - x2r; y6i = x0i - x2i; x0r = x1r - x3i; x0i = x1i + x3r; y10r = wn4r * (x0r - x0i); y10i = wn4r * (x0i + x0r); x0r = x1r + x3i; x0i = x1i - x3r; y14r = wn4r * (x0r + x0i); y14i = wn4r * (x0i - x0r); x0r = a[6] + a[22]; x0i = a[7] + a[23]; x1r = a[6] - a[22]; x1i = a[7] - a[23]; x2r = a[14] + a[30]; x2i = a[15] + a[31]; x3r = a[14] - a[30]; x3i = a[15] - a[31]; y3r = x0r + x2r; y3i = x0i + x2i; y7r = x0r - x2r; y7i = x0i - x2i; x0r = x1r - x3i; x0i = x1i + x3r; y11r = wk1i * x0r - wk1r * x0i; y11i = wk1i * x0i + wk1r * x0r; x0r = x1r + x3i; x0i = x1i - x3r; y15r = wk1r * x0r - wk1i * x0i; y15i = wk1r * x0i + wk1i * x0r; x0r = y12r - y14r; x0i = y12i - y14i; x1r = y12r + y14r; x1i = y12i + y14i; x2r = y13r - y15r; x2i = y13i - y15i; x3r = y13r + y15r; x3i = y13i + y15i; a[24] = x0r + x2r; a[25] = x0i + x2i; a[26] = x0r - x2r; a[27] = x0i - x2i; a[28] = x1r - x3i; a[29] = x1i + x3r; a[30] = x1r + x3i; a[31] = x1i - x3r; x0r = y8r + y10r; x0i = y8i + y10i; x1r = y8r - y10r; x1i = y8i - y10i; x2r = y9r + y11r; x2i = y9i + y11i; x3r = y9r - y11r; x3i = y9i - y11i; a[16] = x0r + x2r; a[17] = x0i + x2i; a[18] = x0r - x2r; a[19] = x0i - x2i; a[20] = x1r - x3i; a[21] = x1i + x3r; a[22] = x1r + x3i; a[23] = x1i - x3r; x0r = y5r - y7i; x0i = y5i + y7r; x2r = wn4r * (x0r - x0i); x2i = wn4r * (x0i + x0r); x0r = y5r + y7i; x0i = y5i - y7r; x3r = wn4r * (x0r - x0i); x3i = wn4r * (x0i + x0r); x0r = y4r - y6i; x0i = y4i + y6r; x1r = y4r + y6i; x1i = y4i - y6r; a[8] = x0r + x2r; a[9] = x0i + x2i; a[10] = x0r - x2r; a[11] = x0i - x2i; a[12] = x1r - x3i; a[13] = x1i + x3r; a[14] = x1r + x3i; a[15] = x1i - x3r; x0r = y0r + y2r; x0i = y0i + y2i; x1r = y0r - y2r; x1i = y0i - y2i; x2r = y1r + y3r; x2i = y1i + y3i; x3r = y1r - y3r; x3i = y1i - y3i; a[0] = x0r + x2r; a[1] = x0i + x2i; a[2] = x0r - x2r; a[3] = x0i - x2i; a[4] = x1r - x3i; a[5] = x1i + x3r; a[6] = x1r + x3i; a[7] = x1i - x3r; } void cftf162(FFTFLT *a, FFTFLT *w) { FFTFLT wn4r, wk1r, wk1i, wk2r, wk2i, wk3r, wk3i, x0r, x0i, x1r, x1i, x2r, x2i, y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i, y8r, y8i, y9r, y9i, y10r, y10i, y11r, y11i, y12r, y12i, y13r, y13i, y14r, y14i, y15r, y15i; wn4r = w[1]; wk1r = w[4]; wk1i = w[5]; wk3r = w[6]; wk3i = -w[7]; wk2r = w[8]; wk2i = w[9]; x1r = a[0] - a[17]; x1i = a[1] + a[16]; x0r = a[8] - a[25]; x0i = a[9] + a[24]; x2r = wn4r * (x0r - x0i); x2i = wn4r * (x0i + x0r); y0r = x1r + x2r; y0i = x1i + x2i; y4r = x1r - x2r; y4i = x1i - x2i; x1r = a[0] + a[17]; x1i = a[1] - a[16]; x0r = a[8] + a[25]; x0i = a[9] - a[24]; x2r = wn4r * (x0r - x0i); x2i = wn4r * (x0i + x0r); y8r = x1r - x2i; y8i = x1i + x2r; y12r = x1r + x2i; y12i = x1i - x2r; x0r = a[2] - a[19]; x0i = a[3] + a[18]; x1r = wk1r * x0r - wk1i * x0i; x1i = wk1r * x0i + wk1i * x0r; x0r = a[10] - a[27]; x0i = a[11] + a[26]; x2r = wk3i * x0r - wk3r * x0i; x2i = wk3i * x0i + wk3r * x0r; y1r = x1r + x2r; y1i = x1i + x2i; y5r = x1r - x2r; y5i = x1i - x2i; x0r = a[2] + a[19]; x0i = a[3] - a[18]; x1r = wk3r * x0r - wk3i * x0i; x1i = wk3r * x0i + wk3i * x0r; x0r = a[10] + a[27]; x0i = a[11] - a[26]; x2r = wk1r * x0r + wk1i * x0i; x2i = wk1r * x0i - wk1i * x0r; y9r = x1r - x2r; y9i = x1i - x2i; y13r = x1r + x2r; y13i = x1i + x2i; x0r = a[4] - a[21]; x0i = a[5] + a[20]; x1r = wk2r * x0r - wk2i * x0i; x1i = wk2r * x0i + wk2i * x0r; x0r = a[12] - a[29]; x0i = a[13] + a[28]; x2r = wk2i * x0r - wk2r * x0i; x2i = wk2i * x0i + wk2r * x0r; y2r = x1r + x2r; y2i = x1i + x2i; y6r = x1r - x2r; y6i = x1i - x2i; x0r = a[4] + a[21]; x0i = a[5] - a[20]; x1r = wk2i * x0r - wk2r * x0i; x1i = wk2i * x0i + wk2r * x0r; x0r = a[12] + a[29]; x0i = a[13] - a[28]; x2r = wk2r * x0r - wk2i * x0i; x2i = wk2r * x0i + wk2i * x0r; y10r = x1r - x2r; y10i = x1i - x2i; y14r = x1r + x2r; y14i = x1i + x2i; x0r = a[6] - a[23]; x0i = a[7] + a[22]; x1r = wk3r * x0r - wk3i * x0i; x1i = wk3r * x0i + wk3i * x0r; x0r = a[14] - a[31]; x0i = a[15] + a[30]; x2r = wk1i * x0r - wk1r * x0i; x2i = wk1i * x0i + wk1r * x0r; y3r = x1r + x2r; y3i = x1i + x2i; y7r = x1r - x2r; y7i = x1i - x2i; x0r = a[6] + a[23]; x0i = a[7] - a[22]; x1r = wk1i * x0r + wk1r * x0i; x1i = wk1i * x0i - wk1r * x0r; x0r = a[14] + a[31]; x0i = a[15] - a[30]; x2r = wk3i * x0r - wk3r * x0i; x2i = wk3i * x0i + wk3r * x0r; y11r = x1r + x2r; y11i = x1i + x2i; y15r = x1r - x2r; y15i = x1i - x2i; x1r = y0r + y2r; x1i = y0i + y2i; x2r = y1r + y3r; x2i = y1i + y3i; a[0] = x1r + x2r; a[1] = x1i + x2i; a[2] = x1r - x2r; a[3] = x1i - x2i; x1r = y0r - y2r; x1i = y0i - y2i; x2r = y1r - y3r; x2i = y1i - y3i; a[4] = x1r - x2i; a[5] = x1i + x2r; a[6] = x1r + x2i; a[7] = x1i - x2r; x1r = y4r - y6i; x1i = y4i + y6r; x0r = y5r - y7i; x0i = y5i + y7r; x2r = wn4r * (x0r - x0i); x2i = wn4r * (x0i + x0r); a[8] = x1r + x2r; a[9] = x1i + x2i; a[10] = x1r - x2r; a[11] = x1i - x2i; x1r = y4r + y6i; x1i = y4i - y6r; x0r = y5r + y7i; x0i = y5i - y7r; x2r = wn4r * (x0r - x0i); x2i = wn4r * (x0i + x0r); a[12] = x1r - x2i; a[13] = x1i + x2r; a[14] = x1r + x2i; a[15] = x1i - x2r; x1r = y8r + y10r; x1i = y8i + y10i; x2r = y9r - y11r; x2i = y9i - y11i; a[16] = x1r + x2r; a[17] = x1i + x2i; a[18] = x1r - x2r; a[19] = x1i - x2i; x1r = y8r - y10r; x1i = y8i - y10i; x2r = y9r + y11r; x2i = y9i + y11i; a[20] = x1r - x2i; a[21] = x1i + x2r; a[22] = x1r + x2i; a[23] = x1i - x2r; x1r = y12r - y14i; x1i = y12i + y14r; x0r = y13r + y15i; x0i = y13i - y15r; x2r = wn4r * (x0r - x0i); x2i = wn4r * (x0i + x0r); a[24] = x1r + x2r; a[25] = x1i + x2i; a[26] = x1r - x2r; a[27] = x1i - x2i; x1r = y12r + y14i; x1i = y12i - y14r; x0r = y13r - y15i; x0i = y13i + y15r; x2r = wn4r * (x0r - x0i); x2i = wn4r * (x0i + x0r); a[28] = x1r - x2i; a[29] = x1i + x2r; a[30] = x1r + x2i; a[31] = x1i - x2r; } void cftf081(FFTFLT *a, FFTFLT *w) { FFTFLT wn4r, x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i; wn4r = w[1]; x0r = a[0] + a[8]; x0i = a[1] + a[9]; x1r = a[0] - a[8]; x1i = a[1] - a[9]; x2r = a[4] + a[12]; x2i = a[5] + a[13]; x3r = a[4] - a[12]; x3i = a[5] - a[13]; y0r = x0r + x2r; y0i = x0i + x2i; y2r = x0r - x2r; y2i = x0i - x2i; y1r = x1r - x3i; y1i = x1i + x3r; y3r = x1r + x3i; y3i = x1i - x3r; x0r = a[2] + a[10]; x0i = a[3] + a[11]; x1r = a[2] - a[10]; x1i = a[3] - a[11]; x2r = a[6] + a[14]; x2i = a[7] + a[15]; x3r = a[6] - a[14]; x3i = a[7] - a[15]; y4r = x0r + x2r; y4i = x0i + x2i; y6r = x0r - x2r; y6i = x0i - x2i; x0r = x1r - x3i; x0i = x1i + x3r; x2r = x1r + x3i; x2i = x1i - x3r; y5r = wn4r * (x0r - x0i); y5i = wn4r * (x0r + x0i); y7r = wn4r * (x2r - x2i); y7i = wn4r * (x2r + x2i); a[8] = y1r + y5r; a[9] = y1i + y5i; a[10] = y1r - y5r; a[11] = y1i - y5i; a[12] = y3r - y7i; a[13] = y3i + y7r; a[14] = y3r + y7i; a[15] = y3i - y7r; a[0] = y0r + y4r; a[1] = y0i + y4i; a[2] = y0r - y4r; a[3] = y0i - y4i; a[4] = y2r - y6i; a[5] = y2i + y6r; a[6] = y2r + y6i; a[7] = y2i - y6r; } void cftf082(FFTFLT *a, FFTFLT *w) { FFTFLT wn4r, wk1r, wk1i, x0r, x0i, x1r, x1i, y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i; wn4r = w[1]; wk1r = w[2]; wk1i = w[3]; y0r = a[0] - a[9]; y0i = a[1] + a[8]; y1r = a[0] + a[9]; y1i = a[1] - a[8]; x0r = a[4] - a[13]; x0i = a[5] + a[12]; y2r = wn4r * (x0r - x0i); y2i = wn4r * (x0i + x0r); x0r = a[4] + a[13]; x0i = a[5] - a[12]; y3r = wn4r * (x0r - x0i); y3i = wn4r * (x0i + x0r); x0r = a[2] - a[11]; x0i = a[3] + a[10]; y4r = wk1r * x0r - wk1i * x0i; y4i = wk1r * x0i + wk1i * x0r; x0r = a[2] + a[11]; x0i = a[3] - a[10]; y5r = wk1i * x0r - wk1r * x0i; y5i = wk1i * x0i + wk1r * x0r; x0r = a[6] - a[15]; x0i = a[7] + a[14]; y6r = wk1i * x0r - wk1r * x0i; y6i = wk1i * x0i + wk1r * x0r; x0r = a[6] + a[15]; x0i = a[7] - a[14]; y7r = wk1r * x0r - wk1i * x0i; y7i = wk1r * x0i + wk1i * x0r; x0r = y0r + y2r; x0i = y0i + y2i; x1r = y4r + y6r; x1i = y4i + y6i; a[0] = x0r + x1r; a[1] = x0i + x1i; a[2] = x0r - x1r; a[3] = x0i - x1i; x0r = y0r - y2r; x0i = y0i - y2i; x1r = y4r - y6r; x1i = y4i - y6i; a[4] = x0r - x1i; a[5] = x0i + x1r; a[6] = x0r + x1i; a[7] = x0i - x1r; x0r = y1r - y3i; x0i = y1i + y3r; x1r = y5r - y7r; x1i = y5i - y7i; a[8] = x0r + x1r; a[9] = x0i + x1i; a[10] = x0r - x1r; a[11] = x0i - x1i; x0r = y1r + y3i; x0i = y1i - y3r; x1r = y5r + y7r; x1i = y5i + y7i; a[12] = x0r - x1i; a[13] = x0i + x1r; a[14] = x0r + x1i; a[15] = x0i - x1r; } void cftf040(FFTFLT *a) { FFTFLT x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; x0r = a[0] + a[4]; x0i = a[1] + a[5]; x1r = a[0] - a[4]; x1i = a[1] - a[5]; x2r = a[2] + a[6]; x2i = a[3] + a[7]; x3r = a[2] - a[6]; x3i = a[3] - a[7]; a[0] = x0r + x2r; a[1] = x0i + x2i; a[2] = x1r - x3i; a[3] = x1i + x3r; a[4] = x0r - x2r; a[5] = x0i - x2i; a[6] = x1r + x3i; a[7] = x1i - x3r; } void cftb040(FFTFLT *a) { FFTFLT x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; x0r = a[0] + a[4]; x0i = a[1] + a[5]; x1r = a[0] - a[4]; x1i = a[1] - a[5]; x2r = a[2] + a[6]; x2i = a[3] + a[7]; x3r = a[2] - a[6]; x3i = a[3] - a[7]; a[0] = x0r + x2r; a[1] = x0i + x2i; a[2] = x1r + x3i; a[3] = x1i - x3r; a[4] = x0r - x2r; a[5] = x0i - x2i; a[6] = x1r - x3i; a[7] = x1i + x3r; } void cftx020(FFTFLT *a) { FFTFLT x0r, x0i; x0r = a[0] - a[2]; x0i = a[1] - a[3]; a[0] += a[2]; a[1] += a[3]; a[2] = x0r; a[3] = x0i; } void rftfsub(int n, FFTFLT *a, int nc, FFTFLT *c) { int j, k, kk, ks, m; FFTFLT wkr, wki, xr, xi, yr, yi; m = n >> 1; ks = 2 * nc / m; kk = 0; for (j = 2; j < m; j += 2) { k = n - j; kk += ks; wkr = 0.5 - c[nc - kk]; wki = c[kk]; xr = a[j] - a[k]; xi = a[j + 1] + a[k + 1]; yr = wkr * xr - wki * xi; yi = wkr * xi + wki * xr; a[j] -= yr; a[j + 1] -= yi; a[k] += yr; a[k + 1] -= yi; } } void rftbsub(int n, FFTFLT *a, int nc, FFTFLT *c) { int j, k, kk, ks, m; FFTFLT wkr, wki, xr, xi, yr, yi; m = n >> 1; ks = 2 * nc / m; kk = 0; for (j = 2; j < m; j += 2) { k = n - j; kk += ks; wkr = 0.5 - c[nc - kk]; wki = c[kk]; xr = a[j] - a[k]; xi = a[j + 1] + a[k + 1]; yr = wkr * xr + wki * xi; yi = wkr * xi - wki * xr; a[j] -= yr; a[j + 1] -= yi; a[k] += yr; a[k + 1] -= yi; } } void dctsub(int n, FFTFLT *a, int nc, FFTFLT *c) { int j, k, kk, ks, m; FFTFLT wkr, wki, xr; m = n >> 1; ks = nc / n; kk = 0; for (j = 1; j < m; j++) { k = n - j; kk += ks; wkr = c[kk] - c[nc - kk]; wki = c[kk] + c[nc - kk]; xr = wki * a[j] - wkr * a[k]; a[j] = wkr * a[j] + wki * a[k]; a[k] = xr; } a[m] *= c[0]; } void dstsub(int n, FFTFLT *a, int nc, FFTFLT *c) { int j, k, kk, ks, m; FFTFLT wkr, wki, xr; m = n >> 1; ks = nc / n; kk = 0; for (j = 1; j < m; j++) { k = n - j; kk += ks; wkr = c[kk] - c[nc - kk]; wki = c[kk] + c[nc - kk]; xr = wki * a[k] - wkr * a[j]; a[k] = wkr * a[k] + wki * a[j]; a[j] = xr; } a[m] *= c[0]; } ================================================ FILE: libs/libpd/pure-data/src/d_filter.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* "filters", both linear and nonlinear. */ #include "m_pd.h" /* ---------------- hip~ - 1-pole 1-zero hipass filter. ----------------- */ typedef struct hipctl { t_sample c_x; t_sample c_coef; } t_hipctl; typedef struct sighip { t_object x_obj; t_float x_sr; t_float x_hz; t_hipctl x_cspace; t_float x_f; } t_sighip; t_class *sighip_class; static void sighip_ft1(t_sighip *x, t_floatarg f); static void *sighip_new(t_floatarg f) { t_sighip *x = (t_sighip *)pd_new(sighip_class); inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("ft1")); outlet_new(&x->x_obj, &s_signal); x->x_sr = 44100; x->x_cspace.c_x = 0; sighip_ft1(x, f); x->x_f = 0; return (x); } static void sighip_ft1(t_sighip *x, t_floatarg f) { if (f < 0) f = 0; x->x_hz = f; x->x_cspace.c_coef = 1 - f * (2 * 3.14159) / x->x_sr; if (x->x_cspace.c_coef < 0) x->x_cspace.c_coef = 0; else if (x->x_cspace.c_coef > 1) x->x_cspace.c_coef = 1; } static t_int *sighip_perform(t_int *w) { t_sample *in = (t_sample *)(w[1]); t_sample *out = (t_sample *)(w[2]); t_hipctl *c = (t_hipctl *)(w[3]); int n = (int)w[4]; int i; t_sample last = c->c_x; t_sample coef = c->c_coef; if (coef < 1) { t_sample normal = 0.5*(1+coef); for (i = 0; i < n; i++) { t_sample new = *in++ + coef * last; *out++ = normal * (new - last); last = new; } if (PD_BIGORSMALL(last)) last = 0; c->c_x = last; } else { for (i = 0; i < n; i++) *out++ = *in++; c->c_x = 0; } return (w+5); } static t_int *sighip_perform_old(t_int *w) { t_sample *in = (t_sample *)(w[1]); t_sample *out = (t_sample *)(w[2]); t_hipctl *c = (t_hipctl *)(w[3]); int n = (int)w[4]; int i; t_sample last = c->c_x; t_sample coef = c->c_coef; if (coef < 1) { for (i = 0; i < n; i++) { t_sample new = *in++ + coef * last; *out++ = new - last; last = new; } if (PD_BIGORSMALL(last)) last = 0; c->c_x = last; } else { for (i = 0; i < n; i++) *out++ = *in++; c->c_x = 0; } return (w+5); } static void sighip_dsp(t_sighip *x, t_signal **sp) { x->x_sr = sp[0]->s_sr; sighip_ft1(x, x->x_hz); dsp_add((pd_compatibilitylevel > 43 ? sighip_perform : sighip_perform_old), 4, sp[0]->s_vec, sp[1]->s_vec, &x->x_cspace, (t_int)sp[0]->s_n); } static void sighip_clear(t_sighip *x, t_floatarg q) { x->x_cspace.c_x = 0; } void sighip_setup(void) { sighip_class = class_new(gensym("hip~"), (t_newmethod)sighip_new, 0, sizeof(t_sighip), 0, A_DEFFLOAT, 0); CLASS_MAINSIGNALIN(sighip_class, t_sighip, x_f); class_addmethod(sighip_class, (t_method)sighip_dsp, gensym("dsp"), A_CANT, 0); class_addmethod(sighip_class, (t_method)sighip_ft1, gensym("ft1"), A_FLOAT, 0); class_addmethod(sighip_class, (t_method)sighip_clear, gensym("clear"), 0); } /* ---------------- lop~ - 1-pole lopass filter. ----------------- */ typedef struct siglop { t_object x_obj; t_float x_conversion; /* frequency-to-coefficient conversion factor */ t_sample x_last; /* last output */ t_float x_hz; /* rolloff frequency in hz (for scalar inlet) */ t_sample x_coef; /* filter coefficient (for scalar inlet) */ t_float x_f; /* value of first inlet if unconnected */ } t_siglop; t_class *siglop_class; static void siglop_ft1(t_siglop *x, t_floatarg f); static void *siglop_new(t_floatarg f) { t_siglop *x = (t_siglop *)pd_new(siglop_class); signalinlet_new(&x->x_obj, f); outlet_new(&x->x_obj, &s_signal); x->x_conversion = x->x_last = x->x_hz = x->x_coef = 0; x->x_f = 0; return (x); } static void siglop_clear(t_siglop *x, t_floatarg q) { x->x_last = 0; } static t_int *siglop_perf_scalar(t_int *w) { t_siglop *x = (t_siglop *)(w[1]); t_sample *in1 = (t_sample *)(w[2]); t_sample newhz = *(t_sample *)(w[3]); t_sample *out = (t_sample *)(w[4]); int i, n = (int)w[5]; t_sample last = x->x_last, coef, feedback; if (newhz != x->x_hz) { x->x_hz = newhz; coef = newhz * x->x_conversion; if (coef > 1) coef = 1; else if (coef < 0) coef = 0; x->x_coef = coef; } else coef = x->x_coef; feedback = 1.f - coef; for (i = 0; i < n; i++) last = *out++ = coef * *in1++ + feedback * last; if (PD_BIGORSMALL(last)) last = 0; x->x_last = last; return (w+6); } static t_int *siglop_perf_vector(t_int *w) { t_siglop *x = (t_siglop *)(w[1]); t_sample *in1 = (t_sample *)(w[2]); t_sample *in2 = (t_sample *)(w[3]); t_sample *out = (t_sample *)(w[4]); int i, n = (int)w[5]; t_sample last = x->x_last, coef; for (i = 0; i < n; i++) { coef = *in2++ * x->x_conversion; if (coef > 1) coef = 1; else if (coef < 0) coef = 0; last = *out++ = coef * *in1++ + (1.f - coef) * last; /* this formulation seems to run slower, at least on Intel hardware: *out++ = (last += coef * (*in1++ - last)); */ } if (PD_BIGORSMALL(last)) last = 0; x->x_last = last; return (w+6); } static void siglop_dsp(t_siglop *x, t_signal **sp) { x->x_conversion = (2*3.14159)/sp[0]->s_sr; x->x_hz = x->x_coef = 0; /* this will be updated at perf time */ dsp_add((sp[1]->s_n > 1 ? siglop_perf_vector : siglop_perf_scalar), 5, x, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, (t_int)sp[0]->s_n); } void siglop_setup(void) { siglop_class = class_new(gensym("lop~"), (t_newmethod)siglop_new, 0, sizeof(t_siglop), CLASS_NOPROMOTESIG, A_DEFFLOAT, 0); CLASS_MAINSIGNALIN(siglop_class, t_siglop, x_f); class_addmethod(siglop_class, (t_method)siglop_dsp, gensym("dsp"), A_CANT, 0); class_addmethod(siglop_class, (t_method)siglop_clear, gensym("clear"), 0); } /* ---------------- bp~ - 2-pole bandpass filter. ----------------- */ typedef struct bpctl { t_sample c_x1; t_sample c_x2; t_sample c_coef1; t_sample c_coef2; t_sample c_gain; } t_bpctl; typedef struct sigbp { t_object x_obj; t_float x_sr; t_float x_freq; t_float x_q; t_bpctl x_cspace; t_float x_f; } t_sigbp; t_class *sigbp_class; static void sigbp_docoef(t_sigbp *x, t_floatarg f, t_floatarg q); static void *sigbp_new(t_floatarg f, t_floatarg q) { t_sigbp *x = (t_sigbp *)pd_new(sigbp_class); inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("ft1")); inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("ft2")); outlet_new(&x->x_obj, &s_signal); x->x_sr = 44100; x->x_cspace.c_x1 = 0; x->x_cspace.c_x2 = 0; sigbp_docoef(x, f, q); x->x_f = 0; return (x); } static t_float sigbp_qcos(t_float f) { if (f >= -(0.5f*3.14159f) && f <= 0.5f*3.14159f) { t_float g = f*f; return (((g*g*g * (-1.0f/720.0f) + g*g*(1.0f/24.0f)) - g*0.5) + 1); } else return (0); } static void sigbp_docoef(t_sigbp *x, t_floatarg f, t_floatarg q) { t_float r, oneminusr, omega; if (f < 0.001) f = 10; if (q < 0) q = 0; x->x_freq = f; x->x_q = q; omega = f * (2.0f * 3.14159f) / x->x_sr; if (q < 0.001) oneminusr = 1.0f; else oneminusr = omega/q; if (oneminusr > 1.0f) oneminusr = 1.0f; r = 1.0f - oneminusr; x->x_cspace.c_coef1 = 2.0f * sigbp_qcos(omega) * r; x->x_cspace.c_coef2 = - r * r; x->x_cspace.c_gain = 2 * oneminusr * (oneminusr + r * omega); /* post("r %f, omega %f, coef1 %f, coef2 %f", r, omega, x->x_cspace.c_coef1, x->x_cspace.c_coef2); */ } static void sigbp_ft1(t_sigbp *x, t_floatarg f) { sigbp_docoef(x, f, x->x_q); } static void sigbp_ft2(t_sigbp *x, t_floatarg q) { sigbp_docoef(x, x->x_freq, q); } static void sigbp_clear(t_sigbp *x, t_floatarg q) { x->x_cspace.c_x1 = x->x_cspace.c_x2 = 0; } static t_int *sigbp_perform(t_int *w) { t_sample *in = (t_sample *)(w[1]); t_sample *out = (t_sample *)(w[2]); t_bpctl *c = (t_bpctl *)(w[3]); int n = (int)w[4]; int i; t_sample last = c->c_x1; t_sample prev = c->c_x2; t_sample coef1 = c->c_coef1; t_sample coef2 = c->c_coef2; t_sample gain = c->c_gain; for (i = 0; i < n; i++) { t_sample output = *in++ + coef1 * last + coef2 * prev; *out++ = gain * output; prev = last; last = output; } if (PD_BIGORSMALL(last)) last = 0; if (PD_BIGORSMALL(prev)) prev = 0; c->c_x1 = last; c->c_x2 = prev; return (w+5); } static void sigbp_dsp(t_sigbp *x, t_signal **sp) { x->x_sr = sp[0]->s_sr; sigbp_docoef(x, x->x_freq, x->x_q); dsp_add(sigbp_perform, 4, sp[0]->s_vec, sp[1]->s_vec, &x->x_cspace, (t_int)sp[0]->s_n); } void sigbp_setup(void) { sigbp_class = class_new(gensym("bp~"), (t_newmethod)sigbp_new, 0, sizeof(t_sigbp), 0, A_DEFFLOAT, A_DEFFLOAT, 0); CLASS_MAINSIGNALIN(sigbp_class, t_sigbp, x_f); class_addmethod(sigbp_class, (t_method)sigbp_dsp, gensym("dsp"), A_CANT, 0); class_addmethod(sigbp_class, (t_method)sigbp_ft1, gensym("ft1"), A_FLOAT, 0); class_addmethod(sigbp_class, (t_method)sigbp_ft2, gensym("ft2"), A_FLOAT, 0); class_addmethod(sigbp_class, (t_method)sigbp_clear, gensym("clear"), 0); } /* ---------------- biquad~ - raw biquad filter ----------------- */ typedef struct biquadctl { t_sample c_x1; t_sample c_x2; t_sample c_fb1; t_sample c_fb2; t_sample c_ff1; t_sample c_ff2; t_sample c_ff3; } t_biquadctl; typedef struct sigbiquad { t_object x_obj; t_float x_f; t_biquadctl x_cspace; } t_sigbiquad; t_class *sigbiquad_class; static void sigbiquad_list(t_sigbiquad *x, t_symbol *s, int argc, t_atom *argv); static void *sigbiquad_new(t_symbol *s, int argc, t_atom *argv) { t_sigbiquad *x = (t_sigbiquad *)pd_new(sigbiquad_class); outlet_new(&x->x_obj, &s_signal); x->x_cspace.c_x1 = x->x_cspace.c_x2 = 0; sigbiquad_list(x, s, argc, argv); x->x_f = 0; return (x); } static t_int *sigbiquad_perform(t_int *w) { t_sample *in = (t_sample *)(w[1]); t_sample *out = (t_sample *)(w[2]); t_biquadctl *c = (t_biquadctl *)(w[3]); int n = (int)w[4]; int i; t_sample last = c->c_x1; t_sample prev = c->c_x2; t_sample fb1 = c->c_fb1; t_sample fb2 = c->c_fb2; t_sample ff1 = c->c_ff1; t_sample ff2 = c->c_ff2; t_sample ff3 = c->c_ff3; for (i = 0; i < n; i++) { t_sample output = *in++ + fb1 * last + fb2 * prev; if (PD_BIGORSMALL(output)) output = 0; *out++ = ff1 * output + ff2 * last + ff3 * prev; prev = last; last = output; } c->c_x1 = last; c->c_x2 = prev; return (w+5); } static void sigbiquad_list(t_sigbiquad *x, t_symbol *s, int argc, t_atom *argv) { t_float fb1 = atom_getfloatarg(0, argc, argv); t_float fb2 = atom_getfloatarg(1, argc, argv); t_float ff1 = atom_getfloatarg(2, argc, argv); t_float ff2 = atom_getfloatarg(3, argc, argv); t_float ff3 = atom_getfloatarg(4, argc, argv); t_float discriminant = fb1 * fb1 + 4 * fb2; t_biquadctl *c = &x->x_cspace; if (discriminant < 0) /* imaginary roots -- resonant filter */ { /* they're conjugates so we just check that the product is less than one */ if (fb2 >= -1.0f) goto stable; } else /* real roots */ { /* check that the parabola 1 - fb1 x - fb2 x^2 has a vertex between -1 and 1, and that it's nonnegative at both ends, which implies both roots are in [1-,1]. */ if (fb1 <= 2.0f && fb1 >= -2.0f && 1.0f - fb1 -fb2 >= 0 && 1.0f + fb1 - fb2 >= 0) goto stable; } /* if unstable, just bash to zero */ fb1 = fb2 = ff1 = ff2 = ff3 = 0; stable: c->c_fb1 = fb1; c->c_fb2 = fb2; c->c_ff1 = ff1; c->c_ff2 = ff2; c->c_ff3 = ff3; } static void sigbiquad_set(t_sigbiquad *x, t_symbol *s, int argc, t_atom *argv) { t_biquadctl *c = &x->x_cspace; c->c_x1 = atom_getfloatarg(0, argc, argv); c->c_x2 = atom_getfloatarg(1, argc, argv); } static void sigbiquad_dsp(t_sigbiquad *x, t_signal **sp) { dsp_add(sigbiquad_perform, 4, sp[0]->s_vec, sp[1]->s_vec, &x->x_cspace, (t_int)sp[0]->s_n); } void sigbiquad_setup(void) { sigbiquad_class = class_new(gensym("biquad~"), (t_newmethod)sigbiquad_new, 0, sizeof(t_sigbiquad), 0, A_GIMME, 0); CLASS_MAINSIGNALIN(sigbiquad_class, t_sigbiquad, x_f); class_addmethod(sigbiquad_class, (t_method)sigbiquad_dsp, gensym("dsp"), A_CANT, 0); class_addlist(sigbiquad_class, sigbiquad_list); class_addmethod(sigbiquad_class, (t_method)sigbiquad_set, gensym("set"), A_GIMME, 0); class_addmethod(sigbiquad_class, (t_method)sigbiquad_set, gensym("clear"), A_GIMME, 0); } /* ---------------- samphold~ - sample and hold ----------------- */ typedef struct sigsamphold { t_object x_obj; t_float x_f; t_sample x_lastin; t_sample x_lastout; } t_sigsamphold; t_class *sigsamphold_class; static void *sigsamphold_new(void) { t_sigsamphold *x = (t_sigsamphold *)pd_new(sigsamphold_class); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); outlet_new(&x->x_obj, &s_signal); x->x_lastin = 0; x->x_lastout = 0; x->x_f = 0; return (x); } static t_int *sigsamphold_perform(t_int *w) { t_sample *in1 = (t_sample *)(w[1]); t_sample *in2 = (t_sample *)(w[2]); t_sample *out = (t_sample *)(w[3]); t_sigsamphold *x = (t_sigsamphold *)(w[4]); int n = (int)w[5]; int i; t_sample lastin = x->x_lastin; t_sample lastout = x->x_lastout; for (i = 0; i < n; i++, in1++) { t_sample next = *in2++; if (next < lastin) lastout = *in1; *out++ = lastout; lastin = next; } x->x_lastin = lastin; x->x_lastout = lastout; return (w+6); } static void sigsamphold_dsp(t_sigsamphold *x, t_signal **sp) { dsp_add(sigsamphold_perform, 5, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, x, (t_int)sp[0]->s_n); } static void sigsamphold_reset(t_sigsamphold *x, t_symbol *s, int argc, t_atom *argv) { x->x_lastin = ((argc > 0 && (argv[0].a_type == A_FLOAT)) ? argv[0].a_w.w_float : 1e20); } static void sigsamphold_set(t_sigsamphold *x, t_float f) { x->x_lastout = f; } void sigsamphold_setup(void) { sigsamphold_class = class_new(gensym("samphold~"), (t_newmethod)sigsamphold_new, 0, sizeof(t_sigsamphold), 0, 0); CLASS_MAINSIGNALIN(sigsamphold_class, t_sigsamphold, x_f); class_addmethod(sigsamphold_class, (t_method)sigsamphold_set, gensym("set"), A_DEFFLOAT, 0); class_addmethod(sigsamphold_class, (t_method)sigsamphold_reset, gensym("reset"), A_GIMME, 0); class_addmethod(sigsamphold_class, (t_method)sigsamphold_dsp, gensym("dsp"), A_CANT, 0); } /* ---------------- rpole~ - real one-pole filter (raw) ----------------- */ typedef struct sigrpole { t_object x_obj; t_float x_f; t_sample x_last; } t_sigrpole; t_class *sigrpole_class; static void *sigrpole_new(t_float f) { t_sigrpole *x = (t_sigrpole *)pd_new(sigrpole_class); pd_float( (t_pd *)inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal), f); outlet_new(&x->x_obj, &s_signal); x->x_last = 0; return (x); } static t_int *sigrpole_perform(t_int *w) { t_sample *in1 = (t_sample *)(w[1]); t_sample *in2 = (t_sample *)(w[2]); t_sample *out = (t_sample *)(w[3]); t_sigrpole *x = (t_sigrpole *)(w[4]); int n = (int)w[5]; int i; t_sample last = x->x_last; for (i = 0; i < n; i++) { t_sample next = *in1++; t_sample coef = *in2++; *out++ = last = coef * last + next; } if (PD_BIGORSMALL(last)) last = 0; x->x_last = last; return (w+6); } static void sigrpole_dsp(t_sigrpole *x, t_signal **sp) { dsp_add(sigrpole_perform, 5, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, x, (t_int)sp[0]->s_n); } static void sigrpole_clear(t_sigrpole *x) { x->x_last = 0; } static void sigrpole_set(t_sigrpole *x, t_float f) { x->x_last = f; } void sigrpole_setup(void) { sigrpole_class = class_new(gensym("rpole~"), (t_newmethod)sigrpole_new, 0, sizeof(t_sigrpole), 0, A_DEFFLOAT, 0); CLASS_MAINSIGNALIN(sigrpole_class, t_sigrpole, x_f); class_addmethod(sigrpole_class, (t_method)sigrpole_set, gensym("set"), A_DEFFLOAT, 0); class_addmethod(sigrpole_class, (t_method)sigrpole_clear, gensym("clear"), 0); class_addmethod(sigrpole_class, (t_method)sigrpole_dsp, gensym("dsp"), A_CANT, 0); } /* ---------------- rzero~ - real one-zero filter (raw) ----------------- */ typedef struct sigrzero { t_object x_obj; t_float x_f; t_sample x_last; } t_sigrzero; t_class *sigrzero_class; static void *sigrzero_new(t_float f) { t_sigrzero *x = (t_sigrzero *)pd_new(sigrzero_class); pd_float( (t_pd *)inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal), f); outlet_new(&x->x_obj, &s_signal); x->x_last = 0; return (x); } static t_int *sigrzero_perform(t_int *w) { t_sample *in1 = (t_sample *)(w[1]); t_sample *in2 = (t_sample *)(w[2]); t_sample *out = (t_sample *)(w[3]); t_sigrzero *x = (t_sigrzero *)(w[4]); int n = (int)w[5]; int i; t_sample last = x->x_last; for (i = 0; i < n; i++) { t_sample next = *in1++; t_sample coef = *in2++; *out++ = next - coef * last; last = next; } x->x_last = last; return (w+6); } static void sigrzero_dsp(t_sigrzero *x, t_signal **sp) { dsp_add(sigrzero_perform, 5, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, x, (t_int)sp[0]->s_n); } static void sigrzero_clear(t_sigrzero *x) { x->x_last = 0; } static void sigrzero_set(t_sigrzero *x, t_float f) { x->x_last = f; } void sigrzero_setup(void) { sigrzero_class = class_new(gensym("rzero~"), (t_newmethod)sigrzero_new, 0, sizeof(t_sigrzero), 0, A_DEFFLOAT, 0); CLASS_MAINSIGNALIN(sigrzero_class, t_sigrzero, x_f); class_addmethod(sigrzero_class, (t_method)sigrzero_set, gensym("set"), A_DEFFLOAT, 0); class_addmethod(sigrzero_class, (t_method)sigrzero_clear, gensym("clear"), 0); class_addmethod(sigrzero_class, (t_method)sigrzero_dsp, gensym("dsp"), A_CANT, 0); } /* ---------- rzero_rev~ - real, reverse one-zero filter (raw) ------------ */ typedef struct sigrzero_rev { t_object x_obj; t_float x_f; t_sample x_last; } t_sigrzero_rev; t_class *sigrzero_rev_class; static void *sigrzero_rev_new(t_float f) { t_sigrzero_rev *x = (t_sigrzero_rev *)pd_new(sigrzero_rev_class); pd_float( (t_pd *)inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal), f); outlet_new(&x->x_obj, &s_signal); x->x_last = 0; return (x); } static t_int *sigrzero_rev_perform(t_int *w) { t_sample *in1 = (t_sample *)(w[1]); t_sample *in2 = (t_sample *)(w[2]); t_sample *out = (t_sample *)(w[3]); t_sigrzero_rev *x = (t_sigrzero_rev *)(w[4]); int n = (int)w[5]; int i; t_sample last = x->x_last; for (i = 0; i < n; i++) { t_sample next = *in1++; t_sample coef = *in2++; *out++ = last - coef * next; last = next; } x->x_last = last; return (w+6); } static void sigrzero_rev_dsp(t_sigrzero_rev *x, t_signal **sp) { dsp_add(sigrzero_rev_perform, 5, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, x, (t_int)sp[0]->s_n); } static void sigrzero_rev_clear(t_sigrzero_rev *x) { x->x_last = 0; } static void sigrzero_rev_set(t_sigrzero_rev *x, t_float f) { x->x_last = f; } void sigrzero_rev_setup(void) { sigrzero_rev_class = class_new(gensym("rzero_rev~"), (t_newmethod)sigrzero_rev_new, 0, sizeof(t_sigrzero_rev), 0, A_DEFFLOAT, 0); CLASS_MAINSIGNALIN(sigrzero_rev_class, t_sigrzero_rev, x_f); class_addmethod(sigrzero_rev_class, (t_method)sigrzero_rev_set, gensym("set"), A_DEFFLOAT, 0); class_addmethod(sigrzero_rev_class, (t_method)sigrzero_rev_clear, gensym("clear"), 0); class_addmethod(sigrzero_rev_class, (t_method)sigrzero_rev_dsp, gensym("dsp"), A_CANT, 0); } /* -------------- cpole~ - complex one-pole filter (raw) --------------- */ typedef struct sigcpole { t_object x_obj; t_float x_f; t_sample x_lastre; t_sample x_lastim; } t_sigcpole; t_class *sigcpole_class; static void *sigcpole_new(t_float re, t_float im) { t_sigcpole *x = (t_sigcpole *)pd_new(sigcpole_class); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); pd_float( (t_pd *)inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal), re); pd_float( (t_pd *)inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal), im); outlet_new(&x->x_obj, &s_signal); outlet_new(&x->x_obj, &s_signal); x->x_lastre = x->x_lastim = 0; x->x_f = 0; return (x); } static t_int *sigcpole_perform(t_int *w) { t_sample *inre1 = (t_sample *)(w[1]); t_sample *inim1 = (t_sample *)(w[2]); t_sample *inre2 = (t_sample *)(w[3]); t_sample *inim2 = (t_sample *)(w[4]); t_sample *outre = (t_sample *)(w[5]); t_sample *outim = (t_sample *)(w[6]); t_sigcpole *x = (t_sigcpole *)(w[7]); int n = (int)w[8]; int i; t_sample lastre = x->x_lastre; t_sample lastim = x->x_lastim; for (i = 0; i < n; i++) { t_sample nextre = *inre1++; t_sample nextim = *inim1++; t_sample coefre = *inre2++; t_sample coefim = *inim2++; t_sample tempre = *outre++ = nextre + lastre * coefre - lastim * coefim; lastim = *outim++ = nextim + lastre * coefim + lastim * coefre; lastre = tempre; } if (PD_BIGORSMALL(lastre)) lastre = 0; if (PD_BIGORSMALL(lastim)) lastim = 0; x->x_lastre = lastre; x->x_lastim = lastim; return (w+9); } static void sigcpole_dsp(t_sigcpole *x, t_signal **sp) { dsp_add(sigcpole_perform, 8, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec, sp[4]->s_vec, sp[5]->s_vec, x, (t_int)sp[0]->s_n); } static void sigcpole_clear(t_sigcpole *x) { x->x_lastre = x->x_lastim = 0; } static void sigcpole_set(t_sigcpole *x, t_float re, t_float im) { x->x_lastre = re; x->x_lastim = im; } void sigcpole_setup(void) { sigcpole_class = class_new(gensym("cpole~"), (t_newmethod)sigcpole_new, 0, sizeof(t_sigcpole), 0, A_DEFFLOAT, A_DEFFLOAT, 0); CLASS_MAINSIGNALIN(sigcpole_class, t_sigcpole, x_f); class_addmethod(sigcpole_class, (t_method)sigcpole_set, gensym("set"), A_DEFFLOAT, A_DEFFLOAT, 0); class_addmethod(sigcpole_class, (t_method)sigcpole_clear, gensym("clear"), 0); class_addmethod(sigcpole_class, (t_method)sigcpole_dsp, gensym("dsp"), A_CANT, 0); } /* -------------- czero~ - complex one-zero filter (raw) --------------- */ typedef struct sigczero { t_object x_obj; t_float x_f; t_sample x_lastre; t_sample x_lastim; } t_sigczero; t_class *sigczero_class; static void *sigczero_new(t_float re, t_float im) { t_sigczero *x = (t_sigczero *)pd_new(sigczero_class); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); pd_float( (t_pd *)inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal), re); pd_float( (t_pd *)inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal), im); outlet_new(&x->x_obj, &s_signal); outlet_new(&x->x_obj, &s_signal); x->x_lastre = x->x_lastim = 0; x->x_f = 0; return (x); } static t_int *sigczero_perform(t_int *w) { t_sample *inre1 = (t_sample *)(w[1]); t_sample *inim1 = (t_sample *)(w[2]); t_sample *inre2 = (t_sample *)(w[3]); t_sample *inim2 = (t_sample *)(w[4]); t_sample *outre = (t_sample *)(w[5]); t_sample *outim = (t_sample *)(w[6]); t_sigczero *x = (t_sigczero *)(w[7]); int n = (int)w[8]; int i; t_sample lastre = x->x_lastre; t_sample lastim = x->x_lastim; for (i = 0; i < n; i++) { t_sample nextre = *inre1++; t_sample nextim = *inim1++; t_sample coefre = *inre2++; t_sample coefim = *inim2++; *outre++ = nextre - lastre * coefre + lastim * coefim; *outim++ = nextim - lastre * coefim - lastim * coefre; lastre = nextre; lastim = nextim; } x->x_lastre = lastre; x->x_lastim = lastim; return (w+9); } static void sigczero_dsp(t_sigczero *x, t_signal **sp) { dsp_add(sigczero_perform, 8, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec, sp[4]->s_vec, sp[5]->s_vec, x, (t_int)sp[0]->s_n); } static void sigczero_clear(t_sigczero *x) { x->x_lastre = x->x_lastim = 0; } static void sigczero_set(t_sigczero *x, t_float re, t_float im) { x->x_lastre = re; x->x_lastim = im; } void sigczero_setup(void) { sigczero_class = class_new(gensym("czero~"), (t_newmethod)sigczero_new, 0, sizeof(t_sigczero), 0, A_DEFFLOAT, A_DEFFLOAT, 0); CLASS_MAINSIGNALIN(sigczero_class, t_sigczero, x_f); class_addmethod(sigczero_class, (t_method)sigczero_set, gensym("set"), A_DEFFLOAT, A_DEFFLOAT, 0); class_addmethod(sigczero_class, (t_method)sigczero_clear, gensym("clear"), 0); class_addmethod(sigczero_class, (t_method)sigczero_dsp, gensym("dsp"), A_CANT, 0); } /* ------ czero_rev~ - complex one-zero filter (raw, reverse form) ----- */ typedef struct sigczero_rev { t_object x_obj; t_float x_f; t_sample x_lastre; t_sample x_lastim; } t_sigczero_rev; t_class *sigczero_rev_class; static void *sigczero_rev_new(t_float re, t_float im) { t_sigczero_rev *x = (t_sigczero_rev *)pd_new(sigczero_rev_class); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); pd_float( (t_pd *)inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal), re); pd_float( (t_pd *)inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal), im); outlet_new(&x->x_obj, &s_signal); outlet_new(&x->x_obj, &s_signal); x->x_lastre = x->x_lastim = 0; x->x_f = 0; return (x); } static t_int *sigczero_rev_perform(t_int *w) { t_sample *inre1 = (t_sample *)(w[1]); t_sample *inim1 = (t_sample *)(w[2]); t_sample *inre2 = (t_sample *)(w[3]); t_sample *inim2 = (t_sample *)(w[4]); t_sample *outre = (t_sample *)(w[5]); t_sample *outim = (t_sample *)(w[6]); t_sigczero_rev *x = (t_sigczero_rev *)(w[7]); int n = (int)w[8]; int i; t_sample lastre = x->x_lastre; t_sample lastim = x->x_lastim; for (i = 0; i < n; i++) { t_sample nextre = *inre1++; t_sample nextim = *inim1++; t_sample coefre = *inre2++; t_sample coefim = *inim2++; /* transfer function is (A bar) - Z^-1, for the same frequency response as 1 - AZ^-1 from czero_tilde. */ *outre++ = lastre - nextre * coefre - nextim * coefim; *outim++ = lastim - nextre * coefim + nextim * coefre; lastre = nextre; lastim = nextim; } x->x_lastre = lastre; x->x_lastim = lastim; return (w+9); } static void sigczero_rev_dsp(t_sigczero_rev *x, t_signal **sp) { dsp_add(sigczero_rev_perform, 8, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec, sp[4]->s_vec, sp[5]->s_vec, x, (t_int)sp[0]->s_n); } static void sigczero_rev_clear(t_sigczero_rev *x) { x->x_lastre = x->x_lastim = 0; } static void sigczero_rev_set(t_sigczero_rev *x, t_float re, t_float im) { x->x_lastre = re; x->x_lastim = im; } void sigczero_rev_setup(void) { sigczero_rev_class = class_new(gensym("czero_rev~"), (t_newmethod)sigczero_rev_new, 0, sizeof(t_sigczero_rev), 0, A_DEFFLOAT, A_DEFFLOAT, 0); CLASS_MAINSIGNALIN(sigczero_rev_class, t_sigczero_rev, x_f); class_addmethod(sigczero_rev_class, (t_method)sigczero_rev_set, gensym("set"), A_DEFFLOAT, A_DEFFLOAT, 0); class_addmethod(sigczero_rev_class, (t_method)sigczero_rev_clear, gensym("clear"), 0); class_addmethod(sigczero_rev_class, (t_method)sigczero_rev_dsp, gensym("dsp"), A_CANT, 0); } /* ---------------- slop~ - slewing low-pass filter ----------------- */ typedef struct slop_tilde { t_object x_obj; t_sample x_f; t_sample x_coef; t_sample x_last; t_sample x_sigin; t_sample x_freqin; t_sample x_poslimitin; t_sample x_posfreqin; t_sample x_neglimitin; t_sample x_negfreqin; } t_slop_tilde; t_class *slop_tilde_class; static void *slop_tilde_new(t_symbol *s, int argc, t_atom *argv) { t_slop_tilde *x = (t_slop_tilde *)pd_new(slop_tilde_class); signalinlet_new(&x->x_obj, atom_getfloatarg(0, argc, argv)); signalinlet_new(&x->x_obj, atom_getfloatarg(1, argc, argv)); signalinlet_new(&x->x_obj, atom_getfloatarg(2, argc, argv)); signalinlet_new(&x->x_obj, atom_getfloatarg(3, argc, argv)); signalinlet_new(&x->x_obj, atom_getfloatarg(4, argc, argv)); outlet_new(&x->x_obj, &s_signal); x->x_coef = 0; return (x); } static void slop_tilde_set(t_slop_tilde *x, t_floatarg q) { x->x_last = q; } static t_int *slop_tilde_perform(t_int *w) { t_slop_tilde *x = (t_slop_tilde *)(w[1]); t_sample *sigin = (t_sample *)(w[2]); t_sample *freqin = (t_sample *)(w[3]); t_sample *neglimit = (t_sample *)(w[4]); t_sample *negfreqin = (t_sample *)(w[5]); t_sample *poslimit = (t_sample *)(w[6]); t_sample *posfreqin = (t_sample *)(w[7]); t_sample coef = x->x_coef; t_sample *out = (t_sample *)(w[8]); int n = (int)w[9]; int i; t_sample last = x->x_last; for (i = 0; i < n; i++) { t_sample diff = *sigin++ - last; t_sample inc = *freqin++ * coef, diffinc; t_sample posinc = *posfreqin++ * coef; t_sample neginc = *negfreqin++ * coef; t_sample maxdiff = *poslimit++; t_sample mindiff = *neglimit++; if (inc < 0.f) inc = 0.f; else if (inc > 1.f) inc = 1.f; if (posinc < 0.f) posinc = 0.f; else if (posinc > 1.f) posinc = 1.f; if (neginc < 0.f) neginc = 0.f; else if (neginc > 1.f) neginc = 1.f; if (maxdiff < 0) maxdiff = 0; if (mindiff < 0) mindiff = 0; if (diff > maxdiff) diffinc = posinc * (diff- maxdiff) + inc * maxdiff; else if (diff < -mindiff) diffinc = neginc * (diff + mindiff) - inc * mindiff; else diffinc = inc * diff; last = *out++ = last + diffinc; } if (PD_BIGORSMALL(last)) last = 0; x->x_last = last; return (w+10); } static void slop_tilde_dsp(t_slop_tilde *x, t_signal **sp) { x->x_coef = (2 * 3.14159) / sp[0]->s_sr; dsp_add(slop_tilde_perform, 9, x, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec, sp[4]->s_vec, sp[5]->s_vec, sp[6]->s_vec, (t_int)sp[0]->s_n); } void slop_tilde_setup(void) { slop_tilde_class = class_new(gensym("slop~"), (t_newmethod)slop_tilde_new, 0, sizeof(t_slop_tilde), 0, A_GIMME, 0); CLASS_MAINSIGNALIN(slop_tilde_class, t_slop_tilde, x_f); class_addmethod(slop_tilde_class, (t_method)slop_tilde_dsp, gensym("dsp"), A_CANT, 0); class_addmethod(slop_tilde_class, (t_method)slop_tilde_set, gensym("set"), A_FLOAT, 0); } /* ------------------------ setup routine ------------------------- */ void d_filter_setup(void) { sighip_setup(); siglop_setup(); sigbp_setup(); sigbiquad_setup(); sigsamphold_setup(); sigrpole_setup(); sigrzero_setup(); sigrzero_rev_setup(); sigcpole_setup(); sigczero_setup(); sigczero_rev_setup(); slop_tilde_setup(); } ================================================ FILE: libs/libpd/pure-data/src/d_global.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* send~, receive~, throw~, catch~ */ #include "m_pd.h" #include /* ----------------------------- send~ ----------------------------- */ static t_class *sigsend_class; typedef struct _sigsend { t_object x_obj; t_symbol *x_sym; t_canvas *x_canvas; int x_length; int x_nchans; t_sample *x_vec; t_float x_f; } t_sigsend; static void *sigsend_new(t_symbol *s, t_floatarg fnchans) { t_sigsend *x = (t_sigsend *)pd_new(sigsend_class); if (*s->s_name) pd_bind(&x->x_obj.ob_pd, s); x->x_sym = s; if ((x->x_nchans = fnchans) < 1) x->x_nchans = 1; x->x_length = 1; x->x_vec = (t_sample *)getbytes(x->x_nchans * sizeof(t_sample)); x->x_f = 0; x->x_canvas = canvas_getcurrent(); return (x); } static t_int *sigsend_perform(t_int *w) { t_sample *in = (t_sample *)(w[1]); t_sample *out = (t_sample *)(w[2]); int n = (int)(w[3]); while (n--) { *out = (PD_BIGORSMALL(*in) ? 0 : *in); out++; in++; } return (w+4); } static void sigsend_channels(t_sigsend *x, t_float fnchans) { x->x_nchans = fnchans >= 1 ? fnchans : 1; x->x_length = 1; /* trigger update via sigsend_fixbuf */ canvas_update_dsp(); } static void sigsend_fixbuf(t_sigsend *x, int length) { if (x->x_length != length) { x->x_vec = (t_sample *)resizebytes(x->x_vec, x->x_length * x->x_nchans * sizeof(t_sample), length * x->x_nchans * sizeof(t_sample)); x->x_length = length; } } static void sigsend_dsp(t_sigsend *x, t_signal **sp) { int usenchans = (x->x_nchans < sp[0]->s_nchans ? x->x_nchans : sp[0]->s_nchans); sigsend_fixbuf(x, sp[0]->s_length); dsp_add(sigsend_perform, 3, sp[0]->s_vec, x->x_vec, x->x_length * usenchans); if (x->x_nchans > usenchans) memset(x->x_vec + usenchans * x->x_length, 0, (x->x_nchans - usenchans) * x->x_length * sizeof(t_sample)); } static void sigsend_free(t_sigsend *x) { if (*x->x_sym->s_name) pd_unbind(&x->x_obj.ob_pd, x->x_sym); freebytes(x->x_vec, x->x_length * sizeof(t_sample)); } static void sigsend_setup(void) { sigsend_class = class_new(gensym("send~"), (t_newmethod)sigsend_new, (t_method)sigsend_free, sizeof(t_sigsend), CLASS_MULTICHANNEL, A_DEFSYM, A_DEFFLOAT, 0); class_addcreator((t_newmethod)sigsend_new, gensym("s~"), A_DEFSYM, A_DEFFLOAT, 0); CLASS_MAINSIGNALIN(sigsend_class, t_sigsend, x_f); class_addmethod(sigsend_class, (t_method)sigsend_channels, gensym("channels"), A_FLOAT, 0); class_addmethod(sigsend_class, (t_method)sigsend_dsp, gensym("dsp"), A_CANT, 0); class_sethelpsymbol(sigsend_class, gensym("send-receive-tilde")); } /* ----------------------------- receive~ ----------------------------- */ static t_class *sigreceive_class; typedef struct _sigreceive { t_object x_obj; t_symbol *x_sym; t_sample *x_wherefrom; int x_length; int x_nchans; } t_sigreceive; static void *sigreceive_new(t_symbol *s) { t_sigreceive *x = (t_sigreceive *)pd_new(sigreceive_class); x->x_length = 0; /* this is changed in dsp routine */ x->x_nchans = 1; x->x_sym = s; x->x_wherefrom = 0; outlet_new(&x->x_obj, &s_signal); return (x); } static t_int *sigreceive_perform(t_int *w) { t_sigreceive *x = (t_sigreceive *)(w[1]); t_sample *out = (t_sample *)(w[2]); int n = (int)(w[3]); t_sample *in = x->x_wherefrom; if (in) { while (n--) *out++ = *in++; } else { while (n--) *out++ = 0; } return (w+4); } /* tb: vectorized receive function */ static t_int *sigreceive_perf8(t_int *w) { t_sigreceive *x = (t_sigreceive *)(w[1]); t_sample *out = (t_sample *)(w[2]); int n = (int)(w[3]); t_sample *in = x->x_wherefrom; if (in) { for (; n; n -= 8, in += 8, out += 8) { out[0] = in[0]; out[1] = in[1]; out[2] = in[2]; out[3] = in[3]; out[4] = in[4]; out[5] = in[5]; out[6] = in[6]; out[7] = in[7]; } } else { for (; n; n -= 8, in += 8, out += 8) { out[0] = 0; out[1] = 0; out[2] = 0; out[3] = 0; out[4] = 0; out[5] = 0; out[6] = 0; out[7] = 0; } } return (w+4); } /* set receive symbol. Also check our signal length (setting x->x_length) and chase down the sender to verify length and nchans match */ static void sigreceive_set(t_sigreceive *x, t_symbol *s) { t_sigsend *sender = (t_sigsend *)pd_findbyclass((x->x_sym = s), sigsend_class); x->x_wherefrom = 0; if (sender) { int length = canvas_getsignallength(sender->x_canvas), dspstate = pd_getdspstate(); sigsend_fixbuf(sender, length); if (!dspstate) x->x_nchans = sender->x_nchans; if (sender->x_nchans == x->x_nchans && length == x->x_length) x->x_wherefrom = sender->x_vec; else if (x->x_length) { if (dspstate && length == x->x_length) pd_error(x, "receive~ (set %s) changed number of channels; restart DSP to fix", s->s_name); else pd_error(x, "receive~ %s: dimensions %dx%d don't match the send~ (%dx%d)", x->x_sym->s_name, x->x_nchans, x->x_length, sender->x_nchans, sender->x_length); } } else if (*x->x_sym->s_name) pd_error(x, "receive~ %s: no matching send", x->x_sym->s_name); } static void sigreceive_dsp(t_sigreceive *x, t_signal **sp) { x->x_length = sp[0]->s_length; sigreceive_set(x, x->x_sym); signal_setmultiout(&sp[0], x->x_nchans); if ((x->x_length * x->x_nchans) & 7) dsp_add(sigreceive_perform, 3, x, sp[0]->s_vec, (t_int)(x->x_length * x->x_nchans)); else dsp_add(sigreceive_perf8, 3, x, sp[0]->s_vec, (t_int)(x->x_length * x->x_nchans)); } static void sigreceive_setup(void) { sigreceive_class = class_new(gensym("receive~"), (t_newmethod)sigreceive_new, 0, sizeof(t_sigreceive), CLASS_MULTICHANNEL, A_DEFSYM, 0); class_addcreator((t_newmethod)sigreceive_new, gensym("r~"), A_DEFSYM, A_DEFFLOAT, 0); class_addmethod(sigreceive_class, (t_method)sigreceive_set, gensym("set"), A_SYMBOL, 0); class_addmethod(sigreceive_class, (t_method)sigreceive_dsp, gensym("dsp"), A_CANT, 0); class_sethelpsymbol(sigreceive_class, gensym("send-receive-tilde")); } /* ----------------------------- catch~ ----------------------------- */ static t_class *sigcatch_class; typedef struct _sigcatch { t_object x_obj; t_symbol *x_sym; t_canvas *x_canvas; int x_length; int x_nchans; t_sample *x_vec; } t_sigcatch; static void *sigcatch_new(t_symbol *s, t_floatarg fnchans) { t_sigcatch *x = (t_sigcatch *)pd_new(sigcatch_class); if (*s->s_name) pd_bind(&x->x_obj.ob_pd, s); x->x_sym = s; x->x_canvas = canvas_getcurrent(); x->x_length = 1; /* replaced later */ if ((x->x_nchans = fnchans) < 1) x->x_nchans = 1; x->x_vec = (t_sample *)getbytes(x->x_length * sizeof(t_sample)); outlet_new(&x->x_obj, &s_signal); return (x); } static void sigcatch_channels(t_sigcatch *x, t_float fnchans) { x->x_nchans = fnchans >= 1 ? fnchans : 1; x->x_length = 1; /* trigger update via sigcatch_fixbuf */ canvas_update_dsp(); } static void sigcatch_fixbuf(t_sigcatch *x, int length) { if (x->x_length != length) { x->x_vec = (t_sample *)resizebytes(x->x_vec, x->x_length * x->x_nchans * sizeof(t_sample), length * x->x_nchans * sizeof(t_sample)); x->x_length = length; } } static t_int *sigcatch_perform(t_int *w) { t_sample *in = (t_sample *)(w[1]); t_sample *out = (t_sample *)(w[2]); int n = (int)(w[3]); while (n--) { float f = *in; *in++ = 0; *out++ = (PD_BIGORSMALL(f) ? 0 : f); } return (w+4); } static void sigcatch_dsp(t_sigcatch *x, t_signal **sp) { sigcatch_fixbuf(x, sp[0]->s_length); signal_setmultiout(&sp[0], x->x_nchans); dsp_add(sigcatch_perform, 3, x->x_vec, sp[0]->s_vec, x->x_length * x->x_nchans); } static void sigcatch_free(t_sigcatch *x) { if (*x->x_sym->s_name) pd_unbind(&x->x_obj.ob_pd, x->x_sym); freebytes(x->x_vec, x->x_length * sizeof(t_sample)); } static void sigcatch_setup(void) { sigcatch_class = class_new(gensym("catch~"), (t_newmethod)sigcatch_new, (t_method)sigcatch_free, sizeof(t_sigcatch), CLASS_MULTICHANNEL, A_DEFSYM, A_DEFFLOAT, 0); class_addmethod(sigcatch_class, (t_method)sigcatch_channels, gensym("channels"), A_FLOAT, 0); class_addmethod(sigcatch_class, (t_method)sigcatch_dsp, gensym("dsp"), A_CANT, 0); class_sethelpsymbol(sigcatch_class, gensym("throw~-catch~")); } /* ----------------------------- throw~ ----------------------------- */ static t_class *sigthrow_class; typedef struct _sigthrow { t_object x_obj; t_symbol *x_sym; t_sample *x_whereto; int x_length; int x_nsamps; t_float x_f; } t_sigthrow; static void *sigthrow_new(t_symbol *s) { t_sigthrow *x = (t_sigthrow *)pd_new(sigthrow_class); x->x_sym = s; x->x_whereto = 0; x->x_length = 0; x->x_f = 0; return (x); } static t_int *sigthrow_perform(t_int *w) { t_sigthrow *x = (t_sigthrow *)(w[1]); t_sample *in = (t_sample *)(w[2]); int n = (int)(w[3]); t_sample *out = x->x_whereto; if (out) { n = (n <= x->x_nsamps ? n : x->x_nsamps); while (n--) *out++ += *in++; } return (w+4); } static void sigthrow_set(t_sigthrow *x, t_symbol *s) { t_sigcatch *catcher = (t_sigcatch *)pd_findbyclass((x->x_sym = s), sigcatch_class); if (catcher) { int length = canvas_getsignallength(catcher->x_canvas); sigcatch_fixbuf(catcher, length); if (x->x_length && length != x->x_length) { pd_error(x, "throw~ %s: my vector size %d doesn't match catch~ (%d)", x->x_sym->s_name, x->x_length, length); x->x_whereto = 0; } else { x->x_whereto = catcher->x_vec; x->x_nsamps = catcher->x_length * catcher->x_nchans; } } else x->x_whereto = 0; } static void sigthrow_dsp(t_sigthrow *x, t_signal **sp) { x->x_length = sp[0]->s_n; sigthrow_set(x, x->x_sym); dsp_add(sigthrow_perform, 3, x, sp[0]->s_vec, (t_int)(sp[0]->s_length * sp[0]->s_nchans)); } static void sigthrow_setup(void) { sigthrow_class = class_new(gensym("throw~"), (t_newmethod)sigthrow_new, 0, sizeof(t_sigthrow), CLASS_MULTICHANNEL, A_DEFSYM, 0); class_addmethod(sigthrow_class, (t_method)sigthrow_set, gensym("set"), A_SYMBOL, 0); CLASS_MAINSIGNALIN(sigthrow_class, t_sigthrow, x_f); class_addmethod(sigthrow_class, (t_method)sigthrow_dsp, gensym("dsp"), A_CANT, 0); class_sethelpsymbol(sigthrow_class, gensym("throw~-catch~")); } /* ----------------------- global setup routine ---------------- */ void d_global_setup(void) { sigsend_setup(); sigreceive_setup(); sigcatch_setup(); sigthrow_setup(); } ================================================ FILE: libs/libpd/pure-data/src/d_math.c ================================================ /* Copyright (c) 1997-2001 Miller Puckette and others. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* mathematical functions and other transfer functions, including tilde versions of stuff from x_acoustics.c. */ #include "m_pd.h" #include #include #define LOGTEN 2.302585092994046 #define SIGTOTAL(s) ((t_int)((s)->s_length * (s)->s_nchans)) /* ------------------------- clip~ -------------------------- */ static t_class *clip_class; typedef struct _clip { t_object x_obj; t_float x_f; t_float x_lo; t_float x_hi; } t_clip; static void *clip_new(t_floatarg lo, t_floatarg hi) { t_clip *x = (t_clip *)pd_new(clip_class); x->x_lo = lo; x->x_hi = hi; outlet_new(&x->x_obj, gensym("signal")); floatinlet_new(&x->x_obj, &x->x_lo); floatinlet_new(&x->x_obj, &x->x_hi); x->x_f = 0; return (x); } static t_int *clip_perform(t_int *w) { t_clip *x = (t_clip *)(w[1]); t_sample *in = (t_sample *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); while (n--) { t_sample f = *in++; if (f < x->x_lo) f = x->x_lo; if (f > x->x_hi) f = x->x_hi; *out++ = f; } return (w+5); } static void clip_dsp(t_clip *x, t_signal **sp) { signal_setmultiout(&sp[1], sp[0]->s_nchans); dsp_add(clip_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, SIGTOTAL(sp[0])); } static void clip_setup(void) { clip_class = class_new(gensym("clip~"), (t_newmethod)clip_new, 0, sizeof(t_clip), CLASS_MULTICHANNEL, A_DEFFLOAT, A_DEFFLOAT, 0); CLASS_MAINSIGNALIN(clip_class, t_clip, x_f); class_addmethod(clip_class, (t_method)clip_dsp, gensym("dsp"), A_CANT, 0); } /* sigrsqrt - reciprocal square root good to 8 mantissa bits */ #define DUMTAB1SIZE 256 #define DUMTAB2SIZE 1024 /* There could be a thread race condition here but it will only cause extra] memory allocation. */ static float *rsqrt_exptab, *rsqrt_mantissatab; static void init_rsqrt(void) { int i; if (!rsqrt_exptab) { rsqrt_exptab = (float *)getbytes(DUMTAB1SIZE*sizeof(float)); rsqrt_mantissatab = (float *)getbytes(DUMTAB2SIZE*sizeof(float)); for (i = 0; i < DUMTAB1SIZE; i++) { union { float f; long l; } u; int32_t l = (i ? (i == DUMTAB1SIZE-1 ? DUMTAB1SIZE-2 : i) : 1)<< 23; u.l = l; rsqrt_exptab[i] = 1./sqrt(u.f); } for (i = 0; i < DUMTAB2SIZE; i++) { float f = 1 + (1./DUMTAB2SIZE) * i; rsqrt_mantissatab[i] = 1./sqrt(f); } } } /* these are used in externs like "bonk" */ t_float q8_rsqrt(t_float f0) { union { float f; long l; } u; init_rsqrt(); u.f = f0; if (u.f < 0) return (0); else return (t_float)(rsqrt_exptab[(u.l >> 23) & 0xff] * rsqrt_mantissatab[(u.l >> 13) & 0x3ff]); } t_float q8_sqrt(t_float f0) { union { float f; long l; } u; init_rsqrt(); u.f = f0; if (u.f < 0) return (0); else return (t_float)(u.f * rsqrt_exptab[(u.l >> 23) & 0xff] * rsqrt_mantissatab[(u.l >> 13) & 0x3ff]); } t_float qsqrt(t_float f) {return (q8_sqrt(f)); } t_float qrsqrt(t_float f) {return (q8_rsqrt(f)); } typedef struct sigrsqrt { t_object x_obj; t_float x_f; } t_sigrsqrt; static t_class *sigrsqrt_class; static void *sigrsqrt_new(void) { t_sigrsqrt *x = (t_sigrsqrt *)pd_new(sigrsqrt_class); init_rsqrt(); outlet_new(&x->x_obj, gensym("signal")); x->x_f = 0; return (x); } static t_int *sigrsqrt_perform(t_int *w) { t_sample *in = (t_sample *)w[1], *out = (t_sample *)w[2]; int n = (int)w[3]; while (n--) { t_sample f = *in++; union { float f; long l; } u; u.f = f; if (f < 0) *out++ = 0; else { t_sample g = rsqrt_exptab[(u.l >> 23) & 0xff] * rsqrt_mantissatab[(u.l >> 13) & 0x3ff]; *out++ = 1.5 * g - 0.5 * g * g * g * f; } } return (w + 4); } static void sigrsqrt_dsp(t_sigrsqrt *x, t_signal **sp) { signal_setmultiout(&sp[1], sp[0]->s_nchans); dsp_add(sigrsqrt_perform, 3, sp[0]->s_vec, sp[1]->s_vec, SIGTOTAL(sp[0])); } void sigrsqrt_setup(void) { sigrsqrt_class = class_new(gensym("rsqrt~"), (t_newmethod)sigrsqrt_new, 0, sizeof(t_sigrsqrt), CLASS_MULTICHANNEL, 0); /* an old name for it: */ class_addcreator(sigrsqrt_new, gensym("q8_rsqrt~"), 0); CLASS_MAINSIGNALIN(sigrsqrt_class, t_sigrsqrt, x_f); class_addmethod(sigrsqrt_class, (t_method)sigrsqrt_dsp, gensym("dsp"), A_CANT, 0); } /* sigsqrt - square root good to 8 mantissa bits */ typedef struct sigsqrt { t_object x_obj; t_float x_f; } t_sigsqrt; static t_class *sigsqrt_class; static void *sigsqrt_new(void) { t_sigsqrt *x = (t_sigsqrt *)pd_new(sigsqrt_class); init_rsqrt(); outlet_new(&x->x_obj, gensym("signal")); x->x_f = 0; return (x); } t_int *sigsqrt_perform(t_int *w) /* not static; also used in d_fft.c */ { t_sample *in = (t_sample *)w[1], *out = (t_sample *)w[2]; int n = (int)w[3]; while (n--) { t_sample f = *in++; union { float f; long l; } u; u.f = f; if (f < 0) *out++ = 0; else { t_sample g = rsqrt_exptab[(u.l >> 23) & 0xff] * rsqrt_mantissatab[(u.l >> 13) & 0x3ff]; *out++ = f * (1.5 * g - 0.5 * g * g * g * f); } } return (w + 4); } static void sigsqrt_dsp(t_sigsqrt *x, t_signal **sp) { signal_setmultiout(&sp[1], sp[0]->s_nchans); dsp_add(sigsqrt_perform, 3, sp[0]->s_vec, sp[1]->s_vec, SIGTOTAL(sp[0])); } void sigsqrt_setup(void) { sigsqrt_class = class_new(gensym("sqrt~"), (t_newmethod)sigsqrt_new, 0, sizeof(t_sigsqrt), CLASS_MULTICHANNEL, 0); class_addcreator(sigsqrt_new, gensym("q8_sqrt~"), 0); /* old name */ CLASS_MAINSIGNALIN(sigsqrt_class, t_sigsqrt, x_f); class_addmethod(sigsqrt_class, (t_method)sigsqrt_dsp, gensym("dsp"), A_CANT, 0); } /* ------------------------------ wrap~ -------------------------- */ typedef struct wrap { t_object x_obj; t_float x_f; } t_sigwrap; t_class *sigwrap_class; static void *sigwrap_new(void) { t_sigwrap *x = (t_sigwrap *)pd_new(sigwrap_class); outlet_new(&x->x_obj, gensym("signal")); x->x_f = 0; return (x); } static t_int *sigwrap_perform(t_int *w) { t_sample *in = (t_sample *)w[1], *out = (t_sample *)w[2]; int n = (int)w[3]; while (n--) { int k; t_sample f = *in++; f = (f>INT_MAX || f 0) *out++ = f-k; else *out++ = f - (k-1); } return (w + 4); } static void sigwrap_dsp(t_sigwrap *x, t_signal **sp) { signal_setmultiout(&sp[1], sp[0]->s_nchans); dsp_add((pd_compatibilitylevel < 48 ? sigwrap_old_perform : sigwrap_perform), 3, sp[0]->s_vec, sp[1]->s_vec, SIGTOTAL(sp[0])); } void sigwrap_setup(void) { sigwrap_class = class_new(gensym("wrap~"), (t_newmethod)sigwrap_new, 0, sizeof(t_sigwrap), CLASS_MULTICHANNEL, 0); CLASS_MAINSIGNALIN(sigwrap_class, t_sigwrap, x_f); class_addmethod(sigwrap_class, (t_method)sigwrap_dsp, gensym("dsp"), A_CANT, 0); } /* ------------------------------ mtof~ -------------------------- */ typedef struct mtof_tilde { t_object x_obj; t_float x_f; } t_mtof_tilde; t_class *mtof_tilde_class; static void *mtof_tilde_new(void) { t_mtof_tilde *x = (t_mtof_tilde *)pd_new(mtof_tilde_class); outlet_new(&x->x_obj, gensym("signal")); x->x_f = 0; return (x); } static t_int *mtof_tilde_perform(t_int *w) { t_sample *in = (t_sample *)w[1], *out = (t_sample *)w[2]; int n = (int)w[3]; for (; n--; in++, out++) { t_sample f = *in; if (f <= -1500) *out = 0; else { if (f > 1499) f = 1499; *out = 8.17579891564 * exp(.0577622650 * f); } } return (w + 4); } static void mtof_tilde_dsp(t_mtof_tilde *x, t_signal **sp) { signal_setmultiout(&sp[1], sp[0]->s_nchans); dsp_add(mtof_tilde_perform, 3, sp[0]->s_vec, sp[1]->s_vec, SIGTOTAL(sp[0])); } void mtof_tilde_setup(void) { mtof_tilde_class = class_new(gensym("mtof~"), (t_newmethod)mtof_tilde_new, 0, sizeof(t_mtof_tilde), CLASS_MULTICHANNEL, 0); CLASS_MAINSIGNALIN(mtof_tilde_class, t_mtof_tilde, x_f); class_addmethod(mtof_tilde_class, (t_method)mtof_tilde_dsp, gensym("dsp"), A_CANT, 0); } /* ------------------------------ ftom~ -------------------------- */ typedef struct ftom_tilde { t_object x_obj; t_float x_f; } t_ftom_tilde; t_class *ftom_tilde_class; static void *ftom_tilde_new(void) { t_ftom_tilde *x = (t_ftom_tilde *)pd_new(ftom_tilde_class); outlet_new(&x->x_obj, gensym("signal")); x->x_f = 0; return (x); } static t_int *ftom_tilde_perform(t_int *w) { t_sample *in = (t_sample *)w[1], *out = (t_sample *)w[2]; int n = (int)w[3]; for (; n--; in++, out++) { t_sample f = *in; *out = (f > 0 ? 17.3123405046 * log(.12231220585 * f) : -1500); } return (w + 4); } static void ftom_tilde_dsp(t_ftom_tilde *x, t_signal **sp) { signal_setmultiout(&sp[1], sp[0]->s_nchans); dsp_add(ftom_tilde_perform, 3, sp[0]->s_vec, sp[1]->s_vec, SIGTOTAL(sp[0])); } void ftom_tilde_setup(void) { ftom_tilde_class = class_new(gensym("ftom~"), (t_newmethod)ftom_tilde_new, 0, sizeof(t_ftom_tilde), CLASS_MULTICHANNEL, 0); CLASS_MAINSIGNALIN(ftom_tilde_class, t_ftom_tilde, x_f); class_addmethod(ftom_tilde_class, (t_method)ftom_tilde_dsp, gensym("dsp"), A_CANT, 0); } /* ------------------------------ dbtorms~ -------------------------- */ typedef struct dbtorms_tilde { t_object x_obj; t_float x_f; } t_dbtorms_tilde; t_class *dbtorms_tilde_class; static void *dbtorms_tilde_new(void) { t_dbtorms_tilde *x = (t_dbtorms_tilde *)pd_new(dbtorms_tilde_class); outlet_new(&x->x_obj, gensym("signal")); x->x_f = 0; return (x); } static t_int *dbtorms_tilde_perform(t_int *w) { t_sample *in = (t_sample *)w[1], *out = (t_sample *)w[2]; int n = (int)w[3]; for (; n--; in++, out++) { t_sample f = *in; if (f <= 0) *out = 0; else { if (f > 485) f = 485; *out = exp((LOGTEN * 0.05) * (f-100.)); } } return (w + 4); } static void dbtorms_tilde_dsp(t_dbtorms_tilde *x, t_signal **sp) { signal_setmultiout(&sp[1], sp[0]->s_nchans); dsp_add(dbtorms_tilde_perform, 3, sp[0]->s_vec, sp[1]->s_vec, SIGTOTAL(sp[0])); } void dbtorms_tilde_setup(void) { dbtorms_tilde_class = class_new(gensym("dbtorms~"), (t_newmethod)dbtorms_tilde_new, 0, sizeof(t_dbtorms_tilde), CLASS_MULTICHANNEL, 0); CLASS_MAINSIGNALIN(dbtorms_tilde_class, t_dbtorms_tilde, x_f); class_addmethod(dbtorms_tilde_class, (t_method)dbtorms_tilde_dsp, gensym("dsp"), A_CANT, 0); } /* ------------------------------ rmstodb~ -------------------------- */ typedef struct rmstodb_tilde { t_object x_obj; t_float x_f; } t_rmstodb_tilde; t_class *rmstodb_tilde_class; static void *rmstodb_tilde_new(void) { t_rmstodb_tilde *x = (t_rmstodb_tilde *)pd_new(rmstodb_tilde_class); outlet_new(&x->x_obj, gensym("signal")); x->x_f = 0; return (x); } static t_int *rmstodb_tilde_perform(t_int *w) { t_sample *in = (t_sample *)w[1], *out = (t_sample *)w[2]; int n = (int)w[3]; for (; n--; in++, out++) { t_sample f = *in; if (f <= 0) *out = 0; else { t_sample g = 100 + 20./LOGTEN * log(f); *out = (g < 0 ? 0 : g); } } return (w + 4); } static void rmstodb_tilde_dsp(t_rmstodb_tilde *x, t_signal **sp) { signal_setmultiout(&sp[1], sp[0]->s_nchans); dsp_add(rmstodb_tilde_perform, 3, sp[0]->s_vec, sp[1]->s_vec, SIGTOTAL(sp[0])); } void rmstodb_tilde_setup(void) { rmstodb_tilde_class = class_new(gensym("rmstodb~"), (t_newmethod)rmstodb_tilde_new, 0, sizeof(t_rmstodb_tilde), CLASS_MULTICHANNEL, 0); CLASS_MAINSIGNALIN(rmstodb_tilde_class, t_rmstodb_tilde, x_f); class_addmethod(rmstodb_tilde_class, (t_method)rmstodb_tilde_dsp, gensym("dsp"), A_CANT, 0); } /* ------------------------------ dbtopow~ -------------------------- */ typedef struct dbtopow_tilde { t_object x_obj; t_float x_f; } t_dbtopow_tilde; t_class *dbtopow_tilde_class; static void *dbtopow_tilde_new(void) { t_dbtopow_tilde *x = (t_dbtopow_tilde *)pd_new(dbtopow_tilde_class); outlet_new(&x->x_obj, gensym("signal")); x->x_f = 0; return (x); } static t_int *dbtopow_tilde_perform(t_int *w) { t_sample *in = (t_sample *)w[1], *out = (t_sample *)w[2]; int n = (int)w[3]; for (; n--; in++, out++) { t_sample f = *in; if (f <= 0) *out = 0; else { if (f > 870) f = 870; *out = exp((LOGTEN * 0.1) * (f-100.)); } } return (w + 4); } static void dbtopow_tilde_dsp(t_dbtopow_tilde *x, t_signal **sp) { signal_setmultiout(&sp[1], sp[0]->s_nchans); dsp_add(dbtopow_tilde_perform, 3, sp[0]->s_vec, sp[1]->s_vec, SIGTOTAL(sp[0])); } void dbtopow_tilde_setup(void) { dbtopow_tilde_class = class_new(gensym("dbtopow~"), (t_newmethod)dbtopow_tilde_new, 0, sizeof(t_dbtopow_tilde), CLASS_MULTICHANNEL, 0); CLASS_MAINSIGNALIN(dbtopow_tilde_class, t_dbtopow_tilde, x_f); class_addmethod(dbtopow_tilde_class, (t_method)dbtopow_tilde_dsp, gensym("dsp"), A_CANT, 0); } /* ------------------------------ powtodb~ -------------------------- */ typedef struct powtodb_tilde { t_object x_obj; t_float x_f; } t_powtodb_tilde; t_class *powtodb_tilde_class; static void *powtodb_tilde_new(void) { t_powtodb_tilde *x = (t_powtodb_tilde *)pd_new(powtodb_tilde_class); outlet_new(&x->x_obj, gensym("signal")); x->x_f = 0; return (x); } static t_int *powtodb_tilde_perform(t_int *w) { t_sample *in = (t_sample *)w[1], *out = (t_sample *)w[2]; int n = (int)w[3]; for (; n--; in++, out++) { t_sample f = *in; if (f <= 0) *out = 0; else { t_sample g = 100 + 10./LOGTEN * log(f); *out = (g < 0 ? 0 : g); } } return (w + 4); } static void powtodb_tilde_dsp(t_powtodb_tilde *x, t_signal **sp) { signal_setmultiout(&sp[1], sp[0]->s_nchans); dsp_add(powtodb_tilde_perform, 3, sp[0]->s_vec, sp[1]->s_vec, SIGTOTAL(sp[0])); } void powtodb_tilde_setup(void) { powtodb_tilde_class = class_new(gensym("powtodb~"), (t_newmethod)powtodb_tilde_new, 0, sizeof(t_powtodb_tilde), CLASS_MULTICHANNEL, 0); CLASS_MAINSIGNALIN(powtodb_tilde_class, t_powtodb_tilde, x_f); class_addmethod(powtodb_tilde_class, (t_method)powtodb_tilde_dsp, gensym("dsp"), A_CANT, 0); } /* ----------------------------- exp~ ----------------------------- */ static t_class *exp_tilde_class; typedef struct _exp_tilde { t_object x_obj; t_float x_f; } t_exp_tilde; static void *exp_tilde_new(void) { t_exp_tilde *x = (t_exp_tilde *)pd_new(exp_tilde_class); outlet_new(&x->x_obj, &s_signal); return (x); } t_int *exp_tilde_perform(t_int *w) { t_sample *in1 = (t_sample *)(w[1]); t_sample *out = (t_sample *)(w[2]); int n = (int)(w[3]); while (n--) *out++ = exp(*in1++); return (w+4); } static void exp_tilde_dsp(t_exp_tilde *x, t_signal **sp) { signal_setmultiout(&sp[1], sp[0]->s_nchans); dsp_add(exp_tilde_perform, 3, sp[0]->s_vec, sp[1]->s_vec, SIGTOTAL(sp[0])); } static void exp_tilde_setup(void) { exp_tilde_class = class_new(gensym("exp~"), (t_newmethod)exp_tilde_new, 0, sizeof(t_exp_tilde), CLASS_MULTICHANNEL, 0); CLASS_MAINSIGNALIN(exp_tilde_class, t_exp_tilde, x_f); class_addmethod(exp_tilde_class, (t_method)exp_tilde_dsp, gensym("dsp"), A_CANT, 0); } /* ----------------------------- abs~ ----------------------------- */ static t_class *abs_tilde_class; typedef struct _abs_tilde { t_object x_obj; t_float x_f; } t_abs_tilde; static void *abs_tilde_new(void) { t_abs_tilde *x = (t_abs_tilde *)pd_new(abs_tilde_class); outlet_new(&x->x_obj, &s_signal); return (x); } t_int *abs_tilde_perform(t_int *w) { t_sample *in1 = (t_sample *)(w[1]); t_sample *out = (t_sample *)(w[2]); int n = (int)(w[3]); while (n--) { t_sample f = *in1++; *out++ = (f >= 0 ? f : -f); } return (w+4); } static void abs_tilde_dsp(t_abs_tilde *x, t_signal **sp) { signal_setmultiout(&sp[1], sp[0]->s_nchans); dsp_add(abs_tilde_perform, 3, sp[0]->s_vec, sp[1]->s_vec, SIGTOTAL(sp[0])); } static void abs_tilde_setup(void) { abs_tilde_class = class_new(gensym("abs~"), (t_newmethod)abs_tilde_new, 0, sizeof(t_abs_tilde), CLASS_MULTICHANNEL, 0); CLASS_MAINSIGNALIN(abs_tilde_class, t_abs_tilde, x_f); class_addmethod(abs_tilde_class, (t_method)abs_tilde_dsp, gensym("dsp"), A_CANT, 0); } /* ------------------------ global setup routine ------------------------- */ void d_math_setup(void) { dbtorms_tilde_setup(); rmstodb_tilde_setup(); dbtopow_tilde_setup(); powtodb_tilde_setup(); mtof_tilde_setup(); ftom_tilde_setup(); sigrsqrt_setup(); sigsqrt_setup(); sigwrap_setup(); exp_tilde_setup(); abs_tilde_setup(); clip_setup(); t_symbol *s1 = gensym("acoustics-tilde.pd"); class_sethelpsymbol(mtof_tilde_class, s1); class_sethelpsymbol(ftom_tilde_class, s1); class_sethelpsymbol(dbtorms_tilde_class, s1); class_sethelpsymbol(rmstodb_tilde_class, s1); class_sethelpsymbol(dbtopow_tilde_class, s1); class_sethelpsymbol(powtodb_tilde_class, s1); t_symbol *s2 = gensym("unops-tilde.pd"); class_sethelpsymbol(sigrsqrt_class, s2); class_sethelpsymbol(sigsqrt_class, s2); class_sethelpsymbol(sigwrap_class, s2); class_sethelpsymbol(exp_tilde_class, s2); class_sethelpsymbol(abs_tilde_class, s2); } ================================================ FILE: libs/libpd/pure-data/src/d_misc.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* miscellaneous: print~; more to come. */ #include "m_pd.h" #include /* ------------------------- print~ -------------------------- */ static t_class *print_class; typedef struct _print { t_object x_obj; t_float x_f; t_symbol *x_sym; int x_count; } t_print; static t_int *print_perform(t_int *w) { t_print *x = (t_print *)(w[1]); t_sample *in = (t_sample *)(w[2]); int n = (int)(w[3]); if (x->x_count) { int i=0; startpost("%s:", x->x_sym->s_name); for(i=0; ix_count--; } return (w+4); } static void print_dsp(t_print *x, t_signal **sp) { dsp_add(print_perform, 3, x, sp[0]->s_vec, (t_int)sp[0]->s_n); } static void print_float(t_print *x, t_float f) { if (f < 0) f = 0; x->x_count = f; } static void print_bang(t_print *x) { x->x_count = 1; } static void *print_new(t_symbol *s) { t_print *x = (t_print *)pd_new(print_class); x->x_sym = (s->s_name[0]? s : gensym("print~")); x->x_count = 0; x->x_f = 0; return (x); } static void print_setup(void) { print_class = class_new(gensym("print~"), (t_newmethod)print_new, 0, sizeof(t_print), 0, A_DEFSYM, 0); CLASS_MAINSIGNALIN(print_class, t_print, x_f); class_addmethod(print_class, (t_method)print_dsp, gensym("dsp"), A_CANT, 0); class_addbang(print_class, print_bang); class_addfloat(print_class, print_float); } /* ------------------------ bang~ -------------------------- */ static t_class *bang_tilde_class; typedef struct _bang { t_object x_obj; t_clock *x_clock; } t_bang; static t_int *bang_tilde_perform(t_int *w) { t_bang *x = (t_bang *)(w[1]); clock_delay(x->x_clock, 0); return (w+2); } static void bang_tilde_dsp(t_bang *x, t_signal **sp) { dsp_add(bang_tilde_perform, 1, x); } static void bang_tilde_tick(t_bang *x) { outlet_bang(x->x_obj.ob_outlet); } static void bang_tilde_free(t_bang *x) { clock_free(x->x_clock); } static void *bang_tilde_new(t_symbol *s) { t_bang *x = (t_bang *)pd_new(bang_tilde_class); x->x_clock = clock_new(x, (t_method)bang_tilde_tick); outlet_new(&x->x_obj, &s_bang); return (x); } static void bang_tilde_setup(void) { bang_tilde_class = class_new(gensym("bang~"), (t_newmethod)bang_tilde_new, (t_method)bang_tilde_free, sizeof(t_bang), 0, 0); class_addmethod(bang_tilde_class, (t_method)bang_tilde_dsp, gensym("dsp"), 0); } /* ------------------------ snake_in~ -------------------------- */ static t_class *snake_in_tilde_class; typedef struct _snake_in { t_object x_obj; t_sample x_f; int x_nchans; } t_snake_in; static void snake_in_tilde_dsp(t_snake_in *x, t_signal **sp) { int i; /* create an n-channel output signal. sp has n+1 elements. */ signal_setmultiout(&sp[x->x_nchans], x->x_nchans); /* add n copy operations to the DSP chain, one from each input */ for (i = 0; i < x->x_nchans; i++) dsp_add_copy(sp[i]->s_vec, sp[x->x_nchans]->s_vec + i * sp[0]->s_length, sp[0]->s_length); } static void *snake_in_tilde_new(t_floatarg fnchans) { t_snake_in *x = (t_snake_in *)pd_new(snake_in_tilde_class); int i; if ((x->x_nchans = fnchans) <= 0) x->x_nchans = 2; for (i = 1; i < x->x_nchans; i++) inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); outlet_new(&x->x_obj, &s_signal); return (x); } /* ------------------------ snake_out~ -------------------------- */ static t_class *snake_out_tilde_class; typedef struct _snake_out { t_object x_obj; t_sample x_f; int x_nchans; } t_snake_out; static void snake_out_tilde_dsp(t_snake_out *x, t_signal **sp) { int i, usenchans = (x->x_nchans < sp[0]->s_nchans ? x->x_nchans : sp[0]->s_nchans); /* create n one-channel output signals and add a copy operation for each one tothe DSP chain */ for (i = 0; i < x->x_nchans; i++) { signal_setmultiout(&sp[i+1], 1); if (i < usenchans) dsp_add_copy(sp[0]->s_vec + i * sp[0]->s_length, sp[i+1]->s_vec, sp[0]->s_length); else dsp_add_zero(sp[i+1]->s_vec, sp[0]->s_length); } } static void *snake_out_tilde_new(t_floatarg fnchans) { t_snake_out *x = (t_snake_out *)pd_new(snake_out_tilde_class); int i; if ((x->x_nchans = fnchans) <= 0) x->x_nchans = 2; for (i = 0; i < x->x_nchans; i++) outlet_new(&x->x_obj, &s_signal); return (x); } static void *snake_tilde_new(t_symbol *s, int argc, t_atom *argv) { if (!argc || argv[0].a_type != A_SYMBOL) pd_this->pd_newest = snake_in_tilde_new(atom_getfloatarg(0, argc, argv)); else { const char *str = argv[0].a_w.w_symbol->s_name; if (!strcmp(str, "in")) pd_this->pd_newest = snake_in_tilde_new(atom_getfloatarg(1, argc, argv)); else if (!strcmp(str, "out")) pd_this->pd_newest = snake_out_tilde_new(atom_getfloatarg(1, argc, argv)); else { pd_error(0, "list %s: unknown function", str); pd_this->pd_newest = 0; } } return (pd_this->pd_newest); } static void snake_tilde_setup(void) { snake_in_tilde_class = class_new(gensym("snake_in~"), (t_newmethod)snake_in_tilde_new, 0, sizeof(t_snake_in), CLASS_MULTICHANNEL, A_DEFFLOAT, 0); CLASS_MAINSIGNALIN(snake_in_tilde_class, t_snake_in, x_f); class_addmethod(snake_in_tilde_class, (t_method)snake_in_tilde_dsp, gensym("dsp"), 0); class_sethelpsymbol(snake_in_tilde_class, gensym("snake-tilde")); snake_out_tilde_class = class_new(gensym("snake_out~"), (t_newmethod)snake_out_tilde_new, 0, sizeof(t_snake_out), CLASS_MULTICHANNEL, A_DEFFLOAT, 0); CLASS_MAINSIGNALIN(snake_out_tilde_class, t_snake_out, x_f); class_addmethod(snake_out_tilde_class, (t_method)snake_out_tilde_dsp, gensym("dsp"), 0); class_sethelpsymbol(snake_out_tilde_class, gensym("snake-tilde")); class_addcreator((t_newmethod)snake_tilde_new, gensym("snake~"), A_GIMME, 0); } /* ------------------------ global setup routine ------------------------- */ void d_misc_setup(void) { print_setup(); bang_tilde_setup(); snake_tilde_setup(); } ================================================ FILE: libs/libpd/pure-data/src/d_osc.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* sinusoidal oscillator and table lookup; see also tabosc4~ in d_array.c. */ #include "m_pd.h" #include "math.h" #define BIGFLOAT 1.0e+19 #define UNITBIT32 1572864. /* 3*2^19; bit 32 has place value 1 */ #include "m_private_utils.h" #if BYTE_ORDER == LITTLE_ENDIAN # define HIOFFSET 1 # define LOWOFFSET 0 #else # define HIOFFSET 0 /* word offset to find MSB */ # define LOWOFFSET 1 /* word offset to find LSB */ #endif union tabfudge { double tf_d; int32_t tf_i[2]; }; /* -------------------------- phasor~ ------------------------------ */ static t_class *phasor_class; #if 1 /* in the style of R. Hoeldrich (ICMC 1995 Banff) */ typedef struct _phasor { t_object x_obj; double x_phase; t_float x_conv; t_float x_f; // scalar frequency } t_phasor; static void *phasor_new(t_floatarg f) { t_phasor *x = (t_phasor *)pd_new(phasor_class); x->x_f = f; inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("ft1")); x->x_phase = 0; x->x_conv = 0; outlet_new(&x->x_obj, gensym("signal")); return (x); } static t_int *phasor_perform(t_int *w) { t_phasor *x = (t_phasor *)(w[1]); t_sample *in = (t_float *)(w[2]); t_sample *out = (t_float *)(w[3]); int n = (int)(w[4]); double dphase = x->x_phase + (double)UNITBIT32; union tabfudge tf; int normhipart; t_float conv = x->x_conv; tf.tf_d = UNITBIT32; normhipart = tf.tf_i[HIOFFSET]; tf.tf_d = dphase; while (n--) { tf.tf_i[HIOFFSET] = normhipart; dphase += *in++ * conv; *out++ = tf.tf_d - UNITBIT32; tf.tf_d = dphase; } tf.tf_i[HIOFFSET] = normhipart; x->x_phase = tf.tf_d - UNITBIT32; return (w+5); } static void phasor_dsp(t_phasor *x, t_signal **sp) { x->x_conv = 1./sp[0]->s_sr; dsp_add(phasor_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, (t_int)sp[0]->s_n); } static void phasor_ft1(t_phasor *x, t_float f) { x->x_phase = (double)f; } static void phasor_setup(void) { phasor_class = class_new(gensym("phasor~"), (t_newmethod)phasor_new, 0, sizeof(t_phasor), 0, A_DEFFLOAT, 0); CLASS_MAINSIGNALIN(phasor_class, t_phasor, x_f); class_addmethod(phasor_class, (t_method)phasor_dsp, gensym("dsp"), A_CANT, 0); class_addmethod(phasor_class, (t_method)phasor_ft1, gensym("ft1"), A_FLOAT, 0); } #endif /* Hoeldrich version */ /* ------------------------ cos~ ----------------------------- */ float *cos_table; static t_class *cos_class; typedef struct _cos { t_object x_obj; t_float x_f; // scalar frequency } t_cos; static void *cos_new(t_floatarg f) { t_cos *x = (t_cos *)pd_new(cos_class); outlet_new(&x->x_obj, gensym("signal")); x->x_f = f; return (x); } static t_int *cos_perform(t_int *w) { t_sample *in = (t_sample *)(w[1]); t_sample *out = (t_sample *)(w[2]); int n = (int)(w[3]); float *tab = cos_table, *addr; t_float f1, f2, frac; double dphase; int normhipart; union tabfudge tf; tf.tf_d = UNITBIT32; normhipart = tf.tf_i[HIOFFSET]; #if 0 /* this is the readable version of the code. */ while (n--) { dphase = (double)(*in++ * (float)(COSTABSIZE)) + UNITBIT32; tf.tf_d = dphase; addr = tab + (tf.tf_i[HIOFFSET] & (COSTABSIZE-1)); tf.tf_i[HIOFFSET] = normhipart; frac = tf.tf_d - UNITBIT32; f1 = addr[0]; f2 = addr[1]; *out++ = f1 + frac * (f2 - f1); } #endif #if 1 /* this is the same, unwrapped by hand. */ dphase = (double)(*in++ * (float)(COSTABSIZE)) + UNITBIT32; tf.tf_d = dphase; addr = tab + (tf.tf_i[HIOFFSET] & (COSTABSIZE-1)); tf.tf_i[HIOFFSET] = normhipart; while (--n) { dphase = (double)(*in++ * (float)(COSTABSIZE)) + UNITBIT32; frac = tf.tf_d - UNITBIT32; tf.tf_d = dphase; f1 = addr[0]; f2 = addr[1]; addr = tab + (tf.tf_i[HIOFFSET] & (COSTABSIZE-1)); *out++ = f1 + frac * (f2 - f1); tf.tf_i[HIOFFSET] = normhipart; } frac = tf.tf_d - UNITBIT32; f1 = addr[0]; f2 = addr[1]; *out++ = f1 + frac * (f2 - f1); #endif return (w+4); } static void cos_dsp(t_cos *x, t_signal **sp) { signal_setmultiout(&sp[1], sp[0]->s_nchans); dsp_add(cos_perform, 3, sp[0]->s_vec, sp[1]->s_vec, (t_int)(sp[0]->s_length * sp[0]->s_nchans)); } static void cos_maketable(void) { int i; float *fp, phase, phsinc = (2. * 3.14159) / COSTABSIZE; union tabfudge tf; if (cos_table) return; cos_table = (float *)getbytes(sizeof(float) * (COSTABSIZE+1)); for (i = COSTABSIZE + 1, fp = cos_table, phase = 0; i--; fp++, phase += phsinc) *fp = cos(phase); /* here we check at startup whether the byte alignment is as we declared it. If not, the code has to be recompiled the other way. */ tf.tf_d = UNITBIT32 + 0.5; if ((unsigned)tf.tf_i[LOWOFFSET] != 0x80000000) bug("cos~: unexpected machine alignment"); } static void cos_cleanup(t_class *c) { freebytes(cos_table, sizeof(float) * (COSTABSIZE+1)); cos_table = 0; } static void cos_setup(void) { cos_class = class_new(gensym("cos~"), (t_newmethod)cos_new, 0, sizeof(t_cos), CLASS_MULTICHANNEL, A_DEFFLOAT, 0); class_setfreefn(cos_class, cos_cleanup); CLASS_MAINSIGNALIN(cos_class, t_cos, x_f); class_addmethod(cos_class, (t_method)cos_dsp, gensym("dsp"), A_CANT, 0); cos_maketable(); } /* ------------------------ osc~ ----------------------------- */ static t_class *osc_class, *scalarosc_class; typedef struct _osc { t_object x_obj; double x_phase; t_float x_conv; t_float x_f; // scalar frequency } t_osc; static void *osc_new(t_floatarg f) { t_osc *x = (t_osc *)pd_new(osc_class); x->x_f = f; outlet_new(&x->x_obj, gensym("signal")); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("ft1")); x->x_phase = 0; x->x_conv = 0; return (x); } static t_int *osc_perform(t_int *w) { t_osc *x = (t_osc *)(w[1]); t_sample *in = (t_sample *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); float *tab = cos_table, *addr; t_float f1, f2, frac; double dphase = x->x_phase + UNITBIT32; int normhipart; union tabfudge tf; float conv = x->x_conv; tf.tf_d = UNITBIT32; normhipart = tf.tf_i[HIOFFSET]; #if 0 while (n--) { tf.tf_d = dphase; dphase += *in++ * conv; addr = tab + (tf.tf_i[HIOFFSET] & (COSTABSIZE-1)); tf.tf_i[HIOFFSET] = normhipart; frac = tf.tf_d - UNITBIT32; f1 = addr[0]; f2 = addr[1]; *out++ = f1 + frac * (f2 - f1); } #endif #if 1 tf.tf_d = dphase; dphase += *in++ * conv; addr = tab + (tf.tf_i[HIOFFSET] & (COSTABSIZE-1)); tf.tf_i[HIOFFSET] = normhipart; frac = tf.tf_d - UNITBIT32; while (--n) { tf.tf_d = dphase; f1 = addr[0]; dphase += *in++ * conv; f2 = addr[1]; addr = tab + (tf.tf_i[HIOFFSET] & (COSTABSIZE-1)); tf.tf_i[HIOFFSET] = normhipart; *out++ = f1 + frac * (f2 - f1); frac = tf.tf_d - UNITBIT32; } f1 = addr[0]; f2 = addr[1]; *out++ = f1 + frac * (f2 - f1); #endif tf.tf_d = UNITBIT32 * COSTABSIZE; normhipart = tf.tf_i[HIOFFSET]; tf.tf_d = dphase + (UNITBIT32 * COSTABSIZE - UNITBIT32); tf.tf_i[HIOFFSET] = normhipart; x->x_phase = tf.tf_d - UNITBIT32 * COSTABSIZE; return (w+5); } static void osc_dsp(t_osc *x, t_signal **sp) { x->x_conv = COSTABSIZE/sp[0]->s_sr; dsp_add(osc_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, (t_int)sp[0]->s_n); } static void osc_ft1(t_osc *x, t_float f) { x->x_phase = COSTABSIZE * f; } static void osc_setup(void) { osc_class = class_new(gensym("osc~"), (t_newmethod)osc_new, 0, sizeof(t_osc), 0, A_DEFFLOAT, 0); CLASS_MAINSIGNALIN(osc_class, t_osc, x_f); class_addmethod(osc_class, (t_method)osc_dsp, gensym("dsp"), A_CANT, 0); class_addmethod(osc_class, (t_method)osc_ft1, gensym("ft1"), A_FLOAT, 0); cos_maketable(); } /* ---- vcf~ - resonant filter with audio-rate center frequency input ----- */ typedef struct vcfctl { t_float c_re; t_float c_im; t_float c_q; t_float c_isr; } t_vcfctl; typedef struct sigvcf { t_object x_obj; t_vcfctl x_cspace; t_float x_f; } t_sigvcf; t_class *sigvcf_class; static void *sigvcf_new(t_floatarg q) { t_sigvcf *x = (t_sigvcf *)pd_new(sigvcf_class); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("ft1")); outlet_new(&x->x_obj, gensym("signal")); outlet_new(&x->x_obj, gensym("signal")); x->x_cspace.c_re = 0; x->x_cspace.c_im = 0; x->x_cspace.c_q = q; x->x_cspace.c_isr = 0; x->x_f = 0; return (x); } static void sigvcf_ft1(t_sigvcf *x, t_float f) { if(f < 0.) f = 0.; if(f > BIGFLOAT) f = BIGFLOAT; x->x_cspace.c_q = f; } static t_int *sigvcf_perform(t_int *w) { t_sample *in1 = (t_sample *)(w[1]); t_sample *in2 = (t_sample *)(w[2]); t_sample *out1 = (t_sample *)(w[3]); t_sample *out2 = (t_sample *)(w[4]); t_vcfctl *c = (t_vcfctl *)(w[5]); int n = (int)w[6]; int i; t_float re = c->c_re, re2; t_float im = c->c_im; t_float q = c->c_q; t_float isr = c->c_isr; t_float qinv = (q > 0? 1.0f/q : 0); t_float ampcorrect = 2. - 2. / (q + 2.); t_float coefr, coefi; float *tab = cos_table, *addr, f1, f2, frac; double dphase; int normhipart, tabindex; union tabfudge tf; tf.tf_d = UNITBIT32; normhipart = tf.tf_i[HIOFFSET]; for (i = 0; i < n; i++) { float cf, cfindx, r, oneminusr; cf = *in2++ * isr; if (cf < 0) cf = 0; cfindx = cf * (float)(COSTABSIZE/6.28318f); r = (qinv > 0 ? 1 - cf * qinv : 0); if (r < 0) r = 0; oneminusr = 1.0f - r; dphase = ((double)(cfindx)) + UNITBIT32; tf.tf_d = dphase; tabindex = tf.tf_i[HIOFFSET] & (COSTABSIZE-1); addr = tab + tabindex; tf.tf_i[HIOFFSET] = normhipart; frac = tf.tf_d - UNITBIT32; f1 = addr[0]; f2 = addr[1]; coefr = r * (f1 + frac * (f2 - f1)); addr = tab + ((tabindex - (COSTABSIZE/4)) & (COSTABSIZE-1)); f1 = addr[0]; f2 = addr[1]; coefi = r * (f1 + frac * (f2 - f1)); f1 = *in1++; re2 = re; *out1++ = re = ampcorrect * oneminusr * f1 + coefr * re2 - coefi * im; *out2++ = im = coefi * re2 + coefr * im; } if (PD_BIGORSMALL(re)) re = 0; if (PD_BIGORSMALL(im)) im = 0; c->c_re = re; c->c_im = im; return (w+7); } static void sigvcf_dsp(t_sigvcf *x, t_signal **sp) { x->x_cspace.c_isr = 6.28318f/sp[0]->s_sr; dsp_add(sigvcf_perform, 6, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec, &x->x_cspace, (t_int)sp[0]->s_n); } static void sigvcf_setup(void) { sigvcf_class = class_new(gensym("vcf~"), (t_newmethod)sigvcf_new, 0, sizeof(t_sigvcf), 0, A_DEFFLOAT, 0); CLASS_MAINSIGNALIN(sigvcf_class, t_sigvcf, x_f); class_addmethod(sigvcf_class, (t_method)sigvcf_dsp, gensym("dsp"), A_CANT, 0); class_addmethod(sigvcf_class, (t_method)sigvcf_ft1, gensym("ft1"), A_FLOAT, 0); } /* -------------------------- noise~ ------------------------------ */ static t_class *noise_class; typedef struct _noise { t_object x_obj; int x_val; } t_noise; static void *noise_new(void) { t_noise *x = (t_noise *)pd_new(noise_class); /* seed each instance differently. Once in a blue moon two threads could grab the same seed value. We can live with that. */ static int init = 307; x->x_val = (init *= 1319); outlet_new(&x->x_obj, gensym("signal")); return (x); } static t_int *noise_perform(t_int *w) { t_sample *out = (t_sample *)(w[1]); int *vp = (int *)(w[2]); int n = (int)(w[3]); int val = *vp; while (n--) { *out++ = ((t_sample)((val & 0x7fffffff) - 0x40000000)) * (t_sample)(1.0 / 0x40000000); val = val * 435898247 + 382842987; } *vp = val; return (w+4); } static void noise_dsp(t_noise *x, t_signal **sp) { dsp_add(noise_perform, 3, sp[0]->s_vec, &x->x_val, (t_int)sp[0]->s_n); } static void noise_float(t_noise *x, t_float f) { /* set the seed */ x->x_val = (int)f; } static void noise_setup(void) { noise_class = class_new(gensym("noise~"), (t_newmethod)noise_new, 0, sizeof(t_noise), 0, A_DEFFLOAT, 0); class_addmethod(noise_class, (t_method)noise_dsp, gensym("dsp"), A_CANT, 0); class_addmethod(noise_class, (t_method)noise_float, gensym("seed"), A_FLOAT, 0); } /******************** tabosc4~ ***********************/ static t_class *tabosc4_tilde_class; typedef struct _tabosc4_tilde { t_object x_obj; t_float x_fnpoints; t_float x_finvnpoints; t_word *x_vec; t_symbol *x_arrayname; t_float x_f; double x_phase; t_float x_conv; } t_tabosc4_tilde; static void *tabosc4_tilde_new(t_symbol *s) { t_tabosc4_tilde *x = (t_tabosc4_tilde *)pd_new(tabosc4_tilde_class); x->x_arrayname = s; x->x_vec = 0; x->x_fnpoints = 512.; x->x_finvnpoints = (1./512.); outlet_new(&x->x_obj, gensym("signal")); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("ft1")); x->x_f = 0; return (x); } static t_int *tabosc4_tilde_perform(t_int *w) { t_tabosc4_tilde *x = (t_tabosc4_tilde *)(w[1]); t_sample *in = (t_sample *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); int normhipart; union tabfudge tf; t_float fnpoints = x->x_fnpoints; int mask = fnpoints - 1; t_float conv = fnpoints * x->x_conv; t_word *tab = x->x_vec, *addr; double dphase = fnpoints * x->x_phase + UNITBIT32; if (!tab) goto zero; tf.tf_d = UNITBIT32; normhipart = tf.tf_i[HIOFFSET]; #if 1 while (n--) { t_sample frac, a, b, c, d, cminusb; tf.tf_d = dphase; dphase += *in++ * conv; addr = tab + (tf.tf_i[HIOFFSET] & mask); tf.tf_i[HIOFFSET] = normhipart; frac = tf.tf_d - UNITBIT32; a = addr[0].w_float; b = addr[1].w_float; c = addr[2].w_float; d = addr[3].w_float; cminusb = c-b; *out++ = b + frac * ( cminusb - 0.1666667f * (1.-frac) * ( (d - a - 3.0f * cminusb) * frac + (d + 2.0f*a - 3.0f*b) ) ); } #endif tf.tf_d = UNITBIT32 * fnpoints; normhipart = tf.tf_i[HIOFFSET]; tf.tf_d = dphase + (UNITBIT32 * fnpoints - UNITBIT32); tf.tf_i[HIOFFSET] = normhipart; x->x_phase = (tf.tf_d - UNITBIT32 * fnpoints) * x->x_finvnpoints; return (w+5); zero: while (n--) *out++ = 0; return (w+5); } static void tabosc4_tilde_set(t_tabosc4_tilde *x, t_symbol *s) { t_garray *a; int npoints, pointsinarray; x->x_arrayname = s; if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class))) { if (*s->s_name) pd_error(x, "tabosc4~: %s: no such array", x->x_arrayname->s_name); x->x_vec = 0; } else if (!garray_getfloatwords(a, &pointsinarray, &x->x_vec)) { pd_error(x, "%s: bad template for tabosc4~", x->x_arrayname->s_name); x->x_vec = 0; } else if ((npoints = pointsinarray - 3) != (1 << ilog2(pointsinarray - 3))) { pd_error(x, "%s: number of points (%d) not a power of 2 plus three", x->x_arrayname->s_name, pointsinarray); x->x_vec = 0; } else { x->x_fnpoints = npoints; x->x_finvnpoints = 1./npoints; garray_usedindsp(a); } } static void tabosc4_tilde_ft1(t_tabosc4_tilde *x, t_float f) { x->x_phase = f; } static void tabosc4_tilde_dsp(t_tabosc4_tilde *x, t_signal **sp) { x->x_conv = 1. / sp[0]->s_sr; tabosc4_tilde_set(x, x->x_arrayname); dsp_add(tabosc4_tilde_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, (t_int)sp[0]->s_n); } static void tabosc4_tilde_setup(void) { tabosc4_tilde_class = class_new(gensym("tabosc4~"), (t_newmethod)tabosc4_tilde_new, 0, sizeof(t_tabosc4_tilde), 0, A_DEFSYM, 0); CLASS_MAINSIGNALIN(tabosc4_tilde_class, t_tabosc4_tilde, x_f); class_addmethod(tabosc4_tilde_class, (t_method)tabosc4_tilde_dsp, gensym("dsp"), A_CANT, 0); class_addmethod(tabosc4_tilde_class, (t_method)tabosc4_tilde_set, gensym("set"), A_SYMBOL, 0); class_addmethod(tabosc4_tilde_class, (t_method)tabosc4_tilde_ft1, gensym("ft1"), A_FLOAT, 0); } /* ----------------------- global setup routine ---------------- */ void d_osc_setup(void) { phasor_setup(); cos_setup(); osc_setup(); sigvcf_setup(); noise_setup(); tabosc4_tilde_setup(); } ================================================ FILE: libs/libpd/pure-data/src/d_resample.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ #include "m_pd.h" /* --------------------- up/down-sampling --------------------- */ t_int *downsampling_perform_0(t_int *w) { t_sample *in = (t_sample *)(w[1]); /* original signal */ t_sample *out = (t_sample *)(w[2]); /* downsampled signal */ int down = (int)(w[3]); /* downsampling factor */ int parent = (int)(w[4]); /* original vectorsize */ int n=parent/down; while(n--){ *out++=*in; in+=down; } return (w+5); } t_int *upsampling_perform_0(t_int *w) { t_sample *in = (t_sample *)(w[1]); /* original signal */ t_sample *out = (t_sample *)(w[2]); /* upsampled signal */ int up = (int)(w[3]); /* upsampling factor */ int parent = (int)(w[4]); /* original vectorsize */ int n=parent*up; t_sample *dummy = out; while(n--)*out++=0; n = parent; out = dummy; while(n--){ *out=*in++; out+=up; } return (w+5); } t_int *upsampling_perform_hold(t_int *w) { t_sample *in = (t_sample *)(w[1]); /* original signal */ t_sample *out = (t_sample *)(w[2]); /* upsampled signal */ int up = (int)(w[3]); /* upsampling factor */ int parent = (int)(w[4]); /* original vectorsize */ int i=up; int n=parent; t_sample *dum_out = out; t_sample *dum_in = in; while (i--) { n = parent; out = dum_out+i; in = dum_in; while(n--){ *out=*in++; out+=up; } } return (w+5); } t_int *upsampling_perform_linear(t_int *w) { t_resample *x= (t_resample *)(w[1]); t_sample *in = (t_sample *)(w[2]); /* original signal */ t_sample *out = (t_sample *)(w[3]); /* upsampled signal */ int up = (int)(w[4]); /* upsampling factor */ int parent = (int)(w[5]); /* original vectorsize */ int length = parent*up; int n; t_sample *fp; t_sample a=*x->buffer, b=*in; for (n=0; nbuffer = a; return (w+6); } /* ----------------------- public -------------------------------- */ /* utils */ void resample_init(t_resample *x) { x->downsample=x->upsample=1; x->s_n = x->coefsize = x->bufsize = 0; x->s_vec = x->coeffs = x->buffer = 0; } void resample_free(t_resample *x) { if (x->s_n) t_freebytes(x->s_vec, x->s_n*sizeof(*x->s_vec)); if (x->coefsize) t_freebytes(x->coeffs, x->coefsize*sizeof(*x->coeffs)); if (x->bufsize) t_freebytes(x->buffer, x->bufsize*sizeof(*x->buffer)); x->s_n = x->coefsize = x->bufsize = 0; x->s_vec = x->coeffs = x->buffer = 0; } /* dsp-adding */ void resample_dsp(t_resample *x, t_sample* in, int insize, t_sample* out, int outsize, int method) { if (insize == outsize){ bug("nothing to be done"); return; } if (insize > outsize) { /* downsampling */ if (insize % outsize) { pd_error(0, "bad downsampling factor"); return; } switch (method) { default: /* always zero padding */ dsp_add(downsampling_perform_0, 4, in, out, (t_int)(insize/outsize), (t_int)insize); } } else { /* upsampling */ if (outsize % insize) { pd_error(0, "bad upsampling factor"); return; } switch (method) { case 1: /* sample and hold */ dsp_add(upsampling_perform_hold, 4, in, out, (t_int)(outsize/insize), (t_int)insize); break; case 2: /* linear interpolation */ if (x->bufsize != 1) { t_freebytes(x->buffer, x->bufsize*sizeof(*x->buffer)); x->bufsize = 1; x->buffer = t_getbytes(x->bufsize*sizeof(*x->buffer)); } dsp_add(upsampling_perform_linear, 5, x, in, out, (t_int)(outsize/insize), (t_int)insize); break; default: /* zero padding */ dsp_add(upsampling_perform_0, 4, in, out, (t_int)(outsize/insize), (t_int)insize); } } } void resamplefrom_dsp(t_resample *x, t_sample *in, int insize, int outsize, int method) { if (insize==outsize) { t_freebytes(x->s_vec, x->s_n * sizeof(*x->s_vec)); x->s_n = 0; x->s_vec = in; return; } if (x->s_n != outsize) { t_sample *buf=x->s_vec; t_freebytes(buf, x->s_n * sizeof(*buf)); buf = (t_sample *)t_getbytes(outsize * sizeof(*buf)); x->s_vec = buf; x->s_n = outsize; } resample_dsp(x, in, insize, x->s_vec, x->s_n, method); return; } void resampleto_dsp(t_resample *x, t_sample *out, int insize, int outsize, int method) { if (insize==outsize) { if (x->s_n)t_freebytes(x->s_vec, x->s_n * sizeof(*x->s_vec)); x->s_n = 0; x->s_vec = out; return; } if (x->s_n != insize) { t_sample *buf=x->s_vec; t_freebytes(buf, x->s_n * sizeof(*buf)); buf = (t_sample *)t_getbytes(insize * sizeof(*buf)); x->s_vec = buf; x->s_n = insize; } resample_dsp(x, x->s_vec, x->s_n, out, outsize, method); return; } ================================================ FILE: libs/libpd/pure-data/src/d_soundfile.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. Updated 2019 Dan Wilcox. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* this file contains, first, a collection of soundfile access routines, a sort of soundfile library. Second, the "soundfiler" object is defined which uses the routines to read or write soundfiles, synchronously, from garrays. These operations are not to be done in "real time" as they may have to wait for disk accesses (even the write routine.) Finally, the realtime objects readsf~ and writesf~ are defined which confine disk operations to a separate thread so that they can be used in real time. The readsf~ and writesf~ objects use Posix-like threads. */ #include "d_soundfile.h" #ifdef _WIN32 #include #endif #include #include #include /* Supported sample formats: LPCM (16 or 24 bit int) & 32 bit float */ #define MAXSFCHANS 64 /* GLIBC large file support */ #ifdef _LARGEFILE64_SOURCE #define open open64 #endif /* MSVC uses different naming for these */ #ifdef _MSC_VER #define O_CREAT _O_CREAT #define O_TRUNC _O_TRUNC #define O_WRONLY _O_WRONLY #endif #define SCALE (1. / (1024. * 1024. * 1024. * 2.)) /* float sample conversion wrapper */ typedef union _floatuint { float f; uint32_t ui; } t_floatuint; /* ----- soundfile ----- */ void soundfile_clear(t_soundfile *sf) { memset(sf, 0, sizeof(t_soundfile)); sf->sf_fd = -1; sf->sf_type = NULL; sf->sf_bytelimit = SFMAXBYTES; } void soundfile_copy(t_soundfile *dst, const t_soundfile *src) { memcpy((char *)dst, (char *)src, sizeof(t_soundfile)); } int soundfile_needsbyteswap(const t_soundfile *sf) { return sf->sf_bigendian != sys_isbigendian(); } const char* soundfile_strerror(int errnum) { switch (errnum) { case SOUNDFILE_ERRUNKNOWN: return "unknown header format"; case SOUNDFILE_ERRMALFORMED: return "bad header format"; case SOUNDFILE_ERRVERSION: return "unsupported header format version"; case SOUNDFILE_ERRSAMPLEFMT: return "unsupported sample format"; default: /* C/POSIX error */ return strerror(errnum); } } /** output soundfile format info as a list */ static void outlet_soundfileinfo(t_outlet *out, t_soundfile *sf) { t_atom info_list[5]; SETFLOAT((t_atom *)info_list, (t_float)sf->sf_samplerate); SETFLOAT((t_atom *)info_list+1, (t_float)(sf->sf_headersize < 0 ? 0 : sf->sf_headersize)); SETFLOAT((t_atom *)info_list+2, (t_float)sf->sf_nchannels); SETFLOAT((t_atom *)info_list+3, (t_float)sf->sf_bytespersample); SETSYMBOL((t_atom *)info_list+4, gensym((sf->sf_bigendian ? "b" : "l"))); outlet_list(out, &s_list, 5, (t_atom *)info_list); } /* post soundfile error, try to print type name */ static void object_sferror(const void *x, const char *header, const char *filename, int errnum, const t_soundfile *sf) { if (sf && sf->sf_type) pd_error(x, "%s %s: %s: %s", header, sf->sf_type->t_name, filename, soundfile_strerror(errnum)); else pd_error(x, "%s: %s: %s", header, filename, soundfile_strerror(errnum)); } /* ----- soundfile type ----- */ #define SFMAXTYPES 4 /* should these globals be PERTHREAD? */ /** supported type implementations */ static t_soundfile_type *sf_types[SFMAXTYPES] = {0}; /** number of types */ static size_t sf_numtypes = 0; /** min required header size, largest among the current types */ static size_t sf_minheadersize = 0; /** printable type argument list, dash prepended and separated by spaces */ static char sf_typeargs[MAXPDSTRING] = {0}; /* built-in type implementations */ void soundfile_wave_setup(void); void soundfile_aiff_setup(void); void soundfile_caf_setup(void); void soundfile_next_setup(void); /** set up built-in types */ void soundfile_type_setup(void) { soundfile_wave_setup(); /* default first */ soundfile_aiff_setup(); soundfile_caf_setup(); soundfile_next_setup(); } int soundfile_addtype(const t_soundfile_type *type) { int i; if (sf_numtypes == SFMAXTYPES) { pd_error(0, "soundfile: max number of type implementations reached"); return 0; } sf_types[sf_numtypes] = (t_soundfile_type *)type; sf_numtypes++; if (type->t_minheadersize > sf_minheadersize) sf_minheadersize = type->t_minheadersize; strcat(sf_typeargs, (sf_numtypes > 1 ? " -" : "-")); strcat(sf_typeargs, type->t_name); return 1; } /** return type list head pointer */ static t_soundfile_type **soundfile_firsttype(void) { return &sf_types[0]; } /** return next type list pointer or NULL if at the end */ static t_soundfile_type **soundfile_nexttype(t_soundfile_type **t) { return (t == &sf_types[sf_numtypes-1] ? NULL : ++t); } /** find type by name, returns NULL if not found */ static t_soundfile_type *soundfile_findtype(const char *name) { t_soundfile_type **t = soundfile_firsttype(); while (t) { if (!strcmp(name, (*t)->t_name)) break; t = soundfile_nexttype(t); } return (t ? *t : NULL); } /* ----- ASCII ----- */ /** compound ascii read/write args */ typedef struct _asciiargs { ssize_t aa_onsetframe; /* start frame */ ssize_t aa_nframes; /* nframes to read/write */ int aa_nchannels; /* number of channels to read/write */ t_word **aa_vectors; /* vectors to read into/write out of */ t_garray **aa_garrays; /* read: arrays to resize & read into */ int aa_resize; /* read: resize when reading? */ size_t aa_maxsize; /* read: max size to read/resize to */ t_sample aa_normfactor; /* write: normalization factor */ } t_asciiargs; static int ascii_hasextension(const char *filename, size_t size) { int len = strnlen(filename, size); if (len >= 5 && !strncmp(filename + (len - 4), ".txt", 4)) return 1; return 0; } static int ascii_addextension(char *filename, size_t size) { size_t len = strnlen(filename, size); if (len + 4 >= size) return 0; strcpy(filename + len, ".txt"); return 1; } /* ----- read write ----- */ ssize_t fd_read(int fd, off_t offset, void *dst, size_t size) { if (lseek(fd, offset, SEEK_SET) != offset) return -1; return read(fd, dst, size); } ssize_t fd_write(int fd, off_t offset, const void *src, size_t size) { if (lseek(fd, offset, SEEK_SET) != offset) return -1; return write(fd, src, size); } /* ----- byte swappers ----- */ int sys_isbigendian(void) { unsigned short s = 1; unsigned char c = *(char *)(&s); return (c == 0); } uint64_t swap8(uint64_t n, int doit) { if (doit) return (((n >> 56) & 0x00000000000000ffULL) | ((n >> 40) & 0x000000000000ff00ULL) | ((n >> 24) & 0x0000000000ff0000ULL) | ((n >> 8) & 0x00000000ff000000ULL) | ((n << 8) & 0x000000ff00000000ULL) | ((n << 24) & 0x0000ff0000000000ULL) | ((n << 40) & 0x00ff000000000000ULL) | ((n << 56) & 0xff00000000000000ULL)); return n; } int64_t swap8s(int64_t n, int doit) { if (doit) { n = ((n << 8) & 0xff00ff00ff00ff00ULL) | ((n >> 8) & 0x00ff00ff00ff00ffULL); n = ((n << 16) & 0xffff0000ffff0000ULL) | ((n >> 16) & 0x0000ffff0000ffffULL ); return (n << 32) | ((n >> 32) & 0xffffffffULL); } return n; } uint32_t swap4(uint32_t n, int doit) { if (doit) return (((n & 0x0000ff) << 24) | ((n & 0x0000ff00) << 8) | ((n & 0xff0000) >> 8) | ((n & 0xff000000) >> 24)); return n; } int32_t swap4s(int32_t n, int doit) { if (doit) { n = ((n << 8) & 0xff00ff00) | ((n >> 8) & 0xff00ff); return (n << 16) | ((n >> 16) & 0xffff); } return n; } uint16_t swap2(uint16_t n, int doit) { if (doit) return (((n & 0x00ff) << 8) | ((n & 0xff00) >> 8)); return n; } void swapstring4(char *foo, int doit) { if (doit) { char a = foo[0], b = foo[1], c = foo[2], d = foo[3]; foo[0] = d; foo[1] = c; foo[2] = b; foo[3] = a; } } void swapstring8(char *foo, int doit) { if (doit) { char a = foo[0], b = foo[1], c = foo[2], d = foo[3], e = foo[4], f = foo[5], g = foo[6], h = foo[7]; foo[0] = h; foo[1] = g; foo[2] = f; foo[3] = e; foo[4] = d; foo[5] = c; foo[6] = b; foo[7] = a; } } /* ----------------------- soundfile access routines ----------------------- */ /** This routine opens a file, looks for a supported file format header, seeks to end of it, and fills in the soundfile header info values. Only 2- and 3-byte fixed-point samples and 4-byte floating point samples are supported. If sf->sf_headersize is nonzero, the caller should supply the number of channels, endinanness, and bytes per sample; the header is ignored. If sf->sf_type is non-NULL, the given type implementation is used. Otherwise, the routine tries to read the header and fill in the properties. Fills sf struct on success, closes fd on failure. */ int open_soundfile_via_fd(int fd, t_soundfile *sf, size_t skipframes) { off_t offset; errno = 0; if (sf->sf_headersize >= 0) /* header detection overridden */ { /* interpret data size from file size */ ssize_t bytelimit = lseek(fd, 0, SEEK_END); if (bytelimit < 0) goto badheader; if (bytelimit > SFMAXBYTES || bytelimit < 0) bytelimit = SFMAXBYTES; sf->sf_bytelimit = bytelimit; sf->sf_fd = fd; } else { char buf[SFHDRBUFSIZE]; ssize_t bytesread = read(fd, buf, sf_minheadersize); if (!sf->sf_type) { /* check header for type */ t_soundfile_type **t = soundfile_firsttype(); while (t) { if ((*t)->t_isheaderfn(buf, bytesread)) break; t = soundfile_nexttype(t); } if (!t) /* not recognized */ { errno = SOUNDFILE_ERRUNKNOWN; goto badheader; } sf->sf_type = *t; } else { /* check header using given type */ if (!sf->sf_type->t_isheaderfn(buf, bytesread)) { errno = SOUNDFILE_ERRUNKNOWN; goto badheader; } } sf->sf_fd = fd; /* rewind and read header */ if (lseek(sf->sf_fd, 0, SEEK_SET) < 0) goto badheader; if (!sf->sf_type->t_readheaderfn(sf)) goto badheader; } /* seek past header and any sample frames to skip */ offset = sf->sf_headersize + (skipframes * sf->sf_bytesperframe); if (lseek(sf->sf_fd, offset, 0) < offset) goto badheader; sf->sf_bytelimit -= skipframes * sf->sf_bytesperframe; if (sf->sf_bytelimit < 0) sf->sf_bytelimit = 0; /* copy sample format back to caller */ return fd; badheader: /* the header wasn't recognized. We're threadable here so let's not print out the error... */ if (!errno) errno = SOUNDFILE_ERRMALFORMED; sf->sf_fd = -1; if (fd >= 0) sys_close(fd); return -1; } /** open a soundfile, using open_via_path(). This is used by readsf~ in a not-perfectly-threadsafe way. LATER replace with a thread-hardened version of open_soundfile_via_canvas(). returns number of frames in the soundfile */ int open_soundfile_via_path(const char *dirname, const char *filename, t_soundfile *sf, size_t skipframes) { char buf[MAXPDSTRING], *dummy; int fd, sf_fd; fd = open_via_path(dirname, filename, "", buf, &dummy, MAXPDSTRING, 1); if (fd < 0) return -1; sf_fd = open_soundfile_via_fd(fd, sf, skipframes); return sf_fd; } /** open a soundfile, using open_via_canvas(). This is used by readsf~ in a not-perfectly-threadsafe way. LATER replace with a thread-hardened version of open_soundfile_via_canvas(). returns number of frames in the soundfile */ int open_soundfile_via_canvas(t_canvas *canvas, const char *filename, t_soundfile *sf, size_t skipframes) { char buf[MAXPDSTRING], *dummy; int fd, sf_fd; fd = canvas_open(canvas, filename, "", buf, &dummy, MAXPDSTRING, 1); if (fd < 0) return -1; sf_fd = open_soundfile_via_fd(fd, sf, skipframes); return sf_fd; } static void soundfile_xferin_sample(const t_soundfile *sf, int nvecs, t_sample **vecs, size_t framesread, unsigned char *buf, size_t nframes) { int nchannels = (sf->sf_nchannels < nvecs ? sf->sf_nchannels : nvecs), i; size_t j; unsigned char *sp, *sp2; t_sample *fp; for (i = 0, sp = buf; i < nchannels; i++, sp += sf->sf_bytespersample) { if (sf->sf_bytespersample == 2) { if (sf->sf_bigendian) { for (j = 0, sp2 = sp, fp = vecs[i] + framesread; j < nframes; j++, sp2 += sf->sf_bytesperframe, fp++) *fp = SCALE * ((sp2[0] << 24) | (sp2[1] << 16)); } else { for (j = 0, sp2 = sp, fp = vecs[i] + framesread; j < nframes; j++, sp2 += sf->sf_bytesperframe, fp++) *fp = SCALE * ((sp2[1] << 24) | (sp2[0] << 16)); } } else if (sf->sf_bytespersample == 3) { if (sf->sf_bigendian) { for (j = 0, sp2 = sp, fp = vecs[i] + framesread; j < nframes; j++, sp2 += sf->sf_bytesperframe, fp++) *fp = SCALE * ((sp2[0] << 24) | (sp2[1] << 16) | (sp2[2] << 8)); } else { for (j = 0, sp2 = sp, fp = vecs[i] + framesread; j < nframes; j++, sp2 += sf->sf_bytesperframe, fp++) *fp = SCALE * ((sp2[2] << 24) | (sp2[1] << 16) | (sp2[0] << 8)); } } else if (sf->sf_bytespersample == 4) { t_floatuint alias; if (sf->sf_bigendian) { for (j = 0, sp2 = sp, fp = vecs[i] + framesread; j < nframes; j++, sp2 += sf->sf_bytesperframe, fp++) { alias.ui = ((sp2[0] << 24) | (sp2[1] << 16) | (sp2[2] << 8) | sp2[3]); *fp = (t_sample)alias.f; } } else { for (j = 0, sp2 = sp, fp = vecs[i] + framesread; j < nframes; j++, sp2 += sf->sf_bytesperframe, fp++) { alias.ui = ((sp2[3] << 24) | (sp2[2] << 16) | (sp2[1] << 8) | sp2[0]); *fp = (t_sample)alias.f; } } } } /* zero out other outputs */ for (i = sf->sf_nchannels; i < nvecs; i++) for (j = nframes, fp = vecs[i]; j--;) *fp++ = 0; } static void soundfile_xferin_words(const t_soundfile *sf, int nvecs, t_word **vecs, size_t framesread, unsigned char *buf, size_t nframes) { unsigned char *sp, *sp2; t_word *wp; int nchannels = (sf->sf_nchannels < nvecs ? sf->sf_nchannels : nvecs), i; size_t j; for (i = 0, sp = buf; i < nchannels; i++, sp += sf->sf_bytespersample) { if (sf->sf_bytespersample == 2) { if (sf->sf_bigendian) { for (j = 0, sp2 = sp, wp = vecs[i] + framesread; j < nframes; j++, sp2 += sf->sf_bytesperframe, wp++) wp->w_float = SCALE * ((sp2[0] << 24) | (sp2[1] << 16)); } else { for (j = 0, sp2 = sp, wp = vecs[i] + framesread; j < nframes; j++, sp2 += sf->sf_bytesperframe, wp++) wp->w_float = SCALE * ((sp2[1] << 24) | (sp2[0] << 16)); } } else if (sf->sf_bytespersample == 3) { if (sf->sf_bigendian) { for (j = 0, sp2 = sp, wp = vecs[i] + framesread; j < nframes; j++, sp2 += sf->sf_bytesperframe, wp++) wp->w_float = SCALE * ((sp2[0] << 24) | (sp2[1] << 16) | (sp2[2] << 8)); } else { for (j = 0, sp2 = sp, wp = vecs[i] + framesread; j < nframes; j++, sp2 += sf->sf_bytesperframe, wp++) wp->w_float = SCALE * ((sp2[2] << 24) | (sp2[1] << 16) | (sp2[0] << 8)); } } else if (sf->sf_bytespersample == 4) { t_floatuint alias; if (sf->sf_bigendian) { for (j = 0, sp2 = sp, wp = vecs[i] + framesread; j < nframes; j++, sp2 += sf->sf_bytesperframe, wp++) { alias.ui = ((sp2[0] << 24) | (sp2[1] << 16) | (sp2[2] << 8) | sp2[3]); wp->w_float = (t_float)alias.f; } } else { for (j = 0, sp2 = sp, wp = vecs[i] + framesread; j < nframes; j++, sp2 += sf->sf_bytesperframe, wp++) { alias.ui = ((sp2[3] << 24) | (sp2[2] << 16) | (sp2[1] << 8) | sp2[0]); wp->w_float = (t_float)alias.f; } } } } /* zero out other outputs */ for (i = sf->sf_nchannels; i < nvecs; i++) for (j = nframes, wp = vecs[i]; j--;) (wp++)->w_float = 0; } /* soundfiler_write ... usage: write [flags] filename table ... flags: -nframes -skip -bytes -rate / -r -normalize -wave -aiff -caf -next / -nextstep -ascii -big -little */ /** parsed write arguments */ typedef struct _soundfiler_writeargs { t_symbol *wa_filesym; /* file path symbol */ t_soundfile_type *wa_type; /* type implementation */ int wa_samplerate; /* sample rate */ int wa_bytespersample; /* number of bytes per sample */ int wa_bigendian; /* is sample data bigendian? */ size_t wa_nframes; /* number of sample frames to write */ size_t wa_onsetframes; /* sample frame onset when writing */ int wa_normalize; /* normalize samples? */ int wa_ascii; /* write ascii? */ } t_soundfiler_writeargs; /* the routine which actually does the work should LATER also be called from garray_write16. */ /** Parse arguments for writing. The "obj" argument is only for flagging errors. For streaming to a file the "normalize" and "nframes" arguments shouldn't be set but the calling routine flags this. */ static int soundfiler_parsewriteargs(void *obj, int *p_argc, t_atom **p_argv, t_soundfiler_writeargs *wa) { int argc = *p_argc; t_atom *argv = *p_argv; int samplerate = -1, bytespersample = 2, bigendian = 0, endianness = -1; size_t nframes = SFMAXFRAMES, onsetframes = 0; int normalize = 0, ascii = 0; t_symbol *filesym; t_soundfile_type *type = NULL; while (argc > 0 && argv->a_type == A_SYMBOL && *argv->a_w.w_symbol->s_name == '-') { const char *flag = argv->a_w.w_symbol->s_name + 1; if (!strcmp(flag, "skip")) { if (argc < 2 || argv[1].a_type != A_FLOAT || (argv[1].a_w.w_float) < 0) return -1; onsetframes = argv[1].a_w.w_float; argc -= 2; argv += 2; } else if (!strcmp(flag, "nframes")) { if (argc < 2 || argv[1].a_type != A_FLOAT || argv[1].a_w.w_float < 0) return -1; nframes = argv[1].a_w.w_float; argc -= 2; argv += 2; } else if (!strcmp(flag, "bytes")) { if (argc < 2 || argv[1].a_type != A_FLOAT || ((bytespersample = argv[1].a_w.w_float) < 2) || bytespersample > 4) return -1; argc -= 2; argv += 2; } else if (!strcmp(flag, "normalize")) { normalize = 1; argc -= 1; argv += 1; } else if (!strcmp(flag, "big")) { endianness = 1; argc -= 1; argv += 1; } else if (!strcmp(flag, "little")) { endianness = 0; argc -= 1; argv += 1; } else if (!strcmp(flag, "rate") || !strcmp(flag, "r")) { if (argc < 2 || argv[1].a_type != A_FLOAT || ((samplerate = argv[1].a_w.w_float) <= 0)) return -1; argc -= 2; argv += 2; } else if (!strcmp(flag, "ascii")) { ascii = 1; argc -= 1; argv += 1; } else if (!strcmp(flag, "nextstep")) { /* handle old "-nextstep" alias */ type = soundfile_findtype("next"); argc -= 1; argv += 1; } else { /* check for type by name */ if (!(type = soundfile_findtype(flag))) return -1; /* unknown flag */ ascii = 0; /* replaced */ argc -= 1; argv += 1; } } if (!argc || argv->a_type != A_SYMBOL) return -1; filesym = argv->a_w.w_symbol; /* deduce from filename extension? */ if (!type) { t_soundfile_type **t = soundfile_firsttype(); while (t) { if ((*t)->t_hasextensionfn(filesym->s_name, MAXPDSTRING)) break; t = soundfile_nexttype(t); } if (!t) { if (!ascii) ascii = ascii_hasextension(filesym->s_name, MAXPDSTRING); t = soundfile_firsttype(); /* default if unknown */ } type = *t; } /* check requested endianness */ bigendian = type->t_endiannessfn(endianness); if (endianness != -1 && endianness != bigendian) { post("%s: forced to %s endian", type->t_name, (bigendian ? "big" : "little")); } /* return to caller */ argc--; argv++; *p_argc = argc; *p_argv = argv; wa->wa_filesym = filesym; wa->wa_type = type; wa->wa_samplerate = samplerate; wa->wa_bytespersample = bytespersample; wa->wa_bigendian = bigendian; wa->wa_nframes = nframes; wa->wa_onsetframes = onsetframes; wa->wa_normalize = normalize; wa->wa_ascii = ascii; return 0; } /** sets sf fd & headerisze on success and returns fd or -1 on failure */ static int create_soundfile(t_canvas *canvas, const char *filename, t_soundfile *sf, size_t nframes) { char filenamebuf[MAXPDSTRING], pathbuf[MAXPDSTRING]; ssize_t headersize = -1; int fd; /* create file */ strncpy(filenamebuf, filename, MAXPDSTRING); if (!sf->sf_type->t_hasextensionfn(filenamebuf, MAXPDSTRING-10)) if (!sf->sf_type->t_addextensionfn(filenamebuf, MAXPDSTRING-10)) return -1; filenamebuf[MAXPDSTRING-10] = 0; /* FIXME: what is the 10 for? */ canvas_makefilename(canvas, filenamebuf, pathbuf, MAXPDSTRING); if ((fd = sys_open(pathbuf, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) return -1; sf->sf_fd = fd; /* write header */ headersize = sf->sf_type->t_writeheaderfn(sf, nframes); if (headersize < 0) goto badcreate; sf->sf_headersize = headersize; return fd; badcreate: sf->sf_fd = -1; if (fd >= 0) sys_close(fd); return -1; } static void soundfile_finishwrite(void *obj, const char *filename, t_soundfile *sf, size_t nframes, size_t frameswritten) { if (frameswritten >= nframes) return; if (nframes < SFMAXFRAMES) pd_error(obj, "soundfiler write: %ld out of %ld frames written", (long)frameswritten, (long)nframes); if (sf->sf_type->t_updateheaderfn(sf, frameswritten)) return; object_sferror(obj, "soundfiler write", filename, errno, sf); } static void soundfile_xferout_sample(const t_soundfile *sf, t_sample **vecs, unsigned char *buf, size_t nframes, size_t onsetframes, t_sample normalfactor) { int i; size_t j; unsigned char *sp, *sp2; t_sample *fp; for (i = 0, sp = buf; i < sf->sf_nchannels; i++, sp += sf->sf_bytespersample) { if (sf->sf_bytespersample == 2) { t_sample ff = normalfactor * 32768.; if (sf->sf_bigendian) { for (j = 0, sp2 = sp, fp = vecs[i] + onsetframes; j < nframes; j++, sp2 += sf->sf_bytesperframe, fp++) { int xx = 32768. + (*fp * ff); xx -= 32768; if (xx < -32767) xx = -32767; if (xx > 32767) xx = 32767; sp2[0] = (xx >> 8); sp2[1] = xx; } } else { for (j = 0, sp2 = sp, fp = vecs[i] + onsetframes; j < nframes; j++, sp2 += sf->sf_bytesperframe, fp++) { int xx = 32768. + (*fp * ff); xx -= 32768; if (xx < -32767) xx = -32767; if (xx > 32767) xx = 32767; sp2[1] = (xx >> 8); sp2[0] = xx; } } } else if (sf->sf_bytespersample == 3) { t_sample ff = normalfactor * 8388608.; if (sf->sf_bigendian) { for (j = 0, sp2 = sp, fp = vecs[i] + onsetframes; j < nframes; j++, sp2 += sf->sf_bytesperframe, fp++) { int xx = 8388608. + (*fp * ff); xx -= 8388608; if (xx < -8388607) xx = -8388607; if (xx > 8388607) xx = 8388607; sp2[0] = (xx >> 16); sp2[1] = (xx >> 8); sp2[2] = xx; } } else { for (j = 0, sp2 = sp, fp = vecs[i] + onsetframes; j < nframes; j++, sp2 += sf->sf_bytesperframe, fp++) { int xx = 8388608. + (*fp * ff); xx -= 8388608; if (xx < -8388607) xx = -8388607; if (xx > 8388607) xx = 8388607; sp2[2] = (xx >> 16); sp2[1] = (xx >> 8); sp2[0] = xx; } } } else if (sf->sf_bytespersample == 4) { t_floatuint f2; if (sf->sf_bigendian) { for (j = 0, sp2 = sp, fp = vecs[i] + onsetframes; j < nframes; j++, sp2 += sf->sf_bytesperframe, fp++) { f2.f = *fp * normalfactor; sp2[0] = (f2.ui >> 24); sp2[1] = (f2.ui >> 16); sp2[2] = (f2.ui >> 8); sp2[3] = f2.ui; } } else { for (j = 0, sp2 = sp, fp = vecs[i] + onsetframes; j < nframes; j++, sp2 += sf->sf_bytesperframe, fp++) { f2.f = *fp * normalfactor; sp2[3] = (f2.ui >> 24); sp2[2] = (f2.ui >> 16); sp2[1] = (f2.ui >> 8); sp2[0] = f2.ui; } } } } } static void soundfile_xferout_words(const t_soundfile *sf, t_word **vecs, unsigned char *buf, size_t nframes, size_t onsetframes, t_sample normalfactor) { int i; size_t j; unsigned char *sp, *sp2; t_word *wp; for (i = 0, sp = buf; i < sf->sf_nchannels; i++, sp += sf->sf_bytespersample) { if (sf->sf_bytespersample == 2) { t_sample ff = normalfactor * 32768.; if (sf->sf_bigendian) { for (j = 0, sp2 = sp, wp = vecs[i] + onsetframes; j < nframes; j++, sp2 += sf->sf_bytesperframe, wp++) { int xx = 32768. + (wp->w_float * ff); xx -= 32768; if (xx < -32767) xx = -32767; if (xx > 32767) xx = 32767; sp2[0] = (xx >> 8); sp2[1] = xx; } } else { for (j = 0, sp2 = sp, wp = vecs[i] + onsetframes; j < nframes; j++, sp2 += sf->sf_bytesperframe, wp++) { int xx = 32768. + (wp->w_float * ff); xx -= 32768; if (xx < -32767) xx = -32767; if (xx > 32767) xx = 32767; sp2[1] = (xx >> 8); sp2[0] = xx; } } } else if (sf->sf_bytespersample == 3) { t_sample ff = normalfactor * 8388608.; if (sf->sf_bigendian) { for (j = 0, sp2 = sp, wp = vecs[i] + onsetframes; j < nframes; j++, sp2 += sf->sf_bytesperframe, wp++) { int xx = 8388608. + (wp->w_float * ff); xx -= 8388608; if (xx < -8388607) xx = -8388607; if (xx > 8388607) xx = 8388607; sp2[0] = (xx >> 16); sp2[1] = (xx >> 8); sp2[2] = xx; } } else { for (j = 0, sp2 = sp, wp = vecs[i] + onsetframes; j < nframes; j++, sp2 += sf->sf_bytesperframe, wp++) { int xx = 8388608. + (wp->w_float * ff); xx -= 8388608; if (xx < -8388607) xx = -8388607; if (xx > 8388607) xx = 8388607; sp2[2] = (xx >> 16); sp2[1] = (xx >> 8); sp2[0] = xx; } } } else if (sf->sf_bytespersample == 4) { t_floatuint f2; if (sf->sf_bigendian) { for (j = 0, sp2 = sp, wp = vecs[i] + onsetframes; j < nframes; j++, sp2 += sf->sf_bytesperframe, wp++) { f2.f = wp->w_float * normalfactor; sp2[0] = (f2.ui >> 24); sp2[1] = (f2.ui >> 16); sp2[2] = (f2.ui >> 8); sp2[3] = f2.ui; } } else { for (j = 0, sp2 = sp, wp = vecs[i] + onsetframes; j < nframes; j++, sp2 += sf->sf_bytesperframe, wp++) { f2.f = wp->w_float * normalfactor; sp2[3] = (f2.ui >> 24); sp2[2] = (f2.ui >> 16); sp2[1] = (f2.ui >> 8); sp2[0] = f2.ui; } } } } } /* ----- soundfiler - reads and writes soundfiles to/from "garrays" ----- */ #define SAMPBUFSIZE 1024 static t_class *soundfiler_class; typedef struct _soundfiler { t_object x_obj; t_outlet *x_out2; t_canvas *x_canvas; } t_soundfiler; static t_soundfiler *soundfiler_new(void) { t_soundfiler *x = (t_soundfiler *)pd_new(soundfiler_class); x->x_canvas = canvas_getcurrent(); outlet_new(&x->x_obj, &s_float); x->x_out2 = outlet_new(&x->x_obj, &s_float); return x; } static int soundfiler_readascii(t_soundfiler *x, const char *filename, t_asciiargs *a) { t_binbuf *b = binbuf_new(); int i, j, vecsize; ssize_t framesinfile, nframes = a->aa_nframes; t_atom *atoms, *ap; if (binbuf_read_via_canvas(b, filename, x->x_canvas, 0)) { nframes = 0; goto done; } atoms = binbuf_getvec(b); framesinfile = binbuf_getnatom(b) / a->aa_nchannels; #ifdef DEBUG_SOUNDFILE post("ascii: read 1 natoms %d frames %d onset %d channels %d", binbuf_getnatom(b), nframes, a->aa_onsetframe, a->aa_nchannels); #endif if (framesinfile < 1) { pd_error(x, "soundfiler read: %s: empty or very short ascii file", filename); nframes = 0; goto done; } if (a->aa_resize) { if ((size_t)framesinfile > a->aa_maxsize) { pd_error(x, "soundfiler read: truncated to %ld elements", (long)a->aa_maxsize); framesinfile = a->aa_maxsize; } nframes = framesinfile - a->aa_onsetframe; for (i = 0; i < a->aa_nchannels; i++) { garray_resize_long(a->aa_garrays[i], nframes); garray_setsaveit(a->aa_garrays[i], 0); if (!garray_getfloatwords(a->aa_garrays[i], &vecsize, &a->aa_vectors[i]) || (vecsize != nframes)) { pd_error(x, "soundfiler read: resize failed"); nframes = 0; goto done; } } } else if (nframes > framesinfile) nframes = framesinfile - a->aa_onsetframe; #ifdef DEBUG_SOUNDFILE post("ascii: read 2 frames %d", nframes); #endif if (a->aa_onsetframe > 0) atoms += a->aa_onsetframe * a->aa_nchannels; for (j = 0, ap = atoms; j < nframes; j++) for (i = 0; i < a->aa_nchannels; i++) a->aa_vectors[i][j].w_float = atom_getfloat(ap++); /* zero out remaining elements of vectors */ for (i = 0; i < a->aa_nchannels; i++) { if (garray_getfloatwords(a->aa_garrays[i], &vecsize, &a->aa_vectors[i])) for (j = nframes; j < vecsize; j++) a->aa_vectors[i][j].w_float = 0; } for (i = 0; i < a->aa_nchannels; i++) garray_redraw(a->aa_garrays[i]); #ifdef DEBUG_SOUNDFILE post("ascii: read 3"); #endif done: binbuf_free(b); return nframes; } /* soundfiler_read ... usage: read [flags] filename [tablename] ... flags: -skip ... frames to skip in file -raw -resize -maxsize -wave -aiff -caf -next -ascii */ static void soundfiler_read(t_soundfiler *x, t_symbol *s, int argc, t_atom *argv) { t_soundfile sf = {0}; int fd = -1, resize = 0, ascii = 0, raw = 0, i; size_t skipframes = 0, finalsize = 0, maxsize = SFMAXFRAMES, framesread = 0, bufframes, j; ssize_t nframes, framesinfile; char endianness; const char *filename; t_garray *garrays[MAXSFCHANS]; t_word *vecs[MAXSFCHANS]; char sampbuf[SAMPBUFSIZE]; soundfile_clear(&sf); sf.sf_headersize = -1; while (argc > 0 && argv->a_type == A_SYMBOL && *argv->a_w.w_symbol->s_name == '-') { const char *flag = argv->a_w.w_symbol->s_name + 1; if (!strcmp(flag, "skip")) { if (argc < 2 || argv[1].a_type != A_FLOAT || (argv[1].a_w.w_float) < 0) goto usage; skipframes = argv[1].a_w.w_float; argc -= 2; argv += 2; } else if (!strcmp(flag, "ascii")) { if (sf.sf_headersize >= 0) post("'-ascii' overridden by '-raw'"); ascii = 1; argc--; argv++; } else if (!strcmp(flag, "raw")) { if (ascii) post("'-ascii' overridden by '-raw'"); else if (sf.sf_type) post("'-%s' overridden by '-raw'", sf.sf_type->t_name); if (argc < 5 || argv[1].a_type != A_FLOAT || ((sf.sf_headersize = argv[1].a_w.w_float) < 0) || argv[2].a_type != A_FLOAT || ((sf.sf_nchannels = argv[2].a_w.w_float) < 1) || (sf.sf_nchannels > MAXSFCHANS) || argv[3].a_type != A_FLOAT || ((sf.sf_bytespersample = argv[3].a_w.w_float) < 2) || (sf.sf_bytespersample > 4) || argv[4].a_type != A_SYMBOL || ((endianness = argv[4].a_w.w_symbol->s_name[0]) != 'b' && endianness != 'l' && endianness != 'n')) goto usage; if (endianness == 'b') sf.sf_bigendian = 1; else if (endianness == 'l') sf.sf_bigendian = 0; else sf.sf_bigendian = sys_isbigendian(); sf.sf_samplerate = sys_getsr(); sf.sf_bytesperframe = sf.sf_nchannels * sf.sf_bytespersample; raw = 1; argc -= 5; argv += 5; } else if (!strcmp(flag, "resize")) { resize = 1; argc -= 1; argv += 1; } else if (!strcmp(flag, "maxsize")) { ssize_t tmp; if (argc < 2 || argv[1].a_type != A_FLOAT || ((tmp = (argv[1].a_w.w_float > SFMAXFRAMES ? SFMAXFRAMES : argv[1].a_w.w_float)) < 0)) goto usage; maxsize = (size_t)tmp; resize = 1; /* maxsize implies resize */ argc -= 2; argv += 2; } else { /* check for type by name */ if (!(sf.sf_type = soundfile_findtype(flag))) goto usage; /* unknown flag */ ascii = 0; /* replaced */ if (sf.sf_headersize >= 0) post("'-%s' overridden by '-raw'", sf.sf_type->t_name); argc -= 1; argv += 1; } } if (sf.sf_headersize >= 0) { ascii = 0; sf.sf_type = NULL; } if (argc < 1 || /* no filename or tables */ argc > MAXSFCHANS + 1 || /* too many tables */ argv[0].a_type != A_SYMBOL) /* bad filename */ goto usage; filename = argv[0].a_w.w_symbol->s_name; argc--; argv++; /* check for implicit ascii */ if (!ascii && !sf.sf_type && sf.sf_headersize < 0) ascii = ascii_hasextension(filename, MAXPDSTRING); for (i = 0; i < argc; i++) { int vecsize; if (argv[i].a_type != A_SYMBOL) goto usage; if (!(garrays[i] = (t_garray *)pd_findbyclass(argv[i].a_w.w_symbol, garray_class))) { pd_error(x, "soundfiler read: %s: no such table", argv[i].a_w.w_symbol->s_name); goto done; } else if (!garray_getfloatwords(garrays[i], &vecsize, &vecs[i])) pd_error(x, "soundfiler read: %s: bad template for tabwrite", argv[i].a_w.w_symbol->s_name); if (finalsize && finalsize != (size_t)vecsize && !resize) { post("arrays have different lengths, resizing..."); resize = 1; } finalsize = vecsize; } if (ascii) { t_asciiargs a = {skipframes, finalsize, argc, vecs, garrays, resize, maxsize, 0}; if (!argc) { pd_error(x, "soundfiler read: " "'-ascii' requires at least one table"); goto done; } if ((framesread = soundfiler_readascii(x, filename, &a)) == 0) goto done; /* fill in for info outlet */ sf.sf_samplerate = sys_getsr(); sf.sf_headersize = 0; sf.sf_nchannels = argc; sf.sf_bytespersample = 4; sf.sf_bigendian = sys_isbigendian(); goto done; } fd = open_soundfile_via_canvas(x->x_canvas, filename, &sf, skipframes); if (fd < 0) { object_sferror(x, "soundfiler read", filename, errno, &sf); goto done; } framesinfile = sf.sf_bytelimit / sf.sf_bytesperframe; if (resize) { /* figure out what to resize to using header info */ if ((size_t)framesinfile > maxsize) { pd_error(x, "soundfiler read: truncated to %ld elements", (long)maxsize); framesinfile = maxsize; } finalsize = framesinfile; for (i = 0; i < argc; i++) { int vecsize; garray_resize_long(garrays[i], finalsize); /* for sanity's sake let's clear the save-in-patch flag here */ garray_setsaveit(garrays[i], 0); if (!garray_getfloatwords(garrays[i], &vecsize, &vecs[i]) /* if the resize failed, garray_resize reported the error */ || (vecsize != framesinfile)) { pd_error(x, "soundfiler read: resize failed"); goto done; } } } if (!finalsize) finalsize = SFMAXFRAMES; if (framesinfile >= 0 && finalsize > (size_t)framesinfile) finalsize = framesinfile; /* no tablenames, try to use header info instead of reading */ if (argc == 0 && !(raw || /* read if raw */ finalsize == SFMAXFRAMES)) /* read if unknown size */ { framesread = finalsize; #ifdef DEBUG_SOUNDFILE post("skipped reading frames"); #endif goto done; } /* read */ #ifdef DEBUG_SOUNDFILE post("reading frames"); #endif bufframes = SAMPBUFSIZE / sf.sf_bytesperframe; for (framesread = 0; framesread < finalsize;) { size_t thisread = finalsize - framesread; thisread = (thisread > bufframes ? bufframes : thisread); nframes = read(sf.sf_fd, sampbuf, thisread * sf.sf_bytesperframe) / sf.sf_bytesperframe; if (nframes <= 0) break; soundfile_xferin_words(&sf, argc, vecs, framesread, (unsigned char *)sampbuf, nframes); framesread += nframes; } /* warn if a file's bad size field is gobbling memory */ if (resize && framesread < (size_t)finalsize) { post("warning: soundfile %s header promised \ %ld points but file was truncated to %ld", filename, (long)finalsize, (long)framesread); } /* zero out remaining elements of vectors */ for (i = 0; i < argc; i++) { int vecsize; if (garray_getfloatwords(garrays[i], &vecsize, &vecs[i])) for (j = framesread; j < (size_t)vecsize; j++) vecs[i][j].w_float = 0; } /* zero out vectors in excess of number of channels */ for (i = sf.sf_nchannels; i < argc; i++) { int vecsize; t_word *foo; if (garray_getfloatwords(garrays[i], &vecsize, &foo)) for (j = 0; j < (size_t)vecsize; j++) foo[j].w_float = 0; } /* do all graphics updates */ for (i = 0; i < argc; i++) garray_redraw(garrays[i]); goto done; usage: pd_error(x, "usage: read [flags] filename [tablename]..."); post("flags: -skip -resize -maxsize %s -ascii ...", sf_typeargs); post("-raw " ""); done: sf.sf_fd = -1; if (fd >= 0) sys_close(fd); outlet_soundfileinfo(x->x_out2, &sf); outlet_float(x->x_obj.ob_outlet, (t_float)framesread); } /** write to an ascii text file, channels (nvecs) are interleaved, assumes all vectors are at least nframes in length adapted from garray_savecontentsto() returns frames written on success or 0 on error */ int soundfiler_writeascii(t_soundfiler *x, const char *filename, t_asciiargs *a) { char path[MAXPDSTRING]; t_binbuf *b = binbuf_new(); int i, j, frameswritten = 0, ret = 1; #ifdef DEBUG_SOUNDFILE post("ascii write: frames %d onset %d channels %d", a->aa_nframes, a->aa_onsetframe, a->aa_nchannels); #endif canvas_makefilename(x->x_canvas, filename, path, MAXPDSTRING); if (a->aa_nframes > 200000) post("warning: writing %d table points to ascii file!"); for (i = a->aa_onsetframe; frameswritten < a->aa_nframes; ++i) { for (j = 0; j < a->aa_nchannels; ++j) binbuf_addv(b, "f", a->aa_vectors[j][i].w_float * a->aa_normfactor); frameswritten++; } binbuf_addv(b, ";"); ret = binbuf_write(b, path, "", 1); /* convert semis to cr */ binbuf_free(b); return (ret == 0 ? frameswritten : 0); } /** this is broken out from soundfiler_write below so garray_write can call it too... not done yet though. */ size_t soundfiler_dowrite(void *obj, t_canvas *canvas, int argc, t_atom *argv, t_soundfile *sf) { t_soundfiler_writeargs wa = {0}; int fd = -1, i; size_t bufframes, frameswritten = 0, j; t_garray *garrays[MAXSFCHANS]; t_word *vectors[MAXSFCHANS]; char sampbuf[SAMPBUFSIZE]; t_sample normfactor = 1, biggest = 0; soundfile_clear(sf); if (soundfiler_parsewriteargs(obj, &argc, &argv, &wa)) goto usage; sf->sf_type = (wa.wa_ascii ? NULL : wa.wa_type); sf->sf_nchannels = argc; sf->sf_samplerate = wa.wa_samplerate; sf->sf_bytespersample = wa.wa_bytespersample; sf->sf_bigendian = wa.wa_bigendian; sf->sf_bytesperframe = argc * wa.wa_bytespersample; if (sf->sf_nchannels < 1 || sf->sf_nchannels > MAXSFCHANS) goto usage; if (sf->sf_samplerate <= 0) sf->sf_samplerate = sys_getsr(); for (i = 0; i < sf->sf_nchannels; i++) { int vecsize; if (argv[i].a_type != A_SYMBOL) goto usage; if (!(garrays[i] = (t_garray *)pd_findbyclass(argv[i].a_w.w_symbol, garray_class))) { pd_error(obj, "soundfiler write: %s: no such table", argv[i].a_w.w_symbol->s_name); goto fail; } else if (!garray_getfloatwords(garrays[i], &vecsize, &vectors[i])) pd_error(obj, "soundfiler write: %s: bad template for tabwrite", argv[i].a_w.w_symbol->s_name); if (wa.wa_nframes > vecsize - wa.wa_onsetframes) wa.wa_nframes = vecsize - wa.wa_onsetframes; } if (wa.wa_nframes <= 0) { pd_error(obj, "soundfiler write: no samples at onset %ld", (long)wa.wa_onsetframes); goto fail; } /* find biggest sample for normalizing */ for (i = 0; i < sf->sf_nchannels; i++) { for (j = wa.wa_onsetframes; j < wa.wa_nframes + wa.wa_onsetframes; j++) { if (vectors[i][j].w_float > biggest) biggest = vectors[i][j].w_float; else if (-vectors[i][j].w_float > biggest) biggest = -vectors[i][j].w_float; } } /* write to ascii text file? */ if (wa.wa_ascii) { char filenamebuf[MAXPDSTRING]; t_asciiargs a = {wa.wa_onsetframes, wa.wa_nframes, sf->sf_nchannels, vectors, 0, 0, 0, 0}; strcpy(filenamebuf, wa.wa_filesym->s_name); if (!ascii_hasextension(filenamebuf, MAXPDSTRING)) ascii_addextension(filenamebuf, MAXPDSTRING); if (wa.wa_normalize) a.aa_normfactor = (biggest > 0 ? 32767./(32768. * biggest) : 1); else a.aa_normfactor = 1; if ((frameswritten = soundfiler_writeascii(obj, filenamebuf, &a)) == 0) { pd_error(obj, "soundfiler write: writing ascii failed"); goto fail; } /* fill in for info outlet */ sf->sf_headersize = 0; sf->sf_bytespersample = 4; sf->sf_bigendian = sys_isbigendian(); return frameswritten; } /* create file and detect if int samples should be normalized */ if ((fd = create_soundfile(canvas, wa.wa_filesym->s_name, sf, wa.wa_nframes)) < 0) { object_sferror(obj, "soundfiler write", wa.wa_filesym->s_name, errno, sf); goto fail; } if (!wa.wa_normalize) { if (sf->sf_bytespersample != 4 && biggest > 1) { post("%s: reducing max amplitude %f to 1", wa.wa_filesym->s_name, biggest); wa.wa_normalize = 1; } else post("%s: biggest amplitude = %f", wa.wa_filesym->s_name, biggest); } if (wa.wa_normalize) normfactor = (biggest > 0 ? 32767./(32768. * biggest) : 1); /* write samples */ bufframes = SAMPBUFSIZE / sf->sf_bytesperframe; for (frameswritten = 0; frameswritten < wa.wa_nframes;) { size_t thiswrite = wa.wa_nframes - frameswritten, datasize; ssize_t byteswritten; thiswrite = (thiswrite > bufframes ? bufframes : thiswrite); datasize = sf->sf_bytesperframe * thiswrite; soundfile_xferout_words(sf, vectors, (unsigned char *)sampbuf, thiswrite, wa.wa_onsetframes, normfactor); byteswritten = write(sf->sf_fd, sampbuf, datasize); if (byteswritten < 0 || (size_t)byteswritten < datasize) { object_sferror(obj, "soundfiler write", wa.wa_filesym->s_name, errno, sf); if (byteswritten > 0) frameswritten += byteswritten / sf->sf_bytesperframe; break; } frameswritten += thiswrite; wa.wa_onsetframes += thiswrite; } /* update header frame size */ if (fd >= 0) { soundfile_finishwrite(obj, wa.wa_filesym->s_name, sf, wa.wa_nframes, frameswritten); sys_close(fd); } sf->sf_fd = -1; return frameswritten; usage: pd_error(obj, "usage: write [flags] filename tablename..."); post("flags: -skip -nframes -bytes %s ...", sf_typeargs); post("-ascii -big -little -normalize"); post("(defaults to a 16 bit wave file)"); fail: soundfile_clear(sf); /* clear any bad data */ if (fd >= 0) sys_close(fd); return 0; } static void soundfiler_write(t_soundfiler *x, t_symbol *s, int argc, t_atom *argv) { size_t frameswritten; t_soundfile sf = {0}; frameswritten = soundfiler_dowrite(x, x->x_canvas, argc, argv, &sf); outlet_soundfileinfo(x->x_out2, &sf); outlet_float(x->x_obj.ob_outlet, (t_float)frameswritten); } static void soundfiler_setup(void) { soundfiler_class = class_new(gensym("soundfiler"), (t_newmethod)soundfiler_new, 0, sizeof(t_soundfiler), 0, 0); class_addmethod(soundfiler_class, (t_method)soundfiler_read, gensym("read"), A_GIMME, 0); class_addmethod(soundfiler_class, (t_method)soundfiler_write, gensym("write"), A_GIMME, 0); } /* ------------------------- readsf object ------------------------- */ /* READSF uses the Posix threads package; for the moment we're Linux only although this should be portable to the other platforms. Each instance of readsf~ owns a "child" thread for doing the Posix file reading. The parent thread signals the child each time: (1) a file wants opening or closing; (2) we've eaten another 1/16 of the shared buffer (so that the child thread should check if it's time to read some more.) The child signals the parent whenever a read has completed. Signaling is done by setting "conditions" and putting data in mutex-controlled common areas. */ #define MAXVECSIZE 128 #define READSIZE 65536 #define WRITESIZE 65536 #define DEFBUFPERCHAN 262144 #define MINBUFSIZE (4 * READSIZE) #define MAXBUFSIZE 16777216 /* arbitrary; just don't want to hang malloc */ /* read/write thread request type */ typedef enum _soundfile_request { REQUEST_NOTHING = 0, REQUEST_OPEN = 1, REQUEST_CLOSE = 2, REQUEST_QUIT = 3, REQUEST_BUSY = 4 } t_soundfile_request; /* read/write thread state */ typedef enum _soundfile_state { STATE_IDLE = 0, STATE_STARTUP = 1, STATE_STREAM = 2 } t_soundfile_state; static t_class *readsf_class; typedef struct _readsf { t_object x_obj; t_canvas *x_canvas; t_clock *x_clock; char *x_buf; /**< soundfile buffer */ int x_bufsize; /**< buffer size in bytes */ int x_noutlets; /**< number of audio outlets */ t_sample *(x_outvec[MAXSFCHANS]); /**< audio vectors */ int x_vecsize; /**< vector size for transfers */ t_outlet *x_bangout; /**< bang-on-done outlet */ t_soundfile_state x_state; /**< opened, running, or idle */ t_float x_insamplerate; /**< input signal sample rate, if known */ /* parameters to communicate with subthread */ t_soundfile_request x_requestcode; /**< pending request to I/O thread */ const char *x_filename; /**< file to open (string permanently allocated) */ int x_fileerror; /**< slot for "errno" return */ t_soundfile x_sf; /**< soundfile fd, type, and format info */ size_t x_onsetframes; /**< number of sample frames to skip */ int x_fifosize; /**< buffer size appropriately rounded down */ int x_fifohead; /**< index of next byte to get from file */ int x_fifotail; /**< index of next byte the ugen will read */ int x_eof; /**< true if fifohead has stopped changing */ int x_sigcountdown; /**< counter for signaling child for more data */ int x_sigperiod; /**< number of ticks per signal */ size_t x_frameswritten; /**< writesf~ only; frames written */ t_float x_f; /**< writesf~ only; scalar for signal inlet */ pthread_mutex_t x_mutex; pthread_cond_t x_requestcondition; pthread_cond_t x_answercondition; pthread_t x_childthread; #ifdef PDINSTANCE t_pdinstance *x_pd_this; /**< pointer to the owner pd instance */ #endif } t_readsf; /* ----- the child thread which performs file I/O ----- */ /** thread state debug prints to stderr */ //#define DEBUG_SOUNDFILE_THREADS #if 1 #define sfread_cond_wait pthread_cond_wait #define sfread_cond_signal pthread_cond_signal #else #include /* debugging version... */ #include static void readsf_fakewait(pthread_mutex_t *b) { struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 1000000; pthread_mutex_unlock(b); select(0, 0, 0, 0, &timeout); pthread_mutex_lock(b); } #define sfread_cond_wait(a,b) readsf_fakewait(b) #define sfread_cond_signal(a) #endif static void *readsf_child_main(void *zz) { t_readsf *x = zz; t_soundfile sf = {0}; soundfile_clear(&sf); #ifdef PDINSTANCE pd_this = x->x_pd_this; #endif #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "readsf~: 1\n"); #endif pthread_mutex_lock(&x->x_mutex); while (1) { int fifohead; char *buf; #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "readsf~: 0\n"); #endif if (x->x_requestcode == REQUEST_NOTHING) { #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "readsf~: wait 2\n"); #endif sfread_cond_signal(&x->x_answercondition); sfread_cond_wait(&x->x_requestcondition, &x->x_mutex); #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "readsf~: 3\n"); #endif } else if (x->x_requestcode == REQUEST_OPEN) { ssize_t bytesread; size_t wantbytes; /* copy file stuff out of the data structure so we can relinquish the mutex while we're in open_soundfile_via_path() */ size_t onsetframes = x->x_onsetframes; const char *filename = x->x_filename; const char *dirname = canvas_getdir(x->x_canvas)->s_name; #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "readsf~: 4\n"); #endif /* alter the request code so that an ensuing "open" will get noticed. */ x->x_requestcode = REQUEST_BUSY; x->x_fileerror = 0; /* if there's already a file open, close it */ if (sf.sf_fd >= 0) { pthread_mutex_unlock(&x->x_mutex); sys_close(sf.sf_fd); sf.sf_fd = -1; pthread_mutex_lock(&x->x_mutex); x->x_sf.sf_fd = -1; if (x->x_requestcode != REQUEST_BUSY) goto lost; } /* cache sf *after* closing as x->sf's type may have changed in readsf_open() */ soundfile_copy(&sf, &x->x_sf); /* open the soundfile with the mutex unlocked */ pthread_mutex_unlock(&x->x_mutex); open_soundfile_via_path(dirname, filename, &sf, onsetframes); pthread_mutex_lock(&x->x_mutex); #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "readsf~: 5\n"); #endif if (sf.sf_fd < 0) { x->x_fileerror = errno; x->x_eof = 1; #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "readsf~: open failed %s %s\n", filename, dirname); #endif goto lost; } /* copy back into the instance structure. */ soundfile_copy(&x->x_sf, &sf); /* check if another request has been made; if so, field it */ if (x->x_requestcode != REQUEST_BUSY) goto lost; #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "readsf~: 6\n"); #endif x->x_fifohead = 0; /* set fifosize from bufsize. fifosize must be a multiple of the number of bytes eaten for each DSP tick. We pessimistically assume MAXVECSIZE samples per tick since that could change. There could be a problem here if the vector size increases while a soundfile is being played... */ x->x_fifosize = x->x_bufsize - (x->x_bufsize % (sf.sf_bytesperframe * MAXVECSIZE)); /* arrange for the "request" condition to be signaled 16 times per buffer */ #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "readsf~: fifosize %d\n", x->x_fifosize); #endif x->x_sigcountdown = x->x_sigperiod = (x->x_fifosize / (16 * sf.sf_bytesperframe * x->x_vecsize)); /* in a loop, wait for the fifo to get hungry and feed it */ while (x->x_requestcode == REQUEST_BUSY) { int fifosize = x->x_fifosize; #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "readsf~: 77\n"); #endif if (x->x_eof) break; if (x->x_fifohead >= x->x_fifotail) { /* if the head is >= the tail, we can immediately read to the end of the fifo. Unless, that is, we would read all the way to the end of the buffer and the "tail" is zero; this would fill the buffer completely which isn't allowed because you can't tell a completely full buffer from an empty one. */ if (x->x_fifotail || (fifosize - x->x_fifohead > READSIZE)) { wantbytes = fifosize - x->x_fifohead; if (wantbytes > READSIZE) wantbytes = READSIZE; if (sf.sf_bytelimit >= 0 && wantbytes > (size_t)sf.sf_bytelimit) wantbytes = sf.sf_bytelimit; #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "readsf~: head %d, tail %d, size %ld\n", x->x_fifohead, x->x_fifotail, wantbytes); #endif } else { #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "readsf~: wait 7a...\n"); #endif sfread_cond_signal(&x->x_answercondition); #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "readsf~: signaled...\n"); #endif sfread_cond_wait(&x->x_requestcondition, &x->x_mutex); #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "readsf~: 7a ... done\n"); #endif continue; } } else { /* otherwise check if there are at least READSIZE bytes to read. If not, wait and loop back. */ wantbytes = x->x_fifotail - x->x_fifohead - 1; if (wantbytes < READSIZE) { #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "readsf~: wait 7...\n"); #endif sfread_cond_signal(&x->x_answercondition); sfread_cond_wait(&x->x_requestcondition, &x->x_mutex); #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "readsf~: 7 ... done\n"); #endif continue; } else wantbytes = READSIZE; if (sf.sf_bytelimit >= 0 && wantbytes > (size_t)sf.sf_bytelimit) wantbytes = sf.sf_bytelimit; } #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "readsf~: 8\n"); #endif buf = x->x_buf; fifohead = x->x_fifohead; pthread_mutex_unlock(&x->x_mutex); bytesread = read(sf.sf_fd, buf + fifohead, wantbytes); pthread_mutex_lock(&x->x_mutex); if (x->x_requestcode != REQUEST_BUSY) break; if (bytesread < 0) { #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "readsf~: fileerror %d\n", errno); #endif x->x_fileerror = errno; break; } else if (bytesread == 0) { x->x_eof = 1; break; } else { x->x_fifohead += bytesread; sf.sf_bytelimit -= bytesread; if (x->x_fifohead == fifosize) x->x_fifohead = 0; if (sf.sf_bytelimit <= 0) { x->x_eof = 1; break; } } #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "readsf~: after, head %d tail %d\n", x->x_fifohead, x->x_fifotail); #endif /* signal parent in case it's waiting for data */ sfread_cond_signal(&x->x_answercondition); } lost: if (x->x_requestcode == REQUEST_BUSY) x->x_requestcode = REQUEST_NOTHING; #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "readsf~: lost\n"); #endif /* fell out of read loop: close file if necessary, set EOF and signal once more */ if (sf.sf_fd >= 0) { /* only set EOF if there is no pending "open" request! Otherwise, we might accidentally set EOF after it has been unset in readsf_open() and the stream would fail silently. */ if (x->x_requestcode != REQUEST_OPEN) x->x_eof = 1; x->x_sf.sf_fd = -1; /* use cached sf */ pthread_mutex_unlock(&x->x_mutex); sys_close(sf.sf_fd); sf.sf_fd = -1; pthread_mutex_lock(&x->x_mutex); } sfread_cond_signal(&x->x_answercondition); } else if (x->x_requestcode == REQUEST_CLOSE) { if (sf.sf_fd >= 0) { x->x_sf.sf_fd = -1; /* use cached sf */ pthread_mutex_unlock(&x->x_mutex); sys_close(sf.sf_fd); sf.sf_fd = -1; pthread_mutex_lock(&x->x_mutex); } if (x->x_requestcode == REQUEST_CLOSE) x->x_requestcode = REQUEST_NOTHING; sfread_cond_signal(&x->x_answercondition); } else if (x->x_requestcode == REQUEST_QUIT) { if (sf.sf_fd >= 0) { x->x_sf.sf_fd = -1; /* use cached sf */ pthread_mutex_unlock(&x->x_mutex); sys_close(sf.sf_fd); sf.sf_fd = -1; pthread_mutex_lock(&x->x_mutex); } x->x_requestcode = REQUEST_NOTHING; sfread_cond_signal(&x->x_answercondition); break; } else { #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "readsf~: 13\n"); #endif } } #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "readsf~: thread exit\n"); #endif pthread_mutex_unlock(&x->x_mutex); return 0; } /* ----- the object proper runs in the calling (parent) thread ----- */ static void readsf_tick(t_readsf *x); static void *readsf_new(t_floatarg fnchannels, t_floatarg fbufsize) { t_readsf *x; int nchannels = fnchannels, bufsize = fbufsize, i; char *buf; if (nchannels < 1) nchannels = 1; else if (nchannels > MAXSFCHANS) nchannels = MAXSFCHANS; if (bufsize <= 0) bufsize = DEFBUFPERCHAN * nchannels; else if (bufsize < MINBUFSIZE) bufsize = MINBUFSIZE; else if (bufsize > MAXBUFSIZE) bufsize = MAXBUFSIZE; buf = getbytes(bufsize); if (!buf) return 0; x = (t_readsf *)pd_new(readsf_class); for (i = 0; i < nchannels; i++) outlet_new(&x->x_obj, gensym("signal")); x->x_noutlets = nchannels; x->x_bangout = outlet_new(&x->x_obj, &s_bang); pthread_mutex_init(&x->x_mutex, 0); pthread_cond_init(&x->x_requestcondition, 0); pthread_cond_init(&x->x_answercondition, 0); x->x_vecsize = MAXVECSIZE; x->x_state = STATE_IDLE; x->x_clock = clock_new(x, (t_method)readsf_tick); x->x_canvas = canvas_getcurrent(); soundfile_clear(&x->x_sf); x->x_sf.sf_bytespersample = 2; x->x_sf.sf_nchannels = 1; x->x_sf.sf_bytesperframe = 2; x->x_buf = buf; x->x_bufsize = bufsize; x->x_fifosize = x->x_fifohead = x->x_fifotail = x->x_requestcode = 0; #ifdef PDINSTANCE x->x_pd_this = pd_this; #endif pthread_create(&x->x_childthread, 0, readsf_child_main, x); return x; } static void readsf_tick(t_readsf *x) { outlet_bang(x->x_bangout); } static t_int *readsf_perform(t_int *w) { t_readsf *x = (t_readsf *)(w[1]); int vecsize = x->x_vecsize, noutlets = x->x_noutlets, i; size_t j; t_sample *fp; if (x->x_state == STATE_STREAM) { int wantbytes; t_soundfile sf = {0}; pthread_mutex_lock(&x->x_mutex); /* copy with mutex locked! */ soundfile_copy(&sf, &x->x_sf); wantbytes = vecsize * sf.sf_bytesperframe; while (!x->x_eof && x->x_fifohead >= x->x_fifotail && x->x_fifohead < x->x_fifotail + wantbytes-1) { #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "readsf~: wait...\n"); #endif sfread_cond_signal(&x->x_requestcondition); sfread_cond_wait(&x->x_answercondition, &x->x_mutex); /* resync local variables -- bug fix thanks to Shahrokh */ vecsize = x->x_vecsize; soundfile_copy(&sf, &x->x_sf); wantbytes = vecsize * sf.sf_bytesperframe; #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "readsf~: ... done\n"); #endif } if (x->x_eof && x->x_fifohead >= x->x_fifotail && x->x_fifohead < x->x_fifotail + wantbytes-1) { int xfersize; if (x->x_fileerror) object_sferror(x, "readsf~", x->x_filename, x->x_fileerror, &x->x_sf); /* if there's a partial buffer left, copy it out */ xfersize = (x->x_fifohead - x->x_fifotail + 1) / sf.sf_bytesperframe; if (xfersize) { soundfile_xferin_sample(&sf, noutlets, x->x_outvec, 0, (unsigned char *)(x->x_buf + x->x_fifotail), xfersize); vecsize -= xfersize; } pthread_mutex_unlock(&x->x_mutex); /* send bang and zero out the (rest of the) output */ clock_delay(x->x_clock, 0); x->x_state = STATE_IDLE; for (i = 0; i < noutlets; i++) for (j = vecsize, fp = x->x_outvec[i] + xfersize; j--;) *fp++ = 0; return w + 2; } soundfile_xferin_sample(&sf, noutlets, x->x_outvec, 0, (unsigned char *)(x->x_buf + x->x_fifotail), vecsize); x->x_fifotail += wantbytes; if (x->x_fifotail >= x->x_fifosize) x->x_fifotail = 0; if ((--x->x_sigcountdown) <= 0) { sfread_cond_signal(&x->x_requestcondition); x->x_sigcountdown = x->x_sigperiod; } pthread_mutex_unlock(&x->x_mutex); } else { for (i = 0; i < noutlets; i++) for (j = vecsize, fp = x->x_outvec[i]; j--;) *fp++ = 0; } return w + 2; } /** start making output. If we're in the "startup" state change to the "running" state. */ static void readsf_start(t_readsf *x) { if (x->x_state == STATE_STARTUP) x->x_state = STATE_STREAM; else pd_error(x, "readsf~: start requested with no prior 'open'"); } /** LATER rethink whether you need the mutex just to set a variable? */ static void readsf_stop(t_readsf *x) { pthread_mutex_lock(&x->x_mutex); x->x_state = STATE_IDLE; x->x_requestcode = REQUEST_CLOSE; sfread_cond_signal(&x->x_requestcondition); pthread_mutex_unlock(&x->x_mutex); } static void readsf_float(t_readsf *x, t_floatarg f) { if (f != 0) readsf_start(x); else readsf_stop(x); } /** open method. Called as: open [flags] filename [onsetframes headersize channels bytes endianness] (if headersize is zero, header is taken to be automatically detected; thus, use the special "-1" to mean a truly headerless file.) if type implementation is set, pass this to open unless headersize is -1 */ static void readsf_open(t_readsf *x, t_symbol *s, int argc, t_atom *argv) { t_symbol *filesym, *endian; t_float onsetframes, headersize, nchannels, bytespersample; t_soundfile_type *type = NULL; while (argc > 0 && argv->a_type == A_SYMBOL && *argv->a_w.w_symbol->s_name == '-') { /* check for type by name */ const char *flag = argv->a_w.w_symbol->s_name + 1; if (!(type = soundfile_findtype(flag))) goto usage; /* unknown flag */ argc -= 1; argv += 1; } filesym = atom_getsymbolarg(0, argc, argv); onsetframes = atom_getfloatarg(1, argc, argv); headersize = atom_getfloatarg(2, argc, argv); nchannels = atom_getfloatarg(3, argc, argv); bytespersample = atom_getfloatarg(4, argc, argv); endian = atom_getsymbolarg(5, argc, argv); if (!*filesym->s_name) return; /* no filename */ pthread_mutex_lock(&x->x_mutex); soundfile_clear(&x->x_sf); x->x_requestcode = REQUEST_OPEN; x->x_filename = filesym->s_name; x->x_fifotail = 0; x->x_fifohead = 0; if (*endian->s_name == 'b') x->x_sf.sf_bigendian = 1; else if (*endian->s_name == 'l') x->x_sf.sf_bigendian = 0; else if (*endian->s_name) pd_error(x, "readsf~ open: endianness neither 'b' nor 'l'"); else x->x_sf.sf_bigendian = sys_isbigendian(); x->x_onsetframes = (onsetframes > 0 ? onsetframes : 0); x->x_sf.sf_headersize = (headersize > 0 ? headersize : (headersize == 0 ? -1 : 0)); x->x_sf.sf_nchannels = (nchannels >= 1 ? nchannels : 1); x->x_sf.sf_bytespersample = (bytespersample > 2 ? bytespersample : 2); x->x_sf.sf_bytesperframe = x->x_sf.sf_nchannels * x->x_sf.sf_bytespersample; if (type && x->x_sf.sf_headersize >= 0) { post("'-%s' overridden by headersize", type->t_name); x->x_sf.sf_type = NULL; } else x->x_sf.sf_type = type; x->x_eof = 0; x->x_fileerror = 0; x->x_state = STATE_STARTUP; sfread_cond_signal(&x->x_requestcondition); pthread_mutex_unlock(&x->x_mutex); return; usage: pd_error(x, "usage: open [flags] filename [onset] [headersize]..."); pd_error(0, "[nchannels] [bytespersample] [endian (b or l)]"); post("flags: %s", sf_typeargs); } static void readsf_dsp(t_readsf *x, t_signal **sp) { int i, noutlets = x->x_noutlets; pthread_mutex_lock(&x->x_mutex); x->x_vecsize = sp[0]->s_n; x->x_sigperiod = x->x_fifosize / (x->x_sf.sf_bytesperframe * x->x_vecsize); for (i = 0; i < noutlets; i++) x->x_outvec[i] = sp[i]->s_vec; pthread_mutex_unlock(&x->x_mutex); dsp_add(readsf_perform, 1, x); } static void readsf_print(t_readsf *x) { post("state %d", x->x_state); post("fifo head %d", x->x_fifohead); post("fifo tail %d", x->x_fifotail); post("fifo size %d", x->x_fifosize); post("fd %d", x->x_sf.sf_fd); post("eof %d", x->x_eof); } /** request QUIT and wait for acknowledge */ static void readsf_free(t_readsf *x) { void *threadrtn; pthread_mutex_lock(&x->x_mutex); x->x_requestcode = REQUEST_QUIT; sfread_cond_signal(&x->x_requestcondition); while (x->x_requestcode != REQUEST_NOTHING) { sfread_cond_signal(&x->x_requestcondition); sfread_cond_wait(&x->x_answercondition, &x->x_mutex); } pthread_mutex_unlock(&x->x_mutex); if (pthread_join(x->x_childthread, &threadrtn)) pd_error(x, "readsf_free: join failed"); pthread_cond_destroy(&x->x_requestcondition); pthread_cond_destroy(&x->x_answercondition); pthread_mutex_destroy(&x->x_mutex); freebytes(x->x_buf, x->x_bufsize); clock_free(x->x_clock); } static void readsf_setup(void) { readsf_class = class_new(gensym("readsf~"), (t_newmethod)readsf_new, (t_method)readsf_free, sizeof(t_readsf), 0, A_DEFFLOAT, A_DEFFLOAT, 0); class_addfloat(readsf_class, (t_method)readsf_float); class_addmethod(readsf_class, (t_method)readsf_start, gensym("start"), 0); class_addmethod(readsf_class, (t_method)readsf_stop, gensym("stop"), 0); class_addmethod(readsf_class, (t_method)readsf_dsp, gensym("dsp"), A_CANT, 0); class_addmethod(readsf_class, (t_method)readsf_open, gensym("open"), A_GIMME, 0); class_addmethod(readsf_class, (t_method)readsf_print, gensym("print"), 0); } /* ------------------------- writesf ------------------------- */ static t_class *writesf_class; typedef t_readsf t_writesf; /* just re-use the structure */ /* ----- the child thread which performs file I/O ----- */ static void *writesf_child_main(void *zz) { t_writesf *x = zz; t_soundfile sf = {0}; soundfile_clear(&sf); #ifdef PDINSTANCE pd_this = x->x_pd_this; #endif #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "writesf~: 1\n"); #endif pthread_mutex_lock(&x->x_mutex); while (1) { #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "writesf~: 0\n"); #endif if (x->x_requestcode == REQUEST_NOTHING) { #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "writesf~: wait 2\n"); #endif sfread_cond_signal(&x->x_answercondition); sfread_cond_wait(&x->x_requestcondition, &x->x_mutex); #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "writesf~: 3\n"); #endif } else if (x->x_requestcode == REQUEST_OPEN) { ssize_t byteswritten; size_t writebytes; /* copy file stuff out of the data structure so we can relinquish the mutex while we're in open_soundfile_via_path() */ const char *filename = x->x_filename; t_canvas *canvas = x->x_canvas; soundfile_copy(&sf, &x->x_sf); /* alter the request code so that an ensuing "open" will get noticed. */ #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "writesf~: 4\n"); #endif x->x_requestcode = REQUEST_BUSY; x->x_fileerror = 0; /* if there's already a file open, close it. This should never happen since writesf_open() calls stop if needed and then waits until we're idle. */ if (sf.sf_fd >= 0) { size_t frameswritten = x->x_frameswritten; pthread_mutex_unlock(&x->x_mutex); soundfile_finishwrite(x, filename, &sf, SFMAXFRAMES, frameswritten); sys_close(sf.sf_fd); sf.sf_fd = -1; pthread_mutex_lock(&x->x_mutex); x->x_sf.sf_fd = -1; #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "writesf~: bug? ditched %ld\n", frameswritten); #endif if (x->x_requestcode != REQUEST_BUSY) continue; } /* cache sf *after* closing as x->sf's type may have changed in writesf_open() */ soundfile_copy(&sf, &x->x_sf); /* open the soundfile with the mutex unlocked */ pthread_mutex_unlock(&x->x_mutex); create_soundfile(canvas, filename, &sf, 0); pthread_mutex_lock(&x->x_mutex); #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "writesf~: 5\n"); #endif if (sf.sf_fd < 0) { x->x_sf.sf_fd = -1; x->x_eof = 1; x->x_fileerror = errno; #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "writesf~: open failed %s\n", filename); #endif goto bail; } /* check if another request has been made; if so, field it */ if (x->x_requestcode != REQUEST_BUSY) continue; #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "writesf~: 6\n"); #endif /* copy back into the instance structure. */ soundfile_copy(&x->x_sf, &sf); x->x_fifotail = 0; x->x_frameswritten = 0; /* in a loop, wait for the fifo to have data and write it to disk */ while (x->x_requestcode == REQUEST_BUSY || (x->x_requestcode == REQUEST_CLOSE && x->x_fifohead != x->x_fifotail)) { int fifosize = x->x_fifosize, fifotail; char *buf = x->x_buf; #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "writesf~: 77\n"); #endif /* if the head is < the tail, we can immediately write from tail to end of fifo to disk; otherwise we hold off writing until there are at least WRITESIZE bytes in the buffer */ if (x->x_fifohead < x->x_fifotail || x->x_fifohead >= x->x_fifotail + WRITESIZE || (x->x_requestcode == REQUEST_CLOSE && x->x_fifohead != x->x_fifotail)) { writebytes = (x->x_fifohead < x->x_fifotail ? fifosize : x->x_fifohead) - x->x_fifotail; if (writebytes > READSIZE) writebytes = READSIZE; } else { #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "writesf~: wait 7a...\n"); #endif sfread_cond_signal(&x->x_answercondition); #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "writesf~: signaled...\n"); #endif sfread_cond_wait(&x->x_requestcondition, &x->x_mutex); #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "writesf~: 7a ... done\n"); #endif continue; } #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "writesf~: 8\n"); #endif fifotail = x->x_fifotail; soundfile_copy(&sf, &x->x_sf); pthread_mutex_unlock(&x->x_mutex); byteswritten = write(sf.sf_fd, buf + fifotail, writebytes); pthread_mutex_lock(&x->x_mutex); if (x->x_requestcode != REQUEST_BUSY && x->x_requestcode != REQUEST_CLOSE) break; if (byteswritten < 0 || (size_t)byteswritten < writebytes) { #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "writesf~: fileerror %d\n", errno); #endif x->x_fileerror = errno; goto bail; } else { x->x_fifotail += byteswritten; if (x->x_fifotail == fifosize) x->x_fifotail = 0; } x->x_frameswritten += byteswritten / sf.sf_bytesperframe; #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "writesf~: after head %d tail %d written %ld\n", x->x_fifohead, x->x_fifotail, x->x_frameswritten); #endif /* signal parent in case it's waiting for data */ sfread_cond_signal(&x->x_answercondition); continue; bail: if (x->x_requestcode == REQUEST_BUSY) x->x_requestcode = REQUEST_NOTHING; /* hit an error; close file if necessary, set EOF and signal once more */ if (sf.sf_fd >= 0) { pthread_mutex_unlock(&x->x_mutex); sys_close(sf.sf_fd); sf.sf_fd = -1; pthread_mutex_lock(&x->x_mutex); x->x_eof = 1; x->x_sf.sf_fd = -1; } sfread_cond_signal(&x->x_answercondition); } } else if (x->x_requestcode == REQUEST_CLOSE || x->x_requestcode == REQUEST_QUIT) { int quit = (x->x_requestcode == REQUEST_QUIT); if (sf.sf_fd >= 0) { const char *filename = x->x_filename; size_t frameswritten = x->x_frameswritten; soundfile_copy(&sf, &x->x_sf); pthread_mutex_unlock(&x->x_mutex); soundfile_finishwrite(x, filename, &sf, SFMAXFRAMES, frameswritten); sys_close(sf.sf_fd); sf.sf_fd = -1; pthread_mutex_lock(&x->x_mutex); x->x_sf.sf_fd = -1; } x->x_requestcode = REQUEST_NOTHING; sfread_cond_signal(&x->x_answercondition); if (quit) break; } else { #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "writesf~: 13\n"); #endif } } #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "writesf~: thread exit\n"); #endif pthread_mutex_unlock(&x->x_mutex); return 0; } /* ----- the object proper runs in the calling (parent) thread ----- */ static void writesf_tick(t_writesf *x); static void *writesf_new(t_floatarg fnchannels, t_floatarg fbufsize) { t_writesf *x; int nchannels = fnchannels, bufsize = fbufsize, i; char *buf; if (nchannels < 1) nchannels = 1; else if (nchannels > MAXSFCHANS) nchannels = MAXSFCHANS; if (bufsize <= 0) bufsize = DEFBUFPERCHAN * nchannels; else if (bufsize < MINBUFSIZE) bufsize = MINBUFSIZE; else if (bufsize > MAXBUFSIZE) bufsize = MAXBUFSIZE; buf = getbytes(bufsize); if (!buf) return 0; x = (t_writesf *)pd_new(writesf_class); for (i = 1; i < nchannels; i++) inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); x->x_f = 0; pthread_mutex_init(&x->x_mutex, 0); pthread_cond_init(&x->x_requestcondition, 0); pthread_cond_init(&x->x_answercondition, 0); x->x_vecsize = MAXVECSIZE; x->x_insamplerate = 0; x->x_state = STATE_IDLE; x->x_clock = 0; /* no callback needed here */ x->x_canvas = canvas_getcurrent(); soundfile_clear(&x->x_sf); x->x_sf.sf_nchannels = nchannels; x->x_sf.sf_bytespersample = 2; x->x_sf.sf_bytesperframe = nchannels * 2; x->x_buf = buf; x->x_bufsize = bufsize; x->x_fifosize = x->x_fifohead = x->x_fifotail = x->x_requestcode = 0; #ifdef PDINSTANCE x->x_pd_this = pd_this; #endif pthread_create(&x->x_childthread, 0, writesf_child_main, x); return x; } static t_int *writesf_perform(t_int *w) { t_writesf *x = (t_writesf *)(w[1]); if (x->x_state == STATE_STREAM) { size_t roominfifo; size_t wantbytes; int vecsize = x->x_vecsize; t_soundfile sf = {0}; pthread_mutex_lock(&x->x_mutex); /* copy with mutex locked! */ soundfile_copy(&sf, &x->x_sf); wantbytes = vecsize * sf.sf_bytesperframe; roominfifo = x->x_fifotail - x->x_fifohead; if (roominfifo <= 0) roominfifo += x->x_fifosize; while (!x->x_eof && roominfifo < wantbytes + 1) { fprintf(stderr, "writesf waiting for disk write..\n"); fprintf(stderr, "(head %d, tail %d, room %d, want %ld)\n", (int)x->x_fifohead, (int)x->x_fifotail, (int)roominfifo, (long)wantbytes); sfread_cond_signal(&x->x_requestcondition); sfread_cond_wait(&x->x_answercondition, &x->x_mutex); fprintf(stderr, "... done waiting.\n"); roominfifo = x->x_fifotail - x->x_fifohead; if (roominfifo <= 0) roominfifo += x->x_fifosize; } if (x->x_eof) { if (x->x_fileerror) object_sferror(x, "writesf~", x->x_filename, x->x_fileerror, &x->x_sf); x->x_state = STATE_IDLE; sfread_cond_signal(&x->x_requestcondition); pthread_mutex_unlock(&x->x_mutex); return w + 2; } soundfile_xferout_sample(&sf, x->x_outvec, (unsigned char *)(x->x_buf + x->x_fifohead), vecsize, 0, 1.); x->x_fifohead += wantbytes; if (x->x_fifohead >= x->x_fifosize) x->x_fifohead = 0; if ((--x->x_sigcountdown) <= 0) { #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "writesf~: signal 1\n"); #endif sfread_cond_signal(&x->x_requestcondition); x->x_sigcountdown = x->x_sigperiod; } pthread_mutex_unlock(&x->x_mutex); } return w + 2; } /** start making output. If we're in the "startup" state change to the "running" state. */ static void writesf_start(t_writesf *x) { if (x->x_state == STATE_STARTUP) x->x_state = STATE_STREAM; else pd_error(x, "writesf~: start requested with no prior 'open'"); } /** LATER rethink whether you need the mutex just to set a variable? */ static void writesf_stop(t_writesf *x) { pthread_mutex_lock(&x->x_mutex); x->x_state = STATE_IDLE; x->x_requestcode = REQUEST_CLOSE; #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "writesf~: signal 2\n"); #endif sfread_cond_signal(&x->x_requestcondition); pthread_mutex_unlock(&x->x_mutex); } /** open method. Called as: open [flags] filename with args as in soundfiler_parsewriteargs(). */ static void writesf_open(t_writesf *x, t_symbol *s, int argc, t_atom *argv) { t_soundfiler_writeargs wa = {0}; if (x->x_state != STATE_IDLE) writesf_stop(x); if (soundfiler_parsewriteargs(x, &argc, &argv, &wa) || wa.wa_ascii) { pd_error(x, "usage: open [flags] filename..."); post("flags: -bytes %s -big -little -rate ", sf_typeargs); return; } if (wa.wa_normalize || wa.wa_onsetframes || (wa.wa_nframes != SFMAXFRAMES)) pd_error(x, "writesf~ open: normalize/onset/nframes argument ignored"); if (argc) pd_error(x, "writesf~ open: extra argument(s) ignored"); pthread_mutex_lock(&x->x_mutex); /* make sure that the child thread has finished writing */ while (x->x_requestcode != REQUEST_NOTHING) { sfread_cond_signal(&x->x_requestcondition); sfread_cond_wait(&x->x_answercondition, &x->x_mutex); } x->x_filename = wa.wa_filesym->s_name; x->x_sf.sf_type = wa.wa_type; if (wa.wa_samplerate > 0) x->x_sf.sf_samplerate = wa.wa_samplerate; else if (x->x_insamplerate > 0) x->x_sf.sf_samplerate = x->x_insamplerate; else x->x_sf.sf_samplerate = sys_getsr(); x->x_sf.sf_bytespersample = (wa.wa_bytespersample > 2 ? wa.wa_bytespersample : 2); x->x_sf.sf_bigendian = wa.wa_bigendian; x->x_sf.sf_bytesperframe = x->x_sf.sf_nchannels * x->x_sf.sf_bytespersample; x->x_frameswritten = 0; x->x_requestcode = REQUEST_OPEN; x->x_fifotail = 0; x->x_fifohead = 0; x->x_eof = 0; x->x_fileerror = 0; x->x_state = STATE_STARTUP; /* set fifosize from bufsize. fifosize must be a multiple of the number of bytes eaten for each DSP tick. */ x->x_fifosize = x->x_bufsize - (x->x_bufsize % (x->x_sf.sf_bytesperframe * MAXVECSIZE)); /* arrange for the "request" condition to be signaled 16 times per buffer */ x->x_sigcountdown = x->x_sigperiod = (x->x_fifosize / (16 * (x->x_sf.sf_bytesperframe * x->x_vecsize))); sfread_cond_signal(&x->x_requestcondition); pthread_mutex_unlock(&x->x_mutex); } static void writesf_dsp(t_writesf *x, t_signal **sp) { int i, ninlets = x->x_sf.sf_nchannels; pthread_mutex_lock(&x->x_mutex); x->x_vecsize = sp[0]->s_n; x->x_sigperiod = (x->x_fifosize / (16 * x->x_sf.sf_bytesperframe * x->x_vecsize)); for (i = 0; i < ninlets; i++) x->x_outvec[i] = sp[i]->s_vec; x->x_insamplerate = sp[0]->s_sr; pthread_mutex_unlock(&x->x_mutex); dsp_add(writesf_perform, 1, x); } static void writesf_print(t_writesf *x) { post("state %d", x->x_state); post("fifo head %d", x->x_fifohead); post("fifo tail %d", x->x_fifotail); post("fifo size %d", x->x_fifosize); post("fd %d", x->x_sf.sf_fd); post("eof %d", x->x_eof); } /** request QUIT and wait for acknowledge */ static void writesf_free(t_writesf *x) { void *threadrtn; pthread_mutex_lock(&x->x_mutex); x->x_requestcode = REQUEST_QUIT; #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "writesf~: stopping thread...\n"); #endif sfread_cond_signal(&x->x_requestcondition); while (x->x_requestcode != REQUEST_NOTHING) { #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "writesf~: signaling...\n"); #endif sfread_cond_signal(&x->x_requestcondition); sfread_cond_wait(&x->x_answercondition, &x->x_mutex); } pthread_mutex_unlock(&x->x_mutex); if (pthread_join(x->x_childthread, &threadrtn)) pd_error(x, "writesf_free: join failed"); #ifdef DEBUG_SOUNDFILE_THREADS fprintf(stderr, "writesf~: ... done\n"); #endif pthread_cond_destroy(&x->x_requestcondition); pthread_cond_destroy(&x->x_answercondition); pthread_mutex_destroy(&x->x_mutex); freebytes(x->x_buf, x->x_bufsize); } static void writesf_setup(void) { writesf_class = class_new(gensym("writesf~"), (t_newmethod)writesf_new, (t_method)writesf_free, sizeof(t_writesf), 0, A_DEFFLOAT, A_DEFFLOAT, 0); class_addmethod(writesf_class, (t_method)writesf_start, gensym("start"), 0); class_addmethod(writesf_class, (t_method)writesf_stop, gensym("stop"), 0); class_addmethod(writesf_class, (t_method)writesf_dsp, gensym("dsp"), A_CANT, 0); class_addmethod(writesf_class, (t_method)writesf_open, gensym("open"), A_GIMME, 0); class_addmethod(writesf_class, (t_method)writesf_print, gensym("print"), 0); CLASS_MAINSIGNALIN(writesf_class, t_writesf, x_f); } /* ------------------------- global setup routine ------------------------ */ void d_soundfile_setup(void) { soundfile_type_setup(); soundfiler_setup(); readsf_setup(); writesf_setup(); } ================================================ FILE: libs/libpd/pure-data/src/d_soundfile.h ================================================ /* Copyright (c) 2019 Dan Wilcox. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* soundfile formats and helper functions */ #pragma once #include "m_pd.h" #ifdef HAVE_UNISTD_H #include #endif #ifdef _WIN32 #include #endif #include #include #include /* GLIBC large file support */ #ifdef _LARGEFILE64_SOURCE #define lseek lseek64 #define off_t __off64_t #endif /* MSVC doesn't define or uses different naming for these Posix types */ #ifdef _MSC_VER #include typedef SSIZE_T ssize_t; #define off_t ssize_t /* choose appropriate size if SSIZE_MAX is not defined */ #ifndef SSIZE_MAX #ifdef _WIN64 #define SSIZE_MAX _I64_MAX #else /* _WIN32 */ #define SSIZE_MAX INT_MAX #endif #endif /* SSIZE_MAX */ #endif /* _MSC_VER */ /** should be large enough for all file type min sizes */ #define SFHDRBUFSIZE 128 #define SFMAXFRAMES SIZE_MAX /**< default max sample frames, unsigned */ #define SFMAXBYTES SSIZE_MAX /**< default max sample bytes, signed */ /** sound file read/write debug posts */ //#define DEBUG_SOUNDFILE /* ----- soundfile ----- */ /** soundfile file descriptor, backend type, and format info note: headersize and bytelimit are signed as they are used for < 0 comparisons, hopefully ssize_t is large enough "headersize" can also be thought of as the audio data byte offset */ typedef struct _soundfile { int sf_fd; /**< file descriptor, >= 0 : open, -1 : closed */ struct _soundfile_type *sf_type; /**< type implementation */ /* format info */ int sf_samplerate; /**< read: file sr, write: pd sr */ int sf_nchannels; /**< number of channels */ int sf_bytespersample; /**< bit rate, 2: 16 bit, 3: 24 bit, 4: 32 bit */ ssize_t sf_headersize; /**< header size in bytes, -1 for unknown size */ int sf_bigendian; /**< sample endianness, 1 : big or 0 : little */ int sf_bytesperframe; /**< number of bytes per sample frame */ ssize_t sf_bytelimit; /**< number of sound data bytes to read/write */ } t_soundfile; /** clear soundfile struct to defaults, does not close or free */ void soundfile_clear(t_soundfile *sf); /** copy src soundfile info into dst */ void soundfile_copy(t_soundfile *dst, const t_soundfile *src); /** returns 1 if bytes need to be swapped due to endianness, otherwise 0 */ int soundfile_needsbyteswap(const t_soundfile *sf); /** generic soundfile errors */ typedef enum _soundfile_errno { SOUNDFILE_ERRUNKNOWN = -1000, /* unknown header */ SOUNDFILE_ERRMALFORMED = -1001, /* bad header */ SOUNDFILE_ERRVERSION = -1002, /* header ok, unsupported version */ SOUNDFILE_ERRSAMPLEFMT = -1003 /* header ok, unsupported sample format */ } t_soundfile_errno; /** returns a soundfile error string, otherwise calls C strerror */ const char* soundfile_strerror(int errnum); /* ----- soundfile type ----- */ /** returns 1 if buffer is the beginning of a supported file header, size will be at least minheadersize this may be called in a background thread */ typedef int (*t_soundfile_isheaderfn)(const char *buf, size_t size); /** read format info from soundfile header, returns 1 on success or 0 on error note: set sf_bytelimit = sound data size, optionally set errno this may be called in a background thread */ typedef int (*t_soundfile_readheaderfn)(t_soundfile *sf); /** write header to beginning of an open file from an info struct returns header bytes written or < 0 on error note: optionally set errno this may be called in a background thread */ typedef int (*t_soundfile_writeheaderfn)(t_soundfile *sf, size_t nframes); /** update file header data size, returns 1 on success or 0 on error this may be called in a background thread */ typedef int (*t_soundfile_updateheaderfn)(t_soundfile *sf, size_t nframes); /** returns 1 if the filename has a supported file extension, otherwise 0 this may be called in a background thread */ typedef int (*t_soundfile_hasextensionfn)(const char *filename, size_t size); /** appends the default file extension, returns 1 on success this may be called in a background thread */ typedef int (*t_soundfile_addextensionfn)(char *filename, size_t size); /** returns the type's preferred sample endianness based on the requested endianness (0 little, 1 big, -1 unspecified) returns 1 for big endian, 0 for little endian */ typedef int (*t_soundfile_endiannessfn)(int endianness); /* type implementation for a single file format */ typedef struct _soundfile_type { char *t_name; /**< type name, unique & w/o white spaces */ size_t t_minheadersize; /**< minimum valid header size */ t_soundfile_isheaderfn t_isheaderfn; /**< must be non-NULL */ t_soundfile_readheaderfn t_readheaderfn; /**< must be non-NULL */ t_soundfile_writeheaderfn t_writeheaderfn; /**< must be non-NULL */ t_soundfile_updateheaderfn t_updateheaderfn; /**< must be non-NULL */ t_soundfile_hasextensionfn t_hasextensionfn; /**< must be non-NULL */ t_soundfile_addextensionfn t_addextensionfn; /**< must be non-NULL */ t_soundfile_endiannessfn t_endiannessfn; /**< must be non-NULL */ } t_soundfile_type; /** add a new type implementation returns 1 on success or 0 if max types has been reached */ int soundfile_addtype(const t_soundfile_type *t); /* ----- read/write helpers ----- */ /** seek to offset in file fd and read size bytes into dst, returns bytes written on success or -1 on failure */ ssize_t fd_read(int fd, off_t offset, void *dst, size_t size); /** seek to offset in file fd and write size bytes from dst, returns number of bytes written on success or -1 if seek or write failed */ ssize_t fd_write(int fd, off_t offset, const void *src, size_t size); /* ----- byte swappers ----- */ /** returns 1 if system is bigendian */ int sys_isbigendian(void); /** swap 8 bytes and return if doit = 1, otherwise return n */ uint64_t swap8(uint64_t n, int doit); /** swap a 64 bit signed int and return if doit = 1, otherwise return n */ int64_t swap8s(int64_t n, int doit); /** swap 4 bytes and return if doit = 1, otherwise return n */ uint32_t swap4(uint32_t n, int doit); /** swap a 32 bit signed int and return if doit = 1, otherwise return n */ int32_t swap4s(int32_t n, int doit); /** swap 2 bytes and return if doit = 1, otherwise return n */ uint16_t swap2(uint16_t n, int doit); /** swap a 4 byte string in place if doit = 1, otherwise do nothing */ void swapstring4(char *foo, int doit); /** swap an 8 byte string in place if doit = 1, otherwise do nothing */ void swapstring8(char *foo, int doit); ================================================ FILE: libs/libpd/pure-data/src/d_soundfile_aiff.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. Updated 2020 Dan Wilcox. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* ref: http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/AIFF/AIFF.html */ #include "d_soundfile.h" #include /* AIFF (Audio Interchange File Format) and AIFF-C (AIFF Compressed) * RIFF variant with sections split into data "chunks" * chunk sizes do not include the chunk id or size (- 8) * the file header's size is the total size of all subsequent chunks (- 8) * chunk data is big endian * sound data is big endian, unless AIFF-C "sowt" compression type * the sound data chunk can be omitted if common chunk sample frames is 0 * chunks start on even byte offsets * chunk string values are pascal strings: - first byte is string length, limited to 255 chars - total length must be even, pad with a '\0' byte, max 256 - padding is not added to string length byte value * AIFF-C compression 4 char types & name (pascal) strings: - NONE "not compressed" big endian - sowt "not compressed" little endian - fl32 "32-bit floating point" big endian - FL32 "Float 32" big endian - the rest are not relevant to Pd... * limited to ~2 GB files as sizes are signed 32 bit ints this implementation: * supports AIFF and AIFF-C * implicitly writes AIFF-C header for 32 bit float (see below) * implements chunks: common, data, version (AIFF-C) * ignores chunks: marker, instrument, comment, name, author, copyright, annotation, audio recording, MIDI data, application, ID3 * assumes common chunk is always before sound data chunk * ignores any chunks after finding the sound data chunk * assumes there is always a sound data chunk * does not block align sound data * sample format: 16 and 24 bit lpcm, 32 bit float, no 32 bit lpcm Pd versions < 0.51 did *not* read or write AIFF files with a 32 bit float sample format. */ /* explicit byte sizes, sizeof(struct) may return alignment-padded values */ #define AIFFCHUNKSIZE 8 /**< chunk header only */ #define AIFFHEADSIZE 12 /**< chunk header and file format only */ #define AIFFVERSIZE 12 /**< chunk header and data */ #define AIFFCOMMSIZE 26 /**< chunk header and data */ #define AIFFDATASIZE 16 /**< chunk header, offset, and block size data */ #define AIFFCVER1 0xA2805140 /**< 2726318400 decimal */ #define AIFFMAXBYTES 0x7fffffff /**< max signed 32 bit size */ /* compression string defines */ #define AIFF_NONE_STR "not compressed" #define AIFF_FL32_STR "32-bit floating point" #define AIFF_NONE_LEN 16 /**< 1 len byte + 15 bytes + 1 \0 pad byte */ #define AIFF_FL32_LEN 22 /**< 1 len byte + 22 bytes, no pad byte */ /** basic chunk header, 8 bytes */ typedef struct _chunk { char c_id[4]; /**< chunk id */ int32_t c_size; /**< chunk data length */ } t_chunk; /** file head container chunk, 12 bytes */ typedef struct _head { char h_id[4]; /**< chunk id "FORM" */ int32_t h_size; /**< chunk data length */ char h_formtype[4]; /**< format: "AIFF" or "AIFC" */ } t_head; /** common chunk, 26 (AIFF) or 30+ (AIFC) bytes note: sample frames is split to avoid struct alignment padding */ typedef struct _commchunk { char cc_id[4]; /**< chunk id "COMM" */ int32_t cc_size; /**< chunk data length */ uint16_t cc_nchannels; /**< number of channels */ uint8_t cc_nframes[4]; /**< # of sample frames, uint32_t */ uint16_t cc_bitspersample; /**< bits per sample */ uint8_t cc_samplerate[10]; /**< sample rate, 80-bit float! */ /* AIFF-C */ char cc_comptype[4]; /**< compression type */ char cc_compname[256]; /**< compression name, pascal str */ } t_commchunk; /** sound data chunk, min 16 bytes before data */ typedef struct _datachunk { char dc_id[4]; /**< chunk id "SSND" */ int32_t dc_size; /**< chunk data length */ uint32_t dc_offset; /**< additional offset in bytes */ uint32_t dc_block; /**< block size */ } t_datachunk; /** AIFF-C format version chunk, 12 bytes */ typedef struct _verchunk { char vc_id[4]; /**< chunk id "FVER" */ int32_t vc_size; /**< chunk data length, 4 bytes */ uint32_t vc_timestamp; /**< AIFF-C version timestamp */ } t_verchunk; /* ----- helpers ----- */ /** returns 1 if format requires AIFF-C */ static int aiff_isaiffc(const t_soundfile *sf) { return (!sf->sf_bigendian || sf->sf_bytespersample == 4); } /** pascal string to c string, max size 256, returns *total* pstring size */ static int aiff_getpstring(const char* pstring, char *cstring) { uint8_t len = (uint8_t)pstring[0]; memcpy(cstring, pstring + 1, len); cstring[len] = '\0'; len++; /* length byte */ if (len & 1) len++; /* pad byte */ return len; } /** c string to pascal string, max size 256, returns *total* pstring size */ static int aiff_setpstring(char *pstring, const char *cstring) { uint8_t len = strlen(cstring); pstring[0] = len; memcpy(pstring + 1, cstring, len); len++; /* length byte */ if (len & 1) { pstring[len] = '\0'; /* pad byte */ len++; } return len; } /** read byte buffer to unsigned 32 bit int */ static uint32_t aiff_get4(const uint8_t *src, int swap) { uint32_t uinttmp = 0; memcpy(&uinttmp, src, 4); return swap4(uinttmp, swap); } /** write unsigned 32 bit int to byte buffer */ static void aiff_set4(uint8_t* dst, uint32_t ui, int swap) { ui = swap4(ui, swap); memcpy(dst, &ui, 4); } /** read sample rate from comm chunk 80-bit AIFF-compatible number */ static double aiff_getsamplerate(const uint8_t *src, int swap) { unsigned char temp[10], *p = temp, exponent; unsigned long mantissa, last = 0; memcpy(temp, src, 10); swapstring4((char *)p + 2, swap); mantissa = (uint32_t) *((uint32_t *)(p + 2)); exponent = 30 - *(p + 1); while (exponent--) { last = mantissa; mantissa >>= 1; } if (last & 0x00000001) mantissa++; return mantissa; } /** write a sample rate to comm chunk 80-bit AIFF-compatible number */ static void aiff_setsamplerate(uint8_t *dst, double sr) { int exponent; double mantissa = frexp(sr, &exponent); unsigned long fixmantissa = ldexp(mantissa, 32); dst[0] = (exponent + 16382) >> 8; dst[1] = exponent + 16382; dst[2] = fixmantissa >> 24; dst[3] = fixmantissa >> 16; dst[4] = fixmantissa >> 8; dst[5] = fixmantissa; dst[6] = dst[7] = dst[8] = dst[9] = 0; } /** read first chunk, returns filled chunk and offset on success or -1 */ static off_t aiff_firstchunk(const t_soundfile *sf, t_chunk *chunk) { if (fd_read(sf->sf_fd, AIFFHEADSIZE, (char *)chunk, AIFFCHUNKSIZE) < AIFFCHUNKSIZE) return -1; return AIFFHEADSIZE; } /** read next chunk, chunk should be filled when calling returns fills chunk offset on success or -1 */ static off_t aiff_nextchunk(const t_soundfile *sf, off_t offset, t_chunk *chunk) { int32_t chunksize = swap4s(chunk->c_size, !sys_isbigendian()); off_t seekto = offset + AIFFCHUNKSIZE + chunksize; if (seekto & 1) /* pad up to even number of bytes */ seekto++; if (fd_read(sf->sf_fd, seekto, (char *)chunk, AIFFCHUNKSIZE) < AIFFCHUNKSIZE) return -1; return seekto; } #ifdef DEBUG_SOUNDFILE /** post chunk info for debugging */ static void aiff_postchunk(const t_chunk *chunk, int swap) { post("%.4s %d", chunk->c_id, swap4s(chunk->c_size, swap)); } /** post head info for debugging */ static void aiff_posthead(const t_head *head, int swap) { aiff_postchunk((const t_chunk *)head, swap); post(" %.4s", head->h_formtype); } /** post comm info for debugging */ static void aiff_postcomm(const t_commchunk *comm, int isaiffc, int swap) { aiff_postchunk((const t_chunk *)comm, swap); post(" channels %u", swap2(comm->cc_nchannels, swap)); post(" frames %u", aiff_get4(comm->cc_nframes, swap)); post(" bits per sample %u", swap2(comm->cc_bitspersample, swap)); post(" sample rate %g", aiff_getsamplerate(comm->cc_samplerate, swap)); if (isaiffc) { /* handle pascal string */ char name[256]; aiff_getpstring(comm->cc_compname, name); post(" comp type %.4s", comm->cc_comptype); post(" comp name \"%s\"", name); } } /** post sata info for debugging */ static void aiff_postdata(const t_datachunk *data, int swap) { aiff_postchunk((const t_chunk *)data, swap); post(" offset %u", swap4(data->dc_offset, swap)); post(" block size %u", swap4(data->dc_block, swap)); } #endif /* DEBUG_SOUNDFILE */ /* ------------------------- AIFF ------------------------- */ static int aiff_isheader(const char *buf, size_t size) { if (size < 4) return 0; return !strncmp(buf, "FORM", 4); } /** loop through chunks to find comm and data */ static int aiff_readheader(t_soundfile *sf) { int nchannels = 1, bytespersample = 2, samplerate = 44100, bigendian = 1, swap = !sys_isbigendian(), isaiffc = 0, commfound = 0; off_t headersize = AIFFHEADSIZE; size_t bytelimit = AIFFMAXBYTES; union { char b_c[SFHDRBUFSIZE]; t_head b_head; t_chunk b_chunk; t_commchunk b_commchunk; t_verchunk b_verchunk; t_datachunk b_datachunk; } buf = {0}; t_head *head = &buf.b_head; t_chunk *chunk = &buf.b_chunk; /* file header */ if (fd_read(sf->sf_fd, 0, buf.b_c, headersize) < headersize) return 0; if (strncmp(head->h_formtype, "AIFF", 4)) { if (strncmp(head->h_formtype, "AIFC", 4)) return 0; isaiffc = 1; } #ifdef DEBUG_SOUNDFILE aiff_posthead(head, swap); #endif /* read chunks in loop until we find the sound data chunk */ if ((headersize = aiff_firstchunk(sf, chunk)) == -1) return 0; while (1) { int32_t chunksize = swap4s(chunk->c_size, swap); /* post("chunk %.4s seek %d", chunk->c_id, seekto); */ if (!strncmp(chunk->c_id, "FVER", 4)) { /* AIFF-C format version chunk */ if (!isaiffc) { errno = SOUNDFILE_ERRMALFORMED; return 0; } if (fd_read(sf->sf_fd, headersize + AIFFCHUNKSIZE, buf.b_c + AIFFCHUNKSIZE, chunksize) < chunksize) return 0; if (swap4(buf.b_verchunk.vc_timestamp, swap) != AIFFCVER1) { errno = SOUNDFILE_ERRVERSION; return 0; } } else if (!strncmp(chunk->c_id, "COMM", 4)) { /* common chunk */ int bitspersample, isfloat = 0; t_commchunk *comm = &buf.b_commchunk; if (fd_read(sf->sf_fd, headersize + AIFFCHUNKSIZE, buf.b_c + AIFFCHUNKSIZE, chunksize) < chunksize) return 0; #ifdef DEBUG_SOUNDFILE aiff_postcomm(comm, isaiffc, swap); #endif nchannels = swap2(comm->cc_nchannels, swap); bitspersample = swap2(comm->cc_bitspersample, swap); switch (bitspersample) { case 16: bytespersample = 2; break; case 24: bytespersample = 3; break; case 32: bytespersample = 4; break; default: { errno = SOUNDFILE_ERRSAMPLEFMT; return 0; } } samplerate = aiff_getsamplerate(comm->cc_samplerate, swap); if (isaiffc) { if (!strncmp(comm->cc_comptype, "NONE", 4)) { /* big endian */ } else if (!strncmp(comm->cc_comptype, "sowt", 4)) { /* little endian */ bigendian = 0; } else if (!strncmp(comm->cc_comptype, "fl32", 4) || !strncmp(comm->cc_comptype, "FL32", 4)) { if (bytespersample != 4) { errno = SOUNDFILE_ERRMALFORMED; return 0; } isfloat = 1; } else { errno = SOUNDFILE_ERRSAMPLEFMT; return 0; } } if (bytespersample == 4 && !isfloat) { /* 32 bit int */ errno = SOUNDFILE_ERRSAMPLEFMT; return 0; } commfound = 1; } else if (!strncmp(chunk->c_id, "SSND", 4)) { /* uncompressed sound data chunk */ #ifdef DEBUG_SOUNDFILE t_datachunk *data = &buf.b_datachunk; if (fd_read(sf->sf_fd, headersize + AIFFCHUNKSIZE, buf.b_c + AIFFCHUNKSIZE, 8) < 8) return 0; aiff_postdata(data, swap); #endif bytelimit = chunksize - 8; /* - offset and block */ headersize += AIFFDATASIZE; break; } else if (!strncmp(chunk->c_id, "CSND", 4)) { /* AIFF-C compressed sound data chunk */ errno = SOUNDFILE_ERRSAMPLEFMT; return 0; } #ifdef DEBUG_SOUNDFILE else { /* everything else */ aiff_postchunk(chunk, swap); } #endif if ((headersize = aiff_nextchunk(sf, headersize, chunk)) == -1) return 0; } if (!commfound) { errno = SOUNDFILE_ERRMALFORMED; return 0; } /* interpret data size from file size? this is not supported by the AIFF spec, but let's do it just in case */ if (bytelimit == AIFFMAXBYTES) { bytelimit = lseek(sf->sf_fd, 0, SEEK_END) - headersize; bytelimit = AIFFMAXBYTES; } /* copy sample format back to caller */ sf->sf_samplerate = samplerate; sf->sf_nchannels = nchannels; sf->sf_bytespersample = bytespersample; sf->sf_headersize = headersize; sf->sf_bytelimit = bytelimit; sf->sf_bigendian = bigendian; sf->sf_bytesperframe = nchannels * bytespersample; return 1; } /** write basic header with order: head [ver] comm data */ static int aiff_writeheader(t_soundfile *sf, size_t nframes) { int isaiffc = aiff_isaiffc(sf), swap = !sys_isbigendian(); size_t commsize = AIFFCOMMSIZE, datasize = nframes * sf->sf_bytesperframe; off_t headersize = 0; ssize_t byteswritten = 0; char buf[SFHDRBUFSIZE] = {0}; t_head head = {"FORM", 0, "AIFF"}; t_commchunk comm = { "COMM", swap4s(18, swap), swap2(sf->sf_nchannels, swap), /* channels */ {0}, /* sample frames */ swap2(sf->sf_bytespersample / 8, swap), /* bits per sample */ {0}, /* sample rate */ {0}, {0} /* comp info */ }; t_datachunk data = {"SSND", swap4s(8, swap), 0, 0}; /* file header */ if (isaiffc) strncpy(head.h_formtype, "AIFC", 4); memcpy(buf + headersize, &head, AIFFHEADSIZE); headersize += AIFFHEADSIZE; /* format ver chunk */ if (isaiffc) { t_verchunk ver = { "FVER", swap4s(4, swap), swap4(AIFFCVER1, swap) /* version timestamp */ }; memcpy(buf + headersize, &ver, AIFFVERSIZE); headersize += AIFFVERSIZE; } /* comm chunk */ comm.cc_nchannels = swap2(sf->sf_nchannels, swap); aiff_set4(comm.cc_nframes, nframes, swap); comm.cc_bitspersample = swap2(8 * sf->sf_bytespersample, swap); aiff_setsamplerate(comm.cc_samplerate, sf->sf_samplerate); if (isaiffc) { /* AIFF-C compression info */ if (sf->sf_bytespersample == 4) { strncpy(comm.cc_comptype, "fl32", 4); commsize += 4 + aiff_setpstring(comm.cc_compname, AIFF_FL32_STR); } else { strncpy(comm.cc_comptype, (sf->sf_bigendian ? "NONE" : "sowt"), 4); commsize += 4 + aiff_setpstring(comm.cc_compname, AIFF_NONE_STR); } } comm.cc_size = swap4(commsize - AIFFCHUNKSIZE, swap); memcpy(buf + headersize, &comm, commsize); headersize += commsize; /* data chunk (+ offset & block) */ data.dc_size = swap4((uint32_t)(datasize + 8), swap); memcpy(buf + headersize, &data, AIFFDATASIZE); headersize += AIFFDATASIZE; /* update file header chunk size (- chunk header) */ head.h_size = swap4s((int32_t)(headersize + datasize - 8), swap); memcpy(buf + 4, &head.h_size, 4); #ifdef DEBUG_SOUNDFILE aiff_posthead(&head, swap); aiff_postcomm(&comm, isaiffc, swap); aiff_postdata(&data, swap); #endif byteswritten = fd_write(sf->sf_fd, 0, buf, headersize); return (byteswritten < headersize ? -1 : byteswritten); } /** assumes chunk order: * AIFF : head comm data * AIFF-C: head version comm data, comm chunk size variable due to str */ static int aiff_updateheader(t_soundfile *sf, size_t nframes) { int isaiffc = aiff_isaiffc(sf), swap = !sys_isbigendian(); size_t datasize = nframes * sf->sf_bytesperframe, headersize = AIFFHEADSIZE, commsize = AIFFCOMMSIZE; uint32_t uinttmp; int32_t inttmp; if (isaiffc) { /* AIFF-C compression info */ if (sf->sf_bytespersample == 4) commsize += 4 + AIFF_FL32_LEN; else commsize += 4 + AIFF_NONE_LEN; headersize += AIFFVERSIZE; } /* num frames */ uinttmp = swap4((uint32_t)nframes, swap); if (fd_write(sf->sf_fd, headersize + 10, &uinttmp, 4) < 4) return 0; headersize += commsize; /* data chunk size (+ offset & block) */ inttmp = swap4s((int32_t)datasize + 8, swap); if (fd_write(sf->sf_fd, headersize + 4, &inttmp, 4) < 4) return 0; headersize += AIFFDATASIZE; /* file header chunk size (- chunk header) */ inttmp = swap4s((int32_t)(headersize + datasize - 8), swap); if (fd_write(sf->sf_fd, 4, &inttmp, 4) < 4) return 0; #ifdef DEBUG_SOUNDFILE post("FORM %d", headersize + datasize - 8); post(" %s", (aiff_isaiffc(sf) ? "AIFC" : "AIFF")); post("COMM %d", commsize); post(" frames %d", nframes); post("SSND %d", datasize + 8); #endif return 1; } static int aiff_hasextension(const char *filename, size_t size) { int len = strnlen(filename, size); if (len >= 5 && (!strncmp(filename + (len - 4), ".aif", 4) || !strncmp(filename + (len - 4), ".AIF", 4))) return 1; if (len >= 6 && (!strncmp(filename + (len - 5), ".aiff", 5) || !strncmp(filename + (len - 5), ".aifc", 5) || !strncmp(filename + (len - 5), ".AIFF", 5) || !strncmp(filename + (len - 5), ".AIFC", 5))) return 1; return 0; } static int aiff_addextension(char *filename, size_t size) { int len = strnlen(filename, size); if (len + 4 >= size) return 0; strcpy(filename + len, ".aif"); return 1; } /* default to big endian unless overridden */ static int aiff_endianness(int endianness) { if (endianness == 0) return 0; return 1; } /* ------------------------- setup routine ------------------------ */ static const t_soundfile_type aiff = { "aiff", AIFFHEADSIZE + AIFFCOMMSIZE + AIFFDATASIZE, aiff_isheader, aiff_readheader, aiff_writeheader, aiff_updateheader, aiff_hasextension, aiff_addextension, aiff_endianness }; void soundfile_aiff_setup( void) { soundfile_addtype(&aiff); } ================================================ FILE: libs/libpd/pure-data/src/d_soundfile_caf.c ================================================ /* Copyright (c) 2020 Dan Wilcox. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* ref: https://developer.apple.com/library/archive/documentation/MusicAudio/Reference/CAFSpec/CAF_spec/CAF_spec.html */ #include "d_soundfile.h" #ifndef _MSC_VER #include #endif /* CAF (Core Audio Format) * RIFF variant with sections split into data "chunks" * chunk sizes do not include the chunk id or size (- 12) * chunk data is big-endian * sound data can be big or little endian (set in desc fmtflags) * header is immediately followed by description chunk * data chunk: - size > 0: can be anywhere after the description chunk - size -1: last chunk in the file, size = file size - (data chunk pos + 12) this implementation: * supports CAF version 1 only * implements chunks: description, data * ignores chunks: packet table, magic cookie, strings, marker, region, instrument, MIDI, overview, peak, edit comments, information, unique material identifier, user-defined * ignores any chunks after finding the data chunk * sample format: 16 and 24 bit lpcm, 32 bit float, no 32 bit lpcm Pd versions < 0.51 did *not* read or write CAF files. */ /* explicit byte sizes, sizeof(struct) can return alignment padded values */ #define CAFHEADSIZE 8 /**< chunk header only */ #define CAFCHUNKSIZE 12 /**< chunk header and format */ #define CAFDESCSIZE 44 /**< chunk header and data */ #define CAFDATASIZE 16 /**< chunk header and edit count data */ #define CAFMAXBYTES SFMAXBYTES /** max signed 64 bit size */ #define CAF_UNKNOWN_SIZE 0xffffffffffffffff /** aka -1 */ /** Apple-style description format flags */ enum { kCAFLinearPCMFormatFlagIsFloat = (1L << 0), kCAFLinearPCMFormatFlagIsLittleEndian = (1L << 1) }; /** basic chunk header, 12 bytes note: size is split to avoid struct alignment padding */ typedef struct _chunk { char c_id[4]; /**< chunk id */ uint8_t c_size[8]; /**< chunk data length, int64_t */ } t_chunk; /** file head container chunk, 8 bytes */ typedef struct _head { char h_id[4]; /**< file id "caff" */ uint16_t h_version; /**< file version, probably 1 */ uint16_t h_flags; /**< format flags, 0 for CAF v1 */ } t_head; /** description chunk, 44 bytes note: size and samplerate are split to avoid struct alignment padding */ typedef struct _descchunk { char ds_id[4]; /**< chunk id "desc" */ uint8_t ds_size[8]; /**< chunk data length, int64_t */ uint8_t ds_samplerate[8]; /**< sample rate, double */ char ds_fmtid[4]; /**< format id, 4 letter char code */ uint32_t ds_fmtflags; /**< format flags, set 0 for "none" */ uint32_t ds_bytesperpacket; /**< bytes per packet */ uint32_t ds_framesperpacket; /**< uncompressed: 1 pckt = 1 frame */ uint32_t ds_nchannels; /**< number of channels */ uint32_t ds_bitsperchannel; /**< bits per chan in 1 sample frame */ } t_descchunk; /** data chunk, min 16 bytes note: size is split to avoid struct alignment padding */ typedef struct _datachunk { char dc_id[4]; /**< chunk id "data" */ uint8_t dc_size[8]; /**< chunk data length, int64_t */ uint32_t dc_editcount; /**< edit count, set 0 for none */ } t_datachunk; /* ----- helpers ----- */ static int64_t caf_getchunksize(const t_chunk *chunk, int swap) { int64_t size = 0; memcpy(&size, chunk->c_size, 8); return swap8s(size, swap); } static void caf_setchunksize(t_chunk *chunk, int64_t size, int swap) { size = swap8s(size, swap); memcpy(chunk->c_size, &size, 8); } static double caf_getsamplerate(const t_descchunk *desc, int swap) { double sr = 0; memcpy(&sr, desc->ds_samplerate, 8); swapstring8((char *)&sr, swap); return sr; } static void caf_setsamplerate(t_descchunk *desc, double sr, int swap) { memcpy(desc->ds_samplerate, &sr, 8); swapstring8((char *)desc->ds_samplerate, swap); } /** read first chunk, returns filled chunk and offset on success or -1 */ static off_t caf_firstchunk(const t_soundfile *sf, t_chunk *chunk) { if (fd_read(sf->sf_fd, CAFHEADSIZE, (char *)chunk, CAFCHUNKSIZE) < CAFCHUNKSIZE) return -1; return CAFHEADSIZE; } /** read next chunk, chunk should be filled when calling returns fills chunk offset on success or -1 */ static off_t caf_nextchunk(const t_soundfile *sf, off_t offset, t_chunk *chunk) { int64_t chunksize = caf_getchunksize(chunk, !sys_isbigendian()); off_t seekto = offset + CAFCHUNKSIZE + chunksize; if (seekto & 1) /* pad up to even number of bytes */ seekto++; if (fd_read(sf->sf_fd, seekto, (char *)chunk, CAFCHUNKSIZE) < CAFCHUNKSIZE) return -1; return seekto; } #ifdef DEBUG_SOUNDFILE /** post head info for debugging */ static void caf_posthead(const t_head *head, int swap) { post("%.4s", head->h_id); post(" version %d", swap2(head->h_version, swap)); post(" flags %d", swap2(head->h_flags, swap)); } /** post chunk info for debugging */ static void caf_postchunk(const t_chunk *chunk, int swap) { post("%.4s %" PRId64, chunk->c_id, caf_getchunksize(chunk, swap)); } /** post desc info for debugging */ static void caf_postdesc(const t_descchunk *desc, int swap) { uint32_t fmtflags = swap4(desc->ds_fmtflags, swap); caf_postchunk((t_chunk *)desc, swap); post(" sample rate %g", caf_getsamplerate(desc, swap)); post(" fmt id %.4s", desc->ds_fmtid); post(" fmt flags %d (%s %s)", fmtflags, (fmtflags & kCAFLinearPCMFormatFlagIsLittleEndian ? "little" : "big"), (fmtflags & kCAFLinearPCMFormatFlagIsFloat ? "float" : "int")); post(" bytes per packet %d", swap4(desc->ds_bytesperpacket, swap)); post(" frames per packet %d", swap4(desc->ds_framesperpacket, swap)); post(" channels %d", swap4(desc->ds_nchannels, swap)); post(" bits per channel %d", swap4(desc->ds_bitsperchannel, swap)); } /** post data header info for debugging, currently edit count only */ static void caf_postdata(const t_datachunk *data, int swap) { caf_postchunk((t_chunk *)data, swap); post(" edit count %d", swap4(data->dc_editcount, swap)); } #endif /* DEBUG_SOUNDFILE */ /* ------------------------- CAF -------------------------- */ static int caf_isheader(const char *buf, size_t size) { if (size < 4) return 0; return !strncmp(buf, "caff", 4); } static int caf_readheader(t_soundfile *sf) { int nchannels = 1, bytespersample = 2, samplerate = 44100, bigendian = 1, fmtflags, swap = !sys_isbigendian(); off_t headersize = CAFHEADSIZE + CAFDESCSIZE; ssize_t bytelimit = CAFMAXBYTES; union { char b_c[SFHDRBUFSIZE]; t_head b_head; t_chunk b_chunk; t_descchunk b_descchunk; t_datachunk b_datachunk; } buf = {0}; t_head *head = &buf.b_head; t_chunk *chunk = &buf.b_chunk; t_descchunk *desc = &buf.b_descchunk; /* file header */ if (fd_read(sf->sf_fd, 0, buf.b_c, headersize) < headersize) return 0; if (strncmp(head->h_id, "caff", 4)) return 0; if (swap2(head->h_version, swap) != 1) { errno = SOUNDFILE_ERRVERSION; return 0; } if (swap2(head->h_flags, swap) != 0) { /* current spec says these should be empty */ errno = SOUNDFILE_ERRVERSION; return 0; } #ifdef DEBUG_SOUNDFILE caf_posthead(head, swap); #endif /* copy the first chunk header to beginning of buffer, use memmove as src & dst are the same */ memmove(buf.b_c, buf.b_c + CAFHEADSIZE, CAFDESCSIZE); headersize = CAFHEADSIZE; /* first chunk must be description */ if (strncmp(desc->ds_id, "desc", 4)) return 0; #ifdef DEBUG_SOUNDFILE caf_postdesc(desc, swap); #endif if (strncmp(desc->ds_fmtid, "lpcm", 4)) { errno = SOUNDFILE_ERRSAMPLEFMT; return 0; } nchannels = swap4(desc->ds_nchannels, swap); fmtflags = swap4(desc->ds_fmtflags, swap); bytespersample = swap4(desc->ds_bitsperchannel, swap) / 8; switch (bytespersample) { case 2: case 3: case 4: break; default: errno = SOUNDFILE_ERRSAMPLEFMT; return 0; } if (bytespersample == 4 && !(fmtflags & kCAFLinearPCMFormatFlagIsFloat)) { errno = SOUNDFILE_ERRSAMPLEFMT; return 0; } bigendian = !(fmtflags & kCAFLinearPCMFormatFlagIsLittleEndian); samplerate = caf_getsamplerate(desc, swap); /* read chunks in loop until we find the sound data chunk */ if ((headersize = caf_nextchunk(sf, headersize, chunk)) == -1) return 0; while (1) { int64_t chunksize = caf_getchunksize(chunk, swap); off_t seekto = headersize + CAFCHUNKSIZE + chunksize, seekout; if (seekto & 1) /* pad up to even number of bytes */ seekto++; /* post("chunk %.4s seek %d", chunk->c_id, seekto); */ if (!strncmp(chunk->c_id, "data", 4)) { /* sound data chunk */ #ifdef DEBUG_SOUNDFILE t_datachunk *data = &buf.b_datachunk; if (fd_read(sf->sf_fd, headersize + CAFCHUNKSIZE, buf.b_c + CAFCHUNKSIZE, 4) < 4) return 0; caf_postdata(data, swap); #endif headersize += CAFDATASIZE; if (chunksize == CAF_UNKNOWN_SIZE) { /* interpret data size from file size */ bytelimit = lseek(sf->sf_fd, 0, SEEK_END) - headersize; if (bytelimit > CAFMAXBYTES || bytelimit < 0) bytelimit = CAFMAXBYTES; } else bytelimit = chunksize - 4; /* - edit count */ break; } #ifdef DEBUG_SOUNDFILE else { /* everything else */ caf_postchunk(chunk, swap); } #endif if ((headersize = caf_nextchunk(sf, headersize, chunk)) == -1) return 0; } /* copy sample format back to caller */ sf->sf_samplerate = samplerate; sf->sf_nchannels = nchannels; sf->sf_bytespersample = bytespersample; sf->sf_headersize = headersize; sf->sf_bytelimit = bytelimit; sf->sf_bigendian = bigendian; sf->sf_bytesperframe = nchannels * bytespersample; return 1; } static int caf_writeheader(t_soundfile *sf, size_t nframes) { int swap = !sys_isbigendian(); size_t datasize = (nframes > 0 ? nframes * sf->sf_bytesperframe : CAF_UNKNOWN_SIZE); off_t headersize = 0; ssize_t byteswritten = 0; uint32_t uinttmp = 0; char buf[SFHDRBUFSIZE] = {0}; t_head head = {"caff", swap2(1, swap), 0}; t_descchunk desc = {"desc", {0}, {0}}; t_datachunk data = {"data", {0}, 0}; /* file header */ memcpy(buf + headersize, &head, CAFHEADSIZE); headersize += CAFHEADSIZE; /* description chunk */ caf_setchunksize((t_chunk *)&desc, CAFDESCSIZE - CAFCHUNKSIZE, swap); caf_setsamplerate(&desc, sf->sf_samplerate, swap); strncpy(desc.ds_fmtid, "lpcm", 4); if (sf->sf_bytespersample == 4) uinttmp |= kCAFLinearPCMFormatFlagIsFloat; if (!sf->sf_bigendian) uinttmp |= kCAFLinearPCMFormatFlagIsLittleEndian; desc.ds_fmtflags = swap4(uinttmp, swap); desc.ds_bytesperpacket = swap4(sf->sf_bytesperframe, swap); desc.ds_framesperpacket = swap4(1, swap); desc.ds_nchannels = swap4(sf->sf_nchannels, swap); desc.ds_bitsperchannel = swap4(sf->sf_bytespersample * 8, swap); memcpy(buf + headersize, &desc, CAFDESCSIZE); headersize += CAFDESCSIZE; /* data chunk (+ edit count) */ caf_setchunksize((t_chunk *)&data, datasize + 4, swap); memcpy(buf + headersize, &data, CAFDATASIZE); headersize += CAFDATASIZE; #ifdef DEBUG_SOUNDFILE caf_posthead(&head, swap); caf_postdesc(&desc, swap); caf_postdata(&data, swap); #endif byteswritten = fd_write(sf->sf_fd, 0, buf, headersize); return (byteswritten < headersize ? -1 : byteswritten); } /** assumes chunk order: head desc data */ static int caf_updateheader(t_soundfile *sf, size_t nframes) { int swap = !sys_isbigendian(); int64_t datasize = swap8s((nframes * sf->sf_bytesperframe) + 4, swap); /* data chunk size (+ edit count) */ if (fd_write(sf->sf_fd, CAFHEADSIZE + CAFDESCSIZE + 4, &datasize, 8) < 8) return 0; #ifdef DEBUG_SOUNDFILE post("caff"); post("data %" PRId64, swap8s(datasize, swap)); #endif return 1; } static int caf_hasextension(const char *filename, size_t size) { int len = strnlen(filename, size); if (len >= 5 && (!strncmp(filename + (len - 4), ".caf", 4) || !strncmp(filename + (len - 4), ".CAF", 4))) return 1; return 0; } static int caf_addextension(char *filename, size_t size) { int len = strnlen(filename, size); if (len + 4 >= size) return 0; strcpy(filename + len, ".caf"); return 1; } /* default to big endian if not specified */ static int caf_endianness(int endianness) { if (endianness == -1) return 1; return endianness; } /* ------------------------- setup routine ------------------------ */ static const t_soundfile_type caf = { "caf", CAFHEADSIZE + CAFDESCSIZE + CAFDATASIZE, caf_isheader, caf_readheader, caf_writeheader, caf_updateheader, caf_hasextension, caf_addextension, caf_endianness }; void soundfile_caf_setup( void) { soundfile_addtype(&caf); } ================================================ FILE: libs/libpd/pure-data/src/d_soundfile_next.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. Updated 2019 Dan Wilcox. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* refs: http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/AU/AU.html https://pubs.opengroup.org/external/auformat.html http://sox.sourceforge.net/AudioFormats-11.html#ss11.2 http://soundfile.sapp.org/doc/NextFormat */ #include "d_soundfile.h" /* NeXTStep/Sun sound * simple header followed by sound data * header and sound data can be big or little endian, depending on id: - ".snd" big endian - "dns." little endian * sound data length can be set to an "unknown size", in which case the actual length is file size - sound data offset * info string after 24 byte header is optional - null terminated - string len is sound data len - 24, min len is 4 * can be headerless, in which case is 8 bit u-law at 8000 Hz * limited to ~4 GB files as sizes are unsigned 32 bit ints this implementation: * does not support headerless files * supports big and little endian, system endianness used by default * sets a default info string: "Pd " * tries to set sound data length, otherwise falls back to "unknown size" * sample format: 16 and 24 bit lpcm, 32 bit float, no 32 bit lpcm Pd versions < 0.51 did *not* write the actual data chunk size when updating the header, but set "unknown size" instead. */ /* explicit byte sizes, sizeof(struct) may return alignment padded values */ #define NEXTHEADSIZE 28 /**< min valid header size + info string */ #define NEXTMAXBYTES 0xffffffff /**< max unsigned 32 bit size */ #define NEXT_FORMAT_LINEAR_16 3 /**< 16 bit int */ #define NEXT_FORMAT_LINEAR_24 4 /**< 24 bit int */ #define NEXT_FORMAT_FLOAT 6 /**< 32 bit float */ #define NEXT_UNKNOWN_SIZE 0xffffffff /** aka -1 */ typedef struct _nextstep { char ns_id[4]; /**< magic number; ".snd" (big endian) or "dns." */ uint32_t ns_onset; /**< offset to sound data in bytes */ uint32_t ns_length; /**< length of sound data in bytes */ uint32_t ns_format; /**< sound data format code; see below */ uint32_t ns_samplerate; /**< sample rate */ uint32_t ns_nchannels; /**< number of channels */ char ns_info[4]; /**< info string, minimum 4 bytes up to onset */ } t_nextstep; /* ----- helpers ----- */ /** returns 1 if big endian, 0 if little endian, or -1 for bad header */ static int next_isbigendian(const t_nextstep *next) { if (!strncmp(next->ns_id, ".snd", 4)) return 1; else if(!strncmp(next->ns_id, "dns.", 4)) return 0; return -1; } #ifdef DEBUG_SOUNDFILE /** post head info for debugging */ static void next_posthead(const t_nextstep *next, int swap) { uint32_t onset = swap4(next->ns_onset, swap), format = swap4(next->ns_format, swap), datasize = swap4(next->ns_length, swap); post("next %.4s (%s)", next->ns_id, (next_isbigendian(next) ? "big" : "little")); post(" data onset %d", onset); if (datasize == NEXT_UNKNOWN_SIZE) post(" data length -1"); else post(" data length %d", datasize); switch (format) { case NEXT_FORMAT_LINEAR_16: post(" format %d (16 bit int)", format); break; case NEXT_FORMAT_LINEAR_24: post(" format %d (24 bit int)", format); break; case NEXT_FORMAT_FLOAT: post(" format %d (32 bit float)", format); break; default: post(" format %d (unsupported)", format); break; } post(" sample rate %d", swap4(next->ns_samplerate, swap)); post(" channels %d", swap4(next->ns_nchannels, swap)); post(" info \"%.4s%s\"", next->ns_info, (onset > NEXTHEADSIZE ? "..." : "")); } #endif /* DEBUG_SOUNDFILE */ /* ------------------------- NEXT ------------------------- */ static int next_isheader(const char *buf, size_t size) { if (size < 4) return 0; if (!strncmp(buf, ".snd", 4) || !strncmp(buf, "dns.", 4)) return 1; return 0; } static int next_readheader(t_soundfile *sf) { int format, bytespersample, bigendian = 1, swap = 0; off_t headersize = NEXTHEADSIZE; size_t bytelimit = NEXTMAXBYTES; union { char b_c[SFHDRBUFSIZE]; t_nextstep b_nextstep; } buf = {0}; t_nextstep *next = &buf.b_nextstep; if (fd_read(sf->sf_fd, 0, buf.b_c, headersize) < headersize) return 0; bigendian = next_isbigendian(next); if (bigendian < 0) return 0; swap = (bigendian != sys_isbigendian()); headersize = swap4(next->ns_onset, swap); if (headersize < NEXTHEADSIZE - 4) /* min valid header w/o info string */ return 0; bytelimit = swap4(next->ns_length, swap); if (bytelimit == NEXT_UNKNOWN_SIZE) { /* interpret data size from file size */ bytelimit = lseek(sf->sf_fd, 0, SEEK_END) - headersize; if (bytelimit > NEXTMAXBYTES || bytelimit < 0) bytelimit = NEXTMAXBYTES; } format = swap4(next->ns_format, swap); switch (format) { case NEXT_FORMAT_LINEAR_16: bytespersample = 2; break; case NEXT_FORMAT_LINEAR_24: bytespersample = 3; break; case NEXT_FORMAT_FLOAT: bytespersample = 4; break; default: errno = SOUNDFILE_ERRSAMPLEFMT; return 0; } #ifdef DEBUG_SOUNDFILE next_posthead(next, swap); #endif /* copy sample format back to caller */ sf->sf_samplerate = swap4(next->ns_samplerate, swap); sf->sf_nchannels = swap4(next->ns_nchannels, swap); sf->sf_bytespersample = bytespersample; sf->sf_headersize = headersize; sf->sf_bytelimit = bytelimit; sf->sf_bigendian = bigendian; sf->sf_bytesperframe = sf->sf_nchannels * bytespersample; return 1; } static int next_writeheader(t_soundfile *sf, size_t nframes) { int swap = soundfile_needsbyteswap(sf); size_t datasize = (nframes > 0 ? nframes * sf->sf_bytesperframe : NEXT_UNKNOWN_SIZE); off_t headersize = NEXTHEADSIZE; ssize_t byteswritten = 0; t_nextstep next = { ".snd", /* id */ swap4((uint32_t)headersize, swap), /* data onset */ swap4((uint32_t)datasize, swap), /* data length */ 0, /* format */ swap4((uint32_t)sf->sf_samplerate, swap), /* sample rate */ swap4((uint32_t)sf->sf_nchannels, swap), /* channels */ "Pd " /* info string */ }; if (!sf->sf_bigendian) swapstring4(next.ns_id, 1); switch (sf->sf_bytespersample) { case 2: next.ns_format = swap4(NEXT_FORMAT_LINEAR_16, swap); break; case 3: next.ns_format = swap4(NEXT_FORMAT_LINEAR_24, swap); break; case 4: next.ns_format = swap4(NEXT_FORMAT_FLOAT, swap); break; default: /* unsupported format */ return 0; } #ifdef DEBUG_SOUNDFILE next_posthead(&next, swap); #endif byteswritten = fd_write(sf->sf_fd, 0, &next, headersize); return (byteswritten < headersize ? -1 : byteswritten); } /** the data length is limited to 4 bytes, so if the size is too large, do it the lazy way: just set the size field to "unknown size" */ static int next_updateheader(t_soundfile *sf, size_t nframes) { int swap = soundfile_needsbyteswap(sf); size_t datasize = nframes * sf->sf_bytesperframe; if (datasize > NEXTMAXBYTES) datasize = NEXT_UNKNOWN_SIZE; datasize = swap4(datasize, swap); if (fd_write(sf->sf_fd, 8, &datasize, 4) < 4) return 0; #ifdef DEBUG_SOUNDFILE datasize = swap4(datasize, swap); post("next %.4s (%s)", (sf->sf_bigendian ? ".snd" : "dns."), (sf->sf_bigendian ? "big" : "little")); if (datasize == NEXT_UNKNOWN_SIZE) post(" data length -1"); else post(" data length %d", datasize); #endif return 1; } static int next_hasextension(const char *filename, size_t size) { int len = strnlen(filename, size); if (len >= 4 && (!strncmp(filename + (len - 3), ".au", 3) || !strncmp(filename + (len - 3), ".AU", 3))) return 1; if (len >= 5 && (!strncmp(filename + (len - 4), ".snd", 4) || !strncmp(filename + (len - 4), ".SND", 4))) return 1; return 0; } static int next_addextension(char *filename, size_t size) { int len = strnlen(filename, size); if (len + 4 >= size) return 0; strcpy(filename + len, ".snd"); return 1; } /* machine native if not specified */ static int next_endianness(int endianness) { if (endianness == -1) return sys_isbigendian(); return endianness; } /* ------------------------- setup routine ------------------------ */ t_soundfile_type next = { "next", NEXTHEADSIZE - 4, /* - info string */ next_isheader, next_readheader, next_writeheader, next_updateheader, next_hasextension, next_addextension, next_endianness }; void soundfile_next_setup( void) { soundfile_addtype(&next); } ================================================ FILE: libs/libpd/pure-data/src/d_soundfile_wave.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. Updated 2019 Dan Wilcox. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* ref: http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html */ #include "d_soundfile.h" /* WAVE (Waveform Audio File Format) * RIFF variant with sections split into data "chunks" * chunk sizes do not include the chunk id or size (- 8) * chunk and sound data are little endian * format and sound data chunks are required * format tags: - PCM linear PCM - FLOAT 32-bit float - EXT extended format -> described in subformat fields - the rest are not relevant to Pd... * extended format must be used when: - PCM data has more than 16 bits per sample - number of channels is more than 2 - mapping of channels to speakers is required (not relevant to Pd) * a fact chunk is required when the format is non-PCM * if sound data length is odd, a 0 pad byte should appended * limited to ~4 GB files as sizes are unsigned 32 bit ints * there are variants with 64-bit sizes (W64 and RF64) as well as extension formats which can split sound data across multiple files (BWF) this implementation: * supports basic and extended format chunks (WAVE Rev. 3) * implicitly writes extended format for 32 bit float (see below) * implements chunks: format, fact, sound data * ignores chunks: info, cset, cue, playlist, associated data, instrument, sample, display, junk, pad, time code, digitization time * assumes format chunk is always before sound data chunk * assumes there is only 1 sound data chunk * does not support 64-bit variants or BWF file-splitting * sample format: 16 and 24 bit lpcm, 32 bit float, no 32 bit lpcm Pd versions < 0.51 did *not* read or write extended format explicitly, but ignored the format chunk format tag and interpreted the sample type based on the bits per sample: 2 : int 16, 3 : int 24, 4 : float 32. This means files created by newer Pd versions are currently more or less compatible with older Pd versions. This may change if 32 bit int support is introduced. */ /* explicit byte sizes, sizeof(struct) may return alignment-padded values */ #define WAVECHUNKSIZE 8 /**< chunk header only */ #define WAVEHEADSIZE 12 /**< chunk header and file format only */ #define WAVEFORMATSIZE 24 /**< chunk header and data */ #define WAVEFACTSIZE 12 /**< chunk header and data */ #define WAVEMAXBYTES 0xffffffff /**< max unsigned 32 bit size */ #define WAVE_FORMAT_PCM 0x0001 /**< 16 or 24 bit int */ #define WAVE_FORMAT_FLOAT 0x0003 /**< 32 bit float */ #define WAVE_FORMAT_EXT 0xfffe /**< extended, see format chunk subformat */ /** extended format header and data length */ #define WAVE_EXT_SIZE 24 /** 14 byte extended subformat GUID */ #define WAVE_EXT_GUID "\x00\x00\x00\x00\x10\x00\x80\x00\x00\xAA\x00\x38\x9B\x71" /** basic chunk header, 8 bytes */ typedef struct _chunk { char c_id[4]; /**< data chunk id */ uint32_t c_size; /**< length of data chunk */ } t_chunk; /** file head container chunk, 12 bytes */ typedef struct _head { char h_id[4]; /**< chunk id "RIFF" */ uint32_t h_size; /**< chunk data length */ char h_formtype[4]; /**< format: "WAVE" */ } t_head; /** format chunk, 24 (basic) or 48 (extended) */ typedef struct _formatchunk { char fc_id[4]; /**< chunk id "fmt " */ uint32_t fc_size; /**< chunk data length */ uint16_t fc_fmttag; /**< format tag */ uint16_t fc_nchannels; /**< number of channels */ uint32_t fc_samplerate; /**< sample rate in hz */ uint32_t fc_bytespersecond; /**< average bytes per second */ uint16_t fc_blockalign; /**< number of bytes per frame */ uint16_t fc_bitspersample; /**< number of bits in a sample */ /* extended format */ uint16_t fc_extsize; /**< extended format info length */ uint16_t fc_validbitspersample; /**< number of valid bits */ uint32_t fc_channelmask; /**< speaker pos channel mask */ char fc_subformat[16]; /**< format tag is bytes 0 & 1 */ } t_formatchunk; /** fact chunk, 12 bytes */ typedef struct _factchunk { char fc_id[4]; /**< chunk id "fact" */ uint32_t fc_size; /**< chunk data length */ uint32_t fc_samplelength; /**< number of samples per channel */ } t_factchunk; /* ----- helpers ----- */ /** returns 1 if format requires extended format and fact chunk */ static int wave_isextended(const t_soundfile *sf) { return sf->sf_bytespersample == 4; } /** read first chunk, returns filled chunk and offset on success or -1 */ static off_t wave_firstchunk(const t_soundfile *sf, t_chunk *chunk) { if (fd_read(sf->sf_fd, WAVEHEADSIZE, (char *)chunk, WAVECHUNKSIZE) < WAVECHUNKSIZE) return -1; return WAVEHEADSIZE; } /** read next chunk, chunk should be filled when calling returns fills chunk offset on success or -1 */ static off_t wave_nextchunk(const t_soundfile *sf, off_t offset, t_chunk *chunk) { uint32_t chunksize = swap4(chunk->c_size, sys_isbigendian()); off_t seekto = offset + WAVECHUNKSIZE + chunksize; if (seekto & 1) /* pad up to even number of bytes */ seekto++; if (fd_read(sf->sf_fd, seekto, (char *)chunk, WAVECHUNKSIZE) < WAVECHUNKSIZE) return -1; return seekto; } #ifdef DEBUG_SOUNDFILE /** post chunk info for debugging */ static void wave_postchunk(const t_chunk *chunk, int swap) { post("%.4s %d", chunk->c_id, swap4s(chunk->c_size, swap)); } /** post head info for debugging */ static void wave_posthead(const t_head *head, int swap) { wave_postchunk((const t_chunk *)head, swap); post(" %.4s", head->h_formtype); } /** post format info for debugging */ static void wave_postformat(const t_formatchunk *format, int swap) { uint16_t formattag = swap2(format->fc_fmttag, swap); wave_postchunk((const t_chunk *)format, swap); switch (formattag) { case WAVE_FORMAT_PCM: post(" format %d (PCM)", formattag); break; case WAVE_FORMAT_FLOAT: post(" format %d (FLOAT)", formattag); break; case WAVE_FORMAT_EXT: post(" format %d (EXT)", formattag); break; default: post(" format %d (unsupported)", formattag); break; } post(" channels %d", swap2(format->fc_nchannels, swap)); post(" sample rate %d", swap4(format->fc_samplerate, swap)); post(" bytes per sec %d", swap4(format->fc_bytespersecond, swap)); post(" block align %d", swap2(format->fc_blockalign, swap)); post(" bits per sample %u", swap2(format->fc_bitspersample, swap)); if (formattag == WAVE_FORMAT_EXT) { formattag = swap2(*(uint16_t *)&format->fc_subformat, swap); post(" ext size %d", swap2(format->fc_extsize, swap)); post(" ext valid bits per sample %d", swap2(format->fc_validbitspersample, swap)); post(" ext channel mask 0x%04x", swap4(format->fc_channelmask, swap)); switch (formattag) { case WAVE_FORMAT_PCM: post(" ext format %d (PCM)", formattag); break; case WAVE_FORMAT_FLOAT: post(" ext format %d (FLOAT)", formattag); break; default: post(" ext format %d (unsupported)", formattag); break; } } } /** post fact info for debugging */ static void wave_postfact(const t_factchunk *fact, int swap) { wave_postchunk((const t_chunk *)fact, swap); post(" sample length %d", swap4(fact->fc_samplelength, swap)); } #endif /* DEBUG_SOUNDFILE */ /* ------------------------- WAVE ------------------------- */ static int wave_isheader(const char *buf, size_t size) { if (size < 4) return 0; return !strncmp(buf, "RIFF", 4); } static int wave_readheader(t_soundfile *sf) { int nchannels = 1, bytespersample = 2, samplerate = 44100, bigendian = 0, swap = (bigendian != sys_isbigendian()), formatfound = 1; off_t headersize = WAVEHEADSIZE; size_t bytelimit = WAVEMAXBYTES; union { char b_c[SFHDRBUFSIZE]; t_head b_head; t_chunk b_chunk; t_formatchunk b_formatchunk; t_factchunk b_factchunk; } buf = {0}; t_chunk *chunk = &buf.b_chunk; /* file header */ if (fd_read(sf->sf_fd, 0, buf.b_c, headersize) < headersize) return 0; if (strncmp(buf.b_c + 8, "WAVE", 4)) return 0; #ifdef DEBUG_SOUNDFILE wave_posthead(&buf.b_head, swap); #endif /* read chunks in loop until we find the sound data chunk */ if ((headersize = wave_firstchunk(sf, chunk)) == -1) return 0; while (1) { uint32_t chunksize = swap4(chunk->c_size, swap); /* post("chunk %.4s seek %d", chunk->c_id, seekto); */ if (!strncmp(chunk->c_id, "fmt ", 4)) { /* format chunk */ int formattag; t_formatchunk *format = &buf.b_formatchunk; if (fd_read(sf->sf_fd, headersize + 8, buf.b_c + 8, chunksize) < chunksize) return 0; #ifdef DEBUG_SOUNDFILE wave_postformat(format, swap); #endif nchannels = swap2(format->fc_nchannels, swap); samplerate = swap4(format->fc_samplerate, swap); formattag = swap2(format->fc_fmttag, swap); if (formattag == WAVE_FORMAT_EXT && chunksize == WAVEFORMATSIZE) { errno = SOUNDFILE_ERRSAMPLEFMT; return 0; } switch (formattag) { case WAVE_FORMAT_PCM: case WAVE_FORMAT_FLOAT: break; case WAVE_FORMAT_EXT: formattag = swap2(*(uint16_t *)&format->fc_subformat, swap); if (formattag == WAVE_FORMAT_PCM || formattag == WAVE_FORMAT_FLOAT) break; default: { errno = SOUNDFILE_ERRSAMPLEFMT; return 0; } } bytespersample = swap2(format->fc_bitspersample, swap) / 8; switch (bytespersample) { case 2: case 3: break; case 4: if (formattag == WAVE_FORMAT_FLOAT) /* 32 bit int? */ break; default: errno = SOUNDFILE_ERRSAMPLEFMT; return 0; } formatfound = 1; } else if(!strncmp(chunk->c_id, "data", 4)) { /* sound data chunk */ bytelimit = swap4(chunk->c_size, swap); headersize += WAVECHUNKSIZE; #ifdef DEBUG_SOUNDFILE wave_postchunk(chunk, swap); #endif break; } #ifdef DEBUG_SOUNDFILE else if (!strncmp(chunk->c_id, "fact", 4)) { /* extended format fact chunk */ wave_postfact(&buf.b_factchunk, swap); } else { /* everything else */ wave_postchunk(chunk, swap); } #endif if ((headersize = wave_nextchunk(sf, headersize, chunk)) == -1) return 0; } if (!formatfound) { errno = SOUNDFILE_ERRMALFORMED; return 0; } /* interpret data size from file size? */ if (bytelimit == WAVEMAXBYTES) { bytelimit = lseek(sf->sf_fd, 0, SEEK_END) - headersize; if (bytelimit > WAVEMAXBYTES || bytelimit < 0) bytelimit = WAVEMAXBYTES; } else if (bytelimit & 1) { /* the actual data chunk size is always even */ bytelimit++; } /* copy sample format back to caller */ sf->sf_samplerate = samplerate; sf->sf_nchannels = nchannels; sf->sf_bytespersample = bytespersample; sf->sf_headersize = headersize; sf->sf_bytelimit = bytelimit; sf->sf_bigendian = bigendian; sf->sf_bytesperframe = nchannels * bytespersample; return 1; } static int wave_writeheader(t_soundfile *sf, size_t nframes) { int isextended = wave_isextended(sf), swap = soundfile_needsbyteswap(sf); size_t formatsize = WAVEFORMATSIZE, datasize = nframes * sf->sf_bytesperframe; off_t headersize = 0; ssize_t byteswritten = 0; char buf[SFHDRBUFSIZE] = {0}; t_head head = {"RIFF", 0, "WAVE"}; t_formatchunk format = { "fmt ", swap4(16, swap), WAVE_FORMAT_PCM, /* format tag */ swap2((uint16_t)sf->sf_nchannels, swap), /* channels */ swap4((uint32_t)sf->sf_samplerate, swap), /* sample rate */ swap4((uint32_t)(sf->sf_samplerate * /* bytes per sec */ sf->sf_bytesperframe), swap), swap2((uint16_t)sf->sf_bytesperframe, swap), /* block align */ swap2((uint16_t)sf->sf_bytespersample * 8, swap), /* bits per sample */ 0 /* extended format */ }; t_chunk data = {"data", swap4((uint32_t)datasize, swap)}; /* file header */ memcpy(buf + headersize, &head, WAVEHEADSIZE); headersize += WAVEHEADSIZE; /* format chunk */ if (sf->sf_bytespersample == 4) format.fc_fmttag = swap2(WAVE_FORMAT_FLOAT, swap); if (isextended) { format.fc_extsize = swap2(WAVE_EXT_SIZE - 2, swap); /* - fmttag */ format.fc_validbitspersample = format.fc_bitspersample; format.fc_channelmask = 0; /* no specific speaker positions */ memcpy(format.fc_subformat, &format.fc_fmttag, 2); /* move tag */ memcpy(format.fc_subformat + 2, WAVE_EXT_GUID, 14); format.fc_fmttag = swap2(WAVE_FORMAT_EXT, swap); formatsize += WAVE_EXT_SIZE; } format.fc_size = swap4(formatsize - 8, swap); memcpy(buf + headersize, &format, formatsize); headersize += formatsize; /* fact chunk */ if (isextended) { t_factchunk fact = { "fact", swap4(4, swap), swap4((uint32_t)(sf->sf_nchannels * nframes), swap) }; memcpy(buf + headersize, &fact, WAVEFACTSIZE); headersize += WAVEFACTSIZE; } /* data chunk */ if (datasize & 1) { /* add pad byte */ data.c_size = swap4((uint32_t)datasize + 1, swap); } memcpy(buf + headersize, &data, WAVECHUNKSIZE); headersize += WAVECHUNKSIZE; /* update file header chunk size (- chunk header) */ head.h_size = swap4s((int32_t)(datasize + headersize - 8), swap); memcpy(buf + 4, &head.h_size, 4); #ifdef DEBUG_SOUNDFILE wave_posthead(&head, swap); wave_postformat(&format, swap); wave_postchunk(&data, swap); #endif byteswritten = fd_write(sf->sf_fd, 0, buf, headersize); return (byteswritten < headersize ? -1 : byteswritten); } /** assumes chunk order: * basic: head format data * extended: head format+ext fact data */ static int wave_updateheader(t_soundfile *sf, size_t nframes) { int isextended = wave_isextended(sf), swap = soundfile_needsbyteswap(sf); size_t datasize = nframes * sf->sf_bytesperframe, headersize = WAVEHEADSIZE + WAVEFORMATSIZE; int padbyte = (datasize & 1); uint32_t uinttmp; if (isextended) { headersize += WAVE_EXT_SIZE; /* fact chunk sample length */ uinttmp = swap4((uint32_t)(nframes * sf->sf_nchannels), swap); if (fd_write(sf->sf_fd, headersize + 8, &uinttmp, 4) < 4) return 0; headersize += WAVEFACTSIZE; } /* sound data chunk size */ datasize += padbyte; uinttmp = swap4((uint32_t)datasize, swap); if (fd_write(sf->sf_fd, headersize + 4, &uinttmp, 4) < 4) return 0; headersize += WAVECHUNKSIZE; /* add pad byte */ if (padbyte) { uinttmp = 0; if (fd_write(sf->sf_fd, headersize + datasize - 1, &uinttmp, 1) < 1) return 0; } /* file header chunk size (- chunk header) */ uinttmp = swap4((uint32_t)(headersize + datasize - 8), swap); if (fd_write(sf->sf_fd, 4, &uinttmp, 4) < 4) return 0; #ifdef DEBUG_SOUNDFILE post("RIFF %d", headersize + datasize - 8); post(" WAVE"); post("data %d", datasize); #endif return 1; } static int wave_hasextension(const char *filename, size_t size) { int len = strnlen(filename, size); if (len >= 5 && (!strncmp(filename + (len - 4), ".wav", 4) || !strncmp(filename + (len - 4), ".WAV", 4))) return 1; if (len >= 6 && (!strncmp(filename + (len - 5), ".wave", 5) || !strncmp(filename + (len - 5), ".WAVE", 5))) return 1; return 0; } static int wave_addextension(char *filename, size_t size) { int len = strnlen(filename, size); if (len + 4 >= size) return 0; strcpy(filename + len, ".wav"); return 1; } /* force little endian */ static int wave_endianness(int endianness) { return 0; } /* ------------------------- setup routine ------------------------ */ t_soundfile_type wave = { "wave", WAVEHEADSIZE + WAVEFORMATSIZE + WAVECHUNKSIZE, wave_isheader, wave_readheader, wave_writeheader, wave_updateheader, wave_hasextension, wave_addextension, wave_endianness }; void soundfile_wave_setup( void) { soundfile_addtype(&wave); } ================================================ FILE: libs/libpd/pure-data/src/d_ugen.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* These routines build a copy of the DSP portion of a graph, which is then sorted into a linear list of DSP operations which are added to the DSP duty cycle called by the scheduler. Once that's been done, we delete the copy. The DSP objects are represented by "ugenbox" structures which are parallel to the DSP objects in the graph and have vectors of siginlets and sigoutlets which record their interconnections. */ #include "m_pd.h" #include "m_imp.h" #include "g_canvas.h" #include #define DEFDACBLKSIZE 64 /* from s_stuff.h - LATER make this dynamic */ extern t_class *vinlet_class, *voutlet_class, *canvas_class, *text_class; EXTERN_STRUCT _vinlet; EXTERN_STRUCT _voutlet; void vinlet_dspprolog(struct _vinlet *x, t_signal **parentsigs, int myvecsize, int phase, int period, int frequency, int downsample, int upsample, int reblock, int switched); void voutlet_dspprolog(struct _voutlet *x, t_signal **parentsigs, int myvecsize, int phase, int period, int frequency, int downsample, int upsample, int reblock, int switched); void voutlet_dspepilog(struct _voutlet *x, t_signal **parentsigs, int myvecsize, int phase, int period, int frequency, int downsample, int upsample, int reblock, int switched); struct _instanceugen { t_int *u_dspchain; /* DSP chain */ int u_dspchainsize; /* number of elements in DSP chain */ t_signal *u_signals; /* list of signals used by DSP chain */ int u_sortno; /* number of DSP sortings so far */ /* list of signals which can be reused, sorted by buffer size */ t_signal *u_freelist[MAXLOGSIG+1]; /* list of reusable "borrowed" signals (which don't own sample buffers) */ t_signal *u_freeborrowed; int u_phase; int u_loud; struct _dspcontext *u_context; }; #define THIS (pd_this->pd_ugen) void d_ugen_newpdinstance(void) { THIS = getbytes(sizeof(*THIS)); THIS->u_dspchain = 0; THIS->u_dspchainsize = 0; THIS->u_signals = 0; } void d_ugen_freepdinstance(void) { freebytes(THIS, sizeof(*THIS)); } t_int *zero_perform(t_int *w) /* zero out a vector */ { t_sample *out = (t_sample *)(w[1]); int n = (int)(w[2]); while (n--) *out++ = 0; return (w+3); } t_int *zero_perf8(t_int *w) { t_sample *out = (t_sample *)(w[1]); int n = (int)(w[2]); for (; n; n -= 8, out += 8) { out[0] = 0; out[1] = 0; out[2] = 0; out[3] = 0; out[4] = 0; out[5] = 0; out[6] = 0; out[7] = 0; } return (w+3); } void dsp_add_zero(t_sample *out, int n) { if (n&7) dsp_add(zero_perform, 2, out, (t_int)n); else dsp_add(zero_perf8, 2, out, (t_int)n); } /* ---------------------------- block~ ----------------------------- */ /* The "block~ object maintains the containing canvas's DSP computation, calling it at a super- or sub-multiple of the containing canvas's calling frequency. The block~'s creation arguments specify block size and overlap. Block~ does no "dsp" computation in its own right, but it adds prolog and epilog code before and after the canvas's unit generators. A subcanvas need not have a block~ at all; if there's none, its ugens are simply put on the list without any prolog or epilog code. Block~ may be invoked as switch~, in which case it also acts to switch the subcanvas on and off. The overall order of scheduling for a subcanvas is thus, inlet and outlet prologue code (1) block prologue (2) the objects in the subcanvas, including inlets and outlets block epilogue (2) outlet epilogue code (2) where (1) means, "if reblocked" and (2) means, "if reblocked or switched". If we're reblocked, the inlet prolog and outlet epilog code takes care of overlapping and buffering to deal with vector size changes. If we're switched but not reblocked, the inlet prolog is not needed, and the output epilog is ONLY run when the block is switched off; in this case the epilog code simply copies zeros to all signal outlets. */ t_class *block_class; typedef struct _block { t_object x_obj; int x_calcsize; /* number of samples actually to compute */ int x_overlap; int x_phase; /* from 0 to period-1; when zero we run the block */ int x_period; /* submultiple of containing canvas */ int x_frequency; /* supermultiple of comtaining canvas */ int x_count; /* number of times parent block has called us */ int x_chainonset; /* beginning of code in DSP chain */ int x_blocklength; /* length of dspchain for this block */ int x_epiloglength; /* length of epilog */ char x_switched; /* true if we're acting as a a switch */ char x_switchon; /* true if we're switched on */ char x_reblock; /* true if inlets and outlets are reblocking */ int x_upsample; /* upsampling-factor */ int x_downsample; /* downsampling-factor */ int x_return; /* stop right after this block (for one-shots) */ } t_block; static void block_set(t_block *x, t_floatarg fvecsize, t_floatarg foverlap, t_floatarg fupsample); static void *block_new(t_floatarg fvecsize, t_floatarg foverlap, t_floatarg fupsample) { t_block *x = (t_block *)pd_new(block_class); x->x_phase = 0; x->x_period = 1; x->x_frequency = 1; x->x_switched = 0; x->x_switchon = 1; block_set(x, fvecsize, foverlap, fupsample); return (x); } static void block_set(t_block *x, t_floatarg fcalcsize, t_floatarg foverlap, t_floatarg fupsample) { int upsample, downsample; int calcsize = fcalcsize; int overlap = foverlap; int dspstate = canvas_suspend_dsp(); if (overlap < 1) overlap = 1; if (calcsize < 0) calcsize = 0; /* this means we'll get it from parent later. */ if (fupsample <= 0) upsample = downsample = 1; else if (fupsample >= 1) { upsample = fupsample; downsample = 1; } else { downsample = 1.0 / fupsample; upsample = 1; } if (overlap != (1 << ilog2(overlap))) { pd_error(x, "block~: overlap not a power of 2"); overlap = 1; } if (downsample != (1 << ilog2(downsample))) { pd_error(x, "block~: downsampling not a power of 2"); downsample = 1; } if (upsample != (1 << ilog2(upsample))) { pd_error(x, "block~: upsampling not a power of 2"); upsample = 1; } x->x_calcsize = calcsize; x->x_overlap = overlap; x->x_upsample = upsample; x->x_downsample = downsample; canvas_resume_dsp(dspstate); } t_float canvas_getsr(t_canvas *x) { t_float srate = sys_getsr(); t_canvas *canvas; t_gobj *g; for (canvas = x; canvas; canvas = canvas->gl_owner) { for (g = canvas->gl_list; g; g = g->g_next) { if (g->g_pd == block_class) { srate *= ((t_float)(((t_block *)g)->x_upsample)) / ((t_float)(((t_block *)g)->x_downsample)); break; } } } return (srate); } int canvas_getsignallength(t_canvas *x) { t_canvas *canvas; t_gobj *g; for (canvas = x; canvas; canvas = canvas->gl_owner) for (g = canvas->gl_list; g; g = g->g_next) if (g->g_pd == block_class && ((t_block *)g)->x_calcsize) return (((t_block *)g)->x_calcsize); return (DEFDACBLKSIZE); } static void *switch_new(t_floatarg fvecsize, t_floatarg foverlap, t_floatarg fupsample) { t_block *x = (t_block *)(block_new(fvecsize, foverlap, fupsample)); x->x_switched = 1; x->x_switchon = 0; return (x); } static void block_float(t_block *x, t_floatarg f) { if (x->x_switched) x->x_switchon = (f != 0); } static void block_bang(t_block *x) { if (x->x_switched && !x->x_switchon && THIS->u_dspchain) { t_int *ip; x->x_return = 1; for (ip = THIS->u_dspchain + x->x_chainonset; ip; ) ip = (*(t_perfroutine)(*ip))(ip); x->x_return = 0; } else if (!x->x_switched) pd_error(x, "[block~]: bang has no effect"); else if (x->x_switched) { if (x->x_switchon) pd_error(x, "[switch~]: bang has no effect at on-state"); if (!THIS->u_dspchain) pd_error(x, "[switch~]: bang has no effect if DSP is off"); } } #define PROLOGCALL 2 #define EPILOGCALL 2 static t_int *block_prolog(t_int *w) { t_block *x = (t_block *)w[1]; int phase = x->x_phase; /* if we're switched off, jump past the epilog code */ if (!x->x_switchon) return (w + x->x_blocklength); if (phase) { phase++; if (phase == x->x_period) phase = 0; x->x_phase = phase; return (w + x->x_blocklength); /* skip block; jump past epilog */ } else { x->x_count = x->x_frequency; x->x_phase = (x->x_period > 1 ? 1 : 0); return (w + PROLOGCALL); /* beginning of block is next ugen */ } } static t_int *block_epilog(t_int *w) { t_block *x = (t_block *)w[1]; int count = x->x_count - 1; if (x->x_return) return (0); if (!x->x_reblock) return (w + x->x_epiloglength + EPILOGCALL); if (count) { x->x_count = count; return (w - (x->x_blocklength - (PROLOGCALL + EPILOGCALL))); /* go to ugen after prolog */ } else return (w + EPILOGCALL); } static void block_dsp(t_block *x, t_signal **sp) { /* do nothing here */ } void block_tilde_setup(void) { block_class = class_new(gensym("block~"), (t_newmethod)block_new, 0, sizeof(t_block), 0, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0); class_addcreator((t_newmethod)switch_new, gensym("switch~"), A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0); class_addmethod(block_class, (t_method)block_set, gensym("set"), A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0); class_addmethod(block_class, (t_method)block_dsp, gensym("dsp"), A_CANT, 0); class_addfloat(block_class, block_float); class_addbang(block_class, block_bang); } /* ------------------ DSP call list ----------------------- */ static t_int dsp_done(t_int *w) { return (0); } void dsp_add(t_perfroutine f, int n, ...) { int newsize = THIS->u_dspchainsize + n+1, i; va_list ap; THIS->u_dspchain = t_resizebytes(THIS->u_dspchain, THIS->u_dspchainsize * sizeof (t_int), newsize * sizeof (t_int)); THIS->u_dspchain[THIS->u_dspchainsize-1] = (t_int)f; if (THIS->u_loud) post("add to chain: %lx", THIS->u_dspchain[THIS->u_dspchainsize-1]); va_start(ap, n); for (i = 0; i < n; i++) { THIS->u_dspchain[THIS->u_dspchainsize + i] = va_arg(ap, t_int); if (THIS->u_loud) post("add to chain: %lx", THIS->u_dspchain[THIS->u_dspchainsize + i]); } va_end(ap); THIS->u_dspchain[newsize-1] = (t_int)dsp_done; THIS->u_dspchainsize = newsize; } /* at Guenter's suggestion, here's a vectorized version */ void dsp_addv(t_perfroutine f, int n, t_int *vec) { int newsize = THIS->u_dspchainsize + n+1, i; THIS->u_dspchain = t_resizebytes(THIS->u_dspchain, THIS->u_dspchainsize * sizeof (t_int), newsize * sizeof (t_int)); THIS->u_dspchain[THIS->u_dspchainsize-1] = (t_int)f; for (i = 0; i < n; i++) THIS->u_dspchain[THIS->u_dspchainsize + i] = vec[i]; THIS->u_dspchain[newsize-1] = (t_int)dsp_done; THIS->u_dspchainsize = newsize; } void dsp_tick(void) { if (THIS->u_dspchain) { t_int *ip; for (ip = THIS->u_dspchain; ip; ) ip = (*(t_perfroutine)(*ip))(ip); THIS->u_phase++; } } /* ---------------- signals ---------------------------- */ int ilog2(int n) { int r = -1; if (n <= 0) return(0); while (n) { r++; n >>= 1; } return (r); } /* call this when DSP is stopped to free all the signals */ static void signal_cleanup(void) { t_signal *sig; int i; while ((sig = THIS->u_signals)) { THIS->u_signals = sig->s_nextused; if (!sig->s_isborrowed && !sig->s_isscalar) t_freebytes(sig->s_vec, sig->s_nalloc * sizeof (*sig->s_vec)); t_freebytes(sig, sizeof *sig); } for (i = 0; i <= MAXLOGSIG; i++) THIS->u_freelist[i] = 0; THIS->u_freeborrowed = 0; } static void signal_dereference(t_signal *s) { if (THIS->u_loud) post("dereference %lx: %d", s, s->s_refcount); if (s->s_refcount <= 0) bug("signal_dereference"); s->s_refcount--; if (!s->s_refcount) signal_makereusable(s); } /* mark the signal "reusable." */ void signal_makereusable(t_signal *sig) { int logn = ilog2(sig->s_nalloc); #if 0 t_signal *s5; for (s5 = THIS->u_freeborrowed; s5; s5 = s5->s_nextfree) { if (s5 == sig) { bug("signal_free 3"); return; } } for (s5 = THIS->u_freelist[logn]; s5; s5 = s5->s_nextfree) { if (s5 == sig) { bug("signal_free 4"); return; } } #endif if (THIS->u_loud) post("free %lx: %d", sig, sig->s_isborrowed); if (sig->s_isborrowed || sig->s_isscalar) { if (sig->s_isborrowed) { /* if the signal is borrowed, decrement the borrowed-from signal's reference count, possibly marking it reusable too */ t_signal *s2 = sig->s_borrowedfrom; if ((s2 == sig) || !s2) bug("signal_free"); signal_dereference(s2); } sig->s_nextfree = THIS->u_freeborrowed; THIS->u_freeborrowed = sig; } else { /* if it's a real signal (not borrowed), put it on the free list so we can reuse it. */ if (THIS->u_freelist[logn] == sig) bug("signal_free 2"); sig->s_nextfree = THIS->u_freelist[logn]; THIS->u_freelist[logn] = sig; } } /* pop an audio signal from free list or create a new one. if "scalarp" is nonzero, it's a pointer to a scalar owned by the tilde object; in this case we neither allocate nor free it. Otherwise, if "length" is zero, return a "borrowed" signal whose buffer and size will be obtained later via signal_setborrowed(). */ t_signal *signal_new(int length, int nchans, t_float sr, t_sample *scalarptr) { int allocsize = 0; t_signal *ret, **whichlist; if (sr < 1) bug("signal_new"); if (length && !scalarptr) { /* figure out which free list to use, depending on size of vector */ int logn = ilog2(length*nchans); /* round up to a power of two */ if ((1< MAXLOGSIG) bug("signal buffer too large"); whichlist = THIS->u_freelist + logn; } else /* scalar or borrowed signal */ whichlist = &THIS->u_freeborrowed; /* try to reclaim one from the free list */ if ((ret = *whichlist)) *whichlist = ret->s_nextfree; else { /* LATER figure out what to do if we ran out of space */ ret = (t_signal *)t_getbytes(sizeof *ret); if (allocsize) ret->s_vec = (t_sample *)getbytes(allocsize * sizeof (*ret->s_vec)); ret->s_nextused = THIS->u_signals; THIS->u_signals = ret; } if (scalarptr) { ret->s_vec = scalarptr; ret->s_isborrowed = 0; ret->s_isscalar = 1; } else if (length) ret->s_isborrowed = ret->s_isscalar = 0; else { ret->s_vec = 0; ret->s_isborrowed = 1; ret->s_isscalar = 0; } ret->s_length = length; ret->s_nchans = nchans; ret->s_nalloc = allocsize; ret->s_sr = sr; ret->s_overlap = 0; ret->s_refcount = 0; ret->s_borrowedfrom = 0; if (THIS->u_loud) post("new %lx: %lx", ret, ret->s_vec); return (ret); } t_signal *signal_newlike(const t_signal *sig) { t_signal *s = signal_new(sig->s_length, sig->s_nchans, sig->s_sr, 0); s->s_overlap = sig->s_overlap; return s; } void signal_setborrowed(t_signal *sig, t_signal *sig2) { if (!sig->s_isborrowed || sig->s_borrowedfrom) bug("signal_setborrowed"); if (sig == sig2) bug("signal_setborrowed 2"); sig->s_borrowedfrom = sig2; sig->s_vec = sig2->s_vec; sig->s_length = sig2->s_length; sig->s_nchans = sig2->s_nchans; sig->s_sr = sig2->s_sr; sig->s_overlap = sig2->s_overlap; sig->s_nalloc = sig2->s_nalloc; sig2->s_refcount++; if (THIS->u_loud) post("set borrowed %lx: from %lx vec %lx", sig, sig2, sig->s_vec); } /* only use this in the context of dsp routines to set number of channels on output signal - we assume it's currently a pointer to the null signal */ void signal_setmultiout(t_signal **sig, int nchans) { int overlap = (*sig)->s_overlap; *sig = signal_new((*sig)->s_length, nchans, (*sig)->s_sr, 0); (*sig)->s_overlap = overlap; } static int signal_compatible(t_signal *s1, t_signal *s2) { return (s1->s_length == s2->s_length && s1->s_nchans == s2->s_nchans && s1->s_sr == s2->s_sr && s1->s_overlap == s2->s_overlap); } /* ------------------ ugen ("unit generator") sorting ----------------- */ typedef struct _ugenbox { struct _siginlet *u_in; int u_nin; struct _sigoutlet *u_out; int u_nout; int u_phase; struct _ugenbox *u_next; t_object *u_obj; int u_done; } t_ugenbox; typedef struct _siginlet { int i_nconnect; int i_ngot; t_signal *i_signal; } t_siginlet; typedef struct _sigoutconnect { t_ugenbox *oc_who; int oc_inno; struct _sigoutconnect *oc_next; } t_sigoutconnect; typedef struct _sigoutlet { int o_nconnect; int o_nsent; t_signal *o_signal; t_sigoutconnect *o_connections; } t_sigoutlet; struct _dspcontext { struct _ugenbox *dc_ugenlist; struct _dspcontext *dc_parentcontext; int dc_ninlets; int dc_noutlets; t_signal **dc_iosigs; t_signal dc_nullsignal; /* non-signal showing sample rate and length */ unsigned int dc_toplevel:1; /* true if "iosigs" is invalid. */ unsigned int dc_reblock:1; /* true if we have to reblock in/outlets */ unsigned int dc_switched:1; /* true if we're switched */ unsigned int dc_warnedmulti:1; /* already warned about bad multi input */ }; #define DC_LENGTH(x) ((x)->dc_nullsignal.s_length) #define DC_SR(x) ((x)->dc_nullsignal.s_sr) #define DC_OVERLAP(x) ((x)->dc_nullsignal.s_overlap) #define t_dspcontext struct _dspcontext /* get a new signal for the current context - used by clone~ object */ t_signal *signal_newfromcontext(int borrowed, int nchans) { t_signal *s = signal_new((borrowed? 0 : DC_LENGTH(THIS->u_context)), nchans, DC_SR(THIS->u_context), 0); s->s_overlap = DC_OVERLAP(THIS->u_context); return s; } void ugen_stop(void) { #if 0 /* test to make sure we aren't leaving signal garbage */ { t_signal *sig; int done = 0, count = 0; /* report any signals still in use */ for (sig = THIS->u_signals; sig; sig = sig->s_nextused) { if (sig->s_refcount) post("signal %lx refcount %d", sig, sig->s_refcount), done = 1; count++; } if (!done) post("all %d signals freed correctly", count); } #endif if (THIS->u_dspchain) { freebytes(THIS->u_dspchain, THIS->u_dspchainsize * sizeof (t_int)); THIS->u_dspchain = 0; } signal_cleanup(); } void ugen_start(void) { ugen_stop(); THIS->u_sortno++; /* THIS->u_loud = 1; -- enable this for volumes of debugging output */ THIS->u_dspchain = (t_int *)getbytes(sizeof(*THIS->u_dspchain)); THIS->u_dspchain[0] = (t_int)dsp_done; THIS->u_dspchainsize = 1; if (THIS->u_context) bug("ugen_start"); } int ugen_getsortno(void) { return (THIS->u_sortno); } #if 0 void glob_ugen_printstate(void *dummy, t_symbol *s, int argc, t_atom *argv) { int i, count; t_signal *sig; for (count = 0, sig = THIS->u_signals; sig; count++, sig = sig->s_nextused) ; post("used signals %d", count); for (i = 0; i < MAXLOGSIG; i++) { for (count = 0, sig = THIS->u_freelist[i]; sig; count++, sig = sig->s_nextfree) ; if (count) post("size %d: free %d", (1 << i), count); } for (count = 0, sig = THIS->u_freeborrowed; sig; count++, sig = sig->s_nextfree) ; post("free borrowed %d", count); THIS->u_loud = argc; } #endif /* start building the graph for a canvas */ t_dspcontext *ugen_start_graph(int toplevel, t_signal **sp, int ninlets, int noutlets) { t_dspcontext *dc = (t_dspcontext *)getbytes(sizeof(*dc)); if (THIS->u_loud) post("ugen_start_graph..."); /* protect against invalid numsignals. This might happen if we have an abstraction with inlet~/outlet~ opened as a toplevel patch */ if (toplevel) ninlets = noutlets = 0; dc->dc_ugenlist = 0; dc->dc_toplevel = toplevel; dc->dc_iosigs = sp; dc->dc_ninlets = ninlets; dc->dc_noutlets = noutlets; dc->dc_warnedmulti = 0; dc->dc_parentcontext = THIS->u_context; THIS->u_context = dc; return (dc); } /* first the canvas calls this to create all the boxes... */ void ugen_add(t_dspcontext *dc, t_object *obj) { t_ugenbox *x = (t_ugenbox *)getbytes(sizeof *x); int i; t_sigoutlet *uout; t_siginlet *uin; x->u_next = dc->dc_ugenlist; dc->dc_ugenlist = x; x->u_obj = obj; x->u_nin = obj_nsiginlets(obj); x->u_in = getbytes(x->u_nin * sizeof (*x->u_in)); for (uin = x->u_in, i = x->u_nin; i--; uin++) uin->i_nconnect = 0; x->u_nout = obj_nsigoutlets(obj); x->u_out = getbytes(x->u_nout * sizeof (*x->u_out)); for (uout = x->u_out, i = x->u_nout; i--; uout++) uout->o_connections = 0, uout->o_nconnect = 0; } /* and then this to make all the connections. */ void ugen_connect(t_dspcontext *dc, t_object *x1, int outno, t_object *x2, int inno) { t_ugenbox *u1, *u2; t_sigoutlet *uout; t_siginlet *uin; t_sigoutconnect *oc; int sigoutno = obj_sigoutletindex(x1, outno); int siginno = obj_siginletindex(x2, inno); if (THIS->u_loud) post("%s -> %s: %d->%d", class_getname(x1->ob_pd), class_getname(x2->ob_pd), outno, inno); for (u1 = dc->dc_ugenlist; u1 && u1->u_obj != x1; u1 = u1->u_next); for (u2 = dc->dc_ugenlist; u2 && u2->u_obj != x2; u2 = u2->u_next); if (!u1 || !u2 || siginno < 0 || !u2->u_nin) { if (!u1) pd_error(0, "object with signal outlets but no DSP method?"); /* check if it's a "text" (i.e., object wasn't created) - if so fail silently */ else if (!(x2 && (pd_class(&x2->ob_pd) == text_class))) pd_error(u1->u_obj, "audio signal outlet connected to nonsignal inlet (ignored)"); return; } if (sigoutno < 0 || sigoutno >= u1->u_nout || siginno >= u2->u_nin) { bug("ugen_connect %s %s %d %d (%d %d)", class_getname(x1->ob_pd), class_getname(x2->ob_pd), sigoutno, siginno, u1->u_nout, u2->u_nin); } uout = u1->u_out + sigoutno; uin = u2->u_in + siginno; /* add a new connection to the outlet's list */ oc = (t_sigoutconnect *)getbytes(sizeof *oc); oc->oc_next = uout->o_connections; uout->o_connections = oc; oc->oc_who = u2; oc->oc_inno = siginno; /* update inlet and outlet counts */ uout->o_nconnect++; uin->i_nconnect++; } /* get the index of a ugenbox or -1 if it's not on the list */ static int ugen_index(t_dspcontext *dc, t_ugenbox *x) { int ret; t_ugenbox *u; for (u = dc->dc_ugenlist, ret = 0; u; u = u->u_next, ret++) if (u == x) return (ret); return (-1); } extern t_class *clone_class; static const t_sample ugen_scalarzero; /* zero for scalar-to-vector copying */ extern int class_getdspflags(const t_class *c); /* put a ugenbox on the chain, recursively putting any others on that this one might uncover. */ static void ugen_doit(t_dspcontext *dc, t_ugenbox *u) { t_sigoutlet *uout; t_siginlet *uin; t_sigoutconnect *oc; t_class *class = pd_class(&u->u_obj->ob_pd); t_signal *freelater = 0, *stmp; int flags = class_getdspflags(class); int i, n; /* suppress creating new signals for the outputs of signal inlets for non-reblocked canvases -- those will be borrowed. */ int nonewsigs = ((class == vinlet_class) && !dc->dc_reblock); /* when we encounter a subcanvas or outlet~ object, suppress freeing the input signals as they may be "borrowed" for the super or sub patch; except blocked or switched outlet~s. */ int nofreesigs = (class == canvas_class || class == clone_class || ((class == voutlet_class) && !(dc->dc_reblock || dc->dc_switched))); t_signal **insig, **outsig, **sig, *s1, *s2, *s3; t_ugenbox *u2; /* if CLASS_MULTICHANNEL isn't set, check that all input signals are one-channel, and if not, just return without doing anything. */ for (i = 0, uin = u->u_in; i < u->u_nin; i++, uin++) if (uin->i_nconnect && uin->i_signal->s_nchans != 1 && !(flags & CLASS_MULTICHANNEL)) { pd_error(u->u_obj, "object %s can't take multichannel inputs", class_getname(u->u_obj->ob_pd)); dc->dc_warnedmulti = 1; return; } if (THIS->u_loud) post("doit %s %d %d", class_getname(class), nofreesigs, nonewsigs); /* Fill in unconnected inlets. Normally we create a signal for it and add a scalar-to-vector copy to the DSP chain to fill it in from the inlet's scalar source. But if the relevant NOPROMOTE flag is set, create a scalar "signal" that just points to the preexisting location of the scalar to be promoted so that the object can use the scalar input directly. */ for (i = 0, uin = u->u_in; i < u->u_nin; i++, uin++) { if (!uin->i_nconnect) { t_float *scalar = obj_findsignalscalar(u->u_obj, i); /* if the object can deal with it, we generate a signal that consists only of a pointer to a scalar. */ if ((i && (flags & CLASS_NOPROMOTESIG)) || (!i && (flags & CLASS_NOPROMOTELEFT))) uin->i_signal = signal_new(1, 1, DC_SR(dc), scalar); else { /* otherwise we have to add a call to the DSP chain to promote the scalar input to a vector. */ uin->i_signal = signal_new(DC_LENGTH(dc), 1, DC_SR(dc), 0); dsp_add_scalarcopy(scalar, uin->i_signal->s_vec, uin->i_signal->s_n); } uin->i_signal->s_overlap = DC_OVERLAP(dc); uin->i_signal->s_refcount = 1; } } /* allocate space for signals to send to the object's DSP routine. */ insig = (t_signal **)getbytes((u->u_nin + u->u_nout) * sizeof(t_signal *)); outsig = insig + u->u_nin; for (sig = insig, uin = u->u_in, i = u->u_nin; i--; sig++, uin++) { *sig = uin->i_signal; if (1) { /* if scalar or borrowed, put on free-after-dsp-call list - we can't reuse them yet because the "dsp" call below might then reclaim and bash them while someone else still needs to see the s_vec/s_length/s_nchans fields. Otherwise, the signal owns its s_vec array and, to maximize in-place reuse of s_vec arrays, we mark it free now so that we can get it back when creating output signals below. */ if (nofreesigs || (*sig)->s_isscalar || (*sig)->s_isborrowed) { if ((*sig)->s_refcount > 1) (*sig)->s_refcount--; else (*sig)->s_nextfree = freelater, freelater = *sig; } else signal_dereference(*sig); } } /* Create output signals. These may re-inhabit space that was freed in the previous step (so tilde objects must be able to compute in place.) We delay creating signals for subcanvases and outlet~ objects; instead we create "borrowed" ones so that we can track the refcount. The subcanvas/outlet~ later fixes the borrowed signal to show where the output data actually is, to avoid having to copy it. Otherwise, in case the CLASS_MULTICHANNEL flag is set for the object, we pass "null" signal and expect the DSP routine to replace it.. In any other case, we just allocate a new output vector. */ for (sig = outsig, uout = u->u_out, i = u->u_nout; i--; sig++, uout++) { if (nonewsigs) *sig = signal_new(0, 1, DC_SR(dc), 0); else if (flags & CLASS_MULTICHANNEL) *sig = &dc->dc_nullsignal; else *sig = signal_new(DC_LENGTH(dc), 1, DC_SR(dc), 0); (*sig)->s_overlap = DC_OVERLAP(dc); } /* now call the DSP scheduling routine for the ugen. This routine must fill in "borrowed" signal outputs in case it's either a subcanvas or a signal inlet. */ mess1(&u->u_obj->ob_pd, gensym("dsp"), insig); for (sig = outsig, uout = u->u_out, i = u->u_nout; i--; sig++, uout++) { uout->o_signal = *sig; (*sig)->s_refcount = uout->o_nconnect; /* if any output signals aren't connected to anyone, free them now; otherwise they'll either get freed when the reference count goes back to zero, or even later as explained above. */ if (!(*sig)->s_refcount) signal_makereusable(*sig); } if (THIS->u_loud) { if (u->u_nin + u->u_nout == 0) post("put %s %d", class_getname(u->u_obj->ob_pd), ugen_index(dc, u)); else if (u->u_nin + u->u_nout == 1) post("put %s %d (%lx)", class_getname(u->u_obj->ob_pd), ugen_index(dc, u), insig[0]); else if (u->u_nin + u->u_nout == 2) post("put %s %d (%lx %lx)", class_getname(u->u_obj->ob_pd), ugen_index(dc, u), insig[0], insig[1]); else post("put %s %d (%lx %lx %lx ...)", class_getname(u->u_obj->ob_pd), ugen_index(dc, u), insig[0], insig[1], insig[2]); } /* now we can act on delayed-free */ while (freelater) { if (THIS->u_loud) post("delayed deref %lx", freelater); stmp = freelater->s_nextfree; signal_dereference(freelater); freelater = stmp; } /* pass it on and add anyone whose last inlet was filled */ for (uout = u->u_out, i = u->u_nout; i--; uout++) { s1 = uout->o_signal; for (oc = uout->o_connections; oc; oc = oc->oc_next) { u2 = oc->oc_who; uin = &u2->u_in[oc->oc_inno]; /* if there's already someone here, sum the two */ if ((s2 = uin->i_signal)) { s1->s_refcount--; s2->s_refcount--; s3 = signal_newlike(s1); if (s1->s_nchans != s2->s_nchans || s1->s_length != s2->s_length) { pd_error(u->u_obj, "%s: incompatible signal inputs (%dx%d vs. %dx%d)", class_getname(u->u_obj->ob_pd), s1->s_nchans, s1->s_length, s2->s_nchans, s2->s_length); dsp_add_copy(s1->s_vec, s3->s_vec, s1->s_length * s1->s_nchans); } else { if (s1->s_sr != s2->s_sr) bug("signals sample rate mismatch"); dsp_add_plus(s1->s_vec, s2->s_vec, s3->s_vec, s1->s_length * s1->s_nchans); } uin->i_signal = s3; s3->s_refcount = 1; if (!s1->s_refcount) signal_makereusable(s1); if (!s2->s_refcount) signal_makereusable(s2); } else uin->i_signal = s1; uin->i_ngot++; /* if we didn't fill this inlet don't bother yet */ if (uin->i_ngot < uin->i_nconnect) goto notyet; /* if there's more than one, check them all */ if (u2->u_nin > 1) { for (uin = u2->u_in, n = u2->u_nin; n--; uin++) if (uin->i_ngot < uin->i_nconnect) goto notyet; } /* so now we can schedule the ugen. */ ugen_doit(dc, u2); notyet: ; } } t_freebytes(insig,(u->u_nin + u->u_nout) * sizeof(t_signal *)); u->u_done = 1; } /* once the DSP graph is built, we call this routine to sort it. This routine also deletes the graph; later we might want to leave the graph around, in case the user is editing the DSP network, to save having to recreate it all the time. But not today. */ void ugen_done_graph(t_dspcontext *dc) { t_ugenbox *u; t_sigoutlet *uout; t_siginlet *uin; t_sigoutconnect *oc, *oc2; int i, n; t_block *blk; t_dspcontext *parent_context = dc->dc_parentcontext; t_float parent_srate; int parent_vecsize, parent_overlap; int period, frequency, phase, calcsize; t_float srate; int chainblockbegin; /* DSP chain onset before block prolog code */ int chainblockend; /* and after block epilog code */ int chainafterall; /* and after signal outlet epilog */ int reblock = 0, switched; int downsample = 1, upsample = 1; int totaloverlap; /* debugging printout */ if (THIS->u_loud) { post("ugen_done_graph..."); for (u = dc->dc_ugenlist; u; u = u->u_next) { post("ugen: %s", class_getname(u->u_obj->ob_pd)); for (uout = u->u_out, i = 0; i < u->u_nout; uout++, i++) for (oc = uout->o_connections; oc; oc = oc->oc_next) { post("... out %d to %s, index %d, inlet %d", i, class_getname(oc->oc_who->u_obj->ob_pd), ugen_index(dc, oc->oc_who), oc->oc_inno); } } } /* search for an object of class "block~" */ for (u = dc->dc_ugenlist, blk = 0; u; u = u->u_next) { t_pd *zz = &u->u_obj->ob_pd; if (pd_class(zz) == block_class) { if (blk) pd_error(blk, "conflicting block~ and/or switch~ objects in same window"); else blk = (t_block *)zz; } } /* figure out block size, calling frequency, sample rate */ if (parent_context) { parent_srate = DC_SR(parent_context); parent_vecsize = DC_LENGTH(parent_context); parent_overlap = DC_OVERLAP(parent_context); } else { parent_srate = sys_getsr(); parent_vecsize = sys_getblksize(); parent_overlap = 1; } totaloverlap = parent_overlap; if (blk) { int realoverlap; calcsize = blk->x_calcsize; if (calcsize == 0) calcsize = parent_vecsize; realoverlap = blk->x_overlap; if (realoverlap > calcsize) realoverlap = calcsize; downsample = blk->x_downsample; upsample = blk->x_upsample; if (downsample > parent_vecsize) downsample = parent_vecsize; period = (calcsize * downsample)/ (parent_vecsize * realoverlap * upsample); frequency = (parent_vecsize * realoverlap * upsample)/ (calcsize * downsample); phase = blk->x_phase; srate = parent_srate * realoverlap * upsample / downsample; if (period < 1) period = 1; if (frequency < 1) frequency = 1; blk->x_frequency = frequency; blk->x_period = period; blk->x_phase = THIS->u_phase & (period - 1); if (! parent_context || (realoverlap != 1) || (calcsize != parent_vecsize) || (downsample != 1) || (upsample != 1)) reblock = 1; switched = blk->x_switched; totaloverlap = parent_overlap * realoverlap; } else { srate = parent_srate; calcsize = parent_vecsize; downsample = upsample = 1; period = frequency = 1; phase = 0; if (!parent_context) reblock = 1; switched = 0; } dc->dc_reblock = reblock; dc->dc_switched = switched; dc->dc_nullsignal.s_sr = srate; dc->dc_nullsignal.s_length = calcsize; dc->dc_nullsignal.s_overlap = totaloverlap; dc->dc_nullsignal.s_nchans = -1; /* fake so we can sanity check */ /* if we're reblocking or switched, we now have to create output signals to fill in for the "borrowed" ones we have now. This is also possibly true even if we're not blocked/switched, in the case that there was a signal loop. But we don't know this yet. */ if (dc->dc_iosigs && (switched || reblock)) { t_signal **sigp; for (i = 0, sigp = dc->dc_iosigs + dc->dc_ninlets; i < dc->dc_noutlets; i++, sigp++) { if ((*sigp)->s_isborrowed && !(*sigp)->s_borrowedfrom) { t_signal *s = signal_new(parent_vecsize, 1, parent_srate, 0); s->s_overlap = totaloverlap; signal_setborrowed(*sigp, s); if (THIS->u_loud) post("set %lx->%lx", *sigp, (*sigp)->s_borrowedfrom); } } } if (THIS->u_loud) post("reblock %d, switched %d", reblock, switched); /* schedule prologs for inlets and outlets. If the "reblock" flag is set, an inlet will put code on the DSP chain to copy its input into an internal buffer here, before any unit generators' DSP code gets scheduled. If we don't "reblock", inlets will need to get pointers to their corresponding inlets/outlets on the box we're inside, if any. Outlets will also need pointers, unless we're switched, in which case outlet epilog code will kick in. */ for (u = dc->dc_ugenlist; u; u = u->u_next) { t_pd *zz = &u->u_obj->ob_pd; t_signal **outsigs = dc->dc_iosigs; if (outsigs) outsigs += dc->dc_ninlets; if (pd_class(zz) == vinlet_class) vinlet_dspprolog((struct _vinlet *)zz, dc->dc_iosigs, calcsize, THIS->u_phase, period, frequency, downsample, upsample, reblock, switched); else if (pd_class(zz) == voutlet_class) voutlet_dspprolog((struct _voutlet *)zz, outsigs, calcsize, THIS->u_phase, period, frequency, downsample, upsample, reblock, switched); } chainblockbegin = THIS->u_dspchainsize; if (blk && (reblock || switched)) /* add the block DSP prolog */ { dsp_add(block_prolog, 1, blk); blk->x_chainonset = THIS->u_dspchainsize - 1; } /* Initialize for sorting */ for (u = dc->dc_ugenlist; u; u = u->u_next) { u->u_done = 0; for (uout = u->u_out, i = u->u_nout; i--; uout++) uout->o_nsent = 0; for (uin = u->u_in, i = u->u_nin; i--; uin++) uin->i_ngot = 0, uin->i_signal = 0; } /* Do the sort */ for (u = dc->dc_ugenlist; u; u = u->u_next) { /* check that we have no connected signal inlets */ if (u->u_done) continue; for (uin = u->u_in, i = u->u_nin; i--; uin++) if (uin->i_nconnect) goto next; ugen_doit(dc, u); next: ; } /* check for a DSP loop, which is evidenced here by the presence of ugens not yet scheduled. */ for (u = dc->dc_ugenlist; u; u = u->u_next) if (!u->u_done) { t_signal **sigp; if (!dc->dc_warnedmulti) pd_error(u->u_obj, "DSP loop detected (some tilde objects weren't scheduled)"); /* this might imply that we have unfilled "borrowed" outputs which we'd better fill in now. */ for (i = 0, sigp = dc->dc_iosigs + dc->dc_ninlets; i < dc->dc_noutlets; i++, sigp++) { if ((*sigp)->s_isborrowed && !(*sigp)->s_borrowedfrom) { t_signal *s3 = signal_new(parent_vecsize, 1, parent_srate, 0); s3->s_overlap = totaloverlap; signal_setborrowed(*sigp, s3); dsp_add_zero(s3->s_vec, s3->s_n); if (THIS->u_loud) post("oops, belatedly set %lx->%lx", *sigp, (*sigp)->s_borrowedfrom); } } break; /* don't need to keep looking. */ } if (blk && (reblock || switched)) /* add block DSP epilog */ dsp_add(block_epilog, 1, blk); chainblockend = THIS->u_dspchainsize; /* add epilogs for outlets. */ for (u = dc->dc_ugenlist; u; u = u->u_next) { t_pd *zz = &u->u_obj->ob_pd; if (pd_class(zz) == voutlet_class) { t_signal **iosigs = dc->dc_iosigs; if (iosigs) iosigs += dc->dc_ninlets; voutlet_dspepilog((struct _voutlet *)zz, iosigs, calcsize, THIS->u_phase, period, frequency, downsample, upsample, reblock, switched); } } chainafterall = THIS->u_dspchainsize; if (blk) { blk->x_blocklength = chainblockend - chainblockbegin; blk->x_epiloglength = chainafterall - chainblockend; blk->x_reblock = reblock; } if (THIS->u_loud) { t_int *ip; if (!dc->dc_parentcontext) for (i = THIS->u_dspchainsize, ip = THIS->u_dspchain; i--; ip++) post("chain %lx", *ip); post("... ugen_done_graph done."); } /* now delete everything. */ while (dc->dc_ugenlist) { for (uout = dc->dc_ugenlist->u_out, n = dc->dc_ugenlist->u_nout; n--; uout++) { oc = uout->o_connections; while (oc) { oc2 = oc->oc_next; freebytes(oc, sizeof *oc); oc = oc2; } } freebytes(dc->dc_ugenlist->u_out, dc->dc_ugenlist->u_nout * sizeof (*dc->dc_ugenlist->u_out)); freebytes(dc->dc_ugenlist->u_in, dc->dc_ugenlist->u_nin * sizeof(*dc->dc_ugenlist->u_in)); u = dc->dc_ugenlist; dc->dc_ugenlist = u->u_next; freebytes(u, sizeof *u); } if (THIS->u_context == dc) THIS->u_context = dc->dc_parentcontext; else bug("THIS->u_context"); freebytes(dc, sizeof(*dc)); } static t_signal *ugen_getiosig(int index, int inout) { if (!THIS->u_context) bug("ugen_getiosig"); if (THIS->u_context->dc_toplevel) return (0); if (inout) index += THIS->u_context->dc_ninlets; return (THIS->u_context->dc_iosigs[index]); } /* -------------- DSP basics, copying and adding signals --------------- */ t_int *plus_perform(t_int *w) { t_sample *in1 = (t_sample *)(w[1]); t_sample *in2 = (t_sample *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); while (n--) *out++ = *in1++ + *in2++; return (w+5); } t_int *plus_perf8(t_int *w) { t_sample *in1 = (t_sample *)(w[1]); t_sample *in2 = (t_sample *)(w[2]); t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); for (; n; n -= 8, in1 += 8, in2 += 8, out += 8) { t_sample f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3]; t_sample f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7]; t_sample g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3]; t_sample g4 = in2[4], g5 = in2[5], g6 = in2[6], g7 = in2[7]; out[0] = f0 + g0; out[1] = f1 + g1; out[2] = f2 + g2; out[3] = f3 + g3; out[4] = f4 + g4; out[5] = f5 + g5; out[6] = f6 + g6; out[7] = f7 + g7; } return (w+5); } void dsp_add_plus(t_sample *in1, t_sample *in2, t_sample *out, int n) { if (n&7) dsp_add(plus_perform, 4, in1, in2, out, (t_int)n); else dsp_add(plus_perf8, 4, in1, in2, out, (t_int)n); } t_int *copy_perform(t_int *w) { t_sample *in1 = (t_sample *)(w[1]); t_sample *out = (t_sample *)(w[2]); int n = (int)(w[3]); while (n--) *out++ = *in1++; return (w+4); } t_int *copy_perf8(t_int *w) { t_sample *in1 = (t_sample *)(w[1]); t_sample *out = (t_sample *)(w[2]); int n = (int)(w[3]); for (; n; n -= 8, in1 += 8, out += 8) { t_sample f0 = in1[0]; t_sample f1 = in1[1]; t_sample f2 = in1[2]; t_sample f3 = in1[3]; t_sample f4 = in1[4]; t_sample f5 = in1[5]; t_sample f6 = in1[6]; t_sample f7 = in1[7]; out[0] = f0; out[1] = f1; out[2] = f2; out[3] = f3; out[4] = f4; out[5] = f5; out[6] = f6; out[7] = f7; } return (w+4); } void dsp_add_copy(t_sample *in, t_sample *out, int n) { if (n&7) dsp_add(copy_perform, 3, in, out, (t_int)n); else dsp_add(copy_perf8, 3, in, out, (t_int)n); } t_int *scalarcopy_perform(t_int *w) { t_float f = *(t_float *)(w[1]); t_sample *out = (t_sample *)(w[2]); int n = (int)(w[3]); while (n--) *out++ = f; return (w+4); } t_int *scalarcopy_perf8(t_int *w) { t_float f = *(t_float *)(w[1]); t_sample *out = (t_sample *)(w[2]); int n = (int)(w[3]); for (; n; n -= 8, out += 8) { out[0] = f; out[1] = f; out[2] = f; out[3] = f; out[4] = f; out[5] = f; out[6] = f; out[7] = f; } return (w+4); } void dsp_add_scalarcopy(t_float *in, t_sample *out, int n) { if (n&7) dsp_add(scalarcopy_perform, 3, in, out, (t_int)n); else dsp_add(scalarcopy_perf8, 3, in, out, (t_int)n); } /* ------------------------ samplerate~~ -------------------------- */ static t_class *samplerate_tilde_class; typedef struct _samplerate { t_object x_obj; t_float x_sr; t_canvas *x_canvas; } t_samplerate; static void samplerate_tilde_bang(t_samplerate *x) { outlet_float(x->x_obj.ob_outlet, canvas_getsr(x->x_canvas)); } static void *samplerate_tilde_new(t_symbol *s) { t_samplerate *x = (t_samplerate *)pd_new(samplerate_tilde_class); outlet_new(&x->x_obj, &s_float); x->x_canvas = canvas_getcurrent(); return (x); } static void samplerate_tilde_setup(void) { samplerate_tilde_class = class_new(gensym("samplerate~"), (t_newmethod)samplerate_tilde_new, 0, sizeof(t_samplerate), 0, 0); class_addbang(samplerate_tilde_class, samplerate_tilde_bang); } /* -------------------- setup routine -------------------------- */ void d_ugen_setup(void) { block_tilde_setup(); samplerate_tilde_setup(); } ================================================ FILE: libs/libpd/pure-data/src/g_all_guis.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */ /* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */ #include #include #include #include #include "m_pd.h" #include "s_stuff.h" #include "g_all_guis.h" #include "m_private_utils.h" #ifdef _WIN32 #include #else #include #endif typedef struct _iemgui_private { int p_prevX, p_prevY; t_iemgui_drawfunctions p_widget; int p_binbuf_valid; } t_iemgui_private; /* #define GGEE_HSLIDER_COMPATIBLE */ /* helpers */ static int srl_is_valid(const t_symbol* s) { return (!!s && s != &s_); } /*------------------ global variables -------------------------*/ int iemgui_color_hex[]= { 16579836, 10526880, 4210752, 16572640, 16572608, 16579784, 14220504, 14220540, 14476540, 16308476, 14737632, 8158332, 2105376, 16525352, 16559172, 15263784, 1370132, 2684148, 3952892, 16003312, 12369084, 6316128, 0, 9177096, 5779456, 7874580, 2641940, 17488, 5256, 5767248 }; int iemgui_vu_db2i[]= { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9,10,10,10,10,10, 11,11,11,11,11,12,12,12,12,12, 13,13,13,13,14,14,14,14,15,15, 15,15,16,16,16,16,17,17,17,18, 18,18,19,19,19,20,20,20,21,21, 22,22,23,23,24,24,25,26,27,28, 29,30,31,32,33,33,34,34,35,35, 36,36,37,37,37,38,38,38,39,39, 39,39,39,39,40,40 }; int iemgui_vu_col[]= { 0,17,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 15,15,15,15,15,15,15,15,15,15,14,14,13,13,13,13,13,13,13,13,13,13,13,19,19,19 }; char *iemgui_vu_scale_str[]= { "", "<-99", "", "", "", "-50", "", "", "", "-30", "", "", "", "-20", "", "", "", "-12", "", "", "", "-6", "", "", "", "-2", "", "", "", "-0dB", "", "", "", "+2", "", "", "", "+6", "", "", "", ">+12", "", "", "", "", "", }; /*------------------ global functions -------------------------*/ int iemgui_clip_size(int size) { if(size < IEM_GUI_MINSIZE) size = IEM_GUI_MINSIZE; return(size); } int iemgui_modulo_color(int col) { while(col >= IEM_GUI_MAX_COLOR) col -= IEM_GUI_MAX_COLOR; while(col < 0) col += IEM_GUI_MAX_COLOR; return(col); } t_symbol *iemgui_dollar2raute(t_symbol *s) { const char *s1; char buf[MAXPDSTRING+1], *s2; if (strlen(s->s_name) >= MAXPDSTRING) return (s); for (s1 = s->s_name, s2 = buf; ; s1++, s2++) { if (*s1 == '$') *s2 = '#'; else if (!(*s2 = *s1)) break; } return(gensym(buf)); } t_symbol *iemgui_raute2dollar(t_symbol *s) { const char *s1; char buf[MAXPDSTRING+1], *s2; if (strlen(s->s_name) >= MAXPDSTRING) return (s); for (s1 = s->s_name, s2 = buf; ; s1++, s2++) { if (*s1 == '#') *s2 = '$'; else if (!(*s2 = *s1)) break; } return(gensym(buf)); } void iemgui_verify_snd_ne_rcv(t_iemgui *iemgui) { iemgui->x_fsf.x_put_in2out = 1; if(iemgui->x_fsf.x_snd_able && iemgui->x_fsf.x_rcv_able) { if(!strcmp(iemgui->x_snd->s_name, iemgui->x_rcv->s_name)) iemgui->x_fsf.x_put_in2out = 0; } } t_symbol *iemgui_new_dogetname(t_iemgui *iemgui, int indx, t_atom *argv) { if (IS_A_SYMBOL(argv, indx)) { t_symbol*name=atom_getsymbolarg(indx, 100000, argv); if(gensym("empty") == name) return 0; return name; } else if (IS_A_FLOAT(argv, indx)) { char str[80]; sprintf(str, "%d", (int)atom_getfloatarg(indx, 100000, argv)); return (gensym(str)); } else return 0; } void iemgui_new_getnames(t_iemgui *iemgui, int indx, t_atom *argv) { if (argv) { iemgui->x_snd = iemgui_new_dogetname(iemgui, indx, argv); iemgui->x_rcv = iemgui_new_dogetname(iemgui, indx+1, argv); if(IS_A_FLOAT(argv, indx+2)) { char str[80]; atom_string(argv+indx+2, str, sizeof(str)); iemgui->x_lab = gensym(str); } else { iemgui->x_lab = iemgui_new_dogetname(iemgui, indx+2, argv); } iemgui->x_private->p_binbuf_valid = 1; } else { iemgui->x_snd = iemgui->x_rcv = iemgui->x_lab = 0; iemgui->x_private->p_binbuf_valid = 0; } /* in the object's constructor, we can't access the raw values yet: */ iemgui->x_snd_unexpanded = iemgui->x_rcv_unexpanded = iemgui->x_lab_unexpanded = 0; iemgui->x_binbufindex = indx; iemgui->x_labelbindex = indx + 3; } /* initialize a single symbol in unexpanded form. We reach into the binbuf to grab them; if there's nothing there, set it to the fallback; if still nothing, set to NULL. */ static void iemgui_init_sym2dollararg(t_iemgui *iemgui, t_symbol **symp, int indx, t_symbol *fallback) { t_binbuf *b = iemgui->x_obj.ob_binbuf; if ((!*symp) && iemgui->x_private->p_binbuf_valid && (binbuf_getnatom(b) > indx)) { t_atom *a = binbuf_getvec(b) + indx; char astring[80]; const char *buf = astring; if(A_SYMBOL == a->a_type) { buf = atom_getsymbol(a)->s_name; } else { atom_string(a, astring, sizeof(astring)); } if(strcmp(buf, "empty")) *symp = gensym(buf); } if (!*symp) *symp = fallback; } /* get the unexpanded versions of the symbols; initialize them if necessary. */ void iemgui_all_sym2dollararg(t_iemgui *iemgui, t_symbol **srlsym) { iemgui_init_sym2dollararg(iemgui, &iemgui->x_snd_unexpanded, iemgui->x_binbufindex+1, iemgui->x_snd); iemgui_init_sym2dollararg(iemgui, &iemgui->x_rcv_unexpanded, iemgui->x_binbufindex+2, iemgui->x_rcv); iemgui_init_sym2dollararg(iemgui, &iemgui->x_lab_unexpanded, iemgui->x_labelbindex, iemgui->x_lab); srlsym[0] = iemgui->x_snd_unexpanded; srlsym[1] = iemgui->x_rcv_unexpanded; srlsym[2] = iemgui->x_lab_unexpanded; } /* helper for iemgui_all_dollararg2sym */ static t_symbol*do_all_dollarg2sym(t_iemgui*iemgui, t_symbol**s, size_t index) { t_symbol*org = s[index]; if(org) { s[index] = canvas_realizedollar(iemgui->x_glist, org); } return org; } /* convert symbols in "$" form to the expanded symbols */ void iemgui_all_dollararg2sym(t_iemgui *iemgui, t_symbol **srlsym) { /* save unexpanded ones for later */ iemgui->x_snd_unexpanded = do_all_dollarg2sym(iemgui, srlsym, 0); iemgui->x_rcv_unexpanded = do_all_dollarg2sym(iemgui, srlsym, 1); iemgui->x_lab_unexpanded = do_all_dollarg2sym(iemgui, srlsym, 2); } static t_symbol* color2symbol(int col) { const int compat = (pd_compatibilitylevel < 48) ? 1 : 0; char colname[MAXPDSTRING]; colname[0] = colname[MAXPDSTRING-1] = 0; if (compat) { /* compatibility with Pd<=0.47: saves colors as numbers with limited resolution */ int col2 = -1 - (((0xfc0000 & col) >> 6)|((0xfc00 & col) >> 4)|((0xfc & col) >> 2)); snprintf(colname, MAXPDSTRING-1, "%d", col2); } else { snprintf(colname, MAXPDSTRING-1, "#%06x", col); } return gensym(colname); } static void iemgui_all_col2save(t_iemgui *iemgui, t_symbol**bflcol) { bflcol[0] = color2symbol(iemgui->x_bcol); bflcol[1] = color2symbol(iemgui->x_fcol); bflcol[2] = color2symbol(iemgui->x_lcol); } static int iemgui_getcolorarg(int index, int argc, t_atom*argv) { if(index < 0 || index >= argc) return 0; if(IS_A_FLOAT(argv,index)) return atom_getfloatarg(index, argc, argv); if(IS_A_SYMBOL(argv,index)) { t_symbol*s=atom_getsymbolarg(index, argc, argv); if ('#' == s->s_name[0]) { int col = (int)strtol(s->s_name+1, 0, 16); return col & 0xFFFFFF; } } return 0; } static int colfromatomload(t_atom*colatom) { int color; /* old-fashioned color argument, either a number or symbol evaluating to an integer */ if (colatom->a_type == A_FLOAT) color = atom_getfloat(colatom); else if (colatom->a_type == A_SYMBOL && (isdigit(colatom->a_w.w_symbol->s_name[0]) || colatom->a_w.w_symbol->s_name[0] == '-')) color = atoi(colatom->a_w.w_symbol->s_name); /* symbolic color */ else return (iemgui_getcolorarg(0, 1, colatom)); if (color < 0) { color = -1 - color; color = ((color & 0x3f000) << 6)|((color & 0xfc0) << 4)| ((color & 0x3f) << 2); } else { color = iemgui_modulo_color(color); color = iemgui_color_hex[color]; } return (color); } void iemgui_all_loadcolors(t_iemgui *iemgui, t_atom*bcol, t_atom*fcol, t_atom*lcol) { if(bcol)iemgui->x_bcol = colfromatomload(bcol); if(fcol)iemgui->x_fcol = colfromatomload(fcol); if(lcol)iemgui->x_lcol = colfromatomload(lcol); } int iemgui_compatible_colorarg(int index, int argc, t_atom* argv) { if (index < 0 || index >= argc) return 0; if(IS_A_FLOAT(argv,index)) { int col=atom_getfloatarg(index, argc, argv); if(col >= 0) { int idx = iemgui_modulo_color(col); return(iemgui_color_hex[(idx)]); } else return((-1 -col)&0xffffff); } return iemgui_getcolorarg(index, argc, argv); } void iemgui_send(void *x, t_iemgui *iemgui, t_symbol *s) { int sndable=1, oldsndrcvable=0; if(iemgui->x_fsf.x_rcv_able) oldsndrcvable |= IEM_GUI_OLD_RCV_FLAG; if(iemgui->x_fsf.x_snd_able) oldsndrcvable |= IEM_GUI_OLD_SND_FLAG; if(s && gensym("empty") == s) s = 0; if(s) { iemgui->x_snd_unexpanded = s; iemgui->x_snd = canvas_realizedollar(iemgui->x_glist, s); } else { iemgui->x_snd_unexpanded = &s_; iemgui->x_snd = 0; sndable = 0; } iemgui->x_fsf.x_snd_able = sndable; iemgui_verify_snd_ne_rcv(iemgui); if(glist_isvisible(iemgui->x_glist) && gobj_shouldvis((t_gobj *)x, iemgui->x_glist)) (*iemgui->x_draw)(x, iemgui->x_glist, IEM_GUI_DRAW_MODE_IO + oldsndrcvable); } void iemgui_receive(void *x, t_iemgui *iemgui, t_symbol *s) { int oldsndrcvable=0; if(iemgui->x_fsf.x_rcv_able) oldsndrcvable |= IEM_GUI_OLD_RCV_FLAG; if(iemgui->x_fsf.x_snd_able) oldsndrcvable |= IEM_GUI_OLD_SND_FLAG; if(s && gensym("empty") == s) s = 0; if(s) { iemgui->x_rcv_unexpanded = s; s = canvas_realizedollar(iemgui->x_glist, s); } else { iemgui->x_rcv_unexpanded = &s_; } if(s) { if(!iemgui->x_rcv || strcmp(s->s_name, iemgui->x_rcv->s_name)) { if(iemgui->x_fsf.x_rcv_able) pd_unbind(&iemgui->x_obj.ob_pd, iemgui->x_rcv); iemgui->x_rcv = s; pd_bind(&iemgui->x_obj.ob_pd, iemgui->x_rcv); } } else if(iemgui->x_fsf.x_rcv_able) { pd_unbind(&iemgui->x_obj.ob_pd, iemgui->x_rcv); iemgui->x_rcv = s; } iemgui->x_fsf.x_rcv_able = (s!=0); iemgui_verify_snd_ne_rcv(iemgui); if(glist_isvisible(iemgui->x_glist) && gobj_shouldvis((t_gobj *)x, iemgui->x_glist)) (*iemgui->x_draw)(x, iemgui->x_glist, IEM_GUI_DRAW_MODE_IO + oldsndrcvable); } static void iemgui_dolabelpos(t_object*obj, t_iemgui*iemgui) { int zoom = glist_getzoom(iemgui->x_glist); int x0 = text_xpix((t_object *)obj, iemgui->x_glist); int y0 = text_ypix((t_object *)obj, iemgui->x_glist); int dx = iemgui->x_ldx, dy = iemgui->x_ldy; char tag[128]; sprintf(tag, "%pLABEL", obj); if(gensym("") == iemgui->x_lab) { /* put empty labels where they don't create scrollbars */ dx = 0; dy = 7; } pdgui_vmess(0, "crs ii", glist_getcanvas(iemgui->x_glist), "coords", tag, x0 + dx*zoom, y0 + dy*zoom); } void iemgui_dolabel(void *x, t_iemgui *iemgui, t_symbol *s, int senditup) { t_symbol *empty = gensym(""); t_symbol *old = iemgui->x_lab; s = s?canvas_realizedollar(iemgui->x_glist, s):0; if (!(s && s->s_name && s->s_name[0] && strcmp(s->s_name, "empty"))) s = empty; iemgui->x_lab = s; if(senditup < 0) { senditup = (glist_isvisible(iemgui->x_glist) && iemgui->x_lab != old); } if(senditup) { const char*label = s->s_name; int have_label = (s != empty); char tag[128]; sprintf(tag, "%pLABEL", x); pdgui_vmess("pdtk_text_set", "cs s", glist_getcanvas(iemgui->x_glist), tag, have_label?s->s_name:""); iemgui_dolabelpos(x, iemgui); } } void iemgui_label(void *x, t_iemgui *iemgui, t_symbol *s) { iemgui->x_lab_unexpanded = s; iemgui_dolabel(x, iemgui, s, -1); } void iemgui_label_pos(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av) { iemgui->x_ldx = (int)atom_getfloatarg(0, ac, av); iemgui->x_ldy = (int)atom_getfloatarg(1, ac, av); if(glist_isvisible(iemgui->x_glist)) iemgui_dolabelpos(x, iemgui); } void iemgui_label_font(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av) { int zoom = glist_getzoom(iemgui->x_glist); int f = (int)atom_getfloatarg(0, ac, av); if(f == 1) strcpy(iemgui->x_font, "helvetica"); else if(f == 2) strcpy(iemgui->x_font, "times"); else { f = 0; strcpy(iemgui->x_font, sys_font); } iemgui->x_fsf.x_font_style = f; f = (int)atom_getfloatarg(1, ac, av); if(f < 4) f = 4; iemgui->x_fontsize = f; if(glist_isvisible(iemgui->x_glist)) { char tag[128]; t_atom fontatoms[3]; sprintf(tag, "%pLABEL", x); SETSYMBOL(fontatoms+0, gensym(iemgui->x_font)); SETFLOAT (fontatoms+1, -iemgui->x_fontsize*zoom); SETSYMBOL(fontatoms+2, gensym(sys_fontweight)); pdgui_vmess(0, "crs rA", glist_getcanvas(iemgui->x_glist), "itemconfigure", tag, "-font", 3, fontatoms); } } static void iemgui_do_drawmove(void *x, t_iemgui*iemgui) { if(glist_isvisible(iemgui->x_glist)) { int xpos = text_xpix(&iemgui->x_obj, iemgui->x_glist); int ypos = text_ypix(&iemgui->x_obj, iemgui->x_glist); (*iemgui->x_draw)(x, iemgui->x_glist, IEM_GUI_DRAW_MODE_MOVE); iemgui->x_private->p_prevX = xpos; iemgui->x_private->p_prevY = ypos; canvas_fixlinesfor(iemgui->x_glist, (t_text*)x); } } void iemgui_size(void *x, t_iemgui *iemgui) { if(glist_isvisible(iemgui->x_glist)) { (*iemgui->x_draw)(x, iemgui->x_glist, IEM_GUI_DRAW_MODE_CONFIG); (*iemgui->x_draw)(x, iemgui->x_glist, IEM_GUI_DRAW_MODE_IO); canvas_fixlinesfor(iemgui->x_glist, (t_text*)x); } } void iemgui_delta(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av) { int zoom = glist_getzoom(iemgui->x_glist); iemgui->x_obj.te_xpix += (int)atom_getfloatarg(0, ac, av); iemgui->x_obj.te_ypix += (int)atom_getfloatarg(1, ac, av); iemgui_do_drawmove(x, iemgui); } void iemgui_pos(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av) { int zoom = glist_getzoom(iemgui->x_glist); iemgui->x_obj.te_xpix = (int)atom_getfloatarg(0, ac, av); iemgui->x_obj.te_ypix = (int)atom_getfloatarg(1, ac, av); iemgui_do_drawmove(x, iemgui); } void iemgui_color(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av) { if (ac >= 1) iemgui->x_bcol = iemgui_compatible_colorarg(0, ac, av); if (ac == 2 && pd_compatibilitylevel < 47) /* old versions of Pd updated foreground and label color if only two args; now we do it more coherently. */ iemgui->x_lcol = iemgui_compatible_colorarg(1, ac, av); else if (ac >= 2) iemgui->x_fcol = iemgui_compatible_colorarg(1, ac, av); if (ac >= 3) iemgui->x_lcol = iemgui_compatible_colorarg(2, ac, av); if(glist_isvisible(iemgui->x_glist)) (*iemgui->x_draw)(x, iemgui->x_glist, IEM_GUI_DRAW_MODE_CONFIG); } void iemgui_displace(t_gobj *z, t_glist *glist, int dx, int dy) { t_iemgui *x = (t_iemgui *)z; x->x_obj.te_xpix += dx; x->x_obj.te_ypix += dy; iemgui_do_drawmove(x, x); } void iemgui_select(t_gobj *z, t_glist *glist, int selected) { t_iemgui *x = (t_iemgui *)z; x->x_fsf.x_selected = selected; if(glist_isvisible(x->x_glist)) (*x->x_draw)((void *)z, glist, IEM_GUI_DRAW_MODE_SELECT); } void iemgui_delete(t_gobj *z, t_glist *glist) { canvas_deletelinesfor(glist, (t_text *)z); } void iemgui_vis(t_gobj *z, t_glist *glist, int vis) { t_iemgui *x = (t_iemgui *)z; (*x->x_draw)((void *)z, glist, IEM_GUI_DRAW_MODE_ERASE); if (vis) (*x->x_draw)((void *)z, glist, IEM_GUI_DRAW_MODE_NEW); else { sys_unqueuegui(z); } x->x_private->p_prevX = text_xpix(&x->x_obj, glist); x->x_private->p_prevY = text_ypix(&x->x_obj, glist); } /* store saveable symbols (with spaces and dollars escaped) into srl[3] */ void iemgui_save(t_iemgui *iemgui, t_symbol **srl, t_symbol**bflcol) { int i; srl[0] = iemgui->x_snd; srl[1] = iemgui->x_rcv; srl[2] = iemgui->x_lab; iemgui_all_sym2dollararg(iemgui, srl); for(i=0; i<3; i++) { if(!srl[i] || !srl[i]->s_name || !srl[i]->s_name[0]) srl[i]=gensym("empty"); } iemgui_all_col2save(iemgui, bflcol); } /* inform GUIs that glist's zoom is about to change. The glist will take care of x,y locations but we have to adjust width and height */ void iemgui_zoom(t_iemgui *iemgui, t_floatarg zoom) { int oldzoom = iemgui->x_glist->gl_zoom; if (oldzoom < 1) oldzoom = 1; iemgui->x_w = (int)(iemgui->x_w)/oldzoom*(int)zoom; iemgui->x_h = (int)(iemgui->x_h)/oldzoom*(int)zoom; } /* when creating a new GUI from menu onto a zoomed canvas, pretend to change the canvas's zoom so we'll get properly sized */ void iemgui_newzoom(t_iemgui *iemgui) { if (iemgui->x_glist->gl_zoom != 1) { int newzoom = iemgui->x_glist->gl_zoom; iemgui->x_glist->gl_zoom = 1; iemgui_zoom(iemgui, (t_floatarg)newzoom); iemgui->x_glist->gl_zoom = newzoom; } } void iemgui_properties(t_iemgui *iemgui, t_symbol **srl) { char label[MAXPDSTRING]; int i; srl[0] = iemgui->x_snd; srl[1] = iemgui->x_rcv; srl[2] = iemgui->x_lab; iemgui_all_sym2dollararg(iemgui, srl); for(i=0; i<3; i++) { if(srl[i]) srl[i] = gensym(pdgui_strnescape(label, sizeof(label), srl[i]->s_name, strlen(srl[i]->s_name))); } } void iemgui_new_dialog(void*x, t_iemgui*iemgui, const char*objname, t_float width, t_float width_min, t_float height, t_float height_min, t_float range_min, t_float range_max, int schedule, int mode, /* lin0_log1 */ const char* label_mode0, const char* label_mode1, int canloadbang, int steady, int number) { char objname_[MAXPDSTRING]; t_symbol *srl[3]; iemgui_properties(iemgui, srl); sprintf(objname_, "|%s|", objname); pdgui_stub_vnew(&iemgui->x_obj.ob_pd, "pdtk_iemgui_dialog", x, "r s ffs ffs sfsfs i iss ii si sss ii ii kkk", objname_, "", width, width_min, "", height, height_min, "", "", range_min, "", range_max, "", schedule, mode, label_mode0, label_mode1, canloadbang?iemgui->x_isa.x_loadinit:-1, steady, "", number, srl[0]?srl[0]->s_name:"", srl[1]?srl[1]->s_name:"", srl[2]?srl[2]->s_name:"", iemgui->x_ldx, iemgui->x_ldy, iemgui->x_fsf.x_font_style, iemgui->x_fontsize, iemgui->x_bcol, iemgui->x_fcol, iemgui->x_lcol); } int iemgui_dialog(t_iemgui *iemgui, t_symbol **srl, int argc, t_atom *argv) { char str[144]; int init = (int)atom_getfloatarg(5, argc, argv); int ldx = (int)atom_getfloatarg(10, argc, argv); int ldy = (int)atom_getfloatarg(11, argc, argv); int f = (int)atom_getfloatarg(12, argc, argv); int fs = (int)atom_getfloatarg(13, argc, argv); int bcol = (int)iemgui_getcolorarg(14, argc, argv); int fcol = (int)iemgui_getcolorarg(15, argc, argv); int lcol = (int)iemgui_getcolorarg(16, argc, argv); int rcv_changed=0, oldsndrcvable=0; int i; if(iemgui->x_fsf.x_rcv_able) oldsndrcvable |= IEM_GUI_OLD_RCV_FLAG; if(iemgui->x_fsf.x_snd_able) oldsndrcvable |= IEM_GUI_OLD_SND_FLAG; if(IS_A_SYMBOL(argv,7)) srl[0] = atom_getsymbolarg(7, argc, argv); else if(IS_A_FLOAT(argv,7)) { srl[0] = gensym("empty"); } if(IS_A_SYMBOL(argv,8)) srl[1] = atom_getsymbolarg(8, argc, argv); else if(IS_A_FLOAT(argv,8)) { srl[1] = gensym("empty"); } if(IS_A_SYMBOL(argv,9)) srl[2] = atom_getsymbolarg(9, argc, argv); else if(IS_A_FLOAT(argv,9)) { sprintf(str, "%g", atom_getfloatarg(9, argc, argv)); srl[2] = gensym(str); } if(init != 0) init = 1; iemgui->x_isa.x_loadinit = init; for(i=0; i<3; i++) if(!srl_is_valid(srl[i]) || (!strcmp(srl[i]->s_name, "empty"))) srl[i] = &s_; /* expand dollargs * after this, srl holds the $-expanded versions of the labels * and iemgui->x_(snd|rcv|lab)_unexpanded hold the unexpanded versions */ iemgui_all_dollararg2sym(iemgui, srl); /* check if the receiver changed */ if(0 || (!srl_is_valid(iemgui->x_rcv) && srl_is_valid(srl[1])) /* there was none, but now there is */ || ( srl_is_valid(iemgui->x_rcv) && !(srl_is_valid(srl[1]))) /* there was one, but now there is */ || ( srl_is_valid(iemgui->x_rcv) && srl_is_valid(srl[1]) && iemgui->x_rcv != srl[1])) /* both are valid, but changed */ rcv_changed = 1; /* if the receiver changed (and was previously set), unbind it */ if(rcv_changed && srl_is_valid(iemgui->x_rcv)) pd_unbind(&iemgui->x_obj.ob_pd, iemgui->x_rcv); iemgui->x_snd = srl[0]; iemgui->x_fsf.x_snd_able = srl_is_valid(srl[0]); iemgui->x_rcv = srl[1]; iemgui->x_fsf.x_rcv_able = srl_is_valid(srl[1]); iemgui->x_lab = srl[2]; iemgui->x_lcol = lcol & 0xffffff; iemgui->x_fcol = fcol & 0xffffff; iemgui->x_bcol = bcol & 0xffffff; iemgui->x_ldx = ldx; iemgui->x_ldy = ldy; if(f == 1) strcpy(iemgui->x_font, "helvetica"); else if(f == 2) strcpy(iemgui->x_font, "times"); else { f = 0; strcpy(iemgui->x_font, sys_font); } iemgui->x_fsf.x_font_style = f; if(fs < 4) fs = 4; iemgui->x_fontsize = fs; /* if the receiver changed (and is now set), bind it */ if(rcv_changed && srl_is_valid(iemgui->x_rcv)) pd_bind(&iemgui->x_obj.ob_pd, iemgui->x_rcv); iemgui_verify_snd_ne_rcv(iemgui); canvas_dirty(iemgui->x_glist, 1); return(oldsndrcvable); } void iemgui_setdialogatoms(t_iemgui *iemgui, int argc, t_atom*argv) { #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) t_float zoom = iemgui->x_glist->gl_zoom; t_symbol *srl[3]; int for_undo = 1; int i; for(i=0; ix_snd_unexpanded; srl[1] = iemgui->x_rcv_unexpanded; srl[2] = iemgui->x_lab_unexpanded; /* just in case one of the labels is NULL, set it to something valid */ for(i=0; i<3; i++) if (!srl[i]) srl[i]=s_empty; } else { iemgui_properties(iemgui, srl); } if(argc> 0) SETFLOAT (argv+ 0, iemgui->x_w/zoom); if(argc> 1) SETFLOAT (argv+ 1, iemgui->x_h/zoom); if(argc> 5) SETFLOAT (argv+ 5, iemgui->x_isa.x_loadinit); if(argc> 6) SETFLOAT (argv+ 6, 1); /* num */ if(argc> 7) SETSYMBOL(argv+ 7, srl[0]); if(argc> 8) SETSYMBOL(argv+ 8, srl[1]); if(argc> 9) SETSYMBOL(argv+ 9, srl[2]); if(argc>10) SETFLOAT (argv+10, iemgui->x_ldx); if(argc>11) SETFLOAT (argv+11, iemgui->x_ldy); if(argc>12) SETFLOAT (argv+12, iemgui->x_fsf.x_font_style); if(argc>13) SETFLOAT (argv+13, iemgui->x_fontsize); if(argc>14) SETCOLOR (argv+14, iemgui->x_bcol); if(argc>15) SETCOLOR (argv+15, iemgui->x_fcol); if(argc>16) SETCOLOR (argv+16, iemgui->x_lcol); } /* pre-0.46 the flags were 1 for 'loadinit' and 1<<20 for 'scale'. Starting in 0.46, take either 1<<20 or 1<<1 for 'scale' and save to both bits (so that old versions can read files we write). In the future (2015?) we can stop writing the annoying 1<<20 bit. */ #define LOADINIT 1 #define SCALE 2 #define SCALEBIS (1<<20) void iem_inttosymargs(t_iem_init_symargs *symargp, int n) { memset(symargp, 0, sizeof(*symargp)); symargp->x_loadinit = ((n & LOADINIT) != 0); symargp->x_scale = ((n & SCALE) || (n & SCALEBIS)) ; symargp->x_flashed = 0; symargp->x_locked = 0; } int iem_symargstoint(t_iem_init_symargs *symargp) { return ((symargp->x_loadinit ? LOADINIT : 0) | (symargp->x_scale ? (SCALE | SCALEBIS) : 0)); } void iem_inttofstyle(t_iem_fstyle_flags *fstylep, int n) { memset(fstylep, 0, sizeof(*fstylep)); fstylep->x_font_style = (n >> 0); fstylep->x_shiftdown = 0; fstylep->x_selected = 0; fstylep->x_finemoved = 0; fstylep->x_put_in2out = 0; fstylep->x_change = 0; fstylep->x_thick = 0; fstylep->x_lin0_log1 = 0; fstylep->x_steady = 0; } int iem_fstyletoint(t_iem_fstyle_flags *fstylep) { return ((fstylep->x_font_style << 0) & 63); } static void iemgui_draw_new(t_iemgui*x, t_glist*glist) {;} static void iemgui_draw_config(t_iemgui*x, t_glist*glist) {;} static void iemgui_draw_update(t_iemgui*x, t_glist*glist) {;} static void iemgui_draw_select(t_iemgui*x, t_glist*glist) {;} static void iemgui_draw_iolets(t_iemgui*x, t_glist*glist, int old_snd_rcv_flags) { const int zoom = x->x_glist->gl_zoom; int xpos = text_xpix(&x->x_obj, glist); int ypos = text_ypix(&x->x_obj, glist); int iow = IOWIDTH * zoom, ioh = IEM_GUI_IOHEIGHT * zoom; t_canvas *canvas = glist_getcanvas(glist); char tag_object[128], tag_label[128], tag[128]; char *tags[] = {tag_object, tag}; (void)old_snd_rcv_flags; sprintf(tag_object, "%pOBJ", x); sprintf(tag_label, "%pLABEL", x); /* re-create outlet */ sprintf(tag, "%pOUT%d", x, 0); pdgui_vmess(0, "crs", canvas, "delete", tag); if(!x->x_fsf.x_snd_able) { pdgui_vmess(0, "crr iiii rs rS", canvas, "create", "rectangle", xpos, ypos + x->x_h + zoom - ioh, xpos + iow, ypos + x->x_h, "-fill", "black", "-tags", 2, tags); /* keep label above outlet */ pdgui_vmess(0, "crss", canvas, "lower", tag, tag_label); } /* re-create inlet */ sprintf(tag, "%pIN%d", x, 0); pdgui_vmess(0, "crs", canvas, "delete", tag); if(!x->x_fsf.x_rcv_able) { pdgui_vmess(0, "crr iiii rs rS", canvas, "create", "rectangle", xpos, ypos, xpos + iow, ypos - zoom + ioh, "-fill", "black", "-tags", 2, tags); /* keep label above inlet */ pdgui_vmess(0, "crss", canvas, "lower", tag, tag_label); } } static void iemgui_draw_erase(t_iemgui* x, t_glist* glist) { t_canvas *canvas = glist_getcanvas(glist); char tag_object[128]; sprintf(tag_object, "%pOBJ", x); pdgui_vmess(0, "crs", canvas, "delete", tag_object); } static void iemgui_draw_move(t_iemgui *x, t_glist *glist) { t_canvas *canvas = glist_getcanvas(glist); int dx = text_xpix(&x->x_obj, glist) - x->x_private->p_prevX; int dy = text_ypix(&x->x_obj, glist) - x->x_private->p_prevY; char tag_object[128]; sprintf(tag_object, "%pOBJ", x); pdgui_vmess(0, "crs ii", canvas, "move", tag_object, dx, dy); } static void iemgui_draw(t_iemgui *x, t_glist *glist, int mode) { #define DRAW_FUN(fun, x, glist) { \ t_iemdrawfunptr do_draw = x->x_private->p_widget.draw_##fun; \ if (!do_draw) do_draw = (t_iemdrawfunptr)iemgui_draw_##fun; \ do_draw(x, glist); \ } switch(mode) { case (IEM_GUI_DRAW_MODE_UPDATE): { t_iemdrawfunptr draw_update = x->x_private->p_widget.draw_update; if(!draw_update) draw_update = (t_iemdrawfunptr)iemgui_draw_update; sys_queuegui(x, x->x_glist, (t_guicallbackfn)draw_update); } break; case (IEM_GUI_DRAW_MODE_MOVE): DRAW_FUN(move, x, glist); break; case (IEM_GUI_DRAW_MODE_NEW): DRAW_FUN(new, x, glist); break; case (IEM_GUI_DRAW_MODE_SELECT): DRAW_FUN(select, x, glist); break; case (IEM_GUI_DRAW_MODE_ERASE): DRAW_FUN(erase, x, glist); break; case (IEM_GUI_DRAW_MODE_CONFIG): DRAW_FUN(config, x, glist); break; default: if(x->x_private->p_widget.draw_iolets) x->x_private->p_widget.draw_iolets(x, glist, mode - IEM_GUI_DRAW_MODE_IO); else iemgui_draw_iolets(x, glist, mode - IEM_GUI_DRAW_MODE_IO); } } void iemgui_setdrawfunctions(t_iemgui *iemgui, t_iemgui_drawfunctions *w) { #define SET_DRAW(x, fun) \ x->x_private->p_widget.draw_##fun = w->draw_##fun SET_DRAW(iemgui, new); SET_DRAW(iemgui, config); SET_DRAW(iemgui, iolets); SET_DRAW(iemgui, update); SET_DRAW(iemgui, select); SET_DRAW(iemgui, erase); SET_DRAW(iemgui, move); } t_iemgui *iemgui_new(t_class*cls) { t_iemgui *x = (t_iemgui *)pd_new(cls); t_glist *cnv = canvas_getcurrent(); int fs = cnv->gl_font; x->x_glist = cnv; x->x_private = (t_iemgui_private*)getbytes(sizeof(*x->x_private)); x->x_draw = (t_iemfunptr)iemgui_draw; x->x_fontsize = (fs<4)?4:fs; /* int fs = x->x_gui.x_fontsize; x->x_gui.x_fontsize = (fs < 4)?4:fs; */ iem_inttosymargs(&x->x_isa, 0); iem_inttofstyle(&x->x_fsf, 0); x->x_bcol = 0xFCFCFC; x->x_fcol = 0x00; x->x_lcol = 0x00; return x; } #if 1 /* LEGACY (for binary compatibility with existing externals) * DO NOT USE */ /* g_all_guis.h */ /* *********** */ int iemgui_clip_font(int size) { if(size < IEM_FONT_MINSIZE) size = IEM_FONT_MINSIZE; return(size); } void iemgui_all_dollar2raute(t_symbol **srlsym) { srlsym[0] = iemgui_dollar2raute(srlsym[0]); srlsym[1] = iemgui_dollar2raute(srlsym[1]); srlsym[2] = iemgui_dollar2raute(srlsym[2]); } void iemgui_all_raute2dollar(t_symbol **srlsym) { srlsym[0] = iemgui_raute2dollar(srlsym[0]); srlsym[1] = iemgui_raute2dollar(srlsym[1]); srlsym[2] = iemgui_raute2dollar(srlsym[2]); } /* g_canvas.h */ /* ********** */ t_symbol *iemgui_put_in_braces(t_symbol *s) { const char *s1; char buf[MAXPDSTRING+1], *s2; int i = 0; if (strlen(s->s_name) >= MAXPDSTRING) return (s); for (s1 = s->s_name, s2 = buf; ; s1++, s2++, i++) { if (i == 0) { *s2 = '{'; s2++; } if (!(*s2 = *s1)) { *s2 = '}'; s2++; *s2 = '\0'; break; } } return(gensym(buf)); } /* no header */ /* ********* */ void iemgui_all_put_in_braces(t_symbol **srlsym) { srlsym[0] = iemgui_put_in_braces(srlsym[0]); srlsym[1] = iemgui_put_in_braces(srlsym[1]); srlsym[2] = iemgui_put_in_braces(srlsym[2]); } /* for compatibility with pre-0.47 unofficial IEM GUIS like "knob". */ void iemgui_all_colfromload(t_iemgui *iemgui, int *bflcol) { static int warned = 0; if (!warned) { post("warning: external GUI object uses obsolete Pd function %s()", __FUNCTION__); warned = 1; } if(bflcol[0] < 0) { bflcol[0] = -1 - bflcol[0]; iemgui->x_bcol = ((bflcol[0] & 0x3f000) << 6)|((bflcol[0] & 0xfc0) << 4)| ((bflcol[0] & 0x3f) << 2); } else { bflcol[0] = iemgui_modulo_color(bflcol[0]); iemgui->x_bcol = iemgui_color_hex[bflcol[0]]; } if(bflcol[1] < 0) { bflcol[1] = -1 - bflcol[1]; iemgui->x_fcol = ((bflcol[1] & 0x3f000) << 6)|((bflcol[1] & 0xfc0) << 4)| ((bflcol[1] & 0x3f) << 2); } else { bflcol[1] = iemgui_modulo_color(bflcol[1]); iemgui->x_fcol = iemgui_color_hex[bflcol[1]]; } if(bflcol[2] < 0) { bflcol[2] = -1 - bflcol[2]; iemgui->x_lcol = ((bflcol[2] & 0x3f000) << 6)|((bflcol[2] & 0xfc0) << 4)| ((bflcol[2] & 0x3f) << 2); } else { bflcol[2] = iemgui_modulo_color(bflcol[2]); iemgui->x_lcol = iemgui_color_hex[bflcol[2]]; } } #endif ================================================ FILE: libs/libpd/pure-data/src/g_all_guis.h ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* g_7_guis.h written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */ #ifndef __g_all_guis_h_ #include "g_canvas.h" #define IEM_GUI_COLNR_WHITE 0 #define IEM_GUI_COLNR_ML_GREY 1 #define IEM_GUI_COLNR_D_GREY 2 #define IEM_GUI_COLNR_L_RED 3 #define IEM_GUI_COLNR_L_ORANGE 4 #define IEM_GUI_COLNR_L_YELLOW 5 #define IEM_GUI_COLNR_L_GREEN 6 #define IEM_GUI_COLNR_L_CYAN 7 #define IEM_GUI_COLNR_L_BLUE 8 #define IEM_GUI_COLNR_L_MAGENTA 9 #define IEM_GUI_COLNR_LL_GREY 10 #define IEM_GUI_COLNR_M_GREY 11 #define IEM_GUI_COLNR_DD_GREY 12 #define IEM_GUI_COLNR_RED 13 #define IEM_GUI_COLNR_ORANGE 14 #define IEM_GUI_COLNR_YELLOW 15 #define IEM_GUI_COLNR_GREEN 16 #define IEM_GUI_COLNR_CYAN 17 #define IEM_GUI_COLNR_BLUE 18 #define IEM_GUI_COLNR_MAGENTA 19 #define IEM_GUI_COLNR_L_GREY 20 #define IEM_GUI_COLNR_MD_GREY 21 #define IEM_GUI_COLNR_BLACK 22 #define IEM_GUI_COLNR_D_RED 23 #define IEM_GUI_COLNR_D_ORANGE 24 #define IEM_GUI_COLNR_D_YELLOW 25 #define IEM_GUI_COLNR_D_GREEN 26 #define IEM_GUI_COLNR_D_CYAN 27 #define IEM_GUI_COLNR_D_BLUE 28 #define IEM_GUI_COLNR_D_MAGENTA 29 #define IEM_GUI_COLOR_SELECTED 0x0000FF #define IEM_GUI_COLOR_NORMAL 0x000000 #define IEM_GUI_COLOR_EDITED 0xFF0000 #define IEM_GUI_MAX_COLOR 30 //#define IEM_GUI_DEFAULTSIZE 15 /* the "+3+2" = "+TMARGIN+BMARGIN" from g_rtext.c */ #define IEM_GUI_DEFAULTSIZE (sys_zoomfontheight(canvas_getcurrent()->gl_font, 1, 0) + 2 + 3) #define IEM_GUI_DEFAULTSIZE_SCALE IEM_GUI_DEFAULTSIZE/15. #define IEM_GUI_MINSIZE 8 #define IEM_GUI_MAXSIZE 1000 #define IEM_SL_DEFAULTSIZE 128 #define IEM_SL_MINSIZE 2 #define IEM_FONT_MINSIZE 4 #define IEM_BNG_DEFAULTHOLDFLASHTIME 250 #define IEM_BNG_DEFAULTBREAKFLASHTIME 50 #define IEM_BNG_MINHOLDFLASHTIME 50 #define IEM_BNG_MINBREAKFLASHTIME 10 #define IEM_VU_DEFAULTSIZE 4 #define IEM_VU_LARGESMALL 2 #define IEM_VU_MINSIZE 2 #define IEM_VU_MAXSIZE 25 #define IEM_VU_STEPS 40 #define IEM_VU_MINDB -99.9 #define IEM_VU_MAXDB 12.0 #define IEM_VU_OFFSET 100.0 #define IEM_RADIO_MAX 128 #define IEM_SYM_UNIQUE_SND 256 #define IEM_SYM_UNIQUE_RCV 512 #define IEM_SYM_UNIQUE_LAB 1024 #define IEM_SYM_UNIQUE_ALL 1792 #define IEM_FONT_STYLE_ALL 255 #define IEM_MAX_SYM_LEN 127 #define IEM_GUI_DRAW_MODE_UPDATE 0 #define IEM_GUI_DRAW_MODE_MOVE 1 #define IEM_GUI_DRAW_MODE_NEW 2 #define IEM_GUI_DRAW_MODE_SELECT 3 #define IEM_GUI_DRAW_MODE_ERASE 4 #define IEM_GUI_DRAW_MODE_CONFIG 5 #define IEM_GUI_DRAW_MODE_IO 6 #define IEM_GUI_IOHEIGHT IHEIGHT #define IS_A_POINTER(atom,index) ((atom+index)->a_type == A_POINTER) #define IS_A_FLOAT(atom,index) ((atom+index)->a_type == A_FLOAT) #define IS_A_SYMBOL(atom,index) ((atom+index)->a_type == A_SYMBOL) #define IS_A_DOLLAR(atom,index) ((atom+index)->a_type == A_DOLLAR) #define IS_A_DOLLSYM(atom,index) ((atom+index)->a_type == A_DOLLSYM) #define IEM_FSTYLE_FLAGS_ALL 0x007fffff #define IEM_INIT_ARGS_ALL 0x01ffffff #define IEM_GUI_OLD_SND_FLAG 1 #define IEM_GUI_OLD_RCV_FLAG 2 #define IEMGUI_MAX_NUM_LEN 32 typedef enum { horizontal = 0, vertical = 1, } t_iem_orientation; #define IEMGUI_ZOOM(x) ((x)->x_gui.x_glist->gl_zoom) typedef struct _iem_fstyle_flags { unsigned int x_font_style:6; unsigned int x_rcv_able:1; unsigned int x_snd_able:1; unsigned int x_lab_is_unique:1; unsigned int x_rcv_is_unique:1; unsigned int x_snd_is_unique:1; unsigned int x_lab_arg_tail_len:6; unsigned int x_lab_is_arg_num:6; unsigned int x_shiftdown:1; unsigned int x_selected:1; unsigned int x_finemoved:1; unsigned int x_put_in2out:1; unsigned int x_change:1; unsigned int x_thick:1; unsigned int x_lin0_log1:1; unsigned int x_steady:1; } t_iem_fstyle_flags; typedef struct _iem_init_symargs { unsigned int x_loadinit:1; unsigned int x_rcv_arg_tail_len:6; unsigned int x_snd_arg_tail_len:6; unsigned int x_rcv_is_arg_num:6; unsigned int x_snd_is_arg_num:6; unsigned int x_scale:1; unsigned int x_flashed:1; unsigned int x_locked:1; } t_iem_init_symargs; typedef void (*t_iemfunptr)(void *x, t_glist *glist, int mode); typedef void (*t_iemdrawfunptr)(void *x, t_glist *glist); typedef struct _iemgui_drawfunctions { t_iemdrawfunptr draw_new; /* create all widgets */ t_iemdrawfunptr draw_config; /* reconfigure (draw, but don't create) all widgets (except iolets) */ t_iemfunptr draw_iolets; /* reconfigure (draw, but don't create) all iolets (0 uses default iolets function) */ t_iemdrawfunptr draw_update; /* update the changeable part of the iemgui (e.g. the number in a numbox) */ t_iemdrawfunptr draw_select; /* highlight object when it's selected */ t_iemdrawfunptr draw_erase; /* destroy all widgets; (0 uses default erase function) */ t_iemdrawfunptr draw_move; /* move all widgets; (0 uses default move function) */ } t_iemgui_drawfunctions; typedef struct _iemgui { t_object x_obj; t_glist *x_glist; t_iemfunptr x_draw; int x_h; int x_w; struct _iemgui_private *x_private; int x_ldx; int x_ldy; char x_font[MAXPDSTRING]; /* font names can be long! */ t_iem_fstyle_flags x_fsf; int x_fontsize; t_iem_init_symargs x_isa; int x_fcol; int x_bcol; int x_lcol; /* send/receive/label as used ($args expanded) */ t_symbol *x_snd; /* send symbol */ t_symbol *x_rcv; /* receive */ t_symbol *x_lab; /* label */ /* same, with $args unexpanded */ t_symbol *x_snd_unexpanded; /* NULL=uninitialized; gensym("")=empty */ t_symbol *x_rcv_unexpanded; t_symbol *x_lab_unexpanded; int x_binbufindex; /* where in binbuf to find these */ int x_labelbindex; /* where in binbuf to find label */ } t_iemgui; typedef struct _bng { t_iemgui x_gui; int x_flashed; int x_flashtime_break; int x_flashtime_hold; t_clock *x_clock_hld; t_clock *x_clock_brk; t_clock *x_clock_lck; double x_lastflashtime; } t_bng; typedef struct _slider { t_iemgui x_gui; int x_pos; int x_val; int x_lin0_log1; int x_steady; double x_min; double x_max; double x_k; t_float x_fval; t_iem_orientation x_orientation; } t_slider; typedef struct _radio { t_iemgui x_gui; int x_on; int x_on_old; /* LATER delete this; it's used for old version */ int x_change; int x_number; int x_drawn; t_float x_fval; t_iem_orientation x_orientation; int x_compat; /* old version */ } t_radio; typedef struct _toggle { t_iemgui x_gui; t_float x_on; t_float x_nonzero; } t_toggle; typedef struct _my_canvas { t_iemgui x_gui; t_atom x_at[3]; int x_vis_w; int x_vis_h; } t_my_canvas; typedef struct _vu { t_iemgui x_gui; int x_led_size; int x_peak; int x_rms; t_float x_fp; t_float x_fr; int x_scale; void *x_out_rms; void *x_out_peak; unsigned int x_updaterms:1; unsigned int x_updatepeak:1; } t_vu; typedef struct _my_numbox { t_iemgui x_gui; t_clock *x_clock_reset; t_clock *x_clock_wait; t_float x_val; double x_min; double x_max; double x_k; int x_lin0_log1; char x_buf[IEMGUI_MAX_NUM_LEN]; int x_numwidth; int x_log_height; } t_my_numbox; extern int iemgui_color_hex[]; extern int iemgui_vu_db2i[]; extern int iemgui_vu_col[]; extern char *iemgui_vu_scale_str[]; EXTERN int iemgui_clip_size(int size); EXTERN int iemgui_clip_font(int size); EXTERN t_symbol *iemgui_dollararg2sym(t_symbol *s, int nth_arg, int tail_len, int pargc, t_atom *pargv); EXTERN void iemgui_verify_snd_ne_rcv(t_iemgui *iemgui); EXTERN void iemgui_all_sym2dollararg(t_iemgui *iemgui, t_symbol **srlsym); EXTERN t_symbol *iemgui_new_dogetname(t_iemgui *iemgui, int indx, t_atom *argv); EXTERN void iemgui_new_getnames(t_iemgui *iemgui, int indx, t_atom *argv); EXTERN void iemgui_all_dollararg2sym(t_iemgui *iemgui, t_symbol **srlsym); EXTERN void iemgui_all_loadcolors(t_iemgui *iemgui, t_atom*bcol, t_atom*fcol, t_atom*lcol); EXTERN void iemgui_all_dollar2raute(t_symbol **srlsym); EXTERN void iemgui_all_raute2dollar(t_symbol **srlsym); EXTERN void iemgui_send(void *x, t_iemgui *iemgui, t_symbol *s); EXTERN void iemgui_receive(void *x, t_iemgui *iemgui, t_symbol *s); EXTERN void iemgui_label(void *x, t_iemgui *iemgui, t_symbol *s); EXTERN void iemgui_label_pos(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av); EXTERN void iemgui_label_font(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av); EXTERN void iemgui_size(void *x, t_iemgui *iemgui); EXTERN void iemgui_delta(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av); EXTERN void iemgui_pos(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av); EXTERN void iemgui_color(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av); EXTERN void iemgui_displace(t_gobj *z, t_glist *glist, int dx, int dy); EXTERN void iemgui_select(t_gobj *z, t_glist *glist, int selected); EXTERN void iemgui_delete(t_gobj *z, t_glist *glist); EXTERN void iemgui_vis(t_gobj *z, t_glist *glist, int vis); EXTERN void iemgui_save(t_iemgui *iemgui, t_symbol **srl, t_symbol **bflcol); EXTERN void iemgui_zoom(t_iemgui *iemgui, t_floatarg zoom); EXTERN void iemgui_newzoom(t_iemgui *iemgui); EXTERN void iemgui_properties(t_iemgui *iemgui, t_symbol **srl); EXTERN int iemgui_dialog(t_iemgui *iemgui, t_symbol **srl, int argc, t_atom *argv); EXTERN void iemgui_setdialogatoms(t_iemgui *iemgui, int argc, t_atom*argv); EXTERN int canvas_getdollarzero(void); EXTERN void iem_inttosymargs(t_iem_init_symargs *symargp, int n); EXTERN int iem_symargstoint(t_iem_init_symargs *symargp); EXTERN void iem_inttofstyle(t_iem_fstyle_flags *fstylep, int n); EXTERN int iem_fstyletoint(t_iem_fstyle_flags *fstylep); EXTERN void iemgui_setdrawfunctions(t_iemgui *iemgui, t_iemgui_drawfunctions *w); #define IEMGUI_SETDRAWFUNCTIONS(x, prefix) \ { \ t_iemgui_drawfunctions w; \ w.draw_new = (t_iemdrawfunptr)prefix##_draw_new; \ w.draw_config = (t_iemdrawfunptr)prefix##_draw_config; \ w.draw_iolets = (t_iemfunptr)prefix##_draw_io; \ w.draw_update = (t_iemdrawfunptr)prefix##_draw_update; \ w.draw_select = (t_iemdrawfunptr)prefix##_draw_select; \ w.draw_erase = 0; \ w.draw_move = 0; \ iemgui_setdrawfunctions(&x->x_gui, &w); \ } /* wrapper around pd_new() for classes that start with a t_iemgui * initializes the iemgui struct */ t_iemgui* iemgui_new(t_class*cls); /* these are deliberately not exported for now */ /* update the label (both internally and on the GUI) * senditup=0 never-to-gui; senditup=1 always-to-gui; senditup<0 autodetect */ void iemgui_dolabel(void *x, t_iemgui *iemgui, t_symbol *s, int senditup); void iemgui_new_dialog(void*x, t_iemgui*iemgui, const char*objname, t_float width, t_float width_min, t_float height, t_float height_min, t_float range_min, t_float range_max, int range_checkmode, int mode, /* lin0_log1 */ const char* mode_label0, const char* mode_label1, int canloadbang, int steady, int number); #define __g_all_guis_h_ #endif /* __g_all_guis_h_ */ ================================================ FILE: libs/libpd/pure-data/src/g_array.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ #include #include /* for read/write to files */ #include "m_pd.h" #include "g_canvas.h" #include /* jsarlo { */ #define ARRAYPAGESIZE 1000 /* this should match the page size in u_main.tk */ /* } jsarlo */ /* --------- "pure" arrays with scalars for elements. --------------- */ /* Pure arrays have no a priori graphical capabilities. They are instantiated by "garrays" below or can be elements of other scalars (g_scalar.c); their graphical behavior is defined accordingly. */ t_array *array_new(t_symbol *templatesym, t_gpointer *parent) { t_array *x = (t_array *)getbytes(sizeof (*x)); t_template *template; template = template_findbyname(templatesym); x->a_templatesym = templatesym; x->a_n = 1; x->a_elemsize = sizeof(t_word) * template->t_n; x->a_vec = (char *)getbytes(x->a_elemsize); /* note here we blithely copy a gpointer instead of "setting" a new one; this gpointer isn't accounted for and needn't be since we'll be deleted before the thing pointed to gets deleted anyway; see array_free. */ x->a_gp = *parent; x->a_stub = gstub_new(0, x); word_init((t_word *)(x->a_vec), template, parent); return (x); } /* jsarlo { */ static void garray_arrayviewlist_close(t_garray *x); /* } jsarlo */ void array_resize(t_array *x, int n) { int elemsize, oldn; char *tmp; t_template *template = template_findbyname(x->a_templatesym); if (n < 1) n = 1; oldn = x->a_n; elemsize = sizeof(t_word) * template->t_n; tmp = (char *)resizebytes(x->a_vec, oldn * elemsize, n * elemsize); if (!tmp) return; x->a_vec = tmp; x->a_n = n; if (n > oldn) { char *cp = x->a_vec + elemsize * oldn; int i = n - oldn; for (; i--; cp += elemsize) { t_word *wp = (t_word *)cp; word_init(wp, template, &x->a_gp); } } x->a_valid = ++glist_valid; } void array_resize_and_redraw(t_array *array, t_glist *glist, int n) { t_array *a2 = array; int vis = glist_isvisible(glist); while (a2->a_gp.gp_stub->gs_which == GP_ARRAY) a2 = a2->a_gp.gp_stub->gs_un.gs_array; if (vis) gobj_vis(&a2->a_gp.gp_un.gp_scalar->sc_gobj, glist, 0); array_resize(array, n); if (vis) gobj_vis(&a2->a_gp.gp_un.gp_scalar->sc_gobj, glist, 1); } void word_free(t_word *wp, t_template *template); void array_free(t_array *x) { int i; t_template *scalartemplate = template_findbyname(x->a_templatesym); gstub_cutoff(x->a_stub); for (i = 0; i < x->a_n; i++) { t_word *wp = (t_word *)(x->a_vec + x->a_elemsize * i); word_free(wp, scalartemplate); } freebytes(x->a_vec, x->a_elemsize * x->a_n); freebytes(x, sizeof *x); } /* --------------------- graphical arrays (garrays) ------------------- */ t_class *garray_class; struct _garray { t_gobj x_gobj; t_scalar *x_scalar; /* scalar "containing" the array */ t_glist *x_glist; /* containing glist */ t_symbol *x_name; /* unexpanded name (possibly with leading '$') */ t_symbol *x_realname; /* expanded name (symbol we're bound to) */ unsigned int x_usedindsp:1; /* 1 if some DSP routine is using this */ unsigned int x_saveit:1; /* we should save this with parent */ unsigned int x_savesize:1; /* save size too */ unsigned int x_listviewing:1; /* list view window is open */ unsigned int x_hidename:1; /* don't print name above graph */ unsigned int x_edit:1; /* we can edit the array */ }; static t_pd *garray_arraytemplatecanvas; /* written at setup w/ global lock */ static const char garray_arraytemplatefile[] = "\ canvas 0 0 458 153 10;\n\ #X obj 43 31 struct float-array array z float float style\n\ float linewidth float color float v;\n\ #X obj 43 70 plot -v v z color linewidth 0 0 1 style;\n\ "; static const char garray_floattemplatefile[] = "\ canvas 0 0 458 153 10;\n\ #X obj 39 26 struct float float y;\n\ "; /* create invisible, built-in canvases to supply templates for floats and float-arrays. */ void garray_init(void) { t_binbuf *b; b = binbuf_new(); glob_setfilename(0, gensym("_float_template"), gensym(".")); binbuf_text(b, garray_floattemplatefile, strlen(garray_floattemplatefile)); binbuf_eval(b, &pd_canvasmaker, 0, 0); vmess(s__X.s_thing, gensym("pop"), "i", 0); glob_setfilename(0, gensym("_float_array_template"), gensym(".")); binbuf_text(b, garray_arraytemplatefile, strlen(garray_arraytemplatefile)); binbuf_eval(b, &pd_canvasmaker, 0, 0); garray_arraytemplatecanvas = s__X.s_thing; vmess(s__X.s_thing, gensym("pop"), "i", 0); glob_setfilename(0, &s_, &s_); binbuf_free(b); } /* create a new scalar attached to a symbol. Used to make floating-point arrays (the scalar will be of type "float-array"). Currently this is always called by graph_array() below; but when we make a more general way to save and create arrays this might get called more directly. */ static t_garray *graph_scalar(t_glist *gl, t_symbol *s, t_symbol *templatesym, int saveit, int savesize) { t_garray *x; if (!template_findbyname(templatesym)) return (0); x = (t_garray *)pd_new(garray_class); x->x_scalar = scalar_new(gl, templatesym); x->x_name = s; x->x_realname = canvas_realizedollar(gl, s); pd_bind(&x->x_gobj.g_pd, x->x_realname); x->x_usedindsp = 0; /* when invoked this way, saving implies saving size too */ x->x_saveit = saveit; x->x_savesize = savesize; x->x_listviewing = 0; x->x_edit = 1; glist_add(gl, &x->x_gobj); x->x_glist = gl; return (x); } /* get a garray's "array" structure. */ t_array *garray_getarray(t_garray *x) { int zonset, ztype; t_symbol *zarraytype; t_scalar *sc = x->x_scalar; t_symbol *templatesym = sc->sc_template; t_template *template = template_findbyname(templatesym); if (!template) { pd_error(0, "array: couldn't find template %s", templatesym->s_name); return (0); } if (!template_find_field(template, gensym("z"), &zonset, &ztype, &zarraytype)) { pd_error(0, "array: template %s has no 'z' field", templatesym->s_name); return (0); } if (ztype != DT_ARRAY) { pd_error(0, "array: template %s, 'z' field is not an array", templatesym->s_name); return (0); } return (sc->sc_vec[zonset].w_array); } /* get the "array" structure and furthermore check it's float */ static t_array *garray_getarray_floatonly(t_garray *x, int *yonsetp, int *elemsizep) { t_array *a = garray_getarray(x); int yonset, type; t_symbol *arraytype; t_template *template = template_findbyname(a->a_templatesym); if (!template_find_field(template, gensym("y"), &yonset, &type, &arraytype) || type != DT_FLOAT) return (0); *yonsetp = yonset; *elemsizep = a->a_elemsize; return (a); } /* get the array's name. Return nonzero if it should be hidden */ int garray_getname(t_garray *x, t_symbol **namep) { *namep = x->x_name; return (x->x_hidename); } /* get a garray's containing glist */ t_glist *garray_getglist(t_garray *x) { return (x->x_glist); } /* get a garray's associated scalar */ t_scalar *garray_getscalar(t_garray *x) { return (x->x_scalar); } /* if there is one garray in a graph, reset the graph's coordinates to fit a new size and style for the garray */ static void garray_fittograph(t_garray *x, int n, int style) { t_array *array = garray_getarray(x); t_glist *gl = x->x_glist; if (gl->gl_list == &x->x_gobj && !x->x_gobj.g_next) { vmess(&gl->gl_pd, gensym("bounds"), "ffff", 0., gl->gl_y1, (double) (style == PLOTSTYLE_POINTS || n == 1 ? n : n-1), gl->gl_y2); /* hack - if the xlabels seem to want to be from 0 to table size-1, update the second label */ if (gl->gl_nxlabels == 2 && !strcmp(gl->gl_xlabel[0]->s_name, "0")) { t_atom a; SETFLOAT(&a, n-1); gl->gl_xlabel[1] = atom_gensym(&a); glist_redraw(gl); } /* close any dialogs that might have the wrong info now... */ pdgui_stub_deleteforkey(gl); } } /* handle "array" message to glists; call graph_scalar above with an appropriate template; then set size and flags. This is called from the menu and in the file format for patches. LATER replace this by a more coherent (and general) invocation. */ t_garray *graph_array(t_glist *gl, t_symbol *s, t_symbol *templateargsym, t_floatarg fsize, t_floatarg fflags) { int n = fsize, zonset, ztype, saveit, savesize; t_symbol *zarraytype, *asym = gensym("#A"); t_garray *x; t_template *template, *ztemplate; t_symbol *templatesym; int flags = fflags; int filestyle = ((flags & GRAPH_ARRAY_PLOTSTYLE) >> 1); int style = (filestyle == 0 ? PLOTSTYLE_POLY : (filestyle == 1 ? PLOTSTYLE_POINTS : filestyle)); if (templateargsym != &s_float) { pd_error(0, "array %s: only 'float' type understood", templateargsym->s_name); return (0); } templatesym = gensym("pd-float-array"); template = template_findbyname(templatesym); if (!template) { pd_error(0, "array: couldn't find template %s", templatesym->s_name); return (0); } if (!template_find_field(template, gensym("z"), &zonset, &ztype, &zarraytype)) { pd_error(0, "array: template %s has no 'z' field", templatesym->s_name); return (0); } if (ztype != DT_ARRAY) { pd_error(0, "array: template %s, 'z' field is not an array", templatesym->s_name); return (0); } if (!(ztemplate = template_findbyname(zarraytype))) { pd_error(0, "array: no template of type %s", zarraytype->s_name); return (0); } saveit = ((flags & GRAPH_ARRAY_SAVE) != 0); savesize = ((flags & GRAPH_ARRAY_SAVESIZE) != 0); x = graph_scalar(gl, s, templatesym, saveit, savesize); x->x_hidename = ((flags & 8) >> 3); if (n <= 0) n = 100; array_resize(x->x_scalar->sc_vec[zonset].w_array, n); template_setfloat(template, gensym("style"), x->x_scalar->sc_vec, style, 1); template_setfloat(template, gensym("linewidth"), x->x_scalar->sc_vec, ((style == PLOTSTYLE_POINTS) ? 2 : 1), 1); template_setfloat(template, gensym("v"), x->x_scalar->sc_vec, 1, 1); /* bashily unbind #A -- this would create garbage if #A were multiply bound but we believe in this context it's at most bound to whichever textobj or array was created most recently */ asym->s_thing = 0; /* and now bind #A to us to receive following messages in the saved file or copy buffer */ pd_bind(&x->x_gobj.g_pd, asym); garray_fittograph(x, n, style); canvas_update_dsp(); return (x); } /* called from array menu item to create a new one */ void canvas_menuarray(t_glist *canvas) { t_glist *x = (t_glist *)canvas; int gcount; char arraybuf[80]; for (gcount = 1; gcount < 1000; gcount++) { sprintf(arraybuf, "array%d", gcount); if (!pd_findbyclass(gensym(arraybuf), garray_class)) break; } pdgui_stub_vnew(&x->gl_pd, "pdtk_array_dialog", x, "siii", arraybuf, 100, 3, 1); } /* called from graph_dialog to set properties */ void garray_properties(t_garray *x) { t_array *a = garray_getarray(x); t_scalar *sc = x->x_scalar; int style = template_getfloat(template_findbyname(sc->sc_template), gensym("style"), x->x_scalar->sc_vec, 1); int filestyle = (style == 0 ? PLOTSTYLE_POLY : (style == 1 ? PLOTSTYLE_POINTS : style)); if (!a) return; pdgui_stub_deleteforkey(x); pdgui_stub_vnew(&x->x_gobj.g_pd, "pdtk_array_dialog", x, "siii", x->x_name->s_name, a->a_n, x->x_saveit + 2 * filestyle, 0); } /* this is called back from the dialog window to create a garray. The otherflag requests that we find an existing graph to put it in. */ void glist_arraydialog(t_glist *parent, t_symbol *name, t_floatarg size, t_floatarg fflags, t_floatarg otherflag) { t_glist *gl; t_garray *a; int flags = fflags; if (size < 1) size = 1; if (otherflag == 0 || (!(gl = glist_findgraph(parent)))) gl = glist_addglist(parent, &s_, 0, 1, size, -1, 0, 0, 0, 0); a = graph_array(gl, name, &s_float, size, flags); canvas_dirty(parent, 1); } /* this is called from the properties dialog window for an existing array */ void garray_arraydialog(t_garray *x, t_symbol *name, t_floatarg fsize, t_floatarg fflags, t_floatarg deleteit) { int flags = fflags; int saveit = ((flags & 1) != 0); int filestyle = ((flags & 6) >> 1); int style = (filestyle == 0 ? PLOTSTYLE_POLY : (filestyle == 1 ? PLOTSTYLE_POINTS : filestyle)); t_float stylewas = template_getfloat( template_findbyname(x->x_scalar->sc_template), gensym("style"), x->x_scalar->sc_vec, 1); if (deleteit != 0) { int wasused = x->x_usedindsp; glist_delete(x->x_glist, &x->x_gobj); if (wasused) canvas_update_dsp(); } else { long size; t_array *a = garray_getarray(x); t_template *scalartemplate; if (!a) { pd_error(x, "can't find array\n"); return; } if (!(scalartemplate = template_findbyname(x->x_scalar->sc_template))) { pd_error(0, "array: no template of type %s", x->x_scalar->sc_template->s_name); return; } if (name != x->x_name) { /* jsarlo { */ if (x->x_listviewing) { garray_arrayviewlist_close(x); } /* } jsarlo */ x->x_name = name; pd_unbind(&x->x_gobj.g_pd, x->x_realname); x->x_realname = canvas_realizedollar(x->x_glist, name); pd_bind(&x->x_gobj.g_pd, x->x_realname); /* redraw the whole glist, just so the name change shows up */ if (x->x_glist->gl_havewindow) canvas_redraw(x->x_glist); else if (glist_isvisible(x->x_glist->gl_owner)) { gobj_vis(&x->x_glist->gl_gobj, x->x_glist->gl_owner, 0); gobj_vis(&x->x_glist->gl_gobj, x->x_glist->gl_owner, 1); } canvas_update_dsp(); } size = fsize; if (size < 1) size = 1; if (size != a->a_n) garray_resize_long(x, size); else if (style != stylewas) garray_fittograph(x, (int)size, style); template_setfloat(scalartemplate, gensym("style"), x->x_scalar->sc_vec, (t_float)style, 0); template_setfloat(scalartemplate, gensym("linewidth"), x->x_scalar->sc_vec, ((style == PLOTSTYLE_POINTS) ? 2 : 1), 0); garray_setsaveit(x, (saveit != 0)); garray_redraw(x); canvas_dirty(x->x_glist, 1); } } /* jsarlo { */ static void garray_arrayviewlist_fillpage(t_garray *x, t_float fPage, t_float fTopItem) { int i, size=0, topItem=(int)fTopItem; int pagesize=ARRAYPAGESIZE, page=(int)fPage, maxpage; int offset, length; t_word *data=0; if(!garray_getfloatwords(x, &size, &data)) { pd_error(x, "error in %s()", __FUNCTION__); return; } /* make sure the requested page is within range */ maxpage = (size - 1) / pagesize; if(page > maxpage) page = maxpage; if(page < 0) page = 0; pdgui_vmess("::dialog_array::listview_setpage", "s iii", x->x_realname->s_name, page, maxpage+1, pagesize); offset = page*pagesize; length = ((offset+pagesize) > size)?size-offset:pagesize; pdgui_vmess("::dialog_array::listview_setdata", "siw", x->x_realname->s_name, offset, length, data + offset); pdgui_vmess("::dialog_array::listview_focus", "si", x->x_realname->s_name, topItem); } static void garray_arrayviewlist_new(t_garray *x) { int size=0; t_word*data=0; if(!garray_getfloatwords(x, &size, &data)) { pd_error(x, "error in %s()", __FUNCTION__); return; } x->x_listviewing = 1; pdgui_stub_vnew(&x->x_gobj.g_pd, "pdtk_array_listview_new", x, "si", x->x_realname->s_name, 0); garray_arrayviewlist_fillpage(x, 0, 0); } static void garray_arrayviewlist_close(t_garray *x) { x->x_listviewing = 0; pdgui_vmess("pdtk_array_listview_closeWindow", "s", x->x_realname->s_name); } /* } jsarlo */ static void garray_free(t_garray *x) { t_pd *x2; sys_unqueuegui(&x->x_gobj); /* jsarlo { */ if (x->x_listviewing) { garray_arrayviewlist_close(x); } /* } jsarlo */ pdgui_stub_deleteforkey(x); pd_unbind(&x->x_gobj.g_pd, x->x_realname); /* just in case we're still bound to #A from loading... */ while ((x2 = pd_findbyclass(gensym("#A"), garray_class))) pd_unbind(x2, gensym("#A")); pd_free(&x->x_scalar->sc_gobj.g_pd); } /* ------------- code used by both array and plot widget functions ---- */ void array_redraw(t_array *a, t_glist *glist) { while (a->a_gp.gp_stub->gs_which == GP_ARRAY) a = a->a_gp.gp_stub->gs_un.gs_array; scalar_redraw(a->a_gp.gp_un.gp_scalar, glist); } /* routine to get screen coordinates of a point in an array */ void array_getcoordinate(t_glist *glist, char *elem, int xonset, int yonset, int wonset, int indx, t_float basex, t_float basey, t_float xinc, t_fielddesc *xfielddesc, t_fielddesc *yfielddesc, t_fielddesc *wfielddesc, t_float *xp, t_float *yp, t_float *wp) { t_float xval, yval, ypix, wpix; if (xonset >= 0) xval = *(t_float *)(elem + xonset); else xval = indx * xinc; if (yonset >= 0) yval = *(t_float *)(elem + yonset); else yval = 0; ypix = glist_ytopixels(glist, basey + fielddesc_cvttocoord(yfielddesc, yval)); if (wonset >= 0) { /* found "w" field which controls linewidth. */ t_float wval = *(t_float *)(elem + wonset); wpix = glist_ytopixels(glist, basey + fielddesc_cvttocoord(yfielddesc, yval) + fielddesc_cvttocoord(wfielddesc, wval)) - ypix; if (wpix < 0) wpix = -wpix; } else wpix = 1; *xp = glist_xtopixels(glist, basex + fielddesc_cvttocoord(xfielddesc, xval)); *yp = ypix; *wp = wpix; } static void array_getrect(t_array *array, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2) { t_float x1 = 0x7fffffff, y1 = 0x7fffffff, x2 = -0x7fffffff, y2 = -0x7fffffff; t_canvas *elemtemplatecanvas; t_template *elemtemplate; int elemsize, yonset, wonset, xonset, i; if (!array_getfields(array->a_templatesym, &elemtemplatecanvas, &elemtemplate, &elemsize, 0, 0, 0, &xonset, &yonset, &wonset)) { int incr; /* if it has more than 2000 points, just check 300 of them. */ if (array->a_n < 2000) incr = 1; else incr = array->a_n / 300; for (i = 0; i < array->a_n; i += incr) { t_float pxpix, pypix, pwpix; array_getcoordinate(glist, (char *)(array->a_vec) + i * elemsize, xonset, yonset, wonset, i, 0, 0, 1, 0, 0, 0, &pxpix, &pypix, &pwpix); if (pwpix < 2) pwpix = 2; if (pxpix < x1) x1 = pxpix; if (pxpix > x2) x2 = pxpix; if (pypix - pwpix < y1) y1 = pypix - pwpix; if (pypix + pwpix > y2) y2 = pypix + pwpix; } } *xp1 = x1; *yp1 = y1; *xp2 = x2; *yp2 = y2; } /* -------------------- widget behavior for garray ------------ */ static void garray_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2) { t_garray *x = (t_garray *)z; gobj_getrect(&x->x_scalar->sc_gobj, glist, xp1, yp1, xp2, yp2); } static void garray_displace(t_gobj *z, t_glist *glist, int dx, int dy) { /* refuse */ } static void garray_select(t_gobj *z, t_glist *glist, int state) { t_garray *x = (t_garray *)z; /* fill in later */ } static void garray_activate(t_gobj *z, t_glist *glist, int state) { } static void garray_delete(t_gobj *z, t_glist *glist) { /* nothing to do */ } static void garray_vis(t_gobj *z, t_glist *glist, int vis) { t_garray *x = (t_garray *)z; gobj_vis(&x->x_scalar->sc_gobj, glist, vis); } static int garray_click(t_gobj *z, t_glist *glist, int xpix, int ypix, int shift, int alt, int dbl, int doit) { t_garray *x = (t_garray *)z; if (x->x_edit) return (gobj_click(&x->x_scalar->sc_gobj, glist, xpix, ypix, shift, alt, dbl, doit)); else return (0); } #define ARRAYWRITECHUNKSIZE 1000 void garray_savecontentsto(t_garray *x, t_binbuf *b) { t_array *array = garray_getarray(x); if (x->x_savesize) binbuf_addv(b, "ssi;", gensym("#A"), gensym("resize"), array->a_n); if (x->x_saveit) { int n = array->a_n, n2 = 0; if (n > 200000) post("warning: I'm saving an array with %d points!\n", n); while (n2 < n) { int chunk = n - n2, i; if (chunk > ARRAYWRITECHUNKSIZE) chunk = ARRAYWRITECHUNKSIZE; binbuf_addv(b, "si", gensym("#A"), n2); for (i = 0; i < chunk; i++) binbuf_addv(b, "f", ((t_word *)(array->a_vec))[n2+i].w_float); binbuf_addv(b, ";"); n2 += chunk; } } } static void garray_save(t_gobj *z, t_binbuf *b) { int style, filestyle; t_garray *x = (t_garray *)z; t_array *array = garray_getarray(x); t_template *scalartemplate; if (x->x_scalar->sc_template != gensym("pd-float-array")) { /* LATER "save" the scalar as such */ pd_error(x, "can't save arrays of type %s yet", x->x_scalar->sc_template->s_name); return; } if (!(scalartemplate = template_findbyname(x->x_scalar->sc_template))) { pd_error(0, "array: no template of type %s", x->x_scalar->sc_template->s_name); return; } style = template_getfloat(scalartemplate, gensym("style"), x->x_scalar->sc_vec, 0); filestyle = (style == PLOTSTYLE_POINTS ? 1 : (style == PLOTSTYLE_POLY ? 0 : style)); binbuf_addv(b, "sssisi;", gensym("#X"), gensym("array"), x->x_name, array->a_n, &s_float, x->x_saveit + 2 * filestyle + 8*x->x_hidename); garray_savecontentsto(x, b); } const t_widgetbehavior garray_widgetbehavior = { garray_getrect, garray_displace, garray_select, garray_activate, garray_delete, garray_vis, garray_click, }; /* ----------------------- public functions -------------------- */ void garray_usedindsp(t_garray *x) { x->x_usedindsp = 1; } static void garray_doredraw(t_gobj *client, t_glist *glist) { t_garray *x = (t_garray *)client; if (glist_isvisible(x->x_glist) && gobj_shouldvis(client, glist)) { garray_vis(&x->x_gobj, x->x_glist, 0); garray_vis(&x->x_gobj, x->x_glist, 1); } } void garray_redraw(t_garray *x) { if (glist_isvisible(x->x_glist)) sys_queuegui(&x->x_gobj, x->x_glist, garray_doredraw); /* jsarlo { */ /* this happens in garray_vis() when array is visible for performance reasons */ else { if (x->x_listviewing) pdgui_vmess("pdtk_array_listview_fillpage", "s", x->x_realname->s_name); } /* } jsarlo */ } /* This functiopn gets the template of an array; if we can't figure out what template an array's elements belong to we're in grave trouble when it's time to free or resize it. */ t_template *garray_template(t_garray *x) { t_array *array = garray_getarray(x); t_template *template = (array ? template_findbyname(array->a_templatesym) : 0); if (!template) bug("garray_template"); return (template); } int garray_npoints(t_garray *x) /* get the length */ { t_array *array = garray_getarray(x); return (array->a_n); } char *garray_vec(t_garray *x) /* get the contents */ { t_array *array = garray_getarray(x); return ((char *)(array->a_vec)); } /* routine that checks if we're just an array of floats and if so returns the goods */ int garray_getfloatwords(t_garray *x, int *size, t_word **vec) { int yonset, elemsize; t_array *a = garray_getarray_floatonly(x, &yonset, &elemsize); if (!a) { pd_error(0, "%s: needs floating-point 'y' field", x->x_realname->s_name); return (0); } else if (elemsize != sizeof(t_word)) { pd_error(0, "%s: has more than one field", x->x_realname->s_name); return (0); } *size = garray_npoints(x); *vec = (t_word *)garray_vec(x); return (1); } /* older, non-64-bit safe version, supplied for older externs */ int garray_getfloatarray(t_garray *x, int *size, t_float **vec) { if (sizeof(t_word) != sizeof(t_float)) { t_symbol *patchname; if (x->x_glist->gl_owner) patchname = x->x_glist->gl_owner->gl_name; else patchname = x->x_glist->gl_name; pd_error(0, "an operation on the array '%s' in the patch '%s'", x->x_name->s_name, patchname->s_name); pd_error(0, "failed since it uses garray_getfloatarray while running 64-bit"); } return (garray_getfloatwords(x, size, (t_word **)vec)); } /* set the "saveit" flag */ void garray_setsaveit(t_garray *x, int saveit) { if (x->x_saveit && !saveit) post("warning: array %s: clearing save-in-patch flag", x->x_name->s_name); x->x_saveit = saveit; } /*------------------- Pd messages ------------------------ */ static void garray_const(t_garray *x, t_floatarg g) { int yonset, i, elemsize; t_array *array = garray_getarray_floatonly(x, &yonset, &elemsize); if (!array) pd_error(0, "%s: needs floating-point 'y' field", x->x_realname->s_name); else for (i = 0; i < array->a_n; i++) *((t_float *)((char *)array->a_vec + elemsize * i) + yonset) = g; garray_redraw(x); } /* sum of Fourier components; called from routines below */ static void garray_dofo(t_garray *x, long npoints, t_float dcval, int nsin, t_float *vsin, int sineflag) { double phase, phaseincr, fj; int yonset, i, j, elemsize; t_array *array = garray_getarray_floatonly(x, &yonset, &elemsize); if (!array) { pd_error(0, "%s: needs floating-point 'y' field", x->x_realname->s_name); return; } if (npoints == 0) npoints = 512; /* dunno what a good default would be... */ if (npoints != (1 << ilog2((int)npoints))) post("%s: rounding to %d points", array->a_templatesym->s_name, (npoints = (1<a_n; i++, phase += phaseincr) { double sum = dcval; if (sineflag) for (j = 0, fj = phase; j < nsin; j++, fj += phase) sum += vsin[j] * sin(fj); else for (j = 0, fj = 0; j < nsin; j++, fj += phase) sum += vsin[j] * cos(fj); *((t_float *)((array->a_vec + elemsize * i)) + yonset) = sum; } garray_redraw(x); } static void garray_sinesum(t_garray *x, t_symbol *s, int argc, t_atom *argv) { t_float *svec; long npoints; int i; if (argc < 2) { pd_error(0, "sinesum: %s: need number of points and partial strengths", x->x_realname->s_name); return; } npoints = atom_getfloatarg(0, argc, argv); argv++, argc--; svec = (t_float *)t_getbytes(sizeof(t_float) * argc); if (!svec) return; for (i = 0; i < argc; i++) svec[i] = atom_getfloatarg(i, argc, argv); garray_dofo(x, npoints, 0, argc, svec, 1); t_freebytes(svec, sizeof(t_float) * argc); } static void garray_cosinesum(t_garray *x, t_symbol *s, int argc, t_atom *argv) { t_float *svec; long npoints; int i; if (argc < 2) { pd_error(0, "sinesum: %s: need number of points and partial strengths", x->x_realname->s_name); return; } npoints = atom_getfloatarg(0, argc, argv); argv++, argc--; svec = (t_float *)t_getbytes(sizeof(t_float) * argc); if (!svec) return; for (i = 0; i < argc; i++) svec[i] = atom_getfloatarg(i, argc, argv); garray_dofo(x, npoints, 0, argc, svec, 0); t_freebytes(svec, sizeof(t_float) * argc); } static void garray_normalize(t_garray *x, t_float f) { int i; double maxv, renormer; int yonset, elemsize; t_array *array = garray_getarray_floatonly(x, &yonset, &elemsize); if (!array) { pd_error(0, "%s: needs floating-point 'y' field", x->x_realname->s_name); return; } if (f <= 0) f = 1; for (i = 0, maxv = 0; i < array->a_n; i++) { double v = *((t_float *)(array->a_vec + elemsize * i) + yonset); if (v > maxv) maxv = v; if (-v > maxv) maxv = -v; } if (maxv > 0) { renormer = f / maxv; for (i = 0; i < array->a_n; i++) *((t_float *)(array->a_vec + elemsize * i) + yonset) *= renormer; } garray_redraw(x); } /* list -- the first value is an index; subsequent values are put in the "y" slot of the array. This generalizes Max's "table", sort of. */ static void garray_list(t_garray *x, t_symbol *s, int argc, t_atom *argv) { int i; int yonset, elemsize; t_array *array = garray_getarray_floatonly(x, &yonset, &elemsize); if (!array) { pd_error(0, "%s: needs floating-point 'y' field", x->x_realname->s_name); return; } if (argc < 2) return; else { int firstindex = atom_getfloat(argv); argc--; argv++; /* drop negative x values */ if (firstindex < 0) { argc += firstindex; argv -= firstindex; firstindex = 0; if (argc <= 0) return; } if (argc + firstindex > array->a_n) { argc = array->a_n - firstindex; if (argc <= 0) return; } for (i = 0; i < argc; i++) *((t_float *)(array->a_vec + elemsize * (i + firstindex)) + yonset) = atom_getfloat(argv + i); } garray_redraw(x); } /* forward a "bounds" message to the owning graph */ static void garray_bounds(t_garray *x, t_floatarg x1, t_floatarg y1, t_floatarg x2, t_floatarg y2) { vmess(&x->x_glist->gl_pd, gensym("bounds"), "ffff", x1, y1, x2, y2); } /* same for "xticks", etc */ static void garray_xticks(t_garray *x, t_floatarg point, t_floatarg inc, t_floatarg f) { vmess(&x->x_glist->gl_pd, gensym("xticks"), "fff", point, inc, f); } static void garray_yticks(t_garray *x, t_floatarg point, t_floatarg inc, t_floatarg f) { vmess(&x->x_glist->gl_pd, gensym("yticks"), "fff", point, inc, f); } static void garray_xlabel(t_garray *x, t_symbol *s, int argc, t_atom *argv) { typedmess(&x->x_glist->gl_pd, s, argc, argv); } static void garray_ylabel(t_garray *x, t_symbol *s, int argc, t_atom *argv) { typedmess(&x->x_glist->gl_pd, s, argc, argv); } static void garray_style(t_garray *x, t_floatarg fstyle) { int stylewas, style = fstyle; t_template *scalartemplate; if (!(scalartemplate = template_findbyname(x->x_scalar->sc_template))) { pd_error(0, "array: no template of type %s", x->x_scalar->sc_template->s_name); return; } stylewas = template_getfloat( scalartemplate, gensym("style"), x->x_scalar->sc_vec, 1); if (style != stylewas) { t_array *a = garray_getarray(x); if (!a) { pd_error(x, "can't find array\n"); return; } if (style == PLOTSTYLE_POINTS || stylewas == PLOTSTYLE_POINTS) garray_fittograph(x, a->a_n, style); template_setfloat(scalartemplate, gensym("style"), x->x_scalar->sc_vec, (t_float)style, 0); #if 1 template_setfloat(scalartemplate, gensym("linewidth"), x->x_scalar->sc_vec, ((style == PLOTSTYLE_POINTS) ? 2 : 1), 1); #endif garray_redraw(x); } } static void garray_width(t_garray *x, t_floatarg width) { t_float widthwas; t_template *scalartemplate; if (!(scalartemplate = template_findbyname(x->x_scalar->sc_template))) { pd_error(0, "array: no template of type %s", x->x_scalar->sc_template->s_name); return; } widthwas = template_getfloat( scalartemplate, gensym("linewidth"), x->x_scalar->sc_vec, 1); if (width < 1) width = 1; if (width != widthwas) { template_setfloat(scalartemplate, gensym("linewidth"), x->x_scalar->sc_vec, width, 0); garray_redraw(x); } } static void garray_color(t_garray *x, t_floatarg color) { t_float colorwas; t_template *scalartemplate; if (!(scalartemplate = template_findbyname(x->x_scalar->sc_template))) { pd_error(0, "array: no template of type %s", x->x_scalar->sc_template->s_name); return; } colorwas = template_getfloat( scalartemplate, gensym("color"), x->x_scalar->sc_vec, 1); if (color != colorwas) { template_setfloat(scalartemplate, gensym("color"), x->x_scalar->sc_vec, color, 0); garray_redraw(x); } } static void garray_vis_msg(t_garray *x, t_floatarg fvis) { int viswas, vis = fvis != 0; t_template *scalartemplate; if (!(scalartemplate = template_findbyname(x->x_scalar->sc_template))) { pd_error(0, "array: no template of type %s", x->x_scalar->sc_template->s_name); return; } viswas = template_getfloat( scalartemplate, gensym("v"), x->x_scalar->sc_vec, 1); if (vis != viswas) { template_setfloat(scalartemplate, gensym("v"), x->x_scalar->sc_vec, vis, 0); garray_redraw(x); } } /* change the name of a garray. */ static void garray_rename(t_garray *x, t_symbol *s) { /* jsarlo { */ if (x->x_listviewing) { garray_arrayviewlist_close(x); } /* } jsarlo */ pd_unbind(&x->x_gobj.g_pd, x->x_realname); pd_bind(&x->x_gobj.g_pd, x->x_realname = x->x_name = s); garray_redraw(x); } static void garray_read(t_garray *x, t_symbol *filename) { int nelem, filedesc, i; FILE *fd; char buf[MAXPDSTRING], *bufptr; int yonset, elemsize; t_array *array = garray_getarray_floatonly(x, &yonset, &elemsize); if (!array) { pd_error(0, "%s: needs floating-point 'y' field", x->x_realname->s_name); return; } nelem = array->a_n; if ((filedesc = canvas_open(glist_getcanvas(x->x_glist), filename->s_name, "", buf, &bufptr, MAXPDSTRING, 0)) < 0 || !(fd = fdopen(filedesc, "r"))) { pd_error(0, "%s: can't open", filename->s_name); return; } for (i = 0; i < nelem; i++) { double f; if (!fscanf(fd, "%lf", &f)) { post("%s: read %d elements into table of size %d", filename->s_name, i, nelem); break; } else *((t_float *)(array->a_vec + elemsize * i) + yonset) = f; } while (i < nelem) *((t_float *)(array->a_vec + elemsize * i) + yonset) = 0, i++; fclose(fd); garray_redraw(x); } static void garray_write(t_garray *x, t_symbol *filename) { FILE *fd; char buf[MAXPDSTRING]; int yonset, elemsize, i; t_array *array = garray_getarray_floatonly(x, &yonset, &elemsize); if (!array) { pd_error(0, "%s: needs floating-point 'y' field", x->x_realname->s_name); return; } canvas_makefilename(glist_getcanvas(x->x_glist), filename->s_name, buf, MAXPDSTRING); if (!(fd = sys_fopen(buf, "w"))) { pd_error(0, "%s: can't create", buf); return; } for (i = 0; i < array->a_n; i++) { if (fprintf(fd, "%g\n", *(t_float *)(((array->a_vec + sizeof(t_word) * i)) + yonset)) < 1) { post("%s: write error", filename->s_name); break; } } fclose(fd); } void garray_resize_long(t_garray *x, long n) { t_array *array = garray_getarray(x); if (n < 1) n = 1; if (n == array->a_n) return; garray_fittograph(x, (int)n, template_getfloat( template_findbyname(x->x_scalar->sc_template), gensym("style"), x->x_scalar->sc_vec, 1)); array_resize_and_redraw(array, x->x_glist, (int)n); if (x->x_usedindsp) canvas_update_dsp(); } /* float version to use as Pd method */ void garray_resize(t_garray *x, t_floatarg f) { garray_resize_long(x, f); } /* ignore zoom for now */ static void garray_zoom(t_garray *x, t_floatarg f) { } static void garray_edit(t_garray *x, t_floatarg f) { x->x_edit = (int)f; } static void garray_print(t_garray *x) { t_array *array = garray_getarray(x); post("garray %s: template %s, length %d", x->x_realname->s_name, array->a_templatesym->s_name, array->a_n); } void g_array_setup(void) { garray_class = class_new(gensym("array"), 0, (t_method)garray_free, sizeof(t_garray), CLASS_GOBJ, 0); class_setwidget(garray_class, &garray_widgetbehavior); class_addmethod(garray_class, (t_method)garray_const, gensym("const"), A_DEFFLOAT, A_NULL); class_addlist(garray_class, garray_list); class_addmethod(garray_class, (t_method)garray_bounds, gensym("bounds"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); class_addmethod(garray_class, (t_method)garray_xticks, gensym("xticks"), A_FLOAT, A_FLOAT, A_FLOAT, 0); class_addmethod(garray_class, (t_method)garray_xlabel, gensym("xlabel"), A_GIMME, 0); class_addmethod(garray_class, (t_method)garray_yticks, gensym("yticks"), A_FLOAT, A_FLOAT, A_FLOAT, 0); class_addmethod(garray_class, (t_method)garray_ylabel, gensym("ylabel"), A_GIMME, 0); class_addmethod(garray_class, (t_method)garray_style, gensym("style"), A_FLOAT, 0); class_addmethod(garray_class, (t_method)garray_width, gensym("width"), A_FLOAT, 0); class_addmethod(garray_class, (t_method)garray_color, gensym("color"), A_FLOAT, 0); class_addmethod(garray_class, (t_method)garray_vis_msg, gensym("vis"), A_FLOAT, 0); class_addmethod(garray_class, (t_method)garray_rename, gensym("rename"), A_SYMBOL, 0); class_addmethod(garray_class, (t_method)garray_read, gensym("read"), A_SYMBOL, A_NULL); class_addmethod(garray_class, (t_method)garray_write, gensym("write"), A_SYMBOL, A_NULL); class_addmethod(garray_class, (t_method)garray_resize, gensym("resize"), A_FLOAT, A_NULL); class_addmethod(garray_class, (t_method)garray_zoom, gensym("zoom"), A_FLOAT, 0); class_addmethod(garray_class, (t_method)garray_edit, gensym("edit"), A_FLOAT, 0); class_addmethod(garray_class, (t_method)garray_print, gensym("print"), A_NULL); class_addmethod(garray_class, (t_method)garray_sinesum, gensym("sinesum"), A_GIMME, 0); class_addmethod(garray_class, (t_method)garray_cosinesum, gensym("cosinesum"), A_GIMME, 0); class_addmethod(garray_class, (t_method)garray_normalize, gensym("normalize"), A_DEFFLOAT, 0); class_addmethod(garray_class, (t_method)garray_arraydialog, gensym("arraydialog"), A_SYMBOL, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); /* jsarlo { */ class_addmethod(garray_class, (t_method)garray_arrayviewlist_new, gensym("arrayviewlistnew"), A_NULL); class_addmethod(garray_class, (t_method)garray_arrayviewlist_fillpage, gensym("arrayviewlistfillpage"), A_FLOAT, A_DEFFLOAT, A_NULL); class_addmethod(garray_class, (t_method)garray_arrayviewlist_close, gensym("arrayviewclose"), A_NULL); /* } jsarlo */ class_setsavefn(garray_class, garray_save); } ================================================ FILE: libs/libpd/pure-data/src/g_bang.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */ /* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */ #include #include #include "m_pd.h" #include "g_all_guis.h" #include "m_private_utils.h" /* --------------- bng gui-bang ------------------------- */ t_widgetbehavior bng_widgetbehavior; static t_class *bng_class; /* widget helper functions */ #define bng_draw_io 0 static void bng_draw_config(t_bng* x, t_glist* glist) { const int zoom = IEMGUI_ZOOM(x); t_iemgui *iemgui = &x->x_gui; t_canvas *canvas = glist_getcanvas(glist); int xpos = text_xpix(&x->x_gui.x_obj, glist); int ypos = text_ypix(&x->x_gui.x_obj, glist); int iow = IOWIDTH * zoom, ioh = IEM_GUI_IOHEIGHT * zoom; int inset = zoom; char tag[128]; t_atom fontatoms[3]; SETSYMBOL(fontatoms+0, gensym(iemgui->x_font)); SETFLOAT (fontatoms+1, -iemgui->x_fontsize*zoom); SETSYMBOL(fontatoms+2, gensym(sys_fontweight)); sprintf(tag, "%pBASE", x); pdgui_vmess(0, "crs iiii", canvas, "coords", tag, xpos, ypos, xpos + x->x_gui.x_w, ypos + x->x_gui.x_h); pdgui_vmess(0, "crs ri rk", canvas, "itemconfigure", tag, "-width", zoom, "-fill", x->x_gui.x_bcol); sprintf(tag, "%pBUT", x); pdgui_vmess(0, "crs iiii", canvas, "coords", tag, xpos + inset, ypos + inset, xpos + x->x_gui.x_w - inset, ypos + x->x_gui.x_h - inset); pdgui_vmess(0, "crs ri rk", canvas, "itemconfigure", tag, "-width", zoom, "-fill", (x->x_flashed ? x->x_gui.x_fcol : x->x_gui.x_bcol)); sprintf(tag, "%pLABEL", x); pdgui_vmess(0, "crs ii", canvas, "coords", tag, xpos + x->x_gui.x_ldx * zoom, ypos + x->x_gui.x_ldy * zoom); pdgui_vmess(0, "crs rA rk", canvas, "itemconfigure", tag, "-font", 3, fontatoms, "-fill", (x->x_gui.x_fsf.x_selected ? IEM_GUI_COLOR_SELECTED : x->x_gui.x_lcol)); iemgui_dolabel(x, &x->x_gui, x->x_gui.x_lab, 1); } static void bng_draw_new(t_bng *x, t_glist *glist) { t_canvas *canvas = glist_getcanvas(glist); char tag[128], tag_object[128]; char*tags[] = {tag_object, tag, "label", "text"}; sprintf(tag_object, "%pOBJ", x); sprintf(tag, "%pBASE", x); pdgui_vmess(0, "crr iiii rS", canvas, "create", "rectangle", 0, 0, 0, 0, "-tags", 2, tags); sprintf(tag, "%pBUT", x); pdgui_vmess(0, "crr iiii rS", canvas, "create", "oval", 0, 0, 0, 0, "-tags", 2, tags); sprintf(tag, "%pLABEL", x); pdgui_vmess(0, "crr ii rs rS", canvas, "create", "text", 0, 0, "-anchor", "w", "-tags", 4, tags); bng_draw_config(x, glist); (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_IO); } static void bng_draw_select(t_bng* x, t_glist* glist) { t_canvas *canvas = glist_getcanvas(glist); int col = IEM_GUI_COLOR_NORMAL, lcol = x->x_gui.x_lcol; char tag[128]; if(x->x_gui.x_fsf.x_selected) col = lcol = IEM_GUI_COLOR_SELECTED; sprintf(tag, "%pBASE", x); pdgui_vmess(0, "crs rk", canvas, "itemconfigure", tag, "-outline", col); sprintf(tag, "%pBUT", x); pdgui_vmess(0, "crs rk", canvas, "itemconfigure", tag, "-outline", col); sprintf(tag, "%pLABEL", x); pdgui_vmess(0, "crs rk", canvas, "itemconfigure", tag, "-fill", lcol); } static void bng_draw_update(t_bng *x, t_glist *glist) { if(glist_isvisible(glist)) { char tag[128]; sprintf(tag, "%pBUT", x); pdgui_vmess(0, "crs rk", glist_getcanvas(glist), "itemconfigure", tag, "-fill", (x->x_flashed ? x->x_gui.x_fcol : x->x_gui.x_bcol)); } } /* ------------------------ bng widgetbehaviour----------------------------- */ static void bng_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2) { t_bng *x = (t_bng *)z; *xp1 = text_xpix(&x->x_gui.x_obj, glist); *yp1 = text_ypix(&x->x_gui.x_obj, glist); *xp2 = *xp1 + x->x_gui.x_w; *yp2 = *yp1 + x->x_gui.x_h; } static void bng_save(t_gobj *z, t_binbuf *b) { t_bng *x = (t_bng *)z; t_symbol *bflcol[3]; t_symbol *srl[3]; iemgui_save(&x->x_gui, srl, bflcol); binbuf_addv(b, "ssiisiiiisssiiiisss", gensym("#X"),gensym("obj"), (int)x->x_gui.x_obj.te_xpix, (int)x->x_gui.x_obj.te_ypix, gensym("bng"), x->x_gui.x_w/IEMGUI_ZOOM(x), x->x_flashtime_hold, x->x_flashtime_break, iem_symargstoint(&x->x_gui.x_isa), srl[0], srl[1], srl[2], x->x_gui.x_ldx, x->x_gui.x_ldy, iem_fstyletoint(&x->x_gui.x_fsf), x->x_gui.x_fontsize, bflcol[0], bflcol[1], bflcol[2]); binbuf_addv(b, ";"); } void bng_check_minmax(t_bng *x, int ftbreak, int fthold) { if(ftbreak > fthold) { int h; h = ftbreak; ftbreak = fthold; fthold = h; } if(ftbreak < IEM_BNG_MINBREAKFLASHTIME) ftbreak = IEM_BNG_MINBREAKFLASHTIME; if(fthold < IEM_BNG_MINHOLDFLASHTIME) fthold = IEM_BNG_MINHOLDFLASHTIME; x->x_flashtime_break = ftbreak; x->x_flashtime_hold = fthold; } static void bng_properties(t_gobj *z, t_glist *owner) { t_bng *x = (t_bng *)z; iemgui_new_dialog(x, &x->x_gui, "bang", x->x_gui.x_w/IEMGUI_ZOOM(x), IEM_GUI_MINSIZE, 0, 0, x->x_flashtime_break, x->x_flashtime_hold, 2, -1, "", "", 1, -1, -1); } static void bng_set(t_bng *x) { int holdtime = x->x_flashtime_hold; int sincelast = clock_gettimesince(x->x_lastflashtime); x->x_lastflashtime = clock_getsystime(); if (sincelast < x->x_flashtime_hold*2) holdtime = sincelast/2; if (holdtime < x->x_flashtime_break) holdtime = x->x_flashtime_break; x->x_flashed = 1; (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); clock_delay(x->x_clock_hld, holdtime); } static void bng_bout1(t_bng *x) /* wird nur mehr gesendet, wenn snd != rcv*/ { if(!x->x_gui.x_fsf.x_put_in2out) { x->x_gui.x_isa.x_locked = 1; clock_delay(x->x_clock_lck, 2); } outlet_bang(x->x_gui.x_obj.ob_outlet); if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing && x->x_gui.x_fsf.x_put_in2out) pd_bang(x->x_gui.x_snd->s_thing); } static void bng_bout2(t_bng *x) /* wird immer gesendet, wenn moeglich*/ { if(!x->x_gui.x_fsf.x_put_in2out) { x->x_gui.x_isa.x_locked = 1; clock_delay(x->x_clock_lck, 2); } outlet_bang(x->x_gui.x_obj.ob_outlet); if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) pd_bang(x->x_gui.x_snd->s_thing); } static void bng_bang(t_bng *x) /* wird nur mehr gesendet, wenn snd != rcv*/ { if(!x->x_gui.x_isa.x_locked) { bng_set(x); bng_bout1(x); } } static void bng_bang2(t_bng *x) /* wird immer gesendet, wenn moeglich*/ { if(!x->x_gui.x_isa.x_locked) { bng_set(x); bng_bout2(x); } } static void bng_dialog(t_bng *x, t_symbol *s, int argc, t_atom *argv) { t_symbol *srl[3]; int a = (int)atom_getfloatarg(0, argc, argv); int fthold = (int)atom_getfloatarg(2, argc, argv); int ftbreak = (int)atom_getfloatarg(3, argc, argv); int sr_flags; t_atom undo[18]; iemgui_setdialogatoms(&x->x_gui, 18, undo); SETFLOAT (undo+1, 0); SETFLOAT (undo+2, x->x_flashtime_break); SETFLOAT (undo+3, x->x_flashtime_hold); pd_undo_set_objectstate(x->x_gui.x_glist, (t_pd*)x, gensym("dialog"), 18, undo, argc, argv); sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv); x->x_gui.x_w = iemgui_clip_size(a) * IEMGUI_ZOOM(x); x->x_gui.x_h = x->x_gui.x_w; bng_check_minmax(x, ftbreak, fthold); iemgui_size((void *)x, &x->x_gui); } static void bng_click(t_bng *x, t_floatarg xpos, t_floatarg ypos, t_floatarg shift, t_floatarg ctrl, t_floatarg alt) { bng_set(x); bng_bout2(x); } static int bng_newclick(t_gobj *z, struct _glist *glist, int xpix, int ypix, int shift, int alt, int dbl, int doit) { if(doit) bng_click((t_bng *)z, (t_floatarg)xpix, (t_floatarg)ypix, (t_floatarg)shift, 0, (t_floatarg)alt); return (1); } static void bng_float(t_bng *x, t_floatarg f) {bng_bang2(x);} static void bng_symbol(t_bng *x, t_symbol *s) {bng_bang2(x);} static void bng_pointer(t_bng *x, t_gpointer *gp) {bng_bang2(x);} static void bng_list(t_bng *x, t_symbol *s, int ac, t_atom *av) {bng_bang2(x);} static void bng_anything(t_bng *x, t_symbol *s, int argc, t_atom *argv) {bng_bang2(x);} static void bng_loadbang(t_bng *x, t_floatarg action) { if (action == LB_LOAD && x->x_gui.x_isa.x_loadinit) { bng_set(x); bng_bout2(x); } } static void bng_size(t_bng *x, t_symbol *s, int ac, t_atom *av) { x->x_gui.x_w = iemgui_clip_size((int)atom_getfloatarg(0, ac, av)) * IEMGUI_ZOOM(x); x->x_gui.x_h = x->x_gui.x_w; iemgui_size((void *)x, &x->x_gui); } static void bng_delta(t_bng *x, t_symbol *s, int ac, t_atom *av) {iemgui_delta((void *)x, &x->x_gui, s, ac, av);} static void bng_pos(t_bng *x, t_symbol *s, int ac, t_atom *av) {iemgui_pos((void *)x, &x->x_gui, s, ac, av);} static void bng_flashtime(t_bng *x, t_symbol *s, int ac, t_atom *av) { bng_check_minmax(x, (int)atom_getfloatarg(0, ac, av), (int)atom_getfloatarg(1, ac, av)); } static void bng_color(t_bng *x, t_symbol *s, int ac, t_atom *av) {iemgui_color((void *)x, &x->x_gui, s, ac, av);} static void bng_send(t_bng *x, t_symbol *s) {iemgui_send(x, &x->x_gui, s);} static void bng_receive(t_bng *x, t_symbol *s) {iemgui_receive(x, &x->x_gui, s);} static void bng_label(t_bng *x, t_symbol *s) {iemgui_label((void *)x, &x->x_gui, s);} static void bng_label_pos(t_bng *x, t_symbol *s, int ac, t_atom *av) {iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);} static void bng_label_font(t_bng *x, t_symbol *s, int ac, t_atom *av) {iemgui_label_font((void *)x, &x->x_gui, s, ac, av);} static void bng_init(t_bng *x, t_floatarg f) { x->x_gui.x_isa.x_loadinit = (f == 0.0) ? 0 : 1; } static void bng_tick_hld(t_bng *x) { x->x_flashed = 0; (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); } static void bng_tick_lck(t_bng *x) { x->x_gui.x_isa.x_locked = 0; } static void *bng_new(t_symbol *s, int argc, t_atom *argv) { t_bng *x = (t_bng *)iemgui_new(bng_class); int a = IEM_GUI_DEFAULTSIZE; int ldx = 0, ldy = -8 * IEM_GUI_DEFAULTSIZE_SCALE; int fs = x->x_gui.x_fontsize; int ftbreak = IEM_BNG_DEFAULTBREAKFLASHTIME, fthold = IEM_BNG_DEFAULTHOLDFLASHTIME; IEMGUI_SETDRAWFUNCTIONS(x, bng); if((argc == 14)&&IS_A_FLOAT(argv,0) &&IS_A_FLOAT(argv,1)&&IS_A_FLOAT(argv,2) &&IS_A_FLOAT(argv,3) &&(IS_A_SYMBOL(argv,4)||IS_A_FLOAT(argv,4)) &&(IS_A_SYMBOL(argv,5)||IS_A_FLOAT(argv,5)) &&(IS_A_SYMBOL(argv,6)||IS_A_FLOAT(argv,6)) &&IS_A_FLOAT(argv,7)&&IS_A_FLOAT(argv,8) &&IS_A_FLOAT(argv,9)&&IS_A_FLOAT(argv,10)) { a = (int)atom_getfloatarg(0, argc, argv); fthold = (int)atom_getfloatarg(1, argc, argv); ftbreak = (int)atom_getfloatarg(2, argc, argv); iem_inttosymargs(&x->x_gui.x_isa, atom_getfloatarg(3, argc, argv)); iemgui_new_getnames(&x->x_gui, 4, argv); ldx = (int)atom_getfloatarg(7, argc, argv); ldy = (int)atom_getfloatarg(8, argc, argv); iem_inttofstyle(&x->x_gui.x_fsf, atom_getfloatarg(9, argc, argv)); fs = (int)atom_getfloatarg(10, argc, argv); iemgui_all_loadcolors(&x->x_gui, argv+11, argv+12, argv+13); } else iemgui_new_getnames(&x->x_gui, 4, 0); x->x_gui.x_fsf.x_snd_able = (0 != x->x_gui.x_snd); x->x_gui.x_fsf.x_rcv_able = (0 != x->x_gui.x_rcv); x->x_flashed = 0; if(x->x_gui.x_fsf.x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica"); else if(x->x_gui.x_fsf.x_font_style == 2) strcpy(x->x_gui.x_font, "times"); else { x->x_gui.x_fsf.x_font_style = 0; strcpy(x->x_gui.x_font, sys_font); } if (x->x_gui.x_fsf.x_rcv_able) pd_bind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); x->x_gui.x_ldx = ldx; x->x_gui.x_ldy = ldy; x->x_gui.x_fontsize = (fs < 4)?4:fs; x->x_gui.x_w = iemgui_clip_size(a); x->x_gui.x_h = x->x_gui.x_w; bng_check_minmax(x, ftbreak, fthold); x->x_gui.x_isa.x_locked = 0; iemgui_verify_snd_ne_rcv(&x->x_gui); x->x_lastflashtime = clock_getsystime(); x->x_clock_hld = clock_new(x, (t_method)bng_tick_hld); x->x_clock_lck = clock_new(x, (t_method)bng_tick_lck); iemgui_newzoom(&x->x_gui); outlet_new(&x->x_gui.x_obj, &s_bang); return (x); } static void bng_free(t_bng *x) { if(x->x_gui.x_fsf.x_rcv_able) pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); clock_free(x->x_clock_lck); clock_free(x->x_clock_hld); pdgui_stub_deleteforkey(x); } void g_bang_setup(void) { bng_class = class_new(gensym("bng"), (t_newmethod)bng_new, (t_method)bng_free, sizeof(t_bng), 0, A_GIMME, 0); class_addbang(bng_class, bng_bang); class_addfloat(bng_class, bng_float); class_addsymbol(bng_class, bng_symbol); class_addpointer(bng_class, bng_pointer); class_addlist(bng_class, bng_list); class_addanything(bng_class, bng_anything); class_addmethod(bng_class, (t_method)bng_click, gensym("click"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); class_addmethod(bng_class, (t_method)bng_dialog, gensym("dialog"), A_GIMME, 0); class_addmethod(bng_class, (t_method)bng_loadbang, gensym("loadbang"), A_DEFFLOAT, 0); class_addmethod(bng_class, (t_method)bng_size, gensym("size"), A_GIMME, 0); class_addmethod(bng_class, (t_method)bng_delta, gensym("delta"), A_GIMME, 0); class_addmethod(bng_class, (t_method)bng_pos, gensym("pos"), A_GIMME, 0); class_addmethod(bng_class, (t_method)bng_flashtime, gensym("flashtime"), A_GIMME, 0); class_addmethod(bng_class, (t_method)bng_color, gensym("color"), A_GIMME, 0); class_addmethod(bng_class, (t_method)bng_send, gensym("send"), A_DEFSYM, 0); class_addmethod(bng_class, (t_method)bng_receive, gensym("receive"), A_DEFSYM, 0); class_addmethod(bng_class, (t_method)bng_label, gensym("label"), A_DEFSYM, 0); class_addmethod(bng_class, (t_method)bng_label_pos, gensym("label_pos"), A_GIMME, 0); class_addmethod(bng_class, (t_method)bng_label_font, gensym("label_font"), A_GIMME, 0); class_addmethod(bng_class, (t_method)bng_init, gensym("init"), A_FLOAT, 0); class_addmethod(bng_class, (t_method)iemgui_zoom, gensym("zoom"), A_CANT, 0); bng_widgetbehavior.w_getrectfn = bng_getrect; bng_widgetbehavior.w_displacefn = iemgui_displace; bng_widgetbehavior.w_selectfn = iemgui_select; bng_widgetbehavior.w_activatefn = NULL; bng_widgetbehavior.w_deletefn = iemgui_delete; bng_widgetbehavior.w_visfn = iemgui_vis; bng_widgetbehavior.w_clickfn = bng_newclick; class_setwidget(bng_class, &bng_widgetbehavior); class_setsavefn(bng_class, bng_save); class_setpropertiesfn(bng_class, bng_properties); } ================================================ FILE: libs/libpd/pure-data/src/g_canvas.c ================================================ /* Copyright (c) 1997-2001 Miller Puckette and others. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* this file defines the "glist" class, also known as "canvas" (the two used to be different but are now unified except for some fossilized names.) */ #include #include #include "m_pd.h" #include "m_imp.h" #include "s_stuff.h" #include "g_canvas.h" #include "s_utf8.h" #include #include "g_undo.h" #ifdef _WIN32 #include #endif #include "m_private_utils.h" /* LATER consider adding font size to this struct (see glist_getfont()) */ struct _canvasenvironment { t_symbol *ce_dir; /* directory patch lives in */ int ce_argc; /* number of "$" arguments */ t_atom *ce_argv; /* array of "$" arguments */ int ce_dollarzero; /* value of "$0" */ t_namelist *ce_path; /* search path */ }; typedef struct _canvas_private { t_undo undo; } t_canvas_private; #define GLIST_DEFCANVASWIDTH 450 #define GLIST_DEFCANVASHEIGHT 300 /* ---------------------- variables --------------------------- */ t_class *canvas_class; t_canvas *canvas_whichfind; /* last canvas we did a find in */ /* ------------------ forward function declarations --------------- */ static void canvas_start_dsp(void); static void canvas_stop_dsp(void); static void canvas_drawlines(t_canvas *x); static void canvas_dosetbounds(t_canvas *x, int x1, int y1, int x2, int y2); void canvas_reflecttitle(t_canvas *x); static void canvas_addtolist(t_canvas *x); static void canvas_takeofflist(t_canvas *x); static void canvas_pop(t_canvas *x, t_floatarg fvis); static void canvas_bind(t_canvas *x); static void canvas_unbind(t_canvas *x); void canvas_declare(t_canvas *x, t_symbol *s, int argc, t_atom *argv); /* ---------------- generic widget behavior ------------------------- */ void gobj_getrect(t_gobj *x, t_glist *glist, int *x1, int *y1, int *x2, int *y2) { if (x->g_pd->c_wb && x->g_pd->c_wb->w_getrectfn) (*x->g_pd->c_wb->w_getrectfn)(x, glist, x1, y1, x2, y2); else *x1 = *y1 = 0, *x2 = *y2 = 10; } void gobj_displace(t_gobj *x, t_glist *glist, int dx, int dy) { if (x->g_pd->c_wb && x->g_pd->c_wb->w_displacefn) (*x->g_pd->c_wb->w_displacefn)(x, glist, dx, dy); } /* here we add an extra check whether we're mapped, because some editing moves are carried out on invisible windows (notably, re-creating abstractions when one is saved). Should any other widget functions also be doing this? */ void gobj_select(t_gobj *x, t_glist *glist, int state) { if (glist->gl_mapped && x->g_pd->c_wb && x->g_pd->c_wb->w_selectfn) (*x->g_pd->c_wb->w_selectfn)(x, glist, state); } void gobj_activate(t_gobj *x, t_glist *glist, int state) { if (x->g_pd->c_wb && x->g_pd->c_wb->w_activatefn) (*x->g_pd->c_wb->w_activatefn)(x, glist, state); } void gobj_delete(t_gobj *x, t_glist *glist) { if (x->g_pd->c_wb && x->g_pd->c_wb->w_deletefn) (*x->g_pd->c_wb->w_deletefn)(x, glist); } int gobj_shouldvis(t_gobj *x, struct _glist *glist) { t_object *ob; int has_parent = !glist->gl_havewindow && glist->gl_isgraph && glist->gl_owner && !glist->gl_isclone; /* if our parent is a graph, and if that graph itself isn't visible, then we aren't either. */ if (has_parent && !gobj_shouldvis(&glist->gl_gobj, glist->gl_owner)) return (0); /* if we're graphing-on-parent and the object falls outside the graph rectangle, don't draw it. */ if (has_parent && glist->gl_goprect) { int x1, y1, x2, y2, gx1, gy1, gx2, gy2, m; /* for some reason the bounds check on arrays and scalars don't seem to apply here. Perhaps this was in order to allow arrays to reach outside their containers? I no longer understand this. */ if (pd_class(&x->g_pd) == scalar_class || pd_class(&x->g_pd) == garray_class) return (1); gobj_getrect(&glist->gl_gobj, glist->gl_owner, &x1, &y1, &x2, &y2); if (x1 > x2) m = x1, x1 = x2, x2 = m; if (y1 > y2) m = y1, y1 = y2, y2 = m; gobj_getrect(x, glist, &gx1, &gy1, &gx2, &gy2); #if 0 post("graph %d %d %d %d, %s %d %d %d %d", x1, x2, y1, y2, class_gethelpname(x->g_pd), gx1, gx2, gy1, gy2); #endif if (gx1 < x1 || gx1 > x2 || gx2 < x1 || gx2 > x2 || gy1 < y1 || gy1 > y2 || gy2 < y1 || gy2 > y2) return (0); } if ((ob = pd_checkobject(&x->g_pd))) { /* return true if the text box should be drawn. We don't show text boxes inside graphs---except comments, if we're doing the new (goprect) style. */ return (glist->gl_havewindow || (ob->te_pd != canvas_class && ob->te_pd->c_wb != &text_widgetbehavior) || (ob->te_pd == canvas_class && (((t_glist *)ob)->gl_isgraph)) || (glist->gl_goprect && (ob->te_type == T_TEXT))); } else return (1); } void gobj_vis(t_gobj *x, struct _glist *glist, int flag) { if (x->g_pd->c_wb && x->g_pd->c_wb->w_visfn && gobj_shouldvis(x, glist)) (*x->g_pd->c_wb->w_visfn)(x, glist, flag); } int gobj_click(t_gobj *x, struct _glist *glist, int xpix, int ypix, int shift, int alt, int dbl, int doit) { if (x->g_pd->c_wb && x->g_pd->c_wb->w_clickfn) return ((*x->g_pd->c_wb->w_clickfn)(x, glist, xpix, ypix, shift, alt, dbl, doit)); else return (0); } /* maintain the list of visible toplevels for the GUI's "windows" menu */ void canvas_updatewindowlist(void) { /* not if we're in a reload */ if (!THISGUI->i_reloadingabstraction) pdgui_vmess("::pd_menus::update_window_menu", 0); } /* add a glist the list of "root" canvases (toplevels without parents.) */ static void canvas_addtolist(t_canvas *x) { x->gl_next = pd_this->pd_canvaslist; pd_this->pd_canvaslist = x; } static void canvas_takeofflist(t_canvas *x) { /* take it off the window list */ if (x == pd_this->pd_canvaslist) pd_this->pd_canvaslist = x->gl_next; else { t_canvas *z; for (z = pd_this->pd_canvaslist; z->gl_next != x; z = z->gl_next) if (!z->gl_next) return; z->gl_next = x->gl_next; } } /* turn message tracing on and off globally */ void obj_dosettracing(t_object *ob, int onoff); static void canvas_dosettracing(t_canvas *x, int onoff) { t_gobj *y; for (y = x->gl_list; y; y = y->g_next) { t_object *ob; if (pd_class(&y->g_pd) == canvas_class) canvas_dosettracing((t_canvas *)y, onoff); if ((ob = pd_checkobject(&y->g_pd))) obj_dosettracing(ob, onoff); } } void canvas_settracing(int onoff) { t_glist *gl; for (gl = pd_this->pd_canvaslist; gl; gl = gl->gl_next) canvas_dosettracing(gl, onoff); } /* --------- functions to handle the canvas environment ----------- */ void canvas_setargs(int argc, const t_atom *argv) { /* if there's an old one lying around free it here. This happens if an abstraction is loaded but never gets as far as calling canvas_new(). */ if (THISGUI->i_newargv) freebytes(THISGUI->i_newargv, THISGUI->i_newargc * sizeof(t_atom)); THISGUI->i_newargc = argc; THISGUI->i_newargv = copybytes(argv, argc * sizeof(t_atom)); } void glob_setfilename(void *dummy, t_symbol *filesym, t_symbol *dirsym) { THISGUI->i_newfilename = filesym; THISGUI->i_newdirectory = dirsym; } void glob_menunew(void *dummy, t_symbol *filesym, t_symbol *dirsym) { glob_setfilename(dummy, filesym, dirsym); canvas_new(0, 0, 0, 0); canvas_pop((t_canvas *)s__X.s_thing, 1); } t_canvas *canvas_getcurrent(void) { return ((t_canvas *)pd_findbyclass(&s__X, canvas_class)); } void canvas_setcurrent(t_canvas *x) { pd_pushsym(&x->gl_pd); } void canvas_unsetcurrent(t_canvas *x) { pd_popsym(&x->gl_pd); } t_canvasenvironment *canvas_getenv(const t_canvas *x) { if (!x) bug("canvas_getenv"); while (!x->gl_env) if (!(x = x->gl_owner)) bug("t_canvasenvironment"); return (x->gl_env); } int canvas_getdollarzero(void) { t_canvas *x = canvas_getcurrent(); t_canvasenvironment *env = (x ? canvas_getenv(x) : 0); if (env) return (env->ce_dollarzero); else return (0); } void canvas_getargs(int *argcp, t_atom **argvp) { t_canvas *x = canvas_getcurrent(); t_canvasenvironment *e = canvas_getenv(x); *argcp = e->ce_argc; *argvp = e->ce_argv; } t_symbol *canvas_realizedollar(t_canvas *x, t_symbol *s) { t_symbol *ret; const char *name = s->s_name; if (strchr(name, '$')) { t_canvasenvironment *env = canvas_getenv(x); canvas_setcurrent(x); ret = binbuf_realizedollsym(s, env->ce_argc, env->ce_argv, 1); canvas_unsetcurrent(x); } else ret = s; return (ret); } t_symbol *canvas_getcurrentdir(void) { t_canvasenvironment *e = canvas_getenv(canvas_getcurrent()); return (e->ce_dir); } t_symbol *canvas_getdir(const t_canvas *x) { t_canvasenvironment *e = canvas_getenv(x); return (e->ce_dir); } void canvas_makefilename(const t_canvas *x, const char *file, char *result, int resultsize) { const char *dir = canvas_getenv(x)->ce_dir->s_name; if (file[0] == '/' || (file[0] && file[1] == ':') || !*dir) { strncpy(result, file, resultsize); result[resultsize-1] = 0; } else { int nleft; strncpy(result, dir, resultsize); result[resultsize-1] = 0; nleft = resultsize - (int)strlen(result) - 1; if (nleft <= 0) return; strcat(result, "/"); strncat(result, file, nleft); result[resultsize-1] = 0; } } void canvas_rename(t_canvas *x, t_symbol *s, t_symbol *dir) { canvas_unbind(x); x->gl_name = s; canvas_bind(x); if (dir && dir != &s_) { t_canvasenvironment *e = canvas_getenv(x); e->ce_dir = dir; } if (x->gl_havewindow) canvas_reflecttitle(x); } /* --------------- traversing the set of lines in a canvas ----------- */ int canvas_getindex(t_canvas *x, t_gobj *y) { t_gobj *y2; int indexno; for (indexno = 0, y2 = x->gl_list; y2 && y2 != y; y2 = y2->g_next) indexno++; return (indexno); } void linetraverser_start(t_linetraverser *t, t_canvas *x) { t->tr_ob = 0; t->tr_x = x; t->tr_nextoc = 0; t->tr_nextoutno = t->tr_nout = 0; } t_outconnect *linetraverser_next(t_linetraverser *t) { t_outconnect *rval = t->tr_nextoc; int outno; while (!rval) { outno = t->tr_nextoutno; while (outno == t->tr_nout) { t_gobj *y; t_object *ob = 0; if (!t->tr_ob) y = t->tr_x->gl_list; else y = t->tr_ob->ob_g.g_next; for (; y; y = y->g_next) if ((ob = pd_checkobject(&y->g_pd))) break; if (!ob) return (0); t->tr_ob = ob; t->tr_nout = obj_noutlets(ob); outno = 0; if (glist_isvisible(t->tr_x)) gobj_getrect(y, t->tr_x, &t->tr_x11, &t->tr_y11, &t->tr_x12, &t->tr_y12); else t->tr_x11 = t->tr_y11 = t->tr_x12 = t->tr_y12 = 0; } t->tr_nextoutno = outno + 1; rval = obj_starttraverseoutlet(t->tr_ob, &t->tr_outlet, outno); t->tr_outno = outno; } t->tr_nextoc = obj_nexttraverseoutlet(rval, &t->tr_ob2, &t->tr_inlet, &t->tr_inno); t->tr_nin = obj_ninlets(t->tr_ob2); if (!t->tr_nin) bug("drawline"); if (glist_isvisible(t->tr_x)) { int inplus = (t->tr_nin == 1 ? 1 : t->tr_nin - 1); int outplus = (t->tr_nout == 1 ? 1 : t->tr_nout - 1); int iow = IOWIDTH * t->tr_x->gl_zoom; int iom = IOMIDDLE * t->tr_x->gl_zoom; gobj_getrect(&t->tr_ob2->ob_g, t->tr_x, &t->tr_x21, &t->tr_y21, &t->tr_x22, &t->tr_y22); t->tr_lx1 = t->tr_x11 + ((t->tr_x12 - t->tr_x11 - iow) * t->tr_outno) / outplus + iom; t->tr_ly1 = t->tr_y12; t->tr_lx2 = t->tr_x21 + ((t->tr_x22 - t->tr_x21 - iow) * t->tr_inno)/inplus + iom; t->tr_ly2 = t->tr_y21; } else { t->tr_x21 = t->tr_y21 = t->tr_x22 = t->tr_y22 = 0; t->tr_lx1 = t->tr_ly1 = t->tr_lx2 = t->tr_ly2 = 0; } return (rval); } void linetraverser_skipobject(t_linetraverser *t) { t->tr_nextoc = 0; t->tr_nextoutno = t->tr_nout; } /* -------------------- the canvas object -------------------------- */ int glist_valid = 10000; void glist_init(t_glist *x) { /* zero out everyone except "pd" field */ memset(((char *)x) + sizeof(x->gl_pd), 0, sizeof(*x) - sizeof(x->gl_pd)); x->gl_stub = gstub_new(x, 0); x->gl_valid = ++glist_valid; x->gl_xlabel = (t_symbol **)t_getbytes(0); x->gl_ylabel = (t_symbol **)t_getbytes(0); x->gl_privatedata = getbytes(sizeof(t_canvas_private)); } /* make a new glist. It will either be a "root" canvas or else it appears as a "text" object in another window (canvas_getcurrent() tells us which.) */ t_canvas *canvas_new(void *dummy, t_symbol *sel, int argc, t_atom *argv) { t_canvas *x = (t_canvas *)pd_new(canvas_class); t_canvas *owner = canvas_getcurrent(); t_symbol *s = &s_; int vis = 0, width = GLIST_DEFCANVASWIDTH, height = GLIST_DEFCANVASHEIGHT; int xloc = GLIST_DEFCANVASXLOC, yloc = GLIST_DEFCANVASYLOC; int font = (owner ? owner->gl_font : sys_defaultfont); glist_init(x); x->gl_obj.te_type = T_OBJECT; if (!owner) canvas_addtolist(x); /* post("canvas %p, owner %p", x, owner); */ if (argc == 5) /* toplevel: x, y, w, h, font */ { xloc = atom_getfloatarg(0, argc, argv); yloc = atom_getfloatarg(1, argc, argv); width = atom_getfloatarg(2, argc, argv); height = atom_getfloatarg(3, argc, argv); font = atom_getfloatarg(4, argc, argv); } else if (argc == 6) /* subwindow: x, y, w, h, name, vis */ { xloc = atom_getfloatarg(0, argc, argv); yloc = atom_getfloatarg(1, argc, argv); width = atom_getfloatarg(2, argc, argv); height = atom_getfloatarg(3, argc, argv); s = atom_getsymbolarg(4, argc, argv); vis = atom_getfloatarg(5, argc, argv); } /* (otherwise assume we're being created from the menu.) */ if (THISGUI->i_newdirectory && THISGUI->i_newdirectory->s_name[0]) { t_canvasenvironment *env = x->gl_env = (t_canvasenvironment *)getbytes(sizeof(*x->gl_env)); if (!THISGUI->i_newargv) THISGUI->i_newargv = getbytes(0); env->ce_dir = THISGUI->i_newdirectory; env->ce_argc = THISGUI->i_newargc; env->ce_argv = THISGUI->i_newargv; env->ce_dollarzero = THISGUI->i_dollarzero++; env->ce_path = 0; THISGUI->i_newdirectory = &s_; THISGUI->i_newargc = 0; THISGUI->i_newargv = 0; } else x->gl_env = 0; /* initialize private data, like the undo-queue */ canvas_undo_init(x); x->gl_x1 = 0; x->gl_y1 = 0; x->gl_x2 = 1; x->gl_y2 = 1; canvas_dosetbounds(x, xloc, yloc, xloc + width, yloc + height); x->gl_owner = owner; x->gl_isclone = 0; x->gl_name = (*s->s_name ? s : (THISGUI->i_newfilename ? THISGUI->i_newfilename : gensym("Pd"))); canvas_bind(x); x->gl_loading = 1; x->gl_goprect = 0; /* no GOP rectangle unless it's turned on later */ /* cancel "vis" flag if we're a subpatch of an abstraction inside another patch. A separate mechanism prevents the toplevel abstraction from showing up. */ if (vis && gensym("#X")->s_thing && ((*gensym("#X")->s_thing) == canvas_class)) { t_canvas *zzz = (t_canvas *)(gensym("#X")->s_thing); while (zzz && !zzz->gl_env) zzz = zzz->gl_owner; if (zzz && canvas_isabstraction(zzz) && zzz->gl_owner) vis = 0; } x->gl_willvis = vis; x->gl_edit = !UNTITLED_STRNCMP(x->gl_name->s_name); x->gl_font = sys_nearestfontsize(font); x->gl_zoom = (owner ? owner->gl_zoom : 1); pd_pushsym(&x->gl_pd); return(x); } void canvas_setgraph(t_glist *x, int flag, int nogoprect); static void canvas_coords(t_glist *x, t_symbol *s, int argc, t_atom *argv) { /* FIXME: this is a stopgap - we should always be using glist_getzoom() and never gl_zoom in rest of code. */ x->gl_zoom = glist_getzoom(x); x->gl_x1 = atom_getfloatarg(0, argc, argv); x->gl_y1 = atom_getfloatarg(1, argc, argv); x->gl_x2 = atom_getfloatarg(2, argc, argv); x->gl_y2 = atom_getfloatarg(3, argc, argv); x->gl_pixwidth = atom_getfloatarg(4, argc, argv); x->gl_pixheight = atom_getfloatarg(5, argc, argv); if (argc <= 7) canvas_setgraph(x, atom_getfloatarg(6, argc, argv), 1); else { x->gl_xmargin = atom_getfloatarg(7, argc, argv); x->gl_ymargin = atom_getfloatarg(8, argc, argv); canvas_setgraph(x, atom_getfloatarg(6, argc, argv), 0); } } /* make a new glist and add it to this glist. It will appear as a "graph", not a text object. */ t_glist *glist_addglist(t_glist *g, t_symbol *sym, t_float x1, t_float y1, t_float x2, t_float y2, t_float px1, t_float py1, t_float px2, t_float py2) { static int gcount = 0; /* it's OK if two threads get the same value */ int zz; int menu = 0; const char *str; t_glist *x = (t_glist *)pd_new(canvas_class); glist_init(x); x->gl_obj.te_type = T_OBJECT; if (!*sym->s_name) { char buf[40]; sprintf(buf, "graph%d", ++gcount); sym = gensym(buf); menu = 1; } else if (!strncmp((str = sym->s_name), "graph", 5) && (zz = atoi(str + 5)) > gcount) gcount = zz; /* in 0.34 and earlier, the pixel rectangle and the y bounds were reversed; this would behave the same, except that the dialog window would be confusing. The "correct" way is to have "py1" be the value that is higher on the screen. */ if (py2 < py1) { t_float zz; zz = y2; y2 = y1; y1 = zz; zz = py2; py2 = py1; py1 = zz; } if (x1 == x2 || y1 == y2) x1 = 0, x2 = 100, y1 = 1, y2 = -1; if (px1 >= px2 || py1 >= py2) px1 = 100, py1 = 20, px2 = 100 + GLIST_DEFGRAPHWIDTH, py2 = 20 + GLIST_DEFGRAPHHEIGHT; x->gl_name = sym; x->gl_x1 = x1; x->gl_x2 = x2; x->gl_y1 = y1; x->gl_y2 = y2; x->gl_obj.te_xpix = px1; x->gl_obj.te_ypix = py1; x->gl_pixwidth = px2 - px1; x->gl_pixheight = py2 - py1; x->gl_font = (canvas_getcurrent() ? canvas_getcurrent()->gl_font : sys_defaultfont); x->gl_zoom = g->gl_zoom; x->gl_screenx1 = GLIST_DEFCANVASXLOC; x->gl_screeny1 = GLIST_DEFCANVASYLOC; x->gl_screenx2 = GLIST_DEFCANVASWIDTH; x->gl_screeny2 = GLIST_DEFCANVASHEIGHT; x->gl_owner = g; canvas_bind(x); x->gl_isgraph = 1; x->gl_goprect = 0; x->gl_obj.te_binbuf = binbuf_new(); /* initialize private data, like the undo-queue */ canvas_undo_init(x); binbuf_addv(x->gl_obj.te_binbuf, "s", gensym("graph")); if (!menu) pd_pushsym(&x->gl_pd); glist_add(g, &x->gl_gobj); return (x); } /* call glist_addglist from a Pd message */ void glist_glist(t_glist *g, t_symbol *s, int argc, t_atom *argv) { t_symbol *sym = atom_getsymbolarg(0, argc, argv); t_float x1 = atom_getfloatarg(1, argc, argv); t_float y1 = atom_getfloatarg(2, argc, argv); t_float x2 = atom_getfloatarg(3, argc, argv); t_float y2 = atom_getfloatarg(4, argc, argv); t_float px1 = atom_getfloatarg(5, argc, argv); t_float py1 = atom_getfloatarg(6, argc, argv); t_float px2 = atom_getfloatarg(7, argc, argv); t_float py2 = atom_getfloatarg(8, argc, argv); glist_addglist(g, sym, x1, y1, x2, y2, px1, py1, px2, py2); if (!canvas_undo_get(glist_getcanvas(g))->u_doing) canvas_undo_add(glist_getcanvas(g), UNDO_CREATE, "create", (void *)canvas_undo_set_create(glist_getcanvas(g))); } /* return true if the glist should appear as a graph on parent; otherwise it appears as a text box. */ int glist_isgraph(t_glist *x) { return (x->gl_isgraph|(x->gl_hidetext<<1)); } /* This is sent from the GUI to inform a toplevel that its window has been moved or resized. */ static void canvas_setbounds(t_canvas *x, t_float left, t_float top, t_float right, t_float bottom) { canvas_dosetbounds(x, (int)left, (int)top, (int)right, (int)bottom); } /* this is the internal version using ints */ static void canvas_dosetbounds(t_canvas *x, int x1, int y1, int x2, int y2) { int heightwas = y2 - y1; int heightchange = y2 - y1 - (x->gl_screeny2 - x->gl_screeny1); if (x->gl_screenx1 == x1 && x->gl_screeny1 == y1 && x->gl_screenx2 == x2 && x->gl_screeny2 == y2) return; x->gl_screenx1 = x1; x->gl_screeny1 = y1; x->gl_screenx2 = x2; x->gl_screeny2 = y2; if (!glist_isgraph(x) && (x->gl_y2 < x->gl_y1)) { /* if it's flipped so that y grows upward, fix so that zero is bottom edge and redraw. This is only appropriate if we're a regular "text" object on the parent. */ t_float diff = x->gl_y1 - x->gl_y2; t_gobj *y; x->gl_y1 = heightwas * diff/x->gl_zoom; x->gl_y2 = x->gl_y1 - diff; /* and move text objects accordingly; they should stick to the bottom, not the top. */ for (y = x->gl_list; y; y = y->g_next) if (pd_checkobject(&y->g_pd)) gobj_displace(y, x, 0, heightchange/x->gl_zoom); canvas_redraw(x); } } t_symbol *canvas_makebindsym(t_symbol *s) { char buf[MAXPDSTRING]; snprintf(buf, MAXPDSTRING-1, "pd-%s", s->s_name); buf[MAXPDSTRING-1] = 0; return (gensym(buf)); } /* functions to bind and unbind canvases to symbol "pd-blah". As discussed on Pd dev list there should be a way to defeat this for abstractions. (Claude Heiland et al. Aug 9 2013) */ static void canvas_bind(t_canvas *x) { if (strcmp(x->gl_name->s_name, "Pd")) pd_bind(&x->gl_pd, canvas_makebindsym(x->gl_name)); } static void canvas_unbind(t_canvas *x) { if (strcmp(x->gl_name->s_name, "Pd")) pd_unbind(&x->gl_pd, canvas_makebindsym(x->gl_name)); } void canvas_reflecttitle(t_canvas *x) { char namebuf[MAXPDSTRING]; t_canvasenvironment *env = canvas_getenv(x); if (!x->gl_havewindow) { bug("canvas_reflecttitle"); return; } if (env->ce_argc) { int i; strcpy(namebuf, " ("); for (i = 0; i < env->ce_argc; i++) { if (strlen(namebuf) > MAXPDSTRING/2 - 5) break; if (i != 0) strcat(namebuf, " "); atom_string(&env->ce_argv[i], namebuf + strlen(namebuf), MAXPDSTRING/2); } strcat(namebuf, ")"); } else namebuf[0] = 0; if (x->gl_edit) { strncat(namebuf, " [edit]", MAXPDSTRING-strlen(namebuf)-1); namebuf[MAXPDSTRING-1] = 0; } pdgui_vmess("pdtk_canvas_reflecttitle", "^ sss i", x, canvas_getdir(x)->s_name, x->gl_name->s_name, namebuf, x->gl_dirty); } /* mark a glist dirty or clean */ void canvas_dirty(t_canvas *x, t_floatarg f) { t_canvas *x2 = canvas_getrootfor(x); unsigned int n = f; if (THISGUI->i_reloadingabstraction) return; if (n != x2->gl_dirty) { x2->gl_dirty = n; if (x2->gl_havewindow) canvas_reflecttitle(x2); } if(!n) canvas_undo_cleardirty(x); } void canvas_drawredrect(t_canvas *x, int doit) { if (doit) { int x1 = x->gl_zoom * x->gl_xmargin, x2 = x1 + x->gl_zoom * x->gl_pixwidth, y1 = x->gl_zoom * x->gl_ymargin, y2 = y1 + x->gl_zoom * x->gl_pixheight; pdgui_vmess(0, "crr iiiiiiiiii rr ri rr rr", glist_getcanvas(x), "create", "line", x1,y1, x1,y2, x2,y2, x2,y1, x1,y1, "-fill", "#ff8080", "-width", x->gl_zoom, "-capstyle", "projecting", "-tags", "GOP"); /* better: "-tags", 1, &"GOP" */ } else pdgui_vmess(0, "crs", glist_getcanvas(x), "delete", "GOP"); } /* the window becomes "mapped" (visible and not miniaturized) or "unmapped" (either miniaturized or just plain gone.) This should be called from the GUI after the fact to "notify" us that we're mapped. */ void canvas_map(t_canvas *x, t_floatarg f) { int flag = (f != 0); t_gobj *y; if (flag) { if (!glist_isvisible(x)) { t_selection *sel; if (!x->gl_havewindow) { bug("canvas_map"); canvas_vis(x, 1); } for (y = x->gl_list; y; y = y->g_next) gobj_vis(y, x, 1); x->gl_mapped = 1; for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next) gobj_select(sel->sel_what, x, 1); canvas_drawlines(x); if (x->gl_isgraph && x->gl_goprect) canvas_drawredrect(x, 1); pdgui_vmess("pdtk_canvas_getscroll", "c", x); } } else { if (glist_isvisible(x)) { if (!x->gl_havewindow) { bug("canvas_map"); return; } /* just clear out the whole canvas */ pdgui_vmess(0, "crs", x, "delete", "all"); x->gl_mapped = 0; } } } void canvas_redraw(t_canvas *x) { if (glist_isvisible(x)) { canvas_map(x, 0); canvas_map(x, 1); } } /* we call this on a non-toplevel glist to "open" it into its own window. */ void glist_menu_open(t_glist *x) { if (glist_isvisible(x) && !glist_istoplevel(x)) { t_glist *gl2 = x->gl_owner; if (!gl2) bug("glist_menu_open"); /* shouldn't happen but not dangerous */ else { /* erase ourself in parent window */ gobj_vis(&x->gl_gobj, gl2, 0); /* get rid of our editor (and subeditors) */ if (x->gl_editor) canvas_destroy_editor(x); x->gl_havewindow = 1; /* redraw ourself in parent window (blanked out this time) */ gobj_vis(&x->gl_gobj, gl2, 1); } } canvas_vis(x, 1); } int glist_isvisible(t_glist *x) { return ((!x->gl_loading) && glist_getcanvas(x)->gl_mapped); } int glist_istoplevel(t_glist *x) { /* we consider a graph "toplevel" if it has its own window or if it appears as a box in its parent window so that we don't draw the actual contents there. */ return (x->gl_havewindow || !x->gl_isgraph); } int glist_getfont(t_glist *x) { while (!x->gl_env) if (!(x = x->gl_owner)) bug("t_canvasenvironment"); return (x->gl_font); } int glist_getzoom(t_glist *x) { t_glist *gl2 = x; while (!glist_istoplevel(gl2) && gl2->gl_owner) gl2 = gl2->gl_owner; return (gl2->gl_zoom); } int glist_fontwidth(t_glist *x) { return (sys_zoomfontwidth(glist_getfont(x), glist_getzoom(x), 0)); } int glist_fontheight(t_glist *x) { return (sys_zoomfontheight(glist_getfont(x), glist_getzoom(x), 0)); } void canvas_free(t_canvas *x) { t_gobj *y; t_canvas_private*private = x->gl_privatedata; int dspstate = canvas_suspend_dsp(); canvas_noundo(x); if (canvas_whichfind == x) canvas_whichfind = 0; glist_noselect(x); while ((y = x->gl_list)) glist_delete(x, y); if (x == glist_getcanvas(x)) canvas_vis(x, 0); if (x->gl_editor) canvas_destroy_editor(x); /* bug workaround; should already be gone*/ canvas_unbind(x); if (x->gl_env) { freebytes(x->gl_env->ce_argv, x->gl_env->ce_argc * sizeof(t_atom)); freebytes(x->gl_env, sizeof(*x->gl_env)); } canvas_undo_free(x); freebytes(private, sizeof(*private)); canvas_resume_dsp(dspstate); freebytes(x->gl_xlabel, x->gl_nxlabels * sizeof(*(x->gl_xlabel))); freebytes(x->gl_ylabel, x->gl_nylabels * sizeof(*(x->gl_ylabel))); gstub_cutoff(x->gl_stub); pdgui_stub_deleteforkey(x); /* probably unnecessary */ if (!x->gl_owner && !x->gl_isclone) canvas_takeofflist(x); } /* ----------------- lines ---------- */ static void canvas_drawlines(t_canvas *x) { t_linetraverser t; t_outconnect *oc; { char tag[128]; const char*tags[2] = {tag, "cord"}; linetraverser_start(&t, x); while ((oc = linetraverser_next(&t))) { sprintf(tag, "l%p", oc); pdgui_vmess(0, "crr iiii ri rS", glist_getcanvas(x), "create", "line", t.tr_lx1,t.tr_ly1, t.tr_lx2,t.tr_ly2, "-width", (outlet_getsymbol(t.tr_outlet) == &s_signal ? 2:1) * x->gl_zoom, "-tags", 2, tags); } } } void canvas_fixlinesfor(t_canvas *x, t_text *text) { t_linetraverser t; t_outconnect *oc; linetraverser_start(&t, x); while ((oc = linetraverser_next(&t))) { if (t.tr_ob == text || t.tr_ob2 == text) { char tag[128]; sprintf(tag, "l%p", oc); pdgui_vmess(0, "crs iiii", glist_getcanvas(x), "coords", tag, t.tr_lx1,t.tr_ly1, t.tr_lx2,t.tr_ly2); } } } static void _canvas_delete_line(t_canvas*x, t_outconnect *oc) { char tag[128]; if (!glist_isvisible(x)) return; sprintf(tag, "l%p", oc); pdgui_vmess(0, "crs", glist_getcanvas(x), "delete", tag); } /* kill all lines for the object */ void canvas_deletelinesfor(t_canvas *x, t_text *text) { t_linetraverser t; t_outconnect *oc; linetraverser_start(&t, x); while ((oc = linetraverser_next(&t))) { if (t.tr_ob == text || t.tr_ob2 == text) { _canvas_delete_line(x, oc); obj_disconnect(t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno); } } } /* kill all lines for one inlet or outlet */ void canvas_deletelinesforio(t_canvas *x, t_text *text, t_inlet *inp, t_outlet *outp) { t_linetraverser t; t_outconnect *oc; linetraverser_start(&t, x); while ((oc = linetraverser_next(&t))) { if ((t.tr_ob == text && t.tr_outlet == outp) || (t.tr_ob2 == text && t.tr_inlet == inp)) { _canvas_delete_line(x, oc); obj_disconnect(t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno); } } } typedef void (*t_zoomfn)(void *x, t_floatarg arg1); static void canvas_pop(t_canvas *x, t_floatarg fvis) { if (glist_istoplevel(x) && (sys_zoom_open == 2)) { t_zoomfn zoommethod = (t_zoomfn)zgetfn(&x->gl_pd, gensym("zoom")); if (zoommethod) (*zoommethod)(&x->gl_pd, (t_floatarg)2); } if (fvis != 0) canvas_vis(x, 1); pd_popsym(&x->gl_pd); canvas_resortinlets(x); canvas_resortoutlets(x); x->gl_loading = 0; } void canvas_objfor(t_glist *gl, t_text *x, int argc, t_atom *argv); void canvas_restore(t_canvas *x, t_symbol *s, int argc, t_atom *argv) { t_pd *z; if (argc > 3) { t_atom *ap=argv+3; if (ap->a_type == A_SYMBOL) { t_canvasenvironment *e = canvas_getenv(canvas_getcurrent()); canvas_rename(x, binbuf_realizedollsym(ap->a_w.w_symbol, e->ce_argc, e->ce_argv, 1), 0); } } canvas_pop(x, x->gl_willvis); if (!(z = gensym("#X")->s_thing)) pd_error(0, "canvas_restore: out of context"); else if (*z != canvas_class) pd_error(0, "canvas_restore: wasn't a canvas"); else { t_canvas *x2 = (t_canvas *)z; x->gl_owner = x2; canvas_objfor(x2, &x->gl_obj, argc, argv); } } static void canvas_loadbangabstractions(t_canvas *x) { t_gobj *y; t_symbol *s = gensym("loadbang"); for (y = x->gl_list; y; y = y->g_next) if (pd_class(&y->g_pd) == canvas_class) { if (canvas_isabstraction((t_canvas *)y)) canvas_loadbang((t_canvas *)y); else canvas_loadbangabstractions((t_canvas *)y); } else if ((pd_class(&y->g_pd) == clone_class) && zgetfn(&y->g_pd, s)) { pd_vmess(&y->g_pd, s, "f", (t_floatarg)LB_LOAD); } } void canvas_loadbangsubpatches(t_canvas *x) { t_gobj *y; t_symbol *s = gensym("loadbang"); for (y = x->gl_list; y; y = y->g_next) if (pd_class(&y->g_pd) == canvas_class) { if (!canvas_isabstraction((t_canvas *)y)) canvas_loadbangsubpatches((t_canvas *)y); } for (y = x->gl_list; y; y = y->g_next) if ((pd_class(&y->g_pd) != canvas_class) && (pd_class(&y->g_pd) != clone_class) && zgetfn(&y->g_pd, s)) { pd_vmess(&y->g_pd, s, "f", (t_floatarg)LB_LOAD); } } void canvas_loadbang(t_canvas *x) { canvas_loadbangabstractions(x); canvas_loadbangsubpatches(x); } /* JMZ/MSP: * initbang is emitted after a canvas is read from a file, but before the parent canvas is finished loading. This is apparently used so that abstractions can create inlets/outlets as a function of creation arguments. This practice is quite ugly but there's no other way to do it so far. */ void canvas_initbang(t_canvas *x) { t_gobj *y; t_symbol *s = gensym("loadbang"); /* run "initbang" for all subpatches, but NOT for the child abstractions */ for (y = x->gl_list; y; y = y->g_next) if (pd_class(&y->g_pd) == canvas_class && !canvas_isabstraction((t_canvas *)y)) canvas_initbang((t_canvas *)y); /* call the initbang()-method for objects that have one */ for (y = x->gl_list; y; y = y->g_next) if ((pd_class(&y->g_pd) != canvas_class) && zgetfn(&y->g_pd, s)) pd_vmess(&y->g_pd, s, "f", (t_floatarg)LB_INIT); } /* JMZ: * closebang is emitted before the canvas is destroyed * and BEFORE subpatches/abstractions in this canvas are destroyed */ void canvas_closebang(t_canvas *x) { t_gobj *y; t_symbol *s = gensym("loadbang"); /* call the closebang()-method for objects that have one * but NOT for subpatches/abstractions: these are called separately * from g_graph:glist_delete() */ for (y = x->gl_list; y; y = y->g_next) if ((pd_class(&y->g_pd) != canvas_class) && zgetfn(&y->g_pd, s)) pd_vmess(&y->g_pd, s, "f", (t_floatarg)LB_CLOSE); } /* no longer used by 'pd-gui', but kept here for backwards compatibility. The * new method calls canvas_setbounds() directly. */ static void canvas_relocate(t_canvas *x, t_symbol *canvasgeom, t_symbol *topgeom) { int cxpix, cypix, cw, ch, txpix, typix, tw, th; if (sscanf(canvasgeom->s_name, "%dx%d+%d+%d", &cw, &ch, &cxpix, &cypix) < 4 || sscanf(topgeom->s_name, "%dx%d+%d+%d", &tw, &th, &txpix, &typix) < 4) bug("canvas_relocate"); /* for some reason this is initially called with cw=ch=1 so we just suppress that here. */ if (cw > 5 && ch > 5) canvas_dosetbounds(x, txpix, typix, txpix + cw, typix + ch); } void canvas_popabstraction(t_canvas *x) { pd_this->pd_newest = &x->gl_pd; gensym("#A")->s_thing = 0; pd_bind(pd_this->pd_newest, gensym("#A")); pd_popsym(&x->gl_pd); x->gl_loading = 0; canvas_resortinlets(x); canvas_resortoutlets(x); } void canvas_logerror(t_object *y) { #ifdef LATER canvas_vis(x, 1); if (!glist_isselected(x, &y->ob_g)) glist_select(x, &y->ob_g); #endif } /* -------------------------- subcanvases ---------------------- */ extern void canvas_obj(t_glist *gl, t_symbol *s, int argc, t_atom *argv); static void *subcanvas_new(t_symbol *s) { t_atom a[6]; t_canvas *x, *z = canvas_getcurrent(); if (!*s->s_name) s = gensym("/SUBPATCH/"); SETFLOAT(a+0, GLIST_DEFCANVASXLOC); SETFLOAT(a+1, GLIST_DEFCANVASYLOC); SETFLOAT(a+2, GLIST_DEFCANVASWIDTH); SETFLOAT(a+3, GLIST_DEFCANVASHEIGHT); SETSYMBOL(a+4, s); SETFLOAT(a+5, 1); x = canvas_new(0, 0, 6, a); /* check if subpatch is supposed to be connected (on the 1st inlet) */ if(z && z->gl_editor && z->gl_editor->e_connectbuf) { t_atom*argv = binbuf_getvec(z->gl_editor->e_connectbuf); int argc = binbuf_getnatom(z->gl_editor->e_connectbuf); t_symbol *sob = 0; if ((argc == 7) && atom_getsymbolarg(0, argc, argv) == gensym("#X") && atom_getsymbolarg(1, argc, argv) == gensym("connect")) { int index2 = canvas_getindex(z, &x->gl_gobj); if (((int)atom_getfloat(argv+5) == 0) && (int)atom_getfloat(argv+4) == index2) { int index1 = (int)atom_getfloat(argv+2); int outno = (int)atom_getfloat(argv+3); t_gobj*outobj=z->gl_list; /* get handle to object */ while(index1-->0 && outobj) outobj=outobj->g_next; if(outobj && pd_checkobject(&outobj->g_pd)) { if (obj_issignaloutlet(pd_checkobject(&outobj->g_pd), outno)) sob = gensym("inlet~"); else sob = gensym("inlet"); } } } if(sob) { /* JMZ: weirdo hardcoded numbers, taken from * glist_getnextxy(): 40 * and canvas_howputnew(): -3 */ SETFLOAT(a+0, 37); SETFLOAT(a+1, 37); SETSYMBOL(a+2, sob); canvas_obj(x, gensym("obj"), 3, a); /* select the newly created inlet to continue autopatching */ canvas_create_editor(x); glist_noselect(x); glist_select(x, x->gl_list); } } x->gl_owner = z; canvas_pop(x, 1); return (x); } static void canvas_click(t_canvas *x, t_floatarg xpos, t_floatarg ypos, t_floatarg shift, t_floatarg ctrl, t_floatarg alt) { canvas_vis(x, 1); } /* find out from subcanvas contents how much to fatten the box */ void canvas_fattensub(t_canvas *x, int *xp1, int *yp1, int *xp2, int *yp2) { t_gobj *y; *xp2 += 50; /* fake for now */ *yp2 += 50; } static void canvas_rename_method(t_canvas *x, t_symbol *s, int ac, t_atom *av) { if (ac && av->a_type == A_SYMBOL) canvas_rename(x, av->a_w.w_symbol, 0); else if (ac && av->a_type == A_DOLLSYM) { t_canvasenvironment *e = canvas_getenv(x); canvas_setcurrent(x); canvas_rename(x, binbuf_realizedollsym(av->a_w.w_symbol, e->ce_argc, e->ce_argv, 1), 0); canvas_unsetcurrent(x); } else canvas_rename(x, gensym("Pd"), 0); } /* return true if the "canvas" object is an abstraction (so we don't save its contents, for example.) */ int canvas_isabstraction(const t_canvas *x) { return (x->gl_env != 0); } /* return true if the "canvas" object should be treated as a text object. This is true for abstractions but also for "table"s... */ /* JMZ: add a flag to gop-abstractions to hide the title */ int canvas_showtext(const t_canvas *x) { t_atom *argv = (x->gl_obj.te_binbuf? binbuf_getvec(x->gl_obj.te_binbuf):0); int argc = (x->gl_obj.te_binbuf? binbuf_getnatom(x->gl_obj.te_binbuf) : 0); int isarray = (argc && argv[0].a_type == A_SYMBOL && argv[0].a_w.w_symbol == gensym("graph")); if(x->gl_hidetext) return 0; else return (!isarray); } /* get the document containing this canvas */ t_canvas *canvas_getrootfor(t_canvas *x) { if ((!x->gl_owner) || canvas_isabstraction(x)) return (x); else return (canvas_getrootfor(x->gl_owner)); } t_undo* canvas_undo_get(t_canvas *x) { t_canvas_private*private = x?(x->gl_privatedata):0; if(private) return &(private->undo); return 0; } /* ------------------------- DSP chain handling ------------------------- */ EXTERN_STRUCT _dspcontext; #define t_dspcontext struct _dspcontext void ugen_start(void); void ugen_stop(void); t_dspcontext *ugen_start_graph(int toplevel, t_signal **sp, int ninlets, int noutlets); void ugen_add(t_dspcontext *dc, t_object *x); void ugen_connect(t_dspcontext *dc, t_object *x1, int outno, t_object *x2, int inno); void ugen_done_graph(t_dspcontext *dc); /* schedule one canvas for DSP. This is called below for all "root" canvases, but is also called from the "dsp" method for sub- canvases, which are treated almost like any other tilde object. */ void canvas_dodsp(t_canvas *x, int toplevel, t_signal **sp) { t_linetraverser t; t_outconnect *oc; t_gobj *y; t_object *ob; t_symbol *dspsym = gensym("dsp"); t_dspcontext *dc; #if 0 { int i, n = obj_nsiginlets(&x->gl_obj) + obj_nsigoutlets(&x->gl_obj); post("signals for %x (toplevel %d):", x, toplevel); for (i = 0; i < n; i++) { if (sp[i]) post("nchans %d, length %d", sp[i]->s_nchans, sp[i]->s_length); else post("(null)"); } } #endif /* create a new "DSP graph" object to use in sorting this canvas. If we aren't toplevel, there are already other dspcontexts around. */ dc = ugen_start_graph(toplevel, sp, obj_nsiginlets(&x->gl_obj), obj_nsigoutlets(&x->gl_obj)); /* find all the "dsp" boxes and add them to the graph */ for (y = x->gl_list; y; y = y->g_next) if ((ob = pd_checkobject(&y->g_pd)) && zgetfn(&y->g_pd, dspsym)) ugen_add(dc, ob); /* ... and all dsp interconnections */ linetraverser_start(&t, x); while ((oc = linetraverser_next(&t))) if (obj_issignaloutlet(t.tr_ob, t.tr_outno)) ugen_connect(dc, t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno); /* finally, sort them and add them to the DSP chain */ ugen_done_graph(dc); } static void canvas_dsp(t_canvas *x, t_signal **sp) { canvas_dodsp(x, 0, sp); } int canvas_dspstate; /* for back compatibility with externs - don't use */ /* this routine starts DSP for all root canvases. */ static void canvas_start_dsp(void) { t_canvas *x; if (THISGUI->i_dspstate) ugen_stop(); else pdgui_vmess("pdtk_pd_dsp", "s", "ON"); ugen_start(); for (x = pd_getcanvaslist(); x; x = x->gl_next) canvas_dodsp(x, 1, 0); canvas_dspstate = THISGUI->i_dspstate = 1; if (gensym("pd-dsp-started")->s_thing) pd_bang(gensym("pd-dsp-started")->s_thing); } static void canvas_stop_dsp(void) { if (THISGUI->i_dspstate) { ugen_stop(); pdgui_vmess("pdtk_pd_dsp", "s", "OFF"); canvas_dspstate = THISGUI->i_dspstate = 0; if (gensym("pd-dsp-stopped")->s_thing) pd_bang(gensym("pd-dsp-stopped")->s_thing); } } /* DSP can be suspended before, and resumed after, operations which might affect the DSP chain. For example, we suspend before loading and resume afterward, so that DSP doesn't get resorted for every DSP object int the patch. */ int canvas_suspend_dsp(void) { int rval = THISGUI->i_dspstate; if (rval) canvas_stop_dsp(); return (rval); } void canvas_resume_dsp(int oldstate) { if (oldstate) canvas_start_dsp(); } /* this is equivalent to suspending and resuming in one step. */ void canvas_update_dsp(void) { if (THISGUI->i_dspstate) { canvas_stop_dsp(); canvas_start_dsp(); } } /* the "dsp" message to pd starts and stops DSP computation, and, if appropriate, also opens and closes the audio device. On exclusive-access APIs such as ALSA, MMIO, and ASIO (I think) it's appropriate to close the audio devices when not using them; but jack behaves better if audio I/O simply keeps running. This is wasteful of CPU cycles but we do it anyway and can perhaps regard this is a design flaw in jack that we're working around here. The function audio_shouldkeepopen() is provided by s_audio.c to tell us that we should elide the step of closing audio when DSP is turned off.*/ void glob_dsp(void *dummy, t_symbol *s, int argc, t_atom *argv) { int newstate; if (argc) { newstate = atom_getfloatarg(0, argc, argv); if (newstate && !THISGUI->i_dspstate) { sys_set_audio_state(1); canvas_start_dsp(); } else if (!newstate && THISGUI->i_dspstate) { canvas_stop_dsp(); if (!audio_shouldkeepopen()) sys_set_audio_state(0); } } else post("dsp state %d", THISGUI->i_dspstate); } /******************* redrawing data *********************/ static int template_usestemplate(t_symbol *usersym, t_template *used) { int i; t_template *user = template_findbyname(usersym); if (!user) return (0); if (user == used) return (1); for (i = 0; i < user->t_n; i++) if (user->t_vec[i].ds_type == DT_ARRAY && template_usestemplate(user->t_vec[i].ds_arraytemplate, used)) return (1); return (0); } /* redraw all "scalars" that depend on a template, e.g., if a drawing command is changed. Action = 0 to redraw, 1 to draw only, 2 to erase. */ static void glist_doredrawfortemplate(t_glist *gl, t_template *template, int action) { t_gobj *g; int vis = glist_isvisible(gl); for (g = gl->gl_list; g; g = g->g_next) { if (vis && g->g_pd == scalar_class && template_usestemplate(((t_scalar *)g)->sc_template, template)) { if (action == 1) { if (glist_isvisible(gl)) gobj_vis(g, gl, 1); } else if (action == 2) { if (glist_isvisible(gl)) gobj_vis(g, gl, 0); } else scalar_redraw((t_scalar *)g, gl); } else if (g->g_pd == canvas_class) glist_doredrawfortemplate((t_glist *)g, template, action); } } /* public interface for above. */ void canvas_redrawallfortemplate(t_template *template, int action) { t_canvas *x; /* find all root canvases */ for (x = pd_getcanvaslist(); x; x = x->gl_next) glist_doredrawfortemplate(x, template, action); } /* find the template defined by a canvas, and redraw all affected scalars */ void canvas_redrawallfortemplatecanvas(t_canvas *x, int action) { t_template *template = template_findbyname(canvas_makebindsym(x->gl_name)); if (template) canvas_redrawallfortemplate(template, action); } /* ------------------------------- declare ------------------------ */ /* put "declare" objects in a patch to tell it about the environment in which objects should be created in this canvas. This includes directories to search ("-path", "-stdpath") and object libraries to load ("-lib" and "-stdlib"). These must be set before the patch containing the "declare" object is filled in with its contents; so when the patch is saved, we throw early messages to the canvas to set the environment before any objects are created in it. */ static t_class *declare_class; typedef struct _declare { t_object x_obj; t_canvas *x_canvas; int x_useme; } t_declare; static void *declare_new(t_symbol *s, int argc, t_atom *argv) { t_declare *x = (t_declare *)pd_new(declare_class); x->x_useme = 1; x->x_canvas = canvas_getcurrent(); /* LATER update environment and/or load libraries */ if (!x->x_canvas->gl_loading) { /* the object is created by the user (not by loading a patch), * so update canvas's properties on the fly */ canvas_declare(x->x_canvas, s, argc, argv); } return (x); } static void declare_free(t_declare *x) { x->x_useme = 0; /* LATER update environment */ } void canvas_savedeclarationsto(t_canvas *x, t_binbuf *b) { t_gobj *y; for (y = x->gl_list; y; y = y->g_next) { if (pd_class(&y->g_pd) == declare_class) { binbuf_addv(b, "s", gensym("#X")); binbuf_addbinbuf(b, ((t_declare *)y)->x_obj.te_binbuf); binbuf_addv(b, ";"); } /* before 0.47 we also allowed abstractions to write out to the parent's declarations; now we only allow non-abstraction subpatches to do so. */ else if (pd_checkglist(&y->g_pd) && (pd_compatibilitylevel < 47 || !canvas_isabstraction((t_canvas *)y))) canvas_savedeclarationsto((t_canvas *)y, b); } } static void canvas_completepath(const char *from, char *to, int bufsize, t_canvas *x) { if (sys_isabsolutepath(from)) { to[0] = '\0'; } else if (x) { /* append canvas dir */ const char *dir = canvas_getdir(x)->s_name; int dirlen = (int)strlen(dir); strncpy(to, dir, bufsize-dirlen); to[bufsize-dirlen-1] = '\0'; strcat(to, "/"); } else { /* append Pd lib dir */ strncpy(to, sys_libdir->s_name, bufsize-10); to[bufsize-9] = '\0'; strcat(to, "/extra/"); } strncat(to, from, bufsize-strlen(to)); to[bufsize-1] = '\0'; } /* maybe we should rename check_exists() to sys_access() and move it to s_path */ #ifdef _WIN32 static int check_exists(const char*path) { char pathbuf[MAXPDSTRING]; wchar_t ucs2path[MAXPDSTRING]; sys_bashfilename(path, pathbuf); u8_utf8toucs2(ucs2path, MAXPDSTRING, pathbuf, MAXPDSTRING-1); return (0 == _waccess(ucs2path, 0)); } #else #include static int check_exists(const char*path) { char pathbuf[MAXPDSTRING]; sys_bashfilename(path, pathbuf); return (0 == access(pathbuf, 0)); } #endif static void canvas_path(t_canvas *x, t_canvasenvironment *e, const char *path) { t_namelist *nl; char strbuf[MAXPDSTRING]; if (sys_isabsolutepath(path)) { e->ce_path = namelist_append(e->ce_path, path, 0); return; } /* explicit relative path, starts with ./ or ../ */ if ((strncmp("./", path, 2) == 0) || (strncmp("../", path, 3) == 0)) { e->ce_path = namelist_append(e->ce_path, path, 0); return; } /* check if path is a subdir of the canvas-path */ canvas_completepath(path, strbuf, MAXPDSTRING, x); if (check_exists(strbuf)) { e->ce_path = namelist_append(e->ce_path, path, 0); return; } /* check whether the given subdir is in one of the user search-paths */ for (nl=STUFF->st_searchpath; nl; nl=nl->nl_next) { snprintf(strbuf, MAXPDSTRING-1, "%s/%s/", nl->nl_string, path); strbuf[MAXPDSTRING-1]=0; if (check_exists(strbuf)) { e->ce_path = namelist_append(e->ce_path, strbuf, 0); return; } } /* check whether the given subdir is in one of the standard-paths */ for (nl=STUFF->st_staticpath; nl; nl=nl->nl_next) { snprintf(strbuf, MAXPDSTRING-1, "%s/%s/", nl->nl_string, path); strbuf[MAXPDSTRING-1]=0; if (check_exists(strbuf)) { e->ce_path = namelist_append(e->ce_path, strbuf, 0); return; } } } static void canvas_lib(t_canvas *x, t_canvasenvironment *e, const char *lib) { t_namelist *nl; char strbuf[MAXPDSTRING]; if (sys_isabsolutepath(lib)) { sys_load_lib(x, lib); return; } /* explicit relative path, starts with ./ or ../ */ if ((strncmp("./", lib, 2) == 0) || (strncmp("../", lib, 3) == 0)) { sys_load_lib(x, lib); return; } /* prefix canvas-path */ canvas_completepath(lib, strbuf, MAXPDSTRING, x); if (sys_load_lib(x, lib)) return; /* check whether the given lib is located in one of the user search-paths */ for (nl=STUFF->st_searchpath; nl; nl=nl->nl_next) { snprintf(strbuf, MAXPDSTRING-1, "%s/%s", nl->nl_string, lib); strbuf[MAXPDSTRING-1]=0; if (sys_load_lib(x, strbuf)) return; } } static void canvas_stdpath(t_canvasenvironment *e, const char *stdpath) { t_namelist *nl; char strbuf[MAXPDSTRING]; if (sys_isabsolutepath(stdpath)) { e->ce_path = namelist_append(e->ce_path, stdpath, 0); return; } /* strip "extra/"-prefix */ if (!strncmp("extra/", stdpath, 6)) stdpath+=6; /* prefix full pd-path (including extra) */ canvas_completepath(stdpath, strbuf, MAXPDSTRING, 0); if (check_exists(strbuf)) { e->ce_path = namelist_append(e->ce_path, strbuf, 0); return; } /* check whether the given subdir is in one of the standard-paths */ for (nl=STUFF->st_staticpath; nl; nl=nl->nl_next) { snprintf(strbuf, MAXPDSTRING-1, "%s/%s/", nl->nl_string, stdpath); strbuf[MAXPDSTRING-1]=0; if (check_exists(strbuf)) { e->ce_path = namelist_append(e->ce_path, strbuf, 0); return; } } } static void canvas_stdlib(t_canvasenvironment *e, const char *stdlib) { t_namelist *nl; char strbuf[MAXPDSTRING]; if (sys_isabsolutepath(stdlib)) { sys_load_lib(0, stdlib); return; } /* strip "extra/"-prefix */ if (!strncmp("extra/", stdlib, 6)) stdlib+=6; /* prefix full pd-path (including extra) */ canvas_completepath(stdlib, strbuf, MAXPDSTRING, 0); if (sys_load_lib(0, strbuf)) return; /* check whether the given lib is located in one of the standard-paths */ for (nl=STUFF->st_staticpath; nl; nl=nl->nl_next) { snprintf(strbuf, MAXPDSTRING-1, "%s/%s", nl->nl_string, stdlib); strbuf[MAXPDSTRING-1]=0; if (sys_load_lib(0, strbuf)) return; } } void canvas_declare(t_canvas *x, t_symbol *s, int argc, t_atom *argv) { int i; t_canvasenvironment *e = canvas_getenv(x); #if 0 startpost("declare:: %s", s->s_name); postatom(argc, argv); endpost(); #endif for (i = 0; i < argc; i++) { const char *flag = atom_getsymbolarg(i, argc, argv)->s_name; const char *item = (argc > i+1)?atom_getsymbolarg(i+1, argc, argv)->s_name:0; if ((item) && !strcmp(flag, "-path")) { canvas_path(x, e, item); i++; } else if ((item) && !strcmp(flag, "-stdpath")) { canvas_stdpath(e, item); i++; } else if ((item) && !strcmp(flag, "-lib")) { canvas_lib(x, e, item); i++; } else if ((item) && !strcmp(flag, "-stdlib")) { canvas_stdlib(e, item); i++; } else post("declare: %s: unknown declaration", flag); } } typedef struct _canvasopen { const char *name; const char *ext; char *dirresult; char **nameresult; unsigned int size; int bin; int fd; } t_canvasopen; static int canvas_open_iter(const char *path, t_canvasopen *co) { int fd; if ((fd = sys_trytoopenone(path, co->name, co->ext, co->dirresult, co->nameresult, co->size, co->bin)) >= 0) { co->fd = fd; return 0; } return 1; } /* utility function to read a file, looking first down the canvas's search path (set with "declare" objects in the patch and recursively in calling patches), then down the system one. The filename is the concatenation of "name" and "ext". "Name" may be absolute, or may be relative with slashes. If anything can be opened, the true directory ais put in the buffer dirresult (provided by caller), which should be "size" bytes. The "nameresult" pointer will be set somewhere in the interior of "dirresult" and will give the file basename (with slashes trimmed). If "bin" is set a 'binary' open is attempted, otherwise ASCII (this only matters on Microsoft.) If "x" is zero, the file is sought in the directory "." or in the global path.*/ int canvas_open(const t_canvas *x, const char *name, const char *ext, char *dirresult, char **nameresult, unsigned int size, int bin) { int fd = -1; t_canvasopen co; /* first check if "name" is absolute (and if so, try to open) */ if (sys_open_absolute(name, ext, dirresult, nameresult, size, bin, &fd)) return (fd); /* otherwise "name" is relative; iterate over all the search-paths */ co.name = name; co.ext = ext; co.dirresult = dirresult; co.nameresult = nameresult; co.size = size; co.bin = bin; co.fd = -1; canvas_path_iterate(x, (t_canvas_path_iterator)canvas_open_iter, &co); return (co.fd); } /* * Iterate over all search-paths for calling with the user-supplied * . The function is called with two arguments: a pathname to try to * open, and . */ int canvas_path_iterate(const t_canvas *x, t_canvas_path_iterator fun, void *user_data) { const t_canvas *y = 0; t_namelist *nl = 0; int count = 0; if (!fun) return 0; /* iterate through canvas-local paths */ for (y = x; y; y = y->gl_owner) if (y->gl_env) { const char *dir; dir = canvas_getdir(y)->s_name; for (nl = y->gl_env->ce_path; nl; nl = nl->nl_next) { char realname[MAXPDSTRING]; if (sys_isabsolutepath(nl->nl_string)) realname[0] = '\0'; else { /* if not absolute path, append Pd lib dir */ strncpy(realname, dir, MAXPDSTRING); realname[MAXPDSTRING-3] = 0; strcat(realname, "/"); } strncat(realname, nl->nl_string, MAXPDSTRING-strlen(realname)); realname[MAXPDSTRING-1] = 0; if (!fun(realname, user_data)) return count+1; count++; } } /* try canvas dir */ if (!fun((x ? canvas_getdir(x)->s_name : "."), user_data)) return count+1; count++; /* now iterate through the global paths */ for (nl = STUFF->st_searchpath; nl; nl = nl->nl_next) { if (!fun(nl->nl_string, user_data)) return count+1; count++; } /* and the temp paths from the commandline */ for (nl = STUFF->st_temppath; nl; nl = nl->nl_next) { if (!fun(nl->nl_string, user_data)) return count+1; count++; } /* and the default paths */ if (sys_usestdpath) for (nl = STUFF->st_staticpath; nl; nl = nl->nl_next) { if (!fun(nl->nl_string, user_data)) return count+1; count++; } return count; } static void canvas_f(t_canvas *x, t_symbol *s, int argc, t_atom *argv) { static int warned; t_gobj *g, *g2; t_object *ob; if (argc > 1 && !warned) { post("** ignoring width or font settings from future Pd version **"); warned = 1; } if (!x->gl_list) return; for (g = x->gl_list; (g2 = g->g_next); g = g2) ; if ((ob = pd_checkobject(&g->g_pd))) { ob->te_width = atom_getfloatarg(0, argc, argv); if (glist_isvisible(x)) { gobj_vis(g, x, 0); gobj_vis(g, x, 1); } } } extern t_class *array_define_class; /* LATER datum class too */ /* check if a pd can be treated as a glist - true if we're of any of the glist classes, which all have 'glist' as the first item in struct */ t_glist *pd_checkglist(t_pd *x) { if (*x == canvas_class || *x == array_define_class) return ((t_canvas *)x); else return (0); } /* ------------------------------- setup routine ------------------------ */ /* why are some of these "glist" and others "canvas"? */ extern void glist_text(t_glist *x, t_symbol *s, int argc, t_atom *argv); extern void canvas_obj(t_glist *gl, t_symbol *s, int argc, t_atom *argv); extern void canvas_bng(t_glist *gl, t_symbol *s, int argc, t_atom *argv); extern void canvas_toggle(t_glist *gl, t_symbol *s, int argc, t_atom *argv); extern void canvas_vslider(t_glist *gl, t_symbol *s, int argc, t_atom *argv); extern void canvas_hslider(t_glist *gl, t_symbol *s, int argc, t_atom *argv); extern void canvas_vdial(t_glist *gl, t_symbol *s, int argc, t_atom *argv); /* old version... */ extern void canvas_hdial(t_glist *gl, t_symbol *s, int argc, t_atom *argv); extern void canvas_hdial(t_glist *gl, t_symbol *s, int argc, t_atom *argv); /* new version: */ extern void canvas_hradio(t_glist *gl, t_symbol *s, int argc, t_atom *argv); extern void canvas_vradio(t_glist *gl, t_symbol *s, int argc, t_atom *argv); extern void canvas_vumeter(t_glist *gl, t_symbol *s, int argc, t_atom *argv); extern void canvas_mycnv(t_glist *gl, t_symbol *s, int argc, t_atom *argv); extern void canvas_numbox(t_glist *gl, t_symbol *s, int argc, t_atom *argv); extern void canvas_msg(t_glist *gl, t_symbol *s, int argc, t_atom *argv); extern void canvas_floatatom(t_glist *gl, t_symbol *s, int argc, t_atom *argv); extern void canvas_listbox(t_glist *gl, t_symbol *s, int argc, t_atom *argv); extern void canvas_symbolatom(t_glist *gl, t_symbol *s, int argc, t_atom *argv); extern void glist_scalar(t_glist *canvas, t_symbol *s, int argc, t_atom *argv); void g_graph_setup(void); void g_editor_setup(void); void g_readwrite_setup(void); extern void canvas_properties(t_gobj *z, t_glist *canvas); void g_canvas_setup(void) { /* we prevent the user from typing "canvas" in an object box by sending 0 for a creator function. */ canvas_class = class_new(gensym("canvas"), 0, (t_method)canvas_free, sizeof(t_canvas), CLASS_NOINLET | CLASS_MULTICHANNEL, 0); /* here is the real creator function, invoked in patch files by sending the "canvas" message to #N, which is bound to pd_camvasmaker. */ class_addmethod(pd_canvasmaker, (t_method)canvas_new, gensym("canvas"), A_GIMME, 0); class_addmethod(canvas_class, (t_method)canvas_restore, gensym("restore"), A_GIMME, 0); class_addmethod(canvas_class, (t_method)canvas_coords, gensym("coords"), A_GIMME, 0); /* -------------------------- objects ----------------------------- */ class_addmethod(canvas_class, (t_method)canvas_obj, gensym("obj"), A_GIMME, A_NULL); class_addmethod(canvas_class, (t_method)canvas_msg, gensym("msg"), A_GIMME, A_NULL); class_addmethod(canvas_class, (t_method)canvas_floatatom, gensym("floatatom"), A_GIMME, A_NULL); class_addmethod(canvas_class, (t_method)canvas_listbox, gensym("listbox"), A_GIMME, A_NULL); class_addmethod(canvas_class, (t_method)canvas_symbolatom, gensym("symbolatom"), A_GIMME, A_NULL); class_addmethod(canvas_class, (t_method)glist_text, gensym("text"), A_GIMME, A_NULL); class_addmethod(canvas_class, (t_method)glist_glist, gensym("graph"), A_GIMME, A_NULL); class_addmethod(canvas_class, (t_method)glist_scalar, gensym("scalar"), A_GIMME, A_NULL); /* -------------- connect method used in reading files ------------------ */ class_addmethod(canvas_class, (t_method)canvas_connect, gensym("connect"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); /* -------------- IEMGUI: button, toggle, slider, etc. ------------ */ class_addmethod(canvas_class, (t_method)canvas_bng, gensym("bng"), A_GIMME, A_NULL); class_addmethod(canvas_class, (t_method)canvas_toggle, gensym("toggle"), A_GIMME, A_NULL); class_addmethod(canvas_class, (t_method)canvas_vslider, gensym("vslider"), A_GIMME, A_NULL); class_addmethod(canvas_class, (t_method)canvas_hslider, gensym("hslider"), A_GIMME, A_NULL); class_addmethod(canvas_class, (t_method)canvas_hdial, gensym("hdial"), A_GIMME, A_NULL); class_addmethod(canvas_class, (t_method)canvas_vdial, gensym("vdial"), A_GIMME, A_NULL); class_addmethod(canvas_class, (t_method)canvas_hradio, gensym("hradio"), A_GIMME, A_NULL); class_addmethod(canvas_class, (t_method)canvas_vradio, gensym("vradio"), A_GIMME, A_NULL); class_addmethod(canvas_class, (t_method)canvas_vumeter, gensym("vumeter"), A_GIMME, A_NULL); class_addmethod(canvas_class, (t_method)canvas_mycnv, gensym("mycnv"), A_GIMME, A_NULL); class_addmethod(canvas_class, (t_method)canvas_numbox, gensym("numbox"), A_GIMME, A_NULL); /* ------------------------ gui stuff --------------------------- */ class_addmethod(canvas_class, (t_method)canvas_pop, gensym("pop"), A_DEFFLOAT, A_NULL); class_addmethod(canvas_class, (t_method)canvas_loadbang, gensym("loadbang"), A_NULL); class_addmethod(canvas_class, (t_method)canvas_setbounds, gensym("setbounds"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); class_addmethod(canvas_class, (t_method)canvas_relocate, gensym("relocate"), A_SYMBOL, A_SYMBOL, A_NULL); class_addmethod(canvas_class, (t_method)canvas_vis, gensym("vis"), A_FLOAT, A_NULL); class_addmethod(canvas_class, (t_method)glist_menu_open, gensym("menu-open"), A_NULL); class_addmethod(canvas_class, (t_method)canvas_map, gensym("map"), A_FLOAT, A_NULL); class_addmethod(canvas_class, (t_method)canvas_dirty, gensym("dirty"), A_FLOAT, A_NULL); class_setpropertiesfn(canvas_class, canvas_properties); /* ---------------------- list handling ------------------------ */ class_addmethod(canvas_class, (t_method)glist_clear, gensym("clear"), A_NULL); /* ----- subcanvases, which you get by typing "pd" in a box ---- */ class_addcreator((t_newmethod)subcanvas_new, gensym("pd"), A_DEFSYMBOL, 0); class_addcreator((t_newmethod)subcanvas_new, gensym("page"), A_DEFSYMBOL, 0); class_addmethod(canvas_class, (t_method)canvas_click, gensym("click"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); class_addmethod(canvas_class, (t_method)canvas_dsp, gensym("dsp"), A_CANT, 0); class_addmethod(canvas_class, (t_method)canvas_rename_method, gensym("rename"), A_GIMME, 0); /*---------------------------- declare ------------------- */ declare_class = class_new(gensym("declare"), (t_newmethod)declare_new, (t_method)declare_free, sizeof(t_declare), CLASS_NOINLET, A_GIMME, 0); class_addmethod(canvas_class, (t_method)canvas_declare, gensym("declare"), A_GIMME, 0); /*--------------- future message to set formatting -------------- */ class_addmethod(canvas_class, (t_method)canvas_f, gensym("f"), A_GIMME, 0); /* -------------- setups from other files for canvas_class ---------------- */ g_graph_setup(); g_editor_setup(); g_readwrite_setup(); } /* functions to add basic gui (e.g., clicking but not editing) to things based on canvases that aren't editable, like "array define" object */ void canvas_editor_for_class(t_class *c); void g_graph_setup_class(t_class *c); void canvas_readwrite_for_class(t_class *c); void canvas_add_for_class(t_class *c) { class_addmethod(c, (t_method)canvas_restore, gensym("restore"), A_GIMME, 0); class_addmethod(c, (t_method)canvas_click, gensym("click"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); class_addmethod(c, (t_method)canvas_dsp, gensym("dsp"), A_CANT, 0); class_addmethod(c, (t_method)canvas_map, gensym("map"), A_FLOAT, A_NULL); class_addmethod(c, (t_method)canvas_setbounds, gensym("setbounds"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); canvas_editor_for_class(c); canvas_readwrite_for_class(c); /* g_graph_setup_class(c); */ } void g_canvas_newpdinstance(void) { THISGUI = getbytes(sizeof(*THISGUI)); THISGUI->i_newfilename = THISGUI->i_newdirectory = &s_; THISGUI->i_newargc = 0; THISGUI->i_newargv = 0; THISGUI->i_reloadingabstraction = 0; THISGUI->i_dspstate = 0; THISGUI->i_dollarzero = 1000; g_editor_newpdinstance(); g_template_newpdinstance(); } void g_canvas_freepdinstance(void) { g_editor_freepdinstance(); g_template_freepdinstance(); freebytes(THISGUI, sizeof(*THISGUI)); } EXTERN int pd_getdspstate(void) { return (THISGUI->i_dspstate); } void pd_doloadbang(void); /* evaluate a file, which is expected to create a patch, and perform post-evaluation cleanup and loadbang */ t_pd *glob_evalfile(t_pd *ignore, t_symbol *name, t_symbol *dir) { t_pd *x = 0, *boundx; int dspstate; /* even though binbuf_evalfile appears to take care of dspstate, we have to do it again here, because canvas_startdsp() assumes that all toplevel canvases are visible. LATER check if this is still necessary -- probably not. */ dspstate = canvas_suspend_dsp(); boundx = s__X.s_thing; s__X.s_thing = 0; /* don't save #X; we'll need to leave it bound for the caller to grab it. */ binbuf_evalfile(name, dir); while ((x != s__X.s_thing) && s__X.s_thing) { x = s__X.s_thing; vmess(x, gensym("pop"), "i", 1); } if (!sys_noloadbang) pd_doloadbang(); canvas_resume_dsp(dspstate); s__X.s_thing = boundx; return x; } /* open a file as if from an open dialog from the GUI. If the optional argument "f" is nonzero, first check if the file is already open and if so, just "vis" it. This would be useful if you want merely to make sure a patch is open, but don't want more than one copy. */ void glob_open(t_pd *ignore, t_symbol *name, t_symbol *dir, t_floatarg f) { t_glist *gl; if (f != 0) for (gl = pd_getcanvaslist(); gl; gl = gl->gl_next) if (name == gl->gl_name && gl->gl_env && gl->gl_env->ce_dir == dir) { /* don't reopen already-open document, just vis it */ canvas_vis(gl, 1); return; } if (!glob_evalfile(ignore, name, dir)) pdgui_vmess("::pdwindow::busyrelease", 0); } ================================================ FILE: libs/libpd/pure-data/src/g_canvas.h ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* this file defines the structure for "glists" and related structures and functions. "Glists" and "canvases" and "graphs" used to be different structures until being unified in version 0.35. A glist occupies its own window if the "gl_havewindow" flag is set. Its appearance on its "parent", also called "owner", (if it has one) is as a graph if "gl_isgraph" is set, and otherwise as a text box. A glist is "root" if it has no owner, i.e., a document window. In this case "gl_havewindow" is always set. We maintain a list of root windows, so that we can traverse the whole collection of everything in a Pd process. If a glist has a window it may still not be "mapped." Miniaturized windows aren't mapped, for example, but a window is also not mapped immediately upon creation. In either case gl_havewindow is true but gl_mapped is false. Closing a non-root window makes it invisible; closing a root destroys it. A glist that's just a text object on its parent is always "toplevel." An embedded glist can switch back and forth to appear as a toplevel by double- clicking on it. Single-clicking a text box makes the toplevel become visible and raises the window it's in. If a glist shows up as a graph on its parent, the graph is blanked while the glist has its own window, even if miniaturized. */ /* NOTE: this file describes Pd implementation details which may change in future releases. The public (stable) API is in m_pd.h. */ #ifndef G_CANVAS_H #define G_CANVAS_H #if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus) extern "C" { #endif /* --------------------- geometry ---------------------------- */ #define IOWIDTH 7 /* width of an inlet/outlet in pixels */ #define IHEIGHT 3 /* height of an inlet in pixels */ #define OHEIGHT 3 /* height of an outlet in pixels */ #define IOMIDDLE ((IOWIDTH-1)/2) #define GLIST_DEFGRAPHWIDTH 200 #define GLIST_DEFGRAPHHEIGHT 140 #define GLIST_DEFCANVASXLOC 0 #ifdef __APPLE__ #define GLIST_DEFCANVASYLOC 22 #else #define GLIST_DEFCANVASYLOC 50 #endif /* ----------------------- data ------------------------------- */ typedef struct _updateheader { struct _updateheader *upd_next; unsigned int upd_array:1; /* true if array, false if glist */ unsigned int upd_queued:1; /* true if we're queued */ } t_updateheader; /* types to support glists grabbing mouse motion or keys from parent */ typedef void (*t_glistmotionfn)(void *z, t_floatarg dx, t_floatarg dy, t_floatarg up); typedef void (*t_glistkeyfn)(void *z, t_symbol *keysym, t_floatarg key); EXTERN_STRUCT _rtext; #define t_rtext struct _rtext EXTERN_STRUCT _gtemplate; #define t_gtemplate struct _gtemplate EXTERN_STRUCT _guiconnect; #define t_guiconnect struct _guiconnect EXTERN_STRUCT _tscalar; #define t_tscalar struct _tscalar EXTERN_STRUCT _canvasenvironment; #define t_canvasenvironment struct _canvasenvironment EXTERN_STRUCT _fielddesc; #define t_fielddesc struct _fielddesc typedef struct _selection { t_gobj *sel_what; struct _selection *sel_next; } t_selection; /* this structure is instantiated whenever a glist becomes visible. */ typedef struct _editor { t_updateheader e_upd; /* update header structure */ t_selection *e_updlist; /* list of objects to update */ t_rtext *e_rtext; /* text responder linked list */ t_selection *e_selection; /* head of the selection list */ t_rtext *e_textedfor; /* the rtext if any that we are editing */ t_gobj *e_grab; /* object being "dragged" */ t_glistmotionfn e_motionfn; /* ... motion callback */ t_glistkeyfn e_keyfn; /* ... keypress callback */ t_binbuf *e_connectbuf; /* connections to deleted objects */ t_binbuf *e_deleted; /* last stuff we deleted */ t_guiconnect *e_guiconnect; /* GUI connection for filtering messages */ struct _glist *e_glist; /* glist which owns this */ int e_xwas; /* xpos on last mousedown or motion event */ int e_ywas; /* ypos, similarly */ int e_selectline_index1; /* indices for the selected line if any */ int e_selectline_outno; /* (only valid if e_selectedline is set) */ int e_selectline_index2; int e_selectline_inno; t_outconnect *e_selectline_tag; unsigned int e_onmotion: 3; /* action to take on motion */ unsigned int e_lastmoved: 1; /* one if mouse has moved since click */ unsigned int e_textdirty: 1; /* one if e_textedfor has changed */ unsigned int e_selectedline: 1; /* one if a line is selected */ t_clock *e_clock; /* clock to filter GUI move messages */ int e_xnew; /* xpos for next move event */ int e_ynew; /* ypos, similarly */ } t_editor; #define MA_NONE 0 /* e_onmotion: do nothing on mouse motion */ #define MA_MOVE 1 /* drag the selection around */ #define MA_CONNECT 2 /* make a connection */ #define MA_REGION 3 /* selection region */ #define MA_PASSOUT 4 /* send on to e_grab */ #define MA_DRAGTEXT 5 /* drag in text editor to alter selection */ #define MA_RESIZE 6 /* drag to resize */ /* editor structure for "garrays". We don't bother to delete and regenerate this structure when the "garray" becomes invisible or visible, although we could do so if the structure gets big (like the "editor" above.) */ typedef struct _arrayvis { t_updateheader av_upd; /* update header structure */ t_garray *av_garray; /* owning structure */ } t_arrayvis; /* the t_tick structure describes where to draw x and y "ticks" for a glist */ typedef struct _tick /* where to put ticks on x or y axes */ { t_float k_point; /* one point to draw a big tick at */ t_float k_inc; /* x or y increment per little tick */ int k_lperb; /* little ticks per big; 0 if no ticks to draw */ } t_tick; /* the t_glist structure, which describes a list of elements that live on an area of a window. */ struct _glist { t_object gl_obj; /* header in case we're a glist */ t_gobj *gl_list; /* the actual data */ struct _gstub *gl_stub; /* safe pointer handler */ int gl_valid; /* incremented when pointers might be stale */ struct _glist *gl_owner; /* parent glist, supercanvas, or 0 if none */ int gl_pixwidth; /* width in pixels (on parent, if a graph) */ int gl_pixheight; t_float gl_x1; /* bounding rectangle in our own coordinates */ t_float gl_y1; t_float gl_x2; t_float gl_y2; int gl_screenx1; /* screen coordinates when toplevel */ int gl_screeny1; int gl_screenx2; int gl_screeny2; int gl_xmargin; /* origin for GOP rectangle */ int gl_ymargin; t_tick gl_xtick; /* ticks marking X values */ int gl_nxlabels; /* number of X coordinate labels */ t_symbol **gl_xlabel; /* ... an array to hold them */ t_float gl_xlabely; /* ... and their Y coordinates */ t_tick gl_ytick; /* same as above for Y ticks and labels */ int gl_nylabels; t_symbol **gl_ylabel; t_float gl_ylabelx; t_editor *gl_editor; /* editor structure when visible */ t_symbol *gl_name; /* symbol bound here */ int gl_font; /* nominal font size in points, e.g., 10 */ struct _glist *gl_next; /* link in list of toplevels */ t_canvasenvironment *gl_env; /* root canvases and abstractions only */ unsigned int gl_havewindow:1; /* true if we own a window */ unsigned int gl_mapped:1; /* true if, moreover, it's "mapped" */ unsigned int gl_dirty:1; /* (root canvas only:) patch has changed */ unsigned int gl_loading:1; /* am now loading from file */ unsigned int gl_willvis:1; /* make me visible after loading */ unsigned int gl_edit:1; /* edit mode */ unsigned int gl_isdeleting:1; /* we're inside glist_delete -- hack! */ unsigned int gl_goprect:1; /* draw rectangle for graph-on-parent */ unsigned int gl_isgraph:1; /* show as graph on parent */ unsigned int gl_hidetext:1; /* hide object-name + args when doing graph on parent */ unsigned int gl_private:1; /* private flag used in x_scalar.c */ unsigned int gl_isclone:1; /* exists as part of a clone object */ int gl_zoom; /* zoom factor (integer zoom-in only) */ void *gl_privatedata; /* private data */ }; #define gl_gobj gl_obj.te_g #define gl_pd gl_gobj.g_pd /* a data structure to describe a field in a pure datum */ #define DT_FLOAT 0 #define DT_SYMBOL 1 #define DT_TEXT 2 #define DT_ARRAY 3 typedef struct _dataslot { int ds_type; t_symbol *ds_name; t_symbol *ds_arraytemplate; /* filled in for arrays only */ } t_dataslot; typedef struct _template { t_pd t_pdobj; /* header */ struct _gtemplate *t_list; /* list of "struct"/gtemplate objects */ t_symbol *t_sym; /* name */ int t_n; /* number of dataslots (fields) */ t_dataslot *t_vec; /* array of dataslots */ struct _template *t_next; } t_template; struct _array { int a_n; /* number of elements */ int a_elemsize; /* size in bytes; LATER get this from template */ char *a_vec; /* array of elements */ t_symbol *a_templatesym; /* template for elements */ int a_valid; /* protection against stale pointers into array */ t_gpointer a_gp; /* pointer to scalar or array element we're in */ t_gstub *a_stub; /* stub for pointing into this array */ }; /* structure for traversing all the connections in a glist */ typedef struct _linetraverser { t_canvas *tr_x; t_object *tr_ob; int tr_nout; int tr_outno; t_object *tr_ob2; t_outlet *tr_outlet; t_inlet *tr_inlet; int tr_nin; int tr_inno; int tr_x11, tr_y11, tr_x12, tr_y12; int tr_x21, tr_y21, tr_x22, tr_y22; int tr_lx1, tr_ly1, tr_lx2, tr_ly2; t_outconnect *tr_nextoc; int tr_nextoutno; } t_linetraverser; struct _instancecanvas { struct _instanceeditor *i_editor; struct _instancetemplate *i_template; t_symbol *i_newfilename; t_symbol *i_newdirectory; int i_newargc; t_atom *i_newargv; t_glist *i_reloadingabstraction; int i_dspstate; int i_dollarzero; t_float i_graph_lastxpix, i_graph_lastypix; }; void g_editor_newpdinstance(void); void g_template_newpdinstance(void); void g_editor_freepdinstance(void); void g_template_freepdinstance(void); #define THISGUI (pd_this->pd_gui) #define EDITOR (pd_this->pd_gui->i_editor) #define TEMPLATE (pd_this->pd_gui->i_template) /* function types used to define graphical behavior for gobjs, a bit like X widgets. We don't use Pd methods because Pd's typechecking can't specify the types of pointer arguments. Also it's more convenient this way, since every "patchable" object can just get the "text" behaviors. */ /* Call this to get a gobj's bounding rectangle in pixels */ typedef void (*t_getrectfn)(t_gobj *x, struct _glist *glist, int *x1, int *y1, int *x2, int *y2); /* and this to displace a gobj: */ typedef void (*t_displacefn)(t_gobj *x, struct _glist *glist, int dx, int dy); /* change color to show selection: */ typedef void (*t_selectfn)(t_gobj *x, struct _glist *glist, int state); /* change appearance to show activation/deactivation: */ typedef void (*t_activatefn)(t_gobj *x, struct _glist *glist, int state); /* warn a gobj it's about to be deleted */ typedef void (*t_deletefn)(t_gobj *x, struct _glist *glist); /* making visible or invisible */ typedef void (*t_visfn)(t_gobj *x, struct _glist *glist, int flag); /* field a mouse click (when not in "edit" mode) */ typedef int (*t_clickfn)(t_gobj *x, struct _glist *glist, int xpix, int ypix, int shift, int alt, int dbl, int doit); /* ... and later, resizing; getting/setting font or color... */ struct _widgetbehavior { t_getrectfn w_getrectfn; t_displacefn w_displacefn; t_selectfn w_selectfn; t_activatefn w_activatefn; t_deletefn w_deletefn; t_visfn w_visfn; t_clickfn w_clickfn; }; /* -------- behaviors for scalars defined by objects in template --------- */ /* these are set by "drawing commands" in g_template.c which add appearance to scalars, which live in some other window. If the scalar is just included in a canvas the "parent" is a misnomer. There is also a text scalar object which really does draw the scalar on the parent window; see g_scalar.c. */ /* note how the click function wants the whole scalar, not the "data", so doesn't work on array elements... LATER reconsider this */ /* bounding rectangle: */ typedef void (*t_parentgetrectfn)(t_gobj *x, struct _glist *glist, t_word *data, t_template *tmpl, t_float basex, t_float basey, int *x1, int *y1, int *x2, int *y2); /* displace it */ typedef void (*t_parentdisplacefn)(t_gobj *x, struct _glist *glist, t_word *data, t_template *tmpl, t_float basex, t_float basey, int dx, int dy); /* change color to show selection */ typedef void (*t_parentselectfn)(t_gobj *x, struct _glist *glist, t_word *data, t_template *tmpl, t_float basex, t_float basey, int state); /* change appearance to show activation/deactivation: */ typedef void (*t_parentactivatefn)(t_gobj *x, struct _glist *glist, t_word *data, t_template *tmpl, t_float basex, t_float basey, int state); /* making visible or invisible */ typedef void (*t_parentvisfn)(t_gobj *x, struct _glist *glist, t_word *data, t_template *tmpl, t_float basex, t_float basey, int flag); /* field a mouse click */ typedef int (*t_parentclickfn)(t_gobj *x, struct _glist *glist, t_word *data, t_template *tmpl, t_scalar *sc, t_array *ap, t_float basex, t_float basey, int xpix, int ypix, int shift, int alt, int dbl, int doit); struct _parentwidgetbehavior { t_parentgetrectfn w_parentgetrectfn; t_parentdisplacefn w_parentdisplacefn; t_parentselectfn w_parentselectfn; t_parentactivatefn w_parentactivatefn; t_parentvisfn w_parentvisfn; t_parentclickfn w_parentclickfn; }; /* cursor definitions; used as return value for t_parentclickfn */ #define CURSOR_RUNMODE_NOTHING 0 #define CURSOR_RUNMODE_CLICKME 1 #define CURSOR_RUNMODE_THICKEN 2 #define CURSOR_RUNMODE_ADDPOINT 3 #define CURSOR_EDITMODE_NOTHING 4 #define CURSOR_EDITMODE_CONNECT 5 #define CURSOR_EDITMODE_DISCONNECT 6 #define CURSOR_EDITMODE_RESIZE 7 EXTERN void canvas_setcursor(t_glist *x, unsigned int cursornum); extern t_canvas *canvas_whichfind; /* last canvas we did a find in */ extern t_class *vinlet_class, *voutlet_class; extern int glist_valid; /* incremented when pointers might be stale */ #define PLOTSTYLE_POINTS 0 /* plotting styles for arrays */ #define PLOTSTYLE_POLY 1 #define PLOTSTYLE_BEZ 2 /* ------------------- functions on any gobj ----------------------------- */ EXTERN void gobj_getrect(t_gobj *x, t_glist *owner, int *x1, int *y1, int *x2, int *y2); EXTERN void gobj_displace(t_gobj *x, t_glist *owner, int dx, int dy); EXTERN void gobj_select(t_gobj *x, t_glist *owner, int state); EXTERN void gobj_activate(t_gobj *x, t_glist *owner, int state); EXTERN void gobj_delete(t_gobj *x, t_glist *owner); EXTERN void gobj_vis(t_gobj *x, t_glist *glist, int flag); EXTERN int gobj_click(t_gobj *x, struct _glist *glist, int xpix, int ypix, int shift, int alt, int dbl, int doit); EXTERN void gobj_save(t_gobj *x, t_binbuf *b); EXTERN int gobj_shouldvis(t_gobj *x, struct _glist *glist); /* -------------------- functions on glists --------------------- */ EXTERN void glist_init(t_glist *x); EXTERN void glist_add(t_glist *x, t_gobj *g); EXTERN void glist_clear(t_glist *x); EXTERN t_canvas *glist_getcanvas(t_glist *x); EXTERN int glist_isselected(t_glist *x, t_gobj *y); EXTERN void glist_select(t_glist *x, t_gobj *y); EXTERN void glist_deselect(t_glist *x, t_gobj *y); EXTERN void glist_noselect(t_glist *x); EXTERN void glist_selectall(t_glist *x); EXTERN void glist_delete(t_glist *x, t_gobj *y); EXTERN void glist_retext(t_glist *x, t_text *y); EXTERN void glist_grab(t_glist *x, t_gobj *y, t_glistmotionfn motionfn, t_glistkeyfn keyfn, int xpos, int ypos); EXTERN int glist_isvisible(t_glist *x); EXTERN int glist_istoplevel(t_glist *x); EXTERN t_glist *glist_findgraph(t_glist *x); EXTERN int glist_getfont(t_glist *x); EXTERN int glist_fontwidth(t_glist *x); EXTERN int glist_fontheight(t_glist *x); EXTERN int glist_getzoom(t_glist *x); EXTERN void glist_sort(t_glist *canvas); EXTERN void glist_read(t_glist *x, t_symbol *filename, t_symbol *format); EXTERN void glist_mergefile(t_glist *x, t_symbol *filename, t_symbol *format); EXTERN t_float glist_pixelstox(t_glist *x, t_float xpix); EXTERN t_float glist_pixelstoy(t_glist *x, t_float ypix); EXTERN t_float glist_xtopixels(t_glist *x, t_float xval); EXTERN t_float glist_ytopixels(t_glist *x, t_float yval); EXTERN t_float glist_dpixtodx(t_glist *x, t_float dxpix); EXTERN t_float glist_dpixtody(t_glist *x, t_float dypix); EXTERN void glist_getnextxy(t_glist *gl, int *xval, int *yval); EXTERN void glist_glist(t_glist *g, t_symbol *s, int argc, t_atom *argv); EXTERN t_glist *glist_addglist(t_glist *g, t_symbol *sym, t_float x1, t_float y1, t_float x2, t_float y2, t_float px1, t_float py1, t_float px2, t_float py2); EXTERN void glist_arraydialog(t_glist *parent, t_symbol *name, t_floatarg size, t_floatarg saveit, t_floatarg newgraph); EXTERN t_binbuf *glist_writetobinbuf(t_glist *x, int wholething); EXTERN int glist_isgraph(t_glist *x); EXTERN void glist_redraw(t_glist *x); EXTERN void glist_drawiofor(t_glist *glist, t_object *ob, int firsttime, const char *tag, int x1, int y1, int x2, int y2); EXTERN void glist_eraseiofor(t_glist *glist, t_object *ob, const char *tag); EXTERN void canvas_create_editor(t_glist *x); EXTERN void canvas_destroy_editor(t_glist *x); void canvas_deletelinesforio(t_canvas *x, t_text *text, t_inlet *inp, t_outlet *outp); /* -------------------- functions on texts ------------------------- */ EXTERN void text_setto(t_text *x, t_glist *glist, const char *buf, int bufsize); EXTERN void text_drawborder(t_text *x, t_glist *glist, const char *tag, int width, int height, int firsttime); EXTERN void text_eraseborder(t_text *x, t_glist *glist, const char *tag); EXTERN int text_xpix(t_text *x, t_glist *glist); EXTERN int text_ypix(t_text *x, t_glist *glist); extern const t_widgetbehavior text_widgetbehavior; /* -------------------- functions on rtexts ------------------------- */ #define RTEXT_DOWN 1 #define RTEXT_DRAG 2 #define RTEXT_DBL 3 #define RTEXT_SHIFT 4 EXTERN t_rtext *rtext_new(t_glist *glist, t_text *who); EXTERN t_rtext *glist_findrtext(t_glist *gl, t_text *who); EXTERN void rtext_draw(t_rtext *x); EXTERN void rtext_erase(t_rtext *x); EXTERN int rtext_height(t_rtext *x); EXTERN void rtext_displace(t_rtext *x, int dx, int dy); EXTERN void rtext_select(t_rtext *x, int state); EXTERN void rtext_activate(t_rtext *x, int state); EXTERN void rtext_free(t_rtext *x); EXTERN void rtext_key(t_rtext *x, int n, t_symbol *s); EXTERN void rtext_mouse(t_rtext *x, int xval, int yval, int flag); EXTERN void rtext_retext(t_rtext *x); EXTERN int rtext_width(t_rtext *x); EXTERN const char *rtext_gettag(t_rtext *x); EXTERN void rtext_gettext(t_rtext *x, char **buf, int *bufsize); EXTERN void rtext_getseltext(t_rtext *x, char **buf, int *bufsize); EXTERN t_text *rtext_getowner(t_rtext *x); /* -------------------- functions on canvases ------------------------ */ EXTERN t_class *canvas_class; EXTERN t_canvas *canvas_new(void *dummy, t_symbol *sel, int argc, t_atom *argv); EXTERN t_symbol *canvas_makebindsym(t_symbol *s); EXTERN void canvas_fixlinesfor(t_canvas *x, t_text *text); EXTERN void canvas_deletelinesfor(t_canvas *x, t_text *text); EXTERN void canvas_stowconnections(t_canvas *x); EXTERN void canvas_restoreconnections(t_canvas *x); EXTERN void canvas_redraw(t_canvas *x); EXTERN void canvas_closebang(t_canvas *x); EXTERN void canvas_initbang(t_canvas *x); EXTERN t_inlet *canvas_addinlet(t_canvas *x, t_pd *who, t_symbol *sym); EXTERN void canvas_rminlet(t_canvas *x, t_inlet *ip); EXTERN t_outlet *canvas_addoutlet(t_canvas *x, t_pd *who, t_symbol *sym); EXTERN void canvas_rmoutlet(t_canvas *x, t_outlet *op); EXTERN void canvas_redrawallfortemplate(t_template *tmpl, int action); EXTERN void canvas_redrawallfortemplatecanvas(t_canvas *x, int action); EXTERN void canvas_setcurrent(t_canvas *x); EXTERN void canvas_unsetcurrent(t_canvas *x); EXTERN t_symbol *canvas_realizedollar(t_canvas *x, t_symbol *s); EXTERN t_canvas *canvas_getrootfor(t_canvas *x); EXTERN void canvas_dirty(t_canvas *x, t_floatarg n); typedef int (*t_canvasapply)(t_canvas *x, t_int x1, t_int x2, t_int x3); EXTERN void canvas_resortinlets(t_canvas *x); EXTERN void canvas_resortoutlets(t_canvas *x); EXTERN void canvas_free(t_canvas *x); EXTERN void canvas_updatewindowlist(void); EXTERN void canvas_editmode(t_canvas *x, t_floatarg state); EXTERN int canvas_isabstraction(const t_canvas *x); EXTERN int canvas_istable(const t_canvas *x); EXTERN int canvas_showtext(const t_canvas *x); EXTERN void canvas_vis(t_canvas *x, t_floatarg f); EXTERN t_canvasenvironment *canvas_getenv(const t_canvas *x); EXTERN void canvas_rename(t_canvas *x, t_symbol *s, t_symbol *dir); EXTERN void canvas_loadbang(t_canvas *x); EXTERN int canvas_hitbox(t_canvas *x, t_gobj *y, int xpos, int ypos, int *x1p, int *y1p, int *x2p, int *y2p); EXTERN int canvas_setdeleting(t_canvas *x, int flag); #define LB_LOAD 0 /* "loadbang" actions - 0 for original meaning */ #define LB_INIT 1 /* loaded but not yet connected to parent patch */ #define LB_CLOSE 2 /* about to close */ typedef int (*t_undofn)(t_canvas *canvas, void *buf, int action); /* a function that does UNDO/REDO */ #define UNDO_FREE 0 /* free current undo/redo buffer */ #define UNDO_UNDO 1 /* undo */ #define UNDO_REDO 2 /* redo */ EXTERN void canvas_setundo(t_canvas *x, t_undofn undofn, void *buf, const char *name); EXTERN void canvas_noundo(t_canvas *x); EXTERN int canvas_getindex(t_canvas *x, t_gobj *y); EXTERN void canvas_connect(t_canvas *x, t_floatarg fwhoout, t_floatarg foutno, t_floatarg fwhoin, t_floatarg finno); EXTERN void canvas_disconnect(t_canvas *x, t_float index1, t_float outno, t_float index2, t_float inno); EXTERN int canvas_isconnected (t_canvas *x, t_text *ob1, int n1, t_text *ob2, int n2); EXTERN void canvas_selectinrect(t_canvas *x, int lox, int loy, int hix, int hiy); EXTERN t_glist *pd_checkglist(t_pd *x); typedef int (*t_canvas_path_iterator)(const char *path, void *user_data); EXTERN int canvas_path_iterate(const t_canvas *x, t_canvas_path_iterator fun, void *user_data); /* check string for untitled canvas filename prefix */ #define UNTITLED_STRNCMP(s) strncmp(s, "PDUNTITLED", 10) /* ---- functions on canvasses as objects --------------------- */ EXTERN void linetraverser_start(t_linetraverser *t, t_canvas *x); EXTERN t_outconnect *linetraverser_next(t_linetraverser *t); EXTERN void linetraverser_skipobject(t_linetraverser *t); /* --------- functions on garrays (graphical arrays) -------------------- */ EXTERN t_template *garray_template(t_garray *x); /* -------------------- arrays --------------------- */ #define GRAPH_ARRAY_SAVE 1 /* flags for graph_array() below */ #define GRAPH_ARRAY_PLOTSTYLE 6 /* 2-bit field, PLOTSTYLE_POINTS, etc */ #define GRAPH_ARRAY_SAVESIZE 8 /* save size as well as contents */ EXTERN t_garray *graph_array(t_glist *gl, t_symbol *s, t_symbol *tmpl, t_floatarg f, t_floatarg flags); EXTERN t_array *array_new(t_symbol *templatesym, t_gpointer *parent); EXTERN void array_resize(t_array *x, int n); EXTERN void array_free(t_array *x); EXTERN void array_redraw(t_array *a, t_glist *glist); EXTERN void array_resize_and_redraw(t_array *array, t_glist *glist, int n); /* --------------------- gpointers and stubs ---------------- */ EXTERN t_gstub *gstub_new(t_glist *gl, t_array *a); EXTERN void gstub_cutoff(t_gstub *gs); EXTERN void gpointer_setglist(t_gpointer *gp, t_glist *glist, t_scalar *x); EXTERN void gpointer_setarray(t_gpointer *gp, t_array *array, t_word *w); /* --------------------- scalars ------------------------- */ EXTERN void word_init(t_word *wp, t_template *tmpl, t_gpointer *gp); EXTERN void word_restore(t_word *wp, t_template *tmpl, int argc, t_atom *argv); EXTERN t_scalar *scalar_new(t_glist *owner, t_symbol *templatesym); EXTERN void word_free(t_word *wp, t_template *tmpl); EXTERN void scalar_getbasexy(t_scalar *x, t_float *basex, t_float *basey); EXTERN void scalar_redraw(t_scalar *x, t_glist *glist); EXTERN void canvas_writescalar(t_symbol *templatesym, t_word *w, t_binbuf *b, int amarrayelement); EXTERN int canvas_readscalar(t_glist *x, int natoms, t_atom *vec, int *p_nextmsg, int selectit); /* ------helper routines for "garrays" and "plots" -------------- */ EXTERN void array_getcoordinate(t_glist *glist, char *elem, int xonset, int yonset, int wonset, int indx, t_float basex, t_float basey, t_float xinc, t_fielddesc *xfielddesc, t_fielddesc *yfielddesc, t_fielddesc *wfielddesc, t_float *xp, t_float *yp, t_float *wp); EXTERN int array_getfields(t_symbol *elemtemplatesym, t_canvas **elemtemplatecanvasp, t_template **elemtemplatep, int *elemsizep, t_fielddesc *xfielddesc, t_fielddesc *yfielddesc, t_fielddesc *wfielddesc, int *xonsetp, int *yonsetp, int *wonsetp); /* --------------------- templates ------------------------- */ EXTERN t_template *template_new(t_symbol *sym, int argc, t_atom *argv); EXTERN void template_free(t_template *x); EXTERN int template_match(t_template *x1, t_template *x2); EXTERN int template_find_field(t_template *x, t_symbol *name, int *p_onset, int *p_type, t_symbol **p_arraytype); EXTERN t_float template_getfloat(t_template *x, t_symbol *fieldname, t_word *wp, int loud); EXTERN void template_setfloat(t_template *x, t_symbol *fieldname, t_word *wp, t_float f, int loud); EXTERN t_symbol *template_getsymbol(t_template *x, t_symbol *fieldname, t_word *wp, int loud); EXTERN void template_setsymbol(t_template *x, t_symbol *fieldname, t_word *wp, t_symbol *s, int loud); EXTERN t_template *gtemplate_get(t_gtemplate *x); EXTERN t_template *template_findbyname(t_symbol *s); EXTERN t_canvas *template_findcanvas(t_template *tmpl); EXTERN void template_notify(t_template *tmpl, t_symbol *s, int argc, t_atom *argv); EXTERN t_float fielddesc_getcoord(t_fielddesc *f, t_template *tmpl, t_word *wp, int loud); EXTERN void fielddesc_setcoord(t_fielddesc *f, t_template *tmpl, t_word *wp, t_float pix, int loud); EXTERN t_float fielddesc_cvttocoord(t_fielddesc *f, t_float val); EXTERN t_float fielddesc_cvtfromcoord(t_fielddesc *f, t_float coord); /* ----------------------- guiconnects, g_guiconnect.c --------- */ EXTERN t_guiconnect *guiconnect_new(t_pd *who, t_symbol *sym); EXTERN void guiconnect_notarget(t_guiconnect *x, double timedelay); /* ------------- IEMGUI routines used in other g_ files ---------------- */ EXTERN t_symbol *iemgui_raute2dollar(t_symbol *s); EXTERN t_symbol *iemgui_dollar2raute(t_symbol *s); EXTERN t_symbol *iemgui_put_in_braces(t_symbol *s); /*------------- g_clone.c ------------- */ EXTERN t_class *clone_class; /*------------- d_ugen.c ------------- */ EXTERN void signal_setborrowed(t_signal *sig, t_signal *sig2); EXTERN void signal_makereusable(t_signal *sig); #if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus) } #endif #endif // G_CANVAS_H ================================================ FILE: libs/libpd/pure-data/src/g_clone.c ================================================ #include "m_pd.h" #include "g_canvas.h" #include "m_imp.h" #include /* ---------- clone - maintain copies of a patch ----------------- */ #include "m_private_utils.h" #define LIST_NGETBYTE 100 /* bigger that this we use alloc, not alloca */ t_class *clone_class; static t_class *clone_in_class, *clone_out_class; typedef struct _outproxy { t_class *o_pd; t_outlet *o_outlet; int o_n; } t_outproxy; typedef struct _copy { t_glist *c_gl; t_outproxy *c_vec; } t_copy; typedef struct _in { t_class *i_pd; struct _clone *i_owner; int i_signal; int i_n; } t_in; typedef struct _out { t_outlet *o_outlet; int o_signal; } t_out; typedef struct _clone { t_object x_obj; t_canvas *x_canvas; /* owning canvas */ int x_n; /* number of copies */ t_copy *x_vec; /* the copies */ int x_nin; t_in *x_invec; /* inlet proxies */ int x_nout; t_out *x_outvec; /* outlets */ t_symbol *x_s; /* name of abstraction */ int x_argc; /* creation arguments for abstractions */ t_atom *x_argv; int x_phase; /* phase for round-robin input message forwarding */ int x_startvoice; /* number of first voice, 0 by default */ unsigned int x_suppressvoice:1; /* suppress voice number as $1 arg */ unsigned int x_distributein:1; /* distribute input signals across clones */ unsigned int x_packout:1; /* pack output signals */ } t_clone; int clone_match(t_pd *z, t_symbol *name, t_symbol *dir) { t_clone *x = (t_clone *)z; if (!x->x_n) return (0); return (x->x_vec[0].c_gl->gl_name == name && canvas_getdir(x->x_vec[0].c_gl) == dir); } void obj_sendinlet(t_object *x, int n, t_symbol *s, int argc, t_atom *argv); static void clone_in_list(t_in *x, t_symbol *s, int argc, t_atom *argv) { int n; if (!x->i_owner->x_nin) return; if (argc < 1 || argv[0].a_type != A_FLOAT) pd_error(x->i_owner, "clone: no instance number in message"); else if ((n = argv[0].a_w.w_float - x->i_owner->x_startvoice) < 0 || n >= x->i_owner->x_n) pd_error(x->i_owner, "clone: instance number %d out of range", n + x->i_owner->x_startvoice); else if (argc > 1 && argv[1].a_type == A_SYMBOL) obj_sendinlet(&x->i_owner->x_vec[n].c_gl->gl_obj, x->i_n, argv[1].a_w.w_symbol, argc-2, argv+2); else obj_sendinlet(&x->i_owner->x_vec[n].c_gl->gl_obj, x->i_n, &s_list, argc-1, argv+1); } static void clone_in_this(t_in *x, t_symbol *s, int argc, t_atom *argv) { int phase = x->i_owner->x_phase; if (phase < 0 || phase >= x->i_owner->x_n) phase = 0; if (argc <= 0 || !x->i_owner->x_nin) return; if (argv->a_type == A_SYMBOL) obj_sendinlet(&x->i_owner->x_vec[phase].c_gl->gl_obj, x->i_n, argv[0].a_w.w_symbol, argc-1, argv+1); else obj_sendinlet(&x->i_owner->x_vec[phase].c_gl->gl_obj, x->i_n, &s_list, argc, argv); } static void clone_in_next(t_in *x, t_symbol *s, int argc, t_atom *argv) { int phase = x->i_owner->x_phase + 1; if (phase < 0 || phase >= x->i_owner->x_n) phase = 0; x->i_owner->x_phase = phase; clone_in_this(x, s, argc, argv); } static void clone_in_set(t_in *x, t_floatarg f) { int phase = f; if (phase < 0 || phase >= x->i_owner->x_n) phase = 0; x->i_owner->x_phase = phase; } static void clone_in_all(t_in *x, t_symbol *s, int argc, t_atom *argv) { int phasewas = x->i_owner->x_phase, i; for (i = 0; i < x->i_owner->x_n; i++) { x->i_owner->x_phase = i; clone_in_this(x, s, argc, argv); } x->i_owner->x_phase = phasewas; } static void clone_in_vis(t_in *x, t_floatarg fn, t_floatarg vis) { int n = fn - x->i_owner->x_startvoice; if (n < 0) n = 0; else if (n >= x->i_owner->x_n) n = x->i_owner->x_n - 1; canvas_vis(x->i_owner->x_vec[n].c_gl, (vis != 0)); } static void clone_in_fwd(t_in *x, t_symbol *s, int argc, t_atom *argv) { if (argc > 0 && argv->a_type == A_SYMBOL) typedmess(&x->i_pd, argv->a_w.w_symbol, argc-1, argv+1); } static void clone_setn(t_clone *, t_floatarg); static void clone_in_resize(t_in *x, t_floatarg f) { int i, oldn = x->i_owner->x_n; /* We need to send closebangs to old instances. Currently, this is done in clone_freeinstance(), but later we would rather do it here, see comment in clone_loadbang() */ canvas_setcurrent(x->i_owner->x_canvas); clone_setn(x->i_owner, f); canvas_unsetcurrent(x->i_owner->x_canvas); /* send loadbangs to new instances */ for (i = oldn; i < x->i_owner->x_n; i++) canvas_loadbang(x->i_owner->x_vec[i].c_gl); } static void clone_out_anything(t_outproxy *x, t_symbol *s, int argc, t_atom *argv) { t_atom *outv; int first = 1 + (s != &s_list && s != &s_float && s != &s_symbol && s != &s_bang), outc = argc + first; ALLOCA(t_atom, outv, outc, LIST_NGETBYTE); SETFLOAT(outv, x->o_n); if (first == 2) SETSYMBOL(outv + 1, s); memcpy(outv+first, argv, sizeof(t_atom) * argc); outlet_list(x->o_outlet, 0, outc, outv); FREEA(t_atom, outv, outc, LIST_NGETBYTE); } static PERTHREAD int clone_voicetovis = -1; static t_canvas *clone_makeone(t_symbol *s, int argc, t_atom *argv) { t_canvas *retval; pd_this->pd_newest = 0; typedmess(&pd_objectmaker, s, argc, argv); if (pd_this->pd_newest == 0) { pd_error(0, "clone: can't create subpatch '%s'", s->s_name); return (0); } if (*pd_this->pd_newest != canvas_class) { pd_error(0, "clone: can't clone '%s' because it's not an abstraction", s->s_name); pd_free(pd_this->pd_newest); pd_this->pd_newest = 0; return (0); } retval = (t_canvas *)pd_this->pd_newest; pd_this->pd_newest = 0; retval->gl_isclone = 1; return (retval); } static void clone_initinstance(t_clone *x, int which, t_canvas *c) { t_outproxy *outvec; int i; x->x_vec[which].c_gl = c; x->x_vec[which].c_vec = outvec = (t_outproxy *)getbytes(x->x_nout * sizeof(*outvec)); for (i = 0; i < x->x_nout; i++) { outvec[i].o_pd = clone_out_class; outvec[i].o_n = x->x_startvoice + which; outvec[i].o_outlet = x->x_outvec[i].o_outlet; obj_connect(&x->x_vec[which].c_gl->gl_obj, i, (t_object *)(&outvec[i]), 0); } } static void clone_freeinstance(t_clone *x, int which) { t_copy *c = &x->x_vec[which]; /* see comment in clone_loadbang() */ canvas_closebang(c->c_gl); pd_free(&c->c_gl->gl_pd); t_freebytes(c->c_vec, x->x_nout * sizeof(*c->c_vec)); } static void clone_setn(t_clone *x, t_floatarg f) { int dspstate = canvas_suspend_dsp(); int nwas = x->x_n, wantn = f, i, j; if (!nwas) { pd_error(x, "clone: no abstraction"); return; } if (wantn < 1) { pd_error(x, "clone: can't resize to zero or negative number; setting to 1"); wantn = 1; } if (wantn > nwas) for (i = nwas; i < wantn; i++) { t_canvas *c; SETFLOAT(x->x_argv, x->x_startvoice + i); if (!(c = clone_makeone(x->x_s, x->x_argc - x->x_suppressvoice, x->x_argv + x->x_suppressvoice))) { pd_error(x, "clone: couldn't create '%s'", x->x_s->s_name); goto done; } x->x_vec = (t_copy *)t_resizebytes(x->x_vec, i * sizeof(t_copy), (i+1) * sizeof(t_copy)); x->x_n++; clone_initinstance(x, i, c); } else if (wantn < nwas) { for (i = wantn; i < nwas; i++) clone_freeinstance(x, i); x->x_vec = (t_copy *)t_resizebytes(x->x_vec, nwas * sizeof(t_copy), wantn * sizeof(t_copy)); x->x_n = wantn; } done: canvas_resume_dsp(dspstate); } static void clone_click(t_clone *x, t_floatarg xpos, t_floatarg ypos, t_floatarg shift, t_floatarg ctrl, t_floatarg alt) { if (!x->x_n) return; canvas_vis(x->x_vec[0].c_gl, 1); } static void clone_loadbang(t_clone *x, t_floatarg f) { int i; if (f == LB_LOAD) for (i = 0; i < x->x_n; i++) canvas_loadbang(x->x_vec[i].c_gl); #if 0 /* Pd currently does not send closebangs to objects on a root canvas or when an object is retexted. There is a pending PR to fix this, but in the meantime we just send the closebang in clone_freeinstance(). */ else if (f == LB_CLOSE) for (i = 0; i < x->x_n; i++) canvas_closebang(x->x_vec[i].c_gl); #endif } void canvas_dodsp(t_canvas *x, int toplevel, t_signal **sp); t_signal *signal_newfromcontext(int borrowed, int nchans); void signal_makereusable(t_signal *sig); static void clone_dsp(t_clone *x, t_signal **sp) { int i, j, nin, nout, *noutchans; t_signal **tempio; if (!x->x_n) return; for (i = nin = 0; i < x->x_nin; i++) if (x->x_invec[i].i_signal) nin++; for (i = nout = 0; i < x->x_nout; i++) if (x->x_outvec[i].o_signal) nout++; for (j = 0; j < x->x_n; j++) { if (obj_ninlets(&x->x_vec[j].c_gl->gl_obj) != x->x_nin || obj_noutlets(&x->x_vec[j].c_gl->gl_obj) != x->x_nout || obj_nsiginlets(&x->x_vec[j].c_gl->gl_obj) != nin || obj_nsigoutlets(&x->x_vec[j].c_gl->gl_obj) != nout) { pd_error(x, "clone: can't do DSP until edited copy is saved"); for (i = 0; i < nout; i++) { /* create dummy output signals */ signal_setmultiout(&sp[nin+i], x->x_packout ? x->x_n : 1); dsp_add_zero(sp[nin+i]->s_vec, sp[nin+i]->s_length * sp[nin+i]->s_nchans); } return; } } tempio = (nin + nout) > 0 ? (t_signal **)alloca((nin + nout) * sizeof(*tempio)) : 0; noutchans = nout > 0 ? (int *)alloca(nout * sizeof(*noutchans)) : 0; /* load input signals into signal vector to send subpatches */ if (x->x_packout) /* pack individual mono outputs to a multichannel one */ { for (j = 0; j < x->x_n; j++) { for (i = 0; i < nin; i++) { if (x->x_distributein) { /* distribute multi-channel signal over instances; wrap around if channel count is lower than instance count */ int offset = j % sp[i]->s_nchans; tempio[i] = signal_new(0, 1, sp[i]->s_sr, 0); signal_setborrowed(tempio[i], sp[i]); tempio[i]->s_nchans = 1; tempio[i]->s_vec = sp[i]->s_vec + offset * sp[i]->s_length; tempio[i]->s_refcount = 1; } else tempio[i] = sp[i]; } for (i = 0; i < nout; i++) tempio[nin + i] = signal_newfromcontext(1, 1); canvas_dodsp(x->x_vec[j].c_gl, 0, tempio); if (x->x_distributein) { for (i = 0; i < nin; i++) { if (!--tempio[i]->s_refcount) signal_makereusable(tempio[i]); else bug("clone 1: %d", tempio[i]->s_refcount); } } for (i = 0; i < nout; i++) { int nchans = tempio[nin + i]->s_nchans; int length = tempio[nin + i]->s_length; t_sample *to, *from = tempio[nin + i]->s_vec; if (j == 0) /* now we can the create the output signal */ { signal_setmultiout(&sp[nin + i], nchans * x->x_n); noutchans[i] = nchans; } /* NB: it is possible for instances to have different output channel counts. In this case we always take the channel count of the first instance. */ to = sp[nin + i]->s_vec + j * length * noutchans[i]; if (nchans == noutchans[i]) dsp_add_copy(from, to, length * nchans); else { if (nchans > noutchans[i]) /* ignore extra channels */ dsp_add_copy(from, to, noutchans[i] * length); else /* fill missing channels with zeros */ { dsp_add_copy(from, to, nchans * length); dsp_add_zero(to + length * nchans, length * (noutchans[i] - nchans)); } #if 1 pd_error(x, "warning: clone instance %d: channel count " "of outlet %d (%d) does not match first instance (%d)", j, i, nchans, noutchans[i]); #endif } signal_makereusable(tempio[nin + i]); } } } else /* otherwise add the individual outputs */ { for (j = 0; j < x->x_n; j++) { for (i = 0; i < nin; i++) { if (x->x_distributein) { /* distribute multi-channel signal over instances; wrap around if channel count is lower than instance count */ int offset = j % sp[i]->s_nchans; tempio[i] = signal_new(0, 1, sp[i]->s_sr, 0); signal_setborrowed(tempio[i], sp[i]); tempio[i]->s_nchans = 1; tempio[i]->s_vec = sp[i]->s_vec + offset * sp[i]->s_length; tempio[i]->s_refcount = 1; } else tempio[i] = sp[i]; } for (i = 0; i < nout; i++) tempio[nin + i] = signal_newfromcontext(1, 1); canvas_dodsp(x->x_vec[j].c_gl, 0, tempio); if (x->x_distributein) { for (i = 0; i < nin; i++) { if (!--tempio[i]->s_refcount) signal_makereusable(tempio[i]); else bug("clone 2: %d", tempio[i]->s_refcount); } } for (i = 0; i < nout; i++) { int nchans = tempio[nin + i]->s_nchans; int length = tempio[nin + i]->s_length; if (j == 0) { /* first instance: create output signal and copy content */ signal_setmultiout(&sp[nin + i], nchans); dsp_add_copy(tempio[nin + i]->s_vec, sp[nin + i]->s_vec, length * nchans); noutchans[i] = nchans; } else /* add to existing signal */ { int nsamples = nchans > noutchans[i] ? noutchans[i] * length : nchans * length; #if 1 if (nchans != noutchans[i]) pd_error(x, "warning: clone instance %d: channel count " "of outlet %d (%d) does not match first instance (%d)", j, i, nchans, noutchans[i]); #endif dsp_add_plus(tempio[nin + i]->s_vec, sp[nin + i]->s_vec, sp[nin + i]->s_vec, nsamples); } signal_makereusable(tempio[nin + i]); } } } for (i = 0; i < nin; i++) { if (sp[i]->s_refcount <= 0) bug("clone 3 %d", sp[i]->s_refcount); } } static void *clone_new(t_symbol *s, int argc, t_atom *argv) { t_clone *x = (t_clone *)pd_new(clone_class); t_canvas *c; int wantn, dspstate, i, voicetovis = clone_voicetovis; x->x_canvas = canvas_getcurrent(); x->x_invec = 0; x->x_outvec = 0; x->x_argv = 0; x->x_startvoice = 0; x->x_suppressvoice = 0; x->x_distributein = 0; x->x_packout = 0; clone_voicetovis = -1; if (argc == 0) { x->x_vec = 0; x->x_n = 0; return (x); } dspstate = canvas_suspend_dsp(); while (argc > 0 && argv[0].a_type == A_SYMBOL && argv[0].a_w.w_symbol->s_name[0] == '-') { if (!strcmp(argv[0].a_w.w_symbol->s_name, "-s") && argc > 1 && argv[1].a_type == A_FLOAT) { x->x_startvoice = argv[1].a_w.w_float; argc -= 2; argv += 2; } else if (!strcmp(argv[0].a_w.w_symbol->s_name, "-x")) x->x_suppressvoice = 1, argc--, argv++; else if (!strcmp(argv[0].a_w.w_symbol->s_name, "-d")) x->x_distributein = x->x_packout = 1, argc--, argv++; else if (!strcmp(argv[0].a_w.w_symbol->s_name, "-di")) x->x_distributein = 1, argc--, argv++; else if (!strcmp(argv[0].a_w.w_symbol->s_name, "-do")) x->x_packout = 1, argc--, argv++; else goto usage; } if (argc >= 2 && (wantn = atom_getfloatarg(0, argc, argv)) >= 0 && argv[1].a_type == A_SYMBOL) x->x_s = argv[1].a_w.w_symbol; else if (argc >= 2 && (wantn = atom_getfloatarg(1, argc, argv)) >= 0 && argv[0].a_type == A_SYMBOL) x->x_s = argv[0].a_w.w_symbol; else goto usage; /* store a copy of the argmuents with an extra space (argc+1) for supplying an instance number, which we'll bash as we go. */ x->x_argc = argc - 1; x->x_argv = getbytes(x->x_argc * sizeof(*x->x_argv)); memcpy(x->x_argv, argv+1, x->x_argc * sizeof(*x->x_argv)); SETFLOAT(x->x_argv, x->x_startvoice); if (!(c = clone_makeone(x->x_s, x->x_argc - x->x_suppressvoice, x->x_argv + x->x_suppressvoice))) goto fail; /* inlets */ x->x_nin = obj_ninlets(&c->gl_obj); if (x->x_nin > 0) { x->x_invec = (t_in *)getbytes(x->x_nin * sizeof(*x->x_invec)); for (i = 0; i < x->x_nin; i++) { x->x_invec[i].i_pd = clone_in_class; x->x_invec[i].i_owner = x; x->x_invec[i].i_signal = obj_issignalinlet(&c->gl_obj, i); x->x_invec[i].i_n = i; if (x->x_invec[i].i_signal) inlet_new(&x->x_obj, &x->x_invec[i].i_pd, &s_signal, &s_signal); else inlet_new(&x->x_obj, &x->x_invec[i].i_pd, 0, 0); } } else /* fake inlet to send messages like "vis" or "resize" */ { x->x_invec = (t_in *)getbytes(sizeof(*x->x_invec)); x->x_invec->i_pd = clone_in_class; x->x_invec->i_owner = x; x->x_invec->i_signal = 0; x->x_invec->i_n = 0; inlet_new(&x->x_obj, &x->x_invec->i_pd, 0, 0); } /* outlets */ x->x_nout = obj_noutlets(&c->gl_obj); x->x_outvec = (t_out *)getbytes(x->x_nout * sizeof(*x->x_outvec)); for (i = 0; i < x->x_nout; i++) { x->x_outvec[i].o_signal = obj_issignaloutlet(&c->gl_obj, i); x->x_outvec[i].o_outlet = outlet_new(&x->x_obj, (x->x_outvec[i].o_signal ? &s_signal : 0)); } /* first copy */ x->x_vec = getbytes(sizeof(*x->x_vec)); x->x_n = 1; clone_initinstance(x, 0, c); /* remaining copies */ clone_setn(x, (t_floatarg)(wantn)); x->x_phase = wantn-1; canvas_resume_dsp(dspstate); if (voicetovis >= 0 && voicetovis < x->x_n) canvas_vis(x->x_vec[voicetovis].c_gl, 1); return (x); usage: pd_error(0, "usage: clone [-s starting-number] [arguments]"); fail: if (x->x_argv) freebytes(x->x_argv, sizeof(x->x_argc * sizeof(*x->x_argv))); freebytes(x, sizeof(t_clone)); canvas_resume_dsp(dspstate); return (0); } static void clone_free(t_clone *x) { if (x->x_vec) { int i, voicetovis = -1; if (THISGUI->i_reloadingabstraction) { for (i = 0; i < x->x_n; i++) if (x->x_vec[i].c_gl == THISGUI->i_reloadingabstraction) voicetovis = i; } for (i = 0; i < x->x_n; i++) clone_freeinstance(x, i); t_freebytes(x->x_vec, x->x_n * sizeof(*x->x_vec)); t_freebytes(x->x_argv, x->x_argc * sizeof(*x->x_argv)); if (x->x_nin) t_freebytes(x->x_invec, x->x_nin * sizeof(*x->x_invec)); else /* fake inlet */ t_freebytes(x->x_invec, sizeof(*x->x_invec)); t_freebytes(x->x_outvec, x->x_nout * sizeof(*x->x_outvec)); clone_voicetovis = voicetovis; } } void clone_setup(void) { clone_class = class_new(gensym("clone"), (t_newmethod)clone_new, (t_method)clone_free, sizeof(t_clone), CLASS_NOINLET | CLASS_MULTICHANNEL, A_GIMME, 0); class_addmethod(clone_class, (t_method)clone_click, gensym("click"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); class_addmethod(clone_class, (t_method)clone_loadbang, gensym("loadbang"), A_FLOAT, 0); class_addmethod(clone_class, (t_method)clone_dsp, gensym("dsp"), A_CANT, 0); clone_in_class = class_new(gensym("clone-inlet"), 0, 0, sizeof(t_in), CLASS_PD, 0); class_addmethod(clone_in_class, (t_method)clone_in_next, gensym("next"), A_GIMME, 0); class_addmethod(clone_in_class, (t_method)clone_in_this, gensym("this"), A_GIMME, 0); class_addmethod(clone_in_class, (t_method)clone_in_set, gensym("set"), A_FLOAT, 0); class_addmethod(clone_in_class, (t_method)clone_in_all, gensym("all"), A_GIMME, 0); class_addmethod(clone_in_class, (t_method)clone_in_vis, gensym("vis"), A_FLOAT, A_FLOAT, 0); class_addmethod(clone_in_class, (t_method)clone_in_fwd, gensym("fwd"), A_GIMME, 0); class_addmethod(clone_in_class, (t_method)clone_in_resize, gensym("resize"), A_FLOAT, 0); class_addlist(clone_in_class, (t_method)clone_in_list); clone_out_class = class_new(gensym("clone-outlet"), 0, 0, sizeof(t_in), CLASS_PD, 0); class_addanything(clone_out_class, (t_method)clone_out_anything); } /* for the needs of g_editor::glist_dofinderror(): */ int clone_get_n(t_gobj *x) { if (pd_class(&x->g_pd) != clone_class) return 0; else return ((t_clone *)x)->x_n; } t_glist *clone_get_instance(t_gobj *x, int n) { t_clone *c; if (pd_class(&x->g_pd) != clone_class) return NULL; c = (t_clone *)x; n -= c->x_startvoice; if (n < 0) n = 0; else if (n >= c->x_n) n = c->x_n - 1; return c->x_vec[n].c_gl; } ================================================ FILE: libs/libpd/pure-data/src/g_editor.c ================================================ /* Copyright (c) 1997-2001 Miller Puckette and others. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ #include #include "m_pd.h" #include "m_imp.h" #include "s_stuff.h" #include "g_canvas.h" #include "g_undo.h" #include "s_utf8.h" /*-- moo --*/ #include #include "m_private_utils.h" struct _instanceeditor { t_binbuf *copy_binbuf; char *canvas_textcopybuf; int canvas_textcopybufsize; t_undofn canvas_undo_fn; /* current undo function if any */ int canvas_undo_whatnext; /* whether we can now UNDO or REDO */ void *canvas_undo_buf; /* data private to the undo function */ t_canvas *canvas_undo_canvas; /* which canvas we can undo on */ const char *canvas_undo_name; int canvas_undo_already_set_move; double canvas_upclicktime; int canvas_upx, canvas_upy; int canvas_find_index, canvas_find_wholeword; t_binbuf *canvas_findbuf; int paste_onset; t_canvas *paste_canvas; t_glist *canvas_last_glist; int canvas_last_glist_x, canvas_last_glist_y; t_canvas *canvas_cursorcanvaswas; unsigned int canvas_cursorwas; }; /* positional offset for duplicated items */ #define PASTE_OFFSET 10 void glist_readfrombinbuf(t_glist *x, const t_binbuf *b, const char *filename, int selectem); /* ------------------ forward declarations --------------- */ static void canvas_doclear(t_canvas *x); static void glist_setlastxy(t_glist *gl, int xval, int yval); static void glist_donewloadbangs(t_glist *x); static t_binbuf *canvas_docopy(t_canvas *x); static void canvas_dopaste(t_canvas *x, t_binbuf *b); static void canvas_paste(t_canvas *x); static void canvas_clearline(t_canvas *x); static t_glist *glist_finddirty(t_glist *x); static void canvas_zoom(t_canvas *x, t_floatarg zoom); static void canvas_displaceselection(t_canvas *x, int dx, int dy); void canvas_setgraph(t_glist *x, int flag, int nogoprect); /* ------------------------ managing the selection ----------------- */ void glist_deselectline(t_glist *x); static void _editor_selectlinecolor(t_glist*x, const char*color) { char tag[128]; sprintf(tag, "l%p", x->gl_editor->e_selectline_tag); pdgui_vmess(0, "crs rs", x, "itemconfigure", tag, "-fill", color); } void glist_selectline(t_glist *x, t_outconnect *oc, int index1, int outno, int index2, int inno) { if (x->gl_editor) { glist_deselectline(x); x->gl_editor->e_selectedline = 1; x->gl_editor->e_selectline_index1 = index1; x->gl_editor->e_selectline_outno = outno; x->gl_editor->e_selectline_index2 = index2; x->gl_editor->e_selectline_inno = inno; x->gl_editor->e_selectline_tag = oc; _editor_selectlinecolor(x, "blue"); } } void glist_deselectline(t_glist *x) { if (x->gl_editor) { x->gl_editor->e_selectedline = 0; _editor_selectlinecolor(x, "black"); } } int glist_isselected(t_glist *x, t_gobj *y) { if (x->gl_editor) { t_selection *sel; for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next) if (sel->sel_what == y) return (1); } return (0); } /* call this for unselected objects only */ void glist_select(t_glist *x, t_gobj *y) { if (x->gl_editor) { t_selection *sel = (t_selection *)getbytes(sizeof(*sel)); /* LATER #ifdef out the following check */ if (glist_isselected(x, y)) bug("glist_select"); sel->sel_next = x->gl_editor->e_selection; sel->sel_what = y; x->gl_editor->e_selection = sel; gobj_select(y, x, 1); } } /* recursively deselect everything in a gobj "g", if it happens to be a glist, in preparation for deselecting g itself in glist_dselect() */ static void glist_checkanddeselectall(t_glist *gl, t_gobj *g) { t_glist *gl2; t_gobj *g2; if (pd_class(&g->g_pd) != canvas_class) return; gl2 = (t_glist *)g; for (g2 = gl2->gl_list; g2; g2 = g2->g_next) glist_checkanddeselectall(gl2, g2); glist_noselect(gl2); } /* call this for selected objects only */ void glist_deselect(t_glist *x, t_gobj *y) { int fixdsp = 0; t_selection *sel, *sel2; t_rtext *z = 0; if (!x->gl_editor) return; if (!glist_isselected(x, y)) bug("glist_deselect"); if (x->gl_editor->e_textedfor) { t_rtext *fuddy = glist_findrtext(x, (t_text *)y); if (x->gl_editor->e_textedfor == fuddy) { if (x->gl_editor->e_textdirty) { z = fuddy; canvas_undo_add(x, UNDO_SEQUENCE_START, "typing", 0); canvas_undo_add(x, UNDO_ARRANGE, "arrange", canvas_undo_set_arrange(x, y, 1)); canvas_stowconnections(glist_getcanvas(x)); glist_checkanddeselectall(x, y); } gobj_activate(y, x, 0); } if (zgetfn(&y->g_pd, gensym("dsp"))) fixdsp = canvas_suspend_dsp(); } if ((sel = x->gl_editor->e_selection)->sel_what == y) { x->gl_editor->e_selection = x->gl_editor->e_selection->sel_next; gobj_select(sel->sel_what, x, 0); freebytes(sel, sizeof(*sel)); } else { for (sel = x->gl_editor->e_selection; (sel2 = sel->sel_next); sel = sel2) { if (sel2->sel_what == y) { sel->sel_next = sel2->sel_next; gobj_select(sel2->sel_what, x, 0); freebytes(sel2, sizeof(*sel2)); break; } } } if (z) { char *buf; int bufsize; rtext_gettext(z, &buf, &bufsize); text_setto((t_text *)y, x, buf, bufsize); canvas_fixlinesfor(x, (t_text *)y); x->gl_editor->e_textedfor = 0; canvas_undo_add(x, UNDO_SEQUENCE_END, "typing", 0); } if (fixdsp) canvas_resume_dsp(1); } void glist_noselect(t_glist *x) { if (x->gl_editor) { while (x->gl_editor->e_selection) glist_deselect(x, x->gl_editor->e_selection->sel_what); if (x->gl_editor->e_selectedline) glist_deselectline(x); } } void glist_selectall(t_glist *x) { if (x->gl_editor) { glist_noselect(x); if (x->gl_list) { t_selection *sel = (t_selection *)getbytes(sizeof(*sel)); t_gobj *y = x->gl_list; x->gl_editor->e_selection = sel; sel->sel_what = y; gobj_select(y, x, 1); while ((y = y->g_next)) { t_selection *sel2 = (t_selection *)getbytes(sizeof(*sel2)); sel->sel_next = sel2; sel = sel2; sel->sel_what = y; gobj_select(y, x, 1); } sel->sel_next = 0; } } } /* get the index of a gobj in a glist. If y is zero, return the total number of objects. */ int glist_getindex(t_glist *x, t_gobj *y) { t_gobj *y2; int indx; for (y2 = x->gl_list, indx = 0; y2 && y2 != y; y2 = y2->g_next) indx++; return (indx); } /* get the index of the object, among selected items, if "selected" is set; otherwise, among unselected ones. If y is zero, just counts the selected or unselected objects. */ int glist_selectionindex(t_glist *x, t_gobj *y, int selected) { t_gobj *y2; int indx; for (y2 = x->gl_list, indx = 0; y2 && y2 != y; y2 = y2->g_next) if (selected == glist_isselected(x, y2)) indx++; return (indx); } static t_gobj *glist_nth(t_glist *x, int n) { t_gobj *y; int indx; for (y = x->gl_list, indx = 0; y; y = y->g_next, indx++) if (indx == n) return (y); return (0); } /* ------------------- support for undo/redo -------------------------- */ static void canvas_applybinbuf(t_canvas *x, t_binbuf *b) { t_symbol*asym = gensym("#A"); t_pd *boundx = s__X.s_thing, *bounda = asym->s_thing, *boundn = s__N.s_thing; asym->s_thing = 0; s__X.s_thing = &x->gl_pd; s__N.s_thing = &pd_canvasmaker; binbuf_eval(b, 0, 0, 0); asym->s_thing = bounda; s__X.s_thing = boundx; s__N.s_thing = boundn; } static int canvas_undo_confirmdiscard(t_gobj *g) { t_glist *gl2; if (pd_class(&g->g_pd) == canvas_class && canvas_isabstraction((t_glist *)g) && (gl2 = glist_finddirty((t_glist *)g))) { t_canvas*c = canvas_getrootfor(gl2); const char *msg[]= {"Discard changes to '%s'?", c->gl_name->s_name}; char buf[80]; t_atom backmsg[2]; sprintf(buf, ".x%lx", gl2); SETSYMBOL(backmsg+0, gensym("dirty")); SETFLOAT (backmsg+1, 0); vmess(&gl2->gl_pd, gensym("menu-open"), ""); pdgui_vmess("pdtk_check", "^ Sms", c, 2, msg, gensym(buf), 2, backmsg, "no"); return 1; } return 0; } void canvas_undo_set_name(const char*name) { EDITOR->canvas_undo_name = name; } void canvas_setundo(t_canvas *x, t_undofn undofn, void *buf, const char *name) { int hadone = 0; /* blow away the old undo information. In one special case the old undo info is re-used; if so we shouldn't free it here. */ if (EDITOR->canvas_undo_fn && EDITOR->canvas_undo_buf && (buf != EDITOR->canvas_undo_buf)) { (*EDITOR->canvas_undo_fn)(EDITOR->canvas_undo_canvas, EDITOR->canvas_undo_buf, UNDO_FREE); hadone = 1; } EDITOR->canvas_undo_canvas = x; EDITOR->canvas_undo_fn = undofn; EDITOR->canvas_undo_buf = buf; EDITOR->canvas_undo_whatnext = UNDO_UNDO; EDITOR->canvas_undo_name = name; if (x && glist_isvisible(x) && glist_istoplevel(x)) /* enable undo in menu */ pdgui_vmess("pdtk_undomenu", "^ss", x, name, "no"); else if (hadone) pdgui_vmess("pdtk_undomenu", "rss", "nobody", "no", "no"); } /* clear undo if it happens to be for the canvas x. (but if x is 0, clear it regardless of who owns it.) */ void canvas_noundo(t_canvas *x) { if (!x || (x == EDITOR->canvas_undo_canvas)) canvas_setundo(0, 0, 0, "foo"); } static void canvas_undo(t_canvas *x) { int dspwas = canvas_suspend_dsp(); if (x != EDITOR->canvas_undo_canvas) bug("canvas_undo 1"); else if (EDITOR->canvas_undo_whatnext != UNDO_UNDO) bug("canvas_undo 2"); else { #if 0 post("undo"); #endif (*EDITOR->canvas_undo_fn)(EDITOR->canvas_undo_canvas, EDITOR->canvas_undo_buf, UNDO_UNDO); /* enable redo in menu */ if (glist_isvisible(x) && glist_istoplevel(x)) pdgui_vmess("pdtk_undomenu", "^ss", x, "no", EDITOR->canvas_undo_name); EDITOR->canvas_undo_whatnext = UNDO_REDO; } canvas_resume_dsp(dspwas); } static void canvas_redo(t_canvas *x) { int dspwas = canvas_suspend_dsp(); if (x != EDITOR->canvas_undo_canvas) bug("canvas_undo 1"); else if (EDITOR->canvas_undo_whatnext != UNDO_REDO) bug("canvas_undo 2"); else { #if 0 post("redo"); #endif (*EDITOR->canvas_undo_fn)(EDITOR->canvas_undo_canvas, EDITOR->canvas_undo_buf, UNDO_REDO); /* enable undo in menu */ if (glist_isvisible(x) && glist_istoplevel(x)) pdgui_vmess("pdtk_undomenu", "^ss", x, EDITOR->canvas_undo_name, "no"); EDITOR->canvas_undo_whatnext = UNDO_UNDO; } canvas_resume_dsp(dspwas); } /* ------- specific undo methods: 1. connect -------- */ typedef struct _undo_connect { int u_index1; int u_outletno; int u_index2; int u_inletno; } t_undo_connect; void *canvas_undo_set_disconnect(t_canvas *x, int index1, int outno, int index2, int inno); /* connect just calls disconnect actions backward... (see below) */ void *canvas_undo_set_connect(t_canvas *x, int index1, int outno, int index2, int inno) { return (canvas_undo_set_disconnect(x, index1, outno, index2, inno)); } int canvas_undo_connect(t_canvas *x, void *z, int action) { int myaction; if (action == UNDO_UNDO) myaction = UNDO_REDO; else if (action == UNDO_REDO) myaction = UNDO_UNDO; else myaction = action; canvas_undo_disconnect(x, z, myaction); return 1; } static void canvas_connect_with_undo(t_canvas *x, t_float index1, t_float outno, t_float index2, t_float inno) { canvas_connect(x, index1, outno, index2, inno); canvas_undo_add(x, UNDO_CONNECT, "connect", canvas_undo_set_connect(x, index1, outno, index2, inno)); } /* ------- specific undo methods: 2. disconnect -------- */ void *canvas_undo_set_disconnect(t_canvas *x, int index1, int outno, int index2, int inno) { t_undo_connect *buf = (t_undo_connect *)getbytes(sizeof(*buf)); buf->u_index1 = index1; buf->u_outletno = outno; buf->u_index2 = index2; buf->u_inletno = inno; return (buf); } void canvas_disconnect(t_canvas *x, t_float index1, t_float outno, t_float index2, t_float inno) { t_linetraverser t; t_outconnect *oc; linetraverser_start(&t, x); while ((oc = linetraverser_next(&t))) { int srcno = canvas_getindex(x, &t.tr_ob->ob_g); int sinkno = canvas_getindex(x, &t.tr_ob2->ob_g); if (srcno == index1 && t.tr_outno == outno && sinkno == index2 && t.tr_inno == inno) { if (glist_isvisible(x) && x->gl_havewindow) { char tag[128]; sprintf(tag, "l%p", oc); pdgui_vmess(0, "crs", x, "delete", tag); } obj_disconnect(t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno); break; } } } int canvas_undo_disconnect(t_canvas *x, void *z, int action) { t_undo_connect *buf = z; if (action == UNDO_UNDO) { canvas_connect(x, buf->u_index1, buf->u_outletno, buf->u_index2, buf->u_inletno); } else if (action == UNDO_REDO) { canvas_disconnect(x, buf->u_index1, buf->u_outletno, buf->u_index2, buf->u_inletno); } else if (action == UNDO_FREE) t_freebytes(buf, sizeof(*buf)); return 1; } static void canvas_disconnect_with_undo(t_canvas *x, t_float index1, t_float outno, t_float index2, t_float inno) { canvas_disconnect(x, index1, outno, index2, inno); canvas_undo_add(x, UNDO_DISCONNECT, "disconnect", canvas_undo_set_disconnect(x, index1, outno, index2, inno)); } /* ---------- ... 3. cut, clear, and typing into objects: -------- */ #define UCUT_CUT 1 /* operation was a cut */ #define UCUT_CLEAR 2 /* .. a clear */ /* following action is not needed any more LATER remove any signs of UCUT_TEXT * since recreate takes care of this in a more elegant way */ #define UCUT_TEXT 3 /* text typed into a box */ typedef struct _undo_cut { t_binbuf *u_objectbuf; /* the object cleared or typed into */ t_binbuf *u_reconnectbuf; /* connections into and out of object */ t_binbuf *u_redotextbuf; /* buffer to paste back for redo if TEXT */ int u_mode; /* from flags above */ int n_obj; /* number of selected objects to be cut */ int p_a[1]; /* array of original glist positions of selected objects. At least one object is selected, we dynamically resize it later */ } t_undo_cut; void *canvas_undo_set_cut(t_canvas *x, int mode) { t_undo_cut *buf; t_linetraverser t; t_outconnect *oc; int nnotsel= glist_selectionindex(x, 0, 0); int nsel = glist_selectionindex(x, 0, 1); buf = (t_undo_cut *)getbytes(sizeof(*buf) + sizeof(buf->p_a[0]) * (nsel - 1)); buf->n_obj = nsel; buf->u_mode = mode; buf->u_redotextbuf = 0; /* store connections into/out of the selection */ buf->u_reconnectbuf = binbuf_new(); linetraverser_start(&t, x); while ((oc = linetraverser_next(&t))) { int issel1 = glist_isselected(x, &t.tr_ob->ob_g); int issel2 = glist_isselected(x, &t.tr_ob2->ob_g); if (issel1 != issel2) { binbuf_addv(buf->u_reconnectbuf, "ssiiii;", gensym("#X"), gensym("connect"), (issel1 ? nnotsel : 0) + glist_selectionindex(x, &t.tr_ob->ob_g, issel1), t.tr_outno, (issel2 ? nnotsel : 0) + glist_selectionindex(x, &t.tr_ob2->ob_g, issel2), t.tr_inno); } } if (mode == UCUT_TEXT) { buf->u_objectbuf = canvas_docopy(x); } else if (mode == UCUT_CUT) { buf->u_objectbuf = canvas_docopy(x); } else if (mode == UCUT_CLEAR) { buf->u_objectbuf = canvas_docopy(x); } /* instantiate num_obj and fill array of positions of selected objects */ if (mode == UCUT_CUT || mode == UCUT_CLEAR) { if (x->gl_list) { int i = 0, j = 0; t_gobj *y; for (y = x->gl_list; y; y = y->g_next) { if (glist_isselected(x, y)) { buf->p_a[i] = j; i++; } j++; } } } return (buf); } int canvas_undo_cut(t_canvas *x, void *z, int action) { t_undo_cut *buf = z; int mode = buf?(buf->u_mode):0; if (action == UNDO_UNDO) { if (mode == UCUT_CUT) { canvas_dopaste(x, buf->u_objectbuf); } else if (mode == UCUT_CLEAR) { canvas_dopaste(x, buf->u_objectbuf); } else if (mode == UCUT_TEXT) { t_gobj *y1, *y2; glist_noselect(x); for (y1 = x->gl_list; (y2 = y1->g_next); y1 = y2) ; if (y1) { if (!buf->u_redotextbuf) { glist_noselect(x); glist_select(x, y1); buf->u_redotextbuf = canvas_docopy(x); glist_noselect(x); } glist_delete(x, y1); } canvas_dopaste(x, buf->u_objectbuf); } if (buf) canvas_applybinbuf(x, buf->u_reconnectbuf); /* now reposition objects to their original locations */ if (mode == UCUT_CUT || mode == UCUT_CLEAR) { int i = 0; /* location of the first newly pasted object */ int paste_pos = glist_getindex(x,0) - buf->n_obj; t_gobj *y_prev, *y, *y_next; for (i = 0; i < buf->n_obj; i++) { /* first check if we are in the same position already */ if (paste_pos+i != buf->p_a[i]) { y_prev = glist_nth(x, paste_pos-1+i); y = glist_nth(x, paste_pos+i); y_next = glist_nth(x, paste_pos+1+i); /* if the object is supposed to be first in the gl_list */ if (buf->p_a[i] == 0) { if (y_prev && y_next) { y_prev->g_next = y_next; } else if (y_prev && !y_next) y_prev->g_next = NULL; /* now put the moved object at the beginning of the cue */ y->g_next = glist_nth(x, 0); x->gl_list = y; /* LATER when objects are properly tagged lower y here */ } /* if the object is supposed to be in the middle of gl_list */ else { if (y_prev && y_next) { y_prev->g_next = y_next; } else if (y_prev && !y_next) { y_prev->g_next = NULL; } /* now put the moved object in its right place */ y_prev = glist_nth(x, buf->p_a[i]-1); y_next = glist_nth(x, buf->p_a[i]); y_prev->g_next = y; y->g_next = y_next; /* LATER when objects are properly tagged lower y here */ } } } /* LATER disable redrawing here */ if (x->gl_havewindow) canvas_redraw(x); if (x->gl_owner && !x->gl_isclone && glist_isvisible(x->gl_owner)) { gobj_vis((t_gobj *)x, x->gl_owner, 0); gobj_vis((t_gobj *)x, x->gl_owner, 1); } } } else if (action == UNDO_REDO) { if (mode == UCUT_CUT || mode == UCUT_CLEAR) { int i; /* we can't just blindly do clear here when the user may have * unselected things between undo and redo, so first let's select * the right stuff */ glist_noselect(x); i = 0; for (i = 0; i < buf->n_obj; i++) glist_select(x, glist_nth(x, buf->p_a[i])); canvas_doclear(x); } else if (mode == UCUT_TEXT) { t_gobj *y1, *y2; for (y1 = x->gl_list; (y2 = y1->g_next); y1 = y2) ; if (y1) glist_delete(x, y1); canvas_dopaste(x, buf->u_redotextbuf); canvas_applybinbuf(x, buf->u_reconnectbuf); } } else if (action == UNDO_FREE) if (buf) { if (buf->u_objectbuf) binbuf_free(buf->u_objectbuf); if (buf->u_reconnectbuf) binbuf_free(buf->u_reconnectbuf); if (buf->u_redotextbuf) binbuf_free(buf->u_redotextbuf); t_freebytes(buf, sizeof(*buf) + sizeof(buf->p_a[0]) * (buf->n_obj-1)); } return 1; } /* --------- 4. motion, including "tidy up" and stretching ----------- */ typedef struct _undo_move_elem { int e_index; t_float e_xpix; t_float e_ypix; } t_undo_move_elem; typedef struct _undo_move { t_undo_move_elem *u_vec; int u_n; } t_undo_move; void *canvas_undo_set_move(t_canvas *x, int selected) { int x1, y1, x2, y2, indx; t_gobj *y; t_undo_move *buf = (t_undo_move *)getbytes(sizeof(*buf)); buf->u_n = selected ? glist_selectionindex(x, 0, 1) : glist_getindex(x, 0); buf->u_vec = (t_undo_move_elem *)getbytes(sizeof(*buf->u_vec) * (selected ? glist_selectionindex(x, 0, 1) : glist_getindex(x, 0))); if (selected) { int i; for (y = x->gl_list, i = indx = 0; y; y = y->g_next, indx++) if (glist_isselected(x, y)) { gobj_getrect(y, x, &x1, &y1, &x2, &y2); buf->u_vec[i].e_index = indx; buf->u_vec[i].e_xpix = x1 / x->gl_zoom; buf->u_vec[i].e_ypix = y1 / x->gl_zoom; i++; } } else { for (y = x->gl_list, indx = 0; y; y = y->g_next, indx++) { gobj_getrect(y, x, &x1, &y1, &x2, &y2); buf->u_vec[indx].e_index = indx; buf->u_vec[indx].e_xpix = x1 / x->gl_zoom; buf->u_vec[indx].e_ypix = y1 / x->gl_zoom; } } EDITOR->canvas_undo_already_set_move = 1; return (buf); } int canvas_undo_move(t_canvas *x, void *z, int action) { t_undo_move *buf = z; if (action == UNDO_UNDO || action == UNDO_REDO) { int resortin = 0, resortout = 0; int i; for (i = 0; i < buf->u_n; i++) { float newx = (buf->u_vec[i].e_xpix)*x->gl_zoom; float newy = (buf->u_vec[i].e_ypix)*x->gl_zoom; t_gobj*y = glist_nth(x, buf->u_vec[i].e_index); if (y) { int x1=0, y1=0, x2=0, y2=0; int doing = EDITOR->canvas_undo_already_set_move; t_class *cl = pd_class(&y->g_pd); glist_noselect(x); glist_select(x, y); gobj_getrect(y, x, &x1, &y1, &x2, &y2); EDITOR->canvas_undo_already_set_move = 1; canvas_displaceselection(x, (newx - x1)/(x->gl_zoom), (newy - y1)/(x->gl_zoom)); EDITOR->canvas_undo_already_set_move = doing; buf->u_vec[i].e_xpix = x1/x->gl_zoom; buf->u_vec[i].e_ypix = y1/x->gl_zoom; if (cl == vinlet_class) resortin = 1; else if (cl == voutlet_class) resortout = 1; } } glist_noselect(x); for (i = 0; i < buf->u_n; i++) { t_gobj* y = glist_nth(x, buf->u_vec[i].e_index); if (y) glist_select(x, y); } if (resortin) canvas_resortinlets(x); if (resortout) canvas_resortoutlets(x); } else if (action == UNDO_FREE) { t_freebytes(buf->u_vec, buf->u_n * sizeof(*buf->u_vec)); t_freebytes(buf, sizeof(*buf)); } return 1; } /* --------- 5. paste (also duplicate) ----------- */ typedef struct _undo_paste { int u_index; /* index of first object pasted */ int u_sel_index; /* index of object selected at the time the other object was pasted (for autopatching) */ int u_offset; /* xy-offset for duplicated items (since it differs when duplicated into same or different canvas */ t_binbuf *u_objectbuf; /* here we store actual copied data */ } t_undo_paste; void *canvas_undo_set_paste(t_canvas *x, int numpasted, int duplicate, int d_offset) { t_undo_paste *buf = (t_undo_paste *)getbytes(sizeof(*buf)); buf->u_index = glist_getindex(x, 0) - numpasted; /* do we need numpasted at all? */ if (!duplicate && x->gl_editor->e_selection && !x->gl_editor->e_selection->sel_next) { /* if only one object is selected which will warrant autopatching */ buf->u_sel_index = glist_getindex(x, x->gl_editor->e_selection->sel_what); } else { buf->u_sel_index = -1; } buf->u_offset = d_offset; buf->u_objectbuf = binbuf_duplicate(EDITOR->copy_binbuf); return (buf); } void *canvas_undo_set_pastebinbuf(t_canvas *x, t_binbuf *b, int numpasted, int duplicate, int d_offset) { t_binbuf*tmpbuf = EDITOR->copy_binbuf; void*ret=0; EDITOR->copy_binbuf = b; ret = canvas_undo_set_paste(x, numpasted, duplicate, d_offset); EDITOR->copy_binbuf = tmpbuf; return ret; } int canvas_undo_paste(t_canvas *x, void *z, int action) { t_undo_paste *buf = z; if (action == UNDO_UNDO) { t_gobj *y; /* check if the paste/duplicate we are undoing contains any * dirty abstractions; and if so, bail out */ for (y = glist_nth(x, buf->u_index); y; y = y->g_next) if (canvas_undo_confirmdiscard(y)) return 0; glist_noselect(x); for (y = glist_nth(x, buf->u_index); y; y = y->g_next) glist_select(x, y); canvas_doclear(x); } else if (action == UNDO_REDO) { glist_noselect(x); /* if the pasted object is supposed to be autopatched * then select the object it should be autopatched to */ if (buf->u_sel_index > -1) { glist_select(x, glist_nth(x, buf->u_sel_index)); } canvas_dopaste(x, buf->u_objectbuf); /* if it was "duplicate" have to re-enact the displacement. */ if (buf->u_offset) { t_selection *y; for (y = x->gl_editor->e_selection; y; y = y->sel_next) gobj_displace(y->sel_what, x, buf->u_offset, buf->u_offset); } } else if (action == UNDO_FREE) { if (buf->u_objectbuf) binbuf_free(buf->u_objectbuf); t_freebytes(buf, sizeof(*buf)); } return 1; } /* --------- 6. apply ----------- */ typedef struct _undo_apply { t_binbuf *u_objectbuf; /* the object cleared or typed into */ t_binbuf *u_reconnectbuf; /* connections into and out of object */ int u_index; /* index of the previous object */ } t_undo_apply; void *canvas_undo_set_apply(t_canvas *x, int n) { t_undo_apply *buf; t_gobj *obj; t_linetraverser t; t_outconnect *oc; int nnotsel; /* enable editor (in case it is disabled) and select the object we are working on */ if (!x->gl_edit) canvas_editmode(x, 1); /* deselect all objects (if we are editing one while multiple are * selected, upon undoing this will recreate other selected objects, * effectively resulting in unwanted duplicates) * LATER: consider allowing concurrent editing of multiple objects */ glist_noselect(x); obj = glist_nth(x, n); if (obj && !glist_isselected(x, obj)) glist_select(x, obj); /* get number of all items for the offset below */ nnotsel= glist_selectionindex(x, 0, 0); buf = (t_undo_apply *)getbytes(sizeof(*buf)); /* store connections into/out of the selection */ buf->u_reconnectbuf = binbuf_new(); linetraverser_start(&t, x); while ((oc = linetraverser_next(&t))) { int issel1 = glist_isselected(x, &t.tr_ob->ob_g); int issel2 = glist_isselected(x, &t.tr_ob2->ob_g); if (issel1 != issel2) { binbuf_addv(buf->u_reconnectbuf, "ssiiii;", gensym("#X"), gensym("connect"), (issel1 ? nnotsel : 0) + glist_selectionindex(x, &t.tr_ob->ob_g, issel1), t.tr_outno, (issel2 ? nnotsel : 0) + glist_selectionindex(x, &t.tr_ob2->ob_g, issel2), t.tr_inno); } } /* copy object in its current state */ buf->u_objectbuf = canvas_docopy(x); /* store index of the currently selected object */ buf->u_index = n; return (buf); } static int canvas_apply_restore_original_position(t_canvas *x, int orig_pos); int canvas_undo_apply(t_canvas *x, void *z, int action) { t_undo_apply *buf = z; if (action == UNDO_UNDO || action == UNDO_REDO) { /* find current instance */ t_binbuf *tmp; glist_noselect(x); glist_select(x, glist_nth(x, buf->u_index)); /* copy it for the new undo/redo */ tmp = canvas_docopy(x); /* delete current instance */ canvas_doclear(x); /* replace it with previous instance */ canvas_dopaste(x, buf->u_objectbuf); /* change previous instance with current one */ buf->u_objectbuf = tmp; /* connections should stay the same */ canvas_applybinbuf(x, buf->u_reconnectbuf); /* now we need to reposition the object to its original place */ if (canvas_apply_restore_original_position(x, buf->u_index) && x->gl_havewindow) canvas_redraw(x); } else if (action == UNDO_FREE) { if (buf->u_objectbuf) binbuf_free(buf->u_objectbuf); if (buf->u_reconnectbuf) binbuf_free(buf->u_reconnectbuf); t_freebytes(buf, sizeof(*buf)); } return 1; } int canvas_apply_restore_original_position(t_canvas *x, int orig_pos) { t_gobj *y, *y_prev, *y_next; /* get the last object */ y = glist_nth(x, glist_getindex(x, 0) - 1); if (glist_getindex(x, y) != orig_pos) { /* first make the object prior to the pasted one the end of the list */ y_prev = glist_nth(x, glist_getindex(x, 0) - 2); if (y_prev) y_prev->g_next = NULL; /* if the object is supposed to be first in the gl_list */ if (orig_pos == 0) { y->g_next = glist_nth(x, 0); x->gl_list = y; } /* if the object is supposed to be in the middle of the gl_list */ else { y_prev = glist_nth(x, orig_pos-1); y_next = y_prev->g_next; y_prev->g_next = y; y->g_next = y_next; } return(1); } return(0); } /* --------- 7. arrange (to front/back) ----------- */ typedef struct _undo_arrange { int u_previndex; /* old index */ int u_newindex; /* new index */ } t_undo_arrange; void *canvas_undo_set_arrange(t_canvas *x, t_gobj *obj, int newindex) { /* newindex tells us is the new index at the beginning (0) or the end (1) */ t_undo_arrange *buf; /* enable editor (in case it is disabled) and select the object we are working on */ if (!x->gl_edit) canvas_editmode(x, 1); /* select the object*/ if (!glist_isselected(x, obj)) glist_select(x, obj); buf = (t_undo_arrange *)getbytes(sizeof(*buf)); /* set the u_newindex appropriately */ if (newindex == 0) buf->u_newindex = 0; else buf->u_newindex = glist_getindex(x, 0) - 1; /* store index of the currently selected object */ buf->u_previndex = glist_getindex(x, obj); return (buf); } /* called by undo/redo arrange and done_canvas_popup. only done_canvas_popup checks if it is a valid action and activates undo option */ static void canvas_doarrange(t_canvas *x, t_float which, t_gobj *oldy, t_gobj *oldy_prev, t_gobj *oldy_next) { t_gobj *y_begin = x->gl_list; t_gobj *y_end = glist_nth(x, glist_getindex(x,0) - 1); switch((int)which) { case 3: /* to front */ /* put the object at the end of the cue */ y_end->g_next = oldy; oldy->g_next = NULL; /* now fix links in the hole made in the list due to moving of the oldy * (we know there is oldy_next as y_end != oldy in canvas_done_popup) */ if (oldy_prev) /* there is indeed more before the oldy position */ oldy_prev->g_next = oldy_next; else x->gl_list = oldy_next; #if 0 /* and finally redraw */ pdgui_vmess("gui_raise", "or", x, "selected"); #endif break; case 4: /* to back */ x->gl_list = oldy; /* put it to the beginning of the cue */ oldy->g_next = y_begin; /* make it point to the old beginning */ /* now fix links in the hole made in the list due to moving of the oldy * (we know there is oldy_prev as y_begin != oldy in canvas_done_popup) */ if (oldy_prev) { if (oldy_next) /* there is indeed more after oldy position */ oldy_prev->g_next = oldy_next; else oldy_prev->g_next = NULL; /* oldy was the last in the cue */ } #if 0 /* and finally redraw */ pdgui_vmess("gui_lower", "or", x, "selected"); #endif break; default: bug("canvas_arrange"); return; } canvas_dirty(x, 1); } int canvas_undo_arrange(t_canvas *x, void *z, int action) { t_undo_arrange *buf = z; t_gobj *y=NULL, *prev=NULL, *next=NULL; if (!x->gl_edit) canvas_editmode(x, 1); switch(action) { case UNDO_UNDO: if(buf->u_newindex == buf->u_previndex) return 1; /* this is our object */ y = glist_nth(x, buf->u_newindex); /* select object */ glist_noselect(x); glist_select(x, y); if (buf->u_newindex) { /* if it is the last object */ /* first previous object should point to nothing */ prev = glist_nth(x, buf->u_newindex - 1); prev->g_next = NULL; /* now we reuse vars for the following: old index should be right before the object previndex is pointing to as the object was moved to the end */ /* old position is not first */ if (buf->u_previndex) { prev = glist_nth(x, buf->u_previndex - 1); next = prev->g_next; /* now readjust pointers */ prev->g_next = y; y->g_next = next; } /* old position is first */ else { prev = NULL; next = x->gl_list; /* now readjust pointers */ y->g_next = next; x->gl_list = y; } } else { /* if it is the first object */ /* old index should be right after the object previndex is pointing to as the object was moved to the end */ prev = glist_nth(x, buf->u_previndex); /* next may be NULL and that is ok */ next = prev->g_next; /* first glist pointer needs to point to the second object */ x->gl_list = y->g_next; /* now readjust pointers */ prev->g_next = y; y->g_next = next; } /* and finally redraw canvas */ if (x->gl_havewindow) canvas_redraw(x); break; case UNDO_REDO: { t_gobj *oldy_prev=NULL, *oldy_next=NULL; int arrangeaction; if(buf->u_newindex == buf->u_previndex) return 1; /* find our object */ y = glist_nth(x, buf->u_previndex); /* select object */ glist_noselect(x); glist_select(x, y); if (!buf->u_newindex) arrangeaction = 4; else arrangeaction = 3; /* if there is an object before ours (in other words our index is > 0) */ if (glist_getindex(x,y)) oldy_prev = glist_nth(x, buf->u_previndex - 1); /* if there is an object after ours */ if (y->g_next) oldy_next = y->g_next; canvas_doarrange(x, arrangeaction, y, oldy_prev, oldy_next); } break; case UNDO_FREE: t_freebytes(buf, sizeof(*buf)); } return 1; } /* --------- 8. apply on canvas ----------- */ typedef struct _undo_canvas_properties { int gl_pixwidth; /* width in pixels (on parent, if a graph) */ int gl_pixheight; t_float gl_x1; /* bounding rectangle in our own coordinates */ t_float gl_y1; t_float gl_x2; t_float gl_y2; int gl_screenx1; /* screen coordinates when toplevel */ int gl_screeny1; int gl_screenx2; int gl_screeny2; int gl_xmargin; /* origin for GOP rectangle */ int gl_ymargin; unsigned int gl_goprect:1; /* draw rectangle for graph-on-parent */ unsigned int gl_isgraph:1; /* show as graph on parent */ unsigned int gl_hidetext:1; /* hide object-name + args when doing graph on parent */ } t_undo_canvas_properties; void *canvas_undo_set_canvas(t_canvas *x) { /* enable editor (in case it is disabled) */ t_undo_canvas_properties *buf = (t_undo_canvas_properties *)getbytes(sizeof(*buf)); buf->gl_pixwidth = x->gl_pixwidth; buf->gl_pixheight = x->gl_pixheight; buf->gl_x1 = x->gl_x1; buf->gl_y1 = x->gl_y1; buf->gl_x2 = x->gl_x2; buf->gl_y2 = x->gl_y2; buf->gl_screenx1 = x->gl_screenx1; buf->gl_screeny1 = x->gl_screeny1; buf->gl_screenx2 = x->gl_screenx2; buf->gl_screeny2 = x->gl_screeny2; buf->gl_xmargin = x->gl_xmargin; buf->gl_ymargin = x->gl_ymargin; buf->gl_goprect = x->gl_goprect; buf->gl_isgraph = x->gl_isgraph; buf->gl_hidetext = x->gl_hidetext; return (buf); } int canvas_undo_canvas_apply(t_canvas *x, void *z, int action) { t_undo_canvas_properties *buf = (t_undo_canvas_properties *)z; t_undo_canvas_properties tmp; if (action == UNDO_UNDO || action == UNDO_REDO) { if (!x->gl_edit) canvas_editmode(x, 1); #if 0 /* close properties window first */ t_int properties = gfxstub_haveproperties((void *)x); if (properties) { pdgui_stub_deleteforkey(x); } #endif /* store current canvas values into temporary data holder */ tmp.gl_pixwidth = x->gl_pixwidth; tmp.gl_pixheight = x->gl_pixheight; tmp.gl_x1 = x->gl_x1; tmp.gl_y1 = x->gl_y1; tmp.gl_x2 = x->gl_x2; tmp.gl_y2 = x->gl_y2; tmp.gl_screenx1 = x->gl_screenx1; tmp.gl_screeny1 = x->gl_screeny1; tmp.gl_screenx2 = x->gl_screenx2; tmp.gl_screeny2 = x->gl_screeny2; tmp.gl_xmargin = x->gl_xmargin; tmp.gl_ymargin = x->gl_ymargin; tmp.gl_goprect = x->gl_goprect; tmp.gl_isgraph = x->gl_isgraph; tmp.gl_hidetext = x->gl_hidetext; /* change canvas values with the ones from the undo buffer */ x->gl_pixwidth = buf->gl_pixwidth; x->gl_pixheight = buf->gl_pixheight; x->gl_x1 = buf->gl_x1; x->gl_y1 = buf->gl_y1; x->gl_x2 = buf->gl_x2; x->gl_y2 = buf->gl_y2; x->gl_screenx1 = buf->gl_screenx1; x->gl_screeny1 = buf->gl_screeny1; x->gl_screenx2 = buf->gl_screenx2; x->gl_screeny2 = buf->gl_screeny2; x->gl_xmargin = buf->gl_xmargin; x->gl_ymargin = buf->gl_ymargin; x->gl_goprect = buf->gl_goprect; x->gl_isgraph = buf->gl_isgraph; x->gl_hidetext = buf->gl_hidetext; /* copy data values from the temporary data to the undo buffer */ buf->gl_pixwidth = tmp.gl_pixwidth; buf->gl_pixheight = tmp.gl_pixheight; buf->gl_x1 = tmp.gl_x1; buf->gl_y1 = tmp.gl_y1; buf->gl_x2 = tmp.gl_x2; buf->gl_y2 = tmp.gl_y2; buf->gl_screenx1 = tmp.gl_screenx1; buf->gl_screeny1 = tmp.gl_screeny1; buf->gl_screenx2 = tmp.gl_screenx2; buf->gl_screeny2 = tmp.gl_screeny2; buf->gl_xmargin = tmp.gl_xmargin; buf->gl_ymargin = tmp.gl_ymargin; buf->gl_goprect = tmp.gl_goprect; buf->gl_isgraph = tmp.gl_isgraph; buf->gl_hidetext = tmp.gl_hidetext; /* redraw */ canvas_setgraph(x, x->gl_isgraph + 2*x->gl_hidetext, 0); canvas_dirty(x, 1); if (x->gl_havewindow) { canvas_redraw(x); } if (x->gl_owner && !x->gl_isclone && glist_isvisible(x->gl_owner)) { glist_noselect(x); gobj_vis(&x->gl_gobj, x->gl_owner, 0); gobj_vis(&x->gl_gobj, x->gl_owner, 1); if (x->gl_owner->gl_havewindow) canvas_redraw(x->gl_owner); } } else if (action == UNDO_FREE) { if (buf) t_freebytes(buf, sizeof(*buf)); } return 1; } /* --------- 9. create ----------- */ typedef struct _undo_create { int u_index; /* index of the created object object */ t_binbuf *u_objectbuf; /* the object cleared or typed into */ t_binbuf *u_reconnectbuf; /* connections into and out of object */ } t_undo_create; void *canvas_undo_set_create(t_canvas *x) { t_gobj *y; t_linetraverser t; int nnotsel; t_undo_create *buf = (t_undo_create *)getbytes(sizeof(*buf)); buf->u_index = glist_getindex(x, 0) - 1; nnotsel= glist_selectionindex(x, 0, 0); buf->u_objectbuf = binbuf_new(); if (x->gl_list) { t_outconnect *oc; for (y = x->gl_list; y; y = y->g_next) { if (!y->g_next) { gobj_save(y, buf->u_objectbuf); break; } } buf->u_reconnectbuf = binbuf_new(); linetraverser_start(&t, x); while ((oc = linetraverser_next(&t))) { int issel1, issel2; issel1 = ( &t.tr_ob->ob_g == y ? 1 : 0); issel2 = ( &t.tr_ob2->ob_g == y ? 1 : 0); if (issel1 != issel2) { binbuf_addv(buf->u_reconnectbuf, "ssiiii;", gensym("#X"), gensym("connect"), (issel1 ? nnotsel : 0) + glist_selectionindex(x, &t.tr_ob->ob_g, issel1), t.tr_outno, (issel2 ? nnotsel : 0) + glist_selectionindex(x, &t.tr_ob2->ob_g, issel2), t.tr_inno); } } } return (buf); } int canvas_undo_create(t_canvas *x, void *z, int action) { t_undo_create *buf = z; t_gobj *y; if (action == UNDO_UNDO) { glist_noselect(x); y = glist_nth(x, buf->u_index); glist_select(x, y); canvas_doclear(x); } else if (action == UNDO_REDO) { canvas_applybinbuf(x, buf->u_objectbuf); canvas_applybinbuf(x, buf->u_reconnectbuf); if (pd_this->pd_newest && pd_class(pd_this->pd_newest) == canvas_class) canvas_loadbang((t_canvas *)pd_this->pd_newest); y = glist_nth(x, buf->u_index); glist_select(x, y); } else if (action == UNDO_FREE) { binbuf_free(buf->u_objectbuf); binbuf_free(buf->u_reconnectbuf); t_freebytes(buf, sizeof(*buf)); } return 1; } /* ------ 10. recreate (called from text_setto after text has changed) ------ */ /* recreate uses t_undo_create struct */ void *canvas_undo_set_recreate(t_canvas *x, t_gobj *y, int pos) { t_linetraverser t; t_outconnect *oc; int nnotsel; t_undo_create *buf = (t_undo_create *)getbytes(sizeof(*buf)); buf->u_index = pos; nnotsel= glist_selectionindex(x, 0, 0) - 1; /* - 1 is a critical difference from the create */ buf->u_objectbuf = binbuf_new(); gobj_save(y, buf->u_objectbuf); buf->u_reconnectbuf = binbuf_new(); linetraverser_start(&t, x); while ((oc = linetraverser_next(&t))) { int issel1, issel2; issel1 = ( &t.tr_ob->ob_g == y ? 1 : 0); issel2 = ( &t.tr_ob2->ob_g == y ? 1 : 0); if (issel1 != issel2) { binbuf_addv(buf->u_reconnectbuf, "ssiiii;", gensym("#X"), gensym("connect"), (issel1 ? nnotsel : 0) + glist_selectionindex(x, &t.tr_ob->ob_g, issel1), t.tr_outno, (issel2 ? nnotsel : 0) + glist_selectionindex(x, &t.tr_ob2->ob_g, issel2), t.tr_inno); } } return (buf); } int canvas_undo_recreate(t_canvas *x, void *z, int action) { t_undo_create *buf = z; t_gobj *y = NULL; if (action == UNDO_UNDO) y = glist_nth(x, glist_getindex(x, 0) - 1); else if (action == UNDO_REDO) y = glist_nth(x, buf->u_index); /* check if we are undoing the creation of a dirty abstraction; * if so, bail out */ if ((action == UNDO_UNDO) && canvas_undo_confirmdiscard(y)) return 0; if (action == UNDO_UNDO || action == UNDO_REDO) { /* first copy new state of the current object */ t_undo_create *buf2 = (t_undo_create *)getbytes(sizeof(*buf)); buf2->u_index = buf->u_index; buf2->u_objectbuf = binbuf_new(); gobj_save(y, buf2->u_objectbuf); buf2->u_reconnectbuf = binbuf_duplicate(buf->u_reconnectbuf); /* now cut the existing object */ glist_noselect(x); glist_select(x, y); canvas_doclear(x); /* then paste the old object */ /* save and clear bindings to symbols #A, #N, #X; restore when done */ canvas_applybinbuf(x, buf->u_objectbuf); canvas_applybinbuf(x, buf->u_reconnectbuf); /* free the old data */ binbuf_free(buf->u_objectbuf); binbuf_free(buf->u_reconnectbuf); t_freebytes(buf, sizeof(*buf)); /* re-adjust pointer * (this should probably belong into g_undo.c, but since it is * a unique case, we'll let it be for the time being) */ canvas_undo_get(x)->u_last->data = (void *)buf2; buf = buf2; /* reposition object to its original place */ if (action == UNDO_UNDO) if (canvas_apply_restore_original_position(x, buf->u_index) && x->gl_havewindow) canvas_redraw(x); /* send a loadbang */ if (pd_this->pd_newest && pd_class(pd_this->pd_newest) == canvas_class) canvas_loadbang((t_canvas *)pd_this->pd_newest); /* select */ if (action == UNDO_REDO) y = glist_nth(x, glist_getindex(x, 0) - 1); else y = glist_nth(x, buf->u_index); glist_select(x, y); } else if (action == UNDO_FREE) { binbuf_free(buf->u_objectbuf); binbuf_free(buf->u_reconnectbuf); t_freebytes(buf, sizeof(*buf)); } return 1; } /* ----------- 11. font -------------- */ static void canvas_dofont(t_canvas *x, t_floatarg font, t_floatarg xresize, t_floatarg yresize); typedef struct _undo_font { int font; t_float resize; int which; } t_undo_font; void *canvas_undo_set_font(t_canvas *x, int font, t_float resize, int which) { t_undo_font *u_f = (t_undo_font *)getbytes(sizeof(*u_f)); u_f->font = font; u_f->resize = resize; u_f->which = which; return (u_f); } int canvas_undo_font(t_canvas *x, void *z, int action) { t_undo_font *u_f = z; if (action == UNDO_UNDO || action == UNDO_REDO) { t_canvas *x2 = canvas_getrootfor(x); int tmp_font = x2->gl_font; int whichresize = u_f->which; t_float realresize = 1./u_f->resize; t_float realresx = 1, realresy = 1; if (whichresize != 3) realresx = realresize; if (whichresize != 2) realresy = realresize; canvas_dofont(x2, u_f->font, realresx, realresy); u_f->resize = realresize; u_f->font = tmp_font; } else if (action == UNDO_FREE) { if (u_f) freebytes(u_f, sizeof(*u_f)); } return 1; } int clone_match(t_pd *z, t_symbol *name, t_symbol *dir); static void canvas_cut(t_canvas *x); /* recursively check for abstractions to reload as result of a save. Don't reload the one we just saved ("except") though. */ /* LATER try to do the same trick for externs. */ static void glist_doreload(t_glist *gl, t_symbol *name, t_symbol *dir, t_gobj *except) { t_gobj *g; int hadwindow = gl->gl_havewindow; int found = 0; /* to optimize redrawing we select all objects that need to be updated and redraw (cut+undo) them together. Then we look for sub-patches that may have more of the same... */ for (g = gl->gl_list; g; g = g->g_next) { /* remake the object if it's an abstraction that appears to have been loaded from the file we just saved */ int remakeit = (g != except && pd_class(&g->g_pd) == canvas_class && canvas_isabstraction((t_canvas *)g) && ((t_canvas *)g)->gl_name == name && canvas_getdir((t_canvas *)g) == dir); /* also remake it if it's a "clone" with that name */ if (pd_class(&g->g_pd) == clone_class && clone_match(&g->g_pd, name, dir)) { /* LATER try not to remake the one that equals "except" */ remakeit = 1; } if (remakeit) { /* Bugfix for cases where canvas_vis doesn't actually create a new editor. We need to fix canvas_vis so that the bug doesn't get triggered. But since we know this fixes a regression we'll keep this as a point in the history as we fix canvas_vis. Once that's done we can remove this call. */ canvas_create_editor(gl); if (!gl->gl_havewindow) { canvas_vis(glist_getcanvas(gl), 1); } if (!found) { glist_noselect(gl); found = 1; } glist_select(gl, g); } } /* cut all selected (matched) objects and undo, to reinstantiate them */ if (found) { canvas_cut(gl); canvas_undo_undo(gl); canvas_undo_rebranch(gl); glist_noselect(gl); } /* now iterate over all the sub-patches... */ for (g = gl->gl_list; g; g = g->g_next) { if (g != except && pd_class(&g->g_pd) == canvas_class && (!canvas_isabstraction((t_canvas *)g) || ((t_canvas *)g)->gl_name != name || canvas_getdir((t_canvas *)g) != dir) ) glist_doreload((t_canvas *)g, name, dir, except); } if (!hadwindow && gl->gl_havewindow) canvas_vis(glist_getcanvas(gl), 0); } /* call canvas_doreload on everyone */ void canvas_reload(t_symbol *name, t_symbol *dir, t_glist *except) { t_canvas *x; int dspwas = canvas_suspend_dsp(); t_binbuf*b = 0; if(EDITOR->copy_binbuf) b = binbuf_duplicate(EDITOR->copy_binbuf); THISGUI->i_reloadingabstraction = except; /* find all root canvases */ for (x = pd_getcanvaslist(); x; x = x->gl_next) glist_doreload(x, name, dir, &except->gl_gobj); THISGUI->i_reloadingabstraction = 0; if(b) { if(EDITOR->copy_binbuf) binbuf_free(EDITOR->copy_binbuf); EDITOR->copy_binbuf = b; } canvas_resume_dsp(dspwas); } /* ------------------------ event handling ------------------------ */ static const char *cursorlist[] = { "$cursor_runmode_nothing", "$cursor_runmode_clickme", "$cursor_runmode_thicken", "$cursor_runmode_addpoint", "$cursor_editmode_nothing", "$cursor_editmode_connect", "$cursor_editmode_disconnect", "$cursor_editmode_resize" }; void canvas_setcursor(t_canvas *x, unsigned int cursornum) { if (cursornum >= sizeof(cursorlist)/sizeof *cursorlist) { bug("canvas_setcursor"); return; } if (EDITOR->canvas_cursorcanvaswas != x || EDITOR->canvas_cursorwas != cursornum) { pdgui_vmess(0, "^r rr", x, "configure", "-cursor", cursorlist[cursornum]); EDITOR->canvas_cursorcanvaswas = x; EDITOR->canvas_cursorwas = cursornum; } } /* check if a point lies in a gobj. */ int canvas_hitbox(t_canvas *x, t_gobj *y, int xpos, int ypos, int *x1p, int *y1p, int *x2p, int *y2p) { int x1, y1, x2, y2; if (!gobj_shouldvis(y, x)) return (0); gobj_getrect(y, x, &x1, &y1, &x2, &y2); if (xpos >= x1 && xpos <= x2 && ypos >= y1 && ypos <= y2) { *x1p = x1; *y1p = y1; *x2p = x2; *y2p = y2; return (1); } else return (0); } /* find the last gobj, if any, containing the point. */ static t_gobj *canvas_findhitbox(t_canvas *x, int xpos, int ypos, int *x1p, int *y1p, int *x2p, int *y2p) { t_gobj *y, *rval = 0; int x1, y1, x2, y2; *x1p = -0x7fffffff; for (y = x->gl_list; y; y = y->g_next) { if (canvas_hitbox(x, y, xpos, ypos, &x1, &y1, &x2, &y2) && (x1 > *x1p)) *x1p = x1, *y1p = y1, *x2p = x2, *y2p = y2, rval = y; } /* if there are at least two selected objects, we'd prefer to find a selected one (never mind which) to the one we got. */ if (x->gl_editor && x->gl_editor->e_selection && x->gl_editor->e_selection->sel_next && !glist_isselected(x, y)) { t_selection *sel; for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next) if (canvas_hitbox(x, sel->sel_what, xpos, ypos, &x1, &y1, &x2, &y2)) *x1p = x1, *y1p = y1, *x2p = x2, *y2p = y2, rval = sel->sel_what; } return (rval); } /* right-clicking on a canvas object pops up a menu. */ static void canvas_rightclick(t_canvas *x, int xpos, int ypos, t_gobj *y) { int canprop, canopen; canprop = (!y || class_getpropertiesfn(pd_class(&y->g_pd))); canopen = (y && zgetfn(&y->g_pd, gensym("menu-open"))); pdgui_vmess("pdtk_canvas_popup", "^ ii ii", x, xpos, ypos, canprop, canopen); } /* ---- editors -- perhaps this and "vis" should go to g_editor.c ------- */ static t_editor *editor_new(t_glist *owner) { char buf[40]; t_editor *x = (t_editor *)getbytes(sizeof(*x)); x->e_connectbuf = binbuf_new(); x->e_deleted = binbuf_new(); x->e_glist = owner; sprintf(buf, ".x%lx", (t_int)owner); x->e_guiconnect = guiconnect_new(&owner->gl_pd, gensym(buf)); x->e_clock = 0; return (x); } static void editor_free(t_editor *x, t_glist *y) { glist_noselect(y); guiconnect_notarget(x->e_guiconnect, 1000); binbuf_free(x->e_connectbuf); binbuf_free(x->e_deleted); if (x->e_clock) clock_free(x->e_clock); freebytes((void *)x, sizeof(*x)); } /* recursively create or destroy all editors of a glist and its sub-glists, as long as they aren't toplevels. */ void canvas_create_editor(t_glist *x) { t_gobj *y; t_object *ob; if (!x->gl_editor) { x->gl_editor = editor_new(x); for (y = x->gl_list; y; y = y->g_next) if ((ob = pd_checkobject(&y->g_pd))) rtext_new(x, ob); } } void canvas_destroy_editor(t_glist *x) { glist_noselect(x); if (x->gl_editor) { t_rtext *rtext; /* this happens if we had activated an atom box in run mode: */ if (x->gl_editor->e_textedfor) rtext_activate(x->gl_editor->e_textedfor, 0); while ((rtext = x->gl_editor->e_rtext)) rtext_free(rtext); editor_free(x->gl_editor, x); x->gl_editor = 0; } } void canvas_reflecttitle(t_canvas *x); void canvas_map(t_canvas *x, t_floatarg f); /* we call this when we want the window to become visible, mapped, and in front of all windows; or with "f" zero, when we want to get rid of the window. */ void canvas_vis(t_canvas *x, t_floatarg f) { int flag = (f != 0); if (flag) { /* If a subpatch/abstraction has GOP/gl_isgraph set, then it will have * a gl_editor already, if its not, it will not have a gl_editor. * canvas_create_editor(x) checks if a gl_editor is already created, * so its ok to run it on a canvas that already has a gl_editor. */ if (x->gl_editor && x->gl_havewindow) { /* just put us in front */ pdgui_vmess("pdtk_canvas_raise", "^", x); } else { char cbuf[MAXPDSTRING]; t_canvas *c = x; t_undo *undo = canvas_undo_get(x); t_undo_action *udo = undo ? undo->u_last : 0; char winpos[128]; t_canvas**parents = getbytes(0); size_t numparents; canvas_create_editor(x); if ((GLIST_DEFCANVASXLOC == x->gl_screenx1) && (GLIST_DEFCANVASYLOC == x->gl_screeny1)) /* initial values for new windows */ { winpos[0]=0; } else { sprintf(winpos, "+%d+%d", (int)(x->gl_screenx1), (int)(x->gl_screeny1)); } pdgui_vmess("pdtk_canvas_new", "^ ii si", x, (int)(x->gl_screenx2 - x->gl_screenx1), (int)(x->gl_screeny2 - x->gl_screeny1), winpos, x->gl_edit); numparents = 0; while (c->gl_owner && !c->gl_isclone) { t_canvas**newparents = (t_canvas**)resizebytes(parents, numparents * sizeof(*newparents), (numparents+1) * sizeof(*newparents)); if (!newparents) break; c = c->gl_owner; parents = newparents; parents[numparents] = c; numparents++; } pdgui_vmess("pdtk_canvas_setparents", "^C", x, numparents, parents); freebytes(parents, numparents * sizeof(t_canvas)); x->gl_havewindow = 1; canvas_reflecttitle(x); canvas_updatewindowlist(); pdgui_vmess("pdtk_undomenu", "^ ss", x, udo?(udo->name):"no", (udo && udo->next)?(udo->next->name):"no"); } } else /* make invisible */ { int i; t_canvas *x2; if (!x->gl_havewindow) { /* bug workaround -- a graph in a visible patch gets "invised" when the patch is closed, and must lose the editor here. It's probably not the natural place to do this. Other cases like subpatches fall here too but don'd need the editor freed, so we check if it exists. */ if (x->gl_editor) canvas_destroy_editor(x); return; } glist_noselect(x); if (glist_isvisible(x)) canvas_map(x, 0); canvas_destroy_editor(x); pdgui_vmess("destroy", "^", x); for (i = 1, x2 = x; x2; x2 = x2->gl_next, i++) ; /* if we're a graph on our parent, and if the parent exists and is visible, show ourselves on parent. */ if (glist_isgraph(x) && x->gl_owner && !x->gl_isclone) { t_glist *gl2 = x->gl_owner; if (glist_isvisible(gl2)) gobj_vis(&x->gl_gobj, gl2, 0); x->gl_havewindow = 0; if (glist_isvisible(gl2) && !gl2->gl_isdeleting) { /* make sure zoom level matches parent, ie. after an open subpatch's zoom level was changed before being closed */ if(x->gl_zoom != gl2->gl_zoom) canvas_zoom(x, gl2->gl_zoom); gobj_vis(&x->gl_gobj, gl2, 1); } } else x->gl_havewindow = 0; canvas_updatewindowlist(); } } /* set a canvas up as a graph-on-parent. Set reasonable defaults for any missing parameters and redraw things if necessary. */ void canvas_setgraph(t_glist *x, int flag, int nogoprect) { int can_graph_on_parent = x->gl_owner && !x->gl_isclone && !x->gl_loading && glist_isvisible(x->gl_owner); if (!flag && glist_isgraph(x)) { if (can_graph_on_parent) gobj_vis(&x->gl_gobj, x->gl_owner, 0); x->gl_isgraph = x->gl_hidetext = 0; if (can_graph_on_parent) { gobj_vis(&x->gl_gobj, x->gl_owner, 1); canvas_fixlinesfor(x->gl_owner, &x->gl_obj); } } else if (flag) { if (x->gl_pixwidth <= 0) x->gl_pixwidth = GLIST_DEFGRAPHWIDTH; if (x->gl_pixheight <= 0) x->gl_pixheight = GLIST_DEFGRAPHHEIGHT; if (can_graph_on_parent) gobj_vis(&x->gl_gobj, x->gl_owner, 0); x->gl_isgraph = 1; x->gl_hidetext = !(!(flag&2)); x->gl_goprect = !nogoprect; if (glist_isvisible(x) && x->gl_goprect) glist_redraw(x); if (can_graph_on_parent) { gobj_vis(&x->gl_gobj, x->gl_owner, 1); canvas_fixlinesfor(x->gl_owner, &x->gl_obj); } } } void garray_properties(t_garray *x); /* tell GUI to create a properties dialog on the canvas. We tell the user the negative of the "pixel" y scale to make it appear to grow naturally upward, whereas pixels grow downward. */ void canvas_properties(t_gobj*z, t_glist*unused) { t_glist *x = (t_glist*)z; t_gobj *y; int isgraph = glist_isgraph(x); t_float x1=0., y1=-1., x2=1., y2=1.; t_float xscale=0., yscale=0.; if(isgraph) { x1=x->gl_x1; y1=x->gl_y1; x2=x->gl_x2; y2=x->gl_y2; } else { xscale= glist_dpixtodx(x, 1); yscale=-glist_dpixtody(x, 1); } pdgui_stub_vnew(&x->gl_pd, "pdtk_canvas_dialog", x, "ff i ffff ii ii", xscale, yscale, /* used to be %g ... */ isgraph, x1,y1, x2,y2, /* used to be %g ... */ (int)x->gl_pixwidth, (int)x->gl_pixheight, (int)x->gl_xmargin, (int)x->gl_ymargin); /* if any arrays are in the graph, put out their dialogs too */ for (y = x->gl_list; y; y = y->g_next) if (pd_class(&y->g_pd) == garray_class) garray_properties((t_garray *)y); } /* called from the gui when "OK" is selected on the canvas properties dialog. Again we negate "y" scale. */ static void canvas_donecanvasdialog(t_glist *x, t_symbol *s, int argc, t_atom *argv) { t_float xperpix, yperpix, x1, y1, x2, y2, xpix, ypix, xmargin, ymargin; int graphme, redraw = 0, fromgui; xperpix = atom_getfloatarg(0, argc, argv); yperpix = atom_getfloatarg(1, argc, argv); graphme = (int)(atom_getfloatarg(2, argc, argv)); x1 = atom_getfloatarg(3, argc, argv); y1 = atom_getfloatarg(4, argc, argv); x2 = atom_getfloatarg(5, argc, argv); y2 = atom_getfloatarg(6, argc, argv); xpix = atom_getfloatarg(7, argc, argv); ypix = atom_getfloatarg(8, argc, argv); xmargin = atom_getfloatarg(9, argc, argv); ymargin = atom_getfloatarg(10, argc, argv); fromgui = atom_getfloatarg(11, argc, argv); /* hack - if graphme is 2 (meaning, not GOP but hide the text anyhow), perhaps we're happier with 0. This is only checked if this is really being called, as intended, from the GUI. For compatibility with old patches that reverse-engineered donecanvasdialog to modify patch parameters, we leave the buggy behavior in when there's no "fromgui" argument supplied. */ if (fromgui && (!(graphme & 1))) graphme = 0; /* parent windows are treated the same as applies to individual objects */ canvas_undo_add(x, UNDO_CANVAS_APPLY, "apply", canvas_undo_set_canvas(x)); x->gl_pixwidth = xpix; x->gl_pixheight = ypix; x->gl_xmargin = xmargin; x->gl_ymargin = ymargin; yperpix = -yperpix; if (xperpix == 0) xperpix = 1; if (yperpix == 0) yperpix = 1; if (graphme) { if (x1 != x2) x->gl_x1 = x1, x->gl_x2 = x2; else x->gl_x1 = 0, x->gl_x2 = 1; if (y1 != y2) x->gl_y1 = y1, x->gl_y2 = y2; else x->gl_y1 = 0, x->gl_y2 = 1; } else { if (xperpix != glist_dpixtodx(x, 1) || yperpix != glist_dpixtody(x, 1)) redraw = 1; if (xperpix > 0) { x->gl_x1 = 0; x->gl_x2 = xperpix; } else { x->gl_x1 = -xperpix * (x->gl_screenx2 - x->gl_screenx1); x->gl_x2 = x->gl_x1 + xperpix; } if (yperpix > 0) { x->gl_y1 = 0; x->gl_y2 = yperpix; } else { x->gl_y1 = -yperpix * (x->gl_screeny2 - x->gl_screeny1); x->gl_y2 = x->gl_y1 + yperpix; } } /* LATER avoid doing 2 redraws here (possibly one inside setgraph) */ canvas_setgraph(x, graphme, 0); canvas_dirty(x, 1); if (x->gl_havewindow) canvas_redraw(x); else if (!x->gl_isclone && glist_isvisible(x->gl_owner)) { gobj_vis(&x->gl_gobj, x->gl_owner, 0); gobj_vis(&x->gl_gobj, x->gl_owner, 1); } } /* called from the gui when a popup menu comes back with "properties," "open," or "help." */ static void canvas_done_popup(t_canvas *x, t_float which, t_float xpos, t_float ypos) { char namebuf[MAXPDSTRING], *basenamep; t_gobj *y; for (y = x->gl_list; y; y = y->g_next) { int x1, y1, x2, y2; if (canvas_hitbox(x, y, xpos, ypos, &x1, &y1, &x2, &y2)) { if (which == 0) /* properties */ { if (!class_getpropertiesfn(pd_class(&y->g_pd))) continue; (*class_getpropertiesfn(pd_class(&y->g_pd)))(y, x); return; } else if (which == 1) /* open */ { if (!zgetfn(&y->g_pd, gensym("menu-open"))) continue; vmess(&y->g_pd, gensym("menu-open"), ""); return; } else /* help */ { const char *dir; if (pd_class(&y->g_pd) == canvas_class && canvas_isabstraction((t_canvas *)y)) { t_object *ob = (t_object *)y; int ac = binbuf_getnatom(ob->te_binbuf); t_atom *av = binbuf_getvec(ob->te_binbuf); if (ac < 1) return; atom_string(av, namebuf, MAXPDSTRING); /* strip dir from name : */ basenamep = strrchr(namebuf, '/'); #ifdef _WIN32 if (!basenamep) basenamep = strrchr(namebuf, '\\'); #endif if (!basenamep) basenamep = namebuf; else basenamep++; /* strip last '/' */ dir = canvas_getdir((t_canvas *)y)->s_name; } else { strncpy(namebuf, class_gethelpname(pd_class(&y->g_pd)), MAXPDSTRING-1); namebuf[MAXPDSTRING-1] = 0; dir = class_gethelpdir(pd_class(&y->g_pd)); basenamep = namebuf; } if (strlen(namebuf) < 4 || strcmp(namebuf + strlen(namebuf) - 3, ".pd")) strcat(namebuf, ".pd"); open_via_helppath(basenamep, dir); return; } } } if (which == 0) canvas_properties(&x->gl_gobj, 0); else if (which == 2) open_via_helppath("intro.pd", canvas_getdir((t_canvas *)x)->s_name); } #define NOMOD 0 #define SHIFTMOD 1 #define CTRLMOD 2 #define ALTMOD 4 #define RIGHTCLICK 8 #define DCLICKINTERVAL 0.25 /* undarken deselected gatoms: * it's slightly ugly to have this in here, but we cannot undarken * in gatom_key (which is the gatom's e_keyfn) as this is also called * when the user just hits Enter */ void gatom_undarken(t_text *x); static void undarken_if_gatom(t_gobj*gobj) { t_object*obj = gobj?pd_checkobject(&gobj->g_pd):0; if(obj && T_ATOM == obj->te_type) gatom_undarken(obj); } /* mouse click */ static void canvas_doclick(t_canvas *x, int xpos, int ypos, int which, int mod, int doit) { t_gobj *hitbox; int shiftmod, runmode, altmod, doublemod = 0, rightclick; int x1=0, y1=0, x2=0, y2=0, clickreturned = 0; t_text *hitobj; if (!x->gl_editor) { bug("editor"); return; } shiftmod = (mod & SHIFTMOD); runmode = ((mod & CTRLMOD) || (!x->gl_edit)); altmod = (mod & ALTMOD); rightclick = (mod & RIGHTCLICK); EDITOR->canvas_undo_already_set_move = 0; /* if keyboard was grabbed, notify grabber and cancel the grab */ if (doit && x->gl_editor->e_grab && x->gl_editor->e_keyfn) { (* x->gl_editor->e_keyfn) (x->gl_editor->e_grab, &s_, 0); undarken_if_gatom(x->gl_editor->e_grab); glist_grab(x, 0, 0, 0, 0, 0); } if (doit && xpos == EDITOR->canvas_upx && ypos == EDITOR->canvas_upy && sys_getrealtime() - EDITOR->canvas_upclicktime < DCLICKINTERVAL) doublemod = 1; x->gl_editor->e_lastmoved = 0; if (doit) { x->gl_editor->e_grab = 0; x->gl_editor->e_onmotion = MA_NONE; } #if 0 post("click %d %d %d %d", xpos, ypos, which, mod); #endif if (x->gl_editor->e_onmotion != MA_NONE) return; x->gl_editor->e_xwas = xpos; x->gl_editor->e_ywas = ypos; if (runmode && !rightclick) { /* is a text activated ? */ if (x->gl_editor->e_textedfor && doit) { hitobj = rtext_getowner(x->gl_editor->e_textedfor); if (canvas_hitbox(x, &hitobj->te_g, xpos, ypos, &x1, &y1, &x2, &y2)) { rtext_mouse(x->gl_editor->e_textedfor, xpos - x1, ypos - y1, (shiftmod? RTEXT_SHIFT : (doublemod ? RTEXT_DBL : RTEXT_DOWN))); x->gl_editor->e_onmotion = MA_DRAGTEXT; x->gl_editor->e_xwas = x1; x->gl_editor->e_ywas = y1; } else { rtext_retext(x->gl_editor->e_textedfor); rtext_activate(x->gl_editor->e_textedfor, 0); } return; } for (hitbox = x->gl_list; hitbox; hitbox = hitbox->g_next) { /* check if the object wants to be clicked */ if (canvas_hitbox(x, hitbox, xpos, ypos, &x1, &y1, &x2, &y2) && (clickreturned = gobj_click(hitbox, x, xpos, ypos, shiftmod, ((mod & CTRLMOD) && (!x->gl_edit)) || altmod, doublemod, doit))) break; } if (!doit) { if (hitbox) canvas_setcursor(x, clickreturned); else canvas_setcursor(x, CURSOR_RUNMODE_NOTHING); } return; } /* if not a runmode left click, fall here. */ hitbox = canvas_findhitbox(x, xpos, ypos, &x1, &y1, &x2, &y2); hitobj = (hitbox ? pd_checkobject(&hitbox->g_pd) : 0); /* if text is activated while we're in locked state, this must be a number box - so if we clicked outside the box, deactivate it. This is a different situation from the "deselect" action below when we're unlocked. */ /* if (doit) post("%d %x %x %x", x->gl_edit, x->gl_editor->e_textedfor, hitobj, (hitobj ? glist_findrtext(x, hitobj) : 0)); if (doit && (!x->gl_edit) && x->gl_editor->e_textedfor && (!hitobj || (glist_findrtext(x, hitobj) != x->gl_editor->e_textedfor))) rtext_activate(x->gl_editor->e_textedfor, 0); */ if (hitbox) { /* we're in a rectangle */ if (rightclick) canvas_rightclick(x, xpos, ypos, hitbox); else if (shiftmod) { if (doit) { t_rtext *rt; if (hitobj && (rt = x->gl_editor->e_textedfor) && rt == glist_findrtext(x, hitobj)) { rtext_mouse(rt, xpos - x1, ypos - y1, RTEXT_SHIFT); x->gl_editor->e_onmotion = MA_DRAGTEXT; x->gl_editor->e_xwas = x1; x->gl_editor->e_ywas = y1; } else { if (glist_isselected(x, hitbox)) glist_deselect(x, hitbox); else glist_select(x, hitbox); } } } else { int noutlet; int out_activeminh = (OHEIGHT + 1) * x->gl_zoom; int out_activemaxh = (y2 - y1) / 4; int out_activeheight = OHEIGHT * 2 * x->gl_zoom; if (out_activeheight > out_activemaxh) out_activeheight = out_activemaxh; if (out_activeheight < out_activeminh) out_activeheight = out_activeminh; /* resize? only for "true" text boxes or canvases */ if (xpos >= x2-4 && ypos < y2-4 && hitobj && (hitobj->te_pd->c_wb == &text_widgetbehavior || hitobj->te_type == T_ATOM || pd_checkglist(&hitobj->te_pd))) { if (doit) { if (!glist_isselected(x, hitbox)) { glist_noselect(x); glist_select(x, hitbox); } x->gl_editor->e_onmotion = MA_RESIZE; x->gl_editor->e_xwas = x1; x->gl_editor->e_ywas = y1; x->gl_editor->e_xnew = xpos; x->gl_editor->e_ynew = ypos; canvas_undo_add(x, UNDO_APPLY, "resize", canvas_undo_set_apply(x, glist_getindex(x, hitbox))); } else canvas_setcursor(x, CURSOR_EDITMODE_RESIZE); } /* look for an outlet */ else if (hitobj && (noutlet = obj_noutlets(hitobj)) && ypos >= y2 - out_activeheight) { int width = x2 - x1; int iow = IOWIDTH * x->gl_zoom; int nout1 = (noutlet > 1 ? noutlet - 1 : 1); int closest = ((xpos-x1) * (nout1) + width/2)/width; if (noutlet == 1 || closest < noutlet) { if (doit) { int issignal = obj_issignaloutlet(hitobj, closest); int xout = x1 + IOMIDDLE * x->gl_zoom + (noutlet > 1 ? ((width - iow) * closest)/nout1 : 0); x->gl_editor->e_onmotion = MA_CONNECT; x->gl_editor->e_xwas = xout; x->gl_editor->e_ywas = y2; pdgui_vmess("::pdtk_canvas::cords_to_foreground", "ci", x, 0); pdgui_vmess(0, "crr iiii ri rs", x, "create", "line", x->gl_editor->e_xwas,x->gl_editor->e_ywas, xpos,ypos, "-width", (issignal ? 2 : 1) * x->gl_zoom, "-tags", "x"); } else canvas_setcursor(x, CURSOR_EDITMODE_CONNECT); } else if (doit) goto nooutletafterall; else canvas_setcursor(x, CURSOR_EDITMODE_NOTHING); } /* not in an outlet; select and move */ else if (doit) { t_rtext *rt; /* check if the box is being text edited */ nooutletafterall: if (hitobj && (rt = x->gl_editor->e_textedfor) && rt == glist_findrtext(x, hitobj)) { rtext_mouse(rt, xpos - x1, ypos - y1, (doublemod ? RTEXT_DBL : RTEXT_DOWN)); x->gl_editor->e_onmotion = MA_DRAGTEXT; x->gl_editor->e_xwas = x1; x->gl_editor->e_ywas = y1; } else { /* otherwise select and drag to displace */ if (!glist_isselected(x, hitbox)) { glist_noselect(x); glist_select(x, hitbox); } x->gl_editor->e_onmotion = MA_MOVE; } } else canvas_setcursor(x, CURSOR_EDITMODE_NOTHING); } return; } /* if right click doesn't hit any boxes, call rightclick routine anyway */ if (rightclick) canvas_rightclick(x, xpos, ypos, 0); /* if not an editing action, and if we didn't hit a box, set cursor and return */ if (runmode || rightclick) { canvas_setcursor(x, CURSOR_RUNMODE_NOTHING); return; } /* having failed to find a box, we try lines now. */ if (!runmode && !altmod) { t_linetraverser t; t_outconnect *oc; t_float fx = xpos, fy = ypos; t_glist *glist2 = glist_getcanvas(x); linetraverser_start(&t, glist2); while ((oc = linetraverser_next(&t))) { int outindex, inindex; t_float lx1 = t.tr_lx1, ly1 = t.tr_ly1, lx2 = t.tr_lx2, ly2 = t.tr_ly2; t_float area = (lx2 - lx1) * (fy - ly1) - (ly2 - ly1) * (fx - lx1); t_float dsquare = (lx2-lx1) * (lx2-lx1) + (ly2-ly1) * (ly2-ly1); if (area * area >= 50 * dsquare) continue; if ((lx2-lx1) * (fx-lx1) + (ly2-ly1) * (fy-ly1) < 0) continue; if ((lx2-lx1) * (lx2-fx) + (ly2-ly1) * (ly2-fy) < 0) continue; outindex = canvas_getindex(glist2, &t.tr_ob->ob_g); inindex = canvas_getindex(glist2, &t.tr_ob2->ob_g); if (shiftmod) { int soutindex, sinindex, soutno, sinno; /* if no line is selected, just add this line to the selection */ if(!x->gl_editor->e_selectedline) { if (doit) { glist_selectline(glist2, oc, outindex, t.tr_outno, inindex, t.tr_inno); } canvas_setcursor(x, CURSOR_EDITMODE_DISCONNECT); return; } soutindex = x->gl_editor->e_selectline_index1; sinindex = x->gl_editor->e_selectline_index2; soutno = x->gl_editor->e_selectline_outno; sinno = x->gl_editor->e_selectline_inno; /* if the hovered line is already selected, deselect it */ if ((outindex == soutindex) && (inindex == sinindex) && (soutno == t.tr_outno) && (sinno == t.tr_inno)) { if(doit) glist_deselectline(x); canvas_setcursor(x, CURSOR_EDITMODE_DISCONNECT); return; } /* swap selected and hovered connection */ if ((!x->gl_editor->e_selection) && ((outindex == soutindex) || (inindex == sinindex))) { if(doit) { canvas_undo_add(x, UNDO_SEQUENCE_START, "reconnect", 0); canvas_disconnect_with_undo(x, soutindex, soutno, sinindex, sinno); canvas_disconnect_with_undo(x, outindex, t.tr_outno, inindex, t.tr_inno); canvas_connect_with_undo(x, outindex, t.tr_outno, sinindex, sinno); canvas_connect_with_undo(x, soutindex, soutno, inindex, t.tr_inno); canvas_undo_add(x, UNDO_SEQUENCE_END, "reconnect", 0); x->gl_editor->e_selectline_index1 = soutindex; x->gl_editor->e_selectline_outno = soutno; x->gl_editor->e_selectline_index2 = inindex; x->gl_editor->e_selectline_inno = t.tr_inno; canvas_dirty(x, 1); } canvas_setcursor(x, CURSOR_EDITMODE_DISCONNECT); return; } } if (!shiftmod) { /* !shiftmode: clear selection before selecting line */ if (doit) { glist_noselect(x); glist_selectline(glist2, oc, outindex, t.tr_outno, inindex, t.tr_inno); } canvas_setcursor(x, CURSOR_EDITMODE_DISCONNECT); return; } } } canvas_setcursor(x, CURSOR_EDITMODE_NOTHING); if (doit) { if (!shiftmod) glist_noselect(x); pdgui_vmess(0, "crr iiii rs", x, "create", "rectangle", xpos,ypos, xpos,ypos, "-tags", "x"); x->gl_editor->e_xwas = xpos; x->gl_editor->e_ywas = ypos; x->gl_editor->e_onmotion = MA_REGION; } } void canvas_mouse(t_canvas *x, t_floatarg xpos, t_floatarg ypos, t_floatarg which, t_floatarg mod) { canvas_doclick(x, xpos, ypos, which, mod, 1); } int canvas_isconnected (t_canvas *x, t_text *ob1, int n1, t_text *ob2, int n2) { t_linetraverser t; t_outconnect *oc; linetraverser_start(&t, x); while ((oc = linetraverser_next(&t))) if (t.tr_ob == ob1 && t.tr_outno == n1 && t.tr_ob2 == ob2 && t.tr_inno == n2) return (1); return (0); } static int canconnect(t_canvas*x, t_object*src, int nout, t_object*sink, int nin) { if (!src || !sink || sink == src) /* do source and sink exist (and are not the same)?*/ return 0; if (nin >= obj_ninlets(sink) || (nout >= obj_noutlets(src))) /* do the requested iolets exist? */ return 0; if (canvas_isconnected(x, src, nout, sink, nin)) /* are the objects already connected? */ return 0; return (!obj_issignaloutlet(src, nout) || /* are the iolets compatible? */ obj_issignalinlet(sink, nin)); } static int tryconnect(t_canvas*x, t_object*src, int nout, t_object*sink, int nin) { if(canconnect(x, src, nout, sink, nin)) { t_outconnect *oc = obj_connect(src, nout, sink, nin); if(oc) { int iow = IOWIDTH * x->gl_zoom; int iom = IOMIDDLE * x->gl_zoom; int x11=0, x12=0, x21=0, x22=0; int y11=0, y12=0, y21=0, y22=0; int noutlets1, ninlets, lx1, ly1, lx2, ly2; char tag[128]; char*tags[] = {tag, "cord"}; sprintf(tag, "l%p", oc); gobj_getrect(&src->ob_g, x, &x11, &y11, &x12, &y12); gobj_getrect(&sink->ob_g, x, &x21, &y21, &x22, &y22); noutlets1 = obj_noutlets(src); ninlets = obj_ninlets(sink); lx1 = x11 + (noutlets1 > 1 ? ((x12-x11-iow) * nout)/(noutlets1-1) : 0) + iom; ly1 = y12; lx2 = x21 + (ninlets > 1 ? ((x22-x21-iow) * nin)/(ninlets-1) : 0) + iom; ly2 = y21; pdgui_vmess(0, "crr iiii ri rS", glist_getcanvas(x), "create", "line", lx1,ly1, lx2,ly2, "-width", (obj_issignaloutlet(src, nout) ? 2 : 1) * x->gl_zoom, "-tags", 2, tags); canvas_undo_add(x, UNDO_CONNECT, "connect", canvas_undo_set_connect(x, canvas_getindex(x, &src->ob_g), nout, canvas_getindex(x, &sink->ob_g), nin)); canvas_dirty(x, 1); return 1; } } return 0; } static void canvas_doconnect(t_canvas *x, int xpos, int ypos, int mod, int doit) { int x11=0, y11=0, x12=0, y12=0; t_gobj *y1; int x21=0, y21=0, x22=0, y22=0; t_gobj *y2; int xwas = x->gl_editor->e_xwas, ywas = x->gl_editor->e_ywas; #if 0 post("canvas_doconnect(%p, %d, %d, %d, %d)", x, xpos, ypos, mod, doit); #endif if (doit) { pdgui_vmess("::pdtk_canvas::cords_to_foreground", "ci", x, 1); pdgui_vmess(0, "crs", x, "delete", "x"); } else pdgui_vmess(0, "crs iiii", x, "coords", "x", x->gl_editor->e_xwas,x->gl_editor->e_ywas, xpos,ypos); if ((y1 = canvas_findhitbox(x, xwas, ywas, &x11, &y11, &x12, &y12)) && (y2 = canvas_findhitbox(x, xpos, ypos, &x21, &y21, &x22, &y22))) { t_object *ob1 = pd_checkobject(&y1->g_pd); t_object *ob2 = pd_checkobject(&y2->g_pd); int noutlet1, ninlet2; if (ob1 && ob2 && ob1 != ob2 && (noutlet1 = obj_noutlets(ob1)) && (ninlet2 = obj_ninlets(ob2))) { int width1 = x12 - x11, closest1, hotspot1; int width2 = x22 - x21, closest2, hotspot2; if (noutlet1 > 1) { closest1 = ((xwas-x11) * (noutlet1-1) + width1/2)/width1; hotspot1 = x11 + (width1 - IOWIDTH) * closest1 / (noutlet1-1); } else closest1 = 0, hotspot1 = x11; if (ninlet2 > 1) { closest2 = ((xpos-x21) * (ninlet2-1) + width2/2)/width2; hotspot2 = x21 + (width2 - IOWIDTH) * closest2 / (ninlet2-1); } else closest2 = 0, hotspot2 = x21; if (closest1 >= noutlet1) closest1 = noutlet1 - 1; if (closest2 >= ninlet2) closest2 = ninlet2 - 1; if (canvas_isconnected (x, ob1, closest1, ob2, closest2)) { canvas_setcursor(x, CURSOR_EDITMODE_NOTHING); return; } if (obj_issignaloutlet(ob1, closest1) && !obj_issignalinlet(ob2, closest2)) { if (doit) pd_error(0, "can't connect audio signal outlet to nonsignal inlet"); canvas_setcursor(x, CURSOR_EDITMODE_NOTHING); return; } if (doit) { t_selection *sel; int selmode = 0; canvas_undo_add(x, UNDO_SEQUENCE_START, "connect", 0); tryconnect(x, ob1, closest1, ob2, closest2); canvas_dirty(x, 1); /* now find out if either ob1 xor ob2 are part of the selection, * and if so, connect the rest of the selection as well */ if(mod & SHIFTMOD) /* intelligent patching needs to be activated by modifier key */ selmode = glist_isselected(x, &ob1->ob_g) + 2 * glist_isselected(x, &ob2->ob_g); switch(selmode) { case 3: /* both source and sink are selected */ /* if only the source & sink are selected, keep connecting them */ if(0 == x->gl_editor->e_selection->sel_next->sel_next) { int i, j; for(i=closest1, j=closest2; (i < noutlet1) && (j < ninlet2); i++, j++ ) tryconnect(x, ob1, i, ob2, j); } else /* if other objects are selected as well, connect those either as * sources or sinks, whichever allows for more connections */ { /* get a left-right sorted list of all selected objects * (but the already connected ones) * count the possibles sinks and sources */ int mode = 0; int i; int sinks = 0, sources = 0; t_float ysinks = 0., ysources = 0.; int msgout = !obj_issignaloutlet(ob1, closest1); int sigin = obj_issignalinlet(ob2, closest2); t_selection*sortedsel = 0; /* sort the selected objects from left-right */ for(sel = x->gl_editor->e_selection, i=1; sel; sel = sel->sel_next, i++) { t_object*ob = pd_checkobject(&sel->sel_what->g_pd); t_selection*sob = 0; /* skip illegal objects and the reference source&sink */ if (!ob || (ob1 == ob) || (ob2 == ob)) continue; if (canconnect(x, ob1, closest1 + 1 + sinks, ob, closest2)) { sinks += 1; ysinks += ob->te_ypix; } if (canconnect(x, ob, closest1, ob2, closest2 + 1 + sources)) { sources += 1; ysources += ob->te_ypix; } /* insert the object into the sortedsel list */ if((sob = getbytes(sizeof(*sob)))) { t_selection*s, *slast=0; sob->sel_what = &ob->te_g; for(s=sortedsel; s; s=s->sel_next) { t_object*o = pd_checkobject(&s->sel_what->g_pd); if(!o) continue; if((ob->te_xpix < o->te_xpix) || ((ob->te_xpix == o->te_xpix) && (ob->te_ypix < o->te_ypix))) { sob->sel_next = s; if(slast) slast->sel_next = sob; else sortedsel = sob; break; } slast=s; } if(slast) slast->sel_next = sob; else sortedsel = sob; } } /* try to maximize connections */ mode = (sinks > sources); /* maximizing failed, so prefer to connect from top to bottom */ if (sinks && (sinks == sources)) { mode = ((ysinks - ob1->te_ypix) / sinks) > ((ysources - ob2->te_ypix) / sources) * -1.; } sinks = 0; sources = 0; if (mode) for(sel=sortedsel; ((closest1 + 1 + sinks) < noutlet1) && sel; sel=sel->sel_next) { sinks += tryconnect(x, ob1, closest1 + 1 + sinks, pd_checkobject(&sel->sel_what->g_pd), closest2); } else for(sel=sortedsel; ((closest2 + 1 + sources) < ninlet2) && sel; sel=sel->sel_next) { sources += tryconnect(x, pd_checkobject(&sel->sel_what->g_pd), closest1, ob2, closest2 + 1 + sources); } /* free the sorted list of selections */ for(sel=sortedsel; sel; ) { t_selection*s = sel->sel_next; freebytes(sel, sizeof(*sel)); sel = s; } } break; case 1: /* source(s) selected */ for(sel = x->gl_editor->e_selection; sel; sel = sel->sel_next) { t_object*selo = pd_checkobject(&sel->sel_what->g_pd); if (!selo || selo == ob1) continue; tryconnect(x, selo, closest1, ob2, closest2); } break; case 2: /* sink(s) selected */ for(sel = x->gl_editor->e_selection; sel; sel = sel->sel_next) { t_object*selo = pd_checkobject(&sel->sel_what->g_pd); if (!selo || selo == ob2) continue; tryconnect(x, ob1, closest1, selo, closest2); } break; default: break; } canvas_undo_add(x, UNDO_SEQUENCE_END, "connect", 0); } else canvas_setcursor(x, CURSOR_EDITMODE_CONNECT); return; } } canvas_setcursor(x, CURSOR_EDITMODE_NOTHING); } void canvas_selectinrect(t_canvas *x, int lox, int loy, int hix, int hiy) { t_gobj *y; for (y = x->gl_list; y; y = y->g_next) { int x1, y1, x2, y2; gobj_getrect(y, x, &x1, &y1, &x2, &y2); if (hix >= x1 && lox <= x2 && hiy >= y1 && loy <= y2 && !glist_isselected(x, y)) glist_select(x, y); } } static void canvas_doregion(t_canvas *x, int xpos, int ypos, int doit) { if (doit) { int lox, loy, hix, hiy; if (x->gl_editor->e_xwas < xpos) lox = x->gl_editor->e_xwas, hix = xpos; else hix = x->gl_editor->e_xwas, lox = xpos; if (x->gl_editor->e_ywas < ypos) loy = x->gl_editor->e_ywas, hiy = ypos; else hiy = x->gl_editor->e_ywas, loy = ypos; canvas_selectinrect(x, lox, loy, hix, hiy); pdgui_vmess(0, "crs", x, "delete", "x"); x->gl_editor->e_onmotion = MA_NONE; } else pdgui_vmess(0, "crs iiii", x, "coords", "x", x->gl_editor->e_xwas,x->gl_editor->e_ywas, xpos,ypos); } void canvas_mouseup(t_canvas *x, t_floatarg fxpos, t_floatarg fypos, t_floatarg fwhich, t_floatarg fmod) { int xpos = fxpos, ypos = fypos, which = fwhich; int mod = fmod; #if 0 post("mouseup %d %d %d %d", xpos, ypos, which, mod); #endif if (!x->gl_editor) { bug("editor"); return; } EDITOR->canvas_upclicktime = sys_getrealtime(); EDITOR->canvas_upx = xpos; EDITOR->canvas_upy = ypos; if (x->gl_editor->e_onmotion == MA_CONNECT) canvas_doconnect(x, xpos, ypos, mod, 1); else if (x->gl_editor->e_onmotion == MA_REGION) canvas_doregion(x, xpos, ypos, 1); else if ((x->gl_editor->e_onmotion == MA_MOVE || x->gl_editor->e_onmotion == MA_RESIZE)) { /* if there's only one text item selected activate the text. LATER consider under sme conditions not activating it, for instance if it appears to have been desired only to move the object. Maybe shift-click could allow dragging without activating text? A different solution (only activating if the object wasn't moved (commit f0df4e586) turned out to flout ctrlD+move+retype. */ if (x->gl_editor->e_selection && !(x->gl_editor->e_selection->sel_next)) { t_gobj *g = x->gl_editor->e_selection->sel_what; t_glist *gl2; /* first though, check we aren't an abstraction with a dirty sub-patch that would be discarded if we edit this. */ if (canvas_undo_confirmdiscard(g)) return; /* OK, activate it */ gobj_activate(x->gl_editor->e_selection->sel_what, x, 1); } } else if (x->gl_editor->e_onmotion == MA_PASSOUT) { if (!x->gl_editor->e_motionfn) bug("e_motionfn"); (*x->gl_editor->e_motionfn)(&x->gl_editor->e_grab->g_pd, xpos - x->gl_editor->e_xwas, ypos - x->gl_editor->e_ywas, 1); } x->gl_editor->e_onmotion = MA_NONE; } /* displace the selection by (dx, dy) pixels */ static void canvas_displaceselection(t_canvas *x, int dx, int dy) { t_selection *y; int resortin = 0, resortout = 0; if (x->gl_editor->e_selection && !EDITOR->canvas_undo_already_set_move) { canvas_undo_add(x, UNDO_MOTION, "motion", canvas_undo_set_move(x, 1)); EDITOR->canvas_undo_already_set_move = 1; } for (y = x->gl_editor->e_selection; y; y = y->sel_next) { t_class *cl = pd_class(&y->sel_what->g_pd); gobj_displace(y->sel_what, x, dx, dy); if (cl == vinlet_class) resortin = 1; else if (cl == voutlet_class) resortout = 1; } if (resortin) canvas_resortinlets(x); if (resortout) canvas_resortoutlets(x); pdgui_vmess("pdtk_canvas_getscroll", "c", x); if (x->gl_editor->e_selection) canvas_dirty(x, 1); } /* this routine is called whenever a key is pressed or released. "x" may be zero if there's no current canvas. The first argument is true or false for down/up; the second one is either a symbolic key name (e.g., "Right" or an Ascii key number. The third is the shift key. */ void canvas_key(t_canvas *x, t_symbol *s, int ac, t_atom *av) { int keynum, fflag; t_symbol *gotkeysym; int down, shift; if (ac < 3) return; EDITOR->canvas_undo_already_set_move = 0; down = (atom_getfloat(av) != 0); /* nonzero if it's a key down */ shift = (atom_getfloat(av+2) != 0); /* nonzero if shift-ed */ if (av[1].a_type == A_SYMBOL) gotkeysym = av[1].a_w.w_symbol; else if (av[1].a_type == A_FLOAT) { char buf[UTF8_MAXBYTES1]; switch((int)(av[1].a_w.w_float)) { case 8: gotkeysym = gensym("BackSpace"); break; case 9: gotkeysym = gensym("Tab"); break; case 10: gotkeysym = gensym("Return"); break; case 27: gotkeysym = gensym("Escape"); break; case 32: gotkeysym = gensym("Space"); break; case 127:gotkeysym = gensym("Delete"); break; default: /*-- moo: assume keynum is a Unicode codepoint; encode as UTF-8 --*/ u8_wc_toutf8_nul(buf, (UCS4)(av[1].a_w.w_float)); gotkeysym = gensym(buf); } } else gotkeysym = gensym("?"); fflag = (av[0].a_type == A_FLOAT ? av[0].a_w.w_float : 0); keynum = (av[1].a_type == A_FLOAT ? av[1].a_w.w_float : 0); if (keynum == '{' || keynum == '}') { post("keycode %d: dropped", (int)keynum); return; } #if 0 post("keynum %d, down %d", (int)keynum, down); #endif if (keynum == '\r') keynum = '\n'; if (av[1].a_type == A_SYMBOL && !strcmp(av[1].a_w.w_symbol->s_name, "Return")) keynum = '\n'; /* alias Apple key numbers to symbols. This is done unconditionally, not just if we're on an Apple, just in case the GUI is remote. */ if (keynum == 30 || keynum == 63232) keynum = 0, gotkeysym = gensym("Up"); else if (keynum == 31 || keynum == 63233) keynum = 0, gotkeysym = gensym("Down"); else if (keynum == 28 || keynum == 63234) keynum = 0, gotkeysym = gensym("Left"); else if (keynum == 29 || keynum == 63235) keynum = 0, gotkeysym = gensym("Right"); else if (keynum == 63273) keynum = 0, gotkeysym = gensym("Home"); else if (keynum == 63275) keynum = 0, gotkeysym = gensym("End"); else if (keynum == 63276) keynum = 0, gotkeysym = gensym("Prior"); else if (keynum == 63277) keynum = 0, gotkeysym = gensym("Next"); else if (keynum == 63236) keynum = 0, gotkeysym = gensym("F1"); else if (keynum == 63237) keynum = 0, gotkeysym = gensym("F2"); else if (keynum == 63238) keynum = 0, gotkeysym = gensym("F3"); else if (keynum == 63239) keynum = 0, gotkeysym = gensym("F4"); else if (keynum == 63240) keynum = 0, gotkeysym = gensym("F5"); else if (keynum == 63241) keynum = 0, gotkeysym = gensym("F6"); else if (keynum == 63242) keynum = 0, gotkeysym = gensym("F7"); else if (keynum == 63243) keynum = 0, gotkeysym = gensym("F8"); else if (keynum == 63244) keynum = 0, gotkeysym = gensym("F9"); else if (keynum == 63245) keynum = 0, gotkeysym = gensym("F10"); else if (keynum == 63246) keynum = 0, gotkeysym = gensym("F11"); else if (keynum == 63247) keynum = 0, gotkeysym = gensym("F12"); if (gensym("#key")->s_thing && down) pd_float(gensym("#key")->s_thing, (t_float)keynum); if (gensym("#keyup")->s_thing && !down) pd_float(gensym("#keyup")->s_thing, (t_float)keynum); if (gensym("#keyname")->s_thing) { t_atom at[2]; at[0] = av[0]; SETFLOAT(at, down); SETSYMBOL(at+1, gotkeysym); pd_list(gensym("#keyname")->s_thing, 0, 2, at); } if (!x || !x->gl_editor) /* if that 'invis'ed the window, stop. */ return; if (x && down) { /* cancel any dragging action */ if (x->gl_editor->e_onmotion == MA_MOVE) x->gl_editor->e_onmotion = MA_NONE; /* if an object has "grabbed" keys just send them on */ if (x->gl_editor->e_grab && x->gl_editor->e_keyfn && keynum) (* x->gl_editor->e_keyfn) (x->gl_editor->e_grab, gotkeysym, (t_float)keynum); /* if a text editor is open send the key on, as long as it is either "real" (has a key number) or else is an arrow key. */ else if (x->gl_editor->e_textedfor && (keynum || !strcmp(gotkeysym->s_name, "Home") || !strcmp(gotkeysym->s_name, "End") || !strcmp(gotkeysym->s_name, "Up") || !strcmp(gotkeysym->s_name, "Down") || !strcmp(gotkeysym->s_name, "Left") || !strcmp(gotkeysym->s_name, "Right"))) { /* send the key to the box's editor */ if (!x->gl_editor->e_textdirty) { canvas_setundo(x, canvas_undo_cut, canvas_undo_set_cut(x, UCUT_TEXT), "typing"); } rtext_key(x->gl_editor->e_textedfor, (int)keynum, gotkeysym); if (x->gl_editor->e_textdirty) canvas_dirty(x, 1); } /* check for backspace or clear */ else if (keynum == 8 || keynum == 127) { if (x->gl_editor->e_selection) canvas_undo_add(x, UNDO_SEQUENCE_START, "clear", 0); if (x->gl_editor->e_selectedline) canvas_clearline(x); if (x->gl_editor->e_selection) { canvas_undo_add(x, UNDO_CUT, "clear", canvas_undo_set_cut(x, UCUT_CLEAR)); canvas_doclear(x); canvas_undo_add(x, UNDO_SEQUENCE_END, "clear", 0); } } /* check for arrow keys */ else if (!strcmp(gotkeysym->s_name, "Up")) canvas_displaceselection(x, 0, shift ? -10 : -1); else if (!strcmp(gotkeysym->s_name, "Down")) canvas_displaceselection(x, 0, shift ? 10 : 1); else if (!strcmp(gotkeysym->s_name, "Left")) canvas_displaceselection(x, shift ? -10 : -1, 0); else if (!strcmp(gotkeysym->s_name, "Right")) canvas_displaceselection(x, shift ? 10 : 1, 0); else if ((MA_CONNECT == x->gl_editor->e_onmotion) && (CURSOR_EDITMODE_CONNECT == EDITOR->canvas_cursorwas) && !strncmp(gotkeysym->s_name, "Shift", 5)) { /* while in connect-mode: create connection... */ canvas_doconnect(x, x->gl_editor->e_xnew, x->gl_editor->e_ynew, 1, 1); /* ... and continue in connect-mode */ canvas_doclick(x, x->gl_editor->e_xwas, x->gl_editor->e_ywas, 0, 0, 1); } } /* if control key goes up or down, and if we're in edit mode, change cursor to indicate how the click action changes */ if (x && keynum == 0 && x->gl_edit && !strncmp(gotkeysym->s_name, "Control", 7)) canvas_setcursor(x, down ? CURSOR_RUNMODE_NOTHING :CURSOR_EDITMODE_NOTHING); } static void delay_move(t_canvas *x) { int incx = (x->gl_editor->e_xnew - x->gl_editor->e_xwas)/x->gl_zoom, incy = (x->gl_editor->e_ynew - x->gl_editor->e_ywas)/x->gl_zoom; if (incx || incy) canvas_displaceselection(x, incx, incy); x->gl_editor->e_xwas += incx * x->gl_zoom; x->gl_editor->e_ywas += incy * x->gl_zoom; } /* defined in g_text.c: */ extern void text_getfont(t_text *x, t_glist *thisglist, int *fwidthp, int *fheightp, int *guifsize); void canvas_motion(t_canvas *x, t_floatarg xpos, t_floatarg ypos, t_floatarg fmod) { #if 0 post("motion %g %g %g", xpos, ypos, fmod); #endif int mod = fmod; if (!x->gl_editor) { bug("editor"); return; } glist_setlastxy(x, xpos, ypos); if (x->gl_editor->e_onmotion == MA_MOVE) { if (!x->gl_editor->e_clock) x->gl_editor->e_clock = clock_new(x, (t_method)delay_move); clock_unset(x->gl_editor->e_clock); clock_delay(x->gl_editor->e_clock, 5); x->gl_editor->e_xnew = xpos; x->gl_editor->e_ynew = ypos; } else if (x->gl_editor->e_onmotion == MA_REGION) canvas_doregion(x, xpos, ypos, 0); else if (x->gl_editor->e_onmotion == MA_CONNECT) { canvas_doconnect(x, xpos, ypos, mod, 0); x->gl_editor->e_xnew = xpos; x->gl_editor->e_ynew = ypos; } else if (x->gl_editor->e_onmotion == MA_PASSOUT) { if (!x->gl_editor->e_motionfn) bug("e_motionfn"); (*x->gl_editor->e_motionfn)(&x->gl_editor->e_grab->g_pd, xpos - x->gl_editor->e_xwas, ypos - x->gl_editor->e_ywas, 0); x->gl_editor->e_xwas = xpos; x->gl_editor->e_ywas = ypos; } else if (x->gl_editor->e_onmotion == MA_DRAGTEXT) { t_rtext *rt = x->gl_editor->e_textedfor; if (rt) rtext_mouse(rt, xpos - x->gl_editor->e_xwas, ypos - x->gl_editor->e_ywas, RTEXT_DRAG); } else if (x->gl_editor->e_onmotion == MA_RESIZE) { int x11=0, y11=0, x12=0, y12=0; t_gobj *y1; if ((y1 = canvas_findhitbox(x, x->gl_editor->e_xwas, x->gl_editor->e_ywas, &x11, &y11, &x12, &y12))) { int wantwidth = xpos - x11; t_object *ob = pd_checkobject(&y1->g_pd); if (ob && ((ob->te_pd->c_wb == &text_widgetbehavior) || ob->te_type == T_ATOM || (pd_checkglist(&ob->te_pd) && !((t_canvas *)ob)->gl_isgraph))) { int fwidth, fheight, guifsize; text_getfont(ob, x, &fwidth, &fheight, &guifsize); wantwidth = wantwidth / fwidth; if (wantwidth < 1) wantwidth = 1; ob->te_width = wantwidth; gobj_vis(y1, x, 0); canvas_fixlinesfor(x, ob); gobj_vis(y1, x, 1); } else if (ob && ob->ob_pd == canvas_class) { gobj_vis(y1, x, 0); ((t_canvas *)ob)->gl_pixwidth += xpos - x->gl_editor->e_xnew; ((t_canvas *)ob)->gl_pixheight += ypos - x->gl_editor->e_ynew; x->gl_editor->e_xnew = xpos; x->gl_editor->e_ynew = ypos; canvas_fixlinesfor(x, ob); gobj_vis(y1, x, 1); } else post("not resizable"); } } else canvas_doclick(x, xpos, ypos, 0, mod, 0); x->gl_editor->e_lastmoved = 1; } void canvas_startmotion(t_canvas *x) { int xval, yval; if (!x->gl_editor) return; glist_getnextxy(x, &xval, &yval); if (xval == 0 && yval == 0) return; x->gl_editor->e_onmotion = MA_MOVE; x->gl_editor->e_xwas = xval; x->gl_editor->e_ywas = yval; } /* ----------------------------- window stuff ----------------------- */ extern int sys_perf; void canvas_print(t_canvas *x, t_symbol *s) { const char*filename = (*s->s_name)?(s->s_name):"x.ps"; pdgui_vmess(0, "cr rs", x, "postscript", "-file", filename); } /* find the innermost dirty sub-glist, if any, of this one (including itself) */ static t_glist *glist_finddirty(t_glist *x) { t_gobj *g; t_glist *g2; for (g = x->gl_list; g; g = g->g_next) if (pd_class(&g->g_pd) == canvas_class && (g2 = glist_finddirty((t_glist *)g))) return (g2); if (x->gl_env && x->gl_dirty) return (x); else return (0); } /* quit, after calling glist_finddirty() on all toplevels and verifying the user really wants to discard changes */ void glob_verifyquit(void *dummy, t_floatarg f) { t_glist *g, *g2; const char*msg = "really quit?"; /* find all root canvases */ for (g = pd_getcanvaslist(); g; g = g->gl_next) if ((g2 = glist_finddirty(g))) { t_atom backmsg[2]; char buf[40]; sprintf(buf, ".x%lx", g2); SETSYMBOL(backmsg+0, gensym("menuclose")); SETFLOAT (backmsg+1, 3); canvas_vis(g2, 1); pdgui_vmess("pdtk_canvas_menuclose", "^m", canvas_getrootfor(g), gensym(buf), 2, backmsg); return; } if (f == 0 && sys_perf) pdgui_vmess("pdtk_check", "r Sss", ".pdwindow", 1, &msg, "pd quit", "yes"); else glob_quit(0); } /* close a window (or possibly quit Pd), checking for dirty flags. The "force" parameter is interpreted as follows: 0 - request from GUI to close, verifying whether clean or dirty 1 - request from GUI to close, no verification 2 - verified - mark this one clean, then continue as in 1 3 - verified - mark this one clean, then verify-and-quit */ void canvas_menuclose(t_canvas *x, t_floatarg fforce) { int force = fforce; t_glist *g; t_atom backmsg[2]; char buf[40]; SETSYMBOL(backmsg+0, gensym("menuclose")); SETSYMBOL(backmsg+1, 0); if (x->gl_owner && (force == 0 || force == 1)) canvas_vis(x, 0); /* if subpatch, just invis it */ else if (force == 0) { g = glist_finddirty(x); if (g) { sprintf(buf, ".x%lx", g); SETFLOAT(backmsg+1, 2); vmess(&g->gl_pd, gensym("menu-open"), ""); pdgui_vmess("pdtk_canvas_menuclose", "^m", canvas_getrootfor(g), gensym(buf), 2, backmsg); return; } else if (sys_perf) { const char*msg = "Close this window?"; sprintf(buf, ".x%lx", x); SETFLOAT(backmsg+1, 1); pdgui_vmess("pdtk_check", "^ Sms", canvas_getrootfor(x), 1, &msg, gensym(buf), 2, backmsg, "yes"); } else pd_free(&x->gl_pd); } else if (force == 1) pd_free(&x->gl_pd); else if (force == 2) { canvas_dirty(x, 0); while (x->gl_owner && !x->gl_isclone) x = x->gl_owner; g = glist_finddirty(x); if (g) { sprintf(buf, ".x%lx", g); SETFLOAT(backmsg+1, 2); vmess(&g->gl_pd, gensym("menu-open"), ""); pdgui_vmess("pdtk_canvas_menuclose", "^m", canvas_getrootfor(g), gensym(buf), 2, backmsg); return; } else pd_free(&x->gl_pd); } else if (force == 3) { canvas_dirty(x, 0); glob_verifyquit(0, 1); } } /* put up a dialog which may call canvas_font back to do the work */ static void canvas_menufont(t_canvas *x) { t_canvas *x2 = canvas_getrootfor(x); pdgui_stub_deleteforkey(x2); pdgui_stub_vnew(&x2->gl_pd, "pdtk_canvas_dofont", &x2->gl_pd, "i", x2->gl_font); } typedef void (*t_zoomfn)(void *x, t_floatarg arg1); /* LATER, if canvas is flipped, re-scroll to preserve bottom left corner */ static void canvas_zoom(t_canvas *x, t_floatarg zoom) { if (zoom != x->gl_zoom && (zoom == 1 || zoom == 2)) { t_gobj *g; t_object *obj; for (g = x->gl_list; g; g = g->g_next) if ((obj = pd_checkobject(&g->g_pd))) { /* pass zoom message on to all objects, except canvases that aren't GOP */ t_gotfn zoommethod; if ((zoommethod = zgetfn(&obj->te_pd, gensym("zoom"))) && (!(pd_class(&obj->te_pd) == canvas_class) || (((t_glist *)obj)->gl_isgraph))) (*(t_zoomfn)zoommethod)(&obj->te_pd, zoom); } x->gl_zoom = zoom; if (x->gl_havewindow) { if (!glist_isgraph(x) && (x->gl_y2 < x->gl_y1)) { /* if it's flipped so that y grows upward, fix so that zero is bottom edge as in canvas_dosetbounds() */ t_float diff = x->gl_y1 - x->gl_y2; x->gl_y1 = (x->gl_screeny2 - x->gl_screeny1) * diff/x->gl_zoom; x->gl_y2 = x->gl_y1 - diff; } canvas_redraw(x); } } } /* function to support searching */ static int atoms_match(int inargc, t_atom *inargv, int searchargc, t_atom *searchargv, int wholeword) { int indexin, nmatched; for (indexin = 0; indexin <= inargc - searchargc; indexin++) { for (nmatched = 0; nmatched < searchargc; nmatched++) { t_atom *a1 = &inargv[indexin + nmatched], *a2 = &searchargv[nmatched]; if (a1->a_type == A_SEMI || a1->a_type == A_COMMA) { if (a2->a_type != a1->a_type) goto nomatch; } else if (a1->a_type == A_FLOAT || a1->a_type == A_DOLLAR) { if (a2->a_type != a1->a_type || a1->a_w.w_float != a2->a_w.w_float) goto nomatch; } else if (a1->a_type == A_SYMBOL || a1->a_type == A_DOLLSYM) { if ((a2->a_type != A_SYMBOL && a2->a_type != A_DOLLSYM) || (wholeword && a1->a_w.w_symbol != a2->a_w.w_symbol) || (!wholeword && !strstr(a1->a_w.w_symbol->s_name, a2->a_w.w_symbol->s_name))) goto nomatch; } } return (1); nomatch: ; } return (0); } extern int clone_get_n(t_gobj *x); extern t_glist *clone_get_instance(t_gobj *x, int n); /* find an atom or string of atoms */ static int canvas_dofind(t_canvas *x, int *myindexp) { t_gobj *y; int findargc = binbuf_getnatom(EDITOR->canvas_findbuf), didit = 0; t_atom *findargv = binbuf_getvec(EDITOR->canvas_findbuf); for (y = x->gl_list; y; y = y->g_next) { t_object *ob = 0; if ((ob = pd_checkobject(&y->g_pd))) { int n; if (atoms_match(binbuf_getnatom(ob->ob_binbuf), binbuf_getvec(ob->ob_binbuf), findargc, findargv, EDITOR->canvas_find_wholeword)) { if (*myindexp == EDITOR->canvas_find_index) { glist_noselect(x); vmess(&x->gl_pd, gensym("menu-open"), ""); canvas_editmode(x, 1.); glist_select(x, y); didit = 1; } (*myindexp)++; } if ((n = clone_get_n((t_gobj *)ob)) != 0) { int i = 0; /* should we search in every clone instance, or only the first one? */ /*for(i = 0; i < n; i++) {*/ didit |= canvas_dofind((t_canvas *)clone_get_instance((t_gobj *)ob, i), myindexp); /*}*/ } } } for (y = x->gl_list; y; y = y->g_next) if (pd_class(&y->g_pd) == canvas_class) didit |= canvas_dofind((t_canvas *)y, myindexp); return (didit); } static void canvas_find(t_canvas *x, t_symbol *s, t_floatarg wholeword) { int myindex = 0, found; t_symbol *decodedsym = sys_decodedialog(s); if (!EDITOR->canvas_findbuf) EDITOR->canvas_findbuf = binbuf_new(); binbuf_text(EDITOR->canvas_findbuf, decodedsym->s_name, strlen(decodedsym->s_name)); EDITOR->canvas_find_index = 0; EDITOR->canvas_find_wholeword = wholeword; canvas_whichfind = x; found = canvas_dofind(x, &myindex); if (found) EDITOR->canvas_find_index = 1; pdgui_vmess("pdtk_showfindresult", "^ iii", x, found, EDITOR->canvas_find_index, myindex); } static void canvas_find_again(t_canvas *x) { int myindex = 0, found; if (!EDITOR->canvas_findbuf || !canvas_whichfind) return; found = canvas_dofind(canvas_whichfind, &myindex); pdgui_vmess("pdtk_showfindresult", "^ iii", x, found, ++EDITOR->canvas_find_index, myindex); if (!found) EDITOR->canvas_find_index = 0; } static void canvas_find_parent(t_canvas *x) { if (x->gl_owner) canvas_vis(x->gl_owner, 1); } extern t_pd *message_get_responder(t_gobj *x); extern t_class *text_class; static int glist_dofinderror(t_glist *gl, const void *error_object) { t_gobj *g; int n; for (g = gl->gl_list; g; g = g->g_next) { if (((const void *)g == error_object) || (message_get_responder(g) == error_object)) { /* got it... now show it. */ glist_noselect(gl); canvas_vis((t_canvas *)gl, 1); canvas_editmode((t_canvas *)gl, 1.); glist_select(gl, g); if (pd_class(&g->g_pd) == text_class) { t_text* x = (t_text*)g; int argc = binbuf_getnatom(x->te_binbuf); t_atom*argv = binbuf_getvec(x->te_binbuf); if(argc>0 && A_SYMBOL == argv[0].a_type) { t_symbol*s = atom_getsymbol(argv); if (s && s->s_name && *s->s_name) pdgui_vmess("::deken::open_search_objects", "s", s->s_name); } } return (1); } else if (g->g_pd == canvas_class) { if (glist_dofinderror((t_canvas *)g, error_object)) return (1); } else if ((n = clone_get_n(g)) != 0) { int i; for(i = 0; i < n; i++) { if (glist_dofinderror(clone_get_instance(g, i), error_object)) return 1; } } } return (0); } void canvas_finderror(const void *error_object) { t_canvas *x; /* find all root canvases */ for (x = pd_getcanvaslist(); x; x = x->gl_next) { if (glist_dofinderror(x, error_object)) return; } pd_error(0, "... sorry, I couldn't find the source of that error."); } void canvas_stowconnections(t_canvas *x) { t_gobj *selhead = 0, *seltail = 0, *nonhead = 0, *nontail = 0, *y, *y2; t_linetraverser t; t_outconnect *oc; if (!x->gl_editor) return; /* split list to "selected" and "unselected" parts */ for (y = x->gl_list; y; y = y2) { y2 = y->g_next; if (glist_isselected(x, y)) { if (seltail) { seltail->g_next = y; seltail = y; y->g_next = 0; } else { selhead = seltail = y; seltail->g_next = 0; } } else { if (nontail) { nontail->g_next = y; nontail = y; y->g_next = 0; } else { nonhead = nontail = y; nontail->g_next = 0; } } } /* move the selected part to the end */ if (!nonhead) x->gl_list = selhead; else x->gl_list = nonhead, nontail->g_next = selhead; /* add connections to binbuf */ binbuf_clear(x->gl_editor->e_connectbuf); linetraverser_start(&t, x); while ((oc = linetraverser_next(&t))) { int s1 = glist_isselected(x, &t.tr_ob->ob_g); int s2 = glist_isselected(x, &t.tr_ob2->ob_g); if (s1 != s2) binbuf_addv(x->gl_editor->e_connectbuf, "ssiiii;", gensym("#X"), gensym("connect"), glist_getindex(x, &t.tr_ob->ob_g), t.tr_outno, glist_getindex(x, &t.tr_ob2->ob_g), t.tr_inno); } } void canvas_restoreconnections(t_canvas *x) { t_pd *boundx = s__X.s_thing; s__X.s_thing = &x->gl_pd; binbuf_eval(x->gl_editor->e_connectbuf, 0, 0, 0); s__X.s_thing = boundx; } static t_binbuf *canvas_docopy(t_canvas *x) { t_gobj *y; t_linetraverser t; t_outconnect *oc; t_binbuf *b = binbuf_new(); for (y = x->gl_list; y; y = y->g_next) { if (glist_isselected(x, y)) gobj_save(y, b); } linetraverser_start(&t, x); while ((oc = linetraverser_next(&t))) { if (glist_isselected(x, &t.tr_ob->ob_g) && glist_isselected(x, &t.tr_ob2->ob_g)) { binbuf_addv(b, "ssiiii;", gensym("#X"), gensym("connect"), glist_selectionindex(x, &t.tr_ob->ob_g, 1), t.tr_outno, glist_selectionindex(x, &t.tr_ob2->ob_g, 1), t.tr_inno); } } return (b); } static void canvas_copy(t_canvas *x) { if (!x->gl_editor) return; if (x->gl_editor->e_selection) { binbuf_free(EDITOR->copy_binbuf); EDITOR->copy_binbuf = canvas_docopy(x); } if (x->gl_editor->e_textedfor) { char *buf; int bufsize; rtext_getseltext(x->gl_editor->e_textedfor, &buf, &bufsize); pdgui_vmess("clipboard", "r", "clear"); pdgui_vmess("clipboard", "rp", "append", bufsize, buf); } } static void canvas_clearline(t_canvas *x) { if (x->gl_editor->e_selectedline) { canvas_disconnect_with_undo(x, x->gl_editor->e_selectline_index1, x->gl_editor->e_selectline_outno, x->gl_editor->e_selectline_index2, x->gl_editor->e_selectline_inno); x->gl_editor->e_selectedline = 0; canvas_dirty(x, 1); } } static void canvas_doclear(t_canvas *x) { t_gobj *y, *y2; int dspstate; dspstate = canvas_suspend_dsp(); if (x->gl_editor->e_selectedline) { canvas_disconnect_with_undo(x, x->gl_editor->e_selectline_index1, x->gl_editor->e_selectline_outno, x->gl_editor->e_selectline_index2, x->gl_editor->e_selectline_inno); x->gl_editor->e_selectedline=0; } /* if text is selected, deselecting it might remake the object. So we deselect it and hunt for a "new" object on the glist to reselect. */ if (x->gl_editor->e_textedfor) { t_gobj *selwas = x->gl_editor->e_selection->sel_what; pd_this->pd_newest = 0; glist_noselect(x); if (pd_this->pd_newest) { for (y = x->gl_list; y; y = y->g_next) if (&y->g_pd == pd_this->pd_newest) glist_select(x, y); } } while (1) /* this is pretty weird... should rewrite it */ { for (y = x->gl_list; y; y = y2) { y2 = y->g_next; if (glist_isselected(x, y)) { glist_delete(x, y); goto next; } } goto restore; next: ; } restore: canvas_resume_dsp(dspstate); canvas_dirty(x, 1); } static void canvas_cut(t_canvas *x) { if (!x->gl_editor) /* ignore if invisible */ return; if (x->gl_editor->e_selectedline) /* delete line */ canvas_clearline(x); else if (x->gl_editor->e_textedfor) /* delete selected text in a box */ { char *buf; int bufsize; rtext_getseltext(x->gl_editor->e_textedfor, &buf, &bufsize); if (!bufsize && x->gl_editor->e_selection && !x->gl_editor->e_selection->sel_next) { /* if the text is already empty, delete the box. We first clear 'textedfor' so that canvas_doclear later will think the whole box was selected, not the text */ x->gl_editor->e_textedfor = 0; goto deleteobj; } canvas_copy(x); rtext_key(x->gl_editor->e_textedfor, 127, &s_); canvas_dirty(x, 1); } else if (x->gl_editor->e_selection) { deleteobj: /* delete one or more objects */ canvas_undo_add(x, UNDO_CUT, "cut", canvas_undo_set_cut(x, UCUT_CUT)); canvas_copy(x); canvas_doclear(x); pdgui_vmess("pdtk_canvas_getscroll", "c", x); } } static void glist_donewloadbangs(t_glist *x) { if (x->gl_editor) { t_selection *sel; for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next) if (pd_class(&sel->sel_what->g_pd) == canvas_class) canvas_loadbang((t_canvas *)(&sel->sel_what->g_pd)); else if (zgetfn(&sel->sel_what->g_pd, gensym("loadbang"))) vmess(&sel->sel_what->g_pd, gensym("loadbang"), "f", LB_LOAD); } } static int binbuf_nextmess(int argc, const t_atom *argv) { int i=0; while(argc--) { argv++; i++; if (A_SEMI == argv->a_type) { return i+1; } } return i; } static int binbuf_getpos(t_binbuf*b, int *x0, int *y0, t_symbol**type) { /* * checks how many objects the binbuf contains and where they are located * for simplicity, we stop after the first object... * "objects" are any patchable things * returns: 0: no objects/... * 1: single object in binbuf * 2: more than one object in binbuf * (x0,y0) are the coordinates of the first object * (type) is the type of the first object ("obj", "msg",...) */ t_atom*argv = binbuf_getvec(b); int argc = binbuf_getnatom(b); const int argc0 = argc; int count = 0; t_symbol*s; /* get the position of the first object in the argv binbuf */ if(argc > 2 && atom_getsymbol(argv+0) == &s__N && atom_getsymbol(argv+1) == gensym("canvas")) { int ac = argc; t_atom*ap = argv; int stack = 0; do { int off = binbuf_nextmess(argc, argv); if(!off) break; ac = argc; ap = argv; argc-=off; argv+=off; count+=off; if(off >= 2) { if (atom_getsymbol(ap+1) == gensym("restore") && atom_getsymbol(ap) == &s__X) { stack--; } if (atom_getsymbol(ap+1) == gensym("canvas") && atom_getsymbol(ap) == &s__N) { stack++; } } if(argc<0) return 0; } while (stack>0); argc = ac; argv = ap; } if(argc < 4 || atom_getsymbol(argv) != &s__X) return 0; /* #X obj|msg|text|floatatom|symbolatom ... * TODO: subpatches #N canvas + #X restore */ s = atom_getsymbol(argv+1); if(gensym("restore") == s || gensym("obj") == s || gensym("msg") == s || gensym("text") == s || gensym("floatatom") == s || gensym("listbox") == s || gensym("symbolatom") == s) { if(x0)*x0=atom_getfloat(argv+2); if(y0)*y0=atom_getfloat(argv+3); if(type)*type=s; } else return 0; /* no wind the binbuf to the next message */ while(argc--) { count++; if (A_SEMI == argv->a_type) break; argv++; } return 1+(argc0 > count); } static void canvas_dopaste(t_canvas *x, t_binbuf *b) { t_gobj *g2; int dspstate = canvas_suspend_dsp(), nbox, count; t_symbol *asym = gensym("#A"); /* save and clear bindings to symbols #A, #N, #X; restore when done */ t_pd *boundx = s__X.s_thing, *bounda = asym->s_thing, *boundn = s__N.s_thing; asym->s_thing = 0; s__X.s_thing = &x->gl_pd; s__N.s_thing = &pd_canvasmaker; canvas_editmode(x, 1.); glist_noselect(x); for (g2 = x->gl_list, nbox = 0; g2; g2 = g2->g_next) nbox++; EDITOR->paste_onset = nbox; EDITOR->paste_canvas = x; binbuf_eval(b, 0, 0, 0); for (g2 = x->gl_list, count = 0; g2; g2 = g2->g_next, count++) if (count >= nbox) glist_select(x, g2); EDITOR->paste_canvas = 0; canvas_resume_dsp(dspstate); canvas_dirty(x, 1); if (x->gl_mapped) pdgui_vmess("pdtk_canvas_getscroll", "c", x); if (!sys_noloadbang) glist_donewloadbangs(x); asym->s_thing = bounda; s__X.s_thing = boundx; s__N.s_thing = boundn; } static t_symbol*get_object_type(t_object *obj) { t_symbol *s=0; t_binbuf *bb=0; if(!obj) return 0; switch(obj->te_type) { case T_OBJECT: return gensym("obj"); case T_MESSAGE: return gensym("msg"); case T_TEXT: return gensym("text"); default: /* detecting the type of a gatom by using it's save function */ bb=binbuf_new(); gobj_save(&obj->te_g, bb); binbuf_getpos(bb, 0, 0, &s); binbuf_free(bb); return s; } return 0; } static void canvas_paste_replace(t_canvas *x) { int x0=0, y0=0; t_symbol *typ0 = 0; if (!x->gl_editor) return; if(x->gl_editor->e_selection && 1==binbuf_getpos(EDITOR->copy_binbuf, &x0, &y0, &typ0)) { t_canvas *canvas = glist_getcanvas(x); t_selection *mysel = 0, *y; t_symbol *seltype = 0; /* check whether all the selected objects have the same type */ for (y = x->gl_editor->e_selection; y; y = y->sel_next) { t_symbol *s=get_object_type(pd_checkobject(&y->sel_what->g_pd)); if (!s) continue; if(seltype && seltype != s) { seltype = 0; break; } seltype = s; } /* we will do a lot of reslecting; so copy the selection */ for (y = x->gl_editor->e_selection; y; y = y->sel_next) { t_selection *sel = 0; t_object *obj=pd_checkobject(&y->sel_what->g_pd); if(!obj) continue; /* if the selection mixes obj, msg,... we only want to * replace the same type; * if the selection is homogeneous (seltype==NULL), we also allow typechanges */ if (!seltype && get_object_type(obj) != typ0) continue; sel = (t_selection *)getbytes(sizeof(*sel)); sel->sel_what = y->sel_what; sel->sel_next = mysel; mysel = sel; } canvas_undo_add(x, UNDO_SEQUENCE_START, "paste/replace", 0); for (y = mysel; y; y = y->sel_next) { t_object *o = (t_object *)(&y->sel_what->g_pd); int dx = o->te_xpix - x0; int dy = o->te_ypix - y0; glist_noselect(x); EDITOR->canvas_undo_already_set_move = 0; /* save connections and move object to the end */ /* note: the undo sequence selects the object as a side-effect */ canvas_undo_add(x, UNDO_ARRANGE, "arrange", canvas_undo_set_arrange(x, y->sel_what, 1)); canvas_stowconnections(canvas); /* recreate object */ /* remove the old object */ canvas_undo_add(x, UNDO_CUT, "clear", canvas_undo_set_cut(x, UCUT_CLEAR)); canvas_doclear(x); /* create the new object (and loadbang if needed) */ canvas_applybinbuf(x, EDITOR->copy_binbuf); glist_noselect(x); glist_select(x, glist_nth(x, glist_getindex(x, 0) - 1)); /* displace object (includes UNDO) */ canvas_displaceselection(x, dx, dy); /* restore connections */ canvas_restoreconnections(canvas); canvas_undo_add(x, UNDO_CREATE, "create", (void *)canvas_undo_set_create(x)); if (pd_this->pd_newest && pd_class(pd_this->pd_newest) == canvas_class) canvas_loadbang((t_canvas *)pd_this->pd_newest); } canvas_undo_add(x, UNDO_SEQUENCE_END, "paste/replace", 0); /* free the selection copy */ for (y = mysel; y; ) { t_selection*next = y->sel_next; freebytes(y, sizeof(*y)); y = next; } } } static void canvas_paste(t_canvas *x) { if (!x->gl_editor) return; if (x->gl_editor->e_textedfor) { /* simulate keystrokes as if the copy buffer were typed in. */ pdgui_vmess("pdtk_pastetext", "^", x); } else { int offset = 0; int x0 = 0, y0 = 0; int foundplace = 0; binbuf_getpos(EDITOR->copy_binbuf, &x0, &y0, 0); do { /* iterate over all existing objects * to see whether one occupies the space we want. * if so, move along */ t_gobj *y; foundplace = 1; for (y = x->gl_list; y; y = y->g_next) { t_text *txt = (t_text *)y; if((x0 + offset) == txt->te_xpix && (y0 + offset) == txt->te_ypix) { foundplace = 0; offset += PASTE_OFFSET; break; } } } while(!foundplace); canvas_undo_add(x, UNDO_PASTE, "paste", (void *)canvas_undo_set_paste(x, 0, 0, offset)); canvas_dopaste(x, EDITOR->copy_binbuf); if(offset) { t_selection *y; for (y = x->gl_editor->e_selection; y; y = y->sel_next) gobj_displace(y->sel_what, x, offset, offset); } } } static void canvas_duplicate(t_canvas *x) { if (!x->gl_editor) return; if (x->gl_editor->e_selection && x->gl_editor->e_selectedline) glist_deselectline(x); /* if a connection is selected, we extend it to the right (if possible) */ if (x->gl_editor->e_selectedline) { int outindex = x->gl_editor->e_selectline_index1; int inindex = x->gl_editor->e_selectline_index2; int outno = x->gl_editor->e_selectline_outno + 1; int inno = x->gl_editor->e_selectline_inno + 1; t_gobj *outgobj = 0, *ingobj = 0; t_object *outobj = 0, *inobj = 0; int whoout = outindex; int whoin = inindex; for (outgobj = x->gl_list; whoout; outgobj = outgobj->g_next, whoout--) if (!outgobj->g_next) return; for (ingobj = x->gl_list; whoin; ingobj = ingobj->g_next, whoin--) if (!ingobj->g_next) return; outobj = (t_object*)outgobj; inobj = (t_object*)ingobj; while(!canconnect(x, outobj, outno, inobj, inno)) { if (!outobj || obj_noutlets(outobj) <= outno) return; if (!inobj || obj_ninlets (inobj ) <= inno ) return; outno++; inno++; } if(tryconnect(x, outobj, outno, inobj, inno)) { x->gl_editor->e_selectline_outno = outno; x->gl_editor->e_selectline_inno = inno; } return; } if (x->gl_editor->e_onmotion == MA_NONE && x->gl_editor->e_selection) { t_selection *y; t_binbuf*b = 0; if(EDITOR->copy_binbuf) b = binbuf_duplicate(EDITOR->copy_binbuf); canvas_copy(x); canvas_undo_add(x, UNDO_PASTE, "duplicate", (void *)canvas_undo_set_paste(x, 0, 1, PASTE_OFFSET)); canvas_dopaste(x, EDITOR->copy_binbuf); for (y = x->gl_editor->e_selection; y; y = y->sel_next) gobj_displace(y->sel_what, x, PASTE_OFFSET, PASTE_OFFSET); if(b) { if(EDITOR->copy_binbuf) binbuf_free(EDITOR->copy_binbuf); EDITOR->copy_binbuf = b; } canvas_dirty(x, 1); } } static void canvas_selectall(t_canvas *x) { t_gobj *y; if (!x->gl_editor) return; if (!x->gl_edit) canvas_editmode(x, 1); /* if everyone is already selected deselect everyone */ if (!glist_selectionindex(x, 0, 0)) glist_noselect(x); else for (y = x->gl_list; y; y = y->g_next) { if (!glist_isselected(x, y)) glist_select(x, y); } } static void canvas_deselectall(t_canvas *x) { if(x)glist_noselect(x); } static void canvas_cycleselect(t_canvas*x, t_float foffset) { /* select (currentselection+offset)%objectcount */ int offset = (int)foffset; if (!x->gl_editor) return; if (x->gl_editor->e_onmotion == MA_CONNECT) { /* during connection, cycle through inlets/outlets */ int xwas = x->gl_editor->e_xwas, ywas = x->gl_editor->e_ywas; int xpos = EDITOR->canvas_last_glist_x, ypos = EDITOR->canvas_last_glist_y; t_object *src, *snk; t_gobj*gobj; int srcX1=0, srcY1=0, srcX2=0, srcY2=0; int snkX1=0, snkY1=0, snkX2=0, snkY2=0; if(EDITOR->canvas_last_glist != x) /* we don't know the current mouse coordinates in this canvas, so return... */ return; gobj = canvas_findhitbox(x, xwas, ywas, &srcX1, &srcY1, &srcX2, &srcY2); src = gobj?pd_checkobject(&gobj->g_pd):0; gobj = canvas_findhitbox(x, xpos, ypos, &snkX1, &snkY1, &snkX2, &snkY2); snk = gobj?pd_checkobject(&gobj->g_pd):0; if(!src) /* this should never happen */ return; /* are we hovering over an object? * - if so, cycle through inlets * - else, cycle through outlets */ if(snk && snk != src) { /* cycle inlets */ int width = snkX2 - snkX1, hotspot, closest; int nios = obj_ninlets(snk); if (nios <= 1) /* no use cycling */ return; closest = ((xpos-snkX1) * (nios-1) + width/2)/width; closest = ((closest + offset) % nios + nios) % nios; hotspot = snkX1 + (width - IOWIDTH) * closest / (nios-1.0) + IOWIDTH*0.5; pdgui_vmess("::pdtk_canvas::setmouse", "cii", glist_getcanvas(x), hotspot, ypos); } else { /* cycle outlets */ int width = srcX2 - srcX1, hotspot, closest; int nios = obj_noutlets(src); if (nios <= 1) /* no use cycling */ return; closest = ((xwas-srcX1) * (nios-1) + width/2)/width; closest = ((closest + offset) % nios + nios) % nios; hotspot = srcX1 + (width - IOWIDTH) * closest / (nios-1.0) + IOWIDTH*0.5; x->gl_editor->e_xwas = hotspot; canvas_doconnect(x, xpos, ypos, 0, 0); } return; } if (x->gl_editor->e_selection) { /* cycle the selection to the next object */ int newindex; int objectcount = glist_getindex(x, 0); /* only cycle selection if the current selection contains exactly 1 item */ t_gobj* y = x->gl_editor->e_selection->sel_next ? 0 : x->gl_editor->e_selection->sel_what; if (!y || !objectcount) return; newindex = (glist_getindex(x, y) + offset) % objectcount; if (newindex < 0) newindex += objectcount; glist_deselect(x, y); glist_select(x, glist_nth(x, newindex)); return; } if (x->gl_editor->e_selectedline) { /* if (only) a line is selected, cycle to next line */ int connectioncount = 0; int foundit = 0; t_linetraverser t; t_outconnect *oc = 0; linetraverser_start(&t, x); while (offset && (oc = linetraverser_next(&t))) { connectioncount++; if(!foundit) { int srcno = glist_getindex(x, &t.tr_ob->ob_g); int sinkno = glist_getindex(x, &t.tr_ob2->ob_g); if((srcno == x->gl_editor->e_selectline_index1) && (t.tr_outno == x->gl_editor->e_selectline_outno) && (sinkno == x->gl_editor->e_selectline_index2) && (t.tr_inno == x->gl_editor->e_selectline_inno)) { foundit = connectioncount; } } else offset--; } if (!connectioncount) offset = 0; /* if the offset is non-0, wrap it... */ if (offset) { offset = (((offset - 1) % connectioncount) + connectioncount) % connectioncount; /* ... and start from the beginning */ linetraverser_start(&t, x); while ((oc = linetraverser_next(&t))) { if(!offset)break; offset--; } } if (oc) glist_selectline(x, oc, glist_getindex(x, &t.tr_ob->ob_g), t.tr_outno, glist_getindex(x, &t.tr_ob2->ob_g), t.tr_inno); return; } } static void canvas_reselect(t_canvas *x) { t_gobj *g, *gwas; /* if someone is text editing, and if only one object is selected, deselect everyone and reselect. */ if (x->gl_editor->e_textedfor) { /* only do this if exactly one item is selected. */ if ((gwas = x->gl_editor->e_selection->sel_what) && !x->gl_editor->e_selection->sel_next) { int nobjwas = glist_getindex(x, 0), indx = canvas_getindex(x, x->gl_editor->e_selection->sel_what); glist_noselect(x); for (g = x->gl_list; g; g = g->g_next) if (g == gwas) { glist_select(x, g); return; } /* "gwas" must have disappeared; just search to the last object and select it */ for (g = x->gl_list; g; g = g->g_next) if (!g->g_next) glist_select(x, g); } } else if (x->gl_editor->e_selection && !x->gl_editor->e_selection->sel_next) /* otherwise activate first item in selection */ gobj_activate(x->gl_editor->e_selection->sel_what, x, 1); } void canvas_connect(t_canvas *x, t_floatarg fwhoout, t_floatarg foutno, t_floatarg fwhoin, t_floatarg finno) { int whoout = fwhoout, outno = foutno, whoin = fwhoin, inno = finno; t_gobj *src = 0, *sink = 0; t_object *objsrc, *objsink; t_outconnect *oc; int nin = whoin, nout = whoout; if (EDITOR->paste_canvas == x) whoout += EDITOR->paste_onset, whoin += EDITOR->paste_onset; for (src = x->gl_list; whoout; src = src->g_next, whoout--) if (!src->g_next) { src = NULL; logpost(sink, 3, "cannot connect non-existing object"); goto bad; /* bug fix thanks to Hannes */ } for (sink = x->gl_list; whoin; sink = sink->g_next, whoin--) if (!sink->g_next) { sink = NULL; logpost(src, 3, "cannot connect to non-existing object"); goto bad; } /* check they're both patchable objects */ if (!(objsrc = pd_checkobject(&src->g_pd)) || !(objsink = pd_checkobject(&sink->g_pd))) { logpost(src?src:sink, 3, "cannot connect unpatchable object"); goto bad; } /* check if objects are already connected */ if (canvas_isconnected(x, objsrc, outno, objsink, inno)) { logpost(src, 3, "io pair already connected"); goto bad; } /* if object creation failed, make dummy inlets or outlets as needed */ if (pd_class(&src->g_pd) == text_class && objsrc->te_type == T_OBJECT) while (outno >= obj_noutlets(objsrc)) outlet_new(objsrc, 0); if (pd_class(&sink->g_pd) == text_class && objsink->te_type == T_OBJECT) while (inno >= obj_ninlets(objsink)) inlet_new(objsink, &objsink->ob_pd, 0, 0); if (!(oc = obj_connect(objsrc, outno, objsink, inno))) goto bad; if (glist_isvisible(x) && x->gl_havewindow) { char tag[128]; char*tags[] = {tag, "cord"}; sprintf(tag, "l%p", oc); pdgui_vmess(0, "crr iiii ri rS", glist_getcanvas(x), "create", "line", 0, 0, 0, 0, "-width", (obj_issignaloutlet(objsrc, outno) ? 2 : 1) * x->gl_zoom, "-tags", 2, tags); canvas_fixlinesfor(x, objsrc); } return; bad: post("%s %d %d %d %d (%s->%s) connection failed", x->gl_name->s_name, nout, outno, nin, inno, (src? class_getname(pd_class(&src->g_pd)) : "???"), (sink? class_getname(pd_class(&sink->g_pd)) : "???")); } #define XTOLERANCE 18 #define YTOLERANCE 17 #define NHIST 35 /* LATER might have to speed this up */ static void canvas_tidy(t_canvas *x) { t_gobj *y, *y2; int ax1, ay1, ax2, ay2, bx1, by1, bx2, by2; int histogram[NHIST], *ip, i, besthist, bestdist; /* if nobody is selected, this means do it to all boxes; otherwise just the selection */ int all = (x->gl_editor ? (x->gl_editor->e_selection == 0) : 1); canvas_undo_add(x, UNDO_MOTION, "{tidy up}", canvas_undo_set_move(x, !all)); /* tidy horizontally */ for (y = x->gl_list; y; y = y->g_next) if (all || glist_isselected(x, y)) { gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2); for (y2 = x->gl_list; y2; y2 = y2->g_next) if (all || glist_isselected(x, y2)) { gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2); if (by1 <= ay1 + YTOLERANCE && by1 >= ay1 - YTOLERANCE && bx1 < ax1) goto nothorizhead; } for (y2 = x->gl_list; y2; y2 = y2->g_next) if (all || glist_isselected(x, y2)) { gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2); if (by1 <= ay1 + YTOLERANCE && by1 >= ay1 - YTOLERANCE && by1 != ay1) gobj_displace(y2, x, 0, ay1-by1); } nothorizhead: ; } /* tidy vertically. First guess the user's favorite vertical spacing */ for (i = NHIST, ip = histogram; i--; ip++) *ip = 0; for (y = x->gl_list; y; y = y->g_next) if (all || glist_isselected(x, y)) { gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2); for (y2 = x->gl_list; y2; y2 = y2->g_next) if (all || glist_isselected(x, y2)) { gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2); if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE) { int distance = by1-ay2; if (distance >= 0 && distance < NHIST) histogram[distance]++; } } } for (i = 2, besthist = 0, bestdist = 4, ip = histogram + 2; i < (NHIST-2); i++, ip++) { int hit = ip[-2] + 2 * ip[-1] + 3 * ip[0] + 2* ip[1] + ip[2]; if (hit > besthist) { besthist = hit; bestdist = i; } } logpost(NULL, 3, "tidy: best vertical distance %d", bestdist); for (y = x->gl_list; y; y = y->g_next) if (all || glist_isselected(x, y)) { int keep = 1; gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2); for (y2 = x->gl_list; y2; y2 = y2->g_next) if (all || glist_isselected(x, y2)) { gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2); if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE && ay1 >= by2 - 10 && ay1 < by2 + NHIST) goto nothead; } while (keep) { keep = 0; for (y2 = x->gl_list; y2; y2 = y2->g_next) if (all || glist_isselected(x, y2)) { gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2); if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE && by1 > ay1 && by1 < ay2 + NHIST) { int vmove = ay2 + bestdist - by1; gobj_displace(y2, x, ax1-bx1, vmove); ay1 = by1 + vmove; ay2 = by2 + vmove; keep = 1; break; } } } nothead: ; } canvas_dirty(x, 1); } /* returns the total number of connections between two objects * outno/inno are set to the last connection indices */ static int canvas_getconns(t_object*objsrc, int *outno, t_object*objsink, int *inno) { int count = 0; int n; for(n=0; nobj1->obj2' becomes * 'obj0->obj2' (+ 'obj1'); only bypass if there's exactly one * connection between both obj0->obj1 and obj1->obj2 and the two * connections are of the same type * skip connection, if it's already there */ /* this is doing an awful lot of iterating over the same things again and again * LATER speed this up */ int A, B, C; /* check connections (obj0->obj1->obj2, but not obj2->obj0) */ /* valid: out0, in1, out1, in2 invalid: in0, out2 */ if(out0<0 || out1<0 || out2>=0 || in0>=0 || in1<0 || in2<0) return 0; /* check whether the connection types match */ if(obj_issignaloutlet(obj0, out0) ^ obj_issignaloutlet(obj1, out1)) return 0; A = glist_getindex(x, &obj0->te_g); B = glist_getindex(x, &obj1->te_g); C = glist_getindex(x, &obj2->te_g); canvas_disconnect_with_undo(x, A, out0, B, in1); canvas_disconnect_with_undo(x, B, out1, C, in2); if (!canvas_isconnected(x, obj0, out0, obj2, in2)) canvas_connect_with_undo(x, A, out0, C, in2); return 1; } static int canvas_try_insert(t_canvas *x , t_object* obj00, int in00, int out00 /* source */ , t_object* obj11, int in11, int out11 /* sink */ , t_object* obj22, int in22, int out22 /* insert */ ) { int out21 = 0, in02 = 0; /* iolets of the insert-objects */ int A, B, C; /* check connections (obj00->obj11, but not obj22) */ if(out00<0 || out22>=0 || out11>=0 || in00>=0 || in22>=0 || in11<0) return 0; /* check whether the connection types match */ if((obj_issignaloutlet(obj00, out00) && !obj_issignalinlet(obj22, in02))) return 0; if((obj_issignaloutlet(obj22, out21) && !obj_issignalinlet(obj11, in11))) return 0; /* then connect them */ A = glist_getindex(x, &obj00->te_g); B = glist_getindex(x, &obj11->te_g); C = glist_getindex(x, &obj22->te_g); canvas_disconnect_with_undo(x, A, out00, B, in11); if (!canvas_isconnected (x, obj00, out00, obj22, in02)) canvas_connect_with_undo(x, A, out00, C, in02); if (!canvas_isconnected (x, obj22, out21, obj11, in11)) canvas_connect_with_undo(x, C, out21, B, in11); return 1; } /* If we have two selected objects on the canvas, try to connect the first outlet of the upper object to the first inlet with a compatible type in the lower one. */ static void canvas_connect_selection(t_canvas *x) { t_gobj *a, *b, *c; t_selection *sel; t_object *objsrc, *objsink; a = b = c = NULL; sel = x->gl_editor ? x->gl_editor->e_selection : NULL; for (; sel; sel = sel->sel_next) { if (!a) a = sel->sel_what; else if (!b) b = sel->sel_what; else if (!c) c = sel->sel_what; else return; } if(!a) return; if(!b) { /* only a single object is selected. * if a connection is selected, insert the object * if no connection is selected, disconnect the object */ t_object*obj = pd_checkobject(&a->g_pd); if(!obj) return; if(x->gl_editor->e_selectedline) { b = glist_nth(x, x->gl_editor->e_selectline_index1); objsrc = b?pd_checkobject(&b->g_pd):0; b = glist_nth(x, x->gl_editor->e_selectline_index2); objsink = b?pd_checkobject(&b->g_pd):0; if(canconnect(x, objsrc, x->gl_editor->e_selectline_outno, obj, 0) && canconnect(x, obj, 0, objsink, x->gl_editor->e_selectline_inno)) { canvas_undo_add(x, UNDO_SEQUENCE_START, "reconnect", 0); tryconnect(x, objsrc, x->gl_editor->e_selectline_outno, obj, 0); tryconnect(x, obj, 0, objsink, x->gl_editor->e_selectline_inno); canvas_clearline(x); canvas_undo_add(x, UNDO_SEQUENCE_END, "reconnect", 0); } } else { /* disconnect the entire object */ t_linetraverser t; t_outconnect *oc; canvas_undo_add(x, UNDO_SEQUENCE_START, "disconnect", 0); linetraverser_start(&t, x); while ((oc = linetraverser_next(&t))) { if ((obj == t.tr_ob) || (obj == t.tr_ob2)) { int srcno = glist_getindex(x, &t.tr_ob->ob_g); int sinkno = glist_getindex(x, &t.tr_ob2->ob_g); canvas_disconnect_with_undo(x, srcno, t.tr_outno, sinkno, t.tr_inno); } } canvas_undo_add(x, UNDO_SEQUENCE_END, "disconnect", 0); } /* need to return since we have touched 'b' */ return; } if(!c) { /* exactly two objects are selected * connect them (top to bottom) if they are patchable */ if (!(objsrc = pd_checkobject(&a->g_pd)) || !(objsink = pd_checkobject(&b->g_pd))) return; if(objsink->te_ypix < objsrc->te_ypix) { t_object*obj = objsink; objsink = objsrc; objsrc = obj; } if (!objsrc || !objsink) return; if (obj_noutlets(objsrc)) { int noutlets = obj_noutlets(objsrc); int ninlets = obj_ninlets(objsink); int fanout = (noutlets == 1) && obj_issignaloutlet(objsrc, 0); int out = 0, in = 0; while(!tryconnect(x, objsrc, out, objsink, in)) { if (noutlets <= out) return; if (ninlets <= in ) return; in++; if(!fanout) out++; } } return; } /* exactly three objects are selected * if they are chained up, unconnect the middle object, and connect the source to the sink * if only two of them are connected, insert the third */ if ((objsrc = pd_checkobject(&a->g_pd)) && (objsink = pd_checkobject(&b->g_pd))) { t_object *obj0 = objsrc, *obj2 = objsink; t_object *obj1 = pd_checkobject(&c->g_pd); int out01, out02, out10, out12, out20, out21; int in01, in02, in10, in12, in20, in21; if(!obj1 || (obj0 == obj1) || (obj2 == obj1) || (obj0 == obj2)) return; #define GET1CONN(a, b) \ if (1 != canvas_getconns(obj##a, &out##a##b, obj##b, &in##b##a)) \ out##a##b = in##b##a = -1 GET1CONN(0, 1); GET1CONN(0, 2); GET1CONN(1, 0); GET1CONN(1, 2); GET1CONN(2, 0); GET1CONN(2, 1); #define TRYCONNCHANGE(fun, a, b, c) \ 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) canvas_undo_add(x, UNDO_SEQUENCE_START, "reconnect", 0); 0 || TRYCONNCHANGE(bypassobj1, 0, 1, 2) || TRYCONNCHANGE(bypassobj1, 0, 2, 1) || TRYCONNCHANGE(bypassobj1, 1, 0, 2) || TRYCONNCHANGE(bypassobj1, 1, 2, 0) || TRYCONNCHANGE(bypassobj1, 2, 0, 1) || TRYCONNCHANGE(bypassobj1, 2, 1, 0) || TRYCONNCHANGE(insert, 0, 1, 2) || TRYCONNCHANGE(insert, 0, 2, 1) || TRYCONNCHANGE(insert, 1, 0, 2) || TRYCONNCHANGE(insert, 1, 2, 0) || TRYCONNCHANGE(insert, 2, 0, 1) || TRYCONNCHANGE(insert, 2, 1, 0) ; canvas_undo_add(x, UNDO_SEQUENCE_END, "reconnect", 0); } } static void canvas_texteditor(t_canvas *x) { t_rtext *foo; char *buf; int bufsize; if ((foo = x->gl_editor->e_textedfor)) rtext_gettext(foo, &buf, &bufsize); else buf = "", bufsize = 0; pdgui_vmess("pdtk_pd_texteditor", "p", bufsize, buf); } void glob_key(void *dummy, t_symbol *s, int ac, t_atom *av) { /* canvas_key checks for zero */ canvas_key(0, s, ac, av); } void canvas_editmode(t_canvas *x, t_floatarg state) { if (x->gl_edit == (unsigned int) state) return; x->gl_edit = (unsigned int) state; if (x->gl_edit && glist_isvisible(x) && glist_istoplevel(x)) { t_gobj *g; t_object *ob; canvas_setcursor(x, CURSOR_EDITMODE_NOTHING); for (g = x->gl_list; g; g = g->g_next) if ((ob = pd_checkobject(&g->g_pd)) && ob->te_type == T_TEXT) { t_rtext *y = glist_findrtext(x, ob); text_drawborder(ob, x, rtext_gettag(y), rtext_width(y), rtext_height(y), 1); } } else { glist_noselect(x); /* this can knock us back into edit mode so : */ x->gl_edit = (unsigned int) state; if (glist_isvisible(x) && glist_istoplevel(x)) { canvas_setcursor(x, CURSOR_RUNMODE_NOTHING); pdgui_vmess(0, "crs", glist_getcanvas(x), "delete", "commentbar"); } } if (glist_isvisible(x) && x->gl_havewindow) { pdgui_vmess("pdtk_canvas_editmode", "^i", glist_getcanvas(x), x->gl_edit); canvas_reflecttitle(x); } } /* called by canvas_font below */ static void canvas_dofont(t_canvas *x, t_floatarg font, t_floatarg xresize, t_floatarg yresize) { t_gobj *y; x->gl_font = font; if (xresize != 1 || yresize != 1) { canvas_setundo(x, canvas_undo_move, canvas_undo_set_move(x, 0), "motion"); for (y = x->gl_list; y; y = y->g_next) { int x1, x2, y1, y2, nx1, ny1; gobj_getrect(y, x, &x1, &y1, &x2, &y2); nx1 = x1 * xresize + 0.5; ny1 = y1 * yresize + 0.5; gobj_displace(y, x, nx1-x1, ny1-y1); } } for (y = x->gl_list; y; y = y->g_next) if (pd_checkglist(&y->g_pd) && !canvas_isabstraction((t_canvas *)y)) canvas_dofont((t_canvas *)y, font, xresize, yresize); if(x->gl_havewindow) canvas_redraw(x); } /* canvas_menufont calls up a TK dialog which calls this back */ static void canvas_font(t_canvas *x, t_floatarg font, t_floatarg resize, t_floatarg whichresize) { t_float realresize, realresx = 1, realresy = 1; t_canvas *x2 = canvas_getrootfor(x); int oldfont = x2->gl_font; if (!resize) realresize = 1; else { if (resize < 20) resize = 20; if (resize > 500) resize = 500; realresize = resize * 0.01; } if (whichresize != 3) realresx = realresize; if (whichresize != 2) realresy = realresize; canvas_dofont(x2, font, realresx, realresy); canvas_undo_add(x2, UNDO_FONT, "font", canvas_undo_set_font(x2, oldfont, realresize, whichresize)); sys_defaultfont = font; } void glist_getnextxy(t_glist *gl, int *xpix, int *ypix) { if (EDITOR->canvas_last_glist == gl) *xpix = EDITOR->canvas_last_glist_x, *ypix = EDITOR->canvas_last_glist_y; else *xpix = *ypix = 40; } static void glist_setlastxy(t_glist *gl, int xval, int yval) { EDITOR->canvas_last_glist = gl; EDITOR->canvas_last_glist_x = xval; EDITOR->canvas_last_glist_y = yval; } void canvas_triggerize(t_glist*cnv); void g_editor_setup(void) { /* ------------------------ events ---------------------------------- */ class_addmethod(canvas_class, (t_method)canvas_mouse, gensym("mouse"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); class_addmethod(canvas_class, (t_method)canvas_mouseup, gensym("mouseup"), A_FLOAT, A_FLOAT, A_FLOAT, A_DEFFLOAT, A_NULL); class_addmethod(canvas_class, (t_method)canvas_key, gensym("key"), A_GIMME, A_NULL); class_addmethod(canvas_class, (t_method)canvas_motion, gensym("motion"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); /* ------------------------ menu actions ---------------------------- */ class_addmethod(canvas_class, (t_method)canvas_menuclose, gensym("menuclose"), A_DEFFLOAT, 0); class_addmethod(canvas_class, (t_method)canvas_cut, gensym("cut"), A_NULL); class_addmethod(canvas_class, (t_method)canvas_copy, gensym("copy"), A_NULL); class_addmethod(canvas_class, (t_method)canvas_paste, gensym("paste"), A_NULL); class_addmethod(canvas_class, (t_method)canvas_paste_replace, gensym("paste-replace"), A_NULL); class_addmethod(canvas_class, (t_method)canvas_duplicate, gensym("duplicate"), A_NULL); class_addmethod(canvas_class, (t_method)canvas_selectall, gensym("selectall"), A_NULL); class_addmethod(canvas_class, (t_method)canvas_deselectall, gensym("deselectall"), A_NULL); class_addmethod(canvas_class, (t_method)canvas_reselect, gensym("reselect"), A_NULL); class_addmethod(canvas_class, (t_method)canvas_cycleselect, gensym("cycleselect"), A_FLOAT, A_NULL); class_addmethod(canvas_class, (t_method)canvas_undo_undo, gensym("undo"), A_NULL); class_addmethod(canvas_class, (t_method)canvas_undo_redo, gensym("redo"), A_NULL); class_addmethod(canvas_class, (t_method)canvas_tidy, gensym("tidy"), A_NULL); class_addmethod(canvas_class, (t_method)canvas_connect_selection, gensym("connect_selection"), A_NULL); class_addmethod(canvas_class, (t_method)canvas_texteditor, gensym("texteditor"), A_NULL); class_addmethod(canvas_class, (t_method)canvas_editmode, gensym("editmode"), A_DEFFLOAT, A_NULL); class_addmethod(canvas_class, (t_method)canvas_print, gensym("print"), A_SYMBOL, A_NULL); class_addmethod(canvas_class, (t_method)canvas_menufont, gensym("menufont"), A_NULL); class_addmethod(canvas_class, (t_method)canvas_font, gensym("font"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); class_addmethod(canvas_class, (t_method)canvas_zoom, gensym("zoom"), A_FLOAT, A_NULL); class_addmethod(canvas_class, (t_method)canvas_find, gensym("find"), A_SYMBOL, A_FLOAT, A_NULL); class_addmethod(canvas_class, (t_method)canvas_find_again, gensym("findagain"), A_NULL); class_addmethod(canvas_class, (t_method)canvas_find_parent, gensym("findparent"), A_NULL); class_addmethod(canvas_class, (t_method)canvas_done_popup, gensym("done-popup"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); class_addmethod(canvas_class, (t_method)canvas_donecanvasdialog, gensym("donecanvasdialog"), A_GIMME, A_NULL); class_addmethod(canvas_class, (t_method)glist_arraydialog, gensym("arraydialog"), A_SYMBOL, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); class_addmethod(canvas_class, (t_method)canvas_triggerize, gensym("triggerize"), 0); class_addmethod(canvas_class, (t_method)canvas_disconnect, gensym("disconnect"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); } void canvas_editor_for_class(t_class *c) { class_addmethod(c, (t_method)canvas_mouse, gensym("mouse"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); class_addmethod(c, (t_method)canvas_mouseup, gensym("mouseup"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); class_addmethod(c, (t_method)canvas_key, gensym("key"), A_GIMME, A_NULL); class_addmethod(c, (t_method)canvas_motion, gensym("motion"), A_FLOAT, A_FLOAT, A_FLOAT, A_DEFFLOAT, A_NULL); /* ------------------------ menu actions ---------------------------- */ class_addmethod(c, (t_method)canvas_menuclose, gensym("menuclose"), A_DEFFLOAT, 0); class_addmethod(c, (t_method)canvas_find_parent, gensym("findparent"), A_NULL); } void g_editor_newpdinstance(void) { EDITOR = getbytes(sizeof(*EDITOR)); /* other stuff is null-checked but this needs to exist: */ EDITOR->copy_binbuf = binbuf_new(); } void g_editor_freepdinstance(void) { if (EDITOR->copy_binbuf) binbuf_free(EDITOR->copy_binbuf); if (EDITOR->canvas_undo_buf) { if (!EDITOR->canvas_undo_fn) bug("g_editor_freepdinstance"); else (*EDITOR->canvas_undo_fn) (EDITOR->canvas_undo_canvas, EDITOR->canvas_undo_buf, UNDO_FREE); } if (EDITOR->canvas_findbuf) binbuf_free(EDITOR->canvas_findbuf); freebytes(EDITOR, sizeof(*EDITOR)); } ================================================ FILE: libs/libpd/pure-data/src/g_editor_extras.c ================================================ /* Copyright (c) 2018 Miller Puckette, IOhannes m zmölnig and others. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ #include "m_pd.h" #include "g_canvas.h" #include "m_imp.h" #include "g_undo.h" void *canvas_undo_set_pastebinbuf(t_canvas *x, t_binbuf *b, int numpasted, int duplicate, int d_offset); /* ------------ utilities ---------- */ typedef struct _triggerize_return { /* data to return from triggerize */ t_gobj*tr_editgobj; /* if set, immediately switch this object to being edited */ } t_triggerize_return; static t_gobj*o2g(t_object*obj) { return &(obj->te_g); } static t_object*g2o(t_gobj*gobj) { return pd_checkobject(&gobj->g_pd); } static t_gobj*glist_getlast(t_glist*cnv) { /* get last object on */ t_gobj*result=NULL; for(result=cnv->gl_list; result->g_next;) result=result->g_next; return result; } static void dereconnect(t_glist*cnv, t_object*org, t_object*replace) { t_gobj*gobj; int replace_i = canvas_getindex(cnv, o2g(replace)); for(gobj=cnv->gl_list; gobj; gobj=gobj->g_next) { t_object*obj=g2o(gobj); int obj_i = canvas_getindex(cnv, gobj); int obj_nout=0; int nout; if(!obj)continue; obj_nout=obj_noutlets(obj); for(nout=0; noutgl_pd; s__N.s_thing = &pd_canvasmaker; binbuf_eval(b, 0, 0, 0); s__X.s_thing = boundx; s__N.s_thing = boundn; return g2o(glist_getlast(x)); } static void stack_conn(t_glist*x, t_object*new, int*newoutlet, t_object*org, int orgoutlet, t_outconnect*conn) { t_object*dest=0; t_inlet *in =0; int which; int new_i, org_i, dest_i; if(!conn) return; conn=obj_nexttraverseoutlet(conn, &dest, &in, &which); stack_conn(x, new, newoutlet, org, orgoutlet, conn); new_i = canvas_getindex(x, o2g(new)); org_i = canvas_getindex(x, o2g(org)); dest_i = canvas_getindex(x, o2g(dest)); obj_disconnect(org, orgoutlet, dest, which); canvas_undo_add(x, UNDO_DISCONNECT, "disconnect", canvas_undo_set_disconnect(x, org_i, orgoutlet, dest_i, which)); obj_connect(new, *newoutlet, dest, which); canvas_undo_add(x, UNDO_CONNECT, "connect", canvas_undo_set_connect(x, new_i, *newoutlet, dest_i, which)); (*newoutlet)++; } static int has_fanout(t_object*obj) { /* check if we actually do have a fan out */ int obj_nout=obj_noutlets(obj); int nout; for(nout=0; nout are triggers */ const t_symbol*s_trigger=gensym("trigger"); t_gobj*gobj = NULL; for(gobj=cnv->gl_list; gobj; gobj=gobj->g_next) { t_object*obj=g2o(gobj); if(obj && glist_isselected(cnv, gobj) && (s_trigger != obj->te_g.g_pd->c_name)) { return 0; } } return 1; } /* ------------------------- triggerize ---------------------------- */ static int triggerize_fanout_inplace(t_glist*x, t_object*obj) { /* avoid fanouts in [t] objects by adding additional outlets */ int posX=obj->te_xpix; int posY=obj->te_ypix; t_atom*argv=binbuf_getvec(obj->te_binbuf); int obj_nout=obj_noutlets(obj); int nout, newout; t_binbuf*b=0; t_object*stub=0; /* check if we actually do have a fan out */ if(!has_fanout(obj))return 0; /* create a new trigger object, that has outlets for the fans */ b=binbuf_new(); binbuf_addv(b, "ssii", gensym("#X"), gensym("obj"), posX, posY); binbuf_add(b, 1, argv); argv++; for(nout=0; noutgl_zoom; posY += yoffset; /* if the object is a [trigger], we just insert new outlets */ if(s_trigger == obj->te_g.g_pd->c_name) { return triggerize_fanout_inplace(x, obj); } /* for other objects, we create a new [trigger a a] object * and replace the fan-out with that */ for(nout=0; nout1) { int i, obj_i, stub_i; t_object *stub; /* fan out: create a [t] object to resolve it */ /* need to get the coordinates of the fanning outlet */ t_linetraverser t; t_binbuf*b=binbuf_new(); linetraverser_start(&t, x); while((conn = linetraverser_next(&t))) { if((obj == t.tr_ob) && nout == t.tr_outno) { posX = (t.tr_lx1 / t.tr_x->gl_zoom) - (IOMIDDLE - xoffset); break; } } obj_i = canvas_getindex(x, o2g(obj)); stub=0; binbuf_clear(b); binbuf_addv(b, "ssiis", gensym("#X"), gensym("obj"), posX, posY, gensym("t")); for(i=0; igl_list; gobj; ) { t_gobj*next=gobj->g_next; t_object*obj=g2o(gobj); if(obj && glist_isselected(cnv, gobj) && triggerize_fanout(cnv, obj)) count++; gobj = next; } canvas_undo_add(cnv, UNDO_SEQUENCE_END, "triggerize", 0); return count; } static int triggerize_line(t_glist*x, t_triggerize_return*tr) { /* triggerize a single selected line, by inserting a [t a] object * (or it's signal equivalent) */ t_editor*ed=x->gl_editor; int src_obj, src_out, dst_obj, dst_in, new_obj; t_gobj *src = 0, *dst = 0; t_binbuf*b=0; int posx=100, posy=100; t_object*stub=0; int sigline = 0; int dspstate = 0; if(!ed->e_selectedline) return 0; src_obj=ed->e_selectline_index1; src_out=ed->e_selectline_outno; dst_obj=ed->e_selectline_index2; dst_in =ed->e_selectline_inno; for (src = x->gl_list; src_obj; src = src->g_next, src_obj--) if (!src->g_next) goto bad; for (dst = x->gl_list; dst_obj; dst = dst->g_next, dst_obj--) if (!dst->g_next) goto bad; src_obj=ed->e_selectline_index1; dst_obj=ed->e_selectline_index2; if(1) { t_object*obj1=g2o(src); t_object*obj2=g2o(dst); if(obj1 && obj2) { float posSource, posSink; int nio; int _x; /* dummy variable */ int posSourceY, posSinkY; int boxHeight; /* height of inserted box */ int posLeft, posRight; /* get real x-position of the outlet */ gobj_getrect(src, x, &posLeft, &_x, &posRight, &posSourceY); posLeft /= x->gl_zoom; posRight /= x->gl_zoom; posSourceY /= x->gl_zoom; nio = obj_noutlets(obj1); posSource = posLeft + (posRight - posLeft - IOWIDTH) * src_out / ((nio==1)?1.:(nio-1.)); /* get real x-position of the inlet */ gobj_getrect(dst, x, &posLeft, &posSinkY, &posRight, &_x); posLeft /= x->gl_zoom; posRight /= x->gl_zoom; posSinkY /= x->gl_zoom; nio = obj_ninlets(obj2); posSink = posLeft + (posRight - posLeft - IOWIDTH) * dst_in / ((nio==1)?1.:(nio-1.)); /* get height of the box that will be inserted */ boxHeight = glist_fontheight(x) / x->gl_zoom + 4; /* ATOM_BMARGIN = 4 */ posx = (posSource + posSink) * 0.5; posy = (posSourceY + posSinkY - boxHeight) >> 1; } } sigline = obj_issignaloutlet(g2o(src), src_out); if(sigline) dspstate = canvas_suspend_dsp(); canvas_undo_add(x, UNDO_SEQUENCE_START, "{insert object}", 0); b=binbuf_new(); if(sigline) { binbuf_addv(b, "ssiiiisi;", gensym("#N"), gensym("canvas"), 200, 100, 190, 200, gensym("nop~"), 0); binbuf_addv(b, "ssiis;", gensym("#X"), gensym("obj"), 50, 70, gensym("inlet~")); binbuf_addv(b, "ssiis;", gensym("#X"), gensym("obj"), 50,140, gensym("outlet~")); binbuf_addv(b, "ssiiii;", gensym("#X"), gensym("connect"), 0,0,1,0); binbuf_addv(b, "ssiiss", gensym("#X"), gensym("restore"), posx, posy, gensym("pd"), gensym("nop~")); } else { binbuf_addv(b,"ssii", gensym("#X"), gensym("obj"), posx, posy); binbuf_addv(b,"ss", gensym("t"), gensym("a")); } binbuf_addsemi(b); canvas_undo_add(x, UNDO_PASTE, "paste", canvas_undo_set_pastebinbuf(x, b, 0, 0, 0)); stub=triggerize_createobj(x, b); new_obj = canvas_getindex(x, &stub->ob_g); binbuf_free(b);b=0; obj_disconnect(g2o(src), src_out, g2o(dst), dst_in); canvas_undo_add(x, UNDO_DISCONNECT, "disconnect", canvas_undo_set_disconnect(x, src_obj, src_out, dst_obj, dst_in)); obj_connect(g2o(src), src_out, stub, 0); canvas_undo_add(x, UNDO_CONNECT, "connect", canvas_undo_set_connect(x, src_obj, src_out, new_obj, 0)); obj_connect(stub, 0, g2o(dst), dst_in); canvas_undo_add(x, UNDO_CONNECT, "connect", canvas_undo_set_connect(x, new_obj, 0, dst_obj, dst_in)); glist_select(x, o2g(stub)); canvas_undo_add(x, UNDO_SEQUENCE_END, "{insert object}", 0); /* remember the inserted object, so we can select/edit it later */ if(tr) tr->tr_editgobj = o2g(stub); if(sigline) canvas_resume_dsp(dspstate); return 1; bad: return 0; } static int minimize_trigger(t_glist*cnv, t_object*obj) { /* remove all unused outlets from [trigger] */ t_binbuf*b=binbuf_new(); t_atom*argv=binbuf_getvec(obj->te_binbuf); t_object*stub=0; int obj_nout=obj_noutlets(obj); int nout, stub_i, obj_i = canvas_getindex(cnv, o2g(obj)); int count = 0; binbuf_addv(b, "ssii", gensym("#X"), gensym("obj"), obj->te_xpix, obj->te_ypix); binbuf_add(b, 1, argv); /* go through all the outlets, and add those that have connections */ for(nout = 0; nout < obj_nout; nout++) { t_outlet*out=0; t_outconnect*conn = obj_starttraverseoutlet(obj, &out, nout); if(conn) { binbuf_add(b, 1, argv + 1 + nout); } else { count++; } } if(!count || count == obj_nout) { /* either no outlet to delete or all: skip this object */ binbuf_free(b); return 0; } /* create the replacement object (which only has outlets that * are going to be connected) */ canvas_undo_add(cnv, UNDO_PASTE, "paste", canvas_undo_set_pastebinbuf(cnv, b, 0, 0, 0)); stub=triggerize_createobj(cnv, b); stub_i = canvas_getindex(cnv, o2g(stub)); /* no go through the original object's outlets, and duplicate the * connection of each to the new object */ for(nout=0, count=0; noutte_binbuf); t_atom*argv=binbuf_getvec(obj->te_binbuf); t_object*stub=0; int obj_nout=obj_noutlets(obj); int stub_i, obj_i = canvas_getindex(cnv, o2g(obj)); int nout; binbuf_addv(b, "ssii", gensym("#X"), gensym("obj"), obj->te_xpix, obj->te_ypix); binbuf_add(b, 1, argv); binbuf_addv(b, "s", gensym("a")); binbuf_add(b, argc-1, argv+1); canvas_undo_add(cnv, UNDO_PASTE, "paste", canvas_undo_set_pastebinbuf(cnv, b, 0, 0, 0)); stub=triggerize_createobj(cnv, b); stub_i = canvas_getindex(cnv, o2g(stub)); for(nout=0; nout on all selected [trigger] objects */ const t_symbol*s_trigger=gensym("trigger"); int count=0; t_gobj*gobj = NULL; for(gobj=cnv->gl_list; gobj;) { t_gobj*next=gobj->g_next; t_object*obj=g2o(gobj); if(obj && glist_isselected(cnv, gobj)) { const t_symbol*c_name=obj->te_g.g_pd->c_name; if((s_trigger == c_name) && fun(cnv, obj)) count++; } gobj=next; } return count; } static int triggerize_triggers(t_glist*cnv) { /* cleanup [trigger] objects */ int count=0; /* nothing to do, if the selection is not exclusively [trigger] objects */ if(!only_triggers_selected(cnv)) return 0; /* TODO: here's the place to merge multiple connected triggers */ /* remove all unused outlets from (selected) triggers */ canvas_undo_add(cnv, UNDO_SEQUENCE_START, "{minimize triggers}", 0); count = with_triggers(cnv, minimize_trigger); canvas_undo_add(cnv, UNDO_SEQUENCE_END, "{minimize triggers}", 0); if(count) return count; /* expand each (selected) trigger to the left */ canvas_undo_add(cnv, UNDO_SEQUENCE_START, "{expand triggers}", 0); count = with_triggers(cnv, expand_trigger); canvas_undo_add(cnv, UNDO_SEQUENCE_END, "{expand triggers}", 0); if(count) return count; /* nothing more to do */ return 0; } static int canvas_do_triggerize(t_glist*cnv, t_triggerize_return*tr) { /* * selected msg-connection: insert [t a] (->triggerize_line) * selected sig-connection: insert [pd nop~] (->triggerize_line) * selected [trigger]s with fan-outs: remove them (by inserting new outlets of the correct type) (->triggerize_fanouts) * selected objects with fan-outs: remove fan-outs (->triggerize_fanouts) * selected [trigger]: remove unused outlets (->triggerize_triggers) * selected [trigger]: else, add left-most "a" outlet (->triggerize_triggers) */ return(triggerize_line(cnv, tr) || triggerize_fanouts(cnv) || triggerize_triggers(cnv)); } void canvas_triggerize(t_glist*cnv) { int count = 0; t_triggerize_return*tr; if(!cnv || !cnv->gl_editor) return; if(!cnv->gl_editor->e_selection && !cnv->gl_editor->e_selectedline) return; tr = getbytes(sizeof(*tr)); if((count = canvas_do_triggerize(cnv, tr))) { canvas_dirty(cnv, 1); /* fix display of connections, objects,... */ canvas_redraw(cnv); glist_redraw(cnv); /* if we inserted an object, allow the user to change it now */ if(tr->tr_editgobj) gobj_activate(tr->tr_editgobj, cnv, 1); } freebytes(tr, sizeof(*tr)); } ================================================ FILE: libs/libpd/pure-data/src/g_graph.c ================================================ /* Copyright (c) 1997-2001 Miller Puckette and others. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* This file deals with the behavior of glists as either "text objects" or "graphs" inside another glist. LATER move the inlet/outlet code of g_canvas.c to this file... */ #include #include "m_pd.h" #include "g_canvas.h" #include /* ---------------------- forward definitions ----------------- */ static void graph_vis(t_gobj *gr, t_glist *unused_glist, int vis); static void graph_graphrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2); static void graph_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2); /* -------------------- maintaining the list -------------------- */ void canvas_drawredrect(t_canvas *x, int doit); void glist_add(t_glist *x, t_gobj *y) { t_object *ob; y->g_next = 0; if (!x->gl_list) x->gl_list = y; else { t_gobj *y2; for (y2 = x->gl_list; y2->g_next; y2 = y2->g_next); y2->g_next = y; } if (x->gl_editor && (ob = pd_checkobject(&y->g_pd))) rtext_new(x, ob); if (x->gl_editor && x->gl_isgraph && !x->gl_goprect && pd_checkobject(&y->g_pd)) { x->gl_goprect = 1; canvas_drawredrect(x, 1); } if (glist_isvisible(x)) gobj_vis(y, x, 1); if (class_isdrawcommand(y->g_pd)) canvas_redrawallfortemplate(template_findbyname(canvas_makebindsym( glist_getcanvas(x)->gl_name)), 0); } /* this is to protect against a hairy problem in which deleting a sub-canvas might delete an inlet on a box, after the box had been invisible-ized, so that we have to protect against redrawing it! */ int canvas_setdeleting(t_canvas *x, int flag) { int ret = x->gl_isdeleting; x->gl_isdeleting = flag; return (ret); } /* JMZ: emit a closebang message */ void rtext_freefortext(t_glist *gl, t_text *who); /* delete an object from a glist and free it */ void glist_delete(t_glist *x, t_gobj *y) { t_gobj *g; t_object *ob; t_gotfn chkdsp = zgetfn(&y->g_pd, gensym("dsp")); t_canvas *canvas = glist_getcanvas(x); t_rtext *rtext = 0; int drawcommand = class_isdrawcommand(y->g_pd); int wasdeleting; if (pd_class(&y->g_pd) == canvas_class) { /* JMZ: send a closebang to the canvas */ canvas_closebang((t_canvas *)y); } wasdeleting = canvas_setdeleting(canvas, 1); if (x->gl_editor) { /* if we've grabbed events from canvas release them */ if (canvas->gl_editor && canvas->gl_editor->e_grab == y) canvas->gl_editor->e_grab = 0; /* perhaps we grabbed our own glist instead? don't know if this ever happens: */ if (x->gl_editor->e_grab == y) x->gl_editor->e_grab = 0; if (glist_isselected(x, y)) glist_deselect(x, y); /* HACK -- we had phantom outlets not getting erased on the screen because the canvas_setdeleting() mechanism is too crude. LATER carefully set up rules for when the rtexts should exist, so that they stay around until all the steps of becoming invisible are done. In the meantime, just zap the inlets and outlets here... */ if (pd_class(&y->g_pd) == canvas_class) { t_glist *gl = (t_glist *)y; if (gl->gl_isgraph && glist_isvisible(x)) { char tag[80]; sprintf(tag, "graph%lx", (t_int)gl); glist_eraseiofor(x, &gl->gl_obj, tag); } else { if (glist_isvisible(x)) text_eraseborder(&gl->gl_obj, x, rtext_gettag(glist_findrtext(x, &gl->gl_obj))); } } } /* if we're a drawing command, erase all scalars now, before deleting it; we'll redraw them once it's deleted below. */ if (drawcommand) canvas_redrawallfortemplate(template_findbyname(canvas_makebindsym( glist_getcanvas(x)->gl_name)), 2); gobj_delete(y, x); if (glist_isvisible(canvas)) { gobj_vis(y, x, 0); } if (x->gl_editor && (ob = pd_checkobject(&y->g_pd)) && !(rtext = glist_findrtext(x, ob))) rtext = rtext_new(x, ob); if (x->gl_list == y) x->gl_list = y->g_next; else for (g = x->gl_list; g; g = g->g_next) if (g->g_next == y) { g->g_next = y->g_next; break; } if (y->g_pd == scalar_class) x->gl_valid = ++glist_valid; pd_free(&y->g_pd); if (rtext) rtext_free(rtext); if (chkdsp) canvas_update_dsp(); if (drawcommand) canvas_redrawallfortemplate(template_findbyname(canvas_makebindsym( glist_getcanvas(x)->gl_name)), 1); canvas_setdeleting(canvas, wasdeleting); } /* remove every object from a glist. Experimental. */ void glist_clear(t_glist *x) { t_gobj *y; int dspstate = 0, suspended = 0; t_symbol *dspsym = gensym("dsp"); while ((y = x->gl_list)) { /* to avoid unnecessary DSP resorting, we suspend DSP only if we hit a patchable object. */ if (!suspended && pd_checkobject(&y->g_pd) && zgetfn(&y->g_pd, dspsym)) { dspstate = canvas_suspend_dsp(); suspended = 1; } /* here's the real deletion. */ glist_delete(x, y); } if (suspended) canvas_resume_dsp(dspstate); } void glist_retext(t_glist *glist, t_text *y) { t_canvas *c = glist_getcanvas(glist); /* check that we have built rtexts yet. LATER need a better test. */ if (glist->gl_editor && glist->gl_editor->e_rtext) { t_rtext *rt = glist_findrtext(glist, y); if (rt) rtext_retext(rt); } } void glist_grab(t_glist *x, t_gobj *y, t_glistmotionfn motionfn, t_glistkeyfn keyfn, int xpos, int ypos) { t_glist *x2 = glist_getcanvas(x); if (motionfn) x2->gl_editor->e_onmotion = MA_PASSOUT; else x2->gl_editor->e_onmotion = 0; x2->gl_editor->e_grab = y; x2->gl_editor->e_motionfn = motionfn; x2->gl_editor->e_keyfn = keyfn; x2->gl_editor->e_xwas = xpos; x2->gl_editor->e_ywas = ypos; } t_canvas *glist_getcanvas(t_glist *x) { while (x->gl_owner && !x->gl_isclone && !x->gl_havewindow && x->gl_isgraph) x = x->gl_owner; return((t_canvas *)x); } static t_float gobj_getxforsort(t_gobj *g) { if (pd_class(&g->g_pd) == scalar_class) { t_float x1, y1; scalar_getbasexy((t_scalar *)g, &x1, &y1); return(x1); } else return (0); } static t_gobj *glist_merge(t_glist *x, t_gobj *g1, t_gobj *g2) { t_gobj *g = 0, *g9 = 0; t_float f1 = 0, f2 = 0; if (g1) f1 = gobj_getxforsort(g1); if (g2) f2 = gobj_getxforsort(g2); while (1) { if (g1) { if (g2) { if (f1 <= f2) goto put1; else goto put2; } else goto put1; } else if (g2) goto put2; else break; put1: if (g9) g9->g_next = g1, g9 = g1; else g9 = g = g1; if ((g1 = g1->g_next)) f1 = gobj_getxforsort(g1); g9->g_next = 0; continue; put2: if (g9) g9->g_next = g2, g9 = g2; else g9 = g = g2; if ((g2 = g2->g_next)) f2 = gobj_getxforsort(g2); g9->g_next = 0; continue; } return (g); } static t_gobj *glist_dosort(t_glist *x, t_gobj *g, int nitems) { if (nitems < 2) return (g); else { int n1 = nitems/2, n2 = nitems - n1, i; t_gobj *g2, *g3; for (g2 = g, i = n1-1; i--; g2 = g2->g_next) ; g3 = g2->g_next; g2->g_next = 0; g = glist_dosort(x, g, n1); g3 = glist_dosort(x, g3, n2); return (glist_merge(x, g, g3)); } } void glist_sort(t_glist *x) { int nitems = 0, foo = 0; t_float lastx = -1e37; t_gobj *g; for (g = x->gl_list; g; g = g->g_next) { t_float x1 = gobj_getxforsort(g); if (x1 < lastx) foo = 1; lastx = x1; nitems++; } if (foo) x->gl_list = glist_dosort(x, x->gl_list, nitems); } /* --------------- inlets and outlets ----------- */ t_inlet *canvas_addinlet(t_canvas *x, t_pd *who, t_symbol *s) { t_inlet *ip = inlet_new(&x->gl_obj, who, s, 0); if (!x->gl_loading && x->gl_owner && !x->gl_isclone && glist_isvisible(x->gl_owner)) { gobj_vis(&x->gl_gobj, x->gl_owner, 0); gobj_vis(&x->gl_gobj, x->gl_owner, 1); canvas_fixlinesfor(x->gl_owner, &x->gl_obj); } if (!x->gl_loading) canvas_resortinlets(x); return (ip); } void canvas_rminlet(t_canvas *x, t_inlet *ip) { t_canvas *owner = x->gl_isclone ? NULL : x->gl_owner; int redraw = (owner && glist_isvisible(owner) && (!owner->gl_isdeleting) && glist_istoplevel(owner)); if (owner) canvas_deletelinesforio(owner, &x->gl_obj, ip, 0); if (redraw) gobj_vis(&x->gl_gobj, x->gl_owner, 0); inlet_free(ip); if (redraw) { gobj_vis(&x->gl_gobj, x->gl_owner, 1); canvas_fixlinesfor(x->gl_owner, &x->gl_obj); } } extern t_inlet *vinlet_getit(t_pd *x); extern void obj_moveinletfirst(t_object *x, t_inlet *i); void canvas_resortinlets(t_canvas *x) { int ninlets = 0, i, j, xmax; t_gobj *y, **vec, **vp, **maxp; for (ninlets = 0, y = x->gl_list; y; y = y->g_next) if (pd_class(&y->g_pd) == vinlet_class) ninlets++; if (ninlets < 2) return; vec = (t_gobj **)getbytes(ninlets * sizeof(*vec)); for (y = x->gl_list, vp = vec; y; y = y->g_next) if (pd_class(&y->g_pd) == vinlet_class) *vp++ = y; for (i = ninlets; i--;) { t_inlet *ip; for (vp = vec, xmax = -0x7fffffff, maxp = 0, j = ninlets; j--; vp++) { int x1, y1, x2, y2; t_gobj *g = *vp; if (!g) continue; gobj_getrect(g, x, &x1, &y1, &x2, &y2); if (x1 > xmax) xmax = x1, maxp = vp; } if (!maxp) break; y = *maxp; *maxp = 0; ip = vinlet_getit(&y->g_pd); obj_moveinletfirst(&x->gl_obj, ip); } freebytes(vec, ninlets * sizeof(*vec)); if (x->gl_owner && !x->gl_isclone && glist_isvisible(x->gl_owner)) canvas_fixlinesfor(x->gl_owner, &x->gl_obj); } t_outlet *canvas_addoutlet(t_canvas *x, t_pd *who, t_symbol *s) { t_outlet *op = outlet_new(&x->gl_obj, s); if (!x->gl_loading && !x->gl_isclone && x->gl_owner && glist_isvisible(x->gl_owner)) { gobj_vis(&x->gl_gobj, x->gl_owner, 0); gobj_vis(&x->gl_gobj, x->gl_owner, 1); canvas_fixlinesfor(x->gl_owner, &x->gl_obj); } if (!x->gl_loading) canvas_resortoutlets(x); return (op); } void canvas_rmoutlet(t_canvas *x, t_outlet *op) { t_canvas *owner = x->gl_isclone ? NULL : x->gl_owner; int redraw = (owner && glist_isvisible(owner) && (!owner->gl_isdeleting) && glist_istoplevel(owner)); if (owner) canvas_deletelinesforio(owner, &x->gl_obj, 0, op); if (redraw) gobj_vis(&x->gl_gobj, x->gl_owner, 0); outlet_free(op); if (redraw) { gobj_vis(&x->gl_gobj, x->gl_owner, 1); canvas_fixlinesfor(x->gl_owner, &x->gl_obj); } } extern t_outlet *voutlet_getit(t_pd *x); extern void obj_moveoutletfirst(t_object *x, t_outlet *i); void canvas_resortoutlets(t_canvas *x) { int noutlets = 0, i, j, xmax; t_gobj *y, **vec, **vp, **maxp; for (noutlets = 0, y = x->gl_list; y; y = y->g_next) if (pd_class(&y->g_pd) == voutlet_class) noutlets++; if (noutlets < 2) return; vec = (t_gobj **)getbytes(noutlets * sizeof(*vec)); for (y = x->gl_list, vp = vec; y; y = y->g_next) if (pd_class(&y->g_pd) == voutlet_class) *vp++ = y; for (i = noutlets; i--;) { t_outlet *ip; for (vp = vec, xmax = -0x7fffffff, maxp = 0, j = noutlets; j--; vp++) { int x1, y1, x2, y2; t_gobj *g = *vp; if (!g) continue; gobj_getrect(g, x, &x1, &y1, &x2, &y2); if (x1 > xmax) xmax = x1, maxp = vp; } if (!maxp) break; y = *maxp; *maxp = 0; ip = voutlet_getit(&y->g_pd); obj_moveoutletfirst(&x->gl_obj, ip); } freebytes(vec, noutlets * sizeof(*vec)); if (x->gl_owner && !x->gl_isclone && glist_isvisible(x->gl_owner)) canvas_fixlinesfor(x->gl_owner, &x->gl_obj); } /* ----------calculating coordinates and controlling appearance --------- */ static void graph_bounds(t_glist *x, t_floatarg x1, t_floatarg y1, t_floatarg x2, t_floatarg y2) { x->gl_x1 = x1; x->gl_x2 = x2; x->gl_y1 = y1; x->gl_y2 = y2; if (x->gl_x2 == x->gl_x1 || x->gl_y2 == x->gl_y1) { pd_error(0, "graph: empty bounds rectangle"); x1 = y1 = 0; x2 = y2 = 1; } glist_redraw(x); } static void graph_xticks(t_glist *x, t_floatarg point, t_floatarg inc, t_floatarg f) { x->gl_xtick.k_point = point; x->gl_xtick.k_inc = inc; x->gl_xtick.k_lperb = f; glist_redraw(x); } static void graph_yticks(t_glist *x, t_floatarg point, t_floatarg inc, t_floatarg f) { x->gl_ytick.k_point = point; x->gl_ytick.k_inc = inc; x->gl_ytick.k_lperb = f; glist_redraw(x); } static void graph_xlabel(t_glist *x, t_symbol *s, int argc, t_atom *argv) { int i; if (argc < 1) pd_error(0, "graph_xlabel: no y value given"); else { x->gl_xlabely = atom_getfloat(argv); argv++; argc--; x->gl_xlabel = (t_symbol **)t_resizebytes(x->gl_xlabel, x->gl_nxlabels * sizeof (t_symbol *), argc * sizeof (t_symbol *)); x->gl_nxlabels = argc; for (i = 0; i < argc; i++) x->gl_xlabel[i] = atom_gensym(&argv[i]); } glist_redraw(x); } static void graph_ylabel(t_glist *x, t_symbol *s, int argc, t_atom *argv) { int i; if (argc < 1) pd_error(0, "graph_ylabel: no x value given"); else { x->gl_ylabelx = atom_getfloat(argv); argv++; argc--; x->gl_ylabel = (t_symbol **)t_resizebytes(x->gl_ylabel, x->gl_nylabels * sizeof (t_symbol *), argc * sizeof (t_symbol *)); x->gl_nylabels = argc; for (i = 0; i < argc; i++) x->gl_ylabel[i] = atom_gensym(&argv[i]); } glist_redraw(x); } /****** routines to convert pixels to X or Y value and vice versa ******/ /* convert an x pixel value to an x coordinate value */ t_float glist_pixelstox(t_glist *x, t_float xpix) { /* if we appear as a text box on parent, our range in our coordinates (x1, etc.) specifies the coordinate range of a one-pixel square at top left of the window. */ if (!x->gl_isgraph) return (x->gl_x1 + (x->gl_x2 - x->gl_x1) * xpix / x->gl_zoom); /* if we're a graph when shown on parent, but own our own window right now, our range in our coordinates (x1, etc.) is spread over the visible window size, given by screenx1, etc. */ else if (x->gl_isgraph && x->gl_havewindow) return (x->gl_x1 + (x->gl_x2 - x->gl_x1) * (xpix) / (x->gl_screenx2 - x->gl_screenx1)); /* otherwise, we appear in a graph within a parent glist, so get our screen rectangle on parent and transform. */ else { int x1, y1, x2, y2; if (!x->gl_owner) bug("glist_pixelstox"); graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2); return (x->gl_x1 + (x->gl_x2 - x->gl_x1) * (xpix - x1) / (x2 - x1)); } } t_float glist_pixelstoy(t_glist *x, t_float ypix) { if (!x->gl_isgraph) return (x->gl_y1 + (x->gl_y2 - x->gl_y1) * ypix / x->gl_zoom); else if (x->gl_isgraph && x->gl_havewindow) return (x->gl_y1 + (x->gl_y2 - x->gl_y1) * (ypix) / (x->gl_screeny2 - x->gl_screeny1)); else { int x1, y1, x2, y2; if (!x->gl_owner) bug("glist_pixelstox"); graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2); return (x->gl_y1 + (x->gl_y2 - x->gl_y1) * (ypix - y1) / (y2 - y1)); } } /* convert an x coordinate value to an x pixel location in window */ t_float glist_xtopixels(t_glist *x, t_float xval) { if (!x->gl_isgraph) return (((xval - x->gl_x1) * x->gl_zoom) / (x->gl_x2 - x->gl_x1)); else if (x->gl_isgraph && x->gl_havewindow) return (x->gl_screenx2 - x->gl_screenx1) * (xval - x->gl_x1) / (x->gl_x2 - x->gl_x1); else { int x1, y1, x2, y2; if (!x->gl_owner) bug("glist_pixelstox"); graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2); return (x1 + (x2 - x1) * (xval - x->gl_x1) / (x->gl_x2 - x->gl_x1)); } } t_float glist_ytopixels(t_glist *x, t_float yval) { if (!x->gl_isgraph) return (((yval - x->gl_y1) * x->gl_zoom) / (x->gl_y2 - x->gl_y1)); else if (x->gl_isgraph && x->gl_havewindow) return (x->gl_screeny2 - x->gl_screeny1) * (yval - x->gl_y1) / (x->gl_y2 - x->gl_y1); else { int x1, y1, x2, y2; if (!x->gl_owner) bug("glist_pixelstox"); graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2); return (y1 + (y2 - y1) * (yval - x->gl_y1) / (x->gl_y2 - x->gl_y1)); } } /* convert an X screen distance to an X coordinate increment. This is terribly inefficient; but probably not a big enough CPU hog to warrant optimizing. */ t_float glist_dpixtodx(t_glist *x, t_float dxpix) { return (dxpix * (glist_pixelstox(x, 1) - glist_pixelstox(x, 0))); } t_float glist_dpixtody(t_glist *x, t_float dypix) { return (dypix * (glist_pixelstoy(x, 1) - glist_pixelstoy(x, 0))); } /* get the window location in pixels of a "text" object. The object's x and y positions are in pixels when the glist they're in is toplevel. Otherwise, if it's a new-style graph-on-parent (so gl_goprect is set) we use the offset into the framing subrectangle as an offset into the parent rectangle. Finally, it might be an old, proportional-style GOP. In this case we do a coordinate transformation. */ int text_xpix(t_text *x, t_glist *glist) { if (glist->gl_havewindow || !glist->gl_isgraph) return (x->te_xpix * glist->gl_zoom); else if (glist->gl_goprect) return (glist_xtopixels(glist, glist->gl_x1) + glist->gl_zoom * (x->te_xpix - glist->gl_xmargin)); else return (glist_xtopixels(glist, glist->gl_x1 + (glist->gl_x2 - glist->gl_x1) * x->te_xpix / (glist->gl_screenx2 - glist->gl_screenx1))); } int text_ypix(t_text *x, t_glist *glist) { if (glist->gl_havewindow || !glist->gl_isgraph) return (x->te_ypix * glist->gl_zoom); else if (glist->gl_goprect) return (glist_ytopixels(glist, glist->gl_y1) + glist->gl_zoom * (x->te_ypix - glist->gl_ymargin)); else return (glist_ytopixels(glist, glist->gl_y1 + (glist->gl_y2 - glist->gl_y1) * x->te_ypix / (glist->gl_screeny2 - glist->gl_screeny1))); } /* redraw all the items in a glist. We construe this to mean redrawing in its own window and on parent, as needed in each case. This is too conservative -- for instance, when you draw an "open" rectangle on the parent, you shouldn't have to redraw the window! */ void glist_redraw(t_glist *x) { if (glist_isvisible(x)) { /* LATER fix the graph_vis() code to handle both cases */ if (glist_istoplevel(x)) { t_gobj *g; t_linetraverser t; t_outconnect *oc; for (g = x->gl_list; g; g = g->g_next) { gobj_vis(g, x, 0); gobj_vis(g, x, 1); } /* redraw all the lines */ linetraverser_start(&t, x); while ((oc = linetraverser_next(&t))) { char tagbuf[128]; sprintf(tagbuf, "l%p", oc); pdgui_vmess(0, "crs iiii", glist_getcanvas(x), "coords", tagbuf, t.tr_lx1, t.tr_ly1, t.tr_lx2, t.tr_ly2); } canvas_drawredrect(x, 0); if (x->gl_goprect) { canvas_drawredrect(x, 1); } } if (x->gl_owner && !x->gl_isclone && glist_isvisible(x->gl_owner)) { graph_vis(&x->gl_gobj, x->gl_owner, 0); graph_vis(&x->gl_gobj, x->gl_owner, 1); } } } /* --------------------------- widget behavior ------------------- */ int garray_getname(t_garray *x, t_symbol **namep); static void _graph_create_line4(t_glist *x, int x1, int y1, int x2, int y2, const char**tags2) { pdgui_vmess(0, "crr iiii ri rS", glist_getcanvas(x->gl_owner), "create", "line", x1,y1, x2,y2, "-width", glist_getzoom(x), "-tags", 2, tags2); } static void _graph_create_text( t_glist *x, int posX, int posY, const char*name, const char*anchor, int fontsize, int numtags, const char**tags) { t_atom fontatoms[3]; SETSYMBOL(fontatoms+0, gensym(sys_font)); SETFLOAT (fontatoms+1, fontsize); SETSYMBOL(fontatoms+2, gensym(sys_fontweight)); pdgui_vmess(0, "crr ii rs rr rA rS", glist_getcanvas(x), "create", "text", posX, posY, "-text", name, "-anchor", anchor, "-font", 3, fontatoms, "-tags", numtags, tags); } /* Note that some code in here would also be useful for drawing graph decorations in toplevels... */ static void graph_vis(t_gobj *gr, t_glist *parent_glist, int vis) { t_glist *x = (t_glist *)gr; char tag[50]; const char *tags2[] = {tag, "graph" }; t_gobj *g; int x1, y1, x2, y2; /* ordinary subpatches: just act like a text object */ if (!x->gl_isgraph) { text_widgetbehavior.w_visfn(gr, parent_glist, vis); return; } if (vis && canvas_showtext(x)) rtext_draw(glist_findrtext(parent_glist, &x->gl_obj)); graph_getrect(gr, parent_glist, &x1, &y1, &x2, &y2); if (!vis) rtext_erase(glist_findrtext(parent_glist, &x->gl_obj)); sprintf(tag, "graph%lx", (t_int)x); if (vis) glist_drawiofor(parent_glist, &x->gl_obj, 1, tag, x1, y1, x2, y2); else glist_eraseiofor(parent_glist, &x->gl_obj, tag); /* if we look like a graph but have been moved to a toplevel, just show the bounding rectangle */ if (x->gl_havewindow) { if (vis) pdgui_vmess(0, "crr iiiiiiiiii ri rr rr rS", glist_getcanvas(x->gl_owner), "create", "polygon", x1,y1, x1,y2, x2,y2, x2,y1, x1,y1, "-width", glist_getzoom(x), "-fill", "#c0c0c0", "-joinstyle", "miter", "-tags", 2, tags2); else pdgui_vmess(0, "crs", glist_getcanvas(x->gl_owner), "delete", tag); return; } /* otherwise draw (or erase) us as a graph inside another glist. */ if (vis) { int i; t_float f; t_gobj *g; t_symbol *arrayname; char *ylabelanchor = (x->gl_ylabelx > 0.5*(x->gl_x1 + x->gl_x2) ? "w" : "e"); char *xlabelanchor = (x->gl_xlabely > 0.5*(x->gl_y1 + x->gl_y2) ? "s" : "n"); int fs = sys_hostfontsize(glist_getfont(x), glist_getzoom(x)); const char *tags3[] = {tag, "label", "graph" }; /* draw a rectangle around the graph */ pdgui_vmess(0, "crr iiiiiiiiii ri rr rS", glist_getcanvas(x->gl_owner), "create", "line", x1,y1, x1,y2, x2,y2, x2,y1, x1,y1, "-width", glist_getzoom(x), "-capstyle", "projecting", "-tags", 2, tags2); /* if there's just one "garray" in the graph, write its name along the top */ for (i = (y1 < y2 ? y1 : y2)-1, g = x->gl_list; g; g = g->g_next) if (g->g_pd == garray_class && !garray_getname((t_garray *)g, &arrayname)) { i -= glist_fontheight(x); _graph_create_text(x, x1, i, arrayname->s_name, "nw", -fs, 3, tags3); } /* draw ticks on horizontal borders. If lperb field is zero, this is disabled. */ if (x->gl_xtick.k_lperb) { t_float upix, lpix; if (y2 < y1) upix = y1, lpix = y2; else upix = y2, lpix = y1; for (i = 0, f = x->gl_xtick.k_point; f < 0.99 * x->gl_x2 + 0.01*x->gl_x1; i++, f += x->gl_xtick.k_inc) { int tickpix = (i % x->gl_xtick.k_lperb ? 2 : 4); _graph_create_line4(x, (int)glist_xtopixels(x, f), (int)upix, (int)glist_xtopixels(x, f), (int)upix - tickpix, tags2); _graph_create_line4(x, (int)glist_xtopixels(x, f), (int)lpix, (int)glist_xtopixels(x, f), (int)lpix + tickpix, tags2); } for (i = 1, f = x->gl_xtick.k_point - x->gl_xtick.k_inc; f > 0.99 * x->gl_x1 + 0.01*x->gl_x2; i++, f -= x->gl_xtick.k_inc) { int tickpix = (i % x->gl_xtick.k_lperb ? 2 : 4); _graph_create_line4(x, (int)glist_xtopixels(x, f), (int)upix, (int)glist_xtopixels(x, f), (int)upix - tickpix, tags2); _graph_create_line4(x, (int)glist_xtopixels(x, f), (int)lpix, (int)glist_xtopixels(x, f), (int)lpix + tickpix, tags2); } } /* draw ticks in vertical borders*/ if (x->gl_ytick.k_lperb) { t_float ubound, lbound; if (x->gl_y2 < x->gl_y1) ubound = x->gl_y1, lbound = x->gl_y2; else ubound = x->gl_y2, lbound = x->gl_y1; for (i = 0, f = x->gl_ytick.k_point; f < 0.99 * ubound + 0.01 * lbound; i++, f += x->gl_ytick.k_inc) { int tickpix = (i % x->gl_ytick.k_lperb ? 2 : 4); _graph_create_line4(x, x1, (int)glist_ytopixels(x, f), x1 + tickpix, (int)glist_ytopixels(x, f), tags2); _graph_create_line4(x, x2, (int)glist_ytopixels(x, f), x2 - tickpix, (int)glist_ytopixels(x, f), tags2); } for (i = 1, f = x->gl_ytick.k_point - x->gl_ytick.k_inc; f > 0.99 * lbound + 0.01 * ubound; i++, f -= x->gl_ytick.k_inc) { int tickpix = (i % x->gl_ytick.k_lperb ? 2 : 4); _graph_create_line4(x, x1, (int)glist_ytopixels(x, f), x1 + tickpix, (int)glist_ytopixels(x, f), tags2); _graph_create_line4(x, x2, (int)glist_ytopixels(x, f), x2 - tickpix, (int)glist_ytopixels(x, f), tags2); } } /* draw x labels */ for (i = 0; i < x->gl_nxlabels; i++) _graph_create_text(x, (int)glist_xtopixels(x, atof(x->gl_xlabel[i]->s_name)), (int)glist_ytopixels(x, x->gl_xlabely), x->gl_xlabel[i]->s_name, xlabelanchor, -fs, 3, tags3); /* draw y labels */ for (i = 0; i < x->gl_nylabels; i++) _graph_create_text(x, (int)glist_xtopixels(x, x->gl_ylabelx), (int)glist_ytopixels(x, atof(x->gl_ylabel[i]->s_name)), x->gl_ylabel[i]->s_name, ylabelanchor, -fs, 3, tags3); /* draw contents of graph as glist */ for (g = x->gl_list; g; g = g->g_next) gobj_vis(g, x, 1); } else { pdgui_vmess(0, "crs", glist_getcanvas(x->gl_owner), "delete", tag); for (g = x->gl_list; g; g = g->g_next) gobj_vis(g, x, 0); } } /* get the graph's rectangle, not counting extra swelling for controls to keep them inside the graph. This is the "logical" pixel size. */ static void graph_graphrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2) { t_glist *x = (t_glist *)z; int x1 = text_xpix(&x->gl_obj, glist); int y1 = text_ypix(&x->gl_obj, glist); int x2, y2; x2 = x1 + x->gl_zoom * x->gl_pixwidth; y2 = y1 + x->gl_zoom * x->gl_pixheight; *xp1 = x1; *yp1 = y1; *xp2 = x2; *yp2 = y2; } /* get the rectangle, enlarged to contain all the "contents" -- meaning their formal bounds rectangles. */ static void graph_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2) { int x1 = 0x7fffffff, y1 = 0x7fffffff, x2 = -0x7fffffff, y2 = -0x7fffffff; t_glist *x = (t_glist *)z; if (x->gl_isgraph) { int hadwindow; t_gobj *g; int x21, y21, x22, y22; graph_graphrect(z, glist, &x1, &y1, &x2, &y2); if (canvas_showtext(x)) { text_widgetbehavior.w_getrectfn(z, glist, &x21, &y21, &x22, &y22); if (x22 > x2) x2 = x22; if (y22 > y2) y2 = y22; } if (!x->gl_goprect) { /* expand the rectangle to fit in text objects; this applies only to the old (0.37) graph-on-parent behavior. */ /* lie about whether we have our own window to affect gobj_getrect calls below. */ hadwindow = x->gl_havewindow; x->gl_havewindow = 0; for (g = x->gl_list; g; g = g->g_next) { /* don't do this for arrays, just let them hang outside the box. And ignore "text" objects which aren't shown on parent */ if (pd_class(&g->g_pd) == garray_class || pd_checkobject(&g->g_pd)) continue; gobj_getrect(g, x, &x21, &y21, &x22, &y22); if (x22 > x2) x2 = x22; if (y22 > y2) y2 = y22; } x->gl_havewindow = hadwindow; } } else text_widgetbehavior.w_getrectfn(z, glist, &x1, &y1, &x2, &y2); *xp1 = x1; *yp1 = y1; *xp2 = x2; *yp2 = y2; } static void graph_displace(t_gobj *z, t_glist *glist, int dx, int dy) { t_glist *x = (t_glist *)z; if (!x->gl_isgraph) text_widgetbehavior.w_displacefn(z, glist, dx, dy); else { x->gl_obj.te_xpix += dx; x->gl_obj.te_ypix += dy; if (glist_isvisible(glist)) { glist_redraw(x); canvas_fixlinesfor(glist, &x->gl_obj); } } } static void graph_select(t_gobj *z, t_glist *glist, int state) { t_glist *x = (t_glist *)z; if (!x->gl_isgraph) text_widgetbehavior.w_selectfn(z, glist, state); else { t_rtext *y = glist_findrtext(glist, &x->gl_obj); char tag[80]; if (canvas_showtext(x)) rtext_select(y, state); sprintf(tag, "%sR", rtext_gettag(y)); pdgui_vmess(0, "crs rr", glist, "itemconfigure", tag, "-fill", (state? "blue" : "black")); sprintf(tag, "graph%lx", (t_int)z); pdgui_vmess(0, "crs rr", glist_getcanvas(glist), "itemconfigure", tag, "-fill", (state? "blue" : "black")); } } static void graph_activate(t_gobj *z, t_glist *glist, int state) { t_glist *x = (t_glist *)z; if (canvas_showtext(x)) text_widgetbehavior.w_activatefn(z, glist, state); } static void graph_delete(t_gobj *z, t_glist *glist) { t_glist *x = (t_glist *)z; t_gobj *y; while ((y = x->gl_list)) glist_delete(x, y); if (glist_isvisible(x)) text_widgetbehavior.w_deletefn(z, glist); /* if we have connections to the actual 'canvas' object, zap them as well (e.g., array or scalar objects that are implemented as canvases with "real" inlets). Connections to ordinary canvas in/outlets already got zapped when we cleared the contents above */ canvas_deletelinesfor(glist, &x->gl_obj); } static void graph_motion(void *z, t_floatarg dx, t_floatarg dy) { t_glist *x = (t_glist *)z; t_float newxpix = THISGUI->i_graph_lastxpix + dx, newypix = THISGUI->i_graph_lastypix + dy; t_garray *a = (t_garray *)(x->gl_list); int oldx = 0.5 + glist_pixelstox(x, THISGUI->i_graph_lastxpix); int newx = 0.5 + glist_pixelstox(x, newxpix); t_word *vec; int nelem, i; t_float oldy = glist_pixelstoy(x, THISGUI->i_graph_lastypix); t_float newy = glist_pixelstoy(x, newypix); THISGUI->i_graph_lastxpix = newxpix; THISGUI->i_graph_lastypix = newypix; /* verify that the array is OK */ if (!a || pd_class((t_pd *)a) != garray_class) return; if (!garray_getfloatwords(a, &nelem, &vec)) return; if (oldx < 0) oldx = 0; if (oldx >= nelem) oldx = nelem - 1; if (newx < 0) newx = 0; if (newx >= nelem) newx = nelem - 1; if (oldx < newx - 1) { for (i = oldx + 1; i <= newx; i++) vec[i].w_float = newy + (oldy - newy) * ((t_float)(newx - i))/(t_float)(newx - oldx); } else if (oldx > newx + 1) { for (i = oldx - 1; i >= newx; i--) vec[i].w_float = newy + (oldy - newy) * ((t_float)(newx - i))/(t_float)(newx - oldx); } else vec[newx].w_float = newy; garray_redraw(a); } static int graph_click(t_gobj *z, struct _glist *glist, int xpix, int ypix, int shift, int alt, int dbl, int doit) { t_glist *x = (t_glist *)z; t_gobj *y; int clickreturned = 0; if (!x->gl_isgraph) return (text_widgetbehavior.w_clickfn(z, glist, xpix, ypix, shift, alt, dbl, doit)); else if (x->gl_havewindow) return (0); else { for (y = x->gl_list; y; y = y->g_next) { int x1, y1, x2, y2; /* check if the object wants to be clicked */ if (canvas_hitbox(x, y, xpix, ypix, &x1, &y1, &x2, &y2) && (clickreturned = gobj_click(y, x, xpix, ypix, shift, alt, 0, doit))) break; } if (!doit) { if (y) canvas_setcursor(glist_getcanvas(x), clickreturned); else canvas_setcursor(glist_getcanvas(x), CURSOR_RUNMODE_NOTHING); } return (clickreturned); } } const t_widgetbehavior graph_widgetbehavior = { graph_getrect, graph_displace, graph_select, graph_activate, graph_delete, graph_vis, graph_click, }; /* find the graph most recently added to this glist; if none exists, return 0. */ t_glist *glist_findgraph(t_glist *x) { t_gobj *y = 0, *z; for (z = x->gl_list; z; z = z->g_next) if (pd_class(&z->g_pd) == canvas_class && ((t_glist *)z)->gl_isgraph) y = z; return ((t_glist *)y); } extern void canvas_menuarray(t_glist *canvas); void g_graph_setup_class(t_class *c) { class_setwidget(c, &graph_widgetbehavior); class_addmethod(c, (t_method)graph_bounds, gensym("bounds"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); class_addmethod(c, (t_method)graph_xticks, gensym("xticks"), A_FLOAT, A_FLOAT, A_FLOAT, 0); class_addmethod(c, (t_method)graph_xlabel, gensym("xlabel"), A_GIMME, 0); class_addmethod(c, (t_method)graph_yticks, gensym("yticks"), A_FLOAT, A_FLOAT, A_FLOAT, 0); class_addmethod(c, (t_method)graph_ylabel, gensym("ylabel"), A_GIMME, 0); class_addmethod(c, (t_method)graph_array, gensym("array"), A_SYMBOL, A_FLOAT, A_SYMBOL, A_DEFFLOAT, A_NULL); class_addmethod(c, (t_method)canvas_menuarray, gensym("menuarray"), A_NULL); class_addmethod(c, (t_method)glist_sort, gensym("sort"), A_NULL); } void g_graph_setup(void) { g_graph_setup_class(canvas_class); } ================================================ FILE: libs/libpd/pure-data/src/g_guiconnect.c ================================================ /* Copyright (c) 1997-2000 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* a thing to forward messages from the GUI, dealing with race conditions in which the "target" gets deleted while the GUI is sending it something. See also the gfxstub object that doesn't oblige the owner to keep a pointer around (so is better suited to one-off dialogs) */ #include "m_pd.h" #include "g_canvas.h" struct _guiconnect { t_object x_obj; t_pd *x_who; t_symbol *x_sym; t_clock *x_clock; }; static t_class *guiconnect_class; t_guiconnect *guiconnect_new(t_pd *who, t_symbol *sym) { t_guiconnect *x = (t_guiconnect *)pd_new(guiconnect_class); x->x_who = who; x->x_sym = sym; pd_bind(&x->x_obj.ob_pd, sym); return (x); } /* cleanup routine; delete any resources we have */ static void guiconnect_free(t_guiconnect *x) { if (x->x_sym) pd_unbind(&x->x_obj.ob_pd, x->x_sym); if (x->x_clock) clock_free(x->x_clock); } /* this is called when the clock times out to indicate the GUI should be gone by now. */ static void guiconnect_tick(t_guiconnect *x) { pd_free(&x->x_obj.ob_pd); } /* the target calls this to disconnect. If the gui has "signed off" we're ready to delete the object; otherwise we wait either for signoff or for a timeout. */ void guiconnect_notarget(t_guiconnect *x, double timedelay) { if (!x->x_sym) pd_free(&x->x_obj.ob_pd); else { x->x_who = 0; if (timedelay > 0) { x->x_clock = clock_new(x, (t_method)guiconnect_tick); clock_delay(x->x_clock, timedelay); } } } /* the GUI calls this to send messages to the target. */ static void guiconnect_anything(t_guiconnect *x, t_symbol *s, int ac, t_atom *av) { if (x->x_who) typedmess(x->x_who, s, ac, av); } /* the GUI calls this when it disappears. (If there's any chance the GUI will fail to do this, the "target", when it signs off, should specify a timeout after which the guiconnect will disappear.) */ static void guiconnect_signoff(t_guiconnect *x) { if (!x->x_who) pd_free(&x->x_obj.ob_pd); else { pd_unbind(&x->x_obj.ob_pd, x->x_sym); x->x_sym = 0; } } void g_guiconnect_setup(void) { guiconnect_class = class_new(gensym("guiconnect"), 0, (t_method)guiconnect_free, sizeof(t_guiconnect), CLASS_PD, 0); class_addanything(guiconnect_class, guiconnect_anything); class_addmethod(guiconnect_class, (t_method)guiconnect_signoff, gensym("signoff"), 0); } ================================================ FILE: libs/libpd/pure-data/src/g_io.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* graphical inlets and outlets, both for control and signals. */ /* This code is highly inefficient; messages actually have to be forwarded by inlets and outlets. The outlet is in even worse shape than the inlet; in order to avoid having a "signal" method in the class, the oulet actually sprouts an inlet, which forwards the message to the "outlet" object, which sends it on to the outlet proper. Another way to do it would be to have separate classes for "signal" and "control" outlets, but this would complicate life elsewhere. */ #include "m_pd.h" #include "g_canvas.h" #include static int symbol2resamplemethod(t_symbol*s) { if (s == gensym("hold" )) return 1; /* up: sample and hold */ else if (s == gensym("lin" )) return 2; /* up: linear interpolation */ else if (s == gensym("linear")) return 2; /* up: linear interpolation */ else if (s == gensym("pad" )) return 0; /* up: zero pad */ return -1; /* default: sample/hold except zero-pad if version<0.44 */ } /* ------------------------- vinlet -------------------------- */ t_class *vinlet_class; typedef struct _reblocker { t_sample *r_buf; /* signal buffer; zero if not a signal */ t_resample r_updown; } t_reblocker; static void reblocker_init(t_reblocker *rb, int buflength) { rb->r_buf = (t_sample *)getbytes(buflength * sizeof(t_sample)); resample_init(&rb->r_updown); } static void reblocker_resize(t_reblocker **rb, int oldn, int newn, int buflength) { int i; if (oldn == newn) return; for (i = newn; i < oldn; i++) { freebytes((*rb)[i].r_buf, buflength * sizeof(t_sample)); resample_free(&(*rb)[i].r_updown); } *rb = (t_reblocker *)resizebytes(*rb, oldn * sizeof(t_reblocker), newn * sizeof(t_reblocker)); for (i = oldn; i < newn; i++) reblocker_init(&(*rb)[i], buflength); } typedef struct _vinlet { t_object x_obj; t_canvas *x_canvas; t_inlet *x_inlet; int x_buflength; /* number of samples per channel in buffer */ int x_fill; int x_read; int x_hop; int x_updownmethod; /* if not reblocking, the next slot communicates the parent's inlet signal from the prolog to the DSP routine: */ t_signal *x_directsignal; int x_nchans; /* this is also set in prolog & used in dsp */ t_outlet *x_fwdout; /* optional outlet for forwarding messages to inlet~ */ t_reblocker *x_rb; /* reblocking and resampling, one per channel */ } t_vinlet; static void *vinlet_new(t_symbol *s) { t_vinlet *x = (t_vinlet *)pd_new(vinlet_class); x->x_canvas = canvas_getcurrent(); x->x_inlet = canvas_addinlet(x->x_canvas, &x->x_obj.ob_pd, 0); x->x_buflength = 0; x->x_nchans = 0; x->x_rb = 0; outlet_new(&x->x_obj, 0); return (x); } static void vinlet_bang(t_vinlet *x) { outlet_bang(x->x_obj.ob_outlet); } static void vinlet_pointer(t_vinlet *x, t_gpointer *gp) { outlet_pointer(x->x_obj.ob_outlet, gp); } static void vinlet_float(t_vinlet *x, t_float f) { outlet_float(x->x_obj.ob_outlet, f); } static void vinlet_symbol(t_vinlet *x, t_symbol *s) { outlet_symbol(x->x_obj.ob_outlet, s); } static void vinlet_list(t_vinlet *x, t_symbol *s, int argc, t_atom *argv) { outlet_list(x->x_obj.ob_outlet, s, argc, argv); } static void vinlet_anything(t_vinlet *x, t_symbol *s, int argc, t_atom *argv) { outlet_anything(x->x_obj.ob_outlet, s, argc, argv); } static void vinlet_free(t_vinlet *x) { int i; canvas_rminlet(x->x_canvas, x->x_inlet); if (x->x_rb) for (i = 0; i < x->x_nchans; i++) { freebytes((x->x_rb)[i].r_buf, x->x_buflength * sizeof(t_sample)); resample_free(&(x->x_rb)[i].r_updown); } } t_inlet *vinlet_getit(t_pd *x) { if (pd_class(x) != vinlet_class) bug("vinlet_getit"); return (((t_vinlet *)x)->x_inlet); } /* ------------------------- signal inlet -------------------------- */ int vinlet_issignal(t_vinlet *x) { return (x->x_rb != 0); } t_int *vinlet_perform(t_int *w) { t_vinlet *x = (t_vinlet *)(w[1]); t_sample *out = (t_sample *)(w[2]); t_reblocker *rb = (t_reblocker *)(w[3]); int hop = (int)(w[4]), n = (int)(w[5]), read = x->x_read; t_sample *in = rb->r_buf + read; while (n--) *out++ = *in++; if (hop) { if ((read += hop) == x->x_buflength) read = 0; x->x_read = read; } return (w+6); } static void vinlet_fwd(t_vinlet *x, t_symbol *s, int argc, t_atom *argv) { if (!x->x_rb) /* if we're not signal, just forward */ outlet_anything(x->x_obj.ob_outlet, s, argc, argv); else if (x->x_fwdout && argc > 0 && argv->a_type == A_SYMBOL) outlet_anything(x->x_fwdout, argv->a_w.w_symbol, argc-1, argv+1); } static void vinlet_dsp(t_vinlet *x, t_signal **sp) { /* no buffer means we're not a signal inlet */ if (!x->x_rb) return; if (x->x_directsignal) { sp[0] = signal_new(0, 1, sp[0]->s_sr, 0); signal_setborrowed(sp[0], x->x_directsignal); } else { int i; signal_setmultiout(sp, x->x_nchans); for (i = 0; i < x->x_nchans; i++) dsp_add(vinlet_perform, 5, x, sp[0]->s_vec + i * sp[0]->s_length, &x->x_rb[i], (t_int)(i == x->x_nchans-1 ? sp[0]->s_length : 0), (t_int)(sp[0]->s_length)); x->x_read = 0; } } /* prolog code: loads buffer from parent patch */ t_int *vinlet_doprolog(t_int *w) { t_vinlet *x = (t_vinlet *)(w[1]); t_sample *in = (t_sample *)(w[2]), *out; t_sample *buf = (t_sample *)(w[3]); int lastone = (int)(w[4]), n = (int)(w[5]), fill = x->x_fill; if (fill == x->x_buflength) { t_sample *f1 = buf, *f2 = buf + x->x_hop; int nshift = x->x_buflength - x->x_hop; while (nshift--) *f1++ = *f2++; fill -= x->x_hop; } out = buf + fill; if (lastone) x->x_fill = fill + n; while (n--) *out++ = *in++; return (w+6); } int inlet_getsignalindex(t_inlet *x); /* set up prolog DSP code */ void vinlet_dspprolog(struct _vinlet *x, t_signal **parentsigs, int myvecsize, int phase, int period, int frequency, int downsample, int upsample, int reblock, int switched) { t_signal *insig; /* no buffer means we're not a signal inlet */ if (!x->x_rb) return; /* if the "reblock" flag is set, arrange to copy data in from the parent. */ if (reblock) { int parentvecsize, bufsize, oldbufsize, prologphase, i; int re_parentvecsize; /* resampled parentvectorsize */ /* the prolog code counts from 0 to period-1; the phase is backed up by one so that AFTER the prolog code runs, the "x_fill" phase is in sync with the "x_read" phase. */ prologphase = (phase + period - 1) % period; if (parentsigs) { insig = parentsigs[inlet_getsignalindex(x->x_inlet)]; parentvecsize = insig->s_length; re_parentvecsize = parentvecsize * upsample / downsample; reblocker_resize(&x->x_rb, x->x_nchans, insig->s_nchans, x->x_buflength); x->x_nchans = insig->s_nchans; } else { insig = 0; parentvecsize = re_parentvecsize = 1; } bufsize = re_parentvecsize; if (bufsize < myvecsize) bufsize = myvecsize; if (bufsize != (oldbufsize = x->x_buflength)) { for (i = 0; i < x->x_nchans; i++) { x->x_rb[i].r_buf = (t_sample *)t_resizebytes(x->x_rb[i].r_buf, oldbufsize * sizeof(t_sample), bufsize * sizeof(t_sample)); memset((char *)x->x_rb[i].r_buf, 0, bufsize * sizeof(t_sample)); } x->x_buflength = bufsize; } if (parentsigs) { x->x_hop = period * re_parentvecsize; x->x_fill = prologphase ? x->x_buflength - (x->x_hop - prologphase * re_parentvecsize) : x->x_buflength; for (i = 0; i < x->x_nchans; i++) { x->x_rb[i].r_updown.downsample = downsample; x->x_rb[i].r_updown.upsample = upsample; if (upsample == 1 && downsample == 1) dsp_add(vinlet_doprolog, 5, x, insig->s_vec + i * parentvecsize, x->x_rb[i].r_buf, (t_int)(i == x->x_nchans-1), (t_int)re_parentvecsize); else { int method = (x->x_updownmethod == -1? (pd_compatibilitylevel < 44 ? 0 : 1) : x->x_updownmethod); resamplefrom_dsp(&x->x_rb[i].r_updown, insig->s_vec + i * parentvecsize, parentvecsize, re_parentvecsize, method); dsp_add(vinlet_doprolog, 5, x, x->x_rb[i].r_updown.s_vec, x->x_rb[i].r_buf, (t_int)(i == x->x_nchans-1), (t_int)re_parentvecsize); } } } else for (i = 0; i < x->x_nchans; i++) memset((char *)(x->x_rb[i].r_buf), 0, bufsize * sizeof(t_sample)); x->x_directsignal = 0; } else { /* no reblocking; in this case our output signal is "borrowed" and merely needs to be pointed to the real one. */ x->x_directsignal = parentsigs[inlet_getsignalindex(x->x_inlet)]; } } static void *vinlet_newsig(t_symbol *s, int argc, t_atom *argv) { t_vinlet *x = (t_vinlet *)pd_new(vinlet_class); x->x_canvas = canvas_getcurrent(); x->x_inlet = canvas_addinlet(x->x_canvas, &x->x_obj.ob_pd, &s_signal); x->x_nchans = 1; x->x_buflength = 0; x->x_rb = (t_reblocker *)getbytes(sizeof(*x->x_rb)); reblocker_init(x->x_rb, x->x_buflength); x->x_directsignal = 0; x->x_fwdout = 0; outlet_new(&x->x_obj, &s_signal); inlet_new(&x->x_obj, (t_pd *)x->x_inlet, 0, 0); /* this should be thought over: * it might prove hard to provide consistency between labeled up- & * downsampling methods - maybe indices would be better... * * up till now we provide several upsampling methods and 1 single * downsampling method (no filtering !) */ x->x_updownmethod = -1; while (argc-->0) { int method; s = atom_getsymbol(argv++); method = symbol2resamplemethod(s); if (method >= 0) x->x_updownmethod = method; } x->x_fwdout = outlet_new(&x->x_obj, 0); return (x); } static void vinlet_setup(void) { vinlet_class = class_new(gensym("inlet"), (t_newmethod)vinlet_new, (t_method)vinlet_free, sizeof(t_vinlet), CLASS_NOINLET | CLASS_MULTICHANNEL, A_DEFSYM, 0); class_addcreator((t_newmethod)vinlet_newsig, gensym("inlet~"), A_GIMME, 0); class_addbang(vinlet_class, vinlet_bang); class_addpointer(vinlet_class, vinlet_pointer); class_addfloat(vinlet_class, vinlet_float); class_addsymbol(vinlet_class, vinlet_symbol); class_addlist(vinlet_class, vinlet_list); class_addanything(vinlet_class, vinlet_anything); class_addmethod(vinlet_class,(t_method)vinlet_fwd, gensym("fwd"), A_GIMME, 0); class_addmethod(vinlet_class, (t_method)vinlet_dsp, gensym("dsp"), A_CANT, 0); class_sethelpsymbol(vinlet_class, gensym("inlet-outlet")); } /* ------------------------- voutlet -------------------------- */ t_class *voutlet_class; typedef struct _voutlet { t_object x_obj; t_canvas *x_canvas; t_outlet *x_parentoutlet; int x_buflength; int x_empty; /* next to read out of buffer in epilog code */ int x_write; /* next to write in to buffer */ int x_hop; /* hopsize */ int x_updownmethod; /* parent's outlet signal, valid between the prolog and the dsp setup routines. */ t_signal **x_parentsignal; int x_nchans; /* this is also set in prolog & used in dsp */ t_reblocker *x_rb; /* reblocking and resampling, one per channel */ unsigned int x_justcopyout:1; /* switched but not blocked */ unsigned int x_borrowed:1; /* output is borrowed from our inlet */ } t_voutlet; static void *voutlet_new(t_symbol *s) { t_voutlet *x = (t_voutlet *)pd_new(voutlet_class); x->x_canvas = canvas_getcurrent(); x->x_parentoutlet = canvas_addoutlet(x->x_canvas, &x->x_obj.ob_pd, 0); inlet_new(&x->x_obj, &x->x_obj.ob_pd, 0, 0); x->x_buflength = 0; x->x_rb = 0; x->x_nchans = 0; return (x); } static void voutlet_bang(t_voutlet *x) { outlet_bang(x->x_parentoutlet); } static void voutlet_pointer(t_voutlet *x, t_gpointer *gp) { outlet_pointer(x->x_parentoutlet, gp); } static void voutlet_float(t_voutlet *x, t_float f) { outlet_float(x->x_parentoutlet, f); } static void voutlet_symbol(t_voutlet *x, t_symbol *s) { outlet_symbol(x->x_parentoutlet, s); } static void voutlet_list(t_voutlet *x, t_symbol *s, int argc, t_atom *argv) { outlet_list(x->x_parentoutlet, s, argc, argv); } static void voutlet_anything(t_voutlet *x, t_symbol *s, int argc, t_atom *argv) { outlet_anything(x->x_parentoutlet, s, argc, argv); } static void voutlet_free(t_voutlet *x) { int i; canvas_rmoutlet(x->x_canvas, x->x_parentoutlet); if (x->x_rb) for (i = 0; i < x->x_nchans; i++) { freebytes((x->x_rb)[i].r_buf, x->x_buflength * sizeof(t_sample)); resample_free(&(x->x_rb)[i].r_updown); } } t_outlet *voutlet_getit(t_pd *x) { if (pd_class(x) != voutlet_class) bug("voutlet_getit"); return (((t_voutlet *)x)->x_parentoutlet); } /* ------------------------- signal outlet -------------------------- */ int voutlet_issignal(t_voutlet *x) { return (x->x_rb != 0); } /* LATER optimize for non-overlapped case where the "+=" isn't needed */ t_int *voutlet_perform(t_int *w) { t_voutlet *x = (t_voutlet *)(w[1]); t_sample *in = (t_sample *)(w[2]), *buf= (t_sample *)(w[3]); int xwrite = x->x_write; int hop = (int)(w[4]); int n = (int)(w[5]); t_sample *out = buf + xwrite, *endbuf = buf + x->x_buflength; while (n--) { *out++ += *in++; if (out == endbuf) out = buf; } xwrite += x->x_hop; if (xwrite >= x->x_buflength) xwrite = 0; x->x_write = xwrite; return (w+6); } /* epilog code for blocking: write buffer to parent patch */ static t_int *voutlet_doepilog(t_int *w) { t_voutlet *x = (t_voutlet *)(w[1]); t_sample *in, *out = (t_sample *)(w[2]), *buf = (t_sample *)(w[3]); int hop = (int)(w[4]), n = (int)(w[5]), empty = x->x_empty; if (empty == x->x_buflength) empty = 0; x->x_empty = empty + hop; in = buf + empty; for (; n--; in++) *out++ = *in, *in = 0; return (w+6); } /* It's dumb that we have to offer a duplicate but we don't have the goddam resampling buffer allocated by the time this goes on the DSP chain so we have to look it up dynamically. */ static t_int *voutlet_doepilog_resample(t_int *w) { t_voutlet *x = (t_voutlet *)(w[1]); t_sample *in, *out = ((t_resample *)(w[2]))->s_vec, *buf = (t_sample *)(w[3]); int hop = (int)(w[4]), n = (int)(w[5]), empty = x->x_empty; if (empty == x->x_buflength) empty = 0; x->x_empty = empty + hop; in = buf + empty; for (; n--; in++) *out++ = *in, *in = 0; return (w+6); } int outlet_getsignalindex(t_outlet *x); /* prolog for outlets -- store pointer to the outlet on the parent, which, if "reblock" is false, will want to refer back to whatever we see on our input during the "dsp" method called later. */ void voutlet_dspprolog(struct _voutlet *x, t_signal **parentsigs, int myvecsize, int phase, int period, int frequency, int downsample, int upsample, int reblock, int switched) { /* no buffer means we're not a signal outlet */ if (!x->x_rb) return; x->x_justcopyout = (switched && !reblock); x->x_parentsignal = (parentsigs? &parentsigs[outlet_getsignalindex(x->x_parentoutlet)] : 0); if (!parentsigs) return; if (switched || reblock) x->x_borrowed = 0; else /* OK, borrow it */ { int overlap = (*(x->x_parentsignal))->s_overlap; x->x_borrowed = 1; if (!parentsigs) bug("voutlet_dspprolog"); /* create new borrowed signal to be set in dsp routine below */ *(x->x_parentsignal) = signal_new(0, 1, (*(x->x_parentsignal))->s_sr, 0); (*(x->x_parentsignal))->s_overlap = overlap; } if (reblock) { int parentvecsize, buflength, i; int re_parentvecsize; int bigperiod, epilogphase, blockphase; parentvecsize = (*x->x_parentsignal)->s_length; re_parentvecsize = parentvecsize * upsample / downsample; buflength = re_parentvecsize; if (buflength < myvecsize) buflength = myvecsize; if (buflength != x->x_buflength) { for (i = 0; i < x->x_nchans; i++) { x->x_rb[i].r_buf = (t_sample *)t_resizebytes(x->x_rb[i].r_buf, x->x_buflength * sizeof(t_sample), buflength * sizeof(t_sample)); memset((char *)x->x_rb[i].r_buf, 0, buflength * sizeof(t_sample)); } x->x_buflength = buflength; } } } static void voutlet_dsp(t_voutlet *x, t_signal **sp) { if (!x->x_rb) return; reblocker_resize(&x->x_rb, x->x_nchans, sp[0]->s_nchans, x->x_buflength); x->x_nchans = sp[0]->s_nchans; if (x->x_borrowed) { /* if we're just going to make the signal available on the parent patch, hand it off to the parent signal. */ signal_setborrowed(*x->x_parentsignal, sp[0]); } else if (x->x_parentsignal) { int i; signal_setmultiout(x->x_parentsignal, sp[0]->s_nchans); if (x->x_justcopyout) dsp_add_copy(sp[0]->s_vec, (*x->x_parentsignal)->s_vec, sp[0]->s_length * sp[0]->s_nchans); else for (i = 0; i < x->x_nchans; i++) dsp_add(voutlet_perform, 5, x, sp[0]->s_vec + i * sp[0]->s_length, x->x_rb[i].r_buf, (t_int)(i == x->x_nchans - 1 ? x->x_hop : 0), (t_int)sp[0]->s_length); } } /* set up epilog DSP code. If we're reblocking, this is the time to copy the samples out to the containing object's outlets. If we aren't reblocking, there's nothing to do here. */ void voutlet_dspepilog(struct _voutlet *x, t_signal **parentsigs, int myvecsize, int phase, int period, int frequency, int downsample, int upsample, int reblock, int switched) { if (!x->x_rb) return; /* this shouldn't be necesssary... */ if (!x->x_parentsignal) /* toplevels do nothing */ return; if (reblock) { int parentvecsize, i; int re_parentvecsize; int bigperiod, epilogphase, blockphase; if (x->x_parentsignal) { parentvecsize = (*x->x_parentsignal)->s_length; re_parentvecsize = parentvecsize * upsample / downsample; } else { bug("voutlet_dspepilog"); parentvecsize = re_parentvecsize = 1; } bigperiod = myvecsize/re_parentvecsize; if (!bigperiod) bigperiod = 1; epilogphase = phase & (bigperiod - 1); blockphase = (phase + period - 1) & (bigperiod - 1) & (- period); if (re_parentvecsize * period > x->x_buflength) bug("voutlet_dspepilog"); x->x_write = re_parentvecsize * blockphase; if (x->x_write == x->x_buflength) x->x_write = 0; if (period == 1 && frequency > 1) x->x_hop = re_parentvecsize / frequency; else x->x_hop = period * re_parentvecsize; if (x->x_parentsignal) { /* set epilog pointer and schedule it */ x->x_empty = re_parentvecsize * epilogphase; for (i = 0; i < x->x_nchans; i++) { t_int hop = (i == x->x_nchans-1 ? re_parentvecsize : 0); if (upsample * downsample == 1) dsp_add(voutlet_doepilog, 5, x, (*x->x_parentsignal)->s_vec + i * parentvecsize, x->x_rb[i].r_buf, hop, (t_int)parentvecsize); else { int method = (x->x_updownmethod < 0 ? (pd_compatibilitylevel < 44 ? 0 : 1) : x->x_updownmethod); x->x_rb[i].r_updown.downsample=downsample; x->x_rb[i].r_updown.upsample=upsample; dsp_add(voutlet_doepilog_resample, 5, x, &x->x_rb[i].r_updown, x->x_rb[i].r_buf, hop, (t_int)re_parentvecsize); resampleto_dsp(&x->x_rb[i].r_updown, (*x->x_parentsignal)->s_vec + i * parentvecsize, re_parentvecsize, parentvecsize, method); } } } } /* if we aren't blocked but we are switched, the epilog code just copies zeros to the output. In this case the DSP chain contains a function that causes DSP passes to jump over the epilog when the block is switched on, so this only happens when switched off. */ else if (switched) { if (*x->x_parentsignal) dsp_add_zero((*x->x_parentsignal)->s_vec, (*x->x_parentsignal)->s_length * (*x->x_parentsignal)->s_nchans); } } static void *voutlet_newsig(t_symbol *s) { t_voutlet *x = (t_voutlet *)pd_new(voutlet_class); x->x_canvas = canvas_getcurrent(); x->x_parentoutlet = canvas_addoutlet(x->x_canvas, &x->x_obj.ob_pd, &s_signal); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); x->x_buflength = 0; x->x_rb = (t_reblocker *)getbytes(sizeof(*x->x_rb)); reblocker_init(x->x_rb, x->x_buflength); x->x_buflength = 0; x->x_nchans = 1; /* * up till now we provide several upsampling methods and 1 single * downsampling method (no filtering !) */ x->x_updownmethod = symbol2resamplemethod(s); return (x); } static void voutlet_setup(void) { voutlet_class = class_new(gensym("outlet"), (t_newmethod)voutlet_new, (t_method)voutlet_free, sizeof(t_voutlet), CLASS_NOINLET | CLASS_MULTICHANNEL, A_DEFSYM, 0); class_addcreator((t_newmethod)voutlet_newsig, gensym("outlet~"), A_DEFSYM, 0); class_addbang(voutlet_class, voutlet_bang); class_addpointer(voutlet_class, voutlet_pointer); class_addfloat(voutlet_class, (t_method)voutlet_float); class_addsymbol(voutlet_class, voutlet_symbol); class_addlist(voutlet_class, voutlet_list); class_addanything(voutlet_class, voutlet_anything); class_addmethod(voutlet_class, (t_method)voutlet_dsp, gensym("dsp"), A_CANT, 0); class_sethelpsymbol(voutlet_class, gensym("inlet-outlet")); } /* ---------------------------- overall setup ----------------------------- */ void g_io_setup(void) { vinlet_setup(); voutlet_setup(); } ================================================ FILE: libs/libpd/pure-data/src/g_mycanvas.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */ /* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */ #include #include #include "m_pd.h" #include "g_all_guis.h" #ifdef _WIN32 #include #else #include #endif /* ---------- cnv my gui-canvas for a window ---------------- */ t_widgetbehavior my_canvas_widgetbehavior; static t_class *my_canvas_class; /* widget helper functions */ #define my_canvas_draw_update 0 static void my_canvas_draw_io(t_my_canvas* x, t_glist* glist, int mode) { ; } static void my_canvas_draw_config(t_my_canvas* x, t_glist* glist) { const int zoom = IEMGUI_ZOOM(x); t_canvas *canvas = glist_getcanvas(glist); int xpos = text_xpix(&x->x_gui.x_obj, glist); int ypos = text_ypix(&x->x_gui.x_obj, glist); int offset = (zoom > 1 ? zoom : 0); /* keep zoomed border inside visible area */ char tag[128]; t_atom fontatoms[3]; SETSYMBOL(fontatoms+0, gensym(x->x_gui.x_font)); SETFLOAT (fontatoms+1, -(x->x_gui.x_fontsize)*zoom); SETSYMBOL(fontatoms+2, gensym(sys_fontweight)); sprintf(tag, "%pRECT", x); pdgui_vmess(0, "crs iiii", canvas, "coords", tag, xpos, ypos, xpos + x->x_vis_w * zoom, ypos + x->x_vis_h * zoom); pdgui_vmess(0, "crs rk rk", canvas, "itemconfigure", tag, "-fill", x->x_gui.x_bcol, "-outline", x->x_gui.x_bcol); sprintf(tag, "%pBASE", x); pdgui_vmess(0, "crs iiii", canvas, "coords", tag, xpos + offset, ypos + offset, xpos + offset + x->x_gui.x_w, ypos + offset + x->x_gui.x_h); pdgui_vmess(0, "crs ri rk", canvas, "itemconfigure", tag, "-width", zoom, "-outline", (x->x_gui.x_fsf.x_selected ? IEM_GUI_COLOR_SELECTED : x->x_gui.x_bcol)); sprintf(tag, "%pLABEL", x); pdgui_vmess(0, "crs ii", canvas, "coords", tag, xpos + x->x_gui.x_ldx * zoom, ypos + x->x_gui.x_ldy * zoom); pdgui_vmess(0, "crs rA rk", canvas, "itemconfigure", tag, "-font", 3, fontatoms, "-fill", x->x_gui.x_lcol); iemgui_dolabel(x, &x->x_gui, x->x_gui.x_lab, 1); } static void my_canvas_draw_new(t_my_canvas *x, t_glist *glist) { t_canvas *canvas = glist_getcanvas(glist); char tag_object[128], tag[128]; char *tags[] = {tag_object, tag, "label", "text"}; sprintf(tag_object, "%pOBJ", x); sprintf(tag, "%pRECT", x); pdgui_vmess(0, "crr iiii rS", canvas, "create", "rectangle", 0, 0, 0, 0, "-tags", 2, tags); sprintf(tag, "%pBASE", x); pdgui_vmess(0, "crr iiii rS", canvas, "create", "rectangle", 0, 0, 0, 0, "-tags", 2, tags); sprintf(tag, "%pLABEL", x); pdgui_vmess(0, "crr ii rs rS", canvas, "create", "text", 0, 0, "-anchor", "w", "-tags", 4, tags); my_canvas_draw_config(x, glist); } static void my_canvas_draw_select(t_my_canvas* x, t_glist* glist) { t_canvas *canvas = glist_getcanvas(glist); char tag[128]; sprintf(tag, "%pBASE", x); pdgui_vmess(0, "crs rk", canvas, "itemconfigure", tag, "-outline", (x->x_gui.x_fsf.x_selected ? IEM_GUI_COLOR_SELECTED : x->x_gui.x_bcol)); } /* ------------------------ cnv widgetbehaviour----------------------------- */ static void my_canvas_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2) { t_my_canvas *x = (t_my_canvas *)z; *xp1 = text_xpix(&x->x_gui.x_obj, glist); *yp1 = text_ypix(&x->x_gui.x_obj, glist); *xp2 = *xp1 + x->x_gui.x_w; *yp2 = *yp1 + x->x_gui.x_h; } static void my_canvas_save(t_gobj *z, t_binbuf *b) { t_my_canvas *x = (t_my_canvas *)z; t_symbol *bflcol[3]; t_symbol *srl[3]; iemgui_save(&x->x_gui, srl, bflcol); binbuf_addv(b, "ssiisiiisssiiiissi", gensym("#X"),gensym("obj"), (int)x->x_gui.x_obj.te_xpix, (int)x->x_gui.x_obj.te_ypix, gensym("cnv"), x->x_gui.x_w/IEMGUI_ZOOM(x), x->x_vis_w, x->x_vis_h, srl[0], srl[1], srl[2], x->x_gui.x_ldx, x->x_gui.x_ldy, iem_fstyletoint(&x->x_gui.x_fsf), x->x_gui.x_fontsize, bflcol[0], bflcol[2], iem_symargstoint(&x->x_gui.x_isa)); binbuf_addv(b, ";"); } static void my_canvas_properties(t_gobj *z, t_glist *owner) { t_my_canvas *x = (t_my_canvas *)z; iemgui_new_dialog(x, &x->x_gui, "cnv", x->x_gui.x_w/IEMGUI_ZOOM(x), 1, 0, 0, x->x_vis_w, x->x_vis_h, 0, -1, "", "", 0, -1, -1); } static void my_canvas_get_pos(t_my_canvas *x) { if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) { x->x_at[0].a_w.w_float = text_xpix(&x->x_gui.x_obj, x->x_gui.x_glist)/IEMGUI_ZOOM(x); x->x_at[1].a_w.w_float = text_ypix(&x->x_gui.x_obj, x->x_gui.x_glist)/IEMGUI_ZOOM(x); pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at); } } static void my_canvas_dialog(t_my_canvas *x, t_symbol *s, int argc, t_atom *argv) { t_symbol *srl[3]; int a = atom_getfloatarg(0, argc, argv); int w = atom_getfloatarg(2, argc, argv); int h = atom_getfloatarg(3, argc, argv); int sr_flags; t_atom undo[18]; iemgui_setdialogatoms(&x->x_gui, 18, undo); SETFLOAT (undo+1, 0); SETFLOAT (undo+2, x->x_vis_w); SETFLOAT (undo+3, x->x_vis_h); SETFLOAT (undo+5, -1); SETSYMBOL(undo+15, gensym("none")); pd_undo_set_objectstate(x->x_gui.x_glist, (t_pd*)x, gensym("dialog"), 18, undo, argc, argv); sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv); x->x_gui.x_isa.x_loadinit = 0; if(a < 1) a = 1; x->x_gui.x_w = a * IEMGUI_ZOOM(x); x->x_gui.x_h = x->x_gui.x_w; if(w < 1) w = 1; x->x_vis_w = w; if(h < 1) h = 1; x->x_vis_h = h; iemgui_size((void *)x, &x->x_gui); } static void my_canvas_size(t_my_canvas *x, t_symbol *s, int ac, t_atom *av) { int i = atom_getfloatarg(0, ac, av); if(i < 1) i = 1; x->x_gui.x_w = i*IEMGUI_ZOOM(x); x->x_gui.x_h = x->x_gui.x_w; iemgui_size((void *)x, &x->x_gui); } static void my_canvas_delta(t_my_canvas *x, t_symbol *s, int ac, t_atom *av) {iemgui_delta((void *)x, &x->x_gui, s, ac, av);} static void my_canvas_pos(t_my_canvas *x, t_symbol *s, int ac, t_atom *av) {iemgui_pos((void *)x, &x->x_gui, s, ac, av);} static void my_canvas_vis_size(t_my_canvas *x, t_symbol *s, int ac, t_atom *av) { int i; i = atom_getfloatarg(0, ac, av); if(i < 1) i = 1; x->x_vis_w = i; if(ac > 1) { i = atom_getfloatarg(1, ac, av); if(i < 1) i = 1; } x->x_vis_h = i; iemgui_size(x, &x->x_gui); } static void my_canvas_color(t_my_canvas *x, t_symbol *s, int ac, t_atom *av) {iemgui_color((void *)x, &x->x_gui, s, ac, av);} static void my_canvas_send(t_my_canvas *x, t_symbol *s) {iemgui_send(x, &x->x_gui, s);} static void my_canvas_receive(t_my_canvas *x, t_symbol *s) {iemgui_receive(x, &x->x_gui, s);} static void my_canvas_label(t_my_canvas *x, t_symbol *s) {iemgui_label((void *)x, &x->x_gui, s);} static void my_canvas_label_pos(t_my_canvas *x, t_symbol *s, int ac, t_atom *av) {iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);} static void my_canvas_label_font(t_my_canvas *x, t_symbol *s, int ac, t_atom *av) {iemgui_label_font((void *)x, &x->x_gui, s, ac, av);} static void *my_canvas_new(t_symbol *s, int argc, t_atom *argv) { t_my_canvas *x = (t_my_canvas *)iemgui_new(my_canvas_class); int a = IEM_GUI_DEFAULTSIZE; int w = 100 * IEM_GUI_DEFAULTSIZE_SCALE, h = 60 * IEM_GUI_DEFAULTSIZE_SCALE; int ldx = 20, ldy = 12, f = 2, i = 0; int fs = x->x_gui.x_fontsize; IEMGUI_SETDRAWFUNCTIONS(x, my_canvas); x->x_gui.x_bcol = 0xE0E0E0; x->x_gui.x_fcol = 0x00; x->x_gui.x_lcol = 0x404040; if(((argc >= 10)&&(argc <= 13)) &&IS_A_FLOAT(argv,0)&&IS_A_FLOAT(argv,1)&&IS_A_FLOAT(argv,2)) { a = atom_getfloatarg(0, argc, argv); w = atom_getfloatarg(1, argc, argv); h = atom_getfloatarg(2, argc, argv); } if((argc >= 12)&&(IS_A_SYMBOL(argv,3)||IS_A_FLOAT(argv,3))&&(IS_A_SYMBOL(argv,4)||IS_A_FLOAT(argv,4))) { i = 2; iemgui_new_getnames(&x->x_gui, 3, argv); } else if((argc == 11)&&(IS_A_SYMBOL(argv,3)||IS_A_FLOAT(argv,3))) { i = 1; iemgui_new_getnames(&x->x_gui, 3, argv); } else iemgui_new_getnames(&x->x_gui, 3, 0); if(((argc >= 10)&&(argc <= 13)) &&(IS_A_SYMBOL(argv,i+3)||IS_A_FLOAT(argv,i+3))&&IS_A_FLOAT(argv,i+4) &&IS_A_FLOAT(argv,i+5)&&IS_A_FLOAT(argv,i+6) &&IS_A_FLOAT(argv,i+7)) { /* disastrously, the "label" sits in a different part of the message. So we have to track its location separately (in the slot x_labelbindex) and initialize it specially here. */ if(IS_A_FLOAT(argv, i+3)) { char str[80]; atom_string(argv+i+3, str, sizeof(str)); x->x_gui.x_lab = gensym(str); } else { x->x_gui.x_lab = iemgui_new_dogetname(&x->x_gui, i+3, argv); } x->x_gui.x_labelbindex = i+4; ldx = atom_getfloatarg(i+4, argc, argv); ldy = atom_getfloatarg(i+5, argc, argv); iem_inttofstyle(&x->x_gui.x_fsf, atom_getfloatarg(i+6, argc, argv)); fs = atom_getfloatarg(i+7, argc, argv); x->x_gui.x_fontsize = fs; iemgui_all_loadcolors(&x->x_gui, argv+i+8, 0, argv+i+9); } if((argc == 13)&&IS_A_FLOAT(argv,i+10)) { iem_inttosymargs(&x->x_gui.x_isa, atom_getfloatarg(i+10, argc, argv)); } x->x_gui.x_fsf.x_snd_able = (0 != x->x_gui.x_snd); x->x_gui.x_fsf.x_rcv_able = (0 != x->x_gui.x_rcv); if(a < 1) a = 1; x->x_gui.x_w = a; x->x_gui.x_h = x->x_gui.x_w; if(w < 1) w = 1; x->x_vis_w = w; if(h < 1) h = 1; x->x_vis_h = h; if(x->x_gui.x_fsf.x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica"); else if(x->x_gui.x_fsf.x_font_style == 2) strcpy(x->x_gui.x_font, "times"); else { x->x_gui.x_fsf.x_font_style = 0; strcpy(x->x_gui.x_font, sys_font); } if (x->x_gui.x_fsf.x_rcv_able) pd_bind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); x->x_gui.x_ldx = ldx; x->x_gui.x_ldy = ldy; x->x_gui.x_fontsize = (fs < 4)?4:fs; x->x_at[0].a_type = A_FLOAT; x->x_at[1].a_type = A_FLOAT; iemgui_verify_snd_ne_rcv(&x->x_gui); iemgui_newzoom(&x->x_gui); return (x); } static void my_canvas_free(t_my_canvas *x) { if(x->x_gui.x_fsf.x_rcv_able) pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); pdgui_stub_deleteforkey(x); } void g_mycanvas_setup(void) { my_canvas_class = class_new(gensym("cnv"), (t_newmethod)my_canvas_new, (t_method)my_canvas_free, sizeof(t_my_canvas), CLASS_NOINLET, A_GIMME, 0); class_addcreator((t_newmethod)my_canvas_new, gensym("my_canvas"), A_GIMME, 0); class_addmethod(my_canvas_class, (t_method)my_canvas_dialog, gensym("dialog"), A_GIMME, 0); class_addmethod(my_canvas_class, (t_method)my_canvas_size, gensym("size"), A_GIMME, 0); class_addmethod(my_canvas_class, (t_method)my_canvas_delta, gensym("delta"), A_GIMME, 0); class_addmethod(my_canvas_class, (t_method)my_canvas_pos, gensym("pos"), A_GIMME, 0); class_addmethod(my_canvas_class, (t_method)my_canvas_vis_size, gensym("vis_size"), A_GIMME, 0); class_addmethod(my_canvas_class, (t_method)my_canvas_color, gensym("color"), A_GIMME, 0); class_addmethod(my_canvas_class, (t_method)my_canvas_send, gensym("send"), A_DEFSYM, 0); class_addmethod(my_canvas_class, (t_method)my_canvas_receive, gensym("receive"), A_DEFSYM, 0); class_addmethod(my_canvas_class, (t_method)my_canvas_label, gensym("label"), A_DEFSYM, 0); class_addmethod(my_canvas_class, (t_method)my_canvas_label_pos, gensym("label_pos"), A_GIMME, 0); class_addmethod(my_canvas_class, (t_method)my_canvas_label_font, gensym("label_font"), A_GIMME, 0); class_addmethod(my_canvas_class, (t_method)my_canvas_get_pos, gensym("get_pos"), 0); class_addmethod(my_canvas_class, (t_method)iemgui_zoom, gensym("zoom"), A_CANT, 0); my_canvas_widgetbehavior.w_getrectfn = my_canvas_getrect; my_canvas_widgetbehavior.w_displacefn = iemgui_displace; my_canvas_widgetbehavior.w_selectfn = iemgui_select; my_canvas_widgetbehavior.w_activatefn = NULL; my_canvas_widgetbehavior.w_deletefn = iemgui_delete; my_canvas_widgetbehavior.w_visfn = iemgui_vis; my_canvas_widgetbehavior.w_clickfn = NULL; class_setwidget(my_canvas_class, &my_canvas_widgetbehavior); class_setsavefn(my_canvas_class, my_canvas_save); class_setpropertiesfn(my_canvas_class, my_canvas_properties); } ================================================ FILE: libs/libpd/pure-data/src/g_numbox.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* my_numbox.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */ #include #include #include #include "m_pd.h" #include "g_all_guis.h" #include #define MINDIGITS 1 #define MINFONT 4 /*------------------ global functions -------------------------*/ static void my_numbox_key(void *z, t_symbol *keysym, t_floatarg fkey); static void my_numbox_draw_update(t_gobj *client, t_glist *glist); void my_numbox_clip(t_my_numbox *x) { if(x->x_val < x->x_min) x->x_val = x->x_min; if(x->x_val > x->x_max) x->x_val = x->x_max; } void my_numbox_calc_fontwidth(t_my_numbox *x) { int w, f = 31; if(x->x_gui.x_fsf.x_font_style == 1) f = 27; else if(x->x_gui.x_fsf.x_font_style == 2) f = 25; w = x->x_gui.x_fontsize * f * x->x_numwidth; w /= 36; x->x_gui.x_w = (w + (x->x_gui.x_h/2)/IEMGUI_ZOOM(x) + 4) * IEMGUI_ZOOM(x); } void my_numbox_ftoa(t_my_numbox *x) { double f = x->x_val; int bufsize, is_exp = 0, i, idecimal; sprintf(x->x_buf, "%g", f); bufsize = (int)strlen(x->x_buf); if(bufsize >= 5)/* if it is in exponential mode */ { i = bufsize - 4; if((x->x_buf[i] == 'e') || (x->x_buf[i] == 'E')) is_exp = 1; } if(bufsize > x->x_numwidth)/* if to reduce */ { if(is_exp) { if(x->x_numwidth <= 5) { x->x_buf[0] = (f < 0.0 ? '-' : '+'); x->x_buf[1] = 0; } i = bufsize - 4; for(idecimal = 0; idecimal < i; idecimal++) if(x->x_buf[idecimal] == '.') break; if(idecimal > (x->x_numwidth - 4)) { x->x_buf[0] = (f < 0.0 ? '-' : '+'); x->x_buf[1] = 0; } else { int new_exp_index = x->x_numwidth - 4; int old_exp_index = bufsize - 4; for(i = 0; i < 4; i++, new_exp_index++, old_exp_index++) x->x_buf[new_exp_index] = x->x_buf[old_exp_index]; x->x_buf[x->x_numwidth] = 0; } } else { for(idecimal = 0; idecimal < bufsize; idecimal++) if(x->x_buf[idecimal] == '.') break; if(idecimal > x->x_numwidth) { x->x_buf[0] = (f < 0.0 ? '-' : '+'); x->x_buf[1] = 0; } else x->x_buf[x->x_numwidth] = 0; } } } /* ------------ nbx gui-my number box ----------------------- */ t_widgetbehavior my_numbox_widgetbehavior; static t_class *my_numbox_class; #define my_numbox_draw_io 0 static void my_numbox_draw_config(t_my_numbox* x, t_glist* glist) { const int zoom = IEMGUI_ZOOM(x); t_canvas *canvas = glist_getcanvas(glist); t_iemgui *iemgui = &x->x_gui; int xpos = text_xpix(&x->x_gui.x_obj, glist); int ypos = text_ypix(&x->x_gui.x_obj, glist); int w = x->x_gui.x_w, half = x->x_gui.x_h/2; int d = zoom + x->x_gui.x_h/(34*zoom); int corner = x->x_gui.x_h/4; int iow = IOWIDTH * zoom, ioh = IEM_GUI_IOHEIGHT * zoom; char tag[128]; int lcol = x->x_gui.x_lcol; int fcol = x->x_gui.x_fcol; t_atom fontatoms[3]; SETSYMBOL(fontatoms+0, gensym(iemgui->x_font)); SETFLOAT (fontatoms+1, -iemgui->x_fontsize*zoom); SETSYMBOL(fontatoms+2, gensym(sys_fontweight)); if(x->x_gui.x_fsf.x_selected) fcol = lcol = IEM_GUI_COLOR_SELECTED; if(x->x_gui.x_fsf.x_change) fcol = IEM_GUI_COLOR_EDITED; my_numbox_ftoa(x); sprintf(tag, "%pBASE1", x); pdgui_vmess(0, "crs ii ii ii ii ii ii", canvas, "coords", tag, xpos, ypos, xpos + w - corner, ypos, xpos + w, ypos + corner, xpos + w, ypos + x->x_gui.x_h, xpos, ypos + x->x_gui.x_h, xpos, ypos); pdgui_vmess(0, "crs ri rk rk", canvas, "itemconfigure", tag, "-width", zoom, "-outline", IEM_GUI_COLOR_NORMAL, "-fill", x->x_gui.x_bcol); sprintf(tag, "%pBASE2", x); pdgui_vmess(0, "crs ii ii ii", canvas, "coords", tag, xpos + zoom, ypos + zoom, xpos + half, ypos + half, xpos + zoom, ypos + x->x_gui.x_h - zoom); pdgui_vmess(0, "crs ri rk", canvas, "itemconfigure", tag, "-width", zoom, "-fill", x->x_gui.x_fcol); sprintf(tag, "%pLABEL", x); pdgui_vmess(0, "crs ii", canvas, "coords", tag, xpos + x->x_gui.x_ldx * zoom, ypos + x->x_gui.x_ldy * zoom); pdgui_vmess(0, "crs rA rk", canvas, "itemconfigure", tag, "-font", 3, fontatoms, "-fill", lcol); iemgui_dolabel(x, &x->x_gui, x->x_gui.x_lab, 1); sprintf(tag, "%pNUMBER", x); pdgui_vmess(0, "crs ii", canvas, "coords", tag, xpos + half + 2*zoom, ypos + half + d); pdgui_vmess(0, "crs rs rA rk", canvas, "itemconfigure", tag, "-text", x->x_buf, "-font", 3, fontatoms, "-fill", fcol); } static void my_numbox_draw_new(t_my_numbox *x, t_glist *glist) { t_canvas *canvas = glist_getcanvas(glist); char tag[128], tag_object[128]; char*tags[] = {tag_object, tag, "label", "text"}; sprintf(tag_object, "%pOBJ", x); sprintf(tag, "%pBASE1", x); pdgui_vmess(0, "crr ii rS", canvas, "create", "polygon", 0, 0, "-tags", 2, tags); sprintf(tag, "%pBASE2", x); pdgui_vmess(0, "crr iiii rS", canvas, "create", "line", 0, 0, 0, 0, "-tags", 2, tags); sprintf(tag, "%pLABEL", x); pdgui_vmess(0, "crr ii rs rS", canvas, "create", "text", 0, 0, "-anchor", "w", "-tags", 4, tags); sprintf(tag, "%pNUMBER", x); pdgui_vmess(0, "crr ii rs rS", canvas, "create", "text", 0, 0, "-anchor", "w", "-tags", 2, tags); my_numbox_draw_config(x, glist); (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_IO); } static void my_numbox_draw_select(t_my_numbox *x, t_glist *glist) { t_canvas *canvas = glist_getcanvas(glist); int bcol = IEM_GUI_COLOR_NORMAL, lcol = x->x_gui.x_lcol, fcol = x->x_gui.x_fcol; char tag[128]; if(x->x_gui.x_fsf.x_selected) { if(x->x_gui.x_fsf.x_change) { x->x_gui.x_fsf.x_change = 0; x->x_buf[0] = 0; sys_queuegui(x, x->x_gui.x_glist, my_numbox_draw_update); } bcol = lcol = fcol = IEM_GUI_COLOR_SELECTED; } sprintf(tag, "%pBASE1", x); pdgui_vmess(0, "crs rk", canvas, "itemconfigure", tag, "-outline", bcol); sprintf(tag, "%pBASE2", x); pdgui_vmess(0, "crs rk", canvas, "itemconfigure", tag, "-fill", fcol); sprintf(tag, "%pLABEL", x); pdgui_vmess(0, "crs rk", canvas, "itemconfigure", tag, "-fill", lcol); sprintf(tag, "%pNUMBER", x); pdgui_vmess(0, "crs rk", canvas, "itemconfigure", tag, "-fill", fcol); } static void my_numbox_draw_update(t_gobj *client, t_glist *glist) { t_my_numbox *x = (t_my_numbox *)client; if(glist_isvisible(glist)) { t_canvas *canvas = glist_getcanvas(glist); char tag[128]; sprintf(tag, "%pNUMBER", x); if(x->x_gui.x_fsf.x_change) { if(x->x_buf[0]) { char *cp = x->x_buf; int sl = (int)strlen(x->x_buf); x->x_buf[sl] = '>'; x->x_buf[sl+1] = 0; if(sl >= x->x_numwidth) cp += sl - x->x_numwidth + 1; pdgui_vmess(0, "crs rk rs", canvas, "itemconfigure", tag, "-fill", IEM_GUI_COLOR_EDITED, "-text", cp); x->x_buf[sl] = 0; } else { my_numbox_ftoa(x); pdgui_vmess(0, "crs rk rs", canvas, "itemconfigure", tag, "-fill", IEM_GUI_COLOR_EDITED, "-text", x->x_buf); x->x_buf[0] = 0; } } else { my_numbox_ftoa(x); pdgui_vmess(0, "crs rk rs", canvas, "itemconfigure", tag, "-fill", (x->x_gui.x_fsf.x_selected ? IEM_GUI_COLOR_SELECTED : x->x_gui.x_fcol), "-text", x->x_buf); x->x_buf[0] = 0; } } } /* widget helper functions */ static void my_numbox_tick_wait(t_my_numbox *x) { sys_queuegui(x, x->x_gui.x_glist, my_numbox_draw_update); } /* ------------------------ nbx widgetbehaviour----------------------------- */ static void my_numbox_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2) { t_my_numbox* x = (t_my_numbox*)z; *xp1 = text_xpix(&x->x_gui.x_obj, glist); *yp1 = text_ypix(&x->x_gui.x_obj, glist); *xp2 = *xp1 + x->x_gui.x_w; *yp2 = *yp1 + x->x_gui.x_h; } static void my_numbox_save(t_gobj *z, t_binbuf *b) { t_my_numbox *x = (t_my_numbox *)z; t_symbol *bflcol[3]; t_symbol *srl[3]; iemgui_save(&x->x_gui, srl, bflcol); if(x->x_gui.x_fsf.x_change) { x->x_gui.x_fsf.x_change = 0; sys_queuegui(x, x->x_gui.x_glist, my_numbox_draw_update); } binbuf_addv(b, "ssiisiiffiisssiiiisssfi", gensym("#X"), gensym("obj"), (int)x->x_gui.x_obj.te_xpix, (int)x->x_gui.x_obj.te_ypix, gensym("nbx"), x->x_numwidth, x->x_gui.x_h/IEMGUI_ZOOM(x), (t_float)x->x_min, (t_float)x->x_max, x->x_lin0_log1, iem_symargstoint(&x->x_gui.x_isa), srl[0], srl[1], srl[2], x->x_gui.x_ldx, x->x_gui.x_ldy, iem_fstyletoint(&x->x_gui.x_fsf), x->x_gui.x_fontsize, bflcol[0], bflcol[1], bflcol[2], x->x_gui.x_isa.x_loadinit?x->x_val:0., x->x_log_height); binbuf_addv(b, ";"); } int my_numbox_check_minmax(t_my_numbox *x, double min, double max) { int ret = 0; if(x->x_lin0_log1) { if((min == 0.0) && (max == 0.0)) max = 1.0; if(max > 0.0) { if(min <= 0.0) min = 0.01 * max; } else { if(min > 0.0) max = 0.01 * min; } } x->x_min = min; x->x_max = max; if(x->x_val < x->x_min) { x->x_val = x->x_min; ret = 1; } if(x->x_val > x->x_max) { x->x_val = x->x_max; ret = 1; } if(x->x_lin0_log1) x->x_k = exp(log(x->x_max/x->x_min) / (double)(x->x_log_height)); else x->x_k = 1.0; return(ret); } static void my_numbox_properties(t_gobj *z, t_glist *owner) { t_my_numbox *x = (t_my_numbox *)z; if(x->x_gui.x_fsf.x_change) { x->x_gui.x_fsf.x_change = 0; sys_queuegui(x, x->x_gui.x_glist, my_numbox_draw_update); } iemgui_new_dialog(x, &x->x_gui, "nbx", x->x_numwidth, MINDIGITS, x->x_gui.x_h/IEMGUI_ZOOM(x), IEM_GUI_MINSIZE, x->x_min, x->x_max, 0, x->x_lin0_log1, "linear", "logarithmic", 1, -1, x->x_log_height); } static void my_numbox_bang(t_my_numbox *x) { outlet_float(x->x_gui.x_obj.ob_outlet, x->x_val); if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) pd_float(x->x_gui.x_snd->s_thing, x->x_val); } static void my_numbox_dialog(t_my_numbox *x, t_symbol *s, int argc, t_atom *argv) { t_symbol *srl[3]; int w = (int)atom_getfloatarg(0, argc, argv); int h = (int)atom_getfloatarg(1, argc, argv); double min = (double)atom_getfloatarg(2, argc, argv); double max = (double)atom_getfloatarg(3, argc, argv); int lilo = (int)atom_getfloatarg(4, argc, argv); int log_height = (int)atom_getfloatarg(6, argc, argv); int sr_flags; t_atom undo[18]; iemgui_setdialogatoms(&x->x_gui, 18, undo); SETFLOAT(undo+0, x->x_numwidth); SETFLOAT(undo+2, x->x_min); SETFLOAT(undo+3, x->x_max); SETFLOAT(undo+4, x->x_lin0_log1); SETFLOAT(undo+6, x->x_log_height); pd_undo_set_objectstate(x->x_gui.x_glist, (t_pd*)x, gensym("dialog"), 18, undo, argc, argv); if(lilo != 0) lilo = 1; x->x_lin0_log1 = lilo; sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv); if(w < MINDIGITS) w = MINDIGITS; x->x_numwidth = w; if(h < IEM_GUI_MINSIZE) h = IEM_GUI_MINSIZE; x->x_gui.x_h = h * IEMGUI_ZOOM(x); if(log_height < 10) log_height = 10; x->x_log_height = log_height; my_numbox_calc_fontwidth(x); /*if(my_numbox_check_minmax(x, min, max)) my_numbox_bang(x);*/ my_numbox_check_minmax(x, min, max); (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); iemgui_size(x, &x->x_gui); } static void my_numbox_motion(t_my_numbox *x, t_floatarg dx, t_floatarg dy, t_floatarg up) { double k2 = 1.0; if (up != 0) return; if(x->x_gui.x_fsf.x_finemoved) k2 = 0.01; if(x->x_lin0_log1) x->x_val *= pow(x->x_k, -k2*dy); else x->x_val -= k2*dy; my_numbox_clip(x); sys_queuegui(x, x->x_gui.x_glist, my_numbox_draw_update); my_numbox_bang(x); } static void my_numbox_click(t_my_numbox *x, t_floatarg xpos, t_floatarg ypos, t_floatarg shift, t_floatarg ctrl, t_floatarg alt) { glist_grab(x->x_gui.x_glist, &x->x_gui.x_obj.te_g, (t_glistmotionfn)my_numbox_motion, my_numbox_key, xpos, ypos); } static int my_numbox_newclick(t_gobj *z, struct _glist *glist, int xpix, int ypix, int shift, int alt, int dbl, int doit) { t_my_numbox* x = (t_my_numbox *)z; if(doit) { my_numbox_click( x, (t_floatarg)xpix, (t_floatarg)ypix, (t_floatarg)shift, 0, (t_floatarg)alt); if(shift) x->x_gui.x_fsf.x_finemoved = 1; else x->x_gui.x_fsf.x_finemoved = 0; if(!x->x_gui.x_fsf.x_change) { clock_delay(x->x_clock_wait, 50); x->x_gui.x_fsf.x_change = 1; x->x_buf[0] = 0; } else { x->x_gui.x_fsf.x_change = 0; x->x_buf[0] = 0; sys_queuegui(x, x->x_gui.x_glist, my_numbox_draw_update); } } return (1); } static void my_numbox_set(t_my_numbox *x, t_floatarg f) { t_float ftocompare = f; /* bitwise comparison, suggested by Dan Borstein - to make this work ftocompare must be t_float type like x_val. */ if (memcmp(&ftocompare, &x->x_val, sizeof(ftocompare))) { x->x_val = ftocompare; if (pd_compatibilitylevel < 53) my_numbox_clip(x); sys_queuegui(x, x->x_gui.x_glist, my_numbox_draw_update); } } static void my_numbox_log_height(t_my_numbox *x, t_floatarg lh) { if(lh < 10.0) lh = 10.0; x->x_log_height = (int)lh; if(x->x_lin0_log1) x->x_k = exp(log(x->x_max/x->x_min)/(double)(x->x_log_height)); else x->x_k = 1.0; } static void my_numbox_float(t_my_numbox *x, t_floatarg f) { my_numbox_set(x, f); if(x->x_gui.x_fsf.x_put_in2out) my_numbox_bang(x); } static void my_numbox_size(t_my_numbox *x, t_symbol *s, int ac, t_atom *av) { int h, w; w = (int)atom_getfloatarg(0, ac, av); if(w < MINDIGITS) w = MINDIGITS; x->x_numwidth = w; if(ac > 1) { h = (int)atom_getfloatarg(1, ac, av); if(h < IEM_GUI_MINSIZE) h = IEM_GUI_MINSIZE; x->x_gui.x_h = h * IEMGUI_ZOOM(x); } my_numbox_calc_fontwidth(x); iemgui_size((void *)x, &x->x_gui); (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); } static void my_numbox_delta(t_my_numbox *x, t_symbol *s, int ac, t_atom *av) {iemgui_delta((void *)x, &x->x_gui, s, ac, av);} static void my_numbox_pos(t_my_numbox *x, t_symbol *s, int ac, t_atom *av) {iemgui_pos((void *)x, &x->x_gui, s, ac, av);} static void my_numbox_range(t_my_numbox *x, t_symbol *s, int ac, t_atom *av) { if(my_numbox_check_minmax(x, (double)atom_getfloatarg(0, ac, av), (double)atom_getfloatarg(1, ac, av))) { sys_queuegui(x, x->x_gui.x_glist, my_numbox_draw_update); /*my_numbox_bang(x);*/ } } static void my_numbox_color(t_my_numbox *x, t_symbol *s, int ac, t_atom *av) {iemgui_color((void *)x, &x->x_gui, s, ac, av);} static void my_numbox_send(t_my_numbox *x, t_symbol *s) {iemgui_send(x, &x->x_gui, s);} static void my_numbox_receive(t_my_numbox *x, t_symbol *s) {iemgui_receive(x, &x->x_gui, s);} static void my_numbox_label(t_my_numbox *x, t_symbol *s) {iemgui_label((void *)x, &x->x_gui, s);} static void my_numbox_label_pos(t_my_numbox *x, t_symbol *s, int ac, t_atom *av) {iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);} static void my_numbox_label_font(t_my_numbox *x, t_symbol *s, int ac, t_atom *av) { int f = (int)atom_getfloatarg(1, ac, av); if(f < 4) f = 4; x->x_gui.x_fontsize = f; f = (int)atom_getfloatarg(0, ac, av); if((f < 0) || (f > 2)) f = 0; x->x_gui.x_fsf.x_font_style = f; my_numbox_calc_fontwidth(x); iemgui_label_font((void *)x, &x->x_gui, s, ac, av); } static void my_numbox_log(t_my_numbox *x) { x->x_lin0_log1 = 1; if(my_numbox_check_minmax(x, x->x_min, x->x_max)) { sys_queuegui(x, x->x_gui.x_glist, my_numbox_draw_update); /*my_numbox_bang(x);*/ } } static void my_numbox_lin(t_my_numbox *x) { x->x_lin0_log1 = 0; } static void my_numbox_init(t_my_numbox *x, t_floatarg f) { x->x_gui.x_isa.x_loadinit = (f == 0.0) ? 0 : 1; } static void my_numbox_loadbang(t_my_numbox *x, t_floatarg action) { if(action == LB_LOAD && x->x_gui.x_isa.x_loadinit) { sys_queuegui(x, x->x_gui.x_glist, my_numbox_draw_update); my_numbox_bang(x); } } static void my_numbox_key(void *z, t_symbol *keysym, t_floatarg fkey) { t_my_numbox *x = z; char c = fkey; char buf[3]; buf[1] = 0; if(c == 0) { x->x_gui.x_fsf.x_change = 0; sys_queuegui(x, x->x_gui.x_glist, my_numbox_draw_update); return; } if(((c >= '0') && (c <= '9')) || (c == '.') || (c == '-') || (c == 'e') || (c == '+') || (c == 'E')) { if(strlen(x->x_buf) < (IEMGUI_MAX_NUM_LEN-2)) { buf[0] = c; strcat(x->x_buf, buf); sys_queuegui(x, x->x_gui.x_glist, my_numbox_draw_update); } } else if((c == '\b') || (c == 127)) { int sl = (int)strlen(x->x_buf) - 1; if(sl < 0) sl = 0; x->x_buf[sl] = 0; sys_queuegui(x, x->x_gui.x_glist, my_numbox_draw_update); } else if((c == '\n') || (c == 13)) { if(x->x_buf[0]) { x->x_val = atof(x->x_buf); x->x_buf[0] = 0; if (pd_compatibilitylevel < 53) my_numbox_clip(x); sys_queuegui(x, x->x_gui.x_glist, my_numbox_draw_update); } my_numbox_bang(x); } } static void my_numbox_list(t_my_numbox *x, t_symbol *s, int ac, t_atom *av) { if(!ac) { my_numbox_bang(x); } else if(IS_A_FLOAT(av, 0)) { my_numbox_set(x, atom_getfloatarg(0, ac, av)); my_numbox_bang(x); } } static void *my_numbox_new(t_symbol *s, int argc, t_atom *argv) { t_my_numbox *x = (t_my_numbox *)iemgui_new(my_numbox_class); int w = 5, h = 14 * IEM_GUI_DEFAULTSIZE_SCALE; int lilo = 0, ldx = 0, ldy = -8 * IEM_GUI_DEFAULTSIZE_SCALE; int fs = x->x_gui.x_fontsize; int log_height = 256; double min = -1.0e+37, max = 1.0e+37, v = 0.0; IEMGUI_SETDRAWFUNCTIONS(x, my_numbox); if((argc >= 17)&&IS_A_FLOAT(argv,0)&&IS_A_FLOAT(argv,1) &&IS_A_FLOAT(argv,2)&&IS_A_FLOAT(argv,3) &&IS_A_FLOAT(argv,4)&&IS_A_FLOAT(argv,5) &&(IS_A_SYMBOL(argv,6)||IS_A_FLOAT(argv,6)) &&(IS_A_SYMBOL(argv,7)||IS_A_FLOAT(argv,7)) &&(IS_A_SYMBOL(argv,8)||IS_A_FLOAT(argv,8)) &&IS_A_FLOAT(argv,9)&&IS_A_FLOAT(argv,10) &&IS_A_FLOAT(argv,11)&&IS_A_FLOAT(argv,12)&&IS_A_FLOAT(argv,16)) { w = (int)atom_getfloatarg(0, argc, argv); h = (int)atom_getfloatarg(1, argc, argv); min = (double)atom_getfloatarg(2, argc, argv); max = (double)atom_getfloatarg(3, argc, argv); lilo = (int)atom_getfloatarg(4, argc, argv); iem_inttosymargs(&x->x_gui.x_isa, atom_getfloatarg(5, argc, argv)); iemgui_new_getnames(&x->x_gui, 6, argv); ldx = (int)atom_getfloatarg(9, argc, argv); ldy = (int)atom_getfloatarg(10, argc, argv); iem_inttofstyle(&x->x_gui.x_fsf, atom_getfloatarg(11, argc, argv)); fs = (int)atom_getfloatarg(12, argc, argv); iemgui_all_loadcolors(&x->x_gui, argv+13, argv+14, argv+15); v = atom_getfloatarg(16, argc, argv); } else iemgui_new_getnames(&x->x_gui, 6, 0); if((argc == 18)&&IS_A_FLOAT(argv,17)) { log_height = (int)atom_getfloatarg(17, argc, argv); } x->x_gui.x_fsf.x_snd_able = (0 != x->x_gui.x_snd); x->x_gui.x_fsf.x_rcv_able = (0 != x->x_gui.x_rcv); if(x->x_gui.x_isa.x_loadinit) x->x_val = v; else x->x_val = 0.0; if(lilo != 0) lilo = 1; x->x_lin0_log1 = lilo; if(log_height < 10) log_height = 10; x->x_log_height = log_height; if(x->x_gui.x_fsf.x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica"); else if(x->x_gui.x_fsf.x_font_style == 2) strcpy(x->x_gui.x_font, "times"); else { x->x_gui.x_fsf.x_font_style = 0; strcpy(x->x_gui.x_font, sys_font); } if(x->x_gui.x_fsf.x_rcv_able) pd_bind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); x->x_gui.x_ldx = ldx; x->x_gui.x_ldy = ldy; x->x_gui.x_fontsize = (fs < MINFONT)?MINFONT:fs; if(w < MINDIGITS) w = MINDIGITS; x->x_numwidth = w; if(h < IEM_GUI_MINSIZE) h = IEM_GUI_MINSIZE; x->x_gui.x_h = h; x->x_buf[0] = 0; my_numbox_check_minmax(x, min, max); iemgui_verify_snd_ne_rcv(&x->x_gui); x->x_clock_wait = clock_new(x, (t_method)my_numbox_tick_wait); x->x_gui.x_fsf.x_change = 0; iemgui_newzoom(&x->x_gui); my_numbox_calc_fontwidth(x); outlet_new(&x->x_gui.x_obj, &s_float); return (x); } static void my_numbox_free(t_my_numbox *x) { if(x->x_gui.x_fsf.x_rcv_able) pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); clock_free(x->x_clock_wait); pdgui_stub_deleteforkey(x); } void g_numbox_setup(void) { my_numbox_class = class_new(gensym("nbx"), (t_newmethod)my_numbox_new, (t_method)my_numbox_free, sizeof(t_my_numbox), 0, A_GIMME, 0); class_addcreator((t_newmethod)my_numbox_new, gensym("my_numbox"), A_GIMME, 0); class_addbang(my_numbox_class, my_numbox_bang); class_addfloat(my_numbox_class, my_numbox_float); class_addlist(my_numbox_class, my_numbox_list); class_addmethod(my_numbox_class, (t_method)my_numbox_click, gensym("click"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); class_addmethod(my_numbox_class, (t_method)my_numbox_motion, gensym("motion"), A_FLOAT, A_FLOAT, A_DEFFLOAT, 0); class_addmethod(my_numbox_class, (t_method)my_numbox_dialog, gensym("dialog"), A_GIMME, 0); class_addmethod(my_numbox_class, (t_method)my_numbox_loadbang, gensym("loadbang"), A_DEFFLOAT, 0); class_addmethod(my_numbox_class, (t_method)my_numbox_set, gensym("set"), A_FLOAT, 0); class_addmethod(my_numbox_class, (t_method)my_numbox_size, gensym("size"), A_GIMME, 0); class_addmethod(my_numbox_class, (t_method)my_numbox_delta, gensym("delta"), A_GIMME, 0); class_addmethod(my_numbox_class, (t_method)my_numbox_pos, gensym("pos"), A_GIMME, 0); class_addmethod(my_numbox_class, (t_method)my_numbox_range, gensym("range"), A_GIMME, 0); class_addmethod(my_numbox_class, (t_method)my_numbox_color, gensym("color"), A_GIMME, 0); class_addmethod(my_numbox_class, (t_method)my_numbox_send, gensym("send"), A_DEFSYM, 0); class_addmethod(my_numbox_class, (t_method)my_numbox_receive, gensym("receive"), A_DEFSYM, 0); class_addmethod(my_numbox_class, (t_method)my_numbox_label, gensym("label"), A_DEFSYM, 0); class_addmethod(my_numbox_class, (t_method)my_numbox_label_pos, gensym("label_pos"), A_GIMME, 0); class_addmethod(my_numbox_class, (t_method)my_numbox_label_font, gensym("label_font"), A_GIMME, 0); class_addmethod(my_numbox_class, (t_method)my_numbox_log, gensym("log"), 0); class_addmethod(my_numbox_class, (t_method)my_numbox_lin, gensym("lin"), 0); class_addmethod(my_numbox_class, (t_method)my_numbox_init, gensym("init"), A_FLOAT, 0); class_addmethod(my_numbox_class, (t_method)my_numbox_log_height, gensym("log_height"), A_FLOAT, 0); class_addmethod(my_numbox_class, (t_method)iemgui_zoom, gensym("zoom"), A_CANT, 0); my_numbox_widgetbehavior.w_getrectfn = my_numbox_getrect; my_numbox_widgetbehavior.w_displacefn = iemgui_displace; my_numbox_widgetbehavior.w_selectfn = iemgui_select; my_numbox_widgetbehavior.w_activatefn = NULL; my_numbox_widgetbehavior.w_deletefn = iemgui_delete; my_numbox_widgetbehavior.w_visfn = iemgui_vis; my_numbox_widgetbehavior.w_clickfn = my_numbox_newclick; class_setwidget(my_numbox_class, &my_numbox_widgetbehavior); class_setsavefn(my_numbox_class, my_numbox_save); class_setpropertiesfn(my_numbox_class, my_numbox_properties); } ================================================ FILE: libs/libpd/pure-data/src/g_radio.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* [hv]dial.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */ /* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */ /* name change to [vv]radio by MSP (it's a radio button really) and changed to put out a "float" as in sliders, toggles, etc. */ #include #include #include "m_pd.h" #include "g_all_guis.h" /* ------------- hdl gui-horizontal dial ---------------------- */ t_widgetbehavior radio_widgetbehavior; static t_class *radio_class; /* widget helper functions */ /* cannot use iemgui's default draw_iolets, because * - vradio would use show the outlet at the 0th button rather than the last... */ static void radio_draw_io(t_radio* x, t_glist* glist, int old_snd_rcv_flags) { const int zoom = IEMGUI_ZOOM(x); int xpos = text_xpix(&x->x_gui.x_obj, glist); int ypos = text_ypix(&x->x_gui.x_obj, glist); int iow = IOWIDTH * zoom, ioh = IEM_GUI_IOHEIGHT * zoom; t_canvas *canvas = glist_getcanvas(glist); char tag_object[128], tag_but[128], tag[128]; char *tags[] = {tag_object, tag}; (void)old_snd_rcv_flags; sprintf(tag_object, "%pOBJ", x); sprintf(tag_but, "%pBUT", x); sprintf(tag, "%pOUT%d", x, 0); pdgui_vmess(0, "crs", canvas, "delete", tag); if(!x->x_gui.x_fsf.x_snd_able) { int height = x->x_gui.x_h * ((x->x_orientation == horizontal)? 1: x->x_number); pdgui_vmess(0, "crr iiii rs rS", canvas, "create", "rectangle", xpos, ypos + height + zoom - ioh, xpos + iow, ypos + height, "-fill", "black", "-tags", 2, tags); /* keep buttons above outlet */ pdgui_vmess(0, "crss", canvas, "lower", tag, tag_but); } sprintf(tag, "%pIN%d", x, 0); pdgui_vmess(0, "crs", canvas, "delete", tag); if(!x->x_gui.x_fsf.x_rcv_able) { pdgui_vmess(0, "crr iiii rs rS", canvas, "create", "rectangle", xpos, ypos, xpos + iow, ypos - zoom + ioh, "-fill", "black", "-tags", 2, tags); /* keep buttons above inlet */ pdgui_vmess(0, "crss", canvas, "lower", tag, tag_but); } } static void radio_draw_config(t_radio* x, t_glist* glist) { int i; const int zoom = IEMGUI_ZOOM(x); t_iemgui *iemgui = &x->x_gui; t_canvas *canvas = glist_getcanvas(glist); int iow = IOWIDTH * zoom, ioh = IEM_GUI_IOHEIGHT * zoom; int xx11b = text_xpix(&x->x_gui.x_obj, glist); int yy11b = text_ypix(&x->x_gui.x_obj, glist); int d, dx = 0, dy = 0, d4; int xx11=xx11b, xx12=0, xx21=0, xx22=0; int yy11=yy11b, yy12=0, yy21=0, yy22=0; char tag[128]; t_atom fontatoms[3]; SETSYMBOL(fontatoms+0, gensym(iemgui->x_font)); SETFLOAT (fontatoms+1, -iemgui->x_fontsize*zoom); SETSYMBOL(fontatoms+2, gensym(sys_fontweight)); if(x->x_orientation == horizontal) { d = dx = x->x_gui.x_w; } else { d = dy = x->x_gui.x_h; } d4 = d / 4; xx12 = xx11 + d; xx21 = xx11 + d4; xx22 = xx12 - d4; yy12 = yy11 + d; yy21 = yy11 + d4; yy22 = yy12 - d4; for(i = 0; i < x->x_number; i++) { int col = (x->x_on == i) ? x->x_gui.x_fcol : x->x_gui.x_bcol; sprintf(tag, "%pBASE%d", x, i); pdgui_vmess(0, "crs iiii", canvas, "coords", tag, xx11, yy11, xx12, yy12); pdgui_vmess(0, "crs ri rk", canvas, "itemconfigure", tag, "-width", zoom, "-fill", x->x_gui.x_bcol); sprintf(tag, "%pBUT%d", x, i); pdgui_vmess(0, "crs iiii", canvas, "coords", tag, xx21, yy21, xx22, yy22); pdgui_vmess(0, "crs rk rk", canvas, "itemconfigure", tag, "-fill", col, "-outline", col); xx11 += dx; xx12 += dx; xx21 += dx; xx22 += dx; yy11 += dy; yy12 += dy; yy21 += dy; yy22 += dy; x->x_drawn = x->x_on; } sprintf(tag, "%pLABEL", x); pdgui_vmess(0, "crs ii", canvas, "coords", tag, xx11b + x->x_gui.x_ldx * zoom, yy11b + x->x_gui.x_ldy * zoom); pdgui_vmess(0, "crs rA rk", canvas, "itemconfigure", tag, "-font", 3, fontatoms, "-fill", x->x_gui.x_lcol); iemgui_dolabel(x, &x->x_gui, x->x_gui.x_lab, 1); } static void radio_draw_new(t_radio *x, t_glist *glist) { t_canvas *canvas = glist_getcanvas(glist); int i; char tag_n[128], tag[128], tag_object[128]; char *tags[] = {tag_object, tag, tag_n, "text"}; sprintf(tag_object, "%pOBJ", x); for(i=0; ix_number; i++) { sprintf(tag, "%pBASE", x); sprintf(tag_n, "%pBASE%d", x, i); pdgui_vmess(0, "crr iiii rS", canvas, "create", "rectangle", 0, 0, 0, 0, "-tags", 3, tags); sprintf(tag, "%pBUT", x); sprintf(tag_n, "%pBUT%d", x, i); pdgui_vmess(0, "crr iiii rS", canvas, "create", "rectangle", 0, 0, 0, 0, "-tags", 3, tags); } /* make sure the buttons are above their base */ sprintf(tag, "%pBUT", x); sprintf(tag_n, "%pBASE", x); pdgui_vmess(0, "crss", canvas, "raise", tag, tag_n); sprintf(tag, "%pLABEL", x); sprintf(tag_n, "label"); pdgui_vmess(0, "crr ii rs rS", canvas, "create", "text", 0, 0, "-anchor", "w", "-tags", 4, tags); radio_draw_config(x, glist); (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_IO); } static void radio_draw_select(t_radio* x, t_glist* glist) { int n = x->x_number, i; t_canvas *canvas = glist_getcanvas(glist); int lcol = x->x_gui.x_lcol; int col = IEM_GUI_COLOR_NORMAL; char tag[128]; if(x->x_gui.x_fsf.x_selected) lcol = col = IEM_GUI_COLOR_SELECTED; sprintf(tag, "%pBASE", x); pdgui_vmess(0, "crs rk", canvas, "itemconfigure", tag, "-outline", col); sprintf(tag, "%pLABEL", x); pdgui_vmess(0, "crs rk", canvas, "itemconfigure", tag, "-fill", lcol); } static void radio_draw_update(t_gobj *client, t_glist *glist) { t_radio *x = (t_radio *)client; if(glist_isvisible(glist)) { t_canvas *canvas = glist_getcanvas(glist); char tag[128]; sprintf(tag, "%pBUT%d", x, x->x_drawn); pdgui_vmess(0, "crs rk rk", canvas, "itemconfigure", tag, "-fill", x->x_gui.x_bcol, "-outline", x->x_gui.x_bcol); sprintf(tag, "%pBUT%d", x, x->x_on); pdgui_vmess(0, "crs rk rk", canvas, "itemconfigure", tag, "-fill", x->x_gui.x_fcol, "-outline", x->x_gui.x_fcol); x->x_drawn = x->x_on; } } /* ------------------------ hdl widgetbehaviour----------------------------- */ static void radio_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2) { t_radio *x = (t_radio *)z; *xp1 = text_xpix(&x->x_gui.x_obj, glist); *yp1 = text_ypix(&x->x_gui.x_obj, glist); if(x->x_orientation == horizontal) { *xp2 = *xp1 + x->x_gui.x_w * x->x_number; *yp2 = *yp1 + x->x_gui.x_h; } else { *xp2 = *xp1 + x->x_gui.x_w; *yp2 = *yp1 + x->x_gui.x_h * x->x_number; } } static void radio_save(t_gobj *z, t_binbuf *b) { t_radio *x = (t_radio *)z; t_symbol *bflcol[3]; t_symbol *srl[3]; const char*objname; if(x->x_orientation == horizontal) { if(x->x_compat) objname="hdl"; else objname="hradio"; } else { if(x->x_compat) objname="vdl"; else objname="vradio"; } iemgui_save(&x->x_gui, srl, bflcol); binbuf_addv(b, "ssiisiiiisssiiiisssf", gensym("#X"), gensym("obj"), (int)x->x_gui.x_obj.te_xpix, (int)x->x_gui.x_obj.te_ypix, gensym(objname), x->x_gui.x_w/IEMGUI_ZOOM(x), x->x_change, iem_symargstoint(&x->x_gui.x_isa), x->x_number, srl[0], srl[1], srl[2], x->x_gui.x_ldx, x->x_gui.x_ldy, iem_fstyletoint(&x->x_gui.x_fsf), x->x_gui.x_fontsize, bflcol[0], bflcol[1], bflcol[2], x->x_gui.x_isa.x_loadinit?x->x_fval:0.); binbuf_addv(b, ";"); } static void radio_properties(t_gobj *z, t_glist *owner) { t_radio *x = (t_radio *)z; int hchange = -1; const char*objname; if(x->x_orientation == horizontal) { objname = "hradio"; } else { objname = "vradio"; } if(x->x_compat) hchange = x->x_change; iemgui_new_dialog(x, &x->x_gui, objname, x->x_gui.x_w/IEMGUI_ZOOM(x), IEM_GUI_MINSIZE, 0, 0, 0, 0, 0, hchange, "new-only", "new&old", 1, -1, x->x_number); } static void radio_dialog(t_radio *x, t_symbol *s, int argc, t_atom *argv) { t_symbol *srl[3]; int a = (int)atom_getfloatarg(0, argc, argv); int chg = (int)atom_getfloatarg(4, argc, argv); int num = (int)atom_getfloatarg(6, argc, argv); int sr_flags; int redraw = 0; t_atom undo[18]; iemgui_setdialogatoms(&x->x_gui, 18, undo); SETFLOAT(undo+1, 0); SETFLOAT(undo+2, 0); SETFLOAT(undo+3, 0); SETFLOAT(undo+4, (x->x_compat)?x->x_change:-1); SETFLOAT(undo+6, x->x_number); pd_undo_set_objectstate(x->x_gui.x_glist, (t_pd*)x, gensym("dialog"), 18, undo, argc, argv); if(chg != 0) chg = 1; x->x_change = chg; sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv); x->x_gui.x_w = iemgui_clip_size(a) * IEMGUI_ZOOM(x); x->x_gui.x_h = x->x_gui.x_w; if (num != x->x_number && glist_isvisible(x->x_gui.x_glist)) { /* we need to recreate the buttons */ (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_ERASE); redraw = 1; } x->x_number = num; if(x->x_on >= x->x_number) { x->x_on_old = x->x_on = x->x_number - 1; x->x_on_old = x->x_on; } if (redraw && gobj_shouldvis((t_gobj *)x, x->x_gui.x_glist)) { (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_NEW); canvas_fixlinesfor(x->x_gui.x_glist, (t_text*)x); } else { /* just reconfigure */ iemgui_size((void *)x, &x->x_gui); } } static void radio_set(t_radio *x, t_floatarg f) { int i = (int)f; x->x_fval = f; if(i < 0) i = 0; if(i >= x->x_number) i = x->x_number - 1; if(x->x_on != x->x_on_old) { int old = x->x_on_old; x->x_on_old = x->x_on; x->x_on = i; (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); x->x_on_old = old; } else { x->x_on = i; (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); } } static void radio_bang(t_radio *x) { if(x->x_compat) { /* compatibility with earlier "[hv]dial" behavior */ t_atom at[2]; if((x->x_change) && (x->x_on != x->x_on_old)) { SETFLOAT(at+0, (t_float)x->x_on_old); SETFLOAT(at+1, 0.0); outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, at); if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, at); } x->x_on_old = x->x_on; SETFLOAT(at+0, (t_float)x->x_on); SETFLOAT(at+1, 1.0); outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, at); if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, at); } else { t_float outval = (pd_compatibilitylevel < 46 ? x->x_on : x->x_fval); outlet_float(x->x_gui.x_obj.ob_outlet, outval); if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) pd_float(x->x_gui.x_snd->s_thing, outval); } } static void radio_fout(t_radio *x, t_floatarg f) { int i = (int)f; x->x_fval = f; if(i < 0) i = 0; if(i >= x->x_number) i = x->x_number - 1; if(x->x_compat) { /* compatibility with earlier "[hv]dial" behavior */ t_atom at[2]; if((x->x_change) && (i != x->x_on_old)) { SETFLOAT(at+0, (t_float)x->x_on_old); SETFLOAT(at+1, 0.0); outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, at); if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, at); } if(x->x_on != x->x_on_old) x->x_on_old = x->x_on; x->x_on = i; (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); x->x_on_old = x->x_on; SETFLOAT(at+0, (t_float)x->x_on); SETFLOAT(at+1, 1.0); outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, at); if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, at); } else { t_float outval = (pd_compatibilitylevel < 46 ? i : x->x_fval); x->x_on_old = x->x_on; x->x_on = i; (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); outlet_float(x->x_gui.x_obj.ob_outlet, outval); if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) pd_float(x->x_gui.x_snd->s_thing, outval); } } static void radio_float(t_radio *x, t_floatarg f) { int i = (int)f; x->x_fval = f; if(i < 0) i = 0; if(i >= x->x_number) i = x->x_number - 1; if(x->x_compat) { t_atom at[2]; /* compatibility with earlier "[hv]dial" behavior */ if((x->x_change) && (i != x->x_on_old)) { if(x->x_gui.x_fsf.x_put_in2out) { SETFLOAT(at+0, (t_float)x->x_on_old); SETFLOAT(at+1, 0.0); outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, at); if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, at); } } if(x->x_on != x->x_on_old) x->x_on_old = x->x_on; x->x_on = i; (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); x->x_on_old = x->x_on; if(x->x_gui.x_fsf.x_put_in2out) { SETFLOAT(at+0, (t_float)x->x_on); SETFLOAT(at+1, 1.0); outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, at); if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, at); } } else { t_float outval = (pd_compatibilitylevel < 46 ? i : x->x_fval); x->x_on_old = x->x_on; x->x_on = i; (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); if(x->x_gui.x_fsf.x_put_in2out) { outlet_float(x->x_gui.x_obj.ob_outlet, outval); if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) pd_float(x->x_gui.x_snd->s_thing, outval); } } } static void radio_click(t_radio *x, t_floatarg xpos, t_floatarg ypos, t_floatarg shift, t_floatarg ctrl, t_floatarg alt) { int selected = 0.; if (x->x_orientation == horizontal) { int xx = (int)xpos - (int)text_xpix(&x->x_gui.x_obj, x->x_gui.x_glist); selected = xx / x->x_gui.x_w; } else { int yy = (int)ypos - text_ypix(&x->x_gui.x_obj, x->x_gui.x_glist); selected = yy / x->x_gui.x_h; } if(selected >= x->x_number) selected = x->x_number-1; if(selected < 0) selected = 0; radio_fout(x, (t_float)selected); } static int radio_newclick(t_gobj *z, struct _glist *glist, int xpix, int ypix, int shift, int alt, int dbl, int doit) { if(doit) radio_click((t_radio *)z, (t_floatarg)xpix, (t_floatarg)ypix, (t_floatarg)shift, 0, (t_floatarg)alt); return (1); } static void radio_loadbang(t_radio *x, t_floatarg action) { if(action == LB_LOAD && x->x_gui.x_isa.x_loadinit) radio_bang(x); } static void radio_number(t_radio *x, t_floatarg num) { int n = (int)num; if(n < 1) n = 1; if(n > IEM_RADIO_MAX) n = IEM_RADIO_MAX; if(n != x->x_number) { int vis = glist_isvisible(x->x_gui.x_glist); if(vis) (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_ERASE); x->x_number = n; if(x->x_on >= x->x_number) x->x_on = x->x_number - 1; x->x_on_old = x->x_on; if(vis && gobj_shouldvis((t_gobj *)x, x->x_gui.x_glist)) { (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_NEW); canvas_fixlinesfor(x->x_gui.x_glist, (t_text*)x); } } } static void radio_orientation(t_radio *x, t_floatarg forient) { x->x_orientation = !!(int)forient; iemgui_size(x, &x->x_gui); } static void radio_size(t_radio *x, t_symbol *s, int ac, t_atom *av) { x->x_gui.x_w = iemgui_clip_size((int)atom_getfloatarg(0, ac, av)) * IEMGUI_ZOOM(x); x->x_gui.x_h = x->x_gui.x_w; iemgui_size((void *)x, &x->x_gui); } static void radio_delta(t_radio *x, t_symbol *s, int ac, t_atom *av) {iemgui_delta((void *)x, &x->x_gui, s, ac, av);} static void radio_pos(t_radio *x, t_symbol *s, int ac, t_atom *av) {iemgui_pos((void *)x, &x->x_gui, s, ac, av);} static void radio_color(t_radio *x, t_symbol *s, int ac, t_atom *av) {iemgui_color((void *)x, &x->x_gui, s, ac, av);} static void radio_send(t_radio *x, t_symbol *s) {iemgui_send(x, &x->x_gui, s);} static void radio_receive(t_radio *x, t_symbol *s) {iemgui_receive(x, &x->x_gui, s);} static void radio_label(t_radio *x, t_symbol *s) {iemgui_label((void *)x, &x->x_gui, s);} static void radio_label_pos(t_radio *x, t_symbol *s, int ac, t_atom *av) {iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);} static void radio_label_font(t_radio *x, t_symbol *s, int ac, t_atom *av) {iemgui_label_font((void *)x, &x->x_gui, s, ac, av);} static void radio_init(t_radio *x, t_floatarg f) {x->x_gui.x_isa.x_loadinit = (f == 0.0) ? 0 : 1;} static void radio_double_change(t_radio *x) { if(x->x_compat) x->x_change = 1; else pd_error(x, "radio: no method for 'double_change'"); } static void radio_single_change(t_radio *x) { if(x->x_compat) x->x_change = 0; else pd_error(x, "radio: no method for 'single_change'"); } static void *radio_donew(t_symbol *s, int argc, t_atom *argv, int old) { t_radio *x = (t_radio *)iemgui_new(radio_class); int a = IEM_GUI_DEFAULTSIZE, on = 0; int ldx = 0, ldy = -8 * IEM_GUI_DEFAULTSIZE_SCALE, chg = 1, num = 8; int fs = x->x_gui.x_fontsize; t_float fval = 0; if('v' == *s->s_name) x->x_orientation = vertical; x->x_compat = old; IEMGUI_SETDRAWFUNCTIONS(x, radio); if((argc == 15)&&IS_A_FLOAT(argv,0)&&IS_A_FLOAT(argv,1)&&IS_A_FLOAT(argv,2) &&IS_A_FLOAT(argv,3) &&(IS_A_SYMBOL(argv,4)||IS_A_FLOAT(argv,4)) &&(IS_A_SYMBOL(argv,5)||IS_A_FLOAT(argv,5)) &&(IS_A_SYMBOL(argv,6)||IS_A_FLOAT(argv,6)) &&IS_A_FLOAT(argv,7)&&IS_A_FLOAT(argv,8) &&IS_A_FLOAT(argv,9)&&IS_A_FLOAT(argv,10)&&IS_A_FLOAT(argv,14)) { a = (int)atom_getfloatarg(0, argc, argv); chg = (int)atom_getfloatarg(1, argc, argv); iem_inttosymargs(&x->x_gui.x_isa, atom_getfloatarg(2, argc, argv)); num = (int)atom_getfloatarg(3, argc, argv); iemgui_new_getnames(&x->x_gui, 4, argv); ldx = (int)atom_getfloatarg(7, argc, argv); ldy = (int)atom_getfloatarg(8, argc, argv); iem_inttofstyle(&x->x_gui.x_fsf, atom_getfloatarg(9, argc, argv)); fs = (int)atom_getfloatarg(10, argc, argv); iemgui_all_loadcolors(&x->x_gui, argv+11, argv+12, argv+13); fval = atom_getfloatarg(14, argc, argv); } else iemgui_new_getnames(&x->x_gui, 4, 0); x->x_gui.x_fsf.x_snd_able = (0 != x->x_gui.x_snd); x->x_gui.x_fsf.x_rcv_able = (0 != x->x_gui.x_rcv); if(x->x_gui.x_fsf.x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica"); else if(x->x_gui.x_fsf.x_font_style == 2) strcpy(x->x_gui.x_font, "times"); else { x->x_gui.x_fsf.x_font_style = 0; strcpy(x->x_gui.x_font, sys_font); } if(num < 1) num = 1; if(num > IEM_RADIO_MAX) num = IEM_RADIO_MAX; x->x_number = num; x->x_fval = fval; on = fval; if(on < 0) on = 0; if(on >= x->x_number) on = x->x_number - 1; if(x->x_gui.x_isa.x_loadinit) x->x_on = on; else x->x_on = 0; x->x_on_old = x->x_on; x->x_change = (chg == 0) ? 0 : 1; if(x->x_gui.x_fsf.x_rcv_able) pd_bind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); x->x_gui.x_ldx = ldx; x->x_gui.x_ldy = ldy; x->x_gui.x_fontsize = (fs < 4)?4:fs; x->x_gui.x_w = iemgui_clip_size(a); x->x_gui.x_h = x->x_gui.x_w; iemgui_verify_snd_ne_rcv(&x->x_gui); iemgui_newzoom(&x->x_gui); outlet_new(&x->x_gui.x_obj, &s_list); return (x); } static void *radio_new(t_symbol *s, int argc, t_atom *argv) { return (radio_donew(s, argc, argv, 0)); } static void *dial_new(t_symbol *s, int argc, t_atom *argv) { return (radio_donew(s, argc, argv, 1)); } static void radio_free(t_radio *x) { if(x->x_gui.x_fsf.x_rcv_able) pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); pdgui_stub_deleteforkey(x); } void g_radio_setup(void) { radio_class = class_new(gensym("hradio"), (t_newmethod)radio_new, (t_method)radio_free, sizeof(t_radio), 0, A_GIMME, 0); class_addcreator((t_newmethod)radio_new, gensym("vradio"), A_GIMME, 0); class_addcreator((t_newmethod)radio_new, gensym("rdb"), A_GIMME, 0); class_addcreator((t_newmethod)radio_new, gensym("radiobut"), A_GIMME, 0); class_addcreator((t_newmethod)radio_new, gensym("radiobutton"), A_GIMME, 0); class_addbang(radio_class, radio_bang); class_addfloat(radio_class, radio_float); class_addmethod(radio_class, (t_method)radio_click, gensym("click"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); class_addmethod(radio_class, (t_method)radio_dialog, gensym("dialog"), A_GIMME, 0); class_addmethod(radio_class, (t_method)radio_loadbang, gensym("loadbang"), A_DEFFLOAT, 0); class_addmethod(radio_class, (t_method)radio_set, gensym("set"), A_FLOAT, 0); class_addmethod(radio_class, (t_method)radio_size, gensym("size"), A_GIMME, 0); class_addmethod(radio_class, (t_method)radio_delta, gensym("delta"), A_GIMME, 0); class_addmethod(radio_class, (t_method)radio_pos, gensym("pos"), A_GIMME, 0); class_addmethod(radio_class, (t_method)radio_color, gensym("color"), A_GIMME, 0); class_addmethod(radio_class, (t_method)radio_send, gensym("send"), A_DEFSYM, 0); class_addmethod(radio_class, (t_method)radio_receive, gensym("receive"), A_DEFSYM, 0); class_addmethod(radio_class, (t_method)radio_label, gensym("label"), A_DEFSYM, 0); class_addmethod(radio_class, (t_method)radio_label_pos, gensym("label_pos"), A_GIMME, 0); class_addmethod(radio_class, (t_method)radio_label_font, gensym("label_font"), A_GIMME, 0); class_addmethod(radio_class, (t_method)radio_init, gensym("init"), A_FLOAT, 0); class_addmethod(radio_class, (t_method)radio_number, gensym("number"), A_FLOAT, 0); class_addmethod(radio_class, (t_method)radio_orientation, gensym("orientation"), A_FLOAT, 0); class_addmethod(radio_class, (t_method)iemgui_zoom, gensym("zoom"), A_CANT, 0); radio_widgetbehavior.w_getrectfn = radio_getrect; radio_widgetbehavior.w_displacefn = iemgui_displace; radio_widgetbehavior.w_selectfn = iemgui_select; radio_widgetbehavior.w_activatefn = NULL; radio_widgetbehavior.w_deletefn = iemgui_delete; radio_widgetbehavior.w_visfn = iemgui_vis; radio_widgetbehavior.w_clickfn = radio_newclick; class_setwidget(radio_class, &radio_widgetbehavior); class_sethelpsymbol(radio_class, gensym("radio")); class_setsavefn(radio_class, radio_save); class_setpropertiesfn(radio_class, radio_properties); /* obsolete version (0.34-0.35) */ class_addcreator((t_newmethod)dial_new, gensym("hdl"), A_GIMME, 0); class_addcreator((t_newmethod)dial_new, gensym("vdl"), A_GIMME, 0); class_addmethod(radio_class, (t_method)radio_single_change, gensym("single_change"), 0); class_addmethod(radio_class, (t_method)radio_double_change, gensym("double_change"), 0); } ================================================ FILE: libs/libpd/pure-data/src/g_readwrite.c ================================================ /* Copyright (c) 1997-2002 Miller Puckette and others. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* Routines to read and write canvases to files: canvas_savetofile() writes a root canvas to a "pd" file. (Reading "pd" files is done simply by passing the contents to the pd message interpreter.) Alternatively, the glist_read() and glist_write() routines read and write "data" from and to files (reading reads into an existing canvas), using a file format as in the dialog window for data. */ #include "m_pd.h" #include "g_canvas.h" #include #include /* object to assist in saving state by abstractions */ static t_class *savestate_class; typedef struct _savestate { t_object x_obj; t_outlet *x_stateout; t_outlet *x_bangout; t_binbuf *x_savetobuf; } t_savestate; static void *savestate_new(void) { t_savestate *x = (t_savestate *)pd_new(savestate_class); x->x_stateout = outlet_new(&x->x_obj, &s_list); x->x_bangout = outlet_new(&x->x_obj, &s_bang); x->x_savetobuf = 0; return (x); } /* call this when the owning abstraction's parent patch is saved so we can add state-restoring messages to binbuf */ static void savestate_doit(t_savestate *x, t_binbuf *b) { x->x_savetobuf = b; outlet_bang(x->x_bangout); x->x_savetobuf = 0; } /* called by abstraction in response to savestate_doit(); lists received here are added to the parent patch's save buffer after the line that will create the abstraction, addressed to "#A" which will be this patch after it is recreated by reopening the parent patch, pasting, or "undo". */ static void savestate_list(t_savestate *x, t_symbol *s, int argc, t_atom *argv) { if (x->x_savetobuf) { binbuf_addv(x->x_savetobuf, "ss", gensym("#A"), gensym("saved")); binbuf_add(x->x_savetobuf, argc, argv); binbuf_addv(x->x_savetobuf, ";"); } else pd_error(x, "savestate: ignoring message sent when not saving parent"); } static void savestate_setup(void) { savestate_class = class_new(gensym("savestate"), (t_newmethod)savestate_new, 0, sizeof(t_savestate), 0, 0); class_addlist(savestate_class, savestate_list); } void canvas_statesavers_doit(t_glist *x, t_binbuf *b) { t_gobj *g; for (g = x->gl_list; g; g = g->g_next) if (g->g_pd == savestate_class) savestate_doit((t_savestate *)g, b); else if (g->g_pd == canvas_class && !canvas_isabstraction((t_canvas *)g)) canvas_statesavers_doit((t_glist *)g, b); } void canvas_saved(t_glist *x, t_symbol *s, int argc, t_atom *argv) { t_gobj *g; for (g = x->gl_list; g; g = g->g_next) if (g->g_pd == savestate_class) outlet_list(((t_savestate *)g)->x_stateout, 0, argc, argv); else if (g->g_pd == canvas_class && !canvas_isabstraction((t_canvas *)g)) canvas_saved((t_glist *)g, s, argc, argv); } static t_class *declare_class; void canvas_savedeclarationsto(t_canvas *x, t_binbuf *b); /* the following routines read "scalars" from a file into a canvas. */ static int canvas_scanbinbuf(int natoms, t_atom *vec, int *p_indexout, int *p_next) { int i; int indexwas = *p_next; *p_indexout = indexwas; if (indexwas >= natoms) return (0); for (i = indexwas; i < natoms && vec[i].a_type != A_SEMI; i++) ; if (i >= natoms) *p_next = i; else *p_next = i + 1; return (i - indexwas); } int canvas_readscalar(t_glist *x, int natoms, t_atom *vec, int *p_nextmsg, int selectit); static void canvas_readerror(int natoms, t_atom *vec, int message, int nline, char *s) { pd_error(0, "%s", s); startpost("line was:"); postatom(nline, vec + message); endpost(); } /* fill in the contents of the scalar into the vector w. */ static void glist_readatoms(t_glist *x, int natoms, t_atom *vec, int *p_nextmsg, t_symbol *templatesym, t_word *w, int argc, t_atom *argv) { int message, n, i; t_template *template = template_findbyname(templatesym); if (!template) { pd_error(0, "%s: no such template", templatesym->s_name); *p_nextmsg = natoms; return; } word_restore(w, template, argc, argv); n = template->t_n; for (i = 0; i < n; i++) { if (template->t_vec[i].ds_type == DT_ARRAY) { t_array *a = w[i].w_array; int elemsize = a->a_elemsize, nitems = 0; t_symbol *arraytemplatesym = template->t_vec[i].ds_arraytemplate; t_template *arraytemplate = template_findbyname(arraytemplatesym); if (!arraytemplate) { pd_error(0, "%s: no such template", arraytemplatesym->s_name); } else while (1) { t_word *element; int nline = canvas_scanbinbuf(natoms, vec, &message, p_nextmsg); /* empty line terminates array */ if (!nline) break; array_resize(a, nitems + 1); element = (t_word *)(((char *)a->a_vec) + nitems * elemsize); glist_readatoms(x, natoms, vec, p_nextmsg, arraytemplatesym, element, nline, vec + message); nitems++; } } else if (template->t_vec[i].ds_type == DT_TEXT) { t_binbuf *z = binbuf_new(); int first = *p_nextmsg, last; for (last = first; last < natoms && vec[last].a_type != A_SEMI; last++); binbuf_restore(z, last-first, vec+first); binbuf_add(w[i].w_binbuf, binbuf_getnatom(z), binbuf_getvec(z)); binbuf_free(z); last++; if (last > natoms) last = natoms; *p_nextmsg = last; } } } int canvas_readscalar(t_glist *x, int natoms, t_atom *vec, int *p_nextmsg, int selectit) { int message, nline; t_template *template; t_symbol *templatesym; t_scalar *sc; int nextmsg = *p_nextmsg; int wasvis = glist_isvisible(x); if (nextmsg >= natoms || vec[nextmsg].a_type != A_SYMBOL) { if (nextmsg < natoms) post("stopping early: type %d", vec[nextmsg].a_type); *p_nextmsg = natoms; return (0); } templatesym = canvas_makebindsym(vec[nextmsg].a_w.w_symbol); *p_nextmsg = nextmsg + 1; if (!(template = template_findbyname(templatesym))) { pd_error(0, "canvas_read: %s: no such template", templatesym->s_name); *p_nextmsg = natoms; return (0); } sc = scalar_new(x, templatesym); if (!sc) { pd_error(0, "couldn't create scalar \"%s\"", templatesym->s_name); *p_nextmsg = natoms; return (0); } if (wasvis) { /* temporarily lie about vis flag while this is built */ glist_getcanvas(x)->gl_mapped = 0; } glist_add(x, &sc->sc_gobj); nline = canvas_scanbinbuf(natoms, vec, &message, p_nextmsg); glist_readatoms(x, natoms, vec, p_nextmsg, templatesym, sc->sc_vec, nline, vec + message); if (wasvis) { /* reset vis flag as before */ glist_getcanvas(x)->gl_mapped = 1; gobj_vis(&sc->sc_gobj, x, 1); } if (selectit) { glist_select(x, &sc->sc_gobj); } return (1); } void glist_readfrombinbuf(t_glist *x, const t_binbuf *b, const char *filename, int selectem) { t_canvas *canvas = glist_getcanvas(x); int natoms, nline, message, nextmsg = 0; t_atom *vec; natoms = binbuf_getnatom(b); vec = binbuf_getvec(b); /* check for file type */ nline = canvas_scanbinbuf(natoms, vec, &message, &nextmsg); if (nline != 1 && vec[message].a_type != A_SYMBOL && strcmp(vec[message].a_w.w_symbol->s_name, "data")) { pd_error(x, "%s: file apparently of wrong type", filename); return; } /* read in templates and check for consistency */ while (1) { t_template *newtemplate, *existtemplate; t_symbol *templatesym; t_atom *templateargs = getbytes(0); int ntemplateargs = 0, newnargs; nline = canvas_scanbinbuf(natoms, vec, &message, &nextmsg); if (nline < 2) { t_freebytes(templateargs, sizeof (*templateargs) * ntemplateargs); break; } else if (nline > 2) canvas_readerror(natoms, vec, message, nline, "extra items ignored"); else if (vec[message].a_type != A_SYMBOL || strcmp(vec[message].a_w.w_symbol->s_name, "template") || vec[message + 1].a_type != A_SYMBOL) { canvas_readerror(natoms, vec, message, nline, "bad template header"); continue; } templatesym = canvas_makebindsym(vec[message + 1].a_w.w_symbol); while (1) { nline = canvas_scanbinbuf(natoms, vec, &message, &nextmsg); if (nline != 2 && nline != 3) break; newnargs = ntemplateargs + nline; templateargs = (t_atom *)t_resizebytes(templateargs, sizeof(*templateargs) * ntemplateargs, sizeof(*templateargs) * newnargs); templateargs[ntemplateargs] = vec[message]; templateargs[ntemplateargs + 1] = vec[message + 1]; if (nline == 3) templateargs[ntemplateargs + 2] = vec[message + 2]; ntemplateargs = newnargs; } if (!(existtemplate = template_findbyname(templatesym))) { pd_error(0, "%s: template not found in current patch", templatesym->s_name); t_freebytes(templateargs, sizeof (*templateargs) * ntemplateargs); return; } newtemplate = template_new(templatesym, ntemplateargs, templateargs); t_freebytes(templateargs, sizeof (*templateargs) * ntemplateargs); if (!template_match(existtemplate, newtemplate)) { pd_error(0, "%s: template doesn't match current one", templatesym->s_name); pd_free(&newtemplate->t_pdobj); return; } pd_free(&newtemplate->t_pdobj); } while (nextmsg < natoms) { canvas_readscalar(x, natoms, vec, &nextmsg, selectem); } } static void glist_doread(t_glist *x, t_symbol *filename, t_symbol *format, int clearme) { t_binbuf *b = binbuf_new(); t_canvas *canvas = glist_getcanvas(x); int wasvis = glist_isvisible(canvas); int cr = 0; if (!strcmp(format->s_name, "cr")) cr = 1; else if (*format->s_name) pd_error(0, "qlist_read: unknown flag: %s", format->s_name); if (binbuf_read_via_canvas(b, filename->s_name, canvas, cr)) { pd_error(x, "read failed"); binbuf_free(b); return; } if (wasvis) canvas_vis(canvas, 0); if (clearme) glist_clear(x); glist_readfrombinbuf(x, b, filename->s_name, 0); if (wasvis) canvas_vis(canvas, 1); binbuf_free(b); } void glist_read(t_glist *x, t_symbol *filename, t_symbol *format) { glist_doread(x, filename, format, 1); } void glist_mergefile(t_glist *x, t_symbol *filename, t_symbol *format) { glist_doread(x, filename, format, 0); } /* read text from a "properties" window, called from a gfxstub set up in scalar_properties(). We try to restore the object; if successful we either copy the data from the new scalar to the old one in place (if their templates match) or else delete the old scalar and put the new thing in its place on the list. */ void canvas_dataproperties(t_canvas *x, t_scalar *sc, t_binbuf *b) { int ntotal, nnew, scindex; t_gobj *y, *y2 = 0, *newone, *oldone = 0; t_template *template; glist_noselect(x); for (y = x->gl_list, ntotal = 0, scindex = -1; y; y = y->g_next) { if (y == &sc->sc_gobj) scindex = ntotal, oldone = y; ntotal++; } if (scindex == -1) { pd_error(x, "data_properties: scalar disappeared"); return; } if (!b) { pd_error(x, "couldn't update properties (none given)"); return; } glist_readfrombinbuf(x, b, "properties dialog", 0); newone = 0; /* take the new object off the list */ if (ntotal) { for (y = x->gl_list, nnew = 1; (y2 = y->g_next); y = y2, nnew++) if (nnew == ntotal) { newone = y2; gobj_vis(newone, x, 0); y->g_next = y2->g_next; break; } } else gobj_vis((newone = x->gl_list), x, 0), x->gl_list = newone->g_next; if (!newone) pd_error(x, "couldn't update properties (perhaps a format problem?)"); else if (!oldone) bug("data_properties: couldn't find old element"); else if (newone->g_pd == scalar_class && oldone->g_pd == scalar_class && ((t_scalar *)newone)->sc_template == ((t_scalar *)oldone)->sc_template && (template = template_findbyname(((t_scalar *)newone)->sc_template))) { /* swap new one with old one; then delete new one */ int i; for (i = 0; i < template->t_n; i++) { t_word w = ((t_scalar *)newone)->sc_vec[i]; ((t_scalar *)newone)->sc_vec[i] = ((t_scalar *)oldone)->sc_vec[i]; ((t_scalar *)oldone)->sc_vec[i] = w; } pd_free(&newone->g_pd); if (glist_isvisible(x)) { gobj_vis(oldone, x, 0); gobj_vis(oldone, x, 1); } } else { /* delete old one; put new one where the old one was on glist */ glist_delete(x, oldone); if (scindex > 0) { for (y = x->gl_list, nnew = 1; y; y = y->g_next, nnew++) if (nnew == scindex || !y->g_next) { newone->g_next = y->g_next; y->g_next = newone; goto didit; } bug("data_properties: can't reinsert"); } else newone->g_next = x->gl_list, x->gl_list = newone; } didit: ; } /* ----------- routines to write data to a binbuf ----------- */ void canvas_doaddtemplate(t_symbol *templatesym, int *p_ntemplates, t_symbol ***p_templatevec) { int n = *p_ntemplates, i; t_symbol **templatevec = *p_templatevec; for (i = 0; i < n; i++) if (templatevec[i] == templatesym) return; templatevec = (t_symbol **)t_resizebytes(templatevec, n * sizeof(*templatevec), (n+1) * sizeof(*templatevec)); templatevec[n] = templatesym; *p_templatevec = templatevec; *p_ntemplates = n+1; } static void glist_writelist(t_gobj *y, t_binbuf *b); void binbuf_savetext(t_binbuf *bfrom, t_binbuf *bto); void canvas_writescalar(t_symbol *templatesym, t_word *w, t_binbuf *b, int amarrayelement) { t_template *template = template_findbyname(templatesym); t_atom *a = (t_atom *)t_getbytes(0); int i, n = template?(template->t_n):0, natom = 0; if (!amarrayelement) { t_atom templatename; SETSYMBOL(&templatename, gensym(templatesym->s_name + 3)); binbuf_add(b, 1, &templatename); } if (!template) bug("canvas_writescalar"); /* write the atoms (floats and symbols) */ for (i = 0; i < n; i++) { if (template->t_vec[i].ds_type == DT_FLOAT || template->t_vec[i].ds_type == DT_SYMBOL) { a = (t_atom *)t_resizebytes(a, natom * sizeof(*a), (natom + 1) * sizeof (*a)); if (template->t_vec[i].ds_type == DT_FLOAT) SETFLOAT(a + natom, w[i].w_float); else SETSYMBOL(a + natom, w[i].w_symbol); natom++; } } /* array elements have to have at least something */ if (natom == 0 && amarrayelement) SETSYMBOL(a + natom, &s_bang), natom++; binbuf_add(b, natom, a); binbuf_addsemi(b); t_freebytes(a, natom * sizeof(*a)); for (i = 0; i < n; i++) { if (template->t_vec[i].ds_type == DT_ARRAY) { int j; t_array *a = w[i].w_array; int elemsize = a->a_elemsize, nitems = a->a_n; t_symbol *arraytemplatesym = template->t_vec[i].ds_arraytemplate; for (j = 0; j < nitems; j++) canvas_writescalar(arraytemplatesym, (t_word *)(((char *)a->a_vec) + elemsize * j), b, 1); binbuf_addsemi(b); } else if (template->t_vec[i].ds_type == DT_TEXT) binbuf_savetext(w[i].w_binbuf, b); } } static void glist_writelist(t_gobj *y, t_binbuf *b) { for (; y; y = y->g_next) { if (pd_class(&y->g_pd) == scalar_class) { canvas_writescalar(((t_scalar *)y)->sc_template, ((t_scalar *)y)->sc_vec, b, 0); } } } /* ------------ routines to write out templates for data ------- */ static void canvas_addtemplatesforlist(t_gobj *y, int *p_ntemplates, t_symbol ***p_templatevec); static void canvas_addtemplatesforscalar(t_symbol *templatesym, t_word *w, int *p_ntemplates, t_symbol ***p_templatevec) { t_dataslot *ds; int i; t_template *template = template_findbyname(templatesym); canvas_doaddtemplate(templatesym, p_ntemplates, p_templatevec); if (!template) bug("canvas_addtemplatesforscalar"); else for (ds = template->t_vec, i = template->t_n; i--; ds++, w++) { if (ds->ds_type == DT_ARRAY) { int j; t_array *a = w->w_array; int elemsize = a->a_elemsize, nitems = a->a_n; t_symbol *arraytemplatesym = ds->ds_arraytemplate; canvas_doaddtemplate(arraytemplatesym, p_ntemplates, p_templatevec); for (j = 0; j < nitems; j++) canvas_addtemplatesforscalar(arraytemplatesym, (t_word *)(((char *)a->a_vec) + elemsize * j), p_ntemplates, p_templatevec); } } } static void canvas_addtemplatesforlist(t_gobj *y, int *p_ntemplates, t_symbol ***p_templatevec) { for (; y; y = y->g_next) { if (pd_class(&y->g_pd) == scalar_class) { canvas_addtemplatesforscalar(((t_scalar *)y)->sc_template, ((t_scalar *)y)->sc_vec, p_ntemplates, p_templatevec); } } } /* write all "scalars" in a glist to a binbuf. */ t_binbuf *glist_writetobinbuf(t_glist *x, int wholething) { int i; t_symbol **templatevec = getbytes(0); int ntemplates = 0; t_gobj *y; t_binbuf *b = binbuf_new(); for (y = x->gl_list; y; y = y->g_next) { if ((pd_class(&y->g_pd) == scalar_class) && (wholething || glist_isselected(x, y))) { canvas_addtemplatesforscalar(((t_scalar *)y)->sc_template, ((t_scalar *)y)->sc_vec, &ntemplates, &templatevec); } } binbuf_addv(b, "s;", gensym("data")); for (i = 0; i < ntemplates; i++) { t_template *template = template_findbyname(templatevec[i]); int j, m = template->t_n; /* drop "pd-" prefix from template symbol to print it: */ binbuf_addv(b, "ss;", gensym("template"), gensym(templatevec[i]->s_name + 3)); for (j = 0; j < m; j++) { t_symbol *type; switch (template->t_vec[j].ds_type) { case DT_FLOAT: type = &s_float; break; case DT_SYMBOL: type = &s_symbol; break; case DT_ARRAY: type = gensym("array"); break; case DT_TEXT: type = &s_list; break; default: type = &s_float; bug("canvas_write"); } if (template->t_vec[j].ds_type == DT_ARRAY) binbuf_addv(b, "sss;", type, template->t_vec[j].ds_name, gensym(template->t_vec[j].ds_arraytemplate->s_name + 3)); else binbuf_addv(b, "ss;", type, template->t_vec[j].ds_name); } binbuf_addsemi(b); } binbuf_addsemi(b); /* now write out the objects themselves */ for (y = x->gl_list; y; y = y->g_next) { if ((pd_class(&y->g_pd) == scalar_class) && (wholething || glist_isselected(x, y))) { canvas_writescalar(((t_scalar *)y)->sc_template, ((t_scalar *)y)->sc_vec, b, 0); } } t_freebytes(templatevec, ntemplates*sizeof(*templatevec)); return (b); } static void glist_write(t_glist *x, t_symbol *filename, t_symbol *format) { int cr = 0; t_binbuf *b; char buf[MAXPDSTRING]; t_canvas *canvas = glist_getcanvas(x); canvas_makefilename(canvas, filename->s_name, buf, MAXPDSTRING); if (!strcmp(format->s_name, "cr")) cr = 1; else if (*format->s_name) pd_error(0, "qlist_read: unknown flag: %s", format->s_name); b = glist_writetobinbuf(x, 1); if (b) { if (binbuf_write(b, buf, "", cr)) pd_error(0, "%s: write failed", filename->s_name); binbuf_free(b); } } /* ------ routines to save and restore canvases (patches) recursively. ----*/ typedef void (*t_zoomfn)(void *x, t_floatarg arg1); /* save to a binbuf, called recursively; cf. canvas_savetofile() which saves the document, and is only called on root canvases. */ static void canvas_saveto(t_canvas *x, t_binbuf *b) { t_gobj *y; t_linetraverser t; t_outconnect *oc; /* subpatch */ if (x->gl_owner && !x->gl_env) { /* have to go to original binbuf to find out how we were named. */ t_binbuf *bz = binbuf_new(); t_symbol *patchsym; binbuf_addbinbuf(bz, x->gl_obj.ob_binbuf); patchsym = atom_getsymbolarg(1, binbuf_getnatom(bz), binbuf_getvec(bz)); binbuf_free(bz); binbuf_addv(b, "ssiiiisi;", gensym("#N"), gensym("canvas"), (int)(x->gl_screenx1), (int)(x->gl_screeny1), (int)(x->gl_screenx2 - x->gl_screenx1), (int)(x->gl_screeny2 - x->gl_screeny1), (patchsym != &s_ ? patchsym: gensym("(subpatch)")), x->gl_mapped); } /* root or abstraction */ else { binbuf_addv(b, "ssiiiii;", gensym("#N"), gensym("canvas"), (int)(x->gl_screenx1), (int)(x->gl_screeny1), (int)(x->gl_screenx2 - x->gl_screenx1), (int)(x->gl_screeny2 - x->gl_screeny1), (int)x->gl_font); canvas_savedeclarationsto(x, b); } for (y = x->gl_list; y; y = y->g_next) gobj_save(y, b); linetraverser_start(&t, x); while ((oc = linetraverser_next(&t))) { int srcno = canvas_getindex(x, &t.tr_ob->ob_g); int sinkno = canvas_getindex(x, &t.tr_ob2->ob_g); binbuf_addv(b, "ssiiii;", gensym("#X"), gensym("connect"), srcno, t.tr_outno, sinkno, t.tr_inno); } /* unless everything is the default (as in ordinary subpatches) print out a "coords" message to set up the coordinate systems */ if (x->gl_isgraph || x->gl_x1 || x->gl_y1 || x->gl_x2 != 1 || x->gl_y2 != 1 || x->gl_pixwidth || x->gl_pixheight) { if (x->gl_isgraph && x->gl_goprect) /* if we have a graph-on-parent rectangle, we're new style. The format is arranged so that old versions of Pd can at least do something with it. */ binbuf_addv(b, "ssfffffffff;", gensym("#X"), gensym("coords"), x->gl_x1, x->gl_y1, x->gl_x2, x->gl_y2, (t_float)x->gl_pixwidth, (t_float)x->gl_pixheight, (t_float)((x->gl_hidetext)?2.:1.), (t_float)x->gl_xmargin, (t_float)x->gl_ymargin); /* otherwise write in 0.38-compatible form */ else binbuf_addv(b, "ssfffffff;", gensym("#X"), gensym("coords"), x->gl_x1, x->gl_y1, x->gl_x2, x->gl_y2, (t_float)x->gl_pixwidth, (t_float)x->gl_pixheight, (t_float)x->gl_isgraph); } } /* call this recursively to collect all the template names for a canvas or for the selection. */ static void canvas_collecttemplatesfor(t_canvas *x, int *ntemplatesp, t_symbol ***templatevecp, int wholething) { t_gobj *y; for (y = x->gl_list; y; y = y->g_next) { if ((pd_class(&y->g_pd) == scalar_class) && (wholething || glist_isselected(x, y))) canvas_addtemplatesforscalar(((t_scalar *)y)->sc_template, ((t_scalar *)y)->sc_vec, ntemplatesp, templatevecp); else if ((pd_class(&y->g_pd) == canvas_class) && (wholething || glist_isselected(x, y))) canvas_collecttemplatesfor((t_canvas *)y, ntemplatesp, templatevecp, 1); } } /* save the templates needed by a canvas to a binbuf. */ static void canvas_savetemplatesto(t_canvas *x, t_binbuf *b, int wholething) { t_symbol **templatevec = getbytes(0); int i, ntemplates = 0; canvas_collecttemplatesfor(x, &ntemplates, &templatevec, wholething); for (i = 0; i < ntemplates; i++) { t_template *template = template_findbyname(templatevec[i]); int j, m; if (!template) { bug("canvas_savetemplatesto"); continue; } m = template->t_n; /* drop "pd-" prefix from template symbol to print */ binbuf_addv(b, "sss", &s__N, gensym("struct"), gensym(templatevec[i]->s_name + 3)); for (j = 0; j < m; j++) { t_symbol *type; switch (template->t_vec[j].ds_type) { case DT_FLOAT: type = &s_float; break; case DT_SYMBOL: type = &s_symbol; break; case DT_ARRAY: type = gensym("array"); break; case DT_TEXT: type = gensym("text"); break; default: type = &s_float; bug("canvas_write"); } if (template->t_vec[j].ds_type == DT_ARRAY) binbuf_addv(b, "sss", type, template->t_vec[j].ds_name, gensym(template->t_vec[j].ds_arraytemplate->s_name + 3)); else binbuf_addv(b, "ss", type, template->t_vec[j].ds_name); } binbuf_addsemi(b); } freebytes(templatevec, ntemplates * sizeof(*templatevec)); } void canvas_reload(t_symbol *name, t_symbol *dir, t_glist *except); /* save a "root" canvas to a file; cf. canvas_saveto() which saves the body (and which is called recursively.) */ static void canvas_savetofile(t_canvas *x, t_symbol *filename, t_symbol *dir, float fdestroy) { t_binbuf *b = binbuf_new(); canvas_savetemplatesto(x, b, 1); canvas_saveto(x, b); errno = 0; if (binbuf_write(b, filename->s_name, dir->s_name, 0)) post("%s/%s: %s", dir->s_name, filename->s_name, (errno ? strerror(errno) : "write failed")); else { /* if not an abstraction, reset title bar and directory */ if (!x->gl_owner) { canvas_rename(x, filename, dir); /* update window list in case Save As changed the window name */ canvas_updatewindowlist(); } post("saved to: %s/%s", dir->s_name, filename->s_name); canvas_dirty(x, 0); canvas_reload(filename, dir, x); if (fdestroy != 0) vmess(&x->gl_pd, gensym("menuclose"), "f", 1.); } binbuf_free(b); } static void canvas_menusaveas(t_canvas *x, t_float fdestroy) { t_canvas *x2 = canvas_getrootfor(x); pdgui_vmess("pdtk_canvas_saveas", "^ ss i", x2, x2->gl_name->s_name, canvas_getdir(x2)->s_name, (fdestroy != 0)); } static void canvas_menusave(t_canvas *x, t_float fdestroy) { t_canvas *x2 = canvas_getrootfor(x); const char *name = x2->gl_name->s_name; if (*name && UNTITLED_STRNCMP(name) && (strlen(name) < 4 || strcmp(name + strlen(name)-4, ".pat") || strcmp(name + strlen(name)-4, ".mxt"))) { canvas_savetofile(x2, x2->gl_name, canvas_getdir(x2), fdestroy); } else canvas_menusaveas(x2, fdestroy); } void g_readwrite_setup(void) { savestate_setup(); class_addmethod(canvas_class, (t_method)glist_write, gensym("write"), A_SYMBOL, A_DEFSYM, A_NULL); class_addmethod(canvas_class, (t_method)glist_read, gensym("read"), A_SYMBOL, A_DEFSYM, A_NULL); class_addmethod(canvas_class, (t_method)glist_mergefile, gensym("mergefile"), A_SYMBOL, A_DEFSYM, A_NULL); class_addmethod(canvas_class, (t_method)canvas_savetofile, gensym("savetofile"), A_SYMBOL, A_SYMBOL, A_DEFFLOAT, 0); class_addmethod(canvas_class, (t_method)canvas_saveto, gensym("saveto"), A_CANT, 0); class_addmethod(canvas_class, (t_method)canvas_saved, gensym("saved"), A_GIMME, 0); /* ------------------ from the menu ------------------------- */ class_addmethod(canvas_class, (t_method)canvas_menusave, gensym("menusave"), A_DEFFLOAT, 0); class_addmethod(canvas_class, (t_method)canvas_menusaveas, gensym("menusaveas"), A_DEFFLOAT, 0); } void canvas_readwrite_for_class(t_class *c) { class_addmethod(c, (t_method)canvas_menusave, gensym("menusave"), A_DEFFLOAT, 0); class_addmethod(c, (t_method)canvas_menusaveas, gensym("menusaveas"), A_DEFFLOAT, 0); } ================================================ FILE: libs/libpd/pure-data/src/g_rtext.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ #include #include #include "m_pd.h" #include "s_stuff.h" #include "g_canvas.h" #include "s_utf8.h" #define LMARGIN 2 #define RMARGIN 2 #define TMARGIN 3 #define BMARGIN 2 #define SEND_FIRST 1 #define SEND_UPDATE 2 #define SEND_CHECK 0 struct _rtext { char *x_buf; /*-- raw byte string, assumed UTF-8 encoded (moo) --*/ int x_bufsize; /*-- byte length --*/ int x_selstart; /*-- byte offset --*/ int x_selend; /*-- byte offset --*/ int x_active; /* 1 if 'actively editing */ int x_dragfrom; /* character onset we're dragging from */ int x_drawnwidth; /* screen size, pixels */ int x_drawnheight; t_text *x_text; /* owner */ t_glist *x_glist; /* glist owner belongs to */ char x_tag[50]; /* tag for gui */ struct _rtext *x_next; /* next in editor list */ }; t_rtext *rtext_new(t_glist *glist, t_text *who) { t_rtext *x = (t_rtext *)getbytes(sizeof *x); int w = 0, h = 0, indx; x->x_text = who; x->x_glist = glist; x->x_next = glist->gl_editor->e_rtext; x->x_selstart = x->x_selend = x->x_active = x->x_drawnwidth = x->x_drawnheight = 0; binbuf_gettext(who->te_binbuf, &x->x_buf, &x->x_bufsize); /* allocate extra space for hidden null terminator */ x->x_buf = resizebytes(x->x_buf, x->x_bufsize, x->x_bufsize+1); x->x_buf[x->x_bufsize] = 0; glist->gl_editor->e_rtext = x; sprintf(x->x_tag, ".x%lx.t%lx", (t_int)glist_getcanvas(x->x_glist), (t_int)x); return (x); } void rtext_free(t_rtext *x) { if (x->x_glist->gl_editor->e_textedfor == x) x->x_glist->gl_editor->e_textedfor = 0; if (x->x_glist->gl_editor->e_rtext == x) x->x_glist->gl_editor->e_rtext = x->x_next; else { t_rtext *e2; for (e2 = x->x_glist->gl_editor->e_rtext; e2; e2 = e2->x_next) if (e2->x_next == x) { e2->x_next = x->x_next; break; } } freebytes(x->x_buf, x->x_bufsize + 1); /* extra 0 byte */ freebytes(x, sizeof *x); } const char *rtext_gettag(t_rtext *x) { return (x->x_tag); } void rtext_gettext(t_rtext *x, char **buf, int *bufsize) { *buf = x->x_buf; *bufsize = x->x_bufsize; } void rtext_getseltext(t_rtext *x, char **buf, int *bufsize) { *buf = x->x_buf + x->x_selstart; *bufsize = x->x_selend - x->x_selstart; } t_text *rtext_getowner(t_rtext *x) { return (x->x_text); } /* convert t_text te_type symbol for use as a Tk tag */ static t_symbol *rtext_gettype(t_rtext *x) { switch (x->x_text->te_type) { case T_TEXT: return gensym("text"); case T_OBJECT: return gensym("obj"); case T_MESSAGE: return gensym("msg"); case T_ATOM: return gensym("atom"); } return (&s_); } /* LATER deal with tcl-significant characters */ /* firstone(), lastone() * + returns byte offset of (first|last) occurrence of 'c' in 's[0..n-1]', or * -1 if none was found * + 's' is a raw byte string * + 'c' is a byte value * + 'n' is the length (in bytes) of the prefix of 's' to be searched. * + we could make these functions work on logical characters in utf8 strings, * but we don't really need to... */ static int firstone(char *s, int c, int n) { char *s2 = s + n; int i = 0; while (s != s2) { if (*s == c) return (i); i++; s++; } return (-1); } static int lastone(char *s, int c, int n) { char *s2 = s + n; while (s2 != s) { s2--; n--; if (*s2 == c) return (n); } return (-1); } /* break the text into lines, and compute byte index of character at location (width, height). Then reset (width, height) to report size of resulting line-broken text. Used for object, message, and comment boxes; another version below is for atoms. Also we report the onsets of the beginning and end of the selection, as byte onsets into the reformatted text, which we'll use to inform the GUI how to show the selection. The input is taken from x->buf and x->bufsize fields of the text object; the wrapped text is put in "tempbuf" with byte length outchars_b_p. x->x_buf is assumed to contain text in UTF-8 format, in which characters may occupy multiple bytes. variables with a "_b" suffix are raw byte strings, lengths, or offsets; those with a "_c" suffix are logical character lengths or offsets. The UTF8 handling was contributed by Bryan Jurish, who says "moo." */ #define DEFAULTBOXWIDTH 60 static void rtext_formattext(t_rtext *x, int *widthp, int *heightp, int *indexp, char *tempbuf, int *outchars_b_p, int *selstart_b_p, int *selend_b_p, int fontwidth, int fontheight) { int widthspec_c = x->x_text->te_width; int widthlimit_c = (widthspec_c ? widthspec_c : DEFAULTBOXWIDTH); int inindex_b = 0; int inindex_c = 0; int x_bufsize_c = u8_charnum(x->x_buf, x->x_bufsize); int nlines = 0, ncolumns = 0, reportedindex = 0; int findx = (*widthp + (fontwidth/2)) / fontwidth; int findy = *heightp / fontheight; *selstart_b_p = *selend_b_p = 0; while (x_bufsize_c - inindex_c > 0) { int inchars_b = x->x_bufsize - inindex_b; int inchars_c = x_bufsize_c - inindex_c; int maxindex_c = (inchars_c > widthlimit_c ? widthlimit_c : inchars_c); int maxindex_b = u8_offset(x->x_buf + inindex_b, maxindex_c); int eatchar = 1; int foundit_b = firstone(x->x_buf + inindex_b, '\n', maxindex_b); int foundit_c; if (foundit_b < 0) { /* too much text to fit in one line? */ if (inchars_c > widthlimit_c) { /* is there a space to break the line at? OK if it's even one byte past the end since in this context we know there's more text */ foundit_b = lastone(x->x_buf + inindex_b, ' ', maxindex_b + 1); if (foundit_b < 0) { foundit_b = maxindex_b; foundit_c = maxindex_c; eatchar = 0; } else foundit_c = u8_charnum(x->x_buf + inindex_b, foundit_b); } else { foundit_b = inchars_b; foundit_c = inchars_c; eatchar = 0; } } else foundit_c = u8_charnum(x->x_buf + inindex_b, foundit_b); if (nlines == findy) { int actualx = (findx < 0 ? 0 : (findx > foundit_c ? foundit_c : findx)); *indexp = inindex_b + u8_offset(x->x_buf + inindex_b, actualx); reportedindex = 1; } strncpy(tempbuf+ *outchars_b_p, x->x_buf + inindex_b, foundit_b); if (x->x_selstart >= inindex_b && x->x_selstart <= inindex_b + foundit_b + eatchar) *selstart_b_p = x->x_selstart + *outchars_b_p - inindex_b; if (x->x_selend >= inindex_b && x->x_selend <= inindex_b + foundit_b + eatchar) *selend_b_p = x->x_selend + *outchars_b_p - inindex_b; *outchars_b_p += foundit_b; inindex_b += (foundit_b + eatchar); inindex_c += (foundit_c + eatchar); if (inindex_b < x->x_bufsize) tempbuf[(*outchars_b_p)++] = '\n'; if (foundit_c > ncolumns) ncolumns = foundit_c; nlines++; } if (!reportedindex) *indexp = *outchars_b_p; if (nlines < 1) nlines = 1; if (!widthspec_c) { while (ncolumns < (x->x_text->te_type == T_TEXT ? 1 : 3)) { tempbuf[(*outchars_b_p)++] = ' '; ncolumns++; } } else ncolumns = widthspec_c; *widthp = ncolumns * fontwidth; *heightp = nlines * fontheight; if (glist_getzoom(x->x_glist) > 1) { /* zoom margins */ *widthp += (LMARGIN + RMARGIN) * glist_getzoom(x->x_glist); *heightp += (TMARGIN + BMARGIN) * glist_getzoom(x->x_glist); } else { *widthp += LMARGIN + RMARGIN; *heightp += TMARGIN + BMARGIN; } } /* same as above, but for atom boxes, which are always on one line. */ static void rtext_formatatom(t_rtext *x, int *widthp, int *heightp, int *indexp, char *tempbuf, int *outchars_b_p, int *selstart_b_p, int *selend_b_p, int fontwidth, int fontheight) { int findx = *widthp / fontwidth; /* character index; want byte index */ *indexp = 0; /* special case: for number boxes, try to pare the number down to the specified width of the box. */ if (x->x_text->te_width > 0 && binbuf_getnatom(x->x_text->te_binbuf) == 1 && binbuf_getvec(x->x_text->te_binbuf)->a_type == A_FLOAT && x->x_bufsize > x->x_text->te_width) { /* try to reduce size by dropping decimal digits */ int wantreduce = x->x_bufsize - x->x_text->te_width; char *decimal = 0, *nextchar, *ebuf = x->x_buf + x->x_bufsize, *s1, *s2; int ndecimals; strncpy(tempbuf, x->x_buf, x->x_bufsize); tempbuf[x->x_bufsize] = 0; ebuf = tempbuf + x->x_bufsize; for (decimal = tempbuf; decimal < ebuf; decimal++) if (*decimal == '.') break; if (decimal >= ebuf) goto giveup; for (nextchar = decimal + 1; nextchar < ebuf; nextchar++) if (*nextchar < '0' || *nextchar > '9') break; if (nextchar - decimal - 1 < wantreduce) goto giveup; for (s1 = nextchar - wantreduce, s2 = s1 + wantreduce; s2 < ebuf; s1++, s2++) *s1 = *s2; *outchars_b_p = x->x_text->te_width; goto done; giveup: /* give up and bash last char to '>' */ tempbuf[x->x_text->te_width-1] = '>'; tempbuf[x->x_text->te_width] = 0; *outchars_b_p = x->x_text->te_width; done: ; *indexp = findx; *widthp = x->x_text->te_width * fontwidth; } else { int outchars_c = 0, prev_b = 0; int widthlimit_c = (x->x_text->te_width > 0 ? x->x_text->te_width : 1000); /* nice big fat limit since we can't wrap */ uint32_t thischar; *outchars_b_p = 0; for (outchars_c = 0; *outchars_b_p < x->x_bufsize && outchars_c < widthlimit_c; outchars_c++) { prev_b = *outchars_b_p; thischar = u8_nextchar(x->x_buf, outchars_b_p); if (findx > outchars_c) *indexp = *outchars_b_p; if (thischar == '\n' || !thischar) { *(outchars_b_p) = prev_b; break; } memcpy(tempbuf + prev_b, x->x_buf + prev_b, *outchars_b_p - prev_b); /* if box is full and there's more, bash last char to '>' */ if (outchars_c == widthlimit_c-1 && x->x_bufsize > *(outchars_b_p) && (x->x_buf[*(outchars_b_p)] != ' ' || x->x_bufsize > *(outchars_b_p)+1)) { tempbuf[prev_b] = '>'; } } if (x->x_text->te_width > 0) *widthp = x->x_text->te_width * fontwidth; else *widthp = (outchars_c > 3 ? outchars_c : 3) * fontwidth; tempbuf[*outchars_b_p] = 0; } if (*indexp > *outchars_b_p) *indexp = *outchars_b_p; if (*indexp < 0) *indexp = 0; *selstart_b_p = x->x_selstart; *selend_b_p = x->x_selend; *widthp += (LMARGIN + RMARGIN - 2) * glist_getzoom(x->x_glist); *heightp = fontheight + (TMARGIN + BMARGIN - 1) * glist_getzoom(x->x_glist); } /* the following routine computes line breaks and carries out some action which could be: SEND_FIRST - draw the box for the first time SEND_UPDATE - redraw the updated box otherwise - don't draw, just calculate. Called with *widthp and *heightp as coordinates of a test point, the routine reports the index of the character found there in *indexp. *widthp and *heightp are set to the width and height of the entire text in pixels. */ /* LATER get this and sys_vgui to work together properly, breaking up messages as needed. As of now, there's a limit of 1950 characters, imposed by sys_vgui(). */ #define UPBUFSIZE 4000 void text_getfont(t_text *x, t_glist *thisglist, int *fheightp, int *fwidthp, int *guifsize); static void rtext_senditup(t_rtext *x, int action, int *widthp, int *heightp, int *indexp) { char smallbuf[200], *tempbuf; int outchars_b = 0, guifontsize, fontwidth, fontheight; t_canvas *canvas = glist_getcanvas(x->x_glist); int selstart_b, selend_b; /* beginning and end of selection in bytes */ /* if we're a GOP (the new, "goprect" style) borrow the font size from the inside to preserve the spacing */ text_getfont(x->x_text, x->x_glist, &fontwidth, &fontheight, &guifontsize); if (x->x_bufsize >= 100) tempbuf = (char *)t_getbytes(2 * x->x_bufsize + 1); else tempbuf = smallbuf; tempbuf[0] = 0; if (x->x_text->te_type == T_ATOM) rtext_formatatom(x, widthp, heightp, indexp, tempbuf, &outchars_b, &selstart_b, &selend_b, fontwidth, fontheight); else rtext_formattext(x, widthp, heightp, indexp, tempbuf, &outchars_b, &selstart_b, &selend_b, fontwidth, fontheight); tempbuf[outchars_b]=0; if (action && x->x_text->te_width && x->x_text->te_type != T_ATOM) { /* if our width is specified but the "natural" width is the same as the specified width, set specified width to zero so future text editing will automatically change width. Except atoms whose content changes at runtime. */ int widthwas = x->x_text->te_width, newwidth = 0, newheight = 0, newindex = 0; x->x_text->te_width = 0; rtext_senditup(x, 0, &newwidth, &newheight, &newindex); if (newwidth != *widthp) x->x_text->te_width = widthwas; } if (action && !canvas->gl_havewindow) action = 0; if (action == SEND_FIRST) { const char *tags[] = {x->x_tag, rtext_gettype(x)->s_name, "text"}; int lmargin = LMARGIN, tmargin = TMARGIN; if (glist_getzoom(x->x_glist) > 1) { /* zoom margins */ lmargin *= glist_getzoom(x->x_glist); tmargin *= glist_getzoom(x->x_glist); } /* we add an extra space to the string just in case the last character is an unescaped backslash ('\') which would have confused tcl/tk by escaping the close brace otherwise. The GUI code drops the last character in the string. */ pdgui_vmess("pdtk_text_new", "c S ii s i r", canvas, 3, tags, text_xpix(x->x_text, x->x_glist) + lmargin, text_ypix(x->x_text, x->x_glist) + tmargin, tempbuf, guifontsize, (glist_isselected(x->x_glist, &x->x_text->te_g)? "blue" : "black")); } else if (action == SEND_UPDATE) { pdgui_vmess("pdtk_text_set", "cs s", canvas, x->x_tag, tempbuf); if (*widthp != x->x_drawnwidth || *heightp != x->x_drawnheight) text_drawborder(x->x_text, x->x_glist, x->x_tag, *widthp, *heightp, 0); if (x->x_active) { if (selend_b > selstart_b) { pdgui_vmess(0, "crr si", canvas, "select", "from", x->x_tag, u8_charnum(x->x_buf, selstart_b)); pdgui_vmess(0, "crr si", canvas, "select", "to", x->x_tag, u8_charnum(x->x_buf, selend_b) - 1); pdgui_vmess(0, "crs", canvas, "focus", ""); } else { pdgui_vmess(0, "crr", canvas, "select", "clear"); pdgui_vmess(0, "cr si", canvas, "icursor", x->x_tag, u8_charnum(x->x_buf, selstart_b)); pdgui_vmess("focus", "c", canvas); pdgui_vmess(0, "crs", canvas, "focus", x->x_tag); } } } x->x_drawnwidth = *widthp; x->x_drawnheight = *heightp; if (tempbuf != smallbuf) t_freebytes(tempbuf, 2 * x->x_bufsize + 1); } /* remake text buffer from binbuf */ void rtext_retext(t_rtext *x) { int w = 0, h = 0, indx; t_text *text = x->x_text; t_freebytes(x->x_buf, x->x_bufsize + 1); /* extra 0 byte */ binbuf_gettext(text->te_binbuf, &x->x_buf, &x->x_bufsize); /* allocate extra space for hidden null terminator */ x->x_buf = resizebytes(x->x_buf, x->x_bufsize, x->x_bufsize+1); x->x_buf[x->x_bufsize] = 0; rtext_senditup(x, SEND_UPDATE, &w, &h, &indx); } /* find the rtext that goes with a text item */ t_rtext *glist_findrtext(t_glist *gl, t_text *who) { t_rtext *x; if (!gl->gl_editor) canvas_create_editor(gl); for (x = gl->gl_editor->e_rtext; x && x->x_text != who; x = x->x_next) ; return (x); } int rtext_width(t_rtext *x) { int w = 0, h = 0, indx; rtext_senditup(x, SEND_CHECK, &w, &h, &indx); return (w); } int rtext_height(t_rtext *x) { int w = 0, h = 0, indx; rtext_senditup(x, SEND_CHECK, &w, &h, &indx); return (h); } void rtext_draw(t_rtext *x) { int w = 0, h = 0, indx; rtext_senditup(x, SEND_FIRST, &w, &h, &indx); } void rtext_erase(t_rtext *x) { pdgui_vmess(0, "crs", glist_getcanvas(x->x_glist), "delete", x->x_tag); } void rtext_displace(t_rtext *x, int dx, int dy) { pdgui_vmess(0, "crs ii", glist_getcanvas(x->x_glist), "move", x->x_tag, dx, dy); } void rtext_select(t_rtext *x, int state) { pdgui_vmess(0, "crs rr", glist_getcanvas(x->x_glist), "itemconfigure", x->x_tag, "-fill", (state? "blue" : "black")); } void gatom_undarken(t_text *x); void rtext_activate(t_rtext *x, int state) { int w = 0, h = 0, indx; t_glist *glist = x->x_glist; t_canvas *canvas = glist_getcanvas(glist); if (state) { pdgui_vmess("pdtk_text_editing", "^si", canvas, x->x_tag, 1); glist->gl_editor->e_textedfor = x; glist->gl_editor->e_textdirty = 0; x->x_dragfrom = x->x_selstart = 0; x->x_selend = x->x_bufsize; x->x_active = 1; } else { pdgui_vmess("pdtk_text_editing", "^si", canvas, "", 0); if (glist->gl_editor->e_textedfor == x) glist->gl_editor->e_textedfor = 0; x->x_active = 0; } rtext_senditup(x, SEND_UPDATE, &w, &h, &indx); } /* figure out which atom a click falls into if any; -1 if you clicked on a space or something */ int rtext_findatomfor(t_rtext *x, int xpos, int ypos) { int w = xpos, h = ypos, indx, natom = 0, i, gotone = 0; /* get byte index of character clicked on */ rtext_senditup(x, SEND_UPDATE, &w, &h, &indx); /* search through for whitespace before that index */ for (i = 0; i <= indx; i++) { if (x->x_buf[i] == ';' || x->x_buf[i] == ',') natom++, gotone = 0; else if (x->x_buf[i] == ' ' || x->x_buf[i] == '\n') gotone = 0; else { if (!gotone) natom++; gotone = 1; } } return (natom-1); } void gatom_key(void *z, t_symbol *keysym, t_floatarg f); void rtext_key(t_rtext *x, int keynum, t_symbol *keysym) { int w = 0, h = 0, indx, i, newsize, ndel; char *s1, *s2; /* CR to atom boxes sends message and resets */ if (keynum == '\n' && x->x_text->te_type == T_ATOM) { gatom_key(x->x_text, keysym, keynum); return; } if (keynum) { int n = keynum; if (n == '\r') n = '\n'; if (n == '\b') /* backspace */ { /* LATER delete the box if all text is selected... this causes reentrancy problems now. */ /* if ((!x->x_selstart) && (x->x_selend == x->x_bufsize)) { .... } */ if (x->x_selstart && (x->x_selstart == x->x_selend)) u8_dec(x->x_buf, &x->x_selstart); } else if (n == 127) /* delete */ { if (x->x_selend < x->x_bufsize && (x->x_selstart == x->x_selend)) u8_inc(x->x_buf, &x->x_selend); } ndel = x->x_selend - x->x_selstart; for (i = x->x_selend; i < x->x_bufsize; i++) x->x_buf[i- ndel] = x->x_buf[i]; newsize = x->x_bufsize - ndel; /* allocate extra space for hidden null terminator */ x->x_buf = resizebytes(x->x_buf, x->x_bufsize, newsize+1); x->x_buf[newsize] = 0; x->x_bufsize = newsize; /* at Guenter's suggestion, use 'n>31' to test whether a character might be printable in whatever 8-bit character set we find ourselves. */ /*-- moo: ... but test with "<" rather than "!=" in order to accommodate unicode codepoints for n (which we get since Tk is sending the "%A" substitution for bind ), effectively reducing the coverage of this clause to 7 bits. Case n>127 is covered by the next clause. */ if (n == '\n' || (n > 31 && n < 127)) { newsize = x->x_bufsize+1; /* allocate extra space for hidden null terminator */ x->x_buf = resizebytes(x->x_buf, x->x_bufsize, newsize+1); for (i = x->x_bufsize; i > x->x_selstart; i--) x->x_buf[i] = x->x_buf[i-1]; x->x_buf[x->x_selstart] = n; x->x_buf[newsize] = 0; x->x_bufsize = newsize; x->x_selstart = x->x_selstart + 1; } /*--moo: check for unicode codepoints beyond 7-bit ASCII --*/ else if (n > 127) { int ch_nbytes = u8_wc_nbytes(n); newsize = x->x_bufsize + ch_nbytes; /* allocate extra space for hidden null terminator */ x->x_buf = resizebytes(x->x_buf, x->x_bufsize, newsize+1); for (i = newsize-1; i > x->x_selstart; i--) x->x_buf[i] = x->x_buf[i-ch_nbytes]; x->x_buf[newsize] = 0; x->x_bufsize = newsize; /*-- moo: assume canvas_key() has encoded keysym as UTF-8 */ strncpy(x->x_buf+x->x_selstart, keysym->s_name, ch_nbytes); x->x_selstart = x->x_selstart + ch_nbytes; } x->x_selend = x->x_selstart; x->x_glist->gl_editor->e_textdirty = 1; } else if (!strcmp(keysym->s_name, "Home")) { if (x->x_selend == x->x_selstart) { x->x_selend = x->x_selstart = 0; } else x->x_selstart = 0; } else if (!strcmp(keysym->s_name, "End")) { if (x->x_selend == x->x_selstart) { x->x_selend = x->x_selstart = x->x_bufsize; } else x->x_selend = x->x_bufsize; } else if (!strcmp(keysym->s_name, "Right")) { if (x->x_selend == x->x_selstart && x->x_selstart < x->x_bufsize) { u8_inc(x->x_buf, &x->x_selstart); x->x_selend = x->x_selstart; } else x->x_selstart = x->x_selend; } else if (!strcmp(keysym->s_name, "Left")) { if (x->x_selend == x->x_selstart && x->x_selstart > 0) { u8_dec(x->x_buf, &x->x_selstart); x->x_selend = x->x_selstart; } else x->x_selend = x->x_selstart; } /* this should be improved... life's too short */ else if (!strcmp(keysym->s_name, "Up")) { if (x->x_selstart) u8_dec(x->x_buf, &x->x_selstart); while (x->x_selstart > 0 && x->x_buf[x->x_selstart] != '\n') u8_dec(x->x_buf, &x->x_selstart); x->x_selend = x->x_selstart; } else if (!strcmp(keysym->s_name, "Down")) { while (x->x_selend < x->x_bufsize && x->x_buf[x->x_selend] != '\n') u8_inc(x->x_buf, &x->x_selend); if (x->x_selend < x->x_bufsize) u8_inc(x->x_buf, &x->x_selend); x->x_selstart = x->x_selend; } rtext_senditup(x, SEND_UPDATE, &w, &h, &indx); } void rtext_mouse(t_rtext *x, int xval, int yval, int flag) { int w = xval, h = yval, indx; rtext_senditup(x, SEND_CHECK, &w, &h, &indx); if (flag == RTEXT_DOWN) { x->x_dragfrom = x->x_selstart = x->x_selend = indx; } else if (flag == RTEXT_DBL) { int whereseparator, newseparator; x->x_dragfrom = -1; whereseparator = 0; if ((newseparator = lastone(x->x_buf, ' ', indx)) > whereseparator) whereseparator = newseparator+1; if ((newseparator = lastone(x->x_buf, '\n', indx)) > whereseparator) whereseparator = newseparator+1; if ((newseparator = lastone(x->x_buf, ';', indx)) > whereseparator) whereseparator = newseparator+1; if ((newseparator = lastone(x->x_buf, ',', indx)) > whereseparator) whereseparator = newseparator+1; x->x_selstart = whereseparator; whereseparator = x->x_bufsize - indx; if ((newseparator = firstone(x->x_buf+indx, ' ', x->x_bufsize - indx)) >= 0 && newseparator < whereseparator) whereseparator = newseparator; if ((newseparator = firstone(x->x_buf+indx, '\n', x->x_bufsize - indx)) >= 0 && newseparator < whereseparator) whereseparator = newseparator; if ((newseparator = firstone(x->x_buf+indx, ';', x->x_bufsize - indx)) >= 0 && newseparator < whereseparator) whereseparator = newseparator; if ((newseparator = firstone(x->x_buf+indx, ',', x->x_bufsize - indx)) >= 0 && newseparator < whereseparator) whereseparator = newseparator; x->x_selend = indx + whereseparator; } else if (flag == RTEXT_SHIFT) { if (indx * 2 > x->x_selstart + x->x_selend) x->x_dragfrom = x->x_selstart, x->x_selend = indx; else x->x_dragfrom = x->x_selend, x->x_selstart = indx; } else if (flag == RTEXT_DRAG) { if (x->x_dragfrom < 0) return; x->x_selstart = (x->x_dragfrom < indx ? x->x_dragfrom : indx); x->x_selend = (x->x_dragfrom > indx ? x->x_dragfrom : indx); } rtext_senditup(x, SEND_UPDATE, &w, &h, &indx); } ================================================ FILE: libs/libpd/pure-data/src/g_scalar.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* This file defines the "scalar" object, which is not a text object, just a "gobj". Scalars have templates which describe their structures, which can contain numbers, sublists, and arrays. */ #include /* for read/write to files */ #include "m_pd.h" #include "g_canvas.h" /* ------------- gstubs and gpointers - safe pointing --------------- */ /* create a gstub which is "owned" by a glist (gl) or an array ("a"). */ t_gstub *gstub_new(t_glist *gl, t_array *a) { t_gstub *gs = t_getbytes(sizeof(*gs)); if (gl) { gs->gs_which = GP_GLIST; gs->gs_un.gs_glist = gl; } else { gs->gs_which = GP_ARRAY; gs->gs_un.gs_array = a; } gs->gs_refcount = 0; return (gs); } /* when a "gpointer" is set to point to this stub (so we can later chase down the owner) we increase a reference count. The following routine is called whenever a gpointer is unset from pointing here. If the owner is gone and the refcount goes to zero, we can free the gstub safely. */ static void gstub_dis(t_gstub *gs) { int refcount = --gs->gs_refcount; if ((!refcount) && gs->gs_which == GP_NONE) t_freebytes(gs, sizeof (*gs)); else if (refcount < 0) bug("gstub_dis"); } /* this routing is called by the owner to inform the gstub that it is being deleted. If no gpointers are pointing here, we can free the gstub; otherwise we wait for the last gstub_dis() to free it. */ void gstub_cutoff(t_gstub *gs) { gs->gs_which = GP_NONE; if (gs->gs_refcount < 0) bug("gstub_cutoff"); if (!gs->gs_refcount) t_freebytes(gs, sizeof (*gs)); } /* call this to verify that a pointer is fresh, i.e., that it either points to real data or to the head of a list, and that in either case the object hasn't disappeared since this pointer was generated. Unless "headok" is set, the routine also fails for the head of a list. */ int gpointer_check(const t_gpointer *gp, int headok) { t_gstub *gs = gp->gp_stub; if (!gs) return (0); if (gs->gs_which == GP_ARRAY) { if (gs->gs_un.gs_array->a_valid != gp->gp_valid) return (0); else return (1); } else if (gs->gs_which == GP_GLIST) { if (!headok && !gp->gp_un.gp_scalar) return (0); else if (gs->gs_un.gs_glist->gl_valid != gp->gp_valid) return (0); else return (1); } else return (0); } /* copy a pointer to another, assuming the second one hasn't yet been initialized. New gpointers should be initialized either by this routine or by gpointer_init below. */ void gpointer_copy(const t_gpointer *gpfrom, t_gpointer *gpto) { *gpto = *gpfrom; if (gpto->gp_stub) gpto->gp_stub->gs_refcount++; else bug("gpointer_copy"); } /* clear a gpointer that was previously set, releasing the associated gstub if this was the last reference to it. */ void gpointer_unset(t_gpointer *gp) { t_gstub *gs; if ((gs = gp->gp_stub)) { gstub_dis(gs); gp->gp_stub = 0; } } void gpointer_setglist(t_gpointer *gp, t_glist *glist, t_scalar *x) { t_gstub *gs; if ((gs = gp->gp_stub)) gstub_dis(gs); gp->gp_stub = gs = glist->gl_stub; gp->gp_valid = glist->gl_valid; gp->gp_un.gp_scalar = x; gs->gs_refcount++; } void gpointer_setarray(t_gpointer *gp, t_array *array, t_word *w) { t_gstub *gs; if ((gs = gp->gp_stub)) gstub_dis(gs); gp->gp_stub = gs = array->a_stub; gp->gp_valid = array->a_valid; gp->gp_un.gp_w = w; gs->gs_refcount++; } void gpointer_init(t_gpointer *gp) { gp->gp_stub = 0; gp->gp_valid = 0; gp->gp_un.gp_scalar = 0; } /********* random utility function to find a binbuf in a datum */ /* get the template for the object pointer to. Assumes we've already checked freshness. */ t_symbol *gpointer_gettemplatesym(const t_gpointer *gp) { t_gstub *gs = gp->gp_stub; if (gs->gs_which == GP_GLIST) { t_scalar *sc = gp->gp_un.gp_scalar; if (sc) return (sc->sc_template); else return (0); } else { t_array *a = gs->gs_un.gs_array; return (a->a_templatesym); } } t_binbuf *pointertobinbuf(t_pd *x, t_gpointer *gp, t_symbol *s, const char *fname) { t_symbol *templatesym = gpointer_gettemplatesym(gp), *arraytype; t_template *template; int onset, type; t_binbuf *b; t_gstub *gs = gp->gp_stub; t_word *vec; if (!templatesym) { pd_error(x, "%s: bad pointer", fname); return (0); } if (!(template = template_findbyname(templatesym))) { pd_error(x, "%s: couldn't find template %s", fname, templatesym->s_name); return (0); } if (!template_find_field(template, s, &onset, &type, &arraytype)) { pd_error(x, "%s: %s.%s: no such field", fname, templatesym->s_name, s->s_name); return (0); } if (type != DT_TEXT) { pd_error(x, "%s: %s.%s: not a list", fname, templatesym->s_name, s->s_name); return (0); } if (gs->gs_which == GP_ARRAY) vec = gp->gp_un.gp_w; else vec = gp->gp_un.gp_scalar->sc_vec; return (vec[onset].w_binbuf); } void word_init(t_word *wp, t_template *template, t_gpointer *gp) { int i, nitems = template->t_n; t_dataslot *datatypes = template->t_vec; for (i = 0; i < nitems; i++, datatypes++, wp++) { int type = datatypes->ds_type; if (type == DT_FLOAT) wp->w_float = 0; else if (type == DT_SYMBOL) wp->w_symbol = &s_symbol; else if (type == DT_ARRAY) wp->w_array = array_new(datatypes->ds_arraytemplate, gp); else if (type == DT_TEXT) wp->w_binbuf = binbuf_new(); } } void word_restore(t_word *wp, t_template *template, int argc, t_atom *argv) { int i, nitems = template->t_n; t_dataslot *datatypes = template->t_vec; for (i = 0; i < nitems; i++, datatypes++, wp++) { int type = datatypes->ds_type; if (type == DT_FLOAT) { t_float f; if (argc) { f = atom_getfloat(argv); argv++, argc--; } else f = 0; wp->w_float = f; } else if (type == DT_SYMBOL) { t_symbol *s; if (argc) { s = atom_getsymbol(argv); argv++, argc--; } else s = &s_; wp->w_symbol = s; } } if (argc) post("warning: word_restore: extra arguments"); } void word_free(t_word *wp, t_template *template) { int i; t_dataslot *dt; for (dt = template->t_vec, i = 0; i < template->t_n; i++, dt++) { if (dt->ds_type == DT_ARRAY) array_free(wp[i].w_array); else if (dt->ds_type == DT_TEXT) binbuf_free(wp[i].w_binbuf); } } static int template_cancreate(t_template *template) { int i, type, nitems = template->t_n; t_dataslot *datatypes = template->t_vec; t_template *elemtemplate; for (i = 0; i < nitems; i++, datatypes++) if (datatypes->ds_type == DT_ARRAY && (!(elemtemplate = template_findbyname(datatypes->ds_arraytemplate)) || !template_cancreate(elemtemplate))) { pd_error(0, "%s: no such template", datatypes->ds_arraytemplate->s_name); return (0); } return (1); } /* ------------------- scalars ---------------------- */ t_class *scalar_class; /* make a new scalar and add to the glist. We create a "gp" here which will be used for array items to point back here. This gp doesn't do reference counting or "validation" updates though; the parent won't go away without the contained arrays going away too. The "gp" is copied out by value in the word_init() routine so we can throw our copy away. */ t_scalar *scalar_new(t_glist *owner, t_symbol *templatesym) { t_scalar *x; t_template *template; t_gpointer gp; gpointer_init(&gp); template = template_findbyname(templatesym); if (!template) { pd_error(0, "scalar: couldn't find template %s", templatesym->s_name); return (0); } if (!template_cancreate(template)) return (0); x = (t_scalar *)getbytes(sizeof(t_scalar) + (template->t_n - 1) * sizeof(*x->sc_vec)); x->sc_gobj.g_pd = scalar_class; x->sc_template = templatesym; gpointer_setglist(&gp, owner, x); word_init(x->sc_vec, template, &gp); return (x); } /* Pd method to create a new scalar, add it to a glist, and initialize it from the message arguments. */ void glist_scalar(t_glist *glist, t_symbol *classname, int argc, t_atom *argv) { t_symbol *templatesym = canvas_makebindsym(atom_getsymbolarg(0, argc, argv)); t_binbuf *b; int natoms, nextmsg = 0; t_atom *vec; if (!template_findbyname(templatesym)) { pd_error(glist, "%s: no such template", atom_getsymbolarg(0, argc, argv)->s_name); return; } b = binbuf_new(); binbuf_restore(b, argc, argv); natoms = binbuf_getnatom(b); vec = binbuf_getvec(b); canvas_readscalar(glist, natoms, vec, &nextmsg, 0); binbuf_free(b); } /* -------------------- widget behavior for scalar ------------ */ void scalar_getbasexy(t_scalar *x, t_float *basex, t_float *basey) { t_template *template = template_findbyname(x->sc_template); *basex = template_getfloat(template, gensym("x"), x->sc_vec, 0); *basey = template_getfloat(template, gensym("y"), x->sc_vec, 0); } static void scalar_getrect(t_gobj *z, t_glist *owner, int *xp1, int *yp1, int *xp2, int *yp2) { t_scalar *x = (t_scalar *)z; t_template *template = template_findbyname(x->sc_template); t_canvas *templatecanvas = template_findcanvas(template); int x1 = 0x7fffffff, x2 = -0x7fffffff, y1 = 0x7fffffff, y2 = -0x7fffffff; t_gobj *y; t_float basex, basey; scalar_getbasexy(x, &basex, &basey); /* if someone deleted the template canvas, we're just a point */ if (!templatecanvas) { x1 = x2 = glist_xtopixels(owner, basex); y1 = y2 = glist_ytopixels(owner, basey); } else { x1 = y1 = 0x7fffffff; x2 = y2 = -0x7fffffff; for (y = templatecanvas->gl_list; y; y = y->g_next) { const t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd); int nx1, ny1, nx2, ny2; if (!wb) continue; (*wb->w_parentgetrectfn)(y, owner, x->sc_vec, template, basex, basey, &nx1, &ny1, &nx2, &ny2); if (nx1 < x1) x1 = nx1; if (ny1 < y1) y1 = ny1; if (nx2 > x2) x2 = nx2; if (ny2 > y2) y2 = ny2; } if (x2 < x1 || y2 < y1) x1 = y1 = x2 = y2 = 0; } /* post("scalar x1 %d y1 %d x2 %d y2 %d", x1, y1, x2, y2); */ *xp1 = x1; *yp1 = y1; *xp2 = x2; *yp2 = y2; } static void scalar_drawselectrect(t_scalar *x, t_glist *glist, int state) { char tag[128]; /* FIXME: get rid of this tag (if it's unused) */ sprintf(tag, "select%p", x); if (state) { int x1, y1, x2, y2; scalar_getrect(&x->sc_gobj, glist, &x1, &y1, &x2, &y2); x1--; x2++; y1--; y2++; pdgui_vmess(0, "crr iiiiiiiiii ri rr rs", glist_getcanvas(glist), "create", "line", x1,y1, x1,y2, x2,y2, x2,y1, x1,y1, "-width", 0, "-fill", "blue", "-tags", tag); } else { pdgui_vmess(0, "crs", glist_getcanvas(glist), "delete", tag); } } static void scalar_select(t_gobj *z, t_glist *owner, int state) { t_scalar *x = (t_scalar *)z; t_template *tmpl; t_symbol *templatesym = x->sc_template; t_atom at; t_gpointer gp; gpointer_init(&gp); gpointer_setglist(&gp, owner, x); SETPOINTER(&at, &gp); if ((tmpl = template_findbyname(templatesym))) template_notify(tmpl, (state ? gensym("select") : gensym("deselect")), 1, &at); gpointer_unset(&gp); scalar_drawselectrect(x, owner, state); } static void scalar_displace(t_gobj *z, t_glist *glist, int dx, int dy) { t_scalar *x = (t_scalar *)z; t_symbol *templatesym = x->sc_template; t_template *template = template_findbyname(templatesym); t_symbol *zz; t_atom at[3]; t_gpointer gp; int xonset, yonset, xtype, ytype, gotx, goty; if (!template) { pd_error(0, "scalar: couldn't find template %s", templatesym->s_name); return; } gotx = template_find_field(template, gensym("x"), &xonset, &xtype, &zz); if (gotx && (xtype != DT_FLOAT)) gotx = 0; goty = template_find_field(template, gensym("y"), &yonset, &ytype, &zz); if (goty && (ytype != DT_FLOAT)) goty = 0; if (gotx) *(t_float *)(((char *)(x->sc_vec)) + xonset) += glist->gl_zoom * dx * (glist_pixelstox(glist, 1) - glist_pixelstox(glist, 0)); if (goty) *(t_float *)(((char *)(x->sc_vec)) + yonset) += glist->gl_zoom * dy * (glist_pixelstoy(glist, 1) - glist_pixelstoy(glist, 0)); gpointer_init(&gp); gpointer_setglist(&gp, glist, x); SETPOINTER(&at[0], &gp); SETFLOAT(&at[1], (t_float)dx); SETFLOAT(&at[2], (t_float)dy); template_notify(template, gensym("displace"), 2, at); scalar_redraw(x, glist); } static void scalar_activate(t_gobj *z, t_glist *owner, int state) { /* post("scalar_activate %d", state); */ /* later */ } static void scalar_delete(t_gobj *z, t_glist *glist) { /* nothing to do */ } static void scalar_vis(t_gobj *z, t_glist *owner, int vis) { t_scalar *x = (t_scalar *)z; t_template *template = template_findbyname(x->sc_template); t_canvas *templatecanvas = template_findcanvas(template); t_gobj *y; t_float basex, basey; scalar_getbasexy(x, &basex, &basey); /* if we don't know how to draw it, make a small rectangle */ if (!templatecanvas) { char tag[128]; /* FIXME: get rid of this tag (if it's unused) */ sprintf(tag, "scalar%p", x); if (vis) { int x1 = glist_xtopixels(owner, basex); int y1 = glist_ytopixels(owner, basey); pdgui_vmess(0, "crr iiii rs", glist_getcanvas(owner), "create", "rectangle", x1-1,y1-1, x1+1,y1+1, "-tags", tag); } else pdgui_vmess(0, "crs", glist_getcanvas(owner), "delete", tag); return; } for (y = templatecanvas->gl_list; y; y = y->g_next) { const t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd); if (!wb) continue; (*wb->w_parentvisfn)(y, owner, x->sc_vec, template, basex, basey, vis); } if (glist_isselected(owner, &x->sc_gobj)) { scalar_drawselectrect(x, owner, 0); scalar_drawselectrect(x, owner, 1); } sys_unqueuegui(x); } static void scalar_doredraw(t_gobj *client, t_glist *glist) { if (glist_isvisible(glist)) { scalar_vis(client, glist, 0); scalar_vis(client, glist, 1); } } void scalar_redraw(t_scalar *x, t_glist *glist) { if (glist_isvisible(glist)) sys_queuegui(x, glist, scalar_doredraw); } extern void template_notifyforscalar(t_template *template, t_glist *owner, t_scalar *sc, t_symbol *s, int argc, t_atom *argv); int scalar_doclick(t_word *data, t_template *template, t_scalar *sc, t_array *ap, struct _glist *owner, t_float xloc, t_float yloc, int xpix, int ypix, int shift, int alt, int dbl, int doit) { int hit = 0; t_canvas *templatecanvas = template_findcanvas(template); t_gobj *y; t_atom at[3]; t_float basex = template_getfloat(template, gensym("x"), data, 0); t_float basey = template_getfloat(template, gensym("y"), data, 0); SETFLOAT(at, 0); /* unused - this is later bashed to the gpointer */ SETFLOAT(at+1, basex + xloc); SETFLOAT(at+2, basey + yloc); if (doit) template_notifyforscalar(template, owner, sc, gensym("click"), 3, at); for (y = templatecanvas->gl_list; y; y = y->g_next) { const t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd); if (!wb) continue; if ((hit = (*wb->w_parentclickfn)(y, owner, data, template, sc, ap, basex + xloc, basey + yloc, xpix, ypix, shift, alt, dbl, doit))) return (hit); } return (0); } static int scalar_click(t_gobj *z, struct _glist *owner, int xpix, int ypix, int shift, int alt, int dbl, int doit) { t_scalar *x = (t_scalar *)z; t_template *template = template_findbyname(x->sc_template); return (scalar_doclick(x->sc_vec, template, x, 0, owner, 0, 0, xpix, ypix, shift, alt, dbl, doit)); } static void scalar_save(t_gobj *z, t_binbuf *b) { t_scalar *x = (t_scalar *)z; t_binbuf *b2 = binbuf_new(); t_atom a, *argv; int i, argc; canvas_writescalar(x->sc_template, x->sc_vec, b2, 0); binbuf_addv(b, "ss", &s__X, gensym("scalar")); binbuf_addbinbuf(b, b2); binbuf_addsemi(b); binbuf_free(b2); } static void scalar_properties(t_gobj *z, struct _glist *owner) { t_scalar *x = (t_scalar *)z; char *buf, buf2[80]; int bufsize; t_binbuf *b; glist_noselect(owner); glist_select(owner, z); b = glist_writetobinbuf(owner, 0); binbuf_gettext(b, &buf, &bufsize); binbuf_free(b); pdgui_stub_vnew((t_pd*)owner, "pdtk_data_dialog", x, "p", bufsize, buf); t_freebytes(buf, bufsize); } static const t_widgetbehavior scalar_widgetbehavior = { scalar_getrect, scalar_displace, scalar_select, scalar_activate, scalar_delete, scalar_vis, scalar_click, }; static void scalar_free(t_scalar *x) { int i; t_dataslot *datatypes, *dt; t_symbol *templatesym = x->sc_template; t_template *template = template_findbyname(templatesym); sys_unqueuegui(x); if (!template) { pd_error(0, "scalar: couldn't find template %s", templatesym->s_name); return; } word_free(x->sc_vec, template); pdgui_stub_deleteforkey(x); /* the "size" field in the class is zero, so Pd doesn't try to free us automatically (see pd_free()) */ freebytes(x, sizeof(t_scalar) + (template->t_n - 1) * sizeof(*x->sc_vec)); } /* ----------------- setup function ------------------- */ void g_scalar_setup(void) { scalar_class = class_new(gensym("scalar"), 0, (t_method)scalar_free, 0, CLASS_GOBJ, 0); class_setwidget(scalar_class, &scalar_widgetbehavior); class_setsavefn(scalar_class, scalar_save); class_setpropertiesfn(scalar_class, scalar_properties); } ================================================ FILE: libs/libpd/pure-data/src/g_slider.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */ /* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja * refactored 2022 by IOhannes m zmölnig */ #include #include #include "m_pd.h" #include "g_all_guis.h" #include #define LMARGIN 3 #define RMARGIN 2 #define TMARGIN 2 #define BMARGIN 3 /* ------------ horizontal/vertical slider ----------------------- */ t_widgetbehavior slider_widgetbehavior; static t_class *slider_class; /* forward declarations */ static void slider_set(t_slider *x, t_floatarg f); /* widget helper functions */ /* cannot use iemgui's default draw_iolets, because * - we have to deal with those stupid offsets,... * - we want to make sure that the iolets are below the KNOB (rather than the LABEL) */ static void slider_draw_io(t_slider* x, t_glist* glist, int old_snd_rcv_flags) { const int zoom = IEMGUI_ZOOM(x); t_canvas *canvas = glist_getcanvas(glist); int xpos = text_xpix(&x->x_gui.x_obj, glist); int ypos = text_ypix(&x->x_gui.x_obj, glist); int iow = IOWIDTH * zoom, ioh = IEM_GUI_IOHEIGHT * zoom; int lmargin = 0, tmargin=0, bmargin = 0; char tag_object[128], tag_knob[128], tag[128]; char *tags[] = {tag_object, tag}; (void)old_snd_rcv_flags; sprintf(tag_object, "%pOBJ", x); sprintf(tag_knob, "%pKNOB", x); if(x->x_orientation == horizontal) { lmargin = LMARGIN * zoom; } else { tmargin = TMARGIN * zoom; bmargin = BMARGIN * zoom; } sprintf(tag, "%pOUT%d", x, 0); pdgui_vmess(0, "crs", canvas, "delete", tag); if(!x->x_gui.x_fsf.x_snd_able) { pdgui_vmess(0, "crr iiii rs rS", canvas, "create", "rectangle", xpos - lmargin, ypos + x->x_gui.x_h + bmargin + zoom - ioh, xpos - lmargin + iow, ypos + x->x_gui.x_h + bmargin, "-fill", "black", "-tags", 2, tags); /* keep knob above outlet */ pdgui_vmess(0, "crss", canvas, "lower", tag, tag_knob); } sprintf(tag, "%pIN%d", x, 0); pdgui_vmess(0, "crs", canvas, "delete", tag); if(!x->x_gui.x_fsf.x_rcv_able) { pdgui_vmess(0, "crr iiii rs rS", canvas, "create", "rectangle", xpos - lmargin, ypos - tmargin, xpos - lmargin + iow, ypos - tmargin - zoom + ioh, "-fill", "black", "-tags", 2, tags); /* keep knob above inlet */ pdgui_vmess(0, "crss", canvas, "lower", tag, tag_knob); } } static void slider_knob_position(t_slider*x, t_glist *glist, int val, int *x0, int *y0, int *x1, int *y1) { const int zoom = IEMGUI_ZOOM(x); int xpos = text_xpix(&x->x_gui.x_obj, glist); int ypos = text_ypix(&x->x_gui.x_obj, glist); if(x->x_orientation == horizontal) { int r = xpos + val; *x0 = r; *y0 = ypos + (zoom + 1); *x1 = r; *y1 = ypos + x->x_gui.x_h - (zoom * 2); } else { int r = ypos + x->x_gui.x_h - val; *x0 = xpos + (zoom + 1); *y0 = r; *x1 = xpos + x->x_gui.x_w - (zoom * 2); *y1 = r; } } static void slider_draw_config(t_slider* x, t_glist* glist) { const int zoom = IEMGUI_ZOOM(x); t_canvas *canvas = glist_getcanvas(glist); t_iemgui *iemgui = &x->x_gui; int xpos = text_xpix(&x->x_gui.x_obj, glist); int ypos = text_ypix(&x->x_gui.x_obj, glist); int val = ((x->x_val + 50)/100); int iow = IOWIDTH * zoom, ioh = IEM_GUI_IOHEIGHT * zoom; int lmargin = 0, rmargin = 0, tmargin = 0, bmargin = 0; int a, b, c, d; char tag[128]; t_atom fontatoms[3]; SETSYMBOL(fontatoms+0, gensym(iemgui->x_font)); SETFLOAT (fontatoms+1, -iemgui->x_fontsize*zoom); SETSYMBOL(fontatoms+2, gensym(sys_fontweight)); if(x->x_orientation == horizontal) { lmargin = LMARGIN * zoom; rmargin = RMARGIN * zoom; } else { tmargin = TMARGIN * zoom; bmargin = BMARGIN * zoom; } slider_knob_position(x, glist, val, &a, &b, &c, &d); sprintf(tag, "%pBASE", x); pdgui_vmess(0, "crs iiii", canvas, "coords", tag, xpos - lmargin, ypos - tmargin, xpos + x->x_gui.x_w + rmargin, ypos + x->x_gui.x_h + bmargin); pdgui_vmess(0, "crs ri rk", canvas, "itemconfigure", tag, "-width", zoom, "-fill", x->x_gui.x_bcol); sprintf(tag, "%pKNOB", x); pdgui_vmess(0, "crs iiii", canvas, "coords", tag, a, b, c, d); pdgui_vmess(0, "crs ri rk", canvas, "itemconfigure", tag, "-width", 1 + 2 * zoom, "-outline", x->x_gui.x_fcol); sprintf(tag, "%pLABEL", x); pdgui_vmess(0, "crs ii", canvas, "coords", tag, xpos + x->x_gui.x_ldx * zoom, ypos + x->x_gui.x_ldy * zoom); pdgui_vmess(0, "crs rA rk", canvas, "itemconfigure", tag, "-font", 3, fontatoms, "-fill", (x->x_gui.x_fsf.x_selected ? IEM_GUI_COLOR_SELECTED : x->x_gui.x_lcol)); iemgui_dolabel(x, &x->x_gui, x->x_gui.x_lab, 1); } static void slider_draw_new(t_slider *x, t_glist *glist) { t_canvas *canvas = glist_getcanvas(glist); char tag[128], tag_object[128]; char*tags[] = {tag_object, tag, "label", "text"}; sprintf(tag_object, "%pOBJ", x); sprintf(tag, "%pBASE", x); pdgui_vmess(0, "crr iiii rS", canvas, "create", "rectangle", 0, 0, 0, 0, "-tags", 2, tags); sprintf(tag, "%pKNOB", x); pdgui_vmess(0, "crr iiii rS", canvas, "create", "rectangle", 0, 0, 0, 0, "-tags", 2, tags); sprintf(tag, "%pLABEL", x); pdgui_vmess(0, "crr ii rs rS", canvas, "create", "text", 0, 0, "-anchor", "w", "-tags", 4, tags); slider_draw_config(x, glist); (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_IO); } static void slider_draw_select(t_slider* x, t_glist* glist) { t_canvas *canvas = glist_getcanvas(glist); int col = IEM_GUI_COLOR_NORMAL, lcol = x->x_gui.x_lcol; char tag[128]; if(x->x_gui.x_fsf.x_selected) col = lcol = IEM_GUI_COLOR_SELECTED; sprintf(tag, "%pBASE", x); pdgui_vmess(0, "crs rk", canvas, "itemconfigure", tag, "-outline", col); sprintf(tag, "%pLABEL", x); pdgui_vmess(0, "crs rk", canvas, "itemconfigure", tag, "-fill", lcol); } static void slider_draw_update(t_gobj *client, t_glist *glist) { t_slider *x = (t_slider *)client; int a, b, c, d; if (glist_isvisible(glist)) { const int zoom = IEMGUI_ZOOM(x); t_canvas *canvas = glist_getcanvas(glist); int xpos = text_xpix(&x->x_gui.x_obj, glist); int ypos = text_ypix(&x->x_gui.x_obj, glist); int val = ((x->x_val + 50) / 100) * zoom; char tag[128]; sprintf(tag, "%pKNOB", x); slider_knob_position(x, glist, val, &a, &b, &c, &d); pdgui_vmess(0, "crs iiii", canvas, "coords", tag, a, b, c, d); } } /* ------------------------ hsl widgetbehaviour----------------------------- */ static void slider_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2) { t_slider* x = (t_slider*)z; int zoom = glist_getzoom(glist); int dx1=0, dx2=0, dy1=0, dy2=0; if(x->x_orientation == horizontal) { dx1 = LMARGIN*zoom; dx2 = (LMARGIN + RMARGIN)*zoom; } else { dy1 = TMARGIN*zoom; dy2 = (TMARGIN + BMARGIN)*zoom; } *xp1 = text_xpix(&x->x_gui.x_obj, glist) - dx1; *yp1 = text_ypix(&x->x_gui.x_obj, glist) - dy1; *xp2 = *xp1 + x->x_gui.x_w + dx2; *yp2 = *yp1 + x->x_gui.x_h + dy2; } static void slider_save(t_gobj *z, t_binbuf *b) { t_slider *x = (t_slider *)z; t_symbol *bflcol[3]; t_symbol *srl[3]; iemgui_save(&x->x_gui, srl, bflcol); binbuf_addv(b, "ssiisiiffiisssiiiisssii", gensym("#X"), gensym("obj"), (int)x->x_gui.x_obj.te_xpix, (int)x->x_gui.x_obj.te_ypix, gensym((x->x_orientation==horizontal)?"hsl":"vsl"), x->x_gui.x_w/IEMGUI_ZOOM(x), x->x_gui.x_h/IEMGUI_ZOOM(x), (t_float)x->x_min, (t_float)x->x_max, x->x_lin0_log1, iem_symargstoint(&x->x_gui.x_isa), srl[0], srl[1], srl[2], x->x_gui.x_ldx, x->x_gui.x_ldy, iem_fstyletoint(&x->x_gui.x_fsf), x->x_gui.x_fontsize, bflcol[0], bflcol[1], bflcol[2], x->x_gui.x_isa.x_loadinit?x->x_val:0, x->x_steady); binbuf_addv(b, ";"); } static int slider_check_range(t_slider *x, int v) { if(v < IEM_SL_MINSIZE * IEMGUI_ZOOM(x)) v = IEM_SL_MINSIZE * IEMGUI_ZOOM(x); if(x->x_val > (v * 100 - 100)) { x->x_val = v * 100 - 100; } if(x->x_lin0_log1) x->x_k = log(x->x_max / x->x_min) / (double)(v/IEMGUI_ZOOM(x) - 1); else x->x_k = (x->x_max - x->x_min) / (double)(v/IEMGUI_ZOOM(x) - 1); return v; } static void slider_check_minmax(t_slider *x, double min, double max, t_float value) { if(x->x_lin0_log1) { if((min == 0.0) && (max == 0.0)) max = 1.0; if(max > 0.0) { if(min <= 0.0) min = 0.01 * max; } else { if(min > 0.0) max = 0.01 * min; } } x->x_min = min; x->x_max = max; if(x->x_lin0_log1) x->x_k = log(x->x_max/x->x_min) / (double)(value/IEMGUI_ZOOM(x) - 1); else x->x_k = (x->x_max - x->x_min) / (double)(value/IEMGUI_ZOOM(x) - 1); } static void slider_properties(t_gobj *z, t_glist *owner) { t_slider *x = (t_slider *)z; const char*objname, *rangeA, *rangeB; int minWidth, minHeight; if(x->x_orientation == horizontal) { objname = "hsl"; minWidth = IEM_SL_MINSIZE; minHeight = IEM_GUI_MINSIZE; } else { objname = "vsl"; minWidth = IEM_GUI_MINSIZE; minHeight = IEM_SL_MINSIZE; } iemgui_new_dialog(x, &x->x_gui, objname, x->x_gui.x_w/IEMGUI_ZOOM(x), minWidth, x->x_gui.x_h/IEMGUI_ZOOM(x), minHeight, x->x_min, x->x_max, 0, x->x_lin0_log1, "linear", "logarithmic", 1, x->x_steady, -1); } /* compute numeric value (fval) from pixel location (val) and range */ static t_float slider_getfval(t_slider *x) { t_float fval; int rounded_val = (x->x_gui.x_fsf.x_finemoved) ? x->x_val : (x->x_val / 100) * 100; /* if rcv==snd, don't round the value to prevent bad dragging when zoomed-in */ if(x->x_gui.x_fsf.x_snd_able && (x->x_gui.x_snd == x->x_gui.x_rcv)) rounded_val = x->x_val; if (x->x_lin0_log1) fval = x->x_min * exp(x->x_k * (double)(rounded_val) * 0.01); else fval = (double)(rounded_val) * 0.01 * x->x_k + x->x_min; if ((fval < 1.0e-10) && (fval > -1.0e-10)) fval = 0.0; return (fval); } static void slider_bang(t_slider *x) { double out; if (pd_compatibilitylevel < 46) out = slider_getfval(x); else out = x->x_fval; outlet_float(x->x_gui.x_obj.ob_outlet, out); if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) pd_float(x->x_gui.x_snd->s_thing, out); } static void slider_dialog(t_slider *x, t_symbol *s, int argc, t_atom *argv) { t_symbol *srl[3]; int w = (int)atom_getfloatarg(0, argc, argv); int h = (int)atom_getfloatarg(1, argc, argv); double min = (double)atom_getfloatarg(2, argc, argv); double max = (double)atom_getfloatarg(3, argc, argv); int lilo = (int)atom_getfloatarg(4, argc, argv); int steady = (int)atom_getfloatarg(17, argc, argv); int sr_flags; t_atom undo[18]; if(x->x_orientation == horizontal) w *= IEMGUI_ZOOM(x); else h *= IEMGUI_ZOOM(x); iemgui_setdialogatoms(&x->x_gui, 18, undo); SETFLOAT(undo+2, x->x_min); SETFLOAT(undo+3, x->x_max); SETFLOAT(undo+4, x->x_lin0_log1); SETFLOAT(undo+17, x->x_steady); pd_undo_set_objectstate(x->x_gui.x_glist, (t_pd*)x, gensym("dialog"), 18, undo, argc, argv); x->x_lin0_log1 = !!lilo; x->x_steady = !!steady; sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv); if(x->x_orientation == horizontal) { x->x_gui.x_h = iemgui_clip_size(h) * IEMGUI_ZOOM(x); x->x_gui.x_w = slider_check_range(x, w); slider_check_minmax(x, min, max, x->x_gui.x_w); } else { x->x_gui.x_h = slider_check_range(x, h); x->x_gui.x_w = iemgui_clip_size(w) * IEMGUI_ZOOM(x); slider_check_minmax(x, min, max, x->x_gui.x_h); } iemgui_size(x, &x->x_gui); slider_set(x, x->x_fval); } static void slider_motion(t_slider *x, t_floatarg dx, t_floatarg dy, t_floatarg up) { int old = x->x_val; int pos; float delta; if (up != 0) return; if(x->x_orientation == horizontal) { pos = x->x_gui.x_w / IEMGUI_ZOOM(x); delta = dx; } else { pos = x->x_gui.x_h / IEMGUI_ZOOM(x); delta = -dy; } if(!x->x_gui.x_fsf.x_finemoved) delta = (100 * delta) / IEMGUI_ZOOM(x); x->x_pos += (int)delta; x->x_val = x->x_pos; if(x->x_val > (100 * pos - 100)) { x->x_val = 100 * pos - 100; x->x_pos += 50 / IEMGUI_ZOOM(x); x->x_pos -= x->x_pos % (100 / IEMGUI_ZOOM(x)); } if(x->x_val < 0) { x->x_val = 0; x->x_pos -= 50 / IEMGUI_ZOOM(x); x->x_pos -= x->x_pos % (100 / IEMGUI_ZOOM(x)); } x->x_fval = slider_getfval(x); if (old != x->x_val) { (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); slider_bang(x); } } static void slider_click(t_slider *x, t_floatarg xpos, t_floatarg ypos, t_floatarg shift, t_floatarg ctrl, t_floatarg alt) { t_float val; int maxval; if(x->x_orientation == horizontal) { val = (xpos - text_xpix(&x->x_gui.x_obj, x->x_gui.x_glist)); maxval = x->x_gui.x_w / IEMGUI_ZOOM(x); } else { val = (x->x_gui.x_h + text_ypix(&x->x_gui.x_obj, x->x_gui.x_glist) - ypos); maxval = x->x_gui.x_h / IEMGUI_ZOOM(x); } maxval = 100 * maxval - 100; if(!x->x_steady) x->x_val = (int)((100.0 * val) / IEMGUI_ZOOM(x)); if(x->x_val > maxval) x->x_val = maxval; if(x->x_val < 0) x->x_val = 0; x->x_fval = slider_getfval(x); x->x_pos = x->x_val; (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); slider_bang(x); glist_grab(x->x_gui.x_glist, &x->x_gui.x_obj.te_g, (t_glistmotionfn)slider_motion, 0, xpos, ypos); } static int slider_newclick(t_gobj *z, struct _glist *glist, int xpix, int ypix, int shift, int alt, int dbl, int doit) { t_slider* x = (t_slider *)z; if(doit) { slider_click(x, (t_floatarg)xpix, (t_floatarg)ypix, (t_floatarg)shift, 0, (t_floatarg)alt); if(shift) x->x_gui.x_fsf.x_finemoved = 1; else x->x_gui.x_fsf.x_finemoved = 0; } return (1); } static void slider_set(t_slider *x, t_floatarg f) { int old = x->x_val; double g; x->x_fval = f; if (x->x_min > x->x_max) { if(f > x->x_min) f = x->x_min; if(f < x->x_max) f = x->x_max; } else { if(f > x->x_max) f = x->x_max; if(f < x->x_min) f = x->x_min; } if(x->x_lin0_log1) g = log(f/x->x_min) / x->x_k; else g = (f - x->x_min) / x->x_k; x->x_val = (int)(100.0*g + 0.49999); if (x->x_val < 0) x->x_val = 0; x->x_pos = x->x_val; if(x->x_val != old) (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); } static void slider_float(t_slider *x, t_floatarg f) { slider_set(x, f); if(x->x_gui.x_fsf.x_put_in2out) slider_bang(x); } static void slider_size(t_slider *x, t_symbol *s, int ac, t_atom *av) { int w = (int)atom_getfloatarg(0, ac, av); int h = (int)atom_getfloatarg(1, ac, av); if(x->x_orientation == horizontal) { x->x_gui.x_w = slider_check_range(x, w*IEMGUI_ZOOM(x)); if(ac > 1) x->x_gui.x_h = iemgui_clip_size(h) * IEMGUI_ZOOM(x); } else { x->x_gui.x_w = iemgui_clip_size(w) * IEMGUI_ZOOM(x); if(ac > 1) x->x_gui.x_h = slider_check_range(x, h*IEMGUI_ZOOM(x)); } iemgui_size((void *)x, &x->x_gui); slider_set(x, x->x_fval); } static void slider_delta(t_slider *x, t_symbol *s, int ac, t_atom *av) {iemgui_delta((void *)x, &x->x_gui, s, ac, av);} static void slider_pos(t_slider *x, t_symbol *s, int ac, t_atom *av) {iemgui_pos((void *)x, &x->x_gui, s, ac, av);} static void slider_range(t_slider *x, t_symbol *s, int ac, t_atom *av) { slider_check_minmax(x, (double)atom_getfloatarg(0, ac, av), (double)atom_getfloatarg(1, ac, av), (x->x_orientation==horizontal)?x->x_gui.x_w:x->x_gui.x_h); slider_set(x, x->x_fval); } static void slider_color(t_slider *x, t_symbol *s, int ac, t_atom *av) {iemgui_color((void *)x, &x->x_gui, s, ac, av);} static void slider_send(t_slider *x, t_symbol *s) {iemgui_send(x, &x->x_gui, s);} static void slider_receive(t_slider *x, t_symbol *s) {iemgui_receive(x, &x->x_gui, s);} static void slider_label(t_slider *x, t_symbol *s) {iemgui_label((void *)x, &x->x_gui, s);} static void slider_label_pos(t_slider *x, t_symbol *s, int ac, t_atom *av) {iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);} static void slider_label_font(t_slider *x, t_symbol *s, int ac, t_atom *av) {iemgui_label_font((void *)x, &x->x_gui, s, ac, av);} static void slider_log(t_slider *x) { x->x_lin0_log1 = 1; slider_check_minmax(x, x->x_min, x->x_max, (x->x_orientation==horizontal)?x->x_gui.x_w:x->x_gui.x_h); slider_set(x, x->x_fval); } static void slider_lin(t_slider *x) { double v = (x->x_orientation==horizontal)?x->x_gui.x_w:x->x_gui.x_h; x->x_lin0_log1 = 0; x->x_k = (x->x_max - x->x_min) / (v/IEMGUI_ZOOM(x) - 1); slider_set(x, x->x_fval); } static void slider_init(t_slider *x, t_floatarg f) { x->x_gui.x_isa.x_loadinit = (f == 0.0) ? 0 : 1; } static void slider_steady(t_slider *x, t_floatarg f) { x->x_steady = (f == 0.0) ? 0 : 1; } static void slider_orientation(t_slider *x, t_floatarg forient) { int orient = !!(int)forient; if(orient != x->x_orientation) { int w = x->x_gui.x_w; x->x_gui.x_w = x->x_gui.x_h; x->x_gui.x_h = w; /* urgh. for historical reasons, * sliders have a different offset depending on their orientation. * for backwards-compatibility, an ideal solution would handle * these offsets during load/save, * but we cannot access the object position in the constructor, * so we need to do it here... */ x->x_gui.x_obj.te_xpix += LMARGIN * ((horizontal==orient)?1:-1); x->x_gui.x_obj.te_ypix -= TMARGIN * ((horizontal==orient)?1:-1); } x->x_orientation = orient; iemgui_size(x, &x->x_gui); } static void slider_zoom(t_slider *x, t_floatarg f) { iemgui_zoom(&x->x_gui, f); (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); } static void slider_loadbang(t_slider *x, t_floatarg action) { if (action == LB_LOAD && x->x_gui.x_isa.x_loadinit) { (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); slider_bang(x); } } static void *slider_new(t_symbol *s, int argc, t_atom *argv) { t_slider *x = (t_slider *)iemgui_new(slider_class); int lilo = 0, steady = 1; int fs = x->x_gui.x_fontsize; double min = 0.0, max = (double)(IEM_SL_DEFAULTSIZE-1); t_float v = 0; int w, h, ldx, ldy; IEMGUI_SETDRAWFUNCTIONS(x, slider); if('v' == *s->s_name) x->x_orientation = vertical; if(x->x_orientation == horizontal) { w = IEM_SL_DEFAULTSIZE; h = IEM_GUI_DEFAULTSIZE; w *= IEM_GUI_DEFAULTSIZE_SCALE; /* keep aspect ratio */ ldx = -2; ldy = -8 * IEM_GUI_DEFAULTSIZE_SCALE; } else { w = IEM_GUI_DEFAULTSIZE; h = IEM_SL_DEFAULTSIZE; h *= IEM_GUI_DEFAULTSIZE_SCALE; /* keep aspect ratio */ ldx = 0; ldy = -9; } if(((argc == 17)||(argc == 18))&&IS_A_FLOAT(argv,0)&&IS_A_FLOAT(argv,1) &&IS_A_FLOAT(argv,2)&&IS_A_FLOAT(argv,3) &&IS_A_FLOAT(argv,4)&&IS_A_FLOAT(argv,5) &&(IS_A_SYMBOL(argv,6)||IS_A_FLOAT(argv,6)) &&(IS_A_SYMBOL(argv,7)||IS_A_FLOAT(argv,7)) &&(IS_A_SYMBOL(argv,8)||IS_A_FLOAT(argv,8)) &&IS_A_FLOAT(argv,9)&&IS_A_FLOAT(argv,10) &&IS_A_FLOAT(argv,11)&&IS_A_FLOAT(argv,12)&&IS_A_FLOAT(argv,16)) { w = (int)atom_getfloatarg(0, argc, argv); h = (int)atom_getfloatarg(1, argc, argv); min = (double)atom_getfloatarg(2, argc, argv); max = (double)atom_getfloatarg(3, argc, argv); lilo = (int)atom_getfloatarg(4, argc, argv); iem_inttosymargs(&x->x_gui.x_isa, atom_getfloatarg(5, argc, argv)); iemgui_new_getnames(&x->x_gui, 6, argv); ldx = (int)atom_getfloatarg(9, argc, argv); ldy = (int)atom_getfloatarg(10, argc, argv); iem_inttofstyle(&x->x_gui.x_fsf, atom_getfloatarg(11, argc, argv)); fs = (int)atom_getfloatarg(12, argc, argv); iemgui_all_loadcolors(&x->x_gui, argv+13, argv+14, argv+15); v = atom_getfloatarg(16, argc, argv); } else iemgui_new_getnames(&x->x_gui, 6, 0); if((argc == 18)&&IS_A_FLOAT(argv,17)) steady = (int)atom_getfloatarg(17, argc, argv); x->x_gui.x_fsf.x_snd_able = (0 != x->x_gui.x_snd); x->x_gui.x_fsf.x_rcv_able = (0 != x->x_gui.x_rcv); if (x->x_gui.x_isa.x_loadinit) x->x_val = v; else x->x_val = 0; if(lilo != 0) lilo = 1; x->x_lin0_log1 = lilo; if(steady != 0) steady = 1; x->x_steady = steady; if(x->x_gui.x_fsf.x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica"); else if(x->x_gui.x_fsf.x_font_style == 2) strcpy(x->x_gui.x_font, "times"); else { x->x_gui.x_fsf.x_font_style = 0; strcpy(x->x_gui.x_font, sys_font); } if(x->x_gui.x_fsf.x_rcv_able) pd_bind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); x->x_gui.x_ldx = ldx; x->x_gui.x_ldy = ldy; x->x_gui.x_fontsize = (fs < 4)?4:fs; if(x->x_orientation == horizontal) { x->x_gui.x_w = slider_check_range(x, w); x->x_gui.x_h = iemgui_clip_size(h); } else { x->x_gui.x_w = iemgui_clip_size(w); x->x_gui.x_h = slider_check_range(x, h); } iemgui_verify_snd_ne_rcv(&x->x_gui); iemgui_newzoom(&x->x_gui); slider_check_minmax(x, min, max, (x->x_orientation==horizontal)?x->x_gui.x_w:x->x_gui.x_h); outlet_new(&x->x_gui.x_obj, &s_float); x->x_fval = slider_getfval(x); return (x); } static void slider_free(t_slider *x) { if(x->x_gui.x_fsf.x_rcv_able) pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); pdgui_stub_deleteforkey(x); } void g_slider_setup(void) { slider_class = class_new(gensym("hsl"), (t_newmethod)slider_new, (t_method)slider_free, sizeof(t_slider), 0, A_GIMME, 0); class_addcreator((t_newmethod)slider_new, gensym("vsl"), A_GIMME, 0); class_addcreator((t_newmethod)slider_new, gensym("hslider"), A_GIMME, 0); class_addcreator((t_newmethod)slider_new, gensym("vslider"), A_GIMME, 0); class_addbang(slider_class, slider_bang); class_addfloat(slider_class, slider_float); class_addmethod(slider_class, (t_method)slider_click, gensym("click"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); class_addmethod(slider_class, (t_method)slider_motion, gensym("motion"), A_FLOAT, A_FLOAT, A_DEFFLOAT, 0); class_addmethod(slider_class, (t_method)slider_dialog, gensym("dialog"), A_GIMME, 0); class_addmethod(slider_class, (t_method)slider_loadbang, gensym("loadbang"), A_DEFFLOAT, 0); class_addmethod(slider_class, (t_method)slider_set, gensym("set"), A_FLOAT, 0); class_addmethod(slider_class, (t_method)slider_size, gensym("size"), A_GIMME, 0); class_addmethod(slider_class, (t_method)slider_delta, gensym("delta"), A_GIMME, 0); class_addmethod(slider_class, (t_method)slider_pos, gensym("pos"), A_GIMME, 0); class_addmethod(slider_class, (t_method)slider_range, gensym("range"), A_GIMME, 0); class_addmethod(slider_class, (t_method)slider_color, gensym("color"), A_GIMME, 0); class_addmethod(slider_class, (t_method)slider_send, gensym("send"), A_DEFSYM, 0); class_addmethod(slider_class, (t_method)slider_receive, gensym("receive"), A_DEFSYM, 0); class_addmethod(slider_class, (t_method)slider_label, gensym("label"), A_DEFSYM, 0); class_addmethod(slider_class, (t_method)slider_label_pos, gensym("label_pos"), A_GIMME, 0); class_addmethod(slider_class, (t_method)slider_label_font, gensym("label_font"), A_GIMME, 0); class_addmethod(slider_class, (t_method)slider_log, gensym("log"), 0); class_addmethod(slider_class, (t_method)slider_lin, gensym("lin"), 0); class_addmethod(slider_class, (t_method)slider_init, gensym("init"), A_FLOAT, 0); class_addmethod(slider_class, (t_method)slider_steady, gensym("steady"), A_FLOAT, 0); class_addmethod(slider_class, (t_method)slider_orientation, gensym("orientation"), A_FLOAT, 0); class_addmethod(slider_class, (t_method)slider_zoom, gensym("zoom"), A_CANT, 0); slider_widgetbehavior.w_getrectfn = slider_getrect; slider_widgetbehavior.w_displacefn = iemgui_displace; slider_widgetbehavior.w_selectfn = iemgui_select; slider_widgetbehavior.w_activatefn = NULL; slider_widgetbehavior.w_deletefn = iemgui_delete; slider_widgetbehavior.w_visfn = iemgui_vis; slider_widgetbehavior.w_clickfn = slider_newclick; class_setwidget(slider_class, &slider_widgetbehavior); class_sethelpsymbol(slider_class, gensym("sliders")); class_setsavefn(slider_class, slider_save); class_setpropertiesfn(slider_class, slider_properties); } ================================================ FILE: libs/libpd/pure-data/src/g_template.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ #include #include #include "m_pd.h" #include "g_canvas.h" /* This file contains text objects you would put in a canvas to define a template. Templates describe objects of type "array" (g_array.c) and "scalar" (g_scalar.c). */ /* the structure of a "struct" object (also the obsolete "gtemplate" you get when using the name "template" in a box.) */ struct _gtemplate { t_object x_obj; t_template *x_template; t_canvas *x_owner; t_symbol *x_sym; struct _gtemplate *x_next; int x_argc; t_atom *x_argv; }; struct _instancetemplate { int curve_motion_field; t_float curve_motion_xcumulative; t_float curve_motion_xbase; t_float curve_motion_xper; t_float curve_motion_ycumulative; t_float curve_motion_ybase; t_float curve_motion_yper; t_glist *curve_motion_glist; t_scalar *curve_motion_scalar; t_array *curve_motion_array; t_word *curve_motion_wp; t_template *curve_motion_template; t_gpointer curve_motion_gpointer; t_float array_motion_xcumulative; t_float array_motion_ycumulative; t_fielddesc *array_motion_xfield; t_fielddesc *array_motion_yfield; t_glist *array_motion_glist; t_scalar *array_motion_scalar; t_array *array_motion_array; t_word *array_motion_wp; t_template *array_motion_template; int array_motion_npoints; int array_motion_elemsize; int array_motion_altkey; t_float array_motion_initx; t_float array_motion_xperpix; t_float array_motion_yperpix; int array_motion_lastx; int array_motion_fatten; t_float drawnumber_motion_ycumulative; t_glist *drawnumber_motion_glist; t_scalar *drawnumber_motion_scalar; t_array *drawnumber_motion_array; t_word *drawnumber_motion_wp; t_template *drawnumber_motion_template; t_gpointer drawnumber_motion_gpointer; int drawnumber_motion_type; int drawnumber_motion_firstkey; }; /* ---------------- forward definitions ---------------- */ static void template_addtolist(t_template *x); static void template_takeofflist(t_template *x); static void template_conformarray(t_template *tfrom, t_template *tto, int *conformaction, t_array *a); static void template_conformglist(t_template *tfrom, t_template *tto, t_glist *glist, int *conformaction); /* ---------------------- storage ------------------------- */ static t_class *gtemplate_class; static t_class *template_class; /* there's a pre-defined "float" template. LATER should we bind this to a symbol such as "pd-float"??? */ /* return true if two dataslot definitions match */ static int dataslot_matches(t_dataslot *ds1, t_dataslot *ds2, int nametoo) { return ((!nametoo || ds1->ds_name == ds2->ds_name) && ds1->ds_type == ds2->ds_type && (ds1->ds_type != DT_ARRAY || ds1->ds_arraytemplate == ds2->ds_arraytemplate)); } /* -- templates, the active ingredient in gtemplates defined below. ------- */ /* add a template to the list */ static void template_addtolist(t_template *x) { x->t_next = pd_this->pd_templatelist; pd_this->pd_templatelist = x; } static void template_takeofflist(t_template *x) { /* take it off the template list */ if (x == pd_this->pd_templatelist) pd_this->pd_templatelist = x->t_next; else { t_template *z; for (z = pd_this->pd_templatelist; z->t_next != x; z = z->t_next) if (!z->t_next) return; z->t_next = x->t_next; } } t_template *template_new(t_symbol *templatesym, int argc, t_atom *argv) { t_template *x = (t_template *)pd_new(template_class); x->t_n = 0; x->t_vec = (t_dataslot *)t_getbytes(0); x->t_next = 0; template_addtolist(x); while (argc > 0) { int newtype, oldn, newn; t_symbol *newname, *newarraytemplate = &s_, *newtypesym; if (argc < 2 || argv[0].a_type != A_SYMBOL || argv[1].a_type != A_SYMBOL) goto bad; newtypesym = argv[0].a_w.w_symbol; newname = argv[1].a_w.w_symbol; if (newtypesym == &s_float) newtype = DT_FLOAT; else if (newtypesym == &s_symbol) newtype = DT_SYMBOL; /* "list" is old name.. accepted here but never saved as such */ else if (newtypesym == gensym("text") || newtypesym == &s_list) newtype = DT_TEXT; else if (newtypesym == gensym("array")) { if (argc < 3 || argv[2].a_type != A_SYMBOL) { pd_error(x, "array lacks element template or name"); goto bad; } newarraytemplate = canvas_makebindsym(argv[2].a_w.w_symbol); newtype = DT_ARRAY; argc--; argv++; } else { pd_error(x, "%s: no such type", newtypesym->s_name); goto bad; } newn = (oldn = x->t_n) + 1; x->t_vec = (t_dataslot *)t_resizebytes(x->t_vec, oldn * sizeof(*x->t_vec), newn * sizeof(*x->t_vec)); x->t_n = newn; x->t_vec[oldn].ds_type = newtype; x->t_vec[oldn].ds_name = newname; x->t_vec[oldn].ds_arraytemplate = newarraytemplate; bad: argc -= 2; argv += 2; } if (*templatesym->s_name) { x->t_sym = templatesym; pd_bind(&x->t_pdobj, x->t_sym); } else x->t_sym = templatesym; return (x); } int template_find_field(t_template *x, t_symbol *name, int *p_onset, int *p_type, t_symbol **p_arraytype) { t_template *t; int i, n; if (!x) { bug("template_find_field"); return (0); } n = x->t_n; for (i = 0; i < n; i++) if (x->t_vec[i].ds_name == name) { *p_onset = i * sizeof(t_word); *p_type = x->t_vec[i].ds_type; *p_arraytype = x->t_vec[i].ds_arraytemplate; return (1); } return (0); } t_float template_getfloat(t_template *x, t_symbol *fieldname, t_word *wp, int loud) { int onset, type; t_symbol *arraytype; t_float val = 0; if (template_find_field(x, fieldname, &onset, &type, &arraytype)) { if (type == DT_FLOAT) val = *(t_float *)(((char *)wp) + onset); else if (loud) pd_error(0, "%s.%s: not a number", x->t_sym->s_name, fieldname->s_name); } else if (loud) pd_error(0, "%s.%s: no such field", x->t_sym->s_name, fieldname->s_name); return (val); } void template_setfloat(t_template *x, t_symbol *fieldname, t_word *wp, t_float f, int loud) { int onset, type; t_symbol *arraytype; if (template_find_field(x, fieldname, &onset, &type, &arraytype)) { if (type == DT_FLOAT) *(t_float *)(((char *)wp) + onset) = f; else if (loud) pd_error(0, "%s.%s: not a number", x->t_sym->s_name, fieldname->s_name); } else if (loud) pd_error(0, "%s.%s: no such field", x->t_sym->s_name, fieldname->s_name); } t_symbol *template_getsymbol(t_template *x, t_symbol *fieldname, t_word *wp, int loud) { int onset, type; t_symbol *arraytype; t_symbol *val = &s_; if (template_find_field(x, fieldname, &onset, &type, &arraytype)) { if (type == DT_SYMBOL) val = *(t_symbol **)(((char *)wp) + onset); else if (loud) pd_error(0, "%s.%s: not a symbol", x->t_sym->s_name, fieldname->s_name); } else if (loud) pd_error(0, "%s.%s: no such field", x->t_sym->s_name, fieldname->s_name); return (val); } void template_setsymbol(t_template *x, t_symbol *fieldname, t_word *wp, t_symbol *s, int loud) { int onset, type; t_symbol *arraytype; if (template_find_field(x, fieldname, &onset, &type, &arraytype)) { if (type == DT_SYMBOL) *(t_symbol **)(((char *)wp) + onset) = s; else if (loud) pd_error(0, "%s.%s: not a symbol", x->t_sym->s_name, fieldname->s_name); } else if (loud) pd_error(0, "%s.%s: no such field", x->t_sym->s_name, fieldname->s_name); } /* stringent check to see if a "saved" template, x2, matches the current one (x1). It's OK if x1 has additional scalar elements but not (yet) arrays. This is used for reading in "data files". */ int template_match(t_template *x1, t_template *x2) { int i; if (x1->t_n < x2->t_n) return (0); for (i = x2->t_n; i < x1->t_n; i++) { if (x1->t_vec[i].ds_type == DT_ARRAY) return (0); } if (x2->t_n > x1->t_n) post("add elements..."); for (i = 0; i < x2->t_n; i++) if (!dataslot_matches(&x1->t_vec[i], &x2->t_vec[i], 1)) return (0); return (1); } /* --------------- CONFORMING TO CHANGES IN A TEMPLATE ------------ */ /* the following routines handle updating scalars to agree with changes in their template. The old template is assumed to be the "installed" one so we can delete old items; but making new ones we have to avoid scalar_new which would make an old one whereas we will want a new one (but whose array elements might still be old ones.) LATER deal with graphics updates too... */ /* conform the word vector of a scalar to the new template */ static void template_conformwords(t_template *tfrom, t_template *tto, int *conformaction, t_word *wfrom, t_word *wto) { int nfrom = tfrom->t_n, nto = tto->t_n, i; for (i = 0; i < nto; i++) { if (conformaction[i] >= 0) { /* we swap the two, in case it's an array or list, so that when "wfrom" is deleted the old one gets cleaned up. */ t_word wwas = wto[i]; wto[i] = wfrom[conformaction[i]]; wfrom[conformaction[i]] = wwas; } } } /* conform a scalar, recursively conforming arrays */ static t_scalar *template_conformscalar(t_template *tfrom, t_template *tto, int *conformaction, t_glist *glist, t_scalar *scfrom) { t_scalar *x; t_gpointer gp; int nto = tto->t_n, nfrom = tfrom->t_n, i; t_template *scalartemplate; /* post("conform scalar"); */ /* possibly replace the scalar */ if (scfrom->sc_template == tfrom->t_sym) { /* see scalar_new() for comment about the gpointer. */ gpointer_init(&gp); x = (t_scalar *)getbytes(sizeof(t_scalar) + (tto->t_n - 1) * sizeof(*x->sc_vec)); x->sc_gobj.g_pd = scalar_class; x->sc_template = tfrom->t_sym; gpointer_setglist(&gp, glist, x); /* Here we initialize to the new template, but array and list elements will still belong to old template. */ word_init(x->sc_vec, tto, &gp); template_conformwords(tfrom, tto, conformaction, scfrom->sc_vec, x->sc_vec); /* replace the old one with the new one in the list */ if (glist->gl_list == &scfrom->sc_gobj) { glist->gl_list = &x->sc_gobj; x->sc_gobj.g_next = scfrom->sc_gobj.g_next; } else { t_gobj *y, *y2; for (y = glist->gl_list; (y2 = y->g_next); y = y2) if (y2 == &scfrom->sc_gobj) { x->sc_gobj.g_next = y2->g_next; y->g_next = &x->sc_gobj; goto nobug; } bug("template_conformscalar"); nobug: ; } /* burn the old one */ pd_free(&scfrom->sc_gobj.g_pd); scalartemplate = tto; } else { x = scfrom; scalartemplate = template_findbyname(x->sc_template); } /* convert all array elements */ for (i = 0; i < scalartemplate->t_n; i++) { t_dataslot *ds = scalartemplate->t_vec + i; if (ds->ds_type == DT_ARRAY) { template_conformarray(tfrom, tto, conformaction, x->sc_vec[i].w_array); } } return (x); } /* conform an array, recursively conforming sublists and arrays */ static void template_conformarray(t_template *tfrom, t_template *tto, int *conformaction, t_array *a) { int i, j; t_template *scalartemplate = 0; if (a->a_templatesym == tfrom->t_sym) { /* the array elements must all be conformed */ int oldelemsize = sizeof(t_word) * tfrom->t_n, newelemsize = sizeof(t_word) * tto->t_n; char *newarray = getbytes(newelemsize * a->a_n); char *oldarray = a->a_vec; if (a->a_elemsize != oldelemsize) bug("template_conformarray"); for (i = 0; i < a->a_n; i++) { t_word *wp = (t_word *)(newarray + newelemsize * i); word_init(wp, tto, &a->a_gp); template_conformwords(tfrom, tto, conformaction, (t_word *)(oldarray + oldelemsize * i), wp); word_free((t_word *)(oldarray + oldelemsize * i), tfrom); } scalartemplate = tto; a->a_vec = newarray; freebytes(oldarray, oldelemsize * a->a_n); } else scalartemplate = template_findbyname(a->a_templatesym); /* convert all arrays and sublist fields in each element of the array */ for (i = 0; i < a->a_n; i++) { t_word *wp = (t_word *)(a->a_vec + sizeof(t_word) * a->a_n * i); for (j = 0; j < scalartemplate->t_n; j++) { t_dataslot *ds = scalartemplate->t_vec + j; if (ds->ds_type == DT_ARRAY) { template_conformarray(tfrom, tto, conformaction, wp[j].w_array); } } } } /* this routine searches for every scalar in the glist that belongs to the "from" template and makes it belong to the "to" template. Descend glists recursively. We don't handle redrawing here; this is to be filled in LATER... */ t_array *garray_getarray(t_garray *x); static void template_conformglist(t_template *tfrom, t_template *tto, t_glist *glist, int *conformaction) { t_gobj *g; /* post("conform glist %s", glist->gl_name->s_name); */ for (g = glist->gl_list; g; g = g->g_next) { if (pd_class(&g->g_pd) == scalar_class) g = &template_conformscalar(tfrom, tto, conformaction, glist, (t_scalar *)g)->sc_gobj; else if (pd_class(&g->g_pd) == canvas_class) template_conformglist(tfrom, tto, (t_glist *)g, conformaction); else if (pd_class(&g->g_pd) == garray_class) template_conformarray(tfrom, tto, conformaction, garray_getarray((t_garray *)g)); } } /* globally conform all scalars from one template to another */ void template_conform(t_template *tfrom, t_template *tto) { int nto = tto->t_n, nfrom = tfrom->t_n, i, j, *conformaction = (int *)getbytes(sizeof(int) * nto), *conformedfrom = (int *)getbytes(sizeof(int) * nfrom), doit = 0; for (i = 0; i < nto; i++) conformaction[i] = -1; for (i = 0; i < nfrom; i++) conformedfrom[i] = 0; for (i = 0; i < nto; i++) { t_dataslot *dataslot = &tto->t_vec[i]; for (j = 0; j < nfrom; j++) { t_dataslot *dataslot2 = &tfrom->t_vec[j]; if (dataslot_matches(dataslot, dataslot2, 1)) { conformaction[i] = j; conformedfrom[j] = 1; } } } for (i = 0; i < nto; i++) if (conformaction[i] < 0) { t_dataslot *dataslot = &tto->t_vec[i]; for (j = 0; j < nfrom; j++) if (!conformedfrom[j] && dataslot_matches(dataslot, &tfrom->t_vec[j], 0)) { conformaction[i] = j; conformedfrom[j] = 1; } } if (nto != nfrom) doit = 1; else for (i = 0; i < nto; i++) if (conformaction[i] != i) doit = 1; if (doit) { t_glist *gl; for (gl = pd_getcanvaslist(); gl; gl = gl->gl_next) template_conformglist(tfrom, tto, gl, conformaction); } freebytes(conformaction, sizeof(int) * nto); freebytes(conformedfrom, sizeof(int) * nfrom); } t_template *template_findbyname(t_symbol *s) { return ((t_template *)pd_findbyclass(s, template_class)); } t_canvas *template_findcanvas(t_template *template) { t_gtemplate *gt; if (!template) { bug("template_findcanvas"); return (0); } if (!(gt = template->t_list)) return (0); return (gt->x_owner); /* return ((t_canvas *)pd_findbyclass(template->t_sym, canvas_class)); */ } void template_notify(t_template *template, t_symbol *s, int argc, t_atom *argv) { if (template->t_list) outlet_anything(template->t_list->x_obj.ob_outlet, s, argc, argv); } /* bash the first of (argv) with a pointer to a scalar, and send on to template as a notification message */ void template_notifyforscalar(t_template *template, t_glist *owner, t_scalar *sc, t_symbol *s, int argc, t_atom *argv) { t_gpointer gp; gpointer_init(&gp); gpointer_setglist(&gp, owner, sc); SETPOINTER(argv, &gp); template_notify(template, s, argc, argv); gpointer_unset(&gp); } /* call this when reading a patch from a file to declare what templates we'll need. If there's already a template, check if it matches. If it doesn't it's still OK as long as there are no "struct" (gtemplate) objects hanging from it; we just conform everyone to the new template. If there are still struct objects belonging to the other template, we're in trouble. LATER we'll figure out how to conform the new patch's objects to the pre-existing struct. */ static void *template_usetemplate(void *dummy, t_symbol *s, int argc, t_atom *argv) { t_template *x; t_symbol *templatesym = canvas_makebindsym(atom_getsymbolarg(0, argc, argv)); if (!argc) return (0); argc--; argv++; /* check if there's already a template by this name. */ if ((x = (t_template *)pd_findbyclass(templatesym, template_class))) { t_template *y = template_new(&s_, argc, argv), *y2; /* If the new template is the same as the old one, there's nothing to do. */ if (!template_match(x, y)) { /* Are there "struct" objects upholding this template? */ if (x->t_list) { /* don't know what to do here! */ pd_error(0, "%s: template mismatch", templatesym->s_name); } else { /* conform everyone to the new template */ template_conform(x, y); pd_free(&x->t_pdobj); y2 = template_new(templatesym, argc, argv); y2->t_list = 0; } } pd_free(&y->t_pdobj); } /* otherwise, just make one. */ else template_new(templatesym, argc, argv); return (0); } /* here we assume someone has already cleaned up all instances of this. */ void template_free(t_template *x) { if (*x->t_sym->s_name) pd_unbind(&x->t_pdobj, x->t_sym); t_freebytes(x->t_vec, x->t_n * sizeof(*x->t_vec)); template_takeofflist(x); } static void template_setup(void) { template_class = class_new(gensym("template"), 0, (t_method)template_free, sizeof(t_template), CLASS_PD, 0); class_addmethod(pd_canvasmaker, (t_method)template_usetemplate, gensym("struct"), A_GIMME, 0); } /* ---------------- gtemplates. One per canvas. ----------- */ /* "Struct": an object that searches for, and if necessary creates, a template (above). Other objects in the canvas then can give drawing instructions for the template. The template doesn't go away when the "struct" is deleted, so that you can replace it with another one to add new fields, for example. */ static void *gtemplate_donew(t_symbol *sym, int argc, t_atom *argv) { t_gtemplate *x = (t_gtemplate *)pd_new(gtemplate_class); t_template *t = template_findbyname(sym); int i; t_symbol *sx = gensym("x"); x->x_owner = canvas_getcurrent(); x->x_next = 0; x->x_sym = sym; x->x_argc = argc; x->x_argv = (t_atom *)getbytes(argc * sizeof(t_atom)); for (i = 0; i < argc; i++) x->x_argv[i] = argv[i]; /* already have a template by this name? */ if (t) { x->x_template = t; /* if it's already got a "struct" object we just tack this one to the end of the list and leave it there. */ if (t->t_list) { t_gtemplate *x2, *x3; for (x2 = x->x_template->t_list; (x3 = x2->x_next); x2 = x3) ; x2->x_next = x; post("template %s: warning: already exists.", sym->s_name); } else { /* if there's none, we just replace the template with our own and conform it. */ t_template *y = template_new(&s_, argc, argv); canvas_redrawallfortemplate(t, 2); /* Unless the new template is different from the old one, there's nothing to do. */ if (!template_match(t, y)) { /* conform everyone to the new template */ template_conform(t, y); pd_free(&t->t_pdobj); x->x_template = t = template_new(sym, argc, argv); } pd_free(&y->t_pdobj); t->t_list = x; canvas_redrawallfortemplate(t, 1); } } else { /* otherwise make a new one and we're the only struct on it. */ x->x_template = t = template_new(sym, argc, argv); t->t_list = x; } outlet_new(&x->x_obj, 0); return (x); } static void *gtemplate_new(t_symbol *s, int argc, t_atom *argv) { t_symbol *sym = atom_getsymbolarg(0, argc, argv); if (argc >= 1) argc--, argv++; if (sym->s_name[0] == '-') post("warning: struct '%s' initial '-' may confuse get/set, etc.", sym->s_name); return (gtemplate_donew(canvas_makebindsym(sym), argc, argv)); } /* old version (0.34) -- delete 2003 or so */ static void *gtemplate_new_old(t_symbol *s, int argc, t_atom *argv) { t_symbol *sym = canvas_makebindsym(canvas_getcurrent()->gl_name); static int warned; if (!warned) { post("warning -- 'template' (%s) is obsolete; replace with 'struct'", sym->s_name); warned = 1; } return (gtemplate_donew(sym, argc, argv)); } t_template *gtemplate_get(t_gtemplate *x) { return (x->x_template); } static void gtemplate_free(t_gtemplate *x) { /* get off the template's list */ t_template *t = x->x_template; t_gtemplate *y; if (x == t->t_list) { canvas_redrawallfortemplate(t, 2); if (x->x_next) { /* if we were first on the list, and there are others on the list, make a new template corresponding to the new first-on-list and replace the existing template with it. */ t_template *z = template_new(&s_, x->x_next->x_argc, x->x_next->x_argv); template_conform(t, z); pd_free(&t->t_pdobj); pd_free(&z->t_pdobj); z = template_new(x->x_sym, x->x_next->x_argc, x->x_next->x_argv); z->t_list = x->x_next; for (y = z->t_list; y ; y = y->x_next) y->x_template = z; } else t->t_list = 0; canvas_redrawallfortemplate(t, 1); } else { t_gtemplate *x2, *x3; for (x2 = t->t_list; (x3 = x2->x_next); x2 = x3) { if (x == x3) { x2->x_next = x3->x_next; break; } } } freebytes(x->x_argv, sizeof(t_atom) * x->x_argc); } static void gtemplate_setup(void) { gtemplate_class = class_new(gensym("struct"), (t_newmethod)gtemplate_new, (t_method)gtemplate_free, sizeof(t_gtemplate), CLASS_NOINLET, A_GIMME, 0); class_addcreator((t_newmethod)gtemplate_new_old, gensym("template"), A_GIMME, 0); } /* --------------- FIELD DESCRIPTORS ---------------------- */ /* a field descriptor can hold a constant or a variable; if a variable, it's the name of a field in the template we belong to. LATER, we might want to cache the offset of the field so we don't have to search for it every single time we draw the object. */ struct _fielddesc { char fd_type; /* LATER consider removing this? */ char fd_var; union { t_float fd_float; /* the field is a constant float */ t_symbol *fd_symbol; /* the field is a constant symbol */ t_symbol *fd_varsym; /* the field is variable and this is the name */ } fd_un; float fd_v1; /* min and max values */ float fd_v2; float fd_screen1; /* min and max screen values */ float fd_screen2; float fd_quantum; /* quantization in value */ }; static void fielddesc_setfloat_const(t_fielddesc *fd, t_float f) { fd->fd_type = A_FLOAT; fd->fd_var = 0; fd->fd_un.fd_float = f; fd->fd_v1 = fd->fd_v2 = fd->fd_screen1 = fd->fd_screen2 = fd->fd_quantum = 0; } static void fielddesc_setsymbol_const(t_fielddesc *fd, t_symbol *s) { fd->fd_type = A_SYMBOL; fd->fd_var = 0; fd->fd_un.fd_symbol = s; fd->fd_v1 = fd->fd_v2 = fd->fd_screen1 = fd->fd_screen2 = fd->fd_quantum = 0; } static void fielddesc_setfloat_var(t_fielddesc *fd, t_symbol *s) { char *s1, *s2, *s3, strbuf[MAXPDSTRING]; int i; fd->fd_type = A_FLOAT; fd->fd_var = 1; if (!(s1 = strchr(s->s_name, '(')) || !(s2 = strchr(s->s_name, ')')) || (s1 > s2)) { fd->fd_un.fd_varsym = s; fd->fd_v1 = fd->fd_v2 = fd->fd_screen1 = fd->fd_screen2 = fd->fd_quantum = 0; } else { int cpy = (int)(s1 - s->s_name), got; double v1, v2, screen1, screen2, quantum; if (cpy > MAXPDSTRING-5) cpy = MAXPDSTRING-5; strncpy(strbuf, s->s_name, cpy); strbuf[cpy] = 0; fd->fd_un.fd_varsym = gensym(strbuf); got = sscanf(s1, "(%lf:%lf)(%lf:%lf)(%lf)", &v1, &v2, &screen1, &screen2, &quantum); fd->fd_v1=v1; fd->fd_v2=v2; fd->fd_screen1=screen1; fd->fd_screen2=screen2; fd->fd_quantum=quantum; if (got < 2) goto fail; if (got == 3 || (got < 4 && strchr(s2, '('))) goto fail; if (got < 5 && (s3 = strchr(s2, '(')) && strchr(s3+1, '(')) goto fail; if (got == 4) fd->fd_quantum = 0; else if (got == 2) { fd->fd_quantum = 0; fd->fd_screen1 = fd->fd_v1; fd->fd_screen2 = fd->fd_v2; } return; fail: post("parse error: %s", s->s_name); fd->fd_v1 = fd->fd_screen1 = fd->fd_v2 = fd->fd_screen2 = fd->fd_quantum = 0; } } #define CLOSED 1 /* polygon */ #define BEZ 2 /* bezier shape */ #define NOMOUSERUN 4 /* disable mouse interaction when in run mode */ #define NOMOUSEEDIT 8 /* same in edit mode */ #define NOVERTICES 16 /* disable only vertex grabbing in run mode */ #define A_ARRAY 55 /* LATER decide whether to enshrine this in m_pd.h */ static void fielddesc_setfloatarg(t_fielddesc *fd, int argc, t_atom *argv) { if (argc <= 0) fielddesc_setfloat_const(fd, 0); else if (argv->a_type == A_SYMBOL) fielddesc_setfloat_var(fd, argv->a_w.w_symbol); else fielddesc_setfloat_const(fd, argv->a_w.w_float); } static void fielddesc_setsymbolarg(t_fielddesc *fd, int argc, t_atom *argv) { if (argc <= 0) fielddesc_setsymbol_const(fd, &s_); else if (argv->a_type == A_SYMBOL) { fd->fd_type = A_SYMBOL; fd->fd_var = 1; fd->fd_un.fd_varsym = argv->a_w.w_symbol; fd->fd_v1 = fd->fd_v2 = fd->fd_screen1 = fd->fd_screen2 = fd->fd_quantum = 0; } else fielddesc_setsymbol_const(fd, &s_); } static void fielddesc_setarrayarg(t_fielddesc *fd, int argc, t_atom *argv) { if (argc <= 0) fielddesc_setfloat_const(fd, 0); else if (argv->a_type == A_SYMBOL) { fd->fd_type = A_ARRAY; fd->fd_var = 1; fd->fd_un.fd_varsym = argv->a_w.w_symbol; } else fielddesc_setfloat_const(fd, argv->a_w.w_float); } /* getting and setting values via fielddescs -- note confusing names; the above are setting up the fielddesc itself. */ static t_float fielddesc_getfloat(t_fielddesc *f, t_template *template, t_word *wp, int loud) { if (f->fd_type == A_FLOAT) { if (f->fd_var) return (template_getfloat(template, f->fd_un.fd_varsym, wp, loud)); else return (f->fd_un.fd_float); } else { if (loud) pd_error(0, "symbolic data field used as number"); return (0); } } /* convert a variable's value to a screen coordinate via its fielddesc */ t_float fielddesc_cvttocoord(t_fielddesc *f, t_float val) { t_float coord, pix, extreme, div; if (f->fd_v2 == f->fd_v1) return (val); div = (f->fd_screen2 - f->fd_screen1)/(f->fd_v2 - f->fd_v1); coord = f->fd_screen1 + (val - f->fd_v1) * div; extreme = (f->fd_screen1 < f->fd_screen2 ? f->fd_screen1 : f->fd_screen2); if (coord < extreme) coord = extreme; extreme = (f->fd_screen1 > f->fd_screen2 ? f->fd_screen1 : f->fd_screen2); if (coord > extreme) coord = extreme; return (coord); } /* read a variable via fielddesc and convert to screen coordinate */ t_float fielddesc_getcoord(t_fielddesc *f, t_template *template, t_word *wp, int loud) { if (f->fd_type == A_FLOAT) { if (f->fd_var) { t_float val = template_getfloat(template, f->fd_un.fd_varsym, wp, loud); return (fielddesc_cvttocoord(f, val)); } else return (f->fd_un.fd_float); } else { if (loud) pd_error(0, "symbolic data field used as number"); return (0); } } static t_symbol *fielddesc_getsymbol(t_fielddesc *f, t_template *template, t_word *wp, int loud) { if (f->fd_type == A_SYMBOL) { if (f->fd_var) return(template_getsymbol(template, f->fd_un.fd_varsym, wp, loud)); else return (f->fd_un.fd_symbol); } else { if (loud) pd_error(0, "numeric data field used as symbol"); return (&s_); } } /* convert from a screen coordinate to a variable value */ t_float fielddesc_cvtfromcoord(t_fielddesc *f, t_float coord) { t_float val; if (f->fd_screen2 == f->fd_screen1) val = coord; else { t_float div = (f->fd_v2 - f->fd_v1)/(f->fd_screen2 - f->fd_screen1); t_float extreme; val = f->fd_v1 + (coord - f->fd_screen1) * div; if (f->fd_quantum != 0) val = ((int)((val/f->fd_quantum) + 0.5)) * f->fd_quantum; extreme = (f->fd_v1 < f->fd_v2 ? f->fd_v1 : f->fd_v2); if (val < extreme) val = extreme; extreme = (f->fd_v1 > f->fd_v2 ? f->fd_v1 : f->fd_v2); if (val > extreme) val = extreme; } return (val); } void fielddesc_setcoord(t_fielddesc *f, t_template *template, t_word *wp, t_float coord, int loud) { if (f->fd_type == A_FLOAT && f->fd_var) { t_float val = fielddesc_cvtfromcoord(f, coord); template_setfloat(template, f->fd_un.fd_varsym, wp, val, loud); } else { if (loud) pd_error(0, "attempt to set constant or symbolic data field to a number"); } } /* ---------------- curves and polygons (joined segments) ---------------- */ /* curves belong to templates and describe how the data in the template are to be drawn. The coordinates of the curve (and other display features) can be attached to fields in the template. */ t_class *curve_class; typedef struct _curve { t_object x_obj; int x_flags; /* CLOSED, BEZ, NOMOUSERUN, NOMOUSEEDIT */ t_fielddesc x_fillcolor; t_fielddesc x_outlinecolor; t_fielddesc x_width; t_fielddesc x_vis; int x_npoints; t_fielddesc *x_vec; t_canvas *x_canvas; } t_curve; static void *curve_new(t_symbol *classsym, int argc, t_atom *argv) { t_curve *x = (t_curve *)pd_new(curve_class); const char *classname = classsym->s_name; int flags = 0; int nxy, i; t_fielddesc *fd; x->x_canvas = canvas_getcurrent(); if (classname[0] == 'f') { classname += 6; flags |= CLOSED; } else classname += 4; if (classname[0] == 'c') flags |= BEZ; fielddesc_setfloat_const(&x->x_vis, 1); while (argc && argv->a_type == A_SYMBOL && *argv->a_w.w_symbol->s_name == '-') { const char *flag = argv->a_w.w_symbol->s_name; if (!strcmp(flag, "-n")) { fielddesc_setfloat_const(&x->x_vis, 0); } else if (!strcmp(flag, "-v") && argc > 1) { fielddesc_setfloatarg(&x->x_vis, 1, argv+1); argc -= 1; argv += 1; } else if (!strcmp(flag, "-x")) { /* disable all mouse interaction */ flags |= (NOMOUSERUN | NOMOUSEEDIT); } else if (!strcmp(flag, "-xr")) { /* disable mouse actions in run mode */ flags |= NOMOUSERUN; } else if (!strcmp(flag, "-xe")) { /* disable mouse actions in edit mode */ flags |= NOMOUSEEDIT; } else if (!strcmp(flag, "-xv")) { /* disable changing vertices in run mode */ flags |= NOVERTICES; } else { pd_error(x, "%s: unknown flag '%s'...", classsym->s_name, flag); } argc--; argv++; } x->x_flags = flags; if ((flags & CLOSED) && argc) fielddesc_setfloatarg(&x->x_fillcolor, argc--, argv++); else fielddesc_setfloat_const(&x->x_fillcolor, 0); if (argc) fielddesc_setfloatarg(&x->x_outlinecolor, argc--, argv++); else fielddesc_setfloat_const(&x->x_outlinecolor, 0); if (argc) fielddesc_setfloatarg(&x->x_width, argc--, argv++); else fielddesc_setfloat_const(&x->x_width, 1); if (argc < 0) argc = 0; nxy = (argc + (argc & 1)); x->x_npoints = (nxy>>1); x->x_vec = (t_fielddesc *)t_getbytes(nxy * sizeof(t_fielddesc)); for (i = 0, fd = x->x_vec; i < argc; i++, fd++, argv++) fielddesc_setfloatarg(fd, 1, argv); if (argc & 1) fielddesc_setfloat_const(fd, 0); return (x); } void curve_float(t_curve *x, t_floatarg f) { int viswas; if (x->x_vis.fd_type != A_FLOAT || x->x_vis.fd_var) { pd_error(x, "global vis/invis for a template with variable visibility"); return; } viswas = (x->x_vis.fd_un.fd_float != 0); if ((f != 0 && viswas) || (f == 0 && !viswas)) return; canvas_redrawallfortemplatecanvas(x->x_canvas, 2); fielddesc_setfloat_const(&x->x_vis, (f != 0)); canvas_redrawallfortemplatecanvas(x->x_canvas, 1); } /* -------------------- widget behavior for curve ------------ */ static void curve_getrect(t_gobj *z, t_glist *glist, t_word *data, t_template *template, t_float basex, t_float basey, int *xp1, int *yp1, int *xp2, int *yp2) { t_curve *x = (t_curve *)z; int i, n = x->x_npoints; t_fielddesc *f = x->x_vec; int x1 = 0x7fffffff, x2 = -0x7fffffff, y1 = 0x7fffffff, y2 = -0x7fffffff; if (!fielddesc_getfloat(&x->x_vis, template, data, 0) || (glist->gl_edit && x->x_flags & NOMOUSEEDIT) || (!glist->gl_edit && x->x_flags & NOMOUSERUN)) { *xp1 = *yp1 = 0x7fffffff; *xp2 = *yp2 = -0x7fffffff; return; } for (i = 0, f = x->x_vec; i < n; i++, f += 2) { int xloc = glist_xtopixels(glist, basex + fielddesc_getcoord(f, template, data, 0)); int yloc = glist_ytopixels(glist, basey + fielddesc_getcoord(f+1, template, data, 0)); if (xloc < x1) x1 = xloc; if (xloc > x2) x2 = xloc; if (yloc < y1) y1 = yloc; if (yloc > y2) y2 = yloc; } *xp1 = x1; *yp1 = y1; *xp2 = x2; *yp2 = y2; } static void curve_displace(t_gobj *z, t_glist *glist, t_word *data, t_template *template, t_float basex, t_float basey, int dx, int dy) { /* refuse */ } static void curve_select(t_gobj *z, t_glist *glist, t_word *data, t_template *template, t_float basex, t_float basey, int state) { /* fill in later */ } static void curve_activate(t_gobj *z, t_glist *glist, t_word *data, t_template *template, t_float basex, t_float basey, int state) { /* fill in later */ } #if 0 static int rangecolor(int n) /* 0 to 9 in 5 steps */ { int n2 = n/2; /* 0 to 4 */ int ret = (n2 << 6); /* 0 to 256 in 5 steps */ if (ret > 255) ret = 255; return (ret); } #endif static int rangecolor(int n) /* 0 to 9 in 5 steps */ { int n2 = (n == 9 ? 8 : n); /* 0 to 8 */ int ret = (n2 << 5); /* 0 to 256 in 9 steps */ if (ret > 255) ret = 255; return (ret); } static int numbertocolor(int n) { int red, green, blue, color = 0; if (n < 0) n = 0; red = n / 100; green = n % 10; blue = ((n / 10) % 10); color |= rangecolor(red) << 16; color |= rangecolor(blue) << 8; color |= rangecolor(green) << 0; return color; } static void curve_vis(t_gobj *z, t_glist *glist, t_word *data, t_template *template, t_float basex, t_float basey, int vis) { t_curve *x = (t_curve *)z; int i, n = x->x_npoints; t_fielddesc *f = x->x_vec; char tag0[80], tag[80]; const char*tags[] = {tag, tag0, "curve"}; /* see comment in plot_vis() */ if (vis && !fielddesc_getfloat(&x->x_vis, template, data, 0)) return; sprintf(tag0, "curve%p", x); sprintf(tag , "curve%p_data%p", x, data); if (vis) { if (n > 1) { int flags = x->x_flags, closed = (flags & CLOSED); t_float width = fielddesc_getfloat(&x->x_width, template, data, 1); int outline; t_word pix[200]; if (n > 100) n = 100; /* calculate the pixel values before we start printing out the TK message so that "error" printout won't be interspersed with it. Only show up to 100 points so we don't have to allocate memory here. */ for (i = 0, f = x->x_vec; i < n; i++, f += 2) { pix[2*i].w_float = glist_xtopixels(glist, basex + fielddesc_getcoord(f, template, data, 1)); pix[2*i+1].w_float = glist_ytopixels(glist, basey + fielddesc_getcoord(f+1, template, data, 1)); } if (width < 1) width = 1; if (glist->gl_isgraph) width *= glist_getzoom(glist); outline = numbertocolor( fielddesc_getfloat(&x->x_outlinecolor, template, data, 1)); pdgui_vmess(0, "crr iiii rf ri rS", glist_getcanvas(glist), "create", (flags & CLOSED)?"polygon":"line", 0, 0, 0, 0, "-width", width, "-smooth", !!(flags & BEZ), "-tags", 3, tags); pdgui_vmess(0, "crs w", glist_getcanvas(glist), "coords", tag, 2*n, pix); if (flags & CLOSED) { int fill = numbertocolor( fielddesc_getfloat(&x->x_fillcolor, template, data, 1)); pdgui_vmess(0, "crs rk rk", glist_getcanvas(glist), "itemconfigure", tag, "-fill", fill, "-outline", outline); } else pdgui_vmess(0, "crs rk", glist_getcanvas(glist), "itemconfigure", tag, "-fill", outline); } else post("warning: drawing shapes need at least two points to be graphed"); } else { if (n > 1) pdgui_vmess(0, "crs", glist_getcanvas(glist), "delete", tag); } } /* LATER protect against the template changing or the scalar disappearing probably by attaching a gpointer here ... */ static void curve_motionfn(void *z, t_floatarg dx, t_floatarg dy, t_floatarg up) { t_curve *x = (t_curve *)z; t_fielddesc *f = x->x_vec + TEMPLATE->curve_motion_field; t_atom at; if (up != 0) return; if (!gpointer_check(&TEMPLATE->curve_motion_gpointer, 0)) { post("curve_motion: scalar disappeared"); return; } TEMPLATE->curve_motion_xcumulative += dx; TEMPLATE->curve_motion_ycumulative += dy; if (f->fd_var && (dx != 0)) { fielddesc_setcoord(f, TEMPLATE->curve_motion_template, TEMPLATE->curve_motion_wp, TEMPLATE->curve_motion_xbase + TEMPLATE->curve_motion_xcumulative * TEMPLATE->curve_motion_xper, 1); } if ((f+1)->fd_var && (dy != 0)) { fielddesc_setcoord(f+1, TEMPLATE->curve_motion_template, TEMPLATE->curve_motion_wp, TEMPLATE->curve_motion_ybase + TEMPLATE->curve_motion_ycumulative * TEMPLATE->curve_motion_yper, 1); } /* LATER figure out what to do to notify for an array? */ if (TEMPLATE->curve_motion_scalar) template_notifyforscalar(TEMPLATE->curve_motion_template, TEMPLATE->curve_motion_glist, TEMPLATE->curve_motion_scalar, gensym("change"), 1, &at); if (TEMPLATE->curve_motion_scalar) scalar_redraw(TEMPLATE->curve_motion_scalar, TEMPLATE->curve_motion_glist); if (TEMPLATE->curve_motion_array) array_redraw(TEMPLATE->curve_motion_array, TEMPLATE->curve_motion_glist); } static int curve_click(t_gobj *z, t_glist *glist, t_word *data, t_template *template, t_scalar *sc, t_array *ap, t_float basex, t_float basey, int xpix, int ypix, int shift, int alt, int dbl, int doit) { t_curve *x = (t_curve *)z; int i, n = x->x_npoints; int bestn = -1; int besterror = 0x7fffffff; t_fielddesc *f; if ((x->x_flags & NOMOUSERUN) || (x->x_flags & NOVERTICES) || !fielddesc_getfloat(&x->x_vis, template, data, 0)) return (0); for (i = 0, f = x->x_vec; i < n; i++, f += 2) { int xval = fielddesc_getcoord(f, template, data, 0), xloc = glist_xtopixels(glist, basex + xval); int yval = fielddesc_getcoord(f+1, template, data, 0), yloc = glist_ytopixels(glist, basey + yval); int xerr = xloc - xpix, yerr = yloc - ypix; if (!f->fd_var && !(f+1)->fd_var) continue; if (xerr < 0) xerr = -xerr; if (yerr < 0) yerr = -yerr; if (yerr > xerr) xerr = yerr; if (xerr < besterror) { TEMPLATE->curve_motion_xbase = xval; TEMPLATE->curve_motion_ybase = yval; besterror = xerr; bestn = i; } } if (besterror > 6) return (0); if (doit) { TEMPLATE->curve_motion_xper = glist_pixelstox(glist, 1) - glist_pixelstox(glist, 0); TEMPLATE->curve_motion_yper = glist_pixelstoy(glist, 1) - glist_pixelstoy(glist, 0); TEMPLATE->curve_motion_xcumulative = 0; TEMPLATE->curve_motion_ycumulative = 0; TEMPLATE->curve_motion_glist = glist; TEMPLATE->curve_motion_scalar = sc; TEMPLATE->curve_motion_array = ap; TEMPLATE->curve_motion_wp = data; TEMPLATE->curve_motion_field = 2*bestn; TEMPLATE->curve_motion_template = template; if (TEMPLATE->curve_motion_scalar) gpointer_setglist(&TEMPLATE->curve_motion_gpointer, TEMPLATE->curve_motion_glist, TEMPLATE->curve_motion_scalar); else gpointer_setarray(&TEMPLATE->curve_motion_gpointer, TEMPLATE->curve_motion_array, TEMPLATE->curve_motion_wp); glist_grab(glist, z, curve_motionfn, 0, xpix, ypix); } return (1); } const t_parentwidgetbehavior curve_widgetbehavior = { curve_getrect, curve_displace, curve_select, curve_activate, curve_vis, curve_click, }; static void curve_free(t_curve *x) { t_freebytes(x->x_vec, 2 * x->x_npoints * sizeof(*x->x_vec)); } static void curve_setup(void) { curve_class = class_new(gensym("drawpolygon"), (t_newmethod)curve_new, (t_method)curve_free, sizeof(t_curve), 0, A_GIMME, 0); class_setdrawcommand(curve_class); class_addcreator((t_newmethod)curve_new, gensym("drawcurve"), A_GIMME, 0); class_addcreator((t_newmethod)curve_new, gensym("filledpolygon"), A_GIMME, 0); class_addcreator((t_newmethod)curve_new, gensym("filledcurve"), A_GIMME, 0); class_sethelpsymbol(curve_class, gensym("draw-shapes")); class_setparentwidget(curve_class, &curve_widgetbehavior); class_addfloat(curve_class, curve_float); } /* --------- plots for showing arrays --------------- */ t_class *plot_class; typedef struct _plot { t_object x_obj; t_canvas *x_canvas; t_fielddesc x_outlinecolor; t_fielddesc x_width; t_fielddesc x_xloc; t_fielddesc x_yloc; t_fielddesc x_xinc; t_fielddesc x_style; t_fielddesc x_data; t_fielddesc x_xpoints; t_fielddesc x_ypoints; t_fielddesc x_wpoints; t_fielddesc x_vis; /* visible */ t_fielddesc x_scalarvis; /* true if drawing the scalar at each point */ t_fielddesc x_edit; /* enable/disable mouse editing */ } t_plot; static void *plot_new(t_symbol *classsym, int argc, t_atom *argv) { t_plot *x = (t_plot *)pd_new(plot_class); int defstyle = PLOTSTYLE_POLY; x->x_canvas = canvas_getcurrent(); fielddesc_setfloat_var(&x->x_xpoints, gensym("x")); fielddesc_setfloat_var(&x->x_ypoints, gensym("y")); fielddesc_setfloat_var(&x->x_wpoints, gensym("w")); fielddesc_setfloat_const(&x->x_vis, 1); fielddesc_setfloat_const(&x->x_scalarvis, 1); fielddesc_setfloat_const(&x->x_edit, 1); while (1) { t_symbol *firstarg = atom_getsymbolarg(0, argc, argv); if (!strcmp(firstarg->s_name, "curve") || !strcmp(firstarg->s_name, "-c")) { defstyle = PLOTSTYLE_BEZ; argc--, argv++; } else if (!strcmp(firstarg->s_name, "-v") && argc > 1) { fielddesc_setfloatarg(&x->x_vis, 1, argv+1); argc -= 2; argv += 2; } else if (!strcmp(firstarg->s_name, "-vs") && argc > 1) { fielddesc_setfloatarg(&x->x_scalarvis, 1, argv+1); argc -= 2; argv += 2; } else if (!strcmp(firstarg->s_name, "-x") && argc > 1) { fielddesc_setfloatarg(&x->x_xpoints, 1, argv+1); argc -= 2; argv += 2; } else if (!strcmp(firstarg->s_name, "-y") && argc > 1) { fielddesc_setfloatarg(&x->x_ypoints, 1, argv+1); argc -= 2; argv += 2; } else if (!strcmp(firstarg->s_name, "-w") && argc > 1) { fielddesc_setfloatarg(&x->x_wpoints, 1, argv+1); argc -= 2; argv += 2; } else if (!strcmp(firstarg->s_name, "-e") && argc > 1) { fielddesc_setfloatarg(&x->x_edit, 1, argv+1); argc -= 2; argv += 2; } else if (!strcmp(firstarg->s_name, "-n")) { fielddesc_setfloat_const(&x->x_vis, 0); argc--; argv++; } else if (*firstarg->s_name == '-') { pd_error(x, "%s: unknown flag '%s'...", classsym->s_name, firstarg->s_name); argc--; argv++; } else break; } if (argc) fielddesc_setarrayarg(&x->x_data, argc--, argv++); else fielddesc_setfloat_const(&x->x_data, 1); if (argc) fielddesc_setfloatarg(&x->x_outlinecolor, argc--, argv++); else fielddesc_setfloat_const(&x->x_outlinecolor, 0); if (argc) fielddesc_setfloatarg(&x->x_width, argc--, argv++); else fielddesc_setfloat_const(&x->x_width, 1); if (argc) fielddesc_setfloatarg(&x->x_xloc, argc--, argv++); else fielddesc_setfloat_const(&x->x_xloc, 1); if (argc) fielddesc_setfloatarg(&x->x_yloc, argc--, argv++); else fielddesc_setfloat_const(&x->x_yloc, 1); if (argc) fielddesc_setfloatarg(&x->x_xinc, argc--, argv++); else fielddesc_setfloat_const(&x->x_xinc, 1); if (argc) fielddesc_setfloatarg(&x->x_style, argc--, argv++); else fielddesc_setfloat_const(&x->x_style, defstyle); return (x); } void plot_float(t_plot *x, t_floatarg f) { int viswas; if (x->x_vis.fd_type != A_FLOAT || x->x_vis.fd_var) { pd_error(x, "global vis/invis for a template with variable visibility"); return; } viswas = (x->x_vis.fd_un.fd_float != 0); if ((f != 0 && viswas) || (f == 0 && !viswas)) return; canvas_redrawallfortemplatecanvas(x->x_canvas, 2); fielddesc_setfloat_const(&x->x_vis, (f != 0)); canvas_redrawallfortemplatecanvas(x->x_canvas, 1); } /* -------------------- widget behavior for plot ------------ */ /* get everything we'll need from the owner template of the array being plotted. Not used for garrays, but see below */ static int plot_readownertemplate(t_plot *x, t_word *data, t_template *ownertemplate, t_symbol **elemtemplatesymp, t_array **arrayp, t_float *linewidthp, t_float *xlocp, t_float *xincp, t_float *ylocp, t_float *stylep, t_float *visp, t_float *scalarvisp, t_float *editp, t_fielddesc **xfield, t_fielddesc **yfield, t_fielddesc **wfield) { int arrayonset, type; t_symbol *elemtemplatesym; t_array *array; /* find the data and verify it's an array */ if (x->x_data.fd_type != A_ARRAY || !x->x_data.fd_var) { pd_error(0, "plot: needs an array field"); return (-1); } if (!template_find_field(ownertemplate, x->x_data.fd_un.fd_varsym, &arrayonset, &type, &elemtemplatesym)) { pd_error(0, "plot: %s: no such field", x->x_data.fd_un.fd_varsym->s_name); return (-1); } if (type != DT_ARRAY) { pd_error(0, "plot: %s: not an array", x->x_data.fd_un.fd_varsym->s_name); return (-1); } array = *(t_array **)(((char *)data) + arrayonset); *linewidthp = fielddesc_getfloat(&x->x_width, ownertemplate, data, 1); *xlocp = fielddesc_getfloat(&x->x_xloc, ownertemplate, data, 1); *xincp = fielddesc_getfloat(&x->x_xinc, ownertemplate, data, 1); *ylocp = fielddesc_getfloat(&x->x_yloc, ownertemplate, data, 1); *stylep = fielddesc_getfloat(&x->x_style, ownertemplate, data, 1); *visp = fielddesc_getfloat(&x->x_vis, ownertemplate, data, 1); *scalarvisp = fielddesc_getfloat(&x->x_scalarvis, ownertemplate, data, 1); *editp = fielddesc_getfloat(&x->x_edit, ownertemplate, data, 1); *elemtemplatesymp = elemtemplatesym; *arrayp = array; *xfield = &x->x_xpoints; *yfield = &x->x_ypoints; *wfield = &x->x_wpoints; return (0); } /* get everything else you could possibly need about a plot, either for plot's own purposes or for plotting a "garray" */ int array_getfields(t_symbol *elemtemplatesym, t_canvas **elemtemplatecanvasp, t_template **elemtemplatep, int *elemsizep, t_fielddesc *xfielddesc, t_fielddesc *yfielddesc, t_fielddesc *wfielddesc, int *xonsetp, int *yonsetp, int *wonsetp) { int arrayonset, elemsize, yonset, wonset, xonset, type; t_template *elemtemplate; t_symbol *dummy, *varname; t_canvas *elemtemplatecanvas = 0; /* the "float" template is special in not having to have a canvas; template_findbyname is hardwired to return a predefined template. */ if (!(elemtemplate = template_findbyname(elemtemplatesym))) { pd_error(0, "plot: %s: no such template", elemtemplatesym->s_name); return (-1); } if (!((elemtemplatesym == &s_float) || (elemtemplatecanvas = template_findcanvas(elemtemplate)))) { pd_error(0, "plot: %s: no canvas for this template", elemtemplatesym->s_name); return (-1); } elemsize = elemtemplate->t_n * sizeof(t_word); if (yfielddesc && yfielddesc->fd_var) varname = yfielddesc->fd_un.fd_varsym; else varname = gensym("y"); if (!template_find_field(elemtemplate, varname, &yonset, &type, &dummy) || type != DT_FLOAT) yonset = -1; if (xfielddesc && xfielddesc->fd_var) varname = xfielddesc->fd_un.fd_varsym; else varname = gensym("x"); if (!template_find_field(elemtemplate, varname, &xonset, &type, &dummy) || type != DT_FLOAT) xonset = -1; if (wfielddesc && wfielddesc->fd_var) varname = wfielddesc->fd_un.fd_varsym; else varname = gensym("w"); if (!template_find_field(elemtemplate, varname, &wonset, &type, &dummy) || type != DT_FLOAT) wonset = -1; /* fill in slots for return values */ *elemtemplatecanvasp = elemtemplatecanvas; *elemtemplatep = elemtemplate; *elemsizep = elemsize; *xonsetp = xonset; *yonsetp = yonset; *wonsetp = wonset; return (0); } static void plot_getrect(t_gobj *z, t_glist *glist, t_word *data, t_template *template, t_float basex, t_float basey, int *xp1, int *yp1, int *xp2, int *yp2) { t_plot *x = (t_plot *)z; int elemsize, yonset, wonset, xonset; t_canvas *elemtemplatecanvas; t_template *elemtemplate; t_symbol *elemtemplatesym; t_float linewidth, xloc, xinc, yloc, style, yval, vis, scalarvis, edit; double xsum; t_array *array; int x1 = 0x7fffffff, y1 = 0x7fffffff, x2 = -0x7fffffff, y2 = -0x7fffffff; int i; t_float xpix, ypix, wpix; t_fielddesc *xfielddesc, *yfielddesc, *wfielddesc; /* if we're the only plot in the glist claim the whole thing */ if (glist->gl_list && !glist->gl_list->g_next) { *xp1 = *yp1 = -0x7fffffff; *xp2 = *yp2 = 0x7fffffff; return; } if (!plot_readownertemplate(x, data, template, &elemtemplatesym, &array, &linewidth, &xloc, &xinc, &yloc, &style, &vis, &scalarvis, &edit, &xfielddesc, &yfielddesc, &wfielddesc) && (vis != 0) && !array_getfields(elemtemplatesym, &elemtemplatecanvas, &elemtemplate, &elemsize, xfielddesc, yfielddesc, wfielddesc, &xonset, &yonset, &wonset)) { /* if it has more than 2000 points, just check 1000 of them. */ int incr = (array->a_n <= 2000 ? 1 : array->a_n / 1000); for (i = 0, xsum = 0; i < array->a_n; i += incr) { t_float usexloc, useyloc; t_gobj *y; /* get the coords of the point proper */ array_getcoordinate(glist, (char *)(array->a_vec) + i * elemsize, xonset, yonset, wonset, i, basex + xloc, basey + yloc, xinc, xfielddesc, yfielddesc, wfielddesc, &xpix, &ypix, &wpix); if (xpix < x1) x1 = xpix; if (xpix > x2) x2 = xpix; if (ypix - wpix < y1) y1 = ypix - wpix; if (ypix + wpix > y2) y2 = ypix + wpix; if (scalarvis != 0) { /* check also the drawing instructions for the scalar */ if (xonset >= 0) usexloc = basex + xloc + fielddesc_cvttocoord(xfielddesc, *(t_float *)(((char *)(array->a_vec) + elemsize * i) + xonset)); else usexloc = basex + xsum, xsum += xinc; if (yonset >= 0) yval = *(t_float *)(((char *)(array->a_vec) + elemsize * i) + yonset); else yval = 0; useyloc = basey + yloc + fielddesc_cvttocoord(yfielddesc, yval); for (y = elemtemplatecanvas->gl_list; y; y = y->g_next) { int xx1, xx2, yy1, yy2; const t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd); if (!wb) continue; (*wb->w_parentgetrectfn)(y, glist, (t_word *)((char *)(array->a_vec) + elemsize * i), elemtemplate, usexloc, useyloc, &xx1, &yy1, &xx2, &yy2); if (xx1 < x1) x1 = xx1; if (yy1 < y1) y1 = yy1; if (xx2 > x2) x2 = xx2; if (yy2 > y2) y2 = yy2; } } } } *xp1 = x1; *yp1 = y1; *xp2 = x2; *yp2 = y2; } static void plot_displace(t_gobj *z, t_glist *glist, t_word *data, t_template *template, t_float basex, t_float basey, int dx, int dy) { /* not yet */ } static void plot_select(t_gobj *z, t_glist *glist, t_word *data, t_template *template, t_float basex, t_float basey, int state) { /* not yet */ } static void plot_activate(t_gobj *z, t_glist *glist, t_word *data, t_template *template, t_float basex, t_float basey, int state) { /* not yet */ } #define CLIP(x) ((x) < 1e20 && (x) > -1e20 ? x : 0) static void plot_vis(t_gobj *z, t_glist *glist, t_word *data, t_template *template, t_float basex, t_float basey, int tovis) { t_plot *x = (t_plot *)z; int elemsize, yonset, wonset, xonset, i; t_canvas *elemtemplatecanvas; t_template *elemtemplate; t_symbol *elemtemplatesym; t_float linewidth, xloc, xinc, yloc, style, yval, vis, scalarvis, edit; double xsum; t_array *array; int nelem; char *elem; t_fielddesc *xfielddesc, *yfielddesc, *wfielddesc; char tag[80], tag0[80]; const char*tags[] = {tag, tag0, "array"}; /* even if the array is "invisible", if its visibility is set by an instance variable you have to explicitly erase it, because the flag could earlier have been on when we were getting drawn. Rather than look to try to find out whether we're visible we just do the erasure. At the TK level this should cause no action because the tag matches nobody. LATER we might want to optimize this somehow. Ditto the "vis()" routines for other drawing instructions. */ if (plot_readownertemplate(x, data, template, &elemtemplatesym, &array, &linewidth, &xloc, &xinc, &yloc, &style, &vis, &scalarvis, &edit, &xfielddesc, &yfielddesc, &wfielddesc) || ((vis == 0) && tovis) /* see above for 'tovis' */ || array_getfields(elemtemplatesym, &elemtemplatecanvas, &elemtemplate, &elemsize, xfielddesc, yfielddesc, wfielddesc, &xonset, &yonset, &wonset)) return; nelem = array->a_n; elem = (char *)array->a_vec; sprintf(tag , "plot%p", data); /* a tag that uniquely identifies the sub-plot */ sprintf(tag0, "plot%p_array%p_onset%+d%+d%+d", data, elem, wonset, xonset, yonset); if (glist->gl_isgraph) linewidth *= glist_getzoom(glist); if (tovis) { /* we use t_word because pdgui_vmess() has a convenient FLOATWORDS type * FLOATARRAY is impractical (as it sends a list, and the GUI expects arguments) */ t_word coordinates[1024*2]; if (style == PLOTSTYLE_POINTS) { t_float minyval = 1e20, maxyval = -1e20; int ndrawn = 0; int color = numbertocolor( fielddesc_getfloat(&x->x_outlinecolor, template, data, 1)); for (xsum = basex + xloc, i = 0; i < nelem; i++) { t_float yval, xpix, ypix, nextxloc, usexloc; int ixpix, inextx; if (xonset >= 0) { usexloc = basex + xloc + *(t_float *)((elem + elemsize * i) + xonset); ixpix = glist_xtopixels(glist, fielddesc_cvttocoord(xfielddesc, usexloc)); inextx = ixpix + 2; } else { usexloc = xsum; xsum += xinc; ixpix = glist_xtopixels(glist, fielddesc_cvttocoord(xfielddesc, usexloc)); inextx = glist_xtopixels(glist, fielddesc_cvttocoord(xfielddesc, xsum)); } if (yonset >= 0) yval = yloc + *(t_float *)((elem + elemsize * i) + yonset); else yval = 0; yval = CLIP(yval); if (yval < minyval) minyval = yval; if (yval > maxyval) maxyval = yval; if (i == nelem-1 || inextx != ixpix) { pdgui_vmess(0, "crr iiii rk rf rS", glist_getcanvas(glist), "create", "rectangle", ixpix , (int) glist_ytopixels(glist, basey + fielddesc_cvttocoord(yfielddesc, minyval)), inextx, (int)(glist_ytopixels(glist, basey + fielddesc_cvttocoord(yfielddesc, maxyval)) + linewidth), "-fill", color, "-width", 0., "-tags", 3, tags); ndrawn++; minyval = 1e20; maxyval = -1e20; } if (ndrawn > 2000) break; } } else { int outline = numbertocolor( fielddesc_getfloat(&x->x_outlinecolor, template, data, 1)); int lastpixel = -1, ndrawn = 0; t_float yval = 0, wval = 0, xpix; int ixpix = 0; /* draw the trace */ if (wonset >= 0) { /* found "w" field which controls linewidth. The trace is a filled polygon with 2n points. */ for (i = 0, xsum = xloc; i < nelem; i++) { t_float usexloc; if (xonset >= 0) usexloc = xloc + *(t_float *)((elem + elemsize * i) + xonset); else usexloc = xsum, xsum += xinc; if (yonset >= 0) yval = *(t_float *)((elem + elemsize * i) + yonset); else yval = 0; yval = CLIP(yval); wval = *(t_float *)((elem + elemsize * i) + wonset); wval = CLIP(wval); xpix = glist_xtopixels(glist, basex + fielddesc_cvttocoord(xfielddesc, usexloc)); ixpix = xpix + 0.5; if (xonset >= 0 || ixpix != lastpixel) { coordinates[ndrawn*2+0].w_float = ixpix; coordinates[ndrawn*2+1].w_float = glist_ytopixels( glist, basey + yloc + fielddesc_cvttocoord(yfielddesc, yval) - fielddesc_cvttocoord(wfielddesc, wval)); ndrawn++; } lastpixel = ixpix; if (ndrawn*2 >= sizeof(coordinates)/sizeof(*coordinates)) goto ouch; } lastpixel = -1; for (i = nelem-1; i >= 0; i--) { t_float usexloc; if (xonset >= 0) usexloc = xloc + *(t_float *)((elem + elemsize * i) + xonset); else xsum -= xinc, usexloc = xsum; if (yonset >= 0) yval = *(t_float *)((elem + elemsize * i) + yonset); else yval = 0; yval = CLIP(yval); wval = *(t_float *)((elem + elemsize * i) + wonset); wval = CLIP(wval); xpix = glist_xtopixels(glist, basex + fielddesc_cvttocoord(xfielddesc, usexloc)); ixpix = xpix + 0.5; if (xonset >= 0 || ixpix != lastpixel) { coordinates[ndrawn*2+0].w_float = ixpix; coordinates[ndrawn*2+1].w_float = glist_ytopixels( glist, basey + yloc + fielddesc_cvttocoord(yfielddesc, yval) + fielddesc_cvttocoord(wfielddesc, wval)); ndrawn++; } lastpixel = ixpix; if (ndrawn*2 >= sizeof(coordinates)/sizeof(*coordinates)) goto ouch; } /* TK will complain if there aren't at least 3 points. There should be at least two already. */ if (ndrawn < 4) { coordinates[ndrawn*2+0].w_float = ixpix + 10; coordinates[ndrawn*2+1].w_float = glist_ytopixels( glist, basey + yloc + fielddesc_cvttocoord(yfielddesc, yval) - fielddesc_cvttocoord(wfielddesc, wval)); ndrawn++; coordinates[ndrawn*2+0].w_float = ixpix + 10; coordinates[ndrawn*2+1].w_float = glist_ytopixels( glist, basey + yloc + fielddesc_cvttocoord(yfielddesc, yval) + fielddesc_cvttocoord(wfielddesc, wval)); ndrawn++; } ouch: pdgui_vmess(0, "crr ri rk rk ri rS", glist_getcanvas(glist), "create", "polygon", "-width", (glist->gl_isgraph ? glist_getzoom(glist) : 1), "-fill", outline, "-outline", outline, "-smooth", (style == PLOTSTYLE_BEZ), "-tags", 3, tags); pdgui_vmess(0, "crs w", glist_getcanvas(glist), "coords", tag0, ndrawn*2, coordinates); } else if (linewidth > 0) { /* no "w" field. If the linewidth is positive, draw a segmented line with the requested width; otherwise don't draw the trace at all. */ for (i = 0, xsum = xloc; i < nelem; i++) { t_float usexloc; if (xonset >= 0) usexloc = xloc + *(t_float *)((elem + elemsize * i) + xonset); else usexloc = xsum, xsum += xinc; if (yonset >= 0) yval = *(t_float *)((elem + elemsize * i) + yonset); else yval = 0; yval = CLIP(yval); xpix = glist_xtopixels(glist, basex + fielddesc_cvttocoord(xfielddesc, usexloc)); ixpix = xpix + 0.5; if (xonset >= 0 || ixpix != lastpixel) { coordinates[ndrawn*2+0].w_float = ixpix; coordinates[ndrawn*2+1].w_float = glist_ytopixels( glist, basey + yloc + fielddesc_cvttocoord(yfielddesc, yval)); ndrawn++; } lastpixel = ixpix; if (ndrawn*2 >= sizeof(coordinates)/sizeof(*coordinates)) break; } /* TK will complain if there aren't at least 2 points... */ if (ndrawn == 1) { coordinates[2].w_float = ixpix + 10; coordinates[3].w_float = glist_ytopixels(glist, basey + yloc + fielddesc_cvttocoord(yfielddesc, yval)); ndrawn = 2; } if(ndrawn) { pdgui_vmess(0, "crr iiii rf rk ri rS", glist_getcanvas(glist), "create", "line", 0, 0, 0, 0, "-width", linewidth, "-fill", outline, "-smooth", (style == PLOTSTYLE_BEZ), "-tags", 3, tags); pdgui_vmess(0, "crs w", glist_getcanvas(glist), "coords", tag0, ndrawn*2, coordinates); } } } /* We're done with the outline; now draw all the points. This code is inefficient since the template has to be searched for drawing instructions for every last point. */ if (scalarvis != 0) { for (xsum = xloc, i = 0; i < nelem; i++) { t_float usexloc, useyloc; t_gobj *y; if (xonset >= 0) usexloc = basex + xloc + *(t_float *)((elem + elemsize * i) + xonset); else usexloc = basex + xsum, xsum += xinc; if (yonset >= 0) yval = *(t_float *)((elem + elemsize * i) + yonset); else yval = 0; useyloc = basey + yloc + fielddesc_cvttocoord(yfielddesc, yval); for (y = elemtemplatecanvas->gl_list; y; y = y->g_next) { const t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd); if (!wb) continue; (*wb->w_parentvisfn)(y, glist, (t_word *)(elem + elemsize * i), elemtemplate, usexloc, useyloc, tovis); } } } } else { /* un-draw the individual points */ if (scalarvis != 0) { int i; for (i = 0; i < nelem; i++) { t_gobj *y; for (y = elemtemplatecanvas->gl_list; y; y = y->g_next) { const t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd); if (!wb) continue; (*wb->w_parentvisfn)(y, glist, (t_word *)(elem + elemsize * i), elemtemplate, 0, 0, 0); } } } /* and then the trace */ pdgui_vmess(0, "crs", glist_getcanvas(glist), "delete", tag); } } /* LATER protect against the template changing or the scalar disappearing probably by attaching a gpointer here ... */ static void array_motionfn(void *z, t_floatarg dx, t_floatarg dy, t_floatarg up) { if (up != 0) return; TEMPLATE->array_motion_xcumulative += dx * TEMPLATE->array_motion_xperpix; TEMPLATE->array_motion_ycumulative += dy * TEMPLATE->array_motion_yperpix; if (TEMPLATE->array_motion_xfield) { /* it's an x, y plot */ int i; for (i = 0; i < TEMPLATE->array_motion_npoints; i++) { t_word *thisword = (t_word *)(((char *)TEMPLATE->array_motion_wp) + i * TEMPLATE->array_motion_elemsize); t_float xwas = fielddesc_getcoord(TEMPLATE->array_motion_xfield, TEMPLATE->array_motion_template, thisword, 1); t_float ywas = (TEMPLATE->array_motion_yfield ? fielddesc_getcoord(TEMPLATE->array_motion_yfield, TEMPLATE->array_motion_template, thisword, 1) : 0); fielddesc_setcoord(TEMPLATE->array_motion_xfield, TEMPLATE->array_motion_template, thisword, xwas + dx * TEMPLATE->array_motion_xperpix, 1); if (TEMPLATE->array_motion_yfield) { if (TEMPLATE->array_motion_fatten) { if (i == 0) { t_float newy = ywas + dy * TEMPLATE->array_motion_yperpix; if (newy < 0) newy = 0; fielddesc_setcoord(TEMPLATE->array_motion_yfield, TEMPLATE->array_motion_template, thisword, newy, 1); } } else { fielddesc_setcoord(TEMPLATE->array_motion_yfield, TEMPLATE->array_motion_template, thisword, ywas + dy * TEMPLATE->array_motion_yperpix, 1); } } } } else if (TEMPLATE->array_motion_yfield) { /* a y-only plot. */ int thisx = TEMPLATE->array_motion_initx + TEMPLATE->array_motion_xcumulative + 0.5, x2; int increment, i, nchange; t_float newy = TEMPLATE->array_motion_ycumulative, oldy = fielddesc_getcoord(TEMPLATE->array_motion_yfield, TEMPLATE->array_motion_template, (t_word *)(((char *)TEMPLATE->array_motion_wp) + TEMPLATE->array_motion_elemsize * TEMPLATE->array_motion_lastx), 1); t_float ydiff = newy - oldy; if (thisx < 0) thisx = 0; else if (thisx >= TEMPLATE->array_motion_npoints) thisx = TEMPLATE->array_motion_npoints - 1; increment = (thisx > TEMPLATE->array_motion_lastx ? -1 : 1); nchange = 1 + increment * (TEMPLATE->array_motion_lastx - thisx); for (i = 0, x2 = thisx; i < nchange; i++, x2 += increment) { fielddesc_setcoord(TEMPLATE->array_motion_yfield, TEMPLATE->array_motion_template, (t_word *)(((char *)TEMPLATE->array_motion_wp) + TEMPLATE->array_motion_elemsize * x2), newy, 1); if (nchange > 1) newy -= ydiff * (1./(nchange - 1)); } TEMPLATE->array_motion_lastx = thisx; } if (TEMPLATE->array_motion_scalar) scalar_redraw(TEMPLATE->array_motion_scalar, TEMPLATE->array_motion_glist); if (TEMPLATE->array_motion_array) array_redraw(TEMPLATE->array_motion_array, TEMPLATE->array_motion_glist); } int scalar_doclick(t_word *data, t_template *template, t_scalar *sc, t_array *ap, struct _glist *owner, t_float xloc, t_float yloc, int xpix, int ypix, int shift, int alt, int dbl, int doit); /* try clicking on an element of the array as a scalar (if clicking on the trace of the array failed) */ static int array_doclick_element(t_array *array, t_glist *glist, t_symbol *elemtemplatesym, t_float xloc, t_float xinc, t_float yloc, t_fielddesc *xfield, t_fielddesc *yfield, t_fielddesc *wfield, int xpix, int ypix, int shift, int alt, int dbl, int doit) { t_canvas *elemtemplatecanvas; t_template *elemtemplate; int elemsize, yonset, wonset, xonset, i, incr, hit; double xsum; if (elemtemplatesym == &s_float) return (0); if (array_getfields(elemtemplatesym, &elemtemplatecanvas, &elemtemplate, &elemsize, xfield, yfield, wfield, &xonset, &yonset, &wonset)) return (0); /* if it has more than 2000 points, just check 300 of them. */ if (array->a_n < 2000) incr = 1; else incr = array->a_n / 300; for (i = 0, xsum = 0; i < array->a_n; i += incr) { t_float usexloc, useyloc; if (xonset >= 0) usexloc = xloc + fielddesc_cvttocoord(xfield, *(t_float *)(((char *)(array->a_vec) + elemsize * i) + xonset)); else usexloc = xloc + xsum, xsum += xinc; useyloc = yloc + (yonset >= 0 ? fielddesc_cvttocoord(yfield, *(t_float *) (((char *)(array->a_vec) + elemsize * i) + yonset)) : 0); if ((hit = scalar_doclick( (t_word *)((char *)(array->a_vec) + i * elemsize), elemtemplate, 0, array, glist, usexloc, useyloc, xpix, ypix, shift, alt, dbl, doit))) return (hit); } return (0); } static int array_doclick(t_array *array, t_glist *glist, t_scalar *sc, t_array *ap, t_symbol *elemtemplatesym, t_float xloc, t_float xinc, t_float yloc, t_float scalarvis, t_float edit, t_fielddesc *xfield, t_fielddesc *yfield, t_fielddesc *wfield, int xpix, int ypix, int shift, int alt, int dbl, int doit) { t_canvas *elemtemplatecanvas; t_template *elemtemplate; int elemsize, yonset, wonset, xonset, i; if (!array_getfields(elemtemplatesym, &elemtemplatecanvas, &elemtemplate, &elemsize, xfield, yfield, wfield, &xonset, &yonset, &wonset)) { t_float best = 100; /* if it has more than 2000 points, just check 1000 of them. */ int incr = (array->a_n <= 2000 ? 1 : array->a_n / 1000); TEMPLATE->array_motion_elemsize = elemsize; TEMPLATE->array_motion_glist = glist; TEMPLATE->array_motion_scalar = sc; TEMPLATE->array_motion_array = ap; TEMPLATE->array_motion_template = elemtemplate; TEMPLATE->array_motion_xperpix = glist_dpixtodx(glist, 1); TEMPLATE->array_motion_yperpix = glist_dpixtody(glist, 1); /* if we're a garray, the only one here, and if we appear to have only a 'y' field, click always succeeds and furthermore we'll call "motion" later. */ if (glist->gl_list && pd_class(&glist->gl_list->g_pd) == garray_class && !glist->gl_list->g_next && elemsize == sizeof(t_word)) { int xval = glist_pixelstox(glist, xpix); if (xval < 0) xval = 0; else if (xval >= array->a_n) xval = array->a_n - 1; TEMPLATE->array_motion_yfield = yfield; TEMPLATE->array_motion_ycumulative = glist_pixelstoy(glist, ypix); TEMPLATE->array_motion_fatten = 0; TEMPLATE->array_motion_xfield = 0; TEMPLATE->array_motion_xcumulative = 0; TEMPLATE->array_motion_lastx = TEMPLATE->array_motion_initx = xval; TEMPLATE->array_motion_npoints = array->a_n; TEMPLATE->array_motion_wp = (t_word *)((char *)array->a_vec); if (doit) { fielddesc_setcoord(yfield, elemtemplate, (t_word *)(((char *)array->a_vec) + elemsize * xval), glist_pixelstoy(glist, ypix), 1); glist_grab(glist, 0, array_motionfn, 0, xpix, ypix); if (TEMPLATE->array_motion_scalar) scalar_redraw(TEMPLATE->array_motion_scalar, TEMPLATE->array_motion_glist); if (TEMPLATE->array_motion_array) array_redraw(TEMPLATE->array_motion_array, TEMPLATE->array_motion_glist); } } else { /* First we get the closest distance to any element */ for (i = 0; i < array->a_n; i += incr) { t_float pxpix, pypix, pwpix, dx, dy; array_getcoordinate(glist, (char *)(array->a_vec) + i * elemsize, xonset, yonset, wonset, i, xloc, yloc, xinc, xfield, yfield, wfield, &pxpix, &pypix, &pwpix); if (pwpix < 4) pwpix = 4; dx = pxpix - xpix; if (dx < 0) dx = -dx; if (dx > 8) continue; dy = pypix - ypix; if (dy < 0) dy = -dy; if (dx + dy < best) best = dx + dy; if (wonset >= 0) { dy = (pypix + pwpix) - ypix; if (dy < 0) dy = -dy; if (dx + dy < best) best = dx + dy; dy = (pypix - pwpix) - ypix; if (dy < 0) dy = -dy; if (dx + dy < best) best = dx + dy; } } /* If we're not too close, we first try to click a scalar (if visible). This would not affect the array */ if (best > 8) { if (scalarvis != 0) { return (array_doclick_element(array, glist, elemtemplatesym, xloc, xinc, yloc, xfield, yfield, wfield, xpix, ypix, shift, alt, dbl, doit)); } else return (0); } /* Now we walk over the array again and decide whether we a) grab an element, b) change the line width, c) delete an element or d) add a new element */ best += 0.001; /* add truncation error margin */ /* otherwise we try to grab a vertex */ if (!edit) return (0); for (i = 0; i < array->a_n; i += incr) { t_float pxpix, pypix, pwpix, dx, dy, dy2, dy3; array_getcoordinate(glist, (char *)(array->a_vec) + i * elemsize, xonset, yonset, wonset, i, xloc, yloc, xinc, xfield, yfield, wfield, &pxpix, &pypix, &pwpix); if (pwpix < 4) pwpix = 4; dx = pxpix - xpix; if (dx < 0) dx = -dx; dy = pypix - ypix; if (dy < 0) dy = -dy; if (wonset >= 0) { dy2 = (pypix + pwpix) - ypix; if (dy2 < 0) dy2 = -dy2; dy3 = (pypix - pwpix) - ypix; if (dy3 < 0) dy3 = -dy3; if (yonset < 0) dy = 100; } else dy2 = dy3 = 100; if (dx + dy <= best || dx + dy2 <= best || dx + dy3 <= best) { if (dy < dy2 && dy < dy3) TEMPLATE->array_motion_fatten = 0; else if (dy2 < dy3) TEMPLATE->array_motion_fatten = -1; else TEMPLATE->array_motion_fatten = 1; if (doit) { char *elem = (char *)array->a_vec; if (alt && xpix < pxpix) /* delete a point */ { if (array->a_n <= 1) return (0); memmove((char *)(array->a_vec) + elemsize * i, (char *)(array->a_vec) + elemsize * (i+1), (array->a_n - 1 - i) * elemsize); array_resize_and_redraw(array, glist, array->a_n - 1); return (0); } else if (alt) { /* add a point (after the clicked-on one) */ array_resize_and_redraw(array, glist, array->a_n + 1); elem = (char *)array->a_vec; memmove(elem + elemsize * (i+1), elem + elemsize * i, (array->a_n - i - 1) * elemsize); i++; } if (xonset >= 0) { TEMPLATE->array_motion_xfield = xfield; TEMPLATE->array_motion_xcumulative = fielddesc_getcoord(xfield, TEMPLATE->array_motion_template, (t_word *)(elem + i * elemsize), 1); TEMPLATE->array_motion_wp = (t_word *)(elem + i * elemsize); if (shift) TEMPLATE->array_motion_npoints = array->a_n - i; else TEMPLATE->array_motion_npoints = 1; } else { TEMPLATE->array_motion_xfield = 0; TEMPLATE->array_motion_xcumulative = 0; TEMPLATE->array_motion_wp = (t_word *)elem; TEMPLATE->array_motion_npoints = array->a_n; TEMPLATE->array_motion_initx = i; TEMPLATE->array_motion_lastx = i; TEMPLATE->array_motion_xperpix *= (xinc == 0 ? 1 : 1./xinc); } if (TEMPLATE->array_motion_fatten) { TEMPLATE->array_motion_yfield = wfield; TEMPLATE->array_motion_ycumulative = fielddesc_getcoord(wfield, TEMPLATE->array_motion_template, (t_word *)(elem + i * elemsize), 1); if (TEMPLATE->array_motion_yperpix < 0) TEMPLATE->array_motion_yperpix *= -1; TEMPLATE->array_motion_yperpix *= -TEMPLATE->array_motion_fatten; } else if (yonset >= 0) { TEMPLATE->array_motion_yfield = yfield; TEMPLATE->array_motion_ycumulative = fielddesc_getcoord(yfield, TEMPLATE->array_motion_template, (t_word *)(elem + i * elemsize), 1); } else { TEMPLATE->array_motion_yfield = 0; TEMPLATE->array_motion_ycumulative = 0; } glist_grab(glist, 0, array_motionfn, 0, xpix, ypix); } if (alt) { if (xpix < pxpix) return (CURSOR_EDITMODE_DISCONNECT); else return (CURSOR_RUNMODE_ADDPOINT); } else return (TEMPLATE->array_motion_fatten ? CURSOR_RUNMODE_THICKEN : CURSOR_RUNMODE_CLICKME); } } } } /* JMZ: change the cursor to "clickme" if the array can be edited */ return (!edit)?CURSOR_RUNMODE_NOTHING:CURSOR_RUNMODE_CLICKME; } static int plot_click(t_gobj *z, t_glist *glist, t_word *data, t_template *template, t_scalar *sc, t_array *ap, t_float basex, t_float basey, int xpix, int ypix, int shift, int alt, int dbl, int doit) { t_plot *x = (t_plot *)z; t_symbol *elemtemplatesym; t_float linewidth, xloc, xinc, yloc, style, vis, scalarvis, edit; t_array *array; t_fielddesc *xfielddesc, *yfielddesc, *wfielddesc; if (!plot_readownertemplate(x, data, template, &elemtemplatesym, &array, &linewidth, &xloc, &xinc, &yloc, &style, &vis, &scalarvis, &edit, &xfielddesc, &yfielddesc, &wfielddesc) && (vis != 0)) { return (array_doclick(array, glist, sc, ap, elemtemplatesym, basex + xloc, xinc, basey + yloc, scalarvis, edit, xfielddesc, yfielddesc, wfielddesc, xpix, ypix, shift, alt, dbl, doit)); } else return (0); } const t_parentwidgetbehavior plot_widgetbehavior = { plot_getrect, plot_displace, plot_select, plot_activate, plot_vis, plot_click, }; static void plot_setup(void) { plot_class = class_new(gensym("plot"), (t_newmethod)plot_new, 0, sizeof(t_plot), 0, A_GIMME, 0); class_setdrawcommand(plot_class); class_addfloat(plot_class, plot_float); class_setparentwidget(plot_class, &plot_widgetbehavior); } /* ---------------- drawnumber: draw a number (or symbol) ---------------- */ /* drawnumbers draw numeric fields at controllable locations, with controllable color and label. invocation: (drawnumber|drawsymbol) [-v ] variable x y color label */ t_class *drawnumber_class; typedef struct _drawnumber { t_object x_obj; t_symbol *x_fieldname; t_fielddesc x_xloc; t_fielddesc x_yloc; t_fielddesc x_color; t_fielddesc x_vis; t_symbol *x_label; t_canvas *x_canvas; } t_drawnumber; static void *drawnumber_new(t_symbol *classsym, int argc, t_atom *argv) { t_drawnumber *x = (t_drawnumber *)pd_new(drawnumber_class); const char *classname = classsym->s_name; fielddesc_setfloat_const(&x->x_vis, 1); x->x_canvas = canvas_getcurrent(); while (1) { t_symbol *firstarg = atom_getsymbolarg(0, argc, argv); if (!strcmp(firstarg->s_name, "-v") && argc > 1) { fielddesc_setfloatarg(&x->x_vis, 1, argv+1); argc -= 2; argv += 2; } else if (!strcmp(firstarg->s_name, "-n")) { fielddesc_setfloat_const(&x->x_vis, 0); argc--; argv++; } else if (*firstarg->s_name == '-') { pd_error(x, "%s: unknown flag '%s'...", classsym->s_name, firstarg->s_name); argc--; argv++; } else break; } /* next argument is name of field to draw - we don't know its type yet but fielddesc_setfloatarg() will do fine here. */ x->x_fieldname = atom_getsymbolarg(0, argc, argv); if (argc) argc--, argv++; if (argc) fielddesc_setfloatarg(&x->x_xloc, argc--, argv++); else fielddesc_setfloat_const(&x->x_xloc, 0); if (argc) fielddesc_setfloatarg(&x->x_yloc, argc--, argv++); else fielddesc_setfloat_const(&x->x_yloc, 0); if (argc) fielddesc_setfloatarg(&x->x_color, argc--, argv++); else fielddesc_setfloat_const(&x->x_color, 0); if (argc) x->x_label = atom_getsymbolarg(0, argc, argv); else x->x_label = &s_; return (x); } void drawnumber_float(t_drawnumber *x, t_floatarg f) { int viswas; if (x->x_vis.fd_type != A_FLOAT || x->x_vis.fd_var) { pd_error(x, "global vis/invis for a template with variable visibility"); return; } viswas = (x->x_vis.fd_un.fd_float != 0); if ((f != 0 && viswas) || (f == 0 && !viswas)) return; canvas_redrawallfortemplatecanvas(x->x_canvas, 2); fielddesc_setfloat_const(&x->x_vis, (f != 0)); canvas_redrawallfortemplatecanvas(x->x_canvas, 1); } /* -------------------- widget behavior for drawnumber ------------ */ static int drawnumber_gettype(t_drawnumber *x, t_word *data, t_template *template, int *onsetp) { int type; t_symbol *arraytype; if (template_find_field(template, x->x_fieldname, onsetp, &type, &arraytype) && type != DT_ARRAY) return (type); else return (-1); } #define DRAWNUMBER_BUFSIZE 1024 static void drawnumber_getbuf(t_drawnumber *x, t_word *data, t_template *template, char *buf) { int nchars, onset, type = drawnumber_gettype(x, data, template, &onset); if (type < 0) buf[0] = 0; else { strncpy(buf, x->x_label->s_name, DRAWNUMBER_BUFSIZE); buf[DRAWNUMBER_BUFSIZE - 1] = 0; nchars = (int)strlen(buf); if (type == DT_TEXT) { char *buf2; int size2, ncopy; binbuf_gettext(((t_word *)((char *)data + onset))->w_binbuf, &buf2, &size2); ncopy = (size2 > DRAWNUMBER_BUFSIZE-1-nchars ? DRAWNUMBER_BUFSIZE-1-nchars: size2); memcpy(buf+nchars, buf2, ncopy); buf[nchars+ncopy] = 0; if (nchars+ncopy == DRAWNUMBER_BUFSIZE-1) strcpy(buf+(DRAWNUMBER_BUFSIZE-4), "..."); t_freebytes(buf2, size2); } else { t_atom at; switch(type) { default: SETSYMBOL(&at, ((t_word *)((char *)data + onset))->w_symbol); atom_string(&at, buf + nchars, DRAWNUMBER_BUFSIZE - nchars); break; case DT_FLOAT: SETFLOAT(&at, ((t_word *)((char *)data + onset))->w_float); atom_string(&at, buf + nchars, DRAWNUMBER_BUFSIZE - nchars); break; case DT_SYMBOL: strncpy(buf + nchars, ((t_word *)((char *)data + onset))->w_symbol->s_name, DRAWNUMBER_BUFSIZE - nchars); } } } } static void drawnumber_getrect(t_gobj *z, t_glist *glist, t_word *data, t_template *template, t_float basex, t_float basey, int *xp1, int *yp1, int *xp2, int *yp2) { t_drawnumber *x = (t_drawnumber *)z; t_atom at; int xloc, yloc, fontwidth, fontheight, bufsize, width, height; char buf[DRAWNUMBER_BUFSIZE], *startline, *newline; if (!fielddesc_getfloat(&x->x_vis, template, data, 0)) { *xp1 = *yp1 = 0x7fffffff; *xp2 = *yp2 = -0x7fffffff; return; } xloc = glist_xtopixels(glist, basex + fielddesc_getcoord(&x->x_xloc, template, data, 0)); yloc = glist_ytopixels(glist, basey + fielddesc_getcoord(&x->x_yloc, template, data, 0)); fontwidth = glist_fontwidth(glist); fontheight = glist_fontheight(glist); drawnumber_getbuf(x, data, template, buf); width = 0; height = 1; for (startline = buf; (newline = strchr(startline, '\n')); startline = newline+1) { if (newline - startline > width) width = (int)(newline - startline); height++; } if (strlen(startline) > (unsigned)width) width = (int)strlen(startline); *xp1 = xloc; *yp1 = yloc; *xp2 = xloc + fontwidth * width; *yp2 = yloc + fontheight * height; } static void drawnumber_displace(t_gobj *z, t_glist *glist, t_word *data, t_template *template, t_float basex, t_float basey, int dx, int dy) { /* refuse */ } static void drawnumber_select(t_gobj *z, t_glist *glist, t_word *data, t_template *template, t_float basex, t_float basey, int state) { post("drawnumber_select %d", state); /* fill in later */ } static void drawnumber_activate(t_gobj *z, t_glist *glist, t_word *data, t_template *template, t_float basex, t_float basey, int state) { post("drawnumber_activate %d", state); } static void drawnumber_vis(t_gobj *z, t_glist *glist, t_word *data, t_template *template, t_float basex, t_float basey, int vis) { t_drawnumber *x = (t_drawnumber *)z; char tag[80]; const char*tags[] = {tag, "label"}; /* see comment in plot_vis() */ if (vis && !fielddesc_getfloat(&x->x_vis, template, data, 0)) return; sprintf(tag, "drawnumber%p", data); if (vis) { t_atom fontatoms[3]; t_atom at; int xloc = glist_xtopixels(glist, basex + fielddesc_getcoord(&x->x_xloc, template, data, 0)); int yloc = glist_ytopixels(glist, basey + fielddesc_getcoord(&x->x_yloc, template, data, 0)); char buf[DRAWNUMBER_BUFSIZE]; int color = numbertocolor( fielddesc_getfloat(&x->x_color, template, data, 1)); drawnumber_getbuf(x, data, template, buf); SETSYMBOL(fontatoms+0, gensym(sys_font)); SETFLOAT (fontatoms+1,-sys_hostfontsize(glist_getfont(glist), glist_getzoom(glist))); SETSYMBOL(fontatoms+2, gensym(sys_fontweight)); pdgui_vmess(0, "crr ii rs rk rs rA rS", glist_getcanvas(glist), "create", "text", xloc, yloc, "-anchor", "nw", "-fill", color, "-text", buf, "-font", 3, fontatoms, "-tags", 2, tags); } else pdgui_vmess(0, "crs", glist_getcanvas(glist), "delete", tag); } static void drawnumber_motionfn(void *z, t_floatarg dx, t_floatarg dy, t_floatarg up) { t_drawnumber *x = (t_drawnumber *)z; t_atom at; if (up != 0) return; if (!gpointer_check(&TEMPLATE->drawnumber_motion_gpointer, 0)) { post("drawnumber_motion: scalar disappeared"); return; } if (TEMPLATE->drawnumber_motion_type != DT_FLOAT) return; TEMPLATE->drawnumber_motion_ycumulative -= dy; template_setfloat(TEMPLATE->drawnumber_motion_template, x->x_fieldname, TEMPLATE->drawnumber_motion_wp, TEMPLATE->drawnumber_motion_ycumulative, 1); if (TEMPLATE->drawnumber_motion_scalar) template_notifyforscalar(TEMPLATE->drawnumber_motion_template, TEMPLATE->drawnumber_motion_glist, TEMPLATE->drawnumber_motion_scalar, gensym("change"), 1, &at); if (TEMPLATE->drawnumber_motion_scalar) scalar_redraw(TEMPLATE->drawnumber_motion_scalar, TEMPLATE->drawnumber_motion_glist); if (TEMPLATE->drawnumber_motion_array) array_redraw(TEMPLATE->drawnumber_motion_array, TEMPLATE->drawnumber_motion_glist); } static void drawnumber_key(void *z, t_symbol *keysym, t_floatarg fkey) { t_drawnumber *x = (t_drawnumber *)z; int key = fkey; char sbuf[MAXPDSTRING]; t_atom at; if (!gpointer_check(&TEMPLATE->drawnumber_motion_gpointer, 0)) { post("drawnumber_motion: scalar disappeared"); return; } if (key == 0) return; if (TEMPLATE->drawnumber_motion_type == DT_SYMBOL) { /* key entry for a symbol field */ if (TEMPLATE->drawnumber_motion_firstkey) sbuf[0] = 0; else strncpy(sbuf, template_getsymbol(TEMPLATE->drawnumber_motion_template, x->x_fieldname, TEMPLATE->drawnumber_motion_wp, 1)->s_name, MAXPDSTRING); sbuf[MAXPDSTRING-1] = 0; if (key == '\b') { if (*sbuf) sbuf[strlen(sbuf)-1] = 0; } else { sbuf[strlen(sbuf)+1] = 0; sbuf[strlen(sbuf)] = key; } } else if (TEMPLATE->drawnumber_motion_type == DT_FLOAT) { /* key entry for a numeric field. This is just a stopgap. */ double newf; if (TEMPLATE->drawnumber_motion_firstkey) sbuf[0] = 0; else sprintf(sbuf, "%g", template_getfloat(TEMPLATE->drawnumber_motion_template, x->x_fieldname, TEMPLATE->drawnumber_motion_wp, 1)); TEMPLATE->drawnumber_motion_firstkey = (key == '\n'); if (key == '\b') { if (*sbuf) sbuf[strlen(sbuf)-1] = 0; } else { sbuf[strlen(sbuf)+1] = 0; sbuf[strlen(sbuf)] = key; } if (sscanf(sbuf, "%lg", &newf) < 1) newf = 0; template_setfloat(TEMPLATE->drawnumber_motion_template, x->x_fieldname, TEMPLATE->drawnumber_motion_wp, (t_float)newf, 1); if (TEMPLATE->drawnumber_motion_scalar) template_notifyforscalar(TEMPLATE->drawnumber_motion_template, TEMPLATE->drawnumber_motion_glist, TEMPLATE->drawnumber_motion_scalar, gensym("change"), 1, &at); if (TEMPLATE->drawnumber_motion_scalar) scalar_redraw(TEMPLATE->drawnumber_motion_scalar, TEMPLATE->drawnumber_motion_glist); if (TEMPLATE->drawnumber_motion_array) array_redraw(TEMPLATE->drawnumber_motion_array, TEMPLATE->drawnumber_motion_glist); } else post("typing at text fields not yet implemented"); } static int drawnumber_click(t_gobj *z, t_glist *glist, t_word *data, t_template *template, t_scalar *sc, t_array *ap, t_float basex, t_float basey, int xpix, int ypix, int shift, int alt, int dbl, int doit) { t_drawnumber *x = (t_drawnumber *)z; int x1, y1, x2, y2, type, onset; drawnumber_getrect(z, glist, data, template, basex, basey, &x1, &y1, &x2, &y2); if (xpix >= x1 && xpix <= x2 && ypix >= y1 && ypix <= y2 && ((type = drawnumber_gettype(x, data, template, &onset)) == DT_FLOAT || type == DT_SYMBOL)) { if (doit) { TEMPLATE->drawnumber_motion_glist = glist; TEMPLATE->drawnumber_motion_wp = data; TEMPLATE->drawnumber_motion_template = template; TEMPLATE->drawnumber_motion_scalar = sc; TEMPLATE->drawnumber_motion_array = ap; TEMPLATE->drawnumber_motion_firstkey = 1; TEMPLATE->drawnumber_motion_ycumulative = template_getfloat(template, x->x_fieldname, data, 0); TEMPLATE->drawnumber_motion_type = type; if (TEMPLATE->drawnumber_motion_scalar) gpointer_setglist(&TEMPLATE->drawnumber_motion_gpointer, TEMPLATE->drawnumber_motion_glist, TEMPLATE->drawnumber_motion_scalar); else gpointer_setarray(&TEMPLATE->drawnumber_motion_gpointer, TEMPLATE->drawnumber_motion_array, TEMPLATE->drawnumber_motion_wp); glist_grab(glist, z, drawnumber_motionfn, drawnumber_key, xpix, ypix); } return (1); } else return (0); } const t_parentwidgetbehavior drawnumber_widgetbehavior = { drawnumber_getrect, drawnumber_displace, drawnumber_select, drawnumber_activate, drawnumber_vis, drawnumber_click, }; static void drawnumber_free(t_drawnumber *x) { } static void drawnumber_setup(void) { drawnumber_class = class_new(gensym("drawtext"), (t_newmethod)drawnumber_new, (t_method)drawnumber_free, sizeof(t_drawnumber), 0, A_GIMME, 0); class_setdrawcommand(drawnumber_class); class_addfloat(drawnumber_class, drawnumber_float); class_addcreator((t_newmethod)drawnumber_new, gensym("drawsymbol"), A_GIMME, 0); class_addcreator((t_newmethod)drawnumber_new, gensym("drawnumber"), A_GIMME, 0); class_setparentwidget(drawnumber_class, &drawnumber_widgetbehavior); } /* ---------------------- setup function ---------------------------- */ void g_template_setup(void) { template_setup(); gtemplate_setup(); curve_setup(); plot_setup(); drawnumber_setup(); } void g_template_newpdinstance(void) { TEMPLATE = getbytes(sizeof(*TEMPLATE)); } void g_template_freepdinstance(void) { freebytes(TEMPLATE, sizeof(*TEMPLATE)); } ================================================ FILE: libs/libpd/pure-data/src/g_text.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* changes by Thomas Musil IEM KUG Graz Austria 2001 */ /* the methods for calling the gui-objects from menu are implemented */ /* all changes are labeled with iemlib */ #include #include "m_pd.h" #include "m_imp.h" #include "g_canvas.h" #include #include #include #include "g_undo.h" /* borrowed from RMARGIN and BMARGIN in g_rtext.c */ #define ATOM_RMARGIN 2 /* 2 pixels smaller than object LMARGIN + RMARGIN */ #define ATOM_BMARGIN 4 /* 1 pixel smaller than object TMARGIN+BMARGIN */ #define MESSAGE_CLICK_WIDTH 5 t_class *text_class; static t_class *message_class; static t_class *gatom_class; static void text_vis(t_gobj *z, t_glist *glist, int vis); static void text_displace(t_gobj *z, t_glist *glist, int dx, int dy); static void text_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2); void canvas_startmotion(t_canvas *x); int glist_getindex(t_glist *x, t_gobj *y); void gatom_undarken(t_text *x); static void glist_nograb(t_glist *x) { if (x->gl_editor) { t_canvas *canvas = glist_getcanvas(x); t_object *ob; t_gobj*g; for (g = canvas->gl_list; g; g = g->g_next) if ((ob = pd_checkobject(&g->g_pd)) && T_ATOM == ob->te_type) gatom_undarken(ob); x->gl_editor->e_grab = 0; } } /* ----------------- the "text" object. ------------------ */ /* add a "text" object (comment) to a glist. Called without args if invoked from the GUI; otherwise at least x and y are provided. */ void glist_text(t_glist *gl, t_symbol *s, int argc, t_atom *argv) { t_text *x = (t_text *)pd_new(text_class); t_atom at; x->te_width = 0; /* don't know it yet. */ x->te_type = T_TEXT; x->te_binbuf = binbuf_new(); if (argc > 1) { x->te_xpix = atom_getfloatarg(0, argc, argv); x->te_ypix = atom_getfloatarg(1, argc, argv); if (argc > 2) binbuf_restore(x->te_binbuf, argc-2, argv+2); else { SETSYMBOL(&at, gensym("comment")); binbuf_restore(x->te_binbuf, 1, &at); } glist_add(gl, &x->te_g); } else { int xpix, ypix; pd_vmess((t_pd *)glist_getcanvas(gl), gensym("editmode"), "i", 1); SETSYMBOL(&at, gensym("comment")); glist_noselect(gl); glist_nograb(gl); glist_getnextxy(gl, &xpix, &ypix); x->te_xpix = xpix/gl->gl_zoom - 1; x->te_ypix = ypix/gl->gl_zoom - 1; binbuf_restore(x->te_binbuf, 1, &at); glist_add(gl, &x->te_g); glist_noselect(gl); glist_select(gl, &x->te_g); /* it would be nice to "activate" here, but then the second, "put-me-down" click changes the text selection, which is quite irritating, so I took this back out. It's OK in messages and objects though since there's no text in them at menu creation. */ /* gobj_activate(&x->te_g, gl, 1); */ if (!canvas_undo_get(glist_getcanvas(gl))->u_doing) canvas_undo_add(glist_getcanvas(gl), UNDO_CREATE, "create", (void *)canvas_undo_set_create(glist_getcanvas(gl))); canvas_startmotion(glist_getcanvas(gl)); } } /* ----------------- the "object" object. ------------------ */ void canvas_getargs(int *argcp, t_atom **argvp); static void canvas_error_couldntcreate(void*x, t_binbuf*b, const char*errmsg) { char *buf=0; int bufsize=0; if (!binbuf_getnatom(b)) return; binbuf_gettext(b, &buf, &bufsize); buf = resizebytes(buf, bufsize, bufsize+1); buf[bufsize] = 0; logpost(x, PD_CRITICAL, "%s", buf); logpost(x, PD_ERROR, "%s", errmsg); freebytes(buf, bufsize); } static void canvas_objtext(t_glist *gl, int xpix, int ypix, int width, int selected, t_binbuf *b) { t_text *x; int argc; t_atom *argv; pd_this->pd_newest = 0; canvas_setcurrent((t_canvas *)gl); canvas_getargs(&argc, &argv); binbuf_eval(b, &pd_objectmaker, argc, argv); if (binbuf_getnatom(b)) { if (!pd_this->pd_newest) x = 0; else if (!(x = pd_checkobject(pd_this->pd_newest))) { canvas_error_couldntcreate(0, b, "... didn't return a patchable object"); } } else x = 0; if (!x) { x = (t_text *)pd_new(text_class); canvas_error_couldntcreate(x, b, "... couldn't create"); } x->te_binbuf = b; x->te_xpix = xpix; x->te_ypix = ypix; x->te_width = width; x->te_type = T_OBJECT; glist_add(gl, &x->te_g); if (selected) { /* this is called if we've been created from the menu. */ glist_select(gl, &x->te_g); gobj_activate(&x->te_g, gl, 1); } if (pd_class(&x->ob_pd) == vinlet_class) canvas_resortinlets(glist_getcanvas(gl)); if (pd_class(&x->ob_pd) == voutlet_class) canvas_resortoutlets(glist_getcanvas(gl)); canvas_unsetcurrent((t_canvas *)gl); } extern int sys_noautopatch; /* utility routine to figure out where to put a new text box from menu and whether to connect to it automatically */ static void canvas_howputnew(t_canvas *x, int *connectp, int *xpixp, int *ypixp, int *indexp, int *totalp) { float dx = 5.5 * x->gl_zoom; int xpix, ypix, indx = 0, nobj = 0, n2, x1, x2, y1, y2; int connectme = (x->gl_editor->e_selection && !x->gl_editor->e_selection->sel_next && !sys_noautopatch); glist_nograb(x); if (connectme) { t_gobj *g, *selected = x->gl_editor->e_selection->sel_what; /* get number of objects */ for (g = x->gl_list, nobj = 0; g; g = g->g_next, nobj++) ; /* deselect the current selection (and create pending objects) */ glist_noselect(x); /* search back for 'selected' and if it isn't on the list, plan just to connect from the last item on the list. */ for (g = x->gl_list, n2 = 0; g; g = g->g_next, n2++) { if (g == selected) { indx = n2; break; } else if (!g->g_next) { /* we couldn't find the selected object any more * this probably means, that it was replaced by a newly * created object, which is now the last on the list. */ indx = nobj-1; break; } } /* so where do we put the new object? just below the one we connect from! */ if(g) { gobj_getrect(g, x, &x1, &y1, &x2, &y2); *xpixp = x1 / x->gl_zoom; *ypixp = (y2+dx) / x->gl_zoom; /* 5 pixels down, rounded */ } else { /* just in case */ glist_getnextxy(x, xpixp, ypixp); *xpixp = *xpixp/x->gl_zoom - 3; *ypixp = *ypixp/x->gl_zoom - 3; } } else { glist_getnextxy(x, xpixp, ypixp); *xpixp = *xpixp/x->gl_zoom - 3; *ypixp = *ypixp/x->gl_zoom - 3; glist_noselect(x); } *connectp = connectme; *indexp = indx; *totalp = nobj; } /* object creation routine. These are called without any arguments if they're invoked from the gui; when pasting or restoring from a file, we get at least x and y. */ void canvas_obj(t_glist *gl, t_symbol *s, int argc, t_atom *argv) { t_text *x; if (argc >= 2) { t_binbuf *b = binbuf_new(); binbuf_restore(b, argc-2, argv+2); canvas_objtext(gl, atom_getfloatarg(0, argc, argv), atom_getfloatarg(1, argc, argv), 0, 0, b); } /* JMZ: don't go into interactive mode in a closed canvas */ else if (!glist_isvisible(gl)) post("unable to create stub object in closed canvas!"); else { /* interactively create new object */ t_binbuf *b = binbuf_new(); int connectme, xpix, ypix, indx, nobj; canvas_howputnew(gl, &connectme, &xpix, &ypix, &indx, &nobj); pd_vmess(&gl->gl_pd, gensym("editmode"), "i", 1); canvas_objtext(gl, xpix, ypix, 0, 1, b); if (connectme) canvas_connect(gl, indx, 0, nobj, 0); else canvas_startmotion(glist_getcanvas(gl)); if (!canvas_undo_get(glist_getcanvas(gl))->u_doing) canvas_undo_add(glist_getcanvas(gl), UNDO_CREATE, "create", (void *)canvas_undo_set_create(glist_getcanvas(gl))); } } /* make an object box for an object that's already there. */ /* iemlib */ void canvas_iemguis(t_glist *gl, t_symbol *guiobjname) { t_atom at; t_binbuf *b = binbuf_new(); int connectme, xpix, ypix, indx, nobj; canvas_howputnew(gl, &connectme, &xpix, &ypix, &indx, &nobj); pd_vmess(&gl->gl_pd, gensym("editmode"), "i", 1); glist_noselect(gl); SETSYMBOL(&at, guiobjname); binbuf_restore(b, 1, &at); canvas_objtext(gl, xpix, ypix, 0, 1, b); if(connectme) canvas_connect(gl, indx, 0, nobj, 0); else canvas_startmotion(glist_getcanvas(gl)); canvas_undo_add(glist_getcanvas(gl), UNDO_CREATE, "create", (void *)canvas_undo_set_create(glist_getcanvas(gl))); } void canvas_bng(t_glist *gl, t_symbol *s, int argc, t_atom *argv) { canvas_iemguis(gl, gensym("bng")); } void canvas_toggle(t_glist *gl, t_symbol *s, int argc, t_atom *argv) { canvas_iemguis(gl, gensym("tgl")); } void canvas_vslider(t_glist *gl, t_symbol *s, int argc, t_atom *argv) { canvas_iemguis(gl, gensym("vsl")); } void canvas_hslider(t_glist *gl, t_symbol *s, int argc, t_atom *argv) { canvas_iemguis(gl, gensym("hsl")); } void canvas_hdial(t_glist *gl, t_symbol *s, int argc, t_atom *argv) { canvas_iemguis(gl, gensym("hdl")); } void canvas_vdial(t_glist *gl, t_symbol *s, int argc, t_atom *argv) { canvas_iemguis(gl, gensym("vdl")); } void canvas_hradio(t_glist *gl, t_symbol *s, int argc, t_atom *argv) { canvas_iemguis(gl, gensym("hradio")); } void canvas_vradio(t_glist *gl, t_symbol *s, int argc, t_atom *argv) { canvas_iemguis(gl, gensym("vradio")); } void canvas_vumeter(t_glist *gl, t_symbol *s, int argc, t_atom *argv) { canvas_iemguis(gl, gensym("vu")); } void canvas_mycnv(t_glist *gl, t_symbol *s, int argc, t_atom *argv) { canvas_iemguis(gl, gensym("cnv")); } void canvas_numbox(t_glist *gl, t_symbol *s, int argc, t_atom *argv) { canvas_iemguis(gl, gensym("nbx")); } /* iemlib */ void canvas_objfor(t_glist *gl, t_text *x, int argc, t_atom *argv) { x->te_width = 0; /* don't know it yet. */ x->te_type = T_OBJECT; x->te_binbuf = binbuf_new(); x->te_xpix = atom_getfloatarg(0, argc, argv); x->te_ypix = atom_getfloatarg(1, argc, argv); if (argc > 2) binbuf_restore(x->te_binbuf, argc-2, argv+2); glist_add(gl, &x->te_g); } /* ---------------------- the "message" text item ------------------------ */ typedef struct _messresponder { t_pd mr_pd; t_outlet *mr_outlet; } t_messresponder; typedef struct _message { t_text m_text; t_messresponder m_messresponder; t_glist *m_glist; t_clock *m_clock; } t_message; static t_class *messresponder_class; static void messresponder_bang(t_messresponder *x) { outlet_bang(x->mr_outlet); } static void messresponder_float(t_messresponder *x, t_float f) { outlet_float(x->mr_outlet, f); } static void messresponder_symbol(t_messresponder *x, t_symbol *s) { outlet_symbol(x->mr_outlet, s); } static void messresponder_list(t_messresponder *x, t_symbol *s, int argc, t_atom *argv) { outlet_list(x->mr_outlet, s, argc, argv); } static void messresponder_anything(t_messresponder *x, t_symbol *s, int argc, t_atom *argv) { outlet_anything(x->mr_outlet, s, argc, argv); } static void message_bang(t_message *x) { binbuf_eval(x->m_text.te_binbuf, &x->m_messresponder.mr_pd, 0, 0); } static void message_float(t_message *x, t_float f) { t_atom at; SETFLOAT(&at, f); binbuf_eval(x->m_text.te_binbuf, &x->m_messresponder.mr_pd, 1, &at); } static void message_symbol(t_message *x, t_symbol *s) { t_atom at; SETSYMBOL(&at, s); binbuf_eval(x->m_text.te_binbuf, &x->m_messresponder.mr_pd, 1, &at); } static void message_list(t_message *x, t_symbol *s, int argc, t_atom *argv) { binbuf_eval(x->m_text.te_binbuf, &x->m_messresponder.mr_pd, argc, argv); } static void message_set(t_message *x, t_symbol *s, int argc, t_atom *argv) { binbuf_clear(x->m_text.te_binbuf); binbuf_add(x->m_text.te_binbuf, argc, argv); glist_retext(x->m_glist, &x->m_text); } static void message_add2(t_message *x, t_symbol *s, int argc, t_atom *argv) { binbuf_add(x->m_text.te_binbuf, argc, argv); glist_retext(x->m_glist, &x->m_text); } static void message_add(t_message *x, t_symbol *s, int argc, t_atom *argv) { binbuf_add(x->m_text.te_binbuf, argc, argv); binbuf_addsemi(x->m_text.te_binbuf); glist_retext(x->m_glist, &x->m_text); } static void message_addcomma(t_message *x) { t_atom a; SETCOMMA(&a); binbuf_add(x->m_text.te_binbuf, 1, &a); glist_retext(x->m_glist, &x->m_text); } static void message_addsemi(t_message *x) { message_add(x, 0, 0, 0); } static void message_adddollar(t_message *x, t_floatarg f) { t_atom a; int n = f; if (n < 0) n = 0; SETDOLLAR(&a, n); binbuf_add(x->m_text.te_binbuf, 1, &a); glist_retext(x->m_glist, &x->m_text); } static void message_adddollsym(t_message *x, t_symbol *s) { t_atom a; char buf[MAXPDSTRING]; buf[0] = '$'; strncpy(buf+1, s->s_name, MAXPDSTRING-2); buf[MAXPDSTRING-1] = 0; SETDOLLSYM(&a, gensym(buf)); binbuf_add(x->m_text.te_binbuf, 1, &a); glist_retext(x->m_glist, &x->m_text); } static void message_click(t_message *x, t_floatarg xpos, t_floatarg ypos, t_floatarg shift, t_floatarg ctrl, t_floatarg alt) { if (glist_isvisible(x->m_glist)) { /* not zooming click width for now as it gets too fat */ t_rtext *y = glist_findrtext(x->m_glist, &x->m_text); char buf[MAXPDSTRING]; sprintf(buf, "%sR", rtext_gettag(y)); pdgui_vmess(0, "crs ri", glist_getcanvas(x->m_glist), "itemconfigure", buf, "-width", MESSAGE_CLICK_WIDTH); clock_delay(x->m_clock, 120); } message_float(x, 0); } static void message_tick(t_message *x) { if (glist_isvisible(x->m_glist)) { t_rtext *y = glist_findrtext(x->m_glist, &x->m_text); char buf[MAXPDSTRING]; sprintf(buf, "%sR", rtext_gettag(y)); pdgui_vmess(0, "crs ri", glist_getcanvas(x->m_glist), "itemconfigure", buf, "-width", glist_getzoom(x->m_glist)); } } static void message_free(t_message *x) { clock_free(x->m_clock); } void canvas_msg(t_glist *gl, t_symbol *s, int argc, t_atom *argv) { t_message *x = (t_message *)pd_new(message_class); x->m_messresponder.mr_pd = messresponder_class; x->m_messresponder.mr_outlet = outlet_new(&x->m_text, &s_float); x->m_text.te_width = 0; /* don't know it yet. */ x->m_text.te_type = T_MESSAGE; x->m_text.te_binbuf = binbuf_new(); x->m_glist = gl; x->m_clock = clock_new(x, (t_method)message_tick); if (argc > 1) { x->m_text.te_xpix = atom_getfloatarg(0, argc, argv); x->m_text.te_ypix = atom_getfloatarg(1, argc, argv); if (argc > 2) binbuf_restore(x->m_text.te_binbuf, argc-2, argv+2); glist_add(gl, &x->m_text.te_g); } else if (!glist_isvisible(gl)) post("unable to create stub message in closed canvas!"); else { int connectme, xpix, ypix, indx, nobj; canvas_howputnew(gl, &connectme, &xpix, &ypix, &indx, &nobj); pd_vmess(&gl->gl_pd, gensym("editmode"), "i", 1); x->m_text.te_xpix = xpix; x->m_text.te_ypix = ypix; glist_add(gl, &x->m_text.te_g); glist_noselect(gl); glist_select(gl, &x->m_text.te_g); gobj_activate(&x->m_text.te_g, gl, 1); if (connectme) canvas_connect(gl, indx, 0, nobj, 0); else canvas_startmotion(glist_getcanvas(gl)); canvas_undo_add(glist_getcanvas(gl), UNDO_CREATE, "create", (void *)canvas_undo_set_create(glist_getcanvas(gl))); } } /* for the needs of g_editor::glist_dofinderror() */ t_pd *message_get_responder(t_gobj *x) { if (pd_class(&x->g_pd) != message_class) return NULL; else return (t_pd *)&((t_message *)x)->m_messresponder.mr_pd; } /* ---------------------- the "atom" text item ------------------------ */ #define ATOM_LABELLEFT 0 #define ATOM_LABELRIGHT 1 #define ATOM_LABELUP 2 #define ATOM_LABELDOWN 3 #define A_LIST A_NULL /* fake atom type - use A_NULL for list 'flavor' */ typedef struct _gatom { t_text a_text; int a_flavor; /* A_FLOAT, A_SYMBOL, or A_LIST */ t_glist *a_glist; /* owning glist */ t_float a_toggle; /* value to toggle to */ t_float a_draghi; /* high end of drag range */ t_float a_draglo; /* low end of drag range */ t_symbol *a_label; /* symbol to show as label next to box */ t_symbol *a_symfrom; /* "receive" name -- bind ourselves to this */ t_symbol *a_symto; /* "send" name -- send to this on output */ t_binbuf *a_revertbuf; /* binbuf to revert to if typing canceled */ int a_dragindex; /* index of atom being dragged */ int a_fontsize; unsigned int a_shift:1; /* was shift key down when drag started? */ unsigned int a_wherelabel:2; /* 0-3 for left, right, above, below */ unsigned int a_grabbed:1; /* 1 if we've grabbed keyboard */ unsigned int a_doubleclicked:1; /* 1 if dragging from a double click */ t_symbol *a_expanded_to; /* a_symto after $0, $1, ... expansion */ } t_gatom; /* prepend "-" as necessary to avoid empty strings, so we can use them in Pd messages. */ static t_symbol *gatom_escapit(t_symbol *s) { if (!*s->s_name) return (gensym("-")); else if (*s->s_name == '-') { char shmo[100]; shmo[0] = '-'; strncpy(shmo+1, s->s_name, 99); shmo[99] = 0; return (gensym(shmo)); } else return (s); } /* undo previous operation: strip leading "-" if found. This is used both to restore send, etc, names when loading from a file, and to set them from the properties dialog. In the former case, since before version 0.52 '$" was aliases to "#", we also bash any "#" characters to "$". This is unnecessary when reading files saved from 0.52 or later, and really we should test for that and only bash when necessary, just in case someone wants to have a "#" in a name. */ static t_symbol *gatom_unescapit(t_symbol *s) { if (*s->s_name == '-') return (gensym(s->s_name+1)); else return (iemgui_raute2dollar(s)); } static void gatom_redraw(t_gobj *client, t_glist *glist) { t_gatom *x = (t_gatom *)client; if (glist->gl_editor) glist_retext(x->a_glist, &x->a_text); } static void gatom_senditup(t_gatom *x) { if (x->a_glist->gl_editor && gobj_shouldvis(&x->a_text.te_g, x->a_glist)) sys_queuegui(x, x->a_glist, gatom_redraw); } static t_atom *gatom_getatom(t_gatom *x) { int ac = binbuf_getnatom(x->a_text.te_binbuf); t_atom *av = binbuf_getvec(x->a_text.te_binbuf); if (x->a_flavor == A_FLOAT && (ac != 1 || av[0].a_type != A_FLOAT)) { binbuf_clear(x->a_text.te_binbuf); binbuf_addv(x->a_text.te_binbuf, "f", 0.); } else if (x->a_flavor == A_SYMBOL && (ac != 1 || av[0].a_type != A_SYMBOL)) { binbuf_clear(x->a_text.te_binbuf); binbuf_addv(x->a_text.te_binbuf, "s", &s_); } return (binbuf_getvec(x->a_text.te_binbuf)); } static void gatom_set(t_gatom *x, t_symbol *s, int argc, t_atom *argv) { t_atom *ap = gatom_getatom(x), oldatom; int changed = 0; if (!argc && x->a_flavor != A_LIST) return; if (x->a_flavor == A_FLOAT) { oldatom = *ap; ap->a_w.w_float = atom_getfloat(argv); /* github PR 791 by Dan Bornstein: treat "-0" as different from "0" and deal with NaNfoo all in one swipe by comparing bitwise: */ changed = memcmp(&ap->a_w.w_float, &oldatom.a_w.w_float, sizeof(t_float)); } else if (x->a_flavor == A_SYMBOL) { oldatom = *ap; ap->a_w.w_symbol = atom_getsymbol(argv), changed = (ap->a_w.w_symbol != oldatom.a_w.w_symbol); } else if (x->a_flavor == A_LIST) /* list */ { t_atom *av = binbuf_getvec(x->a_text.te_binbuf); int ac = binbuf_getnatom(x->a_text.te_binbuf), i; if (ac == argc) { for (i = 0; i < argc; i++) if ((argv[i].a_type != av[i].a_type) || (argv[i].a_type == A_FLOAT && argv[i].a_w.w_float != av[i].a_w.w_float) || (argv[i].a_type == A_SYMBOL && argv[i].a_w.w_symbol != av[i].a_w.w_symbol)) break; if (i == argc) goto nochange; } binbuf_clear(x->a_text.te_binbuf); binbuf_add(x->a_text.te_binbuf, argc, argv); av = binbuf_getvec(x->a_text.te_binbuf); for (i = 0; i < argc; i++) if (argv[i].a_type == A_POINTER) SETSYMBOL(&argv[i], gensym("(pointer)")); changed = 1; } if (changed) gatom_senditup(x); nochange: ; } static void gatom_bang(t_gatom *x) { t_atom *ap = gatom_getatom(x); if (x->a_flavor == A_FLOAT) { if (x->a_text.te_outlet) outlet_float(x->a_text.te_outlet, ap->a_w.w_float); if (*x->a_expanded_to->s_name && x->a_expanded_to->s_thing) { if (x->a_symto == x->a_symfrom) pd_error(x, "%s: atom with same send/receive name (infinite loop)", x->a_symto->s_name); else pd_float(x->a_expanded_to->s_thing, ap->a_w.w_float); } } else if (x->a_flavor == A_SYMBOL) { if (x->a_text.te_outlet) outlet_symbol(x->a_text.te_outlet, ap->a_w.w_symbol); if (*x->a_symto->s_name && x->a_expanded_to->s_thing) { if (x->a_symto == x->a_symfrom) pd_error(x, "%s: atom with same send/receive name (infinite loop)", x->a_symto->s_name); else pd_symbol(x->a_expanded_to->s_thing, ap->a_w.w_symbol); } } else /* list */ { int argc = binbuf_getnatom(x->a_text.te_binbuf), i; t_atom *argv = binbuf_getvec(x->a_text.te_binbuf); for (i = 0; i < argc; i++) if (argv[i].a_type != A_FLOAT && argv[i].a_type != A_SYMBOL) { pd_error(x, "list: only sends literal numbers and symbols"); return; } if (x->a_text.te_outlet) outlet_list(x->a_text.te_outlet, &s_list, argc, argv); if (*x->a_expanded_to->s_name && x->a_expanded_to->s_thing) { if (x->a_symto == x->a_symfrom) pd_error(x, "%s: atom with same send/receive name (infinite loop)", x->a_symto->s_name); else pd_list(x->a_expanded_to->s_thing, &s_list, argc, argv); } } } static void gatom_float(t_gatom *x, t_float f) { t_atom at; SETFLOAT(&at, f); gatom_set(x, 0, 1, &at); gatom_bang(x); } static void gatom_clipfloat(t_gatom *x, t_atom *ap, t_float f) { if (x->a_draglo != 0 || x->a_draghi != 0) { if (f < x->a_draglo) f = x->a_draglo; if (f > x->a_draghi) f = x->a_draghi; } ap->a_w.w_float = f; gatom_senditup(x); gatom_bang(x); } static void gatom_symbol(t_gatom *x, t_symbol *s) { t_atom at; SETSYMBOL(&at, s); gatom_set(x, 0, 1, &at); gatom_bang(x); } /* We need a list method because, since there's both an "inlet" and a "nofirstin" flag, the standard list behavior gets confused. */ static void gatom_list(t_gatom *x, t_symbol *s, int argc, t_atom *argv) { /* empty list is like a bang */ if (argc) gatom_set(x, s, argc, argv); gatom_bang(x); } static void gatom_reborder(t_gatom *x) { t_rtext *y = glist_findrtext(x->a_glist, &x->a_text); text_drawborder(&x->a_text, x->a_glist, rtext_gettag(y), rtext_width(y), rtext_height(y), 0); } void gatom_undarken(t_text *x) { if (x->te_type == T_ATOM) { ((t_gatom *)x)->a_doubleclicked = ((t_gatom *)x)->a_grabbed = 0; gatom_reborder((t_gatom *)x); } else bug("gatom_undarken"); } void gatom_key(void *z, t_symbol *keysym, t_floatarg f) { t_gatom *x = (t_gatom *)z; int c = f, bufsize, i; char *buf; t_atom *ap = gatom_getatom(x); t_rtext *t = glist_findrtext(x->a_glist, &x->a_text); if (c == 0 && !x->a_doubleclicked) { /* we're being notified that no more keys will come for this grab */ if (t == x->a_glist->gl_editor->e_textedfor) rtext_activate(t, 0); x->a_grabbed = 0; gatom_reborder(x); gatom_redraw(&x->a_text.te_g, x->a_glist); } else if (c == '\n') { x->a_doubleclicked = 0; if (t == x->a_glist->gl_editor->e_textedfor) { rtext_gettext(t, &buf, &bufsize); rtext_key(t, 0, gensym("End")); for (i = 0; i < 3; i++) if (buf[bufsize-i-1] != '.') break; while (i--) rtext_key(t, '\b', &s_); rtext_gettext(t, &buf, &bufsize); if (x->a_flavor == A_FLOAT) ap->a_w.w_float = atof(buf); else if (x->a_flavor == A_SYMBOL) ap->a_w.w_symbol = gensym(buf); else text_setto(&x->a_text, x->a_glist, buf, bufsize); rtext_activate(t, 0); } gatom_bang(x); gatom_senditup(x); } else { if (t != x->a_glist->gl_editor->e_textedfor) { rtext_activate(t, 1); rtext_key(t, '.', &s_); rtext_key(t, '.', &s_); rtext_key(t, '.', &s_); rtext_key(t, 0, gensym("Home")); } if (x->a_flavor == A_SYMBOL || x->a_flavor == A_LIST) rtext_key(t, c, keysym); /* insert the character */ else if (x->a_flavor == A_FLOAT && ((c >= '0' && c <= '9') || c == '.' || c == '-' || c == '+' || c == 'e' || c == 'E' || c == '\b')) rtext_key(t, c, keysym); /* insert the accepted characters */ } } static void gatom_motion(void *z, t_floatarg dx, t_floatarg dy, t_floatarg up) { t_gatom *x = (t_gatom *)z; if (up != 0) { t_rtext *t = glist_findrtext(x->a_glist, &x->a_text); rtext_retext(t); if (x->a_doubleclicked) /* double click - activate text on release */ rtext_activate(t, 1); } else { t_atom *ap; x->a_doubleclicked = 0; if (x->a_dragindex <0) return; if (dy == 0 || x->a_dragindex < 0 || x->a_dragindex >= binbuf_getnatom(x->a_text.te_binbuf) || binbuf_getvec(x->a_text.te_binbuf)[x->a_dragindex].a_type != A_FLOAT) return; ap = &binbuf_getvec(x->a_text.te_binbuf)[x->a_dragindex]; if (x->a_shift) { double nval = ap->a_w.w_float - 0.01 * dy; double trunc = 0.01 * (floor(100. * nval + 0.5)); if (trunc < nval + 0.0001 && trunc > nval - 0.0001) nval = trunc; gatom_clipfloat(x, ap, nval); } else { double nval = ap->a_w.w_float - dy; double trunc = 0.01 * (floor(100. * nval + 0.5)); if (trunc < nval + 0.0001 && trunc > nval - 0.0001) nval = trunc; trunc = floor(nval + 0.5); if (trunc < nval + 0.001 && trunc > nval - 0.001) nval = trunc; gatom_clipfloat(x, ap, nval); } } } int rtext_findatomfor(t_rtext *x, int xpos, int ypos); /* this is called when gatom is clicked on with patch in run mode. */ static int gatom_doclick(t_gobj *z, t_glist *gl, int xpos, int ypos, int shift, int alt, int dbl, int doit) { t_gatom *x = (t_gatom *)z; t_atom *ap = gatom_getatom(x); t_rtext *t; if (!doit) return (1); t = glist_findrtext(x->a_glist, &x->a_text); if (t == x->a_glist->gl_editor->e_textedfor) { rtext_mouse(t, xpos, ypos, (dbl ? RTEXT_DBL : RTEXT_DOWN)); x->a_glist->gl_editor->e_onmotion = MA_DRAGTEXT; x->a_glist->gl_editor->e_xwas = xpos; x->a_glist->gl_editor->e_ywas = ypos; return (1); } if (x->a_flavor == A_FLOAT) { if (x->a_text.te_width == 1) gatom_float(x, (ap->a_w.w_float == 0)); else { if (alt) { if (ap->a_w.w_float != 0) { x->a_toggle = ap->a_w.w_float; gatom_float(x, 0); } else gatom_float(x, x->a_toggle); } else { x->a_dragindex = 0; x->a_shift = shift; } } } else if (x->a_flavor == A_LIST) { int x1, y1, x2, y2, indx, argc = binbuf_getnatom(x->a_text.te_binbuf); t_atom *argv = binbuf_getvec(x->a_text.te_binbuf); gobj_getrect(z, gl, &x1, &y1, &x2, &y2); indx = rtext_findatomfor(t, xpos - x1, ypos - y1); if (indx >= 0 && indx < argc && argv[indx].a_type == A_FLOAT) { x->a_dragindex = indx; x->a_shift = shift; } else x->a_dragindex = -1; } x->a_grabbed = 1; x->a_doubleclicked = dbl; gatom_reborder(x); glist_grab(x->a_glist, &x->a_text.te_g, gatom_motion, gatom_key, xpos, ypos); return (1); } /* probably never used but included in case needed for compatibility */ static void gatom_click(t_gatom *x, t_floatarg xpos, t_floatarg ypos, t_floatarg shift, t_floatarg ctrl, t_floatarg alt) { pd_error(x, "gatom_click is obsolete and may be deleted in future"); gatom_doclick(&x->a_text.te_g, x->a_glist, xpos, ypos, shift, ctrl, 0, 1); } /* message back from dialog window */ static void gatom_param(t_gatom *x, t_symbol *sel, int argc, t_atom *argv) { t_float width = atom_getfloatarg(0, argc, argv); t_float draglo = atom_getfloatarg(1, argc, argv); t_float draghi = atom_getfloatarg(2, argc, argv); t_symbol *label = gatom_unescapit(atom_getsymbolarg(3, argc, argv)); t_float wherelabel = atom_getfloatarg(4, argc, argv); t_symbol *symfrom = gatom_unescapit(atom_getsymbolarg(5, argc, argv)); t_symbol *symto = gatom_unescapit(atom_getsymbolarg(6, argc, argv)); int newfont = atom_getfloatarg(7, argc, argv); t_atom undo[8]; int is_visible = glist_isvisible(x->a_glist); int should_visible = gobj_shouldvis((t_gobj*)x, x->a_glist); SETFLOAT (undo+0, x->a_text.te_width); SETFLOAT (undo+1, x->a_draglo); SETFLOAT (undo+2, x->a_draghi); SETSYMBOL(undo+3, gatom_escapit(x->a_label)); SETFLOAT (undo+4, x->a_wherelabel); SETSYMBOL(undo+5, gatom_escapit(x->a_symfrom)); SETSYMBOL(undo+6, gatom_escapit(x->a_symto)); SETFLOAT (undo+7, x->a_fontsize); pd_undo_set_objectstate(x->a_glist, (t_pd*)x, gensym("param"), 8, undo, argc, argv); if(is_visible) gobj_vis(&x->a_text.te_g, x->a_glist, 0); if (!*symfrom->s_name && *x->a_symfrom->s_name) inlet_new(&x->a_text, &x->a_text.te_pd, 0, 0); else if (*symfrom->s_name && !*x->a_symfrom->s_name && x->a_text.te_inlet) { canvas_deletelinesforio(x->a_glist, &x->a_text, x->a_text.te_inlet, 0); inlet_free(x->a_text.te_inlet); } if (!*symto->s_name && *x->a_symto->s_name) outlet_new(&x->a_text, 0); else if (*symto->s_name && !*x->a_symto->s_name && x->a_text.te_outlet) { canvas_deletelinesforio(x->a_glist, &x->a_text, 0, x->a_text.te_outlet); outlet_free(x->a_text.te_outlet); } if (draglo >= draghi) draglo = draghi = 0; x->a_draglo = draglo; x->a_draghi = draghi; if (width < 0) width = 4; else if (width > 1000) width = 1000; x->a_text.te_width = width; x->a_wherelabel = ((int)wherelabel & 3); x->a_label = label; x->a_fontsize = newfont; if (*x->a_symfrom->s_name) pd_unbind(&x->a_text.te_pd, canvas_realizedollar(x->a_glist, x->a_symfrom)); x->a_symfrom = symfrom; if (*x->a_symfrom->s_name) pd_bind(&x->a_text.te_pd, canvas_realizedollar(x->a_glist, x->a_symfrom)); x->a_symto = symto; x->a_expanded_to = canvas_realizedollar(x->a_glist, x->a_symto); if(is_visible && should_visible) gobj_vis(&x->a_text.te_g, x->a_glist, 1); canvas_dirty(x->a_glist, 1); if(is_visible) canvas_fixlinesfor(x->a_glist, (t_text*)x); /* glist_retext(x->a_glist, &x->a_text); */ } static int gatom_fontsize(t_gatom *x) { return (x->a_fontsize ? x->a_fontsize : glist_getfont(x->a_glist)); } /* ---------------- gatom-specific widget functions --------------- */ static void gatom_getwherelabel(t_gatom *x, t_glist *glist, int *xp, int *yp) { int x1, y1, x2, y2; int zoom = glist_getzoom(glist), fontsize = gatom_fontsize(x); text_getrect(&x->a_text.te_g, glist, &x1, &y1, &x2, &y2); if (x->a_wherelabel == ATOM_LABELLEFT) { *xp = x1 - 3 * zoom - ( (int)strlen(canvas_realizedollar(x->a_glist, x->a_label)->s_name) * sys_zoomfontwidth(fontsize, zoom, 0)); *yp = y1 + 2 * zoom; } else if (x->a_wherelabel == ATOM_LABELRIGHT) { *xp = x2 + 2 * zoom; *yp = y1 + 2 * zoom; } else if (x->a_wherelabel == ATOM_LABELUP) { *xp = x1 - 1 * zoom; *yp = y1 - 1 * zoom - sys_zoomfontheight(fontsize, zoom, 0); } else { *xp = x1 - 1 * zoom; *yp = y2 + 3 * zoom; } } static void gatom_displace(t_gobj *z, t_glist *glist, int dx, int dy) { t_gatom *x = (t_gatom*)z; text_displace(z, glist, dx, dy); if (glist_isvisible(glist)) { char buf[MAXPDSTRING]; sprintf(buf, "%p.l", x); pdgui_vmess(0, "crs ii", glist_getcanvas(glist), "move", buf, dx * glist->gl_zoom, dy * glist->gl_zoom); } } static void gatom_vis(t_gobj *z, t_glist *glist, int vis) { t_gatom *x = (t_gatom*)z; text_vis(z, glist, vis); if (*x->a_label->s_name) { char buf[MAXPDSTRING]; sprintf(buf, "%p.l", x); if (vis) { int x1, y1; char *tags[] = { buf, "label", "text" }; gatom_getwherelabel(x, glist, &x1, &y1); pdgui_vmess("pdtk_text_new", "cS ff s ir", glist_getcanvas(glist), 3, tags, (double)x1, (double)y1, canvas_realizedollar(x->a_glist, x->a_label)->s_name, gatom_fontsize(x) * glist_getzoom(glist), "black"); } else pdgui_vmess(0, "crs", glist_getcanvas(glist), "delete", buf); } } void canvas_atom(t_glist *gl, t_atomtype type, t_symbol *s, int argc, t_atom *argv) { t_gatom *x = (t_gatom *)pd_new(gatom_class); x->a_text.te_width = 0; /* don't know it yet. */ x->a_text.te_type = T_ATOM; x->a_text.te_binbuf = binbuf_new(); x->a_glist = gl; x->a_flavor = type; x->a_toggle = 1; x->a_draglo = 0; x->a_draghi = 0; x->a_wherelabel = 0; x->a_label = &s_; x->a_symfrom = &s_; x->a_symto = x->a_expanded_to = &s_; x->a_grabbed = 0; x->a_revertbuf = 0; x->a_fontsize = 0; (void)gatom_getatom(x); /* this forces initialization of binbuf */ if (argc > 1) /* create from file. x, y, width, low-range, high-range, flags, label, receive-name, send-name, fontsize */ { x->a_text.te_xpix = atom_getfloatarg(0, argc, argv); x->a_text.te_ypix = atom_getfloatarg(1, argc, argv); x->a_text.te_width = atom_getfloatarg(2, argc, argv); /* sanity check because some very old patches have trash in this field... remove this in 2003 or so: */ if (x->a_text.te_width < 0 || x->a_text.te_width > 500) x->a_text.te_width = 4; x->a_draglo = atom_getfloatarg(3, argc, argv); x->a_draghi = atom_getfloatarg(4, argc, argv); x->a_wherelabel = (((int)atom_getfloatarg(5, argc, argv)) & 3); x->a_label = gatom_unescapit(atom_getsymbolarg(6, argc, argv)); x->a_symfrom = gatom_unescapit(atom_getsymbolarg(7, argc, argv)); if (*x->a_symfrom->s_name) pd_bind(&x->a_text.te_pd, canvas_realizedollar(x->a_glist, x->a_symfrom)); x->a_symto = gatom_unescapit(atom_getsymbolarg(8, argc, argv)); x->a_expanded_to = canvas_realizedollar(x->a_glist, x->a_symto); if (x->a_symto == &s_) outlet_new(&x->a_text, x->a_flavor == A_FLOAT ? &s_float: &s_symbol); if (x->a_symfrom == &s_) inlet_new(&x->a_text, &x->a_text.te_pd, 0, 0); x->a_fontsize = atom_getfloatarg(9, argc, argv); glist_add(gl, &x->a_text.te_g); } else /* from menu - use default settings */ { int connectme, xpix, ypix, indx, nobj; canvas_howputnew(gl, &connectme, &xpix, &ypix, &indx, &nobj); outlet_new(&x->a_text, x->a_flavor == A_FLOAT ? &s_float: &s_symbol); inlet_new(&x->a_text, &x->a_text.te_pd, 0, 0); pd_vmess(&gl->gl_pd, gensym("editmode"), "i", 1); x->a_text.te_xpix = xpix; x->a_text.te_ypix = ypix; x->a_text.te_width = (x->a_flavor == A_FLOAT ? 5 : (x->a_flavor == A_SYMBOL ? 10 : 20)); glist_add(gl, &x->a_text.te_g); glist_noselect(gl); glist_select(gl, &x->a_text.te_g); if (connectme) canvas_connect(gl, indx, 0, nobj, 0); else canvas_startmotion(glist_getcanvas(gl)); canvas_undo_add(glist_getcanvas(gl), UNDO_CREATE, "create", (void *)canvas_undo_set_create(glist_getcanvas(gl))); } } void canvas_floatatom(t_glist *gl, t_symbol *s, int argc, t_atom *argv) { canvas_atom(gl, A_FLOAT, s, argc, argv); } void canvas_symbolatom(t_glist *gl, t_symbol *s, int argc, t_atom *argv) { canvas_atom(gl, A_SYMBOL, s, argc, argv); } void canvas_listbox(t_glist *gl, t_symbol *s, int argc, t_atom *argv) { canvas_atom(gl, A_LIST, s, argc, argv); } static void gatom_free(t_gatom *x) { if (*x->a_symfrom->s_name) pd_unbind(&x->a_text.te_pd, canvas_realizedollar(x->a_glist, x->a_symfrom)); pdgui_stub_deleteforkey(x); sys_unqueuegui(x); } static void gatom_properties(t_gobj *z, t_glist *owner) { t_gatom *x = (t_gatom *)z; pdgui_stub_vnew(&x->a_text.te_pd, "pdtk_gatom_dialog", x, "i ff i sss i", x->a_text.te_width, x->a_draglo, x->a_draghi, x->a_wherelabel, gatom_escapit(x->a_label)->s_name, gatom_escapit(x->a_symfrom)->s_name, gatom_escapit(x->a_symto)->s_name, x->a_fontsize); } /* -------------------- widget behavior for text objects ------------ */ static void text_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2) { t_text *x = (t_text *)z; int width, height, iscomment = (x->te_type == T_TEXT); t_float x1, y1, x2, y2; if (glist->gl_editor && glist->gl_editor->e_rtext) { t_rtext *y = glist_findrtext(glist, x); width = rtext_width(y); height = rtext_height(y) - (iscomment << 1); } /* for number boxes, we know width and height a priori, and should report them here so that graphs can get swelled to fit. */ else if (x->te_type == T_ATOM && x->te_width > 0) { width = (x->te_width > 0 ? x->te_width : 6) * glist_fontwidth(glist); height = glist_fontheight(glist); if (glist_getzoom(glist) > 1) { /* zoom margins */ width += ATOM_RMARGIN * glist_getzoom(glist); height += ATOM_BMARGIN * glist_getzoom(glist); } else { width += ATOM_RMARGIN; height += ATOM_BMARGIN; } } /* if we're invisible we don't know our size so we just lie about it. This is called on invisible boxes to establish order of inlets and possibly other reasons. To find out if the box is visible we can't just check the "vis" flag because we might be within the vis() routine and not have set that yet. So we check directly whether the "rtext" list has been built. LATER reconsider when "vis" flag should be on and off? */ else width = height = 10; x1 = text_xpix(x, glist); y1 = text_ypix(x, glist); x2 = x1 + width; y2 = y1 + height; y1 += iscomment; *xp1 = x1; *yp1 = y1; *xp2 = x2; *yp2 = y2; } static void text_displace(t_gobj *z, t_glist *glist, int dx, int dy) { t_text *x = (t_text *)z; x->te_xpix += dx; x->te_ypix += dy; if (glist_isvisible(glist)) { t_rtext *y = glist_findrtext(glist, x); rtext_displace(y, glist->gl_zoom * dx, glist->gl_zoom * dy); text_drawborder(x, glist, rtext_gettag(y), rtext_width(y), rtext_height(y), 0); canvas_fixlinesfor(glist, x); } } static void text_select(t_gobj *z, t_glist *glist, int state) { t_text *x = (t_text *)z; t_rtext *y = glist_findrtext(glist, x); rtext_select(y, state); if (glist_isvisible(glist) && gobj_shouldvis(&x->te_g, glist)) { char buf[MAXPDSTRING]; sprintf(buf, "%sR", rtext_gettag(y)); pdgui_vmess(0, "crs rr", glist, "itemconfigure", buf, "-fill", (state? "blue" : "black")); } } static void text_activate(t_gobj *z, t_glist *glist, int state) { t_text *x = (t_text *)z; t_rtext *y = glist_findrtext(glist, x); if (z->g_pd != gatom_class) rtext_activate(y, state); } static void text_delete(t_gobj *z, t_glist *glist) { t_text *x = (t_text *)z; canvas_deletelinesfor(glist, x); } static void text_vis(t_gobj *z, t_glist *glist, int vis) { t_text *x = (t_text *)z; if (vis) { if (gobj_shouldvis(&x->te_g, glist)) { t_rtext *y = glist_findrtext(glist, x); text_drawborder(x, glist, rtext_gettag(y), rtext_width(y), rtext_height(y), 1); rtext_draw(y); } } else { t_rtext *y = glist_findrtext(glist, x); if (gobj_shouldvis(&x->te_g, glist)) { text_eraseborder(x, glist, rtext_gettag(y)); rtext_erase(y); } } } static int text_click(t_gobj *z, struct _glist *glist, int xpix, int ypix, int shift, int alt, int dbl, int doit) { t_text *x = (t_text *)z; if (x->te_type == T_OBJECT) { t_symbol *clicksym = gensym("click"); if (zgetfn(&x->te_pd, clicksym)) { if (doit) pd_vmess(&x->te_pd, clicksym, "fffff", (double)xpix, (double)ypix, (double)shift, (double)0, (double)alt); return (1); } else return (0); } else if (x->te_type == T_MESSAGE) { if (doit) message_click((t_message *)x, (t_floatarg)xpix, (t_floatarg)ypix, (t_floatarg)shift, (t_floatarg)0, (t_floatarg)alt); return (1); } else return (0); } void canvas_statesavers_doit(t_glist *x, t_binbuf *b); void text_save(t_gobj *z, t_binbuf *b) { t_text *x = (t_text *)z; if (x->te_type == T_OBJECT) { /* if we have a "saveto" method, and if we don't happen to be a canvas that's an abstraction, the saveto method does the work */ if (zgetfn(&x->te_pd, gensym("saveto")) && !((pd_class(&x->te_pd) == canvas_class) && (canvas_isabstraction((t_canvas *)x) || canvas_istable((t_canvas *)x)))) { mess1(&x->te_pd, gensym("saveto"), b); binbuf_addv(b, "ssii", gensym("#X"), gensym("restore"), (int)x->te_xpix, (int)x->te_ypix); binbuf_addbinbuf(b, x->te_binbuf); binbuf_addv(b, ";"); if (x->te_width) binbuf_addv(b, "ssi;", gensym("#X"), gensym("f"), (int)x->te_width); } else /* otherwise just save the text */ { binbuf_addv(b, "ssii", gensym("#X"), gensym("obj"), (int)x->te_xpix, (int)x->te_ypix); binbuf_addbinbuf(b, x->te_binbuf); if (x->te_width) binbuf_addv(b, ",si", gensym("f"), (int)x->te_width); binbuf_addv(b, ";"); } /* if an abstraction, give it a chance to save state */ if (pd_class(&x->te_pd) == canvas_class && canvas_isabstraction((t_canvas *)x)) canvas_statesavers_doit((t_glist *)x, b); } else if (x->te_type == T_MESSAGE) { binbuf_addv(b, "ssii", gensym("#X"), gensym("msg"), (int)x->te_xpix, (int)x->te_ypix); binbuf_addbinbuf(b, x->te_binbuf); if (x->te_width) binbuf_addv(b, ",si", gensym("f"), (int)x->te_width); binbuf_addv(b, ";"); } else if (x->te_type == T_ATOM) { t_atomtype t = ((t_gatom *)x)->a_flavor; t_symbol *sel = (t == A_SYMBOL ? gensym("symbolatom") : (t == A_FLOAT ? gensym("floatatom") : gensym("listbox"))); t_symbol *label = gatom_escapit(((t_gatom *)x)->a_label); t_symbol *symfrom = gatom_escapit(((t_gatom *)x)->a_symfrom); t_symbol *symto = gatom_escapit(((t_gatom *)x)->a_symto); binbuf_addv(b, "ssiiifffsssf;", gensym("#X"), sel, (int)x->te_xpix, (int)x->te_ypix, (int)x->te_width, (double)((t_gatom *)x)->a_draglo, (double)((t_gatom *)x)->a_draghi, (double)((t_gatom *)x)->a_wherelabel, label, symfrom, symto, (double)((t_gatom *)x)->a_fontsize); } else { binbuf_addv(b, "ssii", gensym("#X"), gensym("text"), (int)x->te_xpix, (int)x->te_ypix); binbuf_addbinbuf(b, x->te_binbuf); if (x->te_width) binbuf_addv(b, ",si", gensym("f"), (int)x->te_width); binbuf_addv(b, ";"); } } /* this one is for everyone but "gatoms"; it's imposed in m_class.c */ const t_widgetbehavior text_widgetbehavior = { text_getrect, text_displace, text_select, text_activate, text_delete, text_vis, text_click, }; static const t_widgetbehavior gatom_widgetbehavior = { text_getrect, gatom_displace, text_select, text_activate, text_delete, gatom_vis, gatom_doclick, }; /* -------------------- the "text" class ------------ */ /* draw inlets and outlets for a text object or for a graph. */ void glist_drawiofor(t_glist *glist, t_object *ob, int firsttime, const char *tag, int x1, int y1, int x2, int y2) { int n = obj_noutlets(ob), nplus = (n == 1 ? 1 : n-1), i; int width = x2 - x1; int iow = IOWIDTH * glist->gl_zoom; int ih = IHEIGHT * glist->gl_zoom, oh = OHEIGHT * glist->gl_zoom; char *tags[2]; char tagbuf[128]; /* draw over border, so assume border width = 1 pixel * glist->gl_zoom */ for (i = 0; i < n; i++) { int onset = x1 + (width - iow) * i / nplus; sprintf(tagbuf, "%so%d", tag, i); tags[0] = tagbuf; tags[1] = "outlet"; if (firsttime) pdgui_vmess(0, "crr iiii rS rr", glist_getcanvas(glist), "create", "rectangle", onset, y2 - oh + glist->gl_zoom, onset + iow, y2, "-tags", (int)(sizeof(tags)/sizeof(*tags)), tags, "-fill", "black"); else pdgui_vmess(0, "crs iiii", glist_getcanvas(glist), "coords", tagbuf, onset, y2 - oh + glist->gl_zoom, onset + iow, y2); } n = obj_ninlets(ob); nplus = (n == 1 ? 1 : n-1); for (i = 0; i < n; i++) { int onset = x1 + (width - iow) * i / nplus; sprintf(tagbuf, "%si%d", tag, i); tags[0] = tagbuf; tags[1] = "inlet"; if (firsttime) pdgui_vmess(0, "crr iiii rS rr", glist_getcanvas(glist), "create", "rectangle", onset, y1, onset + iow, y1 + ih - glist->gl_zoom, "-tags", (int)(sizeof(tags)/sizeof(*tags)), tags, "-fill", "black"); else pdgui_vmess(0, "crs iiii", glist_getcanvas(glist), "coords", tagbuf, onset, y1, onset + iow, y1 + ih - glist->gl_zoom); } } void text_drawborder(t_text *x, t_glist *glist, const char *tag, int width2, int height2, int firsttime) { t_object *ob; int x1, y1, x2, y2, width, height, corner; char tagR[128]; sprintf(tagR, "%sR", tag); text_getrect(&x->te_g, glist, &x1, &y1, &x2, &y2); width = x2 - x1; height = y2 - y1; if (x->te_type == T_OBJECT) { char *pattern = ((pd_class(&x->te_pd) == text_class) ? "-" : "\"\""); char *tags[] = {tagR, "obj"}; if (firsttime) pdgui_vmess(0, "crr iiiiiiiiii rr ri rr rS", glist_getcanvas(glist), "create", "line", x1, y1, x2, y1, x2, y2, x1, y2, x1, y1, "-dash", pattern, "-width", glist->gl_zoom, "-capstyle", "projecting", "-tags", 2, tags); else { pdgui_vmess(0, "crs iiiiiiiiii", glist_getcanvas(glist), "coords", tagR, x1, y1, x2, y1, x2, y2, x1, y2, x1, y1); pdgui_vmess(0, "crs rr", glist_getcanvas(glist), "itemconfigure", tagR, "-dash", pattern); } } else if (x->te_type == T_MESSAGE) { char *tags[] = {tagR, "msg"}; corner = ((y2-y1)/4); if (corner > 10*glist->gl_zoom) corner = 10*glist->gl_zoom; /* looks bad if too big */ if (firsttime) pdgui_vmess(0, "crr iiiiiiiiiiiiii ri rr rS", glist_getcanvas(glist), "create", "line", x1, y1, x2+corner, y1, x2, y1+corner, x2, y2-corner, x2+corner, y2, x1, y2, x1, y1, "-width", glist->gl_zoom, "-capstyle", "projecting", "-tags", 2, tags); else pdgui_vmess(0, "crs iiiiiiiiiiiiii", glist_getcanvas(glist), "coords", tagR, x1, y1, x2+corner, y1, x2, y1+corner, x2, y2-corner, x2+corner, y2, x1, y2, x1, y1); } else if (x->te_type == T_ATOM && (((t_gatom *)x)->a_flavor == A_FLOAT || ((t_gatom *)x)->a_flavor == A_SYMBOL)) { /* number or symbol */ int grabbed = glist->gl_zoom * ((t_gatom *)x)->a_grabbed; int x1p = x1 + grabbed, y1p = y1 + grabbed; char *tags[] = {tagR, "atom"}; corner = ((y2-y1)/4); if (firsttime) pdgui_vmess(0, "crr iiiiiiiiiiii ri rr rS", glist_getcanvas(glist), "create", "line", x1p, y1p, x2-corner, y1p, x2, y1p+corner, x2, y2, x1p, y2, x1p, y1p, "-width", glist->gl_zoom+grabbed, "-capstyle", "projecting", "-tags", 2, tags); else { pdgui_vmess(0, "crs iiiiiiiiiiii", glist_getcanvas(glist), "coords", tagR, x1p, y1p, x2-corner, y1p, x2, y1p+corner, x2, y2, x1p, y2, x1p, y1p); pdgui_vmess(0, "crs ri", glist_getcanvas(glist), "itemconfigure", tagR, "-width", glist->gl_zoom+grabbed); } } else if (x->te_type == T_ATOM ) /* list (ATOM but not float or symbol) */ { int grabbed = glist->gl_zoom * ((t_gatom *)x)->a_grabbed; int x1p = x1 + grabbed, y1p = y1 + grabbed; char *tags[] = {tagR, "atom"}; corner = ((y2-y1)/4); if (firsttime) pdgui_vmess(0, "crr iiiiiiiiiiiiii ri rr rS", glist_getcanvas(glist), "create", "line", x1p, y1p, x2-corner, y1p, x2, y1p+corner, x2, y2-corner, x2-corner, y2, x1p, y2, x1p, y1p, "-width", glist->gl_zoom+grabbed, "-capstyle", "projecting", "-tags", 2, tags); else { pdgui_vmess(0, "crs iiiiiiiiiiiiii", glist_getcanvas(glist), "coords", tagR, x1p,y1p, x2-corner,y1p, x2,y1p+corner, x2,y2-corner, x2-corner,y2, x1p,y2, x1p,y1p); pdgui_vmess(0, "crs ri", glist_getcanvas(glist), "itemconfigure", tagR, "-width", glist->gl_zoom+grabbed); } } /* for comments, just draw a bar on RHS if unlocked; when a visible canvas is unlocked we have to call this anew on all comments, and when locked we erase them all via the annoying "commentbar" tag. */ else if (x->te_type == T_TEXT && glist->gl_edit) { char *tags[] = {tagR, "commentbar"}; if (firsttime) pdgui_vmess(0, "crr iiii rS", glist_getcanvas(glist), "create", "line", x2, y1, x2, y2, "-tags", 2, tags); else pdgui_vmess(0, "crs iiii", glist_getcanvas(glist), "coords", tagR, x2, y1, x2, y2); } /* draw inlets/outlets */ if ((ob = pd_checkobject(&x->te_pd))) glist_drawiofor(glist, ob, firsttime, tag, x1, y1, x2, y2); if (firsttime) /* raise cords over everything else */ pdgui_vmess(0, "crr", glist_getcanvas(glist), "raise", "cord"); } void glist_eraseiofor(t_glist *glist, t_object *ob, const char *tag) { int i, n; n = obj_noutlets(ob); char tagbuf[MAXPDSTRING]; for (i = 0; i < n; i++) { sprintf(tagbuf, "%so%d", tag, i); pdgui_vmess(0, "crs", glist_getcanvas(glist), "delete", tagbuf); } n = obj_ninlets(ob); for (i = 0; i < n; i++) { sprintf(tagbuf, "%si%d", tag, i); pdgui_vmess(0, "crs", glist_getcanvas(glist), "delete", tagbuf); } } void text_eraseborder(t_text *x, t_glist *glist, const char *tag) { char tagbuf[MAXPDSTRING]; if (x->te_type == T_TEXT && !glist->gl_edit) return; sprintf(tagbuf, "%sR", tag); pdgui_vmess(0, "crs", glist_getcanvas(glist), "delete", tagbuf); glist_eraseiofor(glist, x, tag); } /* change text; if T_OBJECT, remake it. */ void text_setto(t_text *x, t_glist *glist, const char *buf, int bufsize) { int pos = glist_getindex(glist_getcanvas(glist), &x->te_g); if (x->te_type == T_OBJECT) { t_binbuf *b = binbuf_new(); int natom1, natom2, widthwas = x->te_width; t_atom *vec1, *vec2; binbuf_text(b, buf, bufsize); natom1 = binbuf_getnatom(x->te_binbuf); vec1 = binbuf_getvec(x->te_binbuf); natom2 = binbuf_getnatom(b); vec2 = binbuf_getvec(b); /* special case: if pd args change just pass the message on. */ if (natom1 >= 1 && natom2 >= 1 && vec1[0].a_type == A_SYMBOL && !strcmp(vec1[0].a_w.w_symbol->s_name, "pd") && vec2[0].a_type == A_SYMBOL && !strcmp(vec2[0].a_w.w_symbol->s_name, "pd")) { canvas_undo_add(glist_getcanvas(glist), UNDO_RECREATE, "recreate", (void *)canvas_undo_set_recreate(glist_getcanvas(glist), &x->te_g, pos)); typedmess(&x->te_pd, gensym("rename"), natom2-1, vec2+1); binbuf_free(x->te_binbuf); x->te_binbuf = b; } else /* normally, just destroy the old one and make a new one. */ { int xwas = x->te_xpix, ywas = x->te_ypix; canvas_undo_add(glist_getcanvas(glist), UNDO_RECREATE, "recreate", (void *)canvas_undo_set_recreate(glist_getcanvas(glist), &x->te_g, pos)); glist_delete(glist, &x->te_g); canvas_objtext(glist, xwas, ywas, widthwas, 0, b); canvas_restoreconnections(glist_getcanvas(glist)); /* if it's an abstraction loadbang it here */ if (pd_this->pd_newest) { if (pd_class(pd_this->pd_newest) == canvas_class) canvas_loadbang((t_canvas *)pd_this->pd_newest); else if (zgetfn(pd_this->pd_newest, gensym("loadbang"))) vmess(pd_this->pd_newest, gensym("loadbang"), "f", LB_LOAD); } } /* if we made a new "pd" or changed a window name, update window list */ if (natom2 >= 1 && vec2[0].a_type == A_SYMBOL && !strcmp(vec2[0].a_w.w_symbol->s_name, "pd")) canvas_updatewindowlist(); } else { canvas_undo_add(glist_getcanvas(glist), UNDO_RECREATE, "recreate", (void *)canvas_undo_set_recreate(glist_getcanvas(glist), &x->te_g, pos)); binbuf_text(x->te_binbuf, buf, bufsize); } } /* this gets called when a message gets sent to an object whose creation failed, presumably because of loading a patch with a missing extern or abstraction */ static void text_anything(t_text *x, t_symbol *s, int argc, t_atom *argv) { } void text_getfont(t_text *x, t_glist *thisglist, int *fwidthp, int *fheightp, int *guifsize) { int font, zoom; t_glist *gl; if (pd_class(&x->te_pd) == canvas_class && ((t_glist *)(x))->gl_isgraph && ((t_glist *)(x))->gl_goprect) gl = (t_glist *)(x); else gl = thisglist; font = glist_getfont(gl); zoom = glist_getzoom(gl); /* override if atom box has its own specified font size */ if (x->te_type == T_ATOM && ((t_gatom *)x)->a_fontsize > 0) font = ((t_gatom *)x)->a_fontsize; *fwidthp = sys_zoomfontwidth(font, zoom, 0); *fheightp = sys_zoomfontheight(font, zoom, 0); *guifsize = sys_hostfontsize(font, zoom); } /* *fontwidthp = glist_fontwidth((t_glist *)(x->x_text)); fontheightp = glist_fontheight((t_glist *)(x->x_text)); *guifontsizep = sys_hostfontsize(font, glist_getzoom(x->x_glist)); */ void g_text_setup(void) { text_class = class_new(gensym("text"), 0, 0, sizeof(t_text), CLASS_NOINLET | CLASS_PATCHABLE, 0); class_addanything(text_class, text_anything); message_class = class_new(gensym("message"), 0, (t_method)message_free, sizeof(t_message), CLASS_PATCHABLE, 0); class_addbang(message_class, message_bang); class_addfloat(message_class, message_float); class_addsymbol(message_class, message_symbol); class_addlist(message_class, message_list); class_addanything(message_class, message_list); class_addmethod(message_class, (t_method)message_click, gensym("click"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); class_addmethod(message_class, (t_method)message_set, gensym("set"), A_GIMME, 0); class_addmethod(message_class, (t_method)message_add, gensym("add"), A_GIMME, 0); class_addmethod(message_class, (t_method)message_add2, gensym("add2"), A_GIMME, 0); class_addmethod(message_class, (t_method)message_addcomma, gensym("addcomma"), 0); class_addmethod(message_class, (t_method)message_addsemi, gensym("addsemi"), 0); class_addmethod(message_class, (t_method)message_adddollar, gensym("adddollar"), A_FLOAT, 0); class_addmethod(message_class, (t_method)message_adddollsym, gensym("adddollsym"), A_SYMBOL, 0); messresponder_class = class_new(gensym("messresponder"), 0, 0, sizeof(t_text), CLASS_PD, 0); class_addbang(messresponder_class, messresponder_bang); class_addfloat(messresponder_class, (t_method) messresponder_float); class_addsymbol(messresponder_class, messresponder_symbol); class_addlist(messresponder_class, messresponder_list); class_addanything(messresponder_class, messresponder_anything); gatom_class = class_new(gensym("gatom"), 0, (t_method)gatom_free, sizeof(t_gatom), CLASS_NOINLET | CLASS_PATCHABLE, 0); class_addbang(gatom_class, gatom_bang); class_addfloat(gatom_class, gatom_float); class_addsymbol(gatom_class, gatom_symbol); class_addlist(gatom_class, gatom_list); class_addmethod(gatom_class, (t_method)gatom_set, gensym("set"), A_GIMME, 0); class_addmethod(gatom_class, (t_method)gatom_click, gensym("click"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); class_addmethod(gatom_class, (t_method)gatom_param, gensym("param"), A_GIMME, 0); class_setwidget(gatom_class, &gatom_widgetbehavior); class_setpropertiesfn(gatom_class, gatom_properties); class_sethelpsymbol(gatom_class, gensym("gui-boxes")); } ================================================ FILE: libs/libpd/pure-data/src/g_toggle.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */ /* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */ #include #include #include "m_pd.h" #include "g_all_guis.h" /* --------------- tgl gui-toggle ------------------------- */ t_widgetbehavior toggle_widgetbehavior; static t_class *toggle_class; /* widget helper functions */ #define toggle_draw_io 0 void toggle_draw_config(t_toggle* x, t_glist* glist) { const int zoom = IEMGUI_ZOOM(x); t_iemgui *iemgui = &x->x_gui; t_canvas *canvas = glist_getcanvas(glist); int xpos = text_xpix(&x->x_gui.x_obj, glist); int ypos = text_ypix(&x->x_gui.x_obj, glist); int iow = IOWIDTH * zoom, ioh = IEM_GUI_IOHEIGHT * zoom; int crossw = 1, w = x->x_gui.x_w / zoom; int col = x->x_on ? x->x_gui.x_fcol : x->x_gui.x_bcol; char tag[128]; t_atom fontatoms[3]; SETSYMBOL(fontatoms+0, gensym(iemgui->x_font)); SETFLOAT (fontatoms+1, -iemgui->x_fontsize*zoom); SETSYMBOL(fontatoms+2, gensym(sys_fontweight)); if(w >= 30) crossw = 2; if(w >= 60) crossw = 3; crossw *= zoom; sprintf(tag, "%pBASE", x); pdgui_vmess(0, "crs iiii", canvas, "coords", tag, xpos, ypos, xpos + x->x_gui.x_w, ypos + x->x_gui.x_h); pdgui_vmess(0, "crs ri rk", canvas, "itemconfigure", tag, "-width", zoom, "-fill", x->x_gui.x_bcol); sprintf(tag, "%pX1", x); pdgui_vmess(0, "crs iiii", canvas, "coords", tag, xpos + crossw + zoom, ypos + crossw + zoom, xpos + x->x_gui.x_w - crossw - zoom, ypos + x->x_gui.x_h - crossw - zoom); pdgui_vmess(0, "crs ri rk", canvas, "itemconfigure", tag, "-width", crossw, "-fill", col); sprintf(tag, "%pX2", x); pdgui_vmess(0, "crs iiii", canvas, "coords", tag, xpos + crossw + zoom, ypos + x->x_gui.x_h - crossw - zoom, xpos + x->x_gui.x_w - crossw - zoom, ypos + crossw + zoom); pdgui_vmess(0, "crs ri rk", canvas, "itemconfigure", tag, "-width", crossw, "-fill", col); sprintf(tag, "%pLABEL", x); pdgui_vmess(0, "crs ii", canvas, "coords", tag, xpos + x->x_gui.x_ldx * zoom, ypos + x->x_gui.x_ldy * zoom); pdgui_vmess(0, "crs rA rk", canvas, "itemconfigure", tag, "-font", 3, fontatoms, "-fill", (x->x_gui.x_fsf.x_selected ? IEM_GUI_COLOR_SELECTED : x->x_gui.x_lcol)); iemgui_dolabel(x, &x->x_gui, x->x_gui.x_lab, 1); } void toggle_draw_new(t_toggle *x, t_glist *glist) { /* create the widgets (but don't configure them yet) */ t_canvas *canvas = glist_getcanvas(glist); char tag[128], tag_object[128]; char*tags[] = {tag_object, tag, "label", "text"}; sprintf(tag_object, "%pOBJ", x); sprintf(tag, "%pBASE", x); pdgui_vmess(0, "crr iiii rS", canvas, "create", "rectangle", 0, 0, 0, 0, "-tags", 2, tags); sprintf(tag, "%pX1", x); pdgui_vmess(0, "crr iiii rS", canvas, "create", "line", 0, 0, 0, 0, "-tags", 2, tags); sprintf(tag, "%pX2", x); pdgui_vmess(0, "crr iiii rS", canvas, "create", "line", 0, 0, 0, 0, "-tags", 2, tags); sprintf(tag, "%pLABEL", x); pdgui_vmess(0, "crr ii rs rS", canvas, "create", "text", 0, 0, "-anchor", "w", "-tags", 4, tags); toggle_draw_config(x, glist); (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_IO); } void toggle_draw_select(t_toggle* x, t_glist* glist) { t_canvas *canvas = glist_getcanvas(glist); int col = IEM_GUI_COLOR_NORMAL, lcol = x->x_gui.x_lcol; char tag[128]; if(x->x_gui.x_fsf.x_selected) col = lcol = IEM_GUI_COLOR_SELECTED; sprintf(tag, "%pBASE", x); pdgui_vmess(0, "crs rk", canvas, "itemconfigure", tag, "-outline", col); sprintf(tag, "%pLABEL", x); pdgui_vmess(0, "crs rk", canvas, "itemconfigure", tag, "-fill", lcol); } void toggle_draw_update(t_toggle *x, t_glist *glist) { if(glist_isvisible(glist)) { t_canvas *canvas = glist_getcanvas(glist); int col = (x->x_on != 0.0) ? x->x_gui.x_fcol : x->x_gui.x_bcol; char tag[128]; sprintf(tag, "%pX1", x); pdgui_vmess(0, "crs rk", canvas, "itemconfigure", tag, "-fill", col); sprintf(tag, "%pX2", x); pdgui_vmess(0, "crs rk", canvas, "itemconfigure", tag, "-fill", col); } } /* ------------------------ tgl widgetbehaviour----------------------------- */ static void toggle_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2) { t_toggle *x = (t_toggle *)z; *xp1 = text_xpix(&x->x_gui.x_obj, glist); *yp1 = text_ypix(&x->x_gui.x_obj, glist); *xp2 = *xp1 + x->x_gui.x_w; *yp2 = *yp1 + x->x_gui.x_h; } static void toggle_save(t_gobj *z, t_binbuf *b) { t_toggle *x = (t_toggle *)z; t_symbol *bflcol[3]; t_symbol *srl[3]; iemgui_save(&x->x_gui, srl, bflcol); binbuf_addv(b, "ssiisiisssiiiisssff", gensym("#X"), gensym("obj"), (int)x->x_gui.x_obj.te_xpix, (int)x->x_gui.x_obj.te_ypix, gensym("tgl"), x->x_gui.x_w/IEMGUI_ZOOM(x), iem_symargstoint(&x->x_gui.x_isa), srl[0], srl[1], srl[2], x->x_gui.x_ldx, x->x_gui.x_ldy, iem_fstyletoint(&x->x_gui.x_fsf), x->x_gui.x_fontsize, bflcol[0], bflcol[1], bflcol[2], x->x_gui.x_isa.x_loadinit?x->x_on:0.f, x->x_nonzero); binbuf_addv(b, ";"); } static void toggle_properties(t_gobj *z, t_glist *owner) { t_toggle *x = (t_toggle *)z; iemgui_new_dialog(x, &x->x_gui, "tgl", x->x_gui.x_w/IEMGUI_ZOOM(x), IEM_GUI_MINSIZE, 0, 0, x->x_nonzero, 0, 1, -1, "", "", 1, -1, -1); } static void toggle_bang(t_toggle *x) { x->x_on = (x->x_on == 0.0) ? x->x_nonzero : 0.0; (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); outlet_float(x->x_gui.x_obj.ob_outlet, x->x_on); if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) pd_float(x->x_gui.x_snd->s_thing, x->x_on); } static void toggle_dialog(t_toggle *x, t_symbol *s, int argc, t_atom *argv) { t_symbol *srl[3]; int a = (int)atom_getfloatarg(0, argc, argv); t_float nonzero = (t_float)atom_getfloatarg(2, argc, argv); int sr_flags; t_atom undo[18]; iemgui_setdialogatoms(&x->x_gui, 18, undo); SETFLOAT (undo+1, 0); SETFLOAT (undo+2, x->x_nonzero); SETFLOAT (undo+3, 0); pd_undo_set_objectstate(x->x_gui.x_glist, (t_pd*)x, gensym("dialog"), 18, undo, argc, argv); if(nonzero == 0.0) nonzero = 1.0; x->x_nonzero = nonzero; if(x->x_on != 0.0) x->x_on = x->x_nonzero; sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv); x->x_gui.x_w = iemgui_clip_size(a) * IEMGUI_ZOOM(x); x->x_gui.x_h = x->x_gui.x_w; iemgui_size(x, &x->x_gui); } static void toggle_click(t_toggle *x, t_floatarg xpos, t_floatarg ypos, t_floatarg shift, t_floatarg ctrl, t_floatarg alt) {toggle_bang(x);} static int toggle_newclick(t_gobj *z, struct _glist *glist, int xpix, int ypix, int shift, int alt, int dbl, int doit) { if(doit) toggle_click((t_toggle *)z, (t_floatarg)xpix, (t_floatarg)ypix, (t_floatarg)shift, 0, (t_floatarg)alt); return (1); } static void toggle_set(t_toggle *x, t_floatarg f) { int old = (x->x_on != 0); x->x_on = f; if (f != 0.0 && pd_compatibilitylevel < 46) x->x_nonzero = f; if ((x->x_on != 0) != old) (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); } static void toggle_float(t_toggle *x, t_floatarg f) { toggle_set(x, f); if(x->x_gui.x_fsf.x_put_in2out) { outlet_float(x->x_gui.x_obj.ob_outlet, x->x_on); if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) pd_float(x->x_gui.x_snd->s_thing, x->x_on); } } static void toggle_fout(t_toggle *x, t_floatarg f) { toggle_set(x, f); outlet_float(x->x_gui.x_obj.ob_outlet, x->x_on); if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) pd_float(x->x_gui.x_snd->s_thing, x->x_on); } static void toggle_loadbang(t_toggle *x, t_floatarg action) { if (action == LB_LOAD && x->x_gui.x_isa.x_loadinit) toggle_fout(x, (t_float)x->x_on); } static void toggle_size(t_toggle *x, t_symbol *s, int ac, t_atom *av) { x->x_gui.x_w = iemgui_clip_size((int)atom_getfloatarg(0, ac, av)) * IEMGUI_ZOOM(x); x->x_gui.x_h = x->x_gui.x_w; iemgui_size((void *)x, &x->x_gui); } static void toggle_delta(t_toggle *x, t_symbol *s, int ac, t_atom *av) {iemgui_delta((void *)x, &x->x_gui, s, ac, av);} static void toggle_pos(t_toggle *x, t_symbol *s, int ac, t_atom *av) {iemgui_pos((void *)x, &x->x_gui, s, ac, av);} static void toggle_color(t_toggle *x, t_symbol *s, int ac, t_atom *av) {iemgui_color((void *)x, &x->x_gui, s, ac, av);} static void toggle_send(t_toggle *x, t_symbol *s) {iemgui_send(x, &x->x_gui, s);} static void toggle_receive(t_toggle *x, t_symbol *s) {iemgui_receive(x, &x->x_gui, s);} static void toggle_label(t_toggle *x, t_symbol *s) {iemgui_label((void *)x, &x->x_gui, s);} static void toggle_label_font(t_toggle *x, t_symbol *s, int ac, t_atom *av) {iemgui_label_font((void *)x, &x->x_gui, s, ac, av);} static void toggle_label_pos(t_toggle *x, t_symbol *s, int ac, t_atom *av) {iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);} static void toggle_init(t_toggle *x, t_floatarg f) { x->x_gui.x_isa.x_loadinit = (f == 0.0) ? 0 : 1; } static void toggle_nonzero(t_toggle *x, t_floatarg f) { if(f != 0.0) x->x_nonzero = f; } static void *toggle_new(t_symbol *s, int argc, t_atom *argv) { t_toggle *x = (t_toggle *)iemgui_new(toggle_class); int a = IEM_GUI_DEFAULTSIZE, f = 0; int ldx = 0, ldy = -8 * IEM_GUI_DEFAULTSIZE_SCALE; int fs = x->x_gui.x_fontsize; t_float on = 0.0, nonzero = 1.0; char str[144]; IEMGUI_SETDRAWFUNCTIONS(x, toggle); if(((argc == 13)||(argc == 14))&&IS_A_FLOAT(argv,0) &&IS_A_FLOAT(argv,1) &&(IS_A_SYMBOL(argv,2)||IS_A_FLOAT(argv,2)) &&(IS_A_SYMBOL(argv,3)||IS_A_FLOAT(argv,3)) &&(IS_A_SYMBOL(argv,4)||IS_A_FLOAT(argv,4)) &&IS_A_FLOAT(argv,5)&&IS_A_FLOAT(argv,6) &&IS_A_FLOAT(argv,7)&&IS_A_FLOAT(argv,8)&&IS_A_FLOAT(argv,12)) { a = (int)atom_getfloatarg(0, argc, argv); iem_inttosymargs(&x->x_gui.x_isa, atom_getfloatarg(1, argc, argv)); iemgui_new_getnames(&x->x_gui, 2, argv); ldx = (int)atom_getfloatarg(5, argc, argv); ldy = (int)atom_getfloatarg(6, argc, argv); iem_inttofstyle(&x->x_gui.x_fsf, atom_getfloatarg(7, argc, argv)); fs = (int)atom_getfloatarg(8, argc, argv); iemgui_all_loadcolors(&x->x_gui, argv+9, argv+10, argv+11); on = (t_float)atom_getfloatarg(12, argc, argv); } else iemgui_new_getnames(&x->x_gui, 2, 0); if((argc == 14)&&IS_A_FLOAT(argv,13)) nonzero = (t_float)atom_getfloatarg(13, argc, argv); x->x_gui.x_fsf.x_snd_able = (0 != x->x_gui.x_snd); x->x_gui.x_fsf.x_rcv_able = (0 != x->x_gui.x_rcv); if(x->x_gui.x_fsf.x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica"); else if(x->x_gui.x_fsf.x_font_style == 2) strcpy(x->x_gui.x_font, "times"); else { x->x_gui.x_fsf.x_font_style = 0; strcpy(x->x_gui.x_font, sys_font); } x->x_nonzero = (nonzero != 0.0) ? nonzero : 1.0; if(x->x_gui.x_isa.x_loadinit) x->x_on = (on != 0.0) ? nonzero : 0.0; else x->x_on = 0.0; if (x->x_gui.x_fsf.x_rcv_able) pd_bind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); x->x_gui.x_ldx = ldx; x->x_gui.x_ldy = ldy; x->x_gui.x_fontsize = (fs < 4)?4:fs; x->x_gui.x_w = iemgui_clip_size(a); x->x_gui.x_h = x->x_gui.x_w; iemgui_verify_snd_ne_rcv(&x->x_gui); iemgui_newzoom(&x->x_gui); outlet_new(&x->x_gui.x_obj, &s_float); return (x); } static void toggle_free(t_toggle *x) { if(x->x_gui.x_fsf.x_rcv_able) pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); pdgui_stub_deleteforkey(x); } void g_toggle_setup(void) { toggle_class = class_new(gensym("tgl"), (t_newmethod)toggle_new, (t_method)toggle_free, sizeof(t_toggle), 0, A_GIMME, 0); class_addcreator((t_newmethod)toggle_new, gensym("toggle"), A_GIMME, 0); class_addbang(toggle_class, toggle_bang); class_addfloat(toggle_class, toggle_float); class_addmethod(toggle_class, (t_method)toggle_click, gensym("click"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); class_addmethod(toggle_class, (t_method)toggle_dialog, gensym("dialog"), A_GIMME, 0); class_addmethod(toggle_class, (t_method)toggle_loadbang, gensym("loadbang"), A_DEFFLOAT, 0); class_addmethod(toggle_class, (t_method)toggle_set, gensym("set"), A_FLOAT, 0); class_addmethod(toggle_class, (t_method)toggle_size, gensym("size"), A_GIMME, 0); class_addmethod(toggle_class, (t_method)toggle_delta, gensym("delta"), A_GIMME, 0); class_addmethod(toggle_class, (t_method)toggle_pos, gensym("pos"), A_GIMME, 0); class_addmethod(toggle_class, (t_method)toggle_color, gensym("color"), A_GIMME, 0); class_addmethod(toggle_class, (t_method)toggle_send, gensym("send"), A_DEFSYM, 0); class_addmethod(toggle_class, (t_method)toggle_receive, gensym("receive"), A_DEFSYM, 0); class_addmethod(toggle_class, (t_method)toggle_label, gensym("label"), A_DEFSYM, 0); class_addmethod(toggle_class, (t_method)toggle_label_pos, gensym("label_pos"), A_GIMME, 0); class_addmethod(toggle_class, (t_method)toggle_label_font, gensym("label_font"), A_GIMME, 0); class_addmethod(toggle_class, (t_method)toggle_init, gensym("init"), A_FLOAT, 0); class_addmethod(toggle_class, (t_method)toggle_nonzero, gensym("nonzero"), A_FLOAT, 0); class_addmethod(toggle_class, (t_method)iemgui_zoom, gensym("zoom"), A_CANT, 0); toggle_widgetbehavior.w_getrectfn = toggle_getrect; toggle_widgetbehavior.w_displacefn = iemgui_displace; toggle_widgetbehavior.w_selectfn = iemgui_select; toggle_widgetbehavior.w_activatefn = NULL; toggle_widgetbehavior.w_deletefn = iemgui_delete; toggle_widgetbehavior.w_visfn = iemgui_vis; toggle_widgetbehavior.w_clickfn = toggle_newclick; class_setwidget(toggle_class, &toggle_widgetbehavior); class_sethelpsymbol(toggle_class, gensym("toggle")); class_setsavefn(toggle_class, toggle_save); class_setpropertiesfn(toggle_class, toggle_properties); } ================================================ FILE: libs/libpd/pure-data/src/g_traversal.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* This file defines Text objects which traverse data contained in scalars and arrays: pointer - point to an object belonging to a template get - get numeric fields set - set numeric/symbolic fields element - get an array element getsize - get the size of an array setsize - set the size of an array append - add an element to a list */ #include #include "m_pd.h" #include "g_canvas.h" /* templates are named using the name-bashing by which canvases bind thenselves, with a leading "pd-". LATER see if we can have templates occupy their real names. Meanwhile, if a template has an empty name or is named "-" (as when passed as a "-" argument to "get", etc.), just return &s_; objects should check for this and allow it as a wild card when appropriate. */ static t_symbol *template_getbindsym(t_symbol *s) { if (!*s->s_name || !strcmp(s->s_name, "-")) return (&s_); else return (canvas_makebindsym(s)); } /* ---------------------- pointer ----------------------------- */ static t_class *ptrobj_class; typedef struct { t_symbol *to_type; t_outlet *to_outlet; } t_typedout; typedef struct _ptrobj { t_object x_obj; t_gpointer x_gp; t_typedout *x_typedout; int x_ntypedout; t_outlet *x_otherout; t_outlet *x_bangout; } t_ptrobj; static void *ptrobj_new(t_symbol *classname, int argc, t_atom *argv) { t_ptrobj *x = (t_ptrobj *)pd_new(ptrobj_class); t_typedout *to; int n; gpointer_init(&x->x_gp); x->x_typedout = to = (t_typedout *)getbytes(argc * sizeof (*to)); x->x_ntypedout = n = argc; for (; n--; to++) { to->to_outlet = outlet_new(&x->x_obj, &s_pointer); to->to_type = template_getbindsym(atom_getsymbol(argv++)); } x->x_otherout = outlet_new(&x->x_obj, &s_pointer); x->x_bangout = outlet_new(&x->x_obj, &s_bang); pointerinlet_new(&x->x_obj, &x->x_gp); return (x); } static void ptrobj_traverse(t_ptrobj *x, t_symbol *s) { t_glist *glist = (t_glist *)pd_findbyclass(s, canvas_class); if (glist) gpointer_setglist(&x->x_gp, glist, 0); else pd_error(x, "pointer: list '%s' not found", s->s_name); } static void ptrobj_vnext(t_ptrobj *x, t_float f) { t_gobj *gobj; t_gpointer *gp = &x->x_gp; t_gstub *gs = gp->gp_stub; t_glist *glist; int wantselected = (f != 0); if (!gs) { pd_error(x, "pointer next: no current pointer"); return; } if (gs->gs_which != GP_GLIST) { pd_error(x, "pointer next: lists only, not arrays"); return; } glist = gs->gs_un.gs_glist; if (glist->gl_valid != gp->gp_valid) { pd_error(x, "pointer next: stale pointer"); return; } if (wantselected && !glist_isvisible(glist)) { pd_error(x, "pointer vnext: next-selected only works for a visible window"); return; } gobj = &gp->gp_un.gp_scalar->sc_gobj; if (!gobj) gobj = glist->gl_list; else gobj = gobj->g_next; while (gobj && ((pd_class(&gobj->g_pd) != scalar_class) || (wantselected && !glist_isselected(glist, gobj)))) gobj = gobj->g_next; if (gobj) { t_typedout *to; int n; t_scalar *sc = (t_scalar *)gobj; t_symbol *templatesym = sc->sc_template; gp->gp_un.gp_scalar = sc; for (n = x->x_ntypedout, to = x->x_typedout; n--; to++) { if (to->to_type == templatesym) { outlet_pointer(to->to_outlet, &x->x_gp); return; } } outlet_pointer(x->x_otherout, &x->x_gp); } else { gpointer_unset(gp); outlet_bang(x->x_bangout); } } static void ptrobj_next(t_ptrobj *x) { ptrobj_vnext(x, 0); } static void ptrobj_delete(t_ptrobj *x) { t_gobj *gobj, *old; t_gpointer *gp = &x->x_gp; t_gstub *gs = gp->gp_stub; t_glist *glist; if (!gs) { pd_error(x, "pointer delete: no current pointer"); return; } if (gs->gs_which != GP_GLIST) { pd_error(x, "pointer delete: lists only, not arrays"); return; } glist = gs->gs_un.gs_glist; if (glist->gl_valid != gp->gp_valid) { pd_error(x, "pointer delete: stale pointer"); return; } if (!gp->gp_un.gp_scalar) { pd_error(x, "pointer delete: pointing to head"); return; } if (gp->gp_un.gp_scalar->sc_template == gensym("pd-text")) { pd_error(x, "pointer delete: can't delete 'pd-text' scalar"); return; } if (gp->gp_un.gp_scalar->sc_template == gensym("pd-float-array")) { pd_error(x, "pointer delete: can't delete 'pd-float-array' scalar"); return; } old = &gp->gp_un.gp_scalar->sc_gobj; gobj = old->g_next; while (gobj && (pd_class(&gobj->g_pd) != scalar_class)) gobj = gobj->g_next; glist_delete(glist, old); gp->gp_valid = glist->gl_valid; if (gobj) { t_typedout *to; int n; t_scalar *sc = (t_scalar *)gobj; t_symbol *templatesym = sc->sc_template; gp->gp_un.gp_scalar = sc; for (n = x->x_ntypedout, to = x->x_typedout; n--; to++) { if (to->to_type == templatesym) { outlet_pointer(to->to_outlet, &x->x_gp); return; } } outlet_pointer(x->x_otherout, &x->x_gp); } else { gpointer_unset(gp); outlet_bang(x->x_bangout); } } t_symbol *gpointer_gettemplatesym(const t_gpointer *gp); static void ptrobj_equal(t_ptrobj *x, t_gpointer *gp) { t_symbol *templatesym; int n, which, result; t_typedout *to; if (!gpointer_check(&x->x_gp, 1)) { pd_error(x, "pointer equal: empty pointer"); return; } /* we don't care for the actual type in the union because they are all pointers */ result = (gp->gp_stub->gs_un.gs_glist == x->x_gp.gp_stub->gs_un.gs_glist) && (gp->gp_un.gp_scalar == x->x_gp.gp_un.gp_scalar); if (!result) { outlet_bang(x->x_bangout); return; } templatesym = gpointer_gettemplatesym(&x->x_gp); for (n = x->x_ntypedout, to = x->x_typedout; n--; to++) { if (to->to_type == templatesym) { outlet_pointer(to->to_outlet, &x->x_gp); return; } } outlet_pointer(x->x_otherout, &x->x_gp); } /* send a message to the window containing the object pointed to */ static void ptrobj_sendwindow(t_ptrobj *x, t_symbol *s, int argc, t_atom *argv) { t_scalar *sc; t_symbol *templatesym; int n; t_typedout *to; t_glist *glist; t_pd *canvas; t_gstub *gs; if (!gpointer_check(&x->x_gp, 1)) { pd_error(x, "pointer send-window: empty pointer"); return; } gs = x->x_gp.gp_stub; if (gs->gs_which == GP_GLIST) glist = gs->gs_un.gs_glist; else { t_array *owner_array = gs->gs_un.gs_array; while (owner_array->a_gp.gp_stub->gs_which == GP_ARRAY) owner_array = owner_array->a_gp.gp_stub->gs_un.gs_array; glist = owner_array->a_gp.gp_stub->gs_un.gs_glist; } canvas = (t_pd *)glist_getcanvas(glist); if (argc && argv->a_type == A_SYMBOL) pd_typedmess(canvas, argv->a_w.w_symbol, argc-1, argv+1); else pd_error(x, "pointer send-window: no message?"); } /* send the pointer to the named object */ static void ptrobj_send(t_ptrobj *x, t_symbol *s) { if (!s->s_thing) pd_error(x, "%s: no such object", s->s_name); else if (!gpointer_check(&x->x_gp, 1)) pd_error(x, "pointer send: empty pointer"); else pd_pointer(s->s_thing, &x->x_gp); } static void ptrobj_bang(t_ptrobj *x) { t_symbol *templatesym; int n; t_typedout *to; if (!gpointer_check(&x->x_gp, 1)) { pd_error(x, "pointer bang: empty pointer"); return; } templatesym = gpointer_gettemplatesym(&x->x_gp); for (n = x->x_ntypedout, to = x->x_typedout; n--; to++) { if (to->to_type == templatesym) { outlet_pointer(to->to_outlet, &x->x_gp); return; } } outlet_pointer(x->x_otherout, &x->x_gp); } static void ptrobj_pointer(t_ptrobj *x, t_gpointer *gp) { gpointer_unset(&x->x_gp); gpointer_copy(gp, &x->x_gp); ptrobj_bang(x); } static void ptrobj_rewind(t_ptrobj *x) { t_scalar *sc; t_symbol *templatesym; int n; t_typedout *to; t_glist *glist; t_pd *canvas; t_gstub *gs; if (!gpointer_check(&x->x_gp, 1)) { pd_error(x, "pointer rewind: empty pointer"); return; } gs = x->x_gp.gp_stub; if (gs->gs_which != GP_GLIST) { pd_error(x, "pointer rewind: sorry, unavailable for arrays"); return; } glist = gs->gs_un.gs_glist; gpointer_setglist(&x->x_gp, glist, 0); ptrobj_bang(x); } static void ptrobj_free(t_ptrobj *x) { freebytes(x->x_typedout, x->x_ntypedout * sizeof (*x->x_typedout)); gpointer_unset(&x->x_gp); } static void ptrobj_setup(void) { ptrobj_class = class_new(gensym("pointer"), (t_newmethod)ptrobj_new, (t_method)ptrobj_free, sizeof(t_ptrobj), 0, A_GIMME, 0); class_addmethod(ptrobj_class, (t_method)ptrobj_next, gensym("next"), 0); class_addmethod(ptrobj_class, (t_method)ptrobj_send, gensym("send"), A_SYMBOL, 0); class_addmethod(ptrobj_class, (t_method)ptrobj_traverse, gensym("traverse"), A_SYMBOL, 0); class_addmethod(ptrobj_class, (t_method)ptrobj_vnext, gensym("vnext"), A_DEFFLOAT, 0); class_addmethod(ptrobj_class, (t_method)ptrobj_delete, gensym("delete"), 0); class_addmethod(ptrobj_class, (t_method)ptrobj_equal, gensym("equal"), A_POINTER, 0); class_addmethod(ptrobj_class, (t_method)ptrobj_sendwindow, gensym("send-window"), A_GIMME, 0); class_addmethod(ptrobj_class, (t_method)ptrobj_rewind, gensym("rewind"), 0); class_addpointer(ptrobj_class, ptrobj_pointer); class_addbang(ptrobj_class, ptrobj_bang); } /* ---------------------- get ----------------------------- */ static t_class *get_class; typedef struct _getvariable { t_symbol *gv_sym; t_outlet *gv_outlet; } t_getvariable; typedef struct _get { t_object x_obj; t_symbol *x_templatesym; int x_nout; t_getvariable *x_variables; } t_get; static void *get_new(t_symbol *why, int argc, t_atom *argv) { t_get *x = (t_get *)pd_new(get_class); int varcount, i; t_atom at, *varvec; t_getvariable *sp; x->x_templatesym = template_getbindsym(atom_getsymbolarg(0, argc, argv)); if (argc < 2) { varcount = 1; varvec = &at; SETSYMBOL(&at, &s_); } else varcount = argc - 1, varvec = argv + 1; x->x_variables = (t_getvariable *)getbytes(varcount * sizeof (*x->x_variables)); x->x_nout = varcount; for (i = 0, sp = x->x_variables; i < varcount; i++, sp++) { sp->gv_sym = atom_getsymbolarg(i, varcount, varvec); sp->gv_outlet = outlet_new(&x->x_obj, 0); /* LATER connect with the template and set the outlet's type correctly. We can't yet guarantee that the template is there before we hit this routine. */ } return (x); } static void get_set(t_get *x, t_symbol *templatesym, t_symbol *field) { if (x->x_nout != 1) pd_error(x, "get: cannot set multiple fields."); else { x->x_templatesym = template_getbindsym(templatesym); x->x_variables->gv_sym = field; } } static void get_pointer(t_get *x, t_gpointer *gp) { int nitems = x->x_nout, i; t_symbol *templatesym; t_template *template; t_gstub *gs = gp->gp_stub; t_word *vec; t_getvariable *vp; if (!gpointer_check(gp, 0)) { pd_error(x, "get: stale or empty pointer"); return; } if (*x->x_templatesym->s_name) { if ((templatesym = x->x_templatesym) != gpointer_gettemplatesym(gp)) { pd_error(x, "get %s: got wrong template (%s)", templatesym->s_name, gpointer_gettemplatesym(gp)->s_name); return; } } else templatesym = gpointer_gettemplatesym(gp); if (!(template = template_findbyname(templatesym))) { pd_error(x, "get: couldn't find template %s", templatesym->s_name); return; } if (gs->gs_which == GP_ARRAY) vec = gp->gp_un.gp_w; else vec = gp->gp_un.gp_scalar->sc_vec; for (i = nitems - 1, vp = x->x_variables + i; i >= 0; i--, vp--) { int onset, type; t_symbol *arraytype; if (template_find_field(template, vp->gv_sym, &onset, &type, &arraytype)) { if (type == DT_FLOAT) outlet_float(vp->gv_outlet, *(t_float *)(((char *)vec) + onset)); else if (type == DT_SYMBOL) outlet_symbol(vp->gv_outlet, *(t_symbol **)(((char *)vec) + onset)); else pd_error(x, "get: %s.%s is not a number or symbol", template->t_sym->s_name, vp->gv_sym->s_name); } else pd_error(x, "get: %s.%s: no such field", template->t_sym->s_name, vp->gv_sym->s_name); } } static void get_free(t_get *x) { freebytes(x->x_variables, x->x_nout * sizeof (*x->x_variables)); } static void get_setup(void) { get_class = class_new(gensym("get"), (t_newmethod)get_new, (t_method)get_free, sizeof(t_get), 0, A_GIMME, 0); class_addpointer(get_class, get_pointer); class_addmethod(get_class, (t_method)get_set, gensym("set"), A_SYMBOL, A_SYMBOL, 0); } /* ---------------------- set ----------------------------- */ static t_class *set_class; typedef struct _setvariable { t_symbol *gv_sym; union word gv_w; } t_setvariable; typedef struct _set { t_object x_obj; t_gpointer x_gp; t_symbol *x_templatesym; int x_nin; int x_issymbol; t_setvariable *x_variables; } t_set; static void *set_new(t_symbol *why, int argc, t_atom *argv) { t_set *x = (t_set *)pd_new(set_class); int i, varcount; t_setvariable *sp; t_atom at, *varvec; if (argc && (argv[0].a_type == A_SYMBOL) && !strcmp(argv[0].a_w.w_symbol->s_name, "-symbol")) { x->x_issymbol = 1; argc--; argv++; } else x->x_issymbol = 0; x->x_templatesym = template_getbindsym(atom_getsymbolarg(0, argc, argv)); if (argc < 2) { varcount = 1; varvec = &at; SETSYMBOL(&at, &s_); } else varcount = argc - 1, varvec = argv + 1; x->x_variables = (t_setvariable *)getbytes(varcount * sizeof (*x->x_variables)); x->x_nin = varcount; for (i = 0, sp = x->x_variables; i < varcount; i++, sp++) { sp->gv_sym = atom_getsymbolarg(i, varcount, varvec); if (x->x_issymbol) sp->gv_w.w_symbol = &s_; else sp->gv_w.w_float = 0; if (i) { if (x->x_issymbol) symbolinlet_new(&x->x_obj, &sp->gv_w.w_symbol); else floatinlet_new(&x->x_obj, &sp->gv_w.w_float); } } pointerinlet_new(&x->x_obj, &x->x_gp); gpointer_init(&x->x_gp); return (x); } static void set_set(t_set *x, t_symbol *templatesym, t_symbol *field) { if (x->x_nin != 1) pd_error(x, "set: cannot set multiple fields."); else { x->x_templatesym = template_getbindsym(templatesym); x->x_variables->gv_sym = field; if (x->x_issymbol) x->x_variables->gv_w.w_symbol = &s_; else x->x_variables->gv_w.w_float = 0; } } static void set_bang(t_set *x) { int nitems = x->x_nin, i; t_symbol *templatesym; t_template *template; t_setvariable *vp; t_gpointer *gp = &x->x_gp; t_gstub *gs = gp->gp_stub; t_word *vec; if (!gpointer_check(gp, 0)) { pd_error(x, "set: empty pointer"); return; } if (*x->x_templatesym->s_name) { if ((templatesym = x->x_templatesym) != gpointer_gettemplatesym(gp)) { pd_error(x, "set %s: got wrong template (%s)", templatesym->s_name, gpointer_gettemplatesym(gp)->s_name); return; } } else templatesym = gpointer_gettemplatesym(gp); if (!(template = template_findbyname(templatesym))) { pd_error(x, "set: couldn't find template %s", templatesym->s_name); return; } if (!nitems) return; if (gs->gs_which == GP_ARRAY) vec = gp->gp_un.gp_w; else vec = gp->gp_un.gp_scalar->sc_vec; if (x->x_issymbol) for (i = 0, vp = x->x_variables; i < nitems; i++, vp++) template_setsymbol(template, vp->gv_sym, vec, vp->gv_w.w_symbol, 1); else for (i = 0, vp = x->x_variables; i < nitems; i++, vp++) template_setfloat(template, vp->gv_sym, vec, vp->gv_w.w_float, 1); if (gs->gs_which == GP_GLIST) scalar_redraw(gp->gp_un.gp_scalar, gs->gs_un.gs_glist); else { t_array *owner_array = gs->gs_un.gs_array; while (owner_array->a_gp.gp_stub->gs_which == GP_ARRAY) owner_array = owner_array->a_gp.gp_stub->gs_un.gs_array; scalar_redraw(owner_array->a_gp.gp_un.gp_scalar, owner_array->a_gp.gp_stub->gs_un.gs_glist); } } static void set_float(t_set *x, t_float f) { if (x->x_nin && !x->x_issymbol) { x->x_variables[0].gv_w.w_float = f; set_bang(x); } else pd_error(x, "set: type mismatch or no field specified"); } static void set_symbol(t_set *x, t_symbol *s) { if (x->x_nin && x->x_issymbol) { x->x_variables[0].gv_w.w_symbol = s; set_bang(x); } else pd_error(x, "set: type mismatch or no field specified"); } static void set_free(t_set *x) { freebytes(x->x_variables, x->x_nin * sizeof (*x->x_variables)); gpointer_unset(&x->x_gp); } static void set_setup(void) { set_class = class_new(gensym("set"), (t_newmethod)set_new, (t_method)set_free, sizeof(t_set), 0, A_GIMME, 0); class_addfloat(set_class, set_float); class_addsymbol(set_class, set_symbol); class_addbang(set_class, set_bang); class_addmethod(set_class, (t_method)set_set, gensym("set"), A_SYMBOL, A_SYMBOL, 0); } /* ---------------------- element ----------------------------- */ static t_class *elem_class; typedef struct _elem { t_object x_obj; t_symbol *x_templatesym; t_symbol *x_fieldsym; t_gpointer x_gp; t_gpointer x_gparent; } t_elem; static void *elem_new(t_symbol *templatesym, t_symbol *fieldsym) { t_elem *x = (t_elem *)pd_new(elem_class); x->x_templatesym = template_getbindsym(templatesym); x->x_fieldsym = fieldsym; gpointer_init(&x->x_gp); gpointer_init(&x->x_gparent); pointerinlet_new(&x->x_obj, &x->x_gparent); outlet_new(&x->x_obj, &s_pointer); return (x); } static void elem_set(t_elem *x, t_symbol *templatesym, t_symbol *fieldsym) { x->x_templatesym = template_getbindsym(templatesym); x->x_fieldsym = fieldsym; } static void elem_float(t_elem *x, t_float f) { int indx = f, nitems, onset; t_symbol *templatesym, *fieldsym = x->x_fieldsym, *elemtemplatesym; t_template *template; t_template *elemtemplate; t_gpointer *gparent = &x->x_gparent; t_word *w; t_array *array; int elemsize, type; if (!gpointer_check(gparent, 0)) { pd_error(x, "element: empty pointer"); return; } if (*x->x_templatesym->s_name) { if ((templatesym = x->x_templatesym) != gpointer_gettemplatesym(gparent)) { pd_error(x, "element %s: got wrong template (%s)", templatesym->s_name, gpointer_gettemplatesym(gparent)->s_name); return; } } else templatesym = gpointer_gettemplatesym(gparent); if (!(template = template_findbyname(templatesym))) { pd_error(x, "element: couldn't find template %s", templatesym->s_name); return; } if (gparent->gp_stub->gs_which == GP_ARRAY) w = gparent->gp_un.gp_w; else w = gparent->gp_un.gp_scalar->sc_vec; if (!template) { pd_error(x, "element: couldn't find template %s", templatesym->s_name); return; } if (!template_find_field(template, fieldsym, &onset, &type, &elemtemplatesym)) { pd_error(x, "element: couldn't find array field %s", fieldsym->s_name); return; } if (type != DT_ARRAY) { pd_error(x, "element: field %s not of type array", fieldsym->s_name); return; } if (!(elemtemplate = template_findbyname(elemtemplatesym))) { pd_error(x, "element: couldn't find field template %s", elemtemplatesym->s_name); return; } elemsize = elemtemplate->t_n * sizeof(t_word); array = *(t_array **)(((char *)w) + onset); nitems = array->a_n; if (indx < 0) indx = 0; if (indx >= nitems) indx = nitems-1; gpointer_setarray(&x->x_gp, array, (t_word *)((char *)(array->a_vec) + indx * elemsize)); outlet_pointer(x->x_obj.ob_outlet, &x->x_gp); } static void elem_free(t_elem *x, t_gpointer *gp) { gpointer_unset(&x->x_gp); gpointer_unset(&x->x_gparent); } static void elem_setup(void) { elem_class = class_new(gensym("element"), (t_newmethod)elem_new, (t_method)elem_free, sizeof(t_elem), 0, A_DEFSYM, A_DEFSYM, 0); class_addfloat(elem_class, elem_float); class_addmethod(elem_class, (t_method)elem_set, gensym("set"), A_SYMBOL, A_SYMBOL, 0); } /* ---------------------- getsize ----------------------------- */ static t_class *getsize_class; typedef struct _getsize { t_object x_obj; t_symbol *x_templatesym; t_symbol *x_fieldsym; } t_getsize; static void *getsize_new(t_symbol *templatesym, t_symbol *fieldsym) { t_getsize *x = (t_getsize *)pd_new(getsize_class); x->x_templatesym = template_getbindsym(templatesym); x->x_fieldsym = fieldsym; outlet_new(&x->x_obj, &s_float); return (x); } static void getsize_set(t_getsize *x, t_symbol *templatesym, t_symbol *fieldsym) { x->x_templatesym = template_getbindsym(templatesym); x->x_fieldsym = fieldsym; } static void getsize_pointer(t_getsize *x, t_gpointer *gp) { int nitems, onset, type; t_symbol *templatesym, *fieldsym = x->x_fieldsym, *elemtemplatesym; t_template *template; t_word *w; t_array *array; int elemsize; t_gstub *gs = gp->gp_stub; if (!gpointer_check(gp, 0)) { pd_error(x, "getsize: stale or empty pointer"); return; } if (*x->x_templatesym->s_name) { if ((templatesym = x->x_templatesym) != gpointer_gettemplatesym(gp)) { pd_error(x, "getsize %s: got wrong template (%s)", templatesym->s_name, gpointer_gettemplatesym(gp)->s_name); return; } } else templatesym = gpointer_gettemplatesym(gp); if (!(template = template_findbyname(templatesym))) { pd_error(x, "getsize: couldn't find template %s", templatesym->s_name); return; } if (!template_find_field(template, fieldsym, &onset, &type, &elemtemplatesym)) { pd_error(x, "getsize: couldn't find array field %s", fieldsym->s_name); return; } if (type != DT_ARRAY) { pd_error(x, "getsize: field %s not of type array", fieldsym->s_name); return; } if (gs->gs_which == GP_ARRAY) w = gp->gp_un.gp_w; else w = gp->gp_un.gp_scalar->sc_vec; array = *(t_array **)(((char *)w) + onset); outlet_float(x->x_obj.ob_outlet, (t_float)(array->a_n)); } static void getsize_setup(void) { getsize_class = class_new(gensym("getsize"), (t_newmethod)getsize_new, 0, sizeof(t_getsize), 0, A_DEFSYM, A_DEFSYM, 0); class_addpointer(getsize_class, getsize_pointer); class_addmethod(getsize_class, (t_method)getsize_set, gensym("set"), A_SYMBOL, A_SYMBOL, 0); } /* ---------------------- setsize ----------------------------- */ static t_class *setsize_class; typedef struct _setsize { t_object x_obj; t_symbol *x_templatesym; t_symbol *x_fieldsym; t_gpointer x_gp; } t_setsize; static void *setsize_new(t_symbol *templatesym, t_symbol *fieldsym, t_floatarg newsize) { t_setsize *x = (t_setsize *)pd_new(setsize_class); x->x_templatesym = template_getbindsym(templatesym); x->x_fieldsym = fieldsym; gpointer_init(&x->x_gp); pointerinlet_new(&x->x_obj, &x->x_gp); return (x); } static void setsize_set(t_setsize *x, t_symbol *templatesym, t_symbol *fieldsym) { x->x_templatesym = template_getbindsym(templatesym); x->x_fieldsym = fieldsym; } static void setsize_float(t_setsize *x, t_float f) { int nitems, onset, type; t_symbol *templatesym, *fieldsym = x->x_fieldsym, *elemtemplatesym; t_template *template; t_template *elemtemplate; t_word *w; t_atom at; t_array *array; int elemsize; int newsize = f; t_gpointer *gp = &x->x_gp; t_gstub *gs = gp->gp_stub; if (!gpointer_check(&x->x_gp, 0)) { pd_error(x, "setsize: empty pointer"); return; } if (*x->x_templatesym->s_name) { if ((templatesym = x->x_templatesym) != gpointer_gettemplatesym(gp)) { pd_error(x, "setsize %s: got wrong template (%s)", templatesym->s_name, gpointer_gettemplatesym(gp)->s_name); return; } } else templatesym = gpointer_gettemplatesym(gp); if (!(template = template_findbyname(templatesym))) { pd_error(x, "setsize: couldn't find template %s", templatesym->s_name); return; } if (!template_find_field(template, fieldsym, &onset, &type, &elemtemplatesym)) { pd_error(x,"setsize: couldn't find array field %s", fieldsym->s_name); return; } if (type != DT_ARRAY) { pd_error(x,"setsize: field %s not of type array", fieldsym->s_name); return; } if (gs->gs_which == GP_ARRAY) w = gp->gp_un.gp_w; else w = gp->gp_un.gp_scalar->sc_vec; if (!(elemtemplate = template_findbyname(elemtemplatesym))) { pd_error(x,"setsize: couldn't find field template %s", elemtemplatesym->s_name); return; } elemsize = elemtemplate->t_n * sizeof(t_word); array = *(t_array **)(((char *)w) + onset); if (elemsize != array->a_elemsize) bug("setsize_gpointer"); nitems = array->a_n; if (newsize < 1) newsize = 1; if (newsize == nitems) return; /* erase the array before resizing it. If we belong to a scalar it's easy, but if we belong to an element of another array we have to search back until we get to a scalar to erase. When graphics updates become queueable this may fall apart... */ if (gs->gs_which == GP_GLIST) { if (glist_isvisible(gs->gs_un.gs_glist)) gobj_vis((t_gobj *)(gp->gp_un.gp_scalar), gs->gs_un.gs_glist, 0); } else { t_array *owner_array = gs->gs_un.gs_array; while (owner_array->a_gp.gp_stub->gs_which == GP_ARRAY) owner_array = owner_array->a_gp.gp_stub->gs_un.gs_array; if (glist_isvisible(owner_array->a_gp.gp_stub->gs_un.gs_glist)) gobj_vis((t_gobj *)(owner_array->a_gp.gp_un.gp_scalar), owner_array->a_gp.gp_stub->gs_un.gs_glist, 0); } /* if shrinking, free the scalars that will disappear */ if (newsize < nitems) { char *elem; int count; for (elem = ((char *)array->a_vec) + newsize * elemsize, count = nitems - newsize; count--; elem += elemsize) word_free((t_word *)elem, elemtemplate); } /* resize the array */ array->a_vec = (char *)resizebytes(array->a_vec, elemsize * nitems, elemsize * newsize); array->a_n = newsize; /* if growing, initialize new scalars */ if (newsize > nitems) { char *elem; int count; for (elem = ((char *)array->a_vec) + nitems * elemsize, count = newsize - nitems; count--; elem += elemsize) word_init((t_word *)elem, elemtemplate, gp); } /* invalidate all gpointers into the array */ array->a_valid++; /* redraw again. */ if (gs->gs_which == GP_GLIST) { if (glist_isvisible(gs->gs_un.gs_glist)) gobj_vis((t_gobj *)(gp->gp_un.gp_scalar), gs->gs_un.gs_glist, 1); } else { t_array *owner_array = gs->gs_un.gs_array; while (owner_array->a_gp.gp_stub->gs_which == GP_ARRAY) owner_array = owner_array->a_gp.gp_stub->gs_un.gs_array; if (glist_isvisible(owner_array->a_gp.gp_stub->gs_un.gs_glist)) gobj_vis((t_gobj *)(owner_array->a_gp.gp_un.gp_scalar), owner_array->a_gp.gp_stub->gs_un.gs_glist, 1); } } static void setsize_free(t_setsize *x) { gpointer_unset(&x->x_gp); } static void setsize_setup(void) { setsize_class = class_new(gensym("setsize"), (t_newmethod)setsize_new, (t_method)setsize_free, sizeof(t_setsize), 0, A_DEFSYM, A_DEFSYM, A_DEFFLOAT, 0); class_addfloat(setsize_class, setsize_float); class_addmethod(setsize_class, (t_method)setsize_set, gensym("set"), A_SYMBOL, A_SYMBOL, 0); } /* ---------------------- append ----------------------------- */ static t_class *append_class; typedef struct _appendvariable { t_symbol *gv_sym; t_float gv_f; } t_appendvariable; typedef struct _append { t_object x_obj; t_gpointer x_gp; t_symbol *x_templatesym; int x_nin; t_appendvariable *x_variables; } t_append; static void *append_new(t_symbol *why, int argc, t_atom *argv) { t_append *x = (t_append *)pd_new(append_class); int varcount, i; t_atom at, *varvec; t_appendvariable *sp; x->x_templatesym = template_getbindsym(atom_getsymbolarg(0, argc, argv)); if (argc < 2) { varcount = 1; varvec = &at; SETSYMBOL(&at, &s_); } else varcount = argc - 1, varvec = argv + 1; x->x_variables = (t_appendvariable *)getbytes(varcount * sizeof (*x->x_variables)); x->x_nin = varcount; for (i = 0, sp = x->x_variables; i < varcount; i++, sp++) { sp->gv_sym = atom_getsymbolarg(i, varcount, varvec); sp->gv_f = 0; if (i) floatinlet_new(&x->x_obj, &sp->gv_f); } pointerinlet_new(&x->x_obj, &x->x_gp); outlet_new(&x->x_obj, &s_pointer); gpointer_init(&x->x_gp); return (x); } static void append_set(t_append *x, t_symbol *templatesym, t_symbol *field) { if (x->x_nin != 1) pd_error(x, "append set: cannot set multiple fields."); else { x->x_templatesym = template_getbindsym(templatesym); x->x_variables->gv_sym = field; x->x_variables->gv_f = 0; } } static void append_float(t_append *x, t_float f) { int nitems = x->x_nin, i; t_symbol *templatesym = x->x_templatesym; t_template *template; t_appendvariable *vp; t_gpointer *gp = &x->x_gp; t_gstub *gs = gp->gp_stub; t_word *vec; t_scalar *sc, *oldsc; t_glist *glist; if (!templatesym->s_name) { pd_error(x, "append: no template supplied"); return; } template = template_findbyname(templatesym); if (!template) { pd_error(x, "append: couldn't find template %s", templatesym->s_name); return; } if (!gs) { pd_error(x, "append: no current pointer"); return; } if (gs->gs_which != GP_GLIST) { pd_error(x, "append: lists only, not arrays"); return; } glist = gs->gs_un.gs_glist; if (glist->gl_valid != gp->gp_valid) { pd_error(x, "append: stale pointer"); return; } if (!nitems) return; x->x_variables[0].gv_f = f; sc = scalar_new(glist, templatesym); if (!sc) { pd_error(x, "%s: couldn't create scalar", templatesym->s_name); return; } oldsc = gp->gp_un.gp_scalar; if (oldsc) { sc->sc_gobj.g_next = oldsc->sc_gobj.g_next; oldsc->sc_gobj.g_next = &sc->sc_gobj; } else { sc->sc_gobj.g_next = glist->gl_list; glist->gl_list = &sc->sc_gobj; } gp->gp_un.gp_scalar = sc; vec = sc->sc_vec; for (i = 0, vp = x->x_variables; i < nitems; i++, vp++) { template_setfloat(template, vp->gv_sym, vec, vp->gv_f, 1); } if (glist_isvisible(glist_getcanvas(glist))) gobj_vis(&sc->sc_gobj, glist, 1); /* scalar_redraw(sc, glist); ... have to do 'vis' instead here because redraw assumes we're already visible??? ... */ outlet_pointer(x->x_obj.ob_outlet, gp); } static void append_free(t_append *x) { freebytes(x->x_variables, x->x_nin * sizeof (*x->x_variables)); gpointer_unset(&x->x_gp); } static void append_setup(void) { append_class = class_new(gensym("append"), (t_newmethod)append_new, (t_method)append_free, sizeof(t_append), 0, A_GIMME, 0); class_addfloat(append_class, append_float); class_addmethod(append_class, (t_method)append_set, gensym("set"), A_SYMBOL, A_SYMBOL, 0); } /* ----------------- setup function ------------------- */ void g_traversal_setup(void) { ptrobj_setup(); get_setup(); set_setup(); elem_setup(); getsize_setup(); setsize_setup(); append_setup(); } ================================================ FILE: libs/libpd/pure-data/src/g_undo.c ================================================ #include "m_pd.h" #include "g_canvas.h" #include "g_undo.h" #if 0 # define DEBUG_UNDO(x) startpost("[%s:%d] ", __FILE__, __LINE__), x #else # define DEBUG_UNDO(x) do { } while(0) #endif /* used for canvas_objtext to differentiate between objects being created by user vs. those (re)created by the undo/redo actions */ void canvas_undo_set_name(const char*name); /* --------- 12. internal object state --------------- */ int glist_getindex(t_glist *x, t_gobj *y); static t_gobj *glist_nth(t_glist *x, int n) { t_gobj *y; int indx; for (y = x->gl_list, indx = 0; y; y = y->g_next, indx++) if (indx == n) return (y); return (0); } typedef struct _undo_object_state { int u_obj; t_symbol*u_symbol; t_binbuf*u_undo; t_binbuf*u_redo; } t_undo_object_state; static int atom_equal(const t_atom*v0, const t_atom*v1) { if(v0->a_type != v1->a_type) return 0; switch(v0->a_type) { case (A_FLOAT): return (v0->a_w.w_float == v1->a_w.w_float); case (A_SYMBOL): return (v0->a_w.w_symbol == v1->a_w.w_symbol); default: break; } return 0; } static int lists_are_equal(int c0, const t_atom*v0, int c1, const t_atom*v1) { int i; if(c0 != c1) return 0; for(i=0; iu_doing) return; if(lists_are_equal(undo_argc, undo_argv, redo_argc, redo_argv)) return; buf = (t_undo_object_state*)getbytes(sizeof(t_undo_object_state)); buf->u_obj = pos; buf->u_symbol = s; buf->u_undo = binbuf_new(); buf->u_redo = binbuf_new(); binbuf_add(buf->u_undo, undo_argc, undo_argv); binbuf_add(buf->u_redo, redo_argc, redo_argv); #if 0 startpost("UNDO:"); binbuf_print(buf->u_undo); startpost("REDO:"); binbuf_print(buf->u_redo); #endif canvas_undo_add(canvas, UNDO_OBJECT_STATE, "state", buf); } int canvas_undo_objectstate(t_canvas *cnv, void *z, int action) { t_undo_object_state *buf = z; t_binbuf*bbuf = buf->u_undo; t_pd*x = (t_pd*)glist_nth(cnv, buf->u_obj); switch(action) { case UNDO_FREE: binbuf_free(buf->u_undo); binbuf_free(buf->u_redo); t_freebytes(buf, sizeof(*buf)); break; case UNDO_REDO: bbuf = buf->u_redo; /* falls through */ case UNDO_UNDO: if(x) pd_typedmess(x, buf->u_symbol, binbuf_getnatom(bbuf), binbuf_getvec(bbuf)); break; } return 1; } /* --------------- */ static void canvas_show_undomenu(t_canvas*x, const char* undo_action, const char* redo_action) { if (glist_isvisible(x) && glist_istoplevel(x)) pdgui_vmess("pdtk_undomenu", "^ ss", x, undo_action, redo_action); } static void canvas_undo_docleardirty(t_canvas *x) { t_undo *udo = canvas_undo_get(x); if (udo) udo->u_cleanstate = udo->u_last; } void canvas_undo_cleardirty(t_canvas *x) { /* clear dirty flags of this canvas * and all sub-canvases (but not abstractions/ */ t_gobj*y; canvas_undo_docleardirty(x); for(y=x->gl_list; y; y=y->g_next) if (pd_class(&y->g_pd) == canvas_class && !canvas_isabstraction((t_canvas*)y)) canvas_undo_cleardirty((t_canvas*)y); } static int canvas_undo_doisdirty(t_canvas*x) { t_undo *udo = x?canvas_undo_get(x):0; t_gobj*y; if (!udo) return 0; if (udo->u_last != udo->u_cleanstate) return 1; for(y=x->gl_list; y; y=y->g_next) if (pd_class(&y->g_pd) == canvas_class && !canvas_isabstraction((t_canvas*)y)) if (canvas_undo_doisdirty((t_canvas*)y)) return 1; return 0; } static int canvas_undo_isdirty(t_canvas *x) { t_undo *udo = x?canvas_undo_get(x):0; return (0 != udo) && ((udo->u_last != udo->u_cleanstate) || canvas_undo_doisdirty(canvas_getrootfor(x))); } t_undo_action *canvas_undo_init(t_canvas *x) { t_undo_action *a = 0; t_undo *udo = canvas_undo_get(x); if (!udo) return 0; a = (t_undo_action *)getbytes(sizeof(*a)); a->type = 0; a->x = x; a->next = NULL; if (!udo->u_queue) { DEBUG_UNDO(post("%s: first init", __FUNCTION__)); //this is the first init udo->u_queue = a; udo->u_last = a; canvas_undo_cleardirty(x); /* invalidate clean-state for re-created subpatches * since we do not know whether the re-created subpatch * is clean or unclean (it's undo-queue got lost when * it was deleted), we assume the worst */ if (!canvas_isabstraction(x)) udo->u_cleanstate = (void*)1; a->prev = NULL; a->name = "no"; canvas_show_undomenu(x, "no", "no"); } else { DEBUG_UNDO(post("%s: re-init %p", __FUNCTION__, udo->u_last->next)); if (udo->u_last->next) { //we need to rebranch first then add the new action canvas_undo_rebranch(x); } udo->u_last->next = a; a->prev = udo->u_last; udo->u_last = a; } return(a); } t_undo_action *canvas_undo_add(t_canvas *x, t_undo_type type, const char *name, void *data) { t_undo_action *a = 0; t_undo * udo = canvas_undo_get(x); DEBUG_UNDO(post("%s: %d %s %p!", __FUNCTION__, type, name, data)); if(UNDO_SEQUENCE_END == type && udo && udo->u_last && UNDO_SEQUENCE_START == udo->u_last->type) { /* empty undo sequence...get rid of it */ udo->u_last = udo->u_last->prev; canvas_undo_rebranch(x); udo->u_last->next = 0; canvas_undo_set_name(udo->u_last->name); canvas_show_undomenu(x, udo->u_last->name, "no"); return 0; } a = canvas_undo_init(x); if(!a)return a; a->type = type; a->data = (void *)data; a->name = (char *)name; canvas_undo_set_name(name); canvas_show_undomenu(x, a->name, "no"); DEBUG_UNDO(post("%s: done!", __FUNCTION__)); return(a); } static int canvas_undo_doit(t_canvas *x, t_undo_action *udo, int action, const char*funname) { DEBUG_UNDO(post("%s: %s(%d) %d %p", __FUNCTION__, funname, action, udo->type, udo->data)); switch(udo->type) { case UNDO_CONNECT: return canvas_undo_connect(x, udo->data, action); //connect case UNDO_DISCONNECT: return canvas_undo_disconnect(x, udo->data, action); //disconnect case UNDO_CUT: return canvas_undo_cut(x, udo->data, action); //cut case UNDO_MOTION: return canvas_undo_move(x, udo->data, action); //move case UNDO_PASTE: return canvas_undo_paste(x, udo->data, action); //paste case UNDO_APPLY: return canvas_undo_apply(x, udo->data, action); //apply case UNDO_ARRANGE: return canvas_undo_arrange(x, udo->data, action); //arrange case UNDO_CANVAS_APPLY: return canvas_undo_canvas_apply(x, udo->data, action); //canvas apply case UNDO_CREATE: return canvas_undo_create(x, udo->data, action); //create case UNDO_RECREATE: return canvas_undo_recreate(x, udo->data, action); //recreate case UNDO_FONT: return canvas_undo_font(x, udo->data, action); //font case UNDO_OBJECT_STATE: return canvas_undo_objectstate(x, udo->data, action); //font /* undo sequences are handled in canvas_undo_undo resp canvas_undo_redo */ case UNDO_SEQUENCE_START: return 1; //start undo sequence case UNDO_SEQUENCE_END: return 1; //end undo sequence case UNDO_INIT: if (UNDO_FREE == action) return 1;/* FALLS THROUGH */ //init default: pd_error(0, "%s: unsupported undo command %d", funname, udo->type); } return 0; } void canvas_undo_undo(t_canvas *x) { t_undo *udo = canvas_undo_get(x); int dspwas; if (!udo) return; dspwas = canvas_suspend_dsp(); DEBUG_UNDO(post("%s: %p != %p", __FUNCTION__, udo->u_queue, udo->u_last)); if (udo->u_queue && udo->u_last != udo->u_queue) { udo->u_doing = 1; canvas_editmode(x, 1); glist_noselect(x); canvas_undo_set_name(udo->u_last->name); if(UNDO_SEQUENCE_END == udo->u_last->type) { int sequence_depth = 1; while((udo->u_last = udo->u_last->prev) && (UNDO_INIT != udo->u_last->type)) { DEBUG_UNDO(post("%s:sequence[%d] %d", __FUNCTION__, sequence_depth, udo->u_last->type)); switch(udo->u_last->type) { case UNDO_SEQUENCE_START: sequence_depth--; break; case UNDO_SEQUENCE_END: sequence_depth++; break; default: canvas_undo_doit(x, udo->u_last, UNDO_UNDO, __FUNCTION__); } if (sequence_depth < 1) break; } if (sequence_depth < 0) bug("undo sequence missing end"); else if (sequence_depth > 0) bug("undo sequence missing start"); } if(canvas_undo_doit(x, udo->u_last, UNDO_UNDO, __FUNCTION__)) { char *undo_action, *redo_action; udo->u_last = udo->u_last->prev; undo_action = udo->u_last->name; redo_action = udo->u_last->next->name; udo->u_doing = 0; /* here we call updating of all unpaired hubs and nodes since their regular call will fail in case their position needed to be updated by undo/redo first to reflect the old one */ canvas_show_undomenu(x, undo_action, redo_action); canvas_dirty(x, canvas_undo_isdirty(x)); } } canvas_resume_dsp(dspwas); } void canvas_undo_redo(t_canvas *x) { int dspwas; t_undo *udo = canvas_undo_get(x); if (!udo) return; dspwas = canvas_suspend_dsp(); if (udo->u_queue && udo->u_last->next) { char *undo_action, *redo_action; udo->u_doing = 1; udo->u_last = udo->u_last->next; canvas_editmode(x, 1); glist_noselect(x); canvas_undo_set_name(udo->u_last->name); if(UNDO_SEQUENCE_START == udo->u_last->type) { int sequence_depth = 1; while(udo->u_last->next && (udo->u_last = udo->u_last->next)) { DEBUG_UNDO(post("%s:sequence[%d] %d", __FUNCTION__, sequence_depth, udo->u_last->type)); switch(udo->u_last->type) { case UNDO_SEQUENCE_END: sequence_depth--; break; case UNDO_SEQUENCE_START: sequence_depth++; break; default: canvas_undo_doit(x, udo->u_last, UNDO_REDO, __FUNCTION__); } if (sequence_depth < 1) break; } if (sequence_depth < 0) bug("undo sequence end without start"); else if (sequence_depth > 0) bug("undo sequence start without end"); } canvas_undo_doit(x, udo->u_last, UNDO_REDO, __FUNCTION__); undo_action = udo->u_last->name; redo_action = (udo->u_last->next ? udo->u_last->next->name : "no"); udo->u_doing = 0; /* here we call updating of all unpaired hubs and nodes since their regular call will fail in case their position needed to be updated by undo/redo first to reflect the old one */ canvas_show_undomenu(x, undo_action, redo_action); canvas_dirty(x, canvas_undo_isdirty(x)); } canvas_resume_dsp(dspwas); } void canvas_undo_rebranch(t_canvas *x) { int dspwas = canvas_suspend_dsp(); t_undo_action *a1, *a2; t_undo *udo = canvas_undo_get(x); if (!udo) return; if (udo->u_last->next) { a1 = udo->u_last->next; while(a1) { canvas_undo_doit(x, a1, UNDO_FREE, __FUNCTION__); a2 = a1->next; freebytes(a1, sizeof(*a1)); a1 = a2; } udo->u_last->next = 0; } canvas_show_undomenu(x, udo->u_last->name, "no"); canvas_resume_dsp(dspwas); } void canvas_undo_check_canvas_pointers(t_canvas *x) { /* currently unnecessary unless we decide to implement one central undo for all patchers */ } void canvas_undo_purge_abstraction_actions(t_canvas *x) { /* currently unnecessary unless we decide to implement one central undo for all patchers */ } void canvas_undo_free(t_canvas *x) { int dspwas; t_undo_action *a1, *a2; t_undo *udo = canvas_undo_get(x); if (!udo) return; dspwas = canvas_suspend_dsp(); if (udo->u_queue) { a1 = udo->u_queue; while(a1) { canvas_undo_doit(x, a1, UNDO_FREE, __FUNCTION__); a2 = a1->next; freebytes(a1, sizeof(*a1)); a1 = a2; } } canvas_resume_dsp(dspwas); } ================================================ FILE: libs/libpd/pure-data/src/g_undo.h ================================================ #ifndef __g_undo_h_ #define __g_undo_h_ /* Infinite undo by Ivica Ico Bukvic Dec. 2011 Modified for Pd-vanilla by IOhannes m zmölnig Jun. 2018 This is the home of infinite undo queue. Each root canvas has one of these. Only canvas that is root will instantiate the pointer to t_undo_action struct. All sub-canvases (including abstractions) will be linked to the root window. Once the root window is destroyed, so is its undo queue. Each canvas (t_glist) defined in g_canvas.h now also has information on t_undo_action queue and a pointer to the last item in the doubly-linked list (queue). First initialized undo is never filled (it is of a type 0). First (new) action creates another t_undo_action and saves its contents there and updates "last" pointer inside the t_canvas (t_glist). t_undo_action requires canvas information in case we've deleted a canvas and then undo its deletion which will in effect change its pointer (memory location). Then we need to call check_canvas_pointers to update all of the old (stale) undo actions to corresepond with the new memory location. What about abstractions? Once they are recreated (e.g. after delete, followed by an undo) all undo actions (except for its deletion in the parent window should be purged since abstraction's state will now default to its original (saved) state. Types of undo data: 0 - init data (start of the queue) 1 - connect 2 - disconnect 3 - cut, clear & typing into objects 4 - motion, including "tidy up" and stretching 5 - paste & duplicate 6 - apply 7 - arrange (to front/back) 8 - canvas apply 9 - create 10 - recreate 11 - change font 12 - meta: start undo sequence (undo sequences are undone/redone atomically) 13 - meta: end undo sequence 14 - internal object state */ #include "m_pd.h" typedef enum { UNDO_INIT = 0, UNDO_CONNECT, /* 1: OK */ UNDO_DISCONNECT, /* 2: OK */ UNDO_CUT, /* 3: OK */ UNDO_MOTION, /* 4: OK */ UNDO_PASTE, /* 5: OK */ UNDO_APPLY, /* 6: how to test? */ UNDO_ARRANGE, /* 7: FIXME: skipped */ UNDO_CANVAS_APPLY, /* 8: OK */ UNDO_CREATE, /* 9: OK */ UNDO_RECREATE, /* 10: OK */ UNDO_FONT, /* 11: OK */ UNDO_SEQUENCE_START, /* 12 start an atomic sequence of undo actions*/ UNDO_SEQUENCE_END, /* 13 end an atomic sequence of undo actions */ UNDO_OBJECT_STATE, /* 14: internal object state: t_atom-list to send to the object */ UNDO_LAST } t_undo_type; struct _undo_action { t_canvas *x; /* canvas undo is associated with */ t_undo_type type; /* defines what kind of data container it is */ void *data; /* each action will have a different data container */ char *name; /* name of current action */ struct _undo_action *prev; /* previous undo action */ struct _undo_action *next; /* next undo action */ }; #ifndef t_undo_action #define t_undo_action struct _undo_action #endif struct _undo { t_undo_action *u_queue; t_undo_action *u_last; void *u_cleanstate; /* pointer to non-dirty state */ int u_doing; /* currently undoing */ }; #define t_undo struct _undo EXTERN t_undo*canvas_undo_get(t_canvas*x); EXTERN void canvas_undo_cleardirty(t_canvas *x); EXTERN t_undo_action *canvas_undo_init(t_canvas *x); EXTERN t_undo_action *canvas_undo_add(t_canvas *x, t_undo_type type, const char *name, void *data); EXTERN void canvas_undo_undo(t_canvas *x); EXTERN void canvas_undo_redo(t_canvas *x); EXTERN void canvas_undo_rebranch(t_canvas *x); EXTERN void canvas_undo_check_canvas_pointers(t_canvas *x); EXTERN void canvas_undo_purge_abstraction_actions(t_canvas *x); EXTERN void canvas_undo_free(t_canvas *x); /* --------- 1. connect ---------- */ EXTERN void *canvas_undo_set_connect(t_canvas *x, int index1, int outno, int index2, int inno); EXTERN int canvas_undo_connect(t_canvas *x, void *z, int action); /* --------- 2. disconnect ------- */ EXTERN void *canvas_undo_set_disconnect(t_canvas *x, int index1, int outno, int index2, int inno); EXTERN int canvas_undo_disconnect(t_canvas *x, void *z, int action); /* --------- 3. cut -------------- */ EXTERN void *canvas_undo_set_cut(t_canvas *x, int mode); EXTERN int canvas_undo_cut(t_canvas *x, void *z, int action); /* --------- 4. move ------------- */ EXTERN void *canvas_undo_set_move(t_canvas *x, int selected); EXTERN int canvas_undo_move(t_canvas *x, void *z, int action); /* --------- 5. paste ------------ */ EXTERN void *canvas_undo_set_paste(t_canvas *x, int offset, int duplicate, int d_offset); EXTERN int canvas_undo_paste(t_canvas *x, void *z, int action); /* --------- 6. apply ------------ */ EXTERN void *canvas_undo_set_apply(t_canvas *x, int n); EXTERN int canvas_undo_apply(t_canvas *x, void *z, int action); /* --------- 7. arrange ---------- */ EXTERN void *canvas_undo_set_arrange(t_canvas *x, t_gobj *obj, int newindex); EXTERN int canvas_undo_arrange(t_canvas *x, void *z, int action); /* --------- 8. canvas apply ----- */ EXTERN void *canvas_undo_set_canvas(t_canvas *x); EXTERN int canvas_undo_canvas_apply(t_canvas *x, void *z, int action); /* --------- 9. create ----------- */ EXTERN void *canvas_undo_set_create(t_canvas *x); EXTERN int canvas_undo_create(t_canvas *x, void *z, int action); /* --------- 10. recreate -------- */ EXTERN void *canvas_undo_set_recreate(t_canvas *x, t_gobj *y, int old_pos); EXTERN int canvas_undo_recreate(t_canvas *x, void *z, int action); /* --------- 11. font ------------ */ EXTERN void *canvas_undo_set_font(t_canvas *x, int font, t_float realresize, int whichresize); EXTERN int canvas_undo_font(t_canvas *x, void *z, int action); /* ------------------------------- */ #endif /* __g_undo_h_ */ ================================================ FILE: libs/libpd/pure-data/src/g_vumeter.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */ /* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */ #include #include #include "m_pd.h" #include "g_all_guis.h" #define HMARGIN 1 #define VMARGIN 2 #define PEAKHEIGHT 10 /* ----- vu gui-peak- & rms- vu-meter-display ---------- */ t_widgetbehavior vu_widgetbehavior; static t_class *vu_class; /* widget helper functions */ static void vu_update_rms(t_vu *x, t_glist *glist) { if(glist_isvisible(glist)) { const int zoom = IEMGUI_ZOOM(x); int w4 = x->x_gui.x_w / 4, off = text_ypix(&x->x_gui.x_obj, glist) - zoom; int xpos = text_xpix(&x->x_gui.x_obj, glist); int quad1 = xpos + w4 - zoom, quad3 = xpos + x->x_gui.x_w - w4 + zoom; char tag[128]; sprintf(tag, "%pRCOVER", x); pdgui_vmess(0, "crs iiii", glist_getcanvas(glist), "coords", tag, quad1, off, quad3, off + (x->x_led_size+1)*zoom*(IEM_VU_STEPS-x->x_rms)); } } static void vu_update_peak(t_vu *x, t_glist *glist) { t_canvas *canvas = glist_getcanvas(glist); if(glist_isvisible(glist)) { const int zoom = IEMGUI_ZOOM(x); int xpos = text_xpix(&x->x_gui.x_obj, glist); int ypos = text_ypix(&x->x_gui.x_obj, glist); char tag[128]; sprintf(tag, "%pPLED", x); if(x->x_peak) { int i = iemgui_vu_col[x->x_peak]; int j = ypos + (x->x_led_size+1)*zoom*(IEM_VU_STEPS+1-x->x_peak) - (x->x_led_size+1)*zoom/2; pdgui_vmess(0, "crs iiii", canvas, "coords", tag, xpos, j, xpos + x->x_gui.x_w + zoom, j); pdgui_vmess(0, "crs rk", canvas, "itemconfigure", tag, "-fill", iemgui_color_hex[i]); } else { int mid = xpos + x->x_gui.x_w/2; int pkh = PEAKHEIGHT * zoom; pdgui_vmess(0, "crs rk", canvas, "itemconfigure", tag, "-fill", x->x_gui.x_bcol); pdgui_vmess(0, "crs iiii", canvas, "coords", tag, mid, ypos + pkh, mid, ypos + pkh); } } } static void vu_draw_update(t_gobj *client, t_glist *glist); static void vu_draw_io(t_vu* x, t_glist* glist, int old_snd_rcv_flags) { const int zoom = IEMGUI_ZOOM(x); t_canvas *canvas = glist_getcanvas(glist); int xpos = text_xpix(&x->x_gui.x_obj, glist); int ypos = text_ypix(&x->x_gui.x_obj, glist); int hmargin = HMARGIN * zoom, vmargin = VMARGIN * zoom; int iow = IOWIDTH * zoom, ioh = IEM_GUI_IOHEIGHT * zoom; int snd_able = x->x_gui.x_fsf.x_snd_able || x->x_gui.x_fsf.x_rcv_able; char tag_object[128], tag_label[128], tag[128], tag_n[128]; char *tags[] = {tag_object, tag_n, tag}; (void)old_snd_rcv_flags; sprintf(tag_object, "%pOBJ", x); sprintf(tag_label, "%pLABEL", x); /* re-create outlets */ sprintf(tag, "%pOUT", x); pdgui_vmess(0, "crs", canvas, "delete", tag); if(!snd_able) { sprintf(tag_n, "%pOUT%d", x, 0); pdgui_vmess(0, "crr iiii rs rS", canvas, "create", "rectangle", xpos - hmargin, ypos + x->x_gui.x_h + vmargin + zoom - ioh, xpos - hmargin + iow, ypos + x->x_gui.x_h + vmargin, "-fill", "black", "-tags", 3, tags); sprintf(tag_n, "%pOUT%d", x, 1); pdgui_vmess(0, "crr iiii rs rS", canvas, "create", "rectangle", xpos + x->x_gui.x_w + hmargin - iow, ypos + x->x_gui.x_h + vmargin + zoom - ioh, xpos + x->x_gui.x_w + hmargin, ypos + x->x_gui.x_h + vmargin, "-fill", "black", "-tags", 3, tags); /* keep label above outlets */ pdgui_vmess(0, "crss", canvas, "lower", tag, tag_label); } sprintf(tag, "%pIN", x); pdgui_vmess(0, "crs", canvas, "delete", tag); if(!x->x_gui.x_fsf.x_rcv_able) { sprintf(tag_n, "%pIN%d", x, 0); pdgui_vmess(0, "crr iiii rs rS", canvas, "create", "rectangle", xpos - hmargin, ypos - vmargin, xpos - hmargin + iow, ypos - vmargin - zoom + ioh, "-fill", "black", "-tags", 3, tags); sprintf(tag_n, "%pIN%d", x, 1); pdgui_vmess(0, "crr iiii rs rS", canvas, "create", "rectangle", xpos + x->x_gui.x_w + hmargin - iow, ypos - vmargin, xpos + x->x_gui.x_w + hmargin, ypos - vmargin - zoom + ioh, "-fill", "black", "-tags", 3, tags); /* keep label above inlets */ pdgui_vmess(0, "crss", canvas, "lower", tag, tag_label); } } static void vu_draw_config(t_vu* x, t_glist* glist) { const int zoom = IEMGUI_ZOOM(x); t_iemgui *iemgui = &x->x_gui; t_canvas *canvas = glist_getcanvas(glist); int xpos = text_xpix(&x->x_gui.x_obj, glist); int ypos = text_ypix(&x->x_gui.x_obj, glist); int hmargin = HMARGIN * zoom, vmargin = VMARGIN * zoom; int iow = IOWIDTH * zoom, ioh = IEM_GUI_IOHEIGHT * zoom; int ledw = x->x_led_size * zoom; int fs = x->x_gui.x_fontsize * zoom; int w4 = x->x_gui.x_w/4, mid = xpos + x->x_gui.x_w/2, quad1 = xpos + w4 + zoom; int quad3 = xpos + x->x_gui.x_w - w4, end = xpos + x->x_gui.x_w + 4*zoom; int k1 = (x->x_led_size+1)*zoom, k2 = IEM_VU_STEPS+1, k3 = k1/2; int led_col, yyy, k4 = ypos - k3; int i; char tag[128]; t_atom fontatoms[3]; SETSYMBOL(fontatoms+0, gensym(iemgui->x_font)); SETFLOAT (fontatoms+1, -iemgui->x_fontsize*zoom); SETSYMBOL(fontatoms+2, gensym(sys_fontweight)); sprintf(tag, "%pBASE", x); pdgui_vmess(0, "crs iiii", canvas, "coords", tag, xpos - hmargin, ypos - vmargin, xpos+x->x_gui.x_w + hmargin, ypos+x->x_gui.x_h + vmargin); pdgui_vmess(0, "crs ri rk", canvas, "itemconfigure", tag, "-width", zoom, "-fill", x->x_gui.x_bcol); sprintf(tag, "%pSCALE", x); pdgui_vmess(0, "crs rA rk", canvas, "itemconfigure", tag, "-font", 3, fontatoms, "-fill", x->x_gui.x_fsf.x_selected ? IEM_GUI_COLOR_SELECTED : x->x_gui.x_lcol); sprintf(tag, "%pRLED", x); pdgui_vmess(0, "crs ri", canvas, "itemconfigure", tag, "-width", ledw); for(i = 1; i <= IEM_VU_STEPS + 1; i++) { sprintf(tag, "%pRLED%d", x, i); led_col = iemgui_vu_col[i]; yyy = k4 + k1*(k2-i); pdgui_vmess(0, "crs iiii", canvas, "coords", tag, quad1, yyy, quad3, yyy); pdgui_vmess(0, "crs rk", canvas, "itemconfigure", tag, "-fill", iemgui_color_hex[led_col]); sprintf(tag, "%pSCALE%d", x, i); pdgui_vmess(0, "crs ii", canvas, "coords", tag, end, yyy + k3); if((i+2) & 3) pdgui_vmess(0, "crs rs", canvas, "itemconfigure", tag, "-text", (x->x_scale)?iemgui_vu_scale_str[i]:""); } i = IEM_VU_STEPS + 1; yyy = k4 + k1 * (k2 - i); /* this was already set in the loop above, overwritten here */ sprintf(tag, "%pSCALE%d", x, i); pdgui_vmess(0, "crs ii", canvas, "coords", tag, end, yyy + k3); pdgui_vmess(0, "crs rs", canvas, "itemconfigure", tag, "-text", (x->x_scale)?iemgui_vu_scale_str[i]:""); sprintf(tag, "%pRCOVER", x); pdgui_vmess(0, "crs iiii", canvas, "coords", tag, quad1 - zoom, ypos - zoom, quad3 + zoom, ypos - zoom + k1*IEM_VU_STEPS); pdgui_vmess(0, "crs rk rk", canvas, "itemconfigure", tag, "-fill", x->x_gui.x_bcol, "-outline", x->x_gui.x_bcol); sprintf(tag, "%pPLED", x); pdgui_vmess(0, "crs iiii", canvas, "coords", tag, mid, ypos + PEAKHEIGHT * zoom, mid, ypos + PEAKHEIGHT * zoom); pdgui_vmess(0, "crs ri rk", canvas, "itemconfigure", tag, "-width", ledw+zoom, /* peak LED is slightly fatter */ "-fill", x->x_gui.x_bcol); sprintf(tag, "%pLABEL", x); pdgui_vmess(0, "crs ii", canvas, "coords", tag, xpos+x->x_gui.x_ldx * zoom, ypos+x->x_gui.x_ldy * zoom); pdgui_vmess(0, "crs rA rk", canvas, "itemconfigure", tag, "-font", 3, fontatoms, "-fill", x->x_gui.x_fsf.x_selected ? IEM_GUI_COLOR_SELECTED : x->x_gui.x_lcol); iemgui_dolabel(x, &x->x_gui, x->x_gui.x_lab, 1); x->x_updaterms = x->x_updatepeak = 1; } static void vu_draw_new(t_vu *x, t_glist *glist) { t_canvas *canvas = glist_getcanvas(glist); char tag_object[128], tag[128], tag_n[128]; char *tags[] = {tag_object, tag, tag_n, "text"}; int i; sprintf(tag_object, "%pOBJ", x); sprintf(tag, "%pBASE", x); pdgui_vmess(0, "crr iiii rS", canvas, "create", "rectangle", 0, 0, 0, 0, "-tags", 2, tags); for(i = 1; i < IEM_VU_STEPS+1; i++) { sprintf(tag, "%pRLED", x); sprintf(tag_n, "%pRLED%d", x, i); pdgui_vmess(0, "crr iiii rS", canvas, "create", "line", 0, 0, 0, 0, "-tags", 3, tags); sprintf(tag, "%pSCALE", x); sprintf(tag_n, "%pSCALE%d", x, i); pdgui_vmess(0, "crr ii rs rS", canvas, "create", "text", 0, 0, "-anchor", "w", "-tags", 3, tags); } /* and a final scale item */ sprintf(tag, "%pSCALE", x); sprintf(tag_n, "%pSCALE%d", x, i); pdgui_vmess(0, "crr ii rs rS", canvas, "create", "text", 0, 0, "-anchor", "w", "-tags", 3, tags); sprintf(tag, "%pRCOVER", x); pdgui_vmess(0, "crr iiii rS", canvas, "create", "rectangle", 0, 0, 0, 0, "-tags", 2, tags); sprintf(tag, "%pPLED", x); pdgui_vmess(0, "crr iiii rS", canvas, "create", "line", 0, 0, 0, 0, "-tags", 2, tags); sprintf(tag, "%pLABEL", x); sprintf(tag_n, "label"); pdgui_vmess(0, "crr ii rs rS", canvas, "create", "text", 0, 0, "-anchor", "w", "-tags", 4, tags); vu_draw_config(x, glist); vu_draw_io(x, glist, 0); sys_queuegui(x, x->x_gui.x_glist, vu_draw_update); } static void vu_draw_select(t_vu* x,t_glist* glist) { t_canvas *canvas = glist_getcanvas(glist); int col = IEM_GUI_COLOR_NORMAL; int lcol = x->x_gui.x_lcol; char tag[128]; if(x->x_gui.x_fsf.x_selected) col = lcol = IEM_GUI_COLOR_SELECTED; sprintf(tag, "%pBASE", x); pdgui_vmess(0, "crs rk", canvas, "itemconfigure", tag, "-outline", col); sprintf(tag, "%pSCALE", x); pdgui_vmess(0, "crs rk", canvas, "itemconfigure", tag, "-fill", lcol); sprintf(tag, "%pLABEL", x); pdgui_vmess(0, "crs rk", canvas, "itemconfigure", tag, "-fill", lcol); } static void vu_draw_update(t_gobj *client, t_glist *glist) { t_vu *x = (t_vu *)client; if (x->x_updaterms) { vu_update_rms(x, glist); x->x_updaterms = 0; } if (x->x_updatepeak) { vu_update_peak(x, glist); x->x_updatepeak = 0; } } /* ------------------------ vu widgetbehaviour----------------------------- */ static void vu_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2) { t_vu* x = (t_vu*)z; int hmargin = HMARGIN * IEMGUI_ZOOM(x), vmargin = VMARGIN * IEMGUI_ZOOM(x); *xp1 = text_xpix(&x->x_gui.x_obj, glist) - hmargin; *yp1 = text_ypix(&x->x_gui.x_obj, glist) - vmargin; *xp2 = *xp1 + x->x_gui.x_w + hmargin*2; *yp2 = *yp1 + x->x_gui.x_h + vmargin*2; } static void vu_save(t_gobj *z, t_binbuf *b) { t_vu *x = (t_vu *)z; t_symbol *bflcol[3]; t_symbol *srl[3]; iemgui_save(&x->x_gui, srl, bflcol); binbuf_addv(b, "ssiisiissiiiissii", gensym("#X"), gensym("obj"), (int)x->x_gui.x_obj.te_xpix, (int)x->x_gui.x_obj.te_ypix, gensym("vu"), x->x_gui.x_w/IEMGUI_ZOOM(x), x->x_gui.x_h/IEMGUI_ZOOM(x), srl[1], srl[2], x->x_gui.x_ldx, x->x_gui.x_ldy, iem_fstyletoint(&x->x_gui.x_fsf), x->x_gui.x_fontsize, bflcol[0], bflcol[2], x->x_scale, iem_symargstoint(&x->x_gui.x_isa)); binbuf_addv(b, ";"); } void vu_check_height(t_vu *x, int h) { int n; n = h / IEM_VU_STEPS; if(n < IEM_VU_MINSIZE) n = IEM_VU_MINSIZE; x->x_led_size = n - 1; x->x_gui.x_h = (IEM_VU_STEPS * n) * IEMGUI_ZOOM(x); } static void vu_scale(t_vu *x, t_floatarg fscale) { x->x_scale = !!(int)fscale; if(glist_isvisible(x->x_gui.x_glist)) vu_draw_config(x, x->x_gui.x_glist); } static void vu_properties(t_gobj *z, t_glist *owner) { t_vu *x = (t_vu *)z; iemgui_new_dialog(x, &x->x_gui, "vu", x->x_gui.x_w/IEMGUI_ZOOM(x), IEM_SL_MINSIZE, x->x_gui.x_h/IEMGUI_ZOOM(x), IEM_GUI_MINSIZE, 0, 0, 0, x->x_scale, "no scale", "scale", 0, -1, -1); } static void vu_dialog(t_vu *x, t_symbol *s, int argc, t_atom *argv) { t_symbol *srl[3]; int w = (int)atom_getfloatarg(0, argc, argv); int h = (int)atom_getfloatarg(1, argc, argv); int scale = (int)atom_getfloatarg(4, argc, argv); int sr_flags; t_atom undo[18]; iemgui_setdialogatoms(&x->x_gui, 18, undo); SETFLOAT(undo+2, 0.01); SETFLOAT(undo+3, 1); SETFLOAT(undo+4, x->x_scale); SETFLOAT(undo+5, -1); SETSYMBOL(undo+15, gensym("none")); pd_undo_set_objectstate(x->x_gui.x_glist, (t_pd*)x, gensym("dialog"), 18, undo, argc, argv); sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv); x->x_gui.x_fsf.x_snd_able = 0; x->x_gui.x_isa.x_loadinit = 0; x->x_gui.x_w = iemgui_clip_size(w) * IEMGUI_ZOOM(x); vu_check_height(x, h); if(scale != 0) scale = 1; vu_scale(x, (t_float)scale); iemgui_size(x, &x->x_gui); } static void vu_size(t_vu *x, t_symbol *s, int ac, t_atom *av) { x->x_gui.x_w = iemgui_clip_size((int)atom_getfloatarg(0, ac, av)) * IEMGUI_ZOOM(x); if(ac > 1) vu_check_height(x, (int)atom_getfloatarg(1, ac, av)); iemgui_size((void *)x, &x->x_gui); } static void vu_delta(t_vu *x, t_symbol *s, int ac, t_atom *av) {iemgui_delta((void *)x, &x->x_gui, s, ac, av);} static void vu_pos(t_vu *x, t_symbol *s, int ac, t_atom *av) {iemgui_pos((void *)x, &x->x_gui, s, ac, av);} static void vu_color(t_vu *x, t_symbol *s, int ac, t_atom *av) {iemgui_color((void *)x, &x->x_gui, s, ac, av);} static void vu_receive(t_vu *x, t_symbol *s) {iemgui_receive(x, &x->x_gui, s);} static void vu_label(t_vu *x, t_symbol *s) {iemgui_label((void *)x, &x->x_gui, s);} static void vu_label_pos(t_vu *x, t_symbol *s, int ac, t_atom *av) {iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);} static void vu_label_font(t_vu *x, t_symbol *s, int ac, t_atom *av) { iemgui_label_font((void *)x, &x->x_gui, s, ac, av); (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_CONFIG); } static void vu_float(t_vu *x, t_floatarg rms) { int i; int old = x->x_rms; if(rms <= IEM_VU_MINDB) x->x_rms = 0; else if(rms >= IEM_VU_MAXDB) x->x_rms = IEM_VU_STEPS; else { int i = (int)(2.0*(rms + IEM_VU_OFFSET)); x->x_rms = iemgui_vu_db2i[i]; } i = (int)(100.0*rms + 10000.5); rms = 0.01*(t_float)(i - 10000); x->x_fr = rms; outlet_float(x->x_out_rms, rms); x->x_updaterms = 1; if(x->x_rms != old) sys_queuegui(x, x->x_gui.x_glist, vu_draw_update); } static void vu_ft1(t_vu *x, t_floatarg peak) { int i; int old = x->x_peak; if(peak <= IEM_VU_MINDB) x->x_peak = 0; else if(peak >= IEM_VU_MAXDB) x->x_peak = IEM_VU_STEPS; else { int i = (int)(2.0*(peak + IEM_VU_OFFSET)); x->x_peak = iemgui_vu_db2i[i]; } i = (int)(100.0*peak + 10000.5); peak = 0.01*(t_float)(i - 10000); x->x_fp = peak; x->x_updatepeak = 1; if(x->x_peak != old) sys_queuegui(x, x->x_gui.x_glist, vu_draw_update); outlet_float(x->x_out_peak, peak); } static void vu_bang(t_vu *x) { outlet_float(x->x_out_peak, x->x_fp); outlet_float(x->x_out_rms, x->x_fr); x->x_updaterms = x->x_updatepeak = 1; sys_queuegui(x, x->x_gui.x_glist, vu_draw_update); } static void *vu_new(t_symbol *s, int argc, t_atom *argv) { t_vu *x = (t_vu *)iemgui_new(vu_class); int w = IEM_GUI_DEFAULTSIZE, h = IEM_VU_STEPS*IEM_VU_DEFAULTSIZE * IEM_GUI_DEFAULTSIZE_SCALE; int ldx = -1, ldy = -8 * IEM_GUI_DEFAULTSIZE_SCALE, f = 0, scale = 1; int fs = x->x_gui.x_fontsize; int ftbreak = IEM_BNG_DEFAULTBREAKFLASHTIME, fthold = IEM_BNG_DEFAULTHOLDFLASHTIME; char str[144]; IEMGUI_SETDRAWFUNCTIONS(x, vu); x->x_gui.x_bcol = 0x404040; if((argc >= 11)&&IS_A_FLOAT(argv,0)&&IS_A_FLOAT(argv,1) &&(IS_A_SYMBOL(argv,2)||IS_A_FLOAT(argv,2)) &&(IS_A_SYMBOL(argv,3)||IS_A_FLOAT(argv,3)) &&IS_A_FLOAT(argv,4)&&IS_A_FLOAT(argv,5) &&IS_A_FLOAT(argv,6)&&IS_A_FLOAT(argv,7) &&IS_A_FLOAT(argv,10)) { w = (int)atom_getfloatarg(0, argc, argv); h = (int)atom_getfloatarg(1, argc, argv); iemgui_new_getnames(&x->x_gui, 1, argv); x->x_gui.x_snd_unexpanded = x->x_gui.x_snd = gensym("nosndno"); /*no send*/ ldx = (int)atom_getfloatarg(4, argc, argv); ldy = (int)atom_getfloatarg(5, argc, argv); iem_inttofstyle(&x->x_gui.x_fsf, atom_getfloatarg(6, argc, argv)); fs = (int)atom_getfloatarg(7, argc, argv); iemgui_all_loadcolors(&x->x_gui, argv+8, NULL, argv+9); scale = (int)atom_getfloatarg(10, argc, argv); } else iemgui_new_getnames(&x->x_gui, 1, 0); if((argc == 12)&&IS_A_FLOAT(argv,11)) iem_inttosymargs(&x->x_gui.x_isa, atom_getfloatarg(11, argc, argv)); x->x_gui.x_fsf.x_snd_able = 0; x->x_gui.x_fsf.x_rcv_able = (0 != x->x_gui.x_rcv); if (x->x_gui.x_fsf.x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica"); else if(x->x_gui.x_fsf.x_font_style == 2) strcpy(x->x_gui.x_font, "times"); else { x->x_gui.x_fsf.x_font_style = 0; strcpy(x->x_gui.x_font, sys_font); } if(x->x_gui.x_fsf.x_rcv_able) pd_bind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); x->x_gui.x_ldx = ldx; x->x_gui.x_ldy = ldy; x->x_gui.x_fontsize = (fs < 4)?4:fs; x->x_gui.x_w = iemgui_clip_size(w); vu_check_height(x, h); if(scale != 0) scale = 1; x->x_scale = scale; x->x_peak = 0; x->x_rms = 0; x->x_fp = -101.0; x->x_fr = -101.0; iemgui_verify_snd_ne_rcv(&x->x_gui); inlet_new(&x->x_gui.x_obj, &x->x_gui.x_obj.ob_pd, &s_float, gensym("ft1")); x->x_out_rms = outlet_new(&x->x_gui.x_obj, &s_float); x->x_out_peak = outlet_new(&x->x_gui.x_obj, &s_float); x->x_gui.x_h /= IEMGUI_ZOOM(x); /* unzoom, to prevent double-application in iemgui_newzoom */ iemgui_newzoom(&x->x_gui); return (x); } static void vu_free(t_vu *x) { if(x->x_gui.x_fsf.x_rcv_able) pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); pdgui_stub_deleteforkey(x); } void g_vumeter_setup(void) { vu_class = class_new(gensym("vu"), (t_newmethod)vu_new, (t_method)vu_free, sizeof(t_vu), 0, A_GIMME, 0); class_addbang(vu_class,vu_bang); class_addfloat(vu_class,vu_float); class_addmethod(vu_class, (t_method)vu_ft1, gensym("ft1"), A_FLOAT, 0); class_addmethod(vu_class, (t_method)vu_dialog, gensym("dialog"), A_GIMME, 0); class_addmethod(vu_class, (t_method)vu_size, gensym("size"), A_GIMME, 0); class_addmethod(vu_class, (t_method)vu_scale, gensym("scale"), A_DEFFLOAT, 0); class_addmethod(vu_class, (t_method)vu_delta, gensym("delta"), A_GIMME, 0); class_addmethod(vu_class, (t_method)vu_pos, gensym("pos"), A_GIMME, 0); class_addmethod(vu_class, (t_method)vu_color, gensym("color"), A_GIMME, 0); class_addmethod(vu_class, (t_method)vu_receive, gensym("receive"), A_DEFSYM, 0); class_addmethod(vu_class, (t_method)vu_label, gensym("label"), A_DEFSYM, 0); class_addmethod(vu_class, (t_method)vu_label_pos, gensym("label_pos"), A_GIMME, 0); class_addmethod(vu_class, (t_method)vu_label_font, gensym("label_font"), A_GIMME, 0); class_addmethod(vu_class, (t_method)iemgui_zoom, gensym("zoom"), A_CANT, 0); vu_widgetbehavior.w_getrectfn = vu_getrect; vu_widgetbehavior.w_displacefn = iemgui_displace; vu_widgetbehavior.w_selectfn = iemgui_select; vu_widgetbehavior.w_activatefn = NULL; vu_widgetbehavior.w_deletefn = iemgui_delete; vu_widgetbehavior.w_visfn = iemgui_vis; vu_widgetbehavior.w_clickfn = NULL; class_setwidget(vu_class,&vu_widgetbehavior); class_setsavefn(vu_class, vu_save); class_setpropertiesfn(vu_class, vu_properties); } ================================================ FILE: libs/libpd/pure-data/src/m_atom.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ #include "m_pd.h" #include #include /* convenience routines for checking and getting values of atoms. There's no "pointer" version since there's nothing safe to return if there's an error. */ t_float atom_getfloat(const t_atom *a) { if (a->a_type == A_FLOAT) return (a->a_w.w_float); else return (0); } t_int atom_getint(const t_atom *a) { return (atom_getfloat(a)); } t_symbol *atom_getsymbol(const t_atom *a) /* LATER think about this more carefully */ { if (a->a_type == A_SYMBOL) return (a->a_w.w_symbol); else return (&s_float); } t_symbol *atom_gensym(const t_atom *a) /* this works better for graph labels */ { char buf[30]; if (a->a_type == A_SYMBOL) return (a->a_w.w_symbol); else if (a->a_type == A_FLOAT) sprintf(buf, "%g", a->a_w.w_float); else strcpy(buf, "???"); return (gensym(buf)); } t_float atom_getfloatarg(int which, int argc, const t_atom *argv) { if (argc <= which) return (0); argv += which; if (argv->a_type == A_FLOAT) return (argv->a_w.w_float); else return (0); } t_int atom_getintarg(int which, int argc, const t_atom *argv) { return (atom_getfloatarg(which, argc, argv)); } t_symbol *atom_getsymbolarg(int which, int argc, const t_atom *argv) { if (argc <= which) return (&s_); argv += which; if (argv->a_type == A_SYMBOL) return (argv->a_w.w_symbol); else return (&s_); } /* convert an atom into a string, in the reverse sense of binbuf_text (q.v.) * special attention is paid to symbols containing the special characters * ';', ',', '$', and '\'; these are quoted with a preceding '\', except that * the '$' only gets quoted if followed by a digit. */ void atom_string(const t_atom *a, char *buf, unsigned int bufsize) { char tbuf[30]; switch(a->a_type) { case A_SEMI: strcpy(buf, ";"); break; case A_COMMA: strcpy(buf, ","); break; case A_POINTER: strcpy(buf, "(pointer)"); break; case A_FLOAT: sprintf(tbuf, "%g", a->a_w.w_float); if (strlen(tbuf) < bufsize-1) strcpy(buf, tbuf); else if (a->a_w.w_float < 0) strcpy(buf, "-"); else strcpy(buf, "+"); break; case A_SYMBOL: case A_DOLLSYM: { const char *sp; unsigned int len; int quote; for (sp = a->a_w.w_symbol->s_name, len = 0, quote = 0; *sp; sp++, len++) if (*sp == ';' || *sp == ',' || *sp == '\\' || *sp == ' ' || (a->a_type == A_SYMBOL && *sp == '$' && sp[1] >= '0' && sp[1] <= '9')) quote = 1; if (quote) { char *bp = buf, *ep = buf + (bufsize-2); sp = a->a_w.w_symbol->s_name; while (bp < ep && *sp) { if (*sp == ';' || *sp == ',' || *sp == '\\' || *sp == ' ' || (a->a_type == A_SYMBOL && *sp == '$' && sp[1] >= '0' && sp[1] <= '9')) *bp++ = '\\'; *bp++ = *sp++; } if (*sp) *bp++ = '*'; *bp = 0; /* post("quote %s -> %s", a->a_w.w_symbol->s_name, buf); */ } else { if (len < bufsize-1) strcpy(buf, a->a_w.w_symbol->s_name); else { strncpy(buf, a->a_w.w_symbol->s_name, bufsize - 2); strcpy(buf + (bufsize - 2), "*"); } } } break; case A_DOLLAR: sprintf(buf, "$%d", a->a_w.w_index); break; default: bug("atom_string"); } } ================================================ FILE: libs/libpd/pure-data/src/m_binbuf.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ #include #include "m_pd.h" #include "s_stuff.h" #include "g_canvas.h" #include #include #ifdef HAVE_UNISTD_H #include #endif #ifdef _WIN32 #include #endif #include #include #include "m_private_utils.h" struct _binbuf { int b_n; t_atom *b_vec; }; t_binbuf *binbuf_new(void) { t_binbuf *x = (t_binbuf *)t_getbytes(sizeof(*x)); x->b_n = 0; x->b_vec = t_getbytes(0); return (x); } void binbuf_free(t_binbuf *x) { t_freebytes(x->b_vec, x->b_n * sizeof(*x->b_vec)); t_freebytes(x, sizeof(*x)); } t_binbuf *binbuf_duplicate(const t_binbuf *y) { t_binbuf *x = (t_binbuf *)t_getbytes(sizeof(*x)); x->b_n = y->b_n; x->b_vec = t_getbytes(x->b_n * sizeof(*x->b_vec)); memcpy(x->b_vec, y->b_vec, x->b_n * sizeof(*x->b_vec)); return (x); } void binbuf_clear(t_binbuf *x) { x->b_vec = t_resizebytes(x->b_vec, x->b_n * sizeof(*x->b_vec), 0); x->b_n = 0; } /* convert text to a binbuf */ void binbuf_text(t_binbuf *x, const char *text, size_t size) { char buf[MAXPDSTRING+1], *bufp, *ebuf = buf+MAXPDSTRING; const char *textp = text, *etext = text+size; t_atom *ap; int nalloc = 16, natom = 0; binbuf_clear(x); if (!binbuf_resize(x, nalloc)) return; ap = x->b_vec; while (1) { int type; /* skip leading space */ while ((textp != etext) && (*textp == ' ' || *textp == '\n' || *textp == '\r' || *textp == '\t')) textp++; if (textp == etext) break; if (*textp == ';') SETSEMI(ap), textp++; else if (*textp == ',') SETCOMMA(ap), textp++; else { /* it's an atom other than a comma or semi */ char c; int floatstate = 0, slash = 0, lastslash = 0, dollar = 0; bufp = buf; do { c = *bufp = *textp++; lastslash = slash; slash = (c == '\\'); if (floatstate >= 0) { int digit = (c >= '0' && c <= '9'), dot = (c == '.'), minus = (c == '-'), plusminus = (minus || (c == '+')), expon = (c == 'e' || c == 'E'); if (floatstate == 0) /* beginning */ { if (minus) floatstate = 1; else if (digit) floatstate = 2; else if (dot) floatstate = 3; else floatstate = -1; } else if (floatstate == 1) /* got minus */ { if (digit) floatstate = 2; else if (dot) floatstate = 3; else floatstate = -1; } else if (floatstate == 2) /* got digits */ { if (dot) floatstate = 4; else if (expon) floatstate = 6; else if (!digit) floatstate = -1; } else if (floatstate == 3) /* got '.' without digits */ { if (digit) floatstate = 5; else floatstate = -1; } else if (floatstate == 4) /* got '.' after digits */ { if (digit) floatstate = 5; else if (expon) floatstate = 6; else floatstate = -1; } else if (floatstate == 5) /* got digits after . */ { if (expon) floatstate = 6; else if (!digit) floatstate = -1; } else if (floatstate == 6) /* got 'e' */ { if (plusminus) floatstate = 7; else if (digit) floatstate = 8; else floatstate = -1; } else if (floatstate == 7) /* got plus or minus */ { if (digit) floatstate = 8; else floatstate = -1; } else if (floatstate == 8) /* got digits */ { if (!digit) floatstate = -1; } } if (!lastslash && c == '$' && (textp != etext && textp[0] >= '0' && textp[0] <= '9')) dollar = 1; if (!slash) bufp++; else if (lastslash) { bufp++; slash = 0; } } while (textp != etext && bufp != ebuf && (slash || (*textp != ' ' && *textp != '\n' && *textp != '\r' && *textp != '\t' &&*textp != ',' && *textp != ';'))); *bufp = 0; #if 0 post("binbuf_text: buf %s", buf); #endif if (floatstate == 2 || floatstate == 4 || floatstate == 5 || floatstate == 8) SETFLOAT(ap, atof(buf)); /* LATER try to figure out how to mix "$" and "\$" correctly; here, the backslashes were already stripped so we assume all "$" chars are real dollars. In fact, we only know at least one was. */ else if (dollar) { if (buf[0] != '$') dollar = 0; for (bufp = buf+1; *bufp; bufp++) if (*bufp < '0' || *bufp > '9') dollar = 0; if (dollar) SETDOLLAR(ap, atoi(buf+1)); else SETDOLLSYM(ap, gensym(buf)); } else SETSYMBOL(ap, gensym(buf)); } ap++; natom++; if (natom == nalloc) { if (!binbuf_resize(x, nalloc*2)) break; nalloc = nalloc * 2; ap = x->b_vec + natom; } if (textp == etext) break; } /* reallocate the vector to exactly the right size */ binbuf_resize(x, natom); } /* convert a binbuf to text; no null termination. */ void binbuf_gettext(const t_binbuf *x, char **bufp, int *lengthp) { char *buf = getbytes(0), *newbuf; int length = 0; char string[MAXPDSTRING]; const t_atom *ap; int indx; for (ap = x->b_vec, indx = x->b_n; indx--; ap++) { int newlength; if ((ap->a_type == A_SEMI || ap->a_type == A_COMMA) && length && buf[length-1] == ' ') length--; atom_string(ap, string, MAXPDSTRING); newlength = length + (int)strlen(string) + 1; if (!(newbuf = resizebytes(buf, length, newlength))) break; buf = newbuf; strcpy(buf + length, string); length = newlength; if (ap->a_type == A_SEMI) buf[length-1] = '\n'; else buf[length-1] = ' '; } if (length && buf[length-1] == ' ') { if ((newbuf = t_resizebytes(buf, length, length-1))) { buf = newbuf; length--; } } *bufp = buf; *lengthp = length; } /* LATER improve the out-of-space behavior below. Also fix this so that writing to file doesn't buffer everything together. */ void binbuf_add(t_binbuf *x, int argc, const t_atom *argv) { int previoussize = x->b_n; int newsize = previoussize + argc, i; t_atom *ap; if (!binbuf_resize(x, newsize)) { pd_error(0, "binbuf_addmessage: out of space"); return; } #if 0 startpost("binbuf_add: "); postatom(argc, argv); endpost(); #endif for (ap = x->b_vec + previoussize, i = argc; i--; ap++) *ap = *(argv++); } #define MAXADDMESSV 100 void binbuf_addv(t_binbuf *x, const char *fmt, ...) { va_list ap; t_atom arg[MAXADDMESSV], *at =arg; int nargs = 0; const char *fp = fmt; va_start(ap, fmt); while (1) { if (nargs >= MAXADDMESSV) { pd_error(0, "binbuf_addmessv: only %d allowed", MAXADDMESSV); break; } switch(*fp++) { case 'i': SETFLOAT(at, va_arg(ap, int)); break; case 'f': SETFLOAT(at, va_arg(ap, double)); break; case 's': SETSYMBOL(at, va_arg(ap, t_symbol *)); break; case ';': SETSEMI(at); break; case ',': SETCOMMA(at); break; default: goto done; } at++; nargs++; } done: va_end(ap); binbuf_add(x, nargs, arg); } /* add a binbuf to another one for saving. Semicolons and commas go to symbols ";", "'",; and inside symbols, characters ';', ',' and '$' get escaped. LATER also figure out about escaping white space */ void binbuf_addbinbuf(t_binbuf *x, const t_binbuf *y) { t_binbuf *z = binbuf_new(); int i, fixit; t_atom *ap; binbuf_add(z, y->b_n, y->b_vec); for (i = 0, ap = z->b_vec; i < z->b_n; i++, ap++) { char tbuf[MAXPDSTRING]; const char *s; switch (ap->a_type) { case A_FLOAT: break; case A_SEMI: SETSYMBOL(ap, gensym(";")); break; case A_COMMA: SETSYMBOL(ap, gensym(",")); break; case A_DOLLAR: sprintf(tbuf, "$%d", ap->a_w.w_index); SETSYMBOL(ap, gensym(tbuf)); break; case A_DOLLSYM: atom_string(ap, tbuf, MAXPDSTRING); SETSYMBOL(ap, gensym(tbuf)); break; case A_SYMBOL: for (s = ap->a_w.w_symbol->s_name, fixit = 0; *s; s++) if (*s == ';' || *s == ',' || *s == '$' || *s == '\\') fixit = 1; if (fixit) { atom_string(ap, tbuf, MAXPDSTRING); SETSYMBOL(ap, gensym(tbuf)); } break; default: bug("binbuf_addbinbuf"); } } binbuf_add(x, z->b_n, z->b_vec); binbuf_free(z); } void binbuf_addsemi(t_binbuf *x) { t_atom a; SETSEMI(&a); binbuf_add(x, 1, &a); } /* Supply atoms to a binbuf from a message, making the opposite changes from binbuf_addbinbuf. The symbol ";" goes to a semicolon, etc. */ void binbuf_restore(t_binbuf *x, int argc, const t_atom *argv) { int previoussize = x->b_n; int newsize = previoussize + argc, i; t_atom *ap; if (!binbuf_resize(x, newsize)) { pd_error(0, "binbuf_restore: out of space"); return; } for (ap = x->b_vec + previoussize, i = argc; i--; ap++) { if (argv->a_type == A_SYMBOL) { const char *str = argv->a_w.w_symbol->s_name, *str2; if (!strcmp(str, ";")) SETSEMI(ap); else if (!strcmp(str, ",")) SETCOMMA(ap); else { char buf[MAXPDSTRING], *sp1; const char *sp2, *usestr; int dollar = 0; if (strchr(str, '\\')) { int slashed = 0; for (sp1 = buf, sp2 = argv->a_w.w_symbol->s_name; *sp2 && sp1 < buf + (MAXPDSTRING-1); sp2++) { if (slashed) *sp1++ = *sp2, slashed = 0; else if (*sp2 == '\\') slashed = 1; else { if (*sp2 == '$' && sp2[1] >= '0' && sp2[1] <= '9') dollar = 1; *sp1++ = *sp2; slashed = 0; } } *sp1 = 0; usestr = buf; } else usestr = str; if (dollar || (usestr== str && (str2 = strchr(usestr, '$')) && str2[1] >= '0' && str2[1] <= '9')) { int dollsym = 0; if (*usestr != '$') dollsym = 1; else for (str2 = usestr + 1; *str2; str2++) if (*str2 < '0' || *str2 > '9') { dollsym = 1; break; } if (dollsym) SETDOLLSYM(ap, usestr == str ? argv->a_w.w_symbol : gensym(usestr)); else { int dollar = 0; sscanf(usestr + 1, "%d", &dollar); SETDOLLAR(ap, dollar); } } else SETSYMBOL(ap, usestr == str ? argv->a_w.w_symbol : gensym(usestr)); /* fprintf(stderr, "arg %s -> binbuf %s type %d\n", argv->a_w.w_symbol->s_name, usestr, ap->a_type); */ } argv++; } else *ap = *(argv++); } } void binbuf_print(const t_binbuf *x) { int i, startedpost = 0, newline = 1; for (i = 0; i < x->b_n; i++) { if (newline) { if (startedpost) endpost(); startpost(""); startedpost = 1; } postatom(1, x->b_vec + i); if (x->b_vec[i].a_type == A_SEMI) newline = 1; else newline = 0; } if (startedpost) endpost(); } int binbuf_getnatom(const t_binbuf *x) { return (x->b_n); } t_atom *binbuf_getvec(const t_binbuf *x) { return (x->b_vec); } int binbuf_resize(t_binbuf *x, int newsize) { t_atom *new = t_resizebytes(x->b_vec, x->b_n * sizeof(*x->b_vec), newsize * sizeof(*x->b_vec)); if (new) x->b_vec = new, x->b_n = newsize; return (new != 0); } int canvas_getdollarzero(void); /* JMZ: * s points to the first character after the $ * (e.g. if the org.symbol is "$1-bla", then s will point to "1-bla") * (e.g. org.symbol="hu-$1mu", s="1mu") * LATER: think about more complex $args, like ${$1+3} * * the return value holds the length of the $arg (in most cases: 1) * buf holds the expanded $arg * * if some error occurred, "-1" is returned * * e.g. "$1-bla" with list "10 20 30" * s="1-bla" * buf="10" * return value = 1; (s+1=="-bla") */ static int binbuf_expanddollsym(const char *s, char *buf, t_atom *dollar0, int ac, const t_atom *av, int tonew) { int argno = (int)atol(s); int arglen = 0; const char *cs = s; char c = *cs; *buf=0; while (c && (c>='0') && (c<='9')) { c = *cs++; arglen++; } if (cs==s) /* invalid $-expansion (like "$bla") */ { sprintf(buf, "$"); return 0; } else if (argno < 0 || argno > ac) /* undefined argument */ { if (!tonew) return 0; sprintf(buf, "$%d", argno); } else /* well formed; expand it */ { const t_atom *dollarvalue = (argno ? &av[argno-1] : dollar0); if (dollarvalue->a_type == A_SYMBOL) { strncpy(buf, dollarvalue->a_w.w_symbol->s_name, MAXPDSTRING/2-1); buf[MAXPDSTRING/2-2] = 0; } else atom_string(dollarvalue, buf, MAXPDSTRING/2-1); } return (arglen-1); } /* expand any '$' variables in the symbol s. "tonow" is set if this is in the context of a message to create a new object; in this case out-of-range '$' args become 0 - otherwise zero is returned and the caller has to check the result. */ /* LATER remove the dependence on the current canvas for $0; should be another argument. */ t_symbol *binbuf_realizedollsym(t_symbol *s, int ac, const t_atom *av, int tonew) { char buf[MAXPDSTRING]; char buf2[MAXPDSTRING]; const char*str=s->s_name; char*substr; int next=0; t_atom dollarnull; SETFLOAT(&dollarnull, canvas_getdollarzero()); buf2[0] = buf2[MAXPDSTRING-1] = 0; substr=strchr(str, '$'); if (!substr || substr-str >= MAXPDSTRING) return (s); strncpy(buf2, str, (substr-str)); buf2[substr-str] = 0; str=substr+1; while((next=binbuf_expanddollsym(str, buf, &dollarnull, ac, av, tonew))>=0) { /* * JMZ: i am not sure what this means, so i might have broken it * it seems like that if "tonew" is set and the $arg cannot be expanded * (or the dollarsym is in reality a A_DOLLAR) * 0 is returned from binbuf_realizedollsym * this happens, when expanding in a message-box, but does not happen * when the A_DOLLSYM is the name of a subpatch */ if (!tonew && (0==next) && (0==*buf)) { return 0; /* JMZ: this should mimic the original behaviour */ } strncat(buf2, buf, MAXPDSTRING-strlen(buf2)-1); str+=next; substr=strchr(str, '$'); if (substr) { unsigned long n = substr-str; if(n>MAXPDSTRING-strlen(buf2)-1) n=MAXPDSTRING-strlen(buf2)-1; strncat(buf2, str, n); str=substr+1; } else { strncat(buf2, str, MAXPDSTRING-strlen(buf2)-1); goto done; } } done: return (gensym(buf2)); } #define SMALLMSG 5 #define HUGEMSG 1000 void binbuf_eval(const t_binbuf *x, t_pd *target, int argc, const t_atom *argv) { t_atom smallstack[SMALLMSG], *mstack, *msp; const t_atom *at = x->b_vec; int ac = x->b_n; int nargs, maxnargs = 0; t_pd *initial_target = target; if (ac <= SMALLMSG) mstack = smallstack; else { #if 1 /* count number of args in biggest message. The weird treatment of "pd_objectmaker" is because when the message goes out to objectmaker, commas and semis are passed on as regular args (see below). We're tacitly assuming here that the pd_objectmaker target can't come up via a named destination in the message, only because the original "target" points there. */ if (target == &pd_objectmaker) maxnargs = ac; else { int i, j = (target ? 0 : -1); for (i = 0; i < ac; i++) { if (at[i].a_type == A_SEMI) j = -1; else if (at[i].a_type == A_COMMA) j = 0; else if (++j > maxnargs) maxnargs = j; } } if (maxnargs <= SMALLMSG) mstack = smallstack; else ALLOCA(t_atom, mstack, maxnargs, HUGEMSG); #else /* just pessimistically allocate enough to hold everything at once. This turned out to run slower in a simple benchmark I tried, perhaps because the extra memory allocation hurt the cache hit rate. */ maxnargs = ac; ALLOCA(t_atom, mstack, maxnargs, HUGEMSG); #endif } msp = mstack; while (1) { t_pd *nexttarget; /* get a target. */ while (!target) { t_symbol *s; while (ac && (at->a_type == A_SEMI || at->a_type == A_COMMA)) ac--, at++; if (!ac) break; if (at->a_type == A_DOLLAR) { if (at->a_w.w_index <= 0 || at->a_w.w_index > argc) { pd_error(initial_target, "$%d: not enough arguments supplied", at->a_w.w_index); goto cleanup; } else if (argv[at->a_w.w_index-1].a_type != A_SYMBOL) { pd_error(initial_target, "$%d: symbol needed as message destination", at->a_w.w_index); goto cleanup; } else s = argv[at->a_w.w_index-1].a_w.w_symbol; } else if (at->a_type == A_DOLLSYM) { if (!(s = binbuf_realizedollsym(at->a_w.w_symbol, argc, argv, 0))) { pd_error(initial_target, "$%s: not enough arguments supplied", at->a_w.w_symbol->s_name); goto cleanup; } } else s = atom_getsymbol(at); if (!(target = s->s_thing)) { pd_error(initial_target, "%s: no such object ", s->s_name); cleanup: do at++, ac--; while (ac && at->a_type != A_SEMI); /* LATER eat args until semicolon and continue */ continue; } else { at++, ac--; break; } } if (!ac) break; nargs = 0; nexttarget = target; while (1) { t_symbol *s9; if (!ac) goto gotmess; switch (at->a_type) { case A_SEMI: /* semis and commas in new message just get bashed to a symbol. This is needed so you can pass them to "expr." */ if (target == &pd_objectmaker) { SETSYMBOL(msp, gensym(";")); break; } else { nexttarget = 0; goto gotmess; } case A_COMMA: if (target == &pd_objectmaker) { SETSYMBOL(msp, gensym(",")); break; } else goto gotmess; case A_FLOAT: case A_SYMBOL: *msp = *at; break; case A_DOLLAR: if (at->a_w.w_index > 0 && at->a_w.w_index <= argc) *msp = argv[at->a_w.w_index-1]; else if (at->a_w.w_index == 0) SETFLOAT(msp, canvas_getdollarzero()); else { if (target == &pd_objectmaker) SETFLOAT(msp, 0); else { pd_error(target, "$%d: argument number out of range", at->a_w.w_index); SETFLOAT(msp, 0); } } break; case A_DOLLSYM: s9 = binbuf_realizedollsym(at->a_w.w_symbol, argc, argv, target == &pd_objectmaker); if (!s9) { pd_error(target, "%s: argument number out of range", at->a_w.w_symbol->s_name); SETSYMBOL(msp, at->a_w.w_symbol); } else SETSYMBOL(msp, s9); break; default: bug("bad item in binbuf"); goto broken; } msp++; ac--; at++; nargs++; } gotmess: if (nargs) { switch (mstack->a_type) { case A_SYMBOL: typedmess(target, mstack->a_w.w_symbol, nargs-1, mstack+1); break; case A_FLOAT: if (nargs == 1) pd_float(target, mstack->a_w.w_float); else pd_list(target, 0, nargs, mstack); break; case A_POINTER: if (nargs == 1) pd_pointer(target, mstack->a_w.w_gpointer); else pd_list(target, 0, nargs, mstack); break; default: bug("bad selector"); break; } } msp = mstack; if (!ac) break; target = nexttarget; at++; ac--; } broken: if (maxnargs > SMALLMSG) FREEA(t_atom, mstack, maxnargs, HUGEMSG); } int binbuf_read(t_binbuf *b, const char *filename, const char *dirname, int crflag) { long length; int fd; int readret; char *buf; char namebuf[MAXPDSTRING]; if (*dirname) snprintf(namebuf, MAXPDSTRING-1, "%s/%s", dirname, filename); else snprintf(namebuf, MAXPDSTRING-1, "%s", filename); namebuf[MAXPDSTRING-1] = 0; if ((fd = sys_open(namebuf, 0)) < 0) { fprintf(stderr, "open: "); perror(namebuf); return (1); } if ((length = (long)lseek(fd, 0, SEEK_END)) < 0 || lseek(fd, 0, SEEK_SET) < 0 || !(buf = t_getbytes(length))) { fprintf(stderr, "lseek: "); perror(namebuf); close(fd); return(1); } if ((readret = (int)read(fd, buf, length)) < length) { fprintf(stderr, "read (%d %ld) -> %d\n", fd, length, readret); perror(namebuf); close(fd); t_freebytes(buf, length); return(1); } /* optionally map carriage return to semicolon */ if (crflag) { int i; for (i = 0; i < length; i++) if (buf[i] == '\n') buf[i] = ';'; } binbuf_text(b, buf, length); #if 0 startpost("binbuf_read "); postatom(b->b_n, b->b_vec); endpost(); #endif t_freebytes(buf, length); close(fd); return (0); } /* read a binbuf from a file, via the search patch of a canvas */ int binbuf_read_via_canvas(t_binbuf *b, const char *filename, const t_canvas *canvas, int crflag) { int filedesc; char buf[MAXPDSTRING], *bufptr; if ((filedesc = canvas_open(canvas, filename, "", buf, &bufptr, MAXPDSTRING, 0)) < 0) { pd_error(0, "%s: can't open", filename); return (1); } else close (filedesc); if (binbuf_read(b, bufptr, buf, crflag)) return (1); else return (0); } /* old version */ int binbuf_read_via_path(t_binbuf *b, const char *filename, const char *dirname, int crflag) { int filedesc; char buf[MAXPDSTRING], *bufptr; if ((filedesc = open_via_path( dirname, filename, "", buf, &bufptr, MAXPDSTRING, 0)) < 0) { pd_error(0, "%s: can't open", filename); return (1); } else close (filedesc); if (binbuf_read(b, bufptr, buf, crflag)) return (1); else return (0); } #define WBUFSIZE 4096 static t_binbuf *binbuf_convert(const t_binbuf *oldb, int maxtopd); /* write a binbuf to a text file. If "crflag" is set we suppress semicolons. */ int binbuf_write(const t_binbuf *x, const char *filename, const char *dir, int crflag) { FILE *f = 0; char sbuf[WBUFSIZE], fbuf[MAXPDSTRING], *bp = sbuf, *ep = sbuf + WBUFSIZE; t_atom *ap; t_binbuf *y = 0; const t_binbuf *z = x; int indx; if (*dir) snprintf(fbuf, MAXPDSTRING-1, "%s/%s", dir, filename); else snprintf(fbuf, MAXPDSTRING-1, "%s", filename); fbuf[MAXPDSTRING-1] = 0; if (!strcmp(filename + strlen(filename) - 4, ".pat") || !strcmp(filename + strlen(filename) - 4, ".mxt")) { y = binbuf_convert(x, 0); z = y; } if (!(f = sys_fopen(fbuf, "w"))) goto fail; for (ap = z->b_vec, indx = z->b_n; indx--; ap++) { int length; /* estimate how many characters will be needed. Printing out symbols may need extra characters for inserting backslashes. */ if (ap->a_type == A_SYMBOL || ap->a_type == A_DOLLSYM) length = 80 + (int)strlen(ap->a_w.w_symbol->s_name); else length = 40; if (ep - bp < length) { if (fwrite(sbuf, bp-sbuf, 1, f) < 1) goto fail; bp = sbuf; } if ((ap->a_type == A_SEMI || ap->a_type == A_COMMA) && bp > sbuf && bp[-1] == ' ') bp--; if (!crflag || ap->a_type != A_SEMI) { atom_string(ap, bp, (unsigned int)((ep-bp)-2)); length = (int)strlen(bp); bp += length; } if (ap->a_type == A_SEMI) { *bp++ = '\n'; } else { *bp++ = ' '; } } if (fwrite(sbuf, bp-sbuf, 1, f) < 1) goto fail; if (fflush(f) != 0) goto fail; if (y) binbuf_free(y); fclose(f); return (0); fail: if (y) binbuf_free(y); if (f) fclose(f); return (1); } /* The following routine attempts to convert from max to pd or back. The max to pd direction is working OK but you will need to make lots of abstractions for objects like "gate" which don't exist in Pd. conversion from Pd to Max hasn't been tested for patches with subpatches yet! */ #define MAXSTACK 1000 #define ISSYMBOL(a, b) ((a)->a_type == A_SYMBOL && \ !strcmp((a)->a_w.w_symbol->s_name, (b))) static t_binbuf *binbuf_convert(const t_binbuf *oldb, int maxtopd) { t_binbuf *newb = binbuf_new(); t_atom *vec = oldb->b_vec; t_int n = oldb->b_n, nextindex, stackdepth = 0, stack[MAXSTACK] = {0}, nobj = 0, gotfontsize = 0; int i; t_atom outmess[MAXSTACK], *nextmess; t_float fontsize = 10; if (!maxtopd) binbuf_addv(newb, "ss;", gensym("max"), gensym("v2")); for (nextindex = 0; nextindex < n; ) { int endmess, natom; const char *first, *second, *third; for (endmess = (int)nextindex; endmess < n && vec[endmess].a_type != A_SEMI; endmess++) ; if (endmess == n) break; if (endmess == nextindex || endmess == nextindex + 1 || vec[nextindex].a_type != A_SYMBOL || vec[nextindex+1].a_type != A_SYMBOL) { nextindex = endmess + 1; continue; } natom = endmess - (int)nextindex; if (natom > MAXSTACK-10) natom = MAXSTACK-10; nextmess = vec + nextindex; first = nextmess->a_w.w_symbol->s_name; second = (nextmess+1)->a_w.w_symbol->s_name; if (maxtopd) { /* case 1: importing a ".pat" file into Pd. */ /* dollar signs in file translate to symbols */ for (i = 0; i < natom; i++) { if (nextmess[i].a_type == A_DOLLAR) { char buf[100]; sprintf(buf, "$%d", nextmess[i].a_w.w_index); SETSYMBOL(nextmess+i, gensym(buf)); } else if (nextmess[i].a_type == A_DOLLSYM) { char buf[100]; sprintf(buf, "%s", nextmess[i].a_w.w_symbol->s_name); SETSYMBOL(nextmess+i, gensym(buf)); } } if (!strcmp(first, "#N")) { if (!strcmp(second, "vpatcher")) { if (stackdepth >= MAXSTACK) { pd_error(0, "stack depth exceeded: too many embedded patches"); return (newb); } stack[stackdepth] = nobj; stackdepth++; nobj = 0; binbuf_addv(newb, "ssfffff;", gensym("#N"), gensym("canvas"), atom_getfloatarg(2, natom, nextmess), atom_getfloatarg(3, natom, nextmess), atom_getfloatarg(4, natom, nextmess) - atom_getfloatarg(2, natom, nextmess), atom_getfloatarg(5, natom, nextmess) - atom_getfloatarg(3, natom, nextmess), (t_float)sys_defaultfont); } } if (!strcmp(first, "#P")) { /* drop initial "hidden" flag */ if (!strcmp(second, "hidden")) { nextmess++; natom--; second = (nextmess+1)->a_w.w_symbol->s_name; } if (natom >= 7 && !strcmp(second, "newobj") && (ISSYMBOL(&nextmess[6], "patcher") || ISSYMBOL(&nextmess[6], "p"))) { binbuf_addv(newb, "ssffss;", gensym("#X"), gensym("restore"), atom_getfloatarg(2, natom, nextmess), atom_getfloatarg(3, natom, nextmess), gensym("pd"), atom_getsymbolarg(7, natom, nextmess)); if (stackdepth) stackdepth--; nobj = stack[stackdepth]; nobj++; } else if (!strcmp(second, "newex") || !strcmp(second, "newobj")) { t_symbol *classname = atom_getsymbolarg(6, natom, nextmess); if (classname == gensym("trigger") || classname == gensym("t")) { for (i = 7; i < natom; i++) if (nextmess[i].a_type == A_SYMBOL && nextmess[i].a_w.w_symbol == gensym("i")) nextmess[i].a_w.w_symbol = gensym("f"); } if (classname == gensym("table")) classname = gensym("TABLE"); SETSYMBOL(outmess, gensym("#X")); SETSYMBOL(outmess + 1, gensym("obj")); outmess[2] = nextmess[2]; outmess[3] = nextmess[3]; SETSYMBOL(outmess+4, classname); for (i = 7; i < natom; i++) outmess[i-2] = nextmess[i]; SETSEMI(outmess + natom - 2); binbuf_add(newb, natom - 1, outmess); nobj++; } else if (!strcmp(second, "message") || !strcmp(second, "comment")) { SETSYMBOL(outmess, gensym("#X")); SETSYMBOL(outmess + 1, gensym( (strcmp(second, "message") ? "text" : "msg"))); outmess[2] = nextmess[2]; outmess[3] = nextmess[3]; for (i = 6; i < natom; i++) outmess[i-2] = nextmess[i]; SETSEMI(outmess + natom - 2); binbuf_add(newb, natom - 1, outmess); nobj++; } else if (!strcmp(second, "button")) { binbuf_addv(newb, "ssffs;", gensym("#X"), gensym("obj"), atom_getfloatarg(2, natom, nextmess), atom_getfloatarg(3, natom, nextmess), gensym("bng")); nobj++; } else if (!strcmp(second, "number") || !strcmp(second, "flonum")) { binbuf_addv(newb, "ssff;", gensym("#X"), gensym("floatatom"), atom_getfloatarg(2, natom, nextmess), atom_getfloatarg(3, natom, nextmess)); nobj++; } else if (!strcmp(second, "slider")) { t_float inc = atom_getfloatarg(7, natom, nextmess); if (inc <= 0) inc = 1; binbuf_addv(newb, "ssffsffffffsssfffffffff;", gensym("#X"), gensym("obj"), atom_getfloatarg(2, natom, nextmess), atom_getfloatarg(3, natom, nextmess), gensym("vsl"), atom_getfloatarg(4, natom, nextmess), atom_getfloatarg(5, natom, nextmess), atom_getfloatarg(6, natom, nextmess), atom_getfloatarg(6, natom, nextmess) + (atom_getfloatarg(5, natom, nextmess) - 1) * inc, 0., 0., gensym("empty"), gensym("empty"), gensym("empty"), 0., -8., 0., 8., -262144., -1., -1., 0., 1.); nobj++; } else if (!strcmp(second, "toggle")) { binbuf_addv(newb, "ssffs;", gensym("#X"), gensym("obj"), atom_getfloatarg(2, natom, nextmess), atom_getfloatarg(3, natom, nextmess), gensym("tgl")); nobj++; } else if (!strcmp(second, "inlet")) { binbuf_addv(newb, "ssffs;", gensym("#X"), gensym("obj"), atom_getfloatarg(2, natom, nextmess), atom_getfloatarg(3, natom, nextmess), gensym((natom > 5 ? "inlet~" : "inlet"))); nobj++; } else if (!strcmp(second, "outlet")) { binbuf_addv(newb, "ssffs;", gensym("#X"), gensym("obj"), atom_getfloatarg(2, natom, nextmess), atom_getfloatarg(3, natom, nextmess), gensym((natom > 5 ? "outlet~" : "outlet"))); nobj++; } else if (!strcmp(second, "user")) { third = (nextmess+2)->a_w.w_symbol->s_name; if (!strcmp(third, "hslider")) { t_float range = atom_getfloatarg(7, natom, nextmess); t_float multiplier = atom_getfloatarg(8, natom, nextmess); t_float offset = atom_getfloatarg(9, natom, nextmess); binbuf_addv(newb, "ssffsffffffsssfffffffff;", gensym("#X"), gensym("obj"), atom_getfloatarg(3, natom, nextmess), atom_getfloatarg(4, natom, nextmess), gensym("hsl"), atom_getfloatarg(6, natom, nextmess), atom_getfloatarg(5, natom, nextmess), offset, range + offset, 0., 0., gensym("empty"), gensym("empty"), gensym("empty"), 0., -8., 0., 8., -262144., -1., -1., 0., 1.); } else if (!strcmp(third, "uslider")) { t_float range = atom_getfloatarg(7, natom, nextmess); t_float multiplier = atom_getfloatarg(8, natom, nextmess); t_float offset = atom_getfloatarg(9, natom, nextmess); binbuf_addv(newb, "ssffsffffffsssfffffffff;", gensym("#X"), gensym("obj"), atom_getfloatarg(3, natom, nextmess), atom_getfloatarg(4, natom, nextmess), gensym("vsl"), atom_getfloatarg(5, natom, nextmess), atom_getfloatarg(6, natom, nextmess), offset, range + offset, 0., 0., gensym("empty"), gensym("empty"), gensym("empty"), 0., -8., 0., 8., -262144., -1., -1., 0., 1.); } else binbuf_addv(newb, "ssffs;", gensym("#X"), gensym("obj"), atom_getfloatarg(3, natom, nextmess), atom_getfloatarg(4, natom, nextmess), atom_getsymbolarg(2, natom, nextmess)); nobj++; } else if (!strcmp(second, "connect")|| !strcmp(second, "fasten")) { binbuf_addv(newb, "ssffff;", gensym("#X"), gensym("connect"), nobj - atom_getfloatarg(2, natom, nextmess) - 1, atom_getfloatarg(3, natom, nextmess), nobj - atom_getfloatarg(4, natom, nextmess) - 1, atom_getfloatarg(5, natom, nextmess)); } } } else /* Pd to Max */ { if (!strcmp(first, "#N")) { if (!strcmp(second, "canvas")) { t_float x, y; if (stackdepth >= MAXSTACK) { pd_error(0, "stack depth exceeded: too many embedded patches"); return (newb); } stack[stackdepth] = nobj; stackdepth++; nobj = 0; if(!gotfontsize) { /* only the first canvas sets the font size */ fontsize = atom_getfloatarg(6, natom, nextmess); gotfontsize = 1; } x = atom_getfloatarg(2, natom, nextmess); y = atom_getfloatarg(3, natom, nextmess); binbuf_addv(newb, "ssffff;", gensym("#N"), gensym("vpatcher"), x, y, atom_getfloatarg(4, natom, nextmess) + x, atom_getfloatarg(5, natom, nextmess) + y); } } if (!strcmp(first, "#X")) { if (natom >= 5 && !strcmp(second, "restore") && (ISSYMBOL (&nextmess[4], "pd"))) { binbuf_addv(newb, "ss;", gensym("#P"), gensym("pop")); SETSYMBOL(outmess, gensym("#P")); SETSYMBOL(outmess + 1, gensym("newobj")); outmess[2] = nextmess[2]; outmess[3] = nextmess[3]; SETFLOAT(outmess + 4, 50.*(natom-5)); SETFLOAT(outmess + 5, fontsize); SETSYMBOL(outmess + 6, gensym("p")); for (i = 5; i < natom; i++) outmess[i+2] = nextmess[i]; SETSEMI(outmess + natom + 2); binbuf_add(newb, natom + 3, outmess); if (stackdepth) stackdepth--; nobj = stack[stackdepth]; nobj++; } else if (!strcmp(second, "obj")) { t_symbol *classname = atom_getsymbolarg(4, natom, nextmess); if (classname == gensym("inlet")) binbuf_addv(newb, "ssfff;", gensym("#P"), gensym("inlet"), atom_getfloatarg(2, natom, nextmess), atom_getfloatarg(3, natom, nextmess), 10. + fontsize); else if (classname == gensym("inlet~")) binbuf_addv(newb, "ssffff;", gensym("#P"), gensym("inlet"), atom_getfloatarg(2, natom, nextmess), atom_getfloatarg(3, natom, nextmess), 10. + fontsize, 1.); else if (classname == gensym("outlet")) binbuf_addv(newb, "ssfff;", gensym("#P"), gensym("outlet"), atom_getfloatarg(2, natom, nextmess), atom_getfloatarg(3, natom, nextmess), 10. + fontsize); else if (classname == gensym("outlet~")) binbuf_addv(newb, "ssffff;", gensym("#P"), gensym("outlet"), atom_getfloatarg(2, natom, nextmess), atom_getfloatarg(3, natom, nextmess), 10. + fontsize, 1.); else if (classname == gensym("bng")) binbuf_addv(newb, "ssffff;", gensym("#P"), gensym("button"), atom_getfloatarg(2, natom, nextmess), atom_getfloatarg(3, natom, nextmess), atom_getfloatarg(5, natom, nextmess), 0.); else if (classname == gensym("tgl")) binbuf_addv(newb, "ssffff;", gensym("#P"), gensym("toggle"), atom_getfloatarg(2, natom, nextmess), atom_getfloatarg(3, natom, nextmess), atom_getfloatarg(5, natom, nextmess), 0.); else if (classname == gensym("vsl")) binbuf_addv(newb, "ssffffff;", gensym("#P"), gensym("slider"), atom_getfloatarg(2, natom, nextmess), atom_getfloatarg(3, natom, nextmess), atom_getfloatarg(5, natom, nextmess), atom_getfloatarg(6, natom, nextmess), (atom_getfloatarg(8, natom, nextmess) - atom_getfloatarg(7, natom, nextmess)) / (atom_getfloatarg(6, natom, nextmess) == 1? 1 : atom_getfloatarg(6, natom, nextmess) - 1), atom_getfloatarg(7, natom, nextmess)); else if (classname == gensym("hsl")) { t_float slmin = atom_getfloatarg(7, natom, nextmess); t_float slmax = atom_getfloatarg(8, natom, nextmess); binbuf_addv(newb, "sssffffffff;", gensym("#P"), gensym("user"), gensym("hslider"), atom_getfloatarg(2, natom, nextmess), atom_getfloatarg(3, natom, nextmess), atom_getfloatarg(6, natom, nextmess), atom_getfloatarg(5, natom, nextmess), slmax - slmin + 1, /* range */ 1., /* multiplier */ slmin, /* offset */ 0.); } else if ( (classname == gensym("trigger")) || (classname == gensym("t")) ) { t_symbol *arg; SETSYMBOL(outmess, gensym("#P")); SETSYMBOL(outmess + 1, gensym("newex")); outmess[2] = nextmess[2]; outmess[3] = nextmess[3]; SETFLOAT(outmess + 4, 50.*(natom-4)); SETFLOAT(outmess + 5, fontsize); outmess[6] = nextmess[4]; for (i = 5; i < natom; i++) { arg = atom_getsymbolarg(i, natom, nextmess); if (arg == gensym("a")) SETSYMBOL(outmess + i + 2, gensym("l")); else if (arg == gensym("anything")) SETSYMBOL(outmess + i + 2, gensym("l")); else if (arg == gensym("bang")) SETSYMBOL(outmess + i + 2, gensym("b")); else if (arg == gensym("float")) SETSYMBOL(outmess + i + 2, gensym("f")); else if (arg == gensym("list")) SETSYMBOL(outmess + i + 2, gensym("l")); else if (arg == gensym("symbol")) SETSYMBOL(outmess + i + 2, gensym("s")); else outmess[i+2] = nextmess[i]; } SETSEMI(outmess + natom + 2); binbuf_add(newb, natom + 3, outmess); } else { SETSYMBOL(outmess, gensym("#P")); SETSYMBOL(outmess + 1, gensym("newex")); outmess[2] = nextmess[2]; outmess[3] = nextmess[3]; SETFLOAT(outmess + 4, 50.*(natom-4)); SETFLOAT(outmess + 5, fontsize); for (i = 4; i < natom; i++) outmess[i+2] = nextmess[i]; if (classname == gensym("osc~")) SETSYMBOL(outmess + 6, gensym("cycle~")); SETSEMI(outmess + natom + 2); binbuf_add(newb, natom + 3, outmess); } nobj++; } else if (!strcmp(second, "msg") || !strcmp(second, "text")) { SETSYMBOL(outmess, gensym("#P")); SETSYMBOL(outmess + 1, gensym( (strcmp(second, "msg") ? "comment" : "message"))); outmess[2] = nextmess[2]; outmess[3] = nextmess[3]; SETFLOAT(outmess + 4, 50.*(natom-4)); SETFLOAT(outmess + 5, fontsize); for (i = 4; i < natom; i++) outmess[i+2] = nextmess[i]; SETSEMI(outmess + natom + 2); binbuf_add(newb, natom + 3, outmess); nobj++; } else if (!strcmp(second, "floatatom")) { t_float width = atom_getfloatarg(4, natom, nextmess)*fontsize; if(width<8) width = 150; /* if pd width=0, set it big */ binbuf_addv(newb, "ssfff;", gensym("#P"), gensym("flonum"), atom_getfloatarg(2, natom, nextmess), atom_getfloatarg(3, natom, nextmess), width); nobj++; } else if (!strcmp(second, "connect")) { binbuf_addv(newb, "ssffff;", gensym("#P"), gensym("connect"), nobj - atom_getfloatarg(2, natom, nextmess) - 1, atom_getfloatarg(3, natom, nextmess), nobj - atom_getfloatarg(4, natom, nextmess) - 1, atom_getfloatarg(5, natom, nextmess)); } } } nextindex = endmess + 1; } if (!maxtopd) binbuf_addv(newb, "ss;", gensym("#P"), gensym("pop")); #if 0 binbuf_write(newb, "import-result.pd", "/tmp", 0); #endif return (newb); } /* LATER make this evaluate the file on-the-fly. */ /* LATER figure out how to log errors */ void binbuf_evalfile(t_symbol *name, t_symbol *dir) { t_binbuf *b = binbuf_new(); int import = !strcmp(name->s_name + strlen(name->s_name) - 4, ".pat") || !strcmp(name->s_name + strlen(name->s_name) - 4, ".mxt"); int dspstate = canvas_suspend_dsp(); /* set filename so that new canvases can pick them up */ glob_setfilename(0, name, dir); if (binbuf_read(b, name->s_name, dir->s_name, 0)) pd_error(0, "%s: read failed; %s", name->s_name, strerror(errno)); else { /* save bindings of symbols #N, #A (and restore afterward) */ t_pd *bounda = gensym("#A")->s_thing, *boundn = s__N.s_thing; gensym("#A")->s_thing = 0; s__N.s_thing = &pd_canvasmaker; if (import) { t_binbuf *newb = binbuf_convert(b, 1); binbuf_free(b); b = newb; } binbuf_eval(b, 0, 0, 0); /* avoid crashing if no canvas was created by binbuf eval */ if (s__X.s_thing && *s__X.s_thing == canvas_class) canvas_initbang((t_canvas *)(s__X.s_thing)); /* JMZ*/ gensym("#A")->s_thing = bounda; s__N.s_thing = boundn; } glob_setfilename(0, &s_, &s_); binbuf_free(b); canvas_resume_dsp(dspstate); } /* save a text object to a binbuf for a file or copy buf */ void binbuf_savetext(const t_binbuf *bfrom, t_binbuf *bto) { int k, n = binbuf_getnatom(bfrom); const t_atom *ap = binbuf_getvec(bfrom); t_atom at; for (k = 0; k < n; k++) { if (ap[k].a_type == A_FLOAT || (ap[k].a_type == A_SYMBOL && !strchr(ap[k].a_w.w_symbol->s_name, ';') && !strchr(ap[k].a_w.w_symbol->s_name, ',') && !strchr(ap[k].a_w.w_symbol->s_name, '$'))) binbuf_add(bto, 1, &ap[k]); else { char buf[MAXPDSTRING+1]; atom_string(&ap[k], buf, MAXPDSTRING); SETSYMBOL(&at, gensym(buf)); binbuf_add(bto, 1, &at); } } binbuf_addsemi(bto); } ================================================ FILE: libs/libpd/pure-data/src/m_class.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ #define PD_CLASS_DEF #include "m_pd.h" #include "m_imp.h" #include "s_stuff.h" #include "g_canvas.h" #include #ifdef HAVE_UNISTD_H #include #endif #ifdef _WIN32 #include #endif #include #include #include #include "m_private_utils.h" static t_symbol *class_loadsym; /* name under which an extern is invoked */ static void pd_defaultfloat(t_pd *x, t_float f); static void pd_defaultlist(t_pd *x, t_symbol *s, int argc, t_atom *argv); t_pd pd_objectmaker; /* factory for creating "object" boxes */ t_pd pd_canvasmaker; /* factory for creating canvases */ static t_symbol *class_extern_dir; #ifdef PDINSTANCE static t_class *class_list = 0; PERTHREAD t_pdinstance *pd_this = NULL; t_pdinstance **pd_instances; int pd_ninstances; #else t_symbol s_pointer, s_float, s_symbol, s_bang, s_list, s_anything, s_signal, s__N, s__X, s_x, s_y, s_; #endif t_pdinstance pd_maininstance; static t_symbol *dogensym(const char *s, t_symbol *oldsym, t_pdinstance *pdinstance); void x_midi_newpdinstance( void); void x_midi_freepdinstance( void); void s_inter_newpdinstance( void); void s_inter_free(t_instanceinter *inter); void g_canvas_newpdinstance( void); void g_canvas_freepdinstance( void); void d_ugen_newpdinstance( void); void d_ugen_freepdinstance( void); void new_anything(void *dummy, t_symbol *s, int argc, t_atom *argv); void s_stuff_newpdinstance(void) { STUFF = getbytes(sizeof(*STUFF)); STUFF->st_externlist = STUFF->st_searchpath = STUFF->st_staticpath = STUFF->st_helppath = STUFF->st_temppath = 0; STUFF->st_schedblocksize = STUFF->st_blocksize = DEFDACBLKSIZE; STUFF->st_dacsr = DEFDACSAMPLERATE; STUFF->st_printhook = sys_printhook; STUFF->st_impdata = NULL; } void s_stuff_freepdinstance(void) { freebytes(STUFF, sizeof(*STUFF)); } static t_pdinstance *pdinstance_init(t_pdinstance *x) { int i; x->pd_systime = 0; x->pd_clock_setlist = 0; x->pd_canvaslist = 0; x->pd_templatelist = 0; x->pd_symhash = getbytes(SYMTABHASHSIZE * sizeof(*x->pd_symhash)); for (i = 0; i < SYMTABHASHSIZE; i++) x->pd_symhash[i] = 0; #ifdef PDINSTANCE dogensym("pointer", &x->pd_s_pointer, x); dogensym("float", &x->pd_s_float, x); dogensym("symbol", &x->pd_s_symbol, x); dogensym("bang", &x->pd_s_bang, x); dogensym("list", &x->pd_s_list, x); dogensym("anything", &x->pd_s_anything, x); dogensym("signal", &x->pd_s_signal, x); dogensym("#N", &x->pd_s__N, x); dogensym("#X", &x->pd_s__X, x); dogensym("x", &x->pd_s_x, x); dogensym("y", &x->pd_s_y, x); dogensym("", &x->pd_s_, x); pd_this = x; #else dogensym("pointer", &s_pointer, x); dogensym("float", &s_float, x); dogensym("symbol", &s_symbol, x); dogensym("bang", &s_bang, x); dogensym("list", &s_list, x); dogensym("anything", &s_anything, x); dogensym("signal", &s_signal, x); dogensym("#N", &s__N, x); dogensym("#X", &s__X, x); dogensym("x", &s_x, x); dogensym("y", &s_y, x); dogensym("", &s_, x); #endif x_midi_newpdinstance(); g_canvas_newpdinstance(); d_ugen_newpdinstance(); s_stuff_newpdinstance(); return (x); } static void class_addmethodtolist(t_class *c, t_methodentry **methodlist, int nmethod, t_gotfn fn, t_symbol *sel, unsigned char *args, t_pdinstance *pdinstance) { int i; t_methodentry *m; for (i = 0; i < nmethod; i++) if (sel && (*methodlist)[i].me_name == sel) { char nbuf[80]; snprintf(nbuf, 80, "%s_aliased", sel->s_name); nbuf[79] = 0; (*methodlist)[i].me_name = dogensym(nbuf, 0, pdinstance); if (c == pd_objectmaker) logpost(NULL, PD_VERBOSE, "warning: class '%s' overwritten; old one renamed '%s'", sel->s_name, nbuf); else logpost(NULL, PD_VERBOSE, "warning: old method '%s' for class '%s' renamed '%s'", sel->s_name, c->c_name->s_name, nbuf); } (*methodlist) = t_resizebytes((*methodlist), nmethod * sizeof(**methodlist), (nmethod + 1) * sizeof(**methodlist)); m = (*methodlist) + nmethod; m->me_name = sel; m->me_fun = (t_gotfn)fn; memcpy(m->me_arg, args, MAXPDARG+1); } #ifdef PDINSTANCE EXTERN void pd_setinstance(t_pdinstance *x) { pd_this = x; } static void pdinstance_renumber(void) { int i; for (i = 0; i < pd_ninstances; i++) pd_instances[i]->pd_instanceno = i; } extern void text_template_init(void); extern void garray_init(void); EXTERN t_pdinstance *pdinstance_new(void) { t_pdinstance *x = (t_pdinstance *)getbytes(sizeof(t_pdinstance)); t_class *c; int i; pd_this = x; s_inter_newpdinstance(); pdinstance_init(x); sys_lock(); pd_globallock(); pd_instances = (t_pdinstance **)resizebytes(pd_instances, pd_ninstances * sizeof(*pd_instances), (pd_ninstances+1) * sizeof(*pd_instances)); pd_instances[pd_ninstances] = x; for (c = class_list; c; c = c->c_next) { c->c_methods = (t_methodentry **)t_resizebytes(c->c_methods, pd_ninstances * sizeof(*c->c_methods), (pd_ninstances + 1) * sizeof(*c->c_methods)); c->c_methods[pd_ninstances] = t_getbytes(0); for (i = 0; i < c->c_nmethod; i++) class_addmethodtolist(c, &c->c_methods[pd_ninstances], i, c->c_methods[0][i].me_fun, dogensym(c->c_methods[0][i].me_name->s_name, 0, x), c->c_methods[0][i].me_arg, x); } pd_ninstances++; pdinstance_renumber(); pd_bind(&glob_pdobject, gensym("pd")); text_template_init(); garray_init(); pd_globalunlock(); sys_unlock(); return (x); } EXTERN void pdinstance_free(t_pdinstance *x) { t_symbol *s; t_canvas *canvas; int i, instanceno; t_class *c; t_instanceinter *inter; pd_setinstance(x); sys_lock(); pd_globallock(); instanceno = x->pd_instanceno; inter = x->pd_inter; canvas_suspend_dsp(); while (x->pd_canvaslist) pd_free((t_pd *)x->pd_canvaslist); while (x->pd_templatelist) pd_free((t_pd *)x->pd_templatelist); for (c = class_list; c; c = c->c_next) { if(c->c_methods[instanceno]) freebytes(c->c_methods[instanceno], c->c_nmethod * sizeof(**c->c_methods)); c->c_methods[instanceno] = NULL; for (i = instanceno; i < pd_ninstances-1; i++) c->c_methods[i] = c->c_methods[i+1]; c->c_methods = (t_methodentry **)t_resizebytes(c->c_methods, pd_ninstances * sizeof(*c->c_methods), (pd_ninstances - 1) * sizeof(*c->c_methods)); } for (i =0; i < SYMTABHASHSIZE; i++) { while ((s = x->pd_symhash[i])) { x->pd_symhash[i] = s->s_next; if(s != &x->pd_s_pointer && s != &x->pd_s_float && s != &x->pd_s_symbol && s != &x->pd_s_bang && s != &x->pd_s_list && s != &x->pd_s_anything && s != &x->pd_s_signal && s != &x->pd_s__N && s != &x->pd_s__X && s != &x->pd_s_x && s != &x->pd_s_y && s != &x->pd_s_) { freebytes((void *)s->s_name, strlen(s->s_name)+1); freebytes(s, sizeof(*s)); } } } freebytes(x->pd_symhash, SYMTABHASHSIZE * sizeof (*x->pd_symhash)); x_midi_freepdinstance(); g_canvas_freepdinstance(); d_ugen_freepdinstance(); s_stuff_freepdinstance(); for (i = instanceno; i < pd_ninstances-1; i++) pd_instances[i] = pd_instances[i+1]; pd_instances = (t_pdinstance **)resizebytes(pd_instances, pd_ninstances * sizeof(*pd_instances), (pd_ninstances-1) * sizeof(*pd_instances)); pd_ninstances--; pdinstance_renumber(); pd_globalunlock(); sys_unlock(); pd_setinstance(&pd_maininstance); s_inter_free(inter); /* must happen after sys_unlock() */ } #endif /* PDINSTANCE */ /* this bootstraps the class management system (pd_objectmaker, pd_canvasmaker) * it has been moved from the bottom of the file up here, before the class_new() undefine */ void mess_init(void) { if (pd_objectmaker) return; #ifdef PDINSTANCE pd_this = &pd_maininstance; #endif s_inter_newpdinstance(); sys_lock(); pd_globallock(); pdinstance_init(&pd_maininstance); class_extern_dir = &s_; pd_objectmaker = class_new(gensym("objectmaker"), 0, 0, sizeof(t_pd), CLASS_DEFAULT, A_NULL); pd_canvasmaker = class_new(gensym("canvasmaker"), 0, 0, sizeof(t_pd), CLASS_DEFAULT, A_NULL); class_addanything(pd_objectmaker, (t_method)new_anything); pd_globalunlock(); sys_unlock(); } static void pd_defaultanything(t_pd *x, t_symbol *s, int argc, t_atom *argv) { pd_error(x, "%s: no method for '%s'", (*x)->c_name->s_name, s->s_name); } static void pd_defaultbang(t_pd *x) { if (*(*x)->c_listmethod != pd_defaultlist) (*(*x)->c_listmethod)(x, 0, 0, 0); else (*(*x)->c_anymethod)(x, &s_bang, 0, 0); } /* am empty list calls the 'bang' method unless it's the default bang method -- that might turn around and call our 'list' method which could be an infinite recorsion. Fall through to calling our 'anything' method. That had better not turn around and call us with an empty list. */ void pd_emptylist(t_pd *x) { if (*(*x)->c_bangmethod != pd_defaultbang) (*(*x)->c_bangmethod)(x); else (*(*x)->c_anymethod)(x, &s_bang, 0, 0); } static void pd_defaultpointer(t_pd *x, t_gpointer *gp) { if (*(*x)->c_listmethod != pd_defaultlist) { t_atom at; SETPOINTER(&at, gp); (*(*x)->c_listmethod)(x, 0, 1, &at); } else { t_atom at; SETPOINTER(&at, gp); (*(*x)->c_anymethod)(x, &s_pointer, 1, &at); } } static void pd_defaultfloat(t_pd *x, t_float f) { if (*(*x)->c_listmethod != pd_defaultlist) { t_atom at; SETFLOAT(&at, f); (*(*x)->c_listmethod)(x, 0, 1, &at); } else { t_atom at; SETFLOAT(&at, f); (*(*x)->c_anymethod)(x, &s_float, 1, &at); } } static void pd_defaultsymbol(t_pd *x, t_symbol *s) { if (*(*x)->c_listmethod != pd_defaultlist) { t_atom at; SETSYMBOL(&at, s); (*(*x)->c_listmethod)(x, 0, 1, &at); } else { t_atom at; SETSYMBOL(&at, s); (*(*x)->c_anymethod)(x, &s_symbol, 1, &at); } } void obj_list(t_object *x, t_symbol *s, int argc, t_atom *argv); static void class_nosavefn(t_gobj *z, t_binbuf *b); /* handle "list" messages to Pds without explicit list methods defined. */ static void pd_defaultlist(t_pd *x, t_symbol *s, int argc, t_atom *argv) { /* a list with no elements is handled by the 'bang' method if one exists. */ if (argc == 0 && *(*x)->c_bangmethod != pd_defaultbang) { (*(*x)->c_bangmethod)(x); return; } /* a list with one element which is a number can be handled by a "float" method if any is defined; same for "symbol", "pointer". */ if (argc == 1) { if (argv->a_type == A_FLOAT && *(*x)->c_floatmethod != pd_defaultfloat) { (*(*x)->c_floatmethod)(x, argv->a_w.w_float); return; } else if (argv->a_type == A_SYMBOL && *(*x)->c_symbolmethod != pd_defaultsymbol) { (*(*x)->c_symbolmethod)(x, argv->a_w.w_symbol); return; } else if (argv->a_type == A_POINTER && *(*x)->c_pointermethod != pd_defaultpointer) { (*(*x)->c_pointermethod)(x, argv->a_w.w_gpointer); return; } } /* Next try for an "anything" method */ if ((*x)->c_anymethod != pd_defaultanything) (*(*x)->c_anymethod)(x, &s_list, argc, argv); /* if the object is patchable (i.e., can have proper inlets) send it on to obj_list which will unpack the list into the inlets */ else if ((*x)->c_patchable) obj_list((t_object *)x, s, argc, argv); /* otherwise gove up and complain. */ else pd_defaultanything(x, &s_list, argc, argv); } /* for now we assume that all "gobjs" are text unless explicitly overridden later by calling class_setbehavior(). I'm not sure how to deal with Pds that aren't gobjs; shouldn't there be a way to check that at run time? Perhaps the presence of a "newmethod" should be our cue, or perhaps the "tiny" flag. */ /* another matter. This routine does two unrelated things: it creates a Pd class, but also adds a "new" method to create an instance of it. These are combined for historical reasons and for brevity in writing objects. To avoid adding a "new" method send a null function pointer. To add additional ones, use class_addcreator below. Some "classes", like "select", are actually two classes of the same name, one for the single- argument form, one for the multiple one; see select_setup() to find out how this is handled. */ extern void text_save(t_gobj *z, t_binbuf *b); t_class *class_new(t_symbol *s, t_newmethod newmethod, t_method freemethod, size_t size, int flags, t_atomtype type1, ...) { va_list ap; t_atomtype vec[MAXPDARG+1], *vp = vec; int count = 0, i; t_class *c; int typeflag = flags & CLASS_TYPEMASK; if (!typeflag) typeflag = CLASS_PATCHABLE; *vp = type1; va_start(ap, type1); while (*vp) { if (count == MAXPDARG) { if (s) pd_error(0, "class %s: sorry: only %d args typechecked; use A_GIMME", s->s_name, MAXPDARG); else pd_error(0, "unnamed class: sorry: only %d args typechecked; use A_GIMME", MAXPDARG); break; } vp++; count++; *vp = va_arg(ap, t_atomtype); } va_end(ap); if (pd_objectmaker && newmethod) { /* add a "new" method by the name specified by the object */ class_addmethod(pd_objectmaker, (t_method)newmethod, s, vec[0], vec[1], vec[2], vec[3], vec[4], vec[5]); if (s && class_loadsym && !zgetfn(&pd_objectmaker, class_loadsym)) { /* if we're loading an extern it might have been invoked by a longer file name; in this case, make this an admissible name too. */ const char *loadstring = class_loadsym->s_name; size_t l1 = strlen(s->s_name), l2 = strlen(loadstring); if (l2 > l1 && !strcmp(s->s_name, loadstring + (l2 - l1))) class_addmethod(pd_objectmaker, (t_method)newmethod, class_loadsym, vec[0], vec[1], vec[2], vec[3], vec[4], vec[5]); } } c = (t_class *)t_getbytes(sizeof(*c)); c->c_name = c->c_helpname = s; c->c_size = size; c->c_nmethod = 0; c->c_freemethod = (t_method)freemethod; c->c_bangmethod = pd_defaultbang; c->c_pointermethod = pd_defaultpointer; c->c_floatmethod = pd_defaultfloat; c->c_symbolmethod = pd_defaultsymbol; c->c_listmethod = pd_defaultlist; c->c_anymethod = pd_defaultanything; /* set default widget behavior. Things like IEM GUIs override this; they're patchable but have bespoke widget behaviors */ c->c_wb = (typeflag == CLASS_PATCHABLE ? &text_widgetbehavior : 0); c->c_pwb = 0; c->c_firstin = ((flags & CLASS_NOINLET) == 0); c->c_patchable = (typeflag == CLASS_PATCHABLE); c->c_gobj = (typeflag >= CLASS_GOBJ); c->c_multichannel = (flags & CLASS_MULTICHANNEL) != 0; c->c_nopromotesig = (flags & CLASS_NOPROMOTESIG) != 0; c->c_nopromoteleft = (flags & CLASS_NOPROMOTELEFT) != 0; c->c_drawcommand = 0; c->c_floatsignalin = 0; c->c_externdir = class_extern_dir; c->c_savefn = (typeflag == CLASS_PATCHABLE ? text_save : class_nosavefn); c->c_classfreefn = 0; #ifdef PDINSTANCE c->c_methods = (t_methodentry **)t_getbytes( pd_ninstances * sizeof(*c->c_methods)); for (i = 0; i < pd_ninstances; i++) c->c_methods[i] = t_getbytes(0); c->c_next = class_list; class_list = c; #else c->c_methods = t_getbytes(0); #endif #if 0 /* enable this if you want to see a list of all classes */ post("class: %s", c->c_name->s_name); #endif return (c); } void class_free(t_class *c) { int i; #ifdef PDINSTANCE t_class *prev; if (class_list == c) class_list = c->c_next; else { prev = class_list; while (prev->c_next != c) prev = prev->c_next; prev->c_next = c->c_next; } #endif if (c->c_classfreefn) c->c_classfreefn(c); #ifdef PDINSTANCE for (i = 0; i < pd_ninstances; i++) { if(c->c_methods[i]) freebytes(c->c_methods[i], c->c_nmethod * sizeof(*c->c_methods[i])); c->c_methods[i] = NULL; } freebytes(c->c_methods, pd_ninstances * sizeof(*c->c_methods)); #else freebytes(c->c_methods, c->c_nmethod * sizeof(*c->c_methods)); #endif freebytes(c, sizeof(*c)); } void class_setfreefn(t_class *c, t_classfreefn fn) { c->c_classfreefn = fn; } #ifdef PDINSTANCE t_class *class_getfirst(void) { return class_list; } #endif /* add a creation method, which is a function that returns a Pd object suitable for putting in an object box. We presume you've got a class it can belong to, but this won't be used until the newmethod is actually called back (and the new method explicitly takes care of this.) */ void class_addcreator(t_newmethod newmethod, t_symbol *s, t_atomtype type1, ...) { va_list ap; t_atomtype vec[MAXPDARG+1], *vp = vec; int count = 0; *vp = type1; va_start(ap, type1); while (*vp) { if (count == MAXPDARG) { if(s) pd_error(0, "class %s: sorry: only %d creation args allowed", s->s_name, MAXPDARG); else pd_error(0, "unnamed class: sorry: only %d creation args allowed", MAXPDARG); break; } vp++; count++; *vp = va_arg(ap, t_atomtype); } va_end(ap); class_addmethod(pd_objectmaker, (t_method)newmethod, s, vec[0], vec[1], vec[2], vec[3], vec[4], vec[5]); } void class_addmethod(t_class *c, t_method fn, t_symbol *sel, t_atomtype arg1, ...) { va_list ap; t_atomtype argtype = arg1; int nargs, i; if(!c) return; va_start(ap, arg1); /* "signal" method specifies that we take audio signals but that we don't want automatic float to signal conversion. This is obsolete; you should now use the CLASS_MAINSIGNALIN macro. */ if (sel == &s_signal) { if (c->c_floatsignalin) post("warning: signal method overrides class_mainsignalin"); c->c_floatsignalin = -1; } /* check for special cases. "Pointer" is missing here so that pd_objectmaker's pointer method can be typechecked differently. */ if (sel == &s_bang) { if (argtype) goto phooey; class_addbang(c, fn); } else if (sel == &s_float) { if (argtype != A_FLOAT || va_arg(ap, t_atomtype)) goto phooey; class_doaddfloat(c, fn); } else if (sel == &s_symbol) { if (argtype != A_SYMBOL || va_arg(ap, t_atomtype)) goto phooey; class_addsymbol(c, fn); } else if (sel == &s_list) { if (argtype != A_GIMME) goto phooey; class_addlist(c, fn); } else if (sel == &s_anything) { if (argtype != A_GIMME) goto phooey; class_addanything(c, fn); } else { unsigned char argvec[MAXPDARG+1]; nargs = 0; while (argtype != A_NULL && nargs < MAXPDARG) { argvec[nargs++] = argtype; argtype = va_arg(ap, t_atomtype); } if (argtype != A_NULL) pd_error(0, "%s_%s: only 5 arguments are typecheckable; use A_GIMME", (c->c_name)?(c->c_name->s_name):"", sel?(sel->s_name):""); argvec[nargs] = 0; #ifdef PDINSTANCE for (i = 0; i < pd_ninstances; i++) { class_addmethodtolist(c, &c->c_methods[i], c->c_nmethod, (t_gotfn)fn, sel?dogensym(sel->s_name, 0, pd_instances[i]):0, argvec, pd_instances[i]); } #else class_addmethodtolist(c, &c->c_methods, c->c_nmethod, (t_gotfn)fn, sel, argvec, &pd_maininstance); #endif c->c_nmethod++; } goto done; phooey: bug("class_addmethod: %s_%s: bad argument types\n", (c->c_name)?(c->c_name->s_name):"", sel?(sel->s_name):""); done: va_end(ap); return; } /* Instead of these, see the "class_addfloat", etc., macros in m_pd.h */ void class_addbang(t_class *c, t_method fn) { if(!c) return; c->c_bangmethod = (t_bangmethod)fn; } void class_addpointer(t_class *c, t_method fn) { if(!c) return; c->c_pointermethod = (t_pointermethod)fn; } void class_doaddfloat(t_class *c, t_method fn) { if(!c) return; c->c_floatmethod = (t_floatmethod)fn; } void class_addsymbol(t_class *c, t_method fn) { if(!c) return; c->c_symbolmethod = (t_symbolmethod)fn; } void class_addlist(t_class *c, t_method fn) { if(!c) return; c->c_listmethod = (t_listmethod)fn; } void class_addanything(t_class *c, t_method fn) { if(!c) return; c->c_anymethod = (t_anymethod)fn; } void class_setwidget(t_class *c, const t_widgetbehavior *w) { if(!c) return; c->c_wb = w; } void class_setparentwidget(t_class *c, const t_parentwidgetbehavior *pw) { if(!c) return; c->c_pwb = pw; } const char *class_getname(const t_class *c) { if(!c) return 0; return (c->c_name->s_name); } const char *class_gethelpname(const t_class *c) { if(!c) return 0; return (c->c_helpname->s_name); } void class_sethelpsymbol(t_class *c, t_symbol *s) { if(!c) return; c->c_helpname = s; } const t_parentwidgetbehavior *pd_getparentwidget(t_pd *x) { return ((*x)->c_pwb); } void class_setdrawcommand(t_class *c) { if(!c) return; c->c_drawcommand = 1; } int class_isdrawcommand(const t_class *c) { if(!c) return 0; return (c->c_drawcommand); } static void pd_floatforsignal(t_pd *x, t_float f) { int offset = (*x)->c_floatsignalin; if (offset > 0) *(t_float *)(((char *)x) + offset) = f; else pd_error(x, "%s: float unexpected for signal input", (*x)->c_name->s_name); } void class_domainsignalin(t_class *c, int onset) { if(!c) return; if (onset <= 0) onset = -1; else { if (c->c_floatmethod != pd_defaultfloat) post("warning: %s: float method overwritten", c->c_name->s_name); c->c_floatmethod = (t_floatmethod)pd_floatforsignal; } c->c_floatsignalin = onset; } void class_set_extern_dir(t_symbol *s) { class_extern_dir = s; } const char *class_gethelpdir(const t_class *c) { if(!c) return 0; return (c->c_externdir->s_name); } static void class_nosavefn(t_gobj *z, t_binbuf *b) { bug("save function called but not defined"); } void class_setsavefn(t_class *c, t_savefn f) { if(!c) return; c->c_savefn = f; } t_savefn class_getsavefn(const t_class *c) { if(!c) return 0; return (c->c_savefn); } void class_setpropertiesfn(t_class *c, t_propertiesfn f) { if(!c) return; c->c_propertiesfn = f; } t_propertiesfn class_getpropertiesfn(const t_class *c) { if(!c) return 0; return (c->c_propertiesfn); } /* ---------------- the symbol table ------------------------ */ static t_symbol *dogensym(const char *s, t_symbol *oldsym, t_pdinstance *pdinstance) { char *symname = 0; t_symbol **symhashloc, *sym2; unsigned int hash = 5381; int length = 0; const char *s2 = s; while (*s2) /* djb2 hash algo */ { hash = ((hash << 5) + hash) + *s2; length++; s2++; } symhashloc = pdinstance->pd_symhash + (hash & (SYMTABHASHSIZE-1)); while ((sym2 = *symhashloc)) { if (!strcmp(sym2->s_name, s)) return(sym2); symhashloc = &sym2->s_next; } if (oldsym) sym2 = oldsym; else sym2 = (t_symbol *)t_getbytes(sizeof(*sym2)); symname = t_getbytes(length+1); sym2->s_next = 0; sym2->s_thing = 0; strcpy(symname, s); sym2->s_name = symname; *symhashloc = sym2; return (sym2); } t_symbol *gensym(const char *s) { return(dogensym(s, 0, pd_this)); } static t_symbol *addfileextent(t_symbol *s) { char namebuf[MAXPDSTRING]; const char *str = s->s_name; int ln = (int)strlen(str); if (!strcmp(str + ln - 3, ".pd")) return (s); strcpy(namebuf, str); strcpy(namebuf+ln, ".pd"); return (gensym(namebuf)); } #define MAXOBJDEPTH 1000 static int tryingalready; void canvas_popabstraction(t_canvas *x); t_symbol* pathsearch(t_symbol *s,char* ext); int pd_setloadingabstraction(t_symbol *sym); /* this routine is called when a new "object" is requested whose class Pd doesn't know. Pd tries to load it as an extern, then as an abstraction. */ void new_anything(void *dummy, t_symbol *s, int argc, t_atom *argv) { int fd; char dirbuf[MAXPDSTRING], classslashclass[MAXPDSTRING], *nameptr; if (tryingalready>MAXOBJDEPTH){ pd_error(0, "maximum object loading depth %d reached", MAXOBJDEPTH); return; } if (s == &s_anything){ pd_error(0, "object name \"%s\" not allowed", s->s_name); return; } pd_this->pd_newest = 0; class_loadsym = s; pd_globallock(); if (sys_load_lib(canvas_getcurrent(), s->s_name)) { tryingalready++; typedmess(dummy, s, argc, argv); tryingalready--; return; } class_loadsym = 0; pd_globalunlock(); } /* This is externally available, but note that it might later disappear; the whole "newest" thing is a hack which needs to be redesigned. */ t_pd *pd_newest(void) { return (pd_this->pd_newest); } /* horribly, we need prototypes for each of the artificial function calls in typedmess(), to keep the compiler quiet. */ typedef t_pd *(*t_newgimme)(t_symbol *s, int argc, t_atom *argv); typedef void(*t_messgimme)(t_pd *x, t_symbol *s, int argc, t_atom *argv); typedef t_pd *(*t_fun0)( t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5); typedef t_pd *(*t_fun1)(t_int i1, t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5); typedef t_pd *(*t_fun2)(t_int i1, t_int i2, t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5); typedef t_pd *(*t_fun3)(t_int i1, t_int i2, t_int i3, t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5); typedef t_pd *(*t_fun4)(t_int i1, t_int i2, t_int i3, t_int i4, t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5); typedef t_pd *(*t_fun5)(t_int i1, t_int i2, t_int i3, t_int i4, t_int i5, t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5); typedef t_pd *(*t_fun6)(t_int i1, t_int i2, t_int i3, t_int i4, t_int i5, t_int i6, t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5); void pd_typedmess(t_pd *x, t_symbol *s, int argc, t_atom *argv) { t_method *f; t_class *c = *x; t_methodentry *m, *mlist; unsigned char *wp, wanttype; int i; t_int ai[MAXPDARG+1], *ap = ai; t_floatarg ad[MAXPDARG+1], *dp = ad; int narg = 0; t_pd *bonzo; /* check for messages that are handled by fixed slots in the class structure. */ if (s == &s_float) { if (!argc) (*c->c_floatmethod)(x, 0.); else if (argv->a_type == A_FLOAT) (*c->c_floatmethod)(x, argv->a_w.w_float); else goto badarg; return; } if (s == &s_bang) { (*c->c_bangmethod)(x); return; } if (s == &s_list) { (*c->c_listmethod)(x, s, argc, argv); return; } if (s == &s_symbol) { if (argc && argv->a_type == A_SYMBOL) (*c->c_symbolmethod)(x, argv->a_w.w_symbol); else (*c->c_symbolmethod)(x, &s_); return; } /* pd_objectmaker doesn't require an actual pointer value */ if (s == &s_pointer && x != &pd_objectmaker) { if (argc && argv->a_type == A_POINTER) (*c->c_pointermethod)(x, argv->a_w.w_gpointer); else goto badarg; return; } #ifdef PDINSTANCE mlist = c->c_methods[pd_this->pd_instanceno]; #else mlist = c->c_methods; #endif for (i = c->c_nmethod, m = mlist; i--; m++) if (m->me_name == s) { wp = m->me_arg; if (*wp == A_GIMME) { if (x == &pd_objectmaker) pd_this->pd_newest = (*((t_newgimme)(m->me_fun)))(s, argc, argv); else (*((t_messgimme)(m->me_fun)))(x, s, argc, argv); return; } if (argc > MAXPDARG) argc = MAXPDARG; if (x != &pd_objectmaker) *(ap++) = (t_int)x, narg++; while ((wanttype = *wp++)) { switch (wanttype) { case A_POINTER: if (!argc) goto badarg; else { if (argv->a_type == A_POINTER) *ap = (t_int)(argv->a_w.w_gpointer); else goto badarg; argc--; argv++; } narg++; ap++; break; case A_FLOAT: if (!argc) goto badarg; /* falls through */ case A_DEFFLOAT: if (!argc) *dp = 0; else { if (argv->a_type == A_FLOAT) *dp = argv->a_w.w_float; else goto badarg; argc--; argv++; } dp++; break; case A_SYMBOL: if (!argc) goto badarg; /* falls through */ case A_DEFSYM: if (!argc) *ap = (t_int)(&s_); else { if (argv->a_type == A_SYMBOL) *ap = (t_int)(argv->a_w.w_symbol); /* if it's an unfilled "dollar" argument it appears as zero here; cheat and bash it to the null symbol. Unfortunately, this lets real zeros pass as symbols too, which seems wrong... */ else if (x == &pd_objectmaker && argv->a_type == A_FLOAT && argv->a_w.w_float == 0) *ap = (t_int)(&s_); else goto badarg; argc--; argv++; } narg++; ap++; break; default: goto badarg; } } switch (narg) { case 0 : bonzo = (*(t_fun0)(m->me_fun)) (ad[0], ad[1], ad[2], ad[3], ad[4]); break; case 1 : bonzo = (*(t_fun1)(m->me_fun)) (ai[0], ad[0], ad[1], ad[2], ad[3], ad[4]); break; case 2 : bonzo = (*(t_fun2)(m->me_fun)) (ai[0], ai[1], ad[0], ad[1], ad[2], ad[3], ad[4]); break; case 3 : bonzo = (*(t_fun3)(m->me_fun)) (ai[0], ai[1], ai[2], ad[0], ad[1], ad[2], ad[3], ad[4]); break; case 4 : bonzo = (*(t_fun4)(m->me_fun)) (ai[0], ai[1], ai[2], ai[3], ad[0], ad[1], ad[2], ad[3], ad[4]); break; case 5 : bonzo = (*(t_fun5)(m->me_fun)) (ai[0], ai[1], ai[2], ai[3], ai[4], ad[0], ad[1], ad[2], ad[3], ad[4]); break; case 6 : bonzo = (*(t_fun6)(m->me_fun)) (ai[0], ai[1], ai[2], ai[3], ai[4], ai[5], ad[0], ad[1], ad[2], ad[3], ad[4]); break; default: bonzo = 0; } if (x == &pd_objectmaker) pd_this->pd_newest = bonzo; return; } (*c->c_anymethod)(x, s, argc, argv); return; badarg: pd_error(x, "bad arguments for message '%s' to object '%s'", s->s_name, c->c_name->s_name); } /* convenience routine giving a stdarg interface to typedmess(). Only ten args supported; it seems unlikely anyone will need more since longer messages are likely to be programmatically generated anyway. */ void pd_vmess(t_pd *x, t_symbol *sel, const char *fmt, ...) { va_list ap; t_atom arg[10], *at = arg; int nargs = 0; const char *fp = fmt; va_start(ap, fmt); while (1) { if (nargs >= 10) { pd_error(x, "pd_vmess: only 10 allowed"); break; } switch(*fp++) { case 'f': SETFLOAT(at, va_arg(ap, double)); break; case 's': SETSYMBOL(at, va_arg(ap, t_symbol *)); break; case 'i': SETFLOAT(at, va_arg(ap, t_int)); break; case 'p': SETPOINTER(at, va_arg(ap, t_gpointer *)); break; default: goto done; } at++; nargs++; } done: va_end(ap); typedmess(x, sel, nargs, arg); } void pd_forwardmess(t_pd *x, int argc, t_atom *argv) { if (argc) { t_atomtype t = argv->a_type; if (t == A_SYMBOL) pd_typedmess(x, argv->a_w.w_symbol, argc-1, argv+1); else if (t == A_POINTER) { if (argc == 1) pd_pointer(x, argv->a_w.w_gpointer); else pd_list(x, &s_list, argc, argv); } else if (t == A_FLOAT) { if (argc == 1) pd_float(x, argv->a_w.w_float); else pd_list(x, &s_list, argc, argv); } else bug("pd_forwardmess"); } } void nullfn(void) {} t_gotfn getfn(const t_pd *x, t_symbol *s) { const t_class *c = *x; t_methodentry *m, *mlist; int i; #ifdef PDINSTANCE mlist = c->c_methods[pd_this->pd_instanceno]; #else mlist = c->c_methods; #endif for (i = c->c_nmethod, m = mlist; i--; m++) if (m->me_name == s) return(m->me_fun); pd_error(x, "%s: no method for message '%s'", c->c_name->s_name, s->s_name); return((t_gotfn)nullfn); } t_gotfn zgetfn(const t_pd *x, t_symbol *s) { const t_class *c = *x; t_methodentry *m, *mlist; int i; #ifdef PDINSTANCE mlist = c->c_methods[pd_this->pd_instanceno]; #else mlist = c->c_methods; #endif for (i = c->c_nmethod, m = mlist; i--; m++) if (m->me_name == s) return(m->me_fun); return(0); } void c_extern(t_externclass *cls, t_newmethod newroutine, t_method freeroutine, t_symbol *name, size_t size, int tiny, \ t_atomtype arg1, ...) { bug("'c_extern' not implemented."); } void c_addmess(t_method fn, t_symbol *sel, t_atomtype arg1, ...) { bug("'c_addmess' not implemented."); } /* provide 'class_new' fallbacks, in case a double-precision Pd attempts to * load a single-precision external, or vice versa */ #ifdef class_new # undef class_new #endif t_class * #if PD_FLOATSIZE == 32 class_new64 #else class_new #endif (t_symbol *s, t_newmethod newmethod, t_method freemethod, size_t size, int flags, t_atomtype type1, ...) { const int ext_floatsize = #if PD_FLOATSIZE == 32 64 #else 32 #endif ; static int loglevel = 0; if(s) { logpost(0, loglevel, "refusing to load %dbit-float object '%s' into %dbit-float Pd", ext_floatsize, s->s_name, PD_FLOATSIZE); loglevel=3; } else logpost(0, 3, "refusing to load unnamed %dbit-float object into %dbit-float Pd", ext_floatsize, PD_FLOATSIZE); return 0; } /* this is privately shared with d_ugen.c */ int class_getdspflags(const t_class *c) { return ((c->c_multichannel ? CLASS_MULTICHANNEL : 0) | (c->c_nopromotesig ? CLASS_NOPROMOTESIG : 0) | (c->c_nopromoteleft ? CLASS_NOPROMOTELEFT : 0) ); } ================================================ FILE: libs/libpd/pure-data/src/m_conf.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* changes by Thomas Musil IEM KUG Graz Austria 2001 */ /* all changes are labeled with iemlib */ void g_array_setup(void); void g_canvas_setup(void); void g_guiconnect_setup(void); /* iemlib */ void g_bang_setup(void); void g_mycanvas_setup(void); void g_numbox_setup(void); void g_radio_setup(void); void g_slider_setup(void); void g_toggle_setup(void); void g_vumeter_setup(void); /* iemlib */ void g_io_setup(void); void g_scalar_setup(void); void g_template_setup(void); void g_text_setup(void); void g_traversal_setup(void); void clone_setup(void); void m_pd_setup(void); void x_acoustics_setup(void); void x_interface_setup(void); void x_connective_setup(void); void x_time_setup(void); void x_arithmetic_setup(void); void x_array_setup(void); void x_midi_setup(void); void x_misc_setup(void); void x_net_setup(void); void x_file_setup(void); void x_qlist_setup(void); void x_gui_setup(void); void x_list_setup(void); void x_scalar_setup(void); void expr_setup(void); void d_arithmetic_setup(void); void d_array_setup(void); void d_ctl_setup(void); void d_dac_setup(void); void d_delay_setup(void); void d_fft_setup(void); void d_filter_setup(void); void d_global_setup(void); void d_math_setup(void); void d_misc_setup(void); void d_osc_setup(void); void d_soundfile_setup(void); void d_ugen_setup(void); void conf_init(void) { g_array_setup(); g_canvas_setup(); g_guiconnect_setup(); /* iemlib */ g_bang_setup(); g_mycanvas_setup(); g_numbox_setup(); g_radio_setup(); g_slider_setup(); g_toggle_setup(); g_vumeter_setup(); /* iemlib */ g_io_setup(); g_scalar_setup(); g_template_setup(); g_text_setup(); g_traversal_setup(); clone_setup(); m_pd_setup(); x_acoustics_setup(); x_interface_setup(); x_connective_setup(); x_time_setup(); x_arithmetic_setup(); x_array_setup(); x_midi_setup(); x_misc_setup(); x_net_setup(); x_file_setup(); x_qlist_setup(); x_gui_setup(); x_list_setup(); x_scalar_setup(); expr_setup(); d_arithmetic_setup(); d_array_setup(); d_ctl_setup(); d_dac_setup(); d_delay_setup(); d_fft_setup(); d_filter_setup(); d_global_setup(); d_math_setup(); d_misc_setup(); d_osc_setup(); d_soundfile_setup(); d_ugen_setup(); } ================================================ FILE: libs/libpd/pure-data/src/m_glob.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ #include "m_pd.h" #include "m_imp.h" t_class *glob_pdobject; static t_class *maxclass; int sys_perf; /* true if we should query user on close and quit */ int pd_compatibilitylevel = 100000; /* e.g., 43 for pd 0.43 compatibility */ /* These "glob" routines, which implement messages to Pd, are from all over. Some others are prototyped in m_imp.h as well. */ void glob_menunew(void *dummy, t_symbol *name, t_symbol *dir); void glob_verifyquit(void *dummy, t_floatarg f); void glob_dsp(void *dummy, t_symbol *s, int argc, t_atom *argv); void glob_key(void *dummy, t_symbol *s, int ac, t_atom *av); void glob_audiostatus(void *dummy); void glob_finderror(t_pd *dummy); void glob_findinstance(t_pd *dummy, t_symbol*s); void glob_start_preference_dialog(t_pd *dummy, t_symbol*s); void glob_audio_properties(t_pd *dummy, t_floatarg flongform); void glob_audio_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv); void glob_audio_setapi(t_pd *dummy, t_floatarg f); void glob_midi_properties(t_pd *dummy, t_floatarg flongform); void glob_midi_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv); void glob_midi_setapi(t_pd *dummy, t_floatarg f); void glob_start_path_dialog(t_pd *dummy, t_floatarg flongform); void glob_path_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv); void glob_addtopath(t_pd *dummy, t_symbol *path, t_float saveit); void glob_start_startup_dialog(t_pd *dummy, t_floatarg flongform); void glob_startup_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv); void glob_ping(t_pd *dummy); void glob_plugindispatch(t_pd *dummy, t_symbol *s, int argc, t_atom *argv); void glob_watchdog(t_pd *dummy); void glob_loadpreferences(t_pd *dummy, t_symbol *s); void glob_savepreferences(t_pd *dummy, t_symbol *s); void glob_forgetpreferences(t_pd *dummy); void glob_open(t_pd *ignore, t_symbol *name, t_symbol *dir, t_floatarg f); void glob_fastforward(t_pd *ignore, t_floatarg f); void glob_settracing(void *dummy, t_float f); static void glob_helpintro(t_pd *dummy) { open_via_helppath("intro.pd", ""); } static void glob_compatibility(t_pd *dummy, t_floatarg level) { int dspwas = canvas_suspend_dsp(); pd_compatibilitylevel = 0.5 + 100. * level; canvas_resume_dsp(dspwas); } #ifdef _WIN32 void glob_audio(void *dummy, t_floatarg adc, t_floatarg dac); #endif /* a method you add for debugging printout */ void glob_foo(void *dummy, t_symbol *s, int argc, t_atom *argv); #if 1 void glob_foo(void *dummy, t_symbol *s, int argc, t_atom *argv) { #ifdef USEAPI_ALSA void alsa_printstate(void); alsa_printstate(); #endif } #endif static void glob_version(t_pd *dummy, t_float f) { if (f > (PD_MAJOR_VERSION + 0.01*PD_MINOR_VERSION + 0.001)) { static int warned; if (warned < 1) post("warning: file format (%g) newer than this version (%g) of Pd", f, PD_MAJOR_VERSION + 0.01*PD_MINOR_VERSION); else if (warned < 2) post("(... more file format messages suppressed)"); warned++; } } static void glob_perf(t_pd *dummy, t_float f) { sys_perf = (f != 0); } void max_default(t_pd *x, t_symbol *s, int argc, t_atom *argv) { int i; char str[80]; startpost("%s: unknown message %s ", class_getname(pd_class(x)), s->s_name); for (i = 0; i < argc; i++) { atom_string(argv+i, str, 80); poststring(str); } endpost(); } void glob_plugindispatch(t_pd *dummy, t_symbol *s, int argc, t_atom *argv) { pdgui_vmess("pdtk_plugin_dispatch", "a", argc, argv); } int sys_zoom_open = 1; void glob_zoom_open(t_pd *dummy, t_floatarg f) { sys_zoom_open = (f != 0 ? 2 : 1); } void glob_init(void) { maxclass = class_new(gensym("max"), 0, 0, sizeof(t_pd), CLASS_DEFAULT, A_NULL); class_addanything(maxclass, max_default); pd_bind(&maxclass, gensym("max")); glob_pdobject = class_new(gensym("pd"), 0, 0, sizeof(t_pd), CLASS_DEFAULT, A_NULL); class_addmethod(glob_pdobject, (t_method)glob_initfromgui, gensym("init"), A_GIMME, 0); class_addmethod(glob_pdobject, (t_method)glob_menunew, gensym("menunew"), A_SYMBOL, A_SYMBOL, 0); class_addmethod(glob_pdobject, (t_method)glob_open, gensym("open"), A_SYMBOL, A_SYMBOL, A_DEFFLOAT, 0); class_addmethod(glob_pdobject, (t_method)glob_exit, gensym("quit"), A_DEFFLOAT, 0); class_addmethod(glob_pdobject, (t_method)glob_verifyquit, gensym("verifyquit"), A_DEFFLOAT, 0); class_addmethod(glob_pdobject, (t_method)glob_foo, gensym("foo"), A_GIMME, 0); class_addmethod(glob_pdobject, (t_method)glob_dsp, gensym("dsp"), A_GIMME, 0); class_addmethod(glob_pdobject, (t_method)glob_key, gensym("key"), A_GIMME, 0); class_addmethod(glob_pdobject, (t_method)glob_audiostatus, gensym("audiostatus"), 0); class_addmethod(glob_pdobject, (t_method)glob_finderror, gensym("finderror"), 0); class_addmethod(glob_pdobject, (t_method)glob_findinstance, gensym("findinstance"), A_SYMBOL, 0); class_addmethod(glob_pdobject, (t_method)glob_start_preference_dialog, gensym("start-preference-dialog"), A_DEFSYM, 0); class_addmethod(glob_pdobject, (t_method)glob_audio_properties, gensym("audio-properties"), A_DEFFLOAT, 0); class_addmethod(glob_pdobject, (t_method)glob_audio_dialog, gensym("audio-dialog"), A_GIMME, 0); class_addmethod(glob_pdobject, (t_method)glob_audio_setapi, gensym("audio-setapi"), A_FLOAT, 0); class_addmethod(glob_pdobject, (t_method)glob_midi_setapi, gensym("midi-setapi"), A_FLOAT, 0); class_addmethod(glob_pdobject, (t_method)glob_midi_properties, gensym("midi-properties"), A_DEFFLOAT, 0); class_addmethod(glob_pdobject, (t_method)glob_midi_dialog, gensym("midi-dialog"), A_GIMME, 0); class_addmethod(glob_pdobject, (t_method)glob_start_path_dialog, gensym("start-path-dialog"), 0); class_addmethod(glob_pdobject, (t_method)glob_path_dialog, gensym("path-dialog"), A_GIMME, 0); class_addmethod(glob_pdobject, (t_method)glob_addtopath, gensym("add-to-path"), A_SYMBOL, A_DEFFLOAT, 0); class_addmethod(glob_pdobject, (t_method)glob_start_startup_dialog, gensym("start-startup-dialog"), 0); class_addmethod(glob_pdobject, (t_method)glob_startup_dialog, gensym("startup-dialog"), A_GIMME, 0); class_addmethod(glob_pdobject, (t_method)glob_ping, gensym("ping"), 0); class_addmethod(glob_pdobject, (t_method)glob_loadpreferences, gensym("load-preferences"), A_DEFSYM, 0); class_addmethod(glob_pdobject, (t_method)glob_savepreferences, gensym("save-preferences"), A_DEFSYM, 0); class_addmethod(glob_pdobject, (t_method)glob_forgetpreferences, gensym("forget-preferences"), A_DEFSYM, 0); class_addmethod(glob_pdobject, (t_method)glob_zoom_open, gensym("zoom-open"), A_FLOAT, 0); class_addmethod(glob_pdobject, (t_method)glob_version, gensym("version"), A_FLOAT, 0); class_addmethod(glob_pdobject, (t_method)glob_perf, gensym("perf"), A_FLOAT, 0); class_addmethod(glob_pdobject, (t_method)glob_compatibility, gensym("compatibility"), A_FLOAT, 0); class_addmethod(glob_pdobject, (t_method)glob_plugindispatch, gensym("plugin-dispatch"), A_GIMME, 0); class_addmethod(glob_pdobject, (t_method)glob_helpintro, gensym("help-intro"), A_GIMME, 0); class_addmethod(glob_pdobject, (t_method)glob_fastforward, gensym("fast-forward"), A_FLOAT, 0); class_addmethod(glob_pdobject, (t_method)glob_settracing, gensym("set-tracing"), A_FLOAT, 0); class_addmethod(glob_pdobject, (t_method)glob_watchdog, gensym("watchdog"), 0); class_addanything(glob_pdobject, max_default); pd_bind(&glob_pdobject, gensym("pd")); } /* function to return version number at run time. Any of the calling pointers may be zero in case you don't need all of them. */ void sys_getversion(int *major, int *minor, int *bugfix) { if (major) *major = PD_MAJOR_VERSION; if (minor) *minor = PD_MINOR_VERSION; if (bugfix) *bugfix = PD_BUGFIX_VERSION; } unsigned int sys_getfloatsize() { return sizeof(t_float); } ================================================ FILE: libs/libpd/pure-data/src/m_imp.h ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* This file contains function prototypes and data types used to implement Pd, but not shared with Pd objects. */ /* NOTE: this file describes Pd implementation details which may change in future releases. The public (stable) API is in m_pd.h. */ #ifndef __m_imp_h_ /* the structure for a method handler ala Max */ typedef struct _methodentry { t_symbol *me_name; t_gotfn me_fun; unsigned char me_arg[MAXPDARG+1]; } t_methodentry; EXTERN_STRUCT _widgetbehavior; typedef void (*t_bangmethod)(t_pd *x); typedef void (*t_pointermethod)(t_pd *x, t_gpointer *gp); typedef void (*t_floatmethod)(t_pd *x, t_float f); typedef void (*t_symbolmethod)(t_pd *x, t_symbol *s); typedef void (*t_listmethod)(t_pd *x, t_symbol *s, int argc, t_atom *argv); typedef void (*t_anymethod)(t_pd *x, t_symbol *s, int argc, t_atom *argv); struct _class { t_symbol *c_name; /* name (mostly for error reporting) */ t_symbol *c_helpname; /* name of help file */ t_symbol *c_externdir; /* directory extern was loaded from */ size_t c_size; /* size of an instance */ #ifdef PDINSTANCE t_methodentry **c_methods; /* methods other than bang, etc below */ #else t_methodentry *c_methods; #endif int c_nmethod; /* number of methods */ t_method c_freemethod; /* function to call before freeing */ t_bangmethod c_bangmethod; /* common methods */ t_pointermethod c_pointermethod; t_floatmethod c_floatmethod; t_symbolmethod c_symbolmethod; t_listmethod c_listmethod; t_anymethod c_anymethod; const struct _widgetbehavior *c_wb; /* "gobjs" only */ const struct _parentwidgetbehavior *c_pwb;/* widget behavior in parent */ t_savefn c_savefn; /* function to call when saving */ t_propertiesfn c_propertiesfn; /* function to start prop dialog */ struct _class *c_next; int c_floatsignalin; /* onset to float for signal input */ unsigned int c_gobj:1; /* true if is a gobj */ unsigned int c_patchable:1; /* true if we have a t_object header */ unsigned int c_firstin:1; /* if so, true if drawing first inlet */ unsigned int c_drawcommand:1; /* drawing command for a template */ unsigned int c_multichannel:1; /* can deal with multichannel sigs */ unsigned int c_nopromotesig:1; /* don't promote scalars to signals */ unsigned int c_nopromoteleft:1; /* not even the main (left) inlet */ t_classfreefn c_classfreefn; /* function to call before freeing class */ }; /* m_pd.c */ EXTERN void pd_init_systems(void); EXTERN void pd_term_systems(void); /* m_class.c */ EXTERN void pd_emptylist(t_pd *x); /* m_obj.c */ EXTERN int obj_noutlets(const t_object *x); EXTERN int obj_ninlets(const t_object *x); EXTERN t_outconnect *obj_starttraverseoutlet(const t_object *x, t_outlet **op, int nout); EXTERN t_outconnect *obj_nexttraverseoutlet(t_outconnect *lastconnect, t_object **destp, t_inlet **inletp, int *whichp); EXTERN t_outconnect *obj_connect(t_object *source, int outno, t_object *sink, int inno); EXTERN void obj_disconnect(t_object *source, int outno, t_object *sink, int inno); EXTERN void outlet_setstacklim(void); EXTERN int obj_issignalinlet(const t_object *x, int m); EXTERN int obj_issignaloutlet(const t_object *x, int m); EXTERN int obj_nsiginlets(const t_object *x); EXTERN int obj_nsigoutlets(const t_object *x); EXTERN int obj_siginletindex(const t_object *x, int m); EXTERN int obj_sigoutletindex(const t_object *x, int m); EXTERN t_float *obj_findsignalscalar(const t_object *x, int m); /* s_inter.c */ void pd_globallock(void); void pd_globalunlock(void); /* misc */ #ifndef SYMTABHASHSIZE /* set this to, say, 1024 for small memory footprint */ #define SYMTABHASHSIZE 16384 #endif /* SYMTABHASHSIZE */ EXTERN t_pd *glob_evalfile(t_pd *ignore, t_symbol *name, t_symbol *dir); EXTERN void glob_initfromgui(void *dummy, t_symbol *s, int argc, t_atom *argv); EXTERN void glob_quit(void *dummy); /* glob_exit(0); */ EXTERN void glob_exit(void *dummy, t_float status); EXTERN void open_via_helppath(const char *name, const char *dir); #define __m_imp_h_ #endif /* __m_imp_h_ */ ================================================ FILE: libs/libpd/pure-data/src/m_memory.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ #include #include #include "m_pd.h" #if (defined LOUD) || (defined DEBUGMEM) # include #endif /* #define DEBUGMEM */ #ifdef DEBUGMEM static int totalmem = 0; #endif void *getbytes(size_t nbytes) { void *ret; if (nbytes < 1) nbytes = 1; ret = (void *)calloc(nbytes, 1); #ifdef LOUD fprintf(stderr, "new %lx %d\n", (int)ret, nbytes); #endif /* LOUD */ #ifdef DEBUGMEM totalmem += nbytes; #endif if (!ret) post("pd: getbytes() failed -- out of memory"); return (ret); } void *getzbytes(size_t nbytes) /* obsolete name */ { return (getbytes(nbytes)); } void *copybytes(const void *src, size_t nbytes) { void *ret; ret = getbytes(nbytes); if (nbytes && ret) memcpy(ret, src, nbytes); return (ret); } void *resizebytes(void *old, size_t oldsize, size_t newsize) { void *ret; if (newsize < 1) newsize = 1; if (oldsize < 1) oldsize = 1; ret = (void *)realloc((char *)old, newsize); if (newsize > oldsize && ret) memset(((char *)ret) + oldsize, 0, newsize - oldsize); #ifdef LOUD fprintf(stderr, "resize %lx %d --> %lx %d\n", (int)old, oldsize, (int)ret, newsize); #endif /* LOUD */ #ifdef DEBUGMEM totalmem += (newsize - oldsize); #endif if (!ret) post("pd: resizebytes() failed -- out of memory"); return (ret); } void freebytes(void *fatso, size_t nbytes) { if (nbytes == 0) nbytes = 1; #ifdef LOUD fprintf(stderr, "free %lx %d\n", (int)fatso, nbytes); #endif /* LOUD */ #ifdef DEBUGMEM totalmem -= nbytes; #endif free(fatso); } #ifdef DEBUGMEM void glob_foo(void *dummy, t_symbol *s, int argc, t_atom *argv) { fprintf(stderr, "total mem %d\n", totalmem); } #endif ================================================ FILE: libs/libpd/pure-data/src/m_obj.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* this file handles Max-style patchable objects, i.e., objects which can interconnect via inlets and outlets; also, the (terse) generic behavior for "gobjs" appears at the end of this file. */ #include "m_pd.h" #include "m_imp.h" #include #include "m_private_utils.h" #if defined(_MSC_VER) #define INLINE __forceinline #elif defined(__GNUC__) #define INLINE inline __attribute__((always_inline)) #else #define INLINE inline #endif union inletunion { t_symbol *iu_symto; t_gpointer *iu_pointerslot; t_float *iu_floatslot; t_symbol **iu_symslot; t_float iu_floatsignalvalue; }; struct _inlet { t_pd i_pd; struct _inlet *i_next; t_object *i_owner; t_pd *i_dest; t_symbol *i_symfrom; union inletunion i_un; }; #define i_symto i_un.iu_symto #define i_pointerslot i_un.iu_pointerslot #define i_floatslot i_un.iu_floatslot #define i_symslot i_un.iu_symslot static t_class *inlet_class, *pointerinlet_class, *floatinlet_class, *symbolinlet_class; #define ISINLET(pd) ((*(pd) == inlet_class) || \ (*(pd) == pointerinlet_class) || \ (*(pd) == floatinlet_class) || \ (*(pd) == symbolinlet_class)) /* --------------------- generic inlets ala max ------------------ */ t_inlet *inlet_new(t_object *owner, t_pd *dest, t_symbol *s1, t_symbol *s2) { t_inlet *x = (t_inlet *)pd_new(inlet_class), *y, *y2; x->i_owner = owner; x->i_dest = dest; if (s1 == &s_signal) x->i_un.iu_floatsignalvalue = 0; else x->i_symto = s2; x->i_symfrom = s1; x->i_next = 0; if ((y = owner->ob_inlet)) { while ((y2 = y->i_next)) y = y2; y->i_next = x; } else owner->ob_inlet = x; return (x); } t_inlet *signalinlet_new(t_object *owner, t_float f) { t_inlet *x = inlet_new(owner, &owner->ob_pd, &s_signal, &s_signal); x->i_un.iu_floatsignalvalue = f; return (x); } static void inlet_wrong(t_inlet *x, t_symbol *s) { pd_error(x->i_owner, "inlet: expected '%s' but got '%s'", x->i_symfrom->s_name, s->s_name); } static void inlet_list(t_inlet *x, t_symbol *s, int argc, t_atom *argv); extern t_class *vinlet_class; /* LATER figure out how to make these efficient: */ static void inlet_bang(t_inlet *x) { if (x->i_symfrom == &s_bang) pd_vmess(x->i_dest, x->i_symto, ""); else if (!x->i_symfrom) pd_bang(x->i_dest); else if (x->i_symfrom == &s_list) inlet_list(x, &s_bang, 0, 0); else if (x->i_symfrom == &s_signal && zgetfn(x->i_dest, gensym("fwd"))) vmess(x->i_dest, gensym("fwd"), "s", &s_bang); else inlet_wrong(x, &s_bang); } static void inlet_pointer(t_inlet *x, t_gpointer *gp) { if (x->i_symfrom == &s_pointer) pd_vmess(x->i_dest, x->i_symto, "p", gp); else if (!x->i_symfrom) pd_pointer(x->i_dest, gp); else if (x->i_symfrom == &s_list) { t_atom a; SETPOINTER(&a, gp); inlet_list(x, &s_pointer, 1, &a); } else inlet_wrong(x, &s_pointer); } static void inlet_float(t_inlet *x, t_float f) { if (x->i_symfrom == &s_float) pd_vmess(x->i_dest, x->i_symto, "f", (t_floatarg)f); else if (x->i_symfrom == &s_signal) x->i_un.iu_floatsignalvalue = f; else if (!x->i_symfrom) pd_float(x->i_dest, f); else if (x->i_symfrom == &s_list) { t_atom a; SETFLOAT(&a, f); inlet_list(x, &s_float, 1, &a); } else inlet_wrong(x, &s_float); } static void inlet_symbol(t_inlet *x, t_symbol *s) { if (x->i_symfrom == &s_symbol) pd_vmess(x->i_dest, x->i_symto, "s", s); else if (!x->i_symfrom) pd_symbol(x->i_dest, s); else if (x->i_symfrom == &s_list) { t_atom a; SETSYMBOL(&a, s); inlet_list(x, &s_symbol, 1, &a); } else if (x->i_symfrom == &s_signal && zgetfn(x->i_dest, gensym("fwd"))) vmess(x->i_dest, gensym("fwd"), "ss", &s_symbol, s); else inlet_wrong(x, &s_symbol); } /* forward a message to an inlet~ object */ static void inlet_fwd(t_inlet *x, t_symbol *s, int argc, t_atom *argv) { t_atom *argvec = (t_atom *)alloca((argc+1) * sizeof(t_atom)); int i; SETSYMBOL(argvec, s); for (i = 0; i < argc; i++) argvec[i+1] = argv[i]; typedmess(x->i_dest, gensym("fwd"), argc+1, argvec); } static void inlet_list(t_inlet *x, t_symbol *s, int argc, t_atom *argv) { t_atom at; if (x->i_symfrom == &s_list || x->i_symfrom == &s_float || x->i_symfrom == &s_symbol || x->i_symfrom == &s_pointer) typedmess(x->i_dest, x->i_symto, argc, argv); else if (!x->i_symfrom) pd_list(x->i_dest, s, argc, argv); else if (!argc) inlet_bang(x); else if (argc==1 && argv->a_type == A_FLOAT) inlet_float(x, atom_getfloat(argv)); else if (argc==1 && argv->a_type == A_SYMBOL) inlet_symbol(x, atom_getsymbol(argv)); else if (x->i_symfrom == &s_signal && zgetfn(x->i_dest, gensym("fwd"))) inlet_fwd(x, &s_list, argc, argv); else post("class %s", class_getname(*x->i_dest)), inlet_wrong(x, &s_list); } static void inlet_anything(t_inlet *x, t_symbol *s, int argc, t_atom *argv) { if (x->i_symfrom == s) { /* the "symto" field is undefined for signal inlets, so we don't attempt to translate the selector, just forward the original msg. */ if (x->i_symfrom == &s_signal) typedmess(x->i_dest, s, argc, argv); else typedmess(x->i_dest, x->i_symto, argc, argv); } else if (!x->i_symfrom) typedmess(x->i_dest, s, argc, argv); else if (x->i_symfrom == &s_signal && zgetfn(x->i_dest, gensym("fwd"))) inlet_fwd(x, s, argc, argv); else inlet_wrong(x, s); } void inlet_free(t_inlet *x) { t_object *y = x->i_owner; t_inlet *x2; if (y->ob_inlet == x) y->ob_inlet = x->i_next; else for (x2 = y->ob_inlet; x2; x2 = x2->i_next) if (x2->i_next == x) { x2->i_next = x->i_next; break; } t_freebytes(x, sizeof(*x)); } /* ----- pointerinlets, floatinlets, syminlets: optimized inlets ------- */ static void pointerinlet_pointer(t_inlet *x, t_gpointer *gp) { gpointer_unset(x->i_pointerslot); *(x->i_pointerslot) = *gp; if (gp->gp_stub) gp->gp_stub->gs_refcount++; } t_inlet *pointerinlet_new(t_object *owner, t_gpointer *gp) { t_inlet *x = (t_inlet *)pd_new(pointerinlet_class), *y, *y2; x->i_owner = owner; x->i_dest = 0; x->i_symfrom = &s_pointer; x->i_pointerslot = gp; x->i_next = 0; if ((y = owner->ob_inlet)) { while ((y2 = y->i_next)) y = y2; y->i_next = x; } else owner->ob_inlet = x; return (x); } static void floatinlet_float(t_inlet *x, t_float f) { *(x->i_floatslot) = f; } t_inlet *floatinlet_new(t_object *owner, t_float *fp) { t_inlet *x = (t_inlet *)pd_new(floatinlet_class), *y, *y2; x->i_owner = owner; x->i_dest = 0; x->i_symfrom = &s_float; x->i_floatslot = fp; x->i_next = 0; if ((y = owner->ob_inlet)) { while ((y2 = y->i_next)) y = y2; y->i_next = x; } else owner->ob_inlet = x; return (x); } static void symbolinlet_symbol(t_inlet *x, t_symbol *s) { *(x->i_symslot) = s; } t_inlet *symbolinlet_new(t_object *owner, t_symbol **sp) { t_inlet *x = (t_inlet *)pd_new(symbolinlet_class), *y, *y2; x->i_owner = owner; x->i_dest = 0; x->i_symfrom = &s_symbol; x->i_symslot = sp; x->i_next = 0; if ((y = owner->ob_inlet)) { while ((y2 = y->i_next)) y = y2; y->i_next = x; } else owner->ob_inlet = x; return (x); } /* ---------------------- routine to handle lists ---------------------- */ /* objects interpret lists by feeding them to the individual inlets. Before you call this check that the object doesn't have a more specific way to handle lists. */ void obj_list(t_object *x, t_symbol *s, int argc, t_atom *argv) { t_atom *ap; int count; t_inlet *ip = ((t_object *)x)->ob_inlet; if (!argc) { pd_emptylist(&x->ob_pd); return; } for (count = argc-1, ap = argv+1; ip && count--; ap++, ip = ip->i_next) { if (ap->a_type == A_POINTER) pd_pointer(&ip->i_pd, ap->a_w.w_gpointer); else if (ap->a_type == A_FLOAT) pd_float(&ip->i_pd, ap->a_w.w_float); else pd_symbol(&ip->i_pd, ap->a_w.w_symbol); } if (argv->a_type == A_POINTER) pd_pointer(&x->ob_pd, argv->a_w.w_gpointer); else if (argv->a_type == A_FLOAT) pd_float(&x->ob_pd, argv->a_w.w_float); else pd_symbol(&x->ob_pd, argv->a_w.w_symbol); } /* --------------------------- outlets ------------------------------ */ struct _outconnect { struct _outconnect *oc_next; t_pd *oc_to; }; struct _outlet { t_object *o_owner; struct _outlet *o_next; t_outconnect *o_connections; t_symbol *o_sym; }; /* ------- backtracer - keep track of stack for backtracing --------- */ #define NARGS 5 typedef struct _msgstack { struct _backtracer *m_owner; t_symbol *m_sel; int m_argc; t_atom m_argv[NARGS]; struct _msgstack *m_next; } t_msgstack; typedef struct _backtracer { t_pd b_pd; t_outconnect *b_connections; t_pd *b_owner; } t_backtracer; static t_msgstack *backtracer_stack; int backtracer_cantrace = 0; int backtracer_tracing; t_class *backtracer_class; static PERTHREAD int stackcount = 0; /* iteration counter */ static PERTHREAD int overflow = 0; #define STACKITER 1000 /* maximum iterations allowed */ static PERTHREAD int outlet_eventno; /* initialize stack depth count on each incoming event that can set off messages so that the outlet functions can check to prevent stack overflow] from message recursion. Also count message initiations. */ static INLINE int stackcount_add(void) { /* set overflow flag to prevent any further messaging */ if (++stackcount >= STACKITER) overflow = 1; return !overflow; } static INLINE void stackcount_release(void) { /* once the stack is completely unwound, we can clear the overflow flag */ if (--stackcount == 0) overflow = 0; } void outlet_setstacklim(void) { t_msgstack *m; while ((m = backtracer_stack)) backtracer_stack = m->m_next, t_freebytes(m, sizeof (*m)); stackcount = 0; outlet_eventno++; } /* get a number unique to the (clock, MIDI, GUI, etc.) event we're on */ int sched_geteventno(void) { return (outlet_eventno); } /* get pointer to connection list for an outlet (for editing/traversing) */ static t_outconnect **outlet_getconnectionpointer(t_outlet *x) { if (x->o_connections && *(x->o_connections->oc_to) == backtracer_class) return (&((t_backtracer *)(x->o_connections->oc_to))->b_connections); else return (&x->o_connections); } static void backtracer_printmsg(t_pd *who, t_symbol *s, int argc, t_atom *argv) { char msgbuf[104]; int nprint = (argc > NARGS ? NARGS : argc), nchar, i; snprintf(msgbuf, 100, "%s: %s ", class_getname(*who), s->s_name); nchar = strlen(msgbuf); for (i = 0; i < nprint && nchar < 100; i++) if (nchar < 100) { char buf[100]; atom_string(&argv[i], buf, 100); snprintf(msgbuf + nchar, 100-nchar, " %s", buf); nchar = strlen(msgbuf); } if (argc > nprint && nchar < 100) sprintf(msgbuf + nchar, "..."); else memcpy(msgbuf+100, "...", 4); /* in case we didn't finish */ logpost(who, 2, "%s", msgbuf); } static void backtracer_anything(t_backtracer *x, t_symbol *s, int argc, t_atom *argv) { t_msgstack *m = (t_msgstack *)t_getbytes(sizeof(t_msgstack)); t_outconnect *oc; int ncopy = (argc > NARGS ? NARGS : argc), i; m->m_next = backtracer_stack; backtracer_stack = m; m->m_sel = s; m->m_argc = argc; for (i = 0; i < ncopy; i++) m->m_argv[i] = argv[i]; m->m_owner = x; if (backtracer_tracing) backtracer_printmsg(x->b_owner, s, argc, argv); for (oc = x->b_connections; oc; oc = oc->oc_next) typedmess(oc->oc_to, s, argc, argv); backtracer_stack = m->m_next; t_freebytes(m, sizeof(*m)); } t_backtracer *backtracer_new(t_pd *owner) { t_backtracer *x = (t_backtracer *)pd_new(backtracer_class); x->b_connections = 0; x->b_owner = owner; return (x); } int backtracer_settracing(void *x, int tracing) { if (tracing) { if (backtracer_tracing) { pd_error(x, "trace: already tracing"); return (0); } else { backtracer_tracing = 1; return (1); } } else /* when stopping, print backtrace to here */ { t_msgstack *m = backtracer_stack; post("backtrace:"); while (m) { backtracer_printmsg(m->m_owner->b_owner, m->m_sel, m->m_argc, m->m_argv); m = m->m_next; } backtracer_tracing = 0; return (0); } } void canvas_settracing(int onoff); static t_clock *backtrace_unsetclock; static void backtrace_dounsettracing(void *dummy) { canvas_settracing(0); backtracer_cantrace = 0; clock_free(backtrace_unsetclock); backtrace_unsetclock = 0; } /* globally turn tracing on and off. */ void glob_settracing(void *dummy, t_float f) { #ifndef PDINSTANCE /* this won't work with pd instances so just don't */ if (f != 0) { if (backtracer_cantrace) post("pd: tracing already enabled"); else canvas_settracing(1); backtracer_cantrace = 1; } else { if (!backtracer_cantrace) post("pd: tracing already disabled"); else if (!backtrace_unsetclock) { backtrace_unsetclock = clock_new(dummy, (t_method)backtrace_dounsettracing); clock_delay(backtrace_unsetclock, 0); } } #endif } /* this is called on every object, via canvas_settracing() call above */ void obj_dosettracing(t_object *ob, int onoff) { t_outlet *o; for (o = ob->ob_outlet; o; o = o->o_next) { if (onoff) { t_backtracer *b = backtracer_new(&ob->ob_pd); b->b_connections = o->o_connections; o->o_connections = (t_outconnect *)t_getbytes(sizeof(t_outconnect)); o->o_connections->oc_next = 0; o->o_connections->oc_to = &b->b_pd; } else if (o->o_connections && (*o->o_connections->oc_to == backtracer_class)) { t_backtracer *b = (t_backtracer *)o->o_connections->oc_to; t_freebytes(o->o_connections, sizeof(*o->o_connections)); o->o_connections = b->b_connections; t_freebytes(b, sizeof(*b)); } else bug("obj_dosettracing"); } } t_outlet *outlet_new(t_object *owner, t_symbol *s) { t_outlet *x = (t_outlet *)getbytes(sizeof(*x)), *y, *y2; x->o_owner = owner; x->o_next = 0; if ((y = owner->ob_outlet)) { while ((y2 = y->o_next)) y = y2; y->o_next = x; } else owner->ob_outlet = x; if (backtracer_cantrace) { t_backtracer *b = backtracer_new(&owner->ob_pd); x->o_connections = (t_outconnect *)t_getbytes(sizeof(t_outconnect)); x->o_connections->oc_next = 0; x->o_connections->oc_to = &b->b_pd; } else x->o_connections = 0; x->o_sym = s; return (x); } static void outlet_stackerror(t_outlet *x) { pd_error(x->o_owner, "stack overflow"); } void outlet_bang(t_outlet *x) { t_outconnect *oc; if(!stackcount_add()) outlet_stackerror(x); else for (oc = x->o_connections; oc; oc = oc->oc_next) pd_bang(oc->oc_to); stackcount_release(); } void outlet_pointer(t_outlet *x, t_gpointer *gp) { t_outconnect *oc; t_gpointer gpointer; if(!stackcount_add()) outlet_stackerror(x); else { gpointer = *gp; for (oc = x->o_connections; oc; oc = oc->oc_next) pd_pointer(oc->oc_to, &gpointer); } stackcount_release(); } void outlet_float(t_outlet *x, t_float f) { t_outconnect *oc; if(!stackcount_add()) outlet_stackerror(x); else for (oc = x->o_connections; oc; oc = oc->oc_next) pd_float(oc->oc_to, f); stackcount_release(); } void outlet_symbol(t_outlet *x, t_symbol *s) { t_outconnect *oc; if(!stackcount_add()) outlet_stackerror(x); else for (oc = x->o_connections; oc; oc = oc->oc_next) pd_symbol(oc->oc_to, s); stackcount_release(); } void outlet_list(t_outlet *x, t_symbol *s, int argc, t_atom *argv) { t_outconnect *oc; if(!stackcount_add()) outlet_stackerror(x); else for (oc = x->o_connections; oc; oc = oc->oc_next) pd_list(oc->oc_to, s, argc, argv); stackcount_release(); } void outlet_anything(t_outlet *x, t_symbol *s, int argc, t_atom *argv) { t_outconnect *oc; if(!stackcount_add()) outlet_stackerror(x); else for (oc = x->o_connections; oc; oc = oc->oc_next) typedmess(oc->oc_to, s, argc, argv); stackcount_release(); } /* get the outlet's declared symbol */ t_symbol *outlet_getsymbol(t_outlet *x) { return (x->o_sym); } void outlet_free(t_outlet *x) { t_object *y = x->o_owner; t_outlet *x2; if (y->ob_outlet == x) y->ob_outlet = x->o_next; else for (x2 = y->ob_outlet; x2; x2 = x2->o_next) if (x2->o_next == x) { x2->o_next = x->o_next; break; } t_freebytes(x, sizeof(*x)); } /* connect an outlet of one object to an inlet of another. The receiving "pd" is usually a patchable object, but this may be used to add a non-patchable pd to an outlet by specifying the 0th inlet. */ t_outconnect *obj_connect(t_object *source, int outno, t_object *sink, int inno) { t_inlet *i; t_outlet *o; t_pd *to; t_outconnect *oc, *oc2, **ochead; for (o = source->ob_outlet; o && outno; o = o->o_next, outno--) ; if (!o) return (0); if (sink->ob_pd->c_firstin) { if (!inno) { to = &sink->ob_pd; goto doit; } else inno--; } for (i = sink->ob_inlet; i && inno; i = i->i_next, inno--) ; if (!i) return (0); to = &i->i_pd; doit: ochead = outlet_getconnectionpointer(o); oc = (t_outconnect *)t_getbytes(sizeof(*oc)); oc->oc_next = 0; oc->oc_to = to; /* append it to the end of the list */ /* LATER we might cache the last "oc" to make this faster. */ if ((oc2 = *ochead)) { while (oc2->oc_next) oc2 = oc2->oc_next; oc2->oc_next = oc; } else *ochead = oc; if (o->o_sym == &s_signal) canvas_update_dsp(); return (oc); } void obj_disconnect(t_object *source, int outno, t_object *sink, int inno) { t_inlet *i; t_outlet *o; t_pd *to; t_outconnect *oc, *oc2, **ochead; for (o = source->ob_outlet; o && outno; o = o->o_next, outno--) ; if (!o) return; if (sink->ob_pd->c_firstin) { if (!inno) { to = &sink->ob_pd; goto doit; } else inno--; } for (i = sink->ob_inlet; i && inno; i = i->i_next, inno--) ; if (!i) return; to = &i->i_pd; doit: ochead = outlet_getconnectionpointer(o); if (!(oc = *ochead)) return; if (oc->oc_to == to) { *ochead = oc->oc_next; freebytes(oc, sizeof(*oc)); goto done; } while ((oc2 = oc->oc_next)) { if (oc2->oc_to == to) { oc->oc_next = oc2->oc_next; freebytes(oc2, sizeof(*oc2)); goto done; } oc = oc2; } done: if (o->o_sym == &s_signal) canvas_update_dsp(); } /* ------ traversal routines for code that can't see our structures ------ */ int obj_noutlets(const t_object *x) { int n; t_outlet *o; for (o = x->ob_outlet, n = 0; o; o = o->o_next) n++; return (n); } int obj_ninlets(const t_object *x) { int n; t_inlet *i; for (i = x->ob_inlet, n = 0; i; i = i->i_next) n++; if (x->ob_pd->c_firstin) n++; return (n); } t_outconnect *obj_starttraverseoutlet(const t_object *x, t_outlet **op, int nout) { t_outlet *o = x->ob_outlet; while (nout-- && o) o = o->o_next; *op = o; if (o) return (*outlet_getconnectionpointer(o)); else return (0); } t_outconnect *obj_nexttraverseoutlet(t_outconnect *lastconnect, t_object **destp, t_inlet **inletp, int *whichp) { t_pd *y; y = lastconnect->oc_to; if (ISINLET(y)) { int n; t_inlet *i = (t_inlet *)y, *i2; t_object *dest = i->i_owner; for (n = dest->ob_pd->c_firstin, i2 = dest->ob_inlet; i2 && i2 != i; i2 = i2->i_next) n++; *whichp = n; *destp = dest; *inletp = i; } else { *whichp = 0; *inletp = 0; *destp = ((t_object *)y); } return (lastconnect->oc_next); } /* this one checks that a pd is indeed a patchable object, and returns it, correctly typed, or zero if the check failed. */ t_object *pd_checkobject(t_pd *x) { if ((*x)->c_patchable) return ((t_object *)x); else return (0); } /* move an inlet or outlet to the head of the list */ void obj_moveinletfirst(t_object *x, t_inlet *i) { t_inlet *i2; if (x->ob_inlet == i) return; else for (i2 = x->ob_inlet; i2; i2 = i2->i_next) if (i2->i_next == i) { i2->i_next = i->i_next; i->i_next = x->ob_inlet; x->ob_inlet = i; return; } } void obj_moveoutletfirst(t_object *x, t_outlet *o) { t_outlet *o2; if (x->ob_outlet == o) return; else for (o2 = x->ob_outlet; o2; o2 = o2->o_next) if (o2->o_next == o) { o2->o_next = o->o_next; o->o_next = x->ob_outlet; x->ob_outlet = o; return; } } /* routines for DSP sorting, which are used in d_ugen.c and g_canvas.c */ /* LATER try to consolidate all the slightly different routines. */ int obj_nsiginlets(const t_object *x) { int n; t_inlet *i; for (i = x->ob_inlet, n = 0; i; i = i->i_next) if (i->i_symfrom == &s_signal) n++; if (x->ob_pd->c_firstin && x->ob_pd->c_floatsignalin) n++; return (n); } /* get the index, among signal inlets, of the mth inlet overall */ int obj_siginletindex(const t_object *x, int m) { int n = 0; t_inlet *i; if (x->ob_pd->c_firstin) { if (!m--) return (0); if (x->ob_pd->c_floatsignalin) n++; } for (i = x->ob_inlet; i; i = i->i_next, m--) if (i->i_symfrom == &s_signal) { if (m == 0) return (n); n++; } return (-1); } int obj_issignalinlet(const t_object *x, int m) { t_inlet *i; if (x->ob_pd->c_firstin) { if (!m) return (x->ob_pd->c_firstin && x->ob_pd->c_floatsignalin); else m--; } for (i = x->ob_inlet; i && m; i = i->i_next, m--) ; return (i && (i->i_symfrom == &s_signal)); } int obj_nsigoutlets(const t_object *x) { int n; t_outlet *o; for (o = x->ob_outlet, n = 0; o; o = o->o_next) if (o->o_sym == &s_signal) n++; return (n); } int obj_sigoutletindex(const t_object *x, int m) { int n; t_outlet *o2; for (o2 = x->ob_outlet, n = 0; o2; o2 = o2->o_next, m--) if (o2->o_sym == &s_signal) { if (m == 0) return (n); n++; } return (-1); } int obj_issignaloutlet(const t_object *x, int m) { int n; t_outlet *o2; for (o2 = x->ob_outlet, n = 0; o2 && m--; o2 = o2->o_next); return (o2 && (o2->o_sym == &s_signal)); } /* return a pointer to a scalar holding the inlet's value. If we can't find a value, return a pointer to a fixed location holding zero. This should only happen for a left-hand signal inlet for which no "MAINSIGNALIN" has been provided, in which case the object won't promote scalars correctly. Nonetheless we provide it so that at least such a badly written object won't crash Pd. */ t_float *obj_findsignalscalar(const t_object *x, int m) { t_inlet *i; static float obj_scalarzero = 0; if (x->ob_pd->c_firstin && x->ob_pd->c_floatsignalin) { if (!m--) return (x->ob_pd->c_floatsignalin > 0 ? (t_float *)(((char *)x) + x->ob_pd->c_floatsignalin) : &obj_scalarzero); } for (i = x->ob_inlet; i; i = i->i_next) if (i->i_symfrom == &s_signal) { if (m-- == 0) return (&i->i_un.iu_floatsignalvalue); } return (&obj_scalarzero); /* this should never happen but OK wtf. */ } /* and these are only used in g_io.c... */ int inlet_getsignalindex(t_inlet *x) { int n = 0; t_inlet *i; if (x->i_symfrom != &s_signal) bug("inlet_getsignalindex"); for (i = x->i_owner->ob_inlet, n = 0; i && i != x; i = i->i_next) if (i->i_symfrom == &s_signal) n++; return (n); } int outlet_getsignalindex(t_outlet *x) { int n = 0; t_outlet *o; for (o = x->o_owner->ob_outlet, n = 0; o && o != x; o = o->o_next) if (o->o_sym == &s_signal) n++; return (n); } void obj_saveformat(const t_object *x, t_binbuf *bb) { if (x->te_width) binbuf_addv(bb, "ssf;", &s__X, gensym("f"), (float)x->te_width); } /* this one only in g_clone.c -- LATER consider sending the message without having to chase the linked list every time? */ void obj_sendinlet(t_object *x, int n, t_symbol *s, int argc, t_atom *argv) { t_inlet *i; for (i = x->ob_inlet; i && n; i = i->i_next, n--) ; if (i) typedmess(&i->i_pd, s, argc, argv); else bug("obj_sendinlet"); } /* ------------------- setup routine, somewhat misnamed */ void obj_init(void) { inlet_class = class_new(gensym("inlet"), 0, 0, sizeof(t_inlet), CLASS_PD, 0); class_addbang(inlet_class, inlet_bang); class_addpointer(inlet_class, inlet_pointer); class_addfloat(inlet_class, inlet_float); class_addsymbol(inlet_class, inlet_symbol); class_addlist(inlet_class, inlet_list); class_addanything(inlet_class, inlet_anything); pointerinlet_class = class_new(gensym("inlet"), 0, 0, sizeof(t_inlet), CLASS_PD, 0); class_addpointer(pointerinlet_class, pointerinlet_pointer); class_addanything(pointerinlet_class, inlet_wrong); floatinlet_class = class_new(gensym("inlet"), 0, 0, sizeof(t_inlet), CLASS_PD, 0); class_addfloat(floatinlet_class, (t_method)floatinlet_float); class_addanything(floatinlet_class, inlet_wrong); symbolinlet_class = class_new(gensym("inlet"), 0, 0, sizeof(t_inlet), CLASS_PD, 0); class_addsymbol(symbolinlet_class, symbolinlet_symbol); class_addanything(symbolinlet_class, inlet_wrong); backtracer_class = class_new(gensym("backtracer"), 0, 0, sizeof(t_backtracer), CLASS_PD, 0); class_addanything(backtracer_class, backtracer_anything); } ================================================ FILE: libs/libpd/pure-data/src/m_pd.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ #include "m_pd.h" #include "m_imp.h" #include "g_canvas.h" /* just for LB_LOAD */ /* FIXME no out-of-memory testing yet! */ t_pd *pd_new(t_class *c) { t_pd *x; if (!c) { bug ("pd_new: apparently called before setup routine"); return NULL; } x = (t_pd *)t_getbytes(c->c_size); *x = c; if (c->c_patchable) { ((t_object *)x)->ob_inlet = 0; ((t_object *)x)->ob_outlet = 0; } return (x); } void pd_free(t_pd *x) { t_class *c = *x; if (c->c_freemethod) (*(t_gotfn)(c->c_freemethod))(x); if (c->c_patchable) { while (((t_object *)x)->ob_outlet) outlet_free(((t_object *)x)->ob_outlet); while (((t_object *)x)->ob_inlet) inlet_free(((t_object *)x)->ob_inlet); if (((t_object *)x)->ob_binbuf) binbuf_free(((t_object *)x)->ob_binbuf); } if (c->c_size) t_freebytes(x, c->c_size); } void gobj_save(t_gobj *x, t_binbuf *b) { t_class *c = x->g_pd; if (c->c_savefn) (c->c_savefn)(x, b); } /* deal with several objects bound to the same symbol. If more than one, we actually bind a collection object to the symbol, which forwards messages sent to the symbol. */ static t_class *bindlist_class; typedef struct _bindelem { t_pd *e_who; struct _bindelem *e_next; } t_bindelem; typedef struct _bindlist { t_pd b_pd; t_bindelem *b_list; } t_bindlist; static void bindlist_bang(t_bindlist *x) { t_bindelem *e; for (e = x->b_list; e; e = e->e_next) pd_bang(e->e_who); } static void bindlist_float(t_bindlist *x, t_float f) { t_bindelem *e; for (e = x->b_list; e; e = e->e_next) pd_float(e->e_who, f); } static void bindlist_symbol(t_bindlist *x, t_symbol *s) { t_bindelem *e; for (e = x->b_list; e; e = e->e_next) pd_symbol(e->e_who, s); } static void bindlist_pointer(t_bindlist *x, t_gpointer *gp) { t_bindelem *e; for (e = x->b_list; e; e = e->e_next) pd_pointer(e->e_who, gp); } static void bindlist_list(t_bindlist *x, t_symbol *s, int argc, t_atom *argv) { t_bindelem *e; for (e = x->b_list; e; e = e->e_next) pd_list(e->e_who, s, argc, argv); } static void bindlist_anything(t_bindlist *x, t_symbol *s, int argc, t_atom *argv) { t_bindelem *e; for (e = x->b_list; e; e = e->e_next) pd_typedmess(e->e_who, s, argc, argv); } void m_pd_setup(void) { bindlist_class = class_new(gensym("bindlist"), 0, 0, sizeof(t_bindlist), CLASS_PD, 0); class_addbang(bindlist_class, bindlist_bang); class_addfloat(bindlist_class, (t_method)bindlist_float); class_addsymbol(bindlist_class, bindlist_symbol); class_addpointer(bindlist_class, bindlist_pointer); class_addlist(bindlist_class, bindlist_list); class_addanything(bindlist_class, bindlist_anything); } void pd_bind(t_pd *x, t_symbol *s) { if (s->s_thing) { if (*s->s_thing == bindlist_class) { t_bindlist *b = (t_bindlist *)s->s_thing; t_bindelem *e = (t_bindelem *)getbytes(sizeof(t_bindelem)); e->e_next = b->b_list; e->e_who = x; b->b_list = e; } else { t_bindlist *b = (t_bindlist *)pd_new(bindlist_class); t_bindelem *e1 = (t_bindelem *)getbytes(sizeof(t_bindelem)); t_bindelem *e2 = (t_bindelem *)getbytes(sizeof(t_bindelem)); b->b_list = e1; e1->e_who = x; e1->e_next = e2; e2->e_who = s->s_thing; e2->e_next = 0; s->s_thing = &b->b_pd; } } else s->s_thing = x; } void pd_unbind(t_pd *x, t_symbol *s) { if (s->s_thing == x) s->s_thing = 0; else if (s->s_thing && *s->s_thing == bindlist_class) { /* bindlists always have at least two elements... if the number goes down to one, get rid of the bindlist and bind the symbol straight to the remaining element. */ t_bindlist *b = (t_bindlist *)s->s_thing; t_bindelem *e, *e2; if ((e = b->b_list)->e_who == x) { b->b_list = e->e_next; e->e_who = 0; e->e_next = 0; freebytes(e, sizeof(t_bindelem)); } else for (e = b->b_list; (e2 = e->e_next); e = e2) if (e2->e_who == x) { e->e_next = e2->e_next; e2->e_who = 0; e2->e_next = 0; freebytes(e2, sizeof(t_bindelem)); break; } if (!b->b_list->e_next) { s->s_thing = b->b_list->e_who; freebytes(b->b_list, sizeof(t_bindelem)); b->b_list = 0; pd_free(&b->b_pd); b = 0; } } else pd_error(x, "%s: couldn't unbind", s->s_name); } t_pd *pd_findbyclass(t_symbol *s, const t_class *c) { t_pd *x = 0; if (!s->s_thing) return (0); if (*s->s_thing == c) return (s->s_thing); if (*s->s_thing == bindlist_class) { t_bindlist *b = (t_bindlist *)s->s_thing; t_bindelem *e, *e2; int warned = 0; for (e = b->b_list; e; e = e->e_next) if (*e->e_who == c) { if (x && !warned) { post("warning: %s: multiply defined", s->s_name); warned = 1; } x = e->e_who; } } return x; } /* stack for maintaining bindings for the #X symbol during nestable loads. */ typedef struct _gstack { t_pd *g_what; t_symbol *g_loadingabstraction; struct _gstack *g_next; } t_gstack; static t_gstack *gstack_head = 0; static t_pd *lastpopped; static t_symbol *pd_loadingabstraction; int pd_setloadingabstraction(t_symbol *sym) { t_gstack *foo = gstack_head; for (foo = gstack_head; foo; foo = foo->g_next) if (foo->g_loadingabstraction == sym) return (1); pd_loadingabstraction = sym; return (0); } void pd_pushsym(t_pd *x) { t_gstack *y = (t_gstack *)t_getbytes(sizeof(*y)); y->g_what = s__X.s_thing; y->g_next = gstack_head; y->g_loadingabstraction = pd_loadingabstraction; pd_loadingabstraction = 0; gstack_head = y; s__X.s_thing = x; } void pd_popsym(t_pd *x) { if (!gstack_head || s__X.s_thing != x) bug("gstack_pop"); else { t_gstack *headwas = gstack_head; s__X.s_thing = headwas->g_what; gstack_head = headwas->g_next; t_freebytes(headwas, sizeof(*headwas)); lastpopped = x; } } void pd_doloadbang(void) { if (lastpopped) pd_vmess(lastpopped, gensym("loadbang"), "f", LB_LOAD); lastpopped = 0; } void pd_bang(t_pd *x) { (*(*x)->c_bangmethod)(x); } void pd_float(t_pd *x, t_float f) { (*(*x)->c_floatmethod)(x, f); } void pd_pointer(t_pd *x, t_gpointer *gp) { (*(*x)->c_pointermethod)(x, gp); } void pd_symbol(t_pd *x, t_symbol *s) { (*(*x)->c_symbolmethod)(x, s); } void pd_list(t_pd *x, t_symbol *s, int argc, t_atom *argv) { (*(*x)->c_listmethod)(x, &s_list, argc, argv); } void pd_anything(t_pd *x, t_symbol *s, int argc, t_atom *argv) { (*(*x)->c_anymethod)(x, s, argc, argv); } void mess_init(void); void obj_init(void); void conf_init(void); void glob_init(void); void garray_init(void); void ooura_term(void); void pd_init(void) { #ifndef PDINSTANCE static int initted = 0; if (initted) return; initted = 1; #else if (pd_instances) return; pd_instances = (t_pdinstance **)getbytes(sizeof(*pd_instances)); pd_instances[0] = &pd_maininstance; pd_ninstances = 1; #endif pd_init_systems(); } EXTERN void pd_init_systems(void) { mess_init(); sys_lock(); obj_init(); conf_init(); glob_init(); garray_init(); sys_unlock(); } EXTERN void pd_term_systems(void) { sys_lock(); sys_unlock(); } EXTERN t_canvas *pd_getcanvaslist(void) { return (pd_this->pd_canvaslist); } ================================================ FILE: libs/libpd/pure-data/src/m_pd.h ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ #ifndef __m_pd_h_ #if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus) extern "C" { #endif #define PD_MAJOR_VERSION 0 #define PD_MINOR_VERSION 54 #define PD_BUGFIX_VERSION 1 #define PD_TEST_VERSION "" extern int pd_compatibilitylevel; /* e.g., 43 for pd 0.43 compatibility */ /* old name for "MSW" flag -- we have to take it for the sake of many old "nmakefiles" for externs, which will define NT and not MSW */ #if defined(NT) && !defined(MSW) #define MSW #endif /* These pragmas are only used for MSVC, not MinGW or Cygwin */ #ifdef _MSC_VER /* #pragma warning( disable : 4091 ) */ #pragma warning( disable : 4305 ) /* uncast const double to float */ #pragma warning( disable : 4244 ) /* uncast float/int conversion etc. */ #pragma warning( disable : 4101 ) /* unused automatic variables */ #endif /* _MSC_VER */ /* the external storage class is "extern" in UNIX; in MSW it's ugly. */ #ifndef EXTERN #ifdef _WIN32 #ifdef PD_INTERNAL #define EXTERN __declspec(dllexport) extern #else #define EXTERN __declspec(dllimport) extern #endif /* PD_INTERNAL */ #else #define EXTERN extern #endif /* _WIN32 */ #endif /* EXTERN */ /* On most c compilers, you can just say "struct foo;" to declare a structure whose elements are defined elsewhere. On very old MSVC versions, when compiling C (but not C++) code, you have to say "extern struct foo;". So we make a stupid macro: */ #if defined(_MSC_VER) && !defined(_LANGUAGE_C_PLUS_PLUS) \ && !defined(__cplusplus) && (_MSC_VER < 1700) #define EXTERN_STRUCT extern struct #else #define EXTERN_STRUCT struct #endif /* Define some attributes, specific to the compiler */ #if defined(__GNUC__) #define ATTRIBUTE_FORMAT_PRINTF(a, b) __attribute__ ((format (printf, a, b))) #else #define ATTRIBUTE_FORMAT_PRINTF(a, b) #endif #if !defined(_SIZE_T) && !defined(_SIZE_T_) #include /* just for size_t -- how lame! */ #endif /* Microsoft Visual Studio is not C99, but since VS2015 has included most C99 headers: https://docs.microsoft.com/en-us/previous-versions/hh409293(v=vs.140)#c-runtime-library These definitions recreate stdint.h types, but only in pre-2015 Visual Studio: */ #if defined(_MSC_VER) && _MSC_VER < 1900 typedef signed __int8 int8_t; typedef signed __int16 int16_t; typedef signed __int32 int32_t; typedef signed __int64 int64_t; typedef unsigned __int8 uint8_t; typedef unsigned __int16 uint16_t; typedef unsigned __int32 uint32_t; typedef unsigned __int64 uint64_t; #else # include #endif /* for FILE, needed by sys_fopen() and sys_fclose() only */ #include #define MAXPDSTRING 1000 /* use this for anything you want */ #define MAXPDARG 5 /* max number of args we can typecheck today */ /* signed and unsigned integer types the size of a pointer: */ #if !defined(PD_LONGINTTYPE) #if defined(_WIN32) && defined(_WIN64) #define PD_LONGINTTYPE long long #else #define PD_LONGINTTYPE long #endif #endif #if !defined(PD_FLOATSIZE) /* normally, our floats (t_float, t_sample,...) are 32bit */ # define PD_FLOATSIZE 32 #endif #if PD_FLOATSIZE == 32 # define PD_FLOATTYPE float /* an unsigned int of the same size as FLOATTYPE: */ # define PD_FLOATUINTTYPE uint32_t #elif PD_FLOATSIZE == 64 # define PD_FLOATTYPE double # define PD_FLOATUINTTYPE uint64_t #else # error invalid FLOATSIZE: must be 32 or 64 #endif typedef PD_LONGINTTYPE t_int; /* pointer-size integer */ typedef PD_FLOATTYPE t_float; /* a float type at most the same size */ typedef PD_FLOATTYPE t_floatarg; /* float type for function calls */ typedef struct _symbol { const char *s_name; struct _class **s_thing; struct _symbol *s_next; } t_symbol; EXTERN_STRUCT _array; #define t_array struct _array /* g_canvas.h */ /* pointers to glist and array elements go through a "stub" which sticks around after the glist or array is freed. The stub itself is deleted when both the glist/array is gone and the refcount is zero, ensuring that no gpointers are pointing here. */ #define GP_NONE 0 /* the stub points nowhere (has been cut off) */ #define GP_GLIST 1 /* the stub points to a glist element */ #define GP_ARRAY 2 /* ... or array */ typedef struct _gstub { union { struct _glist *gs_glist; /* glist we're in */ struct _array *gs_array; /* array we're in */ } gs_un; int gs_which; /* GP_GLIST/GP_ARRAY */ int gs_refcount; /* number of gpointers pointing here */ } t_gstub; typedef struct _gpointer /* pointer to a gobj in a glist */ { union { struct _scalar *gp_scalar; /* scalar we're in (if glist) */ union word *gp_w; /* raw data (if array) */ } gp_un; int gp_valid; /* number which must match gpointee */ t_gstub *gp_stub; /* stub which points to glist/array */ } t_gpointer; typedef union word { t_float w_float; t_symbol *w_symbol; t_gpointer *w_gpointer; t_array *w_array; struct _binbuf *w_binbuf; int w_index; } t_word; typedef enum { A_NULL, A_FLOAT, A_SYMBOL, A_POINTER, A_SEMI, A_COMMA, A_DEFFLOAT, A_DEFSYM, A_DOLLAR, A_DOLLSYM, A_GIMME, A_CANT } t_atomtype; #define A_DEFSYMBOL A_DEFSYM /* better name for this */ typedef struct _atom { t_atomtype a_type; union word a_w; } t_atom; EXTERN_STRUCT _class; #define t_class struct _class EXTERN_STRUCT _outlet; #define t_outlet struct _outlet EXTERN_STRUCT _inlet; #define t_inlet struct _inlet EXTERN_STRUCT _binbuf; #define t_binbuf struct _binbuf EXTERN_STRUCT _clock; #define t_clock struct _clock EXTERN_STRUCT _outconnect; #define t_outconnect struct _outconnect EXTERN_STRUCT _glist; #define t_glist struct _glist #define t_canvas struct _glist /* LATER lose this */ EXTERN_STRUCT _template; typedef t_class *t_pd; /* pure datum: nothing but a class pointer */ typedef struct _gobj /* a graphical object */ { t_pd g_pd; /* pure datum header (class) */ struct _gobj *g_next; /* next in list */ } t_gobj; typedef struct _scalar /* a graphical object holding data */ { t_gobj sc_gobj; /* header for graphical object */ t_symbol *sc_template; /* template name (LATER replace with pointer) */ t_word sc_vec[1]; /* indeterminate-length array of words */ } t_scalar; typedef struct _text /* patchable object - graphical, with text */ { t_gobj te_g; /* header for graphical object */ t_binbuf *te_binbuf; /* holder for the text */ t_outlet *te_outlet; /* linked list of outlets */ t_inlet *te_inlet; /* linked list of inlets */ short te_xpix; /* x&y location (within the toplevel) */ short te_ypix; short te_width; /* requested width in chars, 0 if auto */ unsigned int te_type:2; /* from defs below */ } t_text; #define T_TEXT 0 /* just a textual comment */ #define T_OBJECT 1 /* a MAX style patchable object */ #define T_MESSAGE 2 /* a MAX type message */ #define T_ATOM 3 /* a cell to display a number or symbol */ #define te_pd te_g.g_pd /* t_object is synonym for t_text (LATER unify them) */ typedef struct _text t_object; #define ob_outlet te_outlet #define ob_inlet te_inlet #define ob_binbuf te_binbuf #define ob_pd te_g.g_pd #define ob_g te_g typedef void (*t_method)(void); typedef void *(*t_newmethod)(void); /* in ARM 64 a varargs prototype generates a different function call sequence from a fixed one, so in that special case we make a more restrictive definition for t_gotfn. This will break some code in the "chaos" package in Pd extended. (that code will run incorrectly anyhow so why not catch it at compile time anyhow.) */ #if defined(__APPLE__) && defined(__aarch64__) typedef void (*t_gotfn)(void *x); #else typedef void (*t_gotfn)(void *x, ...); #endif /* ---------------- pre-defined objects and symbols --------------*/ EXTERN t_pd pd_objectmaker; /* factory for creating "object" boxes */ EXTERN t_pd pd_canvasmaker; /* factory for creating canvases */ /* --------- prototypes from the central message system ----------- */ EXTERN void pd_typedmess(t_pd *x, t_symbol *s, int argc, t_atom *argv); EXTERN void pd_forwardmess(t_pd *x, int argc, t_atom *argv); EXTERN t_symbol *gensym(const char *s); EXTERN t_gotfn getfn(const t_pd *x, t_symbol *s); EXTERN t_gotfn zgetfn(const t_pd *x, t_symbol *s); EXTERN void nullfn(void); EXTERN void pd_vmess(t_pd *x, t_symbol *s, const char *fmt, ...); /* the following macros are for sending non-type-checkable messages, i.e., using function lookup but circumventing type checking on arguments. Only use for internal messaging protected by A_CANT so that the message can't be generated at patch level. */ #define mess0(x, s) ((*getfn((x), (s)))((x))) typedef void (*t_gotfn1)(void *x, void *arg1); #define mess1(x, s, a) ((*(t_gotfn1)getfn((x), (s)))((x), (a))) typedef void (*t_gotfn2)(void *x, void *arg1, void *arg2); #define mess2(x, s, a,b) ((*(t_gotfn2)getfn((x), (s)))((x), (a),(b))) typedef void (*t_gotfn3)(void *x, void *arg1, void *arg2, void *arg3); #define mess3(x, s, a,b,c) ((*(t_gotfn3)getfn((x), (s)))((x), (a),(b),(c))) typedef void (*t_gotfn4)(void *x, void *arg1, void *arg2, void *arg3, void *arg4); #define mess4(x, s, a,b,c,d) \ ((*(t_gotfn4)getfn((x), (s)))((x), (a),(b),(c),(d))) typedef void (*t_gotfn5)(void *x, void *arg1, void *arg2, void *arg3, void *arg4, void *arg5); #define mess5(x, s, a,b,c,d,e) \ ((*(t_gotfn5)getfn((x), (s)))((x), (a),(b),(c),(d),(e))) EXTERN void obj_list(t_object *x, t_symbol *s, int argc, t_atom *argv); EXTERN t_pd *pd_newest(void); /* --------------- memory management -------------------- */ EXTERN void *getbytes(size_t nbytes); EXTERN void *getzbytes(size_t nbytes); EXTERN void *copybytes(const void *src, size_t nbytes); EXTERN void freebytes(void *x, size_t nbytes); EXTERN void *resizebytes(void *x, size_t oldsize, size_t newsize); /* -------------------- atoms ----------------------------- */ #define SETSEMI(atom) ((atom)->a_type = A_SEMI, (atom)->a_w.w_index = 0) #define SETCOMMA(atom) ((atom)->a_type = A_COMMA, (atom)->a_w.w_index = 0) #define SETPOINTER(atom, gp) ((atom)->a_type = A_POINTER, \ (atom)->a_w.w_gpointer = (gp)) #define SETFLOAT(atom, f) ((atom)->a_type = A_FLOAT, (atom)->a_w.w_float = (f)) #define SETSYMBOL(atom, s) ((atom)->a_type = A_SYMBOL, \ (atom)->a_w.w_symbol = (s)) #define SETDOLLAR(atom, n) ((atom)->a_type = A_DOLLAR, \ (atom)->a_w.w_index = (n)) #define SETDOLLSYM(atom, s) ((atom)->a_type = A_DOLLSYM, \ (atom)->a_w.w_symbol= (s)) EXTERN t_float atom_getfloat(const t_atom *a); EXTERN t_int atom_getint(const t_atom *a); EXTERN t_symbol *atom_getsymbol(const t_atom *a); EXTERN t_symbol *atom_gensym(const t_atom *a); EXTERN t_float atom_getfloatarg(int which, int argc, const t_atom *argv); EXTERN t_int atom_getintarg(int which, int argc, const t_atom *argv); EXTERN t_symbol *atom_getsymbolarg(int which, int argc, const t_atom *argv); EXTERN void atom_string(const t_atom *a, char *buf, unsigned int bufsize); /* ------------------ binbufs --------------- */ EXTERN t_binbuf *binbuf_new(void); EXTERN void binbuf_free(t_binbuf *x); EXTERN t_binbuf *binbuf_duplicate(const t_binbuf *y); EXTERN void binbuf_text(t_binbuf *x, const char *text, size_t size); EXTERN void binbuf_gettext(const t_binbuf *x, char **bufp, int *lengthp); EXTERN void binbuf_clear(t_binbuf *x); EXTERN void binbuf_add(t_binbuf *x, int argc, const t_atom *argv); EXTERN void binbuf_addv(t_binbuf *x, const char *fmt, ...); EXTERN void binbuf_addbinbuf(t_binbuf *x, const t_binbuf *y); EXTERN void binbuf_addsemi(t_binbuf *x); EXTERN void binbuf_restore(t_binbuf *x, int argc, const t_atom *argv); EXTERN void binbuf_print(const t_binbuf *x); EXTERN int binbuf_getnatom(const t_binbuf *x); EXTERN t_atom *binbuf_getvec(const t_binbuf *x); EXTERN int binbuf_resize(t_binbuf *x, int newsize); EXTERN void binbuf_eval(const t_binbuf *x, t_pd *target, int argc, const t_atom *argv); EXTERN int binbuf_read(t_binbuf *b, const char *filename, const char *dirname, int crflag); EXTERN int binbuf_read_via_canvas(t_binbuf *b, const char *filename, const t_canvas *canvas, int crflag); EXTERN int binbuf_read_via_path(t_binbuf *b, const char *filename, const char *dirname, int crflag); EXTERN int binbuf_write(const t_binbuf *x, const char *filename, const char *dir, int crflag); EXTERN void binbuf_evalfile(t_symbol *name, t_symbol *dir); EXTERN t_symbol *binbuf_realizedollsym(t_symbol *s, int ac, const t_atom *av, int tonew); /* ------------------ clocks --------------- */ EXTERN t_clock *clock_new(void *owner, t_method fn); EXTERN void clock_set(t_clock *x, double systime); EXTERN void clock_delay(t_clock *x, double delaytime); EXTERN void clock_unset(t_clock *x); EXTERN void clock_setunit(t_clock *x, double timeunit, int sampflag); EXTERN double clock_getlogicaltime(void); EXTERN double clock_getsystime(void); /* OBSOLETE; use clock_getlogicaltime() */ EXTERN double clock_gettimesince(double prevsystime); EXTERN double clock_gettimesincewithunits(double prevsystime, double units, int sampflag); EXTERN double clock_getsystimeafter(double delaytime); EXTERN void clock_free(t_clock *x); /* ----------------- pure data ---------------- */ EXTERN t_pd *pd_new(t_class *cls); EXTERN void pd_free(t_pd *x); EXTERN void pd_bind(t_pd *x, t_symbol *s); EXTERN void pd_unbind(t_pd *x, t_symbol *s); EXTERN t_pd *pd_findbyclass(t_symbol *s, const t_class *c); EXTERN void pd_pushsym(t_pd *x); EXTERN void pd_popsym(t_pd *x); EXTERN void pd_bang(t_pd *x); EXTERN void pd_pointer(t_pd *x, t_gpointer *gp); EXTERN void pd_float(t_pd *x, t_float f); EXTERN void pd_symbol(t_pd *x, t_symbol *s); EXTERN void pd_list(t_pd *x, t_symbol *s, int argc, t_atom *argv); EXTERN void pd_anything(t_pd *x, t_symbol *s, int argc, t_atom *argv); #define pd_class(x) (*(x)) /* ----------------- pointers ---------------- */ EXTERN void gpointer_init(t_gpointer *gp); EXTERN void gpointer_copy(const t_gpointer *gpfrom, t_gpointer *gpto); EXTERN void gpointer_unset(t_gpointer *gp); EXTERN int gpointer_check(const t_gpointer *gp, int headok); /* ----------------- patchable "objects" -------------- */ EXTERN t_inlet *inlet_new(t_object *owner, t_pd *dest, t_symbol *s1, t_symbol *s2); EXTERN t_inlet *pointerinlet_new(t_object *owner, t_gpointer *gp); EXTERN t_inlet *floatinlet_new(t_object *owner, t_float *fp); EXTERN t_inlet *symbolinlet_new(t_object *owner, t_symbol **sp); EXTERN t_inlet *signalinlet_new(t_object *owner, t_float f); EXTERN void inlet_free(t_inlet *x); EXTERN t_outlet *outlet_new(t_object *owner, t_symbol *s); EXTERN void outlet_bang(t_outlet *x); EXTERN void outlet_pointer(t_outlet *x, t_gpointer *gp); EXTERN void outlet_float(t_outlet *x, t_float f); EXTERN void outlet_symbol(t_outlet *x, t_symbol *s); EXTERN void outlet_list(t_outlet *x, t_symbol *s, int argc, t_atom *argv); EXTERN void outlet_anything(t_outlet *x, t_symbol *s, int argc, t_atom *argv); EXTERN t_symbol *outlet_getsymbol(t_outlet *x); EXTERN void outlet_free(t_outlet *x); EXTERN t_object *pd_checkobject(t_pd *x); /* -------------------- canvases -------------- */ EXTERN void glob_setfilename(void *dummy, t_symbol *name, t_symbol *dir); EXTERN void canvas_setargs(int argc, const t_atom *argv); EXTERN void canvas_getargs(int *argcp, t_atom **argvp); EXTERN t_symbol *canvas_getcurrentdir(void); EXTERN t_glist *canvas_getcurrent(void); EXTERN void canvas_makefilename(const t_glist *c, const char *file, char *result, int resultsize); EXTERN t_symbol *canvas_getdir(const t_glist *x); EXTERN char sys_font[]; /* default typeface set in s_main.c */ EXTERN char sys_fontweight[]; /* default font weight set in s_main.c */ EXTERN int sys_hostfontsize(int fontsize, int zoom); EXTERN int sys_zoomfontwidth(int fontsize, int zoom, int worstcase); EXTERN int sys_zoomfontheight(int fontsize, int zoom, int worstcase); EXTERN int sys_fontwidth(int fontsize); EXTERN int sys_fontheight(int fontsize); EXTERN void canvas_dataproperties(t_glist *x, t_scalar *sc, t_binbuf *b); EXTERN int canvas_open(const t_canvas *x, const char *name, const char *ext, char *dirresult, char **nameresult, unsigned int size, int bin); EXTERN t_float canvas_getsr(t_canvas *x); EXTERN int canvas_getsignallength(t_canvas *x); /* ---------------- widget behaviors ---------------------- */ EXTERN_STRUCT _widgetbehavior; #define t_widgetbehavior struct _widgetbehavior EXTERN_STRUCT _parentwidgetbehavior; #define t_parentwidgetbehavior struct _parentwidgetbehavior EXTERN const t_parentwidgetbehavior *pd_getparentwidget(t_pd *x); /* -------------------- classes -------------- */ #define CLASS_DEFAULT 0 /* flags for new classes below */ #define CLASS_PD 1 /* non-canvasable (bare) pd such as an inlet */ #define CLASS_GOBJ 2 /* pd that can belong to a canvas */ #define CLASS_PATCHABLE 3 /* pd that also can have inlets and outlets */ #define CLASS_TYPEMASK 3 #define CLASS_NOINLET 8 /* suppress left inlet */ #define CLASS_MULTICHANNEL 0x10 /* can deal with multichannel sigs */ #define CLASS_NOPROMOTESIG 0x20 /* don't promote scalars to signals */ #define CLASS_NOPROMOTELEFT 0x40 /* not even the main (left) inlet */ /* Setting a tilde object's CLASS_MULTICHANNEL flag declares that it can deal with multichannel inputs. In this case the channel counts of the inputs might not match; it's up to the dsp method to figure out what to do. Also, the output signal vectors aren't allocated. The output channel counts have to be specified by the object at DSP time. If the object can't put itself on the DSP chain it then has to create outputs anyway and arrange to zero them. By default, if a tilde object's inputs are unconnected, Pd fills them in by adding scalar-to-vector conversions to the DSP chain as needed before calling the dsp method. This behavior can be suppressed for the left (main) inlet by setting CLASS_NOPROMOTELEFT and for one or more non-main inlets by setting CLASS_NOPROMOTESIG. Seeing this, the object can then opt to supply a faster routine; for example, "+" can do a vector-scalar add. In any case, signal outputs are all vectors, and are allocated automatically unless the CLASS_MULTICHANNEL flag is also set. */ EXTERN t_class *class_new(t_symbol *name, t_newmethod newmethod, t_method freemethod, size_t size, int flags, t_atomtype arg1, ...); EXTERN t_class *class_new64(t_symbol *name, t_newmethod newmethod, t_method freemethod, size_t size, int flags, t_atomtype arg1, ...); EXTERN void class_free(t_class *c); #ifdef PDINSTANCE EXTERN t_class *class_getfirst(void); #endif EXTERN void class_addcreator(t_newmethod newmethod, t_symbol *s, t_atomtype type1, ...); EXTERN void class_addmethod(t_class *c, t_method fn, t_symbol *sel, t_atomtype arg1, ...); EXTERN void class_addbang(t_class *c, t_method fn); EXTERN void class_addpointer(t_class *c, t_method fn); EXTERN void class_doaddfloat(t_class *c, t_method fn); EXTERN void class_addsymbol(t_class *c, t_method fn); EXTERN void class_addlist(t_class *c, t_method fn); EXTERN void class_addanything(t_class *c, t_method fn); EXTERN void class_sethelpsymbol(t_class *c, t_symbol *s); EXTERN void class_setwidget(t_class *c, const t_widgetbehavior *w); EXTERN void class_setparentwidget(t_class *c, const t_parentwidgetbehavior *w); EXTERN const char *class_getname(const t_class *c); EXTERN const char *class_gethelpname(const t_class *c); EXTERN const char *class_gethelpdir(const t_class *c); EXTERN void class_setdrawcommand(t_class *c); EXTERN int class_isdrawcommand(const t_class *c); EXTERN void class_set_extern_dir(t_symbol *s); EXTERN void class_domainsignalin(t_class *c, int onset); #define CLASS_MAINSIGNALIN(c, type, field) \ class_domainsignalin(c, (char *)(&((type *)0)->field) - (char *)0) /* prototype for functions to save Pd's to a binbuf */ typedef void (*t_savefn)(t_gobj *x, t_binbuf *b); EXTERN void class_setsavefn(t_class *c, t_savefn f); EXTERN t_savefn class_getsavefn(const t_class *c); EXTERN void obj_saveformat(const t_object *x, t_binbuf *bb); /* add format to bb */ /* prototype for functions to open properties dialogs */ typedef void (*t_propertiesfn)(t_gobj *x, struct _glist *glist); EXTERN void class_setpropertiesfn(t_class *c, t_propertiesfn f); EXTERN t_propertiesfn class_getpropertiesfn(const t_class *c); typedef void (*t_classfreefn)(t_class *); EXTERN void class_setfreefn(t_class *c, t_classfreefn fn); #ifndef PD_CLASS_DEF #define class_addbang(x, y) class_addbang((x), (t_method)(y)) #define class_addpointer(x, y) class_addpointer((x), (t_method)(y)) #define class_addfloat(x, y) class_doaddfloat((x), (t_method)(y)) #define class_addsymbol(x, y) class_addsymbol((x), (t_method)(y)) #define class_addlist(x, y) class_addlist((x), (t_method)(y)) #define class_addanything(x, y) class_addanything((x), (t_method)(y)) #endif #if PD_FLOATSIZE == 64 # define class_new class_new64 #endif /* ------------ printing --------------------------------- */ EXTERN void post(const char *fmt, ...); EXTERN void startpost(const char *fmt, ...); EXTERN void poststring(const char *s); EXTERN void postfloat(t_floatarg f); EXTERN void postatom(int argc, const t_atom *argv); EXTERN void endpost(void); EXTERN void bug(const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(1, 2); EXTERN void pd_error(const void *object, const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(2, 3); /* for logpost(); does *not* work with verbose()! */ typedef enum { PD_CRITICAL = 0, PD_ERROR, PD_NORMAL, PD_DEBUG, PD_VERBOSE } t_loglevel; EXTERN void logpost(const void *object, int level, const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(3, 4); /* deprecated, use logpost() instead. */ EXTERN void verbose(int level, const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(2, 3); /* ------------ system interface routines ------------------- */ EXTERN int sys_isabsolutepath(const char *dir); EXTERN void sys_bashfilename(const char *from, char *to); EXTERN void sys_unbashfilename(const char *from, char *to); EXTERN int open_via_path(const char *dir, const char *name, const char *ext, char *dirresult, char **nameresult, unsigned int size, int bin); EXTERN int sched_geteventno(void); EXTERN double sys_getrealtime(void); EXTERN int (*sys_idlehook)(void); /* hook to add idle time computation */ /* Win32's open()/fopen() do not handle UTF-8 filenames so we need * these internal versions that handle UTF-8 filenames the same across * all platforms. They are recommended for use in external * objectclasses as well so they work with Unicode filenames on Windows */ EXTERN int sys_open(const char *path, int oflag, ...); EXTERN int sys_close(int fd); EXTERN FILE *sys_fopen(const char *filename, const char *mode); EXTERN int sys_fclose(FILE *stream); /* ------------ threading ------------------- */ EXTERN void sys_lock(void); EXTERN void sys_unlock(void); EXTERN int sys_trylock(void); /* --------------- signals ----------------------------------- */ typedef PD_FLOATTYPE t_sample; typedef union _sampleint_union { t_sample f; PD_FLOATUINTTYPE i; } t_sampleint_union; #define MAXLOGSIG 32 #define MAXSIGSIZE (1 << MAXLOGSIG) typedef struct _signal { union { int s_length; /* number of items per channel */ int s_n; /* for source compatibility: pre-0.54 name */ }; t_sample *s_vec; /* the samples, s_nchans vectors of s_length */ t_float s_sr; /* samples per second per channel */ int s_nchans; /* number of channels */ int s_overlap; /* number of times each sample appears */ int s_refcount; /* number of times signal is referenced */ int s_isborrowed; /* whether we're going to borrow our array */ int s_isscalar; /* scalar for an unconnected signal input */ struct _signal *s_borrowedfrom; /* signal to borrow it from */ struct _signal *s_nextfree; /* next in freelist */ struct _signal *s_nextused; /* next in used list */ int s_nalloc; /* allocated size of array in points */ } t_signal; typedef t_int *(*t_perfroutine)(t_int *args); EXTERN t_signal *signal_new(int length, int nchans, t_float sr, t_sample *scalarptr); EXTERN void signal_setmultiout(t_signal **sig, int nchans); EXTERN t_int *plus_perform(t_int *args); EXTERN t_int *plus_perf8(t_int *args); EXTERN t_int *zero_perform(t_int *args); EXTERN t_int *zero_perf8(t_int *args); EXTERN t_int *copy_perform(t_int *args); EXTERN t_int *copy_perf8(t_int *args); EXTERN t_int *scalarcopy_perform(t_int *args); EXTERN t_int *scalarcopy_perf8(t_int *args); EXTERN void dsp_add_plus(t_sample *in1, t_sample *in2, t_sample *out, int n); EXTERN void dsp_add_copy(t_sample *in, t_sample *out, int n); EXTERN void dsp_add_scalarcopy(t_float *in, t_sample *out, int n); EXTERN void dsp_add_zero(t_sample *out, int n); EXTERN int sys_getblksize(void); EXTERN t_float sys_getsr(void); EXTERN int sys_get_inchannels(void); EXTERN int sys_get_outchannels(void); EXTERN void dsp_add(t_perfroutine f, int n, ...); EXTERN void dsp_addv(t_perfroutine f, int n, t_int *vec); EXTERN void pd_fft(t_float *buf, int npoints, int inverse); EXTERN int ilog2(int n); EXTERN void mayer_fht(t_sample *fz, int n); EXTERN void mayer_fft(int n, t_sample *real, t_sample *imag); EXTERN void mayer_ifft(int n, t_sample *real, t_sample *imag); EXTERN void mayer_realfft(int n, t_sample *real); EXTERN void mayer_realifft(int n, t_sample *real); EXTERN float *cos_table; #define LOGCOSTABSIZE 9 #define COSTABSIZE (1<: receiver on the GUI side (e.g. a Tcl/Tk 'proc') * : string of format specifiers * <...>: values according to the format specifiers * * the can be a NULL pointer (in which case it is ignored) * the user of NULL as a is discouraged * depending on the format specifiers, one or more values are passed * 'f' : : a floating point number * 'i' : : an integer number * 's' : : a string * 'r' : : a raw string * 'x' : : a generic pointer * 'o' : : an graphical object * '^' : : a toplevel window (legacy) * 'c' : : a canvas (on a window) * 'F' : : array of t_float's * 'S' : : array of strings * 'R' : : array of raw strings * 'a' : : list of atoms * 'A' : : array of atoms * 'w' : : list of floatwords * 'W' : : array of floatwords * 'm' : : a Pd message * 'p' : : a pascal string (explicit size; not \0-terminated) * 'k' : : a color (or kolor, if you prefer) * ' ' : none : ignored * the use of the specifiers 'x^' is discouraged * raw-strings ('rR') should only be used for constant, well-known strings */ EXTERN void pdgui_vmess(const char* destination, const char* fmt, ...); /* improved dialog window creation * this will insert a first argument to based on * which the GUI can then use to callback. * gfxstub_new() ensures that the given receiver will be available, * even if the has been removed in the meantime. * see pdgui_vmess() for a description of and the varargs */ EXTERN void pdgui_stub_vnew(t_pd *owner, const char* destination, void *key, const char* fmt, ...); EXTERN void pdgui_stub_deleteforkey(void *key); extern t_class *glob_pdobject; /* object to send "pd" messages */ /*------------- Max 0.26 compatibility --------------------*/ /* the following reflects the new way classes are laid out, with the class pointing to the messlist and not vice versa. Externs shouldn't feel it. */ typedef t_class *t_externclass; EXTERN void c_extern(t_externclass *cls, t_newmethod newroutine, t_method freeroutine, t_symbol *name, size_t size, int tiny, \ t_atomtype arg1, ...); EXTERN void c_addmess(t_method fn, t_symbol *sel, t_atomtype arg1, ...); #define t_getbytes getbytes #define t_freebytes freebytes #define t_resizebytes resizebytes #define typedmess pd_typedmess #define vmess pd_vmess /* A definition to help gui objects straddle 0.34-0.35 changes. If this is defined, there is a "te_xpix" field in objects, not a "te_xpos" as before: */ #define PD_USE_TE_XPIX #ifndef _MSC_VER /* Microoft compiler can't handle "inline" function/macros */ #if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) /* a test for NANs and denormals. Should only be necessary on i386. */ #if PD_FLOATSIZE == 32 typedef union { t_float f; unsigned int ui; } t_bigorsmall32; static inline int PD_BADFLOAT(t_float f) /* malformed float */ { t_bigorsmall32 pun; pun.f = f; pun.ui &= 0x7f800000; return((pun.ui == 0) | (pun.ui == 0x7f800000)); } static inline int PD_BIGORSMALL(t_float f) /* exponent outside (-64,64) */ { t_bigorsmall32 pun; pun.f = f; return((pun.ui & 0x20000000) == ((pun.ui >> 1) & 0x20000000)); } #elif PD_FLOATSIZE == 64 typedef union { t_float f; unsigned int ui[2]; } t_bigorsmall64; static inline int PD_BADFLOAT(t_float f) /* malformed double */ { t_bigorsmall64 pun; pun.f = f; pun.ui[1] &= 0x7ff00000; return((pun.ui[1] == 0) | (pun.ui[1] == 0x7ff00000)); } static inline int PD_BIGORSMALL(t_float f) /* exponent outside (-512,512) */ { t_bigorsmall64 pun; pun.f = f; return((pun.ui[1] & 0x20000000) == ((pun.ui[1] >> 1) & 0x20000000)); } #endif /* PD_FLOATSIZE */ #else /* not INTEL or ARM */ #define PD_BADFLOAT(f) 0 #define PD_BIGORSMALL(f) 0 #endif #else /* _MSC_VER */ #if PD_FLOATSIZE == 32 #define PD_BADFLOAT(f) ((((*(unsigned int*)&(f))&0x7f800000)==0) || \ (((*(unsigned int*)&(f))&0x7f800000)==0x7f800000)) /* more stringent test: anything not between 1e-19 and 1e19 in absolute val */ #define PD_BIGORSMALL(f) ((((*(unsigned int*)&(f))&0x60000000)==0) || \ (((*(unsigned int*)&(f))&0x60000000)==0x60000000)) #else /* 64 bits... don't know what to do here */ #define PD_BADFLOAT(f) (!(((f) >= 0) || ((f) <= 0))) #define PD_BIGORSMALL(f) ((f) > 1e150 || (f) < -1e150 \ || (f) > -1e-150 && (f) < 1e-150 ) #endif #endif /* _MSC_VER */ /* get version number at run time */ EXTERN void sys_getversion(int *major, int *minor, int *bugfix); /* get floatsize at run time */ EXTERN unsigned int sys_getfloatsize(void); EXTERN_STRUCT _instancemidi; #define t_instancemidi struct _instancemidi EXTERN_STRUCT _instanceinter; #define t_instanceinter struct _instanceinter EXTERN_STRUCT _instancecanvas; #define t_instancecanvas struct _instancecanvas EXTERN_STRUCT _instanceugen; #define t_instanceugen struct _instanceugen EXTERN_STRUCT _instancestuff; #define t_instancestuff struct _instancestuff #ifndef PDTHREADS #define PDTHREADS 1 #endif struct _pdinstance { double pd_systime; /* global time in Pd ticks */ t_clock *pd_clock_setlist; /* list of set clocks */ t_canvas *pd_canvaslist; /* list of all root canvases */ struct _template *pd_templatelist; /* list of all templates */ int pd_instanceno; /* ordinal number of this instance */ t_symbol **pd_symhash; /* symbol table hash table */ t_instancemidi *pd_midi; /* private stuff for x_midi.c */ t_instanceinter *pd_inter; /* private stuff for s_inter.c */ t_instanceugen *pd_ugen; /* private stuff for d_ugen.c */ t_instancecanvas *pd_gui; /* semi-private stuff in g_canvas.h */ t_instancestuff *pd_stuff; /* semi-private stuff in s_stuff.h */ t_pd *pd_newest; /* most recently created object */ #ifdef PDINSTANCE t_symbol pd_s_pointer; t_symbol pd_s_float; t_symbol pd_s_symbol; t_symbol pd_s_bang; t_symbol pd_s_list; t_symbol pd_s_anything; t_symbol pd_s_signal; t_symbol pd_s__N; t_symbol pd_s__X; t_symbol pd_s_x; t_symbol pd_s_y; t_symbol pd_s_; #endif #if PDTHREADS int pd_islocked; #endif }; #define t_pdinstance struct _pdinstance EXTERN t_pdinstance pd_maininstance; /* m_pd.c */ #ifdef PDINSTANCE EXTERN t_pdinstance *pdinstance_new(void); EXTERN void pd_setinstance(t_pdinstance *x); EXTERN void pdinstance_free(t_pdinstance *x); #endif /* PDINSTANCE */ #if defined(PDTHREADS) && defined(PDINSTANCE) #ifdef _MSC_VER #define PERTHREAD __declspec(thread) #else #define PERTHREAD __thread #endif /* _MSC_VER */ #else #define PERTHREAD #endif #ifdef PDINSTANCE extern PERTHREAD t_pdinstance *pd_this; EXTERN t_pdinstance **pd_instances; EXTERN int pd_ninstances; #else #define pd_this (&pd_maininstance) #endif /* PDINSTANCE */ #ifdef PDINSTANCE #define s_pointer (pd_this->pd_s_pointer) #define s_float (pd_this->pd_s_float) #define s_symbol (pd_this->pd_s_symbol) #define s_bang (pd_this->pd_s_bang) #define s_list (pd_this->pd_s_list) #define s_anything (pd_this->pd_s_anything) #define s_signal (pd_this->pd_s_signal) #define s__N (pd_this->pd_s__N) #define s__X (pd_this->pd_s__X) #define s_x (pd_this->pd_s_x) #define s_y (pd_this->pd_s_y) #define s_ (pd_this->pd_s_) #else EXTERN t_symbol s_pointer, s_float, s_symbol, s_bang, s_list, s_anything, s_signal, s__N, s__X, s_x, s_y, s_; #endif EXTERN t_canvas *pd_getcanvaslist(void); EXTERN int pd_getdspstate(void); /* x_text.c */ EXTERN t_binbuf *text_getbufbyname(t_symbol *s); /* get binbuf from text obj */ EXTERN void text_notifybyname(t_symbol *s); /* notify it was modified */ /* g_undo.c */ /* store two message-sets to be sent to the object's method for 'undo'ing * resp. 'redo'ing the current state of an object. * this creates an internal copy of the atom-lists (so the caller is responsible * for freeing any dynamically allocated data) * this is a no-op if called during 'undo' (resp. 'redo'). */ EXTERN void pd_undo_set_objectstate(t_canvas*canvas, t_pd*x, t_symbol*s, int undo_argc, t_atom*undo_argv, int redo_argc, t_atom*redo_argv); #if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus) } #endif #define __m_pd_h_ #endif /* __m_pd_h_ */ ================================================ FILE: libs/libpd/pure-data/src/m_private_utils.h ================================================ /* Copyright (c) 1997-1999 Miller Puckette and others. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* This file contains some small header-only utilities to be used by Pd's code. */ /* NOTE: this file is an implementation detail of Pd, not to be in externals */ #ifndef M_PRIVATE_UTILS_H #define M_PRIVATE_UTILS_H #ifndef PD_INTERNAL # error m_private_utils.h is a PRIVATE header. do *not* use it in your externals #endif #ifdef HAVE_CONFIG_H /* autotools might put all the HAVE_... defines into "config.h" */ # include "config.h" #endif /* --------------------------- stack allocation helpers --------------------- */ /* alloca helpers * - ALLOCA(type, array, nmemb, maxnmemb) * allocates elements of and points to the newly * allocated memory. if exceeds , allocation is done on * the heap, otherwise on the stack * - FREEA(type, array, nmemb, maxnmemb) * frees the allocated by ALLOCA() (unless allocation was done on * the heap) * if DONT_USE_ALLOCA is defined (and true), always allocates on the heap * * usage example: * { * t_atom*outv; * ALLOCA(t_atom, outv, argc, 100); * // do something with 'outv' * FREEA(t_atom, outv, argc, 100); * } */ # ifdef ALLOCA # undef ALLOCA # endif # ifdef FREEA # undef FREEA # endif #if DONT_USE_ALLOCA /* heap versions */ # define ALLOCA(type, array, nmemb, maxnmemb) ((array) = (type *)getbytes((nmemb) * sizeof(type))) # define FREEA(type, array, nmemb, maxnmemb) (freebytes((array), (nmemb) * sizeof(type))) #else /* !DONT_USE_ALLOCA */ /* stack version (unless exceeds ) */ # ifdef HAVE_ALLOCA_H # include /* linux, mac, mingw, cygwin,... */ # elif defined _WIN32 # include /* MSVC or mingw on windows */ # else # include /* BSDs for example */ # endif # define ALLOCA(type, array, nmemb, maxnmemb) ((array) = (type *)((nmemb) < (maxnmemb) ? \ alloca((nmemb) * sizeof(type)) : getbytes((nmemb) * sizeof(type)))) # define FREEA(type, array, nmemb, maxnmemb) ( \ ((nmemb) < (maxnmemb) || (freebytes((array), (nmemb) * sizeof(type)), 0))) #endif /* !DONT_USE_ALLOCA */ /* --------------------------- endianness helpers --------------------- */ #ifdef HAVE_MACHINE_ENDIAN_H # include #elif defined HAVE_ENDIAN_H # include #endif #ifdef __MINGW32__ # include #endif /* BSD has deprecated BYTE_ORDER in favour of _BYTE_ORDER * others might follow... */ #if !defined(BYTE_ORDER) && defined(_BYTE_ORDER) # define BYTE_ORDER _BYTE_ORDER #endif #if !defined(LITTLE_ENDIAN) && defined(_LITTLE_ENDIAN) # define LITTLE_ENDIAN _LITTLE_ENDIAN #endif #ifdef _MSC_VER /* _MSVC lacks BYTE_ORDER and LITTLE_ENDIAN */ # if !defined(LITTLE_ENDIAN) # define LITTLE_ENDIAN 0x0001 # endif # if !defined(BYTE_ORDER) # define BYTE_ORDER LITTLE_ENDIAN # endif #endif #if !defined(BYTE_ORDER) || !defined(LITTLE_ENDIAN) # if defined(__GNUC__) && defined(_XOPEN_SOURCE) # warning unable to detect endianness (continuing anyhow) # else # error unable to detect endianness # endif #endif /* -------------------------MSVC compat defines --------------------- */ #ifdef _MSC_VER # define snprintf _snprintf #endif #endif /* M_PRIVATE_UTILS_H */ ================================================ FILE: libs/libpd/pure-data/src/m_sched.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* scheduling stuff */ #include "m_pd.h" #include "m_imp.h" #include "s_stuff.h" #ifdef _WIN32 #include #endif /* LATER consider making this variable. It's now the LCM of all sample rates we expect to see: 32000, 44100, 48000, 88200, 96000. */ #define TIMEUNITPERMSEC (32. * 441.) #define TIMEUNITPERSECOND (TIMEUNITPERMSEC * 1000.) #define SYSTIMEPERTICK \ ((STUFF->st_schedblocksize/STUFF->st_dacsr) * TIMEUNITPERSECOND) #define APPROXTICKSPERSEC \ ((int)(STUFF->st_dacsr /(double)STUFF->st_schedblocksize)) #define SYS_QUIT_QUIT 1 #define SYS_QUIT_RESTART 2 static int sys_quit; extern int sys_nosleep; int sys_usecsincelastsleep(void); int sys_sleepgrain; typedef void (*t_clockmethod)(void *client); struct _clock { double c_settime; /* in TIMEUNITS; <0 if unset */ void *c_owner; t_clockmethod c_fn; struct _clock *c_next; t_float c_unit; /* >0 if in TIMEUNITS; <0 if in samples */ }; #ifdef HAVE_UNISTD_H #include #endif t_clock *clock_new(void *owner, t_method fn) { t_clock *x = (t_clock *)getbytes(sizeof *x); x->c_settime = -1; x->c_owner = owner; x->c_fn = (t_clockmethod)fn; x->c_next = 0; x->c_unit = TIMEUNITPERMSEC; return (x); } void clock_unset(t_clock *x) { if (x->c_settime >= 0) { if (x == pd_this->pd_clock_setlist) pd_this->pd_clock_setlist = x->c_next; else { t_clock *x2 = pd_this->pd_clock_setlist; while (x2->c_next != x) x2 = x2->c_next; x2->c_next = x->c_next; } x->c_settime = -1; } } /* set the clock to call back at an absolute system time */ void clock_set(t_clock *x, double setticks) { if (setticks < pd_this->pd_systime) setticks = pd_this->pd_systime; clock_unset(x); x->c_settime = setticks; if (pd_this->pd_clock_setlist && pd_this->pd_clock_setlist->c_settime <= setticks) { t_clock *cbefore, *cafter; for (cbefore = pd_this->pd_clock_setlist, cafter = pd_this->pd_clock_setlist->c_next; cbefore; cbefore = cafter, cafter = cbefore->c_next) { if (!cafter || cafter->c_settime > setticks) { cbefore->c_next = x; x->c_next = cafter; return; } } } else x->c_next = pd_this->pd_clock_setlist, pd_this->pd_clock_setlist = x; } /* set the clock to call back after a delay in msec */ void clock_delay(t_clock *x, double delaytime) { clock_set(x, (x->c_unit > 0 ? pd_this->pd_systime + x->c_unit * delaytime : pd_this->pd_systime - (x->c_unit*(TIMEUNITPERSECOND/STUFF->st_dacsr)) * delaytime)); } /* set the time unit in msec or (if 'samps' is set) in samples. This is flagged by setting c_unit negative. If the clock is currently set, recalculate the delay based on the new unit and reschedule */ void clock_setunit(t_clock *x, double timeunit, int sampflag) { double timeleft; if (timeunit <= 0) timeunit = 1; /* if no change, return to avoid truncation errors recalculating delay */ if ((sampflag && (timeunit == -x->c_unit)) || (!sampflag && (timeunit == x->c_unit * TIMEUNITPERMSEC))) return; /* figure out time left in the units we were in */ timeleft = (x->c_settime < 0 ? -1 : (x->c_settime - pd_this->pd_systime)/((x->c_unit > 0)? x->c_unit : (x->c_unit*(TIMEUNITPERSECOND/STUFF->st_dacsr)))); if (sampflag) x->c_unit = -timeunit; /* negate to flag sample-based */ else x->c_unit = timeunit * TIMEUNITPERMSEC; if (timeleft >= 0) /* reschedule if already set */ clock_delay(x, timeleft); } /* get current logical time. We don't specify what units this is in; use clock_gettimesince() to measure intervals from time of this call. */ double clock_getlogicaltime(void) { return (pd_this->pd_systime); } /* OBSOLETE (misleading) function name kept for compatibility */ double clock_getsystime(void) { return (pd_this->pd_systime); } /* elapsed time in milliseconds since the given system time */ double clock_gettimesince(double prevsystime) { return ((pd_this->pd_systime - prevsystime)/TIMEUNITPERMSEC); } /* elapsed time in units, ala clock_setunit(), since given system time */ double clock_gettimesincewithunits(double prevsystime, double units, int sampflag) { /* If in samples, divide TIMEUNITPERSECOND/sys_dacsr first (at cost of an extra division) since it's probably an integer and if units == 1 and (sys_time - prevsystime) is an integer number of DSP ticks, the result will be exact. */ if (sampflag) return ((pd_this->pd_systime - prevsystime)/ ((TIMEUNITPERSECOND/STUFF->st_dacsr)*units)); else return ((pd_this->pd_systime - prevsystime)/(TIMEUNITPERMSEC*units)); } /* what value the system clock will have after a delay */ double clock_getsystimeafter(double delaytime) { return (pd_this->pd_systime + TIMEUNITPERMSEC * delaytime); } void clock_free(t_clock *x) { clock_unset(x); freebytes(x, sizeof *x); } void glob_audiostatus(void) { /* rewrite me */ } static int sched_diored; static int sched_dioredtime; static int sched_meterson; static int sched_counter; static void sys_addhist(int n) {} /* maybe revive this later for profiling */ static void sys_clearhist(void) {} void sys_log_error(int type) { if (type != ERR_NOTHING && !sched_diored && (sched_counter >= sched_dioredtime)) { pdgui_vmess("pdtk_pd_dio", "i", 1); sched_diored = 1; } sched_dioredtime = sched_counter + APPROXTICKSPERSEC; } static int sched_lastinclip, sched_lastoutclip, sched_lastindb, sched_lastoutdb; void glob_watchdog(t_pd *dummy); static float sched_fastforward; void glob_fastforward(void *dummy, t_floatarg f) { sched_fastforward = TIMEUNITPERMSEC * f; } void dsp_tick(void); static int sched_useaudio = SCHED_AUDIO_NONE; static double sched_referencerealtime, sched_referencelogicaltime; void sched_reopenmeplease(void) /* request from s_audio for deferred reopen */ { sys_quit = SYS_QUIT_RESTART; } void sched_set_using_audio(int flag) { sched_useaudio = flag; if (flag == SCHED_AUDIO_NONE) { sched_referencerealtime = sys_getrealtime(); sched_referencelogicaltime = clock_getlogicaltime(); } if (flag == SCHED_AUDIO_CALLBACK && sched_useaudio != SCHED_AUDIO_CALLBACK) sys_quit = SYS_QUIT_RESTART; if (flag != SCHED_AUDIO_CALLBACK && sched_useaudio == SCHED_AUDIO_CALLBACK) post("sorry, can't turn off callbacks yet; restart Pd"); /* not right yet! */ pdgui_vmess("pdtk_pd_audio", "r", flag ? "on" : "off"); } /* take the scheduler forward one DSP tick, also handling clock timeouts */ void sched_tick(void) { double next_sys_time = pd_this->pd_systime + SYSTIMEPERTICK; int countdown = 5000; while (pd_this->pd_clock_setlist && pd_this->pd_clock_setlist->c_settime < next_sys_time) { t_clock *c = pd_this->pd_clock_setlist; pd_this->pd_systime = c->c_settime; clock_unset(pd_this->pd_clock_setlist); outlet_setstacklim(); (*c->c_fn)(c->c_owner); if (!countdown--) { countdown = 5000; (void)sys_pollgui(); } if (sys_quit) return; } pd_this->pd_systime = next_sys_time; dsp_tick(); sched_counter++; } int sched_get_sleepgrain( void) { return (sys_sleepgrain > 0 ? sys_sleepgrain : (sys_schedadvance/4 > 5000 ? 5000 : (sys_schedadvance/4 < 100 ? 100 : sys_schedadvance/4))); } /* old stuff for extern binary compatibility -- remove someday */ int *get_sys_sleepgrain(void) {return(&sys_sleepgrain);} /* Here is Pd's "main loop." This routine dispatches clock timeouts and DSP "ticks" deterministically, and polls for input from MIDI and the GUI. If we're left idle we also poll for graphics updates; but these are considered lower priority than the rest. The time source is normally the audio I/O subsystem via the "sys_send_dacs()" call. This call returns true if samples were transferred; false means that the audio I/O system is still busy with previous transfers. */ void sys_pollmidiqueue(void); void sys_initmidiqueue(void); /* sys_idlehook is a hook the user can fill in to grab idle time. Return nonzero if you actually used the time; otherwise we're really really idle and will now sleep. */ int (*sys_idlehook)(void); /* when audio is idle, see to GUI and other stuff */ static int sched_idletask( void) { static int sched_nextmeterpolltime, sched_nextpingtime; int rtn = 0; sys_lock(); if (sys_pollgui()) rtn = 1; sys_unlock(); /* if there's no GUI but we're running in "realtime", here is where we arrange to ping the watchdog every 2 seconds. (If there's a GUI, it initiates the ping instead to be sure there's communication back and forth.) */ if (!sys_havegui() && sys_hipriority && sched_counter > sched_nextpingtime) { glob_watchdog(0); /* ping every 2 seconds */ sched_nextpingtime = sched_counter + 2 * APPROXTICKSPERSEC; } /* clear the "DIO error" warning 1 sec after it flashes */ if (sched_counter > sched_nextmeterpolltime) { if (sched_diored && (sched_counter - sched_dioredtime > 0)) { pdgui_vmess("pdtk_pd_dio", "i", 0); sched_diored = 0; } sched_nextmeterpolltime = sched_counter + APPROXTICKSPERSEC; } return (rtn || sys_idlehook && sys_idlehook()); } static void m_pollingscheduler(void) { sys_lock(); sys_initmidiqueue(); while (!sys_quit) /* outer loop runs once per tick */ { sys_addhist(0); sched_tick(); sys_addhist(1); /* fast forward, in which the scheduler advances without waiting for real time; for patches that alternate between interactive and batch-like computations. */ if (sched_fastforward > 0) { sched_fastforward -= SYSTIMEPERTICK; sched_referencerealtime = sys_getrealtime(); sched_referencelogicaltime = pd_this->pd_systime; continue; } sys_pollgui(); sys_pollmidiqueue(); sys_addhist(2); while (!sys_quit) /* inner loop runs until it can transfer audio */ { int timeforward; /* SENDDACS_YES if audio was transferred, SENDDACS_NO if not, or SENDDACS_SLEPT if yes but time elapsed during xfer */ sys_unlock(); if (sched_useaudio == SCHED_AUDIO_NONE) { /* no audio; use system clock */ double lateness = 1000. * (sys_getrealtime() - sched_referencerealtime) - clock_gettimesince(sched_referencelogicaltime); if (lateness > 20000) /* if 20" late, don't try to catch up */ { sched_referencerealtime = sys_getrealtime(); sched_referencelogicaltime = pd_this->pd_systime; } timeforward = (lateness > 0 ? SENDDACS_YES : SENDDACS_NO); } else timeforward = sys_send_dacs(); sys_addhist(3); /* test for idle; if so, do graphics updates. */ if (timeforward != SENDDACS_YES && !sched_idletask() && !sys_nosleep) { /* if even that had nothing to do, sleep. */ sys_addhist(4); sys_microsleep(); } sys_addhist(5); sys_lock(); if (timeforward != SENDDACS_NO) break; } } sys_unlock(); } void sched_audio_callbackfn(void) { sys_lock(); sys_addhist(0); sched_tick(); sys_addhist(1); sys_pollmidiqueue(); sys_addhist(2); sys_unlock(); (void)sched_idletask(); sys_addhist(3); } static void m_callbackscheduler(void) { sys_initmidiqueue(); while (!sys_quit) { double timewas = pd_this->pd_systime; #ifdef _WIN32 Sleep(1000); #else sleep(1); #endif if (pd_this->pd_systime == timewas) { sys_lock(); (void)sys_pollgui(); sched_tick(); sys_unlock(); } if (sys_idlehook) sys_idlehook(); } } int m_mainloop(void) { while (sys_quit != SYS_QUIT_QUIT) { if (sched_useaudio == SCHED_AUDIO_CALLBACK) m_callbackscheduler(); else m_pollingscheduler(); if (sys_quit == SYS_QUIT_RESTART) { sys_quit = 0; if (audio_isopen()) { sys_close_audio(); sys_reopen_audio(); } } } return (0); } int m_batchmain(void) { while (sys_quit != SYS_QUIT_QUIT) sched_tick(); return (0); } void sys_exit(void) { sys_quit = SYS_QUIT_QUIT; } ================================================ FILE: libs/libpd/pure-data/src/s_audio.c ================================================ /* Copyright (c) 2003, Miller Puckette and others. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* machine-independent (well, mostly!) audio layer. Stores and recalls audio settings from argparse routine and from dialog window. LATER: save audio settings for various APIs for easier switching */ #include "m_pd.h" #include "s_stuff.h" #include #ifdef _WIN32 #include #else #include #include #include #endif /* _WIN32 */ #include #include #include "m_private_utils.h" #define SYS_DEFAULTCH 2 #define MAXNDEV 128 #define DEVDESCSIZE 128 #define MAXBLOCKSIZE 2048 /* exported variables */ int sys_schedadvance; /* scheduler advance in microseconds */ static int sys_audioapiopened; /* what API is open, API_NONE if none */ static int audio_callback_is_open; /* true if we're open in callback mode */ /* current parameters (if an API is open) or requested ones otherwise: */ static t_audiosettings audio_nextsettings; void sched_audio_callbackfn(void); void sched_reopenmeplease(void); int audio_isopen(void) { return (sys_audioapiopened > 0); } static int audio_isfixedsr(int api) { #ifdef USEAPI_JACK /* JACK server sets it's own samplerate */ return (api == API_JACK); #endif return 0; } static int audio_isfixedblocksize(int api) { #ifdef USEAPI_JACK /* JACK server sets it's own blocksize */ return (api == API_JACK); #endif return 0; } #ifdef USEAPI_JACK int jack_get_blocksize(void); #endif static int audio_getfixedblocksize(int api) { #ifdef USEAPI_JACK /* JACK server sets it's own blocksize */ return (api == API_JACK ? jack_get_blocksize() : 0); #endif return 0; } /* inform rest of Pd of current channels and sample rate. Do this when opening audio device. This is also called from alsamm but I think that is no longer in use, so in principle this could be static. */ void sys_setchsr(int chin, int chout, int sr) { int inbytes = (chin ? chin : 2) * (DEFDACBLKSIZE*sizeof(t_sample)); int outbytes = (chout ? chout : 2) * (DEFDACBLKSIZE*sizeof(t_sample)); if (STUFF->st_soundin) freebytes(STUFF->st_soundin, (STUFF->st_inchannels? STUFF->st_inchannels : 2) * (DEFDACBLKSIZE*sizeof(t_sample))); if (STUFF->st_soundout) freebytes(STUFF->st_soundout, (STUFF->st_outchannels? STUFF->st_outchannels : 2) * (DEFDACBLKSIZE*sizeof(t_sample))); STUFF->st_inchannels = chin; STUFF->st_outchannels = chout; if (!audio_isfixedsr(sys_audioapiopened)) STUFF->st_dacsr = sr; STUFF->st_soundin = (t_sample *)getbytes(inbytes); memset(STUFF->st_soundin, 0, inbytes); STUFF->st_soundout = (t_sample *)getbytes(outbytes); memset(STUFF->st_soundout, 0, outbytes); logpost(NULL, PD_VERBOSE, "input channels = %d, output channels = %d", STUFF->st_inchannels, STUFF->st_outchannels); canvas_resume_dsp(canvas_suspend_dsp()); } static void audio_make_sane(int *ndev, int *devvec, int *nchan, int *chanvec, int maxdev) { int i; if (*ndev == -1) { /* no input audio devices specified */ if (*nchan == -1) { if (*ndev >= 1) { *nchan=1; chanvec[0] = SYS_DEFAULTCH; *ndev = 1; devvec[0] = DEFAULTAUDIODEV; } else *ndev = *nchan = 0; } else { for (i = 0; i < maxdev; i++) devvec[i] = i; *ndev = *nchan; } } else { if (*nchan == -1) { *nchan = *ndev; for (i = 0; i < *ndev; i++) chanvec[i] = SYS_DEFAULTCH; } else if (*nchan > *ndev) { for (i = *ndev; i < *nchan; i++) { if (i == 0) devvec[0] = DEFAULTAUDIODEV; else devvec[i] = devvec[i-1] + 1; } *ndev = *nchan; } else if (*nchan < *ndev) { for (i = *nchan; i < *ndev; i++) { if (i == 0) chanvec[0] = SYS_DEFAULTCH; else chanvec[i] = chanvec[i-1]; } *ndev = *nchan; } } for (i = *ndev; i < maxdev; i++) devvec[i] = -1; for (i = *nchan; i < maxdev; i++) chanvec[i] = 0; } /* compact the list of audio devices by skipping those whose channel counts are zero, and add up all channel counts. Assumes you've already called make_sane above */ static void audio_compact_and_count_channels(int *ndev, int *devvec, int *chanvec, int *totalchans, int maxdev) { int i, newndev; /* count total number of input and output channels */ for (i = newndev = *totalchans = 0; i < *ndev; i++) if (chanvec[i] > 0) { chanvec[newndev] = chanvec[i]; devvec[newndev] = devvec[i]; *totalchans += chanvec[i]; newndev++; } *ndev = newndev; } /* ----------------------- public routines ----------------------- */ static int initted = 0; void sys_get_audio_settings(t_audiosettings *a) { if (!initted) { audio_nextsettings.a_api = API_DEFAULT; audio_nextsettings.a_srate = DEFAULTSRATE; audio_nextsettings.a_nindev = audio_nextsettings.a_nchindev = audio_nextsettings.a_noutdev = audio_nextsettings.a_nchoutdev = 1; audio_nextsettings.a_indevvec[0] = audio_nextsettings.a_outdevvec[0] = DEFAULTAUDIODEV; audio_nextsettings.a_chindevvec[0] = audio_nextsettings.a_choutdevvec[0] = SYS_DEFAULTCH; audio_nextsettings.a_advance = DEFAULTADVANCE; audio_nextsettings.a_blocksize = DEFDACBLKSIZE; initted = 1; } *a = audio_nextsettings; if (audio_isfixedsr(a->a_api)) a->a_srate = STUFF->st_dacsr; if (audio_isfixedblocksize(a->a_api)) a->a_blocksize = audio_getfixedblocksize(a->a_api); } /* Since the channel vector might be longer than the audio device vector, or vice versa, we fill the shorter one in to match the longer one. Also, if both are empty, we fill in one device (the default) and two channels. This function can leave number of channels at zero which is appropriate for the dialog window but before starting audio we also call audio_compact_and_count_channels below.*/ /* set audio device settings (after cleaning up the specified device and channel vectors). The audio devices are "zero based" (i.e. "0" means the first one.) We can later re-open audio and/or show the settings on a dialog window. */ void sys_set_audio_settings(t_audiosettings *a) { int i; char indevlist[MAXNDEV*DEVDESCSIZE], outdevlist[MAXNDEV*DEVDESCSIZE]; int indevs = 0, outdevs = 0, canmulti = 0, cancallback = 0; sys_get_audio_devs(indevlist, &indevs, outdevlist, &outdevs, &canmulti, &cancallback, MAXNDEV, DEVDESCSIZE, a->a_api); if (a->a_srate < 1) a->a_srate = DEFAULTSRATE; if (a->a_advance < 0) a->a_advance = DEFAULTADVANCE; a->a_blocksize = 1 << ilog2(a->a_blocksize); if (a->a_blocksize < DEFDACBLKSIZE || a->a_blocksize > MAXBLOCKSIZE) a->a_blocksize = DEFDACBLKSIZE; audio_make_sane(&a->a_noutdev, a->a_outdevvec, &a->a_nchoutdev, a->a_choutdevvec, MAXAUDIOOUTDEV); audio_make_sane(&a->a_nindev, a->a_indevvec, &a->a_nchindev, a->a_chindevvec, MAXAUDIOINDEV); sys_schedadvance = a->a_advance * 1000; audio_nextsettings = *a; initted = 1; sys_log_error(ERR_NOTHING); pdgui_vmess("set", "ri", "pd_whichapi", audio_nextsettings.a_api); } void sys_close_audio(void) { if (sys_externalschedlib) { return; } if (!audio_isopen()) return; #ifdef USEAPI_PORTAUDIO if (sys_audioapiopened == API_PORTAUDIO) pa_close_audio(); else #endif #ifdef USEAPI_JACK if (sys_audioapiopened == API_JACK) jack_close_audio(); else #endif #ifdef USEAPI_OSS if (sys_audioapiopened == API_OSS) oss_close_audio(); else #endif #ifdef USEAPI_ALSA if (sys_audioapiopened == API_ALSA) alsa_close_audio(); else #endif #ifdef USEAPI_MMIO if (sys_audioapiopened == API_MMIO) mmio_close_audio(); else #endif #ifdef USEAPI_AUDIOUNIT if (sys_audioapiopened == API_AUDIOUNIT) audiounit_close_audio(); else #endif #ifdef USEAPI_ESD if (sys_audioapiopened == API_ESD) esd_close_audio(); else #endif #ifdef USEAPI_DUMMY if (sys_audioapiopened == API_DUMMY) dummy_close_audio(); else #endif post("sys_close_audio: unknown API %d", sys_audioapiopened); sys_audioapiopened = API_NONE; sched_set_using_audio(SCHED_AUDIO_NONE); audio_callback_is_open = 0; pdgui_vmess("set", "ri", "pd_whichapi", 0); } void sys_init_audio(void) { t_audiosettings as; int totalinchans, totaloutchans; sys_get_audio_settings(&as); audio_compact_and_count_channels(&as.a_nindev, as.a_indevvec, as.a_chindevvec, &totalinchans, MAXAUDIOINDEV); audio_compact_and_count_channels(&as.a_noutdev, as.a_outdevvec, as.a_choutdevvec, &totaloutchans, MAXAUDIOOUTDEV); sys_setchsr(totalinchans, totaloutchans, as.a_srate); } /* open audio using currently requested parameters */ void sys_reopen_audio(void) { t_audiosettings as; int outcome = 0, totalinchans, totaloutchans; sys_get_audio_settings(&as); /* fprintf(stderr, "audio in ndev %d, dev %d; out ndev %d, dev %d\n", as.a_nindev, as.a_indevvec[0], as.a_noutdev, as.a_outdevvec[0]); */ audio_compact_and_count_channels(&as.a_nindev, as.a_indevvec, as.a_chindevvec, &totalinchans, MAXAUDIOINDEV); audio_compact_and_count_channels(&as.a_noutdev, as.a_outdevvec, as.a_choutdevvec, &totaloutchans, MAXAUDIOOUTDEV); sys_setchsr(totalinchans, totaloutchans, as.a_srate); if (!as.a_nindev && !as.a_noutdev) { sched_set_using_audio(SCHED_AUDIO_NONE); return; } #ifdef USEAPI_PORTAUDIO if (as.a_api == API_PORTAUDIO) { int blksize = (as.a_blocksize ? as.a_blocksize : 64); int nbufs = (double)sys_schedadvance / 1000000. * as.a_srate / blksize; /* make sure that the delay is not smaller than the hardware blocksize */ if (nbufs < 1) { int delay = ((double)sys_schedadvance / 1000.) + 0.5; int limit = ceil(blksize * 1000. / (double)as.a_srate); nbufs = 1; post("warning: 'delay' setting (%d ms) too small for current blocksize " "(%d samples); falling back to minimum value (%d ms)", delay, blksize, limit); } outcome = pa_open_audio((as.a_nindev > 0 ? as.a_chindevvec[0] : 0), (as.a_noutdev > 0 ? as.a_choutdevvec[0] : 0), as.a_srate, STUFF->st_soundin, STUFF->st_soundout, blksize, nbufs, (as.a_nindev > 0 ? as.a_indevvec[0] : 0), (as.a_noutdev > 0 ? as.a_outdevvec[0] : 0), (as.a_callback ? sched_audio_callbackfn : 0)); } else #endif #ifdef USEAPI_JACK if (as.a_api == API_JACK) outcome = jack_open_audio((as.a_nindev > 0 ? as.a_chindevvec[0] : 0), (as.a_noutdev > 0 ? as.a_choutdevvec[0] : 0), (as.a_callback ? sched_audio_callbackfn : 0)); else #endif #ifdef USEAPI_OSS if (as.a_api == API_OSS) outcome = oss_open_audio(as.a_nindev, as.a_indevvec, as.a_nindev, as.a_chindevvec, as.a_noutdev, as.a_outdevvec, as.a_noutdev, as.a_choutdevvec, as.a_srate, as.a_blocksize); else #endif #ifdef USEAPI_ALSA if (as.a_api == API_ALSA) outcome = alsa_open_audio(as.a_nindev, as.a_indevvec, as.a_nindev, as.a_chindevvec, as.a_noutdev, as.a_outdevvec, as.a_noutdev, as.a_choutdevvec, as.a_srate, as.a_blocksize); else #endif #ifdef USEAPI_MMIO if (as.a_api == API_MMIO) outcome = mmio_open_audio(as.a_nindev, as.a_indevvec, as.a_nindev, as.a_chindevvec, as.a_noutdev, as.a_outdevvec, as.a_noutdev, as.a_choutdevvec, as.a_srate, as.a_blocksize); else #endif #ifdef USEAPI_AUDIOUNIT if (as.a_api == API_AUDIOUNIT) outcome = audiounit_open_audio( (as.a_nindev > 0 ? as.a_chindev[0] : 0), (as.a_nindev > 0 ? as.a_choutdev[0] : 0), as.a_srate); else #endif #ifdef USEAPI_ESD if (as.a_api == API_ALSA) outcome = esd_open_audio(as.a_nindev, as.a_indevvec, as.a_nindev, as.a_chindevvec, as.a_noutdev, as.a_outdevvec, as.a_noutdev, as.a_choutdevvec, as.a_srate); else #endif #ifdef USEAPI_DUMMY if (as.a_api == API_DUMMY) outcome = dummy_open_audio(as.a_nindev, as.a_noutdev, as.a_srate); else #endif if (as.a_api == API_NONE) ; else post("unknown audio API specified"); if (outcome) /* failed */ { sys_audioapiopened = API_NONE; sched_set_using_audio(SCHED_AUDIO_NONE); audio_callback_is_open = 0; } else { sys_audioapiopened = as.a_api; sched_set_using_audio( (as.a_callback ? SCHED_AUDIO_CALLBACK : SCHED_AUDIO_POLL)); audio_callback_is_open = as.a_callback; } pdgui_vmess("set", "ri", "pd_whichapi", sys_audioapiopened); } int sys_send_dacs(void) { #ifdef USEAPI_PORTAUDIO if (sys_audioapiopened == API_PORTAUDIO) return (pa_send_dacs()); else #endif #ifdef USEAPI_JACK if (sys_audioapiopened == API_JACK) return (jack_send_dacs()); else #endif #ifdef USEAPI_OSS if (sys_audioapiopened == API_OSS) return (oss_send_dacs()); else #endif #ifdef USEAPI_ALSA if (sys_audioapiopened == API_ALSA) return (alsa_send_dacs()); else #endif #ifdef USEAPI_MMIO if (sys_audioapiopened == API_MMIO) return (mmio_send_dacs()); else #endif #ifdef USEAPI_AUDIOUNIT if (sys_audioapiopened == API_AUDIOUNIT) return (audiounit_send_dacs()); else #endif #ifdef USEAPI_ESD if (sys_audioapiopened == API_ESD) return (esd_send_dacs()); else #endif #ifdef USEAPI_DUMMY if (sys_audioapiopened == API_DUMMY) return (dummy_send_dacs()); else #endif post("unknown API"); return (0); } t_float sys_getsr(void) { return (STUFF->st_dacsr); } int sys_get_outchannels(void) { return (STUFF->st_outchannels); } int sys_get_inchannels(void) { return (STUFF->st_inchannels); } /* this could later be set by a preference but for now it seems OK to just keep jack audio open but close unused audio devices for any other API */ int audio_shouldkeepopen(void) { return (sys_audioapiopened == API_JACK); } /* get names of available audio devices for the specified API */ void sys_get_audio_devs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int *canmulti, int *cancallback, int maxndev, int devdescsize, int api) { *cancallback = 0; /* may be overridden by specific API implementation */ #ifdef USEAPI_PORTAUDIO if (api == API_PORTAUDIO) { pa_getdevs(indevlist, nindevs, outdevlist, noutdevs, canmulti, maxndev, devdescsize); /* portaudio officially allows callbacks but it hangs Pd on MacOS and perhaps on other platforms too - this would potentially reduce latency but doesn't appear to be worth the danger. */ *cancallback = 0; } else #endif #ifdef USEAPI_JACK if (api == API_JACK) { jack_getdevs(indevlist, nindevs, outdevlist, noutdevs, canmulti, maxndev, devdescsize); *cancallback = 1; } else #endif #ifdef USEAPI_OSS if (api == API_OSS) { oss_getdevs(indevlist, nindevs, outdevlist, noutdevs, canmulti, maxndev, devdescsize); } else #endif #ifdef USEAPI_ALSA if (api == API_ALSA) { alsa_getdevs(indevlist, nindevs, outdevlist, noutdevs, canmulti, maxndev, devdescsize); } else #endif #ifdef USEAPI_MMIO if (api == API_MMIO) { mmio_getdevs(indevlist, nindevs, outdevlist, noutdevs, canmulti, maxndev, devdescsize); } else #endif #ifdef USEAPI_AUDIOUNIT if (api == API_AUDIOUNIT) { } else #endif #ifdef USEAPI_ESD if (api == API_ESD) { esd_getdevs(indevlist, nindevs, outdevlist, noutdevs, canmulti, maxndev, devdescsize); } else #endif #ifdef USEAPI_DUMMY if (api == API_DUMMY) { dummy_getdevs(indevlist, nindevs, outdevlist, noutdevs, canmulti, maxndev, devdescsize); } else #endif { /* this shouldn't happen once all the above get filled in. */ int i; *nindevs = *noutdevs = 3; for (i = 0; i < 3; i++) { sprintf(indevlist + i * devdescsize, "input device #%d", i+1); sprintf(outdevlist + i * devdescsize, "output device #%d", i+1); } *canmulti = 0; } } void sys_gui_audiopreferences(void) { t_audiosettings as; /* these are all the devices on your system: */ char indevlist[MAXNDEV*DEVDESCSIZE], outdevlist[MAXNDEV*DEVDESCSIZE]; char srate[80], callback[80], blocksize[80]; const char *devicesI[MAXNDEV], *devicesO[MAXNDEV]; t_float usedevsI[MAXAUDIOINDEV], devchansI[MAXAUDIOINDEV]; t_float usedevsO[MAXAUDIOOUTDEV], devchansO[MAXAUDIOOUTDEV]; int num_usedevsI, num_devchansI, num_usedevsO, num_devchansO; int num_devicesI = 0, num_devicesO = 0, canmulti = 0, cancallback = 0; int i; /* query the current AUDIO settings */ sys_get_audio_settings(&as); sys_get_audio_devs(indevlist, &num_devicesI, outdevlist, &num_devicesO, &canmulti, &cancallback, MAXNDEV, DEVDESCSIZE, as.a_api); /* normalize the data a bit */ if(!num_devicesI) { num_devicesI = 1; devicesI[0] = ""; } else { for(i=0; i= 0) ? atom_getfloatarg(i+4, argc, argv) : 0; as.a_outdevvec[i] = atom_getfloatarg(i+8, argc, argv); as.a_choutdevvec[i] = (as.a_outdevvec[i] >= 0) ? atom_getfloatarg(i+12, argc, argv) : 0; } /* compact out any zeros and count nonzero entries */ for (i = 0, as.a_nindev = 0; i < MAXAUDIOINDEV; i++) { if (as.a_chindevvec[i]) { as.a_indevvec[as.a_nindev] = as.a_indevvec[i]; as.a_chindevvec[as.a_nindev] = as.a_chindevvec[i]; as.a_nindev++; } } for (i = 0, as.a_noutdev = 0; i < MAXAUDIOOUTDEV; i++) { if (as.a_choutdevvec[i]) { as.a_outdevvec[as.a_noutdev] = as.a_outdevvec[i]; as.a_choutdevvec[as.a_noutdev] = as.a_choutdevvec[i]; as.a_noutdev++; } } as.a_nchindev = as.a_nindev; as.a_nchoutdev = as.a_noutdev; if (as.a_callback < 0) as.a_callback = 0; as.a_blocksize = (1< MAXBLOCKSIZE) as.a_blocksize = DEFDACBLKSIZE; if (!audio_callback_is_open && !as.a_callback) sys_close_audio(); sys_set_audio_settings(&as); if (!audio_callback_is_open && !as.a_callback) sys_reopen_audio(); else sched_reopenmeplease(); } void sys_listdevs(void) { char indevlist[MAXNDEV*DEVDESCSIZE], outdevlist[MAXNDEV*DEVDESCSIZE]; int nindevs = 0, noutdevs = 0, i, canmulti = 0, cancallback = 0; int offset = 0; sys_get_audio_devs(indevlist, &nindevs, outdevlist, &noutdevs, &canmulti, &cancallback, MAXNDEV, DEVDESCSIZE, audio_nextsettings.a_api); #if 0 /* To agree with command line flags, normally start at 1 */ /* But microsoft "MMIO" device list starts at 0 (the "mapper"). */ /* JMZ: otoh, it seems that the '-audiodev' flags 0-based * indices on ALSA and PORTAUDIO as well, * so we better show the correct ones here * (hence this line is disabled via #ifdef's) */ offset = (audio_nextsettings.a_api != API_MMIO); #endif if (!nindevs) post("no audio input devices found"); else { post("audio input devices:"); for (i = 0; i < nindevs; i++) post("%d. %s", i + offset, indevlist + i * DEVDESCSIZE); } if (!noutdevs) post("no audio output devices found"); else { post("audio output devices:"); for (i = 0; i < noutdevs; i++) post("%d. %s", i + offset, outdevlist + i * DEVDESCSIZE); } post("API number %d\n", audio_nextsettings.a_api); sys_listmididevs(); } void glob_audio_setapi(void *dummy, t_floatarg f) { int newapi = f; if (newapi) { if (newapi == audio_nextsettings.a_api) { if (!audio_isopen() && audio_shouldkeepopen()) sys_reopen_audio(); } else { sys_close_audio(); audio_nextsettings.a_api = newapi; /* bash device params back to default */ audio_nextsettings.a_nindev = audio_nextsettings.a_nchindev = audio_nextsettings.a_noutdev = audio_nextsettings.a_nchoutdev = 1; audio_nextsettings.a_indevvec[0] = audio_nextsettings.a_outdevvec[0] = DEFAULTAUDIODEV; audio_nextsettings.a_chindevvec[0] = audio_nextsettings.a_choutdevvec[0] = SYS_DEFAULTCH; audio_nextsettings.a_blocksize = DEFDACBLKSIZE; sys_reopen_audio(); } glob_audio_properties(0, 0); } else if (audio_isopen()) { sys_close_audio(); } } /* start or stop the audio hardware */ void sys_set_audio_state(int onoff) { if (onoff) /* start */ { if (!audio_isopen()) sys_reopen_audio(); } else { if (audio_isopen()) sys_close_audio(); } } #define MAXAPIENTRY 10 typedef struct _apientry { char a_name[30]; int a_id; } t_apientry; static t_apientry audio_apilist[] = { #ifdef USEAPI_OSS {"OSS", API_OSS}, #endif #ifdef USEAPI_ALSA {"ALSA", API_ALSA}, #endif #ifdef USEAPI_PORTAUDIO #ifdef _WIN32 {"\"standard (portaudio)\"", API_PORTAUDIO}, #else #ifdef __APPLE__ {"\"standard (portaudio)\"", API_PORTAUDIO}, #else {"portaudio", API_PORTAUDIO}, #endif #endif #endif /* USEAPI_PORTAUDIO */ #ifdef USEAPI_MMIO {"\"old MMIO system\"", API_MMIO}, #endif #ifdef USEAPI_JACK {"jack", API_JACK}, #endif #ifdef USEAPI_AUDIOUNIT {"Audiounit", API_AUDIOUNIT}, #endif #ifdef USEAPI_ESD {"ESD", API_ESD}, #endif #ifdef USEAPI_DUMMY {"dummy", API_DUMMY}, #endif }; void sys_get_audio_apis(char *buf) { /* FIXXME: this returns a raw Tcl-list! * instead it should return something we can use with pdgui_vmess() */ unsigned int n; if (sizeof(audio_apilist)/sizeof(t_apientry) < 2) strcpy(buf, "{}"); else { strcpy(buf, "{ "); for (n = 0; n < sizeof(audio_apilist)/sizeof(t_apientry); n++) sprintf(buf + strlen(buf), "{%s %d} ", audio_apilist[n].a_name, audio_apilist[n].a_id); strcat(buf, "}"); } } /* convert a device name to a (1-based) device number. (Output device if 'output' parameter is true, otherwise input device). Negative on failure. */ int sys_audiodevnametonumber(int output, const char *name) { char indevlist[MAXNDEV*DEVDESCSIZE], outdevlist[MAXNDEV*DEVDESCSIZE]; int nindevs = 0, noutdevs = 0, i, canmulti, cancallback; sys_get_audio_devs(indevlist, &nindevs, outdevlist, &noutdevs, &canmulti, &cancallback, MAXNDEV, DEVDESCSIZE, audio_nextsettings.a_api); if (output) { /* try first for exact match */ for (i = 0; i < noutdevs; i++) if (!strcmp(name, outdevlist + i * DEVDESCSIZE)) return (i); /* failing that, a match up to end of shorter string */ for (i = 0; i < noutdevs; i++) { unsigned long comp = strlen(name); if (comp > strlen(outdevlist + i * DEVDESCSIZE)) comp = strlen(outdevlist + i * DEVDESCSIZE); if (!strncmp(name, outdevlist + i * DEVDESCSIZE, comp)) return (i); } } else { for (i = 0; i < nindevs; i++) if (!strcmp(name, indevlist + i * DEVDESCSIZE)) return (i); for (i = 0; i < nindevs; i++) { unsigned long comp = strlen(name); if (comp > strlen(indevlist + i * DEVDESCSIZE)) comp = strlen(indevlist + i * DEVDESCSIZE); if (!strncmp(name, indevlist + i * DEVDESCSIZE, comp)) return (i); } } return (-1); } /* convert a (1-based) device number to a device name. (Output device if 'output' parameter is true, otherwise input device). Empty string on failure. */ void sys_audiodevnumbertoname(int output, int devno, char *name, int namesize) { char indevlist[MAXNDEV*DEVDESCSIZE], outdevlist[MAXNDEV*DEVDESCSIZE]; int nindevs = 0, noutdevs = 0, canmulti, cancallback; if (devno < 0) { *name = 0; return; } sys_get_audio_devs(indevlist, &nindevs, outdevlist, &noutdevs, &canmulti, &cancallback, MAXNDEV, DEVDESCSIZE, audio_nextsettings.a_api); if (output && (devno < noutdevs)) strncpy(name, outdevlist + devno * DEVDESCSIZE, namesize); else if (!output && (devno < nindevs)) strncpy(name, indevlist + devno * DEVDESCSIZE, namesize); else *name = 0; name[namesize-1] = 0; } ================================================ FILE: libs/libpd/pure-data/src/s_audio_dummy.c ================================================ /* * Copyright (c) 2010 Peter Brinkmann (peter.brinkmann@gmail.com) * * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ #ifdef USEAPI_DUMMY #include int dummy_open_audio(int nin, int nout, int sr) { return 0; } int dummy_close_audio(void) { return 0; } int dummy_send_dacs(void) { return 0; } void dummy_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int *canmulti, int maxndev, int devdescsize) { sprintf(indevlist, "NONE"); sprintf(outdevlist, "NONE"); *nindevs = *noutdevs = 1; *canmulti = 0; } void dummy_listdevs(void) { // do nothing } #endif ================================================ FILE: libs/libpd/pure-data/src/s_inter.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* Pd side of the Pd/Pd-gui interface. Also, some system interface routines that didn't really belong anywhere. */ #include "m_pd.h" #include "s_stuff.h" #include "m_imp.h" #include "g_canvas.h" /* for GUI queueing stuff */ #include "s_net.h" #include #ifndef _WIN32 #include #include #include #include #include #include #include #endif #ifdef HAVE_BSTRING_H #include #endif #ifdef _WIN32 #include #include #include #endif #include #include #include #include #include #include #ifdef __APPLE__ #include #include #include #else #include #endif #ifdef HAVE_SYS_UTSNAME_H # include # ifndef USE_UNAME # define USE_UNAME 1 # endif #endif #include "m_private_utils.h" /* colorize output, but only on a TTY */ #ifdef HAVE_UNISTD_H # include #else /* if isatty exists outside unistd, please add another #ifdef */ # define isatty(fd) 0 #endif static int stderr_isatty; #define stringify(s) str(s) #define str(s) #s #define INTER (pd_this->pd_inter) #define DEBUG_MESSUP 1<<0 /* messages up from pd to pd-gui */ #define DEBUG_MESSDOWN 1<<1 /* messages down from pd-gui to pd */ #define DEBUG_COLORIZE 1<<2 /* colorize messages (if we are on a TTY) */ #ifndef PDBINDIR #define PDBINDIR "bin/" #endif #ifndef PDGUIDIR #define PDGUIDIR "tcl" #endif #ifndef WISH # if defined _WIN32 # define WISH "wish85.exe" # elif defined __APPLE__ // leave undefined to use dummy search path, otherwise // this should be a full path to wish on mac #else # define WISH "wish" # endif #endif #define LOCALHOST "localhost" #if PDTHREADS #include "pthread.h" #endif typedef struct _fdpoll { int fdp_fd; t_fdpollfn fdp_fn; void *fdp_ptr; } t_fdpoll; struct _socketreceiver { char *sr_inbuf; int sr_inhead; int sr_intail; void *sr_owner; int sr_udp; struct sockaddr_storage *sr_fromaddr; /* optional */ t_socketnotifier sr_notifier; t_socketreceivefn sr_socketreceivefn; t_socketfromaddrfn sr_fromaddrfn; /* optional */ }; typedef struct _guiqueue { void *gq_client; t_glist *gq_glist; t_guicallbackfn gq_fn; struct _guiqueue *gq_next; } t_guiqueue; struct _instanceinter { int i_havegui; int i_nfdpoll; t_fdpoll *i_fdpoll; int i_maxfd; int i_guisock; t_socketreceiver *i_socketreceiver; t_guiqueue *i_guiqueuehead; t_binbuf *i_inbinbuf; char *i_guibuf; int i_guihead; int i_guitail; int i_guisize; int i_waitingforping; int i_bytessincelastping; int i_fdschanged; /* flag to break fdpoll loop if fd list changes */ #ifdef _WIN32 LARGE_INTEGER i_inittime; double i_freq; #endif #if PDTHREADS pthread_mutex_t i_mutex; #endif unsigned char i_recvbuf[NET_MAXPACKETSIZE]; }; extern int sys_guisetportnumber; extern int sys_addhist(int phase); void sys_stopgui(void); /* ----------- functions for timing, signals, priorities, etc --------- */ #ifdef _WIN32 static void sys_initntclock(void) { LARGE_INTEGER f1; LARGE_INTEGER now; QueryPerformanceCounter(&now); if (!QueryPerformanceFrequency(&f1)) { fprintf(stderr, "pd: QueryPerformanceFrequency failed\n"); f1.QuadPart = 1; } INTER->i_freq = f1.QuadPart; INTER->i_inittime = now; } #if 0 /* this is a version you can call if you did the QueryPerformanceCounter call yourself. Necessary for time tagging incoming MIDI at interrupt level, for instance; but we're not doing that just now. */ double nt_tixtotime(LARGE_INTEGER *dumbass) { if (INTER->i_freq == 0) sys_initntclock(); return (((double)(dumbass->QuadPart - INTER->i_inittime.QuadPart)) / INTER->i_freq); } #endif #endif /* _WIN32 */ /* get "real time" in seconds; take the first time we get called as a reference time of zero. */ double sys_getrealtime(void) { #ifndef _WIN32 static struct timeval then; struct timeval now; gettimeofday(&now, 0); if (then.tv_sec == 0 && then.tv_usec == 0) then = now; return ((now.tv_sec - then.tv_sec) + (1./1000000.) * (now.tv_usec - then.tv_usec)); #else LARGE_INTEGER now; QueryPerformanceCounter(&now); if (INTER->i_freq == 0) sys_initntclock(); return (((double)(now.QuadPart - INTER->i_inittime.QuadPart)) / INTER->i_freq); #endif } /* sleep (but cancel the sleeping if any file descriptors are ready - in that case, dispatch any resulting Pd messages and return. Called with sys_lock() set. We will temporarily release the lock if we actually sleep. */ static int sys_domicrosleep(int microsec) { struct timeval timeout; int i, didsomething = 0; t_fdpoll *fp; timeout.tv_sec = 0; timeout.tv_usec = 0; if (INTER->i_nfdpoll) { fd_set readset, writeset, exceptset; FD_ZERO(&writeset); FD_ZERO(&readset); FD_ZERO(&exceptset); for (fp = INTER->i_fdpoll, i = INTER->i_nfdpoll; i--; fp++) FD_SET(fp->fdp_fd, &readset); if(select(INTER->i_maxfd+1, &readset, &writeset, &exceptset, &timeout) < 0) perror("microsleep select"); INTER->i_fdschanged = 0; for (i = 0; i < INTER->i_nfdpoll && !INTER->i_fdschanged; i++) if (FD_ISSET(INTER->i_fdpoll[i].fdp_fd, &readset)) { (*INTER->i_fdpoll[i].fdp_fn) (INTER->i_fdpoll[i].fdp_ptr, INTER->i_fdpoll[i].fdp_fd); didsomething = 1; } if (didsomething) return (1); } if (microsec) { sys_unlock(); #ifdef _WIN32 Sleep(microsec/1000); #else usleep(microsec); #endif sys_lock(); } return (0); } /* sleep (but if any incoming or to-gui sending to do, do that instead.) Call with the PD instance lock UNSET - we set it here. */ void sys_microsleep( void) { sys_lock(); sys_domicrosleep(sched_get_sleepgrain()); sys_unlock(); } #if !defined(_WIN32) && !defined(__CYGWIN__) static void sys_signal(int signo, sig_t sigfun) { struct sigaction action; action.sa_flags = 0; action.sa_handler = sigfun; memset(&action.sa_mask, 0, sizeof(action.sa_mask)); #if 0 /* GG says: don't use that */ action.sa_restorer = 0; #endif if (sigaction(signo, &action, 0) < 0) perror("sigaction"); } static void sys_exithandler(int n) { static int trouble = 0; if (!trouble) { trouble = 1; fprintf(stderr, "Pd: signal %d\n", n); sys_bail(1); } else _exit(1); } static void sys_alarmhandler(int n) { fprintf(stderr, "Pd: system call timed out\n"); } static void sys_huphandler(int n) { struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 30000; select(1, 0, 0, 0, &timeout); } void sys_setalarm(int microsec) { struct itimerval gonzo; int sec = (int)(microsec/1000000); microsec %= 1000000; #if 0 fprintf(stderr, "timer %d:%d\n", sec, microsec); #endif gonzo.it_interval.tv_sec = 0; gonzo.it_interval.tv_usec = 0; gonzo.it_value.tv_sec = sec; gonzo.it_value.tv_usec = microsec; if (microsec) sys_signal(SIGALRM, sys_alarmhandler); else sys_signal(SIGALRM, SIG_IGN); setitimer(ITIMER_REAL, &gonzo, 0); } #endif /* NOT _WIN32 && NOT __CYGWIN__ */ /* on startup, set various signal handlers */ void sys_setsignalhandlers(void) { #if !defined(_WIN32) && !defined(__CYGWIN__) signal(SIGHUP, sys_huphandler); signal(SIGINT, sys_exithandler); signal(SIGQUIT, sys_exithandler); # ifdef SIGIOT signal(SIGIOT, sys_exithandler); # endif signal(SIGFPE, SIG_IGN); /* signal(SIGILL, sys_exithandler); signal(SIGBUS, sys_exithandler); signal(SIGSEGV, sys_exithandler); */ signal(SIGPIPE, SIG_IGN); signal(SIGALRM, SIG_IGN); #if 0 /* GG says: don't use that */ signal(SIGSTKFLT, sys_exithandler); #endif #endif /* NOT _WIN32 && NOT __CYGWIN__ */ } #define MODE_NRT 0 #define MODE_RT 1 #define MODE_WATCHDOG 2 #if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__GNU__) #if defined(_POSIX_PRIORITY_SCHEDULING) || defined(_POSIX_MEMLOCK) #include #endif void sys_set_priority(int mode) { #ifdef _POSIX_PRIORITY_SCHEDULING struct sched_param par; int p1, p2, p3; p1 = sched_get_priority_min(SCHED_FIFO); p2 = sched_get_priority_max(SCHED_FIFO); #ifdef USEAPI_JACK p3 = (mode == MODE_WATCHDOG ? p1 + 7 : (mode == MODE_RT ? p1 + 5 : 0)); #else p3 = (mode == MODE_WATCHDOG ? p2 - 5 : (mode == MODE_RT ? p2 - 7 : 0)); #endif par.sched_priority = p3; if (sched_setscheduler(0, (mode == MODE_NRT ? SCHED_OTHER : SCHED_FIFO), &par) < 0) { if (mode == MODE_WATCHDOG) fprintf(stderr, "priority %d scheduling failed.\n", p3); else post("priority %d scheduling failed; running at normal priority", p3); } else { if (mode == MODE_RT) logpost(NULL, PD_VERBOSE, "priority %d scheduling enabled.\n", p3); else logpost(NULL, PD_VERBOSE, "running at normal (non-real-time) priority.\n"); } #endif /* _POSIX_PRIORITY_SCHEDULING */ #if !defined(USEAPI_JACK) if (mode != MODE_NRT) { /* tb: force memlock to physical memory { */ struct rlimit mlock_limit; mlock_limit.rlim_cur=0; mlock_limit.rlim_max=0; setrlimit(RLIMIT_MEMLOCK,&mlock_limit); /* } tb */ if (mlockall(MCL_FUTURE) != -1 && sys_verbose) fprintf(stderr, "memory locking enabled.\n"); } else munlockall(); #endif /* ! USEAPI_JACK */ } #else /* !__linux__ */ void sys_set_priority(int mode) { /* dummy */ (void)mode; } #endif /* !__linux__ */ /* ------------------ receiving incoming messages over sockets ------------- */ unsigned char *sys_getrecvbuf(unsigned int *size) { if (size) *size = NET_MAXPACKETSIZE; return INTER->i_recvbuf; } void sys_sockerror(const char *s) { char buf[MAXPDSTRING]; int err = socket_errno(); socket_strerror(err, buf, sizeof(buf)); pd_error(0, "%s: %s (%d)", s, buf, err); } void sys_addpollfn(int fd, t_fdpollfn fn, void *ptr) { int nfd, size; t_fdpoll *fp; sys_init_fdpoll(); nfd = INTER->i_nfdpoll; size = nfd * sizeof(t_fdpoll); INTER->i_fdpoll = (t_fdpoll *)t_resizebytes( INTER->i_fdpoll, size, size + sizeof(t_fdpoll)); fp = INTER->i_fdpoll + nfd; fp->fdp_fd = fd; fp->fdp_fn = fn; fp->fdp_ptr = ptr; INTER->i_nfdpoll = nfd + 1; if (fd >= INTER->i_maxfd) INTER->i_maxfd = fd + 1; INTER->i_fdschanged = 1; } void sys_rmpollfn(int fd) { int nfd = INTER->i_nfdpoll; int i, size = nfd * sizeof(t_fdpoll); t_fdpoll *fp; INTER->i_fdschanged = 1; for (i = nfd, fp = INTER->i_fdpoll; i--; fp++) { if (fp->fdp_fd == fd) { while (i--) { fp[0] = fp[1]; fp++; } INTER->i_fdpoll = (t_fdpoll *)t_resizebytes( INTER->i_fdpoll, size, size - sizeof(t_fdpoll)); INTER->i_nfdpoll = nfd - 1; return; } } post("warning: %d removed from poll list but not found", fd); } /* Size of the buffer used for parsing FUDI messages received over TCP. Must be a power of two! LATER make this settable per socketreceiver instance */ #define INBUFSIZE 4096 t_socketreceiver *socketreceiver_new(void *owner, t_socketnotifier notifier, t_socketreceivefn socketreceivefn, int udp) { t_socketreceiver *x = (t_socketreceiver *)getbytes(sizeof(*x)); x->sr_inhead = x->sr_intail = 0; x->sr_owner = owner; x->sr_notifier = notifier; x->sr_socketreceivefn = socketreceivefn; x->sr_udp = udp; x->sr_fromaddr = NULL; x->sr_fromaddrfn = NULL; if (!udp) { if (!(x->sr_inbuf = malloc(INBUFSIZE))) bug("t_socketreceiver"); } else x->sr_inbuf = NULL; return (x); } void socketreceiver_free(t_socketreceiver *x) { if (x->sr_inbuf) free(x->sr_inbuf); if (x->sr_fromaddr) free(x->sr_fromaddr); freebytes(x, sizeof(*x)); } /* this is in a separately called subroutine so that the buffer isn't sitting on the stack while the messages are getting passed. */ static int socketreceiver_doread(t_socketreceiver *x) { char messbuf[INBUFSIZE], *bp = messbuf; int indx, first = 1; int inhead = x->sr_inhead; int intail = x->sr_intail; char *inbuf = x->sr_inbuf; for (indx = intail; first || (indx != inhead); first = 0, (indx = (indx+1)&(INBUFSIZE-1))) { /* if we hit a semi that isn't preceded by a \, it's a message boundary. LATER we should deal with the possibility that the preceding \ might itself be escaped! */ char c = *bp++ = inbuf[indx]; if (c == ';' && (!indx || inbuf[indx-1] != '\\')) { intail = (indx+1)&(INBUFSIZE-1); binbuf_text(INTER->i_inbinbuf, messbuf, bp - messbuf); if (sys_debuglevel & DEBUG_MESSDOWN) { size_t bufsize = (bp>messbuf)?(bp-messbuf):0; int colorize = stderr_isatty && (sys_debuglevel & DEBUG_COLORIZE); const char*msg = messbuf; if (('\r' == messbuf[0]) && ('\n' == messbuf[1])) { bufsize-=2; msg+=2; } #ifdef _WIN32 #ifdef _MSC_VER fwprintf(stderr, L"<< %.*S\n", (int)bufsize, msg); #else fwprintf(stderr, L"<< %.*s\n", (int)bufsize, msg); #endif fflush(stderr); #else if(colorize) fprintf(stderr, "\e[0;1;36m<< %.*s\e[0m\n", (int)bufsize, msg); else fprintf(stderr, "<< %.*s\n", (int)bufsize, msg); #endif } x->sr_inhead = inhead; x->sr_intail = intail; return (1); } } return (0); } static void socketreceiver_getudp(t_socketreceiver *x, int fd) { char *buf = (char *)sys_getrecvbuf(0); socklen_t fromaddrlen = sizeof(struct sockaddr_storage); int ret, readbytes = 0; while (1) { ret = (int)recvfrom(fd, buf, NET_MAXPACKETSIZE-1, 0, (struct sockaddr *)x->sr_fromaddr, (x->sr_fromaddr ? &fromaddrlen : 0)); if (ret < 0) { /* socket_errno_udp() ignores some error codes */ if (socket_errno_udp()) { sys_sockerror("recv (udp)"); /* only notify and shutdown a UDP sender! */ if (x->sr_notifier) { (*x->sr_notifier)(x->sr_owner, fd); sys_rmpollfn(fd); sys_closesocket(fd); } } return; } else if (ret > 0) { /* handle too large UDP packets */ if (ret > NET_MAXPACKETSIZE-1) { post("warning: incoming UDP packet truncated from %d to %d bytes.", ret, NET_MAXPACKETSIZE-1); ret = NET_MAXPACKETSIZE-1; } buf[ret] = 0; #if 0 post("%s", buf); #endif if (buf[ret-1] != '\n') { #if 0 pd_error(0, "dropped bad buffer %s\n", buf); #endif } else { char *semi = strchr(buf, ';'); if (semi) *semi = 0; if (x->sr_fromaddrfn) (*x->sr_fromaddrfn)(x->sr_owner, (const void *)x->sr_fromaddr); binbuf_text(INTER->i_inbinbuf, buf, strlen(buf)); outlet_setstacklim(); if (x->sr_socketreceivefn) (*x->sr_socketreceivefn)(x->sr_owner, INTER->i_inbinbuf); else bug("socketreceiver_getudp"); } readbytes += ret; /* throttle */ if (readbytes >= NET_MAXPACKETSIZE) return; /* check for pending UDP packets */ if (socket_bytes_available(fd) <= 0) return; } } } void socketreceiver_read(t_socketreceiver *x, int fd) { if (x->sr_udp) /* UDP ("datagram") socket protocol */ socketreceiver_getudp(x, fd); else /* TCP ("streaming") socket protocol */ { char *semi; int readto = (x->sr_inhead >= x->sr_intail ? INBUFSIZE : x->sr_intail-1); int ret; /* the input buffer might be full. If so, drop the whole thing */ if (readto == x->sr_inhead) { fprintf(stderr, "pd: dropped message from gui\n"); x->sr_inhead = x->sr_intail = 0; readto = INBUFSIZE; } else { ret = (int)recv(fd, x->sr_inbuf + x->sr_inhead, readto - x->sr_inhead, 0); if (ret <= 0) { if (ret < 0) sys_sockerror("recv (tcp)"); if (x == INTER->i_socketreceiver) { if (pd_this == &pd_maininstance) { fprintf(stderr, "read from GUI socket: %s; stopping\n", strerror(errno)); sys_bail(1); } else { sys_rmpollfn(fd); sys_closesocket(fd); sys_stopgui(); } } else { if (x->sr_notifier) (*x->sr_notifier)(x->sr_owner, fd); sys_rmpollfn(fd); sys_closesocket(fd); } } else { x->sr_inhead += ret; if (x->sr_inhead >= INBUFSIZE) x->sr_inhead = 0; while (socketreceiver_doread(x)) { if (x->sr_fromaddrfn) { socklen_t fromaddrlen = sizeof(struct sockaddr_storage); if(!getpeername(fd, (struct sockaddr *)x->sr_fromaddr, &fromaddrlen)) (*x->sr_fromaddrfn)(x->sr_owner, (const void *)x->sr_fromaddr); } outlet_setstacklim(); if (x->sr_socketreceivefn) (*x->sr_socketreceivefn)(x->sr_owner, INTER->i_inbinbuf); else binbuf_eval(INTER->i_inbinbuf, 0, 0, 0); if (x->sr_inhead == x->sr_intail) break; } } } } } void socketreceiver_set_fromaddrfn(t_socketreceiver *x, t_socketfromaddrfn fromaddrfn) { x->sr_fromaddrfn = fromaddrfn; if (fromaddrfn) { if (!x->sr_fromaddr) x->sr_fromaddr = malloc(sizeof(struct sockaddr_storage)); } else if (x->sr_fromaddr) { free(x->sr_fromaddr); x->sr_fromaddr = NULL; } } void sys_closesocket(int sockfd) { socket_close(sockfd); } /* ---------------------- sending messages to the GUI ------------------ */ #define GUI_ALLOCCHUNK 8192 #define GUI_UPDATESLICE 512 /* how much we try to do in one idle period */ #define GUI_BYTESPERPING 1024 /* how much we send up per ping */ static void sys_trytogetmoreguibuf(int newsize) { /* newsize can be negative if it overflows (at 0x7FFFFFFF) * which only happens if we push a huge amount of data to the GUI, * such as printing a billion numbers * * we could fix this by using size_t (or ssize_t), but this will * possibly lead to memory exhaustion. * as the overflow happens at 2GB which is rather large anyhow, * but most machines will still be able to handle this without swapping * and crashing, we just use the 2GB limit to trigger a synchronous write. * also note that on the Tcl/Tk side, the maximum size of a buffer is 2GB, * so there's a nice analogy here. */ char *newbuf = (newsize>=0)?realloc(INTER->i_guibuf, newsize):0; #if 0 static int sizewas; if (newsize > 70000 && sizewas < 70000) { int i; for (i = INTER->i_guitail; i < INTER->i_guihead; i++) fputc(INTER->i_guibuf[i], stderr); } sizewas = newsize; #endif #if 0 fprintf(stderr, "new size %d (head %d, tail %d)\n", newsize, INTER->i_guihead, INTER->i_guitail); #endif /* if realloc fails, make a last-ditch attempt to stay alive by synchronously writing out the existing contents. LATER test this by intentionally setting newbuf to zero */ if (!newbuf) { int bytestowrite = INTER->i_guihead - INTER->i_guitail; int written = 0; while (1) { int res = (int)send( INTER->i_guisock, INTER->i_guibuf + INTER->i_guitail + written, bytestowrite, 0); if (res < 0) { perror("pd output pipe"); sys_bail(1); } else { written += res; if (written >= bytestowrite) break; } } INTER->i_guihead = INTER->i_guitail = 0; } else { INTER->i_guisize = newsize; INTER->i_guibuf = newbuf; } } int sys_havegui(void) { return (INTER->i_havegui); } void sys_vgui(const char *fmt, ...) { int msglen, bytesleft, headwas, nwrote; va_list ap; if (!sys_havegui()) return; if (!INTER->i_guibuf) { if (!(INTER->i_guibuf = malloc(GUI_ALLOCCHUNK))) { fprintf(stderr, "Pd: couldn't allocate GUI buffer\n"); sys_bail(1); } INTER->i_guisize = GUI_ALLOCCHUNK; INTER->i_guihead = INTER->i_guitail = 0; } if (INTER->i_guihead > INTER->i_guisize - (GUI_ALLOCCHUNK/2)) { sys_trytogetmoreguibuf(INTER->i_guisize + GUI_ALLOCCHUNK); } va_start(ap, fmt); msglen = vsnprintf( INTER->i_guibuf + INTER->i_guihead, INTER->i_guisize - INTER->i_guihead, fmt, ap); va_end(ap); if(msglen < 0) { fprintf(stderr, "Pd: buffer space wasn't sufficient for long GUI string\n"); return; } if (msglen >= INTER->i_guisize - INTER->i_guihead) { int msglen2, newsize = INTER->i_guisize + 1 + (msglen > GUI_ALLOCCHUNK ? msglen : GUI_ALLOCCHUNK); sys_trytogetmoreguibuf(newsize); va_start(ap, fmt); msglen2 = vsnprintf( INTER->i_guibuf + INTER->i_guihead, INTER->i_guisize - INTER->i_guihead, fmt, ap); va_end(ap); if (msglen2 != msglen) bug("sys_vgui"); if (msglen >= INTER->i_guisize - INTER->i_guihead) msglen = INTER->i_guisize - INTER->i_guihead; } if (sys_debuglevel & DEBUG_MESSUP) { const char *mess = INTER->i_guibuf + INTER->i_guihead; int colorize = stderr_isatty && (sys_debuglevel & DEBUG_COLORIZE); static int newmess = 1; #ifdef _WIN32 #ifdef _MSC_VER fwprintf(stderr, L"%S", mess); #else fwprintf(stderr, L"%s", mess); #endif fflush(stderr); #else if (colorize) fprintf(stderr, "\e[0;1;35m%s%s\e[0m", (newmess)?">> ":"", mess); else fprintf(stderr, "%s%s", (newmess)?">> ":"", mess); newmess = ('\n' == mess[msglen-1]); #endif } INTER->i_guihead += msglen; INTER->i_bytessincelastping += msglen; } void sys_gui(const char *s) { sys_vgui("%s", s); } static const char**namelist2strings(t_namelist *nl, unsigned int *N) { const char**result = 0; unsigned int n=0; *N = 0; for(; nl; nl = nl->nl_next) { const char**newresult = resizebytes(result, n*sizeof(*result), (n+1)*sizeof(*result)); if(!newresult) break; result = newresult; result[n] = nl->nl_string; n++; *N = n; } return result; } static int sys_flushtogui(void) { int writesize = INTER->i_guihead - INTER->i_guitail, nwrote = 0; if (writesize > 0) nwrote = (int)send( INTER->i_guisock, INTER->i_guibuf + INTER->i_guitail, writesize, 0); #if 0 if (writesize) fprintf(stderr, "wrote %d of %d\n", nwrote, writesize); #endif if (nwrote < 0) { perror("pd-to-gui socket"); sys_bail(1); } else if (!nwrote) return (0); else if (nwrote >= INTER->i_guihead - INTER->i_guitail) INTER->i_guihead = INTER->i_guitail = 0; else if (nwrote) { INTER->i_guitail += nwrote; if (INTER->i_guitail > (INTER->i_guisize >> 2)) { memmove(INTER->i_guibuf, INTER->i_guibuf + INTER->i_guitail, INTER->i_guihead - INTER->i_guitail); INTER->i_guihead = INTER->i_guihead - INTER->i_guitail; INTER->i_guitail = 0; } } return (1); } void glob_ping(t_pd *dummy) { INTER->i_waitingforping = 0; } static int sys_flushqueue(void) { int wherestop = INTER->i_bytessincelastping + GUI_UPDATESLICE; if (wherestop + (GUI_UPDATESLICE >> 1) > GUI_BYTESPERPING) wherestop = 0x7fffffff; if (INTER->i_waitingforping) return (0); if (!INTER->i_guiqueuehead) return (0); while (1) { if (INTER->i_bytessincelastping >= GUI_BYTESPERPING) { sys_gui("pdtk_ping\n"); INTER->i_bytessincelastping = 0; INTER->i_waitingforping = 1; return (1); } if (INTER->i_guiqueuehead) { t_guiqueue *headwas = INTER->i_guiqueuehead; INTER->i_guiqueuehead = headwas->gq_next; (*headwas->gq_fn)(headwas->gq_client, headwas->gq_glist); t_freebytes(headwas, sizeof(*headwas)); if (INTER->i_bytessincelastping >= wherestop) break; } else break; } sys_flushtogui(); return (1); } /* flush output buffer and update queue to gui in small time slices */ static int sys_poll_togui(void) /* returns 1 if did anything */ { if (!sys_havegui()) return (0); /* in case there is stuff still in the buffer, try to flush it. */ sys_flushtogui(); /* if the flush wasn't complete, wait. */ if (INTER->i_guihead > INTER->i_guitail) return (0); /* check for queued updates */ if (sys_flushqueue()) return (1); return (0); } /* if some GUI object is having to do heavy computations, it can tell us to back off from doing more updates by faking a big one itself. */ void sys_pretendguibytes(int n) { INTER->i_bytessincelastping += n; } void sys_queuegui(void *client, t_glist *glist, t_guicallbackfn f) { t_guiqueue **gqnextptr, *gq; if (!INTER->i_guiqueuehead) gqnextptr = &INTER->i_guiqueuehead; else { for (gq = INTER->i_guiqueuehead; gq->gq_next; gq = gq->gq_next) if (gq->gq_client == client) return; if (gq->gq_client == client) return; gqnextptr = &gq->gq_next; } gq = t_getbytes(sizeof(*gq)); gq->gq_next = 0; gq->gq_client = client; gq->gq_glist = glist; gq->gq_fn = f; gq->gq_next = 0; *gqnextptr = gq; } void sys_unqueuegui(void *client) { t_guiqueue *gq, *gq2; while (INTER->i_guiqueuehead && INTER->i_guiqueuehead->gq_client == client) { gq = INTER->i_guiqueuehead; INTER->i_guiqueuehead = INTER->i_guiqueuehead->gq_next; t_freebytes(gq, sizeof(*gq)); } if (!INTER->i_guiqueuehead) return; for (gq = INTER->i_guiqueuehead; (gq2 = gq->gq_next); gq = gq2) if (gq2->gq_client == client) { gq->gq_next = gq2->gq_next; t_freebytes(gq2, sizeof(*gq2)); break; } } /* poll for any incoming packets, or for GUI updates to send. call with the PD instance lock set. */ int sys_pollgui(void) { static double lasttime = 0; double now = 0; int didsomething = sys_domicrosleep(0); if (!didsomething || (now = sys_getrealtime()) > lasttime + 0.5) { didsomething |= sys_poll_togui(); if (now) lasttime = now; } return (didsomething); } void sys_init_fdpoll(void) { if (INTER->i_fdpoll) return; /* create an empty FD poll list */ INTER->i_fdpoll = (t_fdpoll *)t_getbytes(0); INTER->i_nfdpoll = 0; INTER->i_inbinbuf = binbuf_new(); } void sys_gui_preferences(void) { unsigned int nsearch, ntemp, nstatic, nlibs; const char**searchpath = namelist2strings(STUFF->st_searchpath, &nsearch); const char**temppath = namelist2strings(STUFF->st_temppath, &ntemp); const char**staticpath = namelist2strings(STUFF->st_staticpath, &nstatic); const char**startuplibs = namelist2strings(STUFF->st_externlist, &nlibs); pdgui_vmess("::dialog_path::set_paths", "SSS" , nsearch, searchpath , ntemp, temppath , nstatic, staticpath ); /* send the list of loaded libraries ... */ pdgui_vmess("::dialog_startup::set_libraries", "S" , nlibs, startuplibs ); sys_vgui("set_escaped ::sys_verbose %d\n", sys_verbose); sys_vgui("set_escaped ::sys_use_stdpath %d\n", sys_usestdpath); sys_vgui("set_escaped ::sys_defeatrt %d\n", sys_defeatrt); sys_vgui("set_escaped ::sys_zoom_open %d\n", (sys_zoom_open == 2)); pdgui_vmess("::dialog_startup::set_flags", "s", (sys_flags? sys_flags->s_name : "")); freebytes(searchpath, nsearch * sizeof(*searchpath)); freebytes(temppath, ntemp * sizeof(*temppath)); freebytes(staticpath, nstatic * sizeof(*staticpath)); freebytes(startuplibs, nlibs * sizeof(*startuplibs)); } /* --------------------- starting up the GUI connection ------------- */ static int sys_watchfd = -1; void glob_watchdog(t_pd *dummy) { if (sys_watchfd < 0) return; if (write(sys_watchfd, "\n", 1) < 1) { fprintf(stderr, "pd: watchdog process died\n"); sys_bail(1); } } static const char*deken_OS = #if defined DEKEN_OS stringify(DEKEN_OS) #elif defined __linux__ "Linux" #elif defined __APPLE__ "Darwin" #elif defined _WIN32 "Windows" #else # if defined(__GNUC__) # warning unknown OS # endif 0 #endif ; static const char*deken_CPU[] = { #if defined DEKEN_CPU stringify(DEKEN_CPU) #elif defined(__x86_64__) || defined(__amd64__) || defined(_M_X64) || defined(_M_AMD64) "amd64" #elif defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) || defined(_M_IX86) "i386" #elif defined(__ppc__) "ppc" #elif defined(__aarch64__) "arm64" #elif defined (__ARM_ARCH) "armv" stringify(__ARM_ARCH) # if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) # if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ "b" # endif # endif #else # if defined(__GNUC__) # warning unknown architecture # endif 0 #endif , 0, 0, 0, 0, 0, 0, 0, 0, 0}; static const char*strip_quotes(const char*s, char*outbuf, size_t outsize) { size_t len = strlen(s); const char q = (len>1)?s[0]:0; /* only strip single or double quotes */ switch(q) { case '\'': case '"': break; case 0: default: return s; } /* only strip quotes if they are both at the beginning and the end */ if (q != s[len-1]) return s; if(len>outsize) len = outsize; outbuf[0] = 0; strncpy(outbuf, s+1, len-2); outbuf[outsize-1] = 0; return outbuf; } static void init_deken_arch(void) { static int initialized = 0; static char deken_OS_noquotes[MAXPDSTRING]; static char deken_CPU_noquotes[MAXPDSTRING]; if(initialized) return; initialized = 1; #if defined(DEKEN_OS) deken_OS = strip_quotes(deken_OS, deken_OS_noquotes, MAXPDSTRING); #endif /* DEKEN_OS */ #define CPUNAME_SIZE 15 #if defined(DEKEN_CPU) deken_CPU[0] = strip_quotes(deken_CPU[0], deken_CPU_noquotes, MAXPDSTRING); #else /* !DEKEN_CPU */ # if defined __ARM_ARCH /* ARM-specific: * if we are running ARMv7, we can also load ARMv6 externals */ if (deken_CPU && sizeof(deken_CPU)/sizeof(*deken_CPU) > 0) { int arm_cpu = __ARM_ARCH; int cpu_v; int n; const char endianness = # if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) 'b'; # else 0; # endif # if USE_UNAME /* * Pd might be compiled for ARMv6 (as in Raspbian), * but run on an ARMv7 (or higher) (e.g. RPi2 and newer). * Therefore we try to detect the actual CPU, and announce that */ struct utsname name; if (uname (&name) >= 0) { if(!strncmp(name.machine, "armv", 4)) { cpu_v = name.machine[4] - '0'; if((cpu_v >= 6) && (cpu_v <= 9)) arm_cpu = cpu_v; } } # endif /* uname() */ /* list all compatible ARM CPUs */ for(cpu_v = arm_cpu; (cpu_v >= 6 && n < (sizeof(deken_CPU)/sizeof(*deken_CPU))); cpu_v--) { static char cpuname[CPUNAME_SIZE+1]; snprintf(cpuname, CPUNAME_SIZE, "armv%d%c", cpu_v, endianness); deken_CPU[n++] = gensym(cpuname)->s_name; } } # endif /* arm */ #endif /* !DEKEN_CPU */ } /* get the (normalized) deken-specifier * if 'float_agnostic' is non-0, the float-size is included. * otherwise a floatsize-agnostic specifier is generated. * 'cpu' is an index in the list of preferred compatible CPUs * (higher numbers indicate less preferred CPUs) * a negative 'cpu' indicates 'fat' binaries * returns 0, if the deken-specifier cannot be determined * (e.g. on new architectures, or because the 'cpu' index is invalid) */ const char*sys_deken_specifier(char*buf, size_t bufsize, int float_agnostic, int cpu) { unsigned int i; init_deken_arch(); if (!deken_OS) return 0; if ((cpu>=0) && (((!deken_CPU) || (cpu >= (sizeof(deken_CPU)/sizeof(*deken_CPU))) || (!deken_CPU[cpu])))) return 0; snprintf(buf, bufsize-1, "%s-%s-%d", deken_OS, (cpu<0)?"fat":deken_CPU[cpu], (int)((float_agnostic?0:8) * sizeof(t_float))); buf[bufsize-1] = 0; for(i=0; i 2) close(burnfd1); if (burnfd2 > 2) close(burnfd2); if (burnfd3 > 2) close(burnfd3); #endif /* get addrinfo list using hostname & port */ status = addrinfo_get_list(&ailist, LOCALHOST, sys_guisetportnumber, SOCK_STREAM); if (status != 0) { fprintf(stderr, "localhost not found (inet protocol not installed?)\n%s (%d)", gai_strerror(status), status); return (1); } /* Sort to IPv4 for now as the Pd gui uses IPv4. */ addrinfo_sort_list(&ailist, addrinfo_ipv4_first); /* We don't know in advance whether the GUI uses IPv4 or IPv6, so we try both and pick the one which works. */ for (ai = ailist; ai != NULL; ai = ai->ai_next) { /* create a socket */ sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (sockfd < 0) continue; #if 1 if (socket_set_boolopt(sockfd, IPPROTO_TCP, TCP_NODELAY, 1) < 0) fprintf(stderr, "setsockopt (TCP_NODELAY) failed"); #endif /* try to connect */ if (socket_connect(sockfd, ai->ai_addr, ai->ai_addrlen, 10.f) < 0) { sys_closesocket(sockfd); sockfd = -1; continue; } /* this addr worked */ break; } freeaddrinfo(ailist); /* confirm that we could connect */ if (sockfd < 0) { sys_sockerror("connecting stream socket"); return (1); } INTER->i_guisock = sockfd; } else /* default behavior: start up the GUI ourselves. */ { struct sockaddr_storage addr; int status; #ifdef _WIN32 char scriptbuf[MAXPDSTRING+30], wishbuf[MAXPDSTRING+30]; STARTUPINFO si; PROCESS_INFORMATION pi; #else const char *guicmd; #endif char cmdbuf[4*MAXPDSTRING]; /* get addrinfo list using hostname (get random port from OS) */ status = addrinfo_get_list(&ailist, LOCALHOST, 0, SOCK_STREAM); if (status != 0) { fprintf(stderr, "localhost not found (inet protocol not installed?)\n%s (%d)", gai_strerror(status), status); return (1); } /* we prefer the IPv4 addresses because the GUI might not be IPv6 capable. */ addrinfo_sort_list(&ailist, addrinfo_ipv4_first); /* try each addr until we find one that works */ for (ai = ailist; ai != NULL; ai = ai->ai_next) { sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (sockfd < 0) continue; #if 1 /* ask OS to allow another process to reopen this port after we close it */ if (socket_set_boolopt(sockfd, SOL_SOCKET, SO_REUSEADDR, 1) < 0) fprintf(stderr, "setsockopt (SO_REUSEADDR) failed\n"); #endif #if 1 /* stream (TCP) sockets are set NODELAY */ if (socket_set_boolopt(sockfd, IPPROTO_TCP, TCP_NODELAY, 1) < 0) fprintf(stderr, "setsockopt (TCP_NODELAY) failed"); #endif /* name the socket */ if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) { socket_close(sockfd); sockfd = -1; continue; } /* this addr worked */ memcpy(&addr, ai->ai_addr, ai->ai_addrlen); break; } freeaddrinfo(ailist); /* confirm that socket/bind worked */ if (sockfd < 0) { sys_sockerror("bind"); return (1); } /* get the actual port number */ portno = socket_get_port(sockfd); if (sys_verbose) fprintf(stderr, "port %d\n", portno); #ifndef _WIN32 if (sys_guicmd) guicmd = sys_guicmd; else { #ifdef __APPLE__ int i; struct stat statbuf; glob_t glob_buffer; char *homedir = getenv("HOME"); char embed_glob[FILENAME_MAX]; char home_filename[FILENAME_MAX]; char *wish_paths[11] = { "(custom wish not defined)", "(did not find a home directory)", "/Applications/Utilities/Wish.app/Contents/MacOS/Wish", "/Applications/Utilities/Wish Shell.app/Contents/MacOS/Wish Shell", "/Applications/Wish.app/Contents/MacOS/Wish", "/Applications/Wish Shell.app/Contents/MacOS/Wish Shell", "/Library/Frameworks/Tk.framework/Resources/Wish.app/Contents/MacOS/Wish", "/Library/Frameworks/Tk.framework/Resources/Wish Shell.app/Contents/MacOS/Wish Shell", "/System/Library/Frameworks/Tk.framework/Resources/Wish.app/Contents/MacOS/Wish", "/System/Library/Frameworks/Tk.framework/Resources/Wish Shell.app/Contents/MacOS/Wish Shell", "/usr/bin/wish" }; /* this glob is needed so the Wish executable can have the same * filename as the Pd.app, i.e. 'Pd-0.42-3.app' should have a Wish * executable called 'Pd-0.42-3.app/Contents/MacOS/Pd-0.42-3' */ sprintf(embed_glob, "%s/../MacOS/Pd*", libdir); glob_buffer.gl_matchc = 1; /* we only need one match */ glob(embed_glob, GLOB_LIMIT, NULL, &glob_buffer); /* If we are using a copy of Wish embedded in the Pd.app, then it * will automatically load pd-gui.tcl if that embedded Wish can * find ../Resources/Scripts/AppMain.tcl, then Wish doesn't want * to receive the pd-gui.tcl as an argument. Otherwise it needs * to know how to find pd-gui.tcl */ if (glob_buffer.gl_pathc > 0) sprintf(cmdbuf, "\"%s\" %d\n", glob_buffer.gl_pathv[0], portno); else { int wish_paths_count = sizeof(wish_paths)/sizeof(*wish_paths); #ifdef WISH wish_paths[0] = WISH; #endif sprintf(home_filename, "%s/Applications/Wish.app/Contents/MacOS/Wish",homedir); wish_paths[1] = home_filename; for(i=0; i= 0) break; } if(i>=wish_paths_count) { fprintf(stderr, "sys_startgui couldn't find tcl/tk\n"); sys_closesocket(sockfd); return (1); } sprintf(cmdbuf, "\"%s\" \"%s/%s/pd-gui.tcl\" %d\n", wish_paths[i], libdir, PDGUIDIR, portno); } #else /* __APPLE__ */ /* sprintf the wish command with needed environment variables. For some reason the wish script fails if HOME isn't defined so if necessary we put that in here too. */ sprintf(cmdbuf, "TCL_LIBRARY=\"%s/lib/tcl/library\" TK_LIBRARY=\"%s/lib/tk/library\"%s \ " WISH " \"%s/" PDGUIDIR "/pd-gui.tcl\" %d\n", libdir, libdir, (getenv("HOME") ? "" : " HOME=/tmp"), libdir, portno); #endif /* __APPLE__ */ guicmd = cmdbuf; } if (sys_verbose) fprintf(stderr, "%s", guicmd); childpid = fork(); if (childpid < 0) { if (errno) perror("sys_startgui"); else fprintf(stderr, "sys_startgui failed\n"); sys_closesocket(sockfd); return (1); } else if (!childpid) /* we're the child */ { sys_closesocket(sockfd); /* child doesn't listen */ sys_set_priority(MODE_NRT); /* child runs non-real-time */ #ifndef __APPLE__ // TODO this seems unneeded on any platform hans@eds.org /* the wish process in Unix will make a wish shell and read/write standard in and out unless we close the file descriptors. Somehow this doesn't make the MAC OSX version of Wish happy...*/ if (pipe(stdinpipe) < 0) sys_sockerror("pipe"); else { if (stdinpipe[0] != 0) { close (0); dup2(stdinpipe[0], 0); close(stdinpipe[0]); } } #endif /* NOT __APPLE__ */ execl("/bin/sh", "sh", "-c", guicmd, (char*)0); perror("pd: exec"); fprintf(stderr, "Perhaps tcl and tk aren't yet installed?\n"); _exit(1); } #else /* NOT _WIN32 */ /* fprintf(stderr, "%s\n", libdir); */ snprintf(wishbuf, sizeof(wishbuf), "%s/" PDBINDIR WISH, libdir); sys_bashfilename(wishbuf, wishbuf); snprintf(scriptbuf, sizeof(scriptbuf), "%s/" PDGUIDIR "/pd-gui.tcl", libdir); sys_bashfilename(scriptbuf, scriptbuf); snprintf(cmdbuf, sizeof(cmdbuf), "%s \"%s\" %d", /* quote script path! */ WISH, scriptbuf, portno); memset(&si, 0, sizeof(si)); si.cb = sizeof(si); /* CHR: DETACHED_PROCESS makes sure that the GUI process cannot possibly interfere with the core. */ if (!CreateProcessA(wishbuf, cmdbuf, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &si, &pi)) { char errbuf[MAXPDSTRING]; socket_strerror(GetLastError(), errbuf, sizeof(errbuf)); fprintf(stderr, "could not start %s: %s\n", wishbuf, errbuf); return (1); } #endif /* NOT _WIN32 */ if (sys_verbose) fprintf(stderr, "Waiting for connection request... \n"); if (listen(sockfd, 5) < 0) { sys_sockerror("listen"); sys_closesocket(sockfd); return (1); } INTER->i_guisock = accept(sockfd, 0, 0); sys_closesocket(sockfd); if (INTER->i_guisock < 0) { sys_sockerror("accept"); return (1); } if (sys_verbose) fprintf(stderr, "... connected\n"); INTER->i_guihead = INTER->i_guitail = 0; } INTER->i_socketreceiver = socketreceiver_new(0, 0, 0, 0); sys_addpollfn(INTER->i_guisock, (t_fdpollfn)socketreceiver_read, INTER->i_socketreceiver); /* here is where we start the pinging. */ #if PD_WATCHDOG if (sys_hipriority) sys_gui("pdtk_watchdog\n"); #endif sys_get_audio_apis(apibuf); sys_get_midi_apis(apibuf2); sys_gui_preferences(); /* tell GUI about path and startup flags */ /* ... and about font, media APIS, etc */ sys_vgui("pdtk_pd_startup %d %d %d {%s} %s %s {%s} %s\n", PD_MAJOR_VERSION, PD_MINOR_VERSION, PD_BUGFIX_VERSION, PD_TEST_VERSION, apibuf, apibuf2, pdgui_strnescape(quotebuf, MAXPDSTRING, sys_font, 0), sys_fontweight); sys_init_deken(); do { t_audiosettings as; sys_get_audio_settings(&as); sys_vgui("set pd_whichapi %d\n", as.a_api); } while(0); return (0); } void sys_setrealtime(const char *libdir) { char cmdbuf[MAXPDSTRING]; #if PD_WATCHDOG /* promote this process's priority, if we can and want to. If sys_hipriority not specified (-1), we assume real-time was wanted. Starting in Linux 2.6 one can permit real-time operation of Pd by] putting lines like: @audio - rtprio 99 @audio - memlock unlimited in the system limits file, perhaps /etc/limits.conf or /etc/security/limits.conf, and calling Pd from a user in group audio. */ if (sys_hipriority == -1) sys_hipriority = 1; snprintf(cmdbuf, MAXPDSTRING, "%s/bin/pd-watchdog", libdir); cmdbuf[MAXPDSTRING-1] = 0; if (sys_hipriority) { struct stat statbuf; if (stat(cmdbuf, &statbuf) < 0) { fprintf(stderr, "disabling real-time priority due to missing pd-watchdog (%s)\n", cmdbuf); sys_hipriority = 0; } } if (sys_hipriority) { int pipe9[2], watchpid; /* To prevent lockup, we fork off a watchdog process with higher real-time priority than ours. The GUI has to send a stream of ping messages to the watchdog THROUGH the Pd process which has to pick them up from the GUI and forward them. If any of these things aren't happening the watchdog starts sending "stop" and "cont" signals to the Pd process to make it timeshare with the rest of the system. (Version 0.33P2 : if there's no GUI, the watchdog pinging is done from the scheduler idle routine in this process instead.) */ if (pipe(pipe9) < 0) { sys_sockerror("pipe"); return; } watchpid = fork(); if (watchpid < 0) { if (errno) perror("sys_setpriority"); else fprintf(stderr, "sys_setpriority failed\n"); return; } else if (!watchpid) /* we're the child */ { sys_set_priority(MODE_WATCHDOG); if (pipe9[1] != 0) { dup2(pipe9[0], 0); close(pipe9[0]); } close(pipe9[1]); if (sys_verbose) fprintf(stderr, "%s\n", cmdbuf); execl(cmdbuf, cmdbuf, (char*)0); perror("pd: exec"); _exit(1); } else /* we're the parent */ { sys_set_priority(MODE_RT); close(pipe9[0]); /* set close-on-exec so that watchdog will see an EOF when we close our copy - otherwise it might hang waiting for some stupid child process (as seems to happen if jackd auto-starts for us.) */ if(fcntl(pipe9[1], F_SETFD, FD_CLOEXEC) < 0) perror("close-on-exec"); sys_watchfd = pipe9[1]; /* We also have to start the ping loop in the GUI; this is done later when the socket is open. */ } } else logpost(NULL, PD_VERBOSE, "not setting real-time priority"); #endif /* __linux__ */ #ifdef _WIN32 if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS)) fprintf(stderr, "pd: couldn't set high priority class\n"); #endif #ifdef __APPLE__ if (sys_hipriority) { struct sched_param param; int policy = SCHED_RR; int err; param.sched_priority = 80; /* adjust 0 : 100 */ err = pthread_setschedparam(pthread_self(), policy, ¶m); if (err) post("warning: high priority scheduling failed"); } #endif /* __APPLE__ */ } /* This is called when something bad has happened, like a segfault. Call glob_quit() below to exit cleanly. LATER try to save dirty documents even in the bad case. */ void sys_bail(int n) { static int reentered = 0; if (!reentered) { reentered = 1; #if !defined(__linux__) && !defined(__FreeBSD_kernel__) && !defined(__GNU__) /* sys_close_audio() hangs if you're in a signal? */ fprintf(stderr ,"gui socket %d - \n", INTER->i_guisock); fprintf(stderr, "closing audio...\n"); sys_close_audio(); fprintf(stderr, "closing MIDI...\n"); sys_close_midi(); fprintf(stderr, "... done.\n"); #endif exit(n); } else _exit(1); } extern void sys_exit(void); void glob_exit(void *dummy, t_float status) { /* sys_exit() sets the sys_quit flag, so all loops end */ sys_exit(); sys_close_audio(); sys_close_midi(); if (sys_havegui()) { sys_closesocket(INTER->i_guisock); sys_rmpollfn(INTER->i_guisock); } exit((int)status); } void glob_quit(void *dummy) { glob_exit(dummy, 0); } /* recursively descend to all canvases and send them "vis" messages if they believe they're visible, to make it really so. */ static void glist_maybevis(t_glist *gl) { t_gobj *g; for (g = gl->gl_list; g; g = g->g_next) if (pd_class(&g->g_pd) == canvas_class) glist_maybevis((t_glist *)g); if (gl->gl_havewindow) { canvas_vis(gl, 0); canvas_vis(gl, 1); } } int sys_startgui(const char *libdir) { t_canvas *x; stderr_isatty = isatty(2); for (x = pd_getcanvaslist(); x; x = x->gl_next) canvas_vis(x, 0); INTER->i_havegui = 1; INTER->i_guihead = INTER->i_guitail = 0; if (sys_do_startgui(libdir)) return (-1); for (x = pd_getcanvaslist(); x; x = x->gl_next) if (strcmp(x->gl_name->s_name, "_float_template") && strcmp(x->gl_name->s_name, "_float_array_template") && strcmp(x->gl_name->s_name, "_text_template")) { glist_maybevis(x); canvas_vis(x, 1); } return (0); } /* more work needed here - for some reason we can't restart the gui after shutting it down this way. I think the second 'init' message never makes it because the to-gui buffer isn't re-initialized. */ void sys_stopgui(void) { t_canvas *x; for (x = pd_getcanvaslist(); x; x = x->gl_next) canvas_vis(x, 0); sys_vgui("%s", "exit\n"); if (INTER->i_guisock >= 0) { sys_closesocket(INTER->i_guisock); sys_rmpollfn(INTER->i_guisock); INTER->i_guisock = -1; } INTER->i_havegui = 0; } /* ----------- mutexes for thread safety --------------- */ void s_inter_newpdinstance(void) { INTER = getbytes(sizeof(*INTER)); #if PDTHREADS pthread_mutex_init(&INTER->i_mutex, NULL); pd_this->pd_islocked = 0; #endif #ifdef _WIN32 INTER->i_freq = 0; #endif INTER->i_havegui = 0; } void s_inter_free(t_instanceinter *inter) { if (inter->i_fdpoll) { binbuf_free(inter->i_inbinbuf); inter->i_inbinbuf = 0; t_freebytes(inter->i_fdpoll, inter->i_nfdpoll * sizeof(t_fdpoll)); inter->i_fdpoll = 0; inter->i_nfdpoll = 0; } #if PDTHREADS pthread_mutex_destroy(&INTER->i_mutex); #endif freebytes(inter, sizeof(*inter)); } void s_inter_freepdinstance(void) { s_inter_free(INTER); } #if PDTHREADS #ifdef PDINSTANCE static pthread_rwlock_t sys_rwlock = PTHREAD_RWLOCK_INITIALIZER; #else /* PDINSTANCE */ static pthread_mutex_t sys_mutex = PTHREAD_MUTEX_INITIALIZER; #endif /* PDINSTANCE */ #endif /* PDTHREADS */ #if PDTHREADS /* routines to lock and unlock Pd's global class structure or list of Pd instances. These are called internally within Pd when creating classes, adding methods to them, or creating or freeing Pd instances. They should probably not be called from outside Pd. They should be called at a point where the current instance of Pd is currently locked via sys_lock() below; this gains read access to the class and instance lists which must be released for the write-lock to be available. */ void pd_globallock(void) { #ifdef PDINSTANCE if (!pd_this->pd_islocked) bug("pd_globallock"); pthread_rwlock_unlock(&sys_rwlock); pthread_rwlock_wrlock(&sys_rwlock); #endif /* PDINSTANCE */ } void pd_globalunlock(void) { #ifdef PDINSTANCE pthread_rwlock_unlock(&sys_rwlock); pthread_rwlock_rdlock(&sys_rwlock); #endif /* PDINSTANCE */ } /* routines to lock/unlock a Pd instance for thread safety. Call pd_setinsance first. The "pd_this" variable can be written and read thread-safely as it is defined as per-thread storage. */ void sys_lock(void) { #ifdef PDINSTANCE pthread_mutex_lock(&INTER->i_mutex); pthread_rwlock_rdlock(&sys_rwlock); pd_this->pd_islocked = 1; #else pthread_mutex_lock(&sys_mutex); #endif } void sys_unlock(void) { #ifdef PDINSTANCE pd_this->pd_islocked = 0; pthread_rwlock_unlock(&sys_rwlock); pthread_mutex_unlock(&INTER->i_mutex); #else pthread_mutex_unlock(&sys_mutex); #endif } int sys_trylock(void) { #ifdef PDINSTANCE int ret; if (!(ret = pthread_mutex_trylock(&INTER->i_mutex))) { if (!(ret = pthread_rwlock_tryrdlock(&sys_rwlock))) return (0); else { pthread_mutex_unlock(&INTER->i_mutex); return (ret); } } else return (ret); #else return pthread_mutex_trylock(&sys_mutex); #endif } #else /* PDTHREADS */ #ifdef TEST_LOCKING /* run standalone Pd with this to find deadlocks */ static int amlocked; void sys_lock(void) { if (amlocked) bug("duplicate lock"); amlocked = 1; } void sys_unlock(void) { if (!amlocked) bug("duplicate unlock"); amlocked = 0; } #else void sys_lock(void) {} void sys_unlock(void) {} #endif void pd_globallock(void) {} void pd_globalunlock(void) {} #endif /* PDTHREADS */ ================================================ FILE: libs/libpd/pure-data/src/s_inter_gui.c ================================================ /* Copyright (c) 2022 IOhannes m zmölnig. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* Pd side of the Pd/Pd-gui interface. */ #include "m_pd.h" #include "s_stuff.h" #include #include /* NULL-terminated */ #define GUI_VMESS__END 0 /* use space to structure the format-string */ #define GUI_VMESS__IGNORE ' ' /* floating point number (automatically promoted to double) */ #define GUI_VMESS__FLOAT 'f' /* fixed point number (automatically promoted to int) */ #define GUI_VMESS__INT 'i' /* 0-terminated strings are untrusted and need escaping */ #define GUI_VMESS__STRING 's' /* the same goes for char arrays ("pascalstring") */ #define GUI_VMESS__PASCALSTRING 'p' /* rawstrings don't need encoding */ #define GUI_VMESS__RAWSTRING 'r' /* takes an int-representation of a 24bit RGB color */ #define GUI_VMESS__COLOR 'k' /* generic pointer; this is not actually used, and probably should stay that way */ #define GUI_VMESS__POINTER 'x' /* a Pd-object */ #define GUI_VMESS__OBJECT 'o' /* takes a Pd-message of the form "t_symbol*, int argc, t_atom*argv" */ #define GUI_VMESS__MESSAGE 'm' /* arrays are capitalized */ #define GUI_VMESS__ATOMS 'a' /* flat list of atoms */ #define GUI_VMESS__ATOMARRAY 'A' #define GUI_VMESS__FLOATARRAY 'F' #define GUI_VMESS__FLOATWORDS 'w' /* flat list of float words */ #define GUI_VMESS__FLOATWORDARRAY 'W' #define GUI_VMESS__STRINGARRAY 'S' #define GUI_VMESS__RAWSTRINGARRAY 'R' /* canvases */ #define GUI_VMESS__CANVAS 'c' #define GUI_VMESS__CANVASARRAY 'C' /* toplevel windows are legacy: this should go away (use 'c' instead) */ #define GUI_VMESS__WINDOW '^' /* more ideas for types (the IDs need discussion) * - float32 array ('1'), float64 array ('2'): think libpd * - symbols ('y'), symbolarray ('Y'): this is just a shorthand for 's', * so probably overkill * - t_word array ('W') * - a continuation char ('+'), to keep formating and values close together: * e.g. pdgui_vmess(..., "i+", 12, "i+", 13); */ static PERTHREAD char* s_escbuffer = 0; static PERTHREAD size_t s_esclength = 0; #ifndef GUI_ALLOCCHUNK # define GUI_ALLOCCHUNK 8192 #endif static char*get_escapebuffer(const char*s, int size) { size_t len = (size>0)?size:strlen(s); /* worst case needs escaping each character; AND a terminating \0... */ len = 2*len + 1; if (len > s_esclength) { freebytes(s_escbuffer, s_esclength); s_esclength = GUI_ALLOCCHUNK*(1+len/GUI_ALLOCCHUNK); s_escbuffer = getbytes(s_esclength); } return s_escbuffer; } static const char* str_escape(const char*s, int size) { if(!s) return s; if (!get_escapebuffer(s, size)) return 0; return pdgui_strnescape(s_escbuffer, s_esclength, s, size); } typedef struct _val { int type; int size; const char* string; union { double d; int i; const void*p; } value; } t_val; //#define DEBUGME /* constructs a string that can be passed on to sys_gui() */ #ifdef DEBUGME static void print_val(t_val v) { char str[80]; dprintf(2, "value[%c] ", v.type); switch (v.type) { case GUI_VMESS__ATOMS: atom_string(v.value.p, str, sizeof(str)); dprintf(2, "(atom)%s", str); break; case GUI_VMESS__FLOAT: dprintf(2, "(float)%f", v.value.d); break; case GUI_VMESS__INT: dprintf(2, "(int)%d", v.value.i); break; case GUI_VMESS__COLOR: dprintf(2, "(color)#%06x", v.value.i & 0xFFFFFF); break; case GUI_VMESS__STRING: dprintf(2, "(string)\"%s\"", v.value.p); break; case GUI_VMESS__PASCALSTRING: dprintf(2, "(pascalstring)\"%.*s\"", v.size, v.value.p); break; case GUI_VMESS__RAWSTRING: dprintf(2, "(rawstring)\"%s\"", v.value.p); break; case GUI_VMESS__WINDOW: case GUI_VMESS__CANVAS: dprintf(2, "(glist)%p", v.value.p); /* estimate the size required for the array */ break; case GUI_VMESS__ATOMARRAY: case GUI_VMESS__FLOATARRAY: case GUI_VMESS__FLOATWORDS: case GUI_VMESS__FLOATWORDARRAY: case GUI_VMESS__STRINGARRAY: case GUI_VMESS__RAWSTRINGARRAY: case GUI_VMESS__POINTER: case GUI_VMESS__OBJECT: case GUI_VMESS__CANVASARRAY: dprintf(2, "%dx @%p", v.size, v.value.p); /* estimate the size required for the array */ break; case GUI_VMESS__MESSAGE: dprintf(2, "(message)%s %d@%p", v.string, v.size, v.value.p); break; default: break; } dprintf(2, "\n"); } #else static void print_val(t_val v) { ; } int dprintf(int fd, const char* format, ...) { return -1; } #endif static void sendatoms(int argc, t_atom*argv, int raw) { int i; for(i=0; ia_type) { default: break; case A_FLOAT: /* HACK: currently ATOMARRAY is only used for setting fonts * and Tcl/Tk is picky when it receives non-integers as fontsize */ sys_vgui("%g ", atom_getfloat(a)); break; case A_DOLLAR: if(raw) sys_vgui("$%d ", a->a_w.w_index); else sys_vgui("{$%d} ", a->a_w.w_index); break; case A_DOLLSYM: case A_SYMBOL: if(raw) sys_vgui("%s ", a->a_w.w_symbol->s_name); else sys_vgui("{%s} ", str_escape(a->a_w.w_symbol->s_name, 0)); break; case A_POINTER: sys_vgui("%p ", a->a_w.w_gpointer); break; case A_SEMI: sys_vgui("\\; "); break; case A_COMMA: if (raw) sys_vgui(", "); else sys_vgui("{,} "); break; } } } static int addmess(const t_val *v) { int i; char escbuf[MAXPDSTRING]; //dprintf(2, "add-message: "); print_val(v); switch (v->type) { case GUI_VMESS__IGNORE: break; case GUI_VMESS__FLOAT: sys_vgui("%g", v->value.d); break; case GUI_VMESS__INT: sys_vgui("%d", v->value.i); break; case GUI_VMESS__COLOR: sys_vgui("#%06x", v->value.i & 0xFFFFFF); break; case GUI_VMESS__RAWSTRING: sys_vgui("%s", v->value.p); break; case GUI_VMESS__STRING: sys_vgui("{%s}", str_escape(v->value.p, 0)); break; case GUI_VMESS__PASCALSTRING: sys_vgui("{%s}", str_escape(v->value.p, v->size)); break; case GUI_VMESS__POINTER: case GUI_VMESS__OBJECT: sys_vgui("%p", v->value.p); break; case GUI_VMESS__MESSAGE: sys_vgui("{"); if (v->string) sys_vgui("%s ", v->string); else ; sendatoms(v->size, (t_atom*)v->value.p, 1); sys_vgui("}"); break; case GUI_VMESS__WINDOW: sys_vgui(".x%lx", v->value.p); break; case GUI_VMESS__CANVAS: sys_vgui(".x%lx.c", v->value.p); break; case GUI_VMESS__CANVASARRAY: { const t_canvas**data = (const t_canvas**)v->value.p; sys_vgui("{"); for(i=0; isize; i++) sys_vgui(".x%lx.c ", data[i]); sys_vgui("}"); break; } case GUI_VMESS__FLOATARRAY: { const t_float*data = (const t_float*)v->value.p; sys_vgui("{"); for(i=0; isize; i++) sys_vgui("%f ", *data++); sys_vgui("}"); break; } case GUI_VMESS__FLOATWORDS: case GUI_VMESS__FLOATWORDARRAY: { const t_word*data = (const t_word*)v->value.p; if (GUI_VMESS__FLOATWORDARRAY == v->type) sys_vgui("{"); for(i=0; isize; i++) sys_vgui("%g ", data[i].w_float); if (GUI_VMESS__FLOATWORDARRAY == v->type) sys_vgui("}"); break; } case GUI_VMESS__RAWSTRINGARRAY: case GUI_VMESS__STRINGARRAY: { const char**data = (const char**)v->value.p; sys_vgui("{"); for(i=0; isize; i++) { const char*s=data[i]; if (GUI_VMESS__RAWSTRINGARRAY == v->type) sys_vgui("%s ", s); else sys_vgui("{%s} ", str_escape(s, 0)); } sys_vgui("}"); break; } case GUI_VMESS__ATOMS: case GUI_VMESS__ATOMARRAY: { if (GUI_VMESS__ATOMARRAY == v->type) sys_vgui("{"); sendatoms(v->size, (t_atom*)v->value.p, 0); if (GUI_VMESS__ATOMARRAY == v->type) sys_vgui("}"); break; } default: return 1; } return 0; } static int va2value(const char fmt, va_list *args, t_val*v) { int result = 1; v->type = fmt; v->size = 1; switch (fmt) { case GUI_VMESS__IGNORE: /* */ case '\n': case '\t': /* other whitespace */ v->type = GUI_VMESS__IGNORE; break; case GUI_VMESS__ATOMS: v->size = va_arg(*args, int); v->value.p = va_arg(*args, t_atom*); break; case GUI_VMESS__FLOAT: v->value.d = va_arg(*args, double); break; case GUI_VMESS__INT: case GUI_VMESS__COLOR: v->value.i = va_arg(*args, int); break; case GUI_VMESS__RAWSTRING: case GUI_VMESS__STRING: case GUI_VMESS__OBJECT: case GUI_VMESS__POINTER: case GUI_VMESS__WINDOW: case GUI_VMESS__CANVAS: v->value.p = va_arg(*args, void*); break; case GUI_VMESS__ATOMARRAY: case GUI_VMESS__CANVASARRAY: case GUI_VMESS__FLOATARRAY: case GUI_VMESS__FLOATWORDS: case GUI_VMESS__FLOATWORDARRAY: case GUI_VMESS__STRINGARRAY: case GUI_VMESS__RAWSTRINGARRAY: case GUI_VMESS__PASCALSTRING: v->size = va_arg(*args, int); v->value.p = va_arg(*args, void*); break; case GUI_VMESS__MESSAGE: { t_symbol*s = va_arg(*args, t_symbol*); v->string = s?s->s_name:0; v->size = va_arg(*args, int); v->value.p = va_arg(*args, void*); break; } default: result = v->size = 0; fprintf(stderr, "pdgui_vmess: unknown type-ID %d ('%c')\n", fmt, fmt); break; } return result; } void pdgui_vamess(const char* message, const char* format, va_list args_) { const char* fmt; char* buf; t_val v; va_list args; v.type = GUI_VMESS__RAWSTRING; v.size = 1; v.value.p = message; if(message) { addmess(&v); sys_vgui("%s", " "); } va_copy(args, args_); /* iterate over the format-string and add elements */ for(fmt = format; *fmt; fmt++) { if(va2value(*fmt, &args, &v) < 1) continue; addmess(&v); if(GUI_VMESS__IGNORE != v.type) sys_vgui("%s", " "); } va_end(args); } void pdgui_endmess(void) { t_val v; v.type = GUI_VMESS__RAWSTRING; v.size = 1; v.value.p = ";\n"; addmess(&v); } /* constructs a string that can be passed on to sys_gui() */ /* TODO: shouldn't this have a pointer to t_pdinstance? */ void pdgui_vmess(const char* message, const char* format, ...) { va_list args; if (!sys_havegui())return; if(!format) { if (message) sys_vgui("%s;\n", message); return; } va_start(args, format); pdgui_vamess(message, format, args); va_end(args); pdgui_endmess(); } ================================================ FILE: libs/libpd/pure-data/src/s_loader.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ #if !defined (HAVE_LIBDL) && HAVE_DLOPEN # define HAVE_LIBDL 1 #endif #if HAVE_LIBDL # include #endif #ifdef HAVE_UNISTD_H #include #include #include #endif #ifdef _WIN32 #include #include #endif #ifdef __APPLE__ #include #endif #include #include "m_pd.h" #include "s_stuff.h" #include #include #include "m_private_utils.h" #ifdef _MSC_VER /* This is only for Microsoft's compiler, not cygwin, e.g. */ #define stat _stat #endif typedef void (*t_xxx)(void); /* naming convention for externs. The names are kept distinct for those who wish to make "fat" externs compiled for many platforms. Less specific fallbacks are provided, primarily for back-compatibility; these suffice if you are building a package which will run with a single set of compiled objects. The specific name is the letter b, l, d, or m for BSD, linux, darwin, or microsoft, followed by a more specific string, either "fat" for a fat binary or an indication of the instruction set. */ #ifdef __APPLE__ # define FAT_BINARIES 1 #endif #define STR(s) #s #define STRINGIFY(s) STR(s) #if defined(__x86_64__) || defined(_M_X64) # define ARCHEXT "amd64" #elif defined(__i386__) || defined(_M_IX86) # define ARCHEXT "i386" #elif defined(__arm__) # define ARCHEXT "arm" #elif defined(__aarch64__) # define ARCHEXT "arm64" #elif defined(__ppc__) # define ARCHEXT "ppc" #endif #ifdef ARCHEXT #define ARCHDLLEXT(prefix) prefix ARCHEXT , #else #define ARCHDLLEXT(prefix) #endif #if defined(_WIN32) || defined(__CYGWIN__) # define SYSTEMEXT ".dll" #else # define SYSTEMEXT ".so" #endif static const char*sys_dllextent_base[] = { #if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__GNU__) ARCHDLLEXT(".l_") # if defined(__x86_64__) || defined(_M_X64) ".l_ia64", /* incorrect but probably in wide use */ # endif ".pd_linux", #elif defined(__APPLE__) ARCHDLLEXT(".d_") ".d_fat", ".pd_darwin", #elif defined(_WIN32) || defined(__CYGWIN__) ARCHDLLEXT(".m_") SYSTEMEXT, #endif 0 }; static const char**sys_dllextent = 0; static size_t num_dllextents = 0; static void add_dllextension(const char*ext) { const char**extensions; if(ext) { /* prevent duplicate entries */ int i; for(i=0; i 0) add_dllextension(ext); else { freebytes(ext, MAXPDSTRING); ext = 0; } return ext; } /* get an array of dll-extensions */ const char**sys_get_dllextensions(void) { if(!sys_dllextent) { const char *extraext = 0; #if defined EXTERNAL_EXTENSION do { /* the EXTERNAL_EXTENSION might be surrounded by single-quotes * to prevent macro-expansion within the macro * if so, get rid of them */ unsigned int i,j; static char extern_extension[MAXPDSTRING]; strcpy(extern_extension, STRINGIFY(EXTERNAL_EXTENSION)); extern_extension[MAXPDSTRING-1] = 0; for(i=0,j=0; ill_next) if (ll->ll_name == s) return (1); return (0); } /* add to list of loaded modules */ void sys_putonloadlist(const char *classname) { t_loadlist *ll = (t_loadlist *)getbytes(sizeof(*ll)); ll->ll_name = gensym(classname); ll->ll_next = sys_loaded; sys_loaded = ll; /* post("put on list %s", classname); */ } void class_set_extern_dir(t_symbol *s); static int sys_do_load_abs(t_canvas *canvas, const char *objectname, const char *path); static int sys_do_load_lib_from_file(int fd, const char*objectname, const char*dirbuf, const char*nameptr, const char*symname) { char filename[MAXPDSTRING]; t_xxx makeout = NULL; #ifdef _WIN32 HINSTANCE dlobj; #else void*dlobj = NULL; #endif /* close dangling filedescriptor */ close(fd); /* attempt to open the library and call the setup function */ class_set_extern_dir(gensym(dirbuf)); /* rebuild the absolute pathname */ strncpy(filename, dirbuf, MAXPDSTRING); filename[MAXPDSTRING-2] = 0; strcat(filename, "/"); strncat(filename, nameptr, MAXPDSTRING-strlen(filename)); filename[MAXPDSTRING-1] = 0; #ifdef _WIN32 { char dirname[MAXPDSTRING], *s, *basename; sys_bashfilename(filename, filename); /* set the dirname as DllDirectory, meaning in the path for loading other DLLs so that dependent libraries can be included in the same folder as the external. SetDllDirectory() needs a minimum supported version of Windows XP SP1 for SetDllDirectory, so WINVER must be 0x0502 */ strncpy(dirname, filename, MAXPDSTRING); s = strrchr(dirname, '\\'); basename = s; if (s && *s) *s = '\0'; if (!SetDllDirectory(dirname)) pd_error(0, "could not set '%s' as DllDirectory(), '%s' might not load.", dirname, basename); /* now load the DLL for the external */ dlobj = LoadLibrary(filename); if (!dlobj) { wchar_t wbuf[MAXPDSTRING]; char buf[MAXPDSTRING]; DWORD count, err = GetLastError(); count = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, err, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), wbuf, MAXPDSTRING, NULL); if (!count || !WideCharToMultiByte(CP_UTF8, 0, wbuf, count+1, buf, MAXPDSTRING, 0, 0)) *buf = '\0'; pd_error(0, "%s: %s (%d)", filename, buf, err); } else { makeout = (t_xxx)GetProcAddress(dlobj, symname); if (!makeout) makeout = (t_xxx)GetProcAddress(dlobj, "setup"); } SetDllDirectory(NULL); /* reset DLL dir to nothing */ } #elif defined(HAVE_LIBDL) { dlobj = dlopen(filename, RTLD_NOW | RTLD_GLOBAL); if (!dlobj) { pd_error(0, "%s:%s", filename, dlerror()); } else { makeout = (t_xxx)dlsym(dlobj, symname); if(!makeout) makeout = (t_xxx)dlsym(dlobj, "setup"); } } #else #warning "No dynamic loading mechanism specified, \ libdl or WIN32 required for loading externals!" #endif if(makeout) (*makeout)(); else if (dlobj) pd_error(0, "load_object: Symbol \"%s\" not found in \"%s\"", symname, filename); class_set_extern_dir(&s_); return (makeout)?1:0; } static int sys_do_load_lib(t_canvas *canvas, const char *objectname, const char *path) { char symname[MAXPDSTRING], filename[MAXPDSTRING], dirbuf[MAXPDSTRING], *nameptr; const char**dllextent; const char *classname, *cnameptr; void *dlobj; t_xxx makeout = NULL; int i, hexmunge = 0, fd; /* NULL-path is only used as a last resort, but we have already tried all paths */ if(!path)return (0); if ((classname = strrchr(objectname, '/'))) classname++; else classname = objectname; for (i = 0, cnameptr = classname; i < MAXPDSTRING-7 && *cnameptr; cnameptr++) { char c = *cnameptr; if ((c>='0' && c<='9') || (c>='A' && c<='Z')|| (c>='a' && c<='z' )|| c == '_') { symname[i] = c; i++; } /* trailing tilde becomes "_tilde" */ else if (c == '~' && cnameptr[1] == 0) { strcpy(symname+i, "_tilde"); i += strlen(symname+i); } else /* anything you can't put in a C symbol is sprintf'ed in hex */ { sprintf(symname+i, "0x%02x", c); i += strlen(symname+i); hexmunge = 1; } } symname[i] = 0; if (hexmunge) { memmove(symname+6, symname, strlen(symname)+1); strncpy(symname, "setup_", 6); } else strcat(symname, "_setup"); #if 0 fprintf(stderr, "lib: %s\n", classname); #endif /* try looking in the path for (objectname).(sys_dllextent) ... */ for(dllextent=sys_get_dllextensions(); *dllextent; dllextent++) { if ((fd = sys_trytoopenone(path, objectname, *dllextent, dirbuf, &nameptr, MAXPDSTRING, 1)) >= 0) if(sys_do_load_lib_from_file(fd, objectname, dirbuf, nameptr, symname)) return 1; } /* next try (objectname)/(classname).(sys_dllextent) ... */ strncpy(filename, objectname, MAXPDSTRING); filename[MAXPDSTRING-2] = 0; strcat(filename, "/"); strncat(filename, classname, MAXPDSTRING-strlen(filename)); filename[MAXPDSTRING-1] = 0; for(dllextent=sys_get_dllextensions(); *dllextent; dllextent++) { if ((fd = sys_trytoopenone(path, filename, *dllextent, dirbuf, &nameptr, MAXPDSTRING, 1)) >= 0) if(sys_do_load_lib_from_file(fd, objectname, dirbuf, nameptr, symname)) return 1; } #ifdef ANDROID /* Android libs have a 'lib' prefix, '.so' suffix and don't allow ~ */ char libname[MAXPDSTRING] = "lib"; strncat(libname, objectname, MAXPDSTRING - 4); int len = strlen(libname); if (libname[len-1] == '~' && len < MAXPDSTRING - 6) { strcpy(libname+len-1, "_tilde"); } if ((fd = sys_trytoopenone(path, libname, ".so", dirbuf, &nameptr, MAXPDSTRING, 1)) >= 0) if(sys_do_load_lib_from_file(fd, objectname, dirbuf, nameptr, symname)) return 1; #endif return (0); } /* linked list of loaders */ typedef struct loader_queue { loader_t loader; struct loader_queue *next; } loader_queue_t; static loader_queue_t loaders = {sys_do_load_lib, NULL}; /* register class loader function */ void sys_register_loader(loader_t loader) { loader_queue_t *q = &loaders; while (1) { if (q->loader == loader) /* already loaded - nothing to do */ return; else if (q->next) q = q->next; else { q->next = (loader_queue_t *)getbytes(sizeof(loader_queue_t)); q->next->loader = loader; q->next->next = NULL; break; } } } #include "g_canvas.h" /* the data passed to the iter-function */ struct _loadlib_data { t_canvas *canvas; const char *classname; int ok; }; int sys_loadlib_iter(const char *path, struct _loadlib_data *data) { int ok = 0; loader_queue_t *q; for(q = &loaders; q; q = q->next) if ((ok = q->loader(data->canvas, data->classname, path))) break; /* if all loaders failed, try to load as abstraction */ if (!ok) ok = sys_do_load_abs(data->canvas, data->classname, path); data->ok = ok; return (ok == 0); } int sys_load_lib(t_canvas *canvas, const char *classname) { int dspstate = canvas_suspend_dsp(); struct _loadlib_data data; data.canvas = canvas; data.ok = 0; if (sys_onloadlist(classname)) return (1); /* if lib is already loaded, dismiss. */ /* if classname is absolute, try this first */ if (sys_isabsolutepath(classname)) { /* this is just copied from sys_open_absolute() LATER avoid code duplication */ char dirbuf[MAXPDSTRING], *z = strrchr(classname, '/'); int dirlen; if (!z) return (0); dirlen = (int)(z - classname); if (dirlen > MAXPDSTRING-1) dirlen = MAXPDSTRING-1; strncpy(dirbuf, classname, dirlen); dirbuf[dirlen] = 0; data.classname=classname+(dirlen+1); sys_loadlib_iter(dirbuf, &data); } data.classname = classname; if(!data.ok && !sys_isabsolutepath(classname)) /* don't iterate if classname is absolute */ canvas_path_iterate(canvas, (t_canvas_path_iterator)sys_loadlib_iter, &data); /* if loaders failed so far, we try a last time without a PATH * let the loaders search wherever they want */ if (!data.ok) sys_loadlib_iter(0, &data); if(data.ok) sys_putonloadlist(classname); canvas_resume_dsp(dspstate); return data.ok; } int sys_run_scheduler(const char *externalschedlibname, const char *sys_extraflagsstring) { typedef int (*t_externalschedlibmain)(const char *); t_externalschedlibmain externalmainfunc; char filename[MAXPDSTRING]; const char**dllextent; for(dllextent=sys_get_dllextensions(); *dllextent; dllextent++) { struct stat statbuf; snprintf(filename, sizeof(filename), "%s%s", externalschedlibname, *dllextent); sys_bashfilename(filename, filename); if(!stat(filename, &statbuf)) break; } #ifdef _WIN32 { HINSTANCE ntdll = LoadLibrary(filename); if (!ntdll) { fprintf(stderr, "%s: couldn't load external scheduler\n", filename); pd_error(0, "%s: couldn't load external scheduler", filename); return (1); } externalmainfunc = (t_externalschedlibmain)GetProcAddress(ntdll, "pd_extern_sched"); if (!externalmainfunc) externalmainfunc = (t_externalschedlibmain)GetProcAddress(ntdll, "main"); } #elif HAVE_LIBDL { void *dlobj; dlobj = dlopen(filename, RTLD_NOW | RTLD_GLOBAL); if (!dlobj) { pd_error(0, "%s: %s", filename, dlerror()); fprintf(stderr, "dlopen failed for %s: %s\n", filename, dlerror()); return (1); } externalmainfunc = (t_externalschedlibmain)dlsym(dlobj, "pd_extern_sched"); } #else return (0); #endif if (externalmainfunc) return((*externalmainfunc)(sys_extraflagsstring)); else { fprintf(stderr, "%s: couldn't find pd_extern_sched() or main()\n", filename); return (0); } } /* abstraction loading */ void canvas_popabstraction(t_canvas *x); int pd_setloadingabstraction(t_symbol *sym); static t_pd *do_create_abstraction(t_symbol*s, int argc, t_atom *argv) { /* * TODO: check if the there is a binbuf cached for and use that instead. We'll have to invalidate the cache once we are done (either with a clock_delay(0) or something else) */ if (!pd_setloadingabstraction(s)) { const char *objectname = s->s_name; char dirbuf[MAXPDSTRING], classslashclass[MAXPDSTRING], *nameptr; t_glist *glist = (t_glist *)canvas_getcurrent(); t_canvas *canvas = (t_canvas*)glist_getcanvas(glist); int fd = -1; t_pd *was = s__X.s_thing; snprintf(classslashclass, MAXPDSTRING, "%s/%s", objectname, objectname); if ((fd = canvas_open(canvas, objectname, ".pd", dirbuf, &nameptr, MAXPDSTRING, 0)) >= 0 || (fd = canvas_open(canvas, objectname, ".pat", dirbuf, &nameptr, MAXPDSTRING, 0)) >= 0 || (fd = canvas_open(canvas, classslashclass, ".pd", dirbuf, &nameptr, MAXPDSTRING, 0)) >= 0) { close(fd); canvas_setargs(argc, argv); binbuf_evalfile(gensym(nameptr), gensym(dirbuf)); if (s__X.s_thing && was != s__X.s_thing) canvas_popabstraction((t_canvas *)(s__X.s_thing)); else s__X.s_thing = was; canvas_setargs(0, 0); return (pd_this->pd_newest); } /* otherwise we couldn't do it; just return 0 */ } else pd_error(0, "%s: can't load abstraction within itself\n", s->s_name); pd_this->pd_newest = 0; return (0); } /* search for abstraction; register a creator if found */ static int sys_do_load_abs(t_canvas *canvas, const char *objectname, const char *path) { int fd; static t_gobj*abstraction_classes = 0; char dirbuf[MAXPDSTRING], classslashclass[MAXPDSTRING], *nameptr; /* NULL-path is only used as a last resort, but we have already tried all paths */ if (!path) return (0); snprintf(classslashclass, MAXPDSTRING, "%s/%s", objectname, objectname); if ((fd = sys_trytoopenone(path, objectname, ".pd", dirbuf, &nameptr, MAXPDSTRING, 1)) >= 0 || (fd = sys_trytoopenone(path, objectname, ".pat", dirbuf, &nameptr, MAXPDSTRING, 1)) >= 0 || (fd = sys_trytoopenone(path, classslashclass, ".pd", dirbuf, &nameptr, MAXPDSTRING, 1)) >= 0) { t_class*c=0; close(fd); /* found an abstraction, now register it as a new pseudo-class */ class_set_extern_dir(gensym(dirbuf)); if((c=class_new(gensym(objectname), (t_newmethod)do_create_abstraction, 0, 0, 0, A_GIMME, 0))) { /* store away the newly created class, maybe we will need it one day */ t_gobj*absclass=0; absclass=t_getbytes(sizeof(*absclass)); absclass->g_pd=c; absclass->g_next=abstraction_classes; abstraction_classes=absclass; } class_set_extern_dir(&s_); return (1); } return (0); } ================================================ FILE: libs/libpd/pure-data/src/s_main.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette and others. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ #include "m_pd.h" #include "m_imp.h" #include "s_stuff.h" #include #include #include #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #ifdef _WIN32 #include #include #include #endif #ifdef __APPLE__ #include // for _NSGetExecutablePath #endif #include "m_private_utils.h" #define stringify(s) str(s) #define str(s) #s char *pd_version = "Pd-" stringify(PD_MAJOR_VERSION) "." \ stringify(PD_MINOR_VERSION) "." stringify(PD_BUGFIX_VERSION) "\ (" stringify(PD_TEST_VERSION) ")"; char pd_compiletime[] = __TIME__; char pd_compiledate[] = __DATE__; void pd_init(void); int sys_argparse(int argc, const char **argv); void sys_findprogdir(const char *progname); void sys_setsignalhandlers(void); int sys_startgui(const char *guipath); void sys_setrealtime(const char *guipath); int m_mainloop(void); int m_batchmain(void); void sys_addhelppath(char *p); #ifdef USEAPI_ALSA void alsa_adddev(const char *name); #endif int sys_oktoloadfiles(int done); int sys_debuglevel; int sys_verbose; int sys_noloadbang; static int sys_dontstartgui; int sys_hipriority = -1; /* -1 = not specified; 0 = no; 1 = yes */ int sys_guisetportnumber; /* if started from the GUI, this is the port # */ int sys_nosleep = 0; /* skip all "sleep" calls and spin instead */ int sys_defeatrt; /* flag to cancel real-time */ t_symbol *sys_flags; /* more command-line flags */ const char *sys_guicmd; t_symbol *sys_libdir; typedef struct _patchlist { struct _patchlist *pl_next; char *pl_file; char *pl_args; } t_patchlist; static t_patchlist *sys_openlist; static t_namelist *sys_messagelist; static int sys_version; int sys_oldtclversion; /* hack to warn g_rtext.c about old text sel */ int sys_nmidiout = -1; int sys_nmidiin = -1; int sys_midiindevlist[MAXMIDIINDEV] = {1}; int sys_midioutdevlist[MAXMIDIOUTDEV] = {1}; #if __APPLE__ char sys_font[100] = "Menlo"; /* hack until DVSM bug is fixed on macOS 10.15+ */ char sys_fontweight[10] = "normal"; #else char sys_font[100] = "DejaVu Sans Mono"; char sys_fontweight[10] = "bold"; #endif static int sys_listplease; int sys_externalschedlib; char sys_externalschedlibname[MAXPDSTRING]; static int sys_batch; int sys_extraflags; char sys_extraflagsstring[MAXPDSTRING]; int sys_run_scheduler(const char *externalschedlibname, const char *sys_extraflagsstring); int sys_noautopatch; /* temporary hack to defeat new 0.42 editing */ t_sample *get_sys_soundout() { return STUFF->st_soundout; } t_sample *get_sys_soundin() { return STUFF->st_soundin; } double *get_sys_time_per_dsp_tick() { return &STUFF->st_time_per_dsp_tick; } int *get_sys_schedblocksize() { return &STUFF->st_schedblocksize; } double *get_sys_time() { return &pd_this->pd_systime; } t_float *get_sys_dacsr() { return &STUFF->st_dacsr; } int *get_sys_schedadvance() { return &sys_schedadvance; } t_namelist *sys_searchpath; /* so old versions of GEM might compile */ typedef struct _fontinfo { int fi_pointsize; int fi_width; int fi_height; } t_fontinfo; /* these give the nominal point size and maximum height of the characters in the six fonts. */ static t_fontinfo sys_fontspec[] = { {8, 5, 11}, {10, 6, 13}, {12, 7, 16}, {16, 10, 19}, {24, 14, 29}, {36, 22, 44}}; #define NFONT (sizeof(sys_fontspec)/sizeof(*sys_fontspec)) #define NZOOM 2 static t_fontinfo sys_gotfonts[NZOOM][NFONT]; /* here are the actual font size structs on msp's systems: MSW: font 8 5 9 8 5 11 font 10 7 13 10 6 13 font 12 9 16 14 8 16 font 16 10 20 16 10 18 font 24 15 25 16 10 18 font 36 25 42 36 22 41 linux: font 8 5 9 8 5 9 font 10 7 13 12 7 13 font 12 9 16 14 9 15 font 16 10 20 16 10 19 font 24 15 25 24 15 24 font 36 25 42 36 22 41 */ static int sys_findfont(int fontsize) { unsigned int i; t_fontinfo *fi; for (i = 0, fi = sys_fontspec; i < (NFONT-1); i++, fi++) if (fontsize < fi[1].fi_pointsize) return (i); return ((NFONT-1)); } int sys_nearestfontsize(int fontsize) { return (sys_fontspec[sys_findfont(fontsize)].fi_pointsize); } int sys_hostfontsize(int fontsize, int zoom) { zoom = (zoom < 1 ? 1 : (zoom > NZOOM ? NZOOM : zoom)); return (sys_gotfonts[zoom-1][sys_findfont(fontsize)].fi_pointsize); } int sys_zoomfontwidth(int fontsize, int zoomarg, int worstcase) { int zoom = (zoomarg < 1 ? 1 : (zoomarg > NZOOM ? NZOOM : zoomarg)), ret; if (worstcase) ret = zoom * sys_fontspec[sys_findfont(fontsize)].fi_width; else ret = sys_gotfonts[zoom-1][sys_findfont(fontsize)].fi_width; return (ret < 1 ? 1 : ret); } int sys_zoomfontheight(int fontsize, int zoomarg, int worstcase) { int zoom = (zoomarg < 1 ? 1 : (zoomarg > NZOOM ? NZOOM : zoomarg)), ret; if (worstcase) ret = (zoom * sys_fontspec[sys_findfont(fontsize)].fi_height); else ret = sys_gotfonts[zoom-1][sys_findfont(fontsize)].fi_height; return (ret < 1 ? 1 : ret); } int sys_fontwidth(int fontsize) /* old version for extern compatibility */ { return (sys_zoomfontwidth(fontsize, 1, 0)); } int sys_fontheight(int fontsize) { return (sys_zoomfontheight(fontsize, 1, 0)); } int sys_defaultfont; #define DEFAULTFONT 12 static t_patchlist * patchlist_append(t_patchlist *listwas, const char *files, const char *args) { t_namelist *nl, *nl2; nl = namelist_append_files(0, files); for (nl2 = nl; nl2; nl2 = nl2->nl_next) { t_patchlist *pl, *pl2; pl = (t_patchlist *)(getbytes(sizeof(*pl))); pl->pl_next = 0; pl->pl_file = (char *)getbytes(strlen(nl2->nl_string) + 1); strcpy(pl->pl_file, nl2->nl_string); if (args) { pl->pl_args = (char *)getbytes(strlen(args) + 1); strcpy(pl->pl_args, args); } else pl->pl_args = 0; if (!listwas) listwas = pl; else { for (pl2 = listwas; pl2->pl_next; pl2 = pl2->pl_next) ; pl2->pl_next = pl; } } namelist_free(nl); return (listwas); } static void patchlist_free(t_patchlist *list) { t_patchlist *pl, *pl2; for (pl = list; pl; pl = pl2) { pl2 = pl->pl_next; freebytes(pl->pl_file, strlen(pl->pl_file) + 1); if (pl->pl_args) freebytes(pl->pl_args, strlen(pl->pl_args) + 1); freebytes(pl, sizeof(*pl)); } } static void openit(const char *dirname, const char *filename, const char *args) { char dirbuf[MAXPDSTRING], *nameptr; int fd = open_via_path(dirname, filename, "", dirbuf, &nameptr, MAXPDSTRING, 0); if (fd >= 0) { close (fd); if (args && *args) { t_binbuf *b1 = binbuf_new(), *b2 = binbuf_new(); binbuf_text(b1, args, strlen(args)); binbuf_addbinbuf(b2, b1); // bash semis, commas and dollars canvas_setargs(binbuf_getnatom(b2), binbuf_getvec(b2)); binbuf_free(b1); binbuf_free(b2); } glob_evalfile(0, gensym(nameptr), gensym(dirbuf)); } else pd_error(0, "%s: can't open", filename); } /* this is called from the gui process. The first argument is the cwd, and succeeding args give the widths and heights of known fonts. We wait until these are known to open files and send messages specified on the command line. We ask the GUI to specify the "cwd" in case we don't have a local OS to get it from; for instance we could be some kind of RT embedded system. However, to really make this make sense we would have to implement open(), read(), etc, calls to be served somehow from the GUI too. */ void glob_initfromgui(void *dummy, t_symbol *s, int argc, t_atom *argv) { const char *cwd = atom_getsymbolarg(0, argc, argv)->s_name; t_patchlist *pl; t_namelist *nl; unsigned int i; int did_fontwarning = 0; int j; sys_oldtclversion = atom_getfloatarg(1, argc, argv); if (argc != 2 + 3 * NZOOM * NFONT) bug("glob_initfromgui"); for (j = 0; j < NZOOM; j++) for (i = 0; i < NFONT; i++) { int size = atom_getfloatarg(3 * (i + j * NFONT) + 2, argc, argv); int width = atom_getfloatarg(3 * (i + j * NFONT) + 3, argc, argv); int height = atom_getfloatarg(3 * (i + j * NFONT) + 4, argc, argv); if (!(size && width && height)) { size = (j+1)*sys_fontspec[i].fi_pointsize; width = (j+1)*sys_fontspec[i].fi_width; height = (j+1)*sys_fontspec[i].fi_height; if (!did_fontwarning) { logpost(NULL, PD_VERBOSE, "ignoring invalid font-metrics from GUI"); did_fontwarning = 1; } } sys_gotfonts[j][i].fi_pointsize = size; sys_gotfonts[j][i].fi_width = width; sys_gotfonts[j][i].fi_height = height; #if 0 fprintf(stderr, "font (%d %d %d)\n", sys_gotfonts[j][i].fi_pointsize, sys_gotfonts[j][i].fi_width, sys_gotfonts[j][i].fi_height); #endif } /* load dynamic libraries specified with "-lib" args */ if (sys_oktoloadfiles(0)) { for (nl = STUFF->st_externlist; nl; nl = nl->nl_next) if (!sys_load_lib(0, nl->nl_string)) post("%s: can't load library", nl->nl_string); sys_oktoloadfiles(1); } /* open patches specifies with "-open" args */ for (pl = sys_openlist; pl; pl = pl->pl_next) openit(cwd, pl->pl_file, pl->pl_args); patchlist_free(sys_openlist); sys_openlist = 0; /* send messages specified with "-send" args */ for (nl = sys_messagelist; nl; nl = nl->nl_next) { t_binbuf *b = binbuf_new(); binbuf_text(b, nl->nl_string, strlen(nl->nl_string)); binbuf_eval(b, 0, 0, 0); binbuf_free(b); } namelist_free(sys_messagelist); sys_messagelist = 0; } // font char metric triples: pointsize width(pixels) height(pixels) static int defaultfontshit[] = { 8, 5, 11, 10, 6, 13, 12, 7, 16, 16, 10, 19, 24, 14, 29, 36, 22, 44, 16, 10, 22, 20, 12, 26, 24, 14, 32, 32, 20, 38, 48, 28, 58, 72, 44, 88 }; // normal & zoomed (2x) #define NDEFAULTFONT (sizeof(defaultfontshit)/sizeof(*defaultfontshit)) static t_clock *sys_fakefromguiclk; int socket_init(void); static void sys_fakefromgui(void) { /* fake the GUI's message giving cwd and font sizes in case we aren't starting the gui. */ t_atom zz[NDEFAULTFONT+2]; int i; char buf[MAXPDSTRING]; #ifdef _WIN32 if (GetCurrentDirectory(MAXPDSTRING, buf) == 0) strcpy(buf, "."); #else if (!getcwd(buf, MAXPDSTRING)) strcpy(buf, "."); #endif SETSYMBOL(zz, gensym(buf)); SETFLOAT(zz+1, 0); for (i = 0; i < (int)NDEFAULTFONT; i++) SETFLOAT(zz+i+2, defaultfontshit[i]); glob_initfromgui(0, 0, 2+NDEFAULTFONT, zz); clock_free(sys_fakefromguiclk); } static void sys_afterargparse(void); static void sys_printusage(void); /* this is called from main() in s_entry.c */ int sys_main(int argc, const char **argv) { int i, noprefs; const char *prefsfile = ""; sys_externalschedlib = 0; sys_extraflags = 0; #ifdef PD_DEBUG fprintf(stderr, "Pd: COMPILED FOR DEBUGGING\n"); #endif /* use Win32 "binary" mode by default since we don't want the * translation that Win32 does by default */ #ifdef _WIN32 # ifdef _MSC_VER /* MS Visual Studio */ _set_fmode( _O_BINARY ); # else /* MinGW */ { #ifndef _fmode extern int _fmode; #endif _fmode = _O_BINARY; } # endif /* _MSC_VER */ #endif /* _WIN32 */ #ifndef _WIN32 /* long ago Pd used setuid to promote itself to real-time priority. Just in case anyone's installation script still makes it setuid, we complain to stderr and lose setuid here. */ if (getuid() != geteuid()) { fprintf(stderr, "warning: canceling setuid privilege\n"); if(setuid(getuid()) < 0) { /* sometimes this fails (which, according to 'man 2 setuid' is a * grave security error), in which case we bail out and quit. */ fprintf(stderr, "\n\nFATAL: could not cancel setuid privilege"); fprintf(stderr, "\nTo fix this, please remove the setuid flag from the Pd binary"); if(argc>0) { fprintf(stderr, "\ne.g. by running the following as root/superuser:"); fprintf(stderr, "\n chmod u-s '%s'", argv[0]); } fprintf(stderr, "\n\n"); perror("setuid"); return (1); } } #endif /* _WIN32 */ if (socket_init()) sys_sockerror("socket_init()"); pd_init(); /* start the message system */ sys_findprogdir(argv[0]); /* set sys_progname, guipath */ for (i = noprefs = 0; i < argc; i++) /* prescan ... */ { /* for prefs override */ if (!strcmp(argv[i], "-noprefs")) noprefs = 1; else if (!strcmp(argv[i], "-prefsfile") && i < argc-1) prefsfile = argv[i+1]; /* for external scheduler (to ignore audio api in sys_loadpreferences) */ else if (!strcmp(argv[i], "-schedlib") && i < argc-1) sys_externalschedlib = 1; else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help")) { sys_printusage(); return (1); } } if (!noprefs) /* load preferences before parsing args to allow ... */ sys_loadpreferences(prefsfile, 1); /* args to override prefs */ if (sys_argparse(argc-1, argv+1)) /* parse cmd line args */ return (1); if (sys_verbose || sys_version) fprintf(stderr, "%s compiled %s %s\n", pd_version, pd_compiletime, pd_compiledate); if (sys_verbose) fprintf(stderr, "float precision = %lu bits\n", sizeof(t_float)*8); if (sys_version) /* if we were just asked our version, exit here. */ { fflush(stderr); return (0); } sys_setsignalhandlers(); sys_afterargparse(); /* post-argparse settings */ if (sys_dontstartgui) clock_set((sys_fakefromguiclk = clock_new(0, (t_method)sys_fakefromgui)), 0); else if (sys_startgui(sys_libdir->s_name)) /* start the gui */ return (1); if (sys_hipriority) sys_setrealtime(sys_libdir->s_name); /* set desired process priority */ if (sys_externalschedlib) return (sys_run_scheduler(sys_externalschedlibname, sys_extraflagsstring)); else if (sys_batch) return (m_batchmain()); else { /* open audio and MIDI */ sys_reopen_midi(); if (audio_shouldkeepopen()) sys_reopen_audio(); /* run scheduler until it quits */ return (m_mainloop()); } } static char *(usagemessage[]) = { "usage: pd [-flags] [file]...\n", "\naudio configuration flags:\n", "-r -- specify sample rate\n", "-audioindev ... -- audio in devices; e.g., \"1,3\" for first and third\n", "-audiooutdev ... -- audio out devices (same)\n", "-audiodev ... -- specify input and output together\n", "-audioaddindev -- add an audio input device by name\n", "-audioaddoutdev -- add an audio output device by name\n", "-audioadddev -- add an audio input and output device by name\n", "-inchannels ... -- audio input channels (by device, like \"2\" or \"16,8\")\n", "-outchannels ... -- number of audio out channels (same)\n", "-channels ... -- specify both input and output channels\n", "-audiobuf -- specify size of audio buffer in msec\n", "-blocksize -- specify audio I/O block size in sample frames\n", "-sleepgrain -- specify number of milliseconds to sleep when idle\n", "-nodac -- suppress audio output\n", "-noadc -- suppress audio input\n", "-noaudio -- suppress audio input and output (-nosound is synonym) \n", "-callback -- use callbacks if possible\n", "-nocallback -- use polling-mode (true by default)\n", "-listdev -- list audio and MIDI devices\n", #ifdef USEAPI_OSS "-oss -- use OSS audio API\n", #endif #ifdef USEAPI_ALSA "-alsa -- use ALSA audio API\n", "-alsaadd -- add an ALSA device name to list\n", #endif #ifdef USEAPI_JACK "-jack -- use JACK audio API\n", "-jackname -- a name for your JACK client\n", "-nojackconnect -- do not automatically connect pd to the JACK graph\n", "-jackconnect -- automatically connect pd to the JACK graph [default]\n", #endif #ifdef USEAPI_PORTAUDIO #ifdef _WIN32 "-pa -- use Portaudio API (for ASIO or WASAPI)\n", "-asio -- synonym for -pa\n", #else "-pa -- use Portaudio API\n", #endif #endif #ifdef USEAPI_MMIO "-mmio -- use legacy MMIO audio API\n", #endif #ifdef USEAPI_AUDIOUNIT "-audiounit -- use Apple AudioUnit API\n", #endif #ifdef USEAPI_ESD "-esd -- use Enlightenment Sound Daemon (ESD) API\n", #endif " (default audio API for this platform: ", API_DEFSTRING, ")\n\n", "\nMIDI configuration flags:\n", "-midiindev ... -- midi in device list; e.g., \"1,3\" for first and third\n", "-midioutdev ... -- midi out device list, same format\n", "-mididev ... -- specify -midioutdev and -midiindev together\n", "-midiaddindev -- add a MIDI input device by name\n", "-midiaddoutdev -- add a MIDI output device by name\n", "-midiadddev -- add a MIDI input and output device by name\n", "-nomidiin -- suppress MIDI input\n", "-nomidiout -- suppress MIDI output\n", "-nomidi -- suppress MIDI input and output\n", #ifdef USEAPI_OSS "-ossmidi -- use OSS midi API\n", #endif #ifdef USEAPI_ALSA "-alsamidi -- use ALSA midi API\n", #endif "\nother flags:\n", "-path -- add to file search path\n", "-nostdpath -- don't search standard (\"extra\") directory\n", "-stdpath -- search standard directory (true by default)\n", "-helppath -- add to help file search path\n", "-open -- open file(s) on startup\n", "-open-with-args -- open file(s) on startup with arguments\n", "-lib -- load object library(s) (omit file extensions)\n", "-font-size -- specify default font size in points\n", "-font-face -- specify default font\n", "-font-weight -- specify default font weight (normal or bold)\n", "-verbose -- extra printout on startup and when searching for files\n", "-noverbose -- no extra printout\n", "-version -- don't run Pd; just print out which version it is \n", "-d -- specify debug level for inspecting the GUI communication\n", "-loadbang -- do not suppress all loadbangs (true by default)\n", "-noloadbang -- suppress all loadbangs\n", "-stderr -- send printout to standard error instead of GUI\n", "-nostderr -- send printout to GUI (true by default)\n", "-gui -- start GUI (true by default)\n", "-nogui -- suppress starting the GUI\n", "-guiport -- connect to pre-existing GUI over port \n", "-guicmd \"cmd...\" -- start alternative GUI program (e.g., remote via ssh)\n", "-send \"msg...\" -- send a message at startup, after patches are loaded\n", "-prefs -- load preferences on startup (true by default)\n", "-noprefs -- suppress loading preferences on startup\n", "-prefsfile -- load preferences from a file\n", #ifdef HAVE_UNISTD_H "-rt or -realtime -- use real-time priority\n", "-nrt -- don't use real-time priority\n", #endif "-sleep -- sleep when idle, don't spin (true by default)\n", "-nosleep -- spin, don't sleep (may lower latency on multi-CPUs)\n", "-schedlib -- plug in external scheduler (omit file extensions)\n", "-extraflags -- string argument to send schedlib\n", "-batch -- run off-line as a batch process\n", "-nobatch -- run interactively (true by default)\n", "-autopatch -- enable auto-patching to new objects (true by default)\n", "-noautopatch -- defeat auto-patching\n", "-compatibility -- set back-compatibility to version \n", }; static void sys_printusage(void) { unsigned int i; for (i = 0; i < sizeof(usagemessage)/sizeof(*usagemessage); i++) { fprintf(stderr, "%s", usagemessage[i]); fflush(stderr); } } /* parse a comma-separated numeric list, returning the number found */ static int sys_parsedevlist(int *np, int *vecp, int max, const char *str) { int n = 0; while (n < max) { if (! *str) break; else { char *endp; vecp[n] = (int)strtol(str, &endp, 10); if (endp == str) break; n++; if (! *endp) break; str = endp + 1; } } return (*np = n); } static int sys_getmultidevchannels(int n, int *devlist) { int sum = 0; if (n<0)return(-1); if (n==0)return 0; while(n--)sum+=*devlist++; return sum; } /* this routine tries to figure out where to find the auxiliary files Pd will need to run. This is either done by looking at the command line invocation for Pd, or if that fails, by consulting the variable INSTALL_PREFIX. In MSW, we don't try to use INSTALL_PREFIX. */ void sys_findprogdir(const char *progname) { char sbuf[MAXPDSTRING], sbuf2[MAXPDSTRING]; char *lastslash; #ifndef _WIN32 struct stat statbuf; #endif /* NOT _WIN32 */ /* find out by what string Pd was invoked; put answer in "sbuf". */ *sbuf2 = 0; #ifdef _WIN32 GetModuleFileName(NULL, sbuf2, sizeof(sbuf2)); sbuf2[MAXPDSTRING-1] = 0; #else /* !_WIN32 */ if(!*sbuf2) { ssize_t path_length = readlink("/proc/self/exe", sbuf2, sizeof(sbuf2)); if (path_length > 0 && path_length < MAXPDSTRING) sbuf2[path_length] = 0; } #endif /* _WIN32 */ #ifdef __APPLE__ if(!*sbuf2) { uint32_t size = sizeof(sbuf2); _NSGetExecutablePath(sbuf2, &size); } #endif /* ! __APPLE__ */ /* fallback to just using argv[0] */ if(!*sbuf2) strncpy(sbuf2, progname, MAXPDSTRING); sbuf2[MAXPDSTRING-1] = 0; sys_unbashfilename(sbuf2, sbuf); lastslash = strrchr(sbuf, '/'); if (lastslash) { /* bash last slash to zero so that sbuf is directory pd was in, e.g., ~/pd/bin */ *lastslash = 0; /* go back to the parent from there, e.g., ~/pd */ lastslash = strrchr(sbuf, '/'); if (lastslash) { strncpy(sbuf2, sbuf, lastslash-sbuf); sbuf2[lastslash-sbuf] = 0; } else strcpy(sbuf2, ".."); } else { /* no slashes found. Try INSTALL_PREFIX. */ #ifdef INSTALL_PREFIX strcpy(sbuf2, INSTALL_PREFIX); #else strcpy(sbuf2, "."); #endif } /* now we believe sbuf2 holds the parent directory of the directory pd was found in. We now want to infer the "lib" directory and the "gui" directory. In "simple" unix installations, the layout is .../bin/pd .../bin/pd-watchdog (etc) .../bin/pd-gui.tcl .../doc and in "complicated" unix installations, it's: .../bin/pd .../lib/pd/bin/pd-watchdog .../lib/pd/bin/pd-gui.tcl .../lib/pd/doc To decide which, we stat .../lib/pd; if that exists, we assume it's the complicated layout. In MSW, it's the "simple" layout, but "wish" is found in bin: .../bin/pd .../bin/wish80.exe .../doc */ #ifdef _WIN32 sys_libdir = gensym(sbuf2); #else strncpy(sbuf, sbuf2, MAXPDSTRING-30); sbuf[MAXPDSTRING-30] = 0; strcat(sbuf, "/lib/pd"); if (stat(sbuf, &statbuf) >= 0) { /* complicated layout: lib dir is the one we just stat-ed above */ sys_libdir = gensym(sbuf); } else { /* simple layout: lib dir is the parent */ sys_libdir = gensym(sbuf2); } #endif } int sys_argparse(int argc, const char **argv) { t_audiosettings as; /* get the current audio parameters. These are set by the preferences mechanism (sys_loadpreferences()) or else are the default. Overwrite them with any results of argument parsing, and store them again. */ sys_get_audio_settings(&as); while ((argc > 0) && **argv == '-') { /* audio flags */ if (!strcmp(*argv, "-r") && argc > 1 && sscanf(argv[1], "%d", &as.a_srate) >= 1) { argc -= 2; argv += 2; } else if (!strcmp(*argv, "-inchannels")) { if (argc < 2 || !sys_parsedevlist(&as.a_nchindev, as.a_chindevvec, MAXAUDIOINDEV, argv[1])) goto usage; argc -= 2; argv += 2; } else if (!strcmp(*argv, "-outchannels")) { if (argc < 2 || !sys_parsedevlist(&as.a_nchoutdev, as.a_choutdevvec, MAXAUDIOINDEV, argv[1])) goto usage; argc -= 2; argv += 2; } else if (!strcmp(*argv, "-channels")) { if (argc < 2 || !sys_parsedevlist(&as.a_nchindev, as.a_chindevvec, MAXAUDIOINDEV, argv[1]) || !sys_parsedevlist(&as.a_nchoutdev, as.a_choutdevvec, MAXAUDIOINDEV, argv[1])) goto usage; argc -= 2; argv += 2; } else if (!strcmp(*argv, "-soundbuf") || (!strcmp(*argv, "-audiobuf"))) { if (argc < 2) goto usage; as.a_advance = atoi(argv[1]); argc -= 2; argv += 2; } else if (!strcmp(*argv, "-callback")) { as.a_callback = 1; argc--; argv++; } else if (!strcmp(*argv, "-nocallback")) { as.a_callback = 0; argc--; argv++; } else if (!strcmp(*argv, "-blocksize")) { as.a_blocksize = atoi(argv[1]); argc -= 2; argv += 2; } else if (!strcmp(*argv, "-sleepgrain")) { if (argc < 2) goto usage; sys_sleepgrain = 1000 * atof(argv[1]); argc -= 2; argv += 2; } else if (!strcmp(*argv, "-nodac")) { as.a_noutdev = as.a_nchoutdev = 0; argc--; argv++; } else if (!strcmp(*argv, "-noadc")) { as.a_nindev = as.a_nchindev = 0; argc--; argv++; } else if (!strcmp(*argv, "-nosound") || !strcmp(*argv, "-noaudio")) { as.a_noutdev = as.a_nchoutdev = as.a_nindev = as.a_nchindev = 0; argc--; argv++; } #ifdef USEAPI_OSS else if (!strcmp(*argv, "-oss")) { as.a_api = API_OSS; argc--; argv++; } else if (!strcmp(*argv, "-ossmidi")) { sys_set_midi_api(API_OSS); argc--; argv++; } #else else if ((!strcmp(*argv, "-oss")) || (!strcmp(*argv, "-ossmidi"))) { fprintf(stderr, "Pd compiled without OSS-support, ignoring '%s' flag\n", *argv); argc--; argv++; } #endif #ifdef USEAPI_ALSA else if (!strcmp(*argv, "-alsa")) { as.a_api = API_ALSA; argc--; argv++; } else if (!strcmp(*argv, "-alsaadd")) { if (argc < 2) goto usage; as.a_api = API_ALSA; alsa_adddev(argv[1]); argc -= 2; argv +=2; } else if (!strcmp(*argv, "-alsamidi")) { sys_set_midi_api(API_ALSA); argc--; argv++; } #else else if ((!strcmp(*argv, "-alsa")) || (!strcmp(*argv, "-alsamidi"))) { fprintf(stderr, "Pd compiled without ALSA-support, ignoring '%s' flag\n", *argv); argc--; argv++; } else if (!strcmp(*argv, "-alsaadd")) { if (argc < 2) goto usage; fprintf(stderr, "Pd compiled without ALSA-support, ignoring '%s' flag\n", *argv); argc -= 2; argv +=2; } #endif #ifdef USEAPI_JACK else if (!strcmp(*argv, "-jack")) { as.a_api = API_JACK; argc--; argv++; } else if (!strcmp(*argv, "-nojackconnect")) { as.a_api = API_JACK; jack_autoconnect(0); argc--; argv++; } else if (!strcmp(*argv, "-jackconnect")) { as.a_api = API_JACK; jack_autoconnect(1); argc--; argv++; } else if (!strcmp(*argv, "-jackname")) { if (argc < 2) goto usage; as.a_api = API_JACK; jack_client_name(argv[1]); argc -= 2; argv +=2; } #else else if ((!strcmp(*argv, "-jack")) || (!strcmp(*argv, "-jackconnect")) || (!strcmp(*argv, "-nojackconnect"))) { fprintf(stderr, "Pd compiled without JACK-support, ignoring '%s' flag\n", *argv); argc--; argv++; } else if (!strcmp(*argv, "-jackname")) { if (argc < 2) goto usage; fprintf(stderr, "Pd compiled without JACK-support, ignoring '%s' flag\n", *argv); argc -= 2; argv +=2; } #endif #ifdef USEAPI_PORTAUDIO else if (!strcmp(*argv, "-pa") || !strcmp(*argv, "-portaudio") || !strcmp(*argv, "-asio")) { as.a_api = API_PORTAUDIO; argc--; argv++; } #else else if ((!strcmp(*argv, "-pa")) || (!strcmp(*argv, "-portaudio"))) { fprintf(stderr, "Pd compiled without PortAudio-support, ignoring '%s' flag\n", *argv); argc--; argv++; } else if (!strcmp(*argv, "-asio")) { fprintf(stderr, "Pd compiled without ASIO-support, ignoring '%s' flag\n", *argv); argc -= 2; argv +=2; } #endif #ifdef USEAPI_MMIO else if (!strcmp(*argv, "-mmio")) { as.a_api = API_MMIO; argc--; argv++; } #else else if (!strcmp(*argv, "-mmio")) { fprintf(stderr, "Pd compiled without MMIO-support, ignoring '%s' flag\n", *argv); argc--; argv++; } #endif #ifdef USEAPI_AUDIOUNIT else if (!strcmp(*argv, "-audiounit")) { as.a_api = API_AUDIOUNIT; argc--; argv++; } #else else if (!strcmp(*argv, "-audiounit")) { fprintf(stderr, "Pd compiled without AudioUnit-support, ignoring '%s' flag\n", *argv); argc--; argv++; } #endif #ifdef USEAPI_ESD else if (!strcmp(*argv, "-esd")) { as.a_api = API_ESD; argc--; argv++; } #else else if (!strcmp(*argv, "-esd")) { fprintf(stderr, "Pd compiled without ESD-support, ignoring '%s' flag\n", *argv); argc--; argv++; } #endif else if (!strcmp(*argv, "-listdev")) { sys_listplease = 1; argc--; argv++; } else if (!strcmp(*argv, "-soundindev") || !strcmp(*argv, "-audioindev")) { if (argc < 2 || !sys_parsedevlist(&as.a_nindev, as.a_indevvec, MAXAUDIOINDEV, argv[1])) goto usage; argc -= 2; argv += 2; } else if (!strcmp(*argv, "-soundoutdev") || !strcmp(*argv, "-audiooutdev")) { if (argc < 2 || !sys_parsedevlist(&as.a_noutdev, as.a_outdevvec, MAXAUDIOOUTDEV, argv[1])) goto usage; argc -= 2; argv += 2; } else if ((!strcmp(*argv, "-sounddev") || !strcmp(*argv, "-audiodev"))) { if (argc < 2 || !sys_parsedevlist(&as.a_nindev, as.a_indevvec, MAXAUDIOINDEV, argv[1]) || !sys_parsedevlist(&as.a_noutdev, as.a_outdevvec, MAXAUDIOOUTDEV, argv[1])) goto usage; argc -= 2; argv += 2; } else if (!strcmp(*argv, "-audioaddindev")) { if (argc < 2) goto usage; if (as.a_nindev < 0) as.a_nindev = 0; if (as.a_nindev < MAXAUDIOINDEV) { int devn = sys_audiodevnametonumber(0, argv[1]); if (devn < 0) fprintf(stderr, "Couldn't find audio input device: %s\n", argv[1]); else as.a_indevvec[as.a_nindev++] = devn + 1; } else fprintf(stderr, "number of audio devices limited to %d\n", MAXAUDIOINDEV); argc -= 2; argv += 2; } else if (!strcmp(*argv, "-audioaddoutdev")) { if (argc < 2) goto usage; if (as.a_noutdev < 0) as.a_noutdev = 0; if (as.a_noutdev < MAXAUDIOINDEV) { int devn = sys_audiodevnametonumber(1, argv[1]); if (devn < 0) fprintf(stderr, "Couldn't find audio output device: %s\n", argv[1]); else as.a_outdevvec[as.a_noutdev++] = devn + 1; } else fprintf(stderr, "number of audio devices limited to %d\n", MAXAUDIOINDEV); argc -= 2; argv += 2; } else if (!strcmp(*argv, "-audioadddev")) { if (argc < 2) goto usage; if (as.a_nindev < 0) as.a_nindev = 0; if (as.a_noutdev < 0) as.a_noutdev = 0; if (as.a_nindev < MAXAUDIOINDEV && as.a_noutdev < MAXAUDIOINDEV) { int devn = sys_audiodevnametonumber(0, argv[1]); if (devn < 0) fprintf(stderr, "Couldn't find audio input device: %s\n", argv[1]); else as.a_indevvec[as.a_nindev++] = devn + 1; devn = sys_audiodevnametonumber(1, argv[1]); if (devn < 0) fprintf(stderr, "Couldn't find audio output device: %s\n", argv[1]); else as.a_outdevvec[as.a_noutdev++] = devn + 1; } else fprintf(stderr, "number of audio devices limited to %d", MAXAUDIOINDEV); argc -= 2; argv += 2; } /* MIDI flags */ else if (!strcmp(*argv, "-nomidiin")) { sys_nmidiin = 0; argc--; argv++; } else if (!strcmp(*argv, "-nomidiout")) { sys_nmidiout = 0; argc--; argv++; } else if (!strcmp(*argv, "-nomidi")) { sys_nmidiin = sys_nmidiout = 0; argc--; argv++; } else if (!strcmp(*argv, "-midiindev")) { if (argc < 2) goto usage; sys_parsedevlist(&sys_nmidiin, sys_midiindevlist, MAXMIDIINDEV, argv[1]); if (!sys_nmidiin) goto usage; argc -= 2; argv += 2; } else if (!strcmp(*argv, "-midioutdev")) { if (argc < 2) goto usage; sys_parsedevlist(&sys_nmidiout, sys_midioutdevlist, MAXMIDIOUTDEV, argv[1]); if (!sys_nmidiout) goto usage; argc -= 2; argv += 2; } else if (!strcmp(*argv, "-mididev")) { if (argc < 2) goto usage; sys_parsedevlist(&sys_nmidiin, sys_midiindevlist, MAXMIDIINDEV, argv[1]); sys_parsedevlist(&sys_nmidiout, sys_midioutdevlist, MAXMIDIOUTDEV, argv[1]); if (!sys_nmidiout) goto usage; argc -= 2; argv += 2; } else if (!strcmp(*argv, "-midiaddindev")) { if (argc < 2) goto usage; if (sys_nmidiin < 0) sys_nmidiin = 0; if (sys_nmidiin < MAXMIDIINDEV) { int devn = sys_mididevnametonumber(0, argv[1]); if (devn < 0) fprintf(stderr, "Couldn't find MIDI input device: %s\n", argv[1]); else sys_midiindevlist[sys_nmidiin++] = devn + 1; } else fprintf(stderr, "number of MIDI devices limited to %d\n", MAXMIDIINDEV); argc -= 2; argv += 2; } else if (!strcmp(*argv, "-midiaddoutdev")) { if (argc < 2) goto usage; if (sys_nmidiout < 0) sys_nmidiout = 0; if (sys_nmidiout < MAXMIDIINDEV) { int devn = sys_mididevnametonumber(1, argv[1]); if (devn < 0) fprintf(stderr, "Couldn't find MIDI output device: %s\n", argv[1]); else sys_midioutdevlist[sys_nmidiout++] = devn + 1; } else fprintf(stderr, "number of MIDI devices limited to %d\n", MAXMIDIINDEV); argc -= 2; argv += 2; } else if (!strcmp(*argv, "-midiadddev")) { if (argc < 2) goto usage; if (sys_nmidiin < 0) sys_nmidiin = 0; if (sys_nmidiout < 0) sys_nmidiout = 0; if (sys_nmidiin < MAXMIDIINDEV && sys_nmidiout < MAXMIDIINDEV) { int devn = sys_mididevnametonumber(1, argv[1]); if (devn < 0) fprintf(stderr, "Couldn't find MIDI output device: %s\n", argv[1]); else sys_midioutdevlist[sys_nmidiin++] = devn + 1; devn = sys_mididevnametonumber(1, argv[1]); if (devn < 0) fprintf(stderr, "Couldn't find MIDI output device: %s\n", argv[1]); else sys_midioutdevlist[sys_nmidiout++] = devn + 1; } else fprintf(stderr, "number of MIDI devices limited to %d", MAXMIDIINDEV); argc -= 2; argv += 2; } /* other flags */ else if (!strcmp(*argv, "-path")) { if (argc < 2) goto usage; STUFF->st_temppath = namelist_append_files(STUFF->st_temppath, argv[1]); argc -= 2; argv += 2; } else if (!strcmp(*argv, "-nostdpath")) { sys_usestdpath = 0; argc--; argv++; } else if (!strcmp(*argv, "-stdpath")) { sys_usestdpath = 1; argc--; argv++; } else if (!strcmp(*argv, "-helppath")) { if (argc < 2) goto usage; STUFF->st_helppath = namelist_append_files(STUFF->st_helppath, argv[1]); argc -= 2; argv += 2; } else if (!strcmp(*argv, "-open")) { if (argc < 2) goto usage; sys_openlist = patchlist_append(sys_openlist, argv[1], 0); argc -= 2; argv += 2; } else if (!strcmp(*argv, "-open-with-args")) { if (argc < 3) goto usage; sys_openlist = patchlist_append(sys_openlist, argv[1], argv[2]); argc -= 3; argv += 3; } else if (!strcmp(*argv, "-lib")) { if (argc < 2) goto usage; STUFF->st_externlist = namelist_append_files(STUFF->st_externlist, argv[1]); argc -= 2; argv += 2; } else if ((!strcmp(*argv, "-font-size") || !strcmp(*argv, "-font"))) { if (argc < 2) goto usage; sys_defaultfont = sys_nearestfontsize(atoi(argv[1])); argc -= 2; argv += 2; } else if ((!strcmp(*argv, "-font-face") || !strcmp(*argv, "-typeface"))) { if (argc < 2) goto usage; strncpy(sys_font,*(argv+1),sizeof(sys_font)-1); sys_font[sizeof(sys_font)-1] = 0; argc -= 2; argv += 2; } else if (!strcmp(*argv, "-font-weight")) { if (argc < 2) goto usage; strncpy(sys_fontweight,*(argv+1),sizeof(sys_fontweight)-1); sys_fontweight[sizeof(sys_fontweight)-1] = 0; argc -= 2; argv += 2; } else if (!strcmp(*argv, "-verbose")) { sys_verbose++; argc--; argv++; } else if (!strcmp(*argv, "-noverbose")) { sys_verbose=0; argc--; argv++; } else if (!strcmp(*argv, "-version")) { sys_version = 1; argc--; argv++; } else if (!strcmp(*argv, "-d") && argc > 1 && sscanf(argv[1], "%d", &sys_debuglevel) >= 1) { argc -= 2; argv += 2; } else if (!strcmp(*argv, "-loadbang")) { sys_noloadbang = 0; argc--; argv++; } else if (!strcmp(*argv, "-noloadbang")) { sys_noloadbang = 1; argc--; argv++; } else if (!strcmp(*argv, "-gui")) { sys_dontstartgui = 0; argc--; argv++; } else if (!strcmp(*argv, "-nogui")) { sys_dontstartgui = 1; argc--; argv++; } else if (!strcmp(*argv, "-guiport") && argc > 1 && sscanf(argv[1], "%d", &sys_guisetportnumber) >= 1) { argc -= 2; argv += 2; } else if (!strcmp(*argv, "-nostderr")) { sys_printtostderr = 0; argc--; argv++; } else if (!strcmp(*argv, "-stderr")) { sys_printtostderr = 1; argc--; argv++; } else if (!strcmp(*argv, "-guicmd")) { if (argc < 2) goto usage; sys_guicmd = argv[1]; argc -= 2; argv += 2; } else if (!strcmp(*argv, "-send")) { if (argc < 2) goto usage; sys_messagelist = namelist_append(sys_messagelist, argv[1], 1); argc -= 2; argv += 2; } else if (!strcmp(*argv, "-schedlib")) { if (argc < 2) goto usage; sys_externalschedlib = 1; strncpy(sys_externalschedlibname, argv[1], sizeof(sys_externalschedlibname) - 1); #ifndef __APPLE__ /* no audio I/O please, unless overwritten by later args. This is to circumvent a problem running pd~ subprocesses with -nogui; they would open an audio device before pdsched.c could set the API to nothing. For some reason though, on MACOSX this causes Pd to switch to JACK so we just give up and suppress the workaround there. */ as.a_api = 0; #endif argv += 2; argc -= 2; } else if (!strcmp(*argv, "-extraflags")) { if (argc < 2) goto usage; sys_extraflags = 1; strncpy(sys_extraflagsstring, argv[1], sizeof(sys_extraflagsstring) - 1); argv += 2; argc -= 2; } else if (!strcmp(*argv, "-batch")) { sys_batch = 1; argc--; argv++; } else if (!strcmp(*argv, "-nobatch")) { sys_batch = 0; argc--; argv++; } else if (!strcmp(*argv, "-autopatch")) { sys_noautopatch = 0; argc--; argv++; } else if (!strcmp(*argv, "-noautopatch")) { sys_noautopatch = 1; argc--; argv++; } else if (!strcmp(*argv, "-compatibility")) { float f; if (argc < 2) goto usage; if (sscanf(argv[1], "%f", &f) < 1) goto usage; pd_compatibilitylevel = 0.5 + 100. * f; /* e.g., 2.44 --> 244 */ argv += 2; argc -= 2; } #ifdef HAVE_UNISTD_H else if (!strcmp(*argv, "-rt") || !strcmp(*argv, "-realtime")) { sys_hipriority = 1; argc--; argv++; } else if (!strcmp(*argv, "-nrt") || !strcmp(*argv, "-nort") || !strcmp(*argv, "-norealtime")) { sys_hipriority = 0; argc--; argv++; } #else else if (!strcmp(*argv, "-rt") || !strcmp(*argv, "-realtime") || !strcmp(*argv, "-nrt") || !strcmp(*argv, "-nort") || !strcmp(*argv, "-norealtime")) { fprintf(stderr, "Pd compiled without realtime priority-support, ignoring '%s' flag\n", *argv); argc--; argv++; } #endif else if (!strcmp(*argv, "-sleep")) { sys_nosleep = 0; argc--; argv++; } else if (!strcmp(*argv, "-nosleep")) { sys_nosleep = 1; argc--; argv++; } else if (!strcmp(*argv, "-noprefs")) /* did this earlier */ argc--, argv++; else if (!strcmp(*argv, "-prefsfile") && argc > 1) /* this too */ argc -= 2, argv +=2; else { usage: sys_printusage(); return (1); } } if (sys_batch) sys_dontstartgui = 1; if (sys_dontstartgui) sys_printtostderr = 1; #ifdef _WIN32 if (sys_printtostderr) /* we need to tell Windows to output UTF-8 */ SetConsoleOutputCP(CP_UTF8); #endif if (!sys_defaultfont) sys_defaultfont = DEFAULTFONT; for (; argc > 0; argc--, argv++) sys_openlist = patchlist_append(sys_openlist, *argv, 0); sys_set_audio_settings(&as); return (0); } int sys_getblksize(void) { return (DEFDACBLKSIZE); } void sys_init_audio(void); /* stuff to do, once, after calling sys_argparse() -- which may itself be called more than once (first from "settings, second from .pdrc, then from command-line arguments */ static void sys_afterargparse(void) { char sbuf[MAXPDSTRING]; int i; t_audiosettings as; int nmidiindev = 0, midiindev[MAXMIDIINDEV]; int nmidioutdev = 0, midioutdev[MAXMIDIOUTDEV]; /* add "extra" library to path */ strncpy(sbuf, sys_libdir->s_name, MAXPDSTRING-30); sbuf[MAXPDSTRING-30] = 0; strcat(sbuf, "/extra"); sys_setextrapath(sbuf); /* add "doc/5.reference" library to helppath */ strncpy(sbuf, sys_libdir->s_name, MAXPDSTRING-30); sbuf[MAXPDSTRING-30] = 0; strcat(sbuf, "/doc/5.reference"); STUFF->st_helppath = namelist_append_files(STUFF->st_helppath, sbuf); for (i = 0; i < sys_nmidiin; i++) sys_midiindevlist[i]--; for (i = 0; i < sys_nmidiout; i++) sys_midioutdevlist[i]--; if (sys_listplease) sys_listdevs(); sys_get_midi_params(&nmidiindev, midiindev, &nmidioutdev, midioutdev); if (sys_nmidiin >= 0) { nmidiindev = sys_nmidiin; for (i = 0; i < nmidiindev; i++) midiindev[i] = sys_midiindevlist[i]; } if (sys_nmidiout >= 0) { nmidioutdev = sys_nmidiout; for (i = 0; i < nmidioutdev; i++) midioutdev[i] = sys_midioutdevlist[i]; } sys_open_midi(nmidiindev, midiindev, nmidioutdev, midioutdev, 0); sys_init_audio(); } static void sys_addreferencepath(void) { char sbuf[MAXPDSTRING]; } int sys_argparse(int argc, const char **argv); static int string2args(const char * cmd, int * retArgc, const char *** retArgv); void sys_doflags(void) { int rcargc=0; const char**rcargv = NULL; int len; int rcode = 0; if (!sys_flags) sys_flags = &s_; len = (int)strlen(sys_flags->s_name); if (len > MAXPDSTRING) { pd_error(0, "flags: %s: too long", sys_flags->s_name); return; } rcode = string2args(sys_flags->s_name, &rcargc, &rcargv); if(rcode < 0) { pd_error(0, "error#%d while parsing flags", rcode); return; } if (sys_argparse(rcargc, rcargv)) pd_error(0, "error parsing startup arguments"); for(len=0; lens_name; int i; if (*sp != '+') bug("sys_decodedialog: %s", sp); else sp++; for (i = 0; i < MAXPDSTRING-1; i++, sp++) { if (!sp[0]) break; if (sp[0] == '+') { if (sp[1] == '_') buf[i] = ' ', sp++; else if (sp[1] == '+') buf[i] = '+', sp++; else if (sp[1] == 'c') buf[i] = ',', sp++; else if (sp[1] == 's') buf[i] = ';', sp++; else if (sp[1] == 'd') buf[i] = '$', sp++; else buf[i] = sp[0]; } else buf[i] = sp[0]; } buf[i] = 0; return (gensym(buf)); } /* start the generic preference window */ void sys_gui_preferences(void); void sys_gui_audiopreferences(void); void sys_gui_midipreferences(void); void glob_start_preference_dialog(t_pd *dummy, t_symbol*s) { sys_gui_preferences(); sys_gui_audiopreferences(); sys_gui_midipreferences(); pdgui_vmess("::dialog_preferences::create", ""); } /* start a search path dialog window */ void glob_start_path_dialog(t_pd *dummy) { sys_gui_preferences(); pdgui_stub_vnew( &glob_pdobject, "pdtk_path_dialog", (void *)glob_start_path_dialog, "ii", sys_usestdpath, sys_verbose); } /* new values from dialog window */ void glob_path_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv) { int i; namelist_free(STUFF->st_searchpath); STUFF->st_searchpath = 0; sys_usestdpath = atom_getfloatarg(0, argc, argv); sys_verbose = atom_getfloatarg(1, argc, argv); for (i = 0; i < argc-2; i++) { t_symbol *s = sys_decodedialog(atom_getsymbolarg(i+2, argc, argv)); if (*s->s_name) STUFF->st_searchpath = namelist_append_files(STUFF->st_searchpath, s->s_name); } } /* add one item to search path (intended for use by Deken plugin). if "saveit" is > 0, this also saves all settings, if "saveit" is < 0, the path is only added temporarily */ void glob_addtopath(t_pd *dummy, t_symbol *path, t_float saveit) { int saveflag = (int)saveit; t_symbol *s = sys_decodedialog(path); if (*s->s_name) { if (saveflag < 0) STUFF->st_temppath = namelist_append_files(STUFF->st_temppath, s->s_name); else STUFF->st_searchpath = namelist_append_files(STUFF->st_searchpath, s->s_name); if (saveit > 0) sys_savepreferences(0); } } /* start a startup dialog window */ void glob_start_startup_dialog(t_pd *dummy) { sys_gui_preferences(); pdgui_stub_vnew( &glob_pdobject, "pdtk_startup_dialog", (void *)glob_start_path_dialog, "is", sys_defeatrt, sys_flags?sys_flags->s_name:""); } /* new values from dialog window */ void glob_startup_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv) { int i; namelist_free(STUFF->st_externlist); STUFF->st_externlist = 0; sys_defeatrt = atom_getfloatarg(0, argc, argv); sys_flags = sys_decodedialog(atom_getsymbolarg(1, argc, argv)); for (i = 0; i < argc-2; i++) { t_symbol *s = sys_decodedialog(atom_getsymbolarg(i+2, argc, argv)); if (*s->s_name) STUFF->st_externlist = namelist_append_files(STUFF->st_externlist, s->s_name); } } /* * the following string2args function is based on from sash-3.8 (the StandAlone SHell) * Copyright (c) 2014 by David I. Bell * Permission is granted to use, distribute, or modify this source, * provided that this copyright notice remains intact. */ #define isBlank(ch) (((ch) == ' ') || ((ch) == '\t')) int string2args(const char * cmd, int * retArgc, const char *** retArgv) { int errCode = 1; int len = strlen(cmd), argCount = 0; char strings[MAXPDSTRING], *cp; const char **argTable = 0, **newArgTable; if(retArgc) *retArgc = 0; if(retArgv) *retArgv = NULL; /* * Copy the command string into a buffer that we can modify, * reallocating it if necessary. */ if(len >= MAXPDSTRING) { errCode = 1; goto ouch; } memset(strings, 0, MAXPDSTRING); memcpy(strings, cmd, len); cp = strings; /* Keep parsing the command string as long as there are any arguments left. */ while (*cp) { const char *cpIn = cp; char *cpOut = cp, *argument; int quote = '\0'; /* * Loop over the string collecting the next argument while * looking for quoted strings or quoted characters. */ while (*cp) { int ch = *cp++; /* If we are not in a quote and we see a blank then this argument is done. */ if (isBlank(ch) && (quote == '\0')) break; /* If we see a backslash then accept the next character no matter what it is. */ if (ch == '\\') { ch = *cp++; if (ch == '\0') { /* but only if there is a next char */ errCode = 10; goto ouch; } *cpOut++ = ch; continue; } /* If we were in a quote and we saw the same quote character again then the quote is done. */ if (ch == quote) { quote = '\0'; continue; } /* If we weren't in a quote and we see either type of quote character, * then remember that we are now inside of a quote. */ if ((quote == '\0') && ((ch == '\'') || (ch == '"'))) { quote = ch; continue; } /* Store the character. */ *cpOut++ = ch; } if (quote) { /* Unmatched quote character */ errCode = 11; goto ouch; } /* * Null terminate the argument if it had shrunk, and then * skip over all blanks to the next argument, nulling them * out too. */ if (cp != cpOut) *cpOut = '\0'; while (isBlank(*cp)) *cp++ = '\0'; if (!(argument = calloc(1+cpOut-cpIn, 1))) { errCode = 22; goto ouch; } memcpy(argument, cpIn, cpOut-cpIn); /* Now reallocate the argument table to hold the argument, add add it. */ if (!(newArgTable = (const char **) realloc(argTable, (sizeof(const char *) * (argCount + 1))))) { free(argument); errCode= 23; goto ouch; } else argTable = newArgTable; argTable[argCount] = argument; argCount++; } /* * Null terminate the argument list and return it. */ if (!(newArgTable = (const char **) realloc(argTable, (sizeof(const char *) * (argCount + 1))))) { errCode = 23; goto ouch; } else argTable = newArgTable; argTable[argCount] = NULL; if(retArgc) *retArgc = argCount; if(retArgv) *retArgv = argTable; else free(argTable); return argCount; ouch: free(argTable); return -errCode; } ================================================ FILE: libs/libpd/pure-data/src/s_net.c ================================================ /* Copyright (c) 2019 Dan Wilcox. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ #include "s_net.h" #include #include #include #ifndef _WIN32 #include #include #include #include #endif /* Windows XP winsock doesn't provide inet_ntop */ #ifdef _WIN32 const char* INET_NTOP(int af, const void *src, char *dst, socklen_t size) { struct sockaddr_storage addr; socklen_t addrlen; memset(&addr, 0, sizeof(struct sockaddr_storage)); addr.ss_family = af; if (af == AF_INET6) { struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&addr; memcpy(&(sa6->sin6_addr.s6_addr), src, sizeof(sa6->sin6_addr.s6_addr)); addrlen = sizeof(struct sockaddr_in6); } else if (af == AF_INET) { struct sockaddr_in *sa4 = (struct sockaddr_in *)&addr; memcpy(&(sa4->sin_addr.s_addr), src, sizeof(sa4->sin_addr.s_addr)); addrlen = sizeof(struct sockaddr_in); } else return NULL; if (WSAAddressToStringA((struct sockaddr *)&addr, addrlen, 0, dst, (LPDWORD)&size) != 0) return NULL; return dst; } #else /* _WIN32 */ #define INET_NTOP inet_ntop #endif int addrinfo_get_list(struct addrinfo **ailist, const char *hostname, int port, int protocol) { struct addrinfo hints; int result; char portstr[10]; /* largest port is 65535 */ memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; /* IPv4 or IPv6 */ hints.ai_socktype = protocol; hints.ai_protocol = (protocol == SOCK_STREAM ? IPPROTO_TCP : IPPROTO_UDP); hints.ai_flags = #ifdef AI_ALL AI_ALL | /* both IPv4 and IPv6 addrs */ #endif #ifdef AI_V4MAPPED AI_V4MAPPED | /* fallback to IPv4-mapped IPv6 addrs */ #endif AI_PASSIVE; /* listen to any addr if hostname is NULL */ portstr[0] = '\0'; sprintf(portstr, "%d", port); result = getaddrinfo(hostname, portstr, &hints, ailist); /* There's currently a bug in the BSD libc where getaddrinfo() * will return EAI_BADFLAGS for the AI_ALL and AI_V4MAPPED flags. * NOTE: this also seems to affect Android! * In practice, this means we can't use dual stack sockets, * so we fall back to IPv4 networking... */ if (result == EAI_BADFLAGS) { static int warned = 0; if (!warned) { fprintf(stderr, "Warning: can't create IPv6 dual-stack socket - falling " "back to IPv4. (This is a known bug in the BSD libc, which doesn't " "implement the AI_ALL and AI_V4MAPPED flags for getaddrinfo().)\n"); warned = 1; } hints.ai_family = AF_INET; hints.ai_flags = AI_PASSIVE; result = getaddrinfo(hostname, portstr, &hints, ailist); } return result; } int addrinfo_ipv4_first(const struct addrinfo* ai1, const struct addrinfo* ai2) { return (ai1->ai_family == AF_INET) ? ((ai2->ai_family == AF_INET) ? 0 : -1) : 1; } int addrinfo_ipv6_first(const struct addrinfo* ai1, const struct addrinfo* ai2){ return (ai1->ai_family == AF_INET6) ? ((ai2->ai_family == AF_INET6) ? 0 : -1) : 1; } void addrinfo_sort_list(struct addrinfo **ailist, int (*compare)(const struct addrinfo*, const struct addrinfo*)) { struct addrinfo *result = NULL, *ai = (*ailist); while (ai) { struct addrinfo *temp = ai; ai = ai->ai_next; if (result) { struct addrinfo *ai2 = result, *last = NULL; while (ai2 && (compare(temp, ai2) >= 0)) { last = ai2; ai2 = ai2->ai_next; } if (last) { last->ai_next = temp; temp->ai_next = ai2; } else { result = temp; temp->ai_next = ai2; } } else { result = temp; temp->ai_next = NULL; } } *ailist = result; } void addrinfo_print_list(const struct addrinfo *ailist) { const struct addrinfo *ai; char addrstr[INET6_ADDRSTRLEN]; for (ai = ailist; ai != NULL; ai = ai->ai_next) { char *ipver; void *addr; unsigned int port = 0; if (ai->ai_family == AF_INET6) { struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)ai->ai_addr; addr = &(sa6->sin6_addr); ipver = "IPv6"; port = ntohs(sa6->sin6_port); } else if (ai->ai_family == AF_INET) { struct sockaddr_in *sa4 = (struct sockaddr_in *)ai->ai_addr; addr = &(sa4->sin_addr); ipver = "IPv4"; port = ntohs(sa4->sin_port); } else continue; INET_NTOP(ai->ai_family, addr, addrstr, INET6_ADDRSTRLEN); printf("%s %s %d\n", ipver, addrstr, port); } } const char* sockaddr_get_addrstr(const struct sockaddr *sa, char *addrstr, int addrstrlen) { const void *addr; addrstr[0] = '\0'; if (sa->sa_family == AF_INET6) { struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa; addr = (const void *)&sa6->sin6_addr.s6_addr; } else if (sa->sa_family == AF_INET) { struct sockaddr_in *sa4 = (struct sockaddr_in *)sa; addr = (const void *)&sa4->sin_addr.s_addr; } else return NULL; return INET_NTOP(sa->sa_family, addr, addrstr, addrstrlen); } unsigned int sockaddr_get_port(const struct sockaddr *sa) { if (sa->sa_family == AF_INET6) { struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa; return ntohs(sa6->sin6_port); } else if (sa->sa_family == AF_INET) { struct sockaddr_in *sa4 = (struct sockaddr_in *)sa; return ntohs(sa4->sin_port); } else return 0; } void sockaddr_set_port(const struct sockaddr *sa, unsigned int port) { if (sa->sa_family == AF_INET6) { struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa; sa6->sin6_port = htons(port); } else if (sa->sa_family == AF_INET) { struct sockaddr_in *sa4 = (struct sockaddr_in *)sa; sa4->sin_port = htons(port); } } int sockaddr_is_multicast(const struct sockaddr *sa) { if (sa->sa_family == AF_INET6) { struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa; return (sa6->sin6_addr.s6_addr[0] == 0xFF); } else if (sa->sa_family == AF_INET) { struct sockaddr_in *sa4 = (struct sockaddr_in *)sa; return ((ntohl(sa4->sin_addr.s_addr) & 0xF0000000) == 0xE0000000); } return 0; } int socket_init(void) { #ifdef _WIN32 static int initialized = 0; if (!initialized) { short version = MAKEWORD(2, 0); WSADATA nobby; if (WSAStartup(version, &nobby)) return -1; initialized = 1; } #endif return 0; } /* kudos to https://stackoverflow.com/a/46062474/6063908 */ int socket_connect(int socket, const struct sockaddr *addr, socklen_t addrlen, float timeout) { /* set nonblocking and connect */ socket_set_nonblocking(socket, 1); if (connect(socket, addr, addrlen) < 0) { int status; struct timeval timeoutval; fd_set writefds, errfds; #ifdef _WIN32 if (socket_errno() != WSAEWOULDBLOCK) #else if (socket_errno() != EINPROGRESS) #endif return -1; /* break on "real" error */ /* block with select using timeout */ if (timeout < 0) timeout = 0; timeoutval.tv_sec = (int)timeout; timeoutval.tv_usec = (timeout - timeoutval.tv_sec) * 1000000; FD_ZERO(&writefds); FD_SET(socket, &writefds); /* socket is connected when writable */ FD_ZERO(&errfds); FD_SET(socket, &errfds); /* catch exceptions */ status = select(socket+1, NULL, &writefds, &errfds, &timeoutval); if (status < 0) /* select failed */ { fprintf(stderr, "socket_connect: select failed"); return -1; } else if (status == 0) /* connection timed out */ { #ifdef _WIN32 WSASetLastError(WSAETIMEDOUT); #else errno = ETIMEDOUT; #endif return -1; } if (FD_ISSET(socket, &errfds)) /* connection failed */ { int err; socklen_t len = sizeof(err); getsockopt(socket, SOL_SOCKET, SO_ERROR, (void *)&err, &len); #ifdef _WIN32 WSASetLastError(err); #else errno = err; #endif return -1; } } /* done, set blocking again */ socket_set_nonblocking(socket, 0); return 0; } void socket_close(int socket) { #ifdef _WIN32 closesocket(socket); #else if (socket < 0) return; close(socket); #endif } unsigned int socket_get_port(int socket) { struct sockaddr_storage sa; socklen_t len = sizeof(sa); if (getsockname(socket, (struct sockaddr *)&sa, &len) < 0) return 0; if (sa.ss_family == AF_INET6) { struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&sa; return ntohs(sa6->sin6_port); } else if (sa.ss_family == AF_INET) { struct sockaddr_in *sa4 = (struct sockaddr_in *)&sa; return ntohs(sa4->sin_port); } else return 0; } int socket_set_boolopt(int socket, int level, int option_name, int bool_value) { return setsockopt(socket, level, option_name, (void *)&bool_value, sizeof(int)); } int socket_set_nonblocking(int socket, int nonblocking) { #ifdef _WIN32 u_long modearg = nonblocking; if (ioctlsocket(socket, FIONBIO, &modearg) != NO_ERROR) return -1; #else int sockflags = fcntl(socket, F_GETFL, 0); if (nonblocking) sockflags |= O_NONBLOCK; else sockflags &= ~O_NONBLOCK; if (fcntl(socket, F_SETFL, sockflags) < 0) return -1; #endif return 0; } int socket_bytes_available(int socket) { #ifdef _WIN32 u_long n = 0; if (ioctlsocket(socket, FIONREAD, &n) != NO_ERROR) return -1; return n; #else int n = 0; if (ioctl(socket, FIONREAD, &n) < 0) return -1; return n; #endif } int socket_join_multicast_group(int socket, const struct sockaddr *sa) { if (sa->sa_family == AF_INET6) { struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa; struct ipv6_mreq mreq6 = {0}; memcpy(&mreq6.ipv6mr_multiaddr, &sa6->sin6_addr, sizeof(struct in6_addr)); return setsockopt(socket, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *)&mreq6, sizeof(mreq6)); } else if (sa->sa_family == AF_INET) { struct sockaddr_in *sa4 = (struct sockaddr_in *)sa; struct ip_mreq mreq = {0}; mreq.imr_multiaddr.s_addr = sa4->sin_addr.s_addr; mreq.imr_interface.s_addr = INADDR_ANY; return setsockopt(socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq)); } return -1; } int socket_leave_multicast_group(int socket, const struct sockaddr *sa) { if (sa->sa_family == AF_INET6) { struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa; struct ipv6_mreq mreq6 = {0}; memcpy(&mreq6.ipv6mr_multiaddr, &sa6->sin6_addr, sizeof(struct in6_addr)); return setsockopt(socket, IPPROTO_IPV6, IPV6_LEAVE_GROUP, (char *)&mreq6, sizeof(mreq6)); } else if (sa->sa_family == AF_INET) { struct sockaddr_in *sa4 = (struct sockaddr_in *)sa; struct ip_mreq mreq = {0}; mreq.imr_multiaddr.s_addr = sa4->sin_addr.s_addr; mreq.imr_interface.s_addr = INADDR_ANY; return setsockopt(socket, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char *)&mreq, sizeof(mreq)); } return -1; } int socket_errno(void) { #ifdef _WIN32 int err = WSAGetLastError(); if (err == 10044) /* WSAESOCKTNOSUPPORT */ { fprintf(stderr, "Warning: you might not have TCP/IP \"networking\" turned on\n"); } return err; #else return errno; #endif } int socket_errno_udp(void) { #ifdef _WIN32 int err = socket_errno(); /* ignore WSAECONNRESET to prevent UDP sockets from closing in case of a missing receiver */ if (err == 10054) return 0; else return err; #else return socket_errno(); #endif } void socket_strerror(int err, char *buf, int size) { if (size > 0) { #ifdef _WIN32 wchar_t wbuf[1024]; int count = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, err, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), wbuf, 1024, NULL); if (!count || !WideCharToMultiByte(CP_UTF8, 0, wbuf, count+1, buf, size, 0, 0)) *buf = '\0'; #else snprintf(buf, size, "%s", strerror(err)); #endif } } ================================================ FILE: libs/libpd/pure-data/src/s_net.h ================================================ /* Copyright (c) 2019 Dan Wilcox. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* networking headers and helper functions */ #pragma once #ifdef _WIN32 #include #include typedef int socklen_t; #else #include #include #include #include #include #include #endif #ifndef NET_MAXPACKETSIZE #define NET_MAXPACKETSIZE 65536 #endif /* ----- socket address ----- */ /** getaddrinfo() convenience wrapper which generates a list of IPv4 & IPv6 addresses from a given address/hostname string, port, and protocol (SOCK_STREAM or SOCK_DGRAM), set hostname to NULL for "any" address returns 0 on success or < 0 on error the ailist must be freed after usage using freeaddrinfo(), status errno can be printed using gai_strerr() basic usage example: int sockfd, status; struct addrinfo *ailist = NULL, *ai; struct sockaddr_storage ss = {0}; // IPv4 or IPv6 addr // generate addrinfo list status = addrinfo_get_list(&ailist, "127.0.0.1", 5000, SOCK_DGRAM); if (status != 0) { printf("bad host or port? %s (%d)", gai_strerror(status), status); return; } // try each addr until we find one that works for (ai = ailist; ai != NULL; ai = ai->ai_next) { sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (sockfd < 0) continue; // go to the next addr // perform socket setsockopt(), bind(), connect(), etc as required, // cleanup and continue to next addr on failure // save addr that worked and exit loop memcpy(&ss, ai->ai_addr, ai->ai_addrlen); break; } freeaddrinfo(ailist); // cleanup // confirm that socket setup succeeded if (sockfd == -1) { int err = socket_errno(); printf("socket setup failed: %s (%d)", strerror(errno), errno); return; } */ int addrinfo_get_list(struct addrinfo **ailist, const char *hostname, int port, int protocol); /** sort an address list with a compare function */ void addrinfo_sort_list(struct addrinfo **ailist, int (*compare)(const struct addrinfo*, const struct addrinfo*)); /** compare function which puts IPv4 addresses first */ int addrinfo_ipv4_first(const struct addrinfo* ai1, const struct addrinfo* ai2); /** compare function which puts IPv6 addresses first */ int addrinfo_ipv6_first(const struct addrinfo* ai1, const struct addrinfo* ai2); /** print addrinfo linked list sockaddrs: IP version, hostname, port */ void addrinfo_print_list(const struct addrinfo *ailist); /** read address/hostname string from a sockaddr, fills addrstr and returns pointer on success or NULL on failure */ const char* sockaddr_get_addrstr(const struct sockaddr *sa, char *addrstr, int addrstrlen); /** returns sockaddr port or 0 on failure */ unsigned int sockaddr_get_port(const struct sockaddr *sa); /** sets sockaddr port */ void sockaddr_set_port(const struct sockaddr *sa, unsigned int port); /** returns 1 if address is a IPv4 or IPv6 multicast address, otherwise 0 */ int sockaddr_is_multicast(const struct sockaddr *sa); /* ----- socket ----- */ /** cross-platform initialization routine, returns -1 on failure */ int socket_init(void); /** connect a socket to an address with a settable timeout in seconds returns -1 on error, use socket_errno() to get the actual error code */ int socket_connect(int socket, const struct sockaddr *addr, socklen_t addrlen, float timeout); /** cross-platform socket close() */ void socket_close(int socket); /** returns port or 0 on failure */ unsigned int socket_get_port(int socket); /** get number of immediately readable bytes for the socket returns -1 on error or bytes available on success */ int socket_bytes_available(int socket); /** setsockopt() convenience wrapper for socket bool options */ int socket_set_boolopt(int socket, int level, int option_name, int bool_value); /** enable/disable socket non-blocking mode */ int socket_set_nonblocking(int socket, int nonblocking); /** join a multicast group address, returns < 0 on error */ int socket_join_multicast_group(int socket, const struct sockaddr *sa); /** leave a multicast group address, returns < 0 on error */ int socket_leave_multicast_group(int socket, const struct sockaddr *sa); /** cross-platform socket errno() which catches WSAESOCKTNOSUPPORT on Windows */ int socket_errno(void); /** like socket_errno() but ignores WSAECONNRESET on Windows */ int socket_errno_udp(void); /** get an error string from errno */ void socket_strerror(int err, char *buf, int size); ================================================ FILE: libs/libpd/pure-data/src/s_path.c ================================================ /* Copyright (c) 1999 Guenter Geiger and others. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* * This file implements the loader for linux, which includes * a little bit of path handling. * * Generalized by MSP to provide an open_via_path function * and lists of files for all purposes. */ /* #define DEBUG(x) x */ #define DEBUG(x) #include #ifdef HAVE_UNISTD_H #include #endif #include #ifdef _WIN32 #include #include #endif #include #include "m_pd.h" #include "m_imp.h" #include "s_stuff.h" #include "s_utf8.h" #include #include #ifdef _LARGEFILE64_SOURCE # define open open64 # define lseek lseek64 # define fstat fstat64 # define stat stat64 #endif #include "m_private_utils.h" /* change '/' characters to the system's native file separator */ void sys_bashfilename(const char *from, char *to) { char c; while ((c = *from++)) { #ifdef _WIN32 if (c == '/') c = '\\'; #endif *to++ = c; } *to = 0; } /* change the system's native file separator to '/' characters */ void sys_unbashfilename(const char *from, char *to) { char c; while ((c = *from++)) { #ifdef _WIN32 if (c == '\\') c = '/'; #endif *to++ = c; } *to = 0; } /* test if path is absolute or relative, based on leading /, env vars, ~, etc */ int sys_isabsolutepath(const char *dir) { if (dir[0] == '/' || dir[0] == '~' #ifdef _WIN32 || dir[0] == '%' || (dir[1] == ':' && dir[2] == '/') #endif ) { return 1; } else { return 0; } } /* expand env vars and ~ at the beginning of a path and make a copy to return */ static void sys_expandpath(const char *from, char *to, int bufsize) { if ((strlen(from) == 1 && from[0] == '~') || (strncmp(from,"~/", 2) == 0)) { #ifdef _WIN32 const char *home = getenv("USERPROFILE"); #else const char *home = getenv("HOME"); #endif if (home) { strncpy(to, home, bufsize); to[bufsize-1] = 0; strncpy(to + strlen(to), from + 1, bufsize - strlen(to)); to[bufsize-1] = 0; } else *to = 0; } else { strncpy(to, from, bufsize); to[bufsize-1] = 0; } #ifdef _WIN32 { char *buf = alloca(bufsize); ExpandEnvironmentStrings(to, buf, bufsize-1); buf[bufsize-1] = 0; strncpy(to, buf, bufsize); to[bufsize-1] = 0; } #endif } /******************* Utility functions used below ******************/ /*! * \brief copy until delimiter * * \arg to destination buffer * \arg to_len destination buffer length * \arg from source buffer * \arg delim string delimiter to stop copying on * * \return position after delimiter in string. If it was the last * substring, return NULL. */ static const char *strtokcpy(char *to, size_t to_len, const char *from, char delim) { unsigned int i = 0; for (; i < (to_len - 1) && from[i] && from[i] != delim; i++) to[i] = from[i]; to[i] = '\0'; if (i && from[i] != '\0') return from + i + 1; return NULL; } /* add a single item to a namelist. If "allowdup" is true, duplicates may be added; otherwise they're dropped. */ t_namelist *namelist_append(t_namelist *listwas, const char *s, int allowdup) { t_namelist *nl, *nl2; nl2 = (t_namelist *)(getbytes(sizeof(*nl))); nl2->nl_next = 0; nl2->nl_string = (char *)getbytes(strlen(s) + 1); strcpy(nl2->nl_string, s); sys_unbashfilename(nl2->nl_string, nl2->nl_string); if (!listwas) return (nl2); else { for (nl = listwas; ;) { if (!allowdup && !strcmp(nl->nl_string, s)) { freebytes(nl2->nl_string, strlen(nl2->nl_string) + 1); return (listwas); } if (!nl->nl_next) break; nl = nl->nl_next; } nl->nl_next = nl2; } return (listwas); } /* add a colon-separated list of names to a namelist */ #ifdef _WIN32 #define SEPARATOR ';' /* in MSW the natural separator is semicolon instead */ #else #define SEPARATOR ':' #endif t_namelist *namelist_append_files(t_namelist *listwas, const char *s) { const char *npos; char temp[MAXPDSTRING]; t_namelist *nl = listwas; npos = s; do { npos = strtokcpy(temp, sizeof(temp), npos, SEPARATOR); if (! *temp) continue; nl = namelist_append(nl, temp, 0); } while (npos); return (nl); } void namelist_free(t_namelist *listwas) { t_namelist *nl, *nl2; for (nl = listwas; nl; nl = nl2) { nl2 = nl->nl_next; t_freebytes(nl->nl_string, strlen(nl->nl_string) + 1); t_freebytes(nl, sizeof(*nl)); } } const char *namelist_get(const t_namelist *namelist, int n) { int i; const t_namelist *nl; for (i = 0, nl = namelist; i < n && nl; i++, nl = nl->nl_next) ; return (nl ? nl->nl_string : 0); } int sys_usestdpath = 1; void sys_setextrapath(const char *p) { char pathbuf[MAXPDSTRING]; namelist_free(STUFF->st_staticpath); /* add standard place for users to install stuff first */ #ifdef __gnu_linux__ sys_expandpath("~/.local/lib/pd/extra/", pathbuf, MAXPDSTRING); STUFF->st_staticpath = namelist_append(0, pathbuf, 0); sys_expandpath("~/pd-externals", pathbuf, MAXPDSTRING); STUFF->st_staticpath = namelist_append(STUFF->st_staticpath, pathbuf, 0); STUFF->st_staticpath = namelist_append(STUFF->st_staticpath, "/usr/local/lib/pd-externals", 0); #endif #ifdef __APPLE__ sys_expandpath("~/Library/Pd", pathbuf, MAXPDSTRING); STUFF->st_staticpath = namelist_append(0, pathbuf, 0); STUFF->st_staticpath = namelist_append(STUFF->st_staticpath, "/Library/Pd", 0); #endif #ifdef _WIN32 sys_expandpath("%AppData%/Pd", pathbuf, MAXPDSTRING); STUFF->st_staticpath = namelist_append(0, pathbuf, 0); sys_expandpath("%CommonProgramFiles%/Pd", pathbuf, MAXPDSTRING); STUFF->st_staticpath = namelist_append(STUFF->st_staticpath, pathbuf, 0); #endif /* add built-in "extra" path last so its checked last */ STUFF->st_staticpath = namelist_append(STUFF->st_staticpath, p, 0); } /* try to open a file in the directory "dir", named "name""ext", for reading. "Name" may have slashes. The directory is copied to "dirresult" which must be at least "size" bytes. "nameresult" is set to point to the filename (copied elsewhere into the same buffer). The "bin" flag requests opening for binary (which only makes a difference on Windows). */ int sys_trytoopenone(const char *dir, const char *name, const char* ext, char *dirresult, char **nameresult, unsigned int size, int bin) { int fd; char buf[MAXPDSTRING]; if (strlen(dir) + strlen(name) + strlen(ext) + 4 > size) return (-1); sys_expandpath(dir, buf, MAXPDSTRING); strcpy(dirresult, buf); if (*dirresult && dirresult[strlen(dirresult)-1] != '/') strcat(dirresult, "/"); strcat(dirresult, name); strcat(dirresult, ext); DEBUG(post("looking for %s",dirresult)); /* see if we can open the file for reading */ if ((fd=sys_open(dirresult, O_RDONLY)) >= 0) { /* in unix, further check that it's not a directory */ #ifdef HAVE_UNISTD_H struct stat statbuf; int ok = ((fstat(fd, &statbuf) >= 0) && !S_ISDIR(statbuf.st_mode)); if (!ok) { logpost(NULL, PD_VERBOSE, "tried %s; stat failed or directory", dirresult); close (fd); fd = -1; } else #endif { char *slash; logpost(NULL, PD_VERBOSE, "tried %s and succeeded", dirresult); sys_unbashfilename(dirresult, dirresult); slash = strrchr(dirresult, '/'); if (slash) { *slash = 0; *nameresult = slash + 1; } else *nameresult = dirresult; return (fd); } } else { logpost(NULL, PD_VERBOSE, "tried %s and failed", dirresult); } return (-1); } /* check if we were given an absolute pathname, if so try to open it and return 1 to signal the caller to cancel any path searches */ int sys_open_absolute(const char *name, const char* ext, char *dirresult, char **nameresult, unsigned int size, int bin, int *fdp) { if (sys_isabsolutepath(name)) { char dirbuf[MAXPDSTRING], *z = strrchr(name, '/'); int dirlen; if (!z) return (0); dirlen = (int)(z - name); if (dirlen > MAXPDSTRING-1) dirlen = MAXPDSTRING-1; strncpy(dirbuf, name, dirlen); dirbuf[dirlen] = 0; *fdp = sys_trytoopenone(dirbuf, name+(dirlen+1), ext, dirresult, nameresult, size, bin); return (1); } else return (0); } /* search for a file in a specified directory, then along the globally defined search path, using ext as filename extension. The fd is returned, the directory ends up in the "dirresult" which must be at least "size" bytes. "nameresult" is set to point to the filename, which ends up in the same buffer as dirresult. Exception: if the 'name' starts with a slash or a letter, colon, and slash in MSW, there is no search and instead we just try to open the file literally. */ /* see also canvas_open() which, in addition, searches down the canvas-specific path. */ static int do_open_via_path(const char *dir, const char *name, const char *ext, char *dirresult, char **nameresult, unsigned int size, int bin, t_namelist *searchpath) { t_namelist *nl; int fd = -1; /* first check if "name" is absolute (and if so, try to open) */ if (sys_open_absolute(name, ext, dirresult, nameresult, size, bin, &fd)) return (fd); /* otherwise "name" is relative; try the directory "dir" first. */ if ((fd = sys_trytoopenone(dir, name, ext, dirresult, nameresult, size, bin)) >= 0) return (fd); /* next go through the temp paths from the commandline */ for (nl = STUFF->st_temppath; nl; nl = nl->nl_next) if ((fd = sys_trytoopenone(nl->nl_string, name, ext, dirresult, nameresult, size, bin)) >= 0) return (fd); /* next look in built-in paths like "extra" */ for (nl = searchpath; nl; nl = nl->nl_next) if ((fd = sys_trytoopenone(nl->nl_string, name, ext, dirresult, nameresult, size, bin)) >= 0) return (fd); /* next look in built-in paths like "extra" */ if (sys_usestdpath) for (nl = STUFF->st_staticpath; nl; nl = nl->nl_next) if ((fd = sys_trytoopenone(nl->nl_string, name, ext, dirresult, nameresult, size, bin)) >= 0) return (fd); *dirresult = 0; *nameresult = dirresult; return (-1); } /* open via path, using the global search path. */ int open_via_path(const char *dir, const char *name, const char *ext, char *dirresult, char **nameresult, unsigned int size, int bin) { return (do_open_via_path(dir, name, ext, dirresult, nameresult, size, bin, STUFF->st_searchpath)); } /* open a file with a UTF-8 filename This is needed because WIN32 does not support UTF-8 filenames, only UCS2. Having this function prevents lots of #ifdefs all over the place. */ #ifdef _WIN32 int sys_open(const char *path, int oflag, ...) { int i, fd; char pathbuf[MAXPDSTRING]; wchar_t ucs2path[MAXPDSTRING]; sys_bashfilename(path, pathbuf); u8_utf8toucs2(ucs2path, MAXPDSTRING, pathbuf, MAXPDSTRING-1); /* For the create mode, Win32 does not have the same possibilities, * so we ignore the argument and just hard-code read/write. */ if (oflag & O_CREAT) fd = _wopen(ucs2path, oflag | O_BINARY, _S_IREAD | _S_IWRITE); else fd = _wopen(ucs2path, oflag | O_BINARY); return fd; } FILE *sys_fopen(const char *filename, const char *mode) { char namebuf[MAXPDSTRING]; wchar_t ucs2buf[MAXPDSTRING]; wchar_t ucs2mode[MAXPDSTRING]; sys_bashfilename(filename, namebuf); u8_utf8toucs2(ucs2buf, MAXPDSTRING, namebuf, MAXPDSTRING-1); /* mode only uses ASCII, so no need for a full conversion, just copy it */ mbstowcs(ucs2mode, mode, MAXPDSTRING); return (_wfopen(ucs2buf, ucs2mode)); } #else #include int sys_open(const char *path, int oflag, ...) { int i, fd; char pathbuf[MAXPDSTRING]; sys_bashfilename(path, pathbuf); if (oflag & O_CREAT) { mode_t mode; int imode; va_list ap; va_start(ap, oflag); /* Mac compiler complains if we just set mode = va_arg ... so, even though we all know it's just an int, we explicitly va_arg to an int and then convert. -> http://www.mail-archive.com/bug-gnulib@gnu.org/msg14212.html -> http://bugs.debian.org/647345 */ imode = va_arg (ap, int); mode = (mode_t)imode; va_end(ap); fd = open(pathbuf, oflag, mode); } else fd = open(pathbuf, oflag); return fd; } FILE *sys_fopen(const char *filename, const char *mode) { char namebuf[MAXPDSTRING]; sys_bashfilename(filename, namebuf); return fopen(namebuf, mode); } #endif /* _WIN32 */ /* close a previously opened file this is needed on platforms where you cannot open/close resources across dll-boundaries, but we provide it for other platforms as well */ int sys_close(int fd) { #ifdef _WIN32 return _close(fd); /* Bill Gates is a big fat hen */ #else return close(fd); #endif } int sys_fclose(FILE *stream) { return fclose(stream); } /* Open a help file using the help search path. We expect the ".pd" suffix here, even though we have to tear it back off for one of the search attempts. */ void open_via_helppath(const char *name, const char *dir) { char realname[MAXPDSTRING], newname[MAXPDSTRING], dirbuf[MAXPDSTRING], *basename; /* make up a silly "dir" if none is supplied */ const char *usedir = (*dir ? dir : "./"); int fd; /* 1. "objectname-help.pd" */ strncpy(realname, name, MAXPDSTRING-10); realname[MAXPDSTRING-10] = 0; if (strlen(realname) > 3 && !strcmp(realname+strlen(realname)-3, ".pd")) realname[strlen(realname)-3] = 0; strncpy(newname, realname, MAXPDSTRING-10); strcat(realname, "-help.pd"); if ((fd = do_open_via_path(usedir, realname, "", dirbuf, &basename, MAXPDSTRING, 0, STUFF->st_helppath)) >= 0) goto gotone; /* 2. "help-objectname.pd" */ strcpy(realname, "help-"); strncat(realname, name, MAXPDSTRING-10); realname[MAXPDSTRING-1] = 0; if ((fd = do_open_via_path(usedir, realname, "", dirbuf, &basename, MAXPDSTRING, 0, STUFF->st_helppath)) >= 0) goto gotone; post("sorry, couldn't find help patch for \"%s\"", newname); return; gotone: close (fd); glob_evalfile(0, gensym((char*)basename), gensym(dirbuf)); } ================================================ FILE: libs/libpd/pure-data/src/s_print.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ #include "m_pd.h" #include #include #include #include #include #include "s_stuff.h" #include "m_private_utils.h" t_printhook sys_printhook = NULL; int sys_printtostderr; /* escape characters for tcl/tk */ char* pdgui_strnescape(char *dst, size_t dstlen, const char *src, size_t srclen) { unsigned ptin = 0, ptout = 0; if(!dst || !src)return 0; while(1) { int c = src[ptin]; if (c == '\\' || c == '{' || c == '}' || c == '[' || c == ']') { dst[ptout++] = '\\'; if (dstlen && ptout >= dstlen){ dst[ptout-1] = 0; break; } } dst[ptout] = c; ptin++; ptout++; if (c==0) break; if (srclen && ptin >= srclen) break; if (dstlen && ptout >= dstlen) break; } if(!dstlen || ptout < dstlen) dst[ptout]=0; else dst[dstlen-1]=0; return dst; } static void dopost(const char *s) { if (STUFF->st_printhook) (*STUFF->st_printhook)(s); else if (sys_printtostderr || !sys_havegui()) { #ifdef _WIN32 #ifdef _MSC_VER fwprintf(stderr, L"%S", s); #else fwprintf(stderr, L"%s", s); #endif fflush(stderr); #else fprintf(stderr, "%s", s); #endif } else { pdgui_vmess("::pdwindow::post", "s", s); } } static void doerror(const void *object, const char *s) { char upbuf[MAXPDSTRING]; upbuf[MAXPDSTRING-1]=0; // what about sys_printhook_error ? if (STUFF->st_printhook) { snprintf(upbuf, MAXPDSTRING-1, "error: %s", s); (*STUFF->st_printhook)(upbuf); } else if (sys_printtostderr) { #ifdef _WIN32 #ifdef _MSC_VER fwprintf(stderr, L"error: %S", s); #else fwprintf(stderr, L"error: %s", s); #endif fflush(stderr); #else fprintf(stderr, "error: %s", s); #endif } else pdgui_vmess("::pdwindow::logpost", "ois", object, 1, s); } static void dologpost(const void *object, const int level, const char *s) { char upbuf[MAXPDSTRING]; upbuf[MAXPDSTRING-1]=0; /* if it's a verbose message and we aren't set to 'verbose' just do nothing */ if (level >= PD_VERBOSE && !sys_verbose) return; // what about sys_printhook_verbose ? if (STUFF->st_printhook) { snprintf(upbuf, MAXPDSTRING-1, "verbose(%d): %s", level, s); (*STUFF->st_printhook)(upbuf); } else if (sys_printtostderr) { #ifdef _WIN32 #ifdef _MSC_VER fwprintf(stderr, L"verbose(%d): %S", level, s); #else fwprintf(stderr, L"verbose(%d): %s", level, s); #endif fflush(stderr); #else fprintf(stderr, "verbose(%d): %s", level, s); #endif } else pdgui_vmess("::pdwindow::logpost", "ois", object, level, s); } void logpost(const void *object, int level, const char *fmt, ...) { char buf[MAXPDSTRING]; va_list ap; if (level > PD_DEBUG && !sys_verbose) return; va_start(ap, fmt); vsnprintf(buf, MAXPDSTRING-1, fmt, ap); va_end(ap); strcat(buf, "\n"); dologpost(object, level, buf); } void startlogpost(const void *object, const int level, const char *fmt, ...) { char buf[MAXPDSTRING]; va_list ap; if (level > PD_DEBUG && !sys_verbose) return; va_start(ap, fmt); vsnprintf(buf, MAXPDSTRING-1, fmt, ap); va_end(ap); dologpost(object, level, buf); } void post(const char *fmt, ...) { char buf[MAXPDSTRING]; va_list ap; t_int arg[8]; int i; va_start(ap, fmt); vsnprintf(buf, MAXPDSTRING-1, fmt, ap); va_end(ap); strcat(buf, "\n"); dopost(buf); } void startpost(const char *fmt, ...) { char buf[MAXPDSTRING]; va_list ap; t_int arg[8]; int i; va_start(ap, fmt); vsnprintf(buf, MAXPDSTRING-1, fmt, ap); va_end(ap); dopost(buf); } void poststring(const char *s) { dopost(" "); dopost(s); } void postatom(int argc, const t_atom *argv) { int i; for (i = 0; i < argc; i++) { char buf[MAXPDSTRING]; atom_string(argv+i, buf, MAXPDSTRING); poststring(buf); } } void postfloat(t_float f) { char buf[80]; t_atom a; SETFLOAT(&a, f); postatom(1, &a); } void endpost(void) { if (STUFF->st_printhook) (*STUFF->st_printhook)("\n"); else if (sys_printtostderr) fprintf(stderr, "\n"); else post(""); } /* keep this in the Pd app for binary extern compatibility but don't include in libpd because it conflicts with the posix pd_error(0, ) function. */ #ifdef PD_INTERNAL EXTERN void error(const char *fmt, ...) { char buf[MAXPDSTRING]; va_list ap; t_int arg[8]; int i; va_start(ap, fmt); vsnprintf(buf, MAXPDSTRING-1, fmt, ap); va_end(ap); strcat(buf, "\n"); doerror(NULL, buf); } #endif /* deprecated in favor of logpost() */ void verbose(int level, const char *fmt, ...) { char buf[MAXPDSTRING]; va_list ap; if (level > sys_verbose) return; va_start(ap, fmt); vsnprintf(buf, MAXPDSTRING-1, fmt, ap); va_end(ap); strcat(buf, "\n"); /* log levels for verbose() traditionally start at -3, so we have to adjust it before passing it on to dologpost() */ dologpost(NULL, level + 3, buf); } /* here's the good way to log errors -- keep a pointer to the offending or offended object around so the user can search for it later. */ static const void *error_object; static char error_string[256]; void canvas_finderror(const void *object); void pd_error(const void *object, const char *fmt, ...) { char buf[MAXPDSTRING]; va_list ap; t_int arg[8]; int i; static int saidit = 0; va_start(ap, fmt); vsnprintf(buf, MAXPDSTRING-1, fmt, ap); va_end(ap); strcat(buf, "\n"); doerror(object, buf); error_object = object; strncpy(error_string, buf, 256); error_string[255] = 0; if (object && !saidit) { if (sys_havegui()) logpost(NULL, 4, "... you might be able to track this down from the Find menu."); saidit = 1; } } void glob_finderror(t_pd *dummy) { if (!error_object) post("no findable error yet"); else { post("last trackable error:"); post("%s", error_string); canvas_finderror(error_object); } } void glob_findinstance(t_pd *dummy, t_symbol*s) { // revert s to (potential) pointer to object PD_LONGINTTYPE obj = 0; const char*addr; if(!s || !s->s_name) return; addr = s->s_name; if (('.' != addr[0]) && ('0' != addr[0])) return; if (!sscanf(addr+1, "x%lx", &obj)) return; if(!obj) return; canvas_finderror((void *)obj); } void bug(const char *fmt, ...) { char buf[MAXPDSTRING]; va_list ap; t_int arg[8]; int i; va_start(ap, fmt); vsnprintf(buf, MAXPDSTRING-1, fmt, ap); va_end(ap); pd_error(0, "consistency check failed: %s", buf); } /* don't use these. They're included for binary compatibility with old externs but never worked and now do nothing. */ void sys_logerror(const char *object, const char *s) {} void sys_unixerror(const char *object) {} void sys_ouch(void) {} ================================================ FILE: libs/libpd/pure-data/src/s_stuff.h ================================================ #pragma once /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* Audio and MIDI I/O, and other scheduling and system stuff. */ /* NOTE: this file describes Pd implementation details which may change in future releases. The public (stable) API is in m_pd.h. */ /* in s_path.c */ typedef struct _namelist /* element in a linked list of stored strings */ { struct _namelist *nl_next; /* next in list */ char *nl_string; /* the string */ } t_namelist; t_namelist *namelist_append(t_namelist *listwas, const char *s, int allowdup); EXTERN t_namelist *namelist_append_files(t_namelist *listwas, const char *s); void namelist_free(t_namelist *listwas); const char *namelist_get(const t_namelist *namelist, int n); void sys_setextrapath(const char *p); extern int sys_usestdpath; int sys_open_absolute(const char *name, const char* ext, char *dirresult, char **nameresult, unsigned int size, int bin, int *fdp); int sys_trytoopenone(const char *dir, const char *name, const char* ext, char *dirresult, char **nameresult, unsigned int size, int bin); t_symbol *sys_decodedialog(t_symbol *s); /* s_file.c */ void sys_loadpreferences(const char *filename, int startingup); void sys_savepreferences(const char *filename); extern int sys_defeatrt; extern t_symbol *sys_flags; /* s_main.c */ extern int sys_debuglevel; extern int sys_verbose; extern int sys_noloadbang; EXTERN int sys_havegui(void); extern const char *sys_guicmd; EXTERN int sys_nearestfontsize(int fontsize); extern int sys_defaultfont; EXTERN t_symbol *sys_libdir; /* library directory for auxiliary files */ /* s_loader.c */ typedef int (*loader_t)(t_canvas *canvas, const char *classname, const char*path); /* callback type */ EXTERN int sys_load_lib(t_canvas *canvas, const char *classname); EXTERN void sys_register_loader(loader_t loader); EXTERN const char**sys_get_dllextensions(void); /* s_audio.c */ #define MAXAUDIOINDEV 4 #define MAXAUDIOOUTDEV 4 typedef struct _audiosettings { int a_api; int a_nindev; int a_indevvec[MAXAUDIOINDEV]; int a_nchindev; int a_chindevvec[MAXAUDIOINDEV]; int a_noutdev; int a_outdevvec[MAXAUDIOOUTDEV]; int a_nchoutdev; int a_choutdevvec[MAXAUDIOOUTDEV]; int a_srate; int a_advance; int a_callback; int a_blocksize; } t_audiosettings; #define SENDDACS_NO 0 /* return values for sys_send_dacs() */ #define SENDDACS_YES 1 #define SENDDACS_SLEPT 2 #define DEFDACBLKSIZE 64 #define DEFDACSAMPLERATE 48000 /* s_audio.c */ #define API_NONE 0 #define API_ALSA 1 #define API_OSS 2 #define API_MMIO 3 #define API_PORTAUDIO 4 #define API_JACK 5 #define API_SGI 6 /* gone */ #define API_AUDIOUNIT 7 #define API_ESD 8 /* no idea what this was, probably gone now */ #define API_DUMMY 9 /* figure out which API should be the default. The one we judge most likely to offer a working device takes precedence so that if you start up Pd for the first time there's a reasonable chance you'll have sound. (You'd think portaudio would be best but it seems to default to jack on linux, and on Windows we only use it for ASIO). If nobody shows up, define DUMMY and make it the default.*/ #if defined(USEAPI_ALSA) # define API_DEFAULT API_ALSA # define API_DEFSTRING "ALSA" #elif defined(USEAPI_PORTAUDIO) # define API_DEFAULT API_PORTAUDIO # define API_DEFSTRING "portaudio" #elif defined(USEAPI_OSS) # define API_DEFAULT API_OSS # define API_DEFSTRING "OSS" #elif defined(USEAPI_AUDIOUNIT) # define API_DEFAULT API_AUDIOUNIT # define API_DEFSTRING "AudioUnit" #elif defined(USEAPI_ESD) # define API_DEFAULT API_ESD # define API_DEFSTRING "ESD (?)" #elif defined(USEAPI_JACK) # define API_DEFAULT API_JACK # define API_DEFSTRING "Jack audio connection kit" #elif defined(USEAPI_MMIO) # define API_DEFAULT API_MMIO # define API_DEFSTRING "MMIO" #else # ifndef USEAPI_DUMMY /* we need at least one so bring in the dummy */ # define USEAPI_DUMMY # endif /* USEAPI_DUMMY */ # define API_DEFAULT API_DUMMY # define API_DEFSTRING "dummy audio" #endif #define DEFAULTAUDIODEV 0 #define DEFMIDIDEV 0 #define DEFAULTSRATE 44100 #if defined(_WIN32) #define DEFAULTADVANCE 80 #elif defined(__APPLE__) #define DEFAULTADVANCE 5 /* this is in addition to their own delay */ #else #define DEFAULTADVANCE 25 #endif typedef void (*t_audiocallback)(void); extern int sys_schedadvance; void sys_set_audio_state(int onoff); int sys_send_dacs(void); void sys_reportidle(void); void sys_listdevs(void); EXTERN void sys_set_audio_settings(t_audiosettings *as); EXTERN void sys_get_audio_settings(t_audiosettings *as); EXTERN void sys_reopen_audio(void); EXTERN void sys_close_audio(void); /* return true if the interface prefers always being open (ala jack) : */ EXTERN int audio_shouldkeepopen(void); EXTERN int audio_isopen(void); /* true if audio interface is open */ EXTERN int sys_audiodevnametonumber(int output, const char *name); EXTERN void sys_audiodevnumbertoname(int output, int devno, char *name, int namesize); EXTERN void sys_get_audio_devs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int *canmulti, int *cancallback, int maxndev, int devdescsize, int api); EXTERN void sys_get_audio_apis(char *buf); /* audio API specific functions */ int pa_open_audio(int inchans, int outchans, int rate, t_sample *soundin, t_sample *soundout, int framesperbuf, int nbuffers, int indeviceno, int outdeviceno, t_audiocallback callback); void pa_close_audio(void); int pa_send_dacs(void); void pa_listdevs(void); void pa_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int *canmulti, int maxndev, int devdescsize); int oss_open_audio(int naudioindev, int *audioindev, int nchindev, int *chindev, int naudiooutdev, int *audiooutdev, int nchoutdev, int *choutdev, int rate, int blocksize); void oss_close_audio(void); int oss_send_dacs(void); void oss_reportidle(void); void oss_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int *canmulti, int maxndev, int devdescsize); int alsa_open_audio(int naudioindev, int *audioindev, int nchindev, int *chindev, int naudiooutdev, int *audiooutdev, int nchoutdev, int *choutdev, int rate, int blocksize); void alsa_close_audio(void); int alsa_send_dacs(void); void alsa_reportidle(void); void alsa_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int *canmulti, int maxndev, int devdescsize); int jack_open_audio(int inchans, int outchans, t_audiocallback callback); void jack_close_audio(void); int jack_send_dacs(void); void jack_reportidle(void); void jack_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int *canmulti, int maxndev, int devdescsize); void jack_listdevs(void); void jack_client_name(const char *name); void jack_autoconnect(int); int mmio_open_audio(int naudioindev, int *audioindev, int nchindev, int *chindev, int naudiooutdev, int *audiooutdev, int nchoutdev, int *choutdev, int rate, int blocksize); void mmio_close_audio(void); void mmio_reportidle(void); int mmio_send_dacs(void); void mmio_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int *canmulti, int maxndev, int devdescsize); int audiounit_open_audio(int naudioindev, int *audioindev, int nchindev, int *chindev, int naudiooutdev, int *audiooutdev, int nchoutdev, int *choutdev, int rate); void audiounit_close_audio(void); int audiounit_send_dacs(void); void audiounit_listdevs(void); void audiounit_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int *canmulti, int maxndev, int devdescsize); int esd_open_audio(int naudioindev, int *audioindev, int nchindev, int *chindev, int naudiooutdev, int *audiooutdev, int nchoutdev, int *choutdev, int rate); void esd_close_audio(void); int esd_send_dacs(void); void esd_listdevs(void); void esd_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int *canmulti, int maxndev, int devdescsize); int dummy_open_audio(int nin, int nout, int sr); int dummy_close_audio(void); int dummy_send_dacs(void); void dummy_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int *canmulti, int maxndev, int devdescsize); void dummy_listdevs(void); /* s_midi.c */ #define MAXMIDIINDEV 16 /* max. number of input ports */ #define MAXMIDIOUTDEV 16 /* max. number of output ports */ extern int sys_midiapi; extern int sys_nmidiin; extern int sys_nmidiout; extern int sys_midiindevlist[]; extern int sys_midioutdevlist[]; EXTERN void sys_open_midi(int nmidiin, int *midiinvec, int nmidiout, int *midioutvec, int enable); EXTERN void sys_get_midi_apis(char *buf); EXTERN void sys_get_midi_devs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int maxndev, int devdescsize); EXTERN void sys_get_midi_params(int *pnmidiindev, int *pmidiindev, int *pnmidioutdev, int *pmidioutdev); EXTERN int sys_mididevnametonumber(int output, const char *name); EXTERN void sys_mididevnumbertoname(int output, int devno, char *name, int namesize); EXTERN void sys_reopen_midi(void); EXTERN void sys_close_midi(void); EXTERN void sys_putmidimess(int portno, int a, int b, int c); EXTERN void sys_putmidibyte(int portno, int a); EXTERN void sys_poll_midi(void); EXTERN void sys_midibytein(int portno, int byte); void sys_listmididevs(void); EXTERN void sys_set_midi_api(int whichapi); /* implemented in the system dependent MIDI code (s_midi_pm.c, etc. ) */ void midi_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int maxndev, int devdescsize); void sys_do_open_midi(int nmidiindev, int *midiindev, int nmidioutdev, int *midioutdev); #ifdef USEAPI_ALSA EXTERN void sys_alsa_putmidimess(int portno, int a, int b, int c); EXTERN void sys_alsa_putmidibyte(int portno, int a); EXTERN void sys_alsa_poll_midi(void); EXTERN void sys_alsa_close_midi(void); /* implemented in the system dependent MIDI code (s_midi_pm.c, etc. ) */ void midi_alsa_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int maxndev, int devdescsize); void sys_alsa_do_open_midi(int nmidiindev, int *midiindev, int nmidioutdev, int *midioutdev); #endif /* m_sched.c */ EXTERN void sys_log_error(int type); #define ERR_NOTHING 0 #define ERR_ADCSLEPT 1 #define ERR_DACSLEPT 2 #define ERR_RESYNC 3 #define ERR_DATALATE 4 #define SCHED_AUDIO_NONE 0 #define SCHED_AUDIO_POLL 1 #define SCHED_AUDIO_CALLBACK 2 void sched_set_using_audio(int flag); extern int sys_sleepgrain; /* override value set in command line */ EXTERN int sched_get_sleepgrain( void); /* returns actual value */ /* s_inter.c */ EXTERN void sys_microsleep( void); EXTERN void sys_init_fdpoll(void); EXTERN void sys_bail(int exitcode); EXTERN int sys_pollgui(void); EXTERN_STRUCT _socketreceiver; #define t_socketreceiver struct _socketreceiver typedef void (*t_socketnotifier)(void *x, int n); typedef void (*t_socketreceivefn)(void *x, t_binbuf *b); /* from addr sockaddr_storage struct, optional */ typedef void (*t_socketfromaddrfn)(void *x, const void *fromaddr); EXTERN t_socketreceiver *socketreceiver_new(void *owner, t_socketnotifier notifier, t_socketreceivefn socketreceivefn, int udp); EXTERN void socketreceiver_read(t_socketreceiver *x, int fd); EXTERN void socketreceiver_set_fromaddrfn(t_socketreceiver *x, t_socketfromaddrfn fromaddrfn); EXTERN void sys_sockerror(const char *s); EXTERN void sys_closesocket(int fd); EXTERN unsigned char *sys_getrecvbuf(unsigned int *size); typedef void (*t_fdpollfn)(void *ptr, int fd); EXTERN void sys_addpollfn(int fd, t_fdpollfn fn, void *ptr); EXTERN void sys_rmpollfn(int fd); #if defined(USEAPI_OSS) || defined(USEAPI_ALSA) void sys_setalarm(int microsec); #endif void sys_set_priority(int higher); extern int sys_hipriority; /* real-time flag, true if priority boosted */ /* s_print.c */ typedef void (*t_printhook)(const char *s); /* set this to override printing; used as default for STUFF->st_printhook */ extern t_printhook sys_printhook; extern int sys_printtostderr; /* jsarlo { */ EXTERN int sys_externalschedlib; EXTERN t_sample* get_sys_soundout(void); EXTERN t_sample* get_sys_soundin(void); EXTERN int* get_sys_main_advance(void); EXTERN double* get_sys_time_per_dsp_tick(void); EXTERN int* get_sys_schedblocksize(void); EXTERN double* get_sys_time(void); EXTERN t_float* get_sys_dacsr(void); EXTERN int* get_sys_sleepgrain(void); EXTERN int* get_sys_schedadvance(void); EXTERN void sys_initmidiqueue(void); EXTERN void sched_tick(void); EXTERN void sys_pollmidiqueue(void); EXTERN void sys_setchsr(int chin, int chout, int sr); EXTERN void inmidi_realtimein(int portno, int cmd); EXTERN void inmidi_byte(int portno, int byte); EXTERN void inmidi_sysex(int portno, int byte); EXTERN void inmidi_noteon(int portno, int channel, int pitch, int velo); EXTERN void inmidi_controlchange(int portno, int channel, int ctlnumber, int value); EXTERN void inmidi_programchange(int portno, int channel, int value); EXTERN void inmidi_pitchbend(int portno, int channel, int value); EXTERN void inmidi_aftertouch(int portno, int channel, int value); EXTERN void inmidi_polyaftertouch(int portno, int channel, int pitch, int value); /* } jsarlo */ EXTERN int sys_zoom_open; struct _instancestuff { t_namelist *st_externlist; t_namelist *st_searchpath; t_namelist *st_staticpath; t_namelist *st_helppath; t_namelist *st_temppath; /* temp search paths ie. -path on commandline */ int st_schedblocksize; /* audio block size for scheduler */ int st_blocksize; /* audio I/O block size in sample frames */ t_float st_dacsr; /* I/O sample rate */ int st_inchannels; int st_outchannels; t_sample *st_soundout; t_sample *st_soundin; double st_time_per_dsp_tick; /* obsolete - included for GEM?? */ t_printhook st_printhook; /* set this to override per-instance printing */ void *st_impdata; /* optional implementation-specific data for libpd, etc */ }; #define STUFF (pd_this->pd_stuff) /* escape characters for tcl/tk * escapes special characters ("{}\") in the string 'src', which * has a maximum length of 'srclen' and might be 0-terminated, * and writes them into the 'dstlen' sized output buffer 'dst' * the result is zero-terminated; if the 'dst' buffer cannot hold the * fully escaped 'src' string, the result might be incomplete. * 'srclen' can be 0, in which case the 'src' string must be 0-terminated. */ EXTERN char*pdgui_strnescape(char* dst, size_t dstlen, const char*src, size_t srclen); ================================================ FILE: libs/libpd/pure-data/src/s_utf8.c ================================================ /* Basic UTF-8 manipulation routines by Jeff Bezanson placed in the public domain Fall 2005 This code is designed to provide the utilities you need to manipulate UTF-8 as an internal string encoding. These functions do not perform the error checking normally needed when handling UTF-8 data, so if you happen to be from the Unicode Consortium you will want to flay me alive. I do this because error checking can be performed at the boundaries (I/O), with these routines reserved for higher performance on data known to be valid. modified by Bryan Jurish (moo) March 2009 + removed some unneeded functions (escapes, printf etc), added others modified by IOhannes m zmölnig (umlaeute) Nov 2021 + convert native strings to UTF-8 */ #include #include #include "s_utf8.h" static const uint32_t offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, 0x03C82080UL, 0xFA082080UL, 0x82082080UL }; static const char trailingBytesForUTF8[256] = { 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, 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, 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, 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, 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, 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, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 }; /* returns length of next utf-8 sequence */ int u8_seqlen(const char *s) { return trailingBytesForUTF8[(unsigned int)(unsigned char)s[0]] + 1; } /* conversions without error checking only works for valid UTF-8, i.e. no 5- or 6-byte sequences srcsz = source size in bytes, or -1 if 0-terminated sz = dest size in # of wide characters returns # characters converted dest will always be L'\0'-terminated, even if there isn't enough room for all the characters. if sz = srcsz+1 (i.e. 4*srcsz+4 bytes), there will always be enough space. */ int u8_utf8toucs2(uint16_t *dest, int sz, const char *src, int srcsz) { uint16_t ch; const char *src_end = src + srcsz; int nb; int i=0; while (i < sz-1) { nb = trailingBytesForUTF8[(unsigned char)*src]; if (srcsz == -1) { if (*src == 0) goto done_toucs; } else { if (src + nb >= src_end) goto done_toucs; } ch = 0; switch (nb) { case 3: ch += (unsigned char)*src++; ch <<= 6; /* falls through */ case 2: ch += (unsigned char)*src++; ch <<= 6; /* falls through */ case 1: ch += (unsigned char)*src++; ch <<= 6; /* falls through */ case 0: ch += (unsigned char)*src++; /* falls through */ } ch -= offsetsFromUTF8[nb]; dest[i++] = ch; } done_toucs: dest[i] = 0; return i; } /* srcsz = number of source characters, or -1 if 0-terminated sz = size of dest buffer in bytes returns # characters converted dest will only be '\0'-terminated if there is enough space. this is for consistency; imagine there are 2 bytes of space left, but the next character requires 3 bytes. in this case we could NUL-terminate, but in general we can't when there's insufficient space. therefore this function only NUL-terminates if all the characters fit, and there's space for the NUL as well. the destination string will never be bigger than the source string. */ int u8_ucs2toutf8(char *dest, int sz, const uint16_t *src, int srcsz) { uint16_t ch; int i = 0; char *dest_end = dest + sz; while (srcsz<0 ? src[i]!=0 : i < srcsz) { ch = src[i]; if (ch < 0x80) { if (dest >= dest_end) return i; *dest++ = (char)ch; } else if (ch < 0x800) { if (dest >= dest_end-1) return i; *dest++ = (ch>>6) | 0xC0; *dest++ = (ch & 0x3F) | 0x80; } else { if (dest >= dest_end-2) return i; *dest++ = (ch>>12) | 0xE0; *dest++ = ((ch>>6) & 0x3F) | 0x80; *dest++ = (ch & 0x3F) | 0x80; } i++; } if (dest < dest_end) *dest = '\0'; return i; } /* moo: get byte length of character number, or 0 if not supported */ int u8_wc_nbytes(uint32_t ch) { if (ch < 0x80) return 1; if (ch < 0x800) return 2; if (ch < 0x10000) return 3; if (ch < 0x200000) return 4; return 0; /*-- bad input --*/ } int u8_wc_toutf8(char *dest, uint32_t ch) { if (ch < 0x80) { dest[0] = (char)ch; return 1; } if (ch < 0x800) { dest[0] = (ch>>6) | 0xC0; dest[1] = (ch & 0x3F) | 0x80; return 2; } if (ch < 0x10000) { dest[0] = (ch>>12) | 0xE0; dest[1] = ((ch>>6) & 0x3F) | 0x80; dest[2] = (ch & 0x3F) | 0x80; return 3; } if (ch < 0x110000) { dest[0] = (ch>>18) | 0xF0; dest[1] = ((ch>>12) & 0x3F) | 0x80; dest[2] = ((ch>>6) & 0x3F) | 0x80; dest[3] = (ch & 0x3F) | 0x80; return 4; } return 0; } /*-- moo --*/ int u8_wc_toutf8_nul(char *dest, uint32_t ch) { int sz = u8_wc_toutf8(dest,ch); dest[sz] = '\0'; return sz; } /* charnum => byte offset */ int u8_offset(const char *str, int charnum) { const char *string = str; while (charnum > 0 && *string != '\0') { if (*string++ & 0x80) { if (!isutf(*string)) { ++string; if (!isutf(*string)) { ++string; if (!isutf(*string)) { ++string; } } } } --charnum; } return (int)(string - str); } /* byte offset => charnum */ int u8_charnum(const char *s, int offset) { int charnum = 0; const char *string = s; const char *const end = string + offset; while (string < end && *string != '\0') { if (*string++ & 0x80) { if (!isutf(*string)) { ++string; if (!isutf(*string)) { ++string; if (!isutf(*string)) { ++string; } } } } ++charnum; } return charnum; } /* reads the next utf-8 sequence out of a string, updating an index */ uint32_t u8_nextchar(const char *s, int *i) { uint32_t ch = 0; int sz = 0; do { ch <<= 6; ch += (unsigned char)s[(*i)++]; sz++; } while (s[*i] && !isutf(s[*i])); ch -= offsetsFromUTF8[sz-1]; return ch; } /* number of characters */ int u8_strlen(const char *s) { int count = 0; int i = 0; while (u8_nextchar(s, &i) != 0) count++; return count; } void u8_inc(const char *s, int *i) { if (s[(*i)++] & 0x80) { if (!isutf(s[*i])) { ++(*i); if (!isutf(s[*i])) { ++(*i); if (!isutf(s[*i])) { ++(*i); } } } } } void u8_dec(const char *s, int *i) { (void)(isutf(s[--(*i)]) || isutf(s[--(*i)]) || isutf(s[--(*i)]) || --(*i)); } /* srcsz = number of source characters, or -1 if 0-terminated sz = size of dest buffer in bytes returns # characters converted */ #ifdef _WIN32 #include #endif int u8_nativetoutf8(char *dest, int sz, const char *src, int srcsz) { int len; #ifdef _WIN32 int res; wchar_t*wbuf = 0; if(srcsz < 0) { len = MultiByteToWideChar(CP_OEMCP, 0, src, srcsz, wbuf, 0); } else { len = (srcsz < sz)?srcsz:sz; } wbuf = getbytes(len * sizeof(*wbuf)); res = MultiByteToWideChar(CP_OEMCP, 0, src, srcsz, wbuf, len); if(res) { res = WideCharToMultiByte(CP_UTF8, 0, wbuf, len, dest, sz, 0, 0); } freebytes(wbuf, len * sizeof(*wbuf)); return (res)?len:0; #endif /* on other systems, we use UTF-8 for everything, so this is a no-op */ if(srcsz < 0) srcsz = strlen(src) + 1; len = (srcsz < sz)?srcsz:sz; strncpy(dest, src, len); return len; } ================================================ FILE: libs/libpd/pure-data/src/s_utf8.h ================================================ #ifndef S_UTF8_H #define S_UTF8_H #include "m_pd.h" #ifndef UCS4 # define UCS4 uint32_t #endif /* UTF8_SUPPORT_FULL_UCS4 * define this to support the full potential range of UCS-4 codepoints * (in anticipation of a future UTF-8 standard) */ /*#define UTF8_SUPPORT_FULL_UCS4 1*/ #undef UTF8_SUPPORT_FULL_UCS4 /* UTF8_MAXBYTES * maximum number of bytes required to represent a single character in UTF-8 * * UTF8_MAXBYTES1 = UTF8_MAXBYTES+1 * maximum bytes per character including NUL terminator */ #ifdef UTF8_SUPPORT_FULL_UCS4 # ifndef UTF8_MAXBYTES # define UTF8_MAXBYTES 6 # endif # ifndef UTF8_MAXBYTES1 # define UTF8_MAXBYTES1 7 # endif #else # ifndef UTF8_MAXBYTES # define UTF8_MAXBYTES 4 # endif # ifndef UTF8_MAXBYTES1 # define UTF8_MAXBYTES1 5 # endif #endif /*--/moo--*/ /* is c the start of a utf8 sequence? */ #define isutf(c) (((c)&0xC0)!=0x80) /* convert UTF-8 data to UCS-2 wide character */ int u8_utf8toucs2(uint16_t *dest, int sz, const char *src, int srcsz); /* the opposite conversion */ int u8_ucs2toutf8(char *dest, int sz, const uint16_t *src, int srcsz); /* moo: get byte length of character number, or 0 if not supported */ int u8_wc_nbytes(uint32_t ch); /* moo: compute required storage for UTF-8 encoding of 's[0..n-1]' */ int u8_wcs_nbytes(const uint32_t *ucs, int size); /* single character to UTF-8, no NUL termination */ int u8_wc_toutf8(char *dest, uint32_t ch); /* moo: single character to UTF-8, with NUL termination */ int u8_wc_toutf8_nul(char *dest, uint32_t ch); /* character number to byte offset */ int u8_offset(const char *str, int charnum); /* byte offset to character number */ int u8_charnum(const char *s, int offset); /* return next character, updating an index variable */ uint32_t u8_nextchar(const char *s, int *i); /* move to next character */ void u8_inc(const char *s, int *i); /* move to previous character */ void u8_dec(const char *s, int *i); /* moo: move pointer to next character */ void u8_inc_ptr(char **sp); /* moo: move pointer to previous character */ void u8_dec_ptr(char **sp); /* returns length of next utf-8 sequence */ int u8_seqlen(const char *s); /* convert a string in the current encoding to UTF-8 */ int u8_nativetoutf8(char* dest, int sz, const char* src, int srcsz); #endif /* S_UTF8_H */ ================================================ FILE: libs/libpd/pure-data/src/x_acoustics.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* utility functions for signals */ #include "m_pd.h" #include #define LOGTEN 2.302585092994046 t_float mtof(t_float f) { if (f <= -1500) return(0); else if (f > 1499) return(mtof(1499)); else return (8.17579891564 * exp(.0577622650 * f)); } t_float ftom(t_float f) { return (f > 0 ? 17.3123405046 * log(.12231220585 * f) : -1500); } t_float powtodb(t_float f) { if (f <= 0) return (0); else { t_float val = 100 + 10./LOGTEN * log(f); return (val < 0 ? 0 : val); } } t_float rmstodb(t_float f) { if (f <= 0) return (0); else { t_float val = 100 + 20./LOGTEN * log(f); return (val < 0 ? 0 : val); } } t_float dbtopow(t_float f) { if (f <= 0) return(0); else { if (f > 870) f = 870; return (exp((LOGTEN * 0.1) * (f-100.))); } } t_float dbtorms(t_float f) { if (f <= 0) return(0); else { if (f > 485) f = 485; } return (exp((LOGTEN * 0.05) * (f-100.))); } /* ------------- corresponding objects ----------------------- */ static t_class *mtof_class; static void *mtof_new(void) { t_object *x = (t_object *)pd_new(mtof_class); outlet_new(x, &s_float); return (x); } static void mtof_float(t_object *x, t_float f) { outlet_float(x->ob_outlet, mtof(f)); } static t_class *ftom_class; static void *ftom_new(void) { t_object *x = (t_object *)pd_new(ftom_class); outlet_new(x, &s_float); return (x); } static void ftom_float(t_object *x, t_float f) { outlet_float(x->ob_outlet, ftom(f)); } static t_class *rmstodb_class; static void *rmstodb_new(void) { t_object *x = (t_object *)pd_new(rmstodb_class); outlet_new(x, &s_float); return (x); } static void rmstodb_float(t_object *x, t_float f) { outlet_float(x->ob_outlet, rmstodb(f)); } static t_class *powtodb_class; static void *powtodb_new(void) { t_object *x = (t_object *)pd_new(powtodb_class); outlet_new(x, &s_float); return (x); } static void powtodb_float(t_object *x, t_float f) { outlet_float(x->ob_outlet, powtodb(f)); } static t_class *dbtopow_class; static void *dbtopow_new(void) { t_object *x = (t_object *)pd_new(dbtopow_class); outlet_new(x, &s_float); return (x); } static void dbtopow_float(t_object *x, t_float f) { outlet_float(x->ob_outlet, dbtopow(f)); } static t_class *dbtorms_class; static void *dbtorms_new(void) { t_object *x = (t_object *)pd_new(dbtorms_class); outlet_new(x, &s_float); return (x); } static void dbtorms_float(t_object *x, t_float f) { outlet_float(x->ob_outlet, dbtorms(f)); } void x_acoustics_setup(void) { t_symbol *s = gensym("acoustics.pd"); mtof_class = class_new(gensym("mtof"), mtof_new, 0, sizeof(t_object), 0, 0); class_addfloat(mtof_class, (t_method)mtof_float); class_sethelpsymbol(mtof_class, s); ftom_class = class_new(gensym("ftom"), ftom_new, 0, sizeof(t_object), 0, 0); class_addfloat(ftom_class, (t_method)ftom_float); class_sethelpsymbol(ftom_class, s); powtodb_class = class_new(gensym("powtodb"), powtodb_new, 0, sizeof(t_object), 0, 0); class_addfloat(powtodb_class, (t_method)powtodb_float); class_sethelpsymbol(powtodb_class, s); rmstodb_class = class_new(gensym("rmstodb"), rmstodb_new, 0, sizeof(t_object), 0, 0); class_addfloat(rmstodb_class, (t_method)rmstodb_float); class_sethelpsymbol(rmstodb_class, s); dbtopow_class = class_new(gensym("dbtopow"), dbtopow_new, 0, sizeof(t_object), 0, 0); class_addfloat(dbtopow_class, (t_method)dbtopow_float); class_sethelpsymbol(dbtopow_class, s); dbtorms_class = class_new(gensym("dbtorms"), dbtorms_new, 0, sizeof(t_object), 0, 0); class_addfloat(dbtorms_class, (t_method)dbtorms_float); class_sethelpsymbol(dbtorms_class, s); } ================================================ FILE: libs/libpd/pure-data/src/x_arithmetic.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* arithmetic: binops ala C language. The 4 functions and relationals are done on floats; the logical and bitwise binops convert their inputs to int and their outputs back to float. */ #include "m_pd.h" #include #if PD_FLOATSIZE == 32 # define POW powf # define SIN sinf # define COS cosf # define ATAN atanf # define ATAN2 atan2f # define SQRT sqrtf # define LOG logf # define EXP expf # define FABS fabsf # define MAXLOG 87.3365 /* log(FLT_MAX / 4.) */ #else # define POW pow # define SIN sin # define COS cos # define ATAN atan # define ATAN2 atan2 # define SQRT sqrt # define LOG log # define EXP exp # define FABS fabs # define MAXLOG 708.396 /* log(DBL_MAX / 4.) */ #endif typedef struct _binop { t_object x_obj; t_float x_f1; t_float x_f2; } t_binop; /* ------------------ binop1: +, -, *, / ----------------------------- */ static void *binop1_new(t_class *floatclass, t_floatarg f) { t_binop *x = (t_binop *)pd_new(floatclass); outlet_new(&x->x_obj, &s_float); floatinlet_new(&x->x_obj, &x->x_f2); x->x_f1 = 0; x->x_f2 = f; return (x); } /* --------------------- addition ------------------------------- */ static t_class *binop1_plus_class; static void *binop1_plus_new(t_floatarg f) { return (binop1_new(binop1_plus_class, f)); } static void binop1_plus_bang(t_binop *x) { outlet_float(x->x_obj.ob_outlet, x->x_f1 + x->x_f2); } static void binop1_plus_float(t_binop *x, t_float f) { outlet_float(x->x_obj.ob_outlet, (x->x_f1 = f) + x->x_f2); } /* --------------------- subtraction ------------------------------- */ static t_class *binop1_minus_class; static void *binop1_minus_new(t_floatarg f) { return (binop1_new(binop1_minus_class, f)); } static void binop1_minus_bang(t_binop *x) { outlet_float(x->x_obj.ob_outlet, x->x_f1 - x->x_f2); } static void binop1_minus_float(t_binop *x, t_float f) { outlet_float(x->x_obj.ob_outlet, (x->x_f1 = f) - x->x_f2); } /* --------------------- multiplication ------------------------------- */ static t_class *binop1_times_class; static void *binop1_times_new(t_floatarg f) { return (binop1_new(binop1_times_class, f)); } static void binop1_times_bang(t_binop *x) { outlet_float(x->x_obj.ob_outlet, x->x_f1 * x->x_f2); } static void binop1_times_float(t_binop *x, t_float f) { outlet_float(x->x_obj.ob_outlet, (x->x_f1 = f) * x->x_f2); } /* --------------------- division ------------------------------- */ static t_class *binop1_div_class; static void *binop1_div_new(t_floatarg f) { return (binop1_new(binop1_div_class, f)); } static void binop1_div_bang(t_binop *x) { outlet_float(x->x_obj.ob_outlet, (x->x_f2 != 0 ? x->x_f1 / x->x_f2 : 0)); } static void binop1_div_float(t_binop *x, t_float f) { x->x_f1 = f; outlet_float(x->x_obj.ob_outlet, (x->x_f2 != 0 ? x->x_f1 / x->x_f2 : 0)); } /* ------------------------ pow -------------------------------- */ static t_class *binop1_pow_class; static void *binop1_pow_new(t_floatarg f) { return (binop1_new(binop1_pow_class, f)); } static void binop1_pow_bang(t_binop *x) { t_float r = (x->x_f1 == 0 && x->x_f2 < 0) || (x->x_f1 < 0 && (x->x_f2 - (int)x->x_f2) != 0) ? 0 : POW(x->x_f1, x->x_f2); outlet_float(x->x_obj.ob_outlet, r); } static void binop1_pow_float(t_binop *x, t_float f) { x->x_f1 = f; binop1_pow_bang(x); } /* ------------------------ max -------------------------------- */ static t_class *binop1_max_class; static void *binop1_max_new(t_floatarg f) { return (binop1_new(binop1_max_class, f)); } static void binop1_max_bang(t_binop *x) { outlet_float(x->x_obj.ob_outlet, (x->x_f1 > x->x_f2 ? x->x_f1 : x->x_f2)); } static void binop1_max_float(t_binop *x, t_float f) { x->x_f1 = f; outlet_float(x->x_obj.ob_outlet, (x->x_f1 > x->x_f2 ? x->x_f1 : x->x_f2)); } /* ------------------------ min -------------------------------- */ static t_class *binop1_min_class; static void *binop1_min_new(t_floatarg f) { return (binop1_new(binop1_min_class, f)); } static void binop1_min_bang(t_binop *x) { outlet_float(x->x_obj.ob_outlet, (x->x_f1 < x->x_f2 ? x->x_f1 : x->x_f2)); } static void binop1_min_float(t_binop *x, t_float f) { x->x_f1 = f; outlet_float(x->x_obj.ob_outlet, (x->x_f1 < x->x_f2 ? x->x_f1 : x->x_f2)); } /* ------------------ binop2: ==, !=, >, <, >=, <=. -------------------- */ static void *binop2_new(t_class *floatclass, t_floatarg f) { t_binop *x = (t_binop *)pd_new(floatclass); outlet_new(&x->x_obj, &s_float); floatinlet_new(&x->x_obj, &x->x_f2); x->x_f1 = 0; x->x_f2 = f; return (x); } /* --------------------- == ------------------------------- */ static t_class *binop2_ee_class; static void *binop2_ee_new(t_floatarg f) { return (binop2_new(binop2_ee_class, f)); } static void binop2_ee_bang(t_binop *x) { outlet_float(x->x_obj.ob_outlet, x->x_f1 == x->x_f2); } static void binop2_ee_float(t_binop *x, t_float f) { outlet_float(x->x_obj.ob_outlet, (x->x_f1 = f) == x->x_f2); } /* --------------------- != ------------------------------- */ static t_class *binop2_ne_class; static void *binop2_ne_new(t_floatarg f) { return (binop2_new(binop2_ne_class, f)); } static void binop2_ne_bang(t_binop *x) { outlet_float(x->x_obj.ob_outlet, x->x_f1 != x->x_f2); } static void binop2_ne_float(t_binop *x, t_float f) { outlet_float(x->x_obj.ob_outlet, (x->x_f1 = f) != x->x_f2); } /* --------------------- > ------------------------------- */ static t_class *binop2_gt_class; static void *binop2_gt_new(t_floatarg f) { return (binop2_new(binop2_gt_class, f)); } static void binop2_gt_bang(t_binop *x) { outlet_float(x->x_obj.ob_outlet, x->x_f1 > x->x_f2); } static void binop2_gt_float(t_binop *x, t_float f) { outlet_float(x->x_obj.ob_outlet, (x->x_f1 = f) > x->x_f2); } /* --------------------- < ------------------------------- */ static t_class *binop2_lt_class; static void *binop2_lt_new(t_floatarg f) { return (binop2_new(binop2_lt_class, f)); } static void binop2_lt_bang(t_binop *x) { outlet_float(x->x_obj.ob_outlet, x->x_f1 < x->x_f2); } static void binop2_lt_float(t_binop *x, t_float f) { outlet_float(x->x_obj.ob_outlet, (x->x_f1 = f) < x->x_f2); } /* --------------------- >= ------------------------------- */ static t_class *binop2_ge_class; static void *binop2_ge_new(t_floatarg f) { return (binop2_new(binop2_ge_class, f)); } static void binop2_ge_bang(t_binop *x) { outlet_float(x->x_obj.ob_outlet, x->x_f1 >= x->x_f2); } static void binop2_ge_float(t_binop *x, t_float f) { outlet_float(x->x_obj.ob_outlet, (x->x_f1 = f) >= x->x_f2); } /* --------------------- <= ------------------------------- */ static t_class *binop2_le_class; static void *binop2_le_new(t_floatarg f) { return (binop2_new(binop2_le_class, f)); } static void binop2_le_bang(t_binop *x) { outlet_float(x->x_obj.ob_outlet, x->x_f1 <= x->x_f2); } static void binop2_le_float(t_binop *x, t_float f) { outlet_float(x->x_obj.ob_outlet, (x->x_f1 = f) <= x->x_f2); } /* ------------- binop3: &, |, &&, ||, <<, >>, %, mod, div ------------------ */ static void *binop3_new(t_class *fixclass, t_floatarg f) { t_binop *x = (t_binop *)pd_new(fixclass); outlet_new(&x->x_obj, &s_float); floatinlet_new(&x->x_obj, &x->x_f2); x->x_f1 = 0; x->x_f2 = f; return (x); } /* --------------------------- & ---------------------------- */ static t_class *binop3_ba_class; static void *binop3_ba_new(t_floatarg f) { return (binop3_new(binop3_ba_class, f)); } static void binop2_ba_bang(t_binop *x) { outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1)) & (int)(x->x_f2)); } static void binop2_ba_float(t_binop *x, t_float f) { outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1 = f)) & (int)(x->x_f2)); } /* --------------------------- && ---------------------------- */ static t_class *binop3_la_class; static void *binop3_la_new(t_floatarg f) { return (binop3_new(binop3_la_class, f)); } static void binop2_la_bang(t_binop *x) { outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1)) && (int)(x->x_f2)); } static void binop2_la_float(t_binop *x, t_float f) { outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1 = f)) && (int)(x->x_f2)); } /* --------------------------- | ---------------------------- */ static t_class *binop3_bo_class; static void *binop3_bo_new(t_floatarg f) { return (binop3_new(binop3_bo_class, f)); } static void binop2_bo_bang(t_binop *x) { outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1)) | (int)(x->x_f2)); } static void binop2_bo_float(t_binop *x, t_float f) { outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1 = f)) | (int)(x->x_f2)); } /* --------------------------- || ---------------------------- */ static t_class *binop3_lo_class; static void *binop3_lo_new(t_floatarg f) { return (binop3_new(binop3_lo_class, f)); } static void binop2_lo_bang(t_binop *x) { outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1)) || (int)(x->x_f2)); } static void binop2_lo_float(t_binop *x, t_float f) { outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1 = f)) || (int)(x->x_f2)); } /* --------------------------- << ---------------------------- */ static t_class *binop3_ls_class; static void *binop3_ls_new(t_floatarg f) { return (binop3_new(binop3_ls_class, f)); } static void binop2_ls_bang(t_binop *x) { outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1)) << (int)(x->x_f2)); } static void binop2_ls_float(t_binop *x, t_float f) { outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1 = f)) << (int)(x->x_f2)); } /* --------------------------- >> ---------------------------- */ static t_class *binop3_rs_class; static void *binop3_rs_new(t_floatarg f) { return (binop3_new(binop3_rs_class, f)); } static void binop2_rs_bang(t_binop *x) { outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1)) >> (int)(x->x_f2)); } static void binop2_rs_float(t_binop *x, t_float f) { outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1 = f)) >> (int)(x->x_f2)); } /* --------------------------- % ---------------------------- */ static t_class *binop3_pc_class; static void *binop3_pc_new(t_floatarg f) { return (binop3_new(binop3_pc_class, f)); } static void binop2_pc_bang(t_binop *x) { int n2 = x->x_f2; /* apparently "%" raises an exception for INT_MIN and -1 */ if (n2 == -1) outlet_float(x->x_obj.ob_outlet, 0); else outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1)) % (n2 ? n2 : 1)); } static void binop2_pc_float(t_binop *x, t_float f) { int n2 = x->x_f2; if (n2 == -1) outlet_float(x->x_obj.ob_outlet, 0); else outlet_float(x->x_obj.ob_outlet, ((int)(x->x_f1 = f)) % (n2 ? n2 : 1)); } /* --------------------------- mod ---------------------------- */ static t_class *binop3_mod_class; static void *binop3_mod_new(t_floatarg f) { return (binop3_new(binop3_mod_class, f)); } static void binop3_mod_bang(t_binop *x) { int n2 = x->x_f2, result; if (n2 < 0) n2 = -n2; else if (!n2) n2 = 1; result = ((int)(x->x_f1)) % n2; if (result < 0) result += n2; outlet_float(x->x_obj.ob_outlet, (t_float)result); } static void binop3_mod_float(t_binop *x, t_float f) { x->x_f1 = f; binop3_mod_bang(x); } /* --------------------------- div ---------------------------- */ static t_class *binop3_div_class; static void *binop3_div_new(t_floatarg f) { return (binop3_new(binop3_div_class, f)); } static void binop3_div_bang(t_binop *x) { int n1 = x->x_f1, n2 = x->x_f2, result; if (n2 < 0) n2 = -n2; else if (!n2) n2 = 1; if (n1 < 0) n1 -= (n2-1); result = n1 / n2; outlet_float(x->x_obj.ob_outlet, (t_float)result); } static void binop3_div_float(t_binop *x, t_float f) { x->x_f1 = f; binop3_div_bang(x); } /* -------------------- mathematical functions ------------------ */ static t_class *sin_class; /* ----------- sin --------------- */ static void *sin_new(void) { t_object *x = (t_object *)pd_new(sin_class); outlet_new(x, &s_float); return (x); } static void sin_float(t_object *x, t_float f) { outlet_float(x->ob_outlet, SIN(f)); } static t_class *cos_class; /* ----------- cos --------------- */ static void *cos_new(void) { t_object *x = (t_object *)pd_new(cos_class); outlet_new(x, &s_float); return (x); } static void cos_float(t_object *x, t_float f) { outlet_float(x->ob_outlet, COS(f)); } static t_class *tan_class; /* ----------- tan --------------- */ static void *tan_new(void) { t_object *x = (t_object *)pd_new(tan_class); outlet_new(x, &s_float); return (x); } static void tan_float(t_object *x, t_float f) { t_float c = cosf(f); t_float t = (c == 0 ? 0 : SIN(f)/c); outlet_float(x->ob_outlet, t); } static t_class *atan_class; /* ----------- atan --------------- */ static void *atan_new(void) { t_object *x = (t_object *)pd_new(atan_class); outlet_new(x, &s_float); return (x); } static void atan_float(t_object *x, t_float f) { outlet_float(x->ob_outlet, ATAN(f)); } static t_class *atan2_class; /* ----------- atan2 --------------- */ typedef struct _atan2 { t_object x_ob; t_float x_f1; t_float x_f2; } t_atan2; static void *atan2_new(void) { t_atan2 *x = (t_atan2 *)pd_new(atan2_class); floatinlet_new(&x->x_ob, &x->x_f2); x->x_f1 = x->x_f2 = 0; outlet_new(&x->x_ob, &s_float); return (x); } static void atan2_bang(t_atan2 *x) { outlet_float(x->x_ob.ob_outlet, (x->x_f1 == 0 && x->x_f2 == 0 ? 0 : ATAN2(x->x_f1, x->x_f2))); } static void atan2_float(t_atan2 *x, t_float f) { x->x_f1 = f; atan2_bang(x); } static t_class *sqrt_class; /* ----------- sqrt --------------- */ static void *sqrt_new(void) { t_object *x = (t_object *)pd_new(sqrt_class); outlet_new(x, &s_float); return (x); } static void sqrt_float(t_object *x, t_float f) { t_float r = (f > 0 ? SQRT(f) : 0); outlet_float(x->ob_outlet, r); } /* --------------------- log ------------------------------- */ static t_class *binop1_log_class; static void *binop1_log_new(t_floatarg f) { return (binop1_new(binop1_log_class, f)); } static void binop1_log_bang(t_binop *x) { t_float r; if (x->x_f1 <= 0) r = -1000; else if (x->x_f2 <= 0) r = LOG(x->x_f1); else r = LOG(x->x_f1)/LOG(x->x_f2); outlet_float(x->x_obj.ob_outlet, r); } static void binop1_log_float(t_binop *x, t_float f) { x->x_f1 = f; binop1_log_bang(x); } static t_class *exp_class; /* ----------- exp --------------- */ static void *exp_new(void) { t_object *x = (t_object *)pd_new(exp_class); outlet_new(x, &s_float); return (x); } static void exp_float(t_object *x, t_float f) { t_float g; #ifdef _WIN32 char buf[10]; #endif if (f > MAXLOG) f = MAXLOG; g = EXP(f); outlet_float(x->ob_outlet, g); } static t_class *abs_class; /* ----------- abs --------------- */ static void *abs_new(void) { t_object *x = (t_object *)pd_new(abs_class); outlet_new(x, &s_float); return (x); } static void abs_float(t_object *x, t_float f) { outlet_float(x->ob_outlet, FABS(f)); } static t_class *wrap_class; /* ----------- wrap --------------- */ static void *wrap_new(void) { t_object *x = (t_object *)pd_new(wrap_class); outlet_new(x, &s_float); return (x); } static void wrap_float(t_object *x, t_float f) { outlet_float(x->ob_outlet, f - floor(f)); } /* ------------------------ misc ------------------------ */ static t_class *clip_class; typedef struct _clip { t_object x_ob; t_float x_f1; t_float x_f2; t_float x_f3; } t_clip; static void *clip_new(t_floatarg f1, t_floatarg f2) { t_clip *x = (t_clip *)pd_new(clip_class); floatinlet_new(&x->x_ob, &x->x_f2); floatinlet_new(&x->x_ob, &x->x_f3); outlet_new(&x->x_ob, &s_float); x->x_f2 = f1; x->x_f3 = f2; return (x); } static void clip_bang(t_clip *x) { outlet_float(x->x_ob.ob_outlet, (x->x_f1 < x->x_f2 ? x->x_f2 : ( x->x_f1 > x->x_f3 ? x->x_f3 : x->x_f1))); } static void clip_float(t_clip *x, t_float f) { x->x_f1 = f; outlet_float(x->x_ob.ob_outlet, (x->x_f1 < x->x_f2 ? x->x_f2 : ( x->x_f1 > x->x_f3 ? x->x_f3 : x->x_f1))); } static void clip_setup(void) { clip_class = class_new(gensym("clip"), (t_newmethod)clip_new, 0, sizeof(t_clip), 0, A_DEFFLOAT, A_DEFFLOAT, 0); class_addfloat(clip_class, clip_float); class_addbang(clip_class, clip_bang); } void x_arithmetic_setup(void) { t_symbol *binop1_sym = gensym("binops"); t_symbol *binop23_sym = gensym("binops-other"); t_symbol *trig_sym = gensym("trigonometric"); t_symbol *unop_sym = gensym("unops"); binop1_plus_class = class_new(gensym("+"), (t_newmethod)binop1_plus_new, 0, sizeof(t_binop), 0, A_DEFFLOAT, 0); class_addbang(binop1_plus_class, binop1_plus_bang); class_addfloat(binop1_plus_class, (t_method)binop1_plus_float); class_sethelpsymbol(binop1_plus_class, binop1_sym); binop1_minus_class = class_new(gensym("-"), (t_newmethod)binop1_minus_new, 0, sizeof(t_binop), 0, A_DEFFLOAT, 0); class_addbang(binop1_minus_class, binop1_minus_bang); class_addfloat(binop1_minus_class, (t_method)binop1_minus_float); class_sethelpsymbol(binop1_minus_class, binop1_sym); binop1_times_class = class_new(gensym("*"), (t_newmethod)binop1_times_new, 0, sizeof(t_binop), 0, A_DEFFLOAT, 0); class_addbang(binop1_times_class, binop1_times_bang); class_addfloat(binop1_times_class, (t_method)binop1_times_float); class_sethelpsymbol(binop1_times_class, binop1_sym); binop1_div_class = class_new(gensym("/"), (t_newmethod)binop1_div_new, 0, sizeof(t_binop), 0, A_DEFFLOAT, 0); class_addbang(binop1_div_class, binop1_div_bang); class_addfloat(binop1_div_class, (t_method)binop1_div_float); class_sethelpsymbol(binop1_div_class, binop1_sym); binop1_pow_class = class_new(gensym("pow"), (t_newmethod)binop1_pow_new, 0, sizeof(t_binop), 0, A_DEFFLOAT, 0); class_addbang(binop1_pow_class, binop1_pow_bang); class_addfloat(binop1_pow_class, (t_method)binop1_pow_float); class_sethelpsymbol(binop1_pow_class, binop1_sym); binop1_max_class = class_new(gensym("max"), (t_newmethod)binop1_max_new, 0, sizeof(t_binop), 0, A_DEFFLOAT, 0); class_addbang(binop1_max_class, binop1_max_bang); class_addfloat(binop1_max_class, (t_method)binop1_max_float); class_sethelpsymbol(binop1_max_class, binop1_sym); binop1_min_class = class_new(gensym("min"), (t_newmethod)binop1_min_new, 0, sizeof(t_binop), 0, A_DEFFLOAT, 0); class_addbang(binop1_min_class, binop1_min_bang); class_addfloat(binop1_min_class, (t_method)binop1_min_float); class_sethelpsymbol(binop1_min_class, binop1_sym); binop1_log_class = class_new(gensym("log"), (t_newmethod)binop1_log_new, 0, sizeof(t_binop), 0, A_DEFFLOAT, 0); class_addbang(binop1_log_class, binop1_log_bang); class_addfloat(binop1_log_class, (t_method)binop1_log_float); class_sethelpsymbol(binop1_log_class, binop1_sym); /* ------------------ binop2 ----------------------- */ binop2_ee_class = class_new(gensym("=="), (t_newmethod)binop2_ee_new, 0, sizeof(t_binop), 0, A_DEFFLOAT, 0); class_addbang(binop2_ee_class, binop2_ee_bang); class_addfloat(binop2_ee_class, (t_method)binop2_ee_float); class_sethelpsymbol(binop2_ee_class, binop23_sym); binop2_ne_class = class_new(gensym("!="), (t_newmethod)binop2_ne_new, 0, sizeof(t_binop), 0, A_DEFFLOAT, 0); class_addbang(binop2_ne_class, binop2_ne_bang); class_addfloat(binop2_ne_class, (t_method)binop2_ne_float); class_sethelpsymbol(binop2_ne_class, binop23_sym); binop2_gt_class = class_new(gensym(">"), (t_newmethod)binop2_gt_new, 0, sizeof(t_binop), 0, A_DEFFLOAT, 0); class_addbang(binop2_gt_class, binop2_gt_bang); class_addfloat(binop2_gt_class, (t_method)binop2_gt_float); class_sethelpsymbol(binop2_gt_class, binop23_sym); binop2_lt_class = class_new(gensym("<"), (t_newmethod)binop2_lt_new, 0, sizeof(t_binop), 0, A_DEFFLOAT, 0); class_addbang(binop2_lt_class, binop2_lt_bang); class_addfloat(binop2_lt_class, (t_method)binop2_lt_float); class_sethelpsymbol(binop2_lt_class, binop23_sym); binop2_ge_class = class_new(gensym(">="), (t_newmethod)binop2_ge_new, 0, sizeof(t_binop), 0, A_DEFFLOAT, 0); class_addbang(binop2_ge_class, binop2_ge_bang); class_addfloat(binop2_ge_class, (t_method)binop2_ge_float); class_sethelpsymbol(binop2_ge_class, binop23_sym); binop2_le_class = class_new(gensym("<="), (t_newmethod)binop2_le_new, 0, sizeof(t_binop), 0, A_DEFFLOAT, 0); class_addbang(binop2_le_class, binop2_le_bang); class_addfloat(binop2_le_class, (t_method)binop2_le_float); class_sethelpsymbol(binop2_le_class, binop23_sym); /* ------------------ binop3 ----------------------- */ binop3_ba_class = class_new(gensym("&"), (t_newmethod)binop3_ba_new, 0, sizeof(t_binop), 0, A_DEFFLOAT, 0); class_addbang(binop3_ba_class, binop2_ba_bang); class_addfloat(binop3_ba_class, (t_method)binop2_ba_float); class_sethelpsymbol(binop3_ba_class, binop23_sym); binop3_la_class = class_new(gensym("&&"), (t_newmethod)binop3_la_new, 0, sizeof(t_binop), 0, A_DEFFLOAT, 0); class_addbang(binop3_la_class, binop2_la_bang); class_addfloat(binop3_la_class, (t_method)binop2_la_float); class_sethelpsymbol(binop3_la_class, binop23_sym); binop3_bo_class = class_new(gensym("|"), (t_newmethod)binop3_bo_new, 0, sizeof(t_binop), 0, A_DEFFLOAT, 0); class_addbang(binop3_bo_class, binop2_bo_bang); class_addfloat(binop3_bo_class, (t_method)binop2_bo_float); class_sethelpsymbol(binop3_bo_class, binop23_sym); binop3_lo_class = class_new(gensym("||"), (t_newmethod)binop3_lo_new, 0, sizeof(t_binop), 0, A_DEFFLOAT, 0); class_addbang(binop3_lo_class, binop2_lo_bang); class_addfloat(binop3_lo_class, (t_method)binop2_lo_float); class_sethelpsymbol(binop3_lo_class, binop23_sym); binop3_ls_class = class_new(gensym("<<"), (t_newmethod)binop3_ls_new, 0, sizeof(t_binop), 0, A_DEFFLOAT, 0); class_addbang(binop3_ls_class, binop2_ls_bang); class_addfloat(binop3_ls_class, (t_method)binop2_ls_float); class_sethelpsymbol(binop3_ls_class, binop23_sym); binop3_rs_class = class_new(gensym(">>"), (t_newmethod)binop3_rs_new, 0, sizeof(t_binop), 0, A_DEFFLOAT, 0); class_addbang(binop3_rs_class, binop2_rs_bang); class_addfloat(binop3_rs_class, (t_method)binop2_rs_float); class_sethelpsymbol(binop3_rs_class, binop23_sym); binop3_pc_class = class_new(gensym("%"), (t_newmethod)binop3_pc_new, 0, sizeof(t_binop), 0, A_DEFFLOAT, 0); class_addbang(binop3_pc_class, binop2_pc_bang); class_addfloat(binop3_pc_class, (t_method)binop2_pc_float); class_sethelpsymbol(binop3_pc_class, binop23_sym); binop3_mod_class = class_new(gensym("mod"), (t_newmethod)binop3_mod_new, 0, sizeof(t_binop), 0, A_DEFFLOAT, 0); class_addbang(binop3_mod_class, binop3_mod_bang); class_addfloat(binop3_mod_class, (t_method)binop3_mod_float); class_sethelpsymbol(binop3_mod_class, binop23_sym); binop3_div_class = class_new(gensym("div"), (t_newmethod)binop3_div_new, 0, sizeof(t_binop), 0, A_DEFFLOAT, 0); class_addbang(binop3_div_class, binop3_div_bang); class_addfloat(binop3_div_class, (t_method)binop3_div_float); class_sethelpsymbol(binop3_div_class, binop23_sym); /* ------------------- trig functions --------------- */ sin_class = class_new(gensym("sin"), sin_new, 0, sizeof(t_object), 0, 0); class_addfloat(sin_class, (t_method)sin_float); class_sethelpsymbol(sin_class, trig_sym); cos_class = class_new(gensym("cos"), cos_new, 0, sizeof(t_object), 0, 0); class_addfloat(cos_class, (t_method)cos_float); class_sethelpsymbol(cos_class, trig_sym); tan_class = class_new(gensym("tan"), tan_new, 0, sizeof(t_object), 0, 0); class_addfloat(tan_class, (t_method)tan_float); class_sethelpsymbol(tan_class, trig_sym); atan_class = class_new(gensym("atan"), atan_new, 0, sizeof(t_object), 0, 0); class_addfloat(atan_class, (t_method)atan_float); class_sethelpsymbol(atan_class, trig_sym); atan2_class = class_new(gensym("atan2"), atan2_new, 0, sizeof(t_atan2), 0, 0); class_addfloat(atan2_class, (t_method)atan2_float); class_addbang(atan2_class, atan2_bang); class_sethelpsymbol(atan2_class, trig_sym); /* ------------------- trig functions --------------- */ sqrt_class = class_new(gensym("sqrt"), sqrt_new, 0, sizeof(t_object), 0, 0); class_addfloat(sqrt_class, (t_method)sqrt_float); class_sethelpsymbol(sqrt_class, unop_sym); exp_class = class_new(gensym("exp"), exp_new, 0, sizeof(t_object), 0, 0); class_addfloat(exp_class, (t_method)exp_float); class_sethelpsymbol(exp_class, unop_sym); abs_class = class_new(gensym("abs"), abs_new, 0, sizeof(t_object), 0, 0); class_addfloat(abs_class, (t_method)abs_float); class_sethelpsymbol(abs_class, unop_sym); wrap_class = class_new(gensym("wrap"), wrap_new, 0, sizeof(t_object), 0, 0); class_addfloat(wrap_class, (t_method)wrap_float); class_sethelpsymbol(wrap_class, unop_sym); /* ------------------------ misc ------------------------ */ clip_setup(); } ================================================ FILE: libs/libpd/pure-data/src/x_array.c ================================================ /* Copyright (c) 1997-2013 Miller Puckette and others. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* The "array" object. */ #include "m_pd.h" #include "g_canvas.h" #include "m_imp.h" #include #include #ifdef HAVE_UNISTD_H #include #endif #ifdef _WIN32 #include #endif #include "m_private_utils.h" #define TEXT_NGETBYTE 100 /* bigger that this we use alloc, not alloca */ /* -- "table" - classic "array define" object by Guenter Geiger --*/ static int tabcount = 0; static void *table_donew(t_symbol *s, int size, int save, int savesize, int xpix, int ypix) { t_atom a[9]; t_glist *gl; t_canvas *x, *z = canvas_getcurrent(); if (s == &s_) { char tabname[255]; t_symbol *t = gensym("table"); sprintf(tabname, "%s%d", t->s_name, tabcount++); s = gensym(tabname); } if (size < 1) size = 100; SETFLOAT(a, GLIST_DEFCANVASXLOC); SETFLOAT(a+1, GLIST_DEFCANVASYLOC); SETFLOAT(a+2, xpix + 100); SETFLOAT(a+3, ypix + 100); SETSYMBOL(a+4, s); SETFLOAT(a+5, 0); x = canvas_new(0, 0, 6, a); x->gl_owner = z; /* create a graph for the table */ gl = glist_addglist((t_glist*)x, &s_, 0, -1, (size > 1 ? size-1 : 1), 1, 50, ypix+50, xpix+50, 50); graph_array(gl, s, &s_float, size, save*GRAPH_ARRAY_SAVE + savesize*GRAPH_ARRAY_SAVESIZE); pd_this->pd_newest = &x->gl_pd; /* mimic action of canvas_pop() */ pd_popsym(&x->gl_pd); x->gl_loading = 0; return (x); } static void *table_new(t_symbol *s, t_floatarg f) { return (table_donew(s, f, 0, 0, 500, 300)); } /* return true if the "canvas" object is a "table". */ int canvas_istable(const t_canvas *x) { t_atom *argv = (x->gl_obj.te_binbuf? binbuf_getvec(x->gl_obj.te_binbuf):0); int argc = (x->gl_obj.te_binbuf? binbuf_getnatom(x->gl_obj.te_binbuf) : 0); int istable = (argc && argv[0].a_type == A_SYMBOL && argv[0].a_w.w_symbol == gensym("table")); return (istable); } t_class *array_define_class; static void array_define_yrange(t_glist *x, t_floatarg ylo, t_floatarg yhi) { t_glist *gl = (x->gl_list ? pd_checkglist(&x->gl_list->g_pd) : 0); if (gl && gl->gl_list && pd_class(&gl->gl_list->g_pd) == garray_class) { int n = garray_getarray((t_garray *)gl->gl_list)->a_n; vmess(&x->gl_list->g_pd, gensym("bounds"), "ffff", 0., yhi, (double)(n == 1 ? n : n-1), ylo); vmess(&x->gl_list->g_pd, gensym("xlabel"), "fff", ylo + glist_pixelstoy(gl, 2) - glist_pixelstoy(gl, 0), 0., (t_float)(n-1)); vmess(&x->gl_list->g_pd, gensym("ylabel"), "fff", glist_pixelstox(gl, 0) - glist_pixelstox(gl, 5), ylo, yhi); } else bug("array_define_yrange"); } static void *array_define_new(t_symbol *s, int argc, t_atom *argv) { t_symbol *arrayname = &s_; t_float arraysize = 100; t_glist *x; int keep = 0, gavesize = 0; t_float ylo = -1, yhi = 1; t_float xpix = 500, ypix = 300; while (argc && argv->a_type == A_SYMBOL && *argv->a_w.w_symbol->s_name == '-') { if (!strcmp(argv->a_w.w_symbol->s_name, "-k")) keep = 1; else if (!strcmp(argv->a_w.w_symbol->s_name, "-yrange") && argc >= 3 && argv[1].a_type == A_FLOAT && argv[2].a_type == A_FLOAT) { ylo = atom_getfloatarg(1, argc, argv); yhi = atom_getfloatarg(2, argc, argv); if (ylo == yhi) ylo = -1, yhi = 1; argc -= 2; argv += 2; } else if (!strcmp(argv->a_w.w_symbol->s_name, "-pix") && argc >= 3 && argv[1].a_type == A_FLOAT && argv[2].a_type == A_FLOAT) { if ((xpix = atom_getfloatarg(1, argc, argv)) < 10) xpix = 10; if ((ypix = atom_getfloatarg(2, argc, argv)) < 10) ypix = 10; argc -= 2; argv += 2; } else { pd_error(0, "array define: unknown flag ..."); postatom(argc, argv); endpost(); } argc--; argv++; } if (argc && argv->a_type == A_SYMBOL) { arrayname = argv->a_w.w_symbol; argc--; argv++; } if (argc && argv->a_type == A_FLOAT) { arraysize = argv->a_w.w_float; gavesize = 1; argc--; argv++; } if (argc) { post("warning: array define ignoring extra argument: "); postatom(argc, argv); endpost(); } x = (t_glist *)table_donew(arrayname, arraysize, keep, keep && !gavesize, xpix, ypix); /* bash the class to "array define". We don't do this earlier in part so that canvas_getcurrent() will work while the glist and garray are being created. There may be other, unknown side effects. */ x->gl_obj.ob_pd = array_define_class; array_define_yrange(x, ylo, yhi); outlet_new(&x->gl_obj, &s_pointer); return (x); } void garray_savecontentsto(t_garray *x, t_binbuf *b); void array_define_save(t_gobj *z, t_binbuf *bb) { t_glist *x = (t_glist *)z; t_glist *gl = (x->gl_list ? pd_checkglist(&x->gl_list->g_pd) : 0); binbuf_addv(bb, "ssff", &s__X, gensym("obj"), (t_float)x->gl_obj.te_xpix, (t_float)x->gl_obj.te_ypix); binbuf_addbinbuf(bb, x->gl_obj.ob_binbuf); binbuf_addsemi(bb); if (gl) { garray_savecontentsto((t_garray *)gl->gl_list, bb); obj_saveformat(&x->gl_obj, bb); } else bug("array_define_save"); } t_scalar *garray_getscalar(t_garray *x); /* send a pointer to the scalar that owns this array to whomever is bound to the given symbol */ static void array_define_send(t_glist *x, t_symbol *s) { t_glist *gl = (x->gl_list ? pd_checkglist(&x->gl_list->g_pd) : 0); if (!s->s_thing) pd_error(x, "array_define_send: %s: no such object", s->s_name); else if (gl && gl->gl_list && pd_class(&gl->gl_list->g_pd) == garray_class) { t_gpointer gp; gpointer_init(&gp); gpointer_setglist(&gp, gl, garray_getscalar((t_garray *)gl->gl_list)); pd_pointer(s->s_thing, &gp); gpointer_unset(&gp); } else bug("array_define_send"); } void garray_properties(t_garray *x); static void array_define_done_popup(t_glist*x, t_float which, t_float xpos, t_float ypos) { int iwhich = (int)which; t_glist *gl = (x->gl_list ? pd_checkglist(&x->gl_list->g_pd) : 0); t_gobj *obj = 0; if (!gl || !gl->gl_list || pd_class(&gl->gl_list->g_pd) != garray_class) return; obj = gl->gl_list; switch(iwhich) { case 0: /* properties */ garray_properties((t_garray *)obj); break; case 1: /* open */ typedmess(&(obj->g_pd), gensym("arrayviewlistnew"), 0, 0); break; case 2: /* help */ open_via_helppath(class_gethelpname(array_define_class), ""); break; } } static void array_define_bang(t_glist *x) { t_glist *gl = (x->gl_list ? pd_checkglist(&x->gl_list->g_pd) : 0); if (gl && gl->gl_list && pd_class(&gl->gl_list->g_pd) == garray_class) { t_gpointer gp; gpointer_init(&gp); gpointer_setglist(&gp, gl, garray_getscalar((t_garray *)gl->gl_list)); outlet_pointer(x->gl_obj.ob_outlet, &gp); gpointer_unset(&gp); } else bug("array_define_bang"); } /* just forward any messages to the garray */ static void array_define_anything(t_glist *x, t_symbol *s, int argc, t_atom *argv) { t_glist *gl = (x->gl_list ? pd_checkglist(&x->gl_list->g_pd) : 0); if (gl && gl->gl_list && pd_class(&gl->gl_list->g_pd) == garray_class) typedmess(&gl->gl_list->g_pd, s, argc, argv); else bug("array_define_anything"); } /* ignore messages like "editmode" */ static void array_define_ignore(t_glist *x, t_symbol *s, int argc, t_atom *argv) { } /* --- array_client - common code for objects that refer to arrays -- */ typedef struct _array_client { t_object tc_obj; t_symbol *tc_sym; t_gpointer tc_gp; t_symbol *tc_struct; t_symbol *tc_field; t_canvas *tc_canvas; } t_array_client; #define x_sym x_tc.tc_sym #define x_struct x_tc.tc_struct #define x_field x_tc.tc_field #define x_gp x_tc.tc_gp /* find the array for this object. Prints an error message and returns 0 on failure. */ static t_array *array_client_getbuf(t_array_client *x, t_glist **glist) { if (x->tc_sym) /* named array object */ { t_garray *y = (t_garray *)pd_findbyclass(x->tc_sym, garray_class); if (y) { *glist = garray_getglist(y); return (garray_getarray(y)); } else { pd_error(x, "array: couldn't find named array '%s'", x->tc_sym->s_name); *glist = 0; return (0); } } else if (x->tc_struct) /* by pointer */ { t_template *template = template_findbyname(x->tc_struct); t_gstub *gs = x->tc_gp.gp_stub; t_word *vec; int onset, type; t_symbol *arraytype; if (!template) { pd_error(x, "array: couldn't find struct %s", x->tc_struct->s_name); return (0); } if (!gpointer_check(&x->tc_gp, 0)) { pd_error(x, "array: stale or empty pointer"); return (0); } if (gs->gs_which == GP_ARRAY) vec = x->tc_gp.gp_un.gp_w; else vec = x->tc_gp.gp_un.gp_scalar->sc_vec; if (!template_find_field(template, x->tc_field, &onset, &type, &arraytype)) { pd_error(x, "array: no field named %s", x->tc_field->s_name); return (0); } if (type != DT_ARRAY) { pd_error(x, "array: field %s not of type array", x->tc_field->s_name); return (0); } if (gs->gs_which == GP_GLIST) *glist = gs->gs_un.gs_glist; else { t_array *owner_array = gs->gs_un.gs_array; while (owner_array->a_gp.gp_stub->gs_which == GP_ARRAY) owner_array = owner_array->a_gp.gp_stub->gs_un.gs_array; *glist = owner_array->a_gp.gp_stub->gs_un.gs_glist; } return (*(t_array **)(((char *)vec) + onset)); } else return (0); /* shouldn't happen */ } static void array_client_senditup(t_array_client *x) { t_glist *glist = 0; t_array *a = array_client_getbuf(x, &glist); if (glist) array_redraw(a, glist); } static void array_client_free(t_array_client *x) { gpointer_unset(&x->tc_gp); } /* ---------- array size : get or set size of an array ---------------- */ static t_class *array_size_class; typedef struct _array_size { t_array_client x_tc; } t_array_size; #define x_outlet x_tc.tc_obj.ob_outlet static void *array_size_new(t_symbol *s, int argc, t_atom *argv) { t_array_size *x = (t_array_size *)pd_new(array_size_class); x->x_sym = x->x_struct = x->x_field = 0; gpointer_init(&x->x_gp); while (argc && argv->a_type == A_SYMBOL && *argv->a_w.w_symbol->s_name == '-') { if (!strcmp(argv->a_w.w_symbol->s_name, "-s") && argc >= 3 && argv[1].a_type == A_SYMBOL && argv[2].a_type == A_SYMBOL) { x->x_struct = canvas_makebindsym(argv[1].a_w.w_symbol); x->x_field = argv[2].a_w.w_symbol; argc -= 2; argv += 2; } else { pd_error(x, "array setline: unknown flag ..."); postatom(argc, argv); endpost(); } argc--; argv++; } if (argc && argv->a_type == A_SYMBOL) { if (x->x_struct) { pd_error(x, "array setline: extra names after -s.."); postatom(argc, argv); endpost(); } else x->x_sym = argv->a_w.w_symbol; argc--; argv++; } if (argc) { post("warning: array setline ignoring extra argument: "); postatom(argc, argv); endpost(); } if (x->x_struct) pointerinlet_new(&x->x_tc.tc_obj, &x->x_gp); else symbolinlet_new(&x->x_tc.tc_obj, &x->x_tc.tc_sym); outlet_new(&x->x_tc.tc_obj, &s_float); return (x); } static void array_size_bang(t_array_size *x) { t_glist *glist; t_array *a = array_client_getbuf(&x->x_tc, &glist); if (a) outlet_float(x->x_outlet, a->a_n); } static void array_size_float(t_array_size *x, t_floatarg f) { t_glist *glist; t_array *a = array_client_getbuf(&x->x_tc, &glist); if (a) { /* if it's a named array object we have to go back and find the garray (repeating work done in array_client_getbuf()) because the garray might want to adjust. Maybe array_client_getbuf should have a return slot for the garray if any? */ if (x->x_tc.tc_sym) { t_garray *y = (t_garray *)pd_findbyclass(x->x_tc.tc_sym, garray_class); if (!y) { pd_error(x, "no such array '%s'", x->x_tc.tc_sym->s_name); return; } garray_resize(y, f); } else { int n = f; if (n < 1) n = 1; array_resize_and_redraw(a, glist, n); } } } /* ------ range operations - act on a specifiable range in an array ----- */ static t_class *array_sum_class; typedef struct _array_rangeop /* any operation meaningful on a subrange */ { t_array_client x_tc; t_float x_onset; t_float x_n; t_symbol *x_elemfield; t_symbol *x_elemtemplate; /* unused - perhaps should at least check it */ } t_array_rangeop; /* generic creator for operations on ranges (array {get,set,sum,random, quantile,search,...} "onsetin" and "nin" are true if we should make inlets for onset and n - if no inlet for 'n' we also won't allow it to be specified as an argument. Everything can take an onset but sometimes we don't need an inlet because it's the inlet itself. In any case we allow onset to be specified as an argument (even if it's the 'hot inlet') -- for the same reason as in the 'delay' object. Finally we can optionally warn if there are extra arguments; some specific arguments (e.g., search) allow them but most don't. */ static void *array_rangeop_new(t_class *class, t_symbol *s, int *argcp, t_atom **argvp, int onsetin, int nin, int warnextra) { int argc = *argcp; t_atom *argv = *argvp; t_array_rangeop *x = (t_array_rangeop *)pd_new(class); x->x_sym = x->x_struct = x->x_field = 0; gpointer_init(&x->x_gp); x->x_elemtemplate = &s_; x->x_elemfield = gensym("y"); x->x_onset = 0; x->x_n = -1; if (onsetin) floatinlet_new(&x->x_tc.tc_obj, &x->x_onset); if (nin) floatinlet_new(&x->x_tc.tc_obj, &x->x_n); while (argc && argv->a_type == A_SYMBOL && *argv->a_w.w_symbol->s_name == '-') { if (!strcmp(argv->a_w.w_symbol->s_name, "-s") && argc >= 3 && argv[1].a_type == A_SYMBOL && argv[2].a_type == A_SYMBOL) { x->x_struct = canvas_makebindsym(argv[1].a_w.w_symbol); x->x_field = argv[2].a_w.w_symbol; argc -= 2; argv += 2; } else if (!strcmp(argv->a_w.w_symbol->s_name, "-f") && argc >= 3 && argv[1].a_type == A_SYMBOL && argv[2].a_type == A_SYMBOL) { x->x_elemtemplate = argv[1].a_w.w_symbol; x->x_elemfield = argv[2].a_w.w_symbol; argc -= 2; argv += 2; } else { pd_error(x, "%s: unknown flag ...", class_getname(class)); postatom(argc, argv); endpost(); } argc--; argv++; } if (argc && argv->a_type == A_SYMBOL) { if (x->x_struct) { pd_error(x, "%s: extra names after -s..", class_getname(class)); postatom(argc, argv); endpost(); } else x->x_sym = argv->a_w.w_symbol; argc--; argv++; } if (argc && argv->a_type == A_FLOAT) { x->x_onset = argv->a_w.w_float; argc--; argv++; } if (argc && argv->a_type == A_FLOAT) { x->x_n = argv->a_w.w_float; argc--; argv++; } if (argc && warnextra) { post("warning: %s ignoring extra argument: ", class_getname(class)); postatom(argc, argv); endpost(); } if (x->x_struct) pointerinlet_new(&x->x_tc.tc_obj, &x->x_gp); else symbolinlet_new(&x->x_tc.tc_obj, &x->x_tc.tc_sym); *argcp = argc; *argvp = argv; return (x); } static int array_rangeop_getrange(t_array_rangeop *x, char **firstitemp, int *nitemp, int *stridep, int *arrayonsetp) { t_glist *glist; t_array *a = array_client_getbuf(&x->x_tc, &glist); int stride, fieldonset, arrayonset, nitem, type; t_symbol *arraytype; t_template *template; if (!a) return (0); template = template_findbyname(a->a_templatesym); if (!template_find_field(template, x->x_elemfield, &fieldonset, &type, &arraytype) || type != DT_FLOAT) { pd_error(x, "can't find field %s in struct %s", x->x_elemfield->s_name, a->a_templatesym->s_name); return (0); } stride = a->a_elemsize; arrayonset = x->x_onset; if (arrayonset < 0) arrayonset = 0; else if (arrayonset > a->a_n) arrayonset = a->a_n; if (x->x_n < 0) nitem = a->a_n - arrayonset; else { nitem = x->x_n; if (nitem + arrayonset > a->a_n) nitem = a->a_n - arrayonset; } *firstitemp = a->a_vec+(fieldonset+arrayonset*stride); *nitemp = nitem; *stridep = stride; *arrayonsetp = arrayonset; return (1); } /* -------- specific operations on ranges of arrays -------- */ /* ---------------- array sum -- add them up ------------------- */ static t_class *array_sum_class; #define t_array_sum t_array_rangeop static void *array_sum_new(t_symbol *s, int argc, t_atom *argv) { t_array_sum *x = array_rangeop_new(array_sum_class, s, &argc, &argv, 0, 1, 1); outlet_new(&x->x_tc.tc_obj, &s_float); return (x); } static void array_sum_bang(t_array_rangeop *x) { char *itemp, *firstitem; int stride, nitem, arrayonset, i; double sum; if (!array_rangeop_getrange(x, &firstitem, &nitem, &stride, &arrayonset)) return; for (i = 0, sum = 0, itemp = firstitem; i < nitem; i++, itemp += stride) sum += *(t_float *)itemp; outlet_float(x->x_outlet, sum); } static void array_sum_float(t_array_rangeop *x, t_floatarg f) { x->x_onset = f; array_sum_bang(x); } /* ---------------- array get -- output as list ------------------- */ static t_class *array_get_class; #define t_array_get t_array_rangeop static void *array_get_new(t_symbol *s, int argc, t_atom *argv) { t_array_get *x = array_rangeop_new(array_get_class, s, &argc, &argv, 0, 1, 1); outlet_new(&x->x_tc.tc_obj, &s_float); return (x); } static void array_get_bang(t_array_rangeop *x) { char *itemp, *firstitem; int stride, nitem, arrayonset, i; t_atom *outv; if (!array_rangeop_getrange(x, &firstitem, &nitem, &stride, &arrayonset)) return; ALLOCA(t_atom, outv, nitem, TEXT_NGETBYTE); for (i = 0, itemp = firstitem; i < nitem; i++, itemp += stride) SETFLOAT(&outv[i], *(t_float *)itemp); outlet_list(x->x_outlet, 0, nitem, outv); FREEA(t_atom, outv, nitem, TEXT_NGETBYTE); } static void array_get_float(t_array_rangeop *x, t_floatarg f) { x->x_onset = f; array_get_bang(x); } /* -------------- array set -- copy list to array -------------- */ static t_class *array_set_class; #define t_array_set t_array_rangeop static void *array_set_new(t_symbol *s, int argc, t_atom *argv) { t_array_set *x = array_rangeop_new(array_set_class, s, &argc, &argv, 1, 0, 1); return (x); } static void array_set_list(t_array_rangeop *x, t_symbol *s, int argc, t_atom *argv) { char *itemp, *firstitem; int stride, nitem, arrayonset, i; if (!array_rangeop_getrange(x, &firstitem, &nitem, &stride, &arrayonset)) return; if (nitem > argc) nitem = argc; for (i = 0, itemp = firstitem; i < nitem; i++, itemp += stride) *(t_float *)itemp = atom_getfloatarg(i, argc, argv); array_client_senditup(&x->x_tc); } /* ----- array quantile -- output quantile for input from 0 to 1 ------- */ static t_class *array_quantile_class; #define t_array_quantile t_array_rangeop static void *array_quantile_new(t_symbol *s, int argc, t_atom *argv) { t_array_quantile *x = array_rangeop_new(array_quantile_class, s, &argc, &argv, 1, 1, 1); outlet_new(&x->x_tc.tc_obj, &s_float); return (x); } static void array_quantile_float(t_array_rangeop *x, t_floatarg f) { char *itemp, *firstitem; int stride, nitem, arrayonset, i; double sum; if (!array_rangeop_getrange(x, &firstitem, &nitem, &stride, &arrayonset)) return; for (i = 0, sum = 0, itemp = firstitem; i < nitem; i++, itemp += stride) sum += (*(t_float *)itemp > 0? *(t_float *)itemp : 0); sum *= f; for (i = 0, itemp = firstitem; i < (nitem-1); i++, itemp += stride) { sum -= (*(t_float *)itemp > 0? *(t_float *)itemp : 0); if (sum < 0) break; } outlet_float(x->x_outlet, i); } /* ---- array random -- output random value with array as distribution ---- */ static t_class *array_random_class; typedef struct _array_random /* any operation meaningful on a subrange */ { t_array_rangeop x_r; unsigned int x_state; } t_array_random; static void *array_random_new(t_symbol *s, int argc, t_atom *argv) { t_array_random *x = array_rangeop_new(array_random_class, s, &argc, &argv, 0, 1, 1); static unsigned int random_nextseed = 584926371; random_nextseed = random_nextseed * 435898247 + 938284287; x->x_state = random_nextseed; outlet_new(&x->x_r.x_tc.tc_obj, &s_float); return (x); } static void array_random_seed(t_array_random *x, t_floatarg f) { x->x_state = f; } static void array_random_bang(t_array_random *x) { char *firstitem; int stride, nitem, arrayonset; if (!array_rangeop_getrange(&x->x_r, &firstitem, &nitem, &stride, &arrayonset)) return; x->x_state = x->x_state * 472940017 + 832416023; array_quantile_float(&x->x_r, (1./4294967296.0) * (double)(x->x_state)); } static void array_random_float(t_array_random *x, t_floatarg f) { x->x_r.x_onset = f; array_random_bang(x); } /* ---- array max -- output largest value and its index ------------ */ static t_class *array_max_class; typedef struct _array_max { t_array_rangeop x_rangeop; t_outlet *x_out1; /* value */ t_outlet *x_out2; /* index */ } t_array_max; static void *array_max_new(t_symbol *s, int argc, t_atom *argv) { t_array_max *x = array_rangeop_new(array_max_class, s, &argc, &argv, 0, 1, 1); x->x_out1 = outlet_new(&x->x_rangeop.x_tc.tc_obj, &s_float); x->x_out2 = outlet_new(&x->x_rangeop.x_tc.tc_obj, &s_float); return (x); } static void array_max_bang(t_array_max *x) { char *itemp, *firstitem; int stride, nitem, arrayonset, i, besti; t_float bestf; if (!array_rangeop_getrange(&x->x_rangeop, &firstitem, &nitem, &stride, &arrayonset)) return; for (i = 0, besti = -1, bestf= -1e30, itemp = firstitem; i < nitem; i++, itemp += stride) if (*(t_float *)itemp > bestf) bestf = *(t_float *)itemp, besti = i+arrayonset; outlet_float(x->x_out2, besti); outlet_float(x->x_out1, bestf); } static void array_max_float(t_array_max *x, t_floatarg f) { x->x_rangeop.x_onset = f; array_max_bang(x); } /* ---- array min -- output largest value and its index ------------ */ static t_class *array_min_class; typedef struct _array_min { t_array_rangeop x_rangeop; t_outlet *x_out1; /* value */ t_outlet *x_out2; /* index */ } t_array_min; static void *array_min_new(t_symbol *s, int argc, t_atom *argv) { t_array_min *x = array_rangeop_new(array_min_class, s, &argc, &argv, 0, 1, 1); x->x_out1 = outlet_new(&x->x_rangeop.x_tc.tc_obj, &s_float); x->x_out2 = outlet_new(&x->x_rangeop.x_tc.tc_obj, &s_float); return (x); } static void array_min_bang(t_array_min *x) { char *itemp, *firstitem; int stride, nitem, i, arrayonset, besti; t_float bestf; if (!array_rangeop_getrange(&x->x_rangeop, &firstitem, &nitem, &stride, &arrayonset)) return; for (i = 0, besti = -1, bestf= 1e30, itemp = firstitem; i < nitem; i++, itemp += stride) if (*(t_float *)itemp < bestf) bestf = *(t_float *)itemp, besti = i+arrayonset; outlet_float(x->x_out2, besti); outlet_float(x->x_out1, bestf); } static void array_min_float(t_array_min *x, t_floatarg f) { x->x_rangeop.x_onset = f; array_min_bang(x); } /* overall creator for "array" objects - dispatch to "array define" etc */ static void *arrayobj_new(t_symbol *s, int argc, t_atom *argv) { if (!argc || argv[0].a_type != A_SYMBOL) pd_this->pd_newest = array_define_new(s, argc, argv); else { const char *str = argv[0].a_w.w_symbol->s_name; if (!strcmp(str, "d") || !strcmp(str, "define")) pd_this->pd_newest = array_define_new(s, argc-1, argv+1); else if (!strcmp(str, "size")) pd_this->pd_newest = array_size_new(s, argc-1, argv+1); else if (!strcmp(str, "sum")) pd_this->pd_newest = array_sum_new(s, argc-1, argv+1); else if (!strcmp(str, "get")) pd_this->pd_newest = array_get_new(s, argc-1, argv+1); else if (!strcmp(str, "set")) pd_this->pd_newest = array_set_new(s, argc-1, argv+1); else if (!strcmp(str, "quantile")) pd_this->pd_newest = array_quantile_new(s, argc-1, argv+1); else if (!strcmp(str, "random")) pd_this->pd_newest = array_random_new(s, argc-1, argv+1); else if (!strcmp(str, "max")) pd_this->pd_newest = array_max_new(s, argc-1, argv+1); else if (!strcmp(str, "min")) pd_this->pd_newest = array_min_new(s, argc-1, argv+1); else { pd_error(0, "array %s: unknown function", str); pd_this->pd_newest = 0; } } return (pd_this->pd_newest); } void canvas_add_for_class(t_class *c); /* ---------------- global setup function -------------------- */ void x_array_setup(void) { array_define_class = class_new(gensym("array define"), 0, (t_method)canvas_free, sizeof(t_canvas), 0, 0); canvas_add_for_class(array_define_class); class_addmethod(array_define_class, (t_method)array_define_send, gensym("send"), A_SYMBOL, 0); class_addbang(array_define_class, array_define_bang); class_addanything(array_define_class, array_define_anything); class_sethelpsymbol(array_define_class, gensym("array-object")); class_setsavefn(array_define_class, array_define_save); class_addmethod(array_define_class, (t_method)array_define_done_popup, gensym("done-popup"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); class_addmethod(array_define_class, (t_method)array_define_ignore, gensym("editmode"), A_GIMME, 0); class_addcreator((t_newmethod)arrayobj_new, gensym("array"), A_GIMME, 0); class_addcreator((t_newmethod)table_new, gensym("table"), A_DEFSYM, A_DEFFLOAT, 0); array_size_class = class_new(gensym("array size"), (t_newmethod)array_size_new, (t_method)array_client_free, sizeof(t_array_size), 0, A_GIMME, 0); class_addbang(array_size_class, array_size_bang); class_addfloat(array_size_class, array_size_float); class_sethelpsymbol(array_size_class, gensym("array-object")); array_sum_class = class_new(gensym("array sum"), (t_newmethod)array_sum_new, (t_method)array_client_free, sizeof(t_array_sum), 0, A_GIMME, 0); class_addbang(array_sum_class, array_sum_bang); class_addfloat(array_sum_class, array_sum_float); class_sethelpsymbol(array_sum_class, gensym("array-object")); array_get_class = class_new(gensym("array get"), (t_newmethod)array_get_new, (t_method)array_client_free, sizeof(t_array_get), 0, A_GIMME, 0); class_addbang(array_get_class, array_get_bang); class_addfloat(array_get_class, array_get_float); class_sethelpsymbol(array_get_class, gensym("array-object")); array_set_class = class_new(gensym("array set"), (t_newmethod)array_set_new, (t_method)array_client_free, sizeof(t_array_set), 0, A_GIMME, 0); class_addlist(array_set_class, array_set_list); class_sethelpsymbol(array_set_class, gensym("array-object")); array_quantile_class = class_new(gensym("array quantile"), (t_newmethod)array_quantile_new, (t_method)array_client_free, sizeof(t_array_quantile), 0, A_GIMME, 0); class_addfloat(array_quantile_class, array_quantile_float); class_sethelpsymbol(array_quantile_class, gensym("array-object")); array_random_class = class_new(gensym("array random"), (t_newmethod)array_random_new, (t_method)array_client_free, sizeof(t_array_random), 0, A_GIMME, 0); class_addmethod(array_random_class, (t_method)array_random_seed, gensym("seed"), A_FLOAT, 0); class_addfloat(array_random_class, array_random_float); class_addbang(array_random_class, array_random_bang); class_sethelpsymbol(array_random_class, gensym("array-object")); array_max_class = class_new(gensym("array max"), (t_newmethod)array_max_new, (t_method)array_client_free, sizeof(t_array_max), 0, A_GIMME, 0); class_addfloat(array_max_class, array_max_float); class_addbang(array_max_class, array_max_bang); class_sethelpsymbol(array_max_class, gensym("array-object")); array_min_class = class_new(gensym("array min"), (t_newmethod)array_min_new, (t_method)array_client_free, sizeof(t_array_min), 0, A_GIMME, 0); class_addfloat(array_min_class, array_min_float); class_addbang(array_min_class, array_min_bang); class_sethelpsymbol(array_min_class, gensym("array-object")); } ================================================ FILE: libs/libpd/pure-data/src/x_connective.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* connective objects */ #include "m_pd.h" #include #include #include #ifdef _WIN32 # include /* MSVC or mingw on windows */ #elif defined(__linux__) || defined(__APPLE__) || defined(HAVE_ALLOCA_H) # include /* linux, mac, mingw, cygwin */ #endif /* -------------------------- int ------------------------------ */ static t_class *pdint_class; typedef struct _pdint { t_object x_obj; t_float x_f; } t_pdint; static void *pdint_new(t_floatarg f) { t_pdint *x = (t_pdint *)pd_new(pdint_class); x->x_f = f; outlet_new(&x->x_obj, &s_float); floatinlet_new(&x->x_obj, &x->x_f); return (x); } static void pdint_bang(t_pdint *x) { outlet_float(x->x_obj.ob_outlet, (t_float)(int64_t)(x->x_f)); } static void pdint_float(t_pdint *x, t_float f) { outlet_float(x->x_obj.ob_outlet, (t_float)(int64_t)(x->x_f = f)); } static void pdint_send(t_pdint *x, t_symbol *s) { if (s->s_thing) pd_float(s->s_thing, (t_float)(int64_t)x->x_f); else pd_error(x, "%s: no such object", s->s_name); } void pdint_setup(void) { pdint_class = class_new(gensym("int"), (t_newmethod)pdint_new, 0, sizeof(t_pdint), 0, A_DEFFLOAT, 0); class_addcreator((t_newmethod)pdint_new, gensym("i"), A_DEFFLOAT, 0); class_addmethod(pdint_class, (t_method)pdint_send, gensym("send"), A_SYMBOL, 0); class_addbang(pdint_class, pdint_bang); class_addfloat(pdint_class, pdint_float); } /* -------------------------- float ------------------------------ */ static t_class *pdfloat_class; typedef struct _pdfloat { t_object x_obj; t_float x_f; } t_pdfloat; /* "float," "symbol," and "bang" are special because they're created by short-circuited messages to the "new" object which are handled specially in pd_typedmess(). */ static void *pdfloat_new(t_pd *dummy, t_float f) { t_pdfloat *x = (t_pdfloat *)pd_new(pdfloat_class); x->x_f = f; outlet_new(&x->x_obj, &s_float); floatinlet_new(&x->x_obj, &x->x_f); pd_this->pd_newest = &x->x_obj.ob_pd; return (x); } static void *pdfloat_new2(t_floatarg f) { return (pdfloat_new(0, f)); } static void pdfloat_bang(t_pdfloat *x) { outlet_float(x->x_obj.ob_outlet, x->x_f); } static void pdfloat_float(t_pdfloat *x, t_float f) { outlet_float(x->x_obj.ob_outlet, x->x_f = f); } static void pdfloat_symbol(t_pdfloat *x, t_symbol *s) { t_float f = 0.0f; char *str_end = NULL; f = strtod(s->s_name, &str_end); if (f == 0 && s->s_name == str_end) pd_error(x, "couldn't convert %s to float", s->s_name); else outlet_float(x->x_obj.ob_outlet, x->x_f = f); } static void pdfloat_send(t_pdfloat *x, t_symbol *s) { if (s->s_thing) pd_float(s->s_thing, x->x_f); else pd_error(x, "%s: no such object", s->s_name); } void pdfloat_setup(void) { pdfloat_class = class_new(gensym("float"), (t_newmethod)pdfloat_new, 0, sizeof(t_pdfloat), 0, A_FLOAT, 0); class_addcreator((t_newmethod)pdfloat_new2, gensym("f"), A_DEFFLOAT, 0); class_addmethod(pdfloat_class, (t_method)pdfloat_send, gensym("send"), A_SYMBOL, 0); class_addbang(pdfloat_class, pdfloat_bang); class_addfloat(pdfloat_class, (t_method)pdfloat_float); class_addsymbol(pdfloat_class, (t_method)pdfloat_symbol); } /* -------------------------- symbol ------------------------------ */ static t_class *pdsymbol_class; typedef struct _pdsymbol { t_object x_obj; t_symbol *x_s; } t_pdsymbol; static void *pdsymbol_new(t_pd *dummy, t_symbol *s) { t_pdsymbol *x = (t_pdsymbol *)pd_new(pdsymbol_class); x->x_s = s; outlet_new(&x->x_obj, &s_symbol); symbolinlet_new(&x->x_obj, &x->x_s); pd_this->pd_newest = &x->x_obj.ob_pd; return (x); } static void pdsymbol_bang(t_pdsymbol *x) { outlet_symbol(x->x_obj.ob_outlet, x->x_s); } static void pdsymbol_symbol(t_pdsymbol *x, t_symbol *s) { outlet_symbol(x->x_obj.ob_outlet, x->x_s = s); } static void pdsymbol_anything(t_pdsymbol *x, t_symbol *s, int ac, t_atom *av) { outlet_symbol(x->x_obj.ob_outlet, x->x_s = s); } /* For "list" message don't just output "list"; if empty, we want to bang the symbol and if it starts with a symbol, we output that. Otherwise it's not clear what we should do so we just go for the "anything" method. LATER figure out if there are other places where empty lists aren't equivalent to "bang"??? Should Pd's message passer always check and call the more specific method, or should it be the object's responsibility? Dunno... */ static void pdsymbol_list(t_pdsymbol *x, t_symbol *s, int ac, t_atom *av) { if (!ac) pdsymbol_bang(x); else if (av->a_type == A_SYMBOL) pdsymbol_symbol(x, av->a_w.w_symbol); else pdsymbol_anything(x, s, ac, av); } void pdsymbol_setup(void) { pdsymbol_class = class_new(gensym("symbol"), (t_newmethod)pdsymbol_new, 0, sizeof(t_pdsymbol), 0, A_SYMBOL, 0); class_addbang(pdsymbol_class, pdsymbol_bang); class_addsymbol(pdsymbol_class, pdsymbol_symbol); class_addanything(pdsymbol_class, pdsymbol_anything); } /* -------------------------- bang ------------------------------ */ static t_class *bang_class; typedef struct _bang { t_object x_obj; } t_bang; static void *bang_new(t_pd *dummy) { t_bang *x = (t_bang *)pd_new(bang_class); outlet_new(&x->x_obj, &s_bang); pd_this->pd_newest = &x->x_obj.ob_pd; return (x); } static void *bang_new2(t_bang f) { return (bang_new(0)); } static void bang_bang(t_bang *x) { outlet_bang(x->x_obj.ob_outlet); } void bang_setup(void) { bang_class = class_new(gensym("bang"), (t_newmethod)bang_new, 0, sizeof(t_bang), 0, 0); class_addcreator((t_newmethod)bang_new2, gensym("b"), 0); class_addbang(bang_class, bang_bang); class_addfloat(bang_class, bang_bang); class_addsymbol(bang_class, bang_bang); class_addlist(bang_class, bang_bang); class_addanything(bang_class, bang_bang); } /* -------------------- send ------------------------------ */ static t_class *send_class; typedef struct _send { t_object x_obj; t_symbol *x_sym; } t_send; static void send_bang(t_send *x) { if (x->x_sym->s_thing) pd_bang(x->x_sym->s_thing); } static void send_float(t_send *x, t_float f) { if (x->x_sym->s_thing) pd_float(x->x_sym->s_thing, f); } static void send_symbol(t_send *x, t_symbol *s) { if (x->x_sym->s_thing) pd_symbol(x->x_sym->s_thing, s); } static void send_pointer(t_send *x, t_gpointer *gp) { if (x->x_sym->s_thing) pd_pointer(x->x_sym->s_thing, gp); } static void send_list(t_send *x, t_symbol *s, int argc, t_atom *argv) { if (x->x_sym->s_thing) pd_list(x->x_sym->s_thing, s, argc, argv); } static void send_anything(t_send *x, t_symbol *s, int argc, t_atom *argv) { if (x->x_sym->s_thing) typedmess(x->x_sym->s_thing, s, argc, argv); } static void *send_new(t_symbol *s) { t_send *x = (t_send *)pd_new(send_class); if (!*s->s_name) symbolinlet_new(&x->x_obj, &x->x_sym); x->x_sym = s; return (x); } static void send_setup(void) { send_class = class_new(gensym("send"), (t_newmethod)send_new, 0, sizeof(t_send), 0, A_DEFSYM, 0); class_addcreator((t_newmethod)send_new, gensym("s"), A_DEFSYM, 0); class_addbang(send_class, send_bang); class_addfloat(send_class, send_float); class_addsymbol(send_class, send_symbol); class_addpointer(send_class, send_pointer); class_addlist(send_class, send_list); class_addanything(send_class, send_anything); class_sethelpsymbol(send_class, gensym("send-receive")); } /* -------------------- receive ------------------------------ */ static t_class *receive_class; typedef struct _receive { t_object x_obj; t_symbol *x_sym; } t_receive; static void receive_bang(t_receive *x) { outlet_bang(x->x_obj.ob_outlet); } static void receive_float(t_receive *x, t_float f) { outlet_float(x->x_obj.ob_outlet, f); } static void receive_symbol(t_receive *x, t_symbol *s) { outlet_symbol(x->x_obj.ob_outlet, s); } static void receive_pointer(t_receive *x, t_gpointer *gp) { outlet_pointer(x->x_obj.ob_outlet, gp); } static void receive_list(t_receive *x, t_symbol *s, int argc, t_atom *argv) { outlet_list(x->x_obj.ob_outlet, s, argc, argv); } static void receive_anything(t_receive *x, t_symbol *s, int argc, t_atom *argv) { outlet_anything(x->x_obj.ob_outlet, s, argc, argv); } static void *receive_new(t_symbol *s) { t_receive *x = (t_receive *)pd_new(receive_class); x->x_sym = s; pd_bind(&x->x_obj.ob_pd, s); outlet_new(&x->x_obj, 0); return (x); } static void receive_free(t_receive *x) { pd_unbind(&x->x_obj.ob_pd, x->x_sym); } static void receive_setup(void) { receive_class = class_new(gensym("receive"), (t_newmethod)receive_new, (t_method)receive_free, sizeof(t_receive), CLASS_NOINLET, A_DEFSYM, 0); class_addcreator((t_newmethod)receive_new, gensym("r"), A_DEFSYM, 0); class_addbang(receive_class, receive_bang); class_addfloat(receive_class, (t_method)receive_float); class_addsymbol(receive_class, receive_symbol); class_addpointer(receive_class, receive_pointer); class_addlist(receive_class, receive_list); class_addanything(receive_class, receive_anything); class_sethelpsymbol(receive_class, gensym("send-receive")); } /* -------------------------- select ------------------------------ */ static t_class *sel1_class; typedef struct _sel1 { t_object x_obj; t_atom x_atom; t_outlet *x_outlet1; t_outlet *x_outlet2; } t_sel1; static void sel1_float(t_sel1 *x, t_float f) { if (x->x_atom.a_type == A_FLOAT && f == x->x_atom.a_w.w_float) outlet_bang(x->x_outlet1); else outlet_float(x->x_outlet2, f); } static void sel1_symbol(t_sel1 *x, t_symbol *s) { if (x->x_atom.a_type == A_SYMBOL && s == x->x_atom.a_w.w_symbol) outlet_bang(x->x_outlet1); else outlet_symbol(x->x_outlet2, s); } static t_class *sel2_class; typedef struct _selectelement { t_word e_w; t_outlet *e_outlet; } t_selectelement; typedef struct _sel2 { t_object x_obj; t_atomtype x_type; t_int x_nelement; t_selectelement *x_vec; t_outlet *x_rejectout; } t_sel2; static void sel2_float(t_sel2 *x, t_float f) { t_selectelement *e; int nelement; if (x->x_type == A_FLOAT) { for (nelement = (int)x->x_nelement, e = x->x_vec; nelement--; e++) if (e->e_w.w_float == f) { outlet_bang(e->e_outlet); return; } } outlet_float(x->x_rejectout, f); } static void sel2_symbol(t_sel2 *x, t_symbol *s) { t_selectelement *e; int nelement; if (x->x_type == A_SYMBOL) { for (nelement = (int)x->x_nelement, e = x->x_vec; nelement--; e++) if (e->e_w.w_symbol == s) { outlet_bang(e->e_outlet); return; } } outlet_symbol(x->x_rejectout, s); } static void sel2_free(t_sel2 *x) { freebytes(x->x_vec, x->x_nelement * sizeof(*x->x_vec)); } static void *select_new(t_symbol *s, int argc, t_atom *argv) { t_atom a; if (argc == 0) { argc = 1; SETFLOAT(&a, 0); argv = &a; } if (argc == 1) { t_sel1 *x = (t_sel1 *)pd_new(sel1_class); x->x_atom = *argv; x->x_outlet1 = outlet_new(&x->x_obj, &s_bang); if (argv->a_type == A_FLOAT) { floatinlet_new(&x->x_obj, &x->x_atom.a_w.w_float); x->x_outlet2 = outlet_new(&x->x_obj, &s_float); } else { symbolinlet_new(&x->x_obj, &x->x_atom.a_w.w_symbol); x->x_outlet2 = outlet_new(&x->x_obj, &s_symbol); } return (x); } else { int n; t_selectelement *e; t_sel2 *x = (t_sel2 *)pd_new(sel2_class); x->x_nelement = argc; x->x_vec = (t_selectelement *)getbytes(argc * sizeof(*x->x_vec)); x->x_type = argv[0].a_type; for (n = 0, e = x->x_vec; n < argc; n++, e++) { e->e_outlet = outlet_new(&x->x_obj, &s_bang); if ((x->x_type = argv->a_type) == A_FLOAT) e->e_w.w_float = atom_getfloatarg(n, argc, argv); else e->e_w.w_symbol = atom_getsymbolarg(n, argc, argv); } x->x_rejectout = outlet_new(&x->x_obj, &s_float); return (x); } } void select_setup(void) { sel1_class = class_new(gensym("select"), 0, 0, sizeof(t_sel1), 0, 0); class_addfloat(sel1_class, sel1_float); class_addsymbol(sel1_class, sel1_symbol); sel2_class = class_new(gensym("select"), 0, (t_method)sel2_free, sizeof(t_sel2), 0, 0); class_addfloat(sel2_class, sel2_float); class_addsymbol(sel2_class, sel2_symbol); class_addcreator((t_newmethod)select_new, gensym("select"), A_GIMME, 0); class_addcreator((t_newmethod)select_new, gensym("sel"), A_GIMME, 0); } /* -------------------------- route ------------------------------ */ static t_class *route_class; typedef struct _routeelement { t_word e_w; t_outlet *e_outlet; } t_routeelement; typedef struct _route { t_object x_obj; t_atomtype x_type; int x_nelement; t_routeelement *x_vec; t_outlet *x_rejectout; } t_route; static void route_anything(t_route *x, t_symbol *sel, int argc, t_atom *argv) { t_routeelement *e; int nelement; if (x->x_type == A_SYMBOL) { for (nelement = x->x_nelement, e = x->x_vec; nelement--; e++) if (e->e_w.w_symbol == sel) { if (argc > 0 && argv[0].a_type == A_SYMBOL) outlet_anything(e->e_outlet, argv[0].a_w.w_symbol, argc-1, argv+1); else outlet_list(e->e_outlet, 0, argc, argv); return; } } outlet_anything(x->x_rejectout, sel, argc, argv); } static void route_list(t_route *x, t_symbol *sel, int argc, t_atom *argv) { t_routeelement *e; int nelement; if (x->x_type == A_FLOAT) { t_float f; if (!argc || argv->a_type != A_FLOAT) goto rejected; f = atom_getfloat(argv); for (nelement = x->x_nelement, e = x->x_vec; nelement--; e++) if (e->e_w.w_float == f) { if (argc > 1 && argv[1].a_type == A_SYMBOL) outlet_anything(e->e_outlet, argv[1].a_w.w_symbol, argc-2, argv+2); else outlet_list(e->e_outlet, 0, argc-1, argv+1); return; } } else /* symbol arguments */ { if (argc > 1) /* 2 or more args: treat as "list" */ { for (nelement = x->x_nelement, e = x->x_vec; nelement--; e++) { if (e->e_w.w_symbol == &s_list) { if (argc > 0 && argv[0].a_type == A_SYMBOL) outlet_anything(e->e_outlet, argv[0].a_w.w_symbol, argc-1, argv+1); else outlet_list(e->e_outlet, 0, argc, argv); return; } } } else if (argc == 0) /* no args: treat as "bang" */ { for (nelement = x->x_nelement, e = x->x_vec; nelement--; e++) { if (e->e_w.w_symbol == &s_bang) { outlet_bang(e->e_outlet); return; } } } else if (argv[0].a_type == A_FLOAT) /* one float arg */ { for (nelement = x->x_nelement, e = x->x_vec; nelement--; e++) { if (e->e_w.w_symbol == &s_float) { outlet_float(e->e_outlet, argv[0].a_w.w_float); return; } } } else if (argv[0].a_type == A_POINTER) /* one pointer arg */ { for (nelement = x->x_nelement, e = x->x_vec; nelement--; e++) { if (e->e_w.w_symbol == &s_pointer) { outlet_pointer(e->e_outlet, argv[0].a_w.w_gpointer); return; } } } else /* one symbol arg */ { for (nelement = x->x_nelement, e = x->x_vec; nelement--; e++) { if (e->e_w.w_symbol == &s_symbol) { outlet_symbol(e->e_outlet, argv[0].a_w.w_symbol); return; } } } } rejected: outlet_list(x->x_rejectout, 0, argc, argv); } static void route_free(t_route *x) { freebytes(x->x_vec, x->x_nelement * sizeof(*x->x_vec)); } static void *route_new(t_symbol *s, int argc, t_atom *argv) { int n; t_routeelement *e; t_route *x = (t_route *)pd_new(route_class); t_atom a; if (argc == 0) { argc = 1; SETFLOAT(&a, 0); argv = &a; } x->x_type = argv[0].a_type; x->x_nelement = argc; x->x_vec = (t_routeelement *)getbytes(argc * sizeof(*x->x_vec)); for (n = 0, e = x->x_vec; n < argc; n++, e++) { e->e_outlet = outlet_new(&x->x_obj, &s_list); if (x->x_type == A_FLOAT) e->e_w.w_float = atom_getfloatarg(n, argc, argv); else e->e_w.w_symbol = atom_getsymbolarg(n, argc, argv); } if (argc == 1) { if (argv->a_type == A_FLOAT) floatinlet_new(&x->x_obj, &x->x_vec->e_w.w_float); else symbolinlet_new(&x->x_obj, &x->x_vec->e_w.w_symbol); } x->x_rejectout = outlet_new(&x->x_obj, &s_list); return (x); } void route_setup(void) { route_class = class_new(gensym("route"), (t_newmethod)route_new, (t_method)route_free, sizeof(t_route), 0, A_GIMME, 0); class_addlist(route_class, route_list); class_addanything(route_class, route_anything); } /* -------------------------- pack ------------------------------ */ static t_class *pack_class; typedef struct _pack { t_object x_obj; t_atom *x_vec; /* input values */ t_gpointer *x_gpointer; /* the pointers */ int x_n; /* number of args */ int x_nptr; /* number of pointers */ } t_pack; static void *pack_new(t_symbol *s, int argc, t_atom *argv) { t_pack *x = (t_pack *)pd_new(pack_class); t_atom defarg[2], *vec; t_gpointer *gp; int nptr = 0; int i; if (!argc) { argv = defarg; argc = 2; SETFLOAT(&defarg[0], 0); SETFLOAT(&defarg[1], 0); } x->x_n = argc; vec = x->x_vec = (t_atom *)getbytes(argc * sizeof(*x->x_vec)); for (i = 0; i < argc; i++) if (argv[i].a_type == A_SYMBOL && *argv[i].a_w.w_symbol->s_name == 'p') nptr++; gp = x->x_gpointer = (t_gpointer *)t_getbytes(nptr * sizeof (*gp)); x->x_nptr = nptr; for (i = 0; i < argc; i++) { if (argv[i].a_type == A_FLOAT) { vec[i] = argv[i]; if (i) floatinlet_new(&x->x_obj, &vec[i].a_w.w_float); } else if (argv[i].a_type == A_SYMBOL) { char c = *argv[i].a_w.w_symbol->s_name; if (c == 's') { SETSYMBOL(&vec[i], &s_symbol); if (i) symbolinlet_new(&x->x_obj, &vec[i].a_w.w_symbol); } else if (c == 'p') { vec[i].a_type = A_POINTER; vec[i].a_w.w_gpointer = gp; gpointer_init(gp); if (i) pointerinlet_new(&x->x_obj, gp); gp++; } else { if (c != 'f') pd_error(x, "pack: %s: bad type", argv[i].a_w.w_symbol->s_name); SETFLOAT(&vec[i], 0); if (i) floatinlet_new(&x->x_obj, &vec[i].a_w.w_float); } } } outlet_new(&x->x_obj, &s_list); return (x); } static void pack_bang(t_pack *x) { int i; t_atom *outvec = (t_atom *)alloca(x->x_n * sizeof(t_atom)); t_gpointer *gpvec, *gp; if (x->x_nptr > 0) { gp = gpvec = (t_gpointer *)alloca(x->x_nptr * sizeof(t_gpointer)); for (i = 0; i < x->x_n; i++) { outvec[i] = x->x_vec[i]; if (x->x_vec[i].a_type == A_POINTER) { gpointer_copy(x->x_vec[i].a_w.w_gpointer, gp); outvec[i].a_w.w_gpointer = gp; gp++; } } } else { for (i = 0; i < x->x_n; i++) outvec[i] = x->x_vec[i]; } outlet_list(x->x_obj.ob_outlet, &s_list, x->x_n, outvec); for (i = 0; i < x->x_nptr; i++) gpointer_unset(&gpvec[i]); } static void pack_pointer(t_pack *x, t_gpointer *gp) { if (x->x_vec->a_type == A_POINTER) { gpointer_unset(x->x_gpointer); gpointer_copy(gp, x->x_gpointer); pack_bang(x); } else pd_error(x, "pack_pointer: wrong type"); } static void pack_float(t_pack *x, t_float f) { if (x->x_vec->a_type == A_FLOAT) { x->x_vec->a_w.w_float = f; pack_bang(x); } else pd_error(x, "pack_float: wrong type"); } static void pack_symbol(t_pack *x, t_symbol *s) { if (x->x_vec->a_type == A_SYMBOL) { x->x_vec->a_w.w_symbol = s; pack_bang(x); } else pd_error(x, "pack_symbol: wrong type"); } /* without a list method, pack_anything() would be called */ static void pack_list(t_pack *x, t_symbol *s, int ac, t_atom *av) { obj_list(&x->x_obj, 0, ac, av); } static void pack_anything(t_pack *x, t_symbol *s, int ac, t_atom *av) { t_atom *av2 = (t_atom *)alloca((ac + 1) * sizeof(t_atom)); int i; for (i = 0; i < ac; i++) av2[i + 1] = av[i]; SETSYMBOL(av2, s); obj_list(&x->x_obj, 0, ac + 1, av2); } static void pack_free(t_pack *x) { int i; for (i = 0; i < x->x_nptr; i++) gpointer_unset(&x->x_gpointer[i]); freebytes(x->x_vec, x->x_n * sizeof(*x->x_vec)); freebytes(x->x_gpointer, x->x_nptr * sizeof(*x->x_gpointer)); } static void pack_setup(void) { pack_class = class_new(gensym("pack"), (t_newmethod)pack_new, (t_method)pack_free, sizeof(t_pack), 0, A_GIMME, 0); class_addbang(pack_class, pack_bang); class_addpointer(pack_class, pack_pointer); class_addfloat(pack_class, pack_float); class_addsymbol(pack_class, pack_symbol); class_addlist(pack_class, pack_list); class_addanything(pack_class, pack_anything); } /* -------------------------- unpack ------------------------------ */ static t_class *unpack_class; typedef struct unpackout { t_atomtype u_type; t_outlet *u_outlet; } t_unpackout; typedef struct _unpack { t_object x_obj; t_int x_n; t_unpackout *x_vec; } t_unpack; static void *unpack_new(t_symbol *s, int argc, t_atom *argv) { t_unpack *x = (t_unpack *)pd_new(unpack_class); t_atom defarg[2], *ap; t_unpackout *u; int i; if (!argc) { argv = defarg; argc = 2; SETFLOAT(&defarg[0], 0); SETFLOAT(&defarg[1], 0); } x->x_n = argc; x->x_vec = (t_unpackout *)getbytes(argc * sizeof(*x->x_vec)); for (i = 0, ap = argv, u = x->x_vec; i < argc; u++, ap++, i++) { t_atomtype type = ap->a_type; if (type == A_SYMBOL) { char c = *ap->a_w.w_symbol->s_name; if (c == 's') { u->u_type = A_SYMBOL; u->u_outlet = outlet_new(&x->x_obj, &s_symbol); } else if (c == 'p') { u->u_type = A_POINTER; u->u_outlet = outlet_new(&x->x_obj, &s_pointer); } else { if (c != 'f') pd_error(x, "unpack: %s: bad type", ap->a_w.w_symbol->s_name); u->u_type = A_FLOAT; u->u_outlet = outlet_new(&x->x_obj, &s_float); } } else { u->u_type = A_FLOAT; u->u_outlet = outlet_new(&x->x_obj, &s_float); } } return (x); } static void unpack_list(t_unpack *x, t_symbol *s, int argc, t_atom *argv) { t_atom *ap; t_unpackout *u; int i; if (argc > x->x_n) argc = (int)x->x_n; for (i = argc, u = x->x_vec + i, ap = argv + i; u--, ap--, i--;) { t_atomtype type = u->u_type; if (type != ap->a_type) pd_error(x, "unpack: type mismatch"); else if (type == A_FLOAT) outlet_float(u->u_outlet, ap->a_w.w_float); else if (type == A_SYMBOL) outlet_symbol(u->u_outlet, ap->a_w.w_symbol); else outlet_pointer(u->u_outlet, ap->a_w.w_gpointer); } } static void unpack_anything(t_unpack *x, t_symbol *s, int ac, t_atom *av) { t_atom *av2 = (t_atom *)getbytes((ac + 1) * sizeof(t_atom)); int i; for (i = 0; i < ac; i++) av2[i + 1] = av[i]; SETSYMBOL(av2, s); unpack_list(x, 0, ac+1, av2); freebytes(av2, (ac + 1) * sizeof(t_atom)); } static void unpack_free(t_unpack *x) { freebytes(x->x_vec, x->x_n * sizeof(*x->x_vec)); } static void unpack_setup(void) { unpack_class = class_new(gensym("unpack"), (t_newmethod)unpack_new, (t_method)unpack_free, sizeof(t_unpack), 0, A_GIMME, 0); class_addlist(unpack_class, unpack_list); class_addanything(unpack_class, unpack_anything); } /* -------------------------- trigger ------------------------------ */ static t_class *trigger_class; #define TR_BANG 0 #define TR_FLOAT 1 #define TR_SYMBOL 2 #define TR_POINTER 3 #define TR_LIST 4 #define TR_ANYTHING 5 typedef struct triggerout { int u_type; /* outlet type from above */ t_outlet *u_outlet; } t_triggerout; typedef struct _trigger { t_object x_obj; t_int x_n; t_triggerout *x_vec; } t_trigger; static void *trigger_new(t_symbol *s, int argc, t_atom *argv) { t_trigger *x = (t_trigger *)pd_new(trigger_class); t_atom defarg[2], *ap; t_triggerout *u; int i; if (!argc) { argv = defarg; argc = 2; SETSYMBOL(&defarg[0], &s_bang); SETSYMBOL(&defarg[1], &s_bang); } x->x_n = argc; x->x_vec = (t_triggerout *)getbytes(argc * sizeof(*x->x_vec)); for (i = 0, ap = argv, u = x->x_vec; i < argc; u++, ap++, i++) { t_atomtype thistype = ap->a_type; char c; if (thistype == TR_SYMBOL) c = ap->a_w.w_symbol->s_name[0]; else if (thistype == TR_FLOAT) c = 'f'; else c = 0; if (c == 'p') u->u_type = TR_POINTER, u->u_outlet = outlet_new(&x->x_obj, &s_pointer); else if (c == 'f') u->u_type = TR_FLOAT, u->u_outlet = outlet_new(&x->x_obj, &s_float); else if (c == 'b') u->u_type = TR_BANG, u->u_outlet = outlet_new(&x->x_obj, &s_bang); else if (c == 'l') u->u_type = TR_LIST, u->u_outlet = outlet_new(&x->x_obj, &s_list); else if (c == 's') u->u_type = TR_SYMBOL, u->u_outlet = outlet_new(&x->x_obj, &s_symbol); else if (c == 'a') u->u_type = TR_ANYTHING, u->u_outlet = outlet_new(&x->x_obj, &s_symbol); else { pd_error(x, "trigger: %s: bad type", ap->a_w.w_symbol->s_name); u->u_type = TR_FLOAT, u->u_outlet = outlet_new(&x->x_obj, &s_float); } } return (x); } static void trigger_list(t_trigger *x, t_symbol *s, int argc, t_atom *argv) { t_triggerout *u; int i; for (i = (int)x->x_n, u = x->x_vec + i; u--, i--;) { if (u->u_type == TR_FLOAT) outlet_float(u->u_outlet, (argc ? atom_getfloat(argv) : 0)); else if (u->u_type == TR_BANG) outlet_bang(u->u_outlet); else if (u->u_type == TR_SYMBOL) outlet_symbol(u->u_outlet, (argc ? atom_getsymbol(argv) : &s_symbol)); else if (u->u_type == TR_POINTER) { if (!argc || argv->a_type != TR_POINTER) pd_error(x, "trigger: bad pointer"); else outlet_pointer(u->u_outlet, argv->a_w.w_gpointer); } else if (u->u_type == TR_LIST) outlet_list(u->u_outlet, &s_list, argc, argv); else outlet_anything(u->u_outlet, s, argc, argv); } } static void trigger_anything(t_trigger *x, t_symbol *s, int argc, t_atom *argv) { t_triggerout *u; int i; for (i = (int)x->x_n, u = x->x_vec + i; u--, i--;) { if (u->u_type == TR_BANG) outlet_bang(u->u_outlet); else if (u->u_type == TR_ANYTHING) outlet_anything(u->u_outlet, s, argc, argv); else pd_error(x, "trigger: generic messages can only be converted to 'b' or 'a'"); } } static void trigger_bang(t_trigger *x) { trigger_list(x, &s_bang, 0, 0); } static void trigger_pointer(t_trigger *x, t_gpointer *gp) { t_atom at; SETPOINTER(&at, gp); trigger_list(x, &s_pointer, 1, &at); } static void trigger_float(t_trigger *x, t_float f) { t_atom at; SETFLOAT(&at, f); trigger_list(x, &s_float, 1, &at); } static void trigger_symbol(t_trigger *x, t_symbol *s) { t_atom at; SETSYMBOL(&at, s); trigger_list(x, &s_symbol, 1, &at); } static void trigger_free(t_trigger *x) { freebytes(x->x_vec, x->x_n * sizeof(*x->x_vec)); } static void trigger_setup(void) { trigger_class = class_new(gensym("trigger"), (t_newmethod)trigger_new, (t_method)trigger_free, sizeof(t_trigger), 0, A_GIMME, 0); class_addcreator((t_newmethod)trigger_new, gensym("t"), A_GIMME, 0); class_addlist(trigger_class, trigger_list); class_addbang(trigger_class, trigger_bang); class_addpointer(trigger_class, trigger_pointer); class_addfloat(trigger_class, (t_method)trigger_float); class_addsymbol(trigger_class, trigger_symbol); class_addanything(trigger_class, trigger_anything); } /* -------------------------- spigot ------------------------------ */ static t_class *spigot_class; typedef struct _spigot { t_object x_obj; t_float x_state; } t_spigot; static void *spigot_new(t_floatarg f) { t_spigot *x = (t_spigot *)pd_new(spigot_class); floatinlet_new(&x->x_obj, &x->x_state); outlet_new(&x->x_obj, 0); x->x_state = f; return (x); } static void spigot_bang(t_spigot *x) { if (x->x_state != 0) outlet_bang(x->x_obj.ob_outlet); } static void spigot_pointer(t_spigot *x, t_gpointer *gp) { if (x->x_state != 0) outlet_pointer(x->x_obj.ob_outlet, gp); } static void spigot_float(t_spigot *x, t_float f) { if (x->x_state != 0) outlet_float(x->x_obj.ob_outlet, f); } static void spigot_symbol(t_spigot *x, t_symbol *s) { if (x->x_state != 0) outlet_symbol(x->x_obj.ob_outlet, s); } static void spigot_list(t_spigot *x, t_symbol *s, int argc, t_atom *argv) { if (x->x_state != 0) outlet_list(x->x_obj.ob_outlet, s, argc, argv); } static void spigot_anything(t_spigot *x, t_symbol *s, int argc, t_atom *argv) { if (x->x_state != 0) outlet_anything(x->x_obj.ob_outlet, s, argc, argv); } static void spigot_setup(void) { spigot_class = class_new(gensym("spigot"), (t_newmethod)spigot_new, 0, sizeof(t_spigot), 0, A_DEFFLOAT, 0); class_addbang(spigot_class, spigot_bang); class_addpointer(spigot_class, spigot_pointer); class_addfloat(spigot_class, spigot_float); class_addsymbol(spigot_class, spigot_symbol); class_addlist(spigot_class, spigot_list); class_addanything(spigot_class, spigot_anything); } /* --------------------------- moses ----------------------------- */ static t_class *moses_class; typedef struct _moses { t_object x_ob; t_outlet *x_out2; t_float x_y; } t_moses; static void *moses_new(t_floatarg f) { t_moses *x = (t_moses *)pd_new(moses_class); floatinlet_new(&x->x_ob, &x->x_y); outlet_new(&x->x_ob, &s_float); x->x_out2 = outlet_new(&x->x_ob, &s_float); x->x_y = f; return (x); } static void moses_float(t_moses *x, t_float f) { if (f < x->x_y) outlet_float(x->x_ob.ob_outlet, f); else outlet_float(x->x_out2, f); } static void moses_setup(void) { moses_class = class_new(gensym("moses"), (t_newmethod)moses_new, 0, sizeof(t_moses), 0, A_DEFFLOAT, 0); class_addfloat(moses_class, moses_float); } /* ----------------------- until --------------------- */ static t_class *until_class; typedef struct _until { t_object x_obj; int x_run; int x_count; } t_until; static void *until_new(void) { t_until *x = (t_until *)pd_new(until_class); inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("bang"), gensym("bang2")); outlet_new(&x->x_obj, &s_bang); x->x_run = 0; return (x); } static void until_bang(t_until *x) { x->x_run = 1; x->x_count = -1; while (x->x_run && x->x_count) x->x_count--, outlet_bang(x->x_obj.ob_outlet); } static void until_float(t_until *x, t_float f) { if (f < 0) f = 0; x->x_run = 1; x->x_count = f; while (x->x_run && x->x_count) x->x_count--, outlet_bang(x->x_obj.ob_outlet); } static void until_bang2(t_until *x) { x->x_run = 0; } static void until_setup(void) { until_class = class_new(gensym("until"), (t_newmethod)until_new, 0, sizeof(t_until), 0, 0); class_addbang(until_class, until_bang); class_addfloat(until_class, until_float); class_addmethod(until_class, (t_method)until_bang2, gensym("bang2"), 0); } /* ----------------------- makefilename --------------------- */ static t_class *makefilename_class; typedef enum { NONE = 0, INT, FLOAT, STRING, POINTER, } t_printtype; typedef struct _makefilename { t_object x_obj; t_symbol *x_format; t_printtype x_accept; } t_makefilename; static const char* _formatscan(const char*str, t_printtype*typ) { int infmt=0; for (; *str; str++) { if (!infmt && *str=='%') { infmt=1; continue; } if (infmt) { if (*str=='%') { infmt=0; continue; } if (strchr("-.#0123456789",*str)!=0) continue; if (*str=='s') { *typ = STRING; return str; } if (strchr("fgGeE",*str)!=0) { *typ = FLOAT; return str; } if (strchr("xXdiouc",*str)!=0) { *typ = INT; return str; } if (strchr("p",*str)!=0) { *typ = POINTER; return str; } } } *typ = NONE; return str; } static void makefilename_scanformat(t_makefilename *x) { const char *str; t_printtype typ; if (!x->x_format) return; str = x->x_format->s_name; str = _formatscan(str, &typ); x->x_accept = typ; if (str && (NONE != typ)) { /* try again, to see if there's another format specifier (which we forbid) */ str = _formatscan(str, &typ); if (NONE != typ) { pd_error(x, "makefilename: invalid format string '%s' (too many format specifiers)", x->x_format->s_name); x->x_format = 0; return; } } } static void *makefilename_new(t_symbol *s) { t_makefilename *x = (t_makefilename *)pd_new(makefilename_class); if (!s || !*s->s_name) s = gensym("file.%d"); outlet_new(&x->x_obj, &s_symbol); x->x_format = s; x->x_accept = NONE; makefilename_scanformat(x); return (x); } static void makefilename_float(t_makefilename *x, t_floatarg f) { char buf[MAXPDSTRING]; if(!x->x_format) { pd_error(x, "makefilename: no format specifier given"); return; } switch(x->x_accept) { case NONE: sprintf(buf, "%s", x->x_format->s_name); break; case INT: case POINTER: sprintf(buf, x->x_format->s_name, (int)f); break; case FLOAT: sprintf(buf, x->x_format->s_name, f); break; case STRING: { char buf2[MAXPDSTRING]; sprintf(buf2, "%g", f); sprintf(buf, x->x_format->s_name, buf2); break; } default: sprintf(buf, "%s", x->x_format->s_name); } if (buf[0]!=0) outlet_symbol(x->x_obj.ob_outlet, gensym(buf)); } static void makefilename_symbol(t_makefilename *x, t_symbol *s) { char buf[MAXPDSTRING]; if(!x->x_format) { pd_error(x, "makefilename: no format specifier given"); return; } switch(x->x_accept) { case STRING: case POINTER: sprintf(buf, x->x_format->s_name, s->s_name); break; case INT: sprintf(buf, x->x_format->s_name, 0); break; case FLOAT: sprintf(buf, x->x_format->s_name, 0.); break; case NONE: sprintf(buf, "%s", x->x_format->s_name); break; default: sprintf(buf, "%s", x->x_format->s_name); } if (buf[0]!=0) outlet_symbol(x->x_obj.ob_outlet, gensym(buf)); } static void makefilename_bang(t_makefilename *x) { char buf[MAXPDSTRING]; if(!x->x_format) { pd_error(x, "makefilename: no format specifier given"); return; } switch(x->x_accept) { case INT: sprintf(buf, x->x_format->s_name, 0); break; case FLOAT: sprintf(buf, x->x_format->s_name, 0.); break; case NONE: sprintf(buf, "%s", x->x_format->s_name); break; default: sprintf(buf, "%s", x->x_format->s_name); } if (buf[0]!=0) outlet_symbol(x->x_obj.ob_outlet, gensym(buf)); } static void makefilename_set(t_makefilename *x, t_symbol *s) { x->x_format = s; makefilename_scanformat(x); } static void makefilename_setup(void) { makefilename_class = class_new(gensym("makefilename"), (t_newmethod)makefilename_new, 0, sizeof(t_makefilename), 0, A_DEFSYM, 0); class_addfloat(makefilename_class, makefilename_float); class_addsymbol(makefilename_class, makefilename_symbol); class_addbang(makefilename_class, makefilename_bang); class_addmethod(makefilename_class, (t_method)makefilename_set, gensym("set"), A_SYMBOL, 0); } /* -------------------------- swap ------------------------------ */ static t_class *swap_class; typedef struct _swap { t_object x_obj; t_outlet *x_out2; t_float x_f1; t_float x_f2; } t_swap; static void *swap_new(t_floatarg f) { t_swap *x = (t_swap *)pd_new(swap_class); x->x_f2 = f; x->x_f1 = 0; outlet_new(&x->x_obj, &s_float); x->x_out2 = outlet_new(&x->x_obj, &s_float); floatinlet_new(&x->x_obj, &x->x_f2); return (x); } static void swap_bang(t_swap *x) { outlet_float(x->x_out2, x->x_f1); outlet_float(x->x_obj.ob_outlet, x->x_f2); } static void swap_float(t_swap *x, t_float f) { x->x_f1 = f; swap_bang(x); } void swap_setup(void) { swap_class = class_new(gensym("swap"), (t_newmethod)swap_new, 0, sizeof(t_swap), 0, A_DEFFLOAT, 0); class_addcreator((t_newmethod)swap_new, gensym("fswap"), A_DEFFLOAT, 0); class_addbang(swap_class, swap_bang); class_addfloat(swap_class, swap_float); } /* -------------------------- change ------------------------------ */ static t_class *change_class; typedef struct _change { t_object x_obj; t_float x_f; } t_change; static void *change_new(t_floatarg f) { t_change *x = (t_change *)pd_new(change_class); x->x_f = f; outlet_new(&x->x_obj, &s_float); return (x); } static void change_bang(t_change *x) { outlet_float(x->x_obj.ob_outlet, x->x_f); } static void change_float(t_change *x, t_float f) { if (f != x->x_f) { x->x_f = f; outlet_float(x->x_obj.ob_outlet, x->x_f); } } static void change_set(t_change *x, t_float f) { x->x_f = f; } void change_setup(void) { change_class = class_new(gensym("change"), (t_newmethod)change_new, 0, sizeof(t_change), 0, A_DEFFLOAT, 0); class_addbang(change_class, change_bang); class_addfloat(change_class, change_float); class_addmethod(change_class, (t_method)change_set, gensym("set"), A_DEFFLOAT, 0); } /* -------------------- value ------------------------------ */ static t_class *value_class, *vcommon_class; typedef struct vcommon { t_pd c_pd; int c_refcount; t_float c_f; } t_vcommon; typedef struct _value { t_object x_obj; t_symbol *x_sym; t_float *x_floatstar; } t_value; /* get a pointer to a named floating-point variable. The variable belongs to a "vcommon" object, which is created if necessary. */ t_float *value_get(t_symbol *s) { t_vcommon *c = (t_vcommon *)pd_findbyclass(s, vcommon_class); if (!c) { c = (t_vcommon *)pd_new(vcommon_class); c->c_f = 0; c->c_refcount = 0; pd_bind(&c->c_pd, s); } c->c_refcount++; return (&c->c_f); } /* release a variable. This only frees the "vcommon" resource when the last interested party releases it. */ void value_release(t_symbol *s) { t_vcommon *c = (t_vcommon *)pd_findbyclass(s, vcommon_class); if (c) { if (!--c->c_refcount) { pd_unbind(&c->c_pd, s); pd_free(&c->c_pd); } } else bug("value_release"); } /* * value_getfloat -- obtain the float value of a "value" object * return 0 on success, 1 otherwise */ int value_getfloat(t_symbol *s, t_float *f) { t_vcommon *c = (t_vcommon *)pd_findbyclass(s, vcommon_class); if (!c) return (1); *f = c->c_f; return (0); } /* * value_setfloat -- set the float value of a "value" object * return 0 on success, 1 otherwise */ int value_setfloat(t_symbol *s, t_float f) { t_vcommon *c = (t_vcommon *)pd_findbyclass(s, vcommon_class); if (!c) return (1); c->c_f = f; return (0); } static void vcommon_float(t_vcommon *x, t_float f) { x->c_f = f; } static void *value_new(t_symbol *s) { t_value *x = (t_value *)pd_new(value_class); if (!*s->s_name) inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("symbol"), gensym("symbol2")); x->x_sym = s; x->x_floatstar = value_get(s); outlet_new(&x->x_obj, &s_float); return (x); } static void value_bang(t_value *x) { outlet_float(x->x_obj.ob_outlet, *x->x_floatstar); } static void value_float(t_value *x, t_float f) { *x->x_floatstar = f; } /* set method */ static void value_symbol2(t_value *x, t_symbol *s) { value_release(x->x_sym); x->x_sym = s; x->x_floatstar = value_get(s); } static void value_send(t_value *x, t_symbol *s) { if (s->s_thing) pd_float(s->s_thing, *x->x_floatstar); else pd_error(x, "%s: no such object", s->s_name); } static void value_ff(t_value *x) { value_release(x->x_sym); } static void value_setup(void) { value_class = class_new(gensym("value"), (t_newmethod)value_new, (t_method)value_ff, sizeof(t_value), 0, A_DEFSYM, 0); class_addcreator((t_newmethod)value_new, gensym("v"), A_DEFSYM, 0); class_addbang(value_class, value_bang); class_addfloat(value_class, value_float); class_addmethod(value_class, (t_method)value_symbol2, gensym("symbol2"), A_DEFSYM, 0); class_addmethod(value_class, (t_method)value_send, gensym("send"), A_SYMBOL, 0); vcommon_class = class_new(gensym("value"), 0, 0, sizeof(t_vcommon), CLASS_PD, 0); class_addfloat(vcommon_class, vcommon_float); } /* -------------- overall setup routine for this file ----------------- */ void x_connective_setup(void) { pdint_setup(); pdfloat_setup(); pdsymbol_setup(); bang_setup(); send_setup(); receive_setup(); select_setup(); route_setup(); pack_setup(); unpack_setup(); trigger_setup(); spigot_setup(); moses_setup(); until_setup(); makefilename_setup(); swap_setup(); change_setup(); value_setup(); } ================================================ FILE: libs/libpd/pure-data/src/x_file.c ================================================ /* Copyright (c) 2021 IOhannes m zmölnig. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* The "file" object. */ #define _XOPEN_SOURCE 600 #define _DEFAULT_SOURCE #include "m_pd.h" #include "g_canvas.h" #include "s_utf8.h" #include "m_private_utils.h" #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif #include #include #include #include #ifdef _WIN32 # include # include # include #else # include # include #endif #ifdef _MSC_VER # include typedef unsigned int mode_t; typedef SSIZE_T ssize_t; # define wstat _wstat #endif #ifndef S_ISREG #define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) #endif #ifndef S_ISDIR # define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) #endif #ifndef X_FILE_DEBUG # define X_FILE_DEBUG PD_DEBUG #endif #ifdef _WIN32 static int do_delete_ucs2(wchar_t*pathname) { struct stat sb; if (!wstat(pathname, &sb) && (S_ISDIR(sb.st_mode))) { /* a directory */ return !(RemoveDirectoryW(pathname)); } else { /* probably a file */ return !(DeleteFileW(pathname)); } } static int sys_stat(const char *pathname, struct stat *statbuf) { uint16_t ucs2buf[MAX_PATH]; u8_utf8toucs2(ucs2buf, MAX_PATH, pathname, strlen(pathname)); return wstat(ucs2buf, statbuf); } static int sys_rename(const char *oldpath, const char *newpath) { uint16_t src[MAX_PATH], dst[MAX_PATH]; u8_utf8toucs2(src, MAX_PATH, oldpath, MAX_PATH); u8_utf8toucs2(dst, MAX_PATH, newpath, MAX_PATH); return _wrename(src, dst); } static int sys_mkdir(const char *pathname, mode_t mode) { uint16_t ucs2name[MAX_PATH]; (void)mode; u8_utf8toucs2(ucs2name, MAX_PATH, pathname, MAX_PATH); return !(CreateDirectoryW(ucs2name, 0)); } static int sys_remove(const char *pathname) { uint16_t ucs2buf[MAXPDSTRING]; u8_utf8toucs2(ucs2buf, MAXPDSTRING, pathname, MAXPDSTRING); return do_delete_ucs2(ucs2buf); } static char* sys_getcwd(char *buf) { uint16_t ucs2buf[MAXPDSTRING]; memset(ucs2buf, 0, sizeof(ucs2buf)); if (!_wgetcwd(ucs2buf, MAXPDSTRING)) return 0; u8_ucs2toutf8(buf, MAXPDSTRING-1, ucs2buf, -1); buf[MAXPDSTRING-1] = 0; sys_unbashfilename(buf, buf); return buf; } static int sys_chdir(const char *path) { uint16_t ucs2buf[MAXPDSTRING]; u8_utf8toucs2(ucs2buf, MAXPDSTRING, path, MAXPDSTRING); return _wchdir(ucs2buf); } #else static int sys_stat(const char *pathname, struct stat *statbuf) { return stat(pathname, statbuf); } static int sys_rename(const char *oldpath, const char *newpath) { return rename(oldpath, newpath); } static int sys_mkdir(const char *pathname, mode_t mode) { return mkdir(pathname, mode); } static int sys_remove(const char *pathname) { return remove(pathname); } static char* sys_getcwd(char *buf) { return getcwd(buf, MAXPDSTRING); } static int sys_chdir(const char *path) { return chdir(path); } #endif /* expand env vars and ~ at the beginning of a path and make a copy to return */ static char*do_expandpath(const char *from, char *to, int bufsize) { if ((strlen(from) == 1 && from[0] == '~') || (strncmp(from,"~/", 2) == 0)) { #ifdef _WIN32 const char *home = getenv("USERPROFILE"); #else const char *home = getenv("HOME"); #endif if (home) { strncpy(to, home, bufsize); to[bufsize-1] = 0; strncpy(to + strlen(to), from + 1, bufsize - strlen(to)); to[bufsize-1] = 0; } else *to = 0; } else { strncpy(to, from, bufsize); to[bufsize-1] = 0; } #ifdef _WIN32 { char *buf = alloca(bufsize); ExpandEnvironmentStrings(to, buf, bufsize-1); buf[bufsize-1] = 0; strncpy(to, buf, bufsize); to[bufsize-1] = 0; } #endif return to; } /* unbash '\' to '/', and drop duplicate '/' */ static char*do_pathnormalize(const char *from, char *to) { const char *rp; char *wp, c; sys_unbashfilename(from, to); rp=wp=to; while((*wp++=c=*rp++)) { if('/' == c) { while('/' == *rp++); rp--; } } return to; } static char*do_expandunbash(const char *from, char *to, int bufsize) { do_expandpath(from, to, bufsize); to[bufsize-1]=0; sys_unbashfilename(to, to); to[bufsize-1]=0; return to; } static int str_endswith(char* str, char* end){ size_t strsize = strlen(str), endsize = strlen(end); if(strsizes_name); bufsize += strlen(prefix->s_name); } for(i=0; ia_type) { case A_NULL: abuf = 0; break; case A_SYMBOL: abuf = atom_getsymbol(a)->s_name; break; default: atom_string(a, sbuf, MAXPDSTRING); abuf = sbuf; break; } if(!abuf || !(alen = strlen(abuf))) continue; if(pathseparator == abuf[alen-1]) needseparator = 0; if(bufsize+alen+needseparator >= sizeof(buffer)) break; if(needseparator) { buffer[bufsize]=pathseparator; bufsize++; } needseparator=1; strcpy(buffer+bufsize, abuf); bufsize+=alen; } if(suffix && (alen=strlen(suffix->s_name)) && (bufsize+alen)s_name); bufsize+=alen; } result = gensym(do_pathnormalize(buffer, buffer)); return result; } #ifdef _WIN32 static const char*do_errmsg(char*buffer, size_t bufsize) { char errcode[10]; char*s; wchar_t wbuf[MAXPDSTRING]; DWORD err = GetLastError(); DWORD count = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, err, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), wbuf, MAXPDSTRING, NULL); if (!count || !WideCharToMultiByte(CP_UTF8, 0, wbuf, count+1, buffer, bufsize, 0, 0)) *buffer = '\0'; s=buffer + strlen(buffer)-1; while(('\r' == *s || '\n' == *s) && s>buffer) *s--=0; snprintf(errcode, sizeof(errcode), " [%ld]", err); errcode[sizeof(errcode)-1] = 0; strcat(buffer, errcode); return buffer; } #else /* !_WIN32 */ static const char*do_errmsg(char*buffer, size_t bufsize) { (void)buffer; (void)bufsize; return strerror(errno); } #endif /* !_WIN32 */ typedef struct _file_handler { int fh_fd; int fh_mode; /* 0..read, 1..write */ } t_file_handler; #define x_fd x_fhptr->fh_fd #define x_mode x_fhptr->fh_mode typedef struct _file_handle { t_object x_obj; t_file_handler x_fh; t_file_handler*x_fhptr; t_symbol*x_fcname; /* multiple [file handle] object can refer to the same [file define] */ mode_t x_creationmode; /* default: 0666, 0777 */ int x_verbose; /* default: 0 */ t_canvas*x_canvas; t_outlet*x_dataout; t_outlet*x_infoout; } t_file_handle; static int do_checkpathname(t_file_handle*x, const char*path) { int err_count = 0, warn_count = 0; char buf[4]; const char*s; /* check for illegal characters */ for(s = path; *s; s++) { const char*ill = "illegal"; int oops = 0; switch(*s) { case ':': case '\\': ill = "reserved"; /* falls through */ case '<': case '>': case '|': case '"': //case '/': case '?': case '*': oops++; break; default: if(*s<32) oops++; } if(oops) { #ifdef _WIN32 if(x->x_verbose) pd_error(x, "the path \"%s\" contains the character '%c', which is %s.", path, *s, ill); err_count++; #else if(x->x_verbose) logpost(x, X_FILE_DEBUG, "cross-platform issue: the path \"%s\" contains the character '%c', which is %s on MSW.", path, *s, ill); warn_count++; #endif goto fail; } } /* check for illegal names */ strncpy(buf, path, sizeof(buf)); buf[3] = 0; if(buf[2]) { const char* forbidden_names0[] = {"AUX", "CON", "NUL", "PRN", 0}; const char* forbidden_names1[] = {"COM", "LPT", 0}; const char**ref; /* upper-case the string */ int i; for(i=0; i<3; i++) { if (buf[i] >= 'a' && buf[i] <= 'z') buf[i] = buf[i] - ('a' - 'A'); } /* AUX, CON, NULL, PRN */ for(ref = forbidden_names0; *ref; ref++) { if(!strcmp(*ref, buf) && (!path[3] || '.' == path[3])) { #ifdef _WIN32 if(x->x_verbose) pd_error(x, "the path \"%s\" contains the reserved name '%s'.", path, *ref); err_count++; #else if(x->x_verbose) logpost(x, X_FILE_DEBUG, "cross-platform issue: the path \"%s\" contains the name '%s' which is reserved on MSW.", path, *ref); warn_count++; #endif goto fail; } } /* COM[1-9], LPT[1-9] */ for(ref = forbidden_names1; *ref; ref++) { if(!strcmp(*ref, buf) && path[3] > '0' && path[3] <= '9' && (!path[4] || '.' == path[4])) { #ifdef _WIN32 if(x->x_verbose) pd_error(x, "the path \"%s\" contains the reserved name '%s[1-9]'.", path, *ref); err_count++; #else if(x->x_verbose) logpost(x, X_FILE_DEBUG, "cross-platform issue: the path \"%s\" contains the name '%s[1-9]' which is reserved on MSW.", path, *ref); warn_count++; #endif goto fail; } } } return 0; fail: if(err_count>0) return 2; if(warn_count>0) return 1; return 0; } static int do_parse_creationmode(t_atom*ap) { const char*s; if(A_FLOAT==ap->a_type) return atom_getfloat(ap); if(A_SYMBOL!=ap->a_type) return -1; /* oopsie */ s = atom_getsymbol(ap)->s_name; if(!strncmp(s, "0o", 2)) { /* octal mode */ char*endptr; long mode = strtol(s+2, &endptr, 8); return (*endptr)?-1:(int)mode; } else if(!strncmp(s, "0x", 2)) { /* hex mode: nobody sane uses this... */ char*endptr; long mode = strtol(s+2, &endptr, 16); return (*endptr)?-1:(int)mode; } else { /* free form mode: a+rwx,go-w */ /* not supported yet */ return -1; } return -1; } static void do_parse_args(t_file_handle*x, int argc, t_atom*argv) { /* * -q: quiet mode * -v: verbose mode * -m : creation mode */ t_symbol*flag_m = gensym("-m"); t_symbol*flag_q = gensym("-q"); t_symbol*flag_v = gensym("-v"); x->x_fcname = 0; while(argc--) { const t_symbol*flag = atom_getsymbol(argv); if (0); else if (flag == flag_q) { x->x_verbose--; } else if (flag == flag_v) { x->x_verbose++; } else if (flag == flag_m) { int mode; if(!argc) { pd_error(x, "'-m' requires an argument"); break; } argc--; argv++; mode = do_parse_creationmode(argv); if(mode<0) { char buf[MAXPDSTRING]; atom_string(argv, buf, MAXPDSTRING); pd_error(x, "invalid creation mode '%s'", buf); break; } else { x->x_creationmode = mode; } } else { int filearg = (!argc); if(filearg) { x->x_fcname = (t_symbol*)flag; } else { pd_error(x, "unknown flag %s", flag->s_name); } break; } argv++; } x->x_verbose = x->x_verbose > 0; } static t_file_handle* do_file_handle_new(t_class*cls, t_symbol*s, int argc, t_atom*argv, int verbose, mode_t creationmode) { t_file_handle*x = (t_file_handle*)pd_new(cls); (void)s; x->x_fhptr = &x->x_fh; x->x_fd = -1; x->x_canvas = canvas_getcurrent(); x->x_creationmode = creationmode; x->x_verbose = verbose; x->x_dataout = outlet_new(&x->x_obj, 0); x->x_infoout = outlet_new(&x->x_obj, 0); do_parse_args(x, argc, argv); return x; } static int do_file_open(t_file_handle*x, const char* filename, int mode) { char expandbuf[MAXPDSTRING+1]; int fd = sys_open(do_expandpath(filename, expandbuf, MAXPDSTRING), mode, x?x->x_creationmode:0666); if(x) { x->x_fd = fd; if(fd<0) { if(x->x_verbose) pd_error(x, "unable to open '%s': %s", filename, strerror(errno)); if(x->x_infoout) outlet_bang(x->x_infoout); } } return fd; } static void file_set_verbosity(t_file_handle*x, t_float f) { x->x_verbose = (f>0.5); } static void file_set_creationmode(t_file_handle*x, t_symbol*s, int argc, t_atom*argv) { if(argc!=1) { pd_error(x, "usage: '%s '", s->s_name); return; } x->x_creationmode = do_parse_creationmode(argv); } /* ================ [file handle] ====================== */ t_class *file_define_class; static int file_handle_getdefine(t_file_handle*x) { if(x->x_fcname) { t_file_handle *y = (t_file_handle *)pd_findbyclass(x->x_fcname, file_define_class); if(y) { x->x_fhptr=&y->x_fh; return 1; } return 0; } x->x_fhptr = &x->x_fh; return 1; } static void file_handle_close(t_file_handle*x) { if (x->x_fd>=0) sys_close(x->x_fd); x->x_fd = -1; } static int file_handle_checkopen(t_file_handle*x, const char*cmd) { if(x->x_fcname) { if(!file_handle_getdefine(x)) { pd_error(x, "file handle: couldn't find file-define '%s'", x->x_fcname->s_name); return 0; } } if(x->x_fd<0) { if(!cmd)cmd=(x->x_mode)?"write":"read"; pd_error(x, "'%s' without prior 'open'", cmd); return 0; } return 1; } static void file_handle_do_read(t_file_handle*x, t_float f) { t_atom*outv; unsigned char*buf; ssize_t n, len, outc=f; if(outc<1) { pd_error(x, "cannot read %d bytes", (int)outc); return; } ALLOCA(unsigned char, buf, outc, 100); ALLOCA(t_atom, outv, outc, 100); if(buf && outv) { len = read(x->x_fd, buf, outc); for(n=0; n0) { outlet_list(x->x_dataout, gensym("list"), len, outv); } else if (!len) { file_handle_close(x); outlet_bang(x->x_infoout); } else { if(x->x_verbose) pd_error(x, "read failed: %s", strerror(errno)); file_handle_close(x); outlet_bang(x->x_infoout); } } else { pd_error(x, "couldn't allocate buffer for %d bytes", (int)outc); } FREEA(unsigned char, buf, outc, 100); FREEA(t_atom, outv, outc, 100); } static void file_handle_do_write(t_file_handle*x, int argc, t_atom*argv) { unsigned char*buf; size_t len = (argc>0)?argc:0; ALLOCA(unsigned char, buf, argc, 100); if(buf) { ssize_t n; for(n=0; nx_fd, buf, len); if(n >= 0 && (size_t)n < len) { n = write(x->x_fd, buf+n, len-n); } if (n<0) { pd_error(x, "write failed: %s", strerror(errno)); file_handle_close(x); outlet_bang(x->x_infoout); } } else { pd_error(x, "could not allocate %d bytes for writing", argc); } FREEA(unsigned char, buf, argc, 100); } static void file_handle_list(t_file_handle*x, t_symbol*s, int argc, t_atom*argv) { (void)s; if(!file_handle_checkopen(x, 0)) return; if(x->x_mode) { /* write_mode */ file_handle_do_write(x, argc, argv); } else { /* read mode */ if(1==argc && A_FLOAT==argv->a_type) { file_handle_do_read(x, atom_getfloat(argv)); } else { pd_error(x, "no way to handle 'list' messages while reading file"); } } } static void file_handle_set(t_file_handle*x, t_symbol*s) { if (gensym("") == s) s=0; if (s && x->x_fhptr == &x->x_fh && x->x_fh.fh_fd >= 0) { /* trying to set a name, even though we have an fd open... */ pd_error(x, "file handle: shadowing local file descriptor with '%s'", s->s_name); } else if (!s && x->x_fhptr != &x->x_fh && x->x_fh.fh_fd >= 0) { logpost(x, X_FILE_DEBUG, "file handle: unshadowing local file descriptor"); } x->x_fcname = s; file_handle_getdefine(x); } static void file_handle_seek(t_file_handle*x, t_symbol*s, int argc, t_atom*argv) { off_t offset=0; int whence = SEEK_SET; t_atom a[1]; switch(argc) { case 0: /* just output the current position */ whence=SEEK_CUR; break; case 2: { if (A_SYMBOL!=argv[1].a_type) goto usage; s=atom_getsymbol(argv+1); switch(*s->s_name) { case 'S': case 's': case 0: whence = SEEK_SET; break; case 'E': case 'e': whence = SEEK_END; break; case 'C': case 'c': case 'R': case 'r': whence = SEEK_CUR; break; default: pd_error(x, "seek mode must be 'set', 'end' or 'current' (resp. 'relative')"); return; } } /* falls through */ case 1: if (A_FLOAT!=argv[0].a_type) goto usage; offset = (int)atom_getfloat(argv); break; } if(!file_handle_checkopen(x, "seek")) return; offset = lseek(x->x_fd, offset, whence); SETFLOAT(a, offset); outlet_anything(x->x_infoout, gensym("seek"), 1, a); return; usage: pd_error(x, "usage: seek [ []]"); } static void file_handle_open(t_file_handle*x, t_symbol*file, t_symbol*smode) { int mode = O_RDONLY; if (x->x_fd>=0) { pd_error(x, "'open' without prior 'close'"); return; } if(!file_handle_getdefine(x)) { pd_error(x, "file handle: couldn't find file-define '%s'", x->x_fcname->s_name); return; } if(smode && smode!=&s_) { switch(smode->s_name[0]) { case 'r': /* read */ mode = O_RDONLY; break; case 'w': /* write */ mode = O_WRONLY; break; case 'a': /* append */ mode = O_WRONLY | O_APPEND; break; case 'c': /* create */ mode = O_WRONLY | O_TRUNC; break; } } if(mode & O_WRONLY) { mode |= O_CREAT; } if(do_file_open(x, file->s_name, mode)>=0) { /* check if we haven't accidentally opened a directory */ struct stat sb; if(fstat(x->x_fd, &sb)) { file_handle_close(x); if(x->x_verbose) pd_error(x, "unable to stat '%s': %s", file->s_name, strerror(errno)); outlet_bang(x->x_infoout); return; } if(S_ISDIR(sb.st_mode)) { file_handle_close(x); if(x->x_verbose) pd_error(x, "unable to open directory '%s' as file", file->s_name); outlet_bang(x->x_infoout); return; } x->x_mode = (mode&O_WRONLY)?1:0; } } static void file_handle_free(t_file_handle*x) { /* close our own file handle (if any) */ x->x_fhptr = &x->x_fh; file_handle_close(x); } /* ================ [file stat] ====================== */ static int do_file_stat(t_file_handle*x, const char*filename, struct stat*sb, int*is_symlink) { int result = -1; int fd = -1; char buf[MAXPDSTRING+1]; do_expandpath(filename, buf, MAXPDSTRING); if(is_symlink) { *is_symlink=0; #ifdef S_IFLNK if(!lstat(buf, sb)) { *is_symlink = !!(S_ISLNK(sb->st_mode)); } #endif } result = sys_stat(buf, sb); if(!result) return result; fd = do_file_open(0, filename, 0); if(fd >= 0) { result = fstat(fd, sb); sys_close(fd); } else result = -1; if(x) { x->x_fd = -1; if(result && x->x_verbose) { pd_error(x, "could not stat on '%s': %s", filename, strerror(errno)); } } return result; } static void do_dataout_symbol(t_file_handle*x, const char*selector, t_symbol*s) { t_atom ap[1]; SETSYMBOL(ap, s); outlet_anything(x->x_dataout, gensym(selector), 1, ap); } static void do_dataout_float(t_file_handle*x, const char*selector, t_float f) { t_atom ap[1]; SETFLOAT(ap, f); outlet_anything(x->x_dataout, gensym(selector), 1, ap); } static void do_dataout_time(t_file_handle*x, const char*selector, time_t t) { t_atom ap[7]; struct tm *ts = localtime(&t); if(!ts) { pd_error(x, "unable to convert timestamp %ld", (long int)t); } SETFLOAT(ap+0, ts->tm_year + 1900); SETFLOAT(ap+1, ts->tm_mon + 1); SETFLOAT(ap+2, ts->tm_mday); SETFLOAT(ap+3, ts->tm_hour); SETFLOAT(ap+4, ts->tm_min); SETFLOAT(ap+5, ts->tm_sec); SETFLOAT(ap+6, ts->tm_isdst); outlet_anything(x->x_dataout, gensym(selector), 7, ap); } static void file_stat_symbol(t_file_handle*x, t_symbol*filename) { /* get all the info for the given file */ struct stat sb; t_symbol*s; int is_symlink=0; int readable=0, writable=0, executable=0, owned=-1; char buf[MAXPDSTRING+1]; if(do_file_stat(x, filename->s_name, &sb, &is_symlink) < 0) { outlet_bang(x->x_infoout); return; } /* this is wrong: readable/writable/executable are supposed to report * on the *current* user, not the *owner* */ readable = !!(sb.st_mode & 0400); writable = !!(sb.st_mode & 0200); executable = !!(sb.st_mode & 0100); #ifdef HAVE_UNISTD_H /* this is the right way */ do_expandpath(filename->s_name, buf, MAXPDSTRING); readable = !(access(buf, R_OK)); writable = !(access(buf, W_OK)); executable = !(access(buf, X_OK)); #ifndef _WIN32 owned = (geteuid() == sb.st_uid); #endif #endif switch (sb.st_mode & S_IFMT) { case S_IFREG: #ifdef S_IFLNK case S_IFLNK: #endif do_dataout_float(x, "size", (int)(sb.st_size)); break; case S_IFDIR: do_dataout_float(x, "size", 0); break; default: do_dataout_float(x, "size", -1); break; } do_dataout_float(x, "readable", readable); do_dataout_float(x, "writable", writable); do_dataout_float(x, "executable", executable); do_dataout_float(x, "owned", owned); do_dataout_float(x, "isfile", !!(S_ISREG(sb.st_mode))); do_dataout_float(x, "isdirectory", !!(S_ISDIR(sb.st_mode))); do_dataout_float(x, "issymlink", is_symlink); do_dataout_float(x, "uid", (int)(sb.st_uid)); do_dataout_float(x, "gid", (int)(sb.st_gid)); do_dataout_float(x, "permissions", (int)(sb.st_mode & 0777)); switch (sb.st_mode & S_IFMT) { case S_IFREG: s = gensym("file"); break; case S_IFDIR: s = gensym("directory"); break; #ifdef S_IFBLK case S_IFBLK: s = gensym("blockdevice"); break; #endif #ifdef S_IFCHR case S_IFCHR: s = gensym("characterdevice"); break; #endif #ifdef S_IFIFO case S_IFIFO: s = gensym("pipe"); break; #endif #ifdef S_IFLNK case S_IFLNK: s = gensym("symlink"); break; #endif #ifdef S_IFSOCK case S_IFSOCK: s = gensym("socket"); break; #endif default: s = 0; break; } if(s) do_dataout_symbol(x, "type", s); else do_dataout_symbol(x, "type", gensym("unknown")); do_dataout_time(x, "atime", sb.st_atime); do_dataout_time(x, "mtime", sb.st_mtime); } static void file_size_symbol(t_file_handle*x, t_symbol*filename) { struct stat sb; if(do_file_stat(x, filename->s_name, &sb, 0) < 0) { outlet_bang(x->x_infoout); } else { switch (sb.st_mode & S_IFMT) { case S_IFREG: #ifdef S_IFLNK case S_IFLNK: #endif outlet_float(x->x_dataout, (int)(sb.st_size)); break; case S_IFDIR: outlet_float(x->x_dataout, 0); break; default: outlet_float(x->x_dataout, -1); break; } } } static void file_isfile_symbol(t_file_handle*x, t_symbol*filename) { struct stat sb; if(do_file_stat(x, filename->s_name, &sb, 0) < 0) { outlet_bang(x->x_infoout); } else { outlet_float(x->x_dataout, !!(S_ISREG(sb.st_mode))); } } static void file_isdirectory_symbol(t_file_handle*x, t_symbol*filename) { struct stat sb; if(do_file_stat(x, filename->s_name, &sb, 0) < 0) { outlet_bang(x->x_infoout); } else { outlet_float(x->x_dataout, !!(S_ISDIR(sb.st_mode))); } } /* ================ [file glob] ====================== */ #ifdef _WIN32 /* idiosyncrasies: * - cases are ignored ('a*' matches 'A.txt' and 'a.txt'), even with wine on ext4 * - only the filename component is returned (must prefix path separately) * - non-ASCII needs special handling * - '*?' seems to be illegal (e.g. 'f*?.txt'); '?*' seems to be fine though * - "*" matches files starting with '.' (including '.', '..', but also .gitignore) * - if the pattern includes '*.', it matches a trailing '~' * - wildcards do not apply to directory-components (e.g. 'foo/ * /' (without the spaces, they are just due to C-comments constraints)) * * plan: * - concat the path and the filename * - convert to utf16 (and back again) * - replace '*?' with '*' in the pattern * - manually filter out: * - matches starting with '.' if the pattern does not start with '.' * - matches ending in '~' if the pattern does not end with '[*?~]' * - only (officially) support wildcards in the filename component (not in the paths) * - if the pattern ends with '/', strip it, but return only directories */ static void file_glob_symbol(t_file_handle*x, t_symbol*spattern) { WIN32_FIND_DATAW FindFileData; HANDLE hFind; uint16_t ucs2pattern[MAXPDSTRING]; char pattern[MAXPDSTRING]; int nostartdot=0, noendtilde=0, onlydirs=0; char *filepattern, *strin, *strout; int pathpatternlength=0; int matchdot=0; do_expandunbash(spattern->s_name, pattern, MAXPDSTRING); /* '.' and '..' should only match if the pattern exquisitely asked for them */ if(!strcmp(".", pattern) || !strcmp("./", pattern) || str_endswith(pattern, "/.") || str_endswith(pattern, "/./")) matchdot=1; else if(!strcmp("..", pattern) || !strcmp("../", pattern) || str_endswith(pattern, "/..") || str_endswith(pattern, "/../")) matchdot=2; if (matchdot) { /* windows FindFile would return the actual path rather than '.' * (which would confuse our full-path construction) * so we just return the result directly */ struct stat sb; if (!do_file_stat(0, pattern, &sb, 0)) { t_atom outv[2]; size_t end = strlen(pattern); /* get rid of trailing slash */ if('/' == pattern[end-1]) pattern[end-1]=0; SETSYMBOL(outv+0, gensym(pattern)); SETFLOAT(outv+1, S_ISDIR(sb.st_mode)); outlet_list(x->x_dataout, gensym("list"), 2, outv); } else { // this gets triggered if there is no match... outlet_bang(x->x_infoout); } return; } filepattern=strrchr(pattern, '/'); if(filepattern && !filepattern[1]) { /* patterns ends with slashes: filter for dirs, and bash the trailing slashes */ onlydirs=1; while('/' == *filepattern && filepattern>pattern) { *filepattern--=0; } filepattern=strrchr(pattern, '/'); } if(!filepattern) filepattern=pattern; else { filepattern++; pathpatternlength=filepattern-pattern; } nostartdot=('.' != *filepattern); strin=filepattern; strout=filepattern; while(*strin) { char c = *strin++; *strout++ = c; if('*' == c) { while('?' == *strin || '*' == *strin) strin++; } } *strout=0; if (strout>pattern) { switch(strout[-1]) { case '~': case '*': case '?': noendtilde=0; break; default: noendtilde=1; } } u8_utf8toucs2(ucs2pattern, MAXPDSTRING, pattern, MAXPDSTRING); hFind = FindFirstFileW(ucs2pattern, &FindFileData); if (hFind == INVALID_HANDLE_VALUE) { // this gets triggered if there is no match... outlet_bang(x->x_infoout); return; } do { t_symbol*s; t_atom outv[2]; int len = 0; int isdir = !!(FILE_ATTRIBUTE_DIRECTORY & FindFileData.dwFileAttributes); if (matchdot!=1 && !wcscmp(L"." , FindFileData.cFileName)) continue; if (matchdot!=2 && !wcscmp(L".." , FindFileData.cFileName)) continue; u8_ucs2toutf8(filepattern, MAXPDSTRING-pathpatternlength, FindFileData.cFileName, MAX_PATH); len = strlen(filepattern); if(onlydirs && !isdir) continue; if(nostartdot && '.' == filepattern[0]) continue; if(noendtilde && '~' == filepattern[len-1]) continue; s = gensym(pattern); SETSYMBOL(outv+0, s); SETFLOAT(outv+1, isdir); outlet_list(x->x_dataout, gensym("list"), 2, outv); } while (FindNextFileW(hFind, &FindFileData) != 0); FindClose(hFind); } #else /* !_WIN32 */ static void file_glob_symbol(t_file_handle*x, t_symbol*spattern) { t_atom outv[2]; glob_t gg; int flags = 0; int matchdot=0; char pattern[MAXPDSTRING]; size_t patternlen; int onlydirs; do_expandpath(spattern->s_name, pattern, MAXPDSTRING); patternlen=strlen(pattern); onlydirs = ('/' == pattern[patternlen-1]); if(!strcmp(".", pattern) || !strcmp("./", pattern) || str_endswith(pattern, "/.") || str_endswith(pattern, "/./")) matchdot=1; else if(!strcmp("..", pattern) || !strcmp("../", pattern) || str_endswith(pattern, "/..") || str_endswith(pattern, "/../")) matchdot=2; if(glob(pattern, flags, NULL, &gg)) { // this gets triggered if there is no match... outlet_bang(x->x_infoout); } else { size_t i; for(i=0; ix_dataout, gensym("list"), 2, outv); } } globfree(&gg); } #endif /* _WIN32 */ /* ================ [file which] ====================== */ static void file_which_symbol(t_file_handle*x, t_symbol*s) { /* LATER we might output directories as well,... */ int isdir=0; t_atom outv[2]; char dirresult[MAXPDSTRING], *nameresult; int fd = canvas_open(x->x_canvas, s->s_name, "", dirresult, &nameresult, MAXPDSTRING, 1); if(fd>=0) { sys_close(fd); if(nameresult>dirresult) nameresult[-1]='/'; SETSYMBOL(outv+0, gensym(dirresult)); SETFLOAT(outv+1, isdir); outlet_list(x->x_dataout, gensym("list"), 2, outv); } else { outlet_symbol(x->x_infoout, s); } } /* ================ [file patchpath] ====================== */ static void file_patchpath_list(t_file_handle*x, t_symbol*s, int argc, t_atom*argv) { t_canvas *c = x->x_canvas; const char*pathname = 0; int i, parentlevel = 0, effectivelevel = 0; switch(argc) { default: goto fail; case 0: break; case 1: switch(argv->a_type) { case(A_SYMBOL): pathname = atom_getsymbol(argv)->s_name; break; case (A_FLOAT): parentlevel = (int)atom_getfloat(argv); break; default: goto fail; } break; case 2: if(A_SYMBOL == argv[0].a_type && A_FLOAT == argv[1].a_type) { pathname = atom_getsymbol(argv+0)->s_name; parentlevel = (int)atom_getfloat(argv+1); break; } goto fail; } for (i = 0; i < parentlevel; i++) { while (!c->gl_env) /* back up to containing canvas or abstraction */ c = c->gl_owner; if (c->gl_owner) /* back up one more into an owner if any */ { c = c->gl_owner; effectivelevel++; } } if (pathname) { if(sys_isabsolutepath(pathname)) { s = gensym(pathname); } else { char buf[MAXPDSTRING]; snprintf(buf, MAXPDSTRING, "%s/%s", canvas_getdir(c)->s_name, pathname); buf[MAXPDSTRING-1] = 0; s = gensym(buf); } } else s = canvas_getdir(c); outlet_float(x->x_infoout, effectivelevel); outlet_symbol(x->x_dataout, s); return; fail: pd_error(x, "bad arguments for message to object 'file patchpath'"); } /* ================ [file mkdir] ====================== */ static void file_mkdir_symbol(t_file_handle*x, t_symbol*dir) { char pathname[MAXPDSTRING], *path=pathname, *str; struct stat sb; do_expandpath(dir->s_name, pathname, MAXPDSTRING); pathname[MAXPDSTRING-1]=0; do_pathnormalize(pathname, pathname); if(sys_isabsolutepath(pathname)) { str=strchr(path, '/'); if(str) path=str; } path++; while(*path) { /* get to the next path separator */ str=strchr(path, '/'); if(str) { *str=0; } if(!sys_stat(pathname, &sb) && (S_ISDIR(sb.st_mode))) { // directory exists, skip... } else { if(sys_mkdir(pathname, x?x->x_creationmode:0777)) { char buf[MAXPDSTRING]; pd_error(x, "failed to create '%s': %s", pathname, do_errmsg(buf, MAXPDSTRING)); outlet_bang(x->x_infoout); return; } } if(str) { *str='/'; path=str+1; } else break; } outlet_symbol(x->x_dataout, gensym(pathname)); } /* ================ [file delete] ====================== */ #ifdef _WIN32 static int file_do_delete_recursive_ucs2(uint16_t*path) { WIN32_FIND_DATAW FindFileData; HANDLE hFind; uint16_t pattern[MAX_PATH]; swprintf(pattern, MAX_PATH, L"%ls/*", path); hFind = FindFirstFileW(pattern, &FindFileData); if (hFind == INVALID_HANDLE_VALUE) { return 1; } do { int isdir = !!(FILE_ATTRIBUTE_DIRECTORY & FindFileData.dwFileAttributes); swprintf(pattern, MAX_PATH, L"%ls/%ls", path, FindFileData.cFileName); if(!isdir) { DeleteFileW(pattern); } else { /* skip self and parent */ if(!wcscmp(L".", FindFileData.cFileName))continue; if(!wcscmp(L"..", FindFileData.cFileName))continue; file_do_delete_recursive_ucs2(pattern); } } while (FindNextFileW(hFind, &FindFileData) != 0); FindClose(hFind); return do_delete_ucs2(path); } static int file_do_delete_recursive(const char*path) { uint16_t ucs2path[MAXPDSTRING]; u8_utf8toucs2(ucs2path, MAXPDSTRING, path, MAXPDSTRING); return file_do_delete_recursive_ucs2(ucs2path); } #else /* !_WIN32 */ static int nftw_cb(const char *path, const struct stat *s, int flag, struct FTW *f) { (void)s; (void)f; (void)flag; return remove(path); } static int file_do_delete_recursive(const char*pathname) { return nftw(pathname, nftw_cb, 128, FTW_MOUNT|FTW_PHYS|FTW_DEPTH); } #endif /* !_WIN32 */ static void file_delete_symbol(t_file_handle*x, t_symbol*path) { char pathname[MAXPDSTRING]; do_expandunbash(path->s_name, pathname, MAXPDSTRING); if(sys_remove(pathname)) { char buf[MAXPDSTRING]; if(x && x->x_verbose) pd_error(x, "unable to delete '%s': %s", pathname, do_errmsg(buf, MAXPDSTRING)); outlet_bang(x->x_infoout); } else { outlet_symbol(x->x_dataout, gensym(pathname)); } } static void file_delete_recursive(t_file_handle*x, t_symbol*path) { char pathname[MAXPDSTRING]; do_expandunbash(path->s_name, pathname, MAXPDSTRING); if(file_do_delete_recursive(pathname)) { if(x->x_verbose) { char buf[MAXPDSTRING]; pd_error(x, "unable to recursively delete '%s': %s", pathname, do_errmsg(buf, MAXPDSTRING)); } outlet_bang(x->x_infoout); } else { outlet_symbol(x->x_dataout, gensym(pathname)); } } /* ================ [file copy]/[file move] ====================== */ static int file_do_copy(const char*source, const char*destination, int mode) { int result = 0; ssize_t len; char buf[1024]; int src, dst; int wflags = O_WRONLY | O_CREAT | O_TRUNC; #ifdef _WIN32 wflags |= O_BINARY; #endif src = sys_open(source, O_RDONLY); if(src<0) return 1; dst = sys_open(destination, wflags, mode); if(dst<0) { struct stat sb; /* check if destination is a directory: if so calculate the new filename */ if(!do_file_stat(0, destination, &sb, 0) && (S_ISDIR(sb.st_mode))) { char destfile[MAXPDSTRING]; const char*filename=strrchr(source, '/'); if(!filename) filename=source; else filename++; snprintf(destfile, MAXPDSTRING, "%s/%s", destination, filename); dst = sys_open(destfile, O_WRONLY | O_CREAT | O_TRUNC, mode); } } if(dst<0) return 1; while((len=read(src, buf, sizeof(buf)))>0) { ssize_t wlen=write(dst, buf, len); /* TODO: cater for partial writes */ if (wlen<1) { result = 1 ; } } sys_close(src); sys_close(dst); return (result); } static int file_do_move(const char*source, const char*destination, int mode) { int olderrno=0; int result = sys_rename(source, destination); (void)mode; if(result) { struct stat sb; int isfile=0; int isdir=0; olderrno=errno; /* check whether we are trying to move a file to a directory */ if(do_file_stat(0, source, &sb, 0) < 0) goto done; /* source is not statable, that's a serious error */ isfile = !(S_ISDIR(sb.st_mode)); if(do_file_stat(0, destination, &sb, 0) < 0) goto done; /* destination is not statable (so it doesn't exist and is not a directory either */ isdir = (S_ISDIR(sb.st_mode)); if(isfile && isdir) { char destfile[MAXPDSTRING]; const char*filename=strrchr(source, '/'); if(!filename) filename=source; else filename++; snprintf(destfile, MAXPDSTRING, "%s/%s", destination, filename); result = sys_rename(source, destfile); olderrno = errno; } } if(result && EXDEV == errno) { /* need to manually copy the file to the another filesystem */ result = file_do_copy(source, destination, mode); if(!result) { olderrno=0; /* copy succeeded, now get rid of the source file */ if(sys_remove(source)) { /* oops, couldn't delete the source-file... * we still report this as SUCCESS, as the file has been duplicated. * LATER we might try to unlink() the file first. * set the olderrno to print some error in verbose-mode */ olderrno=errno; } } } done: errno=olderrno; return result; } static void file_do_copymove(t_file_handle*x, const char*verb, int (*fun)(const char*,const char*,int), t_symbol*s, int argc, t_atom*argv) { struct stat sb; char src[MAXPDSTRING], dst[MAXPDSTRING]; if(argc != 2 || A_SYMBOL != argv[0].a_type || A_SYMBOL != argv[1].a_type) { pd_error(x, "bad arguments for [file %s] - should be 'source:symbol destination:symbol'", verb); return; } do_expandunbash(atom_getsymbol(argv+0)->s_name, src, MAXPDSTRING); do_expandunbash(atom_getsymbol(argv+1)->s_name, dst, MAXPDSTRING); if(!sys_stat(src, &sb)) { if(S_ISDIR(sb.st_mode)) { if(x->x_verbose) { pd_error(x, "failed to %s '%s': %s", verb, src, strerror(EISDIR)); } outlet_bang(x->x_infoout); return; } } errno = 0; if(fun(src, dst, x->x_creationmode?x->x_creationmode:sb.st_mode)) { if(x->x_verbose) { char buf[MAXPDSTRING]; pd_error(x, "failed to %s '%s' to '%s': %s", verb, src, dst, do_errmsg(buf, MAXPDSTRING)); } outlet_bang(x->x_infoout); } else { if(errno && x->x_verbose) { char buf[MAXPDSTRING]; pd_error(x, "troubles (but overall success) to %s '%s' to '%s': %s", verb, src, dst, do_errmsg(buf, MAXPDSTRING)); } outlet_list(x->x_dataout, s, argc, argv); } } static void file_copy_list(t_file_handle*x, t_symbol*s, int argc, t_atom*argv) { file_do_copymove(x, "copy", file_do_copy, s, argc, argv); } static void file_move_list(t_file_handle*x, t_symbol*s, int argc, t_atom*argv) { file_do_copymove(x, "move", file_do_move, s, argc, argv); } /* ================ file cwd ====================== */ static void file_cwd_bang(t_file_handle*x) { char buf[MAXPDSTRING]; if(sys_getcwd(buf)) { outlet_symbol(x->x_dataout, gensym(buf)); } else { if(x->x_verbose) pd_error(x, "could not query current working directory: %s", do_errmsg(buf, MAXPDSTRING)); outlet_bang(x->x_infoout); } } static void file_cwd_symbol(t_file_handle*x, t_symbol*path) { if(!sys_chdir(path->s_name)) { file_cwd_bang(x); } else { if(x->x_verbose) { char buf[MAXPDSTRING]; pd_error(x, "could not change the working directory to '%s': %s", path->s_name, do_errmsg(buf, MAXPDSTRING)); } outlet_bang(x->x_infoout); } } /* ================ file path operations ====================== */ static void file_split_symbol(t_file_handle*x, t_symbol*path) { int outc = 0; t_atom*outv = 0; t_symbol*slashsym = do_splitpath(path->s_name, &outc, &outv); if (slashsym) outlet_symbol(x->x_infoout, slashsym); else outlet_bang(x->x_infoout); outlet_list(x->x_dataout, gensym("list"), outc, outv); freebytes(outv, outc * sizeof(*outv)); } static void file_join_list(t_file_handle*x, t_symbol*s, int argc, t_atom*argv) { s = do_joinpath(0, argc, argv, 0); outlet_symbol(x->x_dataout, s); } static void file_splitext_symbol(t_file_handle*x, t_symbol*path) { char pathname[MAXPDSTRING]; t_atom outv[2]; char*str; sys_unbashfilename(path->s_name, pathname); pathname[MAXPDSTRING-1]=0; str=pathname + strlen(pathname)-1; if(str < pathname || '.' != *str) { while(str>=pathname) { char c = *str; switch(c) { case '.': str[0]=0; SETSYMBOL(outv+0, gensym(pathname)); SETSYMBOL(outv+1, gensym(str+1)); outlet_list(x->x_dataout, gensym("list"), 2, outv); return; case '/': str = pathname; break; default: break; } str--; } } outlet_symbol(x->x_infoout, gensym(pathname)); } static void file_splitname_symbol(t_file_handle*x, t_symbol*path) { char pathname[MAXPDSTRING]; char*str; sys_unbashfilename(path->s_name, pathname); pathname[MAXPDSTRING-1]=0; str=strrchr(pathname, '/'); if(str>pathname) { t_symbol*s; *str++=0; s = gensym(pathname); if(*str) { t_atom outv[2]; SETSYMBOL(outv+0, s); SETSYMBOL(outv+1, gensym(str)); outlet_list(x->x_dataout, gensym("list"), 2, outv); } else { outlet_symbol(x->x_dataout, s); } } else { outlet_symbol(x->x_infoout, gensym(pathname)); } } static void file_normalize_symbol(t_file_handle*x, t_symbol*path) { char _path[MAXPDSTRING]; char*xpath=do_expandunbash(path->s_name, _path, sizeof(_path)); int argc=0; t_atom*argv=0; t_symbol*dot=gensym("."), *dotdot=gensym(".."); t_symbol*slash=gensym("/"), *dotslash=gensym("./"); const int isabs=sys_isabsolutepath(xpath); t_symbol*suffix=0,*volume=0, *result=0; int check, changed, i; #ifdef _WIN32 if(xpath[0] && ':' == xpath[1]) { char vol[3]; vol[0] = xpath[0]; vol[1] = xpath[1]; vol[2] = 0; volume = gensym(vol); xpath+=2; } #endif suffix=do_splitpath(xpath, &argc, &argv); /* drop 'empty' elements ('.') */ for(i=isabs; ia_type = argv[i].a_type = A_NULL; changed = 1; } last = 0; } else { last = argv+i; } } } /* drop any leading '..' components in absolute mode... */ if(isabs) { for(i=isabs; ia_type) continue; if (dotdot == atom_getsymbol(a)) { a->a_type = A_NULL; } else break; } } /* an join the leftovers */ result = do_joinpath(volume, argc, argv, suffix); /* find problematic components */ check = 0; for(i=0; ia_type) continue; chk = do_checkpathname(x, atom_getsymbol(a)->s_name); if(chk>check)check=chk; } /* free dynamically allocated memory before outputting data */ freebytes(argv, argc * sizeof(*argv)); /* output the error/warning/ok */ outlet_float(x->x_infoout, check); /* output the normalized path */ if(result && *result->s_name) { if(!isabs && slash == result) outlet_symbol(x->x_dataout, dotslash); else outlet_symbol(x->x_dataout, result); } else { if(suffix) outlet_symbol(x->x_dataout, dotslash); else outlet_symbol(x->x_dataout, dot); } } static void file_isabsolute_symbol(t_file_handle*x, t_symbol*path) { char xpath[MAXPDSTRING]; outlet_float(x->x_dataout, sys_isabsolutepath(do_expandunbash(path->s_name, xpath, MAXPDSTRING))); } /* overall creator for "file" objects - dispatch to "file handle" etc */ t_class *file_handle_class, *file_which_class, *file_glob_class, *file_patchpath_class; t_class *file_stat_class, *file_size_class, *file_isfile_class, *file_isdirectory_class; t_class *file_mkdir_class, *file_delete_class, *file_copy_class, *file_move_class; t_class *file_split_class,*file_join_class,*file_splitext_class, *file_splitname_class; t_class *file_normalize_class, *file_isabsolute_class, *file_cwd_class; #define FILE_PD_NEW(verb, verbose, creationmode) static t_file_handle* file_##verb##_new(t_symbol*s, int argc, t_atom*argv) \ { \ return do_file_handle_new(file_##verb##_class, s, argc, argv, verbose, creationmode); \ } static void file_define_ignore(t_file_handle*x, t_symbol*s, int argc, t_atom*argv) { /* this is a noop (so the object does not bail out if somebody 'send's something to its label) */ (void)x; (void)s; (void)argc; (void)argv; } static t_file_handle*file_define_new(t_symbol*s, int argc, t_atom*argv) { t_file_handle*x = (t_file_handle*)pd_new(file_define_class); x->x_fhptr = &x->x_fh; x->x_fh.fh_fd = -1; x->x_canvas = canvas_getcurrent(); x->x_creationmode = 0666; x->x_verbose = 0; if(1 == argc && A_SYMBOL == argv->a_type) { x->x_fcname = atom_getsymbol(argv); pd_bind(&x->x_obj.ob_pd, x->x_fcname); } else { pd_error(x, "%s requires an argument: handle name", s->s_name); } return x; } static void file_define_free(t_file_handle*x) { file_handle_close(x); if(x->x_fcname) pd_unbind(&x->x_obj.ob_pd, x->x_fcname); } static t_file_handle*file_handle_new(t_symbol*s, int argc, t_atom*argv) { t_file_handle*x=do_file_handle_new(file_handle_class, s, argc, argv, 1, 0666); inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("symbol"), gensym("set")); return x; } FILE_PD_NEW(which, 0, 0); FILE_PD_NEW(patchpath, 0, 0); FILE_PD_NEW(glob, 0, 0); FILE_PD_NEW(stat, 0, 0); FILE_PD_NEW(size, 0, 0); FILE_PD_NEW(isfile, 0, 0); FILE_PD_NEW(isdirectory, 0, 0); FILE_PD_NEW(mkdir, 0, 0777); FILE_PD_NEW(delete, 0, 0); FILE_PD_NEW(copy, 0, 0); FILE_PD_NEW(move, 0, 0); FILE_PD_NEW(cwd, 1, 0); FILE_PD_NEW(split, 0, 0); FILE_PD_NEW(join, 0, 0); FILE_PD_NEW(splitext, 0, 0); FILE_PD_NEW(splitname, 0, 0); FILE_PD_NEW(isabsolute, 0, 0); FILE_PD_NEW(normalize, 1, 0); static t_pd *fileobj_new(t_symbol *s, int argc, t_atom*argv) { t_file_handle*x = 0; const char*verb=0; if(gensym("file") == s) { if (argc && A_SYMBOL == argv->a_type) { verb = atom_getsymbol(argv)->s_name; argc--; argv++; } else { verb = "handle"; } } else if (strlen(s->s_name)>5) { verb = s->s_name + 5; } if (!verb || !*verb) x = do_file_handle_new(file_handle_class, gensym("file handle"), argc, argv, 1, 0666); else { #define ELIF_FILE_PD_NEW(name, verbose, creationmode) else \ if (!strcmp(verb, #name)) \ x = do_file_handle_new(file_##name##_class, gensym("file "#name), argc, argv, verbose, creationmode) if (!strcmp(verb, "define")) x = file_define_new(gensym("file define"), argc, argv); else if (!strcmp(verb, "handle")) x = file_handle_new(gensym("file handle"), argc, argv); ELIF_FILE_PD_NEW(handle, 1, 0666); ELIF_FILE_PD_NEW(which, 0, 0); ELIF_FILE_PD_NEW(patchpath, 0, 0); ELIF_FILE_PD_NEW(glob, 0, 0); ELIF_FILE_PD_NEW(stat, 0, 0); ELIF_FILE_PD_NEW(size, 0, 0); ELIF_FILE_PD_NEW(isfile, 0, 0); ELIF_FILE_PD_NEW(isdirectory, 0, 0); ELIF_FILE_PD_NEW(mkdir, 0, 0777); ELIF_FILE_PD_NEW(delete, 0, 0); ELIF_FILE_PD_NEW(copy, 0, 0); ELIF_FILE_PD_NEW(move, 0, 0); ELIF_FILE_PD_NEW(cwd, 1, 0); ELIF_FILE_PD_NEW(split, 0, 0); ELIF_FILE_PD_NEW(join, 0, 0); ELIF_FILE_PD_NEW(splitext, 0, 0); ELIF_FILE_PD_NEW(splitname, 0, 0); ELIF_FILE_PD_NEW(isabsolute, 0, 0); ELIF_FILE_PD_NEW(normalize, 1, 0); else { pd_error(0, "file %s: unknown function", verb); } } return (t_pd*)x; } typedef enum _filenew_flag { DEFAULT = 0, VERBOSE = 1<<0, MODE = 1<<1 } t_filenew_flag; static t_class*file_class_new(const char*name , t_file_handle* (*ctor)(t_symbol*,int,t_atom*), void (*dtor)(t_file_handle*) , void (*symfun)(t_file_handle*, t_symbol*) , t_filenew_flag flag ) { t_class*cls = class_new(gensym(name), (t_newmethod)ctor, (t_method)dtor, sizeof(t_file_handle), 0, A_GIMME, 0); if (flag & VERBOSE) class_addmethod(cls, (t_method)file_set_verbosity, gensym("verbose"), A_FLOAT, 0); if (flag & MODE) class_addmethod(cls, (t_method)file_set_creationmode, gensym("creationmode"), A_GIMME, 0); if(symfun) class_addsymbol(cls, (t_method)symfun); class_sethelpsymbol(cls, gensym("file")); return cls; } /* ---------------- global setup function -------------------- */ void x_file_setup(void) { class_addcreator((t_newmethod)fileobj_new, gensym("file"), A_GIMME, 0); /* [file define] */ file_define_class = class_new(gensym("file define"), (t_newmethod)file_define_new, (t_method)file_define_free, sizeof(t_file_handle), CLASS_NOINLET, A_GIMME, 0); class_addanything(file_define_class, file_define_ignore); class_sethelpsymbol(file_define_class, gensym("file")); /* [file handle] */ file_handle_class = file_class_new("file handle", file_handle_new, file_handle_free, 0, MODE|VERBOSE); class_addmethod(file_handle_class, (t_method)file_handle_open, gensym("open"), A_SYMBOL, A_DEFSYM, 0); class_addmethod(file_handle_class, (t_method)file_handle_close, gensym("close"), 0); class_addmethod(file_handle_class, (t_method)file_handle_seek, gensym("seek"), A_GIMME, 0); class_addmethod(file_handle_class, (t_method)file_handle_set, gensym("set"), A_DEFSYMBOL, 0); class_addlist(file_handle_class, file_handle_list); /* [file which] */ file_which_class = file_class_new("file which", file_which_new, 0, file_which_symbol, VERBOSE); /* [file patchpath] */ file_patchpath_class = file_class_new("file patchpath", file_patchpath_new, 0, 0, VERBOSE); class_addlist(file_patchpath_class, file_patchpath_list); /* [file glob] */ file_glob_class = file_class_new("file glob", file_glob_new, 0, file_glob_symbol, VERBOSE); /* [file stat] */ file_stat_class = file_class_new("file stat", file_stat_new, 0, file_stat_symbol, VERBOSE); file_size_class = file_class_new("file size", file_size_new, 0, file_size_symbol, VERBOSE); file_isfile_class = file_class_new("file isfile", file_isfile_new, 0, file_isfile_symbol, VERBOSE); file_isdirectory_class = file_class_new("file isdirectory", file_isdirectory_new, 0, file_isdirectory_symbol, VERBOSE); /* [file mkdir] */ file_mkdir_class = file_class_new("file mkdir", file_mkdir_new, 0, file_mkdir_symbol, MODE|VERBOSE); /* [file delete] */ file_delete_class = file_class_new("file delete", file_delete_new, 0, file_delete_symbol, VERBOSE); class_addmethod(file_delete_class, (t_method)file_delete_recursive, gensym("recursive"), A_SYMBOL, 0); /* [file copy] */ file_copy_class = file_class_new("file copy", file_copy_new, 0, 0, MODE|VERBOSE); class_addlist(file_copy_class, (t_method)file_copy_list); /* [file move] */ file_move_class = file_class_new("file move", file_move_new, 0, 0, MODE|VERBOSE); class_addlist(file_move_class, (t_method)file_move_list); /* [file cwd] */ file_cwd_class = file_class_new("file cwd", file_cwd_new, 0, file_cwd_symbol, VERBOSE); class_addbang(file_cwd_class, (t_method)file_cwd_bang); /* file path objects */ file_split_class = file_class_new("file split", file_split_new, 0, file_split_symbol, DEFAULT); file_join_class = file_class_new("file join", file_join_new, 0, 0, DEFAULT); class_addlist(file_join_class, (t_method)file_join_list); file_splitext_class = file_class_new("file splitext", file_splitext_new, 0, file_splitext_symbol, DEFAULT); file_splitname_class = file_class_new("file splitname", file_splitname_new, 0, file_splitname_symbol, DEFAULT); file_isabsolute_class = file_class_new("file isabsolute", file_isabsolute_new, 0, file_isabsolute_symbol, DEFAULT); file_normalize_class = file_class_new("file normalize", file_normalize_new, 0, file_normalize_symbol, VERBOSE); } ================================================ FILE: libs/libpd/pure-data/src/x_gui.c ================================================ /* Copyright (c) 1997-2000 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* dialogs. LATER, deal with the situation where the object goes away before the panel does... */ #include "m_pd.h" #include "g_canvas.h" #include #include #ifdef HAVE_UNISTD_H #include #endif #include "m_private_utils.h" /* --------------------- graphics responder ---------------- */ /* make one of these if you want to put up a dialog window but want to be protected from getting deleted and then having the dialog call you back. In this design the calling object doesn't have to keep the address of the dialog window around; instead we keep a list of all open dialogs. Any object that might have dialogs, when it is deleted, simply checks down the dialog window list and breaks off any dialogs that might later have sent messages to it. Only when the dialog window itself closes do we delete the gfxstub object. */ static t_class *gfxstub_class; typedef struct _gfxstub { t_pd x_pd; t_pd *x_owner; void *x_key; t_symbol *x_sym; struct _gfxstub *x_next; } t_gfxstub; static t_gfxstub *gfxstub_list; /* create a new one. the "key" is an address by which the owner will identify it later; if the owner only wants one dialog, this could just be a pointer to the owner itself. The string "cmd" is a TK command to create the dialog, with "%s" embedded in it so we can provide a name by which the GUI can send us back messages; e.g., "pdtk_canvas_dofont %s 10". */ static t_symbol*gfxstub_symbol(t_gfxstub *x) { char namebuf[80]; sprintf(namebuf, ".gfxstub%lx", (t_int)x); return gensym(namebuf); } void gfxstub_new(t_pd *owner, void *key, const char *cmd) { char buf[4*MAXPDSTRING]; char sprintfbuf[MAXPDSTRING]; char *afterpercent; t_int afterpercentlen; t_gfxstub *x; t_symbol *s; /* if any exists with matching key, burn it. */ for (x = gfxstub_list; x; x = x->x_next) if (x->x_key == key) gfxstub_deleteforkey(key); if (strlen(cmd) + 50 > 4*MAXPDSTRING) { bug("audio dialog too long"); bug("%s", cmd); return; } x = (t_gfxstub *)pd_new(gfxstub_class); s = gfxstub_symbol(x); pd_bind(&x->x_pd, s); x->x_owner = owner; x->x_sym = s; x->x_key = key; x->x_next = gfxstub_list; gfxstub_list = x; /* only replace first %s so sprintf() doesn't crash */ afterpercent = strchr(cmd, '%') + 2; afterpercentlen = afterpercent - cmd; strncpy(sprintfbuf, cmd, afterpercentlen); sprintfbuf[afterpercentlen] = '\0'; sprintf(buf, sprintfbuf, s->s_name); strncat(buf, afterpercent, (4*MAXPDSTRING) - afterpercentlen); sys_gui(buf); } static void gfxstub_offlist(t_gfxstub *x) { t_gfxstub *y1, *y2; if (gfxstub_list == x) gfxstub_list = x->x_next; else for (y1 = gfxstub_list; (y2 = y1->x_next); y1 = y2) if (y2 == x) { y1->x_next = y2->x_next; break; } } /* if the owner disappears, we still may have to stay around until our dialog window signs off. Anyway we can now tell the GUI to destroy the window. */ void gfxstub_deleteforkey(void *key) { t_gfxstub *y; int didit = 1; while (didit) { didit = 0; for (y = gfxstub_list; y; y = y->x_next) { if (y->x_key == key) { t_symbol *s = gfxstub_symbol(y); pdgui_vmess("destroy", "s", s->s_name); y->x_owner = 0; gfxstub_offlist(y); didit = 1; break; } } } } /* --------- pd messages for gfxstub (these come from the GUI) ---------- */ /* "cancel" to request that we close the dialog window. */ static void gfxstub_cancel(t_gfxstub *x) { gfxstub_deleteforkey(x->x_key); } /* "signoff" comes from the GUI to say the dialog window closed. */ static void gfxstub_signoff(t_gfxstub *x) { gfxstub_offlist(x); pd_free(&x->x_pd); } static t_binbuf *gfxstub_binbuf; /* a series of "data" messages rebuilds a scalar */ static void gfxstub_data(t_gfxstub *x, t_symbol *s, int argc, t_atom *argv) { if (!gfxstub_binbuf) gfxstub_binbuf = binbuf_new(); binbuf_add(gfxstub_binbuf, argc, argv); binbuf_addsemi(gfxstub_binbuf); } /* the "end" message terminates rebuilding the scalar */ static void gfxstub_end(t_gfxstub *x) { canvas_dataproperties((t_canvas *)x->x_owner, (t_scalar *)x->x_key, gfxstub_binbuf); if(gfxstub_binbuf) binbuf_free(gfxstub_binbuf); gfxstub_binbuf = 0; } /* anything else is a message from the dialog window to the owner; just forward it. */ static void gfxstub_anything(t_gfxstub *x, t_symbol *s, int argc, t_atom *argv) { if (x->x_owner) pd_typedmess(x->x_owner, s, argc, argv); } static void gfxstub_free(t_gfxstub *x) { pd_unbind(&x->x_pd, x->x_sym); } static void gfxstub_setup(void) { gfxstub_class = class_new(gensym("gfxstub"), 0, (t_method)gfxstub_free, sizeof(t_gfxstub), CLASS_PD, 0); class_addanything(gfxstub_class, gfxstub_anything); class_addmethod(gfxstub_class, (t_method)gfxstub_signoff, gensym("signoff"), 0); class_addmethod(gfxstub_class, (t_method)gfxstub_data, gensym("data"), A_GIMME, 0); class_addmethod(gfxstub_class, (t_method)gfxstub_end, gensym("end"), 0); class_addmethod(gfxstub_class, (t_method)gfxstub_cancel, gensym("cancel"), 0); } #include /* pdgui_*mess() are from s_inter_gui.c */ void pdgui_vamess(const char* message, const char* format, va_list args); void pdgui_endmess(void); static void _pdguistub_vamess(const char*dest, const char*fmt, ...) { va_list args; va_start(args, fmt); pdgui_vamess(dest, fmt, args); va_end(args); } void pdgui_stub_vnew(t_pd *owner, const char* destination, void *key, const char* fmt, ...) { t_symbol*s; t_gfxstub *x; va_list args; /* if any exists with matching key, burn it. */ for (x = gfxstub_list; x; x = x->x_next) if (x->x_key == key) gfxstub_deleteforkey(key); x = (t_gfxstub *)pd_new(gfxstub_class); s = gfxstub_symbol(x); pd_bind(&x->x_pd, s); x->x_owner = owner; x->x_sym = s; x->x_key = key; x->x_next = gfxstub_list; gfxstub_list = x; _pdguistub_vamess(destination, "s", s->s_name); va_start(args, fmt); pdgui_vamess(0, fmt, args); va_end(args); pdgui_endmess(); } void pdgui_stub_deleteforkey(void *key) { gfxstub_deleteforkey(key); } /* -------------------------- openpanel ------------------------------ */ static t_class *openpanel_class; typedef struct _openpanel { t_object x_obj; t_symbol *x_s; int x_mode; /* 0: file, 1: folder, 2: multiple files */ } t_openpanel; static void *openpanel_new(t_floatarg mode) { char buf[50]; int m = (int)mode; t_openpanel *x = (t_openpanel *)pd_new(openpanel_class); x->x_mode = (mode < 0 || mode > 2) ? 0 : mode; sprintf(buf, "d%lx", (t_int)x); x->x_s = gensym(buf); pd_bind(&x->x_obj.ob_pd, x->x_s); outlet_new(&x->x_obj, &s_symbol); return (x); } static void openpanel_symbol(t_openpanel *x, t_symbol *s) { const char *path = (s && s->s_name) ? s->s_name : "\"\""; pdgui_vmess("pdtk_openpanel", "ssi", x->x_s->s_name, path, x->x_mode); } static void openpanel_bang(t_openpanel *x) { openpanel_symbol(x, &s_); } static void openpanel_callback(t_openpanel *x, t_symbol *s, int argc, t_atom *argv) { if (x->x_mode != 2) /* single file or folder */ { if (argc == 1 && argv->a_type == A_SYMBOL) outlet_symbol(x->x_obj.ob_outlet, argv->a_w.w_symbol); else bug("openpanel_callback"); } else /* list of files */ outlet_list(x->x_obj.ob_outlet, s, argc, argv); } static void openpanel_free(t_openpanel *x) { pd_unbind(&x->x_obj.ob_pd, x->x_s); } static void openpanel_setup(void) { openpanel_class = class_new(gensym("openpanel"), (t_newmethod)openpanel_new, (t_method)openpanel_free, sizeof(t_openpanel), 0, A_DEFFLOAT, 0); class_addbang(openpanel_class, openpanel_bang); class_addsymbol(openpanel_class, openpanel_symbol); class_addmethod(openpanel_class, (t_method)openpanel_callback, gensym("callback"), A_GIMME, 0); } /* -------------------------- savepanel ------------------------------ */ static t_class *savepanel_class; typedef struct _savepanel { t_object x_obj; t_canvas *x_canvas; t_symbol *x_s; } t_savepanel; static void *savepanel_new(void) { char buf[50]; t_savepanel *x = (t_savepanel *)pd_new(savepanel_class); sprintf(buf, "d%lx", (t_int)x); x->x_s = gensym(buf); x->x_canvas = canvas_getcurrent(); pd_bind(&x->x_obj.ob_pd, x->x_s); outlet_new(&x->x_obj, &s_symbol); return (x); } static void savepanel_symbol(t_savepanel *x, t_symbol *s) { const char *path = (s && s->s_name) ? s->s_name : "\"\""; pdgui_vmess("pdtk_savepanel", "ss", x->x_s->s_name, path); } static void savepanel_bang(t_savepanel *x) { savepanel_symbol(x, &s_); } static void savepanel_callback(t_savepanel *x, t_symbol *s) { outlet_symbol(x->x_obj.ob_outlet, s); } static void savepanel_free(t_savepanel *x) { pd_unbind(&x->x_obj.ob_pd, x->x_s); } static void savepanel_setup(void) { savepanel_class = class_new(gensym("savepanel"), (t_newmethod)savepanel_new, (t_method)savepanel_free, sizeof(t_savepanel), 0, 0); class_addbang(savepanel_class, savepanel_bang); class_addsymbol(savepanel_class, savepanel_symbol); class_addmethod(savepanel_class, (t_method)savepanel_callback, gensym("callback"), A_SYMBOL, 0); } /* ---------------------- key and its relatives ------------------ */ static t_class *key_class, *keyup_class, *keyname_class; typedef struct _key { t_object x_obj; } t_key; static void *key_new(void) { t_key *x = (t_key *)pd_new(key_class); outlet_new(&x->x_obj, &s_float); pd_bind(&x->x_obj.ob_pd, gensym("#key")); return (x); } static void key_float(t_key *x, t_floatarg f) { outlet_float(x->x_obj.ob_outlet, f); } static void key_free(t_key *x) { pd_unbind(&x->x_obj.ob_pd, gensym("#key")); } typedef struct _keyup { t_object x_obj; } t_keyup; static void *keyup_new(void) { t_keyup *x = (t_keyup *)pd_new(keyup_class); outlet_new(&x->x_obj, &s_float); pd_bind(&x->x_obj.ob_pd, gensym("#keyup")); return (x); } static void keyup_float(t_keyup *x, t_floatarg f) { outlet_float(x->x_obj.ob_outlet, f); } static void keyup_free(t_keyup *x) { pd_unbind(&x->x_obj.ob_pd, gensym("#keyup")); } typedef struct _keyname { t_object x_obj; t_outlet *x_outlet1; t_outlet *x_outlet2; } t_keyname; static void *keyname_new(void) { t_keyname *x = (t_keyname *)pd_new(keyname_class); x->x_outlet1 = outlet_new(&x->x_obj, &s_float); x->x_outlet2 = outlet_new(&x->x_obj, &s_symbol); pd_bind(&x->x_obj.ob_pd, gensym("#keyname")); return (x); } static void keyname_list(t_keyname *x, t_symbol *s, int ac, t_atom *av) { outlet_symbol(x->x_outlet2, atom_getsymbolarg(1, ac, av)); outlet_float(x->x_outlet1, atom_getfloatarg(0, ac, av)); } static void keyname_free(t_keyname *x) { pd_unbind(&x->x_obj.ob_pd, gensym("#keyname")); } static void key_setup(void) { key_class = class_new(gensym("key"), (t_newmethod)key_new, (t_method)key_free, sizeof(t_key), CLASS_NOINLET, 0); class_addfloat(key_class, key_float); class_sethelpsymbol(key_class, gensym("key-input")); keyup_class = class_new(gensym("keyup"), (t_newmethod)keyup_new, (t_method)keyup_free, sizeof(t_keyup), CLASS_NOINLET, 0); class_addfloat(keyup_class, keyup_float); class_sethelpsymbol(keyup_class, gensym("key-input")); keyname_class = class_new(gensym("keyname"), (t_newmethod)keyname_new, (t_method)keyname_free, sizeof(t_keyname), CLASS_NOINLET, 0); class_addlist(keyname_class, keyname_list); class_sethelpsymbol(keyname_class, gensym("key-input")); } /* ------------------------ pdcontrol --------------------------------- */ static t_class *pdcontrol_class; typedef struct _pdcontrol { t_object x_obj; t_canvas *x_canvas; t_outlet *x_outlet; } t_pdcontrol; static void *pdcontrol_new( void) { t_pdcontrol *x = (t_pdcontrol *)pd_new(pdcontrol_class); x->x_canvas = canvas_getcurrent(); x->x_outlet = outlet_new(&x->x_obj, 0); return (x); } /* output containing directory of patch. optional args: 1. a number, zero for this patch, one for the parent, etc.; 2. a symbol to concatenate onto the directory; */ static void pdcontrol_dir(t_pdcontrol *x, t_symbol *s, t_floatarg f) { t_canvas *c = x->x_canvas; int i; for (i = 0; i < (int)f; i++) { while (!c->gl_env) /* back up to containing canvas or abstraction */ c = c->gl_owner; if (c->gl_owner) /* back up one more into an owner if any */ c = c->gl_owner; } if (*s->s_name) { char buf[MAXPDSTRING]; snprintf(buf, MAXPDSTRING, "%s/%s", canvas_getdir(c)->s_name, s->s_name); buf[MAXPDSTRING-1] = 0; outlet_symbol(x->x_outlet, gensym(buf)); } else outlet_symbol(x->x_outlet, canvas_getdir(c)); } static void pdcontrol_args(t_pdcontrol *x, t_floatarg f) { t_canvas *c = x->x_canvas; int i; int argc; t_atom *argv; for (i = 0; i < (int)f; i++) { while (!c->gl_env) /* back up to containing canvas or abstraction */ c = c->gl_owner; if (c->gl_owner) /* back up one more into an owner if any */ c = c->gl_owner; } canvas_setcurrent(c); canvas_getargs(&argc, &argv); canvas_unsetcurrent(c); outlet_list(x->x_outlet, &s_list, argc, argv); } static void pdcontrol_browse(t_pdcontrol *x, t_symbol *s) { pdgui_vmess("::pd_menucommands::menu_openfile", "s", s->s_name); } static void pdcontrol_isvisible(t_pdcontrol *x) { outlet_float(x->x_outlet, glist_isvisible(x->x_canvas)); } static void pdcontrol_sendcanvas(t_pdcontrol *x, t_symbol *s, int argc, t_atom *argv) { if (argc && argv[0].a_type == A_SYMBOL) typedmess((t_pd *)(x->x_canvas), argv[0].a_w.w_symbol, argc-1, argv+1); else pd_error(x, "pdcontrol_sendcanvas: first argument not a symbol"); } static void pdcontrol_setup(void) { pdcontrol_class = class_new(gensym("pdcontrol"), (t_newmethod)pdcontrol_new, 0, sizeof(t_pdcontrol), 0, 0); class_addmethod(pdcontrol_class, (t_method)pdcontrol_dir, gensym("dir"), A_DEFFLOAT, A_DEFSYMBOL, 0); class_addmethod(pdcontrol_class, (t_method)pdcontrol_args, gensym("args"), A_DEFFLOAT, 0); class_addmethod(pdcontrol_class, (t_method)pdcontrol_browse, gensym("browse"), A_SYMBOL, 0); class_addmethod(pdcontrol_class, (t_method)pdcontrol_isvisible, gensym("isvisible"), 0); class_addmethod(pdcontrol_class, (t_method)pdcontrol_sendcanvas, gensym("sendcanvas"), A_GIMME, 0); } /* -------------------------- setup routine ------------------------------ */ void x_gui_setup(void) { gfxstub_setup(); openpanel_setup(); savepanel_setup(); key_setup(); pdcontrol_setup(); } ================================================ FILE: libs/libpd/pure-data/src/x_interface.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* interface objects */ #include "m_pd.h" #include "s_stuff.h" #include /* -------------------------- print ------------------------------ */ static t_class *print_class; #define PRINT_LOGLEVEL 2 /* imported from s_print.c: */ extern void startlogpost(const void *object, const int level, const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(3, 4); /* avoid prefixing with "verbose(PRINT_LOGLEVEL): " when printing to stderr or via printhook. */ #define print_startlogpost(object, fmt, ...) do{ \ if (STUFF->st_printhook || sys_printtostderr) \ startpost(fmt, __VA_ARGS__); \ else startlogpost(object, PRINT_LOGLEVEL, fmt, __VA_ARGS__); \ } while(0) typedef struct _print { t_object x_obj; t_symbol *x_sym; } t_print; static void *print_new(t_symbol *sel, int argc, t_atom *argv) { t_print *x = (t_print *)pd_new(print_class); if (argc == 0) x->x_sym = gensym("print"); else if (argc == 1 && argv->a_type == A_SYMBOL) { t_symbol *s = atom_getsymbolarg(0, argc, argv); if (!strcmp(s->s_name, "-n")) x->x_sym = &s_; else x->x_sym = s; } else { int bufsize; char *buf; t_binbuf *bb = binbuf_new(); binbuf_add(bb, argc, argv); binbuf_gettext(bb, &buf, &bufsize); buf = resizebytes(buf, bufsize, bufsize+1); buf[bufsize] = 0; x->x_sym = gensym(buf); freebytes(buf, bufsize+1); binbuf_free(bb); } return (x); } static void print_bang(t_print *x) { print_startlogpost(x, "%s%sbang", x->x_sym->s_name, (*x->x_sym->s_name ? ": " : "")); endpost(); } static void print_pointer(t_print *x, t_gpointer *gp) { print_startlogpost(x, "%s%s(pointer)", x->x_sym->s_name, (*x->x_sym->s_name ? ": " : "")); endpost(); } static void print_float(t_print *x, t_float f) { print_startlogpost(x, "%s%s%g", x->x_sym->s_name, (*x->x_sym->s_name ? ": " : ""), f); endpost(); } static void print_anything(t_print *x, t_symbol *s, int argc, t_atom *argv) { int i; print_startlogpost(x, "%s%s%s", x->x_sym->s_name, (*x->x_sym->s_name ? ": " : ""), s->s_name); for (i = 0; i < argc; i++) { char buf[MAXPDSTRING]; atom_string(argv+i, buf, MAXPDSTRING); print_startlogpost(x, " %s", buf); } endpost(); } static void print_list(t_print *x, t_symbol *s, int argc, t_atom *argv) { if (!argc) print_bang(x); else if (argc == 1) { switch (argv->a_type) { case A_FLOAT: print_float(x, argv->a_w.w_float); break; case A_SYMBOL: print_anything(x, &s_symbol, 1, argv); break; case A_POINTER: print_pointer(x, argv->a_w.w_gpointer); break; default: bug("print"); break; } } else if (argv->a_type == A_FLOAT) { int i; /* print first (numeric) atom, to avoid a leading space */ if (*x->x_sym->s_name) print_startlogpost(x, "%s: %g", x->x_sym->s_name, atom_getfloat(argv)); else print_startlogpost(x, "%g", atom_getfloat(argv)); argc--; argv++; for (i = 0; i < argc; i++) { char buf[MAXPDSTRING]; atom_string(argv+i, buf, MAXPDSTRING); print_startlogpost(x, " %s", buf); } endpost(); } else print_anything(x, &s_list, argc, argv); } static void print_setup(void) { print_class = class_new(gensym("print"), (t_newmethod)print_new, 0, sizeof(t_print), 0, A_GIMME, 0); class_addbang(print_class, print_bang); class_addfloat(print_class, print_float); class_addpointer(print_class, print_pointer); class_addlist(print_class, print_list); class_addanything(print_class, print_anything); } /* ------------------- trace - see message passing traces ------------- */ int backtracer_settracing(void *x, int tracing); extern int backtracer_cantrace; static t_class *trace_class; typedef struct _trace { t_object x_obj; t_symbol *x_sym; t_float x_f; } t_trace; static void *trace_new(t_symbol *s) { t_trace *x = (t_trace *)pd_new(trace_class); x->x_sym = s; x->x_f = 0; floatinlet_new(&x->x_obj, &x->x_f); outlet_new(&x->x_obj, &s_anything); return (x); } static void trace_anything(t_trace *x, t_symbol *s, int argc, t_atom *argv) { int nturns = x->x_f; if (nturns > 0) { if (!backtracer_cantrace) { pd_error(x, "trace requested but tracing is not enabled"); x->x_f = 0; } else if (backtracer_settracing(x, 1)) { outlet_anything(x->x_obj.ob_outlet, s, argc, argv); x->x_f = nturns-1; (void)backtracer_settracing(x, 0); } } else outlet_anything(x->x_obj.ob_outlet, s, argc, argv); } static void trace_setup(void) { trace_class = class_new(gensym("trace"), (t_newmethod)trace_new, 0, sizeof(t_trace), 0, A_DEFSYM, 0); class_addanything(trace_class, trace_anything); } void x_interface_setup(void) { print_setup(); trace_setup(); } ================================================ FILE: libs/libpd/pure-data/src/x_list.c ================================================ /* Copyright (c) 1997- Miller Puckette and others. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ #include "m_pd.h" #include #include "m_private_utils.h" #define LIST_NGETBYTE 100 /* bigger that this we use alloc, not alloca */ /* the "list" object family. list append - append a list to another list prepend - prepend a list to another list split - first n elements to first outlet, rest to second outlet list trim - trim off "list" selector list length - output number of items in list list fromsymbol - "explode" a symbol into a list of character codes Need to think more about: list foreach - spit out elements of a list one by one (also in reverse?) list reverse - permute elements of a list back to front list cat - build a list by accumulating elements Probably don't need: list first - output first n elements. list last - output last n elements list nth - nth item in list, counting from zero list array - get items from a named array as a list list pack - synonym for 'pack' list unpack - synonym for 'unpack' */ /* -------------- utility functions: storage, copying -------------- */ /* List element for storage. Keep an atom and, in case it's a pointer, an associated 'gpointer' to protect against stale pointers. */ typedef struct _listelem { t_atom l_a; t_gpointer l_p; } t_listelem; typedef struct _alist { t_pd l_pd; /* object to point inlets to */ int l_n; /* number of items */ int l_npointer; /* number of pointers */ t_listelem *l_vec; /* pointer to items */ } t_alist; static void atoms_copy(int argc, t_atom *from, t_atom *to) { int i; for (i = 0; i < argc; i++) to[i] = from[i]; } /* ------------- fake class to divert inlets to ----------------- */ t_class *alist_class; static void alist_init(t_alist *x) { x->l_pd = alist_class; x->l_n = x->l_npointer = 0; x->l_vec = 0; } static void alist_clear(t_alist *x) { int i; for (i = 0; i < x->l_n; i++) { if (x->l_vec[i].l_a.a_type == A_POINTER) gpointer_unset(x->l_vec[i].l_a.a_w.w_gpointer); } if (x->l_vec) freebytes(x->l_vec, x->l_n * sizeof(*x->l_vec)); } static void alist_copyin(t_alist *x, t_symbol *s, int argc, t_atom *argv, int where) { int i, j; for (i = 0, j = where; i < argc; i++, j++) { x->l_vec[j].l_a = argv[i]; if (x->l_vec[j].l_a.a_type == A_POINTER) { x->l_npointer++; gpointer_copy(x->l_vec[j].l_a.a_w.w_gpointer, &x->l_vec[j].l_p); x->l_vec[j].l_a.a_w.w_gpointer = &x->l_vec[j].l_p; } } } /* set contents to a list */ static void alist_list(t_alist *x, t_symbol *s, int argc, t_atom *argv) { alist_clear(x); if (!(x->l_vec = (t_listelem *)getbytes(argc * sizeof(*x->l_vec)))) { x->l_n = 0; pd_error(0, "list: out of memory"); return; } x->l_n = argc; x->l_npointer = 0; alist_copyin(x, s, argc, argv, 0); } /* set contents to an arbitrary non-list message */ static void alist_anything(t_alist *x, t_symbol *s, int argc, t_atom *argv) { int i; alist_clear(x); if (!(x->l_vec = (t_listelem *)getbytes((argc+1) * sizeof(*x->l_vec)))) { x->l_n = 0; pd_error(0, "list_alloc: out of memory"); return; } x->l_n = argc+1; x->l_npointer = 0; SETSYMBOL(&x->l_vec[0].l_a, s); for (i = 0; i < argc; i++) { x->l_vec[i+1].l_a = argv[i]; if (x->l_vec[i+1].l_a.a_type == A_POINTER) { x->l_npointer++; gpointer_copy(x->l_vec[i+1].l_a.a_w.w_gpointer, &x->l_vec[i+1].l_p); x->l_vec[i+1].l_a.a_w.w_gpointer = &x->l_vec[i+1].l_p; } } } static void alist_toatoms(t_alist *x, t_atom *to, int onset, int count) { int i; for (i = 0; i < count; i++) to[i] = x->l_vec[onset + i].l_a; } static void alist_clone(t_alist *x, t_alist *y, int onset, int count) { int i; y->l_pd = alist_class; y->l_n = count; y->l_npointer = 0; if (!(y->l_vec = (t_listelem *)getbytes(y->l_n * sizeof(*y->l_vec)))) { y->l_n = 0; pd_error(0, "list_alloc: out of memory"); } else for (i = 0; i < count; i++) { y->l_vec[i].l_a = x->l_vec[onset + i].l_a; if (y->l_vec[i].l_a.a_type == A_POINTER) { gpointer_copy(y->l_vec[i].l_a.a_w.w_gpointer, &y->l_vec[i].l_p); y->l_vec[i].l_a.a_w.w_gpointer = &y->l_vec[i].l_p; y->l_npointer++; } } } /* function to restore gpointers after the list has moved in memory */ static void alist_restore_gpointers(t_alist *x, int offset, int count) { t_listelem *vec = x->l_vec + offset; while (count--) { if (vec->l_a.a_type == A_POINTER) vec->l_a.a_w.w_gpointer = &vec->l_p; vec++; } } static void alist_setup(void) { alist_class = class_new(gensym("list inlet"), 0, 0, sizeof(t_alist), 0, 0); class_addlist(alist_class, alist_list); class_addanything(alist_class, alist_anything); } /* ------------- list append --------------------- */ t_class *list_append_class; typedef struct _list_append { t_object x_obj; t_alist x_alist; } t_list_append; static void *list_append_new(t_symbol *s, int argc, t_atom *argv) { t_list_append *x = (t_list_append *)pd_new(list_append_class); alist_init(&x->x_alist); alist_list(&x->x_alist, 0, argc, argv); outlet_new(&x->x_obj, &s_list); inlet_new(&x->x_obj, &x->x_alist.l_pd, 0, 0); return (x); } static void list_append_list(t_list_append *x, t_symbol *s, int argc, t_atom *argv) { t_atom *outv; int outc = x->x_alist.l_n + argc; ALLOCA(t_atom, outv, outc, LIST_NGETBYTE); atoms_copy(argc, argv, outv); if (x->x_alist.l_npointer) { t_alist y; alist_clone(&x->x_alist, &y, 0, x->x_alist.l_n); alist_toatoms(&y, outv+argc, 0, x->x_alist.l_n); outlet_list(x->x_obj.ob_outlet, &s_list, outc, outv); alist_clear(&y); } else { alist_toatoms(&x->x_alist, outv+argc, 0, x->x_alist.l_n); outlet_list(x->x_obj.ob_outlet, &s_list, outc, outv); } FREEA(t_atom, outv, outc, LIST_NGETBYTE); } static void list_append_anything(t_list_append *x, t_symbol *s, int argc, t_atom *argv) { t_atom *outv; int outc = x->x_alist.l_n + argc + 1; ALLOCA(t_atom, outv, outc, LIST_NGETBYTE); SETSYMBOL(outv, s); atoms_copy(argc, argv, outv + 1); if (x->x_alist.l_npointer) { t_alist y; alist_clone(&x->x_alist, &y, 0, x->x_alist.l_n); alist_toatoms(&y, outv + 1 + argc, 0, x->x_alist.l_n); outlet_list(x->x_obj.ob_outlet, &s_list, outc, outv); alist_clear(&y); } else { alist_toatoms(&x->x_alist, outv + 1 + argc, 0, x->x_alist.l_n); outlet_list(x->x_obj.ob_outlet, &s_list, outc, outv); } FREEA(t_atom, outv, outc, LIST_NGETBYTE); } static void list_append_free(t_list_append *x) { alist_clear(&x->x_alist); } static void list_append_setup(void) { list_append_class = class_new(gensym("list append"), (t_newmethod)list_append_new, (t_method)list_append_free, sizeof(t_list_append), 0, A_GIMME, 0); class_addlist(list_append_class, list_append_list); class_addanything(list_append_class, list_append_anything); class_sethelpsymbol(list_append_class, &s_list); } /* ------------- list prepend --------------------- */ t_class *list_prepend_class; typedef t_list_append t_list_prepend; static void *list_prepend_new(t_symbol *s, int argc, t_atom *argv) { t_list_prepend *x = (t_list_prepend *)pd_new(list_prepend_class); alist_init(&x->x_alist); alist_list(&x->x_alist, 0, argc, argv); outlet_new(&x->x_obj, &s_list); inlet_new(&x->x_obj, &x->x_alist.l_pd, 0, 0); return (x); } static void list_prepend_list(t_list_prepend *x, t_symbol *s, int argc, t_atom *argv) { t_atom *outv; int n, outc = x->x_alist.l_n + argc; ALLOCA(t_atom, outv, outc, LIST_NGETBYTE); atoms_copy(argc, argv, outv + x->x_alist.l_n); if (x->x_alist.l_npointer) { t_alist y; alist_clone(&x->x_alist, &y, 0, x->x_alist.l_n); alist_toatoms(&y, outv, 0, x->x_alist.l_n); outlet_list(x->x_obj.ob_outlet, &s_list, outc, outv); alist_clear(&y); } else { alist_toatoms(&x->x_alist, outv, 0, x->x_alist.l_n); outlet_list(x->x_obj.ob_outlet, &s_list, outc, outv); } FREEA(t_atom, outv, outc, LIST_NGETBYTE); } static void list_prepend_anything(t_list_prepend *x, t_symbol *s, int argc, t_atom *argv) { t_atom *outv; int n, outc = x->x_alist.l_n + argc + 1; ALLOCA(t_atom, outv, outc, LIST_NGETBYTE); SETSYMBOL(outv + x->x_alist.l_n, s); atoms_copy(argc, argv, outv + x->x_alist.l_n + 1); if (x->x_alist.l_npointer) { t_alist y; alist_clone(&x->x_alist, &y, 0, x->x_alist.l_n); alist_toatoms(&y, outv, 0, x->x_alist.l_n); outlet_list(x->x_obj.ob_outlet, &s_list, outc, outv); alist_clear(&y); } else { alist_toatoms(&x->x_alist, outv, 0, x->x_alist.l_n); outlet_list(x->x_obj.ob_outlet, &s_list, outc, outv); } FREEA(t_atom, outv, outc, LIST_NGETBYTE); } static void list_prepend_free(t_list_prepend *x) { alist_clear(&x->x_alist); } static void list_prepend_setup(void) { list_prepend_class = class_new(gensym("list prepend"), (t_newmethod)list_prepend_new, (t_method)list_prepend_free, sizeof(t_list_prepend), 0, A_GIMME, 0); class_addlist(list_prepend_class, list_prepend_list); class_addanything(list_prepend_class, list_prepend_anything); class_sethelpsymbol(list_prepend_class, &s_list); } /* ------------- list store --------------------- */ t_class *list_store_class; typedef struct _list_store { t_object x_obj; t_alist x_alist; t_outlet *x_out1; t_outlet *x_out2; } t_list_store; static void *list_store_new(t_symbol *s, int argc, t_atom *argv) { t_list_store *x = (t_list_store *)pd_new(list_store_class); alist_init(&x->x_alist); alist_list(&x->x_alist, 0, argc, argv); x->x_out1 = outlet_new(&x->x_obj, &s_list); x->x_out2 = outlet_new(&x->x_obj, &s_bang); inlet_new(&x->x_obj, &x->x_alist.l_pd, 0, 0); return (x); } static void list_store_send(t_list_store *x, t_symbol *s) { t_atom *vec; int n = x->x_alist.l_n; if (!s->s_thing) { pd_error(x, "%s: no such object", s->s_name); return; } ALLOCA(t_atom, vec, n, LIST_NGETBYTE); if (x->x_alist.l_npointer) { t_alist y; alist_clone(&x->x_alist, &y, 0, n); alist_toatoms(&y, vec, 0, n); pd_list(s->s_thing, gensym("list"), n, vec); alist_clear(&y); } else { alist_toatoms(&x->x_alist, vec, 0, n); pd_list(s->s_thing, gensym("list"), n, vec); } FREEA(t_atom, vec, n, LIST_NGETBYTE); } static void list_store_list(t_list_store *x, t_symbol *s, int argc, t_atom *argv) { t_atom *outv; int n, outc = x->x_alist.l_n + argc; ALLOCA(t_atom, outv, outc, LIST_NGETBYTE); atoms_copy(argc, argv, outv); if (x->x_alist.l_npointer) { t_alist y; alist_clone(&x->x_alist, &y, 0, x->x_alist.l_n); alist_toatoms(&y, outv+argc, 0, x->x_alist.l_n); outlet_list(x->x_out1, &s_list, outc, outv); alist_clear(&y); } else { alist_toatoms(&x->x_alist, outv+argc, 0, x->x_alist.l_n); outlet_list(x->x_out1, &s_list, outc, outv); } FREEA(t_atom, outv, outc, LIST_NGETBYTE); } static void list_store_doinsert(t_list_store *x, t_symbol *s, int argc, t_atom *argv, int index) { t_listelem *oldptr = x->x_alist.l_vec; /* try to allocate more memory */ if (!(x->x_alist.l_vec = (t_listelem *)resizebytes(x->x_alist.l_vec, (x->x_alist.l_n) * sizeof(*x->x_alist.l_vec), (x->x_alist.l_n + argc) * sizeof(*x->x_alist.l_vec)))) { x->x_alist.l_n = 0; pd_error(0, "list: out of memory"); return; } /* fix gpointers in case resizebytes() has moved the alist in memory */ if (x->x_alist.l_vec != oldptr && x->x_alist.l_npointer) alist_restore_gpointers(&x->x_alist, 0, x->x_alist.l_n); /* shift existing elements after 'index' to the right */ if (index < x->x_alist.l_n) { memmove(x->x_alist.l_vec + index + argc, x->x_alist.l_vec + index, (x->x_alist.l_n - index) * sizeof(*x->x_alist.l_vec)); /* fix gpointers because of memmove() */ if (x->x_alist.l_npointer) alist_restore_gpointers(&x->x_alist, index + argc, x->x_alist.l_n - index); } /* finally copy new elements */ alist_copyin(&x->x_alist, s, argc, argv, index); x->x_alist.l_n += argc; } static void list_store_insert(t_list_store *x, t_symbol *s, int argc, t_atom *argv) { if (argc > 1) { int index = atom_getfloat(argv); if (index < 0) { pd_error(x, "list_store_insert: index %d out of range", index); return; } else if (index > x->x_alist.l_n) index = x->x_alist.l_n; list_store_doinsert(x, s, --argc, ++argv, index); } } static void list_store_append(t_list_store *x, t_symbol *s, int argc, t_atom *argv) { list_store_doinsert(x, s, argc, argv, x->x_alist.l_n); } static void list_store_prepend(t_list_store *x, t_symbol *s, int argc, t_atom *argv) { list_store_doinsert(x, s, argc, argv, 0); } static void list_store_delete(t_list_store *x, t_floatarg f1, t_floatarg f2) { int i, max, index = (int)f1, n = (int)f2; t_listelem *oldptr = x->x_alist.l_vec; if (index < 0 || index >= x->x_alist.l_n) { pd_error(x, "list_store_delete: index %d out of range", index); return; } max = x->x_alist.l_n - index; if (!n) n = 1; /* default */ else if (n < 0 || n > max) n = max; /* till the end of the list */ /* unset pointers for elements which are to be deleted */ if (x->x_alist.l_npointer) { t_listelem *vec = x->x_alist.l_vec + index; for (i = 0; i < n; i++) { if (vec[i].l_a.a_type == A_POINTER) { gpointer_unset(vec[i].l_a.a_w.w_gpointer); x->x_alist.l_npointer--; } } } /* shift elements (after the deleted elements) to the left */ memmove(x->x_alist.l_vec + index, x->x_alist.l_vec + index + n, (x->x_alist.l_n - index) * sizeof(*x->x_alist.l_vec)); /* shrink memory */ if (!(x->x_alist.l_vec = (t_listelem *)resizebytes(x->x_alist.l_vec, (x->x_alist.l_n) * sizeof(*x->x_alist.l_vec), (x->x_alist.l_n - n) * sizeof(*x->x_alist.l_vec)))) { x->x_alist.l_n = 0; pd_error(0, "list: out of memory"); return; } if (x->x_alist.l_npointer) { /* fix all gpointers in case resizebytes() has moved the alist in memory */ if (x->x_alist.l_vec != oldptr) alist_restore_gpointers(&x->x_alist, 0, x->x_alist.l_n - n); else /* only fix gpointers after index (because of of memmove()) */ alist_restore_gpointers(&x->x_alist, index, x->x_alist.l_n - index - n); } x->x_alist.l_n -= n; } static void list_store_get(t_list_store *x, t_floatarg f1, t_floatarg f2) { t_atom *outv; int onset = f1, outc = f2; if (!outc) outc = 1; /* default */ else if (outc < 0) { outc = x->x_alist.l_n - onset; /* till the end of the list */ if (outc <= 0) /* onset out of range */ { outlet_bang(x->x_out2); return; } } if (onset < 0 || (onset + outc > x->x_alist.l_n)) { outlet_bang(x->x_out2); return; } ALLOCA(t_atom, outv, outc, LIST_NGETBYTE); if (x->x_alist.l_npointer) { t_alist y; alist_clone(&x->x_alist, &y, onset, outc); alist_toatoms(&y, outv, 0, outc); outlet_list(x->x_out1, &s_list, outc, outv); alist_clear(&y); } else { alist_toatoms(&x->x_alist, outv, onset, outc); outlet_list(x->x_out1, &s_list, outc, outv); } FREEA(t_atom, outv, outc, LIST_NGETBYTE); } static void list_store_set(t_list_store *x, t_symbol *s, int argc, t_atom *argv) { if (argc > 1) { int n, max, onset = atom_getfloat(argv); if (onset < 0 || onset >= x->x_alist.l_n) { pd_error(x, "list_store_set: index %d out of range", onset); return; } argc--; argv++; max = x->x_alist.l_n - onset; n = (argc > max) ? max : argc; alist_copyin(&x->x_alist, s, n, argv, onset); } } static void list_store_free(t_list_store *x) { alist_clear(&x->x_alist); } static void list_store_setup(void) { list_store_class = class_new(gensym("list store"), (t_newmethod)list_store_new, (t_method)list_store_free, sizeof(t_list_store), 0, A_GIMME, 0); class_addlist(list_store_class, list_store_list); class_addmethod(list_store_class, (t_method)list_store_send, gensym("send"), A_SYMBOL, 0); class_addmethod(list_store_class, (t_method)list_store_append, gensym("append"), A_GIMME, 0); class_addmethod(list_store_class, (t_method)list_store_prepend, gensym("prepend"), A_GIMME, 0); class_addmethod(list_store_class, (t_method)list_store_insert, gensym("insert"), A_GIMME, 0); class_addmethod(list_store_class, (t_method)list_store_delete, gensym("delete"), A_FLOAT, A_DEFFLOAT, 0); class_addmethod(list_store_class, (t_method)list_store_get, gensym("get"), A_FLOAT, A_DEFFLOAT, 0); class_addmethod(list_store_class, (t_method)list_store_set, gensym("set"), A_GIMME, 0); class_sethelpsymbol(list_store_class, &s_list); } /* ------------- list split --------------------- */ t_class *list_split_class; typedef struct _list_split { t_object x_obj; t_float x_f; t_outlet *x_out1; t_outlet *x_out2; t_outlet *x_out3; } t_list_split; static void *list_split_new(t_floatarg f) { t_list_split *x = (t_list_split *)pd_new(list_split_class); x->x_out1 = outlet_new(&x->x_obj, &s_list); x->x_out2 = outlet_new(&x->x_obj, &s_list); x->x_out3 = outlet_new(&x->x_obj, &s_list); floatinlet_new(&x->x_obj, &x->x_f); x->x_f = f; return (x); } static void list_split_list(t_list_split *x, t_symbol *s, int argc, t_atom *argv) { int n = x->x_f; if (n < 0) n = 0; if (argc >= n) { outlet_list(x->x_out2, &s_list, argc-n, argv+n); outlet_list(x->x_out1, &s_list, n, argv); } else outlet_list(x->x_out3, &s_list, argc, argv); } static void list_split_anything(t_list_split *x, t_symbol *s, int argc, t_atom *argv) { t_atom *outv; ALLOCA(t_atom, outv, argc+1, LIST_NGETBYTE); SETSYMBOL(outv, s); atoms_copy(argc, argv, outv + 1); list_split_list(x, &s_list, argc+1, outv); FREEA(t_atom, outv, argc+1, LIST_NGETBYTE); } static void list_split_setup(void) { list_split_class = class_new(gensym("list split"), (t_newmethod)list_split_new, 0, sizeof(t_list_split), 0, A_DEFFLOAT, 0); class_addlist(list_split_class, list_split_list); class_addanything(list_split_class, list_split_anything); class_sethelpsymbol(list_split_class, &s_list); } /* ------------- list trim --------------------- */ t_class *list_trim_class; typedef struct _list_trim { t_object x_obj; } t_list_trim; static void *list_trim_new(void) { t_list_trim *x = (t_list_trim *)pd_new(list_trim_class); outlet_new(&x->x_obj, &s_list); return (x); } static void list_trim_list(t_list_trim *x, t_symbol *s, int argc, t_atom *argv) { if (argc < 1 || argv[0].a_type != A_SYMBOL) outlet_list(x->x_obj.ob_outlet, &s_list, argc, argv); else outlet_anything(x->x_obj.ob_outlet, argv[0].a_w.w_symbol, argc-1, argv+1); } static void list_trim_anything(t_list_trim *x, t_symbol *s, int argc, t_atom *argv) { outlet_anything(x->x_obj.ob_outlet, s, argc, argv); } static void list_trim_setup(void) { list_trim_class = class_new(gensym("list trim"), (t_newmethod)list_trim_new, 0, sizeof(t_list_trim), 0, 0); class_addlist(list_trim_class, list_trim_list); class_addanything(list_trim_class, list_trim_anything); class_sethelpsymbol(list_trim_class, &s_list); } /* ------------- list length --------------------- */ t_class *list_length_class; typedef struct _list_length { t_object x_obj; } t_list_length; static void *list_length_new(void) { t_list_length *x = (t_list_length *)pd_new(list_length_class); outlet_new(&x->x_obj, &s_float); return (x); } static void list_length_list(t_list_length *x, t_symbol *s, int argc, t_atom *argv) { outlet_float(x->x_obj.ob_outlet, (t_float)argc); } static void list_length_anything(t_list_length *x, t_symbol *s, int argc, t_atom *argv) { outlet_float(x->x_obj.ob_outlet, (t_float)argc+1); } static void list_length_setup(void) { list_length_class = class_new(gensym("list length"), (t_newmethod)list_length_new, 0, sizeof(t_list_length), 0, 0); class_addlist(list_length_class, list_length_list); class_addanything(list_length_class, list_length_anything); class_sethelpsymbol(list_length_class, &s_list); } /* ------------- list fromsymbol --------------------- */ t_class *list_fromsymbol_class; typedef struct _list_fromsymbol { t_object x_obj; } t_list_fromsymbol; static void *list_fromsymbol_new(void) { t_list_fromsymbol *x = (t_list_fromsymbol *)pd_new(list_fromsymbol_class); outlet_new(&x->x_obj, &s_list); return (x); } static void list_fromsymbol_symbol(t_list_fromsymbol *x, t_symbol *s) { t_atom *outv; int n, outc = (int)strlen(s->s_name); ALLOCA(t_atom, outv, outc, LIST_NGETBYTE); for (n = 0; n < outc; n++) SETFLOAT(outv + n, (unsigned char)s->s_name[n]); outlet_list(x->x_obj.ob_outlet, &s_list, outc, outv); FREEA(t_atom, outv, outc, LIST_NGETBYTE); } static void list_fromsymbol_setup(void) { list_fromsymbol_class = class_new(gensym("list fromsymbol"), (t_newmethod)list_fromsymbol_new, 0, sizeof(t_list_fromsymbol), 0, 0); class_addsymbol(list_fromsymbol_class, list_fromsymbol_symbol); class_sethelpsymbol(list_fromsymbol_class, &s_list); } /* ------------- list tosymbol --------------------- */ t_class *list_tosymbol_class; typedef struct _list_tosymbol { t_object x_obj; } t_list_tosymbol; static void *list_tosymbol_new(void) { t_list_tosymbol *x = (t_list_tosymbol *)pd_new(list_tosymbol_class); outlet_new(&x->x_obj, &s_symbol); return (x); } static void list_tosymbol_list(t_list_tosymbol *x, t_symbol *s, int argc, t_atom *argv) { int i; char *str; ALLOCA(char, str, argc + 1, MAXPDSTRING); for (i = 0; i < argc; i++) str[i] = (char)atom_getfloatarg(i, argc, argv); str[argc] = 0; outlet_symbol(x->x_obj.ob_outlet, gensym(str)); FREEA(char, str, argc + 1, MAXPDSTRING); } static void list_tosymbol_setup(void) { list_tosymbol_class = class_new(gensym("list tosymbol"), (t_newmethod)list_tosymbol_new, 0, sizeof(t_list_tosymbol), 0, 0); class_addlist(list_tosymbol_class, list_tosymbol_list); class_sethelpsymbol(list_tosymbol_class, &s_list); } /* ------------- list ------------------- */ static void *list_new(t_pd *dummy, t_symbol *s, int argc, t_atom *argv) { if (!argc || argv[0].a_type != A_SYMBOL) pd_this->pd_newest = list_append_new(s, argc, argv); else { t_symbol *s2 = argv[0].a_w.w_symbol; if (s2 == gensym("append")) pd_this->pd_newest = list_append_new(s, argc-1, argv+1); else if (s2 == gensym("prepend")) pd_this->pd_newest = list_prepend_new(s, argc-1, argv+1); else if (s2 == gensym("split")) pd_this->pd_newest = list_split_new(atom_getfloatarg(1, argc, argv)); else if (s2 == gensym("trim")) pd_this->pd_newest = list_trim_new(); else if (s2 == gensym("length")) pd_this->pd_newest = list_length_new(); else if (s2 == gensym("fromsymbol")) pd_this->pd_newest = list_fromsymbol_new(); else if (s2 == gensym("tosymbol")) pd_this->pd_newest = list_tosymbol_new(); else if (s2 == gensym("store")) pd_this->pd_newest = list_store_new(s, argc-1, argv+1); else { pd_error(0, "list %s: unknown function", s2->s_name); pd_this->pd_newest = 0; } } return (pd_this->pd_newest); } void x_list_setup(void) { alist_setup(); list_append_setup(); list_prepend_setup(); list_store_setup(); list_split_setup(); list_trim_setup(); list_length_setup(); list_fromsymbol_setup(); list_tosymbol_setup(); class_addcreator((t_newmethod)list_new, &s_list, A_GIMME, 0); } ================================================ FILE: libs/libpd/pure-data/src/x_midi.c ================================================ /* Copyright (c) 1997-2001 Miller Puckette and others. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* MIDI. */ #include "m_pd.h" void outmidi_noteon(int portno, int channel, int pitch, int velo); void outmidi_controlchange(int portno, int channel, int ctlno, int value); void outmidi_programchange(int portno, int channel, int value); void outmidi_pitchbend(int portno, int channel, int value); void outmidi_aftertouch(int portno, int channel, int value); void outmidi_polyaftertouch(int portno, int channel, int pitch, int value); void outmidi_byte(int portno, int value); struct _instancemidi { t_symbol *m_midiin_sym; t_symbol *m_sysexin_sym; t_symbol *m_notein_sym; t_symbol *m_ctlin_sym; t_symbol *m_pgmin_sym; t_symbol *m_bendin_sym; t_symbol *m_touchin_sym; t_symbol *m_polytouchin_sym; t_symbol *m_midirealtimein_sym; }; /* ----------------------- midiin and sysexin ------------------------- */ static t_class *midiin_class, *sysexin_class; typedef struct _midiin { t_object x_obj; t_outlet *x_outlet1; t_outlet *x_outlet2; } t_midiin; static void *midiin_new(void) { t_midiin *x = (t_midiin *)pd_new(midiin_class); x->x_outlet1 = outlet_new(&x->x_obj, &s_float); x->x_outlet2 = outlet_new(&x->x_obj, &s_float); pd_bind(&x->x_obj.ob_pd, pd_this->pd_midi->m_midiin_sym); return (x); } static void midiin_list(t_midiin *x, t_symbol *s, int ac, t_atom *av) { outlet_float(x->x_outlet2, atom_getfloatarg(1, ac, av) + 1); outlet_float(x->x_outlet1, atom_getfloatarg(0, ac, av)); } static void midiin_free(t_midiin *x) { pd_unbind(&x->x_obj.ob_pd, pd_this->pd_midi->m_midiin_sym); } static void *sysexin_new(void) { t_midiin *x = (t_midiin *)pd_new(sysexin_class); x->x_outlet1 = outlet_new(&x->x_obj, &s_float); x->x_outlet2 = outlet_new(&x->x_obj, &s_float); pd_bind(&x->x_obj.ob_pd, pd_this->pd_midi->m_sysexin_sym); return (x); } static void sysexin_free(t_midiin *x) { pd_unbind(&x->x_obj.ob_pd, pd_this->pd_midi->m_sysexin_sym); } static void midiin_setup(void) { midiin_class = class_new(gensym("midiin"), (t_newmethod)midiin_new, (t_method)midiin_free, sizeof(t_midiin), CLASS_NOINLET, A_DEFFLOAT, 0); class_addlist(midiin_class, midiin_list); class_sethelpsymbol(midiin_class, gensym("midi")); sysexin_class = class_new(gensym("sysexin"), (t_newmethod)sysexin_new, (t_method)sysexin_free, sizeof(t_midiin), CLASS_NOINLET, A_DEFFLOAT, 0); class_addlist(sysexin_class, midiin_list); class_sethelpsymbol(sysexin_class, gensym("midi")); } void inmidi_byte(int portno, int byte) { t_atom at[2]; if (pd_this->pd_midi->m_midiin_sym->s_thing) { SETFLOAT(at, byte); SETFLOAT(at+1, portno); pd_list(pd_this->pd_midi->m_midiin_sym->s_thing, 0, 2, at); } } void inmidi_sysex(int portno, int byte) { t_atom at[2]; if (pd_this->pd_midi->m_sysexin_sym->s_thing) { SETFLOAT(at, byte); SETFLOAT(at+1, portno); pd_list(pd_this->pd_midi->m_sysexin_sym->s_thing, 0, 2, at); } } /* ----------------------- notein ------------------------- */ static t_class *notein_class; typedef struct _notein { t_object x_obj; t_float x_channel; t_outlet *x_outlet1; t_outlet *x_outlet2; t_outlet *x_outlet3; } t_notein; static void *notein_new(t_floatarg f) { t_notein *x = (t_notein *)pd_new(notein_class); x->x_channel = f; x->x_outlet1 = outlet_new(&x->x_obj, &s_float); x->x_outlet2 = outlet_new(&x->x_obj, &s_float); if (f == 0) x->x_outlet3 = outlet_new(&x->x_obj, &s_float); pd_bind(&x->x_obj.ob_pd, pd_this->pd_midi->m_notein_sym); return (x); } static void notein_list(t_notein *x, t_symbol *s, int argc, t_atom *argv) { t_float pitch = atom_getfloatarg(0, argc, argv); t_float velo = atom_getfloatarg(1, argc, argv); t_float channel = atom_getfloatarg(2, argc, argv); if (x->x_channel != 0) { if (channel != x->x_channel) return; outlet_float(x->x_outlet2, velo); outlet_float(x->x_outlet1, pitch); } else { outlet_float(x->x_outlet3, channel); outlet_float(x->x_outlet2, velo); outlet_float(x->x_outlet1, pitch); } } static void notein_free(t_notein *x) { pd_unbind(&x->x_obj.ob_pd, pd_this->pd_midi->m_notein_sym); } static void notein_setup(void) { notein_class = class_new(gensym("notein"), (t_newmethod)notein_new, (t_method)notein_free, sizeof(t_notein), CLASS_NOINLET, A_DEFFLOAT, 0); class_addlist(notein_class, notein_list); class_sethelpsymbol(notein_class, gensym("midi")); } void inmidi_noteon(int portno, int channel, int pitch, int velo) { if (pd_this->pd_midi->m_notein_sym->s_thing) { t_atom at[3]; SETFLOAT(at, pitch); SETFLOAT(at+1, velo); SETFLOAT(at+2, (channel + (portno << 4) + 1)); pd_list(pd_this->pd_midi->m_notein_sym->s_thing, &s_list, 3, at); } } /* ----------------------- ctlin ------------------------- */ static t_class *ctlin_class; typedef struct _ctlin { t_object x_obj; t_float x_channel; t_float x_ctlno; t_outlet *x_outlet1; t_outlet *x_outlet2; t_outlet *x_outlet3; } t_ctlin; static void *ctlin_new(t_symbol *s, int argc, t_atom *argv) { int ctlno, channel; t_ctlin *x = (t_ctlin *)pd_new(ctlin_class); if (!argc) ctlno = -1; else ctlno = atom_getfloatarg(0, argc, argv); channel = atom_getfloatarg(1, argc, argv); x->x_channel = channel; x->x_ctlno = ctlno; x->x_outlet1 = outlet_new(&x->x_obj, &s_float); if (!channel) { if (x->x_ctlno < 0) x->x_outlet2 = outlet_new(&x->x_obj, &s_float); x->x_outlet3 = outlet_new(&x->x_obj, &s_float); } pd_bind(&x->x_obj.ob_pd, pd_this->pd_midi->m_ctlin_sym); return (x); } static void ctlin_list(t_ctlin *x, t_symbol *s, int argc, t_atom *argv) { t_float ctlnumber = atom_getfloatarg(0, argc, argv); t_float value = atom_getfloatarg(1, argc, argv); t_float channel = atom_getfloatarg(2, argc, argv); if (x->x_ctlno >= 0 && x->x_ctlno != ctlnumber) return; if (x->x_channel > 0 && x->x_channel != channel) return; if (x->x_channel == 0) outlet_float(x->x_outlet3, channel); if (x->x_ctlno < 0) outlet_float(x->x_outlet2, ctlnumber); outlet_float(x->x_outlet1, value); } static void ctlin_free(t_ctlin *x) { pd_unbind(&x->x_obj.ob_pd, pd_this->pd_midi->m_ctlin_sym); } static void ctlin_setup(void) { ctlin_class = class_new(gensym("ctlin"), (t_newmethod)ctlin_new, (t_method)ctlin_free, sizeof(t_ctlin), CLASS_NOINLET, A_GIMME, 0); class_addlist(ctlin_class, ctlin_list); class_sethelpsymbol(ctlin_class, gensym("midi")); } void inmidi_controlchange(int portno, int channel, int ctlnumber, int value) { if (pd_this->pd_midi->m_ctlin_sym->s_thing) { t_atom at[3]; SETFLOAT(at, ctlnumber); SETFLOAT(at+1, value); SETFLOAT(at+2, (channel + (portno << 4) + 1)); pd_list(pd_this->pd_midi->m_ctlin_sym->s_thing, &s_list, 3, at); } } /* ----------------------- pgmin ------------------------- */ static t_class *pgmin_class; typedef struct _pgmin { t_object x_obj; t_float x_channel; t_outlet *x_outlet1; t_outlet *x_outlet2; } t_pgmin; static void *pgmin_new(t_floatarg f) { t_pgmin *x = (t_pgmin *)pd_new(pgmin_class); x->x_channel = f; x->x_outlet1 = outlet_new(&x->x_obj, &s_float); if (f == 0) x->x_outlet2 = outlet_new(&x->x_obj, &s_float); pd_bind(&x->x_obj.ob_pd, pd_this->pd_midi->m_pgmin_sym); return (x); } static void pgmin_list(t_pgmin *x, t_symbol *s, int argc, t_atom *argv) { t_float value = atom_getfloatarg(0, argc, argv); t_float channel = atom_getfloatarg(1, argc, argv); if (x->x_channel != 0) { if (channel != x->x_channel) return; outlet_float(x->x_outlet1, value); } else { outlet_float(x->x_outlet2, channel); outlet_float(x->x_outlet1, value); } } static void pgmin_free(t_pgmin *x) { pd_unbind(&x->x_obj.ob_pd, pd_this->pd_midi->m_pgmin_sym); } static void pgmin_setup(void) { pgmin_class = class_new(gensym("pgmin"), (t_newmethod)pgmin_new, (t_method)pgmin_free, sizeof(t_pgmin), CLASS_NOINLET, A_DEFFLOAT, 0); class_addlist(pgmin_class, pgmin_list); class_sethelpsymbol(pgmin_class, gensym("midi")); } void inmidi_programchange(int portno, int channel, int value) { if (pd_this->pd_midi->m_pgmin_sym->s_thing) { t_atom at[2]; SETFLOAT(at, value + 1); SETFLOAT(at+1, (channel + (portno << 4) + 1)); pd_list(pd_this->pd_midi->m_pgmin_sym->s_thing, &s_list, 2, at); } } /* ----------------------- bendin ------------------------- */ static t_class *bendin_class; typedef struct _bendin { t_object x_obj; t_float x_channel; t_outlet *x_outlet1; t_outlet *x_outlet2; } t_bendin; static void *bendin_new(t_floatarg f) { t_bendin *x = (t_bendin *)pd_new(bendin_class); x->x_channel = f; x->x_outlet1 = outlet_new(&x->x_obj, &s_float); if (f == 0) x->x_outlet2 = outlet_new(&x->x_obj, &s_float); pd_bind(&x->x_obj.ob_pd, pd_this->pd_midi->m_bendin_sym); return (x); } static void bendin_list(t_bendin *x, t_symbol *s, int argc, t_atom *argv) { t_float value = atom_getfloatarg(0, argc, argv); t_float channel = atom_getfloatarg(1, argc, argv); if (x->x_channel != 0) { if (channel != x->x_channel) return; outlet_float(x->x_outlet1, value); } else { outlet_float(x->x_outlet2, channel); outlet_float(x->x_outlet1, value); } } static void bendin_free(t_bendin *x) { pd_unbind(&x->x_obj.ob_pd, pd_this->pd_midi->m_bendin_sym); } static void bendin_setup(void) { bendin_class = class_new(gensym("bendin"), (t_newmethod)bendin_new, (t_method)bendin_free, sizeof(t_bendin), CLASS_NOINLET, A_DEFFLOAT, 0); class_addlist(bendin_class, bendin_list); class_sethelpsymbol(bendin_class, gensym("midi")); } void inmidi_pitchbend(int portno, int channel, int value) { if (pd_this->pd_midi->m_bendin_sym->s_thing) { t_atom at[2]; SETFLOAT(at, value); SETFLOAT(at+1, (channel + (portno << 4) + 1)); pd_list(pd_this->pd_midi->m_bendin_sym->s_thing, &s_list, 2, at); } } /* ----------------------- touchin ------------------------- */ static t_class *touchin_class; typedef struct _touchin { t_object x_obj; t_float x_channel; t_outlet *x_outlet1; t_outlet *x_outlet2; } t_touchin; static void *touchin_new(t_floatarg f) { t_touchin *x = (t_touchin *)pd_new(touchin_class); x->x_channel = f; x->x_outlet1 = outlet_new(&x->x_obj, &s_float); if (f == 0) x->x_outlet2 = outlet_new(&x->x_obj, &s_float); pd_bind(&x->x_obj.ob_pd, pd_this->pd_midi->m_touchin_sym); return (x); } static void touchin_list(t_touchin *x, t_symbol *s, int argc, t_atom *argv) { t_float value = atom_getfloatarg(0, argc, argv); t_float channel = atom_getfloatarg(1, argc, argv); if (x->x_channel) { if (channel != x->x_channel) return; outlet_float(x->x_outlet1, value); } else { outlet_float(x->x_outlet2, channel); outlet_float(x->x_outlet1, value); } } static void touchin_free(t_touchin *x) { pd_unbind(&x->x_obj.ob_pd, pd_this->pd_midi->m_touchin_sym); } static void touchin_setup(void) { touchin_class = class_new(gensym("touchin"), (t_newmethod)touchin_new, (t_method)touchin_free, sizeof(t_touchin), CLASS_NOINLET, A_DEFFLOAT, 0); class_addlist(touchin_class, touchin_list); class_sethelpsymbol(touchin_class, gensym("midi")); } void inmidi_aftertouch(int portno, int channel, int value) { if (pd_this->pd_midi->m_touchin_sym->s_thing) { t_atom at[2]; SETFLOAT(at, value); SETFLOAT(at+1, (channel + (portno << 4) + 1)); pd_list(pd_this->pd_midi->m_touchin_sym->s_thing, &s_list, 2, at); } } /* ----------------------- polytouchin ------------------------- */ static t_class *polytouchin_class; typedef struct _polytouchin { t_object x_obj; t_float x_channel; t_outlet *x_outlet1; t_outlet *x_outlet2; t_outlet *x_outlet3; } t_polytouchin; static void *polytouchin_new(t_floatarg f) { t_polytouchin *x = (t_polytouchin *)pd_new(polytouchin_class); x->x_channel = f; x->x_outlet1 = outlet_new(&x->x_obj, &s_float); x->x_outlet2 = outlet_new(&x->x_obj, &s_float); if (f == 0) x->x_outlet3 = outlet_new(&x->x_obj, &s_float); pd_bind(&x->x_obj.ob_pd, pd_this->pd_midi->m_polytouchin_sym); return (x); } static void polytouchin_list(t_polytouchin *x, t_symbol *s, int argc, t_atom *argv) { t_float pitch = atom_getfloatarg(0, argc, argv); t_float value = atom_getfloatarg(1, argc, argv); t_float channel = atom_getfloatarg(2, argc, argv); if (x->x_channel != 0) { if (channel != x->x_channel) return; outlet_float(x->x_outlet2, pitch); outlet_float(x->x_outlet1, value); } else { outlet_float(x->x_outlet3, channel); outlet_float(x->x_outlet2, pitch); outlet_float(x->x_outlet1, value); } } static void polytouchin_free(t_polytouchin *x) { pd_unbind(&x->x_obj.ob_pd, pd_this->pd_midi->m_polytouchin_sym); } static void polytouchin_setup(void) { polytouchin_class = class_new(gensym("polytouchin"), (t_newmethod)polytouchin_new, (t_method)polytouchin_free, sizeof(t_polytouchin), CLASS_NOINLET, A_DEFFLOAT, 0); class_addlist(polytouchin_class, polytouchin_list); class_sethelpsymbol(polytouchin_class, gensym("midi")); } void inmidi_polyaftertouch(int portno, int channel, int pitch, int value) { if (pd_this->pd_midi->m_polytouchin_sym->s_thing) { t_atom at[3]; SETFLOAT(at, pitch); SETFLOAT(at+1, value); SETFLOAT(at+2, (channel + (portno << 4) + 1)); pd_list(pd_this->pd_midi->m_polytouchin_sym->s_thing, &s_list, 3, at); } } /*----------midirealtimein (midi F8,FA,FB,FC,FE,FF message)-----------------*/ static t_class *midirealtimein_class; typedef struct _midirealtimein { t_object x_obj; t_outlet *x_outlet1; t_outlet *x_outlet2; } t_midirealtimein; static void *midirealtimein_new(void) { t_midirealtimein *x = (t_midirealtimein *)pd_new(midirealtimein_class); x->x_outlet1 = outlet_new(&x->x_obj, &s_float); x->x_outlet2 = outlet_new(&x->x_obj, &s_float); pd_bind(&x->x_obj.ob_pd, pd_this->pd_midi->m_midirealtimein_sym); return (x); } static void midirealtimein_list(t_midirealtimein *x, t_symbol *s, int argc, t_atom *argv) { t_float portno = atom_getfloatarg(0, argc, argv); t_float byte = atom_getfloatarg(1, argc, argv); outlet_float(x->x_outlet2, portno); outlet_float(x->x_outlet1, byte); } static void midirealtimein_free(t_midirealtimein *x) { pd_unbind(&x->x_obj.ob_pd, pd_this->pd_midi->m_midirealtimein_sym); } static void midirealtimein_setup(void) { midirealtimein_class = class_new(gensym("midirealtimein"), (t_newmethod)midirealtimein_new, (t_method)midirealtimein_free, sizeof(t_midirealtimein), CLASS_NOINLET, A_DEFFLOAT, 0); class_addlist(midirealtimein_class, midirealtimein_list); class_sethelpsymbol(midirealtimein_class, gensym("midi")); } void inmidi_realtimein(int portno, int SysMsg) { if (pd_this->pd_midi->m_midirealtimein_sym->s_thing) { t_atom at[2]; SETFLOAT(at, portno); SETFLOAT(at+1, SysMsg); pd_list(pd_this->pd_midi->m_midirealtimein_sym->s_thing, &s_list, 2, at); } } /* -------------------------- midiout -------------------------- */ static t_class *midiout_class; void sys_putmidibyte(int portno, int byte); typedef struct _midiout { t_object x_obj; t_float x_portno; } t_midiout; static void *midiout_new(t_floatarg portno) { t_midiout *x = (t_midiout *)pd_new(midiout_class); if (portno <= 0) portno = 1; x->x_portno = portno; floatinlet_new(&x->x_obj, &x->x_portno); return (x); } static void midiout_float(t_midiout *x, t_floatarg f) { outmidi_byte(x->x_portno - 1, f); } static void midiout_list(t_midiout *x, t_symbol *s, int ac, t_atom *av) { int i; for (i = 0; i < ac; ++i) { if(av[i].a_type == A_FLOAT) outmidi_byte(x->x_portno - 1, av[i].a_w.w_float); } } static void midiout_setup(void) { midiout_class = class_new(gensym("midiout"), (t_newmethod)midiout_new, 0, sizeof(t_midiout), 0, A_DEFFLOAT, A_DEFFLOAT, 0); class_addfloat(midiout_class, midiout_float); class_addlist(midiout_class, midiout_list); class_sethelpsymbol(midiout_class, gensym("midi")); } /* -------------------------- noteout -------------------------- */ static t_class *noteout_class; typedef struct _noteout { t_object x_obj; t_float x_velo; t_float x_channel; } t_noteout; static void *noteout_new(t_floatarg channel) { t_noteout *x = (t_noteout *)pd_new(noteout_class); x->x_velo = 0; if (channel < 1) channel = 1; x->x_channel = channel; floatinlet_new(&x->x_obj, &x->x_velo); floatinlet_new(&x->x_obj, &x->x_channel); return (x); } static void noteout_float(t_noteout *x, t_float f) { int binchan = x->x_channel - 1; if (binchan < 0) binchan = 0; outmidi_noteon((binchan >> 4), (binchan & 15), (int)f, (int)x->x_velo); } static void noteout_setup(void) { noteout_class = class_new(gensym("noteout"), (t_newmethod)noteout_new, 0, sizeof(t_noteout), 0, A_DEFFLOAT, 0); class_addfloat(noteout_class, noteout_float); class_sethelpsymbol(noteout_class, gensym("midi")); } /* -------------------------- ctlout -------------------------- */ static t_class *ctlout_class; typedef struct _ctlout { t_object x_obj; t_float x_ctl; t_float x_channel; } t_ctlout; static void *ctlout_new(t_floatarg ctl, t_floatarg channel) { t_ctlout *x = (t_ctlout *)pd_new(ctlout_class); x->x_ctl = ctl; if (channel <= 0) channel = 1; x->x_channel = channel; floatinlet_new(&x->x_obj, &x->x_ctl); floatinlet_new(&x->x_obj, &x->x_channel); return (x); } static void ctlout_float(t_ctlout *x, t_float f) { int binchan = x->x_channel - 1; if (binchan < 0) binchan = 0; outmidi_controlchange((binchan >> 4), (binchan & 15), (int)(x->x_ctl), (int)f); } static void ctlout_setup(void) { ctlout_class = class_new(gensym("ctlout"), (t_newmethod)ctlout_new, 0, sizeof(t_ctlout), 0, A_DEFFLOAT, A_DEFFLOAT, 0); class_addfloat(ctlout_class, ctlout_float); class_sethelpsymbol(ctlout_class, gensym("midi")); } /* -------------------------- pgmout -------------------------- */ static t_class *pgmout_class; typedef struct _pgmout { t_object x_obj; t_float x_channel; } t_pgmout; static void *pgmout_new(t_floatarg channel) { t_pgmout *x = (t_pgmout *)pd_new(pgmout_class); if (channel <= 0) channel = 1; x->x_channel = channel; floatinlet_new(&x->x_obj, &x->x_channel); return (x); } static void pgmout_float(t_pgmout *x, t_floatarg f) { int binchan = x->x_channel - 1; int n = f - 1; if (binchan < 0) binchan = 0; if (n < 0) n = 0; else if (n > 127) n = 127; outmidi_programchange((binchan >> 4), (binchan & 15), n); } static void pgmout_setup(void) { pgmout_class = class_new(gensym("pgmout"), (t_newmethod)pgmout_new, 0, sizeof(t_pgmout), 0, A_DEFFLOAT, 0); class_addfloat(pgmout_class, pgmout_float); class_sethelpsymbol(pgmout_class, gensym("midi")); } /* -------------------------- bendout -------------------------- */ static t_class *bendout_class; typedef struct _bendout { t_object x_obj; t_float x_channel; } t_bendout; static void *bendout_new(t_floatarg channel) { t_bendout *x = (t_bendout *)pd_new(bendout_class); if (channel <= 0) channel = 1; x->x_channel = channel; floatinlet_new(&x->x_obj, &x->x_channel); return (x); } static void bendout_float(t_bendout *x, t_float f) { int binchan = x->x_channel - 1; int n = (int)f + 8192; if (binchan < 0) binchan = 0; outmidi_pitchbend((binchan >> 4), (binchan & 15), n); } static void bendout_setup(void) { bendout_class = class_new(gensym("bendout"), (t_newmethod)bendout_new, 0, sizeof(t_bendout), 0, A_DEFFLOAT, 0); class_addfloat(bendout_class, bendout_float); class_sethelpsymbol(bendout_class, gensym("midi")); } /* -------------------------- touch -------------------------- */ static t_class *touchout_class; typedef struct _touchout { t_object x_obj; t_float x_channel; } t_touchout; static void *touchout_new(t_floatarg channel) { t_touchout *x = (t_touchout *)pd_new(touchout_class); if (channel <= 0) channel = 1; x->x_channel = channel; floatinlet_new(&x->x_obj, &x->x_channel); return (x); } static void touchout_float(t_touchout *x, t_float f) { int binchan = x->x_channel - 1; if (binchan < 0) binchan = 0; outmidi_aftertouch((binchan >> 4), (binchan & 15), (int)f); } static void touchout_setup(void) { touchout_class = class_new(gensym("touchout"), (t_newmethod)touchout_new, 0, sizeof(t_touchout), 0, A_DEFFLOAT, 0); class_addfloat(touchout_class, touchout_float); class_sethelpsymbol(touchout_class, gensym("midi")); } /* -------------------------- polytouch -------------------------- */ static t_class *polytouchout_class; typedef struct _polytouchout { t_object x_obj; t_float x_channel; t_float x_pitch; } t_polytouchout; static void *polytouchout_new(t_floatarg channel) { t_polytouchout *x = (t_polytouchout *)pd_new(polytouchout_class); if (channel <= 0) channel = 1; x->x_channel = channel; x->x_pitch = 0; floatinlet_new(&x->x_obj, &x->x_pitch); floatinlet_new(&x->x_obj, &x->x_channel); return (x); } static void polytouchout_float(t_polytouchout *x, t_float n) { int binchan = x->x_channel - 1; if (binchan < 0) binchan = 0; outmidi_polyaftertouch((binchan >> 4), (binchan & 15), x->x_pitch, n); } static void polytouchout_setup(void) { polytouchout_class = class_new(gensym("polytouchout"), (t_newmethod)polytouchout_new, 0, sizeof(t_polytouchout), 0, A_DEFFLOAT, 0); class_addfloat(polytouchout_class, polytouchout_float); class_sethelpsymbol(polytouchout_class, gensym("midi")); } /* -------------------------- makenote -------------------------- */ static t_class *makenote_class; typedef struct _hang { t_clock *h_clock; struct _hang *h_next; t_float h_pitch; struct _makenote *h_owner; } t_hang; typedef struct _makenote { t_object x_obj; t_float x_velo; t_float x_dur; t_outlet *x_pitchout; t_outlet *x_velout; t_hang *x_hang; } t_makenote; static void *makenote_new(t_floatarg velo, t_floatarg dur) { t_makenote *x = (t_makenote *)pd_new(makenote_class); x->x_velo = velo; x->x_dur = dur; floatinlet_new(&x->x_obj, &x->x_velo); floatinlet_new(&x->x_obj, &x->x_dur); x->x_pitchout = outlet_new(&x->x_obj, &s_float); x->x_velout = outlet_new(&x->x_obj, &s_float); x->x_hang = 0; return (x); } static void makenote_tick(t_hang *hang) { t_makenote *x = hang->h_owner; t_hang *h2, *h3; outlet_float(x->x_velout, 0); outlet_float(x->x_pitchout, hang->h_pitch); if (x->x_hang == hang) x->x_hang = hang->h_next; else for (h2 = x->x_hang; (h3 = h2->h_next); h2 = h3) { if (h3 == hang) { h2->h_next = h3->h_next; break; } } clock_free(hang->h_clock); freebytes(hang, sizeof(*hang)); } static void makenote_float(t_makenote *x, t_float f) { t_hang *hang; if (!x->x_velo) return; outlet_float(x->x_velout, x->x_velo); outlet_float(x->x_pitchout, f); hang = (t_hang *)getbytes(sizeof *hang); hang->h_next = x->x_hang; x->x_hang = hang; hang->h_pitch = f; hang->h_owner = x; hang->h_clock = clock_new(hang, (t_method)makenote_tick); clock_delay(hang->h_clock, (x->x_dur >= 0 ? x->x_dur : 0)); } static void makenote_stop(t_makenote *x) { t_hang *hang; while ((hang = x->x_hang)) { outlet_float(x->x_velout, 0); outlet_float(x->x_pitchout, hang->h_pitch); x->x_hang = hang->h_next; clock_free(hang->h_clock); freebytes(hang, sizeof(*hang)); } } static void makenote_clear(t_makenote *x) { t_hang *hang; while ((hang = x->x_hang)) { x->x_hang = hang->h_next; clock_free(hang->h_clock); freebytes(hang, sizeof(*hang)); } } static void makenote_setup(void) { makenote_class = class_new(gensym("makenote"), (t_newmethod)makenote_new, (t_method)makenote_clear, sizeof(t_makenote), 0, A_DEFFLOAT, A_DEFFLOAT, 0); class_addfloat(makenote_class, makenote_float); class_addmethod(makenote_class, (t_method)makenote_stop, gensym("stop"), 0); class_addmethod(makenote_class, (t_method)makenote_clear, gensym("clear"), 0); } /* -------------------------- stripnote -------------------------- */ static t_class *stripnote_class; typedef struct _stripnote { t_object x_obj; t_float x_velo; t_outlet *x_pitchout; t_outlet *x_velout; } t_stripnote; static void *stripnote_new(void) { t_stripnote *x = (t_stripnote *)pd_new(stripnote_class); floatinlet_new(&x->x_obj, &x->x_velo); x->x_pitchout = outlet_new(&x->x_obj, &s_float); x->x_velout = outlet_new(&x->x_obj, &s_float); return (x); } static void stripnote_float(t_stripnote *x, t_float f) { if (!x->x_velo) return; outlet_float(x->x_velout, x->x_velo); outlet_float(x->x_pitchout, f); } static void stripnote_setup(void) { stripnote_class = class_new(gensym("stripnote"), (t_newmethod)stripnote_new, 0, sizeof(t_stripnote), 0, 0); class_addfloat(stripnote_class, stripnote_float); } /* -------------------------- poly -------------------------- */ static t_class *poly_class; typedef struct voice { t_float v_pitch; int v_used; unsigned long v_serial; } t_voice; typedef struct poly { t_object x_obj; int x_n; t_voice *x_vec; t_float x_vel; t_outlet *x_pitchout; t_outlet *x_velout; unsigned long x_serial; int x_steal; } t_poly; static void *poly_new(t_float fnvoice, t_float fsteal) { int i, n = fnvoice; t_poly *x = (t_poly *)pd_new(poly_class); t_voice *v; if (n < 1) n = 1; x->x_n = n; x->x_vec = (t_voice *)getbytes(n * sizeof(*x->x_vec)); for (v = x->x_vec, i = n; i--; v++) v->v_pitch = v->v_used = v->v_serial = 0; x->x_vel = 0; x->x_steal = (fsteal != 0); floatinlet_new(&x->x_obj, &x->x_vel); outlet_new(&x->x_obj, &s_float); x->x_pitchout = outlet_new(&x->x_obj, &s_float); x->x_velout = outlet_new(&x->x_obj, &s_float); x->x_serial = 0; return (x); } static void poly_float(t_poly *x, t_float f) { int i; t_voice *v; t_voice *firston, *firstoff; unsigned int serialon, serialoff, onindex = 0, offindex = 0; if (x->x_vel > 0) { /* note on. Look for a vacant voice */ for (v = x->x_vec, i = 0, firston = firstoff = 0, serialon = serialoff = 0xffffffff; i < x->x_n; v++, i++) { if (v->v_used && v->v_serial < serialon) firston = v, serialon = (unsigned int)v->v_serial, onindex = i; else if (!v->v_used && v->v_serial < serialoff) firstoff = v, serialoff = (unsigned int)v->v_serial, offindex = i; } if (firstoff) { outlet_float(x->x_velout, x->x_vel); outlet_float(x->x_pitchout, firstoff->v_pitch = f); outlet_float(x->x_obj.ob_outlet, offindex+1); firstoff->v_used = 1; firstoff->v_serial = x->x_serial++; } /* if none, steal one */ else if (firston && x->x_steal) { outlet_float(x->x_velout, 0); outlet_float(x->x_pitchout, firston->v_pitch); outlet_float(x->x_obj.ob_outlet, onindex+1); outlet_float(x->x_velout, x->x_vel); outlet_float(x->x_pitchout, firston->v_pitch = f); outlet_float(x->x_obj.ob_outlet, onindex+1); firston->v_serial = x->x_serial++; } } else /* note off. Turn off oldest match */ { for (v = x->x_vec, i = 0, firston = 0, serialon = 0xffffffff; i < x->x_n; v++, i++) if (v->v_used && v->v_pitch == f && v->v_serial < serialon) firston = v, serialon = (unsigned int)v->v_serial, onindex = i; if (firston) { firston->v_used = 0; firston->v_serial = x->x_serial++; outlet_float(x->x_velout, 0); outlet_float(x->x_pitchout, firston->v_pitch); outlet_float(x->x_obj.ob_outlet, onindex+1); } } } static void poly_stop(t_poly *x) { int i; t_voice *v; for (i = 0, v = x->x_vec; i < x->x_n; i++, v++) if (v->v_used) { outlet_float(x->x_velout, 0L); outlet_float(x->x_pitchout, v->v_pitch); outlet_float(x->x_obj.ob_outlet, i+1); v->v_used = 0; v->v_serial = x->x_serial++; } } static void poly_clear(t_poly *x) { int i; t_voice *v; for (v = x->x_vec, i = x->x_n; i--; v++) v->v_used = v->v_serial = 0; } static void poly_free(t_poly *x) { freebytes(x->x_vec, x->x_n * sizeof (*x->x_vec)); } static void poly_setup(void) { poly_class = class_new(gensym("poly"), (t_newmethod)poly_new, (t_method)poly_free, sizeof(t_poly), 0, A_DEFFLOAT, A_DEFFLOAT, 0); class_addfloat(poly_class, poly_float); class_addmethod(poly_class, (t_method)poly_stop, gensym("stop"), 0); class_addmethod(poly_class, (t_method)poly_clear, gensym("clear"), 0); } /* -------------------------- bag -------------------------- */ static t_class *bag_class; typedef struct _bagelem { struct _bagelem *e_next; t_float e_value; } t_bagelem; typedef struct _bag { t_object x_obj; t_float x_velo; t_bagelem *x_first; } t_bag; static void *bag_new(void) { t_bag *x = (t_bag *)pd_new(bag_class); x->x_velo = 0; floatinlet_new(&x->x_obj, &x->x_velo); outlet_new(&x->x_obj, &s_float); x->x_first = 0; return (x); } static void bag_float(t_bag *x, t_float f) { t_bagelem *bagelem, *e2, *e3; if (x->x_velo != 0) { bagelem = (t_bagelem *)getbytes(sizeof *bagelem); bagelem->e_next = 0; bagelem->e_value = f; if (!x->x_first) x->x_first = bagelem; else /* LATER replace with a faster algorithm */ { for (e2 = x->x_first; (e3 = e2->e_next); e2 = e3) ; e2->e_next = bagelem; } } else { if (!x->x_first) return; if (x->x_first->e_value == f) { bagelem = x->x_first; x->x_first = x->x_first->e_next; freebytes(bagelem, sizeof(*bagelem)); return; } for (e2 = x->x_first; (e3 = e2->e_next); e2 = e3) if (e3->e_value == f) { e2->e_next = e3->e_next; freebytes(e3, sizeof(*e3)); return; } } } static void bag_flush(t_bag *x) { t_bagelem *bagelem; while ((bagelem = x->x_first)) { outlet_float(x->x_obj.ob_outlet, bagelem->e_value); x->x_first = bagelem->e_next; freebytes(bagelem, sizeof(*bagelem)); } } static void bag_clear(t_bag *x) { t_bagelem *bagelem; while ((bagelem = x->x_first)) { x->x_first = bagelem->e_next; freebytes(bagelem, sizeof(*bagelem)); } } static void bag_setup(void) { bag_class = class_new(gensym("bag"), (t_newmethod)bag_new, (t_method)bag_clear, sizeof(t_bag), 0, 0); class_addfloat(bag_class, bag_float); class_addmethod(bag_class, (t_method)bag_flush, gensym("flush"), 0); class_addmethod(bag_class, (t_method)bag_clear, gensym("clear"), 0); } void x_midi_setup(void) { midiin_setup(); midirealtimein_setup(); notein_setup(); ctlin_setup(); pgmin_setup(); bendin_setup(); touchin_setup(); polytouchin_setup(); midiout_setup(); noteout_setup(); ctlout_setup(); pgmout_setup(); bendout_setup(); touchout_setup(); polytouchout_setup(); makenote_setup(); stripnote_setup(); poly_setup(); bag_setup(); } void x_midi_newpdinstance(void) { pd_this->pd_midi = getbytes(sizeof(t_instancemidi)); pd_this->pd_midi->m_midiin_sym = gensym("#midiin"); pd_this->pd_midi->m_sysexin_sym = gensym("#sysexin"); pd_this->pd_midi->m_notein_sym = gensym("#notein"); pd_this->pd_midi->m_ctlin_sym = gensym("#ctlin"); pd_this->pd_midi->m_pgmin_sym = gensym("#pgmin"); pd_this->pd_midi->m_bendin_sym = gensym("#bendin"); pd_this->pd_midi->m_touchin_sym = gensym("#touchin"); pd_this->pd_midi->m_polytouchin_sym = gensym("#polytouchin"); pd_this->pd_midi->m_midirealtimein_sym = gensym("#midirealtimein"); } void x_midi_freepdinstance(void) { freebytes(pd_this->pd_midi, sizeof(t_instancemidi)); } ================================================ FILE: libs/libpd/pure-data/src/x_misc.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* misc. */ #include "m_pd.h" #include "s_stuff.h" #include "g_canvas.h" #include #ifdef _WIN32 #include #include #else #include #include #include #include #include #endif /* _WIN32 */ #if defined (__APPLE__) || defined (__FreeBSD__) #define CLOCKHZ CLK_TCK #endif #if defined (__linux__) || defined (__CYGWIN__) || defined (ANDROID) #define CLOCKHZ sysconf(_SC_CLK_TCK) #endif #if defined (__FreeBSD_kernel__) || defined(__GNU__) || defined(__OpenBSD__) \ || defined(_WIN32) #include #define CLOCKHZ CLOCKS_PER_SEC #endif #include "m_private_utils.h" /* -------------------------- random ------------------------------ */ /* this is strictly homebrew and untested. */ static t_class *random_class; typedef struct _random { t_object x_obj; t_float x_f; unsigned int x_state; } t_random; static int makeseed(void) { static unsigned int random_nextseed = 1489853723; random_nextseed = random_nextseed * 435898247 + 938284287; return (random_nextseed & 0x7fffffff); } static void *random_new(t_floatarg f) { t_random *x = (t_random *)pd_new(random_class); x->x_f = f; x->x_state = makeseed(); floatinlet_new(&x->x_obj, &x->x_f); outlet_new(&x->x_obj, &s_float); return (x); } static void random_bang(t_random *x) { int n = x->x_f, nval; int range = (n < 1 ? 1 : n); unsigned int randval = x->x_state; x->x_state = randval = randval * 472940017 + 832416023; nval = ((double)range) * ((double)randval) * (1./4294967296.); if (nval >= range) nval = range-1; outlet_float(x->x_obj.ob_outlet, nval); } static void random_seed(t_random *x, t_float f, t_float glob) { x->x_state = f; } static void random_setup(void) { random_class = class_new(gensym("random"), (t_newmethod)random_new, 0, sizeof(t_random), 0, A_DEFFLOAT, 0); class_addbang(random_class, random_bang); class_addmethod(random_class, (t_method)random_seed, gensym("seed"), A_FLOAT, 0); } /* -------------------------- loadbang ------------------------------ */ static t_class *loadbang_class; typedef struct _loadbang { t_object x_obj; } t_loadbang; static void *loadbang_new(void) { t_loadbang *x = (t_loadbang *)pd_new(loadbang_class); outlet_new(&x->x_obj, &s_bang); return (x); } static void loadbang_loadbang(t_loadbang *x, t_floatarg action) { if (action == LB_LOAD) outlet_bang(x->x_obj.ob_outlet); } static void loadbang_setup(void) { loadbang_class = class_new(gensym("loadbang"), (t_newmethod)loadbang_new, 0, sizeof(t_loadbang), CLASS_NOINLET, 0); class_addmethod(loadbang_class, (t_method)loadbang_loadbang, gensym("loadbang"), A_DEFFLOAT, 0); } /* ------------- namecanvas (delete this later) --------------------- */ static t_class *namecanvas_class; typedef struct _namecanvas { t_object x_obj; t_symbol *x_sym; t_pd *x_owner; } t_namecanvas; static void *namecanvas_new(t_symbol *s) { t_namecanvas *x = (t_namecanvas *)pd_new(namecanvas_class); x->x_owner = (t_pd *)canvas_getcurrent(); x->x_sym = s; if (*s->s_name) pd_bind(x->x_owner, s); return (x); } static void namecanvas_free(t_namecanvas *x) { if (*x->x_sym->s_name) pd_unbind(x->x_owner, x->x_sym); } static void namecanvas_setup(void) { namecanvas_class = class_new(gensym("namecanvas"), (t_newmethod)namecanvas_new, (t_method)namecanvas_free, sizeof(t_namecanvas), CLASS_NOINLET, A_DEFSYM, 0); } /* -------------------------- cputime ------------------------------ */ #ifdef CLOCKHZ static t_class *cputime_class; typedef struct _cputime { t_object x_obj; #ifdef _WIN32 LARGE_INTEGER x_kerneltime; LARGE_INTEGER x_usertime; int x_warned; #else struct tms x_setcputime; #endif /* _WIN32 */ } t_cputime; static void cputime_bang(t_cputime *x) { #ifdef _WIN32 FILETIME ignorethis, ignorethat; BOOL retval; retval = GetProcessTimes(GetCurrentProcess(), &ignorethis, &ignorethat, (FILETIME *)&x->x_kerneltime, (FILETIME *)&x->x_usertime); if (!retval) { if (!x->x_warned) post("cputime is apparently not supported on your platform"); x->x_warned = 1; x->x_kerneltime.QuadPart = 0; x->x_usertime.QuadPart = 0; } #else times(&x->x_setcputime); #endif /* _WIN32 */ } static void cputime_bang2(t_cputime *x) { #ifndef _WIN32 t_float elapsedcpu; struct tms newcputime; times(&newcputime); elapsedcpu = 1000 * ( newcputime.tms_utime + newcputime.tms_stime - x->x_setcputime.tms_utime - x->x_setcputime.tms_stime) / CLOCKHZ; outlet_float(x->x_obj.ob_outlet, elapsedcpu); #else t_float elapsedcpu; FILETIME ignorethis, ignorethat; LARGE_INTEGER usertime, kerneltime; BOOL retval; retval = GetProcessTimes(GetCurrentProcess(), &ignorethis, &ignorethat, (FILETIME *)&kerneltime, (FILETIME *)&usertime); if (retval) elapsedcpu = 0.0001 * ((kerneltime.QuadPart - x->x_kerneltime.QuadPart) + (usertime.QuadPart - x->x_usertime.QuadPart)); else elapsedcpu = 0; outlet_float(x->x_obj.ob_outlet, elapsedcpu); #endif /* NOT _WIN32 */ } static void *cputime_new(void) { t_cputime *x = (t_cputime *)pd_new(cputime_class); outlet_new(&x->x_obj, gensym("float")); inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("bang"), gensym("bang2")); #ifdef _WIN32 x->x_warned = 0; #endif cputime_bang(x); return (x); } static void cputime_setup(void) { cputime_class = class_new(gensym("cputime"), (t_newmethod)cputime_new, 0, sizeof(t_cputime), 0, 0); class_addbang(cputime_class, cputime_bang); class_addmethod(cputime_class, (t_method)cputime_bang2, gensym("bang2"), 0); } #endif /* CLOCKHZ */ /* -------------------------- realtime ------------------------------ */ static t_class *realtime_class; typedef struct _realtime { t_object x_obj; double x_setrealtime; } t_realtime; static void realtime_bang(t_realtime *x) { x->x_setrealtime = sys_getrealtime(); } static void realtime_bang2(t_realtime *x) { outlet_float(x->x_obj.ob_outlet, (sys_getrealtime() - x->x_setrealtime) * 1000.); } static void *realtime_new(void) { t_realtime *x = (t_realtime *)pd_new(realtime_class); outlet_new(&x->x_obj, gensym("float")); inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("bang"), gensym("bang2")); realtime_bang(x); return (x); } static void realtime_setup(void) { realtime_class = class_new(gensym("realtime"), (t_newmethod)realtime_new, 0, sizeof(t_realtime), 0, 0); class_addbang(realtime_class, realtime_bang); class_addmethod(realtime_class, (t_method)realtime_bang2, gensym("bang2"), 0); } /* ---------- oscparse - parse simple OSC messages ----------------- */ static t_class *oscparse_class; typedef struct _oscparse { t_object x_obj; } t_oscparse; #define ROUNDUPTO4(x) (((x) + 3) & (~3)) #define READINT(x) ((((int)(((x) )->a_w.w_float)) & 0xff) << 24) | \ ((((int)(((x)+1)->a_w.w_float)) & 0xff) << 16) | \ ((((int)(((x)+2)->a_w.w_float)) & 0xff) << 8) | \ ((((int)(((x)+3)->a_w.w_float)) & 0xff) << 0) static t_symbol *grabstring(int argc, t_atom *argv, int *ip, int slash) { char buf[MAXPDSTRING]; int first, nchar; if (slash) while (*ip < argc && argv[*ip].a_w.w_float == '/') (*ip)++; for (nchar = 0; nchar < MAXPDSTRING-1 && *ip < argc; nchar++, (*ip)++) { char c = argv[*ip].a_w.w_float; if (c == 0 || (slash && c == '/')) break; buf[nchar] = c; } buf[nchar] = 0; if (!slash) *ip = ROUNDUPTO4(*ip+1); if (*ip > argc) *ip = argc; return (gensym(buf)); } static void oscparse_list(t_oscparse *x, t_symbol *s, int argc, t_atom *argv) { int i, j, j2, k, outc = 1, blob = 0, typeonset, dataonset, nfield; t_atom *outv; if (!argc) return; for (i = 0; i < argc; i++) if (argv[i].a_type != A_FLOAT) { pd_error(x, "oscparse: takes numbers only"); return; } if (argv[0].a_w.w_float == '#') /* it's a bundle */ { if (argv[1].a_w.w_float != 'b' || argc < 16) { pd_error(x, "oscparse: malformed bundle"); return; } /* we ignore the timetag since there's no correct way to convert it to Pd logical time that I can think of. LATER consider at least outputting timetag differentially converted into Pd time units. */ for (i = 16; i < argc-4; ) { int msize = READINT(argv+i); if (msize <= 0 || msize & 3) { pd_error(x, "oscparse: bad bundle element size"); return; } oscparse_list(x, 0, msize, argv+i+4); i += msize+4; } return; } else if (argv[0].a_w.w_float != '/') { pd_error(x, "oscparse: not an OSC message (no leading slash)"); return; } for (i = 1; i < argc && argv[i].a_w.w_float != 0; i++) if (argv[i].a_w.w_float == '/') outc++; i = ROUNDUPTO4(i+1); if (argv[i].a_w.w_float != ',' || (i+1) >= argc) { pd_error(x, "oscparse: malformed type string (char %d, index %d)", (int)(argv[i].a_w.w_float), i); return; } typeonset = ++i; for (; i < argc && argv[i].a_w.w_float != 0; i++) if (argv[i].a_w.w_float == 'b') blob = 1; nfield = i - typeonset; if (blob) outc += argc - typeonset; else outc += nfield; outv = (t_atom *)alloca(outc * sizeof(t_atom)); dataonset = ROUNDUPTO4(i + 1); /* post("outc %d, typeonset %d, dataonset %d, nfield %d", outc, typeonset, dataonset, nfield); */ for (i = j = 0; i < typeonset-1 && argv[i].a_w.w_float != 0 && j < outc; j++) SETSYMBOL(outv+j, grabstring(argc, argv, &i, 1)); for (i = typeonset, k = dataonset; i < typeonset + nfield; i++) { union { float z_f; uint32_t z_i; } z; t_float f; int blobsize; switch ((int)(argv[i].a_w.w_float)) { case 'f': if (k > argc - 4) goto tooshort; z.z_i = READINT(argv+k); f = z.z_f; if (PD_BADFLOAT(f)) f = 0; if (j >= outc) { bug("oscparse 1: %d >=%d", j, outc); return; } SETFLOAT(outv+j, f); j++; k += 4; break; case 'i': if (k > argc - 4) goto tooshort; if (j >= outc) { bug("oscparse 2"); return; } SETFLOAT(outv+j, READINT(argv+k)); j++; k += 4; break; case 's': if (j >= outc) { bug("oscparse 3"); return; } SETSYMBOL(outv+j, grabstring(argc, argv, &k, 0)); j++; break; case 'b': if (k > argc - 4) goto tooshort; blobsize = READINT(argv+k); k += 4; if (blobsize < 0 || blobsize > argc - k) goto tooshort; if (j + blobsize + 1 > outc) { bug("oscparse 4"); return; } if (k + blobsize > argc) goto tooshort; SETFLOAT(outv+j, blobsize); j++; for (j2 = 0; j2 < blobsize; j++, j2++, k++) SETFLOAT(outv+j, argv[k].a_w.w_float); k = ROUNDUPTO4(k); break; default: pd_error(x, "oscparse: unknown tag '%c' (%d)", (int)(argv[i].a_w.w_float), (int)(argv[i].a_w.w_float)); } } outlet_list(x->x_obj.ob_outlet, 0, j, outv); return; tooshort: pd_error(x, "oscparse: OSC message ended prematurely"); } static t_oscparse *oscparse_new(t_symbol *s, int argc, t_atom *argv) { t_oscparse *x = (t_oscparse *)pd_new(oscparse_class); outlet_new(&x->x_obj, gensym("list")); return (x); } void oscparse_setup(void) { oscparse_class = class_new(gensym("oscparse"), (t_newmethod)oscparse_new, 0, sizeof(t_oscparse), 0, A_GIMME, 0); class_addlist(oscparse_class, oscparse_list); class_sethelpsymbol(oscparse_class, gensym("osc-format-parse")); } /* --------- oscformat - format simple OSC messages -------------- */ static t_class *oscformat_class; typedef struct _oscformat { t_object x_obj; char *x_pathbuf; size_t x_pathsize; t_symbol *x_format; } t_oscformat; static void oscformat_set(t_oscformat *x, t_symbol *s, int argc, t_atom *argv) { char buf[MAXPDSTRING]; int i; size_t newsize; *x->x_pathbuf = 0; buf[0] = '/'; for (i = 0; i < argc; i++) { char *where = (argv[i].a_type == A_SYMBOL && *argv[i].a_w.w_symbol->s_name == '/' ? buf : buf+1); atom_string(&argv[i], where, MAXPDSTRING-1); if ((newsize = strlen(buf) + strlen(x->x_pathbuf) + 1) > x->x_pathsize) { x->x_pathbuf = resizebytes(x->x_pathbuf, x->x_pathsize, newsize); x->x_pathsize = newsize; } strcat(x->x_pathbuf, buf); } } static void oscformat_format(t_oscformat *x, t_symbol *s) { const char *sp; for (sp = s->s_name; *sp; sp++) { if (*sp != 'f' && *sp != 'i' && *sp != 's' && *sp != 'b') { pd_error(x, "oscformat '%s' may only contain 'f', 'i'. 's', and/or 'b'", sp); return; } } x->x_format = s; } #define WRITEINT(msg, i) SETFLOAT((msg), (((i) >> 24) & 0xff)); \ SETFLOAT((msg)+1, (((i) >> 16) & 0xff)); \ SETFLOAT((msg)+2, (((i) >> 8) & 0xff)); \ SETFLOAT((msg)+3, (((i) ) & 0xff)) static void putstring(t_atom *msg, int *ip, const char *s) { const char *sp = s; do { SETFLOAT(&msg[*ip], *sp & 0xff); (*ip)++; } while (*sp++); while (*ip & 3) { SETFLOAT(&msg[*ip], 0); (*ip)++; } } static void oscformat_list(t_oscformat *x, t_symbol *s, int argc, t_atom *argv) { int typeindex = 0, j, msgindex, msgsize, datastart, ndata; t_atom *msg; const char *sp, *formatp = x->x_format->s_name; char typecode; /* pass 1: go through args to find overall message size */ for (j = ndata = 0, sp = formatp, msgindex = 0; j < argc;) { if (*sp) typecode = *sp++; else if (argv[j].a_type == A_SYMBOL) typecode = 's'; else typecode = 'f'; if (typecode == 's') { if (argv[j].a_type == A_SYMBOL) msgindex += ROUNDUPTO4(strlen(argv[j].a_w.w_symbol->s_name) + 1); else { pd_error(x, "oscformat: expected symbol for argument %d", j+1); return; } } else if (typecode == 'b') { int blobsize = 0x7fffffff, blobindex; /* check if we have a nonnegative size field */ if (argv[j].a_type == A_FLOAT && (int)(argv[j].a_w.w_float) >= 0) blobsize = (int)(argv[j].a_w.w_float); if (blobsize > argc - j - 1) blobsize = argc - j - 1; /* if no or bad size, eat it all */ msgindex += 4 + ROUNDUPTO4(blobsize); j += blobsize; } else msgindex += 4; j++; ndata++; } datastart = (int)(ROUNDUPTO4(strlen(x->x_pathbuf)+1) + ROUNDUPTO4(ndata + 2)); msgsize = datastart + msgindex; msg = (t_atom *)alloca(msgsize * sizeof(t_atom)); putstring(msg, &typeindex, x->x_pathbuf); SETFLOAT(&msg[typeindex], ','); typeindex++; /* pass 2: fill in types and data portion of packet */ for (j = 0, sp = formatp, msgindex = datastart; j < argc;) { if (*sp) typecode = *sp++; else if (argv[j].a_type == A_SYMBOL) typecode = 's'; else typecode = 'f'; SETFLOAT(&msg[typeindex], typecode & 0xff); typeindex++; if (typecode == 'f') { union { float z_f; uint32_t z_i; } z; z.z_f = atom_getfloat(&argv[j]); WRITEINT(msg+msgindex, z.z_i); msgindex += 4; } else if (typecode == 'i') { int dat = atom_getfloat(&argv[j]); WRITEINT(msg+msgindex, dat); msgindex += 4; } else if (typecode == 's') putstring(msg, &msgindex, argv[j].a_w.w_symbol->s_name); else if (typecode == 'b') { int blobsize = 0x7fffffff, blobindex; if (argv[j].a_type == A_FLOAT && (int)(argv[j].a_w.w_float) >= 0) blobsize = (int)(argv[j].a_w.w_float); if (blobsize > argc - j - 1) blobsize = argc - j - 1; WRITEINT(msg+msgindex, blobsize); msgindex += 4; for (blobindex = 0; blobindex < blobsize; blobindex++) SETFLOAT(msg+msgindex+blobindex, (argv[j+1+blobindex].a_type == A_FLOAT ? argv[j+1+blobindex].a_w.w_float : (argv[j+1+blobindex].a_type == A_SYMBOL ? argv[j+1+blobindex].a_w.w_symbol->s_name[0] & 0xff : 0))); j += blobsize; while (blobsize & 3) SETFLOAT(msg+msgindex+blobsize, 0), blobsize++; msgindex += blobsize; } j++; } SETFLOAT(&msg[typeindex], 0); typeindex++; while (typeindex & 3) SETFLOAT(&msg[typeindex], 0), typeindex++; if (typeindex != datastart || msgindex != msgsize) bug("oscformat: typeindex %d, datastart %d, msgindex %d, msgsize %d", typeindex, datastart, msgindex, msgsize); /* else post("datastart %d, msgsize %d", datastart, msgsize); */ outlet_list(x->x_obj.ob_outlet, 0, msgsize, msg); } static void oscformat_free(t_oscformat *x) { freebytes(x->x_pathbuf, x->x_pathsize); } static void *oscformat_new(t_symbol *s, int argc, t_atom *argv) { t_oscformat *x = (t_oscformat *)pd_new(oscformat_class); outlet_new(&x->x_obj, gensym("list")); x->x_pathbuf = getbytes(1); x->x_pathsize = 1; *x->x_pathbuf = 0; x->x_format = &s_; if (argc > 1 && argv[0].a_type == A_SYMBOL && argv[1].a_type == A_SYMBOL && !strcmp(argv[0].a_w.w_symbol->s_name, "-f")) { oscformat_format(x, argv[1].a_w.w_symbol); argc -= 2; argv += 2; } oscformat_set(x, 0, argc, argv); return (x); } void oscformat_setup(void) { oscformat_class = class_new(gensym("oscformat"), (t_newmethod)oscformat_new, (t_method)oscformat_free, sizeof(t_oscformat), 0, A_GIMME, 0); class_addmethod(oscformat_class, (t_method)oscformat_set, gensym("set"), A_GIMME, 0); class_addmethod(oscformat_class, (t_method)oscformat_format, gensym("format"), A_DEFSYM, 0); class_addlist(oscformat_class, oscformat_list); class_sethelpsymbol(oscformat_class, gensym("osc-format-parse")); } /* ---------- fudiparse - parse bytelists to to FUDI messages ----------------- */ static t_class *fudiparse_class; typedef struct _fudiparse { t_object x_obj; t_outlet *x_msgout; char *x_bytes; size_t x_numbytes; } t_fudiparse; static void fudiparse_binbufout(t_fudiparse *x, t_binbuf *b) { int msg, natom = binbuf_getnatom(b); t_atom *at = binbuf_getvec(b); for (msg = 0; msg < natom;) { int emsg; for (emsg = msg; emsg < natom && at[emsg].a_type != A_COMMA && at[emsg].a_type != A_SEMI; emsg++) ; if (emsg > msg) { int i; /* check for illegal atoms */ for (i = msg; i < emsg; i++) if (at[i].a_type == A_DOLLAR || at[i].a_type == A_DOLLSYM) { pd_error(x, "fudiparse: got dollar sign in message"); goto nodice; } if (at[msg].a_type == A_FLOAT) { if (emsg > msg + 1) outlet_list(x->x_msgout, 0, emsg-msg, at + msg); else outlet_float(x->x_msgout, at[msg].a_w.w_float); } else if (at[msg].a_type == A_SYMBOL) { outlet_anything(x->x_msgout, at[msg].a_w.w_symbol, emsg-msg-1, at + msg + 1); } } nodice: msg = emsg + 1; } } static void fudiparse_list(t_fudiparse *x, t_symbol*s, int argc, t_atom*argv) { size_t len = argc; t_binbuf* bbuf = binbuf_new(); char*cbuf; if((size_t)argc > x->x_numbytes) { freebytes(x->x_bytes, x->x_numbytes); x->x_numbytes = argc; x->x_bytes = getbytes(x->x_numbytes); } cbuf = x->x_bytes; while(argc--) { char b = atom_getfloat(argv++); *cbuf++ = b; } binbuf_text(bbuf, x->x_bytes, len); fudiparse_binbufout(x, bbuf); binbuf_free(bbuf); } static void fudiparse_free(t_fudiparse *x) { freebytes(x->x_bytes, x->x_numbytes); x->x_bytes = NULL; x->x_numbytes = 0; } static void *fudiparse_new(void) { t_fudiparse *x = (t_fudiparse *)pd_new(fudiparse_class); x->x_msgout = outlet_new(&x->x_obj, 0); x->x_numbytes = 1024; x->x_bytes = getbytes(x->x_numbytes); return (void *)x; } void fudiparse_setup(void) { fudiparse_class = class_new(gensym("fudiparse"), (t_newmethod)fudiparse_new, (t_method)fudiparse_free, sizeof(t_fudiparse), CLASS_DEFAULT, 0); class_addlist(fudiparse_class, fudiparse_list); class_sethelpsymbol(fudiparse_class, gensym("fudi-format-parse")); } /* --------- fudiformat - format Pd (FUDI) messages to bytelists ------------ */ static t_class *fudiformat_class; typedef struct _fudiformat { t_object x_obj; t_outlet *x_msgout; t_atom *x_atoms; size_t x_numatoms; int x_udp; } t_fudiformat; static void fudiformat_any(t_fudiformat *x, t_symbol*s, int argc, t_atom*argv) { char *buf; int length; int i; t_atom at; t_binbuf*bbuf = binbuf_new(); SETSYMBOL(&at, s); binbuf_add(bbuf, 1, &at); binbuf_add(bbuf, argc, argv); if(!x->x_udp) { SETSEMI(&at); binbuf_add(bbuf, 1, &at); } binbuf_gettext(bbuf, &buf, &length); binbuf_free(bbuf); if((size_t)length>x->x_numatoms) { freebytes(x->x_atoms, sizeof(*x->x_atoms) * x->x_numatoms); x->x_numatoms = length; x->x_atoms = getbytes(sizeof(*x->x_atoms) * x->x_numatoms); } for(i=0; ix_atoms+i, buf[i]); } freebytes(buf, length); outlet_list(x->x_msgout, 0, length, x->x_atoms); } static void fudiformat_free(t_fudiformat *x) { freebytes(x->x_atoms, sizeof(*x->x_atoms) * x->x_numatoms); x->x_atoms = NULL; x->x_numatoms = 0; } static void *fudiformat_new(t_symbol*s) { t_fudiformat *x = (t_fudiformat *)pd_new(fudiformat_class); x->x_msgout = outlet_new(&x->x_obj, 0); x->x_numatoms = 1024; x->x_atoms = getbytes(sizeof(*x->x_atoms) * x->x_numatoms); if (gensym("-u") == s) x->x_udp = 1; else if (gensym("-t") == s) x->x_udp = 0; else if (gensym("") != s) { pd_error(x, "fudiformat: unsupported mode '%s'", s->s_name); } return (void *)x; } static void fudiformat_setup(void) { fudiformat_class = class_new(gensym("fudiformat"), (t_newmethod)fudiformat_new, (t_method)fudiformat_free, sizeof(t_fudiformat), CLASS_DEFAULT, A_DEFSYMBOL, 0); class_addanything(fudiformat_class, fudiformat_any); class_sethelpsymbol(fudiformat_class, gensym("fudi-format-parse")); } void x_misc_setup(void) { random_setup(); loadbang_setup(); namecanvas_setup(); #ifdef CLOCKHZ cputime_setup(); #endif /* CLOCKHZ */ realtime_setup(); oscparse_setup(); oscformat_setup(); fudiparse_setup(); fudiformat_setup(); } ================================================ FILE: libs/libpd/pure-data/src/x_net.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* network */ #include "m_pd.h" #include "s_stuff.h" #include "s_net.h" #include #include "m_private_utils.h" /* print addrinfo lists for debugging */ /* #define PRINT_ADDRINFO */ /* ----------------------------- helpers ------------------------- */ void socketreceiver_free(t_socketreceiver *x); static void outlet_sockaddr(t_outlet *o, const struct sockaddr *sa) { char addrstr[INET6_ADDRSTRLEN]; unsigned short port = sockaddr_get_port(sa); if (sockaddr_get_addrstr(sa, addrstr, INET6_ADDRSTRLEN)) { t_atom ap[2]; SETSYMBOL(&ap[0], gensym(addrstr)); SETFLOAT(&ap[1], (t_float)port); outlet_list(o, NULL, 2, ap); } } /* ----------------------------- net ------------------------- */ static t_class *netsend_class; typedef struct _netsend { t_object x_obj; t_outlet *x_msgout; t_outlet *x_connectout; t_outlet *x_fromout; int x_sockfd; int x_protocol; int x_bin; t_socketreceiver *x_receiver; struct sockaddr_storage x_server; t_float x_timeout; /* TCP connect timeout in seconds */ } t_netsend; static t_class *netreceive_class; typedef struct _netreceive { t_netsend x_ns; int x_nconnections; int x_sockfd; int *x_connections; int x_old; t_socketreceiver **x_receivers; } t_netreceive; static void netsend_disconnect(t_netsend *x); static void netreceive_notify(t_netreceive *x, int fd); /* ----------------------------- netsend ------------------------- */ static void *netsend_new(t_symbol *s, int argc, t_atom *argv) { t_netsend *x = (t_netsend *)pd_new(netsend_class); outlet_new(&x->x_obj, &s_float); x->x_protocol = SOCK_STREAM; x->x_bin = 0; if (argc && argv->a_type == A_FLOAT) { x->x_protocol = (argv->a_w.w_float != 0 ? SOCK_DGRAM : SOCK_STREAM); argc = 0; } else while (argc && argv->a_type == A_SYMBOL && *argv->a_w.w_symbol->s_name == '-') { if (!strcmp(argv->a_w.w_symbol->s_name, "-b")) x->x_bin = 1; else if (!strcmp(argv->a_w.w_symbol->s_name, "-u")) x->x_protocol = SOCK_DGRAM; else { pd_error(x, "netsend: unknown flag ..."); postatom(argc, argv); endpost(); } argc--; argv++; } if (argc) { pd_error(x, "netsend: extra arguments ignored:"); postatom(argc, argv); endpost(); } x->x_sockfd = -1; x->x_receiver = NULL; x->x_msgout = outlet_new(&x->x_obj, &s_anything); x->x_connectout = NULL; x->x_fromout = NULL; x->x_timeout = 10; memset(&x->x_server, 0, sizeof(struct sockaddr_storage)); return (x); } static void netsend_readbin(t_netsend *x, int fd) { unsigned char *inbuf = sys_getrecvbuf(0); int ret = 0, readbytes = 0, i; struct sockaddr_storage fromaddr = {0}; socklen_t fromaddrlen = sizeof(struct sockaddr_storage); if (!x->x_msgout) { bug("netsend_readbin"); return; } while (1) { if (x->x_protocol == SOCK_DGRAM) ret = (int)recvfrom(fd, inbuf, NET_MAXPACKETSIZE, 0, (struct sockaddr *)&fromaddr, &fromaddrlen); else ret = (int)recv(fd, inbuf, NET_MAXPACKETSIZE, 0); if (ret <= 0) { if (ret < 0) { /* socket_errno_udp() ignores some error codes */ if (x->x_protocol == SOCK_DGRAM && !socket_errno_udp()) return; sys_sockerror("recv (bin)"); } if (x->x_obj.ob_pd == netreceive_class) { /* never close UDP socket because we can't really notify it */ if (x->x_protocol != SOCK_DGRAM) { sys_rmpollfn(fd); sys_closesocket(fd); netreceive_notify((t_netreceive *)x, fd); } } else /* properly shutdown netsend */ netsend_disconnect(x); return; } if (x->x_protocol == SOCK_DGRAM) { t_atom *ap; if (x->x_fromout) outlet_sockaddr(x->x_fromout, (const struct sockaddr *)&fromaddr); /* handle too large UDP packets */ if (ret > NET_MAXPACKETSIZE) { post("warning: incoming UDP packet truncated from %d to %d bytes.", ret, NET_MAXPACKETSIZE); ret = NET_MAXPACKETSIZE; } ap = (t_atom *)alloca(ret * sizeof(t_atom)); for (i = 0; i < ret; i++) SETFLOAT(ap+i, inbuf[i]); outlet_list(x->x_msgout, 0, ret, ap); readbytes += ret; /* throttle */ if (readbytes >= NET_MAXPACKETSIZE) return; /* check for pending UDP packets */ if (socket_bytes_available(fd) <= 0) return; } else { if (x->x_fromout && !getpeername(fd, (struct sockaddr *)&fromaddr, &fromaddrlen)) outlet_sockaddr(x->x_fromout, (const struct sockaddr *)&fromaddr); for (i = 0; i < ret; i++) outlet_float(x->x_msgout, inbuf[i]); return; } } } static void netsend_read(void *z, t_binbuf *b) { t_netsend *x = (t_netsend *)z; int msg, natom = binbuf_getnatom(b); t_atom *at = binbuf_getvec(b); for (msg = 0; msg < natom;) { int emsg; for (emsg = msg; emsg < natom && at[emsg].a_type != A_COMMA && at[emsg].a_type != A_SEMI; emsg++) ; if (emsg > msg) { int i; for (i = msg; i < emsg; i++) if (at[i].a_type == A_DOLLAR || at[i].a_type == A_DOLLSYM) { pd_error(x, "netreceive: got dollar sign in message"); goto nodice; } if (at[msg].a_type == A_FLOAT) { if (emsg > msg + 1) outlet_list(x->x_msgout, 0, emsg-msg, at + msg); else outlet_float(x->x_msgout, at[msg].a_w.w_float); } else if (at[msg].a_type == A_SYMBOL) outlet_anything(x->x_msgout, at[msg].a_w.w_symbol, emsg-msg-1, at + msg + 1); } nodice: msg = emsg + 1; } } static void netsend_notify(void *z, int fd) { t_netsend *x = (t_netsend *)z; if (x->x_sockfd >= 0) { /* sys_rmpollfn() and sys_closesocket() are already called in socketreceiver_read */ x->x_sockfd = -1; if (x->x_receiver) socketreceiver_free(x->x_receiver); x->x_receiver = NULL; memset(&x->x_server, 0, sizeof(struct sockaddr_storage)); outlet_float(x->x_obj.ob_outlet, 0); } } static void netsend_connect(t_netsend *x, t_symbol *s, int argc, t_atom *argv) { int portno, sportno, sockfd, multicast = 0, status; struct addrinfo *ailist = NULL, *ai; const char *hostname = NULL; char hostbuf[256]; /* check argument types */ if ((argc < 2) || argv[0].a_type != A_SYMBOL || argv[1].a_type != A_FLOAT || ((argc > 2) && argv[2].a_type != A_FLOAT)) { pd_error(0, "netsend: bad connect arguments"); return; } hostname = argv[0].a_w.w_symbol->s_name; portno = (int)argv[1].a_w.w_float; sportno = (argc > 2 ? (int)argv[2].a_w.w_float : 0); if (x->x_sockfd >= 0) { pd_error(0, "netsend: already connected"); return; } /* get addrinfo list using hostname & port */ status = addrinfo_get_list(&ailist, hostname, portno, x->x_protocol); if (status != 0) { pd_error(x, "netsend: bad host or port? %s (%d)", gai_strerror(status), status); return; } addrinfo_sort_list(&ailist, addrinfo_ipv4_first); /* IPv4 first! */ #ifdef PRINT_ADDRINFO addrinfo_print_list(ailist); #endif /* try each addr until we find one that works */ for (ai = ailist; ai != NULL; ai = ai->ai_next) { /* create a socket */ sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); #if 0 fprintf(stderr, "send socket %d\n", sockfd); #endif if (sockfd < 0) continue; #if 0 if (socket_set_boolopt(sockfd, SOL_SOCKET, SO_SNDBUF, 0) < 0) post("netsend: setsockopt (SO_RCVBUF) failed"); #endif /* for stream (TCP) sockets, specify "nodelay" */ if (x->x_protocol == SOCK_STREAM) { if (socket_set_boolopt(sockfd, IPPROTO_TCP, TCP_NODELAY, 1) < 0) post("netsend: setsockopt (TCP_NODELAY) failed"); } else /* datagram (UDP) broadcasting */ { if (socket_set_boolopt(sockfd, SOL_SOCKET, SO_BROADCAST, 1) < 0) post("netsend: setsockopt (SO_BROADCAST) failed"); multicast = sockaddr_is_multicast(ai->ai_addr); } /* if this is an IPv6 address, also listen to IPv4 adapters */ if (ai->ai_family == AF_INET6 && socket_set_boolopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, 0) < 0) { /* post("netreceive: setsockopt (IPV6_V6ONLY) failed"); */ } sockaddr_get_addrstr(ai->ai_addr, hostbuf, sizeof(hostbuf)); /* bind optional src listening port */ if (sportno != 0) { int bound = 0; struct addrinfo *sailist = NULL, *sai; logpost(NULL, PD_VERBOSE, "connecting to %s %d, src port %d", hostbuf, portno, sportno); status = addrinfo_get_list(&sailist, NULL, sportno, x->x_protocol); if (status != 0) { pd_error(x, "netsend: could not set src port: %s (%d)", gai_strerror(status), status); freeaddrinfo(sailist); goto connect_fail; } addrinfo_sort_list(&sailist, addrinfo_ipv6_first); /* IPv6 first! */ for (sai = sailist; sai != NULL; sai = sai->ai_next) { /* if this is an IPv6 address, also listen to IPv4 adapters (if not supported, fall back to IPv4) */ if (sai->ai_family == AF_INET6 && socket_set_boolopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, 0) < 0) { /* post("netreceive: setsockopt (IPV6_V6ONLY) failed"); */ continue; } if (bind(sockfd, sai->ai_addr, sai->ai_addrlen) < 0) continue; bound = 1; break; } freeaddrinfo(sailist); if (!bound) { sys_sockerror("setting source port"); goto connect_fail; } } else if (hostname && multicast) logpost(NULL, PD_VERBOSE, "connecting to %s %d (multicast)", hostbuf, portno); else logpost(NULL, PD_VERBOSE, "connecting to %s %d", hostbuf, portno); if (x->x_protocol == SOCK_STREAM) { if (socket_connect(sockfd, ai->ai_addr, ai->ai_addrlen, x->x_timeout) < 0) { sys_sockerror("connecting stream socket"); sys_closesocket(sockfd); freeaddrinfo(ailist); /* output 0 on connection failure so the user can easily retry in a loop */ outlet_float(x->x_obj.ob_outlet, 0); return; } } /* this addr worked */ memcpy(&x->x_server, ai->ai_addr, ai->ai_addrlen); break; } freeaddrinfo(ailist); /* confirm that socket worked */ if (sockfd < 0) { int err = socket_errno(); char buf[MAXPDSTRING]; socket_strerror(err, buf, sizeof(buf)); pd_error(x, "netsend: connect failed: %s (%d)", buf, err); return; } x->x_sockfd = sockfd; if (x->x_msgout) /* add polling function for return messages */ { if (x->x_bin) sys_addpollfn(x->x_sockfd, (t_fdpollfn)netsend_readbin, x); else { t_socketreceiver *y = socketreceiver_new((void *)x, netsend_notify, netsend_read, x->x_protocol == SOCK_DGRAM); sys_addpollfn(x->x_sockfd, (t_fdpollfn)socketreceiver_read, y); x->x_receiver = y; } } outlet_float(x->x_obj.ob_outlet, 1); return; connect_fail: freeaddrinfo(ailist); if (sockfd > 0) sys_closesocket(sockfd); } static void netsend_disconnect(t_netsend *x) { if (x->x_sockfd >= 0) { sys_rmpollfn(x->x_sockfd); sys_closesocket(x->x_sockfd); x->x_sockfd = -1; if (x->x_receiver) socketreceiver_free(x->x_receiver); x->x_receiver = NULL; memset(&x->x_server, 0, sizeof(struct sockaddr_storage)); outlet_float(x->x_obj.ob_outlet, 0); } } static int netsend_dosend(t_netsend *x, int sockfd, int argc, t_atom *argv) { char *buf, *bp; int length, sent, fail = 0; t_binbuf *b = 0; if (x->x_bin) { int i; buf = alloca(argc); for (i = 0; i < argc; i++) ((unsigned char *)buf)[i] = atom_getfloatarg(i, argc, argv); length = argc; } else { t_atom at; b = binbuf_new(); binbuf_add(b, argc, argv); SETSEMI(&at); binbuf_add(b, 1, &at); binbuf_gettext(b, &buf, &length); } for (bp = buf, sent = 0; sent < length;) { static double lastwarntime; static double pleasewarn; double timebefore = sys_getrealtime(), timeafter; int late; int res = 0; if (x->x_protocol == SOCK_DGRAM) { socklen_t addrlen = (x->x_server.ss_family == AF_INET6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)); res = (int)sendto(sockfd, bp, length-sent, 0, (struct sockaddr *)&x->x_server, addrlen); } else res = (int)send(sockfd, bp, length-sent, 0); timeafter = sys_getrealtime(); late = (timeafter - timebefore > 0.005); if (late || pleasewarn) { if (timeafter > lastwarntime + 2) { logpost(NULL, PD_DEBUG, "netsend/netreceive: blocked %d msec", (int)(1000 * ((timeafter - timebefore) + pleasewarn))); pleasewarn = 0; lastwarntime = timeafter; } else if (late) pleasewarn += timeafter - timebefore; } if (res <= 0) { sys_sockerror("send"); fail = 1; break; } else { sent += res; bp += res; } } done: if (!x->x_bin) { t_freebytes(buf, length); binbuf_free(b); } return (fail); } static void netsend_send(t_netsend *x, t_symbol *s, int argc, t_atom *argv) { if (x->x_sockfd >= 0) { if (netsend_dosend(x, x->x_sockfd, argc, argv)) netsend_disconnect(x); } } static void netsend_timeout(t_netsend *x, t_float timeout) { if (timeout >= 0) x->x_timeout = timeout * 0.001; } static void netsend_free(t_netsend *x) { netsend_disconnect(x); } static void netsend_setup(void) { netsend_class = class_new(gensym("netsend"), (t_newmethod)netsend_new, (t_method)netsend_free, sizeof(t_netsend), 0, A_GIMME, 0); class_addmethod(netsend_class, (t_method)netsend_connect, gensym("connect"), A_GIMME, 0); class_addmethod(netsend_class, (t_method)netsend_disconnect, gensym("disconnect"), 0); class_addmethod(netsend_class, (t_method)netsend_send, gensym("send"), A_GIMME, 0); class_addlist(netsend_class, (t_method)netsend_send); class_addmethod(netsend_class, (t_method)netsend_timeout, gensym("timeout"), A_DEFFLOAT, 0); class_sethelpsymbol(netsend_class, gensym("netsend-receive")); } /* ----------------------------- netreceive ------------------------- */ static void netreceive_notify(t_netreceive *x, int fd) { int i; for (i = 0; i < x->x_nconnections; i++) { if (x->x_connections[i] == fd) { memmove(x->x_connections+i, x->x_connections+(i+1), sizeof(int) * (x->x_nconnections - (i+1))); x->x_connections = (int *)t_resizebytes(x->x_connections, x->x_nconnections * sizeof(int), (x->x_nconnections-1) * sizeof(int)); if (x->x_receivers[i]) socketreceiver_free(x->x_receivers[i]); memmove(x->x_receivers+i, x->x_receivers+(i+1), sizeof(t_socketreceiver*) * (x->x_nconnections - (i+1))); x->x_receivers = (t_socketreceiver **)t_resizebytes(x->x_receivers, x->x_nconnections * sizeof(t_socketreceiver*), (x->x_nconnections-1) * sizeof(t_socketreceiver*)); x->x_nconnections--; } } if (x->x_ns.x_connectout) { outlet_float(x->x_ns.x_connectout, x->x_nconnections); } else bug("netreceive_notify"); } /* socketreceiver from sockaddr_in */ static void netreceive_fromaddr(void *z, const void *fromaddr) { t_netreceive *x = (t_netreceive *)z; if (x->x_ns.x_fromout) outlet_sockaddr(x->x_ns.x_fromout, (const struct sockaddr *)fromaddr); } static void netreceive_connectpoll(t_netreceive *x) { int fd = accept(x->x_ns.x_sockfd, 0, 0); if (fd < 0) post("netreceive: accept failed"); else { int nconnections = x->x_nconnections+1; x->x_connections = (int *)t_resizebytes(x->x_connections, x->x_nconnections * sizeof(int), nconnections * sizeof(int)); x->x_connections[x->x_nconnections] = fd; x->x_receivers = (t_socketreceiver **)t_resizebytes(x->x_receivers, x->x_nconnections * sizeof(t_socketreceiver*), nconnections * sizeof(t_socketreceiver*)); x->x_receivers[x->x_nconnections] = NULL; if (x->x_ns.x_bin) sys_addpollfn(fd, (t_fdpollfn)netsend_readbin, x); else { t_socketreceiver *y = socketreceiver_new((void *)x, (t_socketnotifier)netreceive_notify, (x->x_ns.x_msgout ? netsend_read : 0), 0); if (x->x_ns.x_fromout) socketreceiver_set_fromaddrfn(y, (t_socketfromaddrfn)netreceive_fromaddr); sys_addpollfn(fd, (t_fdpollfn)socketreceiver_read, y); x->x_receivers[x->x_nconnections] = y; } outlet_float(x->x_ns.x_connectout, (x->x_nconnections = nconnections)); } } static void netreceive_closeall(t_netreceive *x) { int i; for (i = 0; i < x->x_nconnections; i++) { sys_rmpollfn(x->x_connections[i]); sys_closesocket(x->x_connections[i]); if (x->x_receivers[i]) { socketreceiver_free(x->x_receivers[i]); x->x_receivers[i] = NULL; } } x->x_connections = (int *)t_resizebytes(x->x_connections, x->x_nconnections * sizeof(int), 0); x->x_receivers = (t_socketreceiver**)t_resizebytes(x->x_receivers, x->x_nconnections * sizeof(t_socketreceiver*), 0); x->x_nconnections = 0; if (x->x_ns.x_sockfd >= 0) { sys_rmpollfn(x->x_ns.x_sockfd); sys_closesocket(x->x_ns.x_sockfd); } x->x_ns.x_sockfd = -1; if (x->x_ns.x_receiver) socketreceiver_free(x->x_ns.x_receiver); x->x_ns.x_receiver = NULL; if (x->x_ns.x_connectout) outlet_float(x->x_ns.x_connectout, x->x_nconnections); } static void netreceive_listen(t_netreceive *x, t_symbol *s, int argc, t_atom *argv) { int portno = 0, sockfd, status, protocol = x->x_ns.x_protocol, multicast = 0; struct addrinfo *ailist = NULL, *ai; const char *hostname = NULL; /* allowed or UDP multicast hostname */ netreceive_closeall(x); if (argc && argv->a_type == A_FLOAT) portno = argv->a_w.w_float, argc--, argv++; if (argc && argv->a_type == A_SYMBOL) { hostname = argv->a_w.w_symbol->s_name; argv++; argc--; } if (argc) { pd_error(x, "netreceive: extra arguments ignored:"); postatom(argc, argv); endpost(); } if (portno <= 0) return; status = addrinfo_get_list(&ailist, hostname, portno, protocol); if (status != 0) { pd_error(x, "netreceive: bad host or port? %s (%d)", gai_strerror(status), status); return; } if (hostname) { /* If we specify a hostname or IP address we can only listen to a single adapter. * For host names, we prefer IPv4 for now. LATER we might create several sockets */ addrinfo_sort_list(&ailist, addrinfo_ipv4_first); } else { /* For the "any" address we want to prefer IPv6, so we can create a dual stack socket */ addrinfo_sort_list(&ailist, addrinfo_ipv6_first); } #ifdef PRINT_ADDRINFO addrinfo_print_list(ailist); #endif /* try each addr until we find one that works */ for (ai = ailist; ai != NULL; ai = ai->ai_next) { /* create a socket */ sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (sockfd < 0) continue; #if 0 fprintf(stderr, "receive socket %d\n", sockfd); #endif #if 1 /* ask OS to allow another Pd to reopen this port after we close it */ if (socket_set_boolopt(sockfd, SOL_SOCKET, SO_REUSEADDR, 1) < 0) post("netreceive: setsockopt (SO_REUSEADDR) failed"); #endif #if 0 intarg = 0; if (socket_set_boolopt(sockfd, SOL_SOCKET, SO_RCVBUF, 0) < 0) post("netreceive: setsockopt (SO_RCVBUF) failed"); #endif if (protocol == SOCK_STREAM) { /* stream (TCP) sockets are set NODELAY */ if (socket_set_boolopt(sockfd, IPPROTO_TCP, TCP_NODELAY, 1) < 0) post("netreceive: setsockopt (TCP_NODELAY) failed"); } else if (protocol == SOCK_DGRAM && ai->ai_family == AF_INET) { /* enable IPv4 UDP broadcasting */ if (socket_set_boolopt(sockfd, SOL_SOCKET, SO_BROADCAST, 1) < 0) post("netreceive: setsockopt (SO_BROADCAST) failed"); } /* if this is the IPv6 "any" address, also listen to IPv4 adapters (if not supported, fall back to IPv4) */ if (!hostname && ai->ai_family == AF_INET6 && socket_set_boolopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, 0) < 0) { /* post("netreceive: setsockopt (IPV6_V6ONLY) failed"); */ sys_closesocket(sockfd); sockfd = -1; continue; } multicast = sockaddr_is_multicast(ai->ai_addr); #if 1 if (multicast) { /* binding to the multicast address doesn't work on Windows and on Linux it doesn't seem to work for IPv6 multicast addresses, so we bind to the "any" address instead */ struct addrinfo *any; int status = addrinfo_get_list(&any, (ai->ai_family == AF_INET6) ? "::" : "0.0.0.0", portno, protocol); if (status != 0) { pd_error(x, "netreceive: getting \"any\" address for multicast failed %s (%d)", gai_strerror(status), status); sys_closesocket(sockfd); return; } /* name the socket */ status = bind(sockfd, any->ai_addr, any->ai_addrlen); freeaddrinfo(any); if (status < 0) { sys_closesocket(sockfd); sockfd = -1; continue; } } else #endif { /* name the socket */ if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) { sys_closesocket(sockfd); sockfd = -1; continue; } } /* join multicast group */ if (multicast && socket_join_multicast_group(sockfd, ai->ai_addr) < 0) { int err = socket_errno(); char buf[MAXPDSTRING]; socket_strerror(err, buf, sizeof(buf)); pd_error(x, "netreceive: joining multicast group %s failed: %s (%d)", hostname, buf, err); } /* this addr worked */ if (hostname) { char hostbuf[256]; sockaddr_get_addrstr(ai->ai_addr, hostbuf, sizeof(hostbuf)); logpost(NULL, PD_VERBOSE, "listening on %s %d%s", hostbuf, portno, (multicast ? " (multicast)" : "")); } else logpost(NULL, PD_VERBOSE, "listening on %d", portno); break; } freeaddrinfo(ailist); /* confirm that socket/bind worked */ if (sockfd < 0) { int err = socket_errno(); char buf[MAXPDSTRING]; socket_strerror(err, buf, sizeof(buf)); pd_error(x, "netreceive: listen failed: %s (%d)", buf, err); return; } x->x_ns.x_sockfd = sockfd; if (protocol == SOCK_DGRAM) /* datagram protocol */ { if (x->x_ns.x_bin) sys_addpollfn(x->x_ns.x_sockfd, (t_fdpollfn)netsend_readbin, x); else { /* a UDP receiver doesn't get notifications! */ t_socketreceiver *y = socketreceiver_new(x, 0, (x->x_ns.x_msgout ? netsend_read : 0), 1); if (x->x_ns.x_fromout) socketreceiver_set_fromaddrfn(y, (t_socketfromaddrfn)netreceive_fromaddr); sys_addpollfn(x->x_ns.x_sockfd, (t_fdpollfn)socketreceiver_read, y); x->x_ns.x_connectout = 0; x->x_ns.x_receiver = y; } } else /* streaming protocol */ { if (listen(x->x_ns.x_sockfd, 5) < 0) { sys_sockerror("listen"); sys_closesocket(x->x_ns.x_sockfd); x->x_ns.x_sockfd = -1; } else { sys_addpollfn(x->x_ns.x_sockfd, (t_fdpollfn)netreceive_connectpoll,x); } } } static void netreceive_send(t_netreceive *x, t_symbol *s, int argc, t_atom *argv) { int i; if (x->x_ns.x_protocol != SOCK_STREAM) { pd_error(x, "netreceive: 'send' only works for TCP"); return; } for (i = 0; i < x->x_nconnections; i++) { if (netsend_dosend(&x->x_ns, x->x_connections[i], argc, argv)) pd_error(x, "netreceive: send message failed"); /* should we now close the connection? */ } } static void *netreceive_new(t_symbol *s, int argc, t_atom *argv) { t_netreceive *x = (t_netreceive *)pd_new(netreceive_class); int from = 0; x->x_ns.x_protocol = SOCK_STREAM; x->x_old = 0; x->x_ns.x_bin = 0; x->x_nconnections = 0; x->x_connections = (int *)t_getbytes(0); x->x_receivers = (t_socketreceiver **)t_getbytes(0); x->x_ns.x_sockfd = -1; if (argc && argv->a_type == A_FLOAT) { /* port argument is later passed to netreceive_listen */ x->x_ns.x_protocol = (atom_getfloatarg(1, argc, argv) != 0 ? SOCK_DGRAM : SOCK_STREAM); x->x_old = (!strcmp(atom_getsymbolarg(2, argc, argv)->s_name, "old")); argc = 1; /* don't pass other arguments */ } else { while (argc && argv->a_type == A_SYMBOL && *argv->a_w.w_symbol->s_name == '-') { if (!strcmp(argv->a_w.w_symbol->s_name, "-b")) x->x_ns.x_bin = 1; else if (!strcmp(argv->a_w.w_symbol->s_name, "-u")) x->x_ns.x_protocol = SOCK_DGRAM; else if (!strcmp(argv->a_w.w_symbol->s_name, "-f")) from = 1; else { pd_error(x, "netreceive: unknown flag ..."); postatom(argc, argv); endpost(); } argc--; argv++; } } if (x->x_old) { /* old style, nonsecure version */ x->x_ns.x_msgout = 0; } else x->x_ns.x_msgout = outlet_new(&x->x_ns.x_obj, &s_anything); if (x->x_ns.x_protocol == SOCK_STREAM) x->x_ns.x_connectout = outlet_new(&x->x_ns.x_obj, &s_float); else x->x_ns.x_connectout = 0; if (from) x->x_ns.x_fromout = outlet_new(&x->x_ns.x_obj, &s_symbol); else x->x_ns.x_fromout = NULL; /* create a socket */ netreceive_listen(x, 0, argc, argv); /* pass arguments */ return (x); } static void netreceive_free(t_netreceive *x) { netreceive_closeall(x); } static void netreceive_setup(void) { netreceive_class = class_new(gensym("netreceive"), (t_newmethod)netreceive_new, (t_method)netreceive_free, sizeof(t_netreceive), 0, A_GIMME, 0); class_addmethod(netreceive_class, (t_method)netreceive_listen, gensym("listen"), A_GIMME, 0); class_addmethod(netreceive_class, (t_method)netreceive_send, gensym("send"), A_GIMME, 0); class_addlist(netreceive_class, (t_method)netreceive_send); class_sethelpsymbol(netreceive_class, gensym("netsend-receive")); } void x_net_setup(void) { netsend_setup(); netreceive_setup(); } ================================================ FILE: libs/libpd/pure-data/src/x_scalar.c ================================================ /* Copyright (c) 1997-2013 Miller Puckette and others. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* The "scalar" object. */ #include "m_pd.h" #include "g_canvas.h" #include #include #ifdef HAVE_UNISTD_H #include #endif #ifdef _WIN32 #include #endif t_class *scalar_define_class; static void *scalar_define_new(t_symbol *s, int argc, t_atom *argv) { t_atom a[9]; t_canvas *x, *z = canvas_getcurrent(); t_symbol *templatesym = &s_float, *asym = gensym("#A"); t_template *template; t_scalar *sc; int keep = 0; while (argc && argv->a_type == A_SYMBOL && *argv->a_w.w_symbol->s_name == '-') { if (!strcmp(argv->a_w.w_symbol->s_name, "-k")) keep = 1; else { pd_error(0, "scalar define: unknown flag ..."); postatom(argc, argv); } argc--; argv++; } if (argc && argv->a_type == A_SYMBOL) { templatesym = argv->a_w.w_symbol; argc--; argv++; } if (argc) { post("warning: scalar define ignoring extra argument: "); postatom(argc, argv); } /* make a canvas... */ SETFLOAT(a+0, GLIST_DEFCANVASXLOC); /* xpos */ SETFLOAT(a+1, GLIST_DEFCANVASYLOC); /* ypos */ SETFLOAT(a+2, 600); /* width */ SETFLOAT(a+3, 400); /* height */ SETSYMBOL(a+4, s); SETFLOAT(a+5, 0); x = canvas_new(0, 0, 6, a); x->gl_owner = z; x->gl_private = 0; /* put a scalar in it */ template = template_findbyname(canvas_makebindsym(templatesym)); if (!template) { pd_error(x, "scalar define: couldn't find template %s", templatesym->s_name); goto noscalar; } sc = scalar_new(x, canvas_makebindsym(templatesym)); if (!sc) { pd_error(x, "%s: couldn't create scalar", templatesym->s_name); goto noscalar; } sc->sc_gobj.g_next = 0; x->gl_list = &sc->sc_gobj; x->gl_private = keep; /* bashily unbind #A -- this would create garbage if #A were multiply bound but we believe in this context it's at most bound to whichever text_define or array was created most recently */ asym->s_thing = 0; /* and now bind #A to us to receive following messages in the saved file or copy buffer */ pd_bind(&x->gl_obj.ob_pd, asym); noscalar: pd_this->pd_newest = &x->gl_pd; /* mimic action of canvas_pop() */ pd_popsym(&x->gl_pd); x->gl_loading = 0; /* bash the class to "scalar define" -- see comment in x_array,c */ x->gl_obj.ob_pd = scalar_define_class; outlet_new(&x->gl_obj, &s_pointer); return (x); } /* send a pointer to the scalar to whomever is bound to the symbol */ static void scalar_define_send(t_glist *x, t_symbol *s) { if (!s->s_thing) pd_error(x, "scalar_define_send: %s: no such object", s->s_name); else if (x->gl_list && pd_class(&x->gl_list->g_pd) == scalar_class) { t_gpointer gp; gpointer_init(&gp); gpointer_setglist(&gp, x, (t_scalar *)&x->gl_list->g_pd); pd_pointer(s->s_thing, &gp); gpointer_unset(&gp); } else bug("scalar_define_send"); } static void scalar_define_bang(t_glist *x) { if (x->gl_list && pd_class(&x->gl_list->g_pd) == scalar_class) { t_gpointer gp; gpointer_init(&gp); gpointer_setglist(&gp, x, (t_scalar *)&x->gl_list->g_pd); outlet_pointer(x->gl_obj.ob_outlet, &gp); gpointer_unset(&gp); } else bug("scalar_define_bang"); } /* set to a list, used to restore from scalar_define_save()s below */ static void scalar_define_set(t_glist *x, t_symbol *s, int argc, t_atom *argv) { if (x->gl_list && pd_class(&x->gl_list->g_pd) == scalar_class) { t_binbuf *b = binbuf_new(); int nextmsg = 0, natoms; t_atom *vec; glist_clear(x); binbuf_restore(b, argc, argv); natoms = binbuf_getnatom(b); vec = binbuf_getvec(b); canvas_readscalar(x, natoms, vec, &nextmsg, 0); binbuf_free(b); } else bug("scalar_define_set"); } /* save to a binbuf (for file save or copy) */ static void scalar_define_save(t_gobj *z, t_binbuf *bb) { t_glist *x = (t_glist *)z; binbuf_addv(bb, "ssff", &s__X, gensym("obj"), (t_float)x->gl_obj.te_xpix, (t_float)x->gl_obj.te_ypix); binbuf_addbinbuf(bb, x->gl_obj.ob_binbuf); binbuf_addsemi(bb); if (x->gl_private && x->gl_list && pd_class(&x->gl_list->g_pd) == scalar_class) { t_binbuf *b2 = binbuf_new(); t_scalar *sc = (t_scalar *)(x->gl_list); binbuf_addv(bb, "ss", gensym("#A"), gensym("set")); canvas_writescalar(sc->sc_template, sc->sc_vec, b2, 0); binbuf_addbinbuf(bb, b2); binbuf_addsemi(bb); binbuf_free(b2); } } /* overall creator for "scalar" objects - dispatch to "scalar define" etc */ static void *scalarobj_new(t_symbol *s, int argc, t_atom *argv) { if (!argc || argv[0].a_type != A_SYMBOL) pd_this->pd_newest = scalar_define_new(s, argc, argv); else { const char *str = argv[0].a_w.w_symbol->s_name; if (!strcmp(str, "d") || !strcmp(str, "define")) pd_this->pd_newest = scalar_define_new(s, argc-1, argv+1); else { pd_error(0, "scalar %s: unknown function", str); pd_this->pd_newest = 0; } } return (pd_this->pd_newest); } void canvas_add_for_class(t_class *c); /* ---------------- global setup function -------------------- */ void x_scalar_setup(void) { scalar_define_class = class_new(gensym("scalar define"), 0, (t_method)canvas_free, sizeof(t_canvas), 0, 0); canvas_add_for_class(scalar_define_class); class_addmethod(scalar_define_class, (t_method)scalar_define_send, gensym("send"), A_SYMBOL, 0); class_addbang(scalar_define_class, (t_method)scalar_define_bang); class_addmethod(scalar_define_class, (t_method)scalar_define_set, gensym("set"), A_GIMME, 0); class_sethelpsymbol(scalar_define_class, gensym("scalar-object")); class_setsavefn(scalar_define_class, scalar_define_save); class_addcreator((t_newmethod)scalarobj_new, gensym("scalar"), A_GIMME, 0); } ================================================ FILE: libs/libpd/pure-data/src/x_text.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette and others. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* historically this file defined the qlist and textfile objects - at the moment it also defines "text" but it may later be better to split this off. */ #include "m_pd.h" #include "g_canvas.h" /* just for glist_getfont, bother */ #include #include #define __USE_GNU /* needed so stdlib will define qsort_r */ #include #ifdef HAVE_UNISTD_H #include #endif #ifdef _WIN32 #include #endif static t_class *text_define_class; #include "m_private_utils.h" #define TEXT_NGETBYTE 100 /* bigger that this we use alloc, not alloca */ /* --- unified qsort_r --- */ #if !HAVE_QSORT_R_ARG_LAST && !HAVE_QSORT_R_COMPAR_LAST # undef STUPID_SORT # define STUPID_SORT 1 #endif /* GNU and BSD have incompatible implementations of qsort_r * so we define a few macros to fix that. * STUPID_SORT uses the same calling convention as GNU */ #if HAVE_QSORT_R_COMPAR_LAST /* BSD variant of qsort_r */ # define QSORT_R(base, nmemb, size, compar, arg) \ qsort_r((base), (nmemb), (size), (arg), (compar)) # define TEXT_SORTCOMPARE(z1, z2, zkeyinfo) text_sortcompare(zkeyinfo, z1, z2) #else /* GNU variant of qsort_r */ # define QSORT_R(base, nmemb, size, compar, arg) \ qsort_r((base), (nmemb), (size), (compar), (arg)) # define TEXT_SORTCOMPARE(z1, z2, zkeyinfo) text_sortcompare(z1, z2, zkeyinfo) #endif /* QSORT_R variants */ /* --- common code for text define, textfile, and qlist for storing text -- */ typedef struct _textbuf { t_object b_ob; t_binbuf *b_binbuf; t_canvas *b_canvas; t_guiconnect *b_guiconnect; t_symbol *b_sym; } t_textbuf; static void textbuf_init(t_textbuf *x, t_symbol *sym) { x->b_binbuf = binbuf_new(); x->b_canvas = canvas_getcurrent(); x->b_sym = sym; } static void textbuf_senditup(t_textbuf *x) { int ntxt; char *txt, *buf; if (!x->b_guiconnect) return; #if 0 binbuf_gettext(x->b_binbuf, &txt, &ntxt); buf = getbytes(ntxt+2); memcpy(buf, txt, ntxt); buf[ntxt] = buf[ntxt+1] = 0; /* append a trailing newline, but only if there isn't one there already */ if ('\n' != buf[ntxt-1]) buf[ntxt] = '\n'; pdgui_vmess("pdtk_textwindow_clear", "^", x); pdgui_vmess("pdtk_textwindow_append", "^s", x, buf); freebytes(txt, ntxt); freebytes(buf, ntxt+2); #else /* send the binbuf directly and let the GUI figure out when to do * linebreaks and how to escape special character $1*/ pdgui_vmess("pdtk_textwindow_clear", "^", x); pdgui_vmess("pdtk_textwindow_appendatoms", "^A", x, binbuf_getnatom(x->b_binbuf), binbuf_getvec(x->b_binbuf)); #endif pdgui_vmess("pdtk_textwindow_setdirty", "^i", x, 0); } static void textbuf_open(t_textbuf *x) { if (x->b_guiconnect) { char textid[128]; sprintf(textid, ".x%lx.text", x); pdgui_vmess("wm", "r^", "deiconify", x); pdgui_vmess("raise", "^", x); pdgui_vmess("focus", "s", textid); } else { char buf[40]; sprintf(buf, "%dx%d", 600, 340); pdgui_vmess("pdtk_textwindow_open", "^r si", x, buf, x->b_sym->s_name, sys_hostfontsize(glist_getfont(x->b_canvas), glist_getzoom(x->b_canvas))); sprintf(buf, ".x%lx", x); x->b_guiconnect = guiconnect_new(&x->b_ob.ob_pd, gensym(buf)); textbuf_senditup(x); } } static void textbuf_close(t_textbuf *x) { if (x->b_guiconnect) { pdgui_vmess("pdtk_textwindow_doclose", "^", x); guiconnect_notarget(x->b_guiconnect, 1000); x->b_guiconnect = 0; } } static void textbuf_addline(t_textbuf *b, t_symbol *s, int argc, t_atom *argv) { t_binbuf *z = binbuf_new(); binbuf_restore(z, argc, argv); binbuf_add(b->b_binbuf, binbuf_getnatom(z), binbuf_getvec(z)); binbuf_free(z); } static void textbuf_read(t_textbuf *x, t_symbol *s, int argc, t_atom *argv) { int cr = 0; t_symbol *filename; while (argc && argv->a_type == A_SYMBOL && *argv->a_w.w_symbol->s_name == '-') { if (!strcmp(argv->a_w.w_symbol->s_name, "-c")) cr = 1; else { pd_error(x, "text read: unknown flag ..."); postatom(argc, argv); endpost(); } argc--; argv++; } if (argc && argv->a_type == A_SYMBOL) { filename = argv->a_w.w_symbol; argc--; argv++; } else { pd_error(x, "text read: no file name given"); return; } if (argc) { post("warning: text define ignoring extra argument: "); postatom(argc, argv); endpost(); } if (binbuf_read_via_canvas(x->b_binbuf, filename->s_name, x->b_canvas, cr)) pd_error(x, "%s: read failed", filename->s_name); textbuf_senditup(x); } static void textbuf_write(t_textbuf *x, t_symbol *s, int argc, t_atom *argv) { int cr = 0; t_symbol *filename; char buf[MAXPDSTRING]; while (argc && argv->a_type == A_SYMBOL && *argv->a_w.w_symbol->s_name == '-') { if (!strcmp(argv->a_w.w_symbol->s_name, "-c")) cr = 1; else { pd_error(x, "text write: unknown flag ..."); postatom(argc, argv); endpost(); } argc--; argv++; } if (argc && argv->a_type == A_SYMBOL) { filename = argv->a_w.w_symbol; argc--; argv++; } else { pd_error(x, "text write: no file name given"); return; } if (argc) { post("warning: text define ignoring extra argument: "); postatom(argc, argv); endpost(); } canvas_makefilename(x->b_canvas, filename->s_name, buf, MAXPDSTRING); if (binbuf_write(x->b_binbuf, buf, "", cr)) pd_error(x, "%s: write failed", filename->s_name); } static void textbuf_free(t_textbuf *x) { t_pd *x2; if (x->b_binbuf) binbuf_free(x->b_binbuf); if (x->b_guiconnect) { pdgui_vmess("destroy", "^", x); guiconnect_notarget(x->b_guiconnect, 1000); } /* just in case we're still bound to #A from loading... */ while ((x2 = pd_findbyclass(gensym("#A"), text_define_class))) pd_unbind(x2, gensym("#A")); } /* random helper function to find the nth line in a text buffer */ static int text_nthline(int n, t_atom *vec, int line, int *startp, int *endp) { int i, cnt = 0; for (i = 0; i < n; i++) { if (cnt == line) { int j = i; while (j < n && vec[j].a_type != A_SEMI && vec[j].a_type != A_COMMA) j++; *startp = i; *endp = j; return (1); } else if (vec[i].a_type == A_SEMI || vec[i].a_type == A_COMMA) cnt++; } return (0); } /* text_define object - text buffer, accessible by other accessor objects */ typedef struct _text_define { t_textbuf x_textbuf; t_outlet *x_out; t_outlet *x_notifyout; t_symbol *x_bindsym; t_scalar *x_scalar; /* faux scalar (struct text-scalar) to point to */ t_gpointer x_gp; /* pointer to it */ t_canvas *x_canvas; /* owning canvas whose stub we use for x_gp */ unsigned char x_keep; /* whether to embed contents in patch on save */ } t_text_define; #define x_ob x_textbuf.b_ob #define x_binbuf x_textbuf.b_binbuf #define x_canvas x_textbuf.b_canvas static void *text_define_new(t_symbol *s, int argc, t_atom *argv) { t_text_define *x = (t_text_define *)pd_new(text_define_class); t_symbol *asym = gensym("#A"); x->x_keep = 0; x->x_bindsym = &s_; while (argc && argv->a_type == A_SYMBOL && *argv->a_w.w_symbol->s_name == '-') { if (!strcmp(argv->a_w.w_symbol->s_name, "-k")) x->x_keep = 1; else { pd_error(x, "text define: unknown flag ..."); postatom(argc, argv); endpost(); } argc--; argv++; } if (argc && argv->a_type == A_SYMBOL) { pd_bind(&x->x_ob.ob_pd, argv->a_w.w_symbol); x->x_bindsym = argv->a_w.w_symbol; argc--; argv++; } if (argc) { post("warning: text define ignoring extra argument: "); postatom(argc, argv); endpost(); } textbuf_init(&x->x_textbuf, *x->x_bindsym->s_name ? x->x_bindsym : gensym("text")); /* set up a scalar and a pointer to it that we can output */ x->x_scalar = scalar_new(canvas_getcurrent(), gensym("pd-text")); binbuf_free(x->x_scalar->sc_vec[2].w_binbuf); x->x_scalar->sc_vec[2].w_binbuf = x->x_binbuf; x->x_out = outlet_new(&x->x_ob, &s_pointer); x->x_notifyout = outlet_new(&x->x_ob, 0); gpointer_init(&x->x_gp); x->x_canvas = canvas_getcurrent(); /* bashily unbind #A -- this would create garbage if #A were multiply bound but we believe in this context it's at most bound to whichever text_define or array was created most recently */ asym->s_thing = 0; /* and now bind #A to us to receive following messages in the saved file or copy buffer */ pd_bind(&x->x_ob.ob_pd, asym); return (x); } static void text_define_clear(t_text_define *x) { binbuf_clear(x->x_binbuf); textbuf_senditup(&x->x_textbuf); } /* from g_traversal.c - maybe put in a header? */ t_binbuf *pointertobinbuf(t_pd *x, t_gpointer *gp, t_symbol *s, const char *fname); /* these are unused; they copy text from this object to and from a text field in a scalar. */ static void text_define_frompointer(t_text_define *x, t_gpointer *gp, t_symbol *s) { t_binbuf *b = pointertobinbuf(&x->x_textbuf.b_ob.ob_pd, gp, s, "text_frompointer"); if (b) { binbuf_clear(x->x_textbuf.b_binbuf); binbuf_add(x->x_textbuf.b_binbuf, binbuf_getnatom(b), binbuf_getvec(b)); } } static void text_define_topointer(t_text_define *x, t_gpointer *gp, t_symbol *s) { t_binbuf *b = pointertobinbuf(&x->x_textbuf.b_ob.ob_pd, gp, s, "text_topointer"); if (b) { t_gstub *gs = gp->gp_stub; binbuf_clear(b); binbuf_add(b, binbuf_getnatom(x->x_textbuf.b_binbuf), binbuf_getvec(x->x_textbuf.b_binbuf)); if (gs->gs_which == GP_GLIST) scalar_redraw(gp->gp_un.gp_scalar, gs->gs_un.gs_glist); else { t_array *owner_array = gs->gs_un.gs_array; while (owner_array->a_gp.gp_stub->gs_which == GP_ARRAY) owner_array = owner_array->a_gp.gp_stub->gs_un.gs_array; scalar_redraw(owner_array->a_gp.gp_un.gp_scalar, owner_array->a_gp.gp_stub->gs_un.gs_glist); } } } /* bang: output a pointer to a struct containing this text */ void text_define_bang(t_text_define *x) { gpointer_setglist(&x->x_gp, x->x_canvas, x->x_scalar); outlet_pointer(x->x_out, &x->x_gp); } /* set from a list */ void text_define_set(t_text_define *x, t_symbol *s, int argc, t_atom *argv) { binbuf_clear(x->x_binbuf); binbuf_restore(x->x_binbuf, argc, argv); textbuf_senditup(&x->x_textbuf); } static void text_define_save(t_gobj *z, t_binbuf *bb) { t_text_define *x = (t_text_define *)z; binbuf_addv(bb, "ssff", &s__X, gensym("obj"), (float)x->x_ob.te_xpix, (float)x->x_ob.te_ypix); binbuf_addbinbuf(bb, x->x_ob.ob_binbuf); binbuf_addsemi(bb); if (x->x_keep) { binbuf_addv(bb, "ss", gensym("#A"), gensym("set")); binbuf_addbinbuf(bb, x->x_binbuf); binbuf_addsemi(bb); } obj_saveformat(&x->x_ob, bb); } /* send a pointer to the scalar that owns this text to whomever is bound to the given symbol */ static void text_define_send(t_text_define *x, t_symbol *s) { if (!s->s_thing) pd_error(x, "text_define_send: %s: no such object", s->s_name); else { gpointer_setglist(&x->x_gp, x->x_canvas, x->x_scalar); pd_pointer(s->s_thing, &x->x_gp); } } typedef struct _keyinfo { int ki_forward; /* one if forward, -1 if reversed */ int ki_onset; /* number of fields to skip over */ } t_keyinfo; static int TEXT_SORTCOMPARE(const void *z1, const void *z2, void *zkeyinfo) { const t_atom *a1 = *(t_atom **)z1, *a2 = *(t_atom **)z2; t_keyinfo *k = (t_keyinfo *)zkeyinfo; int count; /* advance first line by key onset and react if we run out early */ for (count = k->ki_onset; count--; a1++) { if (a1->a_type == A_SEMI || a1->a_type == A_COMMA) { /* if second line runs out early too consider them equal */ for (count = k->ki_onset; count--; a2++) if (a2->a_type == A_SEMI || a2->a_type == A_COMMA) goto equal; return (-k->ki_forward); } } for (count = k->ki_onset; count--; a2++) if (a2->a_type == A_SEMI || a2->a_type == A_COMMA) return (-k->ki_forward); /* compare remaining fields */ for (; ; a1++, a2++) { if (a1->a_type == A_SEMI || a1->a_type == A_COMMA) { /* hit end of first line */ if (a2->a_type == A_SEMI || a2->a_type == A_COMMA) goto equal; else return (-k->ki_forward); } else if (a2->a_type == A_SEMI || a2->a_type == A_COMMA) return (k->ki_forward); /* hit end of second line */ /* otherwise if they're different return something, and if not proceed to next field */ else if (a1->a_type == A_FLOAT) { if (a2->a_type == A_FLOAT) { if (a1->a_w.w_float < a2->a_w.w_float) return (-k->ki_forward); else if (a1->a_w.w_float > a2->a_w.w_float) return (k->ki_forward); } else return (-k->ki_forward); } else if (a1->a_type == A_SYMBOL) { if (a2->a_type == A_SYMBOL) { int z = strcmp(a1->a_w.w_symbol->s_name, a2->a_w.w_symbol->s_name); if (z) return (z * k->ki_forward); } else return (k->ki_forward); } } equal: /* ran out of both lines at same time, so we're "equal". in this case compare pointers so that "equal" lines (which might not be identical because of a nonzero onset) stay in the same order as before. */ if (a1 < a2) return (-1); else return (1); } /* 'qsort_r' is a GNU extension and 'qsort_s' is part of C11. * Both are not available in Emscripten, Android or older MSVC versions. * 'stupid_sortcompare' is thread-safe but not reentrant. */ #if STUPID_SORT static PERTHREAD void *stupid_zkeyinfo; static int stupid_sortcompare(const void *z1, const void *z2) { return (text_sortcompare(z1, z2, stupid_zkeyinfo)); } #endif /* sort the contents */ static void text_define_sort(t_text_define *x, t_symbol *s, int argc, t_atom *argv) { int nlines = 0, unique = 0, natom = binbuf_getnatom(x->x_binbuf), i, thisline, startline; t_atom *vec = binbuf_getvec(x->x_binbuf), **sortbuf, *a1, *a2; t_binbuf *newb; t_keyinfo k; k.ki_forward = 1; k.ki_onset = 0; while (argc && argv->a_type == A_SYMBOL && *argv->a_w.w_symbol->s_name == '-') { if (!strcmp(argv->a_w.w_symbol->s_name, "-u")) unique = 1; else if (!strcmp(argv->a_w.w_symbol->s_name, "-r")) k.ki_forward = -1; else if (!strcmp(argv->a_w.w_symbol->s_name, "-k") && argc > 1 && argv[1].a_type == A_FLOAT) { if ((k.ki_onset = argv[1].a_w.w_float) < 0) k.ki_onset = 0; argc--; argv++; } else { pd_error(x, "text define sort: unknown flag ..."); postatom(argc, argv); endpost(); } argc--; argv++; } if (argc) { post("warning: text define sort ignoring extra argument: "); postatom(argc, argv); endpost(); } if (!natom) return; /* last thing in buffer should be a terminator */ if (vec[natom-1].a_type != A_SEMI && vec[natom-1].a_type != A_COMMA) binbuf_addsemi(x->x_binbuf), vec = binbuf_getvec(x->x_binbuf), natom = binbuf_getnatom(x->x_binbuf), nlines++; for (i = nlines = 0; i < natom; i++) if (vec[i].a_type == A_SEMI || vec[i].a_type == A_COMMA) nlines++; sortbuf = (t_atom **)getbytes(nlines * sizeof(*sortbuf)); for (i = thisline = 0, startline = 1; i < natom; i++) { if (startline) { if (thisline >= nlines) bug("text_define_sort"); sortbuf[thisline++] = vec+i; } startline = (vec[i].a_type == A_SEMI || vec[i].a_type == A_COMMA); } #if STUPID_SORT stupid_zkeyinfo = &k; qsort(sortbuf, nlines, sizeof(*sortbuf), stupid_sortcompare); #else QSORT_R(sortbuf, nlines, sizeof(*sortbuf), text_sortcompare, &k); #endif /* STUPID_SORT */ newb = binbuf_new(); for (thisline = 0; thisline < nlines; thisline++) { if (unique && thisline > 0) /* check for duplicates */ { for (a1 = sortbuf[thisline-1], a2 = sortbuf[thisline]; ; a1++, a2++) { if (a1->a_type == A_SEMI || a1->a_type == A_COMMA) { if (a1->a_type == a2->a_type) goto skipit; /* duplicate line, don't copy */ else goto doit; } else if (a1->a_type != a2->a_type || (a1->a_type == A_FLOAT && a1->a_w.w_float != a2->a_w.w_float) || (a1->a_type == A_SYMBOL && a1->a_w.w_symbol != a2->a_w.w_symbol)) goto doit; } } doit: for (i = 0, a1 = sortbuf[thisline]; a1->a_type != A_SEMI && a1->a_type != A_COMMA; i++, a1++) ; binbuf_add(newb, i+1, sortbuf[thisline]); skipit: ; } binbuf_free(x->x_binbuf); x->x_scalar->sc_vec[2].w_binbuf = x->x_binbuf = newb; freebytes(sortbuf, nlines * sizeof(*sortbuf)); textbuf_senditup(&x->x_textbuf); } /* notification from GUI that we've been updated */ static void text_define_notify(t_text_define *x) { outlet_anything(x->x_notifyout, gensym("updated"), 0, 0); textbuf_senditup(&x->x_textbuf); } static void text_define_free(t_text_define *x) { x->x_binbuf = 0; /* prevent double deletion */ textbuf_free(&x->x_textbuf); if (x->x_bindsym != &s_) pd_unbind(&x->x_ob.ob_pd, x->x_bindsym); gpointer_unset(&x->x_gp); /* deleting the scalar will automatically free the binbuf */ pd_free(&x->x_scalar->sc_gobj.g_pd); x->x_canvas->gl_valid = ++glist_valid; /* invalidate pointers */ } /* --- text_client - common code for objects that refer to text buffers -- */ typedef struct _text_client { t_object tc_obj; t_symbol *tc_sym; t_gpointer tc_gp; t_symbol *tc_struct; t_symbol *tc_field; } t_text_client; /* parse buffer-finding arguments */ static void text_client_argparse(t_text_client *x, int *argcp, t_atom **argvp, char *name) { int argc = *argcp; t_atom *argv = *argvp; x->tc_sym = x->tc_struct = x->tc_field = 0; gpointer_init(&x->tc_gp); if (argc && argv->a_type == A_SYMBOL && !strcmp(argv->a_w.w_symbol->s_name, "-s")) { if (argc < 3 || argv[1].a_type != A_SYMBOL || argv[2].a_type != A_SYMBOL) pd_error(x, "%s: '-s' needs a struct and field name", name); else { x->tc_struct = canvas_makebindsym(argv[1].a_w.w_symbol); x->tc_field = argv[2].a_w.w_symbol; argc -= 3; argv += 3; } } else if (argc && argv->a_type == A_SYMBOL) { x->tc_sym = argv->a_w.w_symbol; argc--; argv++; } *argcp = argc; *argvp = argv; } /* find the binbuf for this object. This should be reusable for other objects. Prints an error message and returns 0 on failure. */ static t_binbuf *text_client_getbuf(t_text_client *x) { if (x->tc_sym) /* named text object */ { t_textbuf *y = (t_textbuf *)pd_findbyclass(x->tc_sym, text_define_class); if (y) return (y->b_binbuf); else { pd_error(x, "text: couldn't find text buffer '%s'", x->tc_sym->s_name); return (0); } } else if (x->tc_struct) /* by pointer */ { t_template *template = template_findbyname(x->tc_struct); t_gstub *gs = x->tc_gp.gp_stub; t_word *vec; int onset, type; t_symbol *arraytype; if (!template) { pd_error(x, "text: couldn't find struct %s", x->tc_struct->s_name); return (0); } if (!gpointer_check(&x->tc_gp, 0)) { pd_error(x, "text: stale or empty pointer"); return (0); } if (gs->gs_which == GP_ARRAY) vec = x->tc_gp.gp_un.gp_w; else vec = x->tc_gp.gp_un.gp_scalar->sc_vec; if (!template_find_field(template, x->tc_field, &onset, &type, &arraytype)) { pd_error(x, "text: no field named %s", x->tc_field->s_name); return (0); } if (type != DT_TEXT) { pd_error(x, "text: field %s not of type text", x->tc_field->s_name); return (0); } return (*(t_binbuf **)(((char *)vec) + onset)); } else return (0); /* shouldn't happen */ } static void text_client_senditup(t_text_client *x) { if (x->tc_sym) /* named text object */ { t_textbuf *y = (t_textbuf *)pd_findbyclass(x->tc_sym, text_define_class); if (y) textbuf_senditup(y); else bug("text_client_senditup"); } else if (x->tc_struct) /* by pointer */ { t_template *template = template_findbyname(x->tc_struct); t_gstub *gs = x->tc_gp.gp_stub; if (!template) { pd_error(x, "text: couldn't find struct %s", x->tc_struct->s_name); return; } if (!gpointer_check(&x->tc_gp, 0)) { pd_error(x, "text: stale or empty pointer"); return; } if (gs->gs_which == GP_GLIST) scalar_redraw(x->tc_gp.gp_un.gp_scalar, gs->gs_un.gs_glist); else { t_array *owner_array = gs->gs_un.gs_array; while (owner_array->a_gp.gp_stub->gs_which == GP_ARRAY) owner_array = owner_array->a_gp.gp_stub->gs_un.gs_array; scalar_redraw(owner_array->a_gp.gp_un.gp_scalar, owner_array->a_gp.gp_stub->gs_un.gs_glist); } } } static void text_client_free(t_text_client *x) { gpointer_unset(&x->tc_gp); } /* ------- text_get object - output all or part of nth lines ----------- */ t_class *text_get_class; typedef struct _text_get { t_text_client x_tc; t_outlet *x_out1; /* list */ t_outlet *x_out2; /* 1 if comma terminated, 0 if semi, 2 if none */ t_float x_f1; /* field number, -1 for whole line */ t_float x_f2; /* number of fields */ } t_text_get; #define x_obj x_tc.tc_obj #define x_sym x_tc.tc_sym #define x_gp x_tc.tc_gp #define x_struct x_tc.tc_struct #define x_field x_tc.tc_field static void *text_get_new(t_symbol *s, int argc, t_atom *argv) { t_text_get *x = (t_text_get *)pd_new(text_get_class); x->x_out1 = outlet_new(&x->x_obj, &s_list); x->x_out2 = outlet_new(&x->x_obj, &s_float); floatinlet_new(&x->x_obj, &x->x_f1); floatinlet_new(&x->x_obj, &x->x_f2); x->x_f1 = -1; x->x_f2 = 1; text_client_argparse(&x->x_tc, &argc, &argv, "text get"); if (argc) { if (argv->a_type == A_FLOAT) x->x_f1 = argv->a_w.w_float; else { post("text get: can't understand field number"); postatom(argc, argv); endpost(); } argc--; argv++; } if (argc) { if (argv->a_type == A_FLOAT) x->x_f2 = argv->a_w.w_float; else { post("text get: can't understand field count"); postatom(argc, argv); endpost(); } argc--; argv++; } if (argc) { post("warning: text get ignoring extra argument: "); postatom(argc, argv); endpost(); } if (x->x_struct) pointerinlet_new(&x->x_obj, &x->x_gp); else symbolinlet_new(&x->x_obj, &x->x_tc.tc_sym); return (x); } static void text_get_float(t_text_get *x, t_floatarg f) { t_binbuf *b = text_client_getbuf(&x->x_tc); int start, end, n, startfield, nfield; t_atom *vec; if (!b) return; vec = binbuf_getvec(b); n = binbuf_getnatom(b); startfield = x->x_f1; nfield = x->x_f2; if (text_nthline(n, vec, f, &start, &end)) { int outc = end - start, k; t_atom *outv; if (x->x_f1 < 0) /* negative start field for whole line */ { /* tell us what terminated the line (semi or comma) */ outlet_float(x->x_out2, (end < n && vec[end].a_type == A_COMMA)); ALLOCA(t_atom, outv, outc, TEXT_NGETBYTE); for (k = 0; k < outc; k++) outv[k] = vec[start+k]; outlet_list(x->x_out1, 0, outc, outv); FREEA(t_atom, outv, outc, TEXT_NGETBYTE); } else if (startfield + nfield > outc) pd_error(x, "text get: field request (%d %d) out of range", startfield, nfield); else if (nfield < 0) pd_error(x, "text get: bad field count (%d)", nfield); else { ALLOCA(t_atom, outv, nfield, TEXT_NGETBYTE); for (k = 0; k < nfield; k++) outv[k] = vec[(start+startfield)+k]; outlet_list(x->x_out1, 0, nfield, outv); FREEA(t_atom, outv, nfield, TEXT_NGETBYTE); } } else if (x->x_f1 < 0) /* whole line but out of range: empty list and 2 */ { outlet_float(x->x_out2, 2); /* 2 for out of range */ outlet_list(x->x_out1, 0, 0, 0); /* ... and empty list */ } } /* --------- text_set object - replace all or part of nth line ----------- */ typedef struct _text_set { t_text_client x_tc; t_float x_f1; /* line number */ t_float x_f2; /* field number, -1 for whole line */ } t_text_set; t_class *text_set_class; static void *text_set_new(t_symbol *s, int argc, t_atom *argv) { t_text_set *x = (t_text_set *)pd_new(text_set_class); floatinlet_new(&x->x_obj, &x->x_f1); floatinlet_new(&x->x_obj, &x->x_f2); x->x_f1 = 0; x->x_f2 = -1; text_client_argparse(&x->x_tc, &argc, &argv, "text set"); if (argc) { if (argv->a_type == A_FLOAT) x->x_f1 = argv->a_w.w_float; else { post("text set: can't understand line number"); postatom(argc, argv); endpost(); } argc--; argv++; } if (argc) { if (argv->a_type == A_FLOAT) x->x_f2 = argv->a_w.w_float; else { post("text set: can't understand field number"); postatom(argc, argv); endpost(); } argc--; argv++; } if (argc) { post("warning: text set ignoring extra argument: "); postatom(argc, argv); endpost(); } if (x->x_struct) pointerinlet_new(&x->x_obj, &x->x_gp); else symbolinlet_new(&x->x_obj, &x->x_tc.tc_sym); return (x); } static void text_set_list(t_text_set *x, t_symbol *s, int argc, t_atom *argv) { t_binbuf *b = text_client_getbuf(&x->x_tc); int start, end, n, fieldno = x->x_f2, i, /* check for overflow in this conversion: */ lineno = (x->x_f1 > (double)0x7fffffff ? 0x7fffffff : (int)x->x_f1); t_atom *vec; if (!b) return; vec = binbuf_getvec(b); n = binbuf_getnatom(b); if (lineno < 0) { pd_error(x, "text set: line number (%d) < 0", lineno); return; } if (text_nthline(n, vec, lineno, &start, &end)) { if (fieldno < 0) { if (end - start != argc) /* grow or shrink */ { int oldn = n; n = n + (argc - (end-start)); if (n > oldn) (void)binbuf_resize(b, n); vec = binbuf_getvec(b); memmove(&vec[start + argc], &vec[end], sizeof(*vec) * (oldn - end)); if (n < oldn) { (void)binbuf_resize(b, n); vec = binbuf_getvec(b); } } } else { if (fieldno >= end - start) { pd_error(x, "text set: field number (%d) past end of line", fieldno); return; } if (fieldno + argc > end - start) argc = (end - start) - fieldno; start += fieldno; } } else if (fieldno < 0) /* if line number too high just append to end */ { int addsemi = (n && vec[n-1].a_type != A_SEMI && vec[n-1].a_type != A_COMMA), newsize = n + addsemi + argc + 1; (void)binbuf_resize(b, newsize); vec = binbuf_getvec(b); if (addsemi) SETSEMI(&vec[n]); SETSEMI(&vec[newsize-1]); start = n+addsemi; } else { post("text set: %d: line number out of range", lineno); return; } for (i = 0; i < argc; i++) { if (argv[i].a_type == A_POINTER) SETSYMBOL(&vec[start+i], gensym("(pointer)")); else vec[start+i] = argv[i]; } text_client_senditup(&x->x_tc); } /* --------- text_insert object - insert a line ----------- */ typedef struct _text_insert { t_text_client x_tc; t_float x_f1; /* line number */ } t_text_insert; t_class *text_insert_class; static void *text_insert_new(t_symbol *s, int argc, t_atom *argv) { t_text_insert *x = (t_text_insert *)pd_new(text_insert_class); floatinlet_new(&x->x_obj, &x->x_f1); x->x_f1 = 0; text_client_argparse(&x->x_tc, &argc, &argv, "text insert"); if (argc) { if (argv->a_type == A_FLOAT) x->x_f1 = argv->a_w.w_float; else { post("text insert: can't understand line number"); postatom(argc, argv); endpost(); } argc--; argv++; } if (argc) { post("warning: text insert ignoring extra argument: "); postatom(argc, argv); endpost(); } if (x->x_struct) pointerinlet_new(&x->x_obj, &x->x_gp); else symbolinlet_new(&x->x_obj, &x->x_tc.tc_sym); return (x); } static void text_insert_list(t_text_insert *x, t_symbol *s, int argc, t_atom *argv) { t_binbuf *b = text_client_getbuf(&x->x_tc); int start, end, n, nwas, i, lineno = (x->x_f1 > (double)0x7fffffff ? 0x7fffffff : (int)x->x_f1); t_atom *vec; if (!b) return; if (lineno < 0) { pd_error(x, "text insert: line number (%d) < 0", lineno); return; } nwas = binbuf_getnatom(b); if (!text_nthline(nwas, binbuf_getvec(b), lineno, &start, &end)) start = nwas; (void)binbuf_resize(b, (n = nwas + argc + 1)); vec = binbuf_getvec(b); if (start < n) memmove(&vec[start+(argc+1)], &vec[start], sizeof(*vec) * (nwas-start)); for (i = 0; i < argc; i++) { if (argv[i].a_type == A_POINTER) SETSYMBOL(&vec[start+i], gensym("(pointer)")); else vec[start+i] = argv[i]; } SETSEMI(&vec[start+argc]); text_client_senditup(&x->x_tc); } /* --------- text_delete object - delete nth line ----------- */ typedef struct _text_delete { t_text_client x_tc; } t_text_delete; t_class *text_delete_class; static void *text_delete_new(t_symbol *s, int argc, t_atom *argv) { t_text_delete *x = (t_text_delete *)pd_new(text_delete_class); text_client_argparse(&x->x_tc, &argc, &argv, "text delete"); if (argc) { post("warning: text delete ignoring extra argument: "); postatom(argc, argv); endpost(); } if (x->x_struct) pointerinlet_new(&x->x_obj, &x->x_gp); else symbolinlet_new(&x->x_obj, &x->x_tc.tc_sym); return (x); } static void text_delete_float(t_text_delete *x, t_floatarg f) { t_binbuf *b = text_client_getbuf(&x->x_tc); int start, end, n, lineno = (f > (double)0x7fffffff ? 0x7fffffff : f); t_atom *vec; if (!b) return; vec = binbuf_getvec(b); n = binbuf_getnatom(b); if (lineno < 0) binbuf_clear(b); else if (text_nthline(n, vec, lineno, &start, &end)) { if (end < n) end++; memmove(&vec[start], &vec[end], sizeof(*vec) * (n - end)); (void)binbuf_resize(b, n - (end - start)); } else { post("text delete: %d: line number out of range", lineno); return; } text_client_senditup(&x->x_tc); } /* ---------------- text_size object - output number of lines -------------- */ t_class *text_size_class; typedef struct _text_size { t_text_client x_tc; t_outlet *x_out1; /* float */ } t_text_size; static void *text_size_new(t_symbol *s, int argc, t_atom *argv) { t_text_size *x = (t_text_size *)pd_new(text_size_class); x->x_out1 = outlet_new(&x->x_obj, &s_float); text_client_argparse(&x->x_tc, &argc, &argv, "text size"); if (argc) { post("warning: text size ignoring extra argument: "); postatom(argc, argv); endpost(); } if (x->x_struct) pointerinlet_new(&x->x_obj, &x->x_gp); else symbolinlet_new(&x->x_obj, &x->x_tc.tc_sym); return (x); } static void text_size_bang(t_text_size *x) { t_binbuf *b = text_client_getbuf(&x->x_tc); int n, i, cnt = 0; t_atom *vec; if (!b) return; vec = binbuf_getvec(b); n = binbuf_getnatom(b); for (i = 0; i < n; i++) { if (vec[i].a_type == A_SEMI || vec[i].a_type == A_COMMA) cnt++; } if (n && vec[n-1].a_type != A_SEMI && vec[n-1].a_type != A_COMMA) cnt++; outlet_float(x->x_out1, cnt); } static void text_size_float(t_text_size *x, t_floatarg f) { t_binbuf *b = text_client_getbuf(&x->x_tc); int start, end, n; t_atom *vec; if (!b) return; vec = binbuf_getvec(b); n = binbuf_getnatom(b); if (text_nthline(n, vec, f, &start, &end)) outlet_float(x->x_out1, end-start); else outlet_float(x->x_out1, -1); } /* ---------------- text_tolist object - output text as a list ----------- */ t_class *text_tolist_class; #define t_text_tolist t_text_client static void *text_tolist_new(t_symbol *s, int argc, t_atom *argv) { t_text_tolist *x = (t_text_tolist *)pd_new(text_tolist_class); outlet_new(&x->tc_obj, &s_list); text_client_argparse(x, &argc, &argv, "text tolist"); if (argc) { post("warning: text tolist ignoring extra argument: "); postatom(argc, argv); endpost(); } if (x->tc_struct) pointerinlet_new(&x->tc_obj, &x->tc_gp); else symbolinlet_new(&x->tc_obj, &x->tc_sym); return (x); } static void text_tolist_bang(t_text_tolist *x) { t_binbuf *b = text_client_getbuf(x), *b2; if (!b) return; b2 = binbuf_new(); binbuf_addbinbuf(b2, b); outlet_list(x->tc_obj.ob_outlet, 0, binbuf_getnatom(b2), binbuf_getvec(b2)); binbuf_free(b2); } /* ------------- text_fromlist object - set text from a list -------- */ t_class *text_fromlist_class; #define t_text_fromlist t_text_client static void *text_fromlist_new(t_symbol *s, int argc, t_atom *argv) { t_text_fromlist *x = (t_text_fromlist *)pd_new(text_fromlist_class); text_client_argparse(x, &argc, &argv, "text fromlist"); if (argc) { post("warning: text fromlist ignoring extra argument: "); postatom(argc, argv); endpost(); } if (x->tc_struct) pointerinlet_new(&x->tc_obj, &x->tc_gp); else symbolinlet_new(&x->tc_obj, &x->tc_sym); return (x); } static void text_fromlist_list(t_text_fromlist *x, t_symbol *s, int argc, t_atom *argv) { t_binbuf *b = text_client_getbuf(x); if (!b) return; binbuf_clear(b); binbuf_restore(b, argc, argv); text_client_senditup(x); } /* ---- text_search object - output index of line(s) matching criteria ---- */ t_class *text_search_class; /* relations we can test when searching: */ #define KB_EQ 0 /* equal */ #define KB_GT 1 /* > (etc..) */ #define KB_GE 2 #define KB_LT 3 #define KB_LE 4 #define KB_NEAR 5 /* anything matches but closer is better */ typedef struct _key { int k_field; int k_binop; } t_key; typedef struct _text_search { t_text_client x_tc; t_outlet *x_out1; /* line indices */ int x_nkeys; int x_onset; /* first line to include in search */ int x_range; /* max number of lines to search */ t_key *x_keyvec; } t_text_search; static void *text_search_new(t_symbol *s, int argc, t_atom *argv) { t_text_search *x = (t_text_search *)pd_new(text_search_class); int i, key, nkey, nextop; x->x_out1 = outlet_new(&x->x_obj, &s_list); text_client_argparse(&x->x_tc, &argc, &argv, "text search"); for (i = nkey = 0; i < argc; i++) if (argv[i].a_type == A_FLOAT) nkey++; if (nkey == 0) nkey = 1; x->x_nkeys = nkey; x->x_onset = 0; x->x_range = 0x7fffffff; x->x_keyvec = (t_key *)getbytes(nkey * sizeof(*x->x_keyvec)); if (!argc) x->x_keyvec[0].k_field = 0, x->x_keyvec[0].k_binop = KB_EQ; else for (i = key = 0, nextop = -1; i < argc; i++) { if (argv[i].a_type == A_FLOAT) { x->x_keyvec[key].k_field = (argv[i].a_w.w_float > 0 ? argv[i].a_w.w_float : 0); x->x_keyvec[key].k_binop = (nextop >= 0 ? nextop : KB_EQ); nextop = -1; key++; } else { const char *s = argv[i].a_w.w_symbol->s_name; if (nextop >= 0) pd_error(x, "text search: extra operation argument ignored: %s", s); else if (!strcmp(argv[i].a_w.w_symbol->s_name, ">")) nextop = KB_GT; else if (!strcmp(argv[i].a_w.w_symbol->s_name, ">=")) nextop = KB_GE; else if (!strcmp(argv[i].a_w.w_symbol->s_name, "<")) nextop = KB_LT; else if (!strcmp(argv[i].a_w.w_symbol->s_name, "<=")) nextop = KB_LE; else if (!strcmp(argv[i].a_w.w_symbol->s_name, "near")) nextop = KB_NEAR; else pd_error(x, "text search: unknown operation argument: %s", s); } } if (x->x_struct) pointerinlet_new(&x->x_obj, &x->x_gp); else symbolinlet_new(&x->x_obj, &x->x_tc.tc_sym); return (x); } static void text_search_list(t_text_search *x, t_symbol *s, int argc, t_atom *argv) { t_binbuf *b = text_client_getbuf(&x->x_tc); int i, n, lineno, bestline = -1, beststart=-1, bestn, thisstart, nkeys = x->x_nkeys, failed = 0; t_atom *vec; if (!b) return; if (argc < nkeys) { pd_error(x, "need %d keys, only got %d in list", nkeys, argc); } vec = binbuf_getvec(b); n = binbuf_getnatom(b); if (nkeys < 1) bug("text_search"); for (i = lineno = thisstart = 0; i < n; i++) { if (vec[i].a_type == A_SEMI || vec[i].a_type == A_COMMA || i == n-1) { int thisn, j, field, binop; if (lineno < x->x_onset) goto nomatch; if (lineno >= x->x_onset + x->x_range) break; thisn = i - thisstart; field = x->x_keyvec[0].k_field; binop = x->x_keyvec[0].k_binop; /* do we match? */ for (j = 0; j < argc; ) { if (field >= thisn || vec[thisstart+field].a_type != argv[j].a_type) goto nomatch; if (argv[j].a_type == A_FLOAT) /* arg is a float */ { switch (binop) { case KB_EQ: if (vec[thisstart+field].a_w.w_float != argv[j].a_w.w_float) goto nomatch; break; case KB_GT: if (vec[thisstart+field].a_w.w_float <= argv[j].a_w.w_float) goto nomatch; break; case KB_GE: if (vec[thisstart+field].a_w.w_float < argv[j].a_w.w_float) goto nomatch; break; case KB_LT: if (vec[thisstart+field].a_w.w_float >= argv[j].a_w.w_float) goto nomatch; break; case KB_LE: if (vec[thisstart+field].a_w.w_float > argv[j].a_w.w_float) goto nomatch; break; /* the other possibility ('near') never fails */ } } else /* arg is a symbol */ { if (binop != KB_EQ) { if (!failed) { pd_error(x, "text search (%s): only exact matches allowed for symbols", argv[j].a_w.w_symbol->s_name); failed = 1; } goto nomatch; } if (vec[thisstart+field].a_w.w_symbol != argv[j].a_w.w_symbol) goto nomatch; } if (++j >= nkeys) /* if at last key just increment field */ field++; else field = x->x_keyvec[j].k_field, /* else next key */ binop = x->x_keyvec[j].k_binop; } /* the line matches. Now, if there is a previous match, are we better than it? */ if (bestline >= 0) { field = x->x_keyvec[0].k_field; binop = x->x_keyvec[0].k_binop; for (j = 0; j < argc; ) { if (field >= thisn || vec[thisstart+field].a_type != argv[j].a_type) bug("text search 2"); if (argv[j].a_type == A_FLOAT) /* arg is a float */ { float thisv = vec[thisstart+field].a_w.w_float, bestv = (beststart >= 0 ? vec[beststart+field].a_w.w_float : -1e20); switch (binop) { case KB_GT: case KB_GE: if (thisv < bestv) goto replace; else if (thisv > bestv) goto nomatch; break; case KB_LT: case KB_LE: if (thisv > bestv) goto replace; else if (thisv < bestv) goto nomatch; break; case KB_NEAR: if (thisv >= argv[j].a_w.w_float && bestv >= argv[j].a_w.w_float) { if (thisv < bestv) goto replace; else if (thisv > bestv) goto nomatch; } else if (thisv <= argv[j].a_w.w_float && bestv <= argv[j].a_w.w_float) { if (thisv > bestv) goto replace; else if (thisv < bestv) goto nomatch; } else { float d1 = thisv - argv[j].a_w.w_float, d2 = bestv - argv[j].a_w.w_float; if (d1 < 0) d1 = -d1; if (d2 < 0) d2 = -d2; if (d1 < d2) goto replace; else if (d1 > d2) goto nomatch; } break; /* the other possibility ('=') never decides */ } } if (++j >= nkeys) /* last key - increment field */ field++; else field = x->x_keyvec[j].k_field, /* else next key */ binop = x->x_keyvec[j].k_binop; } goto nomatch; /* a tie - keep the old one */ replace: bestline = lineno, beststart = thisstart, bestn = thisn; } /* no previous match so we're best */ else bestline = lineno, beststart = thisstart, bestn = thisn; nomatch: lineno++; thisstart = i+1; } } outlet_float(x->x_out1, bestline); } static void text_search_range(t_text_search *x, t_floatarg onset, t_floatarg range) { x->x_onset = (onset >= 0x7fffffff ? 0x7ffffff : (onset < 0 ? 0 : onset)); x->x_range = (range >= 0x7fffffff ? 0x7ffffff : (range < 0 ? 0 : range)); } /* ---------------- text_sequence object - sequencer ----------- */ t_class *text_sequence_class; typedef struct _text_sequence { t_text_client x_tc; t_outlet *x_mainout; /* outlet for lists, zero if "global" */ t_outlet *x_waitout; /* outlet for wait times, zero if we never wait */ t_outlet *x_endout; /* bang when hit end */ int x_onset; int x_argc; t_atom *x_argv; t_symbol *x_waitsym; /* symbol to initiate wait, zero if none */ int x_waitargc; /* how many leading numbers to use for waiting */ t_clock *x_clock; /* callback for auto mode */ t_float x_nextdelay; t_symbol *x_lastto; /* destination symbol if we're after a comma */ unsigned char x_eaten; /* true if we've eaten leading numbers already */ unsigned char x_loop; /* true if we can send multiple lines */ unsigned char x_auto; /* set timer when we hit single-number time list */ } t_text_sequence; static void text_sequence_tick(t_text_sequence *x); static void text_sequence_tempo(t_text_sequence *x, t_symbol *unitname, t_floatarg tempo); void parsetimeunits(void *x, t_float amount, t_symbol *unitname, t_float *unit, int *samps); /* time unit parsing from x_time.c */ static void *text_sequence_new(t_symbol *s, int argc, t_atom *argv) { t_text_sequence *x = (t_text_sequence *)pd_new(text_sequence_class); int global = 0; text_client_argparse(&x->x_tc, &argc, &argv, "text sequence"); x->x_waitsym = 0; x->x_waitargc = 0; x->x_eaten = 0; x->x_loop = 0; x->x_lastto = 0; x->x_clock = clock_new(x, (t_method)text_sequence_tick); while (argc && argv->a_type == A_SYMBOL && *argv->a_w.w_symbol->s_name == '-') { if (!strcmp(argv->a_w.w_symbol->s_name, "-w") && argc >= 2) { if (argv[1].a_type == A_SYMBOL) { x->x_waitsym = argv[1].a_w.w_symbol; x->x_waitargc = 0; } else { x->x_waitsym = 0; if ((x->x_waitargc = argv[1].a_w.w_float) < 0) x->x_waitargc = 0; } argc -= 1; argv += 1; } else if (!strcmp(argv->a_w.w_symbol->s_name, "-g")) global = 1; else if (!strcmp(argv->a_w.w_symbol->s_name, "-t") && argc >= 3) { text_sequence_tempo(x, atom_getsymbolarg(2, argc, argv), atom_getfloatarg(1, argc, argv)); argc -= 2; argv += 2; } else { pd_error(x, "text sequence: unknown flag '%s'...", argv->a_w.w_symbol->s_name); } argc--; argv++; } if (argc) { post("warning: text sequence ignoring extra argument: "); postatom(argc, argv); endpost(); } if (x->x_tc.tc_struct) pointerinlet_new(&x->x_tc.tc_obj, &x->x_tc.tc_gp); else symbolinlet_new(&x->x_tc.tc_obj, &x->x_tc.tc_sym); x->x_argc = 0; x->x_argv = (t_atom *)getbytes(0); x->x_onset = 0x7fffffff; x->x_mainout = (!global ? outlet_new(&x->x_obj, &s_list) : 0); x->x_waitout = (global || x->x_waitsym || x->x_waitargc ? outlet_new(&x->x_obj, &s_list) : 0); x->x_endout = outlet_new(&x->x_obj, &s_bang); if (global) { if (x->x_waitargc) pd_error(x, "warning: text sequence: numeric 'w' argument ignored if '-g' given"); x->x_waitargc = 0x40000000; } return (x); } static void text_sequence_doit(t_text_sequence *x, int argc, t_atom *argv) { t_binbuf *b = text_client_getbuf(&x->x_tc); int n, i, onset, nfield, wait, eatsemi = 1, gotcomma = 0; t_atom *vec, *outvec, *ap; if (!b) goto nosequence; vec = binbuf_getvec(b); n = binbuf_getnatom(b); if (x->x_onset >= n) { nosequence: x->x_onset = 0x7fffffff; x->x_loop = x->x_auto = 0; outlet_bang(x->x_endout); return; } onset = x->x_onset; /* test if leading numbers, or a leading symbol equal to our "wait symbol", are directing us to wait */ if (!x->x_lastto && ( (vec[onset].a_type == A_FLOAT && x->x_waitargc && !x->x_eaten) || (vec[onset].a_type == A_SYMBOL && vec[onset].a_w.w_symbol == x->x_waitsym))) { if (vec[onset].a_type == A_FLOAT) { for (i = onset; i < n && i < onset + x->x_waitargc && vec[i].a_type == A_FLOAT; i++) ; x->x_eaten = 1; eatsemi = 0; } else { for (i = onset; i < n && vec[i].a_type != A_SEMI && vec[i].a_type != A_COMMA; i++) ; x->x_eaten = 1; onset++; /* symbol isn't part of wait list */ } wait = 1; } else /* message to send */ { for (i = onset; i < n && vec[i].a_type != A_SEMI && vec[i].a_type != A_COMMA; i++) ; wait = 0; x->x_eaten = 0; if (i < n && vec[i].a_type == A_COMMA) gotcomma = 1; } nfield = i - onset; i += eatsemi; if (i >= n) i = 0x7fffffff; x->x_onset = i; /* generate output list, realizing dolar sign atoms. Allocate one extra atom in case we want to prepend a symbol later */ ALLOCA(t_atom, outvec, nfield+1, TEXT_NGETBYTE); for (i = 0, ap = vec+onset; i < nfield; i++, ap++) { int type = ap->a_type; if (type == A_FLOAT || type == A_SYMBOL) outvec[i] = *ap; else if (type == A_DOLLAR) { int atno = ap->a_w.w_index-1; if (atno < 0 || atno >= argc) { pd_error(x, "argument $%d out of range", atno+1); SETFLOAT(outvec+i, 0); } else outvec[i] = argv[atno]; } else if (type == A_DOLLSYM) { t_symbol *s = binbuf_realizedollsym(ap->a_w.w_symbol, argc, argv, 0); if (s) SETSYMBOL(outvec+i, s); else { pd_error(0, "$%s: not enough arguments supplied", ap->a_w.w_symbol->s_name); SETSYMBOL(outvec+i, &s_symbol); } } else bug("text sequence"); } if (wait) { x->x_loop = 0; x->x_lastto = 0; if (x->x_auto && nfield == 1 && outvec[0].a_type == A_FLOAT) x->x_nextdelay = outvec[0].a_w.w_float; else if (!x->x_waitout) bug("text sequence 3"); else { x->x_auto = 0; outlet_list(x->x_waitout, 0, nfield, outvec); } } else if (x->x_mainout) { int n2 = nfield; if (x->x_lastto) { memmove(outvec+1, outvec, nfield * sizeof(*outvec)); SETSYMBOL(outvec, x->x_lastto); n2++; } if (!gotcomma) x->x_lastto = 0; else if (!x->x_lastto && nfield && outvec->a_type == A_SYMBOL) x->x_lastto = outvec->a_w.w_symbol; outlet_list(x->x_mainout, 0, n2, outvec); } else if (nfield) { t_symbol *tosym = x->x_lastto; t_pd *to = 0; t_atom *vecleft = outvec; int nleft = nfield; if (!tosym) { if (outvec[0].a_type != A_SYMBOL) bug("text sequence 2"); else tosym = outvec[0].a_w.w_symbol; vecleft++; nleft--; } if (tosym) { if (!(to = tosym->s_thing)) pd_error(x, "%s: no such object", tosym->s_name); } x->x_lastto = (gotcomma ? tosym : 0); if (to) { if (nleft > 0 && vecleft[0].a_type == A_SYMBOL) typedmess(to, vecleft->a_w.w_symbol, nleft-1, vecleft+1); else pd_list(to, 0, nleft, vecleft); } } FREEA(t_atom, outvec, nfield+1, TEXT_NGETBYTE); } static void text_sequence_list(t_text_sequence *x, t_symbol *s, int argc, t_atom *argv) { x->x_loop = 1; while (x->x_loop) { if (argc) text_sequence_doit(x, argc, argv); else text_sequence_doit(x, x->x_argc, x->x_argv); } } static void text_sequence_stop(t_text_sequence *x) { x->x_loop = 0; if (x->x_auto) { clock_unset(x->x_clock); x->x_auto = 0; } } static void text_sequence_tick(t_text_sequence *x) /* clock callback */ { x->x_lastto = 0; while (x->x_auto) { x->x_loop = 1; while (x->x_loop) text_sequence_doit(x, x->x_argc, x->x_argv); if (x->x_nextdelay > 0) break; } if (x->x_auto) clock_delay(x->x_clock, x->x_nextdelay); } static void text_sequence_auto(t_text_sequence *x) { x->x_lastto = 0; if (x->x_auto) clock_unset(x->x_clock); x->x_auto = 1; text_sequence_tick(x); } static void text_sequence_step(t_text_sequence *x) { text_sequence_stop(x); text_sequence_doit(x, x->x_argc, x->x_argv); } static void text_sequence_line(t_text_sequence *x, t_floatarg f) { t_binbuf *b = text_client_getbuf(&x->x_tc); int n, start, end; t_atom *vec; if (!b) return; x->x_lastto = 0; vec = binbuf_getvec(b); n = binbuf_getnatom(b); if (!text_nthline(n, vec, f, &start, &end)) { pd_error(x, "text sequence: line number %d out of range", (int)f); x->x_onset = 0x7fffffff; } else x->x_onset = start; x->x_eaten = 0; } static void text_sequence_args(t_text_sequence *x, t_symbol *s, int argc, t_atom *argv) { int i; x->x_argv = t_resizebytes(x->x_argv, x->x_argc * sizeof(t_atom), argc * sizeof(t_atom)); for (i = 0; i < argc; i++) x->x_argv[i] = argv[i]; x->x_argc = argc; } static void text_sequence_tempo(t_text_sequence *x, t_symbol *unitname, t_floatarg tempo) { t_float unit; int samps; parsetimeunits(x, tempo, unitname, &unit, &samps); clock_setunit(x->x_clock, unit, samps); } static void text_sequence_free(t_text_sequence *x) { t_freebytes(x->x_argv, sizeof(t_atom) * x->x_argc); clock_free(x->x_clock); text_client_free(&x->x_tc); } /* --- overall creator for "text" objects: dispatch to "text define" etc --- */ static void *text_new(t_symbol *s, int argc, t_atom *argv) { if (!argc || argv[0].a_type != A_SYMBOL) pd_this->pd_newest = text_define_new(s, argc, argv); else { const char *str = argv[0].a_w.w_symbol->s_name; if (!strcmp(str, "d") || !strcmp(str, "define")) pd_this->pd_newest = text_define_new(s, argc-1, argv+1); else if (!strcmp(str, "get")) pd_this->pd_newest = text_get_new(s, argc-1, argv+1); else if (!strcmp(str, "set")) pd_this->pd_newest = text_set_new(s, argc-1, argv+1); else if (!strcmp(str, "insert")) pd_this->pd_newest = text_insert_new(s, argc-1, argv+1); else if (!strcmp(str, "delete")) pd_this->pd_newest = text_delete_new(s, argc-1, argv+1); else if (!strcmp(str, "size")) pd_this->pd_newest = text_size_new(s, argc-1, argv+1); else if (!strcmp(str, "tolist")) pd_this->pd_newest = text_tolist_new(s, argc-1, argv+1); else if (!strcmp(str, "fromlist")) pd_this->pd_newest = text_fromlist_new(s, argc-1, argv+1); else if (!strcmp(str, "search")) pd_this->pd_newest = text_search_new(s, argc-1, argv+1); else if (!strcmp(str, "sequence")) pd_this->pd_newest = text_sequence_new(s, argc-1, argv+1); else { pd_error(0, "list %s: unknown function", str); pd_this->pd_newest = 0; } } return (pd_this->pd_newest); } /* the qlist and textfile objects, as of 0.44, are 'derived' from * the text object above. Maybe later it will be desirable to add new * functionality to textfile; qlist is an ancient holdover (1987) and * is probably best left alone. */ typedef struct _qlist { t_textbuf x_textbuf; t_outlet *x_bangout; int x_onset; /* playback position */ t_clock *x_clock; t_float x_tempo; double x_whenclockset; t_float x_clockdelay; int x_rewound; /* we've been rewound since last start */ int x_innext; /* we're currently inside the "next" routine */ } t_qlist; #define x_ob x_textbuf.b_ob #define x_binbuf x_textbuf.b_binbuf #define x_canvas x_textbuf.b_canvas static void qlist_tick(t_qlist *x); static t_class *qlist_class; static void *qlist_new(void) { t_qlist *x = (t_qlist *)pd_new(qlist_class); textbuf_init(&x->x_textbuf, gensym("qlist")); x->x_clock = clock_new(x, (t_method)qlist_tick); outlet_new(&x->x_ob, &s_list); x->x_bangout = outlet_new(&x->x_ob, &s_bang); x->x_onset = 0x7fffffff; x->x_tempo = 1; x->x_whenclockset = 0; x->x_clockdelay = 0; x->x_rewound = x->x_innext = 0; return (x); } static void qlist_rewind(t_qlist *x) { x->x_onset = 0; if (x->x_clock) clock_unset(x->x_clock); x->x_whenclockset = 0; x->x_rewound = 1; } static void qlist_donext(t_qlist *x, int drop, int automatic) { t_pd *target = 0; if (x->x_innext) { pd_error(x, "qlist sent 'next' from within itself"); return; } x->x_innext = 1; while (1) { int argc = binbuf_getnatom(x->x_binbuf), count, onset = x->x_onset, onset2, wasrewound; t_atom *argv = binbuf_getvec(x->x_binbuf); t_atom *ap = argv + onset, *ap2; if (onset >= argc) goto end; while (ap->a_type == A_SEMI || ap->a_type == A_COMMA) { if (ap->a_type == A_SEMI) target = 0; onset++, ap++; if (onset >= argc) goto end; } if (!target && ap->a_type == A_FLOAT) { ap2 = ap + 1; onset2 = onset + 1; while (onset2 < argc && ap2->a_type == A_FLOAT) onset2++, ap2++; x->x_onset = onset2; if (automatic) { clock_delay(x->x_clock, x->x_clockdelay = ap->a_w.w_float * x->x_tempo); x->x_whenclockset = clock_getsystime(); } else outlet_list(x->x_ob.ob_outlet, 0, onset2-onset, ap); x->x_innext = 0; return; } ap2 = ap + 1; onset2 = onset + 1; while (onset2 < argc && (ap2->a_type == A_FLOAT || ap2->a_type == A_SYMBOL)) onset2++, ap2++; x->x_onset = onset2; count = onset2 - onset; if (!target) { if (ap->a_type != A_SYMBOL) continue; else if (!(target = ap->a_w.w_symbol->s_thing)) { pd_error(x, "qlist: %s: no such object", ap->a_w.w_symbol->s_name); continue; } ap++; onset++; count--; if (!count) { x->x_onset = onset2; continue; } } wasrewound = x->x_rewound; x->x_rewound = 0; if (!drop) { if (ap->a_type == A_FLOAT) typedmess(target, &s_list, count, ap); else if (ap->a_type == A_SYMBOL) typedmess(target, ap->a_w.w_symbol, count-1, ap+1); } if (x->x_rewound) { x->x_innext = 0; return; } x->x_rewound = wasrewound; } /* while (1); never falls through */ end: x->x_onset = 0x7fffffff; x->x_whenclockset = 0; x->x_innext = 0; outlet_bang(x->x_bangout); } static void qlist_next(t_qlist *x, t_floatarg drop) { qlist_donext(x, drop != 0, 0); } static void qlist_bang(t_qlist *x) { qlist_rewind(x); /* if we're restarted reentrantly from a "next" message set ourselves up to do this non-reentrantly after a delay of 0 */ if (x->x_innext) { x->x_whenclockset = clock_getsystime(); x->x_clockdelay = 0; clock_delay(x->x_clock, 0); } else qlist_donext(x, 0, 1); } static void qlist_tick(t_qlist *x) { x->x_whenclockset = 0; qlist_donext(x, 0, 1); } static void qlist_add(t_qlist *x, t_symbol *s, int argc, t_atom *argv) { t_atom a; SETSEMI(&a); binbuf_add(x->x_binbuf, argc, argv); binbuf_add(x->x_binbuf, 1, &a); } static void qlist_add2(t_qlist *x, t_symbol *s, int argc, t_atom *argv) { binbuf_add(x->x_binbuf, argc, argv); } static void qlist_clear(t_qlist *x) { qlist_rewind(x); binbuf_clear(x->x_binbuf); } static void qlist_set(t_qlist *x, t_symbol *s, int argc, t_atom *argv) { qlist_clear(x); qlist_add(x, s, argc, argv); } static void qlist_read(t_qlist *x, t_symbol *filename, t_symbol *format) { int cr = 0; if (!strcmp(format->s_name, "cr")) cr = 1; else if (*format->s_name) pd_error(x, "qlist_read: unknown flag: %s", format->s_name); if (binbuf_read_via_canvas(x->x_binbuf, filename->s_name, x->x_canvas, cr)) pd_error(x, "%s: read failed", filename->s_name); x->x_onset = 0x7fffffff; x->x_rewound = 1; } static void qlist_write(t_qlist *x, t_symbol *filename, t_symbol *format) { int cr = 0; char buf[MAXPDSTRING]; canvas_makefilename(x->x_canvas, filename->s_name, buf, MAXPDSTRING); if (!strcmp(format->s_name, "cr")) cr = 1; else if (*format->s_name) pd_error(x, "qlist_read: unknown flag: %s", format->s_name); if (binbuf_write(x->x_binbuf, buf, "", cr)) pd_error(x, "%s: write failed", filename->s_name); } static void qlist_print(t_qlist *x) { post("--------- textfile or qlist contents: -----------"); binbuf_print(x->x_binbuf); } static void qlist_tempo(t_qlist *x, t_float f) { t_float newtempo; if (f < 1e-20) f = 1e-20; else if (f > 1e20) f = 1e20; newtempo = 1./f; if (x->x_whenclockset != 0) { t_float elapsed = clock_gettimesince(x->x_whenclockset); t_float left = x->x_clockdelay - elapsed; if (left < 0) left = 0; left *= newtempo / x->x_tempo; clock_delay(x->x_clock, left); } x->x_tempo = newtempo; } static void qlist_free(t_qlist *x) { textbuf_free(&x->x_textbuf); clock_free(x->x_clock); } /* -------------------- textfile ------------------------------- */ /* has the same struct as qlist (so we can reuse some of its * methods) but "sequencing" here only relies on 'binbuf' and 'onset' * fields. */ static t_class *textfile_class; static void *textfile_new(void) { t_qlist *x = (t_qlist *)pd_new(textfile_class); textbuf_init(&x->x_textbuf, gensym("textfile")); outlet_new(&x->x_ob, &s_list); x->x_bangout = outlet_new(&x->x_ob, &s_bang); x->x_onset = 0x7fffffff; x->x_rewound = 0; x->x_tempo = 1; x->x_whenclockset = 0; x->x_clockdelay = 0; x->x_clock = NULL; return (x); } static void textfile_bang(t_qlist *x) { int argc = binbuf_getnatom(x->x_binbuf), onset = x->x_onset, onset2; t_atom *argv = binbuf_getvec(x->x_binbuf); t_atom *ap = argv + onset, *ap2; while (onset < argc && (ap->a_type == A_SEMI || ap->a_type == A_COMMA)) onset++, ap++; onset2 = onset; ap2 = ap; while (onset2 < argc && (ap2->a_type != A_SEMI && ap2->a_type != A_COMMA)) onset2++, ap2++; if (onset2 > onset) { x->x_onset = onset2; if (ap->a_type == A_SYMBOL) outlet_anything(x->x_ob.ob_outlet, ap->a_w.w_symbol, onset2-onset-1, ap+1); else outlet_list(x->x_ob.ob_outlet, 0, onset2-onset, ap); } else { x->x_onset = 0x7fffffff; outlet_bang(x->x_bangout); } } static void textfile_rewind(t_qlist *x) { x->x_onset = 0; } /* ---------------- global setup function -------------------- */ static t_pd *text_templatecanvas; static char text_templatefile[] = "\ canvas 0 0 458 153 10;\n\ #X obj 43 31 struct text float x float y text t;\n\ "; /* create invisible, built-in canvas to supply template containing one text field named 't'. I don't know how to make this not break pre-0.45 patches using templates named 'text'... perhaps this is a minor enough incompatibility that I'll just get away with it. */ void text_template_init(void) { t_binbuf *b; b = binbuf_new(); glob_setfilename(0, gensym("_text_template"), gensym(".")); binbuf_text(b, text_templatefile, strlen(text_templatefile)); binbuf_eval(b, &pd_canvasmaker, 0, 0); vmess(s__X.s_thing, gensym("pop"), "i", 0); glob_setfilename(0, &s_, &s_); binbuf_free(b); } void x_qlist_setup(void) { text_template_init(); text_define_class = class_new(gensym("text define"), (t_newmethod)text_define_new, (t_method)text_define_free, sizeof(t_text_define), 0, A_GIMME, 0); class_addmethod(text_define_class, (t_method)textbuf_open, gensym("click"), 0); class_addmethod(text_define_class, (t_method)textbuf_close, gensym("close"), 0); class_addmethod(text_define_class, (t_method)textbuf_addline, gensym("addline"), A_GIMME, 0); class_addmethod(text_define_class, (t_method)text_define_notify, gensym("notify"), 0, 0); class_addmethod(text_define_class, (t_method)text_define_set, gensym("set"), A_GIMME, 0); class_addmethod(text_define_class, (t_method)text_define_clear, gensym("clear"), 0); class_addmethod(text_define_class, (t_method)textbuf_write, gensym("write"), A_GIMME, 0); class_addmethod(text_define_class, (t_method)textbuf_read, gensym("read"), A_GIMME, 0); class_addmethod(text_define_class, (t_method)text_define_send, gensym("send"), A_SYMBOL, 0); class_addmethod(text_define_class, (t_method)text_define_sort, gensym("sort"), A_GIMME, 0); class_setsavefn(text_define_class, text_define_save); class_addbang(text_define_class, text_define_bang); class_sethelpsymbol(text_define_class, gensym("text-object")); class_addcreator((t_newmethod)text_new, gensym("text"), A_GIMME, 0); text_get_class = class_new(gensym("text get"), (t_newmethod)text_get_new, (t_method)text_client_free, sizeof(t_text_get), 0, A_GIMME, 0); class_addfloat(text_get_class, text_get_float); class_sethelpsymbol(text_get_class, gensym("text-object")); text_set_class = class_new(gensym("text set"), (t_newmethod)text_set_new, (t_method)text_client_free, sizeof(t_text_set), 0, A_GIMME, 0); class_addlist(text_set_class, text_set_list); class_sethelpsymbol(text_set_class, gensym("text-object")); text_insert_class = class_new(gensym("text insert"), (t_newmethod)text_insert_new, (t_method)text_client_free, sizeof(t_text_insert), 0, A_GIMME, 0); class_addlist(text_insert_class, text_insert_list); class_sethelpsymbol(text_insert_class, gensym("text-object")); text_delete_class = class_new(gensym("text delete"), (t_newmethod)text_delete_new, (t_method)text_client_free, sizeof(t_text_delete), 0, A_GIMME, 0); class_addfloat(text_delete_class, text_delete_float); class_sethelpsymbol(text_delete_class, gensym("text-object")); text_size_class = class_new(gensym("text size"), (t_newmethod)text_size_new, (t_method)text_client_free, sizeof(t_text_size), 0, A_GIMME, 0); class_addbang(text_size_class, text_size_bang); class_addfloat(text_size_class, text_size_float); class_sethelpsymbol(text_size_class, gensym("text-object")); text_tolist_class = class_new(gensym("text tolist"), (t_newmethod)text_tolist_new, (t_method)text_client_free, sizeof(t_text_tolist), 0, A_GIMME, 0); class_addbang(text_tolist_class, text_tolist_bang); class_sethelpsymbol(text_tolist_class, gensym("text-object")); text_fromlist_class = class_new(gensym("text fromlist"), (t_newmethod)text_fromlist_new, (t_method)text_client_free, sizeof(t_text_fromlist), 0, A_GIMME, 0); class_addlist(text_fromlist_class, text_fromlist_list); class_sethelpsymbol(text_fromlist_class, gensym("text-object")); text_search_class = class_new(gensym("text search"), (t_newmethod)text_search_new, (t_method)text_client_free, sizeof(t_text_search), 0, A_GIMME, 0); class_addlist(text_search_class, text_search_list); class_addmethod(text_search_class, (t_method)text_search_range, gensym("range"), A_FLOAT, A_FLOAT, 0); class_sethelpsymbol(text_search_class, gensym("text-object")); text_sequence_class = class_new(gensym("text sequence"), (t_newmethod)text_sequence_new, (t_method)text_sequence_free, sizeof(t_text_sequence), 0, A_GIMME, 0); class_addmethod(text_sequence_class, (t_method)text_sequence_step, gensym("step"), 0); class_addmethod(text_sequence_class, (t_method)text_sequence_line, gensym("line"), A_FLOAT, 0); class_addmethod(text_sequence_class, (t_method)text_sequence_auto, gensym("auto"), 0); class_addmethod(text_sequence_class, (t_method)text_sequence_stop, gensym("stop"), 0); class_addmethod(text_sequence_class, (t_method)text_sequence_args, gensym("args"), A_GIMME, 0); class_addmethod(text_sequence_class, (t_method)text_sequence_tempo, gensym("tempo"), A_FLOAT, A_SYMBOL, 0); class_addlist(text_sequence_class, text_sequence_list); class_sethelpsymbol(text_sequence_class, gensym("text-object")); qlist_class = class_new(gensym("qlist"), (t_newmethod)qlist_new, (t_method)qlist_free, sizeof(t_qlist), 0, 0); class_addmethod(qlist_class, (t_method)qlist_rewind, gensym("rewind"), 0); class_addmethod(qlist_class, (t_method)qlist_next, gensym("next"), A_DEFFLOAT, 0); class_addmethod(qlist_class, (t_method)qlist_set, gensym("set"), A_GIMME, 0); class_addmethod(qlist_class, (t_method)qlist_clear, gensym("clear"), 0); class_addmethod(qlist_class, (t_method)qlist_add, gensym("add"), A_GIMME, 0); class_addmethod(qlist_class, (t_method)qlist_add2, gensym("add2"), A_GIMME, 0); class_addmethod(qlist_class, (t_method)qlist_add, gensym("append"), A_GIMME, 0); class_addmethod(qlist_class, (t_method)qlist_read, gensym("read"), A_SYMBOL, A_DEFSYM, 0); class_addmethod(qlist_class, (t_method)qlist_write, gensym("write"), A_SYMBOL, A_DEFSYM, 0); class_addmethod(qlist_class, (t_method)textbuf_open, gensym("click"), 0); class_addmethod(qlist_class, (t_method)textbuf_close, gensym("close"), 0); class_addmethod(qlist_class, (t_method)textbuf_addline, gensym("addline"), A_GIMME, 0); class_addmethod(qlist_class, (t_method)textbuf_senditup, gensym("notify"), 0, 0); class_addmethod(qlist_class, (t_method)qlist_print, gensym("print"), A_DEFSYM, 0); class_addmethod(qlist_class, (t_method)qlist_tempo, gensym("tempo"), A_FLOAT, 0); class_addbang(qlist_class, qlist_bang); textfile_class = class_new(gensym("textfile"), (t_newmethod)textfile_new, (t_method)textbuf_free, sizeof(t_qlist), 0, 0); class_addmethod(textfile_class, (t_method)textfile_rewind, gensym("rewind"), 0); class_addmethod(textfile_class, (t_method)qlist_set, gensym("set"), A_GIMME, 0); class_addmethod(textfile_class, (t_method)qlist_clear, gensym("clear"), 0); class_addmethod(textfile_class, (t_method)qlist_add, gensym("add"), A_GIMME, 0); class_addmethod(textfile_class, (t_method)qlist_add2, gensym("add2"), A_GIMME, 0); class_addmethod(textfile_class, (t_method)qlist_add, gensym("append"), A_GIMME, 0); class_addmethod(textfile_class, (t_method)qlist_read, gensym("read"), A_SYMBOL, A_DEFSYM, 0); class_addmethod(textfile_class, (t_method)qlist_write, gensym("write"), A_SYMBOL, A_DEFSYM, 0); class_addmethod(textfile_class, (t_method)textbuf_open, gensym("click"), 0); class_addmethod(textfile_class, (t_method)textbuf_close, gensym("close"), 0); class_addmethod(textfile_class, (t_method)textbuf_addline, gensym("addline"), A_GIMME, 0); class_addmethod(textfile_class, (t_method)textbuf_senditup, gensym("notify"), 0, 0); class_addmethod(textfile_class, (t_method)qlist_print, gensym("print"), A_DEFSYM, 0); class_addbang(textfile_class, textfile_bang); } /* public interface to get text buffers by name */ t_binbuf *text_getbufbyname(t_symbol *s) { t_text_define *y = (t_text_define *)pd_findbyclass(s, text_define_class); if (y) return (y->x_textbuf.b_binbuf); else return (0); } /* notify text object that binbuf was modified */ void text_notifybyname(t_symbol *s) { t_text_define *y = (t_text_define *)pd_findbyclass(s, text_define_class); if (y) text_define_notify(y); } ================================================ FILE: libs/libpd/pure-data/src/x_time.c ================================================ /* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* clock objects */ #include "m_pd.h" #include #include /* parse a time unit such as "5 msec", "60 perminute", or "1 sample" to a form usable by clock_setunit)( and clock_gettimesincewithunits(). This brute-force search through symbols really ought not to be done on the fly for incoming 'tempo' messages, hmm... This isn't public because its interface might want to change - but it's used in x_text.c as well as here. */ void parsetimeunits(void *x, t_float amount, t_symbol *unitname, t_float *unit, int *samps) { const char *s = unitname->s_name; if (amount <= 0) amount = 1; if (s[0] == 'p' && s[1] == 'e' && s[2] == 'r') /* starts with 'per' */ { const char *s2 = s+3; if (!strcmp(s2, "millisecond") || !strcmp(s2, "msec")) /* msec */ *samps = 0, *unit = 1./amount; else if (!strncmp(s2, "sec", 3)) /* seconds */ *samps = 0, *unit = 1000./amount; else if (!strncmp(s2, "min", 3)) /* minutes */ *samps = 0, *unit = 60000./amount; else if (!strncmp(s2, "sam", 3)) /* samples */ *samps = 1, *unit = 1./amount; else goto fail; } else { /* empty string defaults to msec */ if (!strcmp(s, "millisecond") || !strcmp(s, "msec")) *samps = 0, *unit = amount; else if (!strncmp(s, "sec", 3)) *samps = 0, *unit = 1000.*amount; else if (!strncmp(s, "min", 3)) *samps = 0, *unit = 60000.*amount; else if (!strncmp(s, "sam", 3)) *samps = 1, *unit = amount; else { fail: /* empty time unit specification defaults to 1 msec for back compatibility, since it's possible someone threw a float argument to timer which had previously been ignored. */ if (*s) pd_error(x, "%s: unknown time unit", s); else pd_error(x, "tempo setting needs time unit ('sec', 'samp', 'permin', etc."); *unit = 1; *samps = 0; } } } /* -------------------------- delay ------------------------------ */ static t_class *delay_class; typedef struct _delay { t_object x_obj; t_clock *x_clock; double x_deltime; } t_delay; static void delay_ft1(t_delay *x, t_floatarg g) { if (g < 0) g = 0; x->x_deltime = g; } static void delay_tick(t_delay *x) { outlet_bang(x->x_obj.ob_outlet); } static void delay_bang(t_delay *x) { clock_delay(x->x_clock, x->x_deltime); } static void delay_stop(t_delay *x) { clock_unset(x->x_clock); } static void delay_float(t_delay *x, t_float f) { delay_ft1(x, f); delay_bang(x); } static void delay_tempo(t_delay *x, t_symbol *unitname, t_floatarg tempo) { t_float unit; int samps; parsetimeunits(x, tempo, unitname, &unit, &samps); clock_setunit(x->x_clock, unit, samps); } static void delay_free(t_delay *x) { clock_free(x->x_clock); } static void *delay_new(t_symbol *unitname, t_floatarg f, t_floatarg tempo) { t_delay *x = (t_delay *)pd_new(delay_class); delay_ft1(x, f); x->x_clock = clock_new(x, (t_method)delay_tick); outlet_new(&x->x_obj, gensym("bang")); inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("ft1")); if (tempo != 0) delay_tempo(x, unitname, tempo); return (x); } static void delay_setup(void) { delay_class = class_new(gensym("delay"), (t_newmethod)delay_new, (t_method)delay_free, sizeof(t_delay), 0, A_DEFFLOAT, A_DEFFLOAT, A_DEFSYM, 0); class_addcreator((t_newmethod)delay_new, gensym("del"), A_DEFFLOAT, A_DEFFLOAT, A_DEFSYM, 0); class_addbang(delay_class, delay_bang); class_addmethod(delay_class, (t_method)delay_stop, gensym("stop"), 0); class_addmethod(delay_class, (t_method)delay_ft1, gensym("ft1"), A_FLOAT, 0); class_addmethod(delay_class, (t_method)delay_tempo, gensym("tempo"), A_FLOAT, A_SYMBOL, 0); class_addfloat(delay_class, (t_method)delay_float); } /* -------------------------- metro ------------------------------ */ static t_class *metro_class; typedef struct _metro { t_object x_obj; t_clock *x_clock; double x_deltime; int x_hit; } t_metro; static void metro_ft1(t_metro *x, t_floatarg g) { if (g <= 0) /* as of 0.45, we're willing to try any positive time value */ g = 1; /* but default to 1 (arbitrary and probably not so good) */ x->x_deltime = g; } static void metro_tick(t_metro *x) { x->x_hit = 0; outlet_bang(x->x_obj.ob_outlet); if (!x->x_hit) clock_delay(x->x_clock, x->x_deltime); } static void metro_float(t_metro *x, t_float f) { if (f != 0) metro_tick(x); else clock_unset(x->x_clock); x->x_hit = 1; } static void metro_bang(t_metro *x) { metro_float(x, 1); } static void metro_stop(t_metro *x) { metro_float(x, 0); } static void metro_tempo(t_metro *x, t_symbol *unitname, t_floatarg tempo) { t_float unit; int samps; parsetimeunits(x, tempo, unitname, &unit, &samps); clock_setunit(x->x_clock, unit, samps); } static void metro_free(t_metro *x) { clock_free(x->x_clock); } static void *metro_new(t_symbol *unitname, t_floatarg f, t_floatarg tempo) { t_metro *x = (t_metro *)pd_new(metro_class); metro_ft1(x, f); x->x_hit = 0; x->x_clock = clock_new(x, (t_method)metro_tick); outlet_new(&x->x_obj, gensym("bang")); inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("ft1")); if (tempo != 0) metro_tempo(x, unitname, tempo); return (x); } static void metro_setup(void) { metro_class = class_new(gensym("metro"), (t_newmethod)metro_new, (t_method)metro_free, sizeof(t_metro), 0, A_DEFFLOAT, A_DEFFLOAT, A_DEFSYM, 0); class_addbang(metro_class, metro_bang); class_addmethod(metro_class, (t_method)metro_stop, gensym("stop"), 0); class_addmethod(metro_class, (t_method)metro_ft1, gensym("ft1"), A_FLOAT, 0); class_addmethod(metro_class, (t_method)metro_tempo, gensym("tempo"), A_FLOAT, A_SYMBOL, 0); class_addfloat(metro_class, (t_method)metro_float); } /* -------------------------- line ------------------------------ */ #define DEFAULTLINEGRAIN 20 static t_class *line_class; typedef struct _line { t_object x_obj; t_clock *x_clock; double x_targettime; t_float x_targetval; double x_prevtime; t_float x_setval; int x_gotinlet; t_float x_grain; double x_1overtimediff; double x_in1val; } t_line; static void line_tick(t_line *x) { double timenow = clock_getsystime(); double msectogo = - clock_gettimesince(x->x_targettime); if (msectogo < 1E-9) { outlet_float(x->x_obj.ob_outlet, x->x_targetval); } else { outlet_float(x->x_obj.ob_outlet, x->x_setval + x->x_1overtimediff * (timenow - x->x_prevtime) * (x->x_targetval - x->x_setval)); if (x->x_grain <= 0) x->x_grain = DEFAULTLINEGRAIN; clock_delay(x->x_clock, (x->x_grain > msectogo ? msectogo : x->x_grain)); } } static void line_float(t_line *x, t_float f) { double timenow = clock_getsystime(); if (x->x_gotinlet && x->x_in1val > 0) { if (timenow > x->x_targettime) x->x_setval = x->x_targetval; else x->x_setval = x->x_setval + x->x_1overtimediff * (timenow - x->x_prevtime) * (x->x_targetval - x->x_setval); x->x_prevtime = timenow; x->x_targettime = clock_getsystimeafter(x->x_in1val); x->x_targetval = f; line_tick(x); x->x_gotinlet = 0; x->x_1overtimediff = 1./ (x->x_targettime - timenow); if (x->x_grain <= 0) x->x_grain = DEFAULTLINEGRAIN; clock_delay(x->x_clock, (x->x_grain > x->x_in1val ? x->x_in1val : x->x_grain)); } else { clock_unset(x->x_clock); x->x_targetval = x->x_setval = f; outlet_float(x->x_obj.ob_outlet, f); } x->x_gotinlet = 0; } static void line_ft1(t_line *x, t_floatarg g) { x->x_in1val = g; x->x_gotinlet = 1; } static void line_stop(t_line *x) { if (pd_compatibilitylevel >= 48) { if (clock_getsystime() >= x->x_targettime) x->x_setval = x->x_targetval; else x->x_setval += x->x_1overtimediff * (clock_getsystime() - x->x_prevtime) * (x->x_targetval - x->x_setval); } x->x_targetval = x->x_setval; clock_unset(x->x_clock); } static void line_set(t_line *x, t_floatarg f) { clock_unset(x->x_clock); x->x_targetval = x->x_setval = f; } static void line_free(t_line *x) { clock_free(x->x_clock); } static void *line_new(t_floatarg f, t_floatarg grain) { t_line *x = (t_line *)pd_new(line_class); x->x_targetval = x->x_setval = f; x->x_gotinlet = 0; x->x_1overtimediff = 1; x->x_clock = clock_new(x, (t_method)line_tick); x->x_targettime = x->x_prevtime = clock_getsystime(); x->x_grain = grain; outlet_new(&x->x_obj, gensym("float")); inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("ft1")); floatinlet_new(&x->x_obj, &x->x_grain); return (x); } static void line_setup(void) { line_class = class_new(gensym("line"), (t_newmethod)line_new, (t_method)line_free, sizeof(t_line), 0, A_DEFFLOAT, A_DEFFLOAT, 0); class_addmethod(line_class, (t_method)line_ft1, gensym("ft1"), A_FLOAT, 0); class_addmethod(line_class, (t_method)line_stop, gensym("stop"), 0); class_addmethod(line_class, (t_method)line_set, gensym("set"), A_FLOAT, 0); class_addfloat(line_class, (t_method)line_float); } /* -------------------------- timer ------------------------------ */ static t_class *timer_class; typedef struct _timer { t_object x_obj; double x_settime; double x_moreelapsed; t_float x_unit; int x_samps; } t_timer; static void timer_bang(t_timer *x) { x->x_settime = clock_getsystime(); x->x_moreelapsed = 0; } static void timer_bang2(t_timer *x) { outlet_float(x->x_obj.ob_outlet, clock_gettimesincewithunits(x->x_settime, x->x_unit, x->x_samps) + x->x_moreelapsed); } static void timer_tempo(t_timer *x, t_symbol *unitname, t_floatarg tempo) { x->x_moreelapsed += clock_gettimesincewithunits(x->x_settime, x->x_unit, x->x_samps); x->x_settime = clock_getsystime(); parsetimeunits(x, tempo, unitname, &x->x_unit, &x->x_samps); } static void *timer_new(t_symbol *unitname, t_floatarg tempo) { t_timer *x = (t_timer *)pd_new(timer_class); x->x_unit = 1; x->x_samps = 0; timer_bang(x); outlet_new(&x->x_obj, gensym("float")); inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("bang"), gensym("bang2")); if (tempo != 0) timer_tempo(x, unitname, tempo); return (x); } static void timer_setup(void) { timer_class = class_new(gensym("timer"), (t_newmethod)timer_new, 0, sizeof(t_timer), 0, A_DEFFLOAT, A_DEFSYM, 0); class_addbang(timer_class, timer_bang); class_addmethod(timer_class, (t_method)timer_bang2, gensym("bang2"), 0); class_addmethod(timer_class, (t_method)timer_tempo, gensym("tempo"), A_FLOAT, A_SYMBOL, 0); } /* -------------------------- pipe -------------------------- */ static t_class *pipe_class; typedef struct _hang { t_clock *h_clock; struct _hang *h_next; struct _pipe *h_owner; t_gpointer *h_gp; union word h_vec[1]; /* not the actual number. */ } t_hang; typedef struct pipeout { t_atom p_atom; t_outlet *p_outlet; } t_pipeout; typedef struct _pipe { t_object x_obj; int x_n; int x_nptr; t_float x_deltime; t_pipeout *x_vec; t_gpointer *x_gp; t_hang *x_hang; } t_pipe; static void *pipe_new(t_symbol *s, int argc, t_atom *argv) { t_pipe *x = (t_pipe *)pd_new(pipe_class); t_atom defarg, *ap; t_pipeout *vec, *vp; t_gpointer *gp; int nptr = 0; int i; t_float deltime; if (argc) { if (argv[argc-1].a_type != A_FLOAT) { char stupid[80]; atom_string(&argv[argc-1], stupid, 79); pd_error(x, "pipe: %s: bad time delay value", stupid); deltime = 0; } else deltime = argv[argc-1].a_w.w_float; argc--; } else deltime = 0; if (!argc) { argv = &defarg; argc = 1; SETFLOAT(&defarg, 0); } x->x_n = argc; vec = x->x_vec = (t_pipeout *)getbytes(argc * sizeof(*x->x_vec)); for (i = argc, ap = argv; i--; ap++) if (ap->a_type == A_SYMBOL && *ap->a_w.w_symbol->s_name == 'p') nptr++; gp = x->x_gp = (t_gpointer *)t_getbytes(nptr * sizeof (*gp)); x->x_nptr = nptr; for (i = 0, vp = vec, ap = argv; i < argc; i++, ap++, vp++) { if (ap->a_type == A_FLOAT) { vp->p_atom = *ap; vp->p_outlet = outlet_new(&x->x_obj, &s_float); if (i) floatinlet_new(&x->x_obj, &vp->p_atom.a_w.w_float); } else if (ap->a_type == A_SYMBOL) { char c = *ap->a_w.w_symbol->s_name; if (c == 's') { SETSYMBOL(&vp->p_atom, &s_symbol); vp->p_outlet = outlet_new(&x->x_obj, &s_symbol); if (i) symbolinlet_new(&x->x_obj, &vp->p_atom.a_w.w_symbol); } else if (c == 'p') { vp->p_atom.a_type = A_POINTER; vp->p_atom.a_w.w_gpointer = gp; gpointer_init(gp); vp->p_outlet = outlet_new(&x->x_obj, &s_pointer); if (i) pointerinlet_new(&x->x_obj, gp); gp++; } else { if (c != 'f') pd_error(x, "pipe: %s: bad type", ap->a_w.w_symbol->s_name); SETFLOAT(&vp->p_atom, 0); vp->p_outlet = outlet_new(&x->x_obj, &s_float); if (i) floatinlet_new(&x->x_obj, &vp->p_atom.a_w.w_float); } } } floatinlet_new(&x->x_obj, &x->x_deltime); x->x_hang = 0; x->x_deltime = deltime; return (x); } static void hang_free(t_hang *h) { t_pipe *x = h->h_owner; t_gpointer *gp; int i; for (gp = h->h_gp, i = x->x_nptr; i--; gp++) gpointer_unset(gp); freebytes(h->h_gp, x->x_nptr * sizeof(*h->h_gp)); clock_free(h->h_clock); freebytes(h, sizeof(*h) + (x->x_n - 1) * sizeof(*h->h_vec)); } static void hang_tick(t_hang *h) { t_pipe *x = h->h_owner; t_hang *h2, *h3; t_pipeout *p; int i; union word *w; if (x->x_hang == h) x->x_hang = h->h_next; else for (h2 = x->x_hang; (h3 = h2->h_next); h2 = h3) { if (h3 == h) { h2->h_next = h3->h_next; break; } } for (i = x->x_n, p = x->x_vec + (x->x_n - 1), w = h->h_vec + (x->x_n - 1); i--; p--, w--) { switch (p->p_atom.a_type) { case A_FLOAT: outlet_float(p->p_outlet, w->w_float); break; case A_SYMBOL: outlet_symbol(p->p_outlet, w->w_symbol); break; case A_POINTER: if (gpointer_check(w->w_gpointer, 1)) outlet_pointer(p->p_outlet, w->w_gpointer); else pd_error(x, "pipe: stale pointer"); break; default: break; } } hang_free(h); } static void pipe_list(t_pipe *x, t_symbol *s, int ac, t_atom *av) { t_hang *h = (t_hang *) getbytes(sizeof(*h) + (x->x_n - 1) * sizeof(*h->h_vec)); t_gpointer *gp, *gp2; t_pipeout *p; int i, n = x->x_n; t_atom *ap; t_word *w; h->h_gp = (t_gpointer *)getbytes(x->x_nptr * sizeof(t_gpointer)); if (ac > n) { if (av[n].a_type == A_FLOAT) x->x_deltime = av[n].a_w.w_float; else pd_error(x, "pipe: symbol or pointer in time inlet"); ac = n; } for (i = 0, gp = x->x_gp, p = x->x_vec, ap = av; i < ac; i++, p++, ap++) { switch (p->p_atom.a_type) { case A_FLOAT: p->p_atom.a_w.w_float = atom_getfloat(ap); break; case A_SYMBOL: p->p_atom.a_w.w_symbol = atom_getsymbol(ap); break; case A_POINTER: gpointer_unset(gp); if (ap->a_type != A_POINTER) pd_error(x, "pipe: bad pointer"); else { *gp = *(ap->a_w.w_gpointer); if (gp->gp_stub) gp->gp_stub->gs_refcount++; } gp++; default: break; } } for (i = 0, gp = x->x_gp, gp2 = h->h_gp, p = x->x_vec, w = h->h_vec; i < n; i++, p++, w++) { if (p->p_atom.a_type == A_POINTER) { if (gp->gp_stub) gp->gp_stub->gs_refcount++; w->w_gpointer = gp2; *gp2++ = *gp++; } else *w = p->p_atom.a_w; } h->h_next = x->x_hang; x->x_hang = h; h->h_owner = x; h->h_clock = clock_new(h, (t_method)hang_tick); clock_delay(h->h_clock, (x->x_deltime >= 0 ? x->x_deltime : 0)); } static void pipe_flush(t_pipe *x) { while (x->x_hang) hang_tick(x->x_hang); } static void pipe_clear(t_pipe *x) { t_hang *hang; while ((hang = x->x_hang)) { x->x_hang = hang->h_next; hang_free(hang); } } static void pipe_free(t_pipe *x) { pipe_clear(x); freebytes(x->x_vec, x->x_n * sizeof(*x->x_vec)); freebytes(x->x_gp, x->x_nptr * sizeof(*x->x_gp)); } static void pipe_setup(void) { pipe_class = class_new(gensym("pipe"), (t_newmethod)pipe_new, (t_method)pipe_free, sizeof(t_pipe), 0, A_GIMME, 0); class_addlist(pipe_class, pipe_list); class_addmethod(pipe_class, (t_method)pipe_flush, gensym("flush"), 0); class_addmethod(pipe_class, (t_method)pipe_clear, gensym("clear"), 0); } void x_time_setup(void) { delay_setup(); metro_setup(); line_setup(); timer_setup(); pipe_setup(); } ================================================ FILE: libs/libpd/pure-data/src/x_vexp.c ================================================ /* Copyright (c) IRCAM. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* "expr" was written by Shahrokh Yadegari c. 1989. -msp */ /* "expr~" and "fexpr~" conversion by Shahrokh Yadegari c. 1999,2000 */ /* * Feb 2002 - added access to variables * multiple expression support * new short hand forms for fexpr~ * now $y or $y1 = $y1[-1] and $y2 = $y2[-1] * --sdy * * July 2002 * fixed bugs introduced in last changes in store and ET_EQ * --sdy * * Oct 2015 * $x[-1] was not equal $x1[-1], not accessing the previous block * (bug fix by Dan Ellis) * * July 2017, Version 0.55 * - The arrays now redraw after a store into one of their members * - ex_if() (the "if()" function is reworked to only evaluate * either the left or the right args depending on the truth * value of the condition. However, if the condition is a * vector, both the left and the right are evaluated regardless. * - priority of ',' and '=' was switched to fix the bug of using * store "=" in functions with multiple arguments, which caused * an error during execution. * - The number of inlet and outlets (MAX_VARS) is now set at 100 * * Jan 2018, Version 0.56 * -fexpr~ now accepts a float in its first input * -Added avg() and Avg() back to the list of functions * * Oct 2020, Version 0.57 * - fixed a bug in fact() * - fixed the bad lvalue bug - "4 + 5 = 3" was not caught before * - fact() (factorial) now calculates and returns its value in double * - Added mtof(), mtof(), dbtorms(), rmstodb(), powtodb(), dbtopow() */ /* * vexp.c -- a variable expression evaluator * * This modules implements an expression evaluator using the * operator-precedence parsing. It transforms an infix expression * to a prefix stack ready to be evaluated. The expression sysntax * is close to that of C. There are a few operators that are not * supported and functions are also recognized. Strings can be * passed to functions when they are quoted in '"'s. "[]" are implemented * as an easy way of accessing the content of tables, and the syntax * table_name[index]. * Variables (inlets) are specified with the following syntax: $x#, * where x is either i(integers), f(floats), and s(strings); and # * is a digit that corresponds to the inlet number. The string variables * can be used as strings when they are quoted and can also be used as * table names when they are followed by "[]". * * signal vectors have been added to this implementation: * $v# denotes a signal vector * $x#[index] is the value of a sample at the index of a the signal vector * $x# is the shorthand for $x#[0] * $y[index] is the value of the sample output at the index of a the * signal output * "index" for $x#[index] has to have this range (0 <= index < vectorsize) * "index" for $y[index] has to have this range (0 < index < vectorsize) */ #include #include #include #include "x_vexp.h" #include #ifdef MSP #undef isdigit #define isdigit(x) (x >= '0' && x <= '9') #endif #if defined _MSC_VER && (_MSC_VER < 1800) #define strtof(a, b) _atoldbl(a, *b) #endif char *atoif(char *s, long int *value, long int *type); static struct ex_ex *ex_lex(struct expr *expr, long int *n); struct ex_ex *ex_match(struct ex_ex *eptr, long int op); struct ex_ex *ex_parse(struct expr *expr, struct ex_ex *iptr, struct ex_ex *optr, long int *argc); static int ex_checklval (struct ex_ex *eptr); struct ex_ex *ex_eval(struct expr *expr, struct ex_ex *eptr, struct ex_ex *optr, int i); int expr_donew(struct expr *exprr, int ac, t_atom *av); struct ex_ex *eval_func(struct expr *expr,struct ex_ex *eptr, struct ex_ex *optr, int i); struct ex_ex *eval_tab(struct expr *expr, struct ex_ex *eptr, struct ex_ex *optr, int i); struct ex_ex *eval_var(struct expr *expr, struct ex_ex *eptr, struct ex_ex *optr, int i); struct ex_ex *eval_store(struct expr *expr, struct ex_ex *eptr, struct ex_ex *optr, int i); struct ex_ex *eval_sigidx(struct expr *expr, struct ex_ex *eptr, struct ex_ex *optr, int i); static int cal_sigidx(struct ex_ex *optr, /* The output value */ int i, t_float rem_i, /* integer and fractinal part of index */ int idx, /* index of current fexpr~ processing */ int vsize, /* vector size */ t_float *curvec, t_float *prevec); /* current and previous table */ t_ex_func *find_func(char *s); void ex_dzdetect(struct expr *expr); #define MAX_ARGS 10 extern t_ex_func ex_funcs[]; struct ex_ex nullex = { 0 }; void set_tokens (char *s); int getoken (struct expr *expr, struct ex_ex *eptr); void ex_print (struct ex_ex *eptr); #ifdef MSP void atom_string(t_atom *a, char *buf, unsigned int bufsize); void atom_string(t_atom *a, char *buf, unsigned int bufsize) { char tbuf[30]; switch(a->a_type) { case A_SEMI: strcpy(buf, ";"); break; case A_COMMA: strcpy(buf, ","); break; #ifdef PD case A_POINTER: strcpy(buf, "(pointer)"); break; #endif case A_FLOAT: sprintf(tbuf, "%g", a->a_w.w_float); if (strlen(tbuf) < bufsize-1) strcpy(buf, tbuf); else if (a->a_w.w_float < 0) strcpy(buf, "-"); else strcat(buf, "+"); break; case A_LONG: sprintf(tbuf, "%d", a->a_w.w_long); if (strlen(tbuf) < bufsize-1) strcpy(buf, tbuf); else if (a->a_w.w_float < 0) strcpy(buf, "-"); else strcat(buf, "+"); break; case A_SYMBOL: { char *sp; unsigned int len; int quote; for (sp = a->a_w.w_symbol->s_name, len = 0, quote = 0; *sp; sp++, len++) if (*sp == ';' || *sp == ',' || *sp == '\\' || (*sp == '$' && sp == a->a_w.w_symbol->s_name && sp[1] >= '0' && sp[1] <= '9')) quote = 1; if (quote) { char *bp = buf, *ep = buf + (bufsize-2); sp = a->a_w.w_symbol->s_name; while (bp < ep && *sp) { if (*sp == ';' || *sp == ',' || *sp == '\\' || (*sp == '$' && bp == buf && sp[1] >= '0' && sp[1] <= '9')) *bp++ = '\\'; *bp++ = *sp++; } if (*sp) *bp++ = '*'; *bp = 0; /* post("quote %s -> %s", a->a_w.w_symbol->s_name, buf); */ } else { if (len < bufsize-1) strcpy(buf, a->a_w.w_symbol->s_name); else { strncpy(buf, a->a_w.w_symbol->s_name, bufsize - 2); strcpy(buf + (bufsize - 2), "*"); } } } break; #ifdef PD case A_DOLLAR: sprintf(buf, "$%d", a->a_w.w_index); break; case A_DOLLSYM: sprintf(buf, "$%s", a->a_w.w_symbol->s_name); break; #else /* MAX */ case A_DOLLAR: sprintf(buf, "$%s", a->a_w.w_symbol->s_name); break; #endif default: post("atom_string bug"); } } #endif /* MSP */ /* * expr_donew -- create a new "expr" object. * returns 1 on failure, 0 on success. */ int expr_donew(struct expr *expr, int ac, t_atom *av) { struct ex_ex *list; struct ex_ex *ret; long max_node = 0; /* maximum number of nodes needed */ char *exp_string; int exp_strlen; t_binbuf *b; int i; memset(expr->exp_var, 0, MAX_VARS * sizeof (*expr->exp_var)); #ifdef PD b = binbuf_new(); binbuf_add(b, ac, av); binbuf_gettext(b, &exp_string, &exp_strlen); binbuf_free(b); #else /* MSP */ { char *buf = getbytes(0), *newbuf; int length = 0; char string[250]; t_atom *ap; int indx; for (ap = av, indx = 0; indx < ac; indx++, ap = ++av) { int newlength; if ((ap->a_type == A_SEMI || ap->a_type == A_COMMA) && length && buf[length-1] == ' ') length--; atom_string(ap, string, 250); newlength = length + strlen(string) + 1; if (!(newbuf = t_resizebytes(buf, length, newlength))) break; buf = newbuf; strcpy(buf + length, string); length = newlength; if (ap->a_type == A_SEMI) buf[length-1] = '\n'; else buf[length-1] = ' '; } if (length && buf[length-1] == ' ') { if (newbuf = t_resizebytes(buf, length, length-1)) { buf = newbuf; length--; } } exp_string = buf; exp_strlen = length; } #endif exp_string = (char *)t_resizebytes(exp_string, exp_strlen,exp_strlen+1); exp_string[exp_strlen] = 0; expr->exp_string = exp_string; expr->exp_str = exp_string; expr->exp_nexpr = 0; ret = (struct ex_ex *) 0; /* * if ret == 0 it means that we have no expression * so we let the pass go through to build a single null stack */ while (*expr->exp_str || !ret) { list = ex_lex(expr, &max_node); if (!list) { /* syntax error */ goto error; } expr->exp_stack[expr->exp_nexpr] = (struct ex_ex *)fts_malloc(max_node * sizeof (struct ex_ex)); if (!expr->exp_stack[expr->exp_nexpr]) { post_error( (fts_object_t *) expr, "expr: malloc for expr nodes failed\n"); goto error; } expr->exp_stack[expr->exp_nexpr][max_node-1].ex_type=0; expr->exp_nexpr++; ret = ex_match(list, (long)0); if (expr->exp_nexpr > MAX_VARS) /* we cannot exceed MAX_VARS '$' variables */ { post_error((fts_object_t *) expr, "expr: too many variables (maximum %d allowed)", MAX_VARS); goto error; } if (!ret) /* syntax error */ goto error; ret = ex_parse(expr, list, expr->exp_stack[expr->exp_nexpr - 1], (long *)0); if (!ret || ex_checklval(expr->exp_stack[expr->exp_nexpr - 1])) goto error; fts_free(list); } *ret = nullex; t_freebytes(exp_string, exp_strlen+1); return (0); error: for (i = 0; i < expr->exp_nexpr; i++) { fts_free(expr->exp_stack[i]); expr->exp_stack[i] = 0; } expr->exp_nexpr = 0; if (list) fts_free(list); t_freebytes(exp_string, exp_strlen+1); return (1); } /* * ex_lex -- This routine is a bit more than a lexical parser since it will * also do some syntax checking. It reads the string s and will * return a linked list of struct ex_ex. * It will also put the number of the nodes in *n. */ struct ex_ex * ex_lex(struct expr *expr, long int *n) { struct ex_ex *list_arr; struct ex_ex *exptr; long non = 0; /* number of nodes */ long maxnode = 0; list_arr = (struct ex_ex *)fts_malloc(sizeof (struct ex_ex) * MINODES); if (! list_arr) { post("ex_lex: no mem\n"); return ((struct ex_ex *)0); } exptr = list_arr; maxnode = MINODES; while (8) { if (non >= maxnode) { maxnode += MINODES; list_arr = fts_realloc((void *)list_arr, sizeof (struct ex_ex) * maxnode); if (!list_arr) { post("ex_lex: no mem\n"); return ((struct ex_ex *)0); } exptr = &(list_arr)[non]; } if (getoken(expr, exptr)) { fts_free(list_arr); return ((struct ex_ex *)0); } non++; if (!exptr->ex_type) break; exptr++; } *n = non; return list_arr; } /* * ex_match -- this routine walks through the eptr and matches the * perentheses and brackets, it also converts the function * names to a pointer to the describing structure of the * specified function */ /* operator to match */ struct ex_ex * ex_match(struct ex_ex *eptr, long int op) { int firstone = 1; struct ex_ex *ret; t_ex_func *fun; for (; 8; eptr++, firstone = 0) { switch (eptr->ex_type) { case 0: if (!op) return (eptr); post("expr syntax error: an open %s not matched\n", op == OP_RP ? "parenthesis" : "bracket"); return (exNULL); case ET_INT: case ET_FLT: case ET_II: case ET_FI: case ET_SI: case ET_VI: case ET_SYM: case ET_VSYM: continue; case ET_YO: if (eptr[1].ex_type != ET_OP || eptr[1].ex_op != OP_LB) eptr->ex_type = ET_YOM1; continue; case ET_XI: if (eptr[1].ex_type != ET_OP || eptr[1].ex_op != OP_LB) eptr->ex_type = ET_XI0; continue; /* * tables, functions, parenthesis, and brackets, are marked as * operations, and they are assigned their proper operation * in this function. Thus, if we arrive to any of these in this * type tokens at this location, we must have had some error */ case ET_TBL: case ET_FUNC: case ET_LP: case ET_LB: post("ex_match: unexpected type, %ld\n", eptr->ex_type); return (exNULL); case ET_OP: if (op == eptr->ex_op) return (eptr); /* * if we are looking for a right peranthesis * or a right bracket and find the other kind, * it has to be a syntax error */ if ((eptr->ex_op == OP_RP && op == OP_RB) || (eptr->ex_op == OP_RB && op == OP_RP)) { post("expr syntax error: prenthesis or brackets not matched\n"); return (exNULL); } /* * Up to now we have marked the unary minuses as * subrtacts. Any minus that is the first one in * chain or is preceded by anything except ')' and * ']' is a unary minus. */ if (eptr->ex_op == OP_SUB) { ret = eptr - 1; if (firstone || (ret->ex_type == ET_OP && ret->ex_op != OP_RB && ret->ex_op != OP_RP)) eptr->ex_op = OP_UMINUS; } else if (eptr->ex_op == OP_LP) { ret = ex_match(eptr + 1, OP_RP); if (!ret) return (ret); eptr->ex_type = ET_LP; eptr->ex_ptr = (char *) ret; eptr = ret; } else if (eptr->ex_op == OP_LB) { ret = ex_match(eptr + 1, OP_RB); if (!ret) return (ret); /* * this is a special case handling * for $1, $2 processing in Pd * * Pure data translates $#[x] (e.g. $1[x]) to 0[x] * for abstracting patches so that later * in the instantiation of the abstraction * the $# is replaced with the proper argument * of the abstraction * so we change 0[x] to a special table pointing to null * and catch errors in execution time */ if (!firstone && (eptr - 1)->ex_type == ET_INT && ((eptr - 1)->ex_int == 0)) { (eptr - 1)->ex_type = ET_TBL; (eptr - 1)->ex_ptr = (char *)0; } eptr->ex_type = ET_LB; eptr->ex_ptr = (char *) ret; eptr = ret; } continue; case ET_STR: if (eptr[1].ex_op == OP_LB) { char *tmp; eptr->ex_type = ET_TBL; tmp = eptr->ex_ptr; if (ex_getsym(tmp, (t_symbol **)&(eptr->ex_ptr))) { post("expr: syntax error: problms with ex_getsym\n"); return (exNULL); } fts_free((void *)tmp); } else if (eptr[1].ex_op == OP_LP) { fun = find_func(eptr->ex_ptr); if (!fun) { post( "expr: error: function %s not found\n", eptr->ex_ptr); return (exNULL); } eptr->ex_type = ET_FUNC; eptr->ex_ptr = (char *) fun; } else { char *tmp; if (eptr[1].ex_type && eptr[1].ex_type!=ET_OP){ post("expr: syntax error: bad string '%s'\n", eptr->ex_ptr); return (exNULL); } /* it is a variable */ eptr->ex_type = ET_VAR; tmp = eptr->ex_ptr; if (ex_getsym(tmp, (t_symbol **)&(eptr->ex_ptr))) { post("expr: variable '%s' not found",tmp); return (exNULL); } } continue; default: post("ex_match: bad type\n"); return (exNULL); } } /* NOTREACHED */ } /* * ex_parse -- This function is called when we have already done some * parsing on the expression, and we have already matched * our brackets and parenthesis. The main job of this * function is to convert the infix expression to the * prefix form. * First we find the operator with the lowest precedence and * put it on the stack ('optr', it is really just an array), then * we call ourself (ex_parse()), on its arguments (unary operators * only have one operator.) * When "argc" is set it means that we are parsing the arguments * of a function and we will increment *argc anytime we find * a segment that can qualify as an argument (counting commas). * * returns 0 on syntax error */ /* number of argument separated by comma */ struct ex_ex * ex_parse(struct expr *x, struct ex_ex *iptr, struct ex_ex *optr, long int *argc) { struct ex_ex *eptr; struct ex_ex *lowpre = 0; /* pointer to the lowest precedence */ struct ex_ex savex = { 0 }; struct ex_ex *tmpex; long pre = HI_PRE; long count; if (!iptr) { post("ex_parse: input is null, iptr = 0x%lx\n", iptr); return (exNULL); } if (!iptr->ex_type) return (exNULL); /* * the following loop finds the lowest precedence operator in the * the input token list, comma is explicitly checked here since * that is a special operator and is only legal in functions */ for (eptr = iptr, count = 0; eptr->ex_type; eptr++, count++) switch (eptr->ex_type) { case ET_SYM: case ET_VSYM: if (!argc) { post("expr: syntax error: symbols allowed for functions only\n"); ex_print(eptr); return (exNULL); } /* falls through */ case ET_INT: case ET_FLT: case ET_II: case ET_FI: case ET_XI0: case ET_YOM1: case ET_VI: case ET_VAR: if (!count && !eptr[1].ex_type) { *optr = *eptr; tmpex = optr; tmpex->ex_end = ++optr; return (optr); } break; case ET_XI: case ET_YO: case ET_SI: case ET_TBL: if (eptr[1].ex_type != ET_LB) { post("expr: syntax error: brackets missing\n"); ex_print(eptr); return (exNULL); } /* if this table is the only token, parse the table */ if (!count && !((struct ex_ex *) eptr[1].ex_ptr)[1].ex_type) { savex = *((struct ex_ex *) eptr[1].ex_ptr); *((struct ex_ex *) eptr[1].ex_ptr) = nullex; *optr = *eptr; lowpre = ex_parse(x, &eptr[2], optr + 1, (long *)0); *((struct ex_ex *) eptr[1].ex_ptr) = savex; optr->ex_end = lowpre; return(lowpre); } eptr = (struct ex_ex *) eptr[1].ex_ptr; break; case ET_OP: if (eptr->ex_op == OP_COMMA) { if (!argc || !count || !eptr[1].ex_type) { post("expr: syntax error: illegal comma\n"); ex_print(eptr[1].ex_type ? eptr : iptr); return (exNULL); } } if (!eptr[1].ex_type) { post("expr: syntax error: missing operand\n"); ex_print(iptr); return (exNULL); } if ((eptr->ex_op & PRE_MASK) <= pre) { pre = eptr->ex_op & PRE_MASK; lowpre = eptr; } break; case ET_FUNC: if (eptr[1].ex_type != ET_LP) { post("expr: ex_parse: no parenthesis\n"); return (exNULL); } /* if this function is the only token, parse it */ if (!count && !((struct ex_ex *) eptr[1].ex_ptr)[1].ex_type) { long ac; if (eptr[1].ex_ptr == (char *) &eptr[2]) { post("expr: syntax error: missing argument\n"); ex_print(eptr); return (exNULL); } ac = 0; savex = *((struct ex_ex *) eptr[1].ex_ptr); *((struct ex_ex *) eptr[1].ex_ptr) = nullex; *optr = *eptr; lowpre = ex_parse(x, &eptr[2], optr + 1, &ac); if (!lowpre) return (exNULL); ac++; if (ac != ((t_ex_func *)eptr->ex_ptr)->f_argc){ post("expr: syntax error: function '%s' needs %ld arguments\n", ((t_ex_func *)eptr->ex_ptr)->f_name, ((t_ex_func *)eptr->ex_ptr)->f_argc); return (exNULL); } *((struct ex_ex *) eptr[1].ex_ptr) = savex; optr->ex_end = lowpre; return (lowpre); } eptr = (struct ex_ex *) eptr[1].ex_ptr; break; case ET_LP: case ET_LB: if (!count && !((struct ex_ex *) eptr->ex_ptr)[1].ex_type) { if (eptr->ex_ptr == (char *)(&eptr[1])) { post("expr: syntax error: empty '%s'\n", eptr->ex_type==ET_LP?"()":"[]"); ex_print(eptr); return (exNULL); } savex = *((struct ex_ex *) eptr->ex_ptr); *((struct ex_ex *) eptr->ex_ptr) = nullex; lowpre = ex_parse(x, &eptr[1], optr, (long *)0); *((struct ex_ex *) eptr->ex_ptr) = savex; optr->ex_end = lowpre; return (lowpre); } eptr = (struct ex_ex *)eptr->ex_ptr; break; case ET_STR: default: ex_print(eptr); post("expr: ex_parse: type = 0x%lx\n", eptr->ex_type); return (exNULL); } if (pre == HI_PRE) { post("expr: syntax error: missing operation\n"); ex_print(iptr); return (exNULL); } if (count < 2) { post("expr: syntax error: mission operand\n"); ex_print(iptr); return (exNULL); } if (count == 2) { if (lowpre != iptr) { post("expr: ex_parse: unary operator should be first\n"); return (exNULL); } if (!unary_op(lowpre->ex_op)) { post("expr: syntax error: not a uniary operator\n"); ex_print(iptr); return (exNULL); } *optr = *lowpre; eptr = ex_parse(x, &lowpre[1], optr + 1, argc); optr->ex_end = eptr; return (eptr); } /* this is the case of using unary operator as a binary opetator */ if (count == 3 && unary_op(lowpre->ex_op)) { post("expr: syntax error, missing operand before unary operator\n"); ex_print(iptr); return (exNULL); } if (lowpre == iptr) { post("expr: syntax error: mission operand\n"); ex_print(iptr); return (exNULL); } savex = *lowpre; *lowpre = nullex; if (savex.ex_op != OP_COMMA) { *optr = savex; eptr = ex_parse(x, iptr, optr + 1, argc); } else { (*argc)++; eptr = ex_parse(x, iptr, optr, argc); } if (eptr) { eptr = ex_parse(x, &lowpre[1], eptr, argc); *lowpre = savex; } optr->ex_end = eptr; return (eptr); } /* * ex_checklval -- check the left value for all stores ('=') * all left values should either be a variable or a table * return 1 if syntax error * return 0 on success */ static int ex_checklval(struct ex_ex *eptr) { struct ex_ex *extmp; extmp = eptr->ex_end; while (eptr->ex_type && eptr != extmp) { if (eptr->ex_type == ET_OP && eptr->ex_op == OP_STORE) { if (eptr[1].ex_type != ET_VAR && eptr[1].ex_type != ET_SI && eptr[1].ex_type != ET_TBL) { post("Bad left value: "); ex_print(eptr); return (1); } } eptr++; } return (0); } /* * this is the divide zero check for a non divide operator */ #define DZC(ARG1,OPR,ARG2) (ARG1 OPR ARG2) #define EVAL(OPR); \ eptr = ex_eval(expr, ex_eval(expr, eptr, &left, idx), &right, idx); \ switch (left.ex_type) { \ case ET_INT: \ switch(right.ex_type) { \ case ET_INT: \ if (optr->ex_type == ET_VEC) { \ op = optr->ex_vec; \ scalar = (t_float)DZC(left.ex_int, OPR, right.ex_int); \ for (j = 0; j < expr->exp_vsize; j++) \ *op++ = scalar; \ } else { \ optr->ex_type = ET_INT; \ optr->ex_int = DZC(left.ex_int, OPR, right.ex_int); \ } \ break; \ case ET_FLT: \ if (optr->ex_type == ET_VEC) { \ op = optr->ex_vec; \ scalar = DZC(((t_float)left.ex_int), OPR, right.ex_flt);\ for (j = 0; j < expr->exp_vsize; j++) \ *op++ = scalar; \ } else { \ optr->ex_type = ET_FLT; \ optr->ex_flt = DZC(((t_float)left.ex_int), OPR, \ right.ex_flt); \ } \ break; \ case ET_VEC: \ case ET_VI: \ if (optr->ex_type != ET_VEC) { \ if (optr->ex_type == ET_VI) { \ post("expr~: Int. error %d", __LINE__); \ abort(); \ } \ optr->ex_type = ET_VEC; \ optr->ex_vec = (t_float *) \ fts_malloc(sizeof (t_float)*expr->exp_vsize); \ } \ scalar = left.ex_int; \ rp = right.ex_vec; \ op = optr->ex_vec; \ for (i = 0; i < expr->exp_vsize; i++) { \ *op++ = DZC (scalar, OPR, *rp); \ rp++; \ } \ break; \ case ET_SYM: \ default: \ post_error((fts_object_t *) expr, \ "expr: ex_eval(%d): bad right type %ld\n", \ __LINE__, right.ex_type); \ nullret = 1; \ } \ break; \ case ET_FLT: \ switch(right.ex_type) { \ case ET_INT: \ if (optr->ex_type == ET_VEC) { \ op = optr->ex_vec; \ scalar = DZC((t_float) left.ex_flt, OPR, right.ex_int); \ for (j = 0; j < expr->exp_vsize; j++) \ *op++ = scalar; \ } else { \ optr->ex_type = ET_FLT; \ optr->ex_flt = DZC(left.ex_flt, OPR, right.ex_int); \ } \ break; \ case ET_FLT: \ if (optr->ex_type == ET_VEC) { \ op = optr->ex_vec; \ scalar = DZC(left.ex_flt, OPR, right.ex_flt); \ for (j = 0; j < expr->exp_vsize; j++) \ *op++ = scalar; \ } else { \ optr->ex_type = ET_FLT; \ optr->ex_flt= DZC(left.ex_flt, OPR, right.ex_flt); \ } \ break; \ case ET_VEC: \ case ET_VI: \ if (optr->ex_type != ET_VEC) { \ if (optr->ex_type == ET_VI) { \ post("expr~: Int. error %d", __LINE__); \ abort(); \ } \ optr->ex_type = ET_VEC; \ optr->ex_vec = (t_float *) \ fts_malloc(sizeof (t_float)*expr->exp_vsize); \ } \ scalar = left.ex_flt; \ rp = right.ex_vec; \ op = optr->ex_vec; \ for (i = 0; i < expr->exp_vsize; i++) { \ *op++ = DZC(scalar, OPR, *rp); \ rp++; \ } \ break; \ case ET_SYM: \ default: \ post_error((fts_object_t *) expr, \ "expr: ex_eval(%d): bad right type %ld\n", \ __LINE__, right.ex_type); \ nullret = 1; \ } \ break; \ case ET_VEC: \ case ET_VI: \ if (optr->ex_type != ET_VEC) { \ if (optr->ex_type == ET_VI) { \ post("expr~: Int. error %d", __LINE__); \ abort(); \ } \ optr->ex_type = ET_VEC; \ optr->ex_vec = (t_float *) \ fts_malloc(sizeof (t_float)*expr->exp_vsize); \ } \ op = optr->ex_vec; \ lp = left.ex_vec; \ switch(right.ex_type) { \ case ET_INT: \ scalar = right.ex_int; \ for (i = 0; i < expr->exp_vsize; i++) { \ *op++ = DZC(*lp, OPR, scalar); \ lp++; \ } \ break; \ case ET_FLT: \ scalar = right.ex_flt; \ for (i = 0; i < expr->exp_vsize; i++) { \ *op++ = DZC(*lp, OPR, scalar); \ lp++; \ } \ break; \ case ET_VEC: \ case ET_VI: \ rp = right.ex_vec; \ for (i = 0; i < expr->exp_vsize; i++) { \ /* \ * on a RISC processor one could copy \ * 8 times in each round to get a considerable \ * improvement \ */ \ *op++ = DZC(*lp, OPR, *rp); \ rp++; lp++; \ } \ break; \ case ET_SYM: \ default: \ post_error((fts_object_t *) expr, \ "expr: ex_eval(%d): bad right type %ld\n", \ __LINE__, right.ex_type); \ nullret = 1; \ } \ break; \ case ET_SYM: \ default: \ post_error((fts_object_t *) expr, \ "expr: ex_eval(%d): bad left type %ld\n", \ __LINE__, left.ex_type); \ } \ break; /* * evaluate a unary operator, TYPE is applied to float operands */ #define EVAL_UNARY(OPR, TYPE) \ eptr = ex_eval(expr, eptr, &left, idx); \ switch(left.ex_type) { \ case ET_INT: \ if (optr->ex_type == ET_VEC) { \ ex_mkvector(optr->ex_vec,(t_float)(OPR left.ex_int),\ expr->exp_vsize);\ break; \ } \ optr->ex_type = ET_INT; \ optr->ex_int = OPR left.ex_int; \ break; \ case ET_FLT: \ if (optr->ex_type == ET_VEC) { \ ex_mkvector(optr->ex_vec, OPR (TYPE left.ex_flt),\ expr->exp_vsize);\ break; \ } \ optr->ex_type = ET_FLT; \ optr->ex_flt = OPR (TYPE left.ex_flt); \ break; \ case ET_VI: \ case ET_VEC: \ j = expr->exp_vsize; \ if (optr->ex_type != ET_VEC) { \ optr->ex_type = ET_VEC; \ optr->ex_vec = (t_float *) \ fts_malloc(sizeof (t_float)*expr->exp_vsize); \ } \ op = optr->ex_vec; \ lp = left.ex_vec; \ j = expr->exp_vsize; \ for (i = 0; i < j; i++) \ *op++ = OPR (TYPE *lp++); \ break; \ default: \ post_error((fts_object_t *) expr, \ "expr: ex_eval(%d): bad left type %ld\n", \ __LINE__, left.ex_type); \ nullret++; \ } \ break; void ex_mkvector(t_float *fp, t_float x, int size) { while (size--) *fp++ = x; } /* * ex_dzdetect -- divide by zero detected */ void ex_dzdetect(struct expr *expr) { char *etype; if ((!expr->exp_error) & EE_DZ) { if (IS_EXPR(expr)) etype = "expr"; else if (IS_EXPR_TILDE(expr)) etype = "expr~"; else if (IS_FEXPR_TILDE(expr)) etype = "fexpr~"; else { post ("expr -- ex_dzdetect internal error"); etype = ""; } post ("%s divide by zero detected", etype); expr->exp_error |= EE_DZ; } } /* * ex_eval -- evaluate the array of prefix expression * ex_eval returns the pointer to the first unevaluated node * in the array. This is a recursive routine. */ /* SDY - potential memory leak all the returns in this function need to be changed so that the code ends up at the end to check for newly allocated right and left vectors which need to be freed look into the variable nullret */ struct ex_ex * ex_eval(struct expr *expr, struct ex_ex *eptr, struct ex_ex *optr, int idx) /* the expr object data pointer */ /* the operation stack */ /* the result pointer */ /* the sample number processed for fexpr~ */ { int i, j; t_float *lp, *rp, *op; /* left, right, and out pointer to vectors */ t_float scalar; int nullret = 0; /* did we have an error */ struct ex_ex left = { 0 }, right = { 0 }; /* left and right operands */ left.ex_type = 0; left.ex_int = 0; right.ex_type = 0; right.ex_int = 0; if (!eptr) return (exNULL); switch (eptr->ex_type) { case ET_INT: if (optr->ex_type == ET_VEC) ex_mkvector(optr->ex_vec, (t_float) eptr->ex_int, expr->exp_vsize); else *optr = *eptr; return (++eptr); case ET_FLT: if (optr->ex_type == ET_VEC) ex_mkvector(optr->ex_vec, eptr->ex_flt, expr->exp_vsize); else *optr = *eptr; return (++eptr); case ET_SYM: if (optr->ex_type == ET_VEC) { post_error((fts_object_t *) expr, "expr: ex_eval: cannot turn string to vector\n"); return (exNULL); } *optr = *eptr; return (++eptr); case ET_II: if (eptr->ex_int == -1) { post_error((fts_object_t *) expr, "expr: ex_eval: inlet number not set\n"); return (exNULL); } if (optr->ex_type == ET_VEC) { ex_mkvector(optr->ex_vec, (t_float)expr->exp_var[eptr->ex_int].ex_int, expr->exp_vsize); } else { optr->ex_type = ET_INT; optr->ex_int = expr->exp_var[eptr->ex_int].ex_int; } return (++eptr); case ET_FI: if (eptr->ex_int == -1) { post_error((fts_object_t *) expr, "expr: ex_eval: inlet number not set\n"); return (exNULL); } if (optr->ex_type == ET_VEC) { ex_mkvector(optr->ex_vec, expr->exp_var[eptr->ex_int].ex_flt, expr->exp_vsize); } else { optr->ex_type = ET_FLT; optr->ex_flt = expr->exp_var[eptr->ex_int].ex_flt; } return (++eptr); case ET_VSYM: if (optr->ex_type == ET_VEC) { post_error((fts_object_t *) expr, "expr: IntErr. vsym in for vec out\n"); return (exNULL); } if (eptr->ex_int == -1) { post_error((fts_object_t *) expr, "expr: ex_eval: inlet number not set\n"); return (exNULL); } optr->ex_type = ET_SYM; optr->ex_ptr = expr->exp_var[eptr->ex_int].ex_ptr; return(++eptr); case ET_VI: if (optr->ex_type != ET_VEC) *optr = expr->exp_var[eptr->ex_int]; else if (optr->ex_vec != expr->exp_var[eptr->ex_int].ex_vec) memcpy(optr->ex_vec, expr->exp_var[eptr->ex_int].ex_vec, expr->exp_vsize * sizeof (t_float)); return(++eptr); case ET_VEC: if (optr->ex_type != ET_VEC) { optr->ex_type = ET_VEC; optr->ex_vec = eptr->ex_vec; eptr->ex_type = ET_INT; eptr->ex_int = 0; } else if (optr->ex_vec != eptr->ex_vec) { memcpy(optr->ex_vec, eptr->ex_vec, expr->exp_vsize * sizeof (t_float)); fts_free(eptr->ex_vec); } else { /* this should not happen */ post("expr int. error, optr->ex_vec = %d",optr->ex_vec); abort(); } return(++eptr); case ET_XI0: /* short hand for $x?[0] */ /* SDY delete the following check */ if (!IS_FEXPR_TILDE(expr) || optr->ex_type==ET_VEC) { post("%d:exp->exp_flags = %d", __LINE__,expr->exp_flags); abort(); } optr->ex_type = ET_FLT; optr->ex_flt = expr->exp_var[eptr->ex_int].ex_vec[idx]; return(++eptr); case ET_YOM1: /* * short hand for $y?[-1] * if we are calculating the first sample of the vector * we need to look at the previous results buffer */ optr->ex_type = ET_FLT; if (idx == 0) optr->ex_flt = expr->exp_p_res[eptr->ex_int][expr->exp_vsize - 1]; else optr->ex_flt=expr->exp_tmpres[eptr->ex_int][idx-1]; return(++eptr); case ET_YO: case ET_XI: /* SDY delete the following */ if (!IS_FEXPR_TILDE(expr) || optr->ex_type==ET_VEC) { post("%d:expr->exp_flags = %d", __LINE__,expr->exp_flags); abort(); } return (eval_sigidx(expr, eptr, optr, idx)); case ET_TBL: case ET_SI: return (eval_tab(expr, eptr, optr, idx)); case ET_FUNC: return (eval_func(expr, eptr, optr, idx)); case ET_VAR: return (eval_var(expr, eptr, optr, idx)); case ET_OP: break; case ET_STR: case ET_LP: case ET_LB: default: post_error((fts_object_t *) expr, "expr: ex_eval: unexpected type %d\n", (int)eptr->ex_type); return (exNULL); } if (!eptr[1].ex_type) { post_error((fts_object_t *) expr, "expr: ex_eval: not enough nodes 1\n"); return (exNULL); } if (!unary_op(eptr->ex_op) && !eptr[2].ex_type) { post_error((fts_object_t *) expr, "expr: ex_eval: not enough nodes 2\n"); return (exNULL); } switch((eptr++)->ex_op) { case OP_STORE: return (eval_store(expr, eptr, optr, idx)); case OP_NOT: EVAL_UNARY(!, +); case OP_NEG: EVAL_UNARY(~, (long)); case OP_UMINUS: EVAL_UNARY(-, +); case OP_MUL: EVAL(*); case OP_ADD: EVAL(+); case OP_SUB: EVAL(-); case OP_LT: EVAL(<); case OP_LE: EVAL(<=); case OP_GT: EVAL(>); case OP_GE: EVAL(>=); case OP_EQ: EVAL(==); case OP_NE: EVAL(!=); /* * following operators convert their argument to integer */ #undef DZC #define DZC(ARG1,OPR,ARG2) (((int)ARG1) OPR ((int)ARG2)) case OP_SL: EVAL(<<); case OP_SR: EVAL(>>); case OP_AND: EVAL(&); case OP_XOR: EVAL(^); case OP_OR: EVAL(|); case OP_LAND: EVAL(&&); case OP_LOR: EVAL(||); /* * for modulo we need to convert to integer and check for divide by zero */ #undef DZC #define DZC(ARG1,OPR,ARG2) ((((int)ARG2)?(((int)ARG1) OPR ((int)ARG2)) \ : (ex_dzdetect(expr),0))) case OP_MOD: EVAL(%); /* * define the divide by zero check for divide */ #undef DZC #define DZC(ARG1,OPR,ARG2) (((ARG2)?(ARG1 OPR ARG2):(ex_dzdetect(expr),0))) case OP_DIV: EVAL(/); case OP_LP: case OP_RP: case OP_LB: case OP_RB: case OP_COMMA: case OP_SEMI: default: post_error((fts_object_t *) expr, "expr: ex_print: bad op 0x%x\n", (unsigned)eptr->ex_op); return (exNULL); } /* * the left and right nodes could have been transformed to vectors * down the chain */ if (left.ex_type == ET_VEC) fts_free(left.ex_vec); if (right.ex_type == ET_VEC) fts_free(right.ex_vec); if (nullret) return (exNULL); else return (eptr); } extern struct ex_ex * ex_if(t_expr *expr, struct ex_ex *eptr, struct ex_ex *optr,struct ex_ex *argv, int idx); /* * eval_func -- evaluate a function, call ex_eval() on all the arguments * so that all of them are terminal nodes. The call the * appropriate function */ struct ex_ex * eval_func(struct expr *expr, struct ex_ex *eptr, struct ex_ex *optr, int idx) /* the expr object data pointer */ /* the operation stack */ /* the result pointer */ { int i; struct ex_ex args[MAX_ARGS]; t_ex_func *f; f = (t_ex_func *)(eptr++)->ex_ptr; if (!f || !f->f_name) { return (exNULL); } if (f->f_argc > MAX_ARGS) { post_error((fts_object_t *) expr, "expr: eval_func: asking too many arguments\n"); return (exNULL); } /* * We treat the "if" function differently to be able to evaluate * the args selectively based on the truth value of the "condition" */ if (f->f_func != (void (*)) ex_if) { for (i = 0; i < f->f_argc; i++) { args[i].ex_type = 0; args[i].ex_int = 0; eptr = ex_eval(expr, eptr, &args[i], idx); } (*f->f_func)(expr, f->f_argc, args, optr); } else { for (i = 0; i < f->f_argc; i++) { args[i].ex_type = 0; args[i].ex_int = 0; } eptr = ex_if(expr, eptr, optr, args, idx); } for (i = 0; i < f->f_argc; i++) { if (args[i].ex_type == ET_VEC) fts_free(args[i].ex_vec); } return (eptr); } /* * eval_store -- evaluate the '=' operator, * make sure the first operator is a legal left operator * and call ex_eval on the right operator */ struct ex_ex * eval_store(struct expr *expr, struct ex_ex *eptr, struct ex_ex *optr, int idx) /* the expr object data pointer */ /* the operation stack */ /* the result pointer */ { struct ex_ex arg = { 0 }; struct ex_ex rval = { 0 }; struct ex_ex *retp; char *tbl = (char *) 0; char *var = (char *) 0; int badleft = 0; int notable = 0; arg.ex_type = ET_INT; arg.ex_int = 0; switch (eptr->ex_type) { case ET_VAR: var = (char *) eptr->ex_ptr; eptr = ex_eval(expr, ++eptr, &arg, idx); if (max_ex_var_store(expr, (t_symbol *)var, &arg, optr)) retp = exNULL; else retp = eptr; if (arg.ex_type == ET_VEC) fts_free(arg.ex_vec); return(retp); case ET_TBL: tbl = (char *) eptr->ex_ptr; break; case ET_SI: if (!expr->exp_var[eptr->ex_int].ex_ptr) { if (!(expr->exp_error & EE_NOTABLE)) { post("expr: syntax error: no string for inlet %d", eptr->ex_int + 1); post("expr: No more table errors will be reported"); post("expr: till the next reset"); expr->exp_error |= EE_NOTABLE; } badleft++; post("Bad left value: "); /* report Error */ ex_print(eptr); retp = exNULL; return (retp); } else { tbl = (char *) expr->exp_var[eptr->ex_int].ex_ptr; } break; default: post("Bad left value: "); /* report Error */ ex_print(eptr); retp = exNULL; return (retp); } arg.ex_type = 0; arg.ex_int = 0; /* evaluate the index of the table */ if (!(eptr = ex_eval(expr, ++eptr, &arg, idx))) return (eptr); /* evaluate the right index of the table */ if (!(eptr = ex_eval(expr, eptr, &rval, idx))) return (eptr); optr->ex_type = ET_INT; optr->ex_int = 0; if (!notable || badleft) (void)max_ex_tab_store(expr, (t_symbol *)tbl, &arg, &rval, optr); if (arg.ex_type == ET_VEC) fts_free(arg.ex_vec); return (eptr); } /* * eval_tab -- evaluate a table operation */ struct ex_ex * eval_tab(struct expr *expr, struct ex_ex *eptr, struct ex_ex *optr, int idx) /* the expr object data pointer */ /* the operation stack */ /* the result pointer */ { struct ex_ex arg = { 0 }; char *tbl = (char *) 0; int notable = 0; if (eptr->ex_type == ET_SI) { if (!expr->exp_var[eptr->ex_int].ex_ptr) { if (!(expr->exp_error & EE_NOTABLE)) { post("expr: syntax error: no string for inlet %d", eptr->ex_int + 1); post("expr: No more table errors will be reported"); post("expr: till the next reset"); expr->exp_error |= EE_NOTABLE; } notable++; } else tbl = (char *) expr->exp_var[eptr->ex_int].ex_ptr; } else if (eptr->ex_type == ET_TBL) { tbl = (char *) eptr->ex_ptr; if (!tbl) { post("expr: abstraction argument for table not set"); notable++; } } else { post_error((fts_object_t *) expr,"expr: eval_tbl: bad type %ld\n",eptr->ex_type); notable++; } arg.ex_type = 0; arg.ex_int = 0; if (!(eptr = ex_eval(expr, ++eptr, &arg, idx))) return (eptr); optr->ex_type = ET_INT; optr->ex_int = 0; if (!notable) (void)max_ex_tab(expr, (t_symbol *)tbl, &arg, optr); if (arg.ex_type == ET_VEC) fts_free(arg.ex_vec); return (eptr); } /* * eval_var -- evaluate a variable */ struct ex_ex * eval_var(struct expr *expr, struct ex_ex *eptr, struct ex_ex *optr, int idx) /* the expr object data pointer */ /* the operation stack */ /* the result pointer */ { char *var = (char *) 0; int novar = 0; if (eptr->ex_type == ET_SI) { if (!expr->exp_var[eptr->ex_int].ex_ptr) { if (!(expr->exp_error & EE_NOVAR)) { post("expr: syntax error: no string for inlet %d", eptr->ex_int + 1); post("expr: no more table errors will be reported"); post("expr: till the next reset"); expr->exp_error |= EE_NOVAR; } novar++; } else var = (char *) expr->exp_var[eptr->ex_int].ex_ptr; } else if (eptr->ex_type == ET_VAR) var = (char *) eptr->ex_ptr; else { post_error((fts_object_t *) expr, "expr: eval_tbl: bad type %ld\n", eptr->ex_type); novar++; } optr->ex_type = ET_INT; optr->ex_int = 0; if (!novar) (void)max_ex_var(expr, (t_symbol *)var, optr, idx); return (++eptr); } /* * eval_sigidx -- evaluate the value of an indexed signal for fexpr~ */ struct ex_ex * eval_sigidx(struct expr *expr, struct ex_ex *eptr, struct ex_ex *optr, int idx) /* the expr object data pointer */ /* the operation stack */ /* the result pointer */ /* the index */ { struct ex_ex arg = { 0 }; struct ex_ex *reteptr; int i = 0; t_float fi = 0, /* index in float */ rem_i = 0; /* remains of the float */ arg.ex_type = 0; arg.ex_int = 0; reteptr = ex_eval(expr, eptr + 1, &arg, idx); if (arg.ex_type == ET_FLT) { fi = arg.ex_flt; /* float index */ i = (int) arg.ex_flt; /* integer index */ rem_i = arg.ex_flt - i; /* remains of integer */ } else if (arg.ex_type == ET_INT) { fi = arg.ex_int; /* float index */ i = (int) arg.ex_int; /* integer index */ rem_i = 0; } else { post("eval_sigidx: bad res type (%d)", arg.ex_type); } optr->ex_type = ET_FLT; /* * indexing an input vector */ if (eptr->ex_type == ET_XI) { if (fi > 0) { if (!(expr->exp_error & EE_BI_INPUT)) { expr->exp_error |= EE_BI_INPUT; post("expr: input vector index > 0, (vector x%d[%f])", eptr->ex_int + 1, i + rem_i); post("fexpr~: index assumed to be = 0"); post("fexpr~: no error report till next reset"); ex_print(eptr); } /* just replace it with zero */ i = 0; rem_i = 0; } if (cal_sigidx(optr, i, rem_i, idx, expr->exp_vsize, expr->exp_var[eptr->ex_int].ex_vec, expr->exp_p_var[eptr->ex_int])) { if (!(expr->exp_error & EE_BI_INPUT)) { expr->exp_error |= EE_BI_INPUT; post("expr: input vector index < -VectorSize, (vector x%d[%f])", eptr->ex_int + 1, fi); ex_print(eptr); post("fexpr~: index assumed to be = -%d", expr->exp_vsize); post("fexpr~: no error report till next reset"); } } /* * indexing an output vector */ } else if (eptr->ex_type == ET_YO) { /* for output vectors index of zero is not legal */ if (fi >= 0) { if (!(expr->exp_error & EE_BI_OUTPUT)) { expr->exp_error |= EE_BI_OUTPUT; post("fexpr~: bad output index, (%f)", fi); ex_print(eptr); post("fexpr~: no error report till next reset"); post("fexpr~: index assumed to be = -1"); } i = -1; } if (eptr->ex_int >= expr->exp_nexpr) { post("fexpr~: $y%d illegal: not that many exprs", eptr->ex_int); optr->ex_flt = 0; return (reteptr); } if (cal_sigidx(optr, i, rem_i, idx, expr->exp_vsize, expr->exp_tmpres[eptr->ex_int], expr->exp_p_res[eptr->ex_int])) { if (!(expr->exp_error & EE_BI_OUTPUT)) { expr->exp_error |= EE_BI_OUTPUT; post("fexpr~: bad output index, (%f)", fi); ex_print(eptr); post("fexpr~: index assumed to be = -%d", expr->exp_vsize); } } } else { optr->ex_flt = 0; post("fexpr~:eval_sigidx: internal error - unknown vector (%d)", eptr->ex_type); } return (reteptr); } /* * cal_sigidx -- given two tables (one current one previous) calculate an * evaluation of a float index into the vectors by linear * interpolation * return 0 on success, 1 on failure (index out of bound) */ static int cal_sigidx(struct ex_ex *optr, /* The output value */ int i, t_float rem_i,/* integer and fractinal part of index */ int idx, /* index of current fexpr~ processing */ int vsize, /* vector size */ t_float *curvec, t_float *prevec) /* current and previous table */ { int n; n = i + idx; if (n > 0) { /* from the curvec */ if (rem_i) optr->ex_flt = curvec[n] + rem_i * (curvec[n] - curvec[n - 1]); else optr->ex_flt = curvec[n]; return (0); } if (n == 0) { /* * this is the case that the remaining float * is between two tables */ if (rem_i) optr->ex_flt = *curvec + rem_i * (*curvec - prevec[vsize - 1]); else optr->ex_flt = *curvec; return (0); } /* find the index in the saved buffer */ n = vsize + n; if (n > 0) { if (rem_i) optr->ex_flt = prevec[n] + rem_i * (prevec[n] - prevec[n - 1]); else optr->ex_flt = prevec[n]; return (0); } /* out of bound */ optr->ex_flt = *prevec; return (1); } /* * getoken -- return 1 on syntax error otherwise 0 */ int getoken(struct expr *expr, struct ex_ex *eptr) { char *p; long i; if (!expr->exp_str) { post("expr: getoken: expression string not set\n"); return (0); } retry: if (!*expr->exp_str) { eptr->ex_type = 0; eptr->ex_int = 0; return (0); } if (*expr->exp_str == ';') { expr->exp_str++; eptr->ex_type = 0; eptr->ex_int = 0; return (0); } eptr->ex_type = ET_OP; switch (*expr->exp_str++) { case '\\': case ' ': case '\t': goto retry; case ';': post("expr: syntax error: ';' not implemented\n"); return (1); case ',': eptr->ex_op = OP_COMMA; break; case '(': eptr->ex_op = OP_LP; break; case ')': eptr->ex_op = OP_RP; break; case ']': eptr->ex_op = OP_RB; break; case '~': eptr->ex_op = OP_NEG; break; /* we will take care of unary minus later */ case '*': eptr->ex_op = OP_MUL; break; case '/': eptr->ex_op = OP_DIV; break; case '%': eptr->ex_op = OP_MOD; break; case '+': eptr->ex_op = OP_ADD; break; case '-': eptr->ex_op = OP_SUB; break; case '^': eptr->ex_op = OP_XOR; break; case '[': eptr->ex_op = OP_LB; break; case '!': if (*expr->exp_str == '=') { eptr->ex_op = OP_NE; expr->exp_str++; } else eptr->ex_op = OP_NOT; break; case '<': switch (*expr->exp_str) { case '<': eptr->ex_op = OP_SL; expr->exp_str++; break; case '=': eptr->ex_op = OP_LE; expr->exp_str++; break; default: eptr->ex_op = OP_LT; break; } break; case '>': switch (*expr->exp_str) { case '>': eptr->ex_op = OP_SR; expr->exp_str++; break; case '=': eptr->ex_op = OP_GE; expr->exp_str++; break; default: eptr->ex_op = OP_GT; break; } break; case '=': if (*expr->exp_str != '=') eptr->ex_op = OP_STORE; else { expr->exp_str++; eptr->ex_op = OP_EQ; } break; case '&': if (*expr->exp_str == '&') { expr->exp_str++; eptr->ex_op = OP_LAND; } else eptr->ex_op = OP_AND; break; case '|': if (*expr->exp_str == '|') { expr->exp_str++; eptr->ex_op = OP_LOR; } else eptr->ex_op = OP_OR; break; case '$': switch (*expr->exp_str++) { case 'I': case 'i': eptr->ex_type = ET_II; break; case 'F': case 'f': eptr->ex_type = ET_FI; break; case 'S': case 's': eptr->ex_type = ET_SI; break; case 'V': case 'v': if (IS_EXPR_TILDE(expr)) { eptr->ex_type = ET_VI; break; } post("$v? works only for expr~"); post("expr: syntax error: %s\n", &expr->exp_str[-2]); return (1); case 'X': case 'x': if (IS_FEXPR_TILDE(expr)) { eptr->ex_type = ET_XI; if (isdigit((int)(*expr->exp_str))) break; /* for $x[] is a shorhand for $x1[] */ /* eptr->ex_int = 0; */ eptr->ex_op = 0; expr->exp_var[eptr->ex_op].ex_type = eptr->ex_type; goto noinletnum; } post("$x? works only for fexpr~"); post("expr: syntax error: %s\n", &expr->exp_str[-2]); return (1); case 'y': case 'Y': if (IS_FEXPR_TILDE(expr)) { eptr->ex_type = ET_YO; /*$y takes no number */ if (isdigit((int)(*expr->exp_str))) break; /* for $y[] is a shorhand for $y1[] */ /* eptr->ex_int = 0; */ eptr->ex_op = 0; expr->exp_var[eptr->ex_op].ex_type = eptr->ex_type; goto noinletnum; } post("$y works only for fexpr~"); /* falls through */ /* * allow $# for abstraction argument substitution * $1+1 is translated to 0+1 and in abstraction substitution * the value is replaced with the new string */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': p = atoif(--expr->exp_str, &eptr->ex_op, &i); if (!p) return (1); if (i != ET_INT) { post("expr: syntax error: %s\n", expr->exp_str); return (1); } expr->exp_str = p; eptr->ex_type = ET_INT; eptr->ex_int = 0; return (0); default: post("expr: syntax error: %s\n", &expr->exp_str[-2]); return (1); } p = atoif(expr->exp_str, &eptr->ex_op, &i); if (!p) { post("expr: syntax error: %s\n", &expr->exp_str[-2]); return (1); } if (i != ET_INT) { post("expr: syntax error: %s\n", expr->exp_str); return (1); } /* * make the user inlets one based rather than zero based * therefore we decrement the number that user has supplied */ if (!eptr->ex_op || (eptr->ex_op)-- > MAX_VARS) { post("expr: syntax error: inlet or outlet out of range: %s\n", expr->exp_str); return (1); } /* * until we can change the input type of inlets on * the fly (at pd_new() * time) the first input to expr~ is always a vector * and $f1 or $i1 is * illegal for fexpr~ */ if (eptr->ex_op == 0 && (IS_FEXPR_TILDE(expr) || IS_EXPR_TILDE(expr)) && (eptr->ex_type==ET_II || eptr->ex_type==ET_FI || eptr->ex_type==ET_SI)) { post("first inlet of expr~/fexpr~ can only be a vector"); return (1); } /* record the inlet or outlet type and check for consistency */ if (eptr->ex_type == ET_YO ) { /* it is an outlet for fexpr~*/ /* no need to do anything */ ; } else if (!expr->exp_var[eptr->ex_op].ex_type) expr->exp_var[eptr->ex_op].ex_type = eptr->ex_type; else if (expr->exp_var[eptr->ex_op].ex_type != eptr->ex_type) { post("expr: syntax error: inlets can only have one type: %s\n", expr->exp_str); return (1); } expr->exp_str = p; noinletnum: break; case '"': { struct ex_ex ex = { 0 }; p = expr->exp_str; if (!*expr->exp_str || *expr->exp_str == '"') { post("expr: syntax error: empty symbol: %s\n", --expr->exp_str); return (1); } if (getoken(expr, &ex)) return (1); switch (ex.ex_type) { case ET_STR: if (ex_getsym(ex.ex_ptr, (t_symbol **)&(eptr->ex_ptr))) { post("expr: syntax error: getoken: problms with ex_getsym\n"); return (1); } eptr->ex_type = ET_SYM; break; case ET_SI: *eptr = ex; eptr->ex_type = ET_VSYM; break; default: post("expr: syntax error: bad symbol name: %s\n", p); return (1); } if (*expr->exp_str++ != '"') { post("expr: syntax error: missing '\"'\n"); return (1); } break; } case '.': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': p = atoif(--expr->exp_str, &eptr->ex_int, &eptr->ex_type); if (!p) return (1); expr->exp_str = p; break; default: /* * has to be a string, it should either be a * function or a table */ p = --expr->exp_str; for (i = 0; name_ok(*p); i++) p++; if (!i) { post("expr: syntax error: %s\n", expr->exp_str); return (1); } eptr->ex_ptr = (char *)fts_malloc(i + 1); strncpy(eptr->ex_ptr, expr->exp_str, (int) i); (eptr->ex_ptr)[i] = 0; expr->exp_str = p; /* * we mark this as a string and later we will change this * to either a function or a table */ eptr->ex_type = ET_STR; break; } return (0); } /* * atoif -- ascii to float or integer (strtof() understand exponential notations ad strtod() understand hex numbers) */ char * atoif(char *s, long int *value, long int *type) { const char*s0 = s; char *p; long lval; float fval; lval = strtod(s, &p); fval = strtof(s, &p); if (lval != (int) fval) { *type = ET_FLT; *((t_float *) value) = fval; return (p); } while (s != p) { if (*s == 'x' || *s == 'X') break; if (*s == '.' || *s == 'e' || *s == 'E') { *type = ET_FLT; *((t_float *) value) = fval; return (p); } s++; } if(s0 == p) return 0; *type = ET_INT; *((t_int *) value) = lval; return (p); } /* * find_func -- returns a pointer to the found function structure * otherwise it returns 0 */ t_ex_func * find_func(char *s) { t_ex_func *f; for (f = ex_funcs; f->f_name; f++) if (!strcmp(f->f_name, s)) return (f); return ((t_ex_func *) 0); } /* * ex_print -- print an expression array */ void ex_print(struct ex_ex *eptr) { struct ex_ex *extmp; extmp = eptr->ex_end; while (eptr->ex_type && eptr != extmp) { switch (eptr->ex_type) { case ET_INT: post("%ld ", eptr->ex_int); break; case ET_FLT: post("%f ", eptr->ex_flt); break; case ET_STR: post("%s ", eptr->ex_ptr); break; case ET_TBL: if (!eptr->ex_ptr) { /* special case of $# processing */ post("%s ", "$$"); break; } /* falls through */ case ET_VAR: post("%s ", ex_symname((fts_symbol_t )eptr->ex_ptr)); break; case ET_SYM: post("\"%s\" ", ex_symname((fts_symbol_t )eptr->ex_ptr)); break; case ET_VSYM: post("\"$s%ld\" ", eptr->ex_int + 1); break; case ET_FUNC: post("%s ", ((t_ex_func *)eptr->ex_ptr)->f_name); break; case ET_LP: post("%c", '('); break; /* CHANGE case ET_RP: post("%c ", ')'); break; */ case ET_LB: post("%c", '['); break; /* CHANGE case ET_RB: post("%c ", ']'); break; */ case ET_II: post("$i%ld ", eptr->ex_int + 1); break; case ET_FI: post("$f%ld ", eptr->ex_int + 1); break; case ET_SI: post("$s%lx ", eptr->ex_ptr); break; case ET_VI: post("$v%lx ", eptr->ex_vec); break; case ET_VEC: post("vec = %ld ", eptr->ex_vec); break; case ET_YOM1: case ET_YO: post("$y%d", eptr->ex_int + 1); break; case ET_XI: case ET_XI0: post("$x%d", eptr->ex_int + 1); break; case ET_OP: switch (eptr->ex_op) { case OP_LP: post("%c", '('); break; case OP_RP: post("%c ", ')'); break; case OP_LB: post("%c", '['); break; case OP_RB: post("%c ", ']'); break; case OP_NOT: post("%c", '!'); break; case OP_NEG: post("%c", '~'); break; case OP_UMINUS: post("%c", '-'); break; case OP_MUL: post("%c", '*'); break; case OP_DIV: post("%c", '/'); break; case OP_MOD: post("%c", '%'); break; case OP_ADD: post("%c", '+'); break; case OP_SUB: post("%c", '-'); break; case OP_SL: post("%s", "<<"); break; case OP_SR: post("%s", ">>"); break; case OP_LT: post("%c", '<'); break; case OP_LE: post("%s", "<="); break; case OP_GT: post("%c", '>'); break; case OP_GE: post("%s", ">="); break; case OP_EQ: post("%s", "=="); break; case OP_STORE: post("%s", "="); break; case OP_NE: post("%s", "!="); break; case OP_AND: post("%c", '&'); break; case OP_XOR: post("%c", '^'); break; case OP_OR: post("%c", '|'); break; case OP_LAND: post("%s", "&&"); break; case OP_LOR: post("%s", "||"); break; case OP_COMMA: post("%c", ','); break; case OP_SEMI: post("%c", ';'); break; default: post("expr: ex_print: bad op 0x%lx\n", eptr->ex_op); } break; default: post("expr: ex_print: bad type 0x%lx\n", eptr->ex_type); } eptr++; } post("\n"); } #ifdef _WIN32 void ABORT(void) {bug("expr");} #endif ================================================ FILE: libs/libpd/pure-data/src/x_vexp.h ================================================ /* Copyright (c) IRCAM. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* "expr" was written by Shahrokh Yadegari c. 1989. -msp */ /* "expr~" and "fexpr~" conversion by Shahrokh Yadegari c. 1999,2000 */ #define MSP #ifdef PD #undef MSP #endif #ifdef PD #include "m_pd.h" #else /* MSP */ #include "ext.h" #include "z_dsp.h" typedef float t_float; // t_float is from m_pd.h #endif #define fts_malloc malloc #define fts_calloc calloc #define fts_free free #define fts_realloc realloc #define fts_atom_t t_atom #define fts_object_t t_object typedef t_symbol *fts_symbol_t; #ifdef MSP #define t_atom Atom #define t_symbol Symbol #define pd_new(x) newobject(x); #define pd_free(x) freeobject(x); #define t_outlet void #define t_binbuf void typedef t_class *t_pd; typedef float t_floatarg; #include #include #include #include #include void pd_error(void *object, char *fmt, ...); #endif /* MSP */ #define post_error pd_error #define fts_is_floatg(x) ((x)->a_type == A_FLOAT) #define fts_new_symbol_copy gensym #define fts_symbol_name(x) ((x)->s_name) /* * Currently the maximum number of variables (inlets) that are supported * is 10. */ #define MAX_VARS 100 #define MINODES 10 /* was 200 */ /* terminal defines */ /* * operations * (x<<16|y) x defines the level of precedence, * the lower the number the lower the precedence * separators are defines as operators just for convenience */ #define OP_SEMI ((long)(1<<16|1)) /* ; */ #define OP_COMMA ((long)(2<<16|2)) /* , */ #define OP_STORE ((long)(3<<16|28)) /* = */ #define OP_LOR ((long)(4<<16|3)) /* || */ #define OP_LAND ((long)(5<<16|4)) /* && */ #define OP_OR ((long)(6<<16|5)) /* | */ #define OP_XOR ((long)(7<<16|6)) /* ^ */ #define OP_AND ((long)(8<<16|7)) /* & */ #define OP_NE ((long)(9<<16|8)) /* != */ #define OP_EQ ((long)(9<<16|9)) /* == */ #define OP_GE ((long)(10<<16|10)) /* >= */ #define OP_GT ((long)(10<<16|11)) /* > */ #define OP_LE ((long)(10<<16|12)) /* <= */ #define OP_LT ((long)(10<<16|13)) /* < */ #define OP_SR ((long)(11<<16|14)) /* >> */ #define OP_SL ((long)(11<<16|15)) /* << */ #define OP_SUB ((long)(12<<16|16)) /* - */ #define OP_ADD ((long)(12<<16|17)) /* + */ #define OP_MOD ((long)(13<<16|18)) /* % */ #define OP_DIV ((long)(13<<16|19)) /* / */ #define OP_MUL ((long)(13<<16|20)) /* * */ #define OP_UMINUS ((long)(14<<16|21)) /* - unary minus */ #define OP_NEG ((long)(14<<16|22)) /* ~ one complement */ #define OP_NOT ((long)(14<<16|23)) /* ! */ #define OP_RB ((long)(15<<16|24)) /* ] */ #define OP_LB ((long)(15<<16|25)) /* [ */ #define OP_RP ((long)(15<<16|26)) /* ) */ #define OP_LP ((long)(15<<16|27)) /* ( */ #define HI_PRE ((long)(100<<16)) /* infinite precedence */ #define PRE_MASK ((long)0xffff0000) /* precedence level mask */ #define name_ok(c) (((c)=='_') || ((c)>='a' && (c)<='z') || \ ((c)>='A' && (c)<='Z') || ((c) >= '0' && (c) <= '9')) #define unary_op(x) ((x) == OP_NOT || (x) == OP_NEG || (x) == OP_UMINUS) struct ex_ex { union { long v_int; t_float v_flt; t_float *v_vec; /* this is an for allocated vector */ long op; char *ptr; } ex_cont; /* content */ #define ex_int ex_cont.v_int #define ex_flt ex_cont.v_flt #define ex_vec ex_cont.v_vec #define ex_op ex_cont.op #define ex_ptr ex_cont.ptr long ex_type; /* type of the node */ struct ex_ex *ex_end; /* the node after the end of this expression */ }; #define exNULL ((struct ex_ex *)0) /* defines for ex_type */ #define ET_INT 1 /* an int */ #define ET_FLT 2 /* a float */ #define ET_OP 3 /* operator */ #define ET_STR 4 /* string */ #define ET_TBL 5 /* a table, the content is a pointer */ #define ET_FUNC 6 /* a function */ #define ET_SYM 7 /* symbol ("string") */ #define ET_VSYM 8 /* variable symbol ("$s?") */ /* we treat parenthesis and brackets */ /* special to keep a pointer to their */ /* match in the content */ #define ET_LP 9 /* left parenthesis */ #define ET_LB 10 /* left bracket */ #define ET_II 11 /* and integer inlet */ #define ET_FI 12 /* float inlet */ #define ET_SI 13 /* string inlet */ #define ET_VI 14 /* signal inlet */ #define ET_VEC 15 /* allocated signal vector */ /* special types for fexpr~ */ #define ET_YO 16 /* vector output for fexpr~ */ #define ET_YOM1 17 /* shorthand for $y?[-1] */ #define ET_XI 18 /* vector input for fexpr~ */ #define ET_XI0 20 /* shorthand for $x?[0] */ #define ET_VAR 21 /* variable */ /* defines for ex_flags */ #define EF_TYPE_MASK 0x07 /* first three bits define the type of expr */ #define EF_EXPR 0x01 /* expr - control in and out */ #define EF_EXPR_TILDE 0x02 /* expr~ signal and control in, signal out */ #define EF_FEXPR_TILDE 0x04 /* fexpr~ filter expression */ #define EF_STOP 0x08 /* is it stopped used for expr~ and fexpr~ */ #define EF_VERBOSE 0x10 /* verbose mode */ #define IS_EXPR(x) ((((x)->exp_flags&EF_TYPE_MASK)|EF_EXPR) == EF_EXPR) #define IS_EXPR_TILDE(x) \ ((((x)->exp_flags&EF_TYPE_MASK)|EF_EXPR_TILDE)==EF_EXPR_TILDE) #define IS_FEXPR_TILDE(x) \ ((((x)->exp_flags&EF_TYPE_MASK)|EF_FEXPR_TILDE)==EF_FEXPR_TILDE) #define SET_EXPR(x) (x)->exp_flags |= EF_EXPR; \ (x)->exp_flags &= ~EF_EXPR_TILDE; \ (x)->exp_flags &= ~EF_FEXPR_TILDE; #define SET_EXPR_TILDE(x) (x)->exp_flags &= ~EF_EXPR; \ (x)->exp_flags |= EF_EXPR_TILDE; \ (x)->exp_flags &= ~EF_FEXPR_TILDE; #define SET_FEXPR_TILDE(x) (x)->exp_flags &= ~EF_EXPR; \ (x)->exp_flags &= ~EF_EXPR_TILDE; \ (x)->exp_flags |= EF_FEXPR_TILDE; /* * defines for expr_error */ #define EE_DZ 0x01 /* divide by zero error */ #define EE_BI_OUTPUT 0x02 /* Bad output index */ #define EE_BI_INPUT 0x04 /* Bad input index */ #define EE_NOTABLE 0x08 /* NO TABLE */ #define EE_NOVAR 0x10 /* NO VARIABLE */ typedef struct expr { #ifdef PD t_object exp_ob; #else /* MSP */ t_pxobject exp_ob; #endif int exp_flags; /* are we expr~, fexpr~, or expr */ int exp_error; /* reported errors */ int exp_nexpr; /* number of expressions */ char *exp_string; /* the full expression string */ char *exp_str; /* current parsing position */ t_outlet *exp_outlet[MAX_VARS]; #ifdef PD struct _exprproxy *exp_proxy; #else /* MAX */ void *exp_proxy[MAX_VARS]; long exp_proxy_id; #endif struct ex_ex *exp_stack[MAX_VARS]; struct ex_ex exp_var[MAX_VARS]; struct ex_ex exp_res[MAX_VARS]; /* the evluation result */ t_float *exp_p_var[MAX_VARS]; t_float *exp_p_res[MAX_VARS]; /* the previous evaluation result */ t_float *exp_tmpres[MAX_VARS]; /* temporty result for fexpr~ */ int exp_vsize; /* the size of the signal vector */ int exp_nivec; /* # of vector inlets */ t_float exp_f; /* control value to be transformed to signal */ } t_expr; typedef struct ex_funcs { char *f_name; /* function name */ void (*f_func)(t_expr *, long, struct ex_ex *, struct ex_ex *); /* the real function performing the function (void, no return!!!) */ long f_argc; /* number of arguments */ } t_ex_func; /* function prototypes for pd-related functions called within vexp.h */ extern int max_ex_tab_store(struct expr *expr, t_symbol *s, struct ex_ex *arg, struct ex_ex *rval, struct ex_ex *optr); extern int max_ex_tab(struct expr *expr, t_symbol *s, struct ex_ex *arg, struct ex_ex *optr); extern int max_ex_var(struct expr *expr, t_symbol *s, struct ex_ex *optr, int idx); extern int max_ex_var_store(struct expr *, t_symbol *, struct ex_ex *, struct ex_ex *); extern int ex_getsym(char *p, t_symbol **s); extern const char *ex_symname(t_symbol *s); void ex_mkvector(t_float *fp, t_float x, int size); extern void ex_size(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr); extern void ex_sum(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr); extern void ex_Sum(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr); extern void ex_avg(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr); extern void ex_Avg(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr); extern void ex_store(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr); int value_getonly(t_symbol *s, t_float *f); /* These pragmas are only used for MSVC, not MinGW or Cygwin */ #ifdef _MSC_VER #pragma warning (disable: 4305 4244) #endif #ifdef _WIN32 #define abort ABORT void ABORT(void); #endif ================================================ FILE: libs/libpd/pure-data/src/x_vexp_fun.c ================================================ /* Copyright (c) IRCAM. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* "expr" was written by Shahrokh Yadegari c. 1989. -msp * * Nov. 2001 --sdy * conversion for expr~ * * Jan, 2002 --sdy * added fmod() * * May 2002 * added floor and ceil for expr -- Orm Finnendahl * * July 2002 --sdy * added the following math functions: * cbrt - cube root * erf - error function * erfc - complementary error function * expm1 - exponential minus 1, * log1p - logarithm of 1 plus * isinf - is the value infinite, * finite - is the value finite * isnan -- is the resut a nan (Not a number) * copysign - copy sign of a number * ldexp - multiply floating-point number by integral power of 2 * imodf - get signed integral value from floating-point number * modf - get signed fractional value from floating-point number * drem - floating-point remainder function * * The following are done but not popular enough in math libss * to be included yet * hypoth - Euclidean distance function * trunc * round * nearbyint - * November 2015 * - drem() is now obsolete but it is kept here so that other * patches do not break * - added remainder() - floating-point remainder function * - fixed the bug that unary operators could be used as * binary ones (10 ~ 1) * - fixed ceil() and floor() which should have only one argument * - added copysign (the previous one "copysig" which was * defined with one argument was kept for compatibility) * - fixed sum("table"), and Sum("table", x, y) * - deleted avg(), Avg() as they can be simple expressions * - deleted store as this can be achieved by the '=' operator * July 2017 --sdy * * - ex_if() is reworked to only evaluate either the left or the right arg * October 2020 --sdy * - fact() (factorial) now calculates and returns its value in double * - Added mtof(), mtof(), dbtorms(), rmstodb(), powtodb(), dbtopow() * */ /* * vexp_func.c -- this file include all the functions for vexp. * the first two arguments to the function are the number * of argument and an array of arguments (argc, argv) * the last argument is a pointer to a struct ex_ex for * the result. Up do this point, the content of the * struct ex_ex that these functions receive are either * ET_INT (long), ET_FLT (t_float), or ET_SYM (char **, it is * char ** and not char * since NewHandle of Mac returns * a char ** for relocatability.) The common practice in * these functions is that they figure out the type of their * result according to the type of the arguments. In general * the ET_SYM is used an ET_INT when we expect a value. * It is the users responsibility not to pass strings to the * function. */ #include #include #include #include "x_vexp.h" struct ex_ex *ex_eval(struct expr *expr, struct ex_ex *eptr, struct ex_ex *optr, int i); /* forward declarations */ static void ex_min(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_max(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_toint(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_rint(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_tofloat(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_pow(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_exp(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_log(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_ln(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_sin(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_cos(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_asin(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_acos(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_tan(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_atan(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_sinh(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_cosh(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_asinh(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_acosh(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_tanh(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_atanh(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_atan2(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_sqrt(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_fact(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_random(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_abs(t_expr *expr, long int argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_fmod(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_ceil(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_floor(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr); struct ex_ex * ex_if(t_expr *expr, struct ex_ex *argv, struct ex_ex *optr, struct ex_ex *args, int idx); static void ex_ldexp(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_imodf(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_modf(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_mtof(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_ftom(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_dbtorms(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_rmstodb(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_dbtopow(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_powtodb(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr); #if !defined(_MSC_VER) || (_MSC_VER >= 1700) static void ex_cbrt(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_erf(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_erfc(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_expm1(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_log1p(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_isinf(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_finite(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_isnan(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_copysign(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_drem(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_remainder(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_round(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_trunc(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr); static void ex_nearbyint(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr); #endif #ifdef notdef /* the following will be added once they are more popular in math libraries */ static void ex_hypoth(t_expr *expr, long argc, struct ex_ex *argv, struct ex_ex *optr); #endif t_ex_func ex_funcs[] = { {"min", ex_min, 2}, {"max", ex_max, 2}, {"int", ex_toint, 1}, {"rint", ex_rint, 1}, {"float", ex_tofloat, 1}, {"fmod", ex_fmod, 2}, {"floor", ex_floor, 1}, {"ceil", ex_ceil, 1}, {"pow", ex_pow, 2}, {"sqrt", ex_sqrt, 1}, {"exp", ex_exp, 1}, {"log10", ex_log, 1}, {"ln", ex_ln, 1}, {"log", ex_ln, 1}, {"sin", ex_sin, 1}, {"cos", ex_cos, 1}, {"tan", ex_tan, 1}, {"asin", ex_asin, 1}, {"acos", ex_acos, 1}, {"atan", ex_atan, 1}, {"atan2", ex_atan2, 2}, {"sinh", ex_sinh, 1}, {"cosh", ex_cosh, 1}, {"tanh", ex_tanh, 1}, {"fact", ex_fact, 1}, {"random", ex_random, 2}, /* random number */ {"abs", ex_abs, 1}, {"if", (void (*))ex_if, 3}, {"ldexp", ex_ldexp, 2}, {"imodf", ex_imodf, 1}, {"modf", ex_modf, 1}, {"mtof", ex_mtof, 1}, {"ftom", ex_ftom, 1}, {"dbtorms", ex_dbtorms, 1}, {"rmstodb", ex_rmstodb, 1}, {"dbtopow", ex_dbtopow, 1}, {"powtodb", ex_powtodb, 1}, #if !defined(_MSC_VER) || (_MSC_VER >= 1700) {"asinh", ex_asinh, 1}, {"acosh", ex_acosh, 1}, {"atanh", ex_atanh, 1}, /* hyperbolic atan */ {"isnan", ex_isnan, 1}, {"cbrt", ex_cbrt, 1}, {"round", ex_round, 1}, {"trunc", ex_trunc, 1}, {"erf", ex_erf, 1}, {"erfc", ex_erfc, 1}, {"expm1", ex_expm1, 1}, {"log1p", ex_log1p, 1}, {"finite", ex_finite, 1}, {"nearbyint", ex_nearbyint, 1}, {"copysign", ex_copysign, 2}, {"isinf", ex_isinf, 1}, {"remainder", ex_remainder, 2}, #endif #ifdef PD {"size", ex_size, 1}, {"sum", ex_sum, 1}, {"Sum", ex_Sum, 3}, {"avg", ex_avg, 1}, {"Avg", ex_Avg, 3}, #endif #ifdef notdef /* the following will be added once they are more popular in math libraries */ {"hypoth", ex_hypoth, 1}, #endif {0, 0, 0} }; /* * FUN_EVAL -- do type checking, evaluate a function, * if fltret is set return float * otherwise return value based on regular typechecking, */ #define FUNC_EVAL(left, right, func, leftfuncast, rightfuncast, optr, fltret) \ switch (left->ex_type) { \ case ET_INT: \ switch(right->ex_type) { \ case ET_INT: \ if (optr->ex_type == ET_VEC) { \ op = optr->ex_vec; \ scalar = (t_float)func(leftfuncast left->ex_int, \ rightfuncast right->ex_int); \ j = e->exp_vsize; \ while (j--) \ *op++ = scalar; \ } else { \ if (fltret) { \ optr->ex_type = ET_FLT; \ optr->ex_flt = (t_float)func(leftfuncast \ left->ex_int, rightfuncast right->ex_int); \ } else { \ optr->ex_type = ET_INT; \ optr->ex_int = (int)func(leftfuncast \ left->ex_int, rightfuncast right->ex_int); \ } \ } \ break; \ case ET_FLT: \ if (optr->ex_type == ET_VEC) { \ op = optr->ex_vec; \ scalar = (t_float)func(leftfuncast left->ex_int, \ rightfuncast right->ex_flt); \ j = e->exp_vsize; \ while (j--) \ *op++ = scalar; \ } else { \ optr->ex_type = ET_FLT; \ optr->ex_flt = (t_float)func(leftfuncast left->ex_int, \ rightfuncast right->ex_flt); \ } \ break; \ case ET_VEC: \ case ET_VI: \ if (optr->ex_type != ET_VEC) { \ if (optr->ex_type == ET_VI) { \ post("expr~: Int. error %d", __LINE__); \ abort(); \ } \ optr->ex_type = ET_VEC; \ optr->ex_vec = (t_float *) \ fts_malloc(sizeof (t_float)*e->exp_vsize); \ } \ scalar = left->ex_int; \ rp = right->ex_vec; \ op = optr->ex_vec; \ j = e->exp_vsize; \ while (j--) { \ *op++ = (t_float)func(leftfuncast scalar, \ rightfuncast *rp); \ rp++; \ } \ break; \ case ET_SYM: \ default: \ post_error((fts_object_t *) e, \ "expr: FUNC_EVAL(%d): bad right type %ld\n", \ __LINE__, right->ex_type);\ } \ break; \ case ET_FLT: \ switch(right->ex_type) { \ case ET_INT: \ if (optr->ex_type == ET_VEC) { \ op = optr->ex_vec; \ scalar = (t_float)func(leftfuncast left->ex_flt, \ rightfuncast right->ex_int); \ j = e->exp_vsize; \ while (j--) \ *op++ = scalar; \ } else { \ optr->ex_type = ET_FLT; \ optr->ex_flt = (t_float)func(leftfuncast left->ex_flt, \ rightfuncast right->ex_int); \ } \ break; \ case ET_FLT: \ if (optr->ex_type == ET_VEC) { \ op = optr->ex_vec; \ scalar = (t_float)func(leftfuncast left->ex_flt, \ rightfuncast right->ex_flt); \ j = e->exp_vsize; \ while (j--) \ *op++ = scalar; \ } else { \ optr->ex_type = ET_FLT; \ optr->ex_flt = (t_float)func(leftfuncast left->ex_flt, \ rightfuncast right->ex_flt); \ } \ break; \ case ET_VEC: \ case ET_VI: \ if (optr->ex_type != ET_VEC) { \ if (optr->ex_type == ET_VI) { \ post("expr~: Int. error %d", __LINE__); \ abort(); \ } \ optr->ex_type = ET_VEC; \ optr->ex_vec = (t_float *) \ fts_malloc(sizeof (t_float) * e->exp_vsize);\ } \ scalar = left->ex_flt; \ rp = right->ex_vec; \ op = optr->ex_vec; \ j = e->exp_vsize; \ while (j--) { \ *op++ = (t_float)func(leftfuncast scalar, \ rightfuncast *rp); \ rp++; \ } \ break; \ case ET_SYM: \ default: \ post_error((fts_object_t *) e, \ "expr: FUNC_EVAL(%d): bad right type %ld\n", \ __LINE__, right->ex_type);\ } \ break; \ case ET_VEC: \ case ET_VI: \ if (optr->ex_type != ET_VEC) { \ if (optr->ex_type == ET_VI) { \ post("expr~: Int. error %d", __LINE__); \ abort(); \ } \ optr->ex_type = ET_VEC; \ optr->ex_vec = (t_float *) \ fts_malloc(sizeof (t_float) * e->exp_vsize); \ } \ op = optr->ex_vec; \ lp = left->ex_vec; \ switch(right->ex_type) { \ case ET_INT: \ scalar = right->ex_int; \ j = e->exp_vsize; \ while (j--) { \ *op++ = (t_float)func(leftfuncast *lp, \ rightfuncast scalar); \ lp++; \ } \ break; \ case ET_FLT: \ scalar = right->ex_flt; \ j = e->exp_vsize; \ while (j--) { \ *op++ = (t_float)func(leftfuncast *lp, \ rightfuncast scalar); \ lp++; \ } \ break; \ case ET_VEC: \ case ET_VI: \ rp = right->ex_vec; \ j = e->exp_vsize; \ while (j--) { \ /* \ * on a RISC processor one could copy \ * 8 times in each round to get a considerable \ * improvement \ */ \ *op++ = (t_float)func(leftfuncast *lp, \ rightfuncast *rp); \ rp++; lp++; \ } \ break; \ case ET_SYM: \ default: \ post_error((fts_object_t *) e, \ "expr: FUNC_EVAL(%d): bad right type %ld\n", \ __LINE__, right->ex_type);\ } \ break; \ case ET_SYM: \ default: \ post_error((fts_object_t *) e, \ "expr: FUNC_EVAL(%d): bad left type %ld\n", \ __LINE__, left->ex_type); \ } /* * FUNC_EVAL_UNARY - evaluate a unary function, * if fltret is set return t_float * otherwise return value based on regular typechecking, */ #define FUNC_EVAL_UNARY(left, func, leftcast, optr, fltret) \ switch(left->ex_type) { \ case ET_INT: \ if (optr->ex_type == ET_VEC) { \ ex_mkvector(optr->ex_vec, \ (t_float)(func (leftcast left->ex_int)), e->exp_vsize);\ break; \ } \ if (fltret) { \ optr->ex_type = ET_FLT; \ optr->ex_flt = (t_float) func(leftcast left->ex_int); \ break; \ } \ optr->ex_type = ET_INT; \ optr->ex_int = (int) func(leftcast left->ex_int); \ break; \ case ET_FLT: \ if (optr->ex_type == ET_VEC) { \ ex_mkvector(optr->ex_vec, \ (t_float)(func (leftcast left->ex_flt)), e->exp_vsize);\ break; \ } \ optr->ex_type = ET_FLT; \ optr->ex_flt = (t_float) func(leftcast left->ex_flt); \ break; \ case ET_VI: \ case ET_VEC: \ if (optr->ex_type != ET_VEC) { \ optr->ex_type = ET_VEC; \ optr->ex_vec = (t_float *) \ fts_malloc(sizeof (t_float)*e->exp_vsize); \ } \ op = optr->ex_vec; \ lp = left->ex_vec; \ j = e->exp_vsize; \ while (j--) \ *op++ = (t_float)(func (leftcast *lp++)); \ break; \ default: \ post_error((fts_object_t *) e, \ "expr: FUNV_EVAL_UNARY(%d): bad left type %ld\n",\ __LINE__, left->ex_type); \ } #undef min #undef max #define min(x,y) (x > y ? y : x) #define max(x,y) (x > y ? x : y) #define FUNC_DEF(ex_func, func, castleft, castright, fltret); \ static void \ ex_func(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\ { \ struct ex_ex *left, *right; \ t_float *op; /* output pointer */ \ t_float *lp, *rp; /* left and right vector pointers */ \ t_float scalar; \ int j; \ \ left = argv++; \ right = argv; \ FUNC_EVAL(left, right, func, castleft, castright, optr, fltret); \ } #define FUNC_DEF_UNARY(ex_func, func, cast, fltret); \ static void \ ex_func(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)\ { \ struct ex_ex *left; \ t_float *op; /* output pointer */ \ t_float *lp, *rp; /* left and right vector pointers */ \ t_float scalar; \ int j; \ \ left = argv++; \ \ FUNC_EVAL_UNARY(left, func, cast, optr, fltret); \ } /* * ex_min -- if any of the arguments are or the output are vectors, a vector * of floats is generated otherwise the type of the result is the * type of the smaller value */ static void ex_min(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr) { struct ex_ex *left, *right; t_float *op; /* output pointer */ t_float *lp, *rp; /* left and right vector pointers */ t_float scalar; int j; left = argv++; right = argv; FUNC_EVAL(left, right, min, (double), (double), optr, 0); } /* * ex_max -- if any of the arguments are or the output are vectors, a vector * of floats is generated otherwise the type of the result is the * type of the larger value */ static void ex_max(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr) { struct ex_ex *left, *right; t_float *op; /* output pointer */ t_float *lp, *rp; /* left and right vector pointers */ t_float scalar; int j; left = argv++; right = argv; FUNC_EVAL(left, right, max, (double), (double), optr, 0); } /* * ex_toint -- convert to integer */ static void ex_toint(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr) { struct ex_ex *left; t_float *op; /* output pointer */ t_float *lp, *rp; /* left and right vector pointers */ t_float scalar; int j; left = argv++; #define toint(x) ((int)(x)) FUNC_EVAL_UNARY(left, toint, (int), optr, 0); } #if defined _MSC_VER && (_MSC_VER < 1800) /* rint is not available for Visual Studio Version < Visual Studio 2013 */ static double rint(double x) { return (floor(x + 0.5)); } #endif /* * ex_rint -- rint() round to the nearest int according to the common * rounding mechanism */ static void ex_rint(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr) { struct ex_ex *left; t_float *op; /* output pointer */ t_float *lp, *rp; /* left and right vector pointers */ t_float scalar; int j; left = argv++; FUNC_EVAL_UNARY(left, rint, (double), optr, 1); } /* * ex_tofloat -- convert to t_float */ static void ex_tofloat(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr) { struct ex_ex *left; t_float *op; /* output pointer */ t_float *lp, *rp; /* left and right vector pointers */ t_float scalar; int j; left = argv++; #define tofloat(x) ((t_float)(x)) FUNC_EVAL_UNARY(left, tofloat, (t_float), optr, 1); } /* * ex_pow -- the power of */ static void ex_pow(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr) { struct ex_ex *left, *right; t_float *op; /* output pointer */ t_float *lp, *rp; /* left and right vector pointers */ t_float scalar; int j; left = argv++; right = argv; FUNC_EVAL(left, right, pow, (double), (double), optr, 1); } /* * ex_sqrt -- square root */ static void ex_sqrt(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr) { struct ex_ex *left; t_float *op; /* output pointer */ t_float *lp, *rp; /* left and right vector pointers */ t_float scalar; int j; left = argv++; FUNC_EVAL_UNARY(left, sqrt, (double), optr, 1); } /* * ex_exp -- e to the power of */ static void ex_exp(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr) { struct ex_ex *left; t_float *op; /* output pointer */ t_float *lp, *rp; /* left and right vector pointers */ t_float scalar; int j; left = argv++; FUNC_EVAL_UNARY(left, exp, (double), optr, 1); } /* * ex_log -- 10 based logarithm */ static void ex_log(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr) { struct ex_ex *left; t_float *op; /* output pointer */ t_float *lp, *rp; /* left and right vector pointers */ t_float scalar; int j; left = argv++; FUNC_EVAL_UNARY(left, log10, (double), optr, 1); } /* * ex_ln -- natural log */ static void ex_ln(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr) { struct ex_ex *left; t_float *op; /* output pointer */ t_float *lp, *rp; /* left and right vector pointers */ t_float scalar; int j; left = argv++; FUNC_EVAL_UNARY(left, log, (double), optr, 1); } static void ex_sin(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr) { struct ex_ex *left; t_float *op; /* output pointer */ t_float *lp, *rp; /* left and right vector pointers */ t_float scalar; int j; left = argv++; FUNC_EVAL_UNARY(left, sin, (double), optr, 1); } static void ex_cos(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr) { struct ex_ex *left; t_float *op; /* output pointer */ t_float *lp, *rp; /* left and right vector pointers */ t_float scalar; int j; left = argv++; FUNC_EVAL_UNARY(left, cos, (double), optr, 1); } static void ex_tan(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr) { struct ex_ex *left; t_float *op; /* output pointer */ t_float *lp, *rp; /* left and right vector pointers */ t_float scalar; int j; left = argv++; FUNC_EVAL_UNARY(left, tan, (double), optr, 1); } static void ex_asin(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr) { struct ex_ex *left; t_float *op; /* output pointer */ t_float *lp, *rp; /* left and right vector pointers */ t_float scalar; int j; left = argv++; FUNC_EVAL_UNARY(left, asin, (double), optr, 1); } static void ex_acos(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr) { struct ex_ex *left; t_float *op; /* output pointer */ t_float *lp, *rp; /* left and right vector pointers */ t_float scalar; int j; left = argv++; FUNC_EVAL_UNARY(left, acos, (double), optr, 1); } static void ex_atan(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr) { struct ex_ex *left; t_float *op; /* output pointer */ t_float *lp, *rp; /* left and right vector pointers */ t_float scalar; int j; left = argv++; FUNC_EVAL_UNARY(left, atan, (double), optr, 1); } /* *ex_atan2 -- */ static void ex_atan2(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr) { struct ex_ex *left, *right; t_float *op; /* output pointer */ t_float *lp, *rp; /* left and right vector pointers */ t_float scalar; int j; left = argv++; right = argv; FUNC_EVAL(left, right, atan2, (double), (double), optr, 1); } /* * ex_fmod -- floating point modulo */ static void ex_fmod(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr) { struct ex_ex *left, *right; t_float *op; /* output pointer */ t_float *lp, *rp; /* left and right vector pointers */ t_float scalar; int j; left = argv++; right = argv; FUNC_EVAL(left, right, fmod, (double), (double), optr, 1); } /* * ex_floor -- floor */ static void ex_floor(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr) { struct ex_ex *left; t_float *op; /* output pointer */ t_float *lp, *rp; /* left and right vector pointers */ t_float scalar; int j; left = argv++; FUNC_EVAL_UNARY(left, floor, (double), optr, 1); } /* * ex_ceil -- ceil */ static void ex_ceil(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr) { struct ex_ex *left; t_float *op; /* output pointer */ t_float *lp, *rp; /* left and right vector pointers */ t_float scalar; int j; left = argv++; FUNC_EVAL_UNARY(left, ceil, (double), optr, 1); } static void ex_sinh(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr) { struct ex_ex *left; t_float *op; /* output pointer */ t_float *lp, *rp; /* left and right vector pointers */ t_float scalar; int j; left = argv++; FUNC_EVAL_UNARY(left, sinh, (double), optr, 1); } static void ex_cosh(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr) { struct ex_ex *left; t_float *op; /* output pointer */ t_float *lp, *rp; /* left and right vector pointers */ t_float scalar; int j; left = argv++; FUNC_EVAL_UNARY(left, cosh, (double), optr, 1); } static void ex_tanh(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr) { struct ex_ex *left; t_float *op; /* output pointer */ t_float *lp, *rp; /* left and right vector pointers */ t_float scalar; int j; left = argv++; FUNC_EVAL_UNARY(left, tanh, (double), optr, 1); } #if !defined(_MSC_VER) || (_MSC_VER >= 1700) static void ex_asinh(t_expr *e, long argc, struct ex_ex *argv, struct ex_ex *optr) { struct ex_ex *left; t_float *op; /* output pointer */ t_float *lp, *rp; /* left and right vector pointers */ t_float scalar; int j; left = argv++; FUNC_EVAL_UNARY(left, asinh, (double), optr, 1); } static void ex_acosh(t_expr *e, long argc, struct ex_ex *argv, struct ex_ex *optr) { struct ex_ex *left; t_float *op; /* output pointer */ t_float *lp, *rp; /* left and right vector pointers */ t_float scalar; int j; left = argv++; FUNC_EVAL_UNARY(left, acosh, (double), optr, 1); } static void ex_atanh(t_expr *e, long argc, struct ex_ex *argv, struct ex_ex *optr) { struct ex_ex *left; t_float *op; /* output pointer */ t_float *lp, *rp; /* left and right vector pointers */ t_float scalar; int j; left = argv++; FUNC_EVAL_UNARY(left, atanh, (double), optr, 1); } #endif static double ex_dofact(int i) { float ret = 0; if (i > 0) ret = 1; else return (1); do { ret *= i; } while (--i); return(ret); } static void ex_fact(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr) { struct ex_ex *left; t_float *op; /* output pointer */ t_float *lp, *rp; /* left and right vector pointers */ t_float scalar; int j; left = argv++; FUNC_EVAL_UNARY(left, ex_dofact, (int), optr, 1); } static int ex_dorandom(int i1, int i2) { int i; int j; j = rand() & 0x7fffL; i = i1 + (int)((((float)(i2 - i1)) * (float)j) / pow (2, 15)); return (i); } /* * ex_random -- return a random number */ static void ex_random(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr) { struct ex_ex *left, *right; t_float *op; /* output pointer */ t_float *lp, *rp; /* left and right vector pointers */ t_float scalar; int j; left = argv++; right = argv; FUNC_EVAL(left, right, ex_dorandom, (int), (int), optr, 0); } static void ex_abs(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr) { struct ex_ex *left; t_float *op; /* output pointer */ t_float *lp, *rp; /* left and right vector pointers */ t_float scalar; int j; left = argv++; FUNC_EVAL_UNARY(left, fabs, (double), optr, 0); } /* * ex_if -- if function */ struct ex_ex * ex_if(t_expr *e, struct ex_ex *eptr, struct ex_ex *optr, struct ex_ex *argv, int idx) { struct ex_ex *left, *right, *cond, *res; t_float *op; /* output pointer */ t_float *lp, *rp; /* left and right vector pointers */ t_float *cp; /* condition pointer */ t_float leftvalue, rightvalue; int j; int condtrue = 0; // evaluate the condition eptr = ex_eval(e, eptr, argv, idx); cond = argv++; // only either the left or right will be evaluated depending // on the truth value of the condition // However, if the condition is a vector, both args will be evaluated switch (cond->ex_type) { case ET_VEC: case ET_VI: if (optr->ex_type != ET_VEC) { if (optr->ex_type == ET_VI) { /* SDY remove this test */ post("expr~: Int. error %d", __LINE__); return (eptr); } optr->ex_type = ET_VEC; optr->ex_vec = (t_float *) fts_malloc(sizeof (t_float) * e->exp_vsize); if (!optr->ex_vec) { post("expr:if: no mem"); /* pass over the left and right args */ return(cond->ex_end->ex_end); } } /* * if the condition is a vector * the left and the right args both will get processed */ eptr = ex_eval(e, eptr, argv, idx); left = argv++; eptr = ex_eval(e, eptr, argv, idx); right = argv; op = optr->ex_vec; j = e->exp_vsize; cp = cond->ex_vec; switch (left->ex_type) { case ET_INT: leftvalue = left->ex_int; switch (right->ex_type) { case ET_INT: rightvalue = right->ex_int; while (j--) { if (*cp++) *op++ = leftvalue; else *op++ = rightvalue; } return (eptr); case ET_FLT: rightvalue = right->ex_flt; while (j--) { if (*cp++) *op++ = leftvalue; else *op++ = rightvalue; } return (eptr); case ET_VEC: case ET_VI: rp = right->ex_vec; while (j--) { if (*cp++) *op++ = leftvalue; else *op++ = *rp; rp++; } return (eptr); case ET_SYM: default: post_error((fts_object_t *) e, "expr: FUNC_EVAL(%d): bad right type %ld\n", __LINE__, right->ex_type); return (eptr); } case ET_FLT: leftvalue = left->ex_flt; switch (right->ex_type) { case ET_INT: rightvalue = right->ex_int; while (j--) { if (*cp++) *op++ = leftvalue; else *op++ = rightvalue; } return (eptr); case ET_FLT: rightvalue = right->ex_flt; while (j--) { if (*cp++) *op++ = leftvalue; else *op++ = rightvalue; } return (eptr); case ET_VEC: case ET_VI: rp = right->ex_vec; while (j--) { if (*cp++) *op++ = leftvalue; else *op++ = *rp; rp++; } return (eptr); case ET_SYM: default: post_error((fts_object_t *) e, "expr: FUNC_EVAL(%d): bad right type %ld\n", __LINE__, right->ex_type); return (eptr); } case ET_VEC: case ET_VI: lp = left->ex_vec; switch (right->ex_type) { case ET_INT: rightvalue = right->ex_int; while (j--) { if (*cp++) *op++ = *lp; else *op++ = rightvalue; lp++; } return (eptr); case ET_FLT: rightvalue = right->ex_flt; while (j--) { if (*cp++) *op++ = *lp; else *op++ = rightvalue; lp++; } return (eptr); case ET_VEC: case ET_VI: rp = right->ex_vec; while (j--) { if (*cp++) *op++ = *lp; else *op++ = *rp; lp++; rp++; } return (eptr); case ET_SYM: default: post_error((fts_object_t *) e, "expr: FUNC_EVAL(%d): bad right type %ld\n", __LINE__, right->ex_type); return (eptr); } case ET_SYM: default: post_error((fts_object_t *) e, "expr: FUNC_EVAL(%d): bad left type %ld\n", __LINE__, left->ex_type); return (eptr); } case ET_INT: if (cond->ex_int) condtrue = 1; else condtrue = 0; break; case ET_FLT: if (cond->ex_flt) condtrue = 1; else condtrue = 0; break; case ET_SYM: default: post_error((fts_object_t *) e, "expr: FUNC_EVAL(%d): bad condition type %ld\n", __LINE__, cond->ex_type); return (eptr); } if (condtrue) { eptr = ex_eval(e, eptr, argv, idx); res = argv++; if (!eptr) return (exNULL); eptr = eptr->ex_end; /* no right processing */ } else { if (!eptr) return (exNULL); eptr = eptr->ex_end; /* no left rocessing */ eptr = ex_eval(e, eptr, argv, idx); res = argv++; } switch(res->ex_type) { case ET_INT: if (optr->ex_type == ET_VEC) { ex_mkvector(optr->ex_vec, (t_float)res->ex_int, e->exp_vsize); return (eptr); } *optr = *res; return (eptr); case ET_FLT: if (optr->ex_type == ET_VEC) { ex_mkvector(optr->ex_vec, (t_float)res->ex_flt, e->exp_vsize); return (eptr); } *optr = *res; return (eptr); case ET_VEC: case ET_VI: if (optr->ex_type != ET_VEC) { if (optr->ex_type == ET_VI) { /* SDY remove this test */ post("expr~: Int. error %d", __LINE__); return (eptr); } optr->ex_type = ET_VEC; optr->ex_vec = (t_float *) fts_malloc(sizeof (t_float) * e->exp_vsize); if (!optr->ex_vec) { post("expr:if: no mem"); return (eptr); } } memcpy(optr->ex_vec, res->ex_vec, e->exp_vsize*sizeof(t_float)); return (eptr); case ET_SYM: default: post_error((fts_object_t *) e, "expr: FUNC_EVAL(%d): bad res type %ld\n", __LINE__, res->ex_type); return (eptr); } } /* * ex_imodf - extract signed integral value from floating-point number */ static double imodf(double x) { double xx; modf(x, &xx); return (xx); } FUNC_DEF_UNARY(ex_imodf, imodf, (double), 1); /* * ex_modf - extract signed fractional value from floating-point number * * using fracmodf because fmodf() is already defined in a .h file */ static double fracmodf(double x) { double xx; return(modf(x, &xx)); } FUNC_DEF_UNARY(ex_modf, fracmodf, (double), 1); FUNC_DEF_UNARY(ex_mtof, mtof, (double), 1); FUNC_DEF_UNARY(ex_ftom, ftom, (double), 1); FUNC_DEF_UNARY(ex_dbtorms, dbtorms, (double), 1); FUNC_DEF_UNARY(ex_rmstodb, rmstodb, (double), 1); FUNC_DEF_UNARY(ex_dbtopow, dbtopow, (double), 1); FUNC_DEF_UNARY(ex_powtodb, powtodb, (double), 1); /* * ex_ldexp - multiply floating-point number by integral power of 2 */ FUNC_DEF(ex_ldexp, ldexp, (double), (int), 1); #if !defined(_MSC_VER) || (_MSC_VER >= 1700) /* * ex_cbrt - cube root */ FUNC_DEF_UNARY(ex_cbrt, cbrt, (double), 1); /* * ex_erf - error function */ FUNC_DEF_UNARY(ex_erf, erf, (double), 1); /* * ex_erfc - complementary error function */ FUNC_DEF_UNARY(ex_erfc, erfc, (double), 1); /* * ex_expm1 - exponential minus 1, */ FUNC_DEF_UNARY(ex_expm1, expm1, (double), 1); /* * ex_log1p - logarithm of 1 plus */ FUNC_DEF_UNARY(ex_log1p, log1p, (double), 1); /* * ex_isinf - is the value infinite, */ FUNC_DEF_UNARY(ex_isinf, isinf, (double), 0); /* * ex_finite - is the value finite */ FUNC_DEF_UNARY(ex_finite, isfinite, (double), 0); /* * ex_isnan -- is the resut a nan (Not a number) */ FUNC_DEF_UNARY(ex_isnan, isnan, (double), 0); /* * ex_copysign - copy sign of a number */ FUNC_DEF(ex_copysign, copysign, (double), (double), 1); /* * drem() is now obsolute * ex_drem - floating-point remainder function */ FUNC_DEF(ex_drem, remainder, (double), (double), 1); /* * ex_remainder - floating-point remainder function */ FUNC_DEF(ex_remainder, remainder, (double), (double), 1); /* * ex_round - round to nearest integer, away from zero */ FUNC_DEF_UNARY(ex_round, round, (double), 1); /* * ex_trunc - round to integer, towards zero */ FUNC_DEF_UNARY(ex_trunc, trunc, (double), 1); /* * ex_nearbyint - round to nearest integer */ FUNC_DEF_UNARY(ex_nearbyint, nearbyint, (double), 1); #endif #ifdef notdef /* the following will be added once they are more popular in math libraries */ /* * ex_hypoth - Euclidean distance function */ FUNC_DEF(ex_hypoth, hypoth, (double), (double), 1); #endif ================================================ FILE: libs/libpd/pure-data/src/x_vexp_if.c ================================================ /* Copyright (c) IRCAM. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* "expr" was written by Shahrokh Yadegari c. 1989. -msp */ /* "expr~" and "fexpr~" conversion by Shahrokh Yadegari c. 1999,2000 */ /* * Feb 2002 - added access to variables * multiple expression support * new short hand forms for fexpr~ * now $y or $y1 = $y1[-1] and $y2 = $y2[-1] * --sdy * * * version 0.50 - March 2016 * version 0.55 - July 2017 * version 0.56 - January 2018 * version 0.57 - October 2020 */ #include #include #include #include "x_vexp.h" static char *exp_version = "0.57"; extern struct ex_ex *ex_eval(struct expr *expr, struct ex_ex *eptr, struct ex_ex *optr, int n); #ifdef PD static t_class *expr_class; static t_class *expr_tilde_class; static t_class *fexpr_tilde_class; #else /* MSP */ void *expr_tilde_class; #endif /*------------------------- expr class -------------------------------------*/ extern int expr_donew(struct expr *expr, int ac, t_atom *av); /*#define EXPR_DEBUG*/ static void expr_bang(t_expr *x); t_int *expr_perform(t_int *w); static void expr_list(t_expr *x, t_symbol *s, int argc, const fts_atom_t *argv) { int i; if (argc > MAX_VARS) argc = MAX_VARS; for (i = 0; i < argc; i++) { if (argv[i].a_type == A_FLOAT) { if (x->exp_var[i].ex_type == ET_FI) x->exp_var[i].ex_flt = argv[i].a_w.w_float; else if (x->exp_var[i].ex_type == ET_II) x->exp_var[i].ex_int = argv[i].a_w.w_float; else if (x->exp_var[i].ex_type) pd_error(x, "expr: type mismatch"); } else if (argv[i].a_type == A_SYMBOL) { if (x->exp_var[i].ex_type == ET_SI) x->exp_var[i].ex_ptr = (char *)argv[i].a_w.w_symbol; else if (x->exp_var[i].ex_type) pd_error(x, "expr: type mismatch"); } } expr_bang(x); } static void expr_flt(t_expr *x, t_float f, int in) { if (in >= MAX_VARS) return; if (x->exp_var[in].ex_type == ET_FI) x->exp_var[in].ex_flt = f; else if (x->exp_var[in].ex_type == ET_II) x->exp_var[in].ex_int = f; } static t_class *exprproxy_class; typedef struct _exprproxy { t_pd p_pd; int p_index; t_expr *p_owner; struct _exprproxy *p_next; } t_exprproxy; t_exprproxy *exprproxy_new(t_expr *owner, int indx); void exprproxy_float(t_exprproxy *p, t_floatarg f); t_exprproxy * exprproxy_new(t_expr *owner, int indx) { t_exprproxy *x = (t_exprproxy *)pd_new(exprproxy_class); x->p_owner = owner; x->p_index = indx; x->p_next = owner->exp_proxy; owner->exp_proxy = x; return (x); } void exprproxy_float(t_exprproxy *p, t_floatarg f) { t_expr *x = p->p_owner; int in = p->p_index; if (in >= MAX_VARS) return; if (x->exp_var[in].ex_type == ET_FI) x->exp_var[in].ex_flt = f; else if (x->exp_var[in].ex_type == ET_II) x->exp_var[in].ex_int = f; } /* method definitions */ static void expr_ff(t_expr *x) { t_exprproxy *y; int i; y = x->exp_proxy; while (y) { x->exp_proxy = y->p_next; #ifdef PD pd_free(&y->p_pd); #else /*MSP */ /* SDY find out what needs to be called for MSP */ #endif y = x->exp_proxy; } for (i = 0 ; i < x->exp_nexpr; i++) if (x->exp_stack[i]) fts_free(x->exp_stack[i]); /* * SDY free all the allocated buffers here for expr~ and fexpr~ * check to see if there are others */ for (i = 0; i < MAX_VARS; i++) { if (x->exp_p_var[i]) fts_free(x->exp_p_var[i]); if (x->exp_p_res[i]) fts_free(x->exp_p_res[i]); if (x->exp_tmpres[i]) fts_free(x->exp_tmpres[i]); } } static void expr_bang(t_expr *x) { int i; #ifdef EXPR_DEBUG { struct ex_ex *eptr; for (i = 0, eptr = x->exp_var; ; eptr++, i++) { if (!eptr->ex_type) break; switch (eptr->ex_type) { case ET_II: fprintf(stderr,"ET_II: %d \n", eptr->ex_int); break; case ET_FI: fprintf(stderr,"ET_FT: %f \n", eptr->ex_flt); break; default: fprintf(stderr,"oups\n"); } } } #endif /* banging a signal or filter object means nothing */ if (!IS_EXPR(x)) return; for (i = x->exp_nexpr - 1; i > -1 ; i--) { if (!ex_eval(x, x->exp_stack[i], &x->exp_res[i], 0)) { /*fprintf(stderr,"expr_bang(error evaluation)\n"); */ /* SDY now that we have multiple ones, on error we should * continue return; */ } switch(x->exp_res[i].ex_type) { case ET_INT: outlet_float(x->exp_outlet[i], (t_float) x->exp_res[i].ex_int); break; case ET_FLT: outlet_float(x->exp_outlet[i], x->exp_res[i].ex_flt); break; case ET_SYM: /* CHANGE this will have to be taken care of */ default: post("expr: bang: unrecognized result %ld\n", x->exp_res[i].ex_type); } } } static t_expr * #ifdef PD expr_new(t_symbol *s, int ac, t_atom *av) #else /* MSP */ Nexpr_new(t_symbol *s, int ac, t_atom *av) #endif { struct expr *x; int i, ninlet; struct ex_ex *eptr; t_atom fakearg; int dsp_index; /* keeping track of the dsp inlets */ /* * SDY - we may need to call dsp_setup() in this function */ if (!ac) { ac = 1; av = &fakearg; SETFLOAT(&fakearg, 0); } #ifdef PD /* * figure out if we are expr, expr~, or fexpr~ */ if (!strcmp("expr", s->s_name)) { x = (t_expr *)pd_new(expr_class); SET_EXPR(x); } else if (!strcmp("expr~", s->s_name)) { x = (t_expr *)pd_new(expr_tilde_class); SET_EXPR_TILDE(x); } else if (!strcmp("fexpr~", s->s_name)) { x = (t_expr *)pd_new(fexpr_tilde_class); SET_FEXPR_TILDE(x); } else { post("expr_new: bad object name '%s'", s->s_name); /* assume expr */ x = (t_expr *)pd_new(expr_class); SET_EXPR(x); } #else /* MSP */ /* for now assume an expr~ */ x = (t_expr *)pd_new(expr_tilde_class); SET_EXPR_TILDE(x); #endif /* * initialize the newly allocated object */ x->exp_proxy = 0; x->exp_nivec = 0; x->exp_nexpr = 0; x->exp_error = 0; for (i = 0; i < MAX_VARS; i++) { x->exp_stack[i] = (struct ex_ex *)0; x->exp_outlet[i] = (t_outlet *)0; x->exp_res[i].ex_type = 0; x->exp_res[i].ex_int = 0; x->exp_p_res[i] = (t_float *)0; x->exp_var[i].ex_type = 0; x->exp_var[i].ex_int = 0; x->exp_p_var[i] = (t_float *)0; x->exp_tmpres[i] = (t_float *)0; x->exp_vsize = 0; } x->exp_f = 0; /* save the control value to be transformed to signal */ if (expr_donew(x, ac, av)) { pd_error(x, "expr: syntax error"); /* SDY the following coredumps why? pd_free(&x->exp_ob.ob_pd); */ return (0); } ninlet = 1; for (i = 0, eptr = x->exp_var; i < MAX_VARS ; i++, eptr++) if (eptr->ex_type) { ninlet = i + 1; } /* * create the new inlets */ for (i = 1, eptr = x->exp_var + 1, dsp_index=1; iex_type) { case 0: /* nothing is using this inlet */ if (i < ninlet) #ifdef PD floatinlet_new(&x->exp_ob, &eptr->ex_flt); #else /* MSP */ inlet_new(&x->exp_ob, "float"); #endif break; case ET_II: case ET_FI: p = exprproxy_new(x, i); #ifdef PD inlet_new(&x->exp_ob, &p->p_pd, &s_float, &s_float); #else /* MSP */ inlet_new(&x->exp_ob, "float"); #endif break; case ET_SI: #ifdef PD symbolinlet_new(&x->exp_ob, (t_symbol **)&eptr->ex_ptr); #else /* MSP */ inlet_new(&x->exp_ob, "symbol"); #endif break; case ET_XI: case ET_VI: if (!IS_EXPR(x)) { dsp_index++; #ifdef PD inlet_new(&x->exp_ob, &x->exp_ob.ob_pd, &s_signal, &s_signal); #else /* MSP */ inlet_new(&x->exp_ob, "signal"); #endif break; } else post("expr: internal error expr_new"); /* falls through */ default: pd_error(x, "expr: bad type (%lx) inlet = %d\n", eptr->ex_type, i + 1); break; } } if (IS_EXPR(x)) { for (i = 0; i < x->exp_nexpr; i++) x->exp_outlet[i] = outlet_new(&x->exp_ob, 0); } else { for (i = 0; i < x->exp_nexpr; i++) x->exp_outlet[i] = outlet_new(&x->exp_ob, gensym("signal")); x->exp_nivec = dsp_index; } /* * for now assume a 64 sample size block but this may change once * expr_dsp is called */ x->exp_vsize = 64; for (i = 0; i < x->exp_nexpr; i++) { x->exp_p_res[i] = fts_calloc(x->exp_vsize, sizeof (t_float)); x->exp_tmpres[i] = fts_calloc(x->exp_vsize, sizeof (t_float)); } for (i = 0; i < MAX_VARS; i++) x->exp_p_var[i] = fts_calloc(x->exp_vsize, sizeof (t_float)); return (x); } t_int * expr_perform(t_int *w) { int i, j; t_expr *x = (t_expr *)w[1]; struct ex_ex res; int n; /* sanity check */ if (IS_EXPR(x)) { post("expr_perform: bad x->exp_flags = %d", x->exp_flags); abort(); } if (x->exp_flags & EF_STOP) { for (i = 0; i < x->exp_nexpr; i++) memset(x->exp_res[i].ex_vec, 0, x->exp_vsize * sizeof (t_float)); return (w + 2); } if (IS_EXPR_TILDE(x)) { /* * if we have only one expression, we can right on * on the output directly, otherwise we have to copy * the data because, outputs could be the same buffer as * inputs */ if ( x->exp_nexpr == 1) ex_eval(x, x->exp_stack[0], &x->exp_res[0], 0); else { res.ex_type = ET_VEC; for (i = 0; i < x->exp_nexpr; i++) { res.ex_vec = x->exp_tmpres[i]; ex_eval(x, x->exp_stack[i], &res, 0); } n = x->exp_vsize * sizeof(t_float); for (i = 0; i < x->exp_nexpr; i++) memcpy(x->exp_res[i].ex_vec, x->exp_tmpres[i], n); } return (w + 2); } if (!IS_FEXPR_TILDE(x)) { post("expr_perform: bad x->exp_flags = %d - expecting fexpr", x->exp_flags); return (w + 2); } /* * since the output buffer could be the same as one of the inputs * we need to keep the output in a different buffer */ for (i = 0; i < x->exp_vsize; i++) for (j = 0; j < x->exp_nexpr; j++) { res.ex_type = 0; res.ex_int = 0; ex_eval(x, x->exp_stack[j], &res, i); switch (res.ex_type) { case ET_INT: x->exp_tmpres[j][i] = (t_float) res.ex_int; break; case ET_FLT: x->exp_tmpres[j][i] = res.ex_flt; break; default: post("expr_perform: bad result type %d", res.ex_type); } } /* * copy inputs and results to the save buffers * inputs need to be copied first as the output buffer can be * same as an input buffer */ n = x->exp_vsize * sizeof(t_float); for (i = 0; i < MAX_VARS; i++) if (x->exp_var[i].ex_type == ET_XI) memcpy(x->exp_p_var[i], x->exp_var[i].ex_vec, n); for (i = 0; i < x->exp_nexpr; i++) { memcpy(x->exp_p_res[i], x->exp_tmpres[i], n); memcpy(x->exp_res[i].ex_vec, x->exp_tmpres[i], n); } return (w + 2); } static void expr_dsp(t_expr *x, t_signal **sp) { int i, nv; int newsize; x->exp_error = 0; /* reset all errors */ newsize = (x->exp_vsize != sp[0]->s_n); x->exp_vsize = sp[0]->s_n; /* record the vector size */ for (i = 0; i < x->exp_nexpr; i++) { x->exp_res[i].ex_type = ET_VEC; x->exp_res[i].ex_vec = sp[x->exp_nivec + i]->s_vec; } for (i = 0, nv = 0; i < MAX_VARS; i++) /* * the first inlet is always a signal * * SDY We are warning the user till this limitation * is taken away from pd */ if (!i || x->exp_var[i].ex_type == ET_VI || x->exp_var[i].ex_type == ET_XI) { if (nv >= x->exp_nivec) { post("expr_dsp int. err nv = %d, x->exp_nive = %d", nv, x->exp_nivec); abort(); } x->exp_var[i].ex_vec = sp[nv]->s_vec; nv++; } /* we always have one inlet but we may not use it */ if (nv != x->exp_nivec && (nv != 0 || x->exp_nivec != 1)) { post("expr_dsp internal error 2 nv = %d, x->exp_nive = %d", nv, x->exp_nivec); abort(); } dsp_add(expr_perform, 1, (t_int *) x); /* * The buffer are now being allocated for expr~ and fexpr~ * because if we have more than one expression we need the * temporary buffers, The save buffers are not really needed if (!IS_FEXPR_TILDE(x)) return; */ /* * if we have already allocated the buffers and we have a * new size free all the buffers */ if (x->exp_p_res[0]) { if (!newsize) return; /* * if new size, reallocate all the previous buffers for fexpr~ */ for (i = 0; i < x->exp_nexpr; i++) { fts_free(x->exp_p_res[i]); fts_free(x->exp_tmpres[i]); } for (i = 0; i < MAX_VARS; i++) fts_free(x->exp_p_var[i]); } for (i = 0; i < x->exp_nexpr; i++) { x->exp_p_res[i] = fts_calloc(x->exp_vsize, sizeof (t_float)); x->exp_tmpres[i] = fts_calloc(x->exp_vsize, sizeof (t_float)); } for (i = 0; i < MAX_VARS; i++) x->exp_p_var[i] = fts_calloc(x->exp_vsize, sizeof (t_float)); } /* * expr_verbose -- toggle the verbose switch */ static void expr_verbose(t_expr *x) { if (x->exp_flags & EF_VERBOSE) { x->exp_flags &= ~EF_VERBOSE; post ("verbose off"); } else { x->exp_flags |= EF_VERBOSE; post ("verbose on"); } } static void expr_version(t_expr *x) { post( "expr, expr~, fexpr~ version %s", exp_version); } /* * expr_start -- turn on expr processing for now only used for fexpr~ */ static void expr_start(t_expr *x) { x->exp_flags &= ~EF_STOP; } /* * expr_stop -- turn on expr processing for now only used for fexpr~ */ static void expr_stop(t_expr *x) { x->exp_flags |= EF_STOP; } static void fexpr_set_usage(void) { post("fexpr~: set val ..."); post("fexpr~: set {xy}[#] val ..."); } /* * fexpr_tilde_set -- set previous values of the buffers * set val val ... - sets the first elements of output buffers * set x val ... - sets the elements of the first input buffer * set x# val ... - sets the elements of the #th input buffers * set y val ... - sets the elements of the first output buffer * set y# val ... - sets the elements of the #th output buffers */ static void fexpr_tilde_set(t_expr *x, t_symbol *s, int argc, t_atom *argv) { t_symbol *sx; int vecno; int i, nargs; if (!argc) return; sx = atom_getsymbolarg(0, argc, argv); switch(sx->s_name[0]) { case 'x': if (!sx->s_name[1]) vecno = 0; else { vecno = atoi(sx->s_name + 1); if (!vecno) { post("fexpr~.set: bad set x vector number"); fexpr_set_usage(); return; } if (vecno >= MAX_VARS) { post("fexpr~.set: no more than %d inlets", MAX_VARS); return; } vecno--; } if (x->exp_var[vecno].ex_type != ET_XI) { post("fexpr~-set: no signal at inlet %d", vecno + 1); return; } nargs = argc - 1; if (!nargs) { post("fexpr~-set: no argument to set"); return; } if (nargs > x->exp_vsize) { post("fexpr~.set: %d set values larger than vector size(%d)", nargs, x->exp_vsize); post("fexpr~.set: only the first %d values will be set", x->exp_vsize); nargs = x->exp_vsize; } for (i = 0; i < nargs; i++) { x->exp_p_var[vecno][x->exp_vsize - i - 1] = atom_getfloatarg(i + 1, argc, argv); } return; case 'y': if (!sx->s_name[1]) vecno = 0; else { vecno = atoi(sx->s_name + 1); if (!vecno) { post("fexpr~.set: bad set y vector number"); fexpr_set_usage(); return; } vecno--; } if (vecno >= x->exp_nexpr) { post("fexpr~.set: only %d outlets", x->exp_nexpr); return; } nargs = argc - 1; if (!nargs) { post("fexpr~-set: no argument to set"); return; } if (nargs > x->exp_vsize) { post("fexpr~-set: %d set values larger than vector size(%d)", nargs, x->exp_vsize); post("fexpr~.set: only the first %d values will be set", x->exp_vsize); nargs = x->exp_vsize; } for (i = 0; i < nargs; i++) { x->exp_p_res[vecno][x->exp_vsize - i - 1] = atom_getfloatarg(i + 1, argc, argv); } return; case 0: if (argc > x->exp_nexpr) { post("fexpr~.set: only %d outlets available", x->exp_nexpr); post("fexpr~.set: the extra set values are ignored"); } for (i = 0; i < x->exp_nexpr && i < argc; i++) x->exp_p_res[i][x->exp_vsize - 1] = atom_getfloatarg(i, argc, argv); return; default: fexpr_set_usage(); return; } return; } /* * fexpr_tilde_clear - clear the past buffers */ static void fexpr_tilde_clear(t_expr *x, t_symbol *s, int argc, t_atom *argv) { t_symbol *sx; int vecno; int i, nargs; /* * if no argument clear all input and output buffers */ if (!argc) { for (i = 0; i < x->exp_nexpr; i++) memset(x->exp_p_res[i], 0, x->exp_vsize*sizeof(t_float)); for (i = 0; i < MAX_VARS; i++) if (x->exp_var[i].ex_type == ET_XI) memset(x->exp_p_var[i], 0, x->exp_vsize*sizeof(t_float)); return; } if (argc > 1) { post("fexpr~ usage: 'clear' or 'clear {xy}[#]'"); return; } sx = atom_getsymbolarg(0, argc, argv); switch(sx->s_name[0]) { case 'x': if (!sx->s_name[1]) vecno = 0; else { vecno = atoi(sx->s_name + 1); if (!vecno) { post("fexpr~.clear: bad clear x vector number"); return; } if (vecno >= MAX_VARS) { post("fexpr~.clear: no more than %d inlets", MAX_VARS); return; } vecno--; } if (x->exp_var[vecno].ex_type != ET_XI) { post("fexpr~-clear: no signal at inlet %d", vecno + 1); return; } memset(x->exp_p_var[vecno], 0, x->exp_vsize*sizeof(t_float)); return; case 'y': if (!sx->s_name[1]) vecno = 0; else { vecno = atoi(sx->s_name + 1); if (!vecno) { post("fexpr~.clear: bad clear y vector number"); return; } vecno--; } if (vecno >= x->exp_nexpr) { post("fexpr~.clear: only %d outlets", x->exp_nexpr); return; } memset(x->exp_p_res[vecno], 0, x->exp_vsize*sizeof(t_float)); return; return; default: post("fexpr~ usage: 'clear' or 'clear {xy}[#]'"); return; } return; } #ifdef PD void expr_setup(void) { /* * expr initialization */ expr_class = class_new(gensym("expr"), (t_newmethod)expr_new, (t_method)expr_ff, sizeof(t_expr), 0, A_GIMME, 0); class_addlist(expr_class, expr_list); exprproxy_class = class_new(gensym("exprproxy"), 0, 0, sizeof(t_exprproxy), CLASS_PD, 0); class_addfloat(exprproxy_class, exprproxy_float); class_addmethod(expr_class,(t_method)expr_version, gensym("version"), 0); /* * expr~ initialization */ expr_tilde_class = class_new(gensym("expr~"), (t_newmethod)expr_new, (t_method)expr_ff, sizeof(t_expr), 0, A_GIMME, 0); class_addmethod(expr_tilde_class, nullfn, gensym("signal"), 0); CLASS_MAINSIGNALIN(expr_tilde_class, t_expr, exp_f); class_addmethod(expr_tilde_class,(t_method)expr_dsp, gensym("dsp"), A_CANT, 0); class_sethelpsymbol(expr_tilde_class, gensym("expr")); class_addmethod(expr_tilde_class,(t_method)expr_version, gensym("version"), 0); /* * fexpr~ initialization */ fexpr_tilde_class = class_new(gensym("fexpr~"), (t_newmethod)expr_new, (t_method)expr_ff, sizeof(t_expr), 0, A_GIMME, 0); class_addmethod(fexpr_tilde_class, nullfn, gensym("signal"), 0); CLASS_MAINSIGNALIN(fexpr_tilde_class, t_expr, exp_f); class_addmethod(fexpr_tilde_class,(t_method)expr_start, gensym("start"), 0); class_addmethod(fexpr_tilde_class,(t_method)expr_stop, gensym("stop"), 0); class_addmethod(fexpr_tilde_class,(t_method)expr_dsp,gensym("dsp"), A_CANT, 0); class_addmethod(fexpr_tilde_class, (t_method)fexpr_tilde_set, gensym("set"), A_GIMME, 0); class_addmethod(fexpr_tilde_class, (t_method)fexpr_tilde_clear, gensym("clear"), A_GIMME, 0); class_addmethod(fexpr_tilde_class,(t_method)expr_verbose, gensym("verbose"), 0); class_addmethod(fexpr_tilde_class,(t_method)expr_version, gensym("version"), 0); class_sethelpsymbol(fexpr_tilde_class, gensym("expr")); } void expr_tilde_setup(void) { expr_setup(); } void fexpr_tilde_setup(void) { expr_setup(); } #else /* MSP */ void main(void) { setup((t_messlist **)&expr_tilde_class, (method)Nexpr_new, (method)expr_ff, (short)sizeof(t_expr), 0L, A_GIMME, 0); addmess((method)expr_dsp, "dsp", A_CANT, 0); // dsp method dsp_initclass(); } #endif /* -- the following functions use Pd internals and so are in the "if" file. */ int ex_getsym(char *p, fts_symbol_t *s) { *s = gensym(p); return (0); } const char * ex_symname(fts_symbol_t s) { if (!s) return (0); return (fts_symbol_name(s)); } /* * max_ex_tab -- evaluate this table access * eptr is the name of the table and arg is the index we * have to put the result in optr * return 1 on error and 0 otherwise * * Arguments: * the expr object * table * the argument * the result pointer */ int max_ex_tab(struct expr *expr, fts_symbol_t s, struct ex_ex *arg, struct ex_ex *optr) { #ifdef PD t_garray *garray; int size; long indx; t_word *wvec; if (!s || !(garray = (t_garray *)pd_findbyclass(s, garray_class)) || !garray_getfloatwords(garray, &size, &wvec)) { optr->ex_type = ET_FLT; optr->ex_flt = 0; pd_error(expr, "no such table '%s'", ex_symname(s)); return (1); } optr->ex_type = ET_FLT; switch (arg->ex_type) { case ET_INT: indx = arg->ex_int; break; case ET_FLT: /* strange interpolation code deleted here -msp */ indx = arg->ex_flt; break; default: /* do something with strings */ pd_error(expr, "expr: bad argument for table '%s'\n", fts_symbol_name(s)); indx = 0; } if (indx < 0) indx = 0; else if (indx >= size) indx = size - 1; optr->ex_flt = wvec[indx].w_float; #else /* MSP */ /* * table lookup not done for MSP yet */ post("max_ex_tab: not complete for MSP yet!"); optr->ex_type = ET_FLT; optr->ex_flt = 0; #endif return (0); } /* * max_ex_tab_store -- store a value in a table * tbl[arg->value] = rval.value * eptr is the name of the table and arg is the index we * have to put the result in optr * return 1 on error and 0 otherwise * * Arguments: * the expr object * table * the argument * value to be stored * the result pointer */ int max_ex_tab_store(struct expr *expr, t_symbol *s, struct ex_ex *arg, struct ex_ex *rval, struct ex_ex *optr) { #ifdef PD t_garray *garray; int size; long indx; t_word *wvec; if (!s || !(garray = (t_garray *)pd_findbyclass(s, garray_class)) || !garray_getfloatwords(garray, &size, &wvec)) { optr->ex_type = ET_FLT; optr->ex_flt = 0; if (s) pd_error(expr, "no such table to store '%s'", s->s_name); else pd_error(expr, "cannot store in unnamed table"); return (1); } optr->ex_type = ET_FLT; switch (arg->ex_type) { case ET_INT: indx = arg->ex_int; break; case ET_FLT: /* strange interpolation code deleted here -msp */ indx = arg->ex_flt; break; default: /* do something with strings */ pd_error(expr, "expr: bad argument for table store '%s'\n", fts_symbol_name(s)); indx = 0; } if (indx < 0) indx = 0; else if (indx >= size) indx = size - 1; *optr = *rval; switch (rval->ex_type) { case ET_INT: wvec[indx].w_float = rval->ex_int; break; case ET_FLT: wvec[indx].w_float = rval->ex_flt; break; default: pd_error(expr, "expr:bad right value type '%ld'", rval->ex_type); optr->ex_type = ET_FLT; optr->ex_flt = 0; return (1); } garray_redraw(garray); return(0); #else /* MSP */ /* * table lookup not done for MSP yet */ post("max_ex_tab: not complete for MSP yet!"); optr->ex_type = ET_FLT; optr->ex_flt = 0; #endif return (0); } int max_ex_var(struct expr *expr, t_symbol *var, struct ex_ex *optr, int idx) { optr->ex_type = ET_FLT; if (!strcmp(var->s_name, "sys_idx")) { optr->ex_flt = idx; return (0); } if (value_getfloat(var, &(optr->ex_flt))) { optr->ex_type = ET_FLT; optr->ex_flt = 0; pd_error(expr, "no such var '%s'", var->s_name); return (1); } return (0); } #ifdef PD /* this goes to the end of this file as the following functions * should be defined in the expr object in MSP */ #define ISTABLE(sym, garray, size, vec) \ if (!sym || !(garray = (t_garray *)pd_findbyclass(sym, garray_class)) || \ !garray_getfloatwords(garray, &size, &vec)) { \ optr->ex_type = ET_FLT; \ optr->ex_int = 0; \ pd_error(0, "no such table '%s'", sym?(sym->s_name):"(null)"); \ return; \ } /* * ex_size -- find the size of a table */ void ex_size(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr) { t_symbol *s; t_garray *garray; int size; t_word *wvec; if (argv->ex_type != ET_SYM) { post("expr: size: need a table name\n"); optr->ex_type = ET_INT; optr->ex_int = 0; return; } s = (fts_symbol_t ) argv->ex_ptr; ISTABLE(s, garray, size, wvec); optr->ex_type = ET_INT; optr->ex_int = size; } /* * ex_sum -- calculate the sum of all elements of a table */ void ex_sum(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr) { t_symbol *s; t_garray *garray; int size; t_word *wvec; t_float sum; int indx; if (argv->ex_type != ET_SYM) { post("expr: sum: need a table name\n"); optr->ex_type = ET_INT; optr->ex_int = 0; return; } s = (fts_symbol_t ) argv->ex_ptr; ISTABLE(s, garray, size, wvec); for (indx = 0, sum = 0; indx < size; indx++) sum += wvec[indx].w_float; optr->ex_type = ET_FLT; optr->ex_flt = sum; } /* * ex_Sum -- calculate the sum of table with the given boundaries */ void ex_Sum(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr) { t_symbol *s; t_garray *garray; int size; t_word *wvec; t_float sum; long indx, n1, n2; if (argv->ex_type != ET_SYM) { post("expr: sum: need a table name\n"); optr->ex_type = ET_INT; optr->ex_int = 0; return; } s = (fts_symbol_t ) argv->ex_ptr; ISTABLE(s, garray, size, wvec); switch((++argv)->ex_type) { case ET_INT: n1 = argv->ex_int; break; case ET_FLT: n1 = argv->ex_flt; break; default: post("expr: Sum: boundaries have to be fix values\n"); optr->ex_type = ET_INT; optr->ex_int = 0; return; } if (n1 < 0) n1 = 0; switch((++argv)->ex_type) { case ET_INT: n2 = argv->ex_int; break; case ET_FLT: n2 = argv->ex_flt; break; default: post("expr: Sum: boundaries have to be fix values\n"); optr->ex_type = ET_INT; optr->ex_int = 0; return; } if (n2 > size) n2 = size; for (indx = n1, sum = 0; indx <= n2; indx++) if (indx >= 0 && indx < size) sum += wvec[indx].w_float; optr->ex_type = ET_FLT; optr->ex_flt = sum; } /* * ex_avg -- calculate the average of a table */ void ex_avg(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr) { t_symbol *s; t_garray *garray; int size; t_word *wvec; t_float sum; int indx; if (argv->ex_type != ET_SYM) { post("expr: avg: need a table name\n"); optr->ex_type = ET_INT; optr->ex_int = 0; return; } s = (fts_symbol_t ) argv->ex_ptr; ISTABLE(s, garray, size, wvec); for (indx = 0, sum = 0; indx < size; indx++) sum += wvec[indx].w_float; optr->ex_type = ET_FLT; optr->ex_flt = sum / size; } /* * ex_Avg -- calculate the average of table with the given boundaries */ void ex_Avg(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr) { t_symbol *s; t_garray *garray; int size; t_word *wvec; t_float sum; long indx, n1, n2; if (argv->ex_type != ET_SYM) { post("expr: sum: need a table name\n"); optr->ex_type = ET_INT; optr->ex_int = 0; return; } s = (fts_symbol_t ) argv->ex_ptr; ISTABLE(s, garray, size, wvec); switch((++argv)->ex_type) { case ET_INT: n1 = argv->ex_int; break; case ET_FLT: n1 = argv->ex_flt; break; default: post("expr: Avg: boundaries have to be fix values\n"); optr->ex_type = ET_INT; optr->ex_int = 0; return; } if (n1 < 0) n1 = 0; switch((++argv)->ex_type) { case ET_INT: n2 = argv->ex_int; break; case ET_FLT: n2 = argv->ex_flt; break; default: post("expr: Avg: boundaries have to be fix values\n"); optr->ex_type = ET_INT; optr->ex_int = 0; return; } if (n2 >= size) n2 = size - 1; for (indx = n1, sum = 0; indx <= n2; indx++) if (indx >= 0 && indx < size) sum += wvec[indx].w_float; optr->ex_type = ET_FLT; optr->ex_flt = sum / (n2 - n1 + 1); } /* * max_ex_store --- store a value in a variable or table */ int max_ex_var_store(struct expr *expr, t_symbol * var, struct ex_ex *eptr, struct ex_ex *optr) { t_float value = 0.; *optr = *eptr; switch (eptr->ex_type) { case ET_INT: value = eptr->ex_int; break; case ET_FLT: value = eptr->ex_flt; break; default: post("do not know yet\n"); } if (value_setfloat(var, value)) { optr->ex_flt = 0; pd_error(expr, "no such var '%s'", var->s_name); return (1); } return (0); } /* * ex_store -- store a value in a table * if the index is greater the size of the table, * we will make a modulo the size of the table */ void ex_store(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr) { /* SDY - look into this function */ #if 0 fts_symbol_t s; fts_integer_vector_t *tw = 0; if (argv->ex_type != ET_SYM) { post("expr: store: need a table name\n"); } s = (fts_symbol_t ) (argv++)->ex_ptr; tw = table_integer_vector_get_by_name(s); if (! tw) { optr->ex_type = ET_INT; optr->ex_int = 0; post("expr: store: no such table %s\n", fts_symbol_name(s)); return; } if (argv->ex_type != ET_INT || argv[1].ex_type != ET_INT) { post("expr: store: arguments have to be integer\n"); optr->ex_type = ET_INT; optr->ex_int = 0; } fts_integer_vector_set_element(tw, argv->ex_int < 0 ? 0 : argv->ex_int % fts_integer_vector_get_size(tw), argv[1].ex_int); *optr = argv[1]; #endif } #else /* MSP */ void pd_error(void *object, char *fmt, ...) { va_list ap; t_int arg[8]; int i; static int saidit = 0; va_start(ap, fmt); /* SDY vsprintf(error_string, fmt, ap); */ post(fmt, ap); va_end(ap); /* SDY fprintf(stderr, "error: %s\n", error_string); error_object = object; */ if (!saidit) { post("... you might be able to track this down from the Find menu."); saidit = 1; } } #endif ================================================ FILE: pdExample/addons.make ================================================ ofxPd ================================================ FILE: pdExample/bin/data/.gitkeep ================================================ ================================================ FILE: pdExample/bin/data/pd/abs/test_abs.pd ================================================ #N canvas 80 290 336 208 10; #X text 14 32 this is a test abstraction ... does it load?; #X obj 37 85 inlet; #X obj 37 155 print PD; #X msg 37 119 test_abs: Hello World!; #X connect 1 0 3 0; #X connect 3 0 2 0; ================================================ FILE: pdExample/bin/data/pd/instance.pd ================================================ #N canvas 0 22 364 323 10; #X obj 39 45 r \$0-instance; #X obj 175 294 print PD; #X obj 244 179 \$0; #X obj 39 120 random 100; #X obj 39 76 route bang; #X msg 244 207 instance \$1; #X obj 244 152 loadbang; #X obj 175 235 list prepend; #X msg 39 150 hello world w/ rand num of \$1; #X obj 175 264 list trim; #X connect 0 0 4 0; #X connect 2 0 5 0; #X connect 3 0 8 0; #X connect 4 0 3 0; #X connect 4 1 7 0; #X connect 5 0 7 1; #X connect 6 0 2 0; #X connect 7 0 9 0; #X connect 8 0 7 0; #X connect 9 0 1 0; ================================================ FILE: pdExample/bin/data/pd/test.pd ================================================ #N canvas 486 364 409 355 10; #X obj 272 270 dac~; #N canvas 369 98 675 256 test 0; #N canvas 0 22 450 300 (subpatch) 0; #X array array1 10 float 3; #A 0 0.0857145 0.328572 0.500001 0.57143 0.514287 0.47143 0.357144 0.285715 0.057143 0; #X coords 0 1 9 -1 200 140 1; #X restore 425 58 graph; #X obj 133 169 tabread array1; #X obj 133 87 until; #X obj 133 109 f; #X obj 162 109 + 1; #X obj 190 109 sel 0; #X obj 162 131 mod 10; #X obj 114 23 inlet; #X msg 12 170 FINISH ARRAY TEST; #X msg 248 171 START ARRAY TEST; #X obj 114 53 t b b b; #X obj 12 202 print PD; #X obj 248 206 print PD; #X obj 133 204 print PD array1; #X connect 1 0 13 0; #X connect 2 0 3 0; #X connect 3 0 4 0; #X connect 3 0 1 0; #X connect 4 0 6 0; #X connect 5 0 2 1; #X connect 6 0 3 1; #X connect 6 0 5 0; #X connect 7 0 10 0; #X connect 8 0 11 0; #X connect 9 0 12 0; #X connect 10 0 8 0; #X connect 10 1 2 0; #X connect 10 2 9 0; #X restore 44 238 pd test array; #N canvas 0 22 949 263 test 0; #X obj 149 202 noteout 1; #X obj 249 18 inlet; #X obj 249 50 t b b b b b b b b; #X obj 265 203 ctlout 1; #X obj 351 203 pgmout 1; #X msg 351 174 100; #X obj 426 203 bendout 1; #X obj 504 204 touchout 1; #X obj 592 205 polytouchout 1; #X msg 426 174 2000; #X msg 30 172 START MIDI TEST; #X msg 801 180 MIDI TEST FINISHED; #X obj 30 201 print PD; #X obj 801 206 print PD; #X obj 705 206 midiout; #X obj 705 180 unpack f f; #X msg 705 155 239 0; #X text 398 231 note: bendout values are -8192 - 8192; #X obj 705 123 t b b; #X text 250 226 note: val ctl; #X text 588 151 note: val note; #X text 693 227 note: byte port; #X msg 592 178 100 64; #X msg 149 172 60 64; #X msg 265 173 100 64; #X msg 504 177 100; #X connect 1 0 2 0; #X connect 2 0 18 0; #X connect 2 1 22 0; #X connect 2 2 25 0; #X connect 2 3 9 0; #X connect 2 4 5 0; #X connect 2 5 24 0; #X connect 2 6 23 0; #X connect 2 7 10 0; #X connect 5 0 4 0; #X connect 9 0 6 0; #X connect 10 0 12 0; #X connect 11 0 13 0; #X connect 15 0 14 0; #X connect 15 1 14 1; #X connect 16 0 15 0; #X connect 18 0 11 0; #X connect 18 1 16 0; #X connect 22 0 8 0; #X connect 23 0 0 0; #X connect 24 0 3 0; #X connect 25 0 7 0; #X restore 61 207 pd test midi; #N canvas 0 22 161 223 sines 0; #X obj 53 110 osc~ 400; #X obj 53 84 +~ 400; #X obj 53 34 osc~ 1; #X obj 53 61 *~ 150; #X obj 53 137 *~ 0.2; #X obj 53 167 outlet~; #X connect 0 0 4 0; #X connect 1 0 0 0; #X connect 2 0 3 0; #X connect 3 0 1 0; #X connect 4 0 5 0; #X restore 272 193 pd sines; #N canvas 508 242 260 393 tone 0; #X obj 99 244 line~; #X obj 3 296 *~; #X obj 3 18 r tone; #X obj 3 123 mtof; #X obj 118 145 t b b; #X obj 118 120 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 -1; #X msg 99 192 1 50; #X obj 153 174 delay 100; #X msg 69 53 pitch 60; #X obj 135 289 env~; #X obj 135 313 change; #X obj 135 339 s env; #X obj 3 52 list trim; #X obj 3 86 route pitch bang; #X obj 3 329 *~ 0.4; #X obj 3 159 phasor~ 200; #X obj 3 360 outlet~; #X msg 153 205 0 500; #X obj 3 251 clip~ -1 1; #X obj 3 201 -~ 0.5; #X obj 3 225 *~ 2; #X text 11 183 make saw; #X connect 0 0 1 1; #X connect 0 0 9 0; #X connect 1 0 14 0; #X connect 2 0 12 0; #X connect 3 0 15 0; #X connect 4 0 7 0; #X connect 4 1 6 0; #X connect 5 0 4 0; #X connect 6 0 0 0; #X connect 7 0 17 0; #X connect 8 0 13 0; #X connect 9 0 10 0; #X connect 10 0 11 0; #X connect 12 0 13 0; #X connect 13 0 3 0; #X connect 13 1 4 0; #X connect 14 0 16 0; #X connect 15 0 19 0; #X connect 17 0 0 0; #X connect 18 0 1 0; #X connect 19 0 20 0; #X connect 20 0 18 0; #X restore 340 194 pd tone; #X obj 203 20 r \$0-fromOF; #X obj 203 52 print PD dollar zero; #X obj 27 22 r fromOF; #X obj 98 53 print PD; #X msg 27 109 bang; #N canvas 0 22 334 183 patch 0; #X obj 38 73 \$0; #X msg 38 103 PATCH OPENED: \$1; #X obj 38 42 loadbang; #X obj 179 73 \$0; #X msg 179 103 PATCH CLOSED: \$1; #X text 177 38 [closebang]; #X obj 38 136 print PD; #X obj 179 136 print PD; #X connect 0 0 1 0; #X connect 1 0 6 0; #X connect 2 0 0 0; #X connect 3 0 4 0; #X connect 4 0 7 0; #X restore 279 105 pd patch open close; #X obj 27 52 list trim; #X obj 27 81 route test; #N canvas 0 22 907 194 midi 0; #X obj 23 21 notein; #X obj 23 55 pack f f f; #X obj 134 55 pack f f f; #X obj 134 21 ctlin; #X obj 244 21 pgmin; #X obj 244 55 pack f f; #X obj 338 21 bendin; #X obj 338 55 pack f f; #X obj 435 21 touchin; #X obj 435 55 pack f f; #X obj 560 22 polytouchin; #X obj 701 57 pack f f; #X obj 701 23 midiin; #X obj 338 169 print PD MIDI; #X obj 560 56 pack f f f; #X text 283 1 note: bendin values are 0 - 16383; #X obj 798 56 pack f f; #X obj 798 22 sysexin; #X msg 23 84 notein \$3 \$1 \$2; #X msg 134 84 ctlin \$3 \$2 \$1; #X msg 244 84 pgm \$2 \$1; #X msg 338 84 bendin \$2 \$1; #X msg 435 84 touchin \$2 \$1; #X msg 560 85 polytouchin \$3 \$2 \$1; #X msg 701 86 midiin \$2 \$1; #X msg 798 85 sysexin \$2 \$1; #X connect 0 0 1 0; #X connect 0 1 1 1; #X connect 0 2 1 2; #X connect 1 0 18 0; #X connect 2 0 19 0; #X connect 3 0 2 0; #X connect 3 1 2 1; #X connect 3 2 2 2; #X connect 4 0 5 0; #X connect 4 1 5 1; #X connect 5 0 20 0; #X connect 6 0 7 0; #X connect 6 1 7 1; #X connect 7 0 21 0; #X connect 8 0 9 0; #X connect 8 1 9 1; #X connect 9 0 22 0; #X connect 10 0 14 0; #X connect 10 1 14 1; #X connect 10 2 14 2; #X connect 11 0 24 0; #X connect 12 0 11 0; #X connect 12 1 11 1; #X connect 14 0 23 0; #X connect 16 0 25 0; #X connect 17 0 16 0; #X connect 17 1 16 1; #X connect 18 0 13 0; #X connect 19 0 13 0; #X connect 20 0 13 0; #X connect 21 0 13 0; #X connect 22 0 13 0; #X connect 23 0 13 0; #X connect 24 0 13 0; #X connect 25 0 13 0; #X restore 279 134 pd midi in; #N canvas 0 22 788 194 test 0; #X obj 185 160 s toOF; #X obj 185 120 f 100; #X obj 237 120 symbol kaaa; #X obj 139 120 bang; #X obj 324 120 list 100 2.3 test 1 2 3; #X msg 484 120 \; toOF kaa 1 2.3 test; #X obj 266 24 inlet; #X obj 266 57 t b b b b b b b; #X msg 26 121 START MSG TEST; #X msg 650 118 MSG TEST FINISH; #X obj 26 150 print PD; #X obj 650 147 print PD; #X connect 1 0 0 0; #X connect 2 0 0 0; #X connect 3 0 0 0; #X connect 4 0 0 0; #X connect 6 0 7 0; #X connect 7 0 9 0; #X connect 7 1 5 0; #X connect 7 2 4 0; #X connect 7 3 2 0; #X connect 7 4 1 0; #X connect 7 5 3 0; #X connect 7 6 8 0; #X connect 8 0 10 0; #X connect 9 0 11 0; #X restore 78 177 pd test message; #N canvas 554 232 235 238 delay 0; #X obj 35 22 inlet~; #X obj 35 197 outlet~; #X obj 35 167 delread~ \$0-delay1; #X obj 35 55 delwrite~ \$0-delay1 5000; #X msg 35 137 1000; #X obj 35 107 loadbang; #X connect 0 0 3 0; #X connect 2 0 1 0; #X connect 4 0 2 0; #X connect 5 0 4 0; #X restore 205 192 pd delay; #X obj 205 166 adc~; #N canvas 742 363 375 145 license 0; #X text 8 15 Copyright (c) 2011 Dan Wilcox ; #X text 10 64 For information on usage and redistribution \, and for a DISCLAIMER OF ALL WARRANTIES \, see the file \, "LICENSE.txt \, " in this distribution.; #X text 9 40 BSD Simplified License; #X text 10 116 See https://github.com/danomatika/ofxPd for documentation ; #X restore 28 316 pd license; #X text 255 317 Dan Wilcox 2011 BSD; #X obj 27 139 t b b b b; #X obj 27 270 test_abs; #N canvas 666 63 404 247 scope~ 0; #X obj 26 28 inlet~ audio; #X obj 75 154 metro 100; #X msg 75 125 1; #X obj 56 59 clip~ -1 1; #X obj 75 95 loadbang; #N canvas 0 22 450 300 (subpatch) 0; #X array scope 512 float 2; #X coords 0 1 512 -1 200 140 1; #X restore 168 27 graph; #X obj 56 190 tabwrite~ scope; #X obj 25 222 outlet~; #X connect 0 0 3 0; #X connect 0 0 7 0; #X connect 1 0 6 0; #X connect 2 0 1 0; #X connect 3 0 6 0; #X connect 4 0 2 0; #X restore 272 240 pd scope~; #X connect 3 0 21 0; #X connect 4 0 21 0; #X connect 5 0 6 0; #X connect 7 0 8 0; #X connect 7 0 11 0; #X connect 9 0 19 0; #X connect 11 0 12 0; #X connect 12 0 9 0; #X connect 15 0 21 0; #X connect 16 0 15 0; #X connect 19 0 20 0; #X connect 19 1 1 0; #X connect 19 2 2 0; #X connect 19 3 14 0; #X connect 21 0 0 0; #X connect 21 0 0 1; ================================================ FILE: pdExample/src/main.cpp ================================================ /* * Copyright (c) 2011 Dan Wilcox * * BSD Simplified License. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. * * See https://github.com/danomatika/ofxPd for documentation * */ #include "ofMain.h" #include "ofApp.h" //======================================================================== int main() { ofSetupOpenGL(200, 200, OF_WINDOW); ofRunApp(new ofApp()); } ================================================ FILE: pdExample/src/ofApp.cpp ================================================ /* * Copyright (c) 2011 Dan Wilcox * * BSD Simplified License. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. * * See https://github.com/danomatika/ofxPd for documentation * */ #include "ofApp.h" //-------------------------------------------------------------- void ofApp::setup() { ofSetFrameRate(60); ofSetVerticalSync(true); ofSetBackgroundColor(100, 100, 100); //ofSetLogLevel("Pd", OF_LOG_VERBOSE); // see verbose info inside // double check where we are ... std::cout << ofFilePath::getCurrentWorkingDirectory() << std::endl; // the number of libpd ticks per buffer, // used to compute the audio buffer len: tpb * blocksize (always 64) #ifdef TARGET_LINUX_ARM // longer latency for Raspberry PI int ticksPerBuffer = 32; // 32 * 64 = buffer len of 2048 int numInputs = 0; // no built in mic #else int ticksPerBuffer = 8; // 8 * 64 = buffer len of 512 int numInputs = 1; #endif // setup OF sound stream ofSoundStreamSettings settings; settings.numInputChannels = 1; settings.numOutputChannels = 2; settings.sampleRate = 44100; settings.bufferSize = ofxPd::blockSize() * ticksPerBuffer; settings.setInListener(this); settings.setOutListener(this); ofSoundStreamSetup(settings); // setup Pd // // set 4th arg to true for queued message passing using an internal ringbuffer, // this is useful if you need to control where and when the message callbacks // happen (ie. within a GUI thread) // // note: you won't see any message prints until update() is called since // the queued messages are processed there, this is normal // if(!pd.init(2, numInputs, 44100, ticksPerBuffer, false)) { OF_EXIT_APP(1); } midiChan = 1; // midi channels are 1-16 // subscribe to receive source names pd.subscribe("toOF"); pd.subscribe("env"); // add message receiver, required if you want to receive messages pd.addReceiver(*this); // automatically receives from all subscribed sources pd.ignoreSource(*this, "env"); // don't receive from "env" //pd.ignoreSource(*this); // ignore all sources //pd.receiveSource(*this, "toOF"); // receive only from "toOF" // add midi receiver, required if you want to receive midi messages pd.addMidiReceiver(*this); // automatically receives from all channels //pd.ignoreMidiChannel(*this, 1); // ignore midi channel 1 //pd.ignoreMidiChannel(*this); // ignore all channels //pd.receiveMidiChannel(*this, 1); // receive only from channel 1 // add the data/pd folder to the search path pd.addToSearchPath("pd/abs"); // audio processing on pd.start(); // ----------------------------------------------------- std::cout << std::endl << "BEGIN Patch Test" << std::endl; // open patch Patch patch = pd.openPatch("pd/test.pd"); std::cout << patch << std::endl; // close patch pd.closePatch(patch); std::cout << patch << std::endl; // open patch again patch = pd.openPatch(patch); std::cout << patch << std::endl; std::cout << "FINISH Patch Test" << std::endl; // ----------------------------------------------------- std::cout << std::endl << "BEGIN Message Test" << std::endl; // test basic atoms pd.sendBang("fromOF"); pd.sendFloat("fromOF", 100); pd.sendSymbol("fromOF", "test string"); // stream interface pd << Bang("fromOF") << Float("fromOF", 100) << Symbol("fromOF", "test string"); // send a list pd.startMessage(); pd.addFloat(1.23); pd.addSymbol("a symbol"); pd.finishList("fromOF"); // send a message to the $0 receiver ie. $0-fromOF pd.startMessage(); pd.addFloat(1.23); pd.addSymbol("a symbol"); pd.finishList(patch.dollarZeroStr()+"-fromOF"); // send a list using the List object List testList; testList.addFloat(1.23); testList.addSymbol("sent from a List object"); pd.sendList("fromOF", testList); pd.sendMessage("fromOF", "msg", testList); // stream interface for list pd << StartMessage() << 1.23 << "sent from a streamed list" << FinishList("fromOF"); std::cout << "FINISH Message Test" << std::endl; // ----------------------------------------------------- std::cout << std::endl << "BEGIN MIDI Test" << std::endl; // send functions pd.sendNoteOn(midiChan, 60); pd.sendControlChange(midiChan, 0, 64); pd.sendProgramChange(midiChan, 100); // note: pgm num range is 1 - 128 pd.sendPitchBend(midiChan, 2000); // note: ofxPd uses -8192 - 8192 while [bendin] returns 0 - 16383, // so sending a val of 2000 gives 10192 in pd pd.sendAftertouch(midiChan, 100); pd.sendPolyAftertouch(midiChan, 64, 100); pd.sendMidiByte(0, 239); // note: pd adds +2 to the port number from [midiin], [sysexin], & [realtimein] pd.sendSysex(0, 239); // so sending to port 0 gives port 2 in pd pd.sendSysRealTime(0, 239); // stream pd << NoteOn(midiChan, 60) << ControlChange(midiChan, 100, 64) << ProgramChange(midiChan, 100) << PitchBend(midiChan, 2000) << Aftertouch(midiChan, 100) << PolyAftertouch(midiChan, 64, 100) << StartMidi(0) << 239 << Finish() << StartSysex(0) << 239 << Finish() << StartSysRealTime(0) << 239 << Finish(); std::cout << "FINISH MIDI Test" << std::endl; // ----------------------------------------------------- std::cout << std::endl << "BEGIN Array Test" << std::endl; // array check length std::cout << "array1 len: " << pd.arraySize("array1") << std::endl; // read array std::vector array1; pd.readArray("array1", array1); // sets array to correct size std::cout << "array1 "; for(int i = 0; i < array1.size(); ++i) { std::cout << array1[i] << " "; } std::cout << std::endl; // write array for(int i = 0; i < array1.size(); ++i) { array1[i] = i; } pd.writeArray("array1", array1); // ready array pd.readArray("array1", array1); std::cout << "array1 "; for(int i = 0; i < array1.size(); ++i) { std::cout << array1[i] << " "; } std::cout << std::endl; // clear array pd.clearArray("array1", 10); // ready array pd.readArray("array1", array1); std::cout << "array1 "; for(int i = 0; i < array1.size(); ++i) { std::cout << array1[i] << " "; } std::cout << std::endl; std::cout << "FINISH Array Test" << std::endl; // ----------------------------------------------------- std::cout << std::endl << "BEGIN PD Test" << std::endl; pd.sendSymbol("fromOF", "test"); std::cout << "FINISH PD Test" << std::endl; // ----------------------------------------------------- std::cout << std::endl << "BEGIN Instance Test" << std::endl; // open 10 instances for(int i = 0; i < 10; ++i) { Patch p = pd.openPatch("pd/instance.pd"); instances.push_back(p); } // send a hello bang to each instance individually using the dollarZero // to [r $0-instance] which should print the instance dollarZero unique id // and a unique random number for(int i = 0; i < instances.size(); ++i) { pd.sendBang(instances[i].dollarZeroStr()+"-instance"); } // send a random float between 0 and 100 for(int i = 0; i < instances.size(); ++i) { pd.sendFloat(instances[i].dollarZeroStr()+"-instance", int(ofRandom(0, 100))); } // send a symbol for(int i = 0; i < instances.size(); ++i) { pd.sendSymbol(instances[i].dollarZeroStr()+"-instance", "howdy dude"); } // close all instances for(int i = 0; i < instances.size(); ++i) { pd.closePatch(instances[i]); } instances.clear(); std::cout << "FINISH Instance Test" << std::endl; // ----------------------------------------------------- // play a tone by sending a list // [list tone pitch 72 ( pd.startMessage(); pd.addSymbol("pitch"); pd.addFloat(72); pd.finishList("tone"); pd.sendBang("tone"); } //-------------------------------------------------------------- void ofApp::update() { // since this is a test and we don't know if init() was called with // queued = true or not, we check it here if(pd.isQueued()) { // process any received messages, if you're using the queue and *do not* // call these, you won't receive any messages or midi! pd.receiveMessages(); pd.receiveMidi(); } // update scope array from pd pd.readArray("scope", scopeArray); } //-------------------------------------------------------------- void ofApp::draw() { // draw scope ofSetColor(0, 255, 0); ofSetRectMode(OF_RECTMODE_CENTER); float x = 0, y = ofGetHeight()/2; float w = ofGetWidth() / (float) scopeArray.size(), h = ofGetHeight()/2; for(int i = 0; i < scopeArray.size()-1; ++i) { ofDrawLine(x, y+scopeArray[i]*h, x+w, y+scopeArray[i+1]*h); x += w; } } //-------------------------------------------------------------- void ofApp::exit() { // cleanup ofSoundStreamStop(); } //-------------------------------------------------------------- void ofApp::playTone(int pitch) { pd << StartMessage() << "pitch" << pitch << FinishList("tone") << Bang("tone"); } //-------------------------------------------------------------- void ofApp::keyPressed (int key) { switch(key) { // musical keyboard case 'a': playTone(60); break; case 'w': playTone(61); break; case 's': playTone(62); break; case 'e': playTone(63); break; case 'd': playTone(64); break; case 'f': playTone(65); break; case 't': playTone(66); break; case 'g': playTone(67); break; case 'y': playTone(68); break; case 'h': playTone(69); break; case 'u': playTone(70); break; case 'j': playTone(71); break; case 'k': playTone(72); break; case ' ': if(pd.isReceivingSource(*this, "env")) { pd.ignoreSource(*this, "env"); std::cout << "ignoring env" << std::endl; } else { pd.receiveSource(*this, "env"); std::cout << "receiving from env" << std::endl; } break; default: break; } } //-------------------------------------------------------------- void ofApp::audioIn(ofSoundBuffer &buffer) { pd.audioIn(buffer); } //-------------------------------------------------------------- void ofApp::audioOut(ofSoundBuffer &buffer) { pd.audioOut(buffer); } //-------------------------------------------------------------- void ofApp::print(const std::string &message) { std::cout << message << std::endl; } //-------------------------------------------------------------- void ofApp::receiveBang(const std::string &dest) { std::cout << "OF: bang " << dest << std::endl; } void ofApp::receiveFloat(const std::string &dest, float value) { std::cout << "OF: float " << dest << ": " << value << std::endl; } void ofApp::receiveSymbol(const std::string &dest, const std::string &symbol) { std::cout << "OF: symbol " << dest << ": " << symbol << std::endl; } void ofApp::receiveList(const std::string &dest, const pd::List &list) { std::cout << "OF: list " << dest << ": "; // step through the list for(int i = 0; i < list.len(); ++i) { if(list.isFloat(i)) std::cout << list.getFloat(i) << " "; else if(list.isSymbol(i)) std::cout << list.getSymbol(i) << " "; } // you can also use the built in toString function or simply stream it out // std::cout << list.toString(); // std::cout << list; // print an OSC-style type string std::cout << list.types() << std::endl; } void ofApp::receiveMessage(const std::string &dest, const std::string &msg, const pd::List &list) { std::cout << "OF: message " << dest << ": " << msg << " " << list.toString() << list.types() << std::endl; } //-------------------------------------------------------------- void ofApp::receiveNoteOn(const int channel, const int pitch, const int velocity) { std::cout << "OF MIDI: note on: " << channel << " " << pitch << " " << velocity << std::endl; } void ofApp::receiveControlChange(const int channel, const int controller, const int value) { std::cout << "OF MIDI: control change: " << channel << " " << controller << " " << value << std::endl; } // note: pgm nums are 1-128 to match pd void ofApp::receiveProgramChange(const int channel, const int value) { std::cout << "OF MIDI: program change: " << channel << " " << value << std::endl; } void ofApp::receivePitchBend(const int channel, const int value) { std::cout << "OF MIDI: pitch bend: " << channel << " " << value << std::endl; } void ofApp::receiveAftertouch(const int channel, const int value) { std::cout << "OF MIDI: aftertouch: " << channel << " " << value << std::endl; } void ofApp::receivePolyAftertouch(const int channel, const int pitch, const int value) { std::cout << "OF MIDI: poly aftertouch: " << channel << " " << pitch << " " << value << std::endl; } // note: pd adds +2 to the port num, so sending to port 3 in pd to [midiout], // shows up at port 1 in ofxPd void ofApp::receiveMidiByte(const int port, const int byte) { std::cout << "OF MIDI: midi byte: " << port << " " << byte << std::endl; } ================================================ FILE: pdExample/src/ofApp.h ================================================ /* * Copyright (c) 2011 Dan Wilcox * * BSD Simplified License. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. * * See https://github.com/danomatika/ofxPd for documentation * */ #pragma once #include "ofMain.h" #include "ofxPd.h" // a namespace for the Pd types using namespace pd; // inherit pd receivers to receive message and midi events class ofApp : public ofBaseApp, public PdReceiver, public PdMidiReceiver { public: // main void setup(); void update(); void draw(); void exit(); // do something void playTone(int pitch); // input callbacks void keyPressed(int key); // audio callbacks void audioIn(ofSoundBuffer &buffer); void audioOut(ofSoundBuffer &buffer); // pd message receiver callbacks void print(const std::string &message); void receiveBang(const std::string &dest); void receiveFloat(const std::string &dest, float value); void receiveSymbol(const std::string &dest, const std::string &symbol); void receiveList(const std::string &dest, const pd::List &list); void receiveMessage(const std::string &dest, const std::string &msg, const pd::List &list); // pd midi receiver callbacks void receiveNoteOn(const int channel, const int pitch, const int velocity); void receiveControlChange(const int channel, const int controller, const int value); void receiveProgramChange(const int channel, const int value); void receivePitchBend(const int channel, const int value); void receiveAftertouch(const int channel, const int value); void receivePolyAftertouch(const int channel, const int pitch, const int value); void receiveMidiByte(const int port, const int byte); ofxPd pd; std::vector scopeArray; std::vector instances; int midiChan; }; ================================================ FILE: pdExampleIOS/addons.make ================================================ ofxPd ================================================ FILE: pdExampleIOS/bin/data/.gitkeep ================================================ ================================================ FILE: pdExampleIOS/bin/data/pd/abs/test_abs.pd ================================================ #N canvas 80 290 336 208 10; #X text 14 32 this is a test abstraction ... does it load?; #X obj 37 85 inlet; #X obj 37 155 print PD; #X msg 37 119 test_abs: Hello World!; #X connect 1 0 3 0; #X connect 3 0 2 0; ================================================ FILE: pdExampleIOS/bin/data/pd/instance.pd ================================================ #N canvas 0 22 364 323 10; #X obj 39 45 r \$0-instance; #X obj 175 294 print PD; #X obj 244 179 \$0; #X obj 39 120 random 100; #X obj 39 76 route bang; #X msg 244 207 instance \$1; #X obj 244 152 loadbang; #X obj 175 235 list prepend; #X msg 39 150 hello world w/ rand num of \$1; #X obj 175 264 list trim; #X connect 0 0 4 0; #X connect 2 0 5 0; #X connect 3 0 8 0; #X connect 4 0 3 0; #X connect 4 1 7 0; #X connect 5 0 7 1; #X connect 6 0 2 0; #X connect 7 0 9 0; #X connect 8 0 7 0; #X connect 9 0 1 0; ================================================ FILE: pdExampleIOS/bin/data/pd/test.pd ================================================ #N canvas 711 355 409 355 10; #X obj 272 270 dac~; #N canvas 369 98 675 256 test 0; #N canvas 0 22 450 300 (subpatch) 0; #X array array1 10 float 3; #A 0 0.0857145 0.328572 0.500001 0.57143 0.514287 0.47143 0.357144 0.285715 0.057143 0; #X coords 0 1 9 -1 200 140 1; #X restore 425 58 graph; #X obj 133 169 tabread array1; #X obj 133 87 until; #X obj 133 109 f; #X obj 162 109 + 1; #X obj 190 109 sel 0; #X obj 162 131 mod 10; #X obj 114 23 inlet; #X msg 12 170 FINISH ARRAY TEST; #X msg 248 171 START ARRAY TEST; #X obj 114 53 t b b b; #X obj 12 202 print PD; #X obj 248 206 print PD; #X obj 133 204 print PD array1; #X connect 1 0 13 0; #X connect 2 0 3 0; #X connect 3 0 4 0; #X connect 3 0 1 0; #X connect 4 0 6 0; #X connect 5 0 2 1; #X connect 6 0 3 1; #X connect 6 0 5 0; #X connect 7 0 10 0; #X connect 8 0 11 0; #X connect 9 0 12 0; #X connect 10 0 8 0; #X connect 10 1 2 0; #X connect 10 2 9 0; #X restore 44 238 pd test array; #N canvas 0 22 949 263 test 0; #X obj 149 202 noteout 1; #X obj 249 18 inlet; #X obj 249 50 t b b b b b b b b; #X obj 265 203 ctlout 1; #X obj 351 203 pgmout 1; #X msg 351 174 100; #X obj 426 203 bendout 1; #X obj 504 204 touchout 1; #X obj 592 205 polytouchout 1; #X msg 426 174 2000; #X msg 30 172 START MIDI TEST; #X msg 801 180 MIDI TEST FINISHED; #X obj 30 201 print PD; #X obj 801 206 print PD; #X obj 705 206 midiout; #X obj 705 180 unpack f f; #X msg 705 155 239 0; #X text 398 231 note: bendout values are -8192 - 8192; #X obj 705 123 t b b; #X text 250 226 note: val ctl; #X text 588 151 note: val note; #X text 693 227 note: byte port; #X msg 592 178 100 64; #X msg 149 172 60 64; #X msg 265 173 100 64; #X msg 504 177 100; #X connect 1 0 2 0; #X connect 2 0 18 0; #X connect 2 1 22 0; #X connect 2 2 25 0; #X connect 2 3 9 0; #X connect 2 4 5 0; #X connect 2 5 24 0; #X connect 2 6 23 0; #X connect 2 7 10 0; #X connect 5 0 4 0; #X connect 9 0 6 0; #X connect 10 0 12 0; #X connect 11 0 13 0; #X connect 15 0 14 0; #X connect 15 1 14 1; #X connect 16 0 15 0; #X connect 18 0 11 0; #X connect 18 1 16 0; #X connect 22 0 8 0; #X connect 23 0 0 0; #X connect 24 0 3 0; #X connect 25 0 7 0; #X restore 61 207 pd test midi; #N canvas 0 22 161 223 sines 0; #X obj 53 110 osc~ 400; #X obj 53 84 +~ 400; #X obj 53 34 osc~ 1; #X obj 53 61 *~ 150; #X obj 53 137 *~ 0.2; #X obj 53 167 outlet~; #X connect 0 0 4 0; #X connect 1 0 0 0; #X connect 2 0 3 0; #X connect 3 0 1 0; #X connect 4 0 5 0; #X restore 272 193 pd sines; #N canvas 508 242 260 393 tone 0; #X obj 99 244 line~; #X obj 3 296 *~; #X obj 3 18 r tone; #X obj 3 123 mtof; #X obj 118 145 t b b; #X obj 118 120 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 -1; #X msg 99 192 1 50; #X obj 153 174 delay 100; #X msg 69 53 pitch 60; #X obj 135 289 env~; #X obj 135 313 change; #X obj 135 339 s env; #X obj 3 52 list trim; #X obj 3 86 route pitch bang; #X obj 3 329 *~ 0.4; #X obj 3 159 phasor~ 200; #X obj 3 360 outlet~; #X msg 153 205 0 500; #X obj 3 251 clip~ -1 1; #X obj 3 201 -~ 0.5; #X obj 3 225 *~ 2; #X text 11 183 make saw; #X connect 0 0 1 1; #X connect 0 0 9 0; #X connect 1 0 14 0; #X connect 2 0 12 0; #X connect 3 0 15 0; #X connect 4 0 7 0; #X connect 4 1 6 0; #X connect 5 0 4 0; #X connect 6 0 0 0; #X connect 7 0 17 0; #X connect 8 0 13 0; #X connect 9 0 10 0; #X connect 10 0 11 0; #X connect 12 0 13 0; #X connect 13 0 3 0; #X connect 13 1 4 0; #X connect 14 0 16 0; #X connect 15 0 19 0; #X connect 17 0 0 0; #X connect 18 0 1 0; #X connect 19 0 20 0; #X connect 20 0 18 0; #X restore 340 194 pd tone; #X obj 203 20 r \$0-fromOF; #X obj 203 52 print PD dollar zero; #X obj 27 22 r fromOF; #X obj 98 53 print PD; #X msg 27 109 bang; #N canvas 0 22 334 183 patch 0; #X obj 38 73 \$0; #X msg 38 103 PATCH OPENED: \$1; #X obj 38 42 loadbang; #X obj 179 73 \$0; #X msg 179 103 PATCH CLOSED: \$1; #X text 177 38 [closebang]; #X obj 38 136 print PD; #X obj 179 136 print PD; #X connect 0 0 1 0; #X connect 1 0 6 0; #X connect 2 0 0 0; #X connect 3 0 4 0; #X connect 4 0 7 0; #X restore 279 105 pd patch open close; #X obj 27 52 list trim; #X obj 27 81 route test; #N canvas 0 22 907 194 midi 0; #X obj 23 21 notein; #X obj 23 55 pack f f f; #X obj 134 55 pack f f f; #X obj 134 21 ctlin; #X obj 244 21 pgmin; #X obj 244 55 pack f f; #X obj 338 21 bendin; #X obj 338 55 pack f f; #X obj 435 21 touchin; #X obj 435 55 pack f f; #X obj 560 22 polytouchin; #X obj 701 57 pack f f; #X obj 701 23 midiin; #X obj 338 169 print PD MIDI; #X obj 560 56 pack f f f; #X text 283 1 note: bendin values are 0 - 16383; #X obj 798 56 pack f f; #X obj 798 22 sysexin; #X msg 23 84 notein \$3 \$1 \$2; #X msg 134 84 ctlin \$3 \$2 \$1; #X msg 244 84 pgm \$2 \$1; #X msg 338 84 bendin \$2 \$1; #X msg 435 84 touchin \$2 \$1; #X msg 560 85 polytouchin \$3 \$2 \$1; #X msg 701 86 midiin \$2 \$1; #X msg 798 85 sysexin \$2 \$1; #X connect 0 0 1 0; #X connect 0 1 1 1; #X connect 0 2 1 2; #X connect 1 0 18 0; #X connect 2 0 19 0; #X connect 3 0 2 0; #X connect 3 1 2 1; #X connect 3 2 2 2; #X connect 4 0 5 0; #X connect 4 1 5 1; #X connect 5 0 20 0; #X connect 6 0 7 0; #X connect 6 1 7 1; #X connect 7 0 21 0; #X connect 8 0 9 0; #X connect 8 1 9 1; #X connect 9 0 22 0; #X connect 10 0 14 0; #X connect 10 1 14 1; #X connect 10 2 14 2; #X connect 11 0 24 0; #X connect 12 0 11 0; #X connect 12 1 11 1; #X connect 14 0 23 0; #X connect 16 0 25 0; #X connect 17 0 16 0; #X connect 17 1 16 1; #X connect 18 0 13 0; #X connect 19 0 13 0; #X connect 20 0 13 0; #X connect 21 0 13 0; #X connect 22 0 13 0; #X connect 23 0 13 0; #X connect 24 0 13 0; #X connect 25 0 13 0; #X restore 279 134 pd midi in; #N canvas 0 22 788 194 test 0; #X obj 185 160 s toOF; #X obj 185 120 f 100; #X obj 237 120 symbol kaaa; #X obj 139 120 bang; #X obj 324 120 list 100 2.3 test 1 2 3; #X msg 484 120 \; toOF kaa 1 2.3 test; #X obj 266 24 inlet; #X obj 266 57 t b b b b b b b; #X msg 26 121 START MSG TEST; #X msg 650 118 MSG TEST FINISH; #X obj 26 150 print PD; #X obj 650 147 print PD; #X connect 1 0 0 0; #X connect 2 0 0 0; #X connect 3 0 0 0; #X connect 4 0 0 0; #X connect 6 0 7 0; #X connect 7 0 9 0; #X connect 7 1 5 0; #X connect 7 2 4 0; #X connect 7 3 2 0; #X connect 7 4 1 0; #X connect 7 5 3 0; #X connect 7 6 8 0; #X connect 8 0 10 0; #X connect 9 0 11 0; #X restore 78 177 pd test message; #N canvas 554 232 235 238 delay 0; #X obj 35 22 inlet~; #X obj 35 197 outlet~; #X obj 35 167 delread~ \$0-delay1; #X obj 35 55 delwrite~ \$0-delay1 5000; #X msg 35 137 1000; #X obj 35 107 loadbang; #X connect 0 0 3 0; #X connect 2 0 1 0; #X connect 4 0 2 0; #X connect 5 0 4 0; #X restore 205 192 pd delay; #X obj 205 166 adc~; #N canvas 742 363 375 145 license 0; #X text 8 15 Copyright (c) 2011 Dan Wilcox ; #X text 10 64 For information on usage and redistribution \, and for a DISCLAIMER OF ALL WARRANTIES \, see the file \, "LICENSE.txt \, " in this distribution.; #X text 9 40 BSD Simplified License; #X text 10 116 See https://github.com/danomatika/ofxPd for documentation ; #X restore 28 316 pd license; #X text 255 317 Dan Wilcox 2011 BSD; #X obj 27 139 t b b b b; #X obj 27 270 test_abs; #N canvas 666 63 404 247 scope~ 0; #X obj 26 28 inlet~ audio; #X obj 75 154 metro 100; #X msg 75 125 1; #X obj 56 59 clip~ -1 1; #X obj 75 95 loadbang; #N canvas 0 22 450 300 (subpatch) 0; #X array scope 512 float 2; #X coords 0 1 512 -1 200 140 1; #X restore 168 27 graph; #X obj 56 190 tabwrite~ scope; #X obj 25 222 outlet~; #X connect 0 0 3 0; #X connect 0 0 7 0; #X connect 1 0 6 0; #X connect 2 0 1 0; #X connect 3 0 6 0; #X connect 4 0 2 0; #X restore 272 240 pd scope~; #X obj 163 237 loadbang; #X obj 162 269 metro 1000; #X obj 197 302 + 1; #X obj 160 300 f 0; #X obj 164 329 s toOF; #X connect 3 0 21 0; #X connect 4 0 21 0; #X connect 5 0 6 0; #X connect 7 0 8 0; #X connect 7 0 11 0; #X connect 9 0 19 0; #X connect 11 0 12 0; #X connect 12 0 9 0; #X connect 15 0 21 0; #X connect 16 0 15 0; #X connect 19 0 20 0; #X connect 19 1 1 0; #X connect 19 2 2 0; #X connect 19 3 14 0; #X connect 21 0 0 0; #X connect 21 0 0 1; #X connect 22 0 23 0; #X connect 23 0 25 0; #X connect 24 0 25 1; #X connect 25 0 24 0; #X connect 25 0 26 0; ================================================ FILE: pdExampleIOS/src/main.mm ================================================ /* * Copyright (c) 2011 Dan Wilcox * * BSD Simplified License. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. * * See https://github.com/danomatika/ofxPd for documentation * */ #include "ofMain.h" #include "ofApp.h" // quick fix until 64bit OF on iOS, // from https://github.com/openframeworks/openFrameworks/issues/3178 //#if OF_VERSION_MAJOR == 0 && OF_VERSION_MINOR <= 8 //extern "C" { // size_t fwrite$UNIX2003(const void *a, size_t b, size_t c, FILE *d) { // return fwrite(a, b, c, d); // } // char* strerror$UNIX2003(int errnum) { // return strerror(errnum); // } // time_t mktime$UNIX2003(struct tm * a) { // return mktime(a); // } // double strtod$UNIX2003(const char * a, char ** b) { // return strtod(a, b); // } //} //#endif //======================================================================== int main() { // here are the most commonly used iOS window settings. //------------------------------------------------------ ofiOSWindowSettings settings; settings.enableRetina = false; // enables retina resolution if the device supports it. settings.enableDepth = false; // enables depth buffer for 3d drawing. settings.enableAntiAliasing = false; // enables anti-aliasing which smooths out graphics on the screen. settings.numOfAntiAliasingSamples = 0; // number of samples used for anti-aliasing. settings.enableHardwareOrientation = true; // enables native view orientation. settings.enableHardwareOrientationAnimation = true; // enables native orientation changes to be animated. //settings.glesVersion = OFXIOS_RENDERER_ES1; // type of renderer to use, ES1, ES2, etc. //settings.setupOrientation = OF_ORIENTATION_90_LEFT; // set default orientation for setup ofCreateWindow(settings); ofRunApp(new ofApp); } ================================================ FILE: pdExampleIOS/src/ofApp.h ================================================ /* * Copyright (c) 2011 Dan Wilcox * * BSD Simplified License. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. * * See https://github.com/danomatika/ofxPd for documentation * */ #pragma once #include "ofMain.h" #include "ofxiOS.h" #include "ofxiOSExtras.h" #include "ofxPd.h" // a namespace for the Pd types using namespace pd; // derive from Pd receiver classes to receive message and midi events class ofApp : public ofxiOSApp, public PdReceiver, public PdMidiReceiver { public: // main void setup(); void update(); void draw(); void exit(); // input callbacks void keyPressed(int key); void touchDown(ofTouchEventArgs &touch); void touchMoved(ofTouchEventArgs &touch); void touchUp(ofTouchEventArgs &touch); void touchDoubleTap(ofTouchEventArgs &touch); void touchCancelled(ofTouchEventArgs &touch); void lostFocus(); void gotFocus(); void gotMemoryWarning(); void deviceOrientationChanged(int newOrientation); // audio callbacks void audioIn(ofSoundBuffer &buffer); void audioOut(ofSoundBuffer &buffer); // pd message receiver callbacks void print(const std::string &message); void receiveBang(const std::string &dest); void receiveFloat(const std::string &dest, float value); void receiveSymbol(const std::string &dest, const std::string &symbol); void receiveList(const std::string &dest, const pd::List &list); void receiveMessage(const std::string &dest, const std::string &msg, const pd::List &list); // pd midi receiver callbacks void receiveNoteOn(const int channel, const int pitch, const int velocity); void receiveControlChange(const int channel, const int controller, const int value); void receiveProgramChange(const int channel, const int value); void receivePitchBend(const int channel, const int value); void receiveAftertouch(const int channel, const int value); void receivePolyAftertouch(const int channel, const int pitch, const int value); void receiveMidiByte(const int port, const int byte); // do something void playTone(int pitch); // sets the preferred sample rate, returns the *actual* samplerate // which may be different ie. iPhone 6S only wants 48k float setAVSessionSampleRate(float preferredSampleRate); ofxPd pd; std::vector scopeArray; std::vector instances; int midiChan; }; ================================================ FILE: pdExampleIOS/src/ofApp.mm ================================================ /* * Copyright (c) 2011 Dan Wilcox * * BSD Simplified License. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. * * See https://github.com/danomatika/ofxPd for documentation * */ #include "ofApp.h" #import //-------------------------------------------------------------- void ofApp::setup() { ofSetFrameRate(60); ofSetVerticalSync(true); ofBackground(127, 127, 127); //ofSetLogLevel("Pd", OF_LOG_VERBOSE); // see verbose info inside // double check where we are ... std::cout << ofFilePath::getCurrentWorkingDirectory() << std::endl; // register touch events ofRegisterTouchEvents(this); // iOSAlerts will be sent to this ofxiOSAlerts.addListener(this); // try to set the preferred iOS sample rate, but get the actual sample rate // being used by the AVSession since newer devices like the iPhone 6S only // want specific values (ie 48000 instead of 44100) float sampleRate = setAVSessionSampleRate(44100); // the number if libpd ticks per buffer, // used to compute the audio buffer len: tpb * blocksize (always 64) int ticksPerBuffer = 8; // 8 * 64 = buffer len of 512 // setup OF sound stream using the current *actual* samplerate ofSoundStreamSettings settings; settings.numInputChannels = 1; settings.numOutputChannels = 2; settings.sampleRate = sampleRate; settings.bufferSize = ofxPd::blockSize() * ticksPerBuffer; settings.setInListener(this); settings.setOutListener(this); ofSoundStreamSetup(settings); // setup Pd // // set 4th arg to true for queued message passing using an internal ringbuffer, // this is useful if you need to control where and when the message callbacks // happen (ie. within a GUI thread) // // note: you won't see any message prints until update() is called since // the queued messages are processed there, this is normal // if(!pd.init(2, 1, sampleRate, ticksPerBuffer-1, false)) { OF_EXIT_APP(1); } midiChan = 1; // midi channels are 1-16 // subscribe to receive source names pd.subscribe("toOF"); pd.subscribe("env"); // add message receiver, required if you want to receive messages pd.addReceiver(*this); // automatically receives from all subscribed sources pd.ignoreSource(*this, "env"); // don't receive from "env" //pd.ignoreSource(*this); // ignore all sources //pd.receiveSource(*this, "toOF"); // receive only from "toOF" // add midi receiver, required if you want to receive midi messages pd.addMidiReceiver(*this); // automatically receives from all channels //pd.ignoreMidiChannel(*this, 1); // ignore midi channel 1 //pd.ignoreMidiChannel(*this); // ignore all channels //pd.receiveMidiChannel(*this, 1); // receive only from channel 1 // add the data/pd folder to the search path pd.addToSearchPath("pd/abs"); // audio processing on pd.start(); // ----------------------------------------------------- std::cout << std::endl << "BEGIN Patch Test" << std::endl; // open patch Patch patch = pd.openPatch("pd/test.pd"); std::cout << patch << std::endl; // close patch pd.closePatch(patch); std::cout << patch << std::endl; // open patch again patch = pd.openPatch(patch); std::cout << patch << std::endl; std::cout << "FINISH Patch Test" << std::endl; // ----------------------------------------------------- std::cout << std::endl << "BEGIN Message Test" << std::endl; // test basic atoms pd.sendBang("fromOF"); pd.sendFloat("fromOF", 100); pd.sendSymbol("fromOF", "test string"); // stream interface pd << Bang("fromOF") << Float("fromOF", 100) << Symbol("fromOF", "test string"); // send a list pd.startMessage(); pd.addFloat(1.23); pd.addSymbol("a symbol"); pd.finishList("fromOF"); // send a message to the $0 receiver ie $0-fromOF pd.startMessage(); pd.addFloat(1.23); pd.addSymbol("a symbol"); pd.finishList(patch.dollarZeroStr()+"-fromOF"); // send a list using the List object pd::List testList; testList.addFloat(1.23); testList.addSymbol("sent from a List object"); pd.sendList("fromOF", testList); pd.sendMessage("fromOF", "msg", testList); // stream interface for list pd << StartMessage() << 1.23 << "sent from a streamed list" << FinishList("fromOF"); std::cout << "FINISH Message Test" << std::endl; // ----------------------------------------------------- std::cout << std::endl << "BEGIN MIDI Test" << std::endl; // send functions pd.sendNoteOn(midiChan, 60); pd.sendControlChange(midiChan, 0, 64); pd.sendProgramChange(midiChan, 100); // note: pgm num range is 1 - 128 pd.sendPitchBend(midiChan, 2000); // note: ofxPd uses -8192 - 8192 while [bendin] returns 0 - 16383, // so sending a val of 2000 gives 10192 in pd pd.sendAftertouch(midiChan, 100); pd.sendPolyAftertouch(midiChan, 64, 100); pd.sendMidiByte(0, 239); // note: pd adds +2 to the port number from [midiin], [sysexin], & [realtimein] pd.sendSysex(0, 239); // so sending to port 0 gives port 2 in pd pd.sendSysRealTime(0, 239); // stream pd << NoteOn(midiChan, 60) << ControlChange(midiChan, 100, 64) << ProgramChange(midiChan, 100) << PitchBend(midiChan, 2000) << Aftertouch(midiChan, 100) << PolyAftertouch(midiChan, 64, 100) << StartMidi(0) << 239 << Finish() << StartSysex(0) << 239 << Finish() << StartSysRealTime(0) << 239 << Finish(); std::cout << "FINISH MIDI Test" << std::endl; // ----------------------------------------------------- std::cout << std::endl << "BEGIN Array Test" << std::endl; // array check length std::cout << "array1 len: " << pd.arraySize("array1") << std::endl; // read array std::vector array1; pd.readArray("array1", array1); // sets array to correct size std::cout << "array1 "; for(int i = 0; i < array1.size(); ++i) { std::cout << array1[i] << " "; } std::cout << std::endl; // write array for(int i = 0; i < array1.size(); ++i) array1[i] = i; pd.writeArray("array1", array1); // ready array pd.readArray("array1", array1); std::cout << "array1 "; for(int i = 0; i < array1.size(); ++i) { std::cout << array1[i] << " "; } std::cout << std::endl; // clear array pd.clearArray("array1", 10); // ready array pd.readArray("array1", array1); std::cout << "array1 "; for(int i = 0; i < array1.size(); ++i) { std::cout << array1[i] << " "; } std::cout << std::endl; std::cout << "FINISH Array Test" << std::endl; // ----------------------------------------------------- std::cout << std::endl << "BEGIN PD Test" << std::endl; pd.sendSymbol("fromOF", "test"); std::cout << "FINISH PD Test" << std::endl; // ----------------------------------------------------- std::cout << std::endl << "BEGIN Instance Test" << std::endl; // open 10 instances for(int i = 0; i < 10; ++i) { Patch p = pd.openPatch("pd/instance.pd"); instances.push_back(p); } // send a hello bang to each instance individually using the dollarZero // to [r $0-instance] which should print the instance dollarZero unique id // and a unique random number for(int i = 0; i < instances.size(); ++i) { pd.sendBang(instances[i].dollarZeroStr()+"-instance"); } // send a random float between 0 and 100 for(int i = 0; i < instances.size(); ++i) { pd.sendFloat(instances[i].dollarZeroStr()+"-instance", int(ofRandom(0, 100))); } // send a symbol for(int i = 0; i < instances.size(); ++i) { pd.sendSymbol(instances[i].dollarZeroStr()+"-instance", "howdy dude"); } // close all instances for(int i = 0; i < instances.size(); ++i) { pd.closePatch(instances[i]); } instances.clear(); std::cout << "FINISH Instance Test" << std::endl; // ----------------------------------------------------- // play a tone by sending a list // [list tone pitch 72 ( pd.startMessage(); pd.addSymbol("pitch"); pd.addFloat(72); pd.finishList("tone"); pd.sendBang("tone"); } //-------------------------------------------------------------- void ofApp::update() { ofBackground(100, 100, 100); // since this is a test and we don't know if init() was called with // queued = true or not, we check it here if(pd.isQueued()) { // process any received messages, if you're using the queue and *do not* // call these, you won't receive any messages or midi! pd.receiveMessages(); pd.receiveMidi(); } // update scope array from pd pd.readArray("scope", scopeArray); } //-------------------------------------------------------------- void ofApp::draw() { // draw scope ofSetColor(0, 255, 0); ofSetRectMode(OF_RECTMODE_CENTER); float x = 0, y = ofGetHeight()/2; float w = ofGetWidth() / (float) scopeArray.size(), h = ofGetHeight()/2; for(int i = 0; i < scopeArray.size()-1; ++i) { ofDrawLine(x, y+scopeArray[i]*h, x+w, y+scopeArray[i+1]*h); x += w; } } //-------------------------------------------------------------- void ofApp::exit() { // cleanup ofSoundStreamStop(); } //-------------------------------------------------------------- void ofApp::keyPressed (int key) { switch(key) { // musical keyboard if you have a usb keyboard case 'a': playTone(60); break; case 'w': playTone(61); break; case 's': playTone(62); break; case 'e': playTone(63); break; case 'd': playTone(64); break; case 'f': playTone(65); break; case 't': playTone(66); break; case 'g': playTone(67); break; case 'y': playTone(68); break; case 'h': playTone(69); break; case 'u': playTone(70); break; case 'j': playTone(71); break; case 'k': playTone(72); break; case ' ': if(pd.isReceivingSource(*this, "env")) { pd.ignoreSource(*this, "env"); std::cout << "ignoring env" << std::endl; } else { pd.receiveSource(*this, "env"); std::cout << "receiving from env" << std::endl; } break; default: break; } } //-------------------------------------------------------------- void ofApp::touchDown(ofTouchEventArgs &touch) { // y pos changes pitch int pitch = (-1 * (touch.y/ofGetHeight()) + 1) * 127; playTone(pitch); } //-------------------------------------------------------------- void ofApp::touchMoved(ofTouchEventArgs &touch) {} //-------------------------------------------------------------- void ofApp::touchUp(ofTouchEventArgs &touch) {} //-------------------------------------------------------------- void ofApp::touchDoubleTap(ofTouchEventArgs &touch) {} //-------------------------------------------------------------- void ofApp::touchCancelled(ofTouchEventArgs &args) {} //-------------------------------------------------------------- void ofApp::lostFocus() {} //-------------------------------------------------------------- void ofApp::gotFocus() {} //-------------------------------------------------------------- void ofApp::gotMemoryWarning() {} //-------------------------------------------------------------- void ofApp::deviceOrientationChanged(int newOrientation) {} //-------------------------------------------------------------- void ofApp::audioIn(ofSoundBuffer &buffer) { pd.audioIn(buffer); } //-------------------------------------------------------------- void ofApp::audioOut(ofSoundBuffer &buffer) { pd.audioOut(buffer); } //-------------------------------------------------------------- void ofApp::print(const std::string &message) { std::cout << message << std::endl; } //-------------------------------------------------------------- void ofApp::receiveBang(const std::string &dest) { std::cout << "OF: bang " << dest << std::endl; } void ofApp::receiveFloat(const std::string &dest, float value) { std::cout << "OF: float " << dest << ": " << value << std::endl; } void ofApp::receiveSymbol(const std::string &dest, const std::string &symbol) { std::cout << "OF: symbol " << dest << ": " << symbol << std::endl; } void ofApp::receiveList(const std::string &dest, const pd::List &list) { std::cout << "OF: list " << dest << ": "; // step through the list for(int i = 0; i < list.len(); ++i) { if(list.isFloat(i)) std::cout << list.getFloat(i) << " "; else if(list.isSymbol(i)) std::cout << list.getSymbol(i) << " "; } // you can also use the built in toString function or simply stream it out // std::cout << list.toString(); // std::cout << list; // print an OSC-style type string std::cout << list.types() << std::endl; } void ofApp::receiveMessage(const std::string &dest, const std::string &msg, const pd::List &list) { std::cout << "OF: message " << dest << ": " << msg << " " << list.toString() << list.types() << std::endl; } //-------------------------------------------------------------- void ofApp::receiveNoteOn(const int channel, const int pitch, const int velocity) { std::cout << "OF MIDI: note on: " << channel << " " << pitch << " " << velocity << std::endl; } void ofApp::receiveControlChange(const int channel, const int controller, const int value) { std::cout << "OF MIDI: control change: " << channel << " " << controller << " " << value << std::endl; } // note: pgm nums are 1-128 to match pd void ofApp::receiveProgramChange(const int channel, const int value) { std::cout << "OF MIDI: program change: " << channel << " " << value << std::endl; } void ofApp::receivePitchBend(const int channel, const int value) { std::cout << "OF MIDI: pitch bend: " << channel << " " << value << std::endl; } void ofApp::receiveAftertouch(const int channel, const int value) { std::cout << "OF MIDI: aftertouch: " << channel << " " << value << std::endl; } void ofApp::receivePolyAftertouch(const int channel, const int pitch, const int value) { std::cout << "OF MIDI: poly aftertouch: " << channel << " " << pitch << " " << value << std::endl; } // note: pd adds +2 to the port num, so sending to port 3 in pd to [midiout], // shows up at port 1 in ofxPd void ofApp::receiveMidiByte(const int port, const int byte) { std::cout << "OF MIDI: midi byte: " << port << " " << byte << std::endl; } //-------------------------------------------------------------- void ofApp::playTone(int pitch) { pd << StartMessage() << "pitch" << pitch << FinishList("tone") << Bang("tone"); } //-------------------------------------------------------------- // set the samplerate the Apple approved way since newer devices // like the iPhone 6S only allow certain sample rates, // the following code may not be needed once this functionality is // incorporated into the ofxiOSSoundStream // thanks to Seth aka cerupcat float ofApp::setAVSessionSampleRate(float preferredSampleRate) { NSError *audioSessionError = nil; AVAudioSession *session = [AVAudioSession sharedInstance]; // disable active [session setActive:NO error:&audioSessionError]; if (audioSessionError) { NSLog(@"Error %ld, %@", (long)audioSessionError.code, audioSessionError.localizedDescription); } // set category [session setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:(AVAudioSessionCategoryOptionAllowBluetooth | AVAudioSessionCategoryOptionMixWithOthers | AVAudioSessionCategoryOptionDefaultToSpeaker) error:&audioSessionError]; if(audioSessionError) { NSLog(@"Error %ld, %@", (long)audioSessionError.code, audioSessionError.localizedDescription); } // try to set the preferred sample rate [session setPreferredSampleRate:preferredSampleRate error:&audioSessionError]; if(audioSessionError) { NSLog(@"Error %ld, %@", (long)audioSessionError.code, audioSessionError.localizedDescription); } // *** Activate the audio session before asking for the "current" values *** [session setActive:YES error:&audioSessionError]; if (audioSessionError) { NSLog(@"Error %ld, %@", (long)audioSessionError.code, audioSessionError.localizedDescription); } ofLogNotice() << "AVSession samplerate: " << session.sampleRate << ", I/O buffer duration: " << session.IOBufferDuration; // our actual samplerate, might be different aka 48k on iPhone 6S return session.sampleRate; } ================================================ FILE: pdMultiExample/addons.make ================================================ ofxPd ================================================ FILE: pdMultiExample/bin/data/.gitkeep ================================================ ================================================ FILE: pdMultiExample/bin/data/test.pd ================================================ #N canvas 406 290 351 173 10; #X obj 39 35 loadbang; #X obj 143 114 dac~; #X obj 39 88 f 0; #X obj 70 88 + 1; #X obj 143 35 r \$0-frequency; #X obj 143 60 osc~; #X obj 205 114 print \$0-frequency; #X obj 205 76 loadbang; #X obj 143 88 *~ 0.5; #X obj 39 60 metro 1000; #X obj 39 114 print \$0-count; #X connect 0 0 9 0; #X connect 2 0 3 0; #X connect 2 0 10 0; #X connect 3 0 2 1; #X connect 4 0 5 0; #X connect 4 0 6 0; #X connect 5 0 8 0; #X connect 7 0 6 0; #X connect 8 0 1 0; #X connect 8 0 1 1; #X connect 9 0 2 0; ================================================ FILE: pdMultiExample/src/main.cpp ================================================ /* * Copyright (c) 2015 Dan Wilcox * * BSD Simplified License. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. * * See https://github.com/danomatika/ofxPd for documentation * */ #include "ofMain.h" #include "ofApp.h" //======================================================================== int main() { ofSetupOpenGL(200, 200, OF_WINDOW); ofRunApp(new ofApp()); } ================================================ FILE: pdMultiExample/src/ofApp.cpp ================================================ /* * Copyright (c) 2015 Dan Wilcox * * BSD Simplified License. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. * * See https://github.com/danomatika/ofxPd for documentation * */ #include "ofApp.h" //-------------------------------------------------------------- void ofApp::setup() { ofSetFrameRate(60); ofSetVerticalSync(true); //ofSetLogLevel("Pd", OF_LOG_VERBOSE); // see verbose info inside // the number of libpd ticks per buffer, // used to compute the audio buffer len: tpb * blocksize (always 64) #ifdef TARGET_LINUX_ARM // longer latency for Raspberry PI int ticksPerBuffer = 32; // 32 * 64 = buffer len of 2048 int numInputs = 0; // no built in mic #else int ticksPerBuffer = 8; // 8 * 64 = buffer len of 512 int numInputs = 1; #endif int numOutputs = 2; // allocate instance output buffers int outputBufferFrames = ticksPerBuffer * ofxPd::blockSize(); outputBuffer1.allocate(outputBufferFrames, numOutputs); outputBuffer2.allocate(outputBufferFrames, numOutputs); // setup OF sound stream ofSoundStreamSettings settings; settings.numInputChannels = numInputs; settings.numOutputChannels = numOutputs; settings.sampleRate = 44100; settings.bufferSize = ofxPd::blockSize() * ticksPerBuffer; settings.setInListener(this); settings.setOutListener(this); ofSoundStreamSetup(settings); // allocate pd instance handles ofLog() << ofxPd::numInstances() - 1 << " instance(s)"; // - main instance ofLog() << "instance 1: " << ofToHex(pd1.instancePtr()); ofLog() << "instance 2: " << ofToHex(pd2.instancePtr()); // setup Pd // // set 4th arg to true for queued message passing using an internal ringbuffer, // this is useful if you need to control where and when the message callbacks // happen (ie. within a GUI thread) // // note: you won't see any message prints until update() is called since // the queued messages are processed there, this is normal if(!pd1.init(numOutputs, numInputs, 44100, ticksPerBuffer, false)) { ofExit(1); } pd1.setReceiver(this); // audio processing on pd1.start(); // open patch pd1.openPatch("test.pd"); // start the osc~ via sending [; 1003-frequency 440( // 1003 refers to the first $0 which is used within the test patch for the // receiver name: [r $0-1003] pd1 << StartMessage() << 440.0f << FinishMessage("1003-frequency", "float"); // now do the same for instance 2 if(!pd2.init(numOutputs, numInputs, 44100, ticksPerBuffer, false)) { ofExit(1); } pd2.setReceiver(this); pd2.start(); pd2.openPatch("test.pd"); // start the osc~ via sending [; 1000-frequency 880( pd2 << StartMessage() << 880.0f << FinishMessage("1003-frequency", "float"); // check if we are really using multiple instances if(pd1.instancePtr() == pd2.instancePtr()) { ofLogError() << "Both instances are the same."; ofLogError() << "Is this example compiled with PDINSTANCE and PDTHREADS set?"; ofExit(); } } //-------------------------------------------------------------- void ofApp::update() { ofBackground(100, 100, 100); // since this is a test and we don't know if init() was called with // queued = true or not, we check it here if(pd1.isQueued()) { // process any received messages, if you're using the queue pd1.receiveMessages(); } if(pd2.isQueued()) { pd2.receiveMessages(); } } //-------------------------------------------------------------- void ofApp::draw() {} //-------------------------------------------------------------- void ofApp::exit() { // cleanup ofSoundStreamStop(); pd1.clear(); pd2.clear(); } //-------------------------------------------------------------- void ofApp::audioIn(ofSoundBuffer& buffer) { // process audio input for instance 1 pd1.audioIn(buffer); // process audio input for instance 2 pd2.audioIn(buffer); } //-------------------------------------------------------------- void ofApp::audioOut(ofSoundBuffer& buffer) { // process audio output for instance 1 pd1.audioOut(outputBuffer1); // process audio output for instance 2 pd2.audioOut(outputBuffer2); // mix the two instance output buffers together for(int i = 0; i < outputBuffer1.size() && i < buffer.size(); i++) { buffer[i] = (outputBuffer1[i] + outputBuffer2[i]) * 0.5f; // simple mix } } //-------------------------------------------------------------- void ofApp::print(const std::string &message) { ofLog() << message; } ================================================ FILE: pdMultiExample/src/ofApp.h ================================================ /* * Copyright (c) 2015 Dan Wilcox * * BSD Simplified License. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. * * See https://github.com/danomatika/ofxPd for documentation * */ #pragma once #include "ofMain.h" #include "ofxPd.h" // a namespace for the Pd types using namespace pd; // This example demonstrates how to use the libpd multiple instance support with // the ofxPd C++ wrapper. When compiled with -DPDINSTANCE and -DPDTHREADS set in // *both* CFLAGS and CXXFLAGS, each ofxPd instance is separate with it's own // internal state and message receivers. If the defines are not set, each ofxPd // instance refers to the same libpd main instance. // // This example is adapted from the libpd pdtest_multi C example which is // originally by Miller Puckette. // // *Do not* use this as your first approach to parallelize CPU hogging patches. // It's highly suggested that you attempt to streamline your patches first before // using this multiple instance support. // class ofApp : public ofBaseApp, public PdReceiver { public: // main void setup(); void update(); void draw(); void exit(); // audio callbacks void audioIn(ofSoundBuffer& buffer); void audioOut(ofSoundBuffer& buffer); // pd message receiver callbacks void print(const std::string &message); // pd instances ofxPd pd1, pd2; ofSoundBuffer outputBuffer1; //< interleaved audio output buffer for instance 1 ofSoundBuffer outputBuffer2; //< interleaved audio output buffer for instance 2 }; ================================================ FILE: pitchShifter/LICENSE.txt ================================================ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ================================================ FILE: pitchShifter/README.md ================================================ PitchShifter ============ ![image](https://raw.github.com/danomatika/ofxPd/master/examplePitchShifter/screenshot.png) Copyright (c) [Dan Wilcox](danomatika.com) 2011, 2013 GNU General Public License 3 For information on usage and redistribution, and for a DISCLAIMER OF ALL WARRANTIES, see the file, "LICENSE.txt," in this distribution. See https://github.com/ofxPd/examplePitchShifter for documentation Description ----------- PitchShifter is a simple live audio pitch shifter example application for desktop & iOS. Made for [Golan Levin](http://flong.com) & the [CMU Studio for Creative Inquiry](http://studiofrocreativeinquiry.com) It uses ofxPd & OpenFrameworks. [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. [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) [OpenFrameworks](http://www.openframeworks.cc/) is a cross platform open source toolkit for creative coding in C++ ================================================ FILE: pitchShifter/addons.make ================================================ ofxPd ================================================ FILE: pitchShifter/bin/data/.gitkeep ================================================ ================================================ FILE: pitchShifter/bin/data/pd/_main.pd ================================================ #N canvas 590 417 508 403 10; #X declare -path rc; #X obj 29 25 adc~ 1; #X obj 28 338 dac~ 1 2; #X obj 326 18 declare -path rc; #X obj 28 239 e_pshift~; #X obj 214 20 loadbang; #N canvas 666 63 404 247 scope~ 0; #X obj 26 28 inlet~ audio; #X obj 75 154 metro 100; #X msg 75 125 1; #X obj 56 59 clip~ -1 1; #X obj 75 95 loadbang; #N canvas 0 22 450 300 (subpatch) 0; #X array scope 512 float 2; #X coords 0 1 512 -1 200 140 1; #X restore 168 27 graph; #X obj 56 190 tabwrite~ scope; #X obj 26 222 outlet~; #X connect 0 0 3 0; #X connect 0 0 7 0; #X connect 1 0 6 0; #X connect 2 0 1 0; #X connect 3 0 6 0; #X connect 4 0 2 0; #X restore 28 268 pd scope~; #X obj 132 21 r TO_PD; #X obj 132 47 list trim; #X obj 132 86 route inGain outGain mix; #X obj 28 207 *~ 1; #X obj 132 120 clip 0 1; #X obj 179 150 clip 0 1; #X obj 28 303 *~ 1; #X obj 260 146 - 1; #X obj 260 173 abs; #X msg 233 226 wet \$1; #X msg 260 201 dry \$1; #X obj 233 119 t f f; #X msg 214 47 transpose 0 \, inGain 0.5 \, outGain 0.5 \, mix 1; #X obj 319 252 t a; #X text 179 301 Dan Wilcox 2012 GPL 3; #X text 178 317 danomatika.com | robotcowboy.com; #X connect 0 0 9 0; #X connect 3 0 5 0; #X connect 4 0 18 0; #X connect 5 0 12 0; #X connect 6 0 7 0; #X connect 7 0 8 0; #X connect 8 0 10 0; #X connect 8 1 11 0; #X connect 8 2 17 0; #X connect 8 3 19 0; #X connect 9 0 3 0; #X connect 10 0 9 1; #X connect 11 0 12 1; #X connect 12 0 1 1; #X connect 12 0 1 0; #X connect 13 0 14 0; #X connect 14 0 16 0; #X connect 15 0 19 0; #X connect 16 0 19 0; #X connect 17 0 15 0; #X connect 17 1 13 0; #X connect 18 0 8 0; #X connect 19 0 3 1; ================================================ FILE: pitchShifter/bin/data/pd/rc/e_pshift~-help.pd ================================================ #N canvas 1295 97 584 398 10; #X text 305 337 danomatika.com | robotcowboy.com; #X text 306 352 https://github.com/danomatika/rc-patches; #X text 306 322 2008 \, 2012 Dan Wilcox GPL v3; #X obj 44 364 dac~; #X obj 43 152 osc~ 440; #X obj 44 180 *~ 0.5; #X obj 95 293 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X obj 193 295 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X text 165 271 pitch shifted audio; #X text 62 271 normal audio; #X msg 260 81 transpose \$1; #X floatatom 260 52 5 0 0 0 - - -; #X msg 353 101 window \$1; #X floatatom 353 75 5 0 0 0 - - -; #X msg 422 135 delay \$1; #X floatatom 422 111 5 0 0 0 - - -; #X text 302 51 semitones; #X text 393 75 msec; #X text 461 113 msec; #X text 287 141 control messages; #X msg 203 111 onoff \$1; #X obj 203 84 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X text 39 65 Creation args: name; #X text 26 22 rc-pshift~: pitch shifter \, make your voice sound weird! ; #X msg 180 154 reset; #X msg 450 194 dry 0.5; #X msg 408 168 wet 0.34; #X text 501 173 wet/dry mix; #X text 135 192 audio in; #X obj 141 212 e_pshift~; #X obj 142 319 u_spigot~; #X obj 44 318 u_spigot~; #X connect 4 0 5 0; #X connect 5 0 29 0; #X connect 5 0 31 0; #X connect 6 0 31 1; #X connect 7 0 30 1; #X connect 10 0 29 1; #X connect 11 0 10 0; #X connect 12 0 29 1; #X connect 13 0 12 0; #X connect 14 0 29 1; #X connect 15 0 14 0; #X connect 20 0 29 1; #X connect 21 0 20 0; #X connect 24 0 29 1; #X connect 25 0 29 1; #X connect 26 0 29 1; #X connect 29 0 30 0; #X connect 30 0 3 1; #X connect 31 0 3 0; ================================================ FILE: pitchShifter/bin/data/pd/rc/e_pshift~.pd ================================================ #N canvas 590 926 392 188 10; #X obj 20 20 inlet~; #X obj 20 149 outlet~; #X obj 50 54 delwrite~ \$0-del 5000; #X text 122 105 from G09.pitchshift; #N canvas 387 113 432 456 guts 0; #X obj 83 274 *~; #X obj 206 211 line~; #X obj 206 185 pack 0 200; #X obj 20 84 exp; #X obj 20 175 /; #X obj 58 120 * 0.001; #X obj 315 281 line~; #X obj 315 256 pack 0 200; #X obj 84 320 +~; #X obj 20 326 cos~; #X obj 21 370 *~; #X obj 20 403 +~; #X obj 107 233 wrap~; #X obj 252 276 *~; #X obj 252 322 +~; #X obj 189 349 cos~; #X obj 189 376 *~; #X obj 58 143 t b f; #X obj 107 206 +~ 0.5; #X obj 20 274 -~ 0.5; #X obj 20 300 *~ 0.5; #X obj 189 275 -~ 0.5; #X obj 189 321 *~ 0.5; #X obj 20 112 - 1; #X obj 20 58 * 0.05776; #X obj 20 138 * -1; #X obj 20 206 phasor~; #X obj 315 232 max 1.5; #X text 381 197 (msec); #X obj 206 82 max 1; #X obj 252 351 vd~ \$0-del; #X obj 84 346 vd~ \$0-del; #X obj 20 31 r \$0-transpose; #X obj 206 52 r \$0-window; #X obj 315 198 r \$0-delay; #X text 112 31 halftones; #X obj 20 430 outlet~; #X text 280 52 (msec); #X obj 300 140 switch~; #X obj 300 106 r \$0-onoff; #X connect 0 0 8 0; #X connect 1 0 0 1; #X connect 1 0 13 1; #X connect 2 0 1 0; #X connect 3 0 23 0; #X connect 4 0 26 0; #X connect 5 0 17 0; #X connect 6 0 8 1; #X connect 6 0 14 1; #X connect 7 0 6 0; #X connect 8 0 31 0; #X connect 9 0 10 0; #X connect 10 0 11 0; #X connect 11 0 36 0; #X connect 12 0 13 0; #X connect 12 0 21 0; #X connect 13 0 14 0; #X connect 14 0 30 0; #X connect 15 0 16 0; #X connect 16 0 11 1; #X connect 17 0 4 0; #X connect 17 1 4 1; #X connect 18 0 12 0; #X connect 19 0 20 0; #X connect 20 0 9 0; #X connect 21 0 22 0; #X connect 22 0 15 0; #X connect 23 0 25 0; #X connect 24 0 3 0; #X connect 25 0 4 0; #X connect 26 0 0 0; #X connect 26 0 19 0; #X connect 26 0 18 0; #X connect 27 0 7 0; #X connect 29 0 5 0; #X connect 29 0 2 0; #X connect 30 0 16 1; #X connect 31 0 10 1; #X connect 32 0 24 0; #X connect 33 0 29 0; #X connect 34 0 27 0; #X connect 39 0 38 0; #X restore 53 88 pd guts; #N canvas 261 237 459 426 mix 0; #X obj 348 304 *~; #X obj 79 306 *~; #X obj 79 344 +~; #X obj 348 65 inlet~; #X obj 80 29 inlet~; #X obj 79 375 outlet~; #X obj 363 186 r \$0-wet; #X obj 94 173 r \$0-dry; #X obj 94 234 f; #X obj 171 31 r \$0-onoff; #X obj 94 261 spigot; #X msg 171 264 1; #X obj 363 266 spigot; #X obj 363 235 f; #X msg 228 262 0; #X obj 171 76 sel 0; #X msg 265 173 bang; #X obj 265 93 t b b; #X msg 292 118 1; #X obj 171 114 t b b; #X msg 198 143 0; #X text 404 67 wet; #X text 123 28 dry; #X obj 235 58 loadbang; #X text 167 358 turn mix on and off; #X text 169 375 turns dry to 1 when off; #X connect 0 0 2 1; #X connect 1 0 2 0; #X connect 2 0 5 0; #X connect 3 0 0 0; #X connect 4 0 1 0; #X connect 6 0 13 0; #X connect 7 0 8 0; #X connect 8 0 10 0; #X connect 9 0 15 0; #X connect 10 0 1 1; #X connect 11 0 1 1; #X connect 12 0 0 1; #X connect 13 0 12 0; #X connect 14 0 0 1; #X connect 15 0 19 0; #X connect 15 1 17 0; #X connect 16 0 13 0; #X connect 16 0 8 0; #X connect 17 0 16 0; #X connect 17 1 18 0; #X connect 18 0 12 1; #X connect 18 0 10 1; #X connect 19 0 14 0; #X connect 19 0 11 0; #X connect 19 1 20 0; #X connect 20 0 12 1; #X connect 20 0 10 1; #X connect 23 0 19 0; #X restore 20 118 pd mix; #N canvas 4 51 450 300 messages 0; #X obj 42 21 inlet; #X obj 42 256 s \$0-transpose; #X obj 82 232 s \$0-window; #X obj 123 205 s \$0-delay; #X obj 164 177 s \$0-onoff; #N canvas 0 22 597 369 defaults 0; #X obj 104 234 s \$0-transpose; #X obj 154 211 s \$0-window; #X obj 204 187 s \$0-delay; #X obj 254 162 s \$0-onoff; #X obj 154 25 loadbang; #X msg 154 87 100; #X msg 204 86 1.5; #X msg 104 83 0; #X obj 104 24 inlet; #X obj 351 123 s \$0-wet; #X obj 329 163 s \$0-dry; #X msg 351 88 1; #X msg 254 87 1; #X msg 293 85 0; #X connect 4 0 5 0; #X connect 4 0 6 0; #X connect 4 0 7 0; #X connect 4 0 12 0; #X connect 4 0 11 0; #X connect 4 0 13 0; #X connect 5 0 1 0; #X connect 6 0 2 0; #X connect 7 0 0 0; #X connect 8 0 7 0; #X connect 8 0 5 0; #X connect 8 0 6 0; #X connect 8 0 12 0; #X connect 8 0 11 0; #X connect 8 0 13 0; #X connect 11 0 9 0; #X connect 12 0 3 0; #X connect 13 0 10 0; #X restore 204 143 pd defaults; #X msg 204 108 bang; #X obj 42 55 route transpose window delay onoff reset wet dry; #X obj 245 112 s \$0-wet; #X obj 286 85 s \$0-dry; #X connect 0 0 7 0; #X connect 6 0 5 0; #X connect 7 0 1 0; #X connect 7 1 2 0; #X connect 7 2 3 0; #X connect 7 3 4 0; #X connect 7 4 6 0; #X connect 7 5 8 0; #X connect 7 6 9 0; #X restore 206 54 pd messages; #X obj 206 18 inlet; #X text 123 136 danomatika.com | robotcowboy.com; #X text 124 151 https://github.com/danomatika/rc-patches; #X text 123 121 2008 \, 2012 Dan Wilcox GPL v3; #X connect 0 0 5 0; #X connect 0 0 2 0; #X connect 4 0 5 1; #X connect 5 0 1 0; #X connect 7 0 6 0; ================================================ FILE: pitchShifter/bin/data/pd/rc/u_spigot~-help.pd ================================================ #N canvas 696 405 404 250 10; #X obj 112 114 tgl 15 0 empty empty empty 0 -6 0 10 -262144 -1 -1 0 1; #X obj 61 54 osc~ 440; #X obj 61 177 dac~; #X obj 61 88 *~ 0.1; #X text 139 112 signal through on/off; #X text 141 134 any nonzero float treated as 1; #X text 137 191 danomatika.com | robotcowboy.com; #X text 138 206 https://github.com/danomatika/rc-patches; #X text 138 176 2012 Dan Wilcox GPL v3; #X text 21 19 u_spigot~: a convenience patch to toggle a signal; #X text 170 45 includes 5 ms declick; #X obj 61 140 u_spigot~; #X connect 0 0 11 1; #X connect 1 0 3 0; #X connect 3 0 11 0; #X connect 11 0 2 0; #X connect 11 0 2 1; ================================================ FILE: pitchShifter/bin/data/pd/rc/u_spigot~.pd ================================================ #N canvas 0 22 489 282 10; #X text 214 224 danomatika.com | robotcowboy.com; #X text 215 239 https://github.com/danomatika/rc-patches; #X text 215 209 2012 Dan Wilcox GPL v3; #X obj 30 235 outlet~; #X obj 31 65 inlet~; #X obj 45 97 inlet; #X text 80 60 audio signal in; #X text 87 237 audio signal out; #X obj 45 120 sel 0; #X text 87 94 left inlet: onoff toggle \, any nonzero is treated as 1; #X obj 45 181 vline~; #X obj 30 208 *~; #X msg 45 143 0 5; #X msg 78 143 1 5; #X text 23 20 spigot with declick for signals; #X connect 4 0 11 0; #X connect 5 0 8 0; #X connect 8 0 12 0; #X connect 8 1 13 0; #X connect 10 0 11 1; #X connect 11 0 3 0; #X connect 12 0 10 0; #X connect 13 0 10 0; ================================================ FILE: pitchShifter/src/main.cpp ================================================ /* * Copyright (c) 2012 Dan Wilcox * for Golan Levin & the CMU Studio for Creative Inquiry * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * See https://github.com/danomatika/ofxPd/examplePitchShifter for documentation * */ #include "ofMain.h" #include "ofApp.h" //======================================================================== int main() { ofSetupOpenGL(1024, 768, OF_WINDOW); ofRunApp(new ofApp()); } ================================================ FILE: pitchShifter/src/ofApp.cpp ================================================ /* * Copyright (c) 2012 Dan Wilcox * for Golan Levin & the CMU Studio for Creative Inquiry * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * See https://github.com/danomatika/ofxPd/examplePitchShifter for documentation * */ #include "ofApp.h" //-------------------------------------------------------------- void ofApp::setup() { ofSetFrameRate(60); ofSetVerticalSync(true); // the number of libpd ticks per buffer, // used to compute the audio buffer len: tpb * blocksize (always 64) #ifdef TARGET_LINUX_ARM // longer latency for Raspberry PI int ticksPerBuffer = 32; // 32 * 64 = buffer len of 2048 // you'll need a USB mic on the Raspberry PI and may need to set the audio device, // otherwise this app won't really do anything without an incoming audio signal ... int numInputs = 0; // no built in mic, change this if you have a USB mic #else int ticksPerBuffer = 8; // 8 * 64 = buffer len of 512 int numInputs = 1; #endif // setup OF sound stream ofSoundStreamSettings settings; settings.numInputChannels = numInputs; settings.numOutputChannels = 2; settings.sampleRate = 44100; settings.bufferSize = ofxPd::blockSize() * ticksPerBuffer; settings.setInListener(this); settings.setOutListener(this); ofSoundStreamSetup(settings); // setup pd if(!pd.init(2, numInputs, 44100, ticksPerBuffer)) { OF_EXIT_APP(1); } pd.subscribe("mix"); pd.subscribe("transpose"); pd.subscribe("inputGain"); pd.subscribe("outputGain"); pd.addToSearchPath("pd"); pd.start(); // open patch Patch patch = pd.openPatch("pd/_main.pd"); std::cout << patch << std::endl; // setup GUI int x = -12, width = 100, step = 75; x += step; transposeSlider.setup(x, 34, width, 700, -12, 12, 0, true, true); x += width + step*2; mixSlider.setup(x, 34, width, 700, 0, 1, 1.0, true, true); x += width + step*2; inGainSlider.setup(x, 34, width, 700, 0, 1, 0.5, true, true); x += width + step*2; outGainSlider.setup(x, 34, 100, 700, 0, 1, 0.25, true, true); transposeSlider.setLabelString("Transpose"); mixSlider.setLabelString("Wet/Dry Mix"); inGainSlider.setLabelString("Input Gain"); outGainSlider.setLabelString("Output Gain"); } //-------------------------------------------------------------- void ofApp::update() { ofBackground(0, 0, 0); // update scope array from pd pd.readArray("scope", scopeArray); // udpate pd from gui pd << StartMessage() << "transpose" << transposeSlider.getValue() << FinishList("TO_PD"); pd << StartMessage() << "mix" << mixSlider.getValue() << FinishList("TO_PD"); pd << StartMessage() << "inGain" << inGainSlider.getValue() << FinishList("TO_PD"); pd << StartMessage() << "outGain" << outGainSlider.getValue() << FinishList("TO_PD"); } //-------------------------------------------------------------- void ofApp::draw() { // draw scope ofSetColor(0, 255, 0, 127); ofSetRectMode(OF_RECTMODE_CENTER); ofSetLineWidth(2.0); float x = 1, y = ofGetHeight()/2; float w = ofGetWidth() / (float) scopeArray.size(), h = ofGetHeight()/2; for(int i = 0; i < scopeArray.size()-1; ++i) { ofDrawLine(x, y+scopeArray[i]*h, x+w, y+scopeArray[i+1]*h); x += w; } ofSetLineWidth(1.0); } //-------------------------------------------------------------- void ofApp::exit() { // cleanup ofSoundStreamStop(); } //-------------------------------------------------------------- void ofApp::keyPressed(int key) {} //-------------------------------------------------------------- void ofApp::audioIn(ofSoundBuffer &buffer) { pd.audioIn(buffer); } //-------------------------------------------------------------- void ofApp::audioOut(ofSoundBuffer &buffer) { pd.audioOut(buffer); } //-------------------------------------------------------------- void ofApp::print(const std::string &message) { std::cout << message << std::endl; } ================================================ FILE: pitchShifter/src/ofApp.h ================================================ /* * Copyright (c) 2012 Dan Wilcox * for Golan Levin & the CMU Studio for Creative Inquiry * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * See https://github.com/danomatika/ofxPd/examplePitchShifter for documentation * */ #pragma once #include "ofMain.h" #include "ofxPd.h" #include "ofxSimpleSlider.h" // a namespace for the Pd types using namespace pd; class ofApp : public ofBaseApp, public PdReceiver { public: // main void setup(); void update(); void draw(); void exit(); // input callbacks void keyPressed(int key); // audio callbacks void audioIn(ofSoundBuffer &buffer); void audioOut(ofSoundBuffer &buffer); // pd message receiver callbacks void print(const std::string &message); ofxPd pd; //< pd instance std::vector scopeArray; // gui ofxSimpleSlider mixSlider, transposeSlider, inGainSlider, outGainSlider; }; ================================================ FILE: pitchShifter/src/ofxSimpleSlider.cpp ================================================ /* * ofxSimpleSlider.cpp * Created by Golan Levin on 2/24/12. * Updated by Dan Wilcox 2012. */ #include "ofxSimpleSlider.h" //---------------------------------------------------- ofxSimpleSlider::ofxSimpleSlider() { bWasSetup = false; } //---------------------------------------------------- ofxSimpleSlider::~ofxSimpleSlider() { clear(); } //---------------------------------------------------- void ofxSimpleSlider::setup(float inx, float iny, float inw, float inh, float loVal, float hiVal, float initialValue, bool bVert, bool bDrawNum) { x = inx; y = iny; width = inw; height = inh; box.set(x,y, width, height); numberDisplayPrecision = 2; bVertical = bVert; bDrawNumber = bDrawNum; bHasFocus = false; lowValue = loVal; highValue = hiVal; percent = ofMap(initialValue, lowValue, highValue, 0, 1); percent = ofClamp(percent, 0, 1); labelString = ""; if(!bWasSetup) { ofAddListener(ofEvents().draw, this, &ofxSimpleSlider::draw); ofAddListener(ofEvents().mouseMoved, this, &ofxSimpleSlider::mouseMoved); ofAddListener(ofEvents().mousePressed, this, &ofxSimpleSlider::mousePressed); ofAddListener(ofEvents().mouseReleased, this, &ofxSimpleSlider::mouseReleased); ofAddListener(ofEvents().mouseDragged, this, &ofxSimpleSlider::mouseDragged); bWasSetup = true; } } //---------------------------------------------------- void ofxSimpleSlider::clear() { if(bWasSetup) { ofRemoveListener(ofEvents().draw, this, &ofxSimpleSlider::draw); ofRemoveListener(ofEvents().mouseMoved, this, &ofxSimpleSlider::mouseMoved); ofRemoveListener(ofEvents().mousePressed, this, &ofxSimpleSlider::mousePressed); ofRemoveListener(ofEvents().mouseReleased, this, &ofxSimpleSlider::mouseReleased); ofRemoveListener(ofEvents().mouseDragged, this, &ofxSimpleSlider::mouseDragged); } bWasSetup = false; } //---------------------------------------------------- void ofxSimpleSlider::setLabelString(std::string str) { labelString = str; } //---------------------------------------------------- void ofxSimpleSlider::draw(ofEventArgs &event) { ofEnableAlphaBlending(); ofDisableSmoothing(); ofPushMatrix(); ofTranslate(x, y, 0); // Use different alphas if we're actively maniupulating me. float sliderAlpha = (bHasFocus ? 128 : 64); float spineAlpha = (bHasFocus ? 192 : 128); float thumbAlpha = (bHasFocus ? 255 : 160); // draw box outline ofNoFill(); ofSetLineWidth(1.0); ofSetColor(200, 200, 200, sliderAlpha); ofSetRectMode(OF_RECTMODE_CORNER); ofDrawRectangle(0, 0, width, height); // draw spine ofSetLineWidth(1.0); ofSetColor(255, 255, 255, spineAlpha); if(bVertical) { ofDrawLine(width/2, 0, width/2, height); } else { ofDrawLine(0, height/2, width, height/2); } // draw thumb ofSetLineWidth(5.0); ofSetColor(255, 255, 255, thumbAlpha); if(bVertical) { float thumbY = ofMap(percent, 0, 1, height, 0, true); ofDrawLine(0, thumbY, width, thumbY); } else { float thumbX = ofMap(percent, 0, 1, 0, width, true); ofDrawLine(thumbX, 0, thumbX, height); } // draw numeric value if(bHasFocus) { ofSetColor(255); } else { ofSetColor(200); } if(bVertical) { ofDrawBitmapString(ofToString(getValue(), numberDisplayPrecision), width+5, height); ofDrawBitmapString(labelString, width+5, height-14); } else { ofDrawBitmapString(ofToString(getValue(), numberDisplayPrecision), width+5, height/2 + 4); float labelStringWidth = labelString.size(); ofDrawBitmapString(labelString, 0-labelStringWidth*8-5, height/2 + 4); } ofPopMatrix(); ofSetLineWidth(1.0); ofDisableAlphaBlending(); } //---------------------------------------------------- float ofxSimpleSlider::getValue() { // THIS IS THE MAIN WAY YOU GET THE VALUE FROM THE SLIDER! float out = ofMap(percent, 0, 1, lowValue, highValue, true); return out; } //---------------------------------------------------- // Probably not used very much. float ofxSimpleSlider::getLowValue() { return lowValue; } float ofxSimpleSlider::getHighValue() { return highValue; } float ofxSimpleSlider::getPercent() { return percent; } //---------------------------------------------------- // Probably not used very much. void ofxSimpleSlider::setLowValue(float lv) { lowValue = lv; } void ofxSimpleSlider::setHighValue(float hv) { highValue = hv; } void ofxSimpleSlider::setPercent(float p) { // Set the slider's percentage from the outside. p = ofClamp(p, 0, 1); percent = p; } void ofxSimpleSlider::setNumberDisplayPrecision(int prec) { numberDisplayPrecision = prec; } //---------------------------------------------------- void ofxSimpleSlider::mouseMoved(ofMouseEventArgs &event) { bHasFocus = false; } void ofxSimpleSlider::mouseDragged(ofMouseEventArgs &event) { if(bHasFocus) { updatePercentFromMouse(event.x, event.y); } } void ofxSimpleSlider::mousePressed(ofMouseEventArgs &event) { bHasFocus = false; if(box.inside(event.x, event.y)) { bHasFocus = true; updatePercentFromMouse(event.x, event.y); } } void ofxSimpleSlider::mouseReleased(ofMouseEventArgs &event) { if(bHasFocus) { if(box.inside(event.x, event.y)) { updatePercentFromMouse(event.x, event.y); } } bHasFocus = false; } //---------------------------------------------------- void ofxSimpleSlider::updatePercentFromMouse(int mx, int my) { // Given the mouse value, compute the percentage. if(bVertical) { percent = ofMap(my, y, y+height, 1, 0, true); } else { percent = ofMap(mx, x, x+width, 0, 1, true); } } ================================================ FILE: pitchShifter/src/ofxSimpleSlider.h ================================================ /* * ofxSimpleSlider.h * Created by Golan Levin on 2/24/12. * Updated by Dan Wilcox 2012. */ #pragma once #include "ofMain.h" class ofxSimpleSlider { public: ofxSimpleSlider(); virtual ~ofxSimpleSlider(); void setup(float inx, float iny, float inw, float inh, float loVal, float hiVal, float initialPercent, bool bVert, bool bDrawNum); void clear(); void draw(ofEventArgs &event); void mouseMoved(ofMouseEventArgs &event); void mouseDragged(ofMouseEventArgs &event); void mousePressed(ofMouseEventArgs &event); void mouseReleased(ofMouseEventArgs &event); float getValue(); float getLowValue(); float getHighValue(); float getPercent(); void setLowValue(float lv); void setHighValue(float hv); void setPercent(float p); void setNumberDisplayPrecision(int prec); void setLabelString(std::string str); void updatePercentFromMouse(int mx, int my); protected: float x; float y; float width; float height; ofRectangle box; int numberDisplayPrecision; bool bVertical; bool bDrawNumber; bool bHasFocus; float lowValue; float highValue; float percent; std::string labelString; private: bool bWasSetup; }; ================================================ FILE: scripts/update_libpd.sh ================================================ #! /bin/sh # exit on error set -e VER=master SRC=libpd DEST=../libs/libpd ### cd "$(dirname $0)" # get source git clone --depth 1 https://github.com/libpd/libpd.git cd $SRC git checkout $VER git submodule init git submodule update cd - # remove uneeded makefiles, etc in src find $SRC/pure-data -name "makefile*" -delete find $SRC/pure-data -name "Makefile.am" -delete find $SRC/pure-data -name "GNUmakefile.am" -delete find $SRC/pure-data -name "*.pd" -delete rm -f $SRC/pure-data/src/CHANGELOG.txt rm -f $SRC/pure-data/src/notes.txt rm -f $SRC/pure-data/src/pd.ico rm $SRC/pure-data/src/pd.rc # remove unneeded audio apis rm $SRC/pure-data/src/s_audio_alsa* rm $SRC/pure-data/src/s_audio_audiounit.c rm $SRC/pure-data/src/s_audio_esd.c rm $SRC/pure-data/src/s_audio_jack.c rm $SRC/pure-data/src/s_audio_mmio.c rm $SRC/pure-data/src/s_audio_oss.c rm $SRC/pure-data/src/s_audio_pa.c rm $SRC/pure-data/src/s_audio_paring.* # remove unneeded midi apis rm $SRC/pure-data/src/s_midi_alsa.c rm $SRC/pure-data/src/s_midi_dummy.c rm -f $SRC/pure-data/src/s_midi_mmio.c rm $SRC/pure-data/src/s_midi_oss.c rm $SRC/pure-data/src/s_midi_pm.c rm $SRC/pure-data/src/s_midi.c # remove uneeded fft library interfaces rm $SRC/pure-data/src/d_fft_fftw.c # remove libpd stuff included with pure-data rm $SRC/pure-data/src/s_libpdmidi.c rm $SRC/pure-data/src/x_libpdreceive.* rm $SRC/pure-data/src/z_*.* # remove some other stuff we don't need ... rm $SRC/pure-data/src/s_entry.c rm $SRC/pure-data/src/s_file.c rm $SRC/pure-data/src/s_watchdog.c rm $SRC/pure-data/src/u_pdreceive.c rm $SRC/pure-data/src/u_pdsend.c rm -f $SRC/pure-data/.dir-locals.el rm -f $SRC/pure-data/src/.dir-locals.el # copy sources mkdir -p $DEST/pure-data cp -Rv $SRC/cpp $DEST/ cp -Rv $SRC/pure-data/src $DEST/pure-data cp -Rv $SRC/pure-data/extra $DEST/pure-data cp -Rv $SRC/libpd_wrapper $DEST/ # copy libs mkdir -p $DEST/libs cp -Rv $SRC/libs/mingw64 $DEST/libs/ # cleanup rm -rf $SRC ================================================ FILE: src/ofxPd.cpp ================================================ /* * Copyright (c) 2011-2022 Dan Wilcox * * BSD Simplified License. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. * * See https://github.com/danomatika/ofxPd for documentation * * This project uses libpd, copyrighted by Miller Puckette and others using the * "Standard Improved BSD License". See the file "LICENSE.txt" in src/pd. * * See http://gitorious.org/pdlib/pages/Libpd for documentation * */ // include before PdBase.hpp to fix conflict between boost & libpd's s_ define #include "ofFileUtils.h" #include "ofxPd.h" #include #include "ofUtils.h" #include "ofLog.h" // needed for libpd audio passing #ifndef USEAPI_DUMMY #define USEAPI_DUMMY #endif using namespace std; using namespace pd; //------------------------------------------------------------------------------ ofxPd::ofxPd() : PdBase() { inBuffer = NULL; computing = false; clear(); } //------------------------------------------------------------------------------ ofxPd::~ofxPd() { clear(); } //------------------------------------------------------------------------------ bool ofxPd::init(const int numOutChannels, const int numInChannels, const int sampleRate, const int ticksPerBuffer, bool queued) { // init pd if(!PdBase::init(numInChannels, numOutChannels, sampleRate, queued)) { ofLogError("Pd") << "could not init"; clear(); return false; } ticks = ticksPerBuffer; bsize = ticksPerBuffer*blockSize(); srate = sampleRate; inChannels = numInChannels; outChannels = numOutChannels; // allocate buffers inBuffer = new float[numInChannels * bsize]; ofLogVerbose("Pd") <<"inited"; ofLogVerbose("Pd") <<" samplerate: " << sampleRate; ofLogVerbose("Pd") <<" channels in: " << numInChannels; ofLogVerbose("Pd") <<" channels out: " << numOutChannels; ofLogVerbose("Pd") <<" ticks: " << ticksPerBuffer; ofLogVerbose("Pd") <<" block size: " << blockSize(); ofLogVerbose("Pd") <<" calc buffer size: " << bsize; ofLogVerbose("Pd") <<" queued: " << (queued ? "yes" : "no"); return true; } void ofxPd::clear() { if(inBuffer != NULL) { delete[] inBuffer; inBuffer = NULL; } unsubscribeAll(); channels.clear(); // add default global channel Channel c; channels.insert(make_pair(-1, c)); ticks = 0; bsize = 0; srate = 0; inChannels = 0; outChannels = 0; } //------------------------------------------------------------------------------ void ofxPd::addToSearchPath(const std::string &path) { string fullpath = ofFilePath::getAbsolutePath(ofToDataPath(path)); ofLogVerbose("Pd") << "adding search path: " + fullpath; PdBase::addToSearchPath(fullpath.c_str()); } void ofxPd::clearSearchPath() { ofLogVerbose("Pd") << "clearing search paths"; PdBase::clearSearchPath(); } //------------------------------------------------------------------------------ Patch ofxPd::openPatch(const std::string &patch) { string fullpath = ofFilePath::getAbsolutePath(ofToDataPath(patch)); string file = ofFilePath::getFileName(fullpath); string folder = ofFilePath::getEnclosingDirectory(fullpath); // trim the trailing slash Poco::Path always adds ... blarg if(folder.size() > 0 && folder.at(folder.size()-1) == '/') { folder.erase(folder.end()-1); } ofLogVerbose("Pd") << "opening patch: "+file+" path: "+folder; // [; pd open file folder( Patch p = PdBase::openPatch(file.c_str(), folder.c_str()); if(!p.isValid()) { ofLogError("Pd") << "opening patch \""+file+"\" failed"; } return p; } pd::Patch ofxPd::openPatch(pd::Patch &patch) { ofLogVerbose("Pd") << "opening patch: "+patch.filename()+" path: "+patch.path(); Patch p = PdBase::openPatch(patch); if(!p.isValid()) { ofLogError("Pd") << "opening patch \""+patch.filename()+"\" failed"; } return p; } void ofxPd::closePatch(const std::string &patch) { ofLogVerbose("Pd") << "closing path: "+patch; PdBase::closePatch(patch); } void ofxPd::closePatch(Patch &patch) { ofLogVerbose("Pd") << "closing patch: "+patch.filename(); PdBase::closePatch(patch); } //------------------------------------------------------------------------------ void ofxPd::computeAudio(bool state) { if(state) { ofLogVerbose("Pd") << "audio processing on"; } else { ofLogVerbose("Pd") << "audio processing off"; } computing = state; // [; pd dsp $1( PdBase::computeAudio(state); } void ofxPd::start() { // [; pd dsp 1( computeAudio(true); } void ofxPd::stop() { // [; pd dsp 0( computeAudio(false); } //------------------------------------------------------------------------------ void ofxPd::subscribe(const std::string &source) { if(exists(source)) { ofLogWarning("Pd") << "subscribe: ignoring duplicate source"; return; } PdBase::subscribe(source); Source s; sources.insert(pair(source, s)); } void ofxPd::unsubscribe(const std::string &source) { map::iterator iter; iter = sources.find(source); if(iter == sources.end()) { ofLogWarning("Pd") << "unsubscribe: ignoring unknown source"; return; } PdBase::unsubscribe(source); sources.erase(iter); } bool ofxPd::exists(const std::string &source) { if(sources.find(source) != sources.end()) { return true; } return false; } void ofxPd::unsubscribeAll() { PdBase::unsubscribeAll(); sources.clear(); // add default global source Source s; sources.insert(make_pair("", s)); } //------------------------------------------------------------------------------ void ofxPd::addReceiver(PdReceiver &receiver) { pair::iterator, bool> ret; ret = receivers.insert(&receiver); if(!ret.second) { ofLogWarning("Pd") << "addReceiver: ignoring duplicate receiver"; return; } // set PdBase receiver on adding first reciever if(receivers.size() == 1) { PdBase::setReceiver(this); } // receive from all sources by default receiveSource(receiver); } void ofxPd::removeReceiver(PdReceiver &receiver) { // exists? set::iterator r_iter; r_iter = receivers.find(&receiver); if(r_iter == receivers.end()) { ofLogWarning("Pd") << "removeReceiver: ignoring unknown receiver"; return; } receivers.erase(r_iter); // clear PdBase receiver on removing last reciever if(receivers.size() == 0) { PdBase::setReceiver(NULL); } // remove from all sources ignoreSource(receiver); } bool ofxPd::receiverExists(PdReceiver &receiver) { if(receivers.find(&receiver) != receivers.end()) return true; return false; } void ofxPd::clearReceivers() { receivers.clear(); map::iterator iter; for(iter = sources.begin(); iter != sources.end(); ++iter) { iter->second.receivers.clear(); } PdBase::setReceiver(NULL); } //------------------------------------------------------------------------------ int ofxPd::ticksPerBuffer() { return ticks; } int ofxPd::bufferSize() { return bsize; } int ofxPd::sampleRate() { return srate; } int ofxPd::numInChannels() { return inChannels; } int ofxPd::numOutChannels() { return outChannels; } bool ofxPd::isComputingAudio() { return computing; } //------------------------------------------------------------------------------ void ofxPd::receiveSource(PdReceiver &receiver, const std::string &source) { if(!receiverExists(receiver)) { ofLogWarning("Pd") << "receive: unknown receiver, call addReceiver first"; return; } if(!exists(source)) { ofLogWarning("Pd") << "receive: unknown source, call subscribe first"; return; } // global source (all sources) map::iterator g_iter; g_iter = sources.find(""); // subscribe to specific source if(source != "") { // make sure global source is ignored if(g_iter->second.receiverExists(&receiver)) { g_iter->second.removeReceiver(&receiver); } // receive from specific source map::iterator s_iter; s_iter = sources.find(source); s_iter->second.addReceiver(&receiver); } else { // make sure all sources are ignored ignoreSource(receiver); // receive from the global source g_iter->second.addReceiver(&receiver); } } void ofxPd::ignoreSource(PdReceiver &receiver, const std::string &source) { if(!receiverExists(receiver)) { ofLogWarning("Pd") << "ignore: ignoring unknown receiver"; return; } if(!exists(source)) { ofLogWarning("Pd") << "ignore: ignoring unknown source"; return; } map::iterator s_iter; // unsubscribe from specific source if(source != "") { // global source (all sources) map::iterator g_iter; g_iter = sources.find(""); // negation from global if(g_iter->second.receiverExists(&receiver)) { // remove from global g_iter->second.removeReceiver(&receiver); // add to *all* other sources for(s_iter = sources.begin(); s_iter != sources.end(); ++s_iter) { if(s_iter != g_iter) { s_iter->second.addReceiver(&receiver); } } } // remove from source s_iter = sources.find(source); s_iter->second.removeReceiver(&receiver); } else { // ignore all sources for(s_iter = sources.begin(); s_iter != sources.end(); ++s_iter) { s_iter->second.removeReceiver(&receiver); } } } bool ofxPd::isReceivingSource(PdReceiver &receiver, const std::string &source) { map::iterator s_iter; s_iter = sources.find(source); if(s_iter != sources.end() && s_iter->second.receiverExists(&receiver)) { return true; } return false; } //------------------------------------------------------------------------------ void ofxPd::addMidiReceiver(PdMidiReceiver &receiver) { pair::iterator, bool> ret; ret = midiReceivers.insert(&receiver); if(!ret.second) { ofLogWarning("Pd") << "addMidiReceiver: ignoring duplicate receiver"; return; } // set PdBase receiver on adding first reciever if(midiReceivers.size() == 1) { PdBase::setMidiReceiver(this); } // receive from all channels by default receiveMidiChannel(receiver); } void ofxPd::removeMidiReceiver(PdMidiReceiver &receiver) { // exists? set::iterator r_iter; r_iter = midiReceivers.find(&receiver); if(r_iter == midiReceivers.end()) { ofLogWarning("Pd") << "removeMidiReceiver: ignoring unknown receiver"; return; } midiReceivers.erase(r_iter); // clear PdBase receiver on removing last reciever if(receivers.size() == 0) { PdBase::setMidiReceiver(NULL); } // remove from all sources ignoreMidiChannel(receiver); } bool ofxPd::midiReceiverExists(PdMidiReceiver &receiver) { if(midiReceivers.find(&receiver) != midiReceivers.end()) { return true; } return false; } void ofxPd::clearMidiReceivers() { midiReceivers.clear(); map::iterator iter; for(iter = channels.begin(); iter != channels.end(); ++iter) { iter->second.receivers.clear(); } PdBase::setMidiReceiver(NULL); } //------------------------------------------------------------------------------ void ofxPd::receiveMidiChannel(PdMidiReceiver &receiver, int channel) { if(!midiReceiverExists(receiver)) { ofLogWarning("Pd") << "receiveMidi: unknown receiver, call addMidiReceiver first"; return; } // handle bad channel numbers if(channel < 0) { channel = 0; } // insert channel if it dosen't exist yet if(channels.find(channel) == channels.end()) { Channel c; channels.insert(pair(channel, c)); } // global channel (all channels) map::iterator g_iter; g_iter = channels.find(0); // subscribe to specific channel if(channel != 0) { // make sure global channel is ignored if(g_iter->second.midiReceiverExists(&receiver)) { g_iter->second.removeMidiReceiver(&receiver); } // receive from specific channel map::iterator c_iter; c_iter = channels.find(channel); c_iter->second.addMidiReceiver(&receiver); } else { // make sure all channels are ignored ignoreMidiChannel(receiver); // receive from the global channel g_iter->second.addMidiReceiver(&receiver); } } void ofxPd::ignoreMidiChannel(PdMidiReceiver &receiver, int channel) { if(!midiReceiverExists(receiver)) { ofLogWarning("Pd") << "ignoreMidi: ignoring unknown receiver"; return; } // handle bad channel numbers if(channel < 0) { channel = 0; } // insert channel if it dosen't exist yet if(channels.find(channel) == channels.end()) { Channel c; channels.insert(pair(channel, c)); } map::iterator c_iter; // unsubscribe from specific channel if(channel != 0) { // global channel (all channels) map::iterator g_iter; g_iter = channels.find(0); // negation from global if(g_iter->second.midiReceiverExists(&receiver)) { // remove from global g_iter->second.removeMidiReceiver(&receiver); // add to *all* other channels for(c_iter = channels.begin(); c_iter != channels.end(); ++c_iter) { if(c_iter != g_iter) { c_iter->second.addMidiReceiver(&receiver); } } } // remove from channel c_iter = channels.find(channel); c_iter->second.removeMidiReceiver(&receiver); } else { // ignore all sources for(c_iter = channels.begin(); c_iter != channels.end(); ++c_iter) { c_iter->second.removeMidiReceiver(&receiver); } } } bool ofxPd::isReceivingMidiChannel(PdMidiReceiver &receiver, int channel) { // handle bad channel numbers if(channel < 0) { channel = 0; } map::iterator c_iter; c_iter = channels.find(channel); if(c_iter != channels.end() && c_iter->second.midiReceiverExists(&receiver)) return true; return false; } //------------------------------------------------------------------------------ void ofxPd::sendNoteOn(const int channel, const int pitch, const int velocity) { PdBase::sendNoteOn(channel-1, pitch, velocity); } void ofxPd::sendControlChange(const int channel, const int control, const int value) { PdBase::sendControlChange(channel-1, control, value); } void ofxPd::sendProgramChange(const int channel, int program) { PdBase::sendProgramChange(channel-1, program-1); } void ofxPd::sendPitchBend(const int channel, const int value) { PdBase::sendPitchBend(channel-1, value); } void ofxPd::sendAftertouch(const int channel, const int value) { PdBase::sendAftertouch(channel-1, value); } void ofxPd::sendPolyAftertouch(const int channel, int pitch, int value) { PdBase::sendPolyAftertouch(channel-1, pitch, value); } //------------------------------------------------------------------------------ void ofxPd::audioIn(float *input, int bufferSize, int nChannels) { try { if(inBuffer != NULL) { if(bufferSize != bsize || nChannels != inChannels) { ticks = bufferSize/blockSize(); bsize = bufferSize; inChannels = nChannels; ofLogVerbose("Pd") << "buffer size or num input channels updated"; init(outChannels, inChannels, srate, ticks, isQueued()); PdBase::computeAudio(computing); } memcpy(inBuffer, input, bufferSize*nChannels*sizeof(float)); } } catch (...) { ofLogError("Pd") << "could not copy input buffer"; } } void ofxPd::audioOut(float *output, int bufferSize, int nChannels) { if(inBuffer != NULL) { if(bufferSize != bsize || nChannels != outChannels) { ticks = bufferSize/blockSize(); bsize = bufferSize; outChannels = nChannels; ofLogVerbose("Pd") << "buffer size or num output channels updated"; init(outChannels, inChannels, srate, ticks, isQueued()); PdBase::computeAudio(computing); } if(!PdBase::processFloat(ticks, inBuffer, output)) { ofLogError("Pd") << "could not process output buffer"; } } } void ofxPd::audioIn(ofSoundBuffer &buffer) { audioIn(buffer.getBuffer().data(), buffer.getNumFrames(), buffer.getNumChannels()); } void ofxPd::audioOut(ofSoundBuffer &buffer) { audioOut(buffer.getBuffer().data(), buffer.getNumFrames(), buffer.getNumChannels()); } /* ***** PROTECTED ***** */ //------------------------------------------------------------------------------ void ofxPd::print(const std::string &message) { ofLogVerbose("Pd") << "print: " << message; // broadcast set::iterator iter; for(iter = receivers.begin(); iter != receivers.end(); ++iter) { (*iter)->print(message); } } void ofxPd::receiveBang(const std::string &dest) { ofLogVerbose("Pd") << "bang: " << dest; set::iterator r_iter; set *r_set; // send to global receivers map::iterator g_iter; g_iter = sources.find(""); r_set = &g_iter->second.receivers; for(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) { (*r_iter)->receiveBang(dest); } // send to subscribed receivers map::iterator s_iter; s_iter = sources.find(dest); r_set = &s_iter->second.receivers; for(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) { (*r_iter)->receiveBang(dest); } } void ofxPd::receiveFloat(const std::string &dest, float value) { ofLogVerbose("Pd") << "float: " << dest << " " << value; set::iterator r_iter; set *r_set; // send to global receivers map::iterator g_iter; g_iter = sources.find(""); r_set = &g_iter->second.receivers; for(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) { (*r_iter)->receiveFloat(dest, value); } // send to subscribed receivers map::iterator s_iter; s_iter = sources.find(dest); r_set = &s_iter->second.receivers; for(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) { (*r_iter)->receiveFloat(dest, value); } } void ofxPd::receiveSymbol(const std::string &dest, const std::string &symbol) { ofLogVerbose("Pd") << "symbol: " << dest << " " << symbol; set::iterator r_iter; set *r_set; // send to global receivers map::iterator g_iter; g_iter = sources.find(""); r_set = &g_iter->second.receivers; for(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) { (*r_iter)->receiveSymbol(dest, (string) symbol); } // send to subscribed receivers map::iterator s_iter; s_iter = sources.find(dest); r_set = &s_iter->second.receivers; for(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) { (*r_iter)->receiveSymbol(dest, (string) symbol); } } void ofxPd::receiveList(const std::string &dest, const List &list) { ofLogVerbose("Pd") << "list: " << dest << " " << list.toString(); set::iterator r_iter; set *r_set; // send to global receivers map::iterator g_iter; g_iter = sources.find(""); r_set = &g_iter->second.receivers; for(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) { (*r_iter)->receiveList(dest, list); } // send to subscribed receivers map::iterator s_iter; s_iter = sources.find(dest); r_set = &s_iter->second.receivers; for(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) { (*r_iter)->receiveList(dest, list); } } void ofxPd::receiveMessage(const std::string &dest, const std::string &msg, const List &list) { ofLogVerbose("Pd") << "message: " << dest << " " << msg << " " << list.toString(); set::iterator r_iter; set *r_set; // send to global receivers map::iterator g_iter; g_iter = sources.find(""); r_set = &g_iter->second.receivers; for(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) { (*r_iter)->receiveMessage(dest, msg, list); } // send to subscribed receivers map::iterator s_iter; s_iter = sources.find(dest); r_set = &s_iter->second.receivers; for(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) { (*r_iter)->receiveMessage(dest, msg, list); } } //---------------------------------------------------------- void ofxPd::receiveNoteOn(const int channel, const int pitch, const int velocity) { ofLogVerbose("Pd") << "note on: " << channel+1 << " " << pitch << " " << velocity; set::iterator r_iter; set *r_set; // send to global receivers map::iterator g_iter; g_iter = channels.find(0); r_set = &g_iter->second.receivers; for(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) { (*r_iter)->receiveNoteOn(channel+1, pitch, velocity); } // send to subscribed receivers map::iterator c_iter; c_iter = channels.find(channel); if(c_iter != channels.end()) { r_set = &c_iter->second.receivers; for(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) { (*r_iter)->receiveNoteOn(channel+1, pitch, velocity); } } } void ofxPd::receiveControlChange(const int channel, const int controller, const int value) { ofLogVerbose("Pd") << "control change: " << channel+1 << " " << controller << " " << value; set::iterator r_iter; set *r_set; // send to global receivers map::iterator g_iter; g_iter = channels.find(0); r_set = &g_iter->second.receivers; for(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) { (*r_iter)->receiveControlChange(channel+1, controller, value); } // send to subscribed receivers map::iterator c_iter; c_iter = channels.find(channel); if(c_iter != channels.end()) { r_set = &c_iter->second.receivers; for(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) { (*r_iter)->receiveControlChange(channel+1, controller, value); } } } void ofxPd::receiveProgramChange(const int channel, const int value) { ofLogVerbose("Pd") << "program change: " << channel+1 << " " << value+1; set::iterator r_iter; set *r_set; // send to global receivers map::iterator g_iter; g_iter = channels.find(0); r_set = &g_iter->second.receivers; for(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) { (*r_iter)->receiveProgramChange(channel+1, value+1); } // send to subscribed receivers map::iterator c_iter; c_iter = channels.find(channel); if(c_iter != channels.end()) { r_set = &c_iter->second.receivers; for(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) { (*r_iter)->receiveProgramChange(channel+1, value+1); } } } void ofxPd::receivePitchBend(const int channel, const int value) { ofLogVerbose("Pd") << "pitch bend: " << channel+1 << value; set::iterator r_iter; set *r_set; // send to global receivers map::iterator g_iter; g_iter = channels.find(0); r_set = &g_iter->second.receivers; for(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) { (*r_iter)->receivePitchBend(channel+1, value); } // send to subscribed receivers map::iterator c_iter; c_iter = channels.find(channel); if(c_iter != channels.end()) { r_set = &c_iter->second.receivers; for(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) { (*r_iter)->receivePitchBend(channel+1, value); } } } void ofxPd::receiveAftertouch(const int channel, const int value) { ofLogVerbose("Pd") << "aftertouch: " << channel+1 << value; set::iterator r_iter; set *r_set; // send to global receivers map::iterator g_iter; g_iter = channels.find(0); r_set = &g_iter->second.receivers; for(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) { (*r_iter)->receiveAftertouch(channel+1, value); } // send to subscribed receivers map::iterator c_iter; c_iter = channels.find(channel); if(c_iter != channels.end()) { r_set = &c_iter->second.receivers; for(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) { (*r_iter)->receiveAftertouch(channel+1, value); } } } void ofxPd::receivePolyAftertouch(const int channel, const int pitch, const int value) { ofLogVerbose("Pd") << "poly aftertouch: " << channel+1 << " " << pitch << " " << value; set::iterator r_iter; set *r_set; // send to global receivers map::iterator g_iter; g_iter = channels.find(0); r_set = &g_iter->second.receivers; for(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) { (*r_iter)->receivePolyAftertouch(channel+1, pitch, value); } // send to subscribed receivers map::iterator c_iter; c_iter = channels.find(channel); if(c_iter != channels.end()) { r_set = &c_iter->second.receivers; for(r_iter = r_set->begin(); r_iter != r_set->end(); ++r_iter) { (*r_iter)->receivePolyAftertouch(channel+1, pitch, value); } } } void ofxPd::receiveMidiByte(const int port, const int byte) { ofLogVerbose("Pd") << "midi byte: " << port << " " << byte; set &r_set = midiReceivers; set::iterator iter; for(iter = r_set.begin(); iter != r_set.end(); ++iter) { (*iter)->receiveMidiByte(port, byte); } } ================================================ FILE: src/ofxPd.h ================================================ /* * Copyright (c) 2011-2022 Dan Wilcox * * BSD Simplified License. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. * * See https://github.com/danomatika/ofxPd for documentation * * This project uses libpd, copyrighted by Miller Puckette and others using the * "Standard Improved BSD License". See the file "LICENSE.txt" in src/pd. * * See http://gitorious.org/pdlib/pages/Libpd for documentation * */ #pragma once #include #include #include "PdBase.hpp" #include "ofSoundBuffer.h" /// /// a Pure Data instance /// /// references: https://github.com/libpd/libpd/wiki /// /// also: see PdBase.h in src/pd/cpp for some functions which are not wrapped by /// ofxPd and PdTypes.h for small Pd C++ Objects /// /// differences from libpd C api and/or C++ wrapper: /// - midi channels are 1-16 to match pd ranges /// - pgm values are 1-128 to match [pgmin]/[pgmout] in pd /// - init() takes numOutChannels first to match ofSoundStream /// /// note: as of ofxPd 1.9.0 & libpd 0.13, ofxPd supports multiple instances if /// compiled with PDINSTANCE defined, in which case each ofxPd instance can /// act separately with it's own PdReceiver and PdMidiReceiver /// class ofxPd : public pd::PdBase, protected pd::PdReceiver, protected pd::PdMidiReceiver { public : ofxPd(); virtual ~ofxPd(); /// \section Initializing Pd /// initialize audio resources /// /// set the audio latency by setting the libpd ticks per buffer: /// ticks per buffer * lib pd block size (always 64) /// /// ie 4 ticks per buffer * 64 = buffer len of 512 /// /// you can call this again after loading patches & setting receivers /// in order to update the audio settings /// /// the lower the number of ticks, the faster the audio processing /// if you experience audio dropouts (audible clicks), increase the /// ticks per buffer /// /// set queued = true to use the built in ringbuffers for message and /// midi event passing, you will then need to call receiveMessages() and /// receiveMidi() in order to pass messages from the ringbuffers to your /// PdReceiver and PdMidiReceiver implementations /// /// the queued ringbuffers are useful when you need to receive events /// on a gui thread and don't want to use locking (aka a mutex) /// bool init(const int numOutChannels, const int numInChannels, const int sampleRate, const int ticksPerBuffer=32, bool queued=false); /// clear resources, here for future proofing, currently does nothing void clear(); /// \section Adding Search Paths /// add to the pd search path /// takes an absolute or relative path (in data folder) /// /// note: fails silently if path not found /// void addToSearchPath(const std::string &path); /// clear the current pd search path void clearSearchPath(); /// \section Opening Patches /// the pd::Patch class is a wrapper around a unique pointer to an open /// instance, giving you access to the $0 value for that instance /// /// if you are going to use multiple instances of a patch, you will need /// need to keep a Patch object in order to differentiate between them /// /// see src/pd/cpp/PdTypes.hpp for more info /// open a patch file, takes an absolute or relative path (in data folder) /// returns a Patch object pd::Patch openPatch(const std::string &patch); /// open a patch file using the filename and path of an existing patch /// /// // open a patch, don't need pd::Patch instance object /// pd.openPatch("apatch.pd", "/some/path"); /// /// set the filename within the patch object or use a previously opened /// object and create a new instance /// /// // open an instance of "somefile.pd", save the instance in a pd::Patch /// Patch p2("somefile.pd", "/some/path"); // set file and path /// pd.openPatch(p2); /// /// // open a new instance of "somefile.pd" /// Patch p3 = pd.openPatch(p2); /// /// p2 and p3 now refer to 2 different instances of "somefile.pd" and /// p2.dollarZero() & p3.dollarZero() should now return different ids /// pd::Patch openPatch(pd::Patch &patch); /// close a patch file, takes the patch's basename (filename without extension), /// use this function if you've only opened 1 instance of the given patch void closePatch(const std::string &patch); /// close a patch file using a Patch object, clears the given Patch object /// does not affect other open instances of the same patch void closePatch(pd::Patch &patch); /// \section Audio Processing Control /// start/stop audio processing /// /// note: in general, once started, you won't need to turn off audio processing /// /// shortcuts for [; pd dsp 1( & [; pd dsp 0( /// void computeAudio(bool state); void start(); void stop(); //// \section Message Receiving /// subscribe/unsubscribe to source names from libpd /// /// aka the pd receive name /// /// [r source] /// | /// /// note: the global source (aka "") exists by default /// void subscribe(const std::string &source); void unsubscribe(const std::string &source); bool exists(const std::string &source); void unsubscribeAll(); ///< receivers will be unsubscribed from *all* sources /// process the internal message queue if using the ringbuffer: /// /// internally, libpd will use a ringbuffer to pass messages & midi without /// needing to require locking (mutexes) if you call init() with queued = true /// /// call these in your update() loop in order to receive waiting messages /// or midi data which are then sent to your PdReceiver & PdMidiReceiver /// /// warning: if you call init() with queued = true and *do not* call these /// functions, you will *not* receive any messages! /// /// void receiveMessages(); -> calls PdReceiver /// void receiveMidi(); -> calls PdMidiReceiver /// add/remove incoming event receiver /// /// receivers automatically receive from *all* subscribed sources /// as well as print events /// /// see receive/ignore for specific source receiving control /// void addReceiver(pd::PdReceiver &receiver); void removeReceiver(pd::PdReceiver &receiver); bool receiverExists(pd::PdReceiver &receiver); void clearReceivers(); ///< also unsubscribes all receivers /// set a receiver to receive/ignore a subscribed source from libpd /// /// receive/ignore using a source name or "" for all sources, /// make sure to add the receiver and source first /// /// note: the global source (aka "") is added by default /// note: ignoring the global source ignores *all* sources, /// so the receiver will not receive any message events, /// but still get print events /// /// also: use negation if you want to plug into all sources but one: /// /// pd.receive(receiver); // receive from *all* /// pd.ignore(receiver, "source"); // ignore "source" /// void receiveSource(pd::PdReceiver &receiver, const std::string &source=""); void ignoreSource(pd::PdReceiver &receiver, const std::string &source=""); bool isReceivingSource(pd::PdReceiver &receiver, const std::string &source=""); /// \section Midi Receiving /// add/remove incoming midi event receiver /// /// receivers automatically receive from *all* incoming midi channels /// /// see receive/ignore for specific source receiving control /// void addMidiReceiver(pd::PdMidiReceiver &receiver); void removeMidiReceiver(pd::PdMidiReceiver &receiver); bool midiReceiverExists(pd::PdMidiReceiver &receiver); void clearMidiReceivers(); /// set a receiver to receive/ignore an incoming midi channel /// /// receive/ignore a specific midi channel or 0 for all channels, /// make sure to add the receiver first /// /// note: midi bytes are sent to all receivers /// note: the global channel (aka 0) is added by default /// note: ignoring the global channel ignores *all* channels, /// so the receiver will not receive any midi events except for /// midi bytes /// /// also: use negation if you want to plug into all channels but one: /// /// pd.receiveMidi(midiReceiver); // receive from *all* channels /// pd.ignoreMidi(midiReceiver, 2); // ignore channel 2 /// void receiveMidiChannel(pd::PdMidiReceiver &receiver, int channel=0); void ignoreMidiChannel(pd::PdMidiReceiver &receiver, int channel=0); bool isReceivingMidiChannel(pd::PdMidiReceiver &receiver, int channel=0); /// \section Sending Functions /// messages /// /// pd.sendBang("test"); /// pd.sendFloat("test", 1.23); /// pd.sendSymbol("test", "hello"); /// /// see PdBase.h for function declarations /// compound messages /// /// pd.startMessage(); /// pd.addSymbol("hello"); /// pd.addFloat(1.23); /// pd.finishList("test"); // "test" is the receiver name in pd /// /// sends [list hello 1.23( -> [r test], /// you will need to use the [list trim] object on the receiving end /// /// finishMsg sends a typed message -> [; test msg1 hello 1.23( /// /// pd.startMessage(); /// pd.addSymbol("hello"); /// pd.addFloat(1.23); /// pd.finishMessage("test", "msg1"); /// /// see PdBase.h for function declarations /// compound messages using the Pd List type /// /// pd::List list; /// list.addSymbol("hello"); /// list.addFloat(1.23); /// pd.sendList("test", list); /// /// sends [list hello 1.23( -> [r test] /// /// clear the list: /// /// list.clear(); /// /// stream operators work as well: /// /// list << "hello" << 1.23; /// pd.sendMessage("test", "msg1", list); /// /// sends a typed message -> [; test msg1 hello 1.23( /// /// see PdBase.h for function declarations /// midi /// /// send midi messages, any out of range messages will be silently ignored /// /// number ranges: /// channel 1 - 16 * dev# (dev #0: 1-16, dev #1: 17-32, etc) /// pitch 0 - 127 /// velocity 0 - 127 /// control value 0 - 127 /// program value 1 - 128 /// bend value -8192 - 8191 /// touch value 0 - 127 /// /// note, in pd: /// [bendin] takes 0 - 16383 while [bendout] returns -8192 - 8192 /// [pgmin] and [pgmout] are 1 - 128 /// void sendNoteOn(const int channel, const int pitch, const int velocity=64); void sendControlChange(const int channel, const int controller, const int value); void sendProgramChange(const int channel, const int value); void sendPitchBend(const int channel, const int value); void sendAftertouch(const int channel, const int value); void sendPolyAftertouch(const int channel, const int pitch, const int value); /// \section Sending Stream Interface /// single messages /// /// pd << Bang("test"); /// "test" is the receiver name in pd /// pd << Float("test", 100); /// pd << Symbol("test", "a symbol"); /// /// compound messages /// /// pd << StartMessage() << 100 << 1.2 << "a symbol" << FinishList("test"); /// /// midi /// /// pd << NoteOn(64) << NoteOn(64, 60) << NoteOn(64, 60, 1); /// pd << ControlChange(100, 64) << ProgramChange(100, 1) << PitchBend(2000, 1); /// pd << Aftertouch(127, 1) << PolyAftertouch(64, 127, 1); /// /// compound raw midi byte stream /// /// pd << StartMidi() << 0xEF << 0x45 << Finish(); /// pd << StartSysex() << 0xE7 << 0x45 << 0x56 << 0x17 << Finish(); /// /// see PdBase.h for function declarations /// \section Array Access /// get array size /// /// int s = pd.arraySize("array1"); /// /// resize an array /// /// pd.resizeArray("array1", 100); /// /// read an array into a float vector /// /// vector array1; /// readArray("array1", array1); /// /// write a float vector to an array /// /// writeArray("array1", array1); /// /// clear array and set to a specific value /// /// clearArray("array1", 0); /// /// see PdBase.h for function declarations /// \section Utils /// has this pd instance been initialized? /// bool isInited(); /// /// is the global pd instance using the ringbuffer queue /// for message padding? /// bool isQueued(); /// /// get the blocksize of pd (sample length per channel) /// static int blockSize(); /// /// get/set the max length of messages and lists, default: 32 /// void setMaxMsgLength(unsigned int len); /// unsigned int maxMsgLength(); /// /// get the pd instance pointer /// returns main instance when libpd is not compiled with PDINSTANCE /// t_pdinstance *instancePtr(); /// /// get the number of pd instances, including the main instance /// returns number or 1 when libpd is not compiled with PDINSTANCE /// static int numInstances(); /// /// see PdBase.h for function declarations /// get the current ticks per buffer, /// updated if the buffer size changes in audioIn or audioOut int ticksPerBuffer(); /// get the current buffer size: ticks per buffer * blockSize(), /// updated if the buffer size changes in audioIn or audioOut int bufferSize(); /// get the current sample rate int sampleRate(); /// get the number of input channels, /// updated if the number changes in audioIn() int numInChannels(); /// get the number of output channels, /// updated if the number changes in audioOut() int numOutChannels(); /// check if ofxPd is currently computing audio bool isComputingAudio(); /// \section Audio Processing Callbacks /// audio settings will be re-inited if the buffersize or number of /// channels changes, will produce a verbose print for debugging as well /// /// note: the libpd processing is done in the audioOut callback /// raw buffer input callback virtual void audioIn(float *input, int bufferSize, int nChannels); /// raw buffer output callback virtual void audioOut(float *output, int bufferSize, int nChannels); /// newer-style input callback which uses ofSoundBuffer void audioIn(ofSoundBuffer &buffer); /// newer-style output callback which uses ofSoundBuffer void audioOut(ofSoundBuffer &buffer); protected: /// message callbacks void print(const std::string &message); void receiveBang(const std::string &dest); void receiveFloat(const std::string &dest, float value); void receiveSymbol(const std::string &dest, const std::string &symbol); void receiveList(const std::string &dest, const pd::List &list); void receiveMessage(const std::string &dest, const std::string &msg, const pd::List &list); /// midi callbacks void receiveNoteOn(const int channel, const int pitch, const int velocity); void receiveControlChange(const int channel, const int controller, const int value); void receiveProgramChange(const int channel, const int value); void receivePitchBend(const int channel, const int value); void receiveAftertouch(const int channel, const int value); void receivePolyAftertouch(const int channel, const int pitch, const int value); void receiveMidiByte(const int port, const int byte); private: int ticks; ///< number of ticks per buffer int bsize; ///< current buffer size aka tbp * blocksize int srate; ///< current sample rate int inChannels, outChannels; ///< current num of input & output channels bool computing; ///< is compute audio on? float *inBuffer; ///< interleaved input audio buffer /// a receiving source's pointer and receivers struct Source { std::set receivers; ///< receivers // helper functions void addReceiver(pd::PdReceiver *receiver) { receivers.insert(receiver); } void removeReceiver(pd::PdReceiver *receiver) { std::set::iterator iter; iter = receivers.find(receiver); if(iter != receivers.end()) receivers.erase(iter); } bool receiverExists(pd::PdReceiver *receiver) { if(receivers.find(receiver) != receivers.end()) return true; return false; } }; std::set receivers; ///< the receivers std::map sources; ///< subscribed sources ///< first object always global /// a receiving midi channel's receivers struct Channel { std::set receivers; ///< receivers // helper functions void addMidiReceiver(pd::PdMidiReceiver *receiver) { receivers.insert(receiver); } void removeMidiReceiver(pd::PdMidiReceiver *receiver) { std::set::iterator iter; iter = receivers.find(receiver); if(iter != receivers.end()) receivers.erase(iter); } bool midiReceiverExists(pd::PdMidiReceiver *receiver) { if(receivers.find(receiver) != receivers.end()) return true; return false; } }; std::set midiReceivers; ///< the midi receivers std::map channels; ///< subscribed channels ///< first object always global };