├── .gitignore ├── Makefile ├── build ├── config-lnx.def ├── config-mac.def ├── config-win.def ├── gnumake-lnx-gcc.inc ├── gnumake-mac-gcc.inc ├── gnumake-win-cygwin.inc └── nmake-win-msvc.inc ├── gpl.txt ├── license.txt ├── maxmsp ├── py-objectmappings.txt ├── py.maxhelp └── thread-1.mxb ├── package.txt ├── pd-lib-builder ├── CHANGELOG.txt ├── Makefile.pdlibbuilder ├── README.md └── tips-tricks.md ├── pd ├── attr-1.pd ├── buffer-1.pd ├── buffer-2.pd ├── builtins-1.pd ├── methods-1.pd ├── methods-2.pd ├── pak.pd ├── script-1.pd ├── sendrecv-1.pd ├── sendrecv-2.pd ├── sendrecv-3.pd ├── sig-1.pd ├── sig-2.pd ├── simple-1.pd ├── simple-2.pd ├── simple-3.pd ├── tcltk.pd └── thread-1.pd ├── py.vcproj ├── py.xcode └── project.pbxproj ├── py.xcodeproj └── project.pbxproj ├── readme.txt ├── scripts ├── buffer.py ├── pak.py ├── script.py ├── sendrecv.py ├── sig.py ├── simple.py ├── tcltk.py └── threads.py └── source ├── bound.cpp ├── clmeth.cpp ├── main.cpp ├── main.h ├── modmeth.cpp ├── py.cpp ├── pyargs.cpp ├── pyatom.cpp ├── pyatom.h ├── pybase.cpp ├── pybase.h ├── pybuffer.cpp ├── pybuffer.h ├── pybundle.cpp ├── pybundle.h ├── pycompat.cpp ├── pycompat.h ├── pydsp.cpp ├── pyext.cpp ├── pyext.h ├── pymeth.cpp ├── pyprefix.h ├── pysymbol.cpp ├── pysymbol.h └── register.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | config.txt 30 | .DS_Store 31 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | NAME=py 2 | cflags=-DFLEXT_INLINE -DFLEXT_ATTRIBUTES=1 3 | 4 | # source files 5 | $(NAME).class.sources = $(wildcard source/*.cpp) 6 | 7 | # help files 8 | datafiles = $(wildcard pd/*.pd scripts/*.py) 9 | 10 | # include Makefile.pdlibbuilder from submodule directory 'pd-lib-builder' 11 | PDLIBBUILDER_DIR=./pd-lib-builder/ 12 | include $(PDLIBBUILDER_DIR)/Makefile.pdlibbuilder 13 | -------------------------------------------------------------------------------- /build/config-lnx.def: -------------------------------------------------------------------------------- 1 | # what is the base prefix of the Python installation? 2 | ifdef PY_CONDA_ROOT 3 | PYTHONPREFIX=$(PY_CONDA_ROOT) 4 | else 5 | PYTHONPREFIX=/usr 6 | endif 7 | 8 | # which Python version do you want to compile against? 9 | PY_MAJOR_VERSION=3 10 | PY_MINOR_VERSION=7 11 | 12 | # uncomment if numpy/numarray/numeric support should be compiled in 13 | # for info see http://numeric.scipy.org 14 | PY_NUMPY=1 15 | # PY_NUMARRAY=1 16 | # PY_NUMERIC=1 17 | 18 | # use thread-safe GIL functionality (do this for python version >= 2.3!) 19 | PY_USE_GIL=1 20 | 21 | # use inofficial (pure data) functionality 22 | # PY_USE_INOFFICIAL=1 23 | 24 | # use python with pymalloc (look for "pythonX.Ym" files) 25 | PY_USE_PYMALLOC=1 26 | -------------------------------------------------------------------------------- /build/config-mac.def: -------------------------------------------------------------------------------- 1 | # which kind of Python installation to use 2 | # system - macOS system default 3 | # local - local installation 4 | # conda - conda environment; specify the environment root dir using the 5 | # PY_CONDA_ROOT environment variable 6 | PY_KIND=conda 7 | 8 | ######################################################################### 9 | 10 | # which Python version are you building for? 11 | # Mac OSX 10.3 -> default Python version (major.minor) = 2.3 12 | # Mac OSX 10.4 -> default Python version (major.minor) = 2.3 13 | # Mac OSX 10.5 -> default Python version (major.minor) = 2.5 14 | # Mac OSX 10.6 -> default Python version (major.minor) = 2.6 15 | 16 | PY_MAJOR_VERSION=3 17 | PY_MINOR_VERSION=7 18 | 19 | ######################################################################### 20 | 21 | # uncomment if numpy/numarray/numeric support should be compiled in 22 | # for info see http://numeric.scipy.org 23 | # numarray and numeric are outdated 24 | 25 | PY_NUMPY=1 26 | # PY_NUMARRAY=1 27 | # PY_NUMERIC=1 28 | 29 | ######################################################################### 30 | 31 | # use thread-safe GIL functionality (do this only if python version >= 2.3!) 32 | PY_USE_GIL=1 33 | 34 | # use inofficial (pure data) functionality 35 | # PY_USE_INOFFICIAL=1 36 | 37 | # use python with pymalloc (look for "pythonX.Ym" files) 38 | PY_USE_PYMALLOC=1 39 | -------------------------------------------------------------------------------- /build/config-win.def: -------------------------------------------------------------------------------- 1 | # which major python version? 2 | PYTHONVER=24 3 | 4 | # where is the Python installation? 5 | PYTHONPATH=%programfiles%/python$(PYTHONVER) 6 | 7 | # uncomment if numpy/numarray/numeric support should be compiled in 8 | # for info see http://numeric.scipy.org 9 | PY_NUMPY=1 10 | # PY_NUMARRAY=1 11 | # PY_NUMERIC=1 12 | 13 | # use thread-safe GIL functionality (do this for python version >= 2.3!) 14 | PY_USE_GIL=1 15 | 16 | # use inofficial (pure data) functionality 17 | # PY_USE_INOFFICIAL=1 18 | -------------------------------------------------------------------------------- /build/gnumake-lnx-gcc.inc: -------------------------------------------------------------------------------- 1 | ifdef PY_USE_PYMALLOC 2 | PYTHONVERSION=$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION)m 3 | else 4 | PYTHONVERSION=$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION) 5 | endif 6 | 7 | DEFS += -DPY_EXPORTS 8 | 9 | INCPATH += -I$(PYTHONPREFIX)/include/python$(PYTHONVERSION) 10 | 11 | ifdef PY_CONDA_ROOT 12 | LIBPATH += -L$(PY_CONDA_ROOT)/lib 13 | LDFLAGS += -Wl,-R$(PY_CONDA_ROOT)/lib 14 | DEFS += -DPY_INTERPRETER=$(PY_CONDA_ROOT)/bin/python 15 | endif 16 | 17 | LIBS += -lpython$(PYTHONVERSION) 18 | 19 | ifdef PY_NUMARRAY 20 | DEFS += -DPY_NUMARRAY 21 | endif 22 | ifdef PY_NUMPY 23 | DEFS += -DPY_NUMPY 24 | INCPATH += -I$(PYTHONPREFIX)/lib/python$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION)/site-packages/numpy/core/include 25 | endif 26 | ifdef PY_NUMERIC 27 | DEFS += -DPY_NUMERIC 28 | endif 29 | 30 | ifdef PY_USE_GIL 31 | DEFS += -DPY_USE_GIL 32 | endif 33 | 34 | ifdef PY_USE_INOFFICIAL 35 | DEFS += -DPY_USE_INOFFICIAL 36 | endif 37 | -------------------------------------------------------------------------------- /build/gnumake-mac-gcc.inc: -------------------------------------------------------------------------------- 1 | DEFS += -DPY_EXPORTS 2 | 3 | ifdef PY_NUMPY 4 | DEFS += -DPY_NUMPY 5 | endif 6 | 7 | ifdef PY_NUMARRAY 8 | DEFS += -DPY_NUMARRAY 9 | endif 10 | 11 | ifdef PY_NUMERIC 12 | DEFS += -DPY_NUMERIC 13 | endif 14 | 15 | ifdef PY_USE_GIL 16 | DEFS += -DPY_USE_GIL 17 | endif 18 | 19 | ifdef PY_USE_INOFFICIAL 20 | DEFS += -DPY_USE_INOFFICIAL 21 | endif 22 | 23 | ifeq ($(PY_KIND),conda) 24 | 25 | ifndef PY_CONDA_ROOT 26 | $(error PY_CONDA_ROOT is undefined) 27 | endif 28 | 29 | DEFS += -DPY_INTERPRETER=$(PY_CONDA_ROOT)/bin/python 30 | INCPATH += -I$(PY_CONDA_ROOT)/include 31 | ifdef PY_USE_PYMALLOC 32 | INCPATH += -I$(PY_CONDA_ROOT)/include/python$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION)m 33 | LIBS += $(PY_CONDA_ROOT)/lib/libpython$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION)m.dylib 34 | else 35 | INCPATH += -I$(PY_CONDA_ROOT)/include/python$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION) 36 | LIBS += $(PY_CONDA_ROOT)/lib/libpython$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION).dylib 37 | endif 38 | LDFLAGS += -rpath $(PY_CONDA_ROOT)/lib 39 | 40 | ifdef PY_NUMPY 41 | INCPATH += -I$(PY_CONDA_ROOT)/lib/python$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION)/site-packages/numpy/core/include 42 | endif 43 | 44 | else 45 | 46 | DEFS += -DPY_USE_FRAMEWORK 47 | 48 | # don't use -framework Python, since this will stick to the default system version 49 | 50 | _LOCAL_FRAMEWORK := /Library/Frameworks/Python.framework/Versions/$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION) 51 | _SYSTEM_FRAMEWORK := /System/Library/Frameworks/Python.framework/Versions/$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION) 52 | _LOCAL_LIBRARY := /Library/Python/$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION) 53 | _SYSTEM_LIBRARY := /System/Library/Python/$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION) 54 | 55 | INCPATH += -F/Library/Frameworks -framework Python 56 | 57 | ifeq ($(PY_KIND),system) 58 | LIBS += $(_SYSTEM_FRAMEWORK)/Python 59 | INCPATH += -I$(_SYSTEM_FRAMEWORK)/Headers 60 | else 61 | LIBS += $(_LOCAL_FRAMEWORK)/Python 62 | INCPATH += -I$(_LOCAL_FRAMEWORK)/Headers 63 | endif 64 | 65 | ifdef PY_NUMPY 66 | INCPATH += -I$(_LOCAL_LIBRARY)/python$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION)/site-packages/numpy/core/include 67 | 68 | ifeq ($(PY_KIND),system) 69 | INCPATH += -I$(_SYSTEM_FRAMEWORK)/lib/python$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION)/site-packages/numpy/core/include 70 | INCPATH += -I$(_SYSTEM_FRAMEWORK)/Extras/lib/python/numpy/core/include 71 | else 72 | INCPATH += -I$(_LOCAL_FRAMEWORK)/lib/python$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION)/site-packages/numpy/core/include 73 | endif 74 | endif 75 | 76 | endif 77 | 78 | #DEBUG = 1 79 | #DFLAGS += -DFLEXT_DEBUG 80 | -------------------------------------------------------------------------------- /build/gnumake-win-cygwin.inc: -------------------------------------------------------------------------------- 1 | DEFS += -DPY_EXPORTS 2 | INCPATH += -I$(PYTHONPATH)/include 3 | LIBPATH += -L$(PYTHONPATH)/libs 4 | LIBS += -lpython$(PYTHONVER) -lshell32 5 | 6 | ifdef PY_NUMARRAY 7 | DEFS += -DPY_NUMARRAY 8 | endif 9 | ifdef PY_NUMPY 10 | DEFS += -DPY_NUMPY 11 | endif 12 | ifdef PY_NUMERIC 13 | DEFS += -DPY_NUMERIC 14 | endif 15 | 16 | ifdef PY_USE_GIL 17 | DEFS += -DPY_USE_GIL 18 | endif 19 | 20 | ifdef PY_USE_INOFFICIAL 21 | DEFS += -DPY_USE_INOFFICIAL 22 | endif 23 | -------------------------------------------------------------------------------- /build/nmake-win-msvc.inc: -------------------------------------------------------------------------------- 1 | DEFS = $(DEFS) /DPY_EXPORTS 2 | INCPATH=/I$(PYTHONPATH)\include 3 | LIBPATH=/LIBPATH:$(PYTHONPATH)\libs 4 | LIBS=$(LIBS) shell32.lib 5 | 6 | !ifdef PY_NUMARRAY 7 | DEFS = $(DEFS) /DPY_NUMARRAY 8 | !endif 9 | !ifdef PY_NUMPY 10 | INCPATH=$(INCPATH) /I$(PYTHONPATH)\Lib\site-packages\numpy\core\include 11 | DEFS = $(DEFS) /DPY_NUMPY 12 | !endif 13 | !ifdef PY_NUMERIC 14 | DEFS = $(DEFS) /DPY_NUMERIC 15 | !endif 16 | 17 | !ifdef PY_USE_GIL 18 | DEFS = $(DEFS) /DPY_USE_GIL 19 | !endif 20 | 21 | !ifdef PY_USE_INOFFICIAL 22 | DEFS = $(DEFS) /DPY_USE_INOFFICIAL 23 | !endif 24 | -------------------------------------------------------------------------------- /gpl.txt: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Library General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | 60 | GNU GENERAL PUBLIC LICENSE 61 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 62 | 63 | 0. This License applies to any program or other work which contains 64 | a notice placed by the copyright holder saying it may be distributed 65 | under the terms of this General Public License. The "Program", below, 66 | refers to any such program or work, and a "work based on the Program" 67 | means either the Program or any derivative work under copyright law: 68 | that is to say, a work containing the Program or a portion of it, 69 | either verbatim or with modifications and/or translated into another 70 | language. (Hereinafter, translation is included without limitation in 71 | the term "modification".) Each licensee is addressed as "you". 72 | 73 | Activities other than copying, distribution and modification are not 74 | covered by this License; they are outside its scope. The act of 75 | running the Program is not restricted, and the output from the Program 76 | is covered only if its contents constitute a work based on the 77 | Program (independent of having been made by running the Program). 78 | Whether that is true depends on what the Program does. 79 | 80 | 1. You may copy and distribute verbatim copies of the Program's 81 | source code as you receive it, in any medium, provided that you 82 | conspicuously and appropriately publish on each copy an appropriate 83 | copyright notice and disclaimer of warranty; keep intact all the 84 | notices that refer to this License and to the absence of any warranty; 85 | and give any other recipients of the Program a copy of this License 86 | along with the Program. 87 | 88 | You may charge a fee for the physical act of transferring a copy, and 89 | you may at your option offer warranty protection in exchange for a fee. 90 | 91 | 2. You may modify your copy or copies of the Program or any portion 92 | of it, thus forming a work based on the Program, and copy and 93 | distribute such modifications or work under the terms of Section 1 94 | above, provided that you also meet all of these conditions: 95 | 96 | a) You must cause the modified files to carry prominent notices 97 | stating that you changed the files and the date of any change. 98 | 99 | b) You must cause any work that you distribute or publish, that in 100 | whole or in part contains or is derived from the Program or any 101 | part thereof, to be licensed as a whole at no charge to all third 102 | parties under the terms of this License. 103 | 104 | c) If the modified program normally reads commands interactively 105 | when run, you must cause it, when started running for such 106 | interactive use in the most ordinary way, to print or display an 107 | announcement including an appropriate copyright notice and a 108 | notice that there is no warranty (or else, saying that you provide 109 | a warranty) and that users may redistribute the program under 110 | these conditions, and telling the user how to view a copy of this 111 | License. (Exception: if the Program itself is interactive but 112 | does not normally print such an announcement, your work based on 113 | the Program is not required to print an announcement.) 114 | 115 | 116 | These requirements apply to the modified work as a whole. If 117 | identifiable sections of that work are not derived from the Program, 118 | and can be reasonably considered independent and separate works in 119 | themselves, then this License, and its terms, do not apply to those 120 | sections when you distribute them as separate works. But when you 121 | distribute the same sections as part of a whole which is a work based 122 | on the Program, the distribution of the whole must be on the terms of 123 | this License, whose permissions for other licensees extend to the 124 | entire whole, and thus to each and every part regardless of who wrote it. 125 | 126 | Thus, it is not the intent of this section to claim rights or contest 127 | your rights to work written entirely by you; rather, the intent is to 128 | exercise the right to control the distribution of derivative or 129 | collective works based on the Program. 130 | 131 | In addition, mere aggregation of another work not based on the Program 132 | with the Program (or with a work based on the Program) on a volume of 133 | a storage or distribution medium does not bring the other work under 134 | the scope of this License. 135 | 136 | 3. You may copy and distribute the Program (or a work based on it, 137 | under Section 2) in object code or executable form under the terms of 138 | Sections 1 and 2 above provided that you also do one of the following: 139 | 140 | a) Accompany it with the complete corresponding machine-readable 141 | source code, which must be distributed under the terms of Sections 142 | 1 and 2 above on a medium customarily used for software interchange; or, 143 | 144 | b) Accompany it with a written offer, valid for at least three 145 | years, to give any third party, for a charge no more than your 146 | cost of physically performing source distribution, a complete 147 | machine-readable copy of the corresponding source code, to be 148 | distributed under the terms of Sections 1 and 2 above on a medium 149 | customarily used for software interchange; or, 150 | 151 | c) Accompany it with the information you received as to the offer 152 | to distribute corresponding source code. (This alternative is 153 | allowed only for noncommercial distribution and only if you 154 | received the program in object code or executable form with such 155 | an offer, in accord with Subsection b above.) 156 | 157 | The source code for a work means the preferred form of the work for 158 | making modifications to it. For an executable work, complete source 159 | code means all the source code for all modules it contains, plus any 160 | associated interface definition files, plus the scripts used to 161 | control compilation and installation of the executable. However, as a 162 | special exception, the source code distributed need not include 163 | anything that is normally distributed (in either source or binary 164 | form) with the major components (compiler, kernel, and so on) of the 165 | operating system on which the executable runs, unless that component 166 | itself accompanies the executable. 167 | 168 | If distribution of executable or object code is made by offering 169 | access to copy from a designated place, then offering equivalent 170 | access to copy the source code from the same place counts as 171 | distribution of the source code, even though third parties are not 172 | compelled to copy the source along with the object code. 173 | 174 | 175 | 4. You may not copy, modify, sublicense, or distribute the Program 176 | except as expressly provided under this License. Any attempt 177 | otherwise to copy, modify, sublicense or distribute the Program is 178 | void, and will automatically terminate your rights under this License. 179 | However, parties who have received copies, or rights, from you under 180 | this License will not have their licenses terminated so long as such 181 | parties remain in full compliance. 182 | 183 | 5. You are not required to accept this License, since you have not 184 | signed it. However, nothing else grants you permission to modify or 185 | distribute the Program or its derivative works. These actions are 186 | prohibited by law if you do not accept this License. Therefore, by 187 | modifying or distributing the Program (or any work based on the 188 | Program), you indicate your acceptance of this License to do so, and 189 | all its terms and conditions for copying, distributing or modifying 190 | the Program or works based on it. 191 | 192 | 6. Each time you redistribute the Program (or any work based on the 193 | Program), the recipient automatically receives a license from the 194 | original licensor to copy, distribute or modify the Program subject to 195 | these terms and conditions. You may not impose any further 196 | restrictions on the recipients' exercise of the rights granted herein. 197 | You are not responsible for enforcing compliance by third parties to 198 | this License. 199 | 200 | 7. If, as a consequence of a court judgment or allegation of patent 201 | infringement or for any other reason (not limited to patent issues), 202 | conditions are imposed on you (whether by court order, agreement or 203 | otherwise) that contradict the conditions of this License, they do not 204 | excuse you from the conditions of this License. If you cannot 205 | distribute so as to satisfy simultaneously your obligations under this 206 | License and any other pertinent obligations, then as a consequence you 207 | may not distribute the Program at all. For example, if a patent 208 | license would not permit royalty-free redistribution of the Program by 209 | all those who receive copies directly or indirectly through you, then 210 | the only way you could satisfy both it and this License would be to 211 | refrain entirely from distribution of the Program. 212 | 213 | If any portion of this section is held invalid or unenforceable under 214 | any particular circumstance, the balance of the section is intended to 215 | apply and the section as a whole is intended to apply in other 216 | circumstances. 217 | 218 | It is not the purpose of this section to induce you to infringe any 219 | patents or other property right claims or to contest validity of any 220 | such claims; this section has the sole purpose of protecting the 221 | integrity of the free software distribution system, which is 222 | implemented by public license practices. Many people have made 223 | generous contributions to the wide range of software distributed 224 | through that system in reliance on consistent application of that 225 | system; it is up to the author/donor to decide if he or she is willing 226 | to distribute software through any other system and a licensee cannot 227 | impose that choice. 228 | 229 | This section is intended to make thoroughly clear what is believed to 230 | be a consequence of the rest of this License. 231 | 232 | 233 | 8. If the distribution and/or use of the Program is restricted in 234 | certain countries either by patents or by copyrighted interfaces, the 235 | original copyright holder who places the Program under this License 236 | may add an explicit geographical distribution limitation excluding 237 | those countries, so that distribution is permitted only in or among 238 | countries not thus excluded. In such case, this License incorporates 239 | the limitation as if written in the body of this License. 240 | 241 | 9. The Free Software Foundation may publish revised and/or new versions 242 | of the General Public License from time to time. Such new versions will 243 | be similar in spirit to the present version, but may differ in detail to 244 | address new problems or concerns. 245 | 246 | Each version is given a distinguishing version number. If the Program 247 | specifies a version number of this License which applies to it and "any 248 | later version", you have the option of following the terms and conditions 249 | either of that version or of any later version published by the Free 250 | Software Foundation. If the Program does not specify a version number of 251 | this License, you may choose any version ever published by the Free Software 252 | Foundation. 253 | 254 | 10. If you wish to incorporate parts of the Program into other free 255 | programs whose distribution conditions are different, write to the author 256 | to ask for permission. For software which is copyrighted by the Free 257 | Software Foundation, write to the Free Software Foundation; we sometimes 258 | make exceptions for this. Our decision will be guided by the two goals 259 | of preserving the free status of all derivatives of our free software and 260 | of promoting the sharing and reuse of software generally. 261 | 262 | NO WARRANTY 263 | 264 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 265 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 266 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 267 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 268 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 269 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 270 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 271 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 272 | REPAIR OR CORRECTION. 273 | 274 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 275 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 276 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 277 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 278 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 279 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 280 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 281 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 282 | POSSIBILITY OF SUCH DAMAGES. 283 | 284 | END OF TERMS AND CONDITIONS 285 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | py/pyext - python script objects for PD and MaxMSP 2 | Copyright (C) 2002-2008 Thomas Grill 3 | 4 | This program is free software; you can redistribute it and/or 5 | modify it under the terms of the GNU General Public License 6 | as published by the Free Software Foundation; either version 2 7 | of the License, or (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program; if not, write to the Free Software 16 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 | 18 | In the official py/pyext distribution, the GNU General Public License is 19 | in the file gpl.txt 20 | 21 | --------------------------------------------------------- 22 | 23 | OTHER COPYRIGHT NOTICES 24 | 25 | --------------------------------------------------------- 26 | This package uses the flext C++ layer - See its license text below: 27 | 28 | 29 | --- flext ---------------------------------------------- 30 | flext - C++ layer for Max/MSP and pd (pure data) externals 31 | Copyright (C) 2001-2008 Thomas Grill 32 | 33 | This program is free software; you can redistribute it and/or 34 | modify it under the terms of the GNU General Public License 35 | as published by the Free Software Foundation; either version 2 36 | of the License, or (at your option) any later version. 37 | 38 | This program is distributed in the hope that it will be useful, 39 | but WITHOUT ANY WARRANTY; without even the implied warranty of 40 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 41 | GNU General Public License for more details. 42 | 43 | You should have received a copy of the GNU General Public License 44 | along with this program; if not, write to the Free Software 45 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 46 | 47 | In the official flext distribution, the GNU General Public License is 48 | in the file gpl.txt 49 | 50 | 51 | -------------------------------------------------------------------------------- /maxmsp/py-objectmappings.txt: -------------------------------------------------------------------------------- 1 | max objectfile py py; 2 | max objectfile py. py; 3 | max objectfile pyext py; 4 | max objectfile pyext. py; 5 | max objectfile pyx py; 6 | max objectfile pyx. py; 7 | max objectfile pyext~ py; 8 | max objectfile pyext.~ py; 9 | max objectfile pyx~ py; 10 | max objectfile pyx.~ py; 11 | max objectfile pym py; 12 | 13 | max oblist python py; 14 | max oblist python py.; 15 | max oblist python pyext; 16 | max oblist python pyext.; 17 | max oblist python pyext~; 18 | max oblist python pyext.~; 19 | max oblist python pym; 20 | -------------------------------------------------------------------------------- /maxmsp/thread-1.mxb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SopiMlab/py/35138a0a7e0917849170a5d3003ed60fb80fa450/maxmsp/thread-1.mxb -------------------------------------------------------------------------------- /package.txt: -------------------------------------------------------------------------------- 1 | NAME=py 2 | 3 | BUILDTYPE=multi 4 | BUILDDIR=build 5 | 6 | SRCDIR=source 7 | PRECOMPILE=pyprefix.h 8 | 9 | SRCS= \ 10 | main.cpp \ 11 | py.cpp pyext.cpp modmeth.cpp clmeth.cpp \ 12 | register.cpp bound.cpp pyargs.cpp \ 13 | pysymbol.cpp pybuffer.cpp pybundle.cpp pydsp.cpp \ 14 | pyatom.cpp pybase.cpp pymeth.cpp pycompat.cpp 15 | 16 | HDRS= pyprefix.h main.h pyext.h pysymbol.h pybuffer.h pybundle.h pyatom.h pybase.h pycompat.h 17 | -------------------------------------------------------------------------------- /pd-lib-builder/CHANGELOG.txt: -------------------------------------------------------------------------------- 1 | Changelog for Makefile.pdlibbuilder. 2 | 3 | v0.6.0, dated 2019-12-21 4 | - detect target platform (OS and architecture) rather than build platform (#55) 5 | - introduce optional user variable 'PLATFORM' for cross compilation 6 | - no longer build OSX/MacOS fat binaries by default (#21, #50) 7 | - do build fat binaries when 'extension=d_fat' is specified for OSX/MacOS 8 | - fix bug where minimum OSX/MacOS version wasn't defined, and set it to 10.6 9 | 10 | v0.5.1, dated 2018-03-15 11 | Fixes and improvements for Windows builds: 12 | - properly evaluate variables 'PDDIR' and 'PDBINDIR' to find pd.dll 13 | - define default path of 32 bit Pd on 64 bit Windows 14 | - link C++ externals with standard C libs on Windows, they don't load otherwise 15 | - strip installed Windows binaries by default 16 | (issues #34, #39, #41, #42 respectively) 17 | Warning for all platforms: variable 'PD_PATH' is no longer supported, use the 18 | equivalent 'PDDIR'. 19 | 20 | v0.5.0, dated 2018-01-23 21 | Implement target architecture detection for Windows builds, 22 | and set appropriate options for 32 and 64 bit (used to be for 32 bit only). 23 | (feature, issue #37 #38, merge commit 215bf3e) 24 | 25 | v0.4.4, dated 2016-11-22 26 | Use variable 'system' when evaluating 'for{Linux,Darwin,Windows}' 27 | (bugfix, issue #31, commit 2c14110) 28 | 29 | v0.4.3, dated 2016-11-02 30 | Replace flags '-fpic' by 'fPIC'. 31 | (bugfix, issue #29, commit 426b38b) 32 | 33 | v0.4.2, dated 2016-10-30 34 | Fix issue where incorrect message about m_pd.h is given. 35 | (bugfix, commit 2e13d8f) 36 | 37 | v0.4.1, dated 2016-10-27 38 | Respect cflag for minimum OSX version when defined by lib makefile. 39 | (bugfix, pull request #22, commit 48c4127) 40 | 41 | v0.4.0, dated 2016-10-14 42 | Introduced path variables PDDIR, PDINCLUDEDIR, PDBINDIR, PDLIBDIR which can 43 | also be defined in environment. 44 | (feature, issue #27, commit b0dab72) 45 | 46 | v0.3.1, dated 2016-10-13 47 | Fix bug where pd.dll wouldn't be found. 48 | (bugfix, commit a0c87be) 49 | 50 | v0.3.0, dated 2016-10-09 51 | Variable 'PD_PATH' introduced for pd-extended / pd-l2ork compatibility. 52 | (feature, issue #26, commit 41e9743) 53 | 54 | v0.2.8, dated 2016-10-09 55 | Allow installed files to contain weird characters (notably '$'). 56 | (bugfix, pull request #20, commit 5b920b1) 57 | 58 | v0.2.7, dated 2016-10-04 59 | Remove all default pd search paths except vanilla's. 60 | (discussion, issue #25, commit a6a89dc) 61 | 62 | v0.2.6, dated 2016-09-20 63 | Redefined dependency checking so it won't stall rebuilds on OSX. 64 | (bugfix, issue #16, commit 9fd1795) 65 | 66 | v0.2.5, dated 2016-06-26 67 | Fixed dependency checking for object files in other directories. 68 | (bugfix, commit f06e550) 69 | 70 | v0.2.4, dated 2016-06-25 71 | Fixed regression bug that disabled all dependency checking. 72 | (bugfix, commit 1d7bb5e) 73 | 74 | v0.2.3, dated 2016-03-29 75 | Disabled dependency checking for OSX <= 10.5 because it stalled rebuilds. 76 | (bugfix, issue #16, commit eb614fd) 77 | 78 | v0.2.2, dated 2016-03-28 79 | Removed target 'pre' because it forced rebuild of everything in 'all'. 80 | (bugfix, issue #17, commit c989c8e) 81 | 82 | v0.2.1, dated 2015-12-27 83 | Implement / respect 'CPPFLAGS','CFLAGS'and 'LDFLAGS'. 84 | (bugfix, issue #5, commit 98f3582) 85 | 86 | v0.2.0, dated 2015-12-19 87 | Added per-platform multiline defines 'forLinux', 'forDarwin', 'forWindows'. 88 | (feature, pull request #9, commit 3946ea5) 89 | 90 | v0.1.0, dated 2015-12-08 91 | Added targets 'pre' and 'post' to automatically run before and after 'all'. 92 | (feature, pull request #4, commit a5678ac) 93 | 94 | v0.0.2, dated 2015-12-06 95 | Improved methods for searching pd paths. 96 | (bugfix, commit ed37e6b) 97 | 98 | v0.0.1, dated 2015-10-31 99 | Fixed expansion of variable 'lib.version'. 100 | (bugfix, issue #1, commit 974b617) 101 | 102 | v0.0.0, dated 2015-06-24 103 | Initial version. 104 | (commit 16517a2) 105 | -------------------------------------------------------------------------------- /pd-lib-builder/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ### Makefile.pdlibbuilder ### 4 | 5 | Helper makefile for Pure Data external libraries. Written by Katja Vetter 6 | March-June 2015 for the public domain and since then developed as a Pd 7 | community project. No warranties. Inspired by Hans Christoph Steiner's Makefile 8 | Template and Stephan Beal's ShakeNMake. 9 | 10 | GNU make version >= 3.81 required. 11 | 12 | 13 | ### characteristics ### 14 | 15 | 16 | * defines build settings based on autodetected target platform 17 | * defines rules to build Pd class- or lib executables from C or C++ sources 18 | * defines rules for libdir installation 19 | * defines convenience targets for developer and user 20 | * evaluates implicit dependencies for non-clean builds 21 | 22 | 23 | ### basic usage ### 24 | 25 | 26 | In your Makefile, define your Pd lib name and class files, and include 27 | Makefile.pdlibbuilder at the end of the Makefile. Like so: 28 | 29 | 30 | # Makefile for mylib 31 | 32 | lib.name = mylib 33 | 34 | class.sources = myclass1.c myclass2.c 35 | 36 | datafiles = myclass1-help.pd myclass2-help.pd README.txt LICENSE.txt 37 | 38 | include Makefile.pdlibbuilder 39 | 40 | 41 | For files in class.sources it is assumed that class name == source file 42 | basename. The default target builds all classes as individual executables 43 | with Pd's default extension for the platform. For anything more than the 44 | most basic usage, read the documentation sections in Makefile.pdlibbuilder. 45 | 46 | 47 | ### paths ### 48 | 49 | 50 | Makefile.pdlibbuilder >= v0.4.0 supports pd path variables which can be 51 | defined not only as make command argument but also in the environment, to 52 | override platform-dependent defaults: 53 | 54 | PDDIR: 55 | Root directory of 'portable' pd package. When defined, PDINCLUDEDIR and 56 | PDBINDIR will be evaluated as $(PDDIR)/src and $(PDDIR)/bin. 57 | 58 | PDINCLUDEDIR: 59 | Directory where Pd API m_pd.h should be found, and other Pd header files. 60 | Overrides the default search path. 61 | 62 | PDBINDIR: 63 | Directory where pd.dll should be found for linking (Windows only). Overrides 64 | the default search path. 65 | 66 | PDLIBDIR: 67 | Root directory for installation of Pd library directories. Overrides the 68 | default install location. 69 | 70 | 71 | ### documentation ### 72 | 73 | 74 | This README.md provides only basic information. A large comment section inside 75 | Makefile.pdlibbuilder lists and explains the available user variables, default 76 | paths, and targets. The internal documentation reflects the exact functionality 77 | of the particular version. For suggestions about project maintenance and 78 | advanced compilation see tips-tricks.md. 79 | 80 | 81 | ### versioning ### 82 | 83 | 84 | The project is versioned in MAJOR.MINOR.BUGFIX format (see http://semver.org), 85 | and maintained at https://github.com/pure-data/pd-lib-builder. Pd lib developers 86 | are invited to regulary check for updates, and to contribute and discuss 87 | improvements here. If you really need to distribute a personalized version with 88 | your library, rename Makefile.pdlibbuilder to avoid confusion. 89 | 90 | 91 | ### examples ### 92 | 93 | The list of projects using pd-lib-builder can be helpful if you are looking for 94 | examples, from the simplest use case to more complex implementations. 95 | 96 | - helloworld: traditional illustration of simplest use case 97 | - pd-windowing: straightforward real world use case of a small library 98 | - pd-nilwind / pd-cyclone: more elaborate source tree 99 | - zexy: migrated from autotools to pd-lib-builder 100 | 101 | 102 | ### projects using pd-lib-builder ### 103 | 104 | non-exhaustive list 105 | 106 | https://github.com/pure-data/helloworld 107 | 108 | https://github.com/electrickery/pd-nilwind 109 | 110 | https://github.com/electrickery/pd-maxlib 111 | 112 | https://github.com/electrickery/pd-sigpack 113 | 114 | https://github.com/electrickery/pd-tof 115 | 116 | https://github.com/electrickery/pd-windowing 117 | 118 | https://github.com/electrickery/pd-smlib 119 | 120 | https://github.com/porres/pd-cyclone 121 | 122 | https://github.com/porres/pd-else 123 | 124 | https://github.com/porres/pd-psycho 125 | 126 | https://git.iem.at/pd/comport 127 | 128 | https://git.iem.at/pd/hexloader 129 | 130 | https://git.iem.at/pd/iemgui 131 | 132 | https://git.iem.at/pd/iemguts 133 | 134 | https://git.iem.at/pd/iemlib 135 | 136 | https://git.iem.at/pd/iemnet 137 | 138 | https://git.iem.at/pd/iem_ambi 139 | 140 | https://git.iem.at/pd/iem_tab 141 | 142 | https://git.iem.at/pd/iem_adaptfilt 143 | 144 | https://git.iem.at/pd/iem_roomsim 145 | 146 | https://git.iem.at/pd/iem_spec2 147 | 148 | https://git.iem.at/pd/mediasettings 149 | 150 | https://git.iem.at/pd/zexy 151 | 152 | https://git.iem.at/pd-gui/punish 153 | 154 | https://github.com/residuum/PuRestJson 155 | 156 | https://github.com/libpd/abl_link 157 | 158 | https://github.com/wbrent/timbreID 159 | 160 | https://github.com/MetaluNet/moonlib 161 | 162 | 163 | -------------------------------------------------------------------------------- /pd-lib-builder/tips-tricks.md: -------------------------------------------------------------------------------- 1 | pd-lib-builder cheatsheet 2 | ========================= 3 | 4 | # Creating special builds 5 | 6 | ## cross-compiling on linux x86_64 for other platforms 7 | 8 | Using pd-lib-builder >=0.6.0 we can define variable `PLATFORM` to specify a 9 | target triplet for cross-compilation. Example to build W32 binaries (assuming 10 | package `mingw-w64` is installed and a W32 package for Pd is unzipped into a 11 | path `${PDWIN32}`: 12 | 13 | make PLATFORM=x86_64-w64-mingw32 PDDIR="${PDWIN32}" 14 | 15 | #### older pd-lib-builder versions 16 | 17 | Using pd-lib-builder < 0.6.0, in the absence of variable `PLATFORM`, you would 18 | instead override variables `system`, `target.arch`, `CC` and / or `CXX`, 19 | `STRIP`. Example: 20 | 21 | make system=Windows target.arch=i686 CC=i686-w64-mingw32-gcc STRIP=i686-w64-mingw32-strip PDDIR="${PDWIN32}" 22 | 23 | #### toolchains 24 | 25 | Cross toolchains for relevant platforms in Debian Buster (install g++ 26 | with dependencies for a given platform to get the whole tool chain): 27 | 28 | - `arm-linux-gnueabihf` 29 | - `aarch64-linux-gnu` 30 | - `i686-linux-gnu` 31 | - `i686-w64-mingw32` and `x86_64-w64-mingw32` (install `mingw-w64`) 32 | 33 | OSX/MacOS cross tool chains are not distributed by Debian. Use project 34 | `osxcross` from Thomas Poechtraeger to create the tools. 35 | 36 | ## building double-precision externals 37 | 38 | At the time of writing (2018-02) there is no official Pd that supports 39 | double-precision numbers yet. 40 | However, if you do get hold of an experimental double-precision Pd, you can 41 | easily build your externals for 64-bit numbers: 42 | 43 | make CPPFLAGS="-DPD_FLOATSIZE=64" 44 | 45 | ## building externals for W64 (64-bit Windows) 46 | 47 | At the time of writing (2018-02) there is no official Pd that supports 48 | W64 yet. 49 | However, if you do get hold of an experimental W64 Pd, you can 50 | easily build your externals for this environment with 51 | 52 | make CPPFLAGS="-DPD_LONGINTTYPE=__int64" CC=x86_64-w64-mingw32-gcc 53 | 54 | 55 | To build a double-precision external for W64, use something like: 56 | 57 | make CPPFLAGS="-DPD_LONGINTTYPE=__int64 -DPD_FLOATSIZE=64" CC=x86_64-w64-mingw32-gcc 58 | 59 | 60 | ## TODO universal binaries on OSX 61 | 62 | 63 | # Project management 64 | 65 | In general it is advised to put the `Makefile.pdlibbuilder` into a separate 66 | subdirectory (e.g. `pd-lib-builder/`). 67 | This makes it much easier to update the `Makefile.pdlibbuilder` later 68 | 69 | You *should* also use a variable to the actual path of the Makefile.pdlibbuilder 70 | (even if you keep it in the root-directory), as this allows easy experimenting 71 | with newer (or older) (or site-specific) versions of the pd-lib-builder 72 | Makefile. 73 | 74 | ~~~make 75 | PDLIBBUILDER_DIR=pd-lib-builder/ 76 | include $(PDLIBBUILDER_DIR)/Makefile.pdlibbuilder 77 | ~~~ 78 | 79 | ## Keeping pd-lib-builder up-to-date 80 | 81 | ### `git subtree` 82 | 83 | With git-subtrees, you make the pd-lib-builder repository (or any other 84 | repository for that matter) part of your own repository - with full history and 85 | everything - put nicely into a distinct subdirectory. 86 | 87 | Support for *manipulating* subtrees has been added with Git-v1.7.11 (May 2012). 88 | The nice thing however is, that from "outside" the subtree is part of your 89 | repository like any other directory. E.g. older versions of Git can clone your 90 | repository with the full subtree (and all it's history) just fine. 91 | You can also use git-archive to make a complete snapshot of your repository 92 | (including the subtree) - nice, if you e.g. want self-contained downloads of 93 | your project from git hosting platforms (like Github, Gitlab, Bitbucket,...) 94 | 95 | In short, `git subtree` is the better `git submodule`. 96 | 97 | So here's how to do it: 98 | 99 | #### Initial setup/check-out 100 | This will create a `pd-lib-builder/` directory containing the full history of 101 | the pd-lib-builder repository up to its release `v0.5.0` 102 | 103 | ~~~sh 104 | git subtree add --prefix=pd-lib-builder/ https://github.com/pure-data/pd-lib-builder v0.5.0 105 | ~~~ 106 | 107 | This will automatically merge the `pd-lib-builder/` history into your current 108 | branch, so everything is ready to go. 109 | 110 | #### Cloning your repository with the subtree 111 | Nothing special, really. 112 | Just clone your repository as always: 113 | 114 | ~~~sh 115 | git clone https://git.example.org/pd/superbonk~.git 116 | ~~~ 117 | 118 | #### Updating the subtree 119 | Time passes and sooner or later you will find, that there is a shiny new 120 | pd-lib-builder with plenty of bugfixes and new features. 121 | To update your local copy to pd-lib-builder's current `master`, simply run: 122 | 123 | ~~~sh 124 | git subtree pull --prefix pd-lib-builder/ https://github.com/pure-data/pd-lib-builder master 125 | ~~~ 126 | 127 | #### Pulling the updated subtree into existing clones 128 | Again, nothing special. 129 | Just pull as always: 130 | 131 | ~~~sh 132 | git pull 133 | ~~~ 134 | 135 | 136 | #### Further reading 137 | More on the power of `git subtree` can be found online 138 | - https://medium.com/@v/git-subtrees-a-tutorial-6ff568381844 139 | - https://www.atlassian.com/blog/git/alternatives-to-git-submodule-git-subtree 140 | - ... 141 | 142 | ### ~~`git submodule`~~ [DISCOURAGED] 143 | 144 | 145 | #### Initial setup/check-out 146 | To add a new submodule to your repository, just run `git submodule add` and 147 | commit the changes: 148 | 149 | ~~~sh 150 | git submodule add https://github.com/pure-data/pd-lib-builder 151 | git commit .gitmodules pd-lib-builder/ -m "Added pd-lib-builder as git-submodule" 152 | ~~~ 153 | 154 | #### Cloning your repository with the submodule 155 | 156 | When doing a fresh clone of your repository, pass the `--recursive` option to 157 | automatically fetch all submodules: 158 | 159 | ~~~sh 160 | git clone --recursive https://git.example.org/pd/superbonk~.git 161 | ~~~ 162 | 163 | If you've cloned non-recursively, you can initialize and update the submodules 164 | manually: 165 | 166 | ~~~sh 167 | git submodule init 168 | git submodule update 169 | ~~~ 170 | 171 | #### Updating the submodule 172 | Submodules are usually fixed to a given commit in their repository. 173 | To update the `pd-lib-builder` submodule to the current `master` do something 174 | like: 175 | 176 | ~~~sh 177 | cd pd-lib-builder 178 | git checkout master 179 | git pull 180 | cd .. 181 | git status pd-lib-builder 182 | git commit pd-lib-builder -m "Updated pd-lib-builder to current master" 183 | ~~~ 184 | 185 | #### Pulling the updated submodule into existing clones 186 | After you have pushed the submodule updates in your repository, other clones of 187 | the repository can be updated as follows: 188 | 189 | ~~~sh 190 | git pull 191 | ~~~ 192 | 193 | The above will make your repository aware, that the submodule is out-of-sync. 194 | 195 | ~~~sh 196 | $ LANG=C git status pd-lib-builder 197 | On branch master 198 | Your branch is up to date with 'origin/master'. 199 | 200 | Changes not staged for commit: 201 | (use "git add ..." to update what will be committed) 202 | (use "git checkout -- ..." to discard changes in working directory) 203 | 204 | modified: pd-lib-builder (new commits) 205 | $ 206 | ~~~ 207 | 208 | In order to sync the submodule to the correct commit, run the following: 209 | 210 | ~~~sh 211 | git submodule update 212 | ~~~ 213 | 214 | #### Drawbacks 215 | `git submodule` has a number of drawbacks: 216 | - it requires special commands to synchronize the submodules, in addition to 217 | synching your repository. 218 | - you must make sure to use an URL for the submodule that is accessible to your 219 | potential users. e.g. using `git@github.com:pure-data/pd-lib-builder` is bad, 220 | because it requires everybody who wants to checkout your sources to have a 221 | github-account - even if they could checkout *your* repository anonymously. 222 | - submodules will be excluded from `git archive`. This means, that if you use a 223 | mainstream git provider (like Github, GitLab, Bitbucket,...) and make releases 224 | by creating a `git tag`, the automatically generated zipfiles with the sources 225 | will lack the submodule - and your users will not be able to compile your 226 | source code. 227 | 228 | In general, I would suggest to **avoid** `git submodule`, and instead use the 229 | better `git subtree` (above). 230 | 231 | -------------------------------------------------------------------------------- /pd/attr-1.pd: -------------------------------------------------------------------------------- 1 | #N canvas 114 127 697 455 12; 2 | #X obj 224 397 pyext simple ex3; 3 | #X obj 392 398 print A; 4 | #X msg 362 247 get tmp; 5 | #X msg 396 320 get _inlets; 6 | #X msg 395 344 set _inlets 4; 7 | #X msg 18 206 dir; 8 | #X msg 15 312 getattributes; 9 | #X msg 15 336 getmethods; 10 | #X msg 18 234 dir+; 11 | #X text 15 75 This demonstrates the usage of attributes. See the simple.py 12 | file.; 13 | #X text 260 212 access a class variable; 14 | #X text 395 301 try to get/set internal stuff; 15 | #X text 506 347 (NOT allowed!); 16 | #X text 14 288 get attributes and methods; 17 | #X text 51 205 Python module dict; 18 | #X text 59 235 Python class dict; 19 | #X text 17 128 All attribute-related methods dump eventual output to 20 | the attribute outlet (which is the right-most one); 21 | #X obj 16 13 cnv 15 650 40 empty empty py/pyext 10 22 0 24 -260818 22 | -1 0; 23 | #X text 235 16 Python script objects \, (C)2003-2005 Thomas Grill; 24 | #X text 235 32 http://grrrr.org/ext; 25 | #X msg 260 248 set tmp \$1; 26 | #X obj 259 230 nbx 5 14 -1e+037 1e+037 0 0 empty empty empty 0 -6 0 27 | 10 -262144 -1 -1 0 256; 28 | #X connect 0 1 1 0; 29 | #X connect 2 0 0 0; 30 | #X connect 3 0 0 0; 31 | #X connect 4 0 0 0; 32 | #X connect 5 0 0 0; 33 | #X connect 6 0 0 0; 34 | #X connect 7 0 0 0; 35 | #X connect 8 0 0 0; 36 | #X connect 20 0 0 0; 37 | #X connect 21 0 20 0; 38 | -------------------------------------------------------------------------------- /pd/buffer-1.pd: -------------------------------------------------------------------------------- 1 | #N canvas 123 58 716 496 12; 2 | #X obj 37 240 print; 3 | #X obj 107 241 print A; 4 | #X msg 30 139 reload; 5 | #N canvas 0 0 450 300 graph1 0; 6 | #X array array1 100 float 3; 7 | #A 0 0 0 0 0 0 0 0 0 0 0 0.00285713 0.00471427 0.00514284 0.00557141 8 | 0.00599998 0.00642855 0.00228571 0.00242857 0.00257143 -0.00542855 9 | -0.0114285 -0.0209999 -0.0377142 -0.0427142 -0.0479998 -0.067857 -0.0891426 10 | -0.104143 -0.108 -0.111857 -0.115714 -0.119571 -0.123428 -0.127285 11 | -0.131142 -0.125 -0.128571 -0.132143 -0.127571 -0.122571 -0.12 -0.117142 12 | -0.114 -0.110571 -0.106857 -0.102857 -0.0985711 -0.0872856 -0.0754282 13 | -0.0629998 -0.0499999 -0.0437141 -0.0371427 -0.0227142 -0.00771428 14 | 0.00785714 0.0239999 0.0298571 0.0359047 0.0421427 0.068571 0.0915 15 | 0.132857 0.161999 0.146285 0.134642 0.122571 0.105285 0.0582855 0.00985713 16 | 0.00999999 0.0101428 -0.0411427 -0.0417142 -0.0739998 -0.0749998 -0.0759998 17 | -0.132 -0.133714 -0.12533 -0.113684 -0.101707 -0.089398 -0.0767592 18 | -0.0637893 -0.0504886 -0.036857 -0.0152967 0.0067692 0.0293405 0.0524174 19 | 0.0759997 0.100088 0.12468 0.14978 0.175384 0.201493 0.228109 0.25523 20 | 0.325284; 21 | #X coords 0 1 99 -1 200 140 1; 22 | #X restore 421 156 graph; 23 | #N canvas 0 0 450 300 graph2 0; 24 | #X array array2 100 float 3; 25 | #A 0 0 0 0 0 0 0 0.0285712 0.0428568 0.0571424 0.12857 0.171427 0.185712 26 | 0.199998 0.242854 0.342853 0.342853 0.364281 0.41428 0.41428 0.357138 27 | 0.314282 0.278568 0.314282 0.328567 0.342853 0.378567 0.41428 0.457137 28 | 0.49285 0.528564 0.54285 0.557135 0.599992 0.614277 0.671419 0.671419 29 | 0.599992 0.614276 0.59999 0.585705 0.571419 0.499991 0.482135 0.464278 30 | 0.446421 0.428564 0.408564 0.388565 0.368565 0.348565 0.328566 0.305709 31 | 0.282852 0.259995 0.237138 0.214282 0.194282 0.174282 0.154282 0.134283 32 | 0.114283 0.0914263 0.0685695 0.0457127 0.0228559 8.84384e-007 0.0142864 33 | 0.0285719 0.0428574 0.0571429 0.0714284 0.096428 0.121428 0.146427 34 | 0.171427 0.181631 0.191835 0.202039 0.212243 0.222446 0.23265 0.242854 35 | 0.25714 0.271425 0.285711 0.299996 0.314282 0.33571 0.357138 0.385709 36 | 0.407138 0.428566 0.457137 0.457137 0.12857 0.514279 0.557135 0.604754 37 | 0.652372 0.814274; 38 | #X coords 0 1 99 -1 200 140 1; 39 | #X restore 421 305 graph; 40 | #N canvas 0 0 450 300 graph3 0; 41 | #X array array3 100 float 2; 42 | #X coords 0 1 99 -1 200 140 1; 43 | #X restore 65 301 graph; 44 | #X obj 36 199 py buffer @detach 1; 45 | #X msg 200 123 mul array3 array1 array2; 46 | #X msg 200 145 add array3 array1 array2; 47 | #X obj 16 13 cnv 15 650 40 empty empty py/pyext 10 22 0 24 -260818 48 | -1 0; 49 | #X text 235 16 Python script objects \, (C)2003-2005 Thomas Grill; 50 | #X text 235 32 http://grrrr.org/ext; 51 | #X text 17 67 This demonstrates the usage of buffers. See the buffer.py 52 | script.; 53 | #X msg 201 172 fadein array1; 54 | #X msg 199 196 neg array2; 55 | #X connect 2 0 6 0; 56 | #X connect 6 0 0 0; 57 | #X connect 6 1 1 0; 58 | #X connect 7 0 6 1; 59 | #X connect 8 0 6 1; 60 | #X connect 13 0 6 1; 61 | #X connect 14 0 6 1; 62 | -------------------------------------------------------------------------------- /pd/buffer-2.pd: -------------------------------------------------------------------------------- 1 | #N canvas 608 159 694 442 12; 2 | #X obj 16 13 cnv 15 650 40 empty empty py/pyext 10 22 0 24 -260818 3 | -1 0; 4 | #X text 235 16 Python script objects \, (C)2003-2005 Thomas Grill; 5 | #X text 235 32 http://grrrr.org/ext; 6 | #X text 17 67 This demonstrates how to resize buffers; 7 | #N canvas 0 0 450 300 graph1 0; 8 | #X array array1 42 float 3; 9 | #A 0 0 0 0 0 0.0285715 0.0857145 0.128572 0.171429 0.257144 0.314287 10 | 0.385715 0.385715 0.385715 0.385715 0.385715 0.37143 0.342858 0.314287 11 | 0.228572 0.128572 0.0428573 -0.0857145 -0.200001 -0.285715 -0.328572 12 | -0.357144 -0.400001 -0.428573 -0.428573 -0.457144 -0.457144 -0.457144 13 | -0.457144 -0.457144 -0.457144 -0.457144 -0.457144 -0.457144 -0.414287 14 | -0.342858 -0.314287 1.02445e-007; 15 | #X coords 0 1 42 -1 200 140 1; 16 | #X restore 413 259 graph; 17 | #X msg 40 148 symbol array1; 18 | #X obj 40 194 py pyext.Buffer @py 1; 19 | #X obj 143 238 nbx 5 14 0 100 0 1 empty empty empty 0 -6 0 10 -260818 20 | -1 -1 42 256; 21 | #X obj 143 260 t b f; 22 | #X obj 39 287 pym 2 resize @py 1; 23 | #X obj 40 120 loadbang; 24 | #X text 37 310 returns buffer object; 25 | #X obj 41 361 py .len; 26 | #X text 111 361 call __builtin__.len; 27 | #X text 222 192 create Buffer object; 28 | #X obj 41 394 nbx 5 14 -1e+037 1e+037 0 0 empty empty empty 0 -6 0 29 | 10 -261681 -1 -1 0 256; 30 | #X connect 5 0 6 1; 31 | #X connect 6 0 9 1; 32 | #X connect 7 0 8 0; 33 | #X connect 8 0 9 0; 34 | #X connect 8 1 9 2; 35 | #X connect 9 0 12 1; 36 | #X connect 10 0 5 0; 37 | #X connect 12 0 15 0; 38 | -------------------------------------------------------------------------------- /pd/builtins-1.pd: -------------------------------------------------------------------------------- 1 | #N canvas 602 394 714 429 12; 2 | #X obj 36 241 py .range @py 1; 3 | #X floatatom 35 356 5 0 0 0 - - -; 4 | #X obj 35 323 py .sum; 5 | #X obj 16 13 cnv 15 650 40 empty empty py/pyext 10 22 0 24 -260818 6 | -1 0; 7 | #X text 235 32 http://grrrr.org/ext; 8 | #X obj 36 159 nbx 5 14 -1e+037 1e+037 0 1 empty empty min 0 -6 0 10 9 | -262131 -1 -1 51 256; 10 | #X obj 95 159 nbx 5 14 -1e+037 1e+037 0 1 empty empty max 0 -6 0 10 11 | -262131 -1 -1 131 256; 12 | #X obj 154 159 nbx 5 14 1 100000 1 1 empty empty step 0 -6 0 10 -262131 13 | -1 -1 6.61169 256; 14 | #N canvas 0 0 466 316 pak3 0; 15 | #X obj 22 25 inlet; 16 | #X obj 81 26 inlet; 17 | #X obj 136 26 inlet; 18 | #X obj 36 158 outlet; 19 | #X obj 36 123 pack 0 0 0; 20 | #X obj 78 71 t b f; 21 | #X obj 133 73 t b f; 22 | #X obj 208 46 loadbang; 23 | #X obj 208 73 1; 24 | #X obj 23 53 int; 25 | #X obj 135 51 int; 26 | #X obj 80 50 int; 27 | #X connect 0 0 9 0; 28 | #X connect 1 0 11 0; 29 | #X connect 2 0 10 0; 30 | #X connect 4 0 3 0; 31 | #X connect 5 0 4 0; 32 | #X connect 5 1 4 1; 33 | #X connect 6 0 4 0; 34 | #X connect 6 1 4 2; 35 | #X connect 7 0 8 0; 36 | #X connect 8 0 4 2; 37 | #X connect 9 0 4 0; 38 | #X connect 10 0 6 0; 39 | #X connect 11 0 5 0; 40 | #X restore 36 190 pd pak3; 41 | #X text 169 239 construct a Python list; 42 | #X text 78 282 Python object pointer is propagated to next object; 43 | #X text 106 320 calculate sum over list elements; 44 | #X text 21 73 Py can use built-in Python functions; 45 | #X text 21 97 A . preceding the function name searches for the function 46 | in either the pyext module or in __builtins__; 47 | #X text 235 16 Python script objects \, (C)2003-2006 Thomas Grill; 48 | #X connect 0 0 2 1; 49 | #X connect 2 0 1 0; 50 | #X connect 5 0 8 0; 51 | #X connect 6 0 8 1; 52 | #X connect 7 0 8 2; 53 | #X connect 8 0 0 1; 54 | -------------------------------------------------------------------------------- /pd/methods-1.pd: -------------------------------------------------------------------------------- 1 | #N canvas 540 469 734 369 12; 2 | #X obj 16 13 cnv 15 650 40 empty empty py/pyext 10 22 0 24 -260818 3 | -1 0; 4 | #X text 235 32 http://grrrr.org/ext; 5 | #X symbolatom 21 139 10 0 0 0 - - -; 6 | #X symbolatom 25 298 10 0 0 0 - - -; 7 | #X obj 22 179 py .str @py 1; 8 | #X text 145 170 convert the symbol to a Python string; 9 | #X text 35 216 pass it as a true Python object; 10 | #X symbolatom 364 295 10 0 0 0 - - -; 11 | #X text 462 269 use module function; 12 | #X text 23 119 enter some text; 13 | #X text 145 187 using the built-in str function; 14 | #X obj 25 252 pym swapcase; 15 | #X text 63 270 use swapcase method; 16 | #X text 235 16 Python script objects \, (C)2003-2006 Thomas Grill; 17 | #X text 21 73 Py can act on Python objects in an object-oriented manner 18 | ; 19 | #X obj 363 250 py string.capwords; 20 | #X connect 2 0 4 1; 21 | #X connect 4 0 11 1; 22 | #X connect 4 0 15 1; 23 | #X connect 11 0 3 0; 24 | #X connect 15 0 7 0; 25 | -------------------------------------------------------------------------------- /pd/methods-2.pd: -------------------------------------------------------------------------------- 1 | #N canvas 540 469 746 351 12; 2 | #X obj 16 13 cnv 15 650 40 empty empty py/pyext 10 22 0 24 -260818 3 | -1 0; 4 | #X text 235 32 http://grrrr.org/ext; 5 | #X symbolatom 21 139 10 0 0 0 - #0-t -; 6 | #X text 23 119 enter some text; 7 | #X obj 25 252 pym 2 *; 8 | #X obj 213 183 t b f; 9 | #X text 215 117 multiply it!; 10 | #X symbolatom 25 307 80 0 0 0 - - -; 11 | #X obj 214 139 nbx 5 14 1 100 0 1 empty empty empty 0 -6 0 10 -262131 12 | -1 -1 6 256; 13 | #N canvas 0 22 462 312 init 0; 14 | #X obj 61 116 s \$0-t; 15 | #X obj 64 44 loadbang; 16 | #X obj 64 81 symbol a; 17 | #X connect 1 0 2 0; 18 | #X connect 2 0 0 0; 19 | #X restore 606 127 pd init; 20 | #X obj 213 159 int; 21 | #X text 21 73 Py can act on Python objects in an object-oriented manner 22 | ; 23 | #X text 20 285 repeated text; 24 | #X text 100 252 method * takes 2 args; 25 | #X text 235 16 Python script objects \, (C)2003-2008 Thomas Grill; 26 | #X connect 2 0 4 1; 27 | #X connect 4 0 7 0; 28 | #X connect 5 0 4 0; 29 | #X connect 5 1 4 2; 30 | #X connect 8 0 10 0; 31 | #X connect 10 0 5 0; 32 | -------------------------------------------------------------------------------- /pd/pak.pd: -------------------------------------------------------------------------------- 1 | #N canvas 463 293 282 232 12; 2 | #X obj 17 32 nbx 5 14 -1e+037 1e+037 0 0 empty empty empty 0 -6 0 10 3 | -262144 -1 -1 47 256; 4 | #X obj 34 52 nbx 5 14 -1e+037 1e+037 0 0 empty empty empty 0 -6 0 10 5 | -262144 -1 -1 182 256; 6 | #X obj 56 68 nbx 5 14 -1e+037 1e+037 0 0 empty empty empty 0 -6 0 10 7 | -262144 -1 -1 86 256; 8 | #X obj 68 88 nbx 5 14 -1e+037 1e+037 0 0 empty empty empty 0 -6 0 10 9 | -262144 -1 -1 31 256; 10 | #X obj 118 29 nbx 5 14 -1e+037 1e+037 0 0 empty empty empty 0 -6 0 11 | 10 -262144 -1 -1 117 256; 12 | #X obj 135 49 nbx 5 14 -1e+037 1e+037 0 0 empty empty empty 0 -6 0 13 | 10 -262144 -1 -1 0 256; 14 | #X obj 157 65 nbx 5 14 -1e+037 1e+037 0 0 empty empty empty 0 -6 0 15 | 10 -262144 -1 -1 86 256; 16 | #X obj 169 85 nbx 5 14 -1e+037 1e+037 0 0 empty empty empty 0 -6 0 17 | 10 -262144 -1 -1 0 256; 18 | #X obj 36 168 print; 19 | #X obj 37 129 pyx. pak 8; 20 | #X connect 0 0 9 1; 21 | #X connect 1 0 9 2; 22 | #X connect 2 0 9 3; 23 | #X connect 3 0 9 4; 24 | #X connect 4 0 9 5; 25 | #X connect 5 0 9 6; 26 | #X connect 6 0 9 7; 27 | #X connect 7 0 9 8; 28 | #X connect 9 0 8 0; 29 | -------------------------------------------------------------------------------- /pd/script-1.pd: -------------------------------------------------------------------------------- 1 | #N canvas 297 17 700 542 12; 2 | #X obj 39 278 print; 3 | #X obj 345 251 print; 4 | #X msg 499 149 freakhole; 5 | #X msg 148 149 list H e l l o; 6 | #X msg 166 175 Hello friend; 7 | #X obj 42 460 print; 8 | #X msg 102 367 0 1 2 3 4; 9 | #X msg 197 367 5 67 3; 10 | #X obj 350 456 print; 11 | #X obj 316 358 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1 12 | -1; 13 | #X obj 515 455 print; 14 | #X obj 484 381 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1 15 | -1; 16 | #X msg 188 204 1 3; 17 | #X msg 345 155 help; 18 | #X msg 350 320 set ret1; 19 | #X msg 369 344 set ret2; 20 | #X text 424 319 functions can be set; 21 | #X msg 421 120 somewhere_past_mars; 22 | #X text 152 101 reload with new arguments; 23 | #X msg 40 104 reload 1 2 3; 24 | #X text 21 69 This demonstrates simple scripting. See the script.py 25 | file.; 26 | #X obj 39 241 py script strcat; 27 | #X obj 43 424 py script addall; 28 | #X obj 350 420 py script; 29 | #X obj 346 204 py script strlen; 30 | #X msg 21 159 dir; 31 | #X obj 146 279 print A; 32 | #X msg 58 160 dir+; 33 | #X obj 16 13 cnv 15 630 40 empty empty py/pyext 10 22 0 24 -260818 34 | -1 0; 35 | #X text 235 16 Python script objects \, (C)2003-2005 Thomas Grill; 36 | #X text 235 32 http://grrrr.org/ext; 37 | #X msg 509 178 a b c; 38 | #X text 556 181 too many args; 39 | #X text 505 372 just trigger without arguments; 40 | #X msg 386 371 set ret4; 41 | #X obj 516 419 py script ret3; 42 | #X connect 2 0 24 1; 43 | #X connect 3 0 21 1; 44 | #X connect 4 0 21 1; 45 | #X connect 6 0 22 1; 46 | #X connect 7 0 22 1; 47 | #X connect 9 0 23 0; 48 | #X connect 11 0 35 0; 49 | #X connect 12 0 21 1; 50 | #X connect 13 0 24 0; 51 | #X connect 14 0 23 0; 52 | #X connect 15 0 23 0; 53 | #X connect 17 0 24 1; 54 | #X connect 19 0 21 0; 55 | #X connect 21 0 0 0; 56 | #X connect 21 1 26 0; 57 | #X connect 22 0 5 0; 58 | #X connect 23 0 8 0; 59 | #X connect 24 0 1 0; 60 | #X connect 25 0 21 0; 61 | #X connect 27 0 21 0; 62 | #X connect 31 0 24 1; 63 | #X connect 34 0 23 0; 64 | #X connect 35 0 10 0; 65 | -------------------------------------------------------------------------------- /pd/sendrecv-1.pd: -------------------------------------------------------------------------------- 1 | #N canvas 145 126 658 333 12; 2 | #X msg 125 81 reload mi ma; 3 | #X floatatom 48 238 5 0 0 0 - - -; 4 | #X floatatom 297 239 5 0 0 0 - - -; 5 | #X obj 297 263 s mi; 6 | #X floatatom 143 265 5 0 0 0 - - -; 7 | #X floatatom 382 267 5 0 0 0 - - -; 8 | #X obj 382 240 r ma; 9 | #X obj 48 262 s he; 10 | #X obj 143 238 r hu; 11 | #X text 247 81 reload with different args; 12 | #X msg 20 82 help; 13 | #X msg 19 114 doc; 14 | #X msg 58 114 doc+; 15 | #X obj 49 165 pyext sendrecv ex1 he hu; 16 | #X text 30 218 scroll here; 17 | #X text 292 219 or here; 18 | #X obj 16 13 cnv 15 600 40 empty empty py/pyext 10 22 0 24 -260818 19 | -1 0; 20 | #X msg 202 128 bind; 21 | #X msg 249 129 unbind; 22 | #X text 213 16 Python script objects \, (C)2003-2005 Thomas Grill; 23 | #X text 213 32 http://grrrr.org/ext; 24 | #X connect 0 0 13 0; 25 | #X connect 1 0 7 0; 26 | #X connect 2 0 3 0; 27 | #X connect 6 0 5 0; 28 | #X connect 8 0 4 0; 29 | #X connect 10 0 13 0; 30 | #X connect 11 0 13 0; 31 | #X connect 12 0 13 0; 32 | #X connect 17 0 13 1; 33 | #X connect 18 0 13 1; 34 | -------------------------------------------------------------------------------- /pd/sendrecv-2.pd: -------------------------------------------------------------------------------- 1 | #N canvas 133 322 647 189 12; 2 | #X obj 152 98 pyext sendrecv ex2 huha; 3 | #X floatatom 152 128 5 0 0 0 - - -; 4 | #X floatatom 33 96 5 0 0 0 - - -; 5 | #X obj 32 123 s huha; 6 | #X text 20 77 scroll here; 7 | #X obj 16 13 cnv 15 600 40 empty empty py/pyext 10 22 0 24 -260818 8 | -1 0; 9 | #X text 213 16 Python script objects \, (C)2003-2005 Thomas Grill; 10 | #X text 213 32 http://grrrr.org/ext; 11 | #X connect 0 0 1 0; 12 | #X connect 2 0 3 0; 13 | -------------------------------------------------------------------------------- /pd/sendrecv-3.pd: -------------------------------------------------------------------------------- 1 | #N canvas 294 237 648 327 12; 2 | #X obj 410 265 pyext sendrecv ex3 @detach 1; 3 | #X obj 611 236 bng 25 250 50 0 empty ugh empty 0 -6 0 8 -258699 -1 4 | -1; 5 | #X obj 16 13 cnv 15 600 40 empty empty py/pyext 10 22 0 24 -260818 6 | -1 0; 7 | #X text 213 16 Python script objects \, (C)2003-2005 Thomas Grill; 8 | #X text 213 32 http://grrrr.org/ext; 9 | #X text 414 213 quite likely not to work...; 10 | #X connect 1 0 0 1; 11 | -------------------------------------------------------------------------------- /pd/sig-1.pd: -------------------------------------------------------------------------------- 1 | #N canvas 52 147 663 315 12; 2 | #X obj 56 234 dac~; 3 | #X msg 523 211 \; pd dsp 1; 4 | #X obj 524 184 loadbang; 5 | #X obj 194 114 hsl 128 15 0.01 1 1 1 empty empty gain -2 -6 0 8 -225271 6 | -1 -1 11200 1; 7 | #X obj 89 116 noise~; 8 | #X msg 21 116 reload; 9 | #X obj 16 13 cnv 15 600 40 empty empty py/pyext 10 22 0 24 -260818 10 | -1 0; 11 | #X text 213 32 http://grrrr.org/ext; 12 | #X text 213 16 Python script objects \, (C)2003-2005 Thomas Grill; 13 | #X text 17 66 This demonstrates signal support. See the sig.py file. 14 | ; 15 | #X obj 191 131 nbx 5 14 0.001 1 1 0 empty empty empty 0 -6 0 10 -225271 16 | -1 -1 0.58047 256; 17 | #X msg 192 148 set gain \$1; 18 | #X text 123 202 message inlets \, outlets; 19 | #X text 123 217 signal inlets \, outlets; 20 | #X obj 67 181 pyext~ 0 0 1 1 sig gain2; 21 | #X connect 2 0 1 0; 22 | #X connect 3 0 10 0; 23 | #X connect 4 0 14 0; 24 | #X connect 5 0 14 0; 25 | #X connect 10 0 11 0; 26 | #X connect 11 0 14 0; 27 | #X connect 14 0 0 0; 28 | #X connect 14 0 0 1; 29 | -------------------------------------------------------------------------------- /pd/sig-2.pd: -------------------------------------------------------------------------------- 1 | #N canvas 56 67 663 315 12; 2 | #X obj 121 246 dac~; 3 | #X msg 523 211 \; pd dsp 1; 4 | #X obj 524 184 loadbang; 5 | #X obj 266 134 hsl 128 15 0 1 0 1 empty empty pan -2 -6 0 8 -225271 6 | -1 -1 4700 1; 7 | #X obj 100 127 noise~; 8 | #X msg 31 127 reload; 9 | #X obj 16 13 cnv 15 600 40 empty empty py/pyext 10 22 0 24 -260818 10 | -1 0; 11 | #X text 213 32 http://grrrr.org/ext; 12 | #X text 213 16 Python script objects \, (C)2003-2005 Thomas Grill; 13 | #X text 17 66 This demonstrates signal support. See the sig.py file. 14 | ; 15 | #X obj 92 179 pyext~ 1 0 1 2 sig pan; 16 | #X text 185 202 message inlets \, outlets; 17 | #X text 183 218 signal inlets \, outlets; 18 | #X connect 2 0 1 0; 19 | #X connect 3 0 10 1; 20 | #X connect 4 0 10 0; 21 | #X connect 5 0 10 0; 22 | #X connect 10 0 0 0; 23 | #X connect 10 1 0 1; 24 | -------------------------------------------------------------------------------- /pd/simple-1.pd: -------------------------------------------------------------------------------- 1 | #N canvas 156 192 682 409 12; 2 | #X obj 53 123 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1 3 | -1; 4 | #X floatatom 52 155 5 0 0 0 - - -; 5 | #X text 388 337 watch the console output!; 6 | #X msg 52 186 2 3 4; 7 | #X msg 277 131 ho; 8 | #X msg 233 155 lets; 9 | #X msg 283 190 go; 10 | #X msg 212 214 !!!; 11 | #X msg 205 113 hey; 12 | #X obj 183 301 pyext simple ex1; 13 | #X msg 434 114 onearg 123; 14 | #X msg 456 167 threeargs 9 8 7; 15 | #X msg 463 196 varargs 8 4 2 1; 16 | #X msg 447 140 twoargs 41 15; 17 | #X msg 453 239 twoargs 1 2 3; 18 | #X msg 71 299 help; 19 | #X text 16 69 This demonstrates message handling. See the simple.py 20 | file.; 21 | #X text 232 322 file class; 22 | #X msg 70 324 doc; 23 | #X msg 106 325 doc+; 24 | #X obj 16 13 cnv 15 600 40 empty empty py/pyext 10 22 0 24 -260818 25 | -1 0; 26 | #X text 213 32 http://grrrr.org/ext; 27 | #X text 213 16 Python script objects \, (C)2003-2005 Thomas Grill; 28 | #X msg 41 246 reload; 29 | #X connect 0 0 9 1; 30 | #X connect 1 0 9 1; 31 | #X connect 3 0 9 1; 32 | #X connect 4 0 9 2; 33 | #X connect 5 0 9 2; 34 | #X connect 6 0 9 2; 35 | #X connect 7 0 9 2; 36 | #X connect 8 0 9 2; 37 | #X connect 10 0 9 3; 38 | #X connect 11 0 9 3; 39 | #X connect 12 0 9 3; 40 | #X connect 13 0 9 3; 41 | #X connect 14 0 9 3; 42 | #X connect 15 0 9 0; 43 | #X connect 18 0 9 0; 44 | #X connect 19 0 9 0; 45 | #X connect 23 0 9 0; 46 | -------------------------------------------------------------------------------- /pd/simple-2.pd: -------------------------------------------------------------------------------- 1 | #N canvas 570 275 788 398 12; 2 | #X floatatom 202 113 5 0 0 0 - - -; 3 | #X text 338 286 watch the console output!; 4 | #X msg 20 115 help; 5 | #X text 16 69 This demonstrates message handling. See the simple.py 6 | file.; 7 | #X msg 19 140 doc; 8 | #X msg 55 141 doc+; 9 | #X floatatom 259 113 5 0 0 0 - - -; 10 | #X msg 169 235 msg 2; 11 | #X obj 123 289 pyext simple ex2; 12 | #X floatatom 123 334 5 0 0 0 - - -; 13 | #X floatatom 240 335 5 0 0 0 - - -; 14 | #X msg 100 204 msg 1 3; 15 | #X msg 234 220 msg a b; 16 | #X msg 120 174 hello; 17 | #X msg 193 180 hello; 18 | #X msg 266 179 msg; 19 | #X obj 16 13 cnv 15 600 40 empty empty py/pyext 10 22 0 24 -260818 20 | -1 0; 21 | #X text 213 16 Python script objects \, (C)2003-2005 Thomas Grill; 22 | #X text 213 32 http://grrrr.org/ext; 23 | #X msg 333 186 whoopie a b c; 24 | #X connect 0 0 8 1; 25 | #X connect 2 0 8 0; 26 | #X connect 4 0 8 0; 27 | #X connect 5 0 8 0; 28 | #X connect 6 0 8 2; 29 | #X connect 7 0 8 2; 30 | #X connect 8 0 9 0; 31 | #X connect 8 1 10 0; 32 | #X connect 11 0 8 1; 33 | #X connect 12 0 8 3; 34 | #X connect 13 0 8 1; 35 | #X connect 14 0 8 3; 36 | #X connect 15 0 8 2; 37 | #X connect 19 0 8 3; 38 | -------------------------------------------------------------------------------- /pd/simple-3.pd: -------------------------------------------------------------------------------- 1 | #N canvas 136 275 657 369 12; 2 | #X msg 73 266 help; 3 | #X text 17 80 This demonstrates message handling. See the simple.py 4 | file.; 5 | #X msg 72 291 doc; 6 | #X msg 108 292 doc+; 7 | #X floatatom 258 305 5 0 0 0 - - -; 8 | #X floatatom 316 119 5 0 0 0 - - -; 9 | #X floatatom 399 119 5 0 0 0 - - -; 10 | #X msg 24 182 reload.; 11 | #X msg 24 210 reload -10; 12 | #X text 95 181 reload script and keep arguments; 13 | #X text 113 211 reload script with new arguments; 14 | #X text 281 140 triggers; 15 | #X text 310 304 result; 16 | #X text 410 140 sets argument; 17 | #X obj 16 13 cnv 15 600 40 empty empty py/pyext 10 22 0 24 -260818 18 | -1 0; 19 | #X text 213 16 Python script objects \, (C)2003-2005 Thomas Grill; 20 | #X text 213 32 http://grrrr.org/ext; 21 | #X obj 210 266 pyext simple.ex3 1; 22 | #X text 369 258 the name can also be given in dotted Python style; 23 | #X connect 0 0 17 0; 24 | #X connect 2 0 17 0; 25 | #X connect 3 0 17 0; 26 | #X connect 5 0 17 1; 27 | #X connect 6 0 17 2; 28 | #X connect 7 0 17 0; 29 | #X connect 8 0 17 0; 30 | #X connect 17 0 4 0; 31 | -------------------------------------------------------------------------------- /pd/tcltk.pd: -------------------------------------------------------------------------------- 1 | #N canvas 156 192 614 333 12; 2 | #X obj 328 118 bng 25 250 50 0 empty empty empty 0 -6 0 8 -258699 -1 3 | -1; 4 | #X msg 94 128 help; 5 | #X msg 139 127 doc; 6 | #X text 14 49 This demonstrates a tcl/tk dialog. See the tcltk.py file. 7 | ; 8 | #X text 10 263 Note: When used concurrently with audio \, you will 9 | notice clicks. This Tk window is NOT called over a net socket \, like 10 | PD is; 11 | #X obj 206 169 pyext tcltk myapp; 12 | #X obj 206 200 print tcltk; 13 | #X text 16 15 py/pyext - Python script objects \, (C)2002-2005 Thomas 14 | Grill; 15 | #X connect 0 0 5 1; 16 | #X connect 1 0 5 0; 17 | #X connect 2 0 5 0; 18 | #X connect 5 0 6 0; 19 | -------------------------------------------------------------------------------- /pd/thread-1.pd: -------------------------------------------------------------------------------- 1 | #N canvas 135 178 652 409 12; 2 | #X msg 35 292 help; 3 | #X msg 34 317 doc; 4 | #X msg 70 318 doc+; 5 | #X floatatom 142 340 5 0 0 0 - - -; 6 | #X text 17 66 This demonstrates threading. See the threads.py file. 7 | ; 8 | #X obj 137 243 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 2 9 | 2; 10 | #X msg 137 263 detach \$1; 11 | #X floatatom 250 341 5 0 0 0 - - -; 12 | #X obj 272 150 bng 15 250 50 0 empty empty empty 0 -6 0 8 -258699 -1 13 | -1; 14 | #X obj 143 154 bng 15 250 50 0 empty empty empty 0 -6 0 8 -258699 -1 15 | -1; 16 | #X obj 143 181 t b b b; 17 | #X obj 272 177 t b b b; 18 | #X obj 286 206 1; 19 | #X obj 157 208 0; 20 | #X text 84 114 without threads; 21 | #X text 248 112 with threads; 22 | #X text 175 362 watch that!; 23 | #X msg 421 264 stop; 24 | #X text 391 243 you can even stop it; 25 | #X obj 142 306 pyext threads ex1; 26 | #X text 90 128 - blocking!! -; 27 | #X obj 16 13 cnv 15 600 40 empty empty py/pyext 10 22 0 24 -260818 28 | -1 0; 29 | #X obj 407 150 bng 15 250 50 0 empty empty empty 0 -6 0 8 -258699 -1 30 | -1; 31 | #X obj 407 177 t b b b; 32 | #X text 383 112 with threads; 33 | #X text 249 129 non-blocking; 34 | #X text 384 129 parallel; 35 | #X obj 445 205 2; 36 | #X text 213 32 http://grrrr.org/ext; 37 | #X text 213 16 Python script objects \, (C)2003-2005 Thomas Grill; 38 | #X msg 44 195 reload; 39 | #X connect 0 0 19 0; 40 | #X connect 1 0 19 0; 41 | #X connect 2 0 19 0; 42 | #X connect 5 0 6 0; 43 | #X connect 6 0 19 0; 44 | #X connect 8 0 11 0; 45 | #X connect 9 0 10 0; 46 | #X connect 10 0 19 1; 47 | #X connect 10 1 19 2; 48 | #X connect 10 2 13 0; 49 | #X connect 11 0 19 1; 50 | #X connect 11 1 19 2; 51 | #X connect 11 2 12 0; 52 | #X connect 12 0 5 0; 53 | #X connect 13 0 5 0; 54 | #X connect 17 0 19 0; 55 | #X connect 19 0 3 0; 56 | #X connect 19 1 7 0; 57 | #X connect 22 0 23 0; 58 | #X connect 23 0 19 1; 59 | #X connect 23 1 19 2; 60 | #X connect 23 2 27 0; 61 | #X connect 27 0 5 0; 62 | #X connect 30 0 19 0; 63 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | py/pyext - python script objects for PD and Max/MSP 2 | 3 | This fork by SOPI research group (https://sopi.aalto.fi) implements support for Python 3 as well as Conda Python installations. 4 | 5 | It was developed for use with GANSpaceSynth (https://github.com/SopiMlab/GANSpaceSynth) and our Deep Learning with Audio course (https://github.com/SopiMlab/DeepLearningWithAudio). 6 | 7 | See also the original repository: https://github.com/grrrr/py 8 | 9 | Copyright (c)2002-2020 Thomas Grill (gr@grrrr.org) 10 | For information on usage and redistribution, and for a DISCLAIMER OF ALL 11 | WARRANTIES, see the file, "license.txt," in this distribution. 12 | 13 | ---------------------------------------------------------------------------- 14 | 15 | You need to have Python installed on your system for the py/pyext external to work. 16 | We recommend using Conda (https://conda.io). 17 | 18 | SopiMlab/py has been tested with Python 3.7-3.8 and 2.7. 19 | 20 | Check out the sample patches and scripts 21 | 22 | ---------------------------------------------------------------------------- 23 | 24 | INSTALLATION 25 | ============ 26 | 27 | We have detailed tutorials for setting up GANSpaceSynth with Pd in a Conda environment: 28 | https://github.com/SopiMlab/DeepLearningWithAudio/tree/master/utilities/pyext-setup 29 | 30 | Known issues: 31 | 32 | - We haven't been able to build on Windows 33 | - Max/MSP is untested 34 | 35 | Contributions are welcome! 36 | 37 | ---------------------------------------------------------------------------- 38 | 39 | DESCRIPTION 40 | =========== 41 | 42 | With the py object you can load python modules and execute the functions therein. 43 | With the pyext you can use python classes to represent full-featured pd/Max message objects. 44 | Multithreading (detached methods) is supported for both objects. 45 | You can send messages to named objects or receive (with pyext) with Python methods. 46 | 47 | ---------------------------------------------------------------------------- 48 | 49 | BUILDING from source 50 | ==================== 51 | 52 | You will need the flext C++ layer for PD and Max/MSP externals to compile this. 53 | See https://github.com/SopiMlab/flext 54 | 55 | TODO: Document our changes to the build configuration. For now, the build.py script from https://github.com/SopiMlab/DeepLearningWithAudio/blob/master/utilities/pyext-setup/build.py can give you some hints 56 | 57 | Pure data - any platform supporting gcc-compatible compilers 58 | ------------------------------------------------------------ 59 | 60 | The pd-lib-builder project (https://github.com/pure-data/pd-lib-builder) is used to compile the project. 61 | A git subtree of this project is already present. 62 | 63 | The compilation is done using the GNU make tool and it will need additional information about the location of the flext source files, and possibly, Pure data, if a specific version should be used. 64 | 65 | This could be an example: 66 | make CPPFLAGS="-I ../flext/source" PDDIR="../../pure-data" 67 | 68 | For OS X, further flags can be needed: 69 | CFLAGS="-mmacosx-version-min=10.9" LDFLAGS="-mmacosx-version-min=10.9" 70 | 71 | 72 | pd/Max - Windows - Microsoft Visual C, Borland C++, MinGW: 73 | ---------------------------------------------------------- 74 | Please see further setup instructions at https://github.com/grrrr/py 75 | 76 | pd/Max - OSX/Linux - GCC: 77 | ------------------------- 78 | Please see further setup instructions at https://github.com/grrrr/py 79 | ---------------------------------------------------------------------------- 80 | Please see further setup instructions at https://github.com/grrrr/py 81 | 82 | -------------------------------------------------------------------------------- /scripts/buffer.py: -------------------------------------------------------------------------------- 1 | # py/pyext - python script objects for PD and MaxMSP 2 | # 3 | # Copyright (c) 2002-2012 Thomas Grill (gr@grrrr.org) 4 | # For information on usage and redistribution, and for a DISCLAIMER OF ALL 5 | # WARRANTIES, see the file, "license.txt," in this distribution. 6 | # 7 | 8 | """This is an example script for the py/pyext object's buffer support. 9 | 10 | Pd/Max buffers can be mapped to Python arrays. 11 | Currently, there are three implementations: 12 | Numeric, numarray and numpy (for all of them see http://numeric.scipy.org) 13 | """ 14 | 15 | from __future__ import print_function 16 | 17 | import sys 18 | 19 | try: 20 | import pyext 21 | except: 22 | print("ERROR: This script must be loaded by the PD/Max py/pyext external") 23 | 24 | try: 25 | # numpy is assumed here... numeric and numarray are considered deprecated 26 | import numpy as N 27 | except: 28 | print("Failed importing numpy module:",sys.exc_value) 29 | 30 | def mul(*args): 31 | # create buffer objects 32 | # as long as these variables live the underlying buffers are locked 33 | c = pyext.Buffer(args[0]) 34 | a = pyext.Buffer(args[1]) 35 | b = pyext.Buffer(args[2]) 36 | 37 | commonlen = min(len(a),len(b),len(c)) 38 | 39 | # slicing causes Python arrays (mapped to buffers) to be created 40 | # note the c[:] - to assign contents you must assign to a slice of the buffer 41 | 42 | c[:commonlen] = a[:commonlen]*b[:commonlen] 43 | 44 | def add(*args): 45 | c = pyext.Buffer(args[0]) 46 | a = pyext.Buffer(args[1]) 47 | b = pyext.Buffer(args[2]) 48 | 49 | commonlen = min(len(a),len(b),len(c)) 50 | 51 | # explicit casting to arrays is also possible 52 | c[:commonlen] = N.array(a[:commonlen],dtype=N.float32) + N.array(b[:commonlen],dtype=N.float32) 53 | 54 | def fadein(target): 55 | a = pyext.Buffer(target) 56 | # in place operations are ok 57 | a *= N.arange(len(a),dtype=N.float32)/len(a) 58 | 59 | def neg(target): 60 | a = pyext.Buffer(target) 61 | # in place transformation (see Python array ufuncs) 62 | N.negative(a[:],a[:]) 63 | # must mark buffer content as dirty to update graph 64 | # (no explicit assignment occurred) 65 | a.dirty() 66 | -------------------------------------------------------------------------------- /scripts/pak.py: -------------------------------------------------------------------------------- 1 | import pyext 2 | 3 | class pak(pyext._class): 4 | def __init__(self,n): 5 | # n should be type-checked 6 | self._inlets = n 7 | self._outlets = 1 8 | # initialize list 9 | self.lst = [0 for x in range(n)] 10 | 11 | def _anything_(self,n,arg): 12 | # arg should be type-checked! 13 | self.lst[n-1] = arg 14 | self._outlet(1,self.lst) 15 | -------------------------------------------------------------------------------- /scripts/script.py: -------------------------------------------------------------------------------- 1 | # py/pyext - python script objects for PD and MaxMSP 2 | # 3 | # Copyright (c) 2002-2005 Thomas Grill (gr@grrrr.org) 4 | # For information on usage and redistribution, and for a DISCLAIMER OF ALL 5 | # WARRANTIES, see the file, "license.txt," in this distribution. 6 | # 7 | 8 | """Several functions to show the py script functionality""" 9 | 10 | from __future__ import print_function 11 | 12 | from functools import reduce 13 | import sys 14 | 15 | print("Script initialized") 16 | 17 | try: 18 | print("Script arguments: ",sys.argv) 19 | except: 20 | print() 21 | 22 | def numargs(*args): # variable argument list 23 | """Return the number of arguments""" 24 | return len(args) 25 | 26 | def strlen(arg): 27 | """Return the string length""" 28 | # we must convert to string first (it's a symbol type most likely) 29 | return len(str(arg)) 30 | 31 | 32 | def strcat(*args): 33 | """Concatenate several symbols""" 34 | return reduce(lambda a,b: a+str(b), args,"") 35 | 36 | def addall(*args): # variable argument list 37 | """Add a couple of numbers""" 38 | return reduce(lambda a,b: a+b, args,0) 39 | 40 | 41 | def ret1(): 42 | return 1,2,3,4 43 | 44 | 45 | def ret2(): 46 | return "sd","lk","ki" 47 | 48 | 49 | def ret3(): 50 | return ["sd","lk","ki"] 51 | 52 | -------------------------------------------------------------------------------- /scripts/sendrecv.py: -------------------------------------------------------------------------------- 1 | # py/pyext - python script objects for PD and MaxMSP 2 | # 3 | # Copyright (c) 2002-2005 Thomas Grill (gr@grrrr.org) 4 | # For information on usage and redistribution, and for a DISCLAIMER OF ALL 5 | # WARRANTIES, see the file, "license.txt," in this distribution. 6 | # 7 | 8 | """This is an example script for the py/pyext object's send/receive functionality. 9 | 10 | You can: 11 | - bind 12 | 13 | 14 | There are several classes exposing py/pyext features: 15 | - ex1: A class receiving messages and sending them out again 16 | - ex2: A class receiving messages and putting them out to an outlet 17 | - ex3: Do some PD scripting 18 | 19 | """ 20 | 21 | from __future__ import print_function 22 | 23 | try: 24 | import pyext 25 | except: 26 | print("ERROR: This script must be loaded by the PD/Max pyext external") 27 | 28 | 29 | from time import sleep 30 | 31 | ################################################################# 32 | 33 | def recv_gl(arg): 34 | """This is a global receive function, it has no access to class members.""" 35 | print("GLOBAL",arg) 36 | 37 | class ex1(pyext._class): 38 | """Example of a class which receives and sends messages 39 | 40 | It has two creation arguments: a receiver and a sender name. 41 | There are no inlets and outlets. 42 | Python functions (one global function, one class method) are bound to PD's or Max/MSP's receive symbols. 43 | The class method sends the received messages out again. 44 | """ 45 | 46 | 47 | # no inlets and outlets 48 | _inlets=1 49 | _outlets=0 50 | 51 | recvname="" 52 | sendname="" 53 | 54 | def recv(self,*arg): 55 | """This is a class-local receive function, which has access to class members.""" 56 | 57 | # print some stuff 58 | print("CLASS",self.recvname,arg) 59 | 60 | # send data to specified send address 61 | pyext._send(self.sendname,arg) 62 | 63 | 64 | def __init__(self,*args): 65 | """Class constructor""" 66 | 67 | # store sender/receiver names 68 | if len(args) >= 1: self.recvname = args[0] 69 | if len(args) >= 2: self.sendname = args[1] 70 | 71 | self.bind_1() 72 | 73 | def bind_1(self): 74 | # bind functions to receiver names 75 | # both are called upon message 76 | self._bind(self.recvname,self.recv) 77 | self._bind(self.recvname,recv_gl) 78 | 79 | def unbind_1(self): 80 | self._unbind(self.recvname,self.recv) 81 | self._unbind(self.recvname,recv_gl) 82 | 83 | def __del__(self): 84 | """Class destructor""" 85 | 86 | # unbinding is automatically done at destruction 87 | pass 88 | 89 | 90 | ################################################################# 91 | 92 | class ex2(pyext._class): 93 | """Example of a class which receives a message and forwards it to an outlet 94 | 95 | It has one creation argument: the receiver name. 96 | """ 97 | 98 | 99 | # define inlets and outlets 100 | _inlets=0 101 | _outlets=1 102 | 103 | recvname="" 104 | 105 | def recv(self,*arg): 106 | """This is a class-local receive function""" 107 | 108 | # send received data to outlet 109 | self._outlet(1,arg) 110 | 111 | 112 | def __init__(self,rname): 113 | """Class constructor""" 114 | 115 | # store receiver names 116 | self.recvname = rname 117 | 118 | # bind function to receiver name 119 | self._bind(self.recvname,self.recv) 120 | 121 | 122 | ################################################################# 123 | 124 | from math import pi 125 | from cmath import exp 126 | from random import random,randint 127 | 128 | class ex3(pyext._class): 129 | """Example of a class which does some object manipulation by scripting""" 130 | 131 | 132 | # define inlets and outlets 133 | _inlets=1 134 | _outlets=0 135 | 136 | def __init__(self): 137 | """Class constructor""" 138 | 139 | # called scripting method should run on its own thread 140 | if self._isthreaded: 141 | print("Threading is on") 142 | self._detach(1) 143 | 144 | def bang_1(self): 145 | """Do some scripting - PD only!""" 146 | 147 | num = 12 # number of objects 148 | ori = complex(150,180) # origin 149 | rad = 100 # radius 150 | l = list(range(num)) # initialize list 151 | 152 | # make flower 153 | self._tocanvas("obj",ori.real,ori.imag,"bng",20,250,50,0,"empty","yeah","empty",0,-6,64,8,-24198,-1,-1) 154 | for i in range(num): 155 | l[i] = ori+rad*exp(complex(0,i*2*pi/num)) 156 | self._tocanvas("obj",l[i].real,l[i].imag,"bng",15,250,50,0,"empty","yeah"+str(i),"empty",0,-6,64,8,0,-1,-1) 157 | self._tocanvas("connect",6,0,7+i,0) 158 | 159 | # blink 160 | for i in range(10): 161 | pyext._send("yeah","bang") 162 | sleep(1./(i+1)) 163 | 164 | # move objects around 165 | for i in range(200): 166 | ix = randint(0,num-1) 167 | l[ix] = ori+rad*complex(2*random()-1,2*random()-1) 168 | pyext._send("yeah"+str(ix),"pos",l[ix].real,l[ix].imag) 169 | sleep(0.02) 170 | 171 | # now delete 172 | # this is not well-done... from time to time an object remains 173 | self._tocanvas("editmode",1) 174 | for i in range(num): 175 | self._tocanvas("mouse",l[i].real,l[i].imag,0,0) 176 | self._tocanvas("cut") 177 | 178 | self._tocanvas("mouse",ori.real+1,ori.imag+1,0,0) 179 | self._tocanvas("cut") 180 | 181 | self._tocanvas("editmode",0) 182 | 183 | -------------------------------------------------------------------------------- /scripts/sig.py: -------------------------------------------------------------------------------- 1 | # py/pyext - python script objects for PD and MaxMSP 2 | # 3 | # Copyright (c) 2002-2017 Thomas Grill (gr@grrrr.org) 4 | # For information on usage and redistribution, and for a DISCLAIMER OF ALL 5 | # WARRANTIES, see the file, "license.txt," in this distribution. 6 | # 7 | 8 | """This is an example script for the py/pyext signal support. 9 | 10 | For numarray see http://numeric.scipy.org 11 | It will probably once be replaced by Numeric(3) 12 | """ 13 | 14 | from __future__ import print_function 15 | 16 | try: 17 | import pyext 18 | except: 19 | print("ERROR: This script must be loaded by the PD/Max py/pyext external") 20 | 21 | try: 22 | import psyco 23 | psyco.full() 24 | print("Using JIT compilation") 25 | except: 26 | # don't care 27 | pass 28 | 29 | import sys,math 30 | 31 | try: 32 | import numpy as N 33 | except: 34 | print("Failed importing numpy module:",sys.exc_value) 35 | 36 | 37 | class gain(pyext._class): 38 | """Just a simple gain stage""" 39 | 40 | def __init__(self): 41 | self.gain = 0 42 | 43 | def _signal(self): 44 | # Multiply input vector by gain and copy to output 45 | try: 46 | self._outvec(0)[:] = self._invec(0)*self.gain 47 | except: 48 | pass 49 | 50 | 51 | class gain2(pyext._class): 52 | """More optimized version""" 53 | 54 | def __init__(self): 55 | self.gain = 0 56 | 57 | def _dsp(self): 58 | if not pyext._arraysupport(): 59 | print("No DSP support") 60 | return False 61 | 62 | # cache vectors in this scope 63 | self.invec = self._invec(0) 64 | self.outvec = self._outvec(0) 65 | # initialize _signal method here for optimized version 66 | if self.invec is self.outvec: 67 | self._signal = self.signal1 68 | else: 69 | self._signal = self.signal2 70 | return True 71 | 72 | def signal1(self): 73 | # Multiply signal vector in place 74 | self.outvec *= self.gain 75 | 76 | def signal2(self): 77 | # Multiply input vector by gain and copy to output 78 | self.outvec[:] = self.invec*self.gain 79 | 80 | 81 | class pan(pyext._class): 82 | """Stereo panning""" 83 | 84 | def __init__(self): 85 | self.float_1(0.5) 86 | 87 | def float_1(self,pos): 88 | """pos ranges from 0 to 1""" 89 | x = pos*math.pi/2 90 | self.fl = math.cos(x) 91 | self.fr = math.sin(x) 92 | 93 | def _dsp(self): 94 | # if _dsp is present it must return True to enable DSP 95 | return pyext._arraysupport() 96 | 97 | def _signal(self): 98 | # Multiply input vector by gain and copy to output 99 | iv = self._invec(0) 100 | # first process right output channel because left one could be 101 | # identical to input 102 | # we could also test with 'self._outvec(1)[:] is iv' 103 | self._outvec(1)[:] = iv*self.fr 104 | self._outvec(0)[:] = iv*self.fl 105 | -------------------------------------------------------------------------------- /scripts/simple.py: -------------------------------------------------------------------------------- 1 | # py/pyext - python script objects for PD and MaxMSP 2 | # 3 | # Copyright (c) 2002-2007 Thomas Grill (gr@grrrr.org) 4 | # For information on usage and redistribution, and for a DISCLAIMER OF ALL 5 | # WARRANTIES, see the file, "license.txt," in this distribution. 6 | # 7 | 8 | """This is an example script for the py/pyext object's basic functionality. 9 | 10 | pyext Usage: 11 | - Import pyext 12 | 13 | - Inherit your class from pyext._class 14 | 15 | - Specfiy the number of inlets and outlets: 16 | Use the class members (variables) _inlets and _outlets 17 | If not given they default to 1 18 | You can also use class methods with the same names to return the respective number 19 | 20 | - Constructors/Destructors 21 | You can specify an __init__ constructor and/or an __del__ destructor. 22 | The constructor will be called with the object's arguments 23 | 24 | e.g. if your PD or MaxMSP object looks like 25 | [pyext script class arg1 arg2 arg3] 26 | 27 | then the __init__(self,*args) function will be called with a tuple argument 28 | args = (arg1,arg2,arg3) 29 | With this syntax, you will have to give at least one argument. 30 | By defining the constructor as __init__(self,*args) you can also initialize 31 | the class without arguments. 32 | 33 | - Methods called by pyext 34 | The general format is 'tag_inlet(self,arg)' resp. 'tag_inlet(self,*args)': 35 | tag is the PD or MaxMSP message header.. either bang, float, list etc. 36 | inlet is the inlet (starting from 1) from which messages are received. 37 | args is a tuple which corresponds to the content of the message. args can be omitted. 38 | 39 | The inlet index can be omitted. The method name then has the format 'tag_(self,inlet,args)'. 40 | Here, the inlet index is a additional parameter to the method 41 | 42 | You can also set up methods which react on any message. These have the special forms 43 | _anything_inlet(self,*args) 44 | or 45 | _anything_(self,inlet,*args) 46 | 47 | Please see below for examples. 48 | 49 | Any return values are ignored - use _outlet (see below). 50 | 51 | Generally, you should avoid method_, method_xx forms for your non-pyext class methods. 52 | Identifiers (variables and functions) with leading underscores are reserved for pyext. 53 | 54 | - Send messages to outlets: 55 | Use the inherited _outlet method. 56 | You can either use the form 57 | self._outlet(outlet,arg1,arg2,arg3,arg4) ... where all args are atoms (no sequence types!) 58 | or 59 | self._outlet(outlet,arg) ... where arg is a sequence containing only atoms 60 | 61 | Do not use _outlet inside __init__, since the outlets have not been created at that time. 62 | 63 | - Use pyext functions and methods: 64 | See the __doc__ strings of the pyext module and the pyext._class base class. 65 | 66 | """ 67 | 68 | from __future__ import print_function 69 | 70 | try: 71 | import pyext 72 | except: 73 | print("ERROR: This script must be loaded by the PD/Max pyext external") 74 | 75 | try: 76 | # Python 2 77 | _long = long 78 | del _long 79 | except NameError: 80 | long = int 81 | 82 | ################################################################# 83 | 84 | class ex1(pyext._class): 85 | """Example of a simple class which receives messages and prints to the console""" 86 | 87 | # number of inlets and outlets 88 | _inlets=3 89 | _outlets=0 90 | 91 | # methods for first inlet 92 | 93 | def bang_1(self): 94 | print("Bang into first inlet") 95 | 96 | def int_1(self,f): 97 | print("Integer",f,"into first inlet") 98 | 99 | def float_1(self,f): 100 | print("Float",f,"into first inlet") 101 | 102 | def list_1(self,*s): 103 | print("List",s,"into first inlet") 104 | 105 | 106 | # methods for second inlet 107 | 108 | def hey_2(self): 109 | print("Tag 'hey' into second inlet") 110 | 111 | def ho_2(self): 112 | print("Tag 'ho' into second inlet") 113 | 114 | def lets_2(self): 115 | print("Tag 'lets' into second inlet") 116 | 117 | def go_2(self): 118 | print("Tag 'go' into second inlet") 119 | 120 | def _anything_2(self,*args): 121 | print("Some other message into second inlet:",args) 122 | 123 | 124 | # methods for third inlet 125 | 126 | def onearg_3(self,a): 127 | print("Tag 'onearg' into third inlet:",a) 128 | 129 | def twoargs_3(self,*a): 130 | if len(a) == 2: 131 | print("Tag 'twoargs' into third inlet:",a[0],a[1]) 132 | else: 133 | print("Tag 'twoargs': wrong number of arguments") 134 | 135 | def threeargs_3(self,*a): 136 | if len(a) == 3: 137 | print("Tag 'threeargs' into third inlet",a[0],a[1],a[2]) 138 | else: 139 | print("Tag 'threeargs': wrong number of arguments") 140 | 141 | def varargs_3(self,*args): 142 | # with *args there can be arguments or not 143 | 144 | print("Tag 'varargs' into third inlet",args) 145 | 146 | 147 | 148 | ################################################################# 149 | 150 | class ex2(pyext._class): 151 | """Example of a simple class which receives messages and writes to outlets""" 152 | 153 | # number of inlets and outlets 154 | _inlets=3 155 | _outlets=2 156 | 157 | # methods for all inlets 158 | 159 | def hello_(self,n): 160 | print("Tag 'hello' into inlet",n) 161 | 162 | def _anything_(self,n,*args): 163 | print("Message into inlet",n,":",args) 164 | 165 | 166 | # methods for first inlet 167 | 168 | def float_1(self,f): 169 | self._outlet(2,f) 170 | 171 | # methods for second inlet 172 | 173 | def float_2(self,f): 174 | self._outlet(1,f) 175 | 176 | 177 | ################################################################# 178 | 179 | # helper function - determine whether argument is a numeric type 180 | def isNumber(value): 181 | import types 182 | if type(value) in (float, int, long): 183 | return 1 184 | else: 185 | return 0 186 | 187 | 188 | class ex3(pyext._class): 189 | """Example of a simple class doing a typical number addition 190 | 191 | It uses a constructor and a class member as temporary storage. 192 | """ 193 | 194 | # number of inlets and outlets 195 | _inlets=2 196 | _outlets=1 197 | 198 | # temporary storage 199 | tmp=0 200 | 201 | # constructor 202 | def __init__(self,*args): 203 | if len(args) == 1: 204 | if isNumber(args[0]): 205 | self.tmp = args[0] 206 | else: 207 | print("ex3: __init__ has superfluous arguments") 208 | 209 | # methods 210 | 211 | def float_1(self,f): 212 | self._outlet(1,self.tmp+f) 213 | 214 | def float_2(self,f): 215 | self.tmp = f 216 | 217 | # handlers for MaxMSP int type 218 | def int_1(self,f): 219 | self.float_1(f) 220 | 221 | def int_2(self,f): 222 | self.float_2(f) 223 | 224 | -------------------------------------------------------------------------------- /scripts/tcltk.py: -------------------------------------------------------------------------------- 1 | # py/pyext - python script objects for PD and MaxMSP 2 | # 3 | # Copyright (c) 2002-2003 Thomas Grill (xovo@gmx.net) 4 | # For information on usage and redistribution, and for a DISCLAIMER OF ALL 5 | # WARRANTIES, see the file, "license.txt," in this distribution. 6 | # 7 | 8 | """This is an example script for showing a nonsense tcl/tk application.""" 9 | 10 | from __future__ import print_function 11 | 12 | try: 13 | import pyext 14 | except: 15 | print("ERROR: This script must be loaded by the PD/Max pyext external") 16 | 17 | from Tkinter import * 18 | import random 19 | 20 | 21 | class Application(Frame): 22 | """This is the TK application class""" 23 | 24 | # Button pressed 25 | def say_hi(self): 26 | self.extcl._outlet(1,"hi there, everyone!") 27 | 28 | # Mouse motion over canvas 29 | def evfunc(self, ev): 30 | x = random.uniform(-3,3) 31 | y = random.uniform(-3,3) 32 | self.mcanv.move('group',x,y) 33 | 34 | # Create interface stuff 35 | def createWidgets(self): 36 | self.hi = Button(self) 37 | self.hi["text"] = "Hi!" 38 | self.hi["fg"] = "red" 39 | self.hi["command"] = self.say_hi 40 | self.hi.pack({"side": "left"}) 41 | 42 | self.mcanv = Canvas(self) 43 | self.mcanv.pack({"side": "left"}) 44 | self.mcanv.bind("", self.evfunc) 45 | self.mcanv.create_rectangle(50,50,200,200) 46 | r = self.mcanv.create_rectangle(50,50,200,200) 47 | self.mcanv.addtag_withtag('group',r) 48 | 49 | for i in xrange(500): 50 | x = random.uniform(50,200) 51 | y = random.uniform(50,200) 52 | l = self.mcanv.create_line(x,y,x+1,y) 53 | self.mcanv.addtag_withtag('group',l) 54 | 55 | # Constructor 56 | def __init__(self,cl): 57 | self.extcl = cl 58 | Frame.__init__(self) 59 | self.pack() 60 | self.createWidgets() 61 | pass 62 | 63 | 64 | # derive class from pyext._class 65 | 66 | class myapp(pyext._class): 67 | """This class demonstrates how a TCL/TK can be openened from within a pyext external""" 68 | 69 | # how many inlets and outlets? 70 | _inlets = 1 71 | _outlets = 1 72 | 73 | # Constructor 74 | def __init__(self): 75 | # detach bang method 76 | self._detach(1) 77 | 78 | def bang_1(self): 79 | pyext._priority(-3) 80 | # display the tcl/tk dialog 81 | app = Application(self) 82 | app.mainloop() 83 | 84 | -------------------------------------------------------------------------------- /scripts/threads.py: -------------------------------------------------------------------------------- 1 | # py/pyext - python script objects for PD and MaxMSP 2 | # 3 | # Copyright (c) 2002-2003 Thomas Grill (xovo@gmx.net) 4 | # For information on usage and redistribution, and for a DISCLAIMER OF ALL 5 | # WARRANTIES, see the file, "license.txt," in this distribution. 6 | # 7 | 8 | """This is an example script for the py/pyext object's threading functionality. 9 | 10 | For threading support pyext exposes several function and variables 11 | 12 | - _detach([0/1]): by enabling thread detaching, threads will run in their own threads 13 | - _priority(prio+-): you can raise or lower the priority of the current thread 14 | - _stop({wait time in ms}): stop all running threads (you can additionally specify a wait time in ms) 15 | - _shouldexit: this is a flag which indicates that the running thread should terminate 16 | 17 | """ 18 | 19 | from __future__ import print_function 20 | 21 | try: 22 | import pyext 23 | except: 24 | print("ERROR: This script must be loaded by the PD/Max pyext external") 25 | 26 | from time import sleep 27 | 28 | try: 29 | # Python 2 30 | range = xrange 31 | except NameError: 32 | # Python 3 33 | pass 34 | 35 | ################################################################# 36 | 37 | class ex1(pyext._class): 38 | """This is a simple class with one method looping over time.""" 39 | 40 | # number of inlets and outlets 41 | _inlets=2 42 | _outlets=2 43 | 44 | sltime=0.1 # sleep time 45 | loops=20 # loops to iterate 46 | 47 | # method for bang to any inlet 48 | def bang_(self,n): 49 | for i in range(self.loops): 50 | # if _shouldexit is true, the thread ought to stop 51 | if self._shouldexit: 52 | print("BREAK") 53 | break 54 | 55 | self._outlet(n,i) 56 | sleep(self.sltime) 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /source/bound.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | py/pyext - python external object for PD and MaxMSP 3 | 4 | Copyright (c)2002-2015 Thomas Grill (gr@grrrr.org) 5 | For information on usage and redistribution, and for a DISCLAIMER OF ALL 6 | WARRANTIES, see the file, "license.txt," in this distribution. 7 | */ 8 | 9 | #include "pyext.h" 10 | #include "flinternal.h" 11 | 12 | #include 13 | 14 | class MethodCompare: 15 | public std::less 16 | { 17 | public: 18 | bool operator()(PyObject *a,PyObject *b) const 19 | { 20 | if(PyMethod_Check(a)) 21 | if(PyMethod_Check(b)) { 22 | // both are methods 23 | PyObject *sa = PyMethod_GET_SELF(a); 24 | PyObject *sb = PyMethod_GET_SELF(b); 25 | if(sa) 26 | if(sb) { 27 | // both have self 28 | if(sa == sb) 29 | return PyMethod_GET_FUNCTION(a) < PyMethod_GET_FUNCTION(b); 30 | else 31 | return sa < sb; 32 | } 33 | else 34 | return false; 35 | else 36 | if(sb) 37 | return true; 38 | else 39 | return PyMethod_GET_FUNCTION(a) < PyMethod_GET_FUNCTION(b); 40 | } 41 | else 42 | return false; 43 | else 44 | if(PyMethod_Check(b)) 45 | return true; 46 | else 47 | // both are non-method callables 48 | return a < b; 49 | } 50 | }; 51 | 52 | typedef std::set FuncSet; 53 | 54 | struct bounddata 55 | { 56 | PyObject *self; 57 | FuncSet funcs; 58 | }; 59 | 60 | bool pyext::boundmeth(flext_base *th,t_symbol *sym,int argc,t_atom *argv,void *data) 61 | { 62 | bounddata *obj = (bounddata *)data; 63 | pyext *pyth = static_cast(th); 64 | 65 | ThrState state = pyth->PyLock(); 66 | 67 | PyObject *args = MakePyArgs(sym,argc,argv); 68 | 69 | // call all functions bound by this symbol 70 | for(FuncSet::iterator it = obj->funcs.begin(); it != obj->funcs.end(); ++it) { 71 | PyObject *ret = PyObject_CallObject(*it,args); 72 | if(!ret) 73 | PyErr_Print(); 74 | else 75 | Py_DECREF(ret); 76 | } 77 | 78 | Py_XDECREF(args); 79 | 80 | pyth->PyUnlock(state); 81 | return true; 82 | } 83 | 84 | PyObject *pyext::pyext_bind(PyObject *self, PyObject *args) 85 | { 86 | PyObject *meth,*name; 87 | if(!PyArg_ParseTuple(args, "OO:pyext_bind",&name,&meth)) // borrowed references 88 | post("py/pyext - Wrong arguments!"); 89 | else if(!PyCallable_Check(meth)) { 90 | post("py/pyext - Wrong argument types!"); 91 | } 92 | else { 93 | pyext *th = GetThis(self); 94 | if(!th) { 95 | PyErr_SetString(PyExc_RuntimeError,"pyext - _bind: instance not associated with pd object"); 96 | return NULL; 97 | } 98 | 99 | const t_symbol *recv = pyObject_AsSymbol(name); 100 | 101 | void *data = NULL; 102 | if(recv && th->GetBoundMethod(recv,boundmeth,data)) { 103 | // already bound to that symbol and function 104 | bounddata *bdt = (bounddata *)data; 105 | FLEXT_ASSERT(bdt != NULL && bdt->self == self); 106 | 107 | FuncSet::iterator it = bdt->funcs.find(meth); 108 | if(it == bdt->funcs.end()) { 109 | bdt->funcs.insert(meth); 110 | Py_INCREF(meth); 111 | } 112 | } 113 | else { 114 | Py_INCREF(self); // self is borrowed reference 115 | Py_INCREF(meth); 116 | 117 | bounddata *data = new bounddata; 118 | data->self = self; 119 | data->funcs.insert(meth); 120 | 121 | th->BindMethod(recv,boundmeth,data); 122 | } 123 | } 124 | 125 | Py_INCREF(Py_None); 126 | return Py_None; 127 | } 128 | 129 | PyObject *pyext::pyext_unbind(PyObject *self, PyObject *args) 130 | { 131 | PyObject *meth,*name; 132 | if(!PyArg_ParseTuple(args, "OO:pyext_bind",&name,&meth)) // borrowed references 133 | post("py/pyext - Wrong arguments!"); 134 | else if(!PyCallable_Check(meth)) { 135 | post("py/pyext - Wrong argument types!"); 136 | } 137 | else { 138 | pyext *th = GetThis(self); 139 | if(!th) { 140 | PyErr_SetString(PyExc_RuntimeError,"pyext - _unbind: instance not associated with pd object"); 141 | return NULL; 142 | } 143 | 144 | const t_symbol *recv = pyObject_AsSymbol(name); 145 | 146 | void *data = NULL; 147 | if(recv && th->GetBoundMethod(recv,boundmeth,data)) { 148 | bounddata *bdt = (bounddata *)data; 149 | FLEXT_ASSERT(bdt != NULL); 150 | 151 | // erase from map 152 | // ATTENTION: meth is different from the element found in the map 153 | // it just points to the same instance method 154 | FuncSet::iterator it = bdt->funcs.find(meth); 155 | if(it != bdt->funcs.end()) { 156 | Py_DECREF(*it); 157 | bdt->funcs.erase(it); 158 | } 159 | else 160 | post("py/pyext - Function to unbind couldn't be found"); 161 | 162 | if(bdt->funcs.empty()) { 163 | Py_DECREF(bdt->self); 164 | delete bdt; 165 | 166 | th->UnbindMethod(recv,boundmeth,NULL); 167 | } 168 | } 169 | } 170 | 171 | Py_INCREF(Py_None); 172 | return Py_None; 173 | } 174 | 175 | 176 | void pyext::ClearBinding() 177 | { 178 | // in case the object couldn't be constructed... 179 | if(!pyobj) return; 180 | 181 | pyext *th = GetThis(pyobj); 182 | if(!th) return; 183 | 184 | void *data = NULL; 185 | const t_symbol *sym = NULL; 186 | 187 | // unbind all 188 | while(th->UnbindMethod(sym,NULL,&data)) { 189 | bounddata *bdt = (bounddata *)data; 190 | if(bdt) { 191 | for(FuncSet::iterator it = bdt->funcs.begin(); it != bdt->funcs.end(); ++it) 192 | Py_DECREF(*it); 193 | 194 | Py_DECREF(bdt->self); 195 | delete bdt; 196 | } 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /source/clmeth.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | py/pyext - python external object for PD and Max/MSP 3 | 4 | Copyright (c)2002-2019 Thomas Grill (gr@grrrr.org) 5 | For information on usage and redistribution, and for a DISCLAIMER OF ALL 6 | WARRANTIES, see the file, "license.txt," in this distribution. 7 | */ 8 | 9 | #include "pyext.h" 10 | 11 | 12 | PyMethodDef pyext::meth_tbl[] = 13 | { 14 | /* 15 | {"__init__", pyext::pyext__init__, METH_VARARGS, "Constructor"}, 16 | {"__del__", pyext::pyext__del__, METH_VARARGS, "Destructor"}, 17 | */ 18 | {"__str__", pyext::pyext__str__, METH_VARARGS, "stringify"}, 19 | {"_outlet", pyext::pyext_outlet, METH_VARARGS,"Send message to outlet"}, 20 | #if FLEXT_SYS == FLEXT_SYS_PD 21 | {"_tocanvas", pyext::pyext_tocanvas, METH_VARARGS,"Send message to canvas" }, 22 | #endif 23 | 24 | { "_bind", pyext::pyext_bind, METH_VARARGS,"Bind function to a receiving symbol" }, 25 | { "_unbind", pyext::pyext_unbind, METH_VARARGS,"Unbind function from a receiving symbol" }, 26 | #ifdef FLEXT_THREADS 27 | { "_detach", pyext::pyext_detach, METH_VARARGS,"Set detach flag for called methods" }, 28 | { "_stop", pyext::pyext_stop, METH_VARARGS,"Stop running threads" }, 29 | #endif 30 | { "_invec", pyext::pyext_invec, METH_VARARGS,"Get input vector" }, 31 | { "_outvec", pyext::pyext_outvec, METH_VARARGS,"Get output vector" }, 32 | { "__setattr__", pyext::pyext_setattr, METH_VARARGS,"Set class attribute" }, 33 | { "__getattr__", pyext::pyext_getattr, METH_VARARGS,"Get class attribute" }, 34 | {NULL, NULL, 0, NULL} /* Sentinel */ 35 | }; 36 | 37 | const char *pyext::pyext_doc = 38 | "py/pyext - python external object for PD and Max/MSP, (C)2002-2008 Thomas Grill\n" 39 | "\n" 40 | "This is the pyext base class. Available methods:\n" 41 | "_outlet(self,ix,args...): Send a message to an indexed outlet\n" 42 | #if FLEXT_SYS == FLEXT_SYS_PD 43 | "_tocanvas(self,args...): Send a message to the parent canvas\n" 44 | #endif 45 | "_bind(self,name,func): Bind a python function to a symbol\n" 46 | "_unbind(self,name,func): Unbind a python function from a symbol\n" 47 | "_isthreaded: Query whether threading is enabled\n" 48 | #ifdef FLEXT_THREADS 49 | "_detach(self,int): Define whether a called Python method has its own thread\n" 50 | "_stop(self): Stop running threads\n" 51 | "_shouldexit: Query whether threads should terminate\n" 52 | #endif 53 | ; 54 | 55 | PyObject* pyext::pyext__str__(PyObject *self, PyObject *args) 56 | { 57 | return 58 | #if PY_MAJOR_VERSION < 3 59 | PyString_FromFormat 60 | #else 61 | PyUnicode_FromFormat 62 | #endif 63 | ("", self); 64 | } 65 | 66 | PyObject* pyext::pyext_setattr(PyObject *self, PyObject *args) 67 | { 68 | PyObject *name,*val; 69 | if(!PyArg_ParseTuple(args, "OO:pyext_setattr",&name,&val)) 70 | { 71 | // handle error 72 | ERRINTERNAL(); 73 | return NULL; 74 | } 75 | 76 | 77 | bool handled = false; 78 | 79 | /* 80 | if(PyString_Check(name)) { 81 | char* sname = PyString_AsString(name); 82 | if (sname) { 83 | // post("pyext::setattr %s",sname); 84 | } 85 | } 86 | */ 87 | if(!handled) { 88 | if(PyObject_GenericSetAttr(self, name, val) < 0) { 89 | ERRINTERNAL(); 90 | return NULL; 91 | } 92 | } 93 | 94 | Py_INCREF(Py_None); 95 | return Py_None; 96 | } 97 | 98 | PyObject* pyext::pyext_getattr(PyObject *self, PyObject *args) 99 | { 100 | PyObject *name, *ret = NULL; 101 | if(!PyArg_ParseTuple(args, "O:pyext_getattr", &name)) 102 | { 103 | // handle error 104 | ERRINTERNAL(); 105 | return NULL; 106 | } 107 | 108 | #if PY_MAJOR_VERSION < 3 109 | if(PyString_Check(name)) 110 | #else 111 | if(PyUnicode_Check(name)) 112 | #endif 113 | { 114 | #if PY_MAJOR_VERSION < 3 115 | const char *sname = PyString_AS_STRING(name); 116 | #else 117 | const char *sname = PyUnicode_AsUTF8(name); 118 | #endif 119 | if(sname) { 120 | #ifdef FLEXT_THREADS 121 | if(!strcmp(sname,"_shouldexit")) { 122 | pyext *ext = GetThis(self); 123 | if(ext) 124 | ret = PyLong_FromLong(ext->shouldexit?1:0); 125 | else { 126 | // return true for _shouldexit if association has been removed 127 | Py_INCREF(Py_True); 128 | ret = Py_True; 129 | } 130 | } 131 | else 132 | #endif 133 | if(!strcmp(sname,"_isthreaded")) { 134 | #ifdef FLEXT_THREADS 135 | Py_INCREF(Py_True); 136 | ret = Py_True; 137 | #else 138 | Py_INCREF(Py_False); 139 | ret = Py_False; 140 | #endif 141 | } 142 | else if(!strcmp(sname, "_canvas_dir")) { 143 | pyext *ext = GetThis(self); 144 | char dir[1024]; 145 | ext->GetCanvasDir(dir, sizeof(dir)); 146 | #if PY_MAJOR_VERSION < 3 147 | ret = PyString_InternFromString(dir); 148 | #else 149 | ret = PyUnicode_InternFromString(dir); 150 | #endif 151 | } 152 | } 153 | } 154 | 155 | if(!ret) { 156 | #if PY_VERSION_HEX >= 0x02020000 157 | ret = PyObject_GenericGetAttr(self,name); // new reference (?) 158 | #else 159 | if(PyInstance_Check(self)) 160 | // borrowed reference 161 | ret = PyDict_GetItem(((PyInstanceObject *)self)->in_dict,name); 162 | #endif 163 | } 164 | return ret; 165 | } 166 | 167 | //! Send message to outlet 168 | PyObject *pyext::pyext_outlet(PyObject *self, PyObject *args) 169 | { 170 | bool ok = false; 171 | 172 | // should always be a tuple! 173 | FLEXT_ASSERT(PyTuple_Check(args)); 174 | 175 | int sz = PyTuple_GET_SIZE(args); 176 | 177 | // borrowed references! 178 | PyObject *outl; 179 | 180 | if( 181 | sz >= 1 && 182 | (outl = PyTuple_GET_ITEM(args,0)) != NULL && 183 | #if PY_MAJOR_VERSION < 3 184 | PyInt_Check(outl) 185 | #else 186 | PyLong_Check(outl) 187 | #endif 188 | ) { 189 | pyext *ext = GetThis(self); 190 | if(!ext) { 191 | PyErr_SetString(PyExc_RuntimeError,"pyext - _outlet: instance not associated with pd object"); 192 | return NULL; 193 | } 194 | 195 | PyObject *val; 196 | #if 0 197 | if(sz == 2) { 198 | val = PyTuple_GET_ITEM(args,1); // borrow reference 199 | Py_INCREF(val); 200 | tp = PySequence_Check(val); 201 | } 202 | else 203 | tp = false; 204 | 205 | if(!tp) 206 | val = PySequence_GetSlice(args,1,sz); // new ref 207 | #else 208 | if(sz == 2) { 209 | val = PyTuple_GET_ITEM(args,1); // borrow reference 210 | Py_INCREF(val); 211 | } 212 | else 213 | val = PyTuple_GetSlice(args,1,sz); // new ref 214 | #endif 215 | 216 | int o; 217 | #if PY_MAJOR_VERSION < 3 218 | o = PyInt_AS_LONG(outl); 219 | #else 220 | o = PyLong_AS_LONG(outl); 221 | #endif 222 | if(o >= 1 && o <= ext->Outlets()) { 223 | // offset outlet by signal outlets 224 | o += ext->sigoutlets; 225 | 226 | if(ext->OutObject(ext,o-1,val)) 227 | ok = true; 228 | else 229 | PyErr_SetString(PyExc_ValueError,"pyext - _outlet: invalid arguments"); 230 | } 231 | else 232 | PyErr_SetString(PyExc_ValueError,"pyext - _outlet: index out of range"); 233 | 234 | Py_DECREF(val); 235 | } 236 | else 237 | PyErr_SetString(PyExc_SyntaxError,"pyext - Syntax: _outlet(self,outlet,args...)"); 238 | 239 | if(!ok) return NULL; 240 | Py_INCREF(Py_None); 241 | return Py_None; 242 | } 243 | 244 | 245 | 246 | #ifdef FLEXT_THREADS 247 | //! Detach threads 248 | PyObject *pyext::pyext_detach(PyObject *self, PyObject *args) 249 | { 250 | int val; 251 | if( 252 | !PyArg_ParseTuple(args, "i:pyext_detach",&val) 253 | ) { 254 | // handle error 255 | PyErr_SetString(PyExc_SyntaxError,"pyext - Syntax: _detach(self,[0/1/2])"); 256 | return NULL; 257 | } 258 | else if(val < 0 || val > 2) { 259 | PyErr_SetString(PyExc_ValueError,"pyext - _detach must be in the range 0..2"); 260 | return NULL; 261 | } 262 | else { 263 | pyext *ext = GetThis(self); 264 | if(!ext) { 265 | PyErr_SetString(PyExc_RuntimeError,"pyext - _detach: instance not associated with pd object"); 266 | return NULL; 267 | } 268 | 269 | ext->detach = val; 270 | } 271 | 272 | Py_INCREF(Py_None); 273 | return Py_None; 274 | } 275 | 276 | //! Stop running threads 277 | PyObject *pyext::pyext_stop(PyObject *self, PyObject *args) 278 | { 279 | int val = -1; 280 | if( 281 | !PyArg_ParseTuple(args, "|i:pyext_stop",&val) 282 | ) { 283 | // handle error 284 | PyErr_SetString(PyExc_SyntaxError,"pyext - Syntax: _stop(self,{wait time})"); 285 | return NULL; 286 | } 287 | else if(val < 0) { 288 | PyErr_SetString(PyExc_ValueError,"pyext - _stop time must be >= 0"); 289 | return NULL; 290 | } 291 | else { 292 | pyext *ext = GetThis(self); 293 | if(!ext) { 294 | PyErr_SetString(PyExc_RuntimeError,"pyext - _stop: instance not associated with pd object"); 295 | return NULL; 296 | } 297 | 298 | int cnt; 299 | t_atom at; 300 | if(val >= 0) cnt = 1,flext::SetInt(at,val); 301 | else cnt = 0; 302 | ext->m_stop(cnt,&at); 303 | } 304 | 305 | Py_INCREF(Py_None); 306 | return Py_None; 307 | } 308 | 309 | #endif 310 | 311 | 312 | #if FLEXT_SYS == FLEXT_SYS_PD 313 | //! Send message to canvas 314 | PyObject *pyext::pyext_tocanvas(PyObject *self, PyObject *args) 315 | { 316 | FLEXT_ASSERT(PyTuple_Check(args)); 317 | 318 | int sz = PyTuple_GET_SIZE(args); 319 | 320 | bool ok = false; 321 | pyext *ext = GetThis(self); 322 | if(!ext) { 323 | PyErr_SetString(PyExc_RuntimeError,"pyext - _tocanvas: instance not associated with pd object"); 324 | return NULL; 325 | } 326 | 327 | PyObject *val; 328 | 329 | bool tp = 330 | sz == 2 && 331 | PySequence_Check( 332 | val = PyTuple_GET_ITEM(args,0) // borrowed ref 333 | ); 334 | 335 | if(!tp) 336 | val = PyTuple_GetSlice(args,0,sz); // new ref 337 | 338 | flext::AtomListStatic<16> lst; 339 | const t_symbol *sym = GetPyArgs(lst,val); 340 | if(sym) { 341 | t_glist *gl = ext->thisCanvas(); 342 | if(gl) { 343 | // \TODO find a flext-based non-locking method 344 | sys_lock(); 345 | pd_forwardmess((t_class **)gl,lst.Count(),lst.Atoms()); 346 | sys_unlock(); 347 | } 348 | #ifdef FLEXT_DEBUG 349 | else 350 | post("pyext - no parent canvas?!"); 351 | #endif 352 | ok = true; 353 | } 354 | else 355 | post("py/pyext - No data to send"); 356 | 357 | if(!tp) Py_DECREF(val); 358 | 359 | if(!ok) { 360 | PyErr_SetString(PyExc_SyntaxError,"pyext - Syntax: _tocanvas(self,args...)"); 361 | return NULL; 362 | } 363 | 364 | Py_INCREF(Py_None); 365 | return Py_None; 366 | } 367 | #endif 368 | 369 | PyObject *pyext::pyext_invec(PyObject *self, PyObject *args) 370 | { 371 | int val = -1; 372 | if( 373 | !PyArg_ParseTuple(args, "|i:pyext_invec",&val) 374 | ) { 375 | // handle error 376 | PyErr_SetString(PyExc_SyntaxError,"pyext - Syntax: _invec(self,inlet)"); 377 | return NULL; 378 | } 379 | else if(val < 0) { 380 | PyErr_SetString(PyExc_ValueError,"pyext - _invec: index out of range"); 381 | return NULL; 382 | } 383 | else { 384 | pyext *ext = GetThis(self); 385 | if(ext) { 386 | PyObject *b = ext->GetSig(val,true); 387 | if(b) return b; 388 | } 389 | else { 390 | PyErr_SetString(PyExc_RuntimeError,"pyext - _invec: instance not associated with pd object"); 391 | return NULL; 392 | } 393 | } 394 | 395 | Py_INCREF(Py_None); 396 | return Py_None; 397 | } 398 | 399 | PyObject *pyext::pyext_outvec(PyObject *self, PyObject *args) 400 | { 401 | int val = -1; 402 | if( 403 | !PyArg_ParseTuple(args, "|i:pyext_outvec",&val) 404 | ) { 405 | // handle error 406 | PyErr_SetString(PyExc_SyntaxError,"pyext - Syntax: _outvec(self,inlet)"); 407 | return NULL; 408 | } 409 | else if(val < 0) { 410 | PyErr_SetString(PyExc_ValueError,"pyext - _outvec: index out of range"); 411 | return NULL; 412 | } 413 | else { 414 | pyext *ext = GetThis(self); 415 | if(ext) { 416 | PyObject *b = ext->GetSig(val,false); 417 | if(b) return b; 418 | } 419 | else { 420 | PyErr_SetString(PyExc_RuntimeError,"pyext - _outvec: instance not associated with pd object"); 421 | return NULL; 422 | } 423 | } 424 | 425 | Py_INCREF(Py_None); 426 | return Py_None; 427 | } 428 | -------------------------------------------------------------------------------- /source/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | py/pyext - python external object for PD and MaxMSP 3 | 4 | Copyright (c)2002-2015 Thomas Grill (gr@grrrr.org) 5 | For information on usage and redistribution, and for a DISCLAIMER OF ALL 6 | WARRANTIES, see the file, "license.txt," in this distribution. 7 | */ 8 | 9 | #include "pybase.h" 10 | -------------------------------------------------------------------------------- /source/main.h: -------------------------------------------------------------------------------- 1 | /* 2 | py/pyext - python script object for PD and MaxMSP 3 | 4 | Copyright (c)2002-2015 Thomas Grill (gr@grrrr.org) 5 | For information on usage and redistribution, and for a DISCLAIMER OF ALL 6 | WARRANTIES, see the file, "license.txt," in this distribution. 7 | */ 8 | 9 | #ifndef __MAIN_H 10 | #define __MAIN_H 11 | 12 | #include "pyprefix.h" 13 | 14 | #define PY__VERSION "0.2.2" 15 | 16 | 17 | #define PYEXT_MODULE "pyext" // name for module 18 | #define PYEXT_CLASS "_class" // name for base class 19 | 20 | #define REGNAME "_registry" 21 | 22 | #define PY_STOP_WAIT 100 // ms 23 | #define PY_STOP_TICK 1 // ms 24 | 25 | 26 | class pybase; 27 | 28 | class FifoEl 29 | : public FifoCell 30 | { 31 | public: 32 | void Set(pybase *t,PyObject *f,PyObject *a) { th = t,fun = f,args = a; } 33 | pybase *th; 34 | PyObject *fun,*args; 35 | }; 36 | 37 | typedef PooledFifo PyFifo; 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /source/modmeth.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | py/pyext - python external object for PD and Max/MSP 3 | 4 | Copyright (c)2002-2015 Thomas Grill (gr@grrrr.org) 5 | For information on usage and redistribution, and for a DISCLAIMER OF ALL 6 | WARRANTIES, see the file, "license.txt," in this distribution. 7 | */ 8 | 9 | #include "pybase.h" 10 | 11 | 12 | // function table for module 13 | PyMethodDef pybase::func_tbl[] = 14 | { 15 | { "_send", pybase::py_send, METH_VARARGS,"Send message to a named object" }, 16 | #ifdef FLEXT_THREADS 17 | { "_priority", pybase::py_priority, METH_VARARGS,"Set priority of current thread" }, 18 | #endif 19 | 20 | { "_arraysupport", pybase::py_arraysupport, METH_NOARGS,"Query Python array support" }, 21 | { "_samplerate", pybase::py_samplerate, METH_NOARGS,"Get system sample rate" }, 22 | { "_blocksize", pybase::py_blocksize, METH_NOARGS,"Get system block size" }, 23 | 24 | { "_searchpaths", pybase::py_searchpaths, METH_NOARGS,"Get system search paths" }, 25 | { "_helppaths", pybase::py_helppaths, METH_NOARGS,"Get system help paths" }, 26 | 27 | #if FLEXT_SYS == FLEXT_SYS_PD 28 | { "_getvalue", pybase::py_getvalue, METH_VARARGS,"Get value of a 'value' object" }, 29 | { "_setvalue", pybase::py_setvalue, METH_VARARGS,"Set value of a 'value' object" }, 30 | #endif 31 | 32 | { "_list", pybase::py_list, METH_VARARGS,"Make a list from arguments" }, 33 | { "_tuple", pybase::py_tuple, METH_VARARGS,"Make a tuple from arguments" }, 34 | 35 | {NULL, NULL, 0, NULL} // sentinel 36 | }; 37 | 38 | const char *pybase::py_doc = 39 | "py/pyext - python external object for PD and Max/MSP, (C)2002-2008 Thomas Grill\n" 40 | "\n" 41 | "This is the pyext module. Available function:\n" 42 | "_send(args...): Send a message to a send symbol\n" 43 | #ifdef FLEXT_THREADS 44 | "_priority(int): Raise/lower thread priority\n" 45 | #endif 46 | "_samplerate(): Get system sample rate\n" 47 | "_blocksize(): Get current blocksize\n" 48 | "_getvalue(name): Get value of a 'value' object\n" 49 | "_setvalue(name,float): Set value of a 'value' object\n" 50 | 51 | "_list(args...): Make a list from args\n" 52 | "_tuple(args...): Make a tuple from args\n" 53 | ; 54 | 55 | #if PY_MAJOR_VERSION >= 3 56 | PyModuleDef pybase::pyext_module_def = { 57 | PyModuleDef_HEAD_INIT, // PyModuleDef_Base m_base 58 | PYEXT_MODULE, // const char *m_name 59 | py_doc, // const char *m_doc 60 | -1, // Py_ssize_t m_size 61 | func_tbl, // PyMethodDef *m_methods 62 | NULL, // PyModuleDef_Slot *m_slots 63 | NULL, // traverseproc m_traverse 64 | NULL, // inquiry m_clear 65 | NULL // freefunc m_free 66 | }; 67 | #endif 68 | 69 | #ifdef FLEXT_THREADS 70 | void pybase::tick(void *) 71 | { 72 | Lock(); 73 | 74 | if(!thrcount) { 75 | // all threads have stopped 76 | shouldexit = false; 77 | stoptick = 0; 78 | } 79 | else { 80 | // still active threads 81 | if(!--stoptick) { 82 | post("py/pyext - Threads couldn't be stopped entirely - %i remaining",thrcount); 83 | shouldexit = false; 84 | } 85 | else 86 | // continue waiting 87 | stoptmr.Delay(PY_STOP_TICK/1000.); 88 | } 89 | 90 | Unlock(); 91 | } 92 | #endif 93 | 94 | void pybase::m_stop(int argc,const t_atom *argv) 95 | { 96 | #ifdef FLEXT_THREADS 97 | if(thrcount) { 98 | Lock(); 99 | 100 | int wait = PY_STOP_WAIT; 101 | if(argc >= 1 && CanbeInt(argv[0])) wait = GetAInt(argv[0]); 102 | 103 | int ticks = wait/PY_STOP_TICK; 104 | if(stoptick) { 105 | // already stopping 106 | if(ticks < stoptick) stoptick = ticks; 107 | } 108 | else 109 | stoptick = ticks; 110 | shouldexit = true; 111 | stoptmr.Delay(PY_STOP_TICK/1000.); 112 | 113 | Unlock(); 114 | } 115 | #endif 116 | } 117 | 118 | PyObject *pybase::py_samplerate(PyObject *self,PyObject *args) 119 | { 120 | return PyFloat_FromDouble(sys_getsr()); 121 | } 122 | 123 | PyObject *pybase::py_blocksize(PyObject *self,PyObject *args) 124 | { 125 | return PyLong_FromLong(sys_getblksize()); 126 | } 127 | 128 | PyObject *pybase::py_searchpaths(PyObject *self,PyObject *args) 129 | { 130 | #if FLEXT_SYS == FLEXT_SYS_PD && defined(PD_DEVEL_VERSION) && defined(PY_USE_INOFFICIAL) 131 | PyObject *ret = PyList_New(0); 132 | char *dir; 133 | for(int i = 0; (dir = namelist_get(sys_searchpath,i)) != NULL; ++i) 134 | PyList_Append(ret,PyString_FromString(dir)); 135 | return ret; 136 | #else 137 | Py_INCREF(Py_None); 138 | return Py_None; 139 | #endif 140 | } 141 | 142 | PyObject *pybase::py_helppaths(PyObject *self,PyObject *args) 143 | { 144 | #if FLEXT_SYS == FLEXT_SYS_PD && defined(PD_DEVEL_VERSION) && defined(PY_USE_INOFFICIAL) 145 | PyObject *ret = PyList_New(0); 146 | char *dir; 147 | for(int i = 0; (dir = namelist_get(sys_helppath,i)) != NULL; ++i) 148 | PyList_Append(ret,PyString_FromString(dir)); 149 | return ret; 150 | #else 151 | Py_INCREF(Py_None); 152 | return Py_None; 153 | #endif 154 | } 155 | 156 | PyObject *pybase::py_send(PyObject *,PyObject *args) 157 | { 158 | // should always be a tuple 159 | FLEXT_ASSERT(PyTuple_Check(args)); 160 | 161 | const int sz = PyTuple_GET_SIZE(args); 162 | 163 | const t_symbol *recv; 164 | if( 165 | sz >= 1 && 166 | (recv = pyObject_AsSymbol(PyTuple_GET_ITEM(args,0))) != NULL 167 | ) { 168 | PyObject *val; 169 | 170 | #if 0 171 | bool tp = 172 | sz == 2 && 173 | PySequence_Check( 174 | val = PyTuple_GET_ITEM(args,1) // borrowed ref 175 | ); 176 | 177 | if(!tp) 178 | val = PySequence_GetSlice(args,1,sz); // new ref 179 | #else 180 | if(sz == 2) { 181 | val = PyTuple_GET_ITEM(args,1); // borrow reference 182 | Py_INCREF(val); 183 | } 184 | else 185 | val = PySequence_GetSlice(args,1,sz); // new ref 186 | #endif 187 | 188 | AtomListStatic<16> lst; 189 | const t_symbol *sym = GetPyArgs(lst,val); 190 | Py_DECREF(val); 191 | 192 | if(sym) { 193 | bool ok; 194 | if(sym == sym_list && !lst.Count()) 195 | // empty list is translated to a bang message 196 | ok = Forward(recv,sym_bang,0,NULL); 197 | else 198 | ok = Forward(recv,sym,lst.Count(),lst.Atoms()); 199 | #ifdef FLEXT_DEBUG 200 | if(!ok) 201 | post("py/pyext - Receiver doesn't exist"); 202 | #endif 203 | Py_INCREF(Py_None); 204 | return Py_None; 205 | } 206 | /* 207 | else if(PyErr_Occurred()) 208 | PyErr_Print(); 209 | else 210 | post("py/pyext - No data to send"); 211 | */ 212 | else { 213 | FLEXT_ASSERT(PyErr_Occurred()); 214 | return NULL; 215 | } 216 | } 217 | /* 218 | else 219 | post("py/pyext - Send name is invalid"); 220 | */ 221 | else { 222 | PyErr_SetString(PyExc_ValueError,"py/pyext - Send name is invalid"); 223 | return NULL; 224 | } 225 | } 226 | 227 | #ifdef FLEXT_THREADS 228 | PyObject *pybase::py_priority(PyObject *self,PyObject *args) 229 | { 230 | int val; 231 | if(!PyArg_ParseTuple(args, "i:py_priority", &val)) { 232 | post("py/pyext - Syntax: _priority [int]"); 233 | } 234 | else 235 | RelPriority(val); 236 | 237 | Py_INCREF(Py_None); 238 | return Py_None; 239 | } 240 | #endif 241 | 242 | #if FLEXT_SYS == FLEXT_SYS_PD 243 | PyObject *pybase::py_getvalue(PyObject *self,PyObject *args) 244 | { 245 | FLEXT_ASSERT(PyTuple_Check(args)); 246 | 247 | const int sz = PyTuple_GET_SIZE(args); 248 | const t_symbol *sym; 249 | PyObject *ret; 250 | 251 | if( 252 | sz == 1 && 253 | (sym = pyObject_AsSymbol(PyTuple_GET_ITEM(args,0))) != NULL 254 | ) { 255 | t_float f; 256 | if(value_getfloat(const_cast(sym),&f)) { 257 | post("py/pyext - Could not get value '%s'",GetString(sym)); 258 | Py_INCREF(Py_None); 259 | ret = Py_None; 260 | } 261 | else 262 | ret = PyFloat_FromDouble(f); 263 | } 264 | else { 265 | post("py/pyext - Syntax: _getvalue [name]"); 266 | Py_INCREF(Py_None); 267 | ret = Py_None; 268 | } 269 | return ret; 270 | } 271 | 272 | PyObject *pybase::py_setvalue(PyObject *self,PyObject *args) 273 | { 274 | FLEXT_ASSERT(PyTuple_Check(args)); 275 | 276 | const int sz = PyTuple_GET_SIZE(args); 277 | const t_symbol *sym; 278 | PyObject *val; // borrowed reference 279 | 280 | if( 281 | sz == 2 && 282 | (sym = pyObject_AsSymbol(PyTuple_GET_ITEM(args,0))) != NULL && 283 | PyNumber_Check(val = PyTuple_GET_ITEM(args,1)) 284 | ) { 285 | float f = (float)PyFloat_AsDouble(val); 286 | 287 | if(value_setfloat(const_cast(sym),f)) 288 | post("py/pyext - Could not set value '%s'",GetString(sym)); 289 | } 290 | else 291 | post("py/pyext - Syntax: _setvalue [name] [value]"); 292 | 293 | Py_INCREF(Py_None); 294 | return Py_None; 295 | } 296 | #endif 297 | 298 | PyObject *pybase::py_list(PyObject *,PyObject *args) 299 | { 300 | // should always be a tuple 301 | FLEXT_ASSERT(PyTuple_Check(args)); 302 | 303 | const int sz = PyTuple_GET_SIZE(args); 304 | PyObject *ret = PyList_New(sz); 305 | for(int i = 0; i < sz; ++i) { 306 | PyObject *el = PyTuple_GET_ITEM(args,i); 307 | Py_INCREF(el); 308 | PyList_SET_ITEM(ret,i,el); 309 | } 310 | return ret; 311 | } 312 | 313 | PyObject *pybase::py_tuple(PyObject *,PyObject *args) 314 | { 315 | // should always be a tuple 316 | FLEXT_ASSERT(PyTuple_Check(args)); 317 | Py_INCREF(args); 318 | return args; 319 | } 320 | -------------------------------------------------------------------------------- /source/py.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | py/pyext - python script object for PD and Max/MSP 3 | 4 | Copyright (c)2002-2015 Thomas Grill (gr@grrrr.org) 5 | For information on usage and redistribution, and for a DISCLAIMER OF ALL 6 | WARRANTIES, see the file, "license.txt," in this distribution. 7 | */ 8 | 9 | #include "pybase.h" 10 | 11 | class pyobj 12 | : public pybase 13 | , public flext_base 14 | { 15 | FLEXT_HEADER_S(pyobj,flext_base,Setup) 16 | 17 | public: 18 | pyobj(int argc,const t_atom *argv); 19 | ~pyobj(); 20 | 21 | protected: 22 | virtual void Exit(); 23 | 24 | virtual bool CbMethodResort(int n,const t_symbol *s,int argc,const t_atom *argv); 25 | virtual void CbClick(); 26 | 27 | void m_help(); 28 | 29 | void m_reload() { Reload(); } 30 | void m_reload_(int argc,const t_atom *argv) { args(argc,argv); Reload(); } 31 | void m_set(int argc,const t_atom *argv); 32 | void m_dir_() { m__dir(function); } 33 | void m_doc_() { m__doc(function); } 34 | 35 | const t_symbol *funname; 36 | PyObject *function; 37 | bool withfunction; 38 | 39 | virtual void LoadModule(); 40 | virtual void UnloadModule(); 41 | 42 | virtual void Load(); 43 | virtual void Unload(); 44 | 45 | bool SetFunction(const t_symbol *func); 46 | bool ResetFunction(); 47 | 48 | virtual void DumpOut(const t_symbol *sym,int argc,const t_atom *argv); 49 | 50 | PyObject **objects; 51 | 52 | private: 53 | 54 | virtual void callpy(PyObject *fun,PyObject *args); 55 | 56 | static void Setup(t_classid c); 57 | 58 | FLEXT_CALLBACK(m_help) 59 | FLEXT_CALLBACK(m_reload) 60 | FLEXT_CALLBACK_V(m_reload_) 61 | FLEXT_CALLBACK_V(m_set) 62 | FLEXT_CALLBACK(m_dir_) 63 | FLEXT_CALLBACK(m_doc_) 64 | 65 | // callbacks 66 | FLEXT_ATTRVAR_I(detach) 67 | FLEXT_ATTRVAR_B(pymsg) 68 | FLEXT_ATTRVAR_B(respond) 69 | 70 | FLEXT_CALLBACK_V(m_stop) 71 | FLEXT_CALLBACK(m_dir) 72 | FLEXT_CALLGET_V(mg_dir) 73 | FLEXT_CALLBACK(m_doc) 74 | 75 | FLEXT_CALLBACK(CbClick) 76 | 77 | #ifdef FLEXT_THREADS 78 | FLEXT_CALLBACK_T(tick) 79 | #endif 80 | }; 81 | 82 | FLEXT_LIB_V("py",pyobj) 83 | 84 | 85 | void pyobj::Setup(t_classid c) 86 | { 87 | FLEXT_CADDMETHOD_(c,0,"doc",m_doc); 88 | FLEXT_CADDMETHOD_(c,0,"dir",m_dir); 89 | #ifdef FLEXT_THREADS 90 | FLEXT_CADDATTR_VAR1(c,"detach",detach); 91 | FLEXT_CADDMETHOD_(c,0,"stop",m_stop); 92 | #endif 93 | 94 | FLEXT_CADDMETHOD_(c,0,"help",m_help); 95 | FLEXT_CADDMETHOD_(c,0,"reload",m_reload_); 96 | FLEXT_CADDMETHOD_(c,0,"reload.",m_reload); 97 | FLEXT_CADDMETHOD_(c,0,"doc+",m_doc_); 98 | FLEXT_CADDMETHOD_(c,0,"dir+",m_dir_); 99 | 100 | FLEXT_CADDMETHOD_(c,0,"set",m_set); 101 | 102 | FLEXT_CADDMETHOD_(c,0,"edit",CbClick); 103 | 104 | FLEXT_CADDATTR_VAR1(c,"py",pymsg); 105 | FLEXT_CADDATTR_VAR1(c,"respond",respond); 106 | } 107 | 108 | pyobj::pyobj(int argc,const t_atom *argv) 109 | : funname(NULL) 110 | , function(NULL) 111 | , withfunction(false) 112 | , objects(NULL) 113 | { 114 | #ifdef FLEXT_THREADS 115 | FLEXT_ADDTIMER(stoptmr,tick); 116 | #endif 117 | 118 | ThrState state = PyLockSys(); 119 | 120 | int inlets; 121 | if(argc && CanbeInt(*argv)) { 122 | inlets = GetAInt(*argv); 123 | if(inlets < 0) inlets = 1; 124 | argv++,argc--; 125 | } 126 | else 127 | // -1 signals non-explicit definition 128 | inlets = -1; 129 | 130 | if(inlets >= 1) { 131 | objects = new PyObject *[inlets]; 132 | for(int i = 0; i < inlets; ++i) { objects[i] = Py_None; Py_INCREF(Py_None); } 133 | } 134 | 135 | AddInAnything(1+(inlets < 0?1:inlets)); 136 | AddOutAnything(); 137 | 138 | const t_symbol *funnm = NULL; 139 | 140 | // init script module 141 | if(argc) { 142 | AddCurrentPath(this); 143 | 144 | const char *sn = GetAString(*argv); 145 | argv++,argc--; 146 | 147 | if(sn) { 148 | char modnm[64]; 149 | strcpy(modnm,sn); 150 | 151 | char *pt = strrchr(modnm,'.'); // search for last dot 152 | if(pt && *pt) { 153 | funnm = MakeSymbol(pt+1); 154 | *pt = 0; 155 | } 156 | 157 | if(*modnm) 158 | ImportModule(modnm); 159 | else 160 | ImportModule(NULL); 161 | } 162 | else 163 | PyErr_SetString(PyExc_ValueError,"Invalid module name"); 164 | } 165 | 166 | Register(GetRegistry(REGNAME)); 167 | 168 | if(funnm || argc) { 169 | if(!funnm) { 170 | funnm = GetASymbol(*argv); 171 | argv++,argc--; 172 | } 173 | 174 | if(funnm) 175 | SetFunction(funnm); 176 | else 177 | PyErr_SetString(PyExc_ValueError,"Invalid function name"); 178 | } 179 | 180 | if(argc) args(argc,argv); 181 | 182 | Report(); 183 | 184 | PyUnlock(state); 185 | } 186 | 187 | pyobj::~pyobj() 188 | { 189 | ThrState state = PyLockSys(); 190 | if(objects) { 191 | for(int i = 0; i < CntIn()-1; ++i) Py_DECREF(objects[i]); 192 | delete[] objects; 193 | } 194 | 195 | Unregister(GetRegistry(REGNAME)); 196 | Report(); 197 | PyUnlock(state); 198 | } 199 | 200 | void pyobj::Exit() 201 | { 202 | pybase::Exit(); 203 | flext_base::Exit(); 204 | } 205 | 206 | void pyobj::m_set(int argc,const t_atom *argv) 207 | { 208 | ThrState state = PyLockSys(); 209 | 210 | // function name has precedence 211 | if(argc >= 2) { 212 | const char *sn = GetAString(*argv); 213 | ++argv,--argc; 214 | 215 | if(sn) { 216 | // if(!module || !strcmp(sn,PyModule_GetName(module))) 217 | { 218 | ImportModule(sn); 219 | Register(GetRegistry(REGNAME)); 220 | } 221 | } 222 | else 223 | PyErr_SetString(PyExc_ValueError,"Invalid module name"); 224 | } 225 | 226 | if(argc) { 227 | const t_symbol *fn = GetASymbol(*argv); 228 | if(fn) 229 | SetFunction(fn); 230 | else 231 | PyErr_SetString(PyExc_ValueError,"Invalid function name"); 232 | } 233 | 234 | Report(); 235 | 236 | PyUnlock(state); 237 | } 238 | 239 | void pyobj::m_help() 240 | { 241 | post(""); 242 | post("%s %s - python script object, (C)2002-2012 Thomas Grill",thisName(),PY__VERSION); 243 | #ifdef FLEXT_DEBUG 244 | post("DEBUG VERSION, compiled on " __DATE__ " " __TIME__); 245 | #endif 246 | 247 | post("Arguments: %s [script name] [function name] {args...}",thisName()); 248 | 249 | post("Inlet 1:messages to control the py object"); 250 | post(" 2:call python function with message as argument(s)"); 251 | post("Outlet: 1:return values from python function"); 252 | post("Methods:"); 253 | post("\thelp: shows this help"); 254 | post("\tbang: call script without arguments"); 255 | post("\tset [script name] [function name]: set (script and) function name"); 256 | post("\treload {args...}: reload python script"); 257 | post("\treload. : reload with former arguments"); 258 | post("\tdoc: display module doc string"); 259 | post("\tdoc+: display function doc string"); 260 | post("\tdir: dump module dictionary"); 261 | post("\tdir+: dump function dictionary"); 262 | #ifdef FLEXT_THREADS 263 | post("\tdetach 0/1/2: detach threads"); 264 | post("\tstop {wait time (ms)}: stop threads"); 265 | #endif 266 | post(""); 267 | } 268 | 269 | bool pyobj::ResetFunction() 270 | { 271 | // function was borrowed from dict! 272 | function = NULL; 273 | 274 | if(!dict) 275 | post("%s - No namespace available",thisName()); 276 | else { 277 | if(funname) { 278 | function = PyDict_GetItemString(dict,(char *)GetString(funname)); // borrowed!!! 279 | 280 | if(!function && dict == module_dict) 281 | // search also in __builtins__ 282 | function = PyDict_GetItemString(builtins_dict,(char *)GetString(funname)); // borrowed!!! 283 | 284 | if(!function) 285 | PyErr_SetString(PyExc_AttributeError,"Function not found"); 286 | else if(!PyCallable_Check(function)) { 287 | function = NULL; 288 | PyErr_SetString(PyExc_TypeError,"Attribute is not callable"); 289 | } 290 | } 291 | } 292 | 293 | // exception could be set here 294 | return function != NULL; 295 | } 296 | 297 | bool pyobj::SetFunction(const t_symbol *func) 298 | { 299 | if(func) { 300 | funname = func; 301 | withfunction = ResetFunction(); 302 | } 303 | else { 304 | function = NULL,funname = NULL; 305 | withfunction = false; 306 | } 307 | 308 | // exception could be set here 309 | return withfunction; 310 | } 311 | 312 | 313 | void pyobj::LoadModule() 314 | { 315 | SetFunction(funname); 316 | } 317 | 318 | void pyobj::UnloadModule() 319 | { 320 | } 321 | 322 | void pyobj::Load() 323 | { 324 | ResetFunction(); 325 | } 326 | 327 | void pyobj::Unload() 328 | { 329 | // SetFunction(NULL); 330 | function = NULL; // just clear the PyObject, not the function name 331 | } 332 | 333 | void pyobj::callpy(PyObject *fun,PyObject *args) 334 | { 335 | PyObject *ret = PyObject_CallObject(fun,args); 336 | if(ret) { 337 | OutObject(this,0,ret); // exception might be raised here 338 | Py_DECREF(ret); 339 | } 340 | } 341 | 342 | bool pyobj::CbMethodResort(int n,const t_symbol *s,int argc,const t_atom *argv) 343 | { 344 | if(n == 0 && s != sym_bang) 345 | return flext_base::CbMethodResort(n,s,argc,argv); 346 | 347 | ThrState state = PyLockSys(); 348 | 349 | bool ret = false; 350 | 351 | if(objects && n >= 1) { 352 | // store args 353 | PyObject *&obj = objects[n-1]; 354 | Py_DECREF(obj); 355 | obj = MakePyArg(s,argc,argv); // steal reference 356 | 357 | if(n > 1) ret = true; // just store, don't trigger 358 | } 359 | 360 | if(!ret) { 361 | if(withfunction) { 362 | if(function) { 363 | Py_INCREF(function); 364 | 365 | PyObject *pargs; 366 | 367 | if(objects || CntIn() == 1) { 368 | int inlets = CntIn()-1; 369 | pargs = PyTuple_New(inlets); 370 | for(int i = 0; i < inlets; ++i) { 371 | Py_INCREF(objects[i]); 372 | PyTuple_SET_ITEM(pargs,i,objects[i]); 373 | } 374 | } 375 | else 376 | // construct tuple from args 377 | // if n == 0, it's a pure bang 378 | pargs = MakePyArgs(n?s:NULL,argc,argv); 379 | 380 | gencall(function,pargs); // references are stolen 381 | ret = true; 382 | } 383 | else 384 | PyErr_SetString(PyExc_RuntimeError,"No function set"); 385 | } 386 | else if(module) { 387 | // no function defined as creation argument -> use message tag 388 | if(s) { 389 | PyObject *func = PyObject_GetAttrString(module,const_cast(GetString(s))); 390 | if(func) { 391 | PyObject *pargs = MakePyArgs(sym_list,argc,argv); 392 | gencall(func,pargs); 393 | ret = true; 394 | } 395 | } 396 | else 397 | PyErr_SetString(PyExc_RuntimeError,"No function set"); 398 | } 399 | 400 | Report(); 401 | } 402 | 403 | PyUnlock(state); 404 | 405 | Respond(ret); 406 | 407 | return ret; 408 | } 409 | 410 | void pyobj::CbClick() { pybase::OpenEditor(); } 411 | 412 | void pyobj::DumpOut(const t_symbol *sym,int argc,const t_atom *argv) 413 | { 414 | ToOutAnything(GetOutAttr(),sym?sym:thisTag(),argc,argv); 415 | } 416 | -------------------------------------------------------------------------------- /source/pyargs.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | py/pyext - python external object for PD and MaxMSP 3 | 4 | Copyright (c)2002-2019 Thomas Grill (gr@grrrr.org) 5 | For information on usage and redistribution, and for a DISCLAIMER OF ALL 6 | WARRANTIES, see the file, "license.txt," in this distribution. 7 | */ 8 | 9 | #include "pybase.h" 10 | #include "pyatom.h" 11 | 12 | static const t_symbol *symatom = flext::MakeSymbol(" py "); 13 | 14 | static PyObject *MakePyAtom(const t_atom &at) 15 | { 16 | if(flext::IsSymbol(at)) 17 | return pySymbol_FromSymbol(flext::GetSymbol(at)); 18 | #if 1 19 | else if(flext::CanbeFloat(at)) { 20 | // if a number can be an integer... let it be an integer! 21 | int ival = flext::GetAInt(at); 22 | double fval = flext::GetAFloat(at); 23 | return (double)ival == fval? 24 | #if PY_MAJOR_VERSION < 3 25 | PyInt_FromLong(ival) 26 | #else 27 | PyLong_FromLong(ival) 28 | #endif 29 | :PyFloat_FromDouble(fval); 30 | } 31 | #else 32 | else if(flext::IsFloat(at)) 33 | return PyFloat_FromDouble(flext::GetFloat(at)); 34 | else if(flext::IsInt(at)) 35 | return PyInt_FromLong(flext::GetInt(at)); 36 | #endif 37 | return NULL; 38 | } 39 | 40 | static PyObject *MakePyAtom(int argc,const t_atom *argv) 41 | { 42 | if(argc != sizeof(size_t)/2) return NULL; 43 | 44 | size_t atom = 0; 45 | for(int i = sizeof(size_t)/2-1; i >= 0; --i) 46 | if(!flext::CanbeInt(argv[i])) { 47 | atom = 0; 48 | break; 49 | } 50 | else 51 | atom = (atom<<16)+flext::GetAInt(argv[i]); 52 | 53 | if(atom) { 54 | PyObject *el = PyAtom::Retrieve(atom); 55 | if(!el) el = Py_None; // object already gone.... 56 | Py_INCREF(el); 57 | return el; 58 | } 59 | else 60 | return NULL; 61 | } 62 | 63 | PyObject *pybase::MakePyArgs(const t_symbol *s,int argc,const t_atom *argv,int inlet) 64 | { 65 | PyObject *ret,*el; 66 | 67 | if(s == symatom && (el = MakePyAtom(argc,argv)) != NULL) { 68 | ret = PyTuple_New(1); 69 | PyTuple_SET_ITEM(ret,0,el); 70 | } 71 | else { 72 | bool any = IsAnything(s); 73 | ret = PyTuple_New(argc+(any?1:0)+(inlet >= 0?1:0)); 74 | 75 | int pix = 0; 76 | 77 | if(inlet >= 0) 78 | PyTuple_SET_ITEM(ret, pix++, 79 | #if PY_MAJOR_VERSION < 3 80 | PyInt_FromLong(inlet) 81 | #else 82 | PyLong_FromLong(inlet) 83 | #endif 84 | ); 85 | 86 | if(any) 87 | PyTuple_SET_ITEM(ret,pix++,pySymbol_FromSymbol(s)); 88 | 89 | for(int i = 0; i < argc; ++i) { 90 | el = MakePyAtom(argv[i]); 91 | if(!el) { 92 | post("py/pyext: cannot convert argument %i",any?i+1:i); 93 | 94 | el = Py_None; 95 | Py_INCREF(Py_None); 96 | } 97 | 98 | PyTuple_SET_ITEM(ret,pix++,el); // reference stolen 99 | } 100 | } 101 | 102 | return ret; 103 | } 104 | 105 | PyObject *pybase::MakePyArg(const t_symbol *s,int argc,const t_atom *argv) 106 | { 107 | PyObject *ret; 108 | 109 | if(s == symatom && (ret = MakePyAtom(argc,argv)) != NULL) { 110 | // ok! 111 | } 112 | else if(argc == 1 && !IsAnything(s)) 113 | // convert atoms and one-element lists 114 | ret = MakePyAtom(*argv); 115 | else { 116 | bool any = s != sym_list; 117 | ret = PyTuple_New(argc+(any?1:0)); 118 | 119 | int pix = 0; 120 | if(any) 121 | PyTuple_SET_ITEM(ret,pix++,pySymbol_FromSymbol(s)); 122 | 123 | for(int i = 0; i < argc; ++i) { 124 | PyObject *el = MakePyAtom(argv[i]); 125 | if(!el) { 126 | post("py/pyext: cannot convert argument %i",any?i+1:i); 127 | 128 | el = Py_None; 129 | Py_INCREF(Py_None); 130 | } 131 | 132 | PyTuple_SET_ITEM(ret,pix++,el); // reference stolen 133 | } 134 | } 135 | 136 | return ret; 137 | } 138 | 139 | inline bool issym(PyObject *p) 140 | { 141 | return 142 | PyUnicode_Check(p) 143 | #if PY_MAJOR_VERSION < 3 144 | || PyString_Check(p) 145 | #endif 146 | || pySymbol_Check(p); 147 | } 148 | 149 | inline bool isseq(PyObject *p) 150 | { 151 | return PySequence_Check(p) && !issym(p); 152 | } 153 | 154 | const t_symbol *pybase::getone(t_atom &at,PyObject *arg) 155 | { 156 | #if PY_MAJOR_VERSION < 3 157 | if(PyInt_Check(arg)) { flext::SetInt(at,PyInt_AsLong(arg)); return sym_fint; } 158 | else 159 | #endif 160 | if(PyLong_Check(arg)) { flext::SetInt(at,PyLong_AsLong(arg)); return sym_fint; } 161 | else if(PyFloat_Check(arg)) { flext::SetFloat(at,(float)PyFloat_AsDouble(arg)); return flext::sym_float; } 162 | else if(pySymbol_Check(arg)) { flext::SetSymbol(at,pySymbol_AS_SYMBOL(arg)); return flext::sym_symbol; } 163 | #if PY_MAJOR_VERSION < 3 164 | else if(PyString_Check(arg)) { flext::SetString(at,PyString_AS_STRING(arg)); return flext::sym_symbol; } 165 | #else 166 | else if(PyUnicode_Check(arg)) { flext::SetString(at,PyUnicode_AsUTF8(arg)); return flext::sym_symbol; } 167 | #endif 168 | else { 169 | PyObject *tp = PyObject_Type(arg); 170 | PyObject *stp = tp?PyObject_Str(tp):NULL; 171 | const char *tmp = ""; 172 | if(stp) 173 | #if PY_MAJOR_VERSION < 3 174 | tmp = PyString_AS_STRING(stp); 175 | #else 176 | tmp = PyUnicode_AsUTF8(stp); 177 | #endif 178 | flext::post("py/pyext: Could not convert argument %s",tmp); 179 | Py_XDECREF(stp); 180 | Py_XDECREF(tp); 181 | 182 | flext::SetSymbol(at,flext::sym__); 183 | return sym_symbol; 184 | } 185 | } 186 | 187 | const t_symbol *pybase::getlist(t_atom *lst,PyObject *seq,int cnt,int offs) 188 | { 189 | for(int ix = 0; ix < cnt; ++ix) { 190 | PyObject *arg = PySequence_GetItem(seq,ix+offs); // new reference 191 | getone(lst[ix],arg); 192 | Py_DECREF(arg); 193 | } 194 | return flext::sym_list; 195 | } 196 | 197 | const t_symbol *pybase::GetPyArgs(AtomList &lst,PyObject *pValue,int offs) 198 | { 199 | if(pValue == NULL) return NULL; 200 | 201 | // output bang on None returned 202 | if(pValue == Py_None) return sym_bang; 203 | 204 | // analyze return value or tuple 205 | const t_symbol *sym = NULL; 206 | 207 | if(isseq(pValue)) { 208 | // Python might crash here if pValue is no "real" sequence, but rather e.g. an instance 209 | 210 | int rargc = PySequence_Size(pValue); 211 | 212 | if(rargc == 2) { 213 | // check if syntax is symbol/string, list -> anything message 214 | PyObject *s = PySequence_GetItem(pValue,0); 215 | PyObject *l = PySequence_GetItem(pValue,1); 216 | 217 | if(issym(s) && isseq(l)) { 218 | // is anything message 219 | rargc = PySequence_Size(l); 220 | lst(offs+rargc); 221 | getlist(lst.Atoms(),l,rargc); 222 | sym = pyObject_AsSymbol(s); 223 | } 224 | else { 225 | // (symbol,atom) list 226 | lst(offs+rargc); 227 | sym = getlist(lst.Atoms(),pValue,rargc); 228 | } 229 | 230 | Py_DECREF(s); 231 | Py_DECREF(l); 232 | } 233 | else { 234 | lst(offs+rargc); 235 | sym = getlist(lst.Atoms(),pValue,rargc); 236 | } 237 | } 238 | else { 239 | lst(offs+1); 240 | sym = getone(lst[offs],pValue); 241 | } 242 | 243 | return sym; 244 | } 245 | 246 | 247 | const t_symbol *pybase::GetPyAtom(AtomList &lst,PyObject *obj) 248 | { 249 | size_t atom = PyAtom::Register(obj); 250 | size_t szat = sizeof(atom)/2; 251 | 252 | lst(szat); 253 | for(size_t i = 0; i < szat; ++i,atom >>= 16) 254 | flext::SetInt(lst[i],(int)(atom&((1<<16)-1))); 255 | return symatom; 256 | } 257 | -------------------------------------------------------------------------------- /source/pyatom.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | py/pyext - python script object for PD and Max/MSP 3 | 4 | Copyright (c)2002-2015 Thomas Grill (gr@grrrr.org) 5 | For information on usage and redistribution, and for a DISCLAIMER OF ALL 6 | WARRANTIES, see the file, "license.txt," in this distribution. 7 | */ 8 | 9 | #include "pyatom.h" 10 | #include 11 | 12 | #define INTV 0.01 13 | 14 | typedef std::map ObjMap; 15 | 16 | static ObjMap objmap; 17 | static size_t collix = 0,curix = 0; 18 | static double last = 0; 19 | 20 | size_t PyAtom::Register(PyObject *obj) 21 | { 22 | Collect(); 23 | 24 | Py_INCREF(obj); 25 | objmap[++curix] = obj; 26 | 27 | #ifdef _DEBUG 28 | // post("REG %p (%i)\n",obj,objmap.size()); 29 | #endif 30 | return curix; 31 | } 32 | 33 | PyObject *PyAtom::Retrieve(size_t id) 34 | { 35 | ObjMap::iterator it = objmap.find(id); 36 | PyObject *ret = it == objmap.end()?NULL:it->second; 37 | Collect(); 38 | return ret; 39 | } 40 | 41 | void PyAtom::Collect() 42 | { 43 | for(;;) { 44 | ObjMap::iterator it = objmap.begin(); 45 | if(it == objmap.end() || it->first > collix) break; 46 | 47 | PyObject *obj = it->second; 48 | Py_DECREF(obj); 49 | objmap.erase(it); 50 | 51 | #ifdef _DEBUG 52 | // post("DEL %p\n",obj); 53 | #endif 54 | } 55 | 56 | // schedule next collect time 57 | double tm = flext::GetTime(); 58 | if(tm > last+INTV) last = tm,collix = curix; 59 | } 60 | -------------------------------------------------------------------------------- /source/pyatom.h: -------------------------------------------------------------------------------- 1 | /* 2 | py/pyext - python script object for PD and Max/MSP 3 | 4 | Copyright (c)2002-2015 Thomas Grill (gr@grrrr.org) 5 | For information on usage and redistribution, and for a DISCLAIMER OF ALL 6 | WARRANTIES, see the file, "license.txt," in this distribution. 7 | */ 8 | 9 | #include "main.h" 10 | 11 | class PyAtom 12 | { 13 | public: 14 | static size_t Register(PyObject *obj); 15 | static PyObject *Retrieve(size_t id); 16 | static void Collect(); 17 | }; 18 | 19 | -------------------------------------------------------------------------------- /source/pybase.h: -------------------------------------------------------------------------------- 1 | /* 2 | py/pyext - python script object for PD and MaxMSP 3 | 4 | Copyright (c)2002-2015 Thomas Grill (gr@grrrr.org) 5 | For information on usage and redistribution, and for a DISCLAIMER OF ALL 6 | WARRANTIES, see the file, "license.txt," in this distribution. 7 | */ 8 | 9 | #ifndef __PYBASE_H 10 | #define __PYBASE_H 11 | 12 | #include "main.h" 13 | #include "pysymbol.h" 14 | #include "pybuffer.h" 15 | #include "pybundle.h" 16 | 17 | #ifdef FLEXT_THREADS 18 | # ifdef PY_USE_GIL 19 | typedef PyGILState_STATE ThrState; 20 | # else 21 | typedef PyThreadState *ThrState; 22 | # endif 23 | #else 24 | typedef int ThrState; // dummy 25 | #endif 26 | 27 | #if PY_MAJOR_VERSION < 3 28 | #define MOD_ERROR_VAL 29 | #define MOD_SUCCESS_VAL(val) 30 | #define MOD_INIT_NAME(name) init##name 31 | #define MOD_INIT(name) void MOD_INIT_NAME(name)(void) 32 | #define MOD_DEF(ob, name, doc, methods) \ 33 | ob = Py_InitModule3(name, methods, doc); 34 | #else 35 | #define MOD_ERROR_VAL NULL 36 | #define MOD_SUCCESS_VAL(val) val 37 | #define MOD_INIT_NAME(name) PyInit_##name 38 | #define MOD_INIT(name) PyMODINIT_FUNC MOD_INIT_NAME(name)(void) 39 | #define MOD_DEF(ob, name, doc, methods) \ 40 | static struct PyModuleDef moduledef = { \ 41 | PyModuleDef_HEAD_INIT, name, doc, -1, methods, }; \ 42 | ob = PyModule_Create(&moduledef); 43 | #endif 44 | 45 | MOD_INIT(pyext); 46 | 47 | class pybase 48 | : public flext 49 | { 50 | public: 51 | pybase(); 52 | virtual ~pybase(); 53 | 54 | void Exit(); 55 | 56 | static PyObject *MakePyArgs(const t_symbol *s,int argc,const t_atom *argv,int inlet = -1); 57 | static PyObject *MakePyArg(const t_symbol *s,int argc,const t_atom *argv); 58 | static const t_symbol *GetPyArgs(AtomList &lst,PyObject *pValue,int offs = 0); 59 | static const t_symbol *GetPyAtom(AtomList &lst,PyObject *pValue); 60 | 61 | static PyObject *pyext_init(); 62 | static void lib_setup(); 63 | 64 | protected: 65 | 66 | virtual void DumpOut(const t_symbol *sym,int argc,const t_atom *argv) = 0; 67 | 68 | void m__dir(PyObject *obj); 69 | void m__doc(PyObject *obj); 70 | 71 | void m_dir() { m__dir(module); } 72 | void mg_dir(AtomList &lst) { m__dir(module); } 73 | void m_doc() { m__doc(dict); } 74 | 75 | std::string modname; // module name 76 | PyObject *module,*dict; // object module and associated dictionary 77 | 78 | static const char *py_doc; 79 | 80 | void GetDir(PyObject *obj,AtomList &lst); 81 | 82 | AtomList args; 83 | 84 | void AddCurrentPath(flext_base *o); 85 | void SetArgs(); 86 | 87 | bool OutObject(flext_base *ext,int o,PyObject *obj); 88 | 89 | // reload module and all connected objects 90 | void Reload(); 91 | 92 | bool ImportModule(const char *name); 93 | void UnimportModule(); 94 | bool ReloadModule(); 95 | 96 | // Get module registry 97 | PyObject *GetRegistry(const char *regname); 98 | // Set module registry 99 | void SetRegistry(const char *regname,PyObject *reg); 100 | 101 | // Register object 102 | void Register(PyObject *reg); 103 | // Unregister object 104 | void Unregister(PyObject *reg); 105 | 106 | virtual void LoadModule() = 0; 107 | virtual void UnloadModule() = 0; 108 | 109 | virtual void Load() = 0; 110 | virtual void Unload() = 0; 111 | 112 | void OpenEditor(); 113 | 114 | void Respond(bool b) 115 | { 116 | if(respond) { 117 | t_atom a; 118 | SetBool(a,b); 119 | DumpOut(sym_response,1,&a); 120 | } 121 | } 122 | 123 | void Report() { while(PyErr_Occurred()) PyErr_Print(); } 124 | 125 | static bool IsAnything(const t_symbol *s) { return s && s != sym_float && s != sym_int && s != sym_symbol && s != sym_list && s != sym_pointer; } 126 | static bool IsAtom(const t_symbol *s) { return s == sym_float || s == sym_int || s == sym_symbol || s == sym_pointer; } 127 | 128 | // enum retval { nothing,atom,sequ }; 129 | 130 | // --- module stuff ----- 131 | 132 | static PyObject *module_obj,*module_dict; 133 | static PyObject *builtins_obj,*builtins_dict; 134 | static PyMethodDef func_tbl[]; 135 | #if PY_MAJOR_VERSION >= 3 136 | static PyModuleDef pyext_module_def; 137 | #endif 138 | 139 | static PyObject *py__doc__(PyObject *,PyObject *args); 140 | static PyObject *py_send(PyObject *,PyObject *args); 141 | #ifdef FLEXT_THREADS 142 | static PyObject *py_priority(PyObject *,PyObject *args); 143 | #endif 144 | 145 | static PyObject *py_arraysupport(PyObject *,PyObject *args); 146 | static PyObject *py_samplerate(PyObject *,PyObject *args); 147 | static PyObject *py_blocksize(PyObject *,PyObject *args); 148 | 149 | static PyObject *py_searchpaths(PyObject *,PyObject *args); 150 | static PyObject *py_helppaths(PyObject *,PyObject *args); 151 | 152 | #if FLEXT_SYS == FLEXT_SYS_PD 153 | static PyObject *py_getvalue(PyObject *,PyObject *args); 154 | static PyObject *py_setvalue(PyObject *,PyObject *args); 155 | #endif 156 | 157 | static PyObject *py_list(PyObject *,PyObject *args); 158 | static PyObject *py_tuple(PyObject *,PyObject *args); 159 | 160 | // ----thread stuff ------------ 161 | 162 | virtual void m_stop(int argc,const t_atom *argv); 163 | 164 | bool respond; 165 | #ifdef FLEXT_THREADS 166 | int thrcount; 167 | bool shouldexit; 168 | int stoptick; 169 | Timer stoptmr; 170 | 171 | void tick(void *); 172 | #endif 173 | 174 | int detach; 175 | bool pymsg; 176 | 177 | bool gencall(PyObject *fun,PyObject *args); 178 | 179 | bool docall(PyObject *fun,PyObject *args) 180 | { 181 | callpy(fun,args); 182 | if(PyErr_Occurred()) { 183 | exchandle(); 184 | return false; 185 | } 186 | else 187 | return true; 188 | } 189 | 190 | virtual void callpy(PyObject *fun,PyObject *args) = 0; 191 | 192 | void exchandle(); 193 | 194 | static bool collect(); 195 | 196 | protected: 197 | 198 | #ifdef FLEXT_THREADS 199 | static void thrworker(thr_params *data); 200 | 201 | bool qucall(PyObject *fun,PyObject *args) 202 | { 203 | FifoEl *el = qufifo.New(); 204 | el->Set(this,fun,args); 205 | qufifo.Put(el); 206 | qucond.Signal(); 207 | return true; 208 | } 209 | 210 | static void quworker(thr_params *); 211 | static void pyworker(thr_params *); 212 | void erasethreads(); 213 | 214 | static PyFifo qufifo; 215 | static ThrCond qucond; 216 | 217 | #ifndef PY_USE_GIL 218 | static ThrState pythrsys; 219 | #endif 220 | #endif 221 | 222 | static const t_symbol *sym_fint; // float or int symbol, depending on native number message type 223 | static const t_symbol *sym_response; 224 | 225 | static const t_symbol *getone(t_atom &at,PyObject *arg); 226 | static const t_symbol *getlist(t_atom *lst,PyObject *seq,int cnt,int offs = 0); 227 | 228 | public: 229 | 230 | static void AddToPath(const char *dir); 231 | 232 | #ifdef FLEXT_THREADS 233 | // this is especially needed when one py/pyext object calls another one 234 | // we don't want the message to be queued, but otoh we have to avoid deadlock 235 | // (recursive calls can only happen in the system thread) 236 | static int lockcount; 237 | 238 | #ifdef PY_USE_GIL 239 | static inline ThrState FindThreadState() { return ThrState(); } 240 | 241 | static inline ThrState PyLock(ThrState = ThrState()) { return PyGILState_Ensure(); } 242 | static inline ThrState PyLockSys() { return PyLock(); } 243 | static inline void PyUnlock(ThrState st) { PyGILState_Release(st); } 244 | #else // PY_USE_GIL 245 | static ThrState FindThreadState(); 246 | static void FreeThreadState(); 247 | 248 | static ThrState PyLock(ThrState st = FindThreadState()) 249 | { 250 | if(st != pythrsys || !lockcount++) PyEval_AcquireLock(); 251 | return PyThreadState_Swap(st); 252 | } 253 | 254 | #if 1 255 | static inline ThrState PyLockSys() { return PyLock(); } 256 | #else 257 | static ThrState PyLockSys() 258 | { 259 | if(!lockcount++) PyEval_AcquireLock(); 260 | return PyThreadState_Swap(pythrsys); 261 | } 262 | #endif 263 | 264 | static void PyUnlock(ThrState st) 265 | { 266 | ThrState old = PyThreadState_Swap(st); 267 | if(old != pythrsys || !--lockcount) PyEval_ReleaseLock(); 268 | } 269 | #endif // PY_USE_GIL 270 | 271 | #else // FLEXT_THREADS 272 | static inline ThrState PyLock(ThrState = NULL) { return NULL; } 273 | static inline ThrState PyLockSys() { return NULL; } 274 | static inline void PyUnlock(ThrState st) {} 275 | #endif 276 | 277 | class ThrLock 278 | { 279 | public: 280 | ThrLock(): state(PyLock()) {} 281 | ThrLock(const ThrState &st): state(PyLock(st)) {} 282 | ThrLock(const ThrLock &t): state(PyLock(t.state)) {} 283 | ~ThrLock() { PyUnlock(state); } 284 | ThrState state; 285 | }; 286 | 287 | class ThrLockSys 288 | { 289 | public: 290 | ThrLockSys(): state(PyLockSys()) {} 291 | ~ThrLockSys() { PyUnlock(state); } 292 | ThrState state; 293 | }; 294 | 295 | static PyObject* StdOut_Write(PyObject* Self, PyObject* Args); 296 | static PyObject* StdOut_Flush(PyObject* Self, PyObject* Args); 297 | }; 298 | 299 | #endif 300 | -------------------------------------------------------------------------------- /source/pybuffer.h: -------------------------------------------------------------------------------- 1 | /* 2 | py/pyext - python script object for PD and Max/MSP 3 | 4 | Copyright (c)2002-2019 Thomas Grill (gr@grrrr.org) 5 | For information on usage and redistribution, and for a DISCLAIMER OF ALL 6 | WARRANTIES, see the file, "license.txt," in this distribution. 7 | */ 8 | 9 | #ifndef __PYBUFFER_H 10 | #define __PYBUFFER_H 11 | 12 | #include 13 | 14 | #if !defined(FLEXT_VERSION) || (FLEXT_VERSION < 500) 15 | #error You need at least flext version 0.5.0 16 | #endif 17 | 18 | #ifdef PY_USE_FRAMEWORK 19 | #include 20 | #else 21 | #include 22 | #endif 23 | 24 | 25 | #ifdef _MSC_VER 26 | #ifdef PY_EXPORTS 27 | #define PY_EXPORT __declspec(dllexport) 28 | #else 29 | #define PY_EXPORT __declspec(dllimport) 30 | #endif 31 | #else 32 | #define PY_EXPORT 33 | #endif 34 | 35 | typedef struct { 36 | PyObject_HEAD 37 | /* Type-specific fields go here. */ 38 | const t_symbol *sym; 39 | flext::buffer *buf; 40 | flext::buffer::lock_t lock; 41 | bool dirty; 42 | } pySamplebuffer; 43 | 44 | PY_EXPORT extern PyTypeObject pySamplebuffer_Type; 45 | 46 | #define pySamplebuffer_Check(op) PyObject_TypeCheck(op, &pySamplebuffer_Type) 47 | #define pySamplebuffer_CheckExact(op) ((op)->ob_type == &pySamplebuffer_Type) 48 | 49 | 50 | PY_EXPORT PyObject *pySamplebuffer_FromSymbol(const t_symbol *sym); 51 | 52 | inline PyObject *pySamplebuffer_FromString(const char *str) 53 | { 54 | return pySamplebuffer_FromSymbol(flext::MakeSymbol(str)); 55 | } 56 | 57 | inline PyObject *pySamplebuffer_FromString(PyObject *str) 58 | { 59 | const char *cstr; 60 | #if PY_MAJOR_VERSION < 3 61 | if(PyString_Check(str)) 62 | cstr = PyString_AsString(str); 63 | #else 64 | if(PyUnicode_Check(str)) 65 | cstr = PyUnicode_AsUTF8(str); 66 | #endif 67 | else 68 | PyErr_SetString(PyExc_TypeError, "Type must be string or unicode"); 69 | return pySamplebuffer_FromString(cstr); 70 | } 71 | 72 | inline const t_symbol *pySamplebuffer_AS_SYMBOL(PyObject *op) 73 | { 74 | return ((pySamplebuffer *)op)->sym; 75 | } 76 | 77 | inline const t_symbol *pySamplebuffer_AsSymbol(PyObject *op) 78 | { 79 | return pySamplebuffer_Check(op)?pySamplebuffer_AS_SYMBOL(op):NULL; 80 | } 81 | 82 | inline const char *pySamplebuffer_AS_STRING(PyObject *op) 83 | { 84 | return flext::GetString(pySamplebuffer_AS_SYMBOL(op)); 85 | } 86 | 87 | #endif 88 | -------------------------------------------------------------------------------- /source/pybundle.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | py/pyext - python script object for PD and Max/MSP 3 | 4 | Copyright (c)2002-2019 Thomas Grill (gr@grrrr.org) 5 | For information on usage and redistribution, and for a DISCLAIMER OF ALL 6 | WARRANTIES, see the file, "license.txt," in this distribution. 7 | */ 8 | 9 | #include "pyprefix.h" 10 | #include "pybundle.h" 11 | #include "pyext.h" 12 | 13 | static PyObject *bundle_new(PyTypeObject *type, PyObject *args, PyObject *kwds) 14 | { 15 | pyBundle *self = (pyBundle *)pyBundle_Type.tp_alloc(&pyBundle_Type, 0); 16 | if(self) self->bundle = flext::MsgNew(); 17 | return (PyObject *)self; 18 | } 19 | 20 | static int bundle_init(PyObject *self, PyObject *args, PyObject *kwds) 21 | { 22 | FLEXT_ASSERT(pyBundle_Check(self)); 23 | 24 | int len = PySequence_Length(args); 25 | if(len) { 26 | PyErr_SetString(PyExc_TypeError,"no arguments expected"); 27 | return -1; 28 | } 29 | 30 | return 0; 31 | } 32 | 33 | static void bundle_dealloc(PyObject *obj) 34 | { 35 | pyBundle *self = (pyBundle *)obj; 36 | if(self->bundle) flext::MsgFree(self->bundle); 37 | obj->ob_type->tp_free(obj); 38 | } 39 | 40 | static PyObject *bundle_send(PyObject *obj) 41 | { 42 | pyBundle *self = (pyBundle *)obj; 43 | if(self->bundle) { 44 | flext::ToOutMsg(self->bundle); 45 | self->bundle = NULL; 46 | 47 | Py_INCREF(obj); 48 | return obj; 49 | } 50 | else { 51 | PyErr_SetString(PyExc_RuntimeError,"already sent"); 52 | return NULL; 53 | } 54 | } 55 | 56 | static PyObject *bundle_repr(PyObject *self) 57 | { 58 | FLEXT_ASSERT(pyBundle_Check(self)); 59 | return (PyObject *) 60 | #if PY_MAJOR_VERSION < 3 61 | PyString_FromFormat 62 | #else 63 | PyUnicode_FromFormat 64 | #endif 65 | ("", pyBundle_AS_BUNDLE(self)); 66 | } 67 | 68 | static PyObject *bundle_str(PyObject *self) 69 | { 70 | return bundle_repr(self); 71 | } 72 | 73 | static PyObject *bundle_richcompare(PyObject *a,PyObject *b,int cmp) 74 | { 75 | if(pyBundle_Check(a) && pyBundle_Check(b)) { 76 | const flext::MsgBundle *abnd = pyBundle_AS_BUNDLE(a); 77 | const flext::MsgBundle *bbnd = pyBundle_AS_BUNDLE(b); 78 | bool ret; 79 | switch(cmp) { 80 | case Py_LT: ret = abnd < bbnd; break; 81 | case Py_LE: ret = abnd <= bbnd; break; 82 | case Py_EQ: ret = abnd == bbnd; break; 83 | case Py_NE: ret = abnd != bbnd; break; 84 | case Py_GT: ret = abnd > bbnd; break; 85 | case Py_GE: ret = abnd >= bbnd; break; 86 | } 87 | return PyBool_FromLong(ret); 88 | } 89 | Py_INCREF(Py_NotImplemented); 90 | return Py_NotImplemented; 91 | } 92 | 93 | static long bundle_hash(PyObject *self) 94 | { 95 | FLEXT_ASSERT(pyBundle_Check(self)); 96 | return (long)pyBundle_AS_BUNDLE(self); 97 | } 98 | 99 | 100 | static PyObject *bundle_append(PyObject *self,PyObject *args) 101 | { 102 | flext::MsgBundle *b = pyBundle_AS_BUNDLE(self); 103 | if(b) { 104 | int sz = PyTuple_GET_SIZE(args),offs = 0; 105 | PyObject *tg,*outl; 106 | pyext *ext = NULL; 107 | const t_symbol *recv; 108 | int o; 109 | 110 | if(sz > 2 && 111 | (tg = PyTuple_GET_ITEM(args,0)) != NULL && 112 | (outl = PyTuple_GET_ITEM(args,1)) != NULL && 113 | #if PY_MAJOR_VERSION < 3 114 | PyInt_Check(outl) 115 | #else 116 | PyLong_Check(outl) 117 | #endif 118 | ) { 119 | // Sending to outlet 120 | ext = pyext::GetThis(tg); 121 | 122 | #if PY_MAJOR_VERSION < 3 123 | o = PyInt_AS_LONG(outl); 124 | #else 125 | o = PyLong_AS_LONG(outl); 126 | #endif 127 | 128 | if(o < 1 || o > ext->Outlets()) { 129 | PyErr_SetString(PyExc_ValueError,"Outlet index out of range"); 130 | return NULL; 131 | } 132 | 133 | offs += 2; 134 | } 135 | else if(sz > 1 && 136 | (tg = PyTuple_GET_ITEM(args,0)) != NULL && (recv = pyObject_AsSymbol(tg)) != NULL 137 | ) { 138 | // Sending to receiver 139 | offs++; 140 | } 141 | else { 142 | // not recognized 143 | PyErr_SetString(PyExc_SyntaxError,"Unrecognized arguments"); 144 | return NULL; 145 | } 146 | 147 | PyObject *val; 148 | if(sz-offs == 1) { 149 | val = PyTuple_GET_ITEM(args,offs); // borrow reference 150 | Py_INCREF(val); 151 | } 152 | else 153 | val = PyTuple_GetSlice(args,offs,sz); // new ref 154 | 155 | flext::AtomListStatic<16> lst; 156 | const t_symbol *sym = pybase::GetPyArgs(lst,val); 157 | Py_DECREF(val); 158 | 159 | if(sym) { 160 | if(ext) { 161 | FLEXT_ASSERT(outl); 162 | ext->MsgAddAnything(b,o-1,sym,lst.Count(),lst.Atoms()); 163 | } 164 | else { 165 | FLEXT_ASSERT(sym); 166 | if(!flext::MsgForward(b,recv,sym,lst.Count(),lst.Atoms())) { 167 | PyErr_SetString(PyExc_ValueError,"Receiver not found"); 168 | return NULL; 169 | } 170 | } 171 | 172 | Py_INCREF(Py_None); 173 | return Py_None; 174 | } 175 | else { 176 | FLEXT_ASSERT(PyErr_Occurred()); 177 | return NULL; 178 | } 179 | 180 | Py_INCREF(self); 181 | return self; 182 | } 183 | else { 184 | PyErr_SetString(PyExc_RuntimeError,"Invalid bundle"); 185 | return NULL; 186 | } 187 | } 188 | 189 | static PyMethodDef bundle_methods[] = { 190 | {"append", (PyCFunction)bundle_append,METH_VARARGS,"Append message to bundle"}, 191 | {"send", (PyCFunction)bundle_send,METH_NOARGS,"Send bundle"}, 192 | {NULL} /* Sentinel */ 193 | }; 194 | 195 | 196 | 197 | PyTypeObject pyBundle_Type = { 198 | PyVarObject_HEAD_INIT(NULL, 0) 199 | "Bundle", /*tp_name*/ 200 | sizeof(pyBundle), /*tp_basicsize*/ 201 | 0, /*tp_itemsize*/ 202 | bundle_dealloc, /*tp_dealloc*/ 203 | 0, /*tp_print*/ 204 | 0, /*tp_getattr*/ 205 | 0, /*tp_setattr*/ 206 | 0, /*tp_compare*/ 207 | bundle_repr, /*tp_repr*/ 208 | 0, /*tp_as_number*/ 209 | 0, /*tp_as_sequence*/ 210 | 0, /*tp_as_mapping*/ 211 | bundle_hash, /*tp_hash */ 212 | 0, /*tp_call*/ 213 | bundle_str, /*tp_str*/ 214 | 0, /*tp_getattro*/ 215 | 0, /*tp_setattro*/ 216 | 0, /*tp_as_buffer*/ 217 | Py_TPFLAGS_DEFAULT /*| Py_TPFLAGS_BASETYPE*/, /*tp_flags*/ 218 | "Bundle objects", /* tp_doc */ 219 | 0, /* tp_traverse */ 220 | 0, /* tp_clear */ 221 | bundle_richcompare, /* tp_richcompare */ 222 | 0, /* tp_weaklistoffset */ 223 | 0, /* tp_iter */ 224 | 0, /* tp_iternext */ 225 | bundle_methods, /* tp_methods */ 226 | 0, /* tp_members */ 227 | 0, /* tp_getset */ 228 | 0, /* tp_base */ 229 | 0, /* tp_dict */ 230 | 0, /* tp_descr_get */ 231 | 0, /* tp_descr_set */ 232 | 0, /* tp_dictoffset */ 233 | bundle_init, /* tp_init */ 234 | 0, /* tp_alloc */ 235 | bundle_new, /* tp_new */ 236 | }; 237 | 238 | 239 | void initbundle() 240 | { 241 | if(PyType_Ready(&pyBundle_Type) < 0) 242 | FLEXT_ASSERT(false); 243 | else 244 | Py_INCREF(&pyBundle_Type); 245 | } 246 | -------------------------------------------------------------------------------- /source/pybundle.h: -------------------------------------------------------------------------------- 1 | /* 2 | py/pyext - python script object for PD and Max/MSP 3 | 4 | Copyright (c)2002-2015 Thomas Grill (gr@grrrr.org) 5 | For information on usage and redistribution, and for a DISCLAIMER OF ALL 6 | WARRANTIES, see the file, "license.txt," in this distribution. 7 | */ 8 | 9 | #ifndef __PYBUNDLE_H 10 | #define __PYBUNDLE_H 11 | 12 | #include 13 | 14 | #if !defined(FLEXT_VERSION) || (FLEXT_VERSION < 500) 15 | #error You need at least flext version 0.5.0 16 | #endif 17 | 18 | #ifdef PY_USE_FRAMEWORK 19 | #include 20 | #else 21 | #include 22 | #endif 23 | 24 | 25 | #ifdef _MSC_VER 26 | #ifdef PY_EXPORTS 27 | #define PY_EXPORT __declspec(dllexport) 28 | #else 29 | #define PY_EXPORT __declspec(dllimport) 30 | #endif 31 | #else 32 | #define PY_EXPORT 33 | #endif 34 | 35 | typedef struct { 36 | PyObject_HEAD 37 | /* Type-specific fields go here. */ 38 | flext::MsgBundle *bundle; 39 | } pyBundle; 40 | 41 | PY_EXPORT extern PyTypeObject pyBundle_Type; 42 | 43 | #define pyBundle_Check(op) PyObject_TypeCheck(op, &pyBundle_Type) 44 | #define pyBundle_CheckExact(op) ((op)->ob_type == &pyBundle_Type) 45 | 46 | 47 | inline flext::MsgBundle *pyBundle_AS_BUNDLE(PyObject *op) 48 | { 49 | return ((pyBundle *)op)->bundle; 50 | } 51 | 52 | inline flext::MsgBundle *pyBundle_AsBundle(PyObject *op) 53 | { 54 | return pyBundle_Check(op)?pyBundle_AS_BUNDLE(op):NULL; 55 | } 56 | 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /source/pycompat.cpp: -------------------------------------------------------------------------------- 1 | #include "pycompat.h" 2 | 3 | // copied from Python 3.8.2 for compatibility with pre-3.6.1 versions because 4 | // doing the right thing with the older slice functions seems difficult... 5 | 6 | #if PY_VERSION_HEX < 0x03060100 7 | int 8 | PySlice_Unpack(PyObject *_r, 9 | Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t *step) 10 | { 11 | PySliceObject *r = (PySliceObject*)_r; 12 | /* this is harder to get right than you might think */ 13 | 14 | Py_BUILD_ASSERT(PY_SSIZE_T_MIN + 1 <= -PY_SSIZE_T_MAX); 15 | 16 | if (r->step == Py_None) { 17 | *step = 1; 18 | } 19 | else { 20 | if (!_PyEval_SliceIndex(r->step, step)) return -1; 21 | if (*step == 0) { 22 | PyErr_SetString(PyExc_ValueError, 23 | "slice step cannot be zero"); 24 | return -1; 25 | } 26 | /* Here *step might be -PY_SSIZE_T_MAX-1; in this case we replace it 27 | * with -PY_SSIZE_T_MAX. This doesn't affect the semantics, and it 28 | * guards against later undefined behaviour resulting from code that 29 | * does "step = -step" as part of a slice reversal. 30 | */ 31 | if (*step < -PY_SSIZE_T_MAX) 32 | *step = -PY_SSIZE_T_MAX; 33 | } 34 | 35 | if (r->start == Py_None) { 36 | *start = *step < 0 ? PY_SSIZE_T_MAX : 0; 37 | } 38 | else { 39 | if (!_PyEval_SliceIndex(r->start, start)) return -1; 40 | } 41 | 42 | if (r->stop == Py_None) { 43 | *stop = *step < 0 ? PY_SSIZE_T_MIN : PY_SSIZE_T_MAX; 44 | } 45 | else { 46 | if (!_PyEval_SliceIndex(r->stop, stop)) return -1; 47 | } 48 | 49 | return 0; 50 | } 51 | 52 | Py_ssize_t 53 | PySlice_AdjustIndices(Py_ssize_t length, 54 | Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t step) 55 | { 56 | /* this is harder to get right than you might think */ 57 | 58 | assert(step != 0); 59 | assert(step >= -PY_SSIZE_T_MAX); 60 | 61 | if (*start < 0) { 62 | *start += length; 63 | if (*start < 0) { 64 | *start = (step < 0) ? -1 : 0; 65 | } 66 | } 67 | else if (*start >= length) { 68 | *start = (step < 0) ? length - 1 : length; 69 | } 70 | 71 | if (*stop < 0) { 72 | *stop += length; 73 | if (*stop < 0) { 74 | *stop = (step < 0) ? -1 : 0; 75 | } 76 | } 77 | else if (*stop >= length) { 78 | *stop = (step < 0) ? length - 1 : length; 79 | } 80 | 81 | if (step < 0) { 82 | if (*stop < *start) { 83 | return (*start - *stop - 1) / (-step) + 1; 84 | } 85 | } 86 | else { 87 | if (*start < *stop) { 88 | return (*stop - *start - 1) / step + 1; 89 | } 90 | } 91 | return 0; 92 | } 93 | #endif 94 | -------------------------------------------------------------------------------- /source/pycompat.h: -------------------------------------------------------------------------------- 1 | #ifndef __PYCOMPAT_H 2 | #define __PYCOMPAT_H 3 | 4 | #ifdef PY_USE_FRAMEWORK 5 | #include 6 | #else 7 | #include 8 | #endif 9 | 10 | // copied from Python 3.8.2 for compatibility with pre-3.6.1 versions because 11 | // doing the right thing with the older slice functions seems difficult... 12 | 13 | #if PY_VERSION_HEX < 0x03060100 14 | #if PY_MAJOR_VERSION < 3 15 | /* Assert a build-time dependency, as an expression. 16 | Your compile will fail if the condition isn't true, or can't be evaluated 17 | by the compiler. This can be used in an expression: its value is 0. 18 | Example: 19 | #define foo_to_char(foo) \ 20 | ((char *)(foo) \ 21 | + Py_BUILD_ASSERT_EXPR(offsetof(struct foo, string) == 0)) 22 | Written by Rusty Russell, public domain, http://ccodearchive.net/ */ 23 | #define Py_BUILD_ASSERT_EXPR(cond) \ 24 | (sizeof(char [1 - 2*!(cond)]) - 1) 25 | 26 | #define Py_BUILD_ASSERT(cond) do { \ 27 | (void)Py_BUILD_ASSERT_EXPR(cond); \ 28 | } while(0) 29 | #endif 30 | 31 | int 32 | PySlice_Unpack(PyObject *_r, 33 | Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t *step); 34 | 35 | Py_ssize_t 36 | PySlice_AdjustIndices(Py_ssize_t length, 37 | Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t step); 38 | #endif 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /source/pydsp.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | py/pyext - python script object for PD and Max/MSP 3 | 4 | Copyright (c)2002-2015 Thomas Grill (gr@grrrr.org) 5 | For information on usage and redistribution, and for a DISCLAIMER OF ALL 6 | WARRANTIES, see the file, "license.txt," in this distribution. 7 | */ 8 | 9 | #ifndef PY_NODSP 10 | 11 | #include "pyext.h" 12 | 13 | class pydsp 14 | : public pyext 15 | { 16 | FLEXT_HEADER(pydsp,pyext) 17 | public: 18 | pydsp(int argc,const t_atom *argv); 19 | 20 | protected: 21 | virtual bool CbDsp(); 22 | virtual void CbSignal(); 23 | 24 | virtual bool DoInit(); 25 | virtual void DoExit(); 26 | 27 | virtual PyObject *GetSig(int ix,bool in); 28 | 29 | void NewBuffers(); 30 | void FreeBuffers(); 31 | 32 | PyObject *dspfun,*sigfun; 33 | PyObject **buffers; 34 | }; 35 | 36 | FLEXT_LIB_DSP_V("pyext~ pyext.~ pyx~ pyx.~",pydsp) 37 | 38 | pydsp::pydsp(int argc,const t_atom *argv) 39 | : pyext(argc,argv,true) 40 | , dspfun(NULL),sigfun(NULL) 41 | , buffers(NULL) 42 | {} 43 | 44 | bool pydsp::DoInit() 45 | { 46 | if(!pyext::DoInit()) return false; 47 | 48 | if(pyobj) 49 | { 50 | dspfun = PyObject_GetAttrString(pyobj,"_dsp"); // get ref 51 | if(!dspfun) 52 | PyErr_Clear(); 53 | else if(!PyMethod_Check(dspfun)) { 54 | Py_DECREF(dspfun); 55 | dspfun = NULL; 56 | } 57 | } 58 | return true; 59 | } 60 | 61 | void pydsp::DoExit() 62 | { 63 | if(dspfun) { Py_DECREF(dspfun); dspfun = NULL; } 64 | if(sigfun) { Py_DECREF(sigfun); sigfun = NULL; } 65 | 66 | FreeBuffers(); 67 | } 68 | 69 | PyObject *arrayfrombuffer(PyObject *buf,int c,int n); 70 | 71 | void pydsp::NewBuffers() 72 | { 73 | int i,n = Blocksize(); 74 | const int ins = CntInSig(),outs = CntOutSig(); 75 | t_sample *const *insigs = InSig(); 76 | t_sample *const *outsigs = OutSig(); 77 | 78 | // inlet/outlet count can't change so we don't have to deallocate 79 | if(!buffers) { 80 | int cnt = ins+outs; 81 | buffers = new PyObject *[cnt]; 82 | memset(buffers,0,cnt*sizeof(*buffers)); 83 | } 84 | 85 | for(i = 0; i < ins; ++i) { 86 | Py_XDECREF(buffers[i]); 87 | PyObject *b = 88 | #if PY_MAJOR_VERSION < 3 89 | PyBuffer_FromReadWriteMemory(insigs[i],n*sizeof(t_sample)); 90 | #elif PY_MINOR_VERSION >= 3 91 | PyMemoryView_FromMemory(reinterpret_cast(insigs[i]), n*sizeof(t_sample), PyBUF_WRITE); 92 | #else 93 | #error "TODO" 94 | #endif 95 | 96 | buffers[i] = arrayfrombuffer(b,1,n); 97 | Py_DECREF(b); 98 | } 99 | for(i = 0; i < outs; ++i) { 100 | Py_XDECREF(buffers[ins+i]); 101 | if(i < ins && outsigs[i] == insigs[i]) { 102 | // same vectors - share the objects! 103 | buffers[ins+i] = buffers[i]; 104 | Py_XINCREF(buffers[i]); 105 | } 106 | else { 107 | PyObject *b = 108 | #if PY_MAJOR_VERSION < 3 109 | PyBuffer_FromReadWriteMemory(outsigs[i],n*sizeof(t_sample)); 110 | #elif PY_MINOR_VERSION >= 3 111 | PyMemoryView_FromMemory(reinterpret_cast(outsigs[i]), n*sizeof(t_sample), PyBUF_WRITE); 112 | #else 113 | #error "TODO" 114 | #endif 115 | 116 | buffers[ins+i] = arrayfrombuffer(b,1,n); 117 | Py_DECREF(b); 118 | } 119 | } 120 | } 121 | 122 | void pydsp::FreeBuffers() 123 | { 124 | if(buffers) { 125 | int cnt = CntInSig()+CntOutSig(); 126 | for(int i = 0; i < cnt; ++i) Py_XDECREF(buffers[i]); 127 | delete[] buffers; 128 | buffers = NULL; 129 | } 130 | } 131 | 132 | bool pydsp::CbDsp() 133 | { 134 | if(pyobj && (CntInSig() || CntOutSig())) 135 | { 136 | ThrLockSys lock; 137 | 138 | NewBuffers(); 139 | 140 | bool dodsp = true; 141 | if(dspfun) { 142 | PyObject *ret = PyObject_CallObject(dspfun,NULL); 143 | if(ret) { 144 | dodsp = PyObject_IsTrue(ret) != 0; 145 | Py_DECREF(ret); 146 | } 147 | else { 148 | #ifdef FLEXT_DEBUG 149 | PyErr_Print(); 150 | #else 151 | PyErr_Clear(); 152 | #endif 153 | } 154 | } 155 | 156 | // do that here instead of where dspfun is initialized, so that 157 | // _signal can be assigned in _dsp 158 | // optimizations may be done there to assign the right _signal version 159 | Py_XDECREF(sigfun); 160 | 161 | if(dodsp) { 162 | sigfun = PyObject_GetAttrString(pyobj,"_signal"); // get ref 163 | if(!sigfun) 164 | PyErr_Clear(); 165 | else if(!PyMethod_Check(sigfun)) { 166 | Py_DECREF(sigfun); 167 | sigfun = NULL; 168 | } 169 | } 170 | else 171 | sigfun = NULL; 172 | 173 | return sigfun != NULL; 174 | } 175 | else 176 | // switch on dsp only if there are signal inlets or outlets 177 | return false; 178 | } 179 | 180 | void pydsp::CbSignal() 181 | { 182 | ThrLockSys lock; 183 | PyObject *ret = PyObject_CallObject(sigfun,NULL); 184 | 185 | if(ret) 186 | Py_DECREF(ret); 187 | else { 188 | #ifdef FLEXT_DEBUG 189 | PyErr_Print(); 190 | #else 191 | PyErr_Clear(); 192 | #endif 193 | } 194 | } 195 | 196 | PyObject *pydsp::GetSig(int ix,bool in) 197 | { 198 | PyObject *r = buffers[in?ix:CntInSig()+ix]; 199 | Py_XINCREF(r); 200 | return r; 201 | } 202 | 203 | #endif 204 | -------------------------------------------------------------------------------- /source/pyext.h: -------------------------------------------------------------------------------- 1 | /* 2 | py/pyext - python external object for PD and MaxMSP 3 | 4 | Copyright (c)2002-2015 Thomas Grill (gr@grrrr.org) 5 | For information on usage and redistribution, and for a DISCLAIMER OF ALL 6 | WARRANTIES, see the file, "license.txt," in this distribution. 7 | */ 8 | 9 | #ifndef __PYEXT_H 10 | #define __PYEXT_H 11 | 12 | #include "pybase.h" 13 | 14 | class pyext 15 | : public pybase 16 | , public flext_dsp 17 | { 18 | FLEXT_HEADER_S(pyext,flext_dsp,Setup) 19 | 20 | public: 21 | pyext(int argc,const t_atom *argv,bool sig = false); 22 | 23 | static PyObject *pyext__str__(PyObject *self, PyObject *args); 24 | 25 | static PyObject *pyext_outlet(PyObject *self, PyObject *args); 26 | #if FLEXT_SYS == FLEXT_SYS_PD 27 | static PyObject *pyext_tocanvas(PyObject *self, PyObject *args); 28 | #endif 29 | 30 | static PyObject *pyext_setattr(PyObject *self, PyObject *args); 31 | static PyObject *pyext_getattr(PyObject *self, PyObject *args); 32 | 33 | static PyObject *pyext_detach(PyObject *self, PyObject *args); 34 | static PyObject *pyext_stop(PyObject *self, PyObject *args); 35 | 36 | static PyObject *pyext_inbuf(PyObject *self, PyObject *args); 37 | static PyObject *pyext_invec(PyObject *self, PyObject *args); 38 | static PyObject *pyext_outbuf(PyObject *self, PyObject *args); 39 | static PyObject *pyext_outvec(PyObject *self, PyObject *args); 40 | 41 | int Inlets() const { return inlets; } 42 | int Outlets() const { return outlets; } 43 | 44 | static pyext *GetThis(PyObject *self); 45 | 46 | static PyMethodDef meth_tbl[]; 47 | static const char *pyext_doc; 48 | 49 | protected: 50 | 51 | virtual bool Init(); 52 | virtual bool Finalize(); 53 | virtual void Exit(); 54 | 55 | virtual bool CbMethodResort(int n,const t_symbol *s,int argc,const t_atom *argv); 56 | virtual void CbClick(); 57 | virtual bool CbDsp(); 58 | 59 | virtual void DumpOut(const t_symbol *sym,int argc,const t_atom *argv); 60 | 61 | bool work(int n,const t_symbol *s,int argc,const t_atom *argv); 62 | 63 | void m_help(); 64 | 65 | void m_reload() { Reload(); } 66 | void m_reload_(int argc,const t_atom *argv) { initargs(argc,argv); Reload(); } 67 | void ms_initargs(const AtomList &a) { m_reload_(a.Count(),a.Atoms()); } 68 | void m_dir_() { m__dir(pyobj); } 69 | void mg_dir_(AtomList &lst) { GetDir(pyobj,lst); } 70 | void m_doc_() { m__doc(pyobj); } 71 | 72 | void m_get(const t_symbol *s); 73 | void m_set(int argc,const t_atom *argv); 74 | 75 | const t_symbol *methname; 76 | PyObject *pyobj; 77 | int inlets,outlets; 78 | int siginlets,sigoutlets; 79 | 80 | flext::AtomList initargs; 81 | 82 | virtual void LoadModule(); 83 | virtual void UnloadModule(); 84 | 85 | virtual void Load(); 86 | virtual void Unload(); 87 | 88 | virtual bool DoInit(); 89 | virtual void DoExit(); 90 | 91 | virtual PyObject *GetSig(int ix,bool in); 92 | 93 | private: 94 | static void Setup(t_classid); 95 | 96 | void SetThis(); 97 | void ClearThis(); 98 | 99 | void ClearBinding(); 100 | bool MakeInstance(); 101 | bool InitInOut(int &inlets,int &outlets); 102 | 103 | // -------- bind stuff ------------------ 104 | static PyObject *pyext_bind(PyObject *self, PyObject *args); 105 | static PyObject *pyext_unbind(PyObject *self, PyObject *args); 106 | 107 | // --------------------------- 108 | 109 | bool call(const char *meth,int inlet,const t_symbol *s,int argc,const t_atom *argv); 110 | 111 | virtual void callpy(PyObject *fun,PyObject *args); 112 | static bool stcallpy(PyObject *fun,PyObject *args); 113 | 114 | #ifndef PY_USE_GIL 115 | ThrState pythr; 116 | #endif 117 | 118 | private: 119 | static bool boundmeth(flext_base *,t_symbol *sym,int argc,t_atom *argv,void *data); 120 | 121 | FLEXT_CALLBACK(m_help) 122 | 123 | FLEXT_CALLBACK(m_reload) 124 | FLEXT_CALLBACK_V(m_reload_) 125 | FLEXT_CALLBACK(m_dir_) 126 | FLEXT_CALLGET_V(mg_dir_) 127 | FLEXT_CALLBACK(m_doc_) 128 | 129 | FLEXT_ATTRGET_V(initargs) 130 | FLEXT_CALLSET_V(ms_initargs) 131 | 132 | FLEXT_CALLBACK_S(m_get) 133 | FLEXT_CALLBACK_V(m_set) 134 | 135 | // callbacks 136 | FLEXT_ATTRVAR_I(detach) 137 | FLEXT_ATTRVAR_B(pymsg) 138 | FLEXT_ATTRVAR_B(respond) 139 | 140 | FLEXT_CALLBACK_V(m_stop) 141 | FLEXT_CALLBACK(m_dir) 142 | FLEXT_CALLGET_V(mg_dir) 143 | FLEXT_CALLBACK(m_doc) 144 | 145 | FLEXT_CALLBACK(CbClick) 146 | 147 | #ifdef FLEXT_THREADS 148 | FLEXT_CALLBACK_T(tick) 149 | #endif 150 | }; 151 | 152 | typedef struct { 153 | PyObject_HEAD 154 | long this_ptr; 155 | } pyPyext; 156 | 157 | PY_EXPORT extern PyTypeObject pyPyext_Type; 158 | 159 | #define pyPyext_Check(op) PyObject_TypeCheck((op), &pyPyext_Type) 160 | 161 | #endif 162 | -------------------------------------------------------------------------------- /source/pymeth.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | py/pyext - python script object for PD and Max/MSP 3 | 4 | Copyright (c)2002-2015 Thomas Grill (gr@grrrr.org) 5 | For information on usage and redistribution, and for a DISCLAIMER OF ALL 6 | WARRANTIES, see the file, "license.txt," in this distribution. 7 | */ 8 | 9 | #include "pybase.h" 10 | #include 11 | 12 | struct xlt { const t_symbol *from,*to; }; 13 | 14 | static const xlt xtdefs[] = { 15 | { flext::MakeSymbol("+"),flext::MakeSymbol("__add__") }, 16 | { flext::MakeSymbol("+="),flext::MakeSymbol("__iadd__") }, 17 | { flext::MakeSymbol("!+"),flext::MakeSymbol("__radd__") }, 18 | { flext::MakeSymbol("-"),flext::MakeSymbol("__sub__") }, 19 | { flext::MakeSymbol("-="),flext::MakeSymbol("__isub__") }, 20 | { flext::MakeSymbol("!-"),flext::MakeSymbol("__rsub__") }, 21 | { flext::MakeSymbol("*"),flext::MakeSymbol("__mul__") }, 22 | { flext::MakeSymbol("*="),flext::MakeSymbol("__imul__") }, 23 | { flext::MakeSymbol("!*"),flext::MakeSymbol("__rmul__") }, 24 | { flext::MakeSymbol("/"),flext::MakeSymbol("__div__") }, 25 | { flext::MakeSymbol("/="),flext::MakeSymbol("__idiv__") }, 26 | { flext::MakeSymbol("!/"),flext::MakeSymbol("__rdiv__") }, 27 | { flext::MakeSymbol("//"),flext::MakeSymbol("__floordiv__") }, 28 | { flext::MakeSymbol("//="),flext::MakeSymbol("__ifloordiv__") }, 29 | { flext::MakeSymbol("!//"),flext::MakeSymbol("__rfloordiv__") }, 30 | { flext::MakeSymbol("%"),flext::MakeSymbol("__mod__") }, 31 | { flext::MakeSymbol("%="),flext::MakeSymbol("__imod__") }, 32 | { flext::MakeSymbol("!%"),flext::MakeSymbol("__rmod__") }, 33 | { flext::MakeSymbol("**"),flext::MakeSymbol("__pow__") }, 34 | { flext::MakeSymbol("**="),flext::MakeSymbol("__ipow__") }, 35 | { flext::MakeSymbol("!**"),flext::MakeSymbol("__rpow__") }, 36 | { flext::MakeSymbol("&"),flext::MakeSymbol("__and__") }, 37 | { flext::MakeSymbol("&="),flext::MakeSymbol("__iand__") }, 38 | { flext::MakeSymbol("!&"),flext::MakeSymbol("__rand__") }, 39 | { flext::MakeSymbol("|"),flext::MakeSymbol("__or__") }, 40 | { flext::MakeSymbol("|="),flext::MakeSymbol("__ior__") }, 41 | { flext::MakeSymbol("!|"),flext::MakeSymbol("__ror__") }, 42 | { flext::MakeSymbol("^"),flext::MakeSymbol("__xor__") }, 43 | { flext::MakeSymbol("^="),flext::MakeSymbol("__ixor__") }, 44 | { flext::MakeSymbol("!^"),flext::MakeSymbol("__rxor__") }, 45 | { flext::MakeSymbol("<<"),flext::MakeSymbol("__lshift__") }, 46 | { flext::MakeSymbol("<<="),flext::MakeSymbol("__ilshift__") }, 47 | { flext::MakeSymbol("!<<"),flext::MakeSymbol("__rlshift__") }, 48 | { flext::MakeSymbol(">>"),flext::MakeSymbol("__rshift__") }, 49 | { flext::MakeSymbol(">>="),flext::MakeSymbol("__irshift__") }, 50 | { flext::MakeSymbol("!>>"),flext::MakeSymbol("__rrshift__") }, 51 | { flext::MakeSymbol("=="),flext::MakeSymbol("__eq__") }, 52 | { flext::MakeSymbol("!="),flext::MakeSymbol("__ne__") }, 53 | { flext::MakeSymbol("<"),flext::MakeSymbol("__lt__") }, 54 | { flext::MakeSymbol(">"),flext::MakeSymbol("__gt__") }, 55 | { flext::MakeSymbol("<="),flext::MakeSymbol("__le__") }, 56 | { flext::MakeSymbol(">="),flext::MakeSymbol("__ge__") }, 57 | { flext::MakeSymbol("!"),flext::MakeSymbol("__nonzero__") }, 58 | { flext::MakeSymbol("~"),flext::MakeSymbol("__invert__") }, 59 | { flext::MakeSymbol("[]"),flext::MakeSymbol("__getitem__") }, 60 | { flext::MakeSymbol("[]="),flext::MakeSymbol("__setitem__") }, 61 | { flext::MakeSymbol("[:]"),flext::MakeSymbol("__getslice__") }, 62 | { flext::MakeSymbol("[:]="),flext::MakeSymbol("__setslice__") }, 63 | 64 | { flext::MakeSymbol(".abs"),flext::MakeSymbol("__abs__") }, 65 | { flext::MakeSymbol(".neg"),flext::MakeSymbol("__neg__") }, 66 | { flext::MakeSymbol(".pos"),flext::MakeSymbol("__pos__") }, 67 | { flext::MakeSymbol(".divmod"),flext::MakeSymbol("__divmod__") }, 68 | 69 | { flext::MakeSymbol(".int"),flext::MakeSymbol("__int__") }, 70 | { flext::MakeSymbol(".long"),flext::MakeSymbol("__long__") }, 71 | { flext::MakeSymbol(".float"),flext::MakeSymbol("__float__") }, 72 | { flext::MakeSymbol(".complex"),flext::MakeSymbol("__complex__") }, 73 | { flext::MakeSymbol(".str"),flext::MakeSymbol("__str__") }, 74 | { flext::MakeSymbol(".coerce"),flext::MakeSymbol("__coerce__") }, 75 | 76 | { flext::MakeSymbol(".doc"),flext::MakeSymbol("__doc__") }, 77 | { flext::MakeSymbol(".repr"),flext::MakeSymbol("__repr__") }, 78 | 79 | { flext::MakeSymbol(".len"),flext::MakeSymbol("__len__") }, 80 | { flext::MakeSymbol(".in"),flext::MakeSymbol("__contains") }, 81 | 82 | { NULL,NULL } // sentinel 83 | }; 84 | 85 | typedef std::map XTable; 86 | static XTable xtable; 87 | 88 | 89 | class pymeth 90 | : public pybase 91 | , public flext_base 92 | { 93 | FLEXT_HEADER_S(pymeth,flext_base,Setup) 94 | 95 | public: 96 | pymeth(int argc,const t_atom *argv); 97 | ~pymeth(); 98 | 99 | protected: 100 | virtual void Exit(); 101 | 102 | virtual bool CbMethodResort(int n,const t_symbol *s,int argc,const t_atom *argv); 103 | 104 | void m_help(); 105 | 106 | void m_reload() { Reload(); } 107 | void m_reload_(int argc,const t_atom *argv) { args(argc,argv); Reload(); } 108 | void m_set(int argc,const t_atom *argv); 109 | void m_dir_() { m__dir(function); } 110 | void m_doc_() { m__doc(function); } 111 | 112 | const t_symbol *funname; 113 | PyObject *function; 114 | 115 | virtual void LoadModule(); 116 | virtual void UnloadModule(); 117 | 118 | virtual void Load(); 119 | virtual void Unload(); 120 | 121 | void SetFunction(const t_symbol *func); 122 | void ResetFunction(); 123 | 124 | virtual void DumpOut(const t_symbol *sym,int argc,const t_atom *argv); 125 | 126 | PyObject **objects; 127 | 128 | private: 129 | 130 | virtual void callpy(PyObject *fun,PyObject *args); 131 | 132 | static void Setup(t_classid c); 133 | 134 | FLEXT_CALLBACK(m_help) 135 | FLEXT_CALLBACK(m_reload) 136 | FLEXT_CALLBACK_V(m_reload_) 137 | FLEXT_CALLBACK_V(m_set) 138 | FLEXT_CALLBACK(m_dir_) 139 | FLEXT_CALLBACK(m_doc_) 140 | 141 | // callbacks 142 | FLEXT_ATTRVAR_I(detach) 143 | FLEXT_ATTRVAR_B(pymsg) 144 | FLEXT_ATTRVAR_B(respond) 145 | 146 | FLEXT_CALLBACK_V(m_stop) 147 | FLEXT_CALLBACK(m_dir) 148 | FLEXT_CALLGET_V(mg_dir) 149 | FLEXT_CALLBACK(m_doc) 150 | 151 | #ifdef FLEXT_THREADS 152 | FLEXT_CALLBACK_T(tick) 153 | #endif 154 | }; 155 | 156 | FLEXT_LIB_V("pym",pymeth) 157 | 158 | 159 | void pymeth::Setup(t_classid c) 160 | { 161 | FLEXT_CADDMETHOD_(c,0,"doc",m_doc); 162 | FLEXT_CADDMETHOD_(c,0,"dir",m_dir); 163 | #ifdef FLEXT_THREADS 164 | FLEXT_CADDATTR_VAR1(c,"detach",detach); 165 | FLEXT_CADDMETHOD_(c,0,"stop",m_stop); 166 | #endif 167 | 168 | FLEXT_CADDMETHOD_(c,0,"help",m_help); 169 | FLEXT_CADDMETHOD_(c,0,"reload",m_reload_); 170 | FLEXT_CADDMETHOD_(c,0,"reload.",m_reload); 171 | FLEXT_CADDMETHOD_(c,0,"doc+",m_doc_); 172 | FLEXT_CADDMETHOD_(c,0,"dir+",m_dir_); 173 | 174 | FLEXT_CADDMETHOD_(c,0,"set",m_set); 175 | 176 | FLEXT_CADDATTR_VAR1(c,"py",pymsg); 177 | FLEXT_CADDATTR_VAR1(c,"respond",respond); 178 | 179 | // init translation map 180 | for(const xlt *xi = xtdefs; xi->from; ++xi) xtable[xi->from] = xi->to; 181 | } 182 | 183 | pymeth::pymeth(int argc,const t_atom *argv) 184 | : funname(NULL) 185 | , function(NULL) 186 | , objects(NULL) 187 | { 188 | #ifdef FLEXT_THREADS 189 | FLEXT_ADDTIMER(stoptmr,tick); 190 | #endif 191 | 192 | ThrLockSys lock; 193 | 194 | int inlets; 195 | if(argc && CanbeInt(*argv)) { 196 | inlets = GetAInt(*argv); 197 | if(inlets < 1) inlets = 1; 198 | argv++,argc--; 199 | } 200 | else inlets = 1; 201 | 202 | objects = new PyObject *[inlets]; 203 | for(int i = 0; i < inlets; ++i) { objects[i] = Py_None; Py_INCREF(Py_None); } 204 | 205 | if(inlets <= 0) InitProblem(); 206 | 207 | AddInAnything(1+(inlets < 0?1:inlets)); 208 | AddOutAnything(); 209 | 210 | Register(GetRegistry(REGNAME)); 211 | 212 | if(argc) { 213 | const t_symbol *funnm = GetASymbol(*argv); 214 | argv++,argc--; 215 | 216 | if(funnm) 217 | SetFunction(funnm); 218 | else 219 | PyErr_SetString(PyExc_ValueError,"Invalid function name"); 220 | } 221 | 222 | if(argc) args(argc,argv); 223 | 224 | Report(); 225 | } 226 | 227 | pymeth::~pymeth() 228 | { 229 | if(objects) { 230 | for(int i = 0; i < CntIn()-1; ++i) Py_DECREF(objects[i]); 231 | delete[] objects; 232 | } 233 | 234 | ThrLockSys lock; 235 | Unregister(GetRegistry(REGNAME)); 236 | Report(); 237 | } 238 | 239 | void pymeth::Exit() 240 | { 241 | pybase::Exit(); 242 | flext_base::Exit(); 243 | } 244 | 245 | void pymeth::m_set(int argc,const t_atom *argv) 246 | { 247 | ThrLockSys lock; 248 | 249 | // function name has precedence 250 | if(argc >= 2) { 251 | const char *sn = GetAString(*argv); 252 | ++argv,--argc; 253 | 254 | if(sn) { 255 | if(!module || !strcmp(sn,PyModule_GetName(module))) { 256 | ImportModule(sn); 257 | Register(GetRegistry(REGNAME)); 258 | } 259 | } 260 | else 261 | PyErr_SetString(PyExc_ValueError,"Invalid module name"); 262 | } 263 | 264 | if(argc) { 265 | const t_symbol *fn = GetASymbol(*argv); 266 | if(fn) 267 | SetFunction(fn); 268 | else 269 | PyErr_SetString(PyExc_ValueError,"Invalid function name"); 270 | } 271 | 272 | Report(); 273 | } 274 | 275 | void pymeth::m_help() 276 | { 277 | post(""); 278 | post("%s %s - python method object, (C)2002-2012 Thomas Grill",thisName(),PY__VERSION); 279 | #ifdef FLEXT_DEBUG 280 | post("DEBUG VERSION, compiled on " __DATE__ " " __TIME__); 281 | #endif 282 | 283 | post("Arguments: %s [method name] {args...}",thisName()); 284 | 285 | post("Inlet 1:messages to control the py object"); 286 | post(" 2:call python function with message as argument(s)"); 287 | post("Outlet: 1:return values from python function"); 288 | post("Methods:"); 289 | post("\thelp: shows this help"); 290 | post("\tbang: call script without arguments"); 291 | post("\tset [script name] [function name]: set (script and) function name"); 292 | post("\treload {args...}: reload python script"); 293 | post("\treload. : reload with former arguments"); 294 | post("\tdoc: display module doc string"); 295 | post("\tdoc+: display function doc string"); 296 | post("\tdir: dump module dictionary"); 297 | post("\tdir+: dump function dictionary"); 298 | #ifdef FLEXT_THREADS 299 | post("\tdetach 0/1/2: detach threads"); 300 | post("\tstop {wait time (ms)}: stop threads"); 301 | #endif 302 | post(""); 303 | } 304 | 305 | void pymeth::ResetFunction() 306 | { 307 | Py_XDECREF(function); 308 | function = NULL; 309 | 310 | if(funname && objects[0] != Py_None) { 311 | function = PyObject_GetAttrString(objects[0],(char *)GetString(funname)); // new reference 312 | if(!function) 313 | PyErr_SetString(PyExc_AttributeError,"Method not found"); 314 | } 315 | 316 | // exception could be set here 317 | } 318 | 319 | void pymeth::SetFunction(const t_symbol *func) 320 | { 321 | // look for method name in translation table 322 | XTable::iterator it = xtable.find(func); 323 | funname = it == xtable.end()?func:it->second; 324 | 325 | ResetFunction(); 326 | } 327 | 328 | 329 | void pymeth::LoadModule() 330 | { 331 | SetFunction(funname); 332 | } 333 | 334 | void pymeth::UnloadModule() 335 | { 336 | } 337 | 338 | void pymeth::Load() 339 | { 340 | ResetFunction(); 341 | } 342 | 343 | void pymeth::Unload() 344 | { 345 | SetFunction(NULL); 346 | } 347 | 348 | void pymeth::callpy(PyObject *fun,PyObject *args) 349 | { 350 | PyObject *ret = PyObject_CallObject(fun,args); 351 | if(ret) { 352 | OutObject(this,0,ret); // exception might be raised here 353 | Py_DECREF(ret); 354 | } 355 | } 356 | 357 | bool pymeth::CbMethodResort(int n,const t_symbol *s,int argc,const t_atom *argv) 358 | { 359 | if(n == 0 && s != sym_bang) 360 | return flext_base::CbMethodResort(n,s,argc,argv); 361 | 362 | ThrState state = PyLockSys(); 363 | 364 | bool ret = false; 365 | 366 | if(n >= 1) { 367 | // store args 368 | PyObject *&obj = objects[n-1]; 369 | Py_DECREF(obj); 370 | obj = MakePyArg(s,argc,argv); // steal reference 371 | 372 | if(n > 1) ret = true; // just store, don't trigger 373 | } 374 | 375 | if(!ret) { 376 | if(function) { 377 | PyObject *self = PyMethod_Self(function); 378 | PyErr_Clear(); 379 | if(!self || self->ob_type != objects[0]->ob_type) 380 | // type has changed, search for new method 381 | ResetFunction(); 382 | else if(self != objects[0]) { 383 | // type hasn't changed, but object has 384 | PyObject *f = function; 385 | #if PY_MAJOR_VERSION < 3 386 | function = PyMethod_New(PyMethod_GET_FUNCTION(f),objects[0],PyMethod_GET_CLASS(f)); 387 | #else 388 | function = PyMethod_New(PyMethod_GET_FUNCTION(f),objects[0]); 389 | #endif 390 | Py_DECREF(f); 391 | } 392 | } 393 | else 394 | ResetFunction(); 395 | 396 | if(function) { 397 | Py_INCREF(function); 398 | 399 | int inlets = CntIn()-1; 400 | PyObject *pargs = PyTuple_New(inlets-1); 401 | for(int i = 1; i < inlets; ++i) { 402 | Py_INCREF(objects[i]); 403 | PyTuple_SET_ITEM(pargs,i-1,objects[i]); 404 | } 405 | 406 | gencall(function,pargs); // references are stolen 407 | ret = true; 408 | } 409 | else 410 | PyErr_SetString(PyExc_RuntimeError,"No function set"); 411 | 412 | Report(); 413 | } 414 | 415 | PyUnlock(state); 416 | 417 | Respond(ret); 418 | 419 | return ret; 420 | } 421 | 422 | void pymeth::DumpOut(const t_symbol *sym,int argc,const t_atom *argv) 423 | { 424 | ToOutAnything(GetOutAttr(),sym?sym:thisTag(),argc,argv); 425 | } 426 | -------------------------------------------------------------------------------- /source/pyprefix.h: -------------------------------------------------------------------------------- 1 | /* 2 | py/pyext - python script object for PD and MaxMSP 3 | 4 | Copyright (c)2002-2015 Thomas Grill (gr@grrrr.org) 5 | For information on usage and redistribution, and for a DISCLAIMER OF ALL 6 | WARRANTIES, see the file, "license.txt," in this distribution. 7 | */ 8 | 9 | #ifndef __PYPREFIX_H 10 | #define __PYPREFIX_H 11 | 12 | #define FLEXT_ATTRIBUTES 1 13 | #include 14 | 15 | // hack: must include math.h before Python.h (at least on OSX) 16 | // otherwise some functions don't get defined 17 | #include 18 | 19 | #ifdef PY_USE_FRAMEWORK 20 | #include 21 | #else 22 | #include 23 | #endif 24 | 25 | #if !defined(FLEXT_VERSION) || (FLEXT_VERSION < 501) 26 | #error You need at least flext version 0.5.1 27 | #endif 28 | 29 | #if FLEXT_OS == FLEXT_LINUX || FLEXT_OS == FLEXT_IRIX 30 | #include 31 | #endif 32 | 33 | #if FLEXT_SYS == FLEXT_SYS_PD && (!defined (PD_MINOR_VERSION) || ( PD_MAJOR_VERSION == 0 && PD_MINOR_VERSION < 37)) 34 | #error PD version >= 0.37 required, please upgrade! 35 | #endif 36 | 37 | #include 38 | #include 39 | 40 | #if FLEXT_SYS == FLEXT_SYS_PD && defined(PY_USE_INOFFICIAL) 41 | extern "C" { 42 | #include 43 | } 44 | #endif 45 | 46 | #if PY_VERSION_HEX < 0x02050000 47 | typedef int Py_ssize_t; 48 | #endif 49 | 50 | // these are copied from the Python 3.8.2 source because doing the right thing 51 | // with the pre-3.6.1 slice functions seems difficult... 52 | 53 | #if PY_MAJOR_VERSION < 3 54 | /* Assert a build-time dependency, as an expression. 55 | Your compile will fail if the condition isn't true, or can't be evaluated 56 | by the compiler. This can be used in an expression: its value is 0. 57 | Example: 58 | #define foo_to_char(foo) \ 59 | ((char *)(foo) \ 60 | + Py_BUILD_ASSERT_EXPR(offsetof(struct foo, string) == 0)) 61 | Written by Rusty Russell, public domain, http://ccodearchive.net/ */ 62 | #define Py_BUILD_ASSERT_EXPR(cond) \ 63 | (sizeof(char [1 - 2*!(cond)]) - 1) 64 | 65 | #define Py_BUILD_ASSERT(cond) do { \ 66 | (void)Py_BUILD_ASSERT_EXPR(cond); \ 67 | } while(0) 68 | #endif 69 | 70 | #if PY_VERSION_HEX < 0x03060100 71 | #endif 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /source/pysymbol.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | py/pyext - python script object for PD and Max/MSP 3 | 4 | Copyright (c)2002-2019 Thomas Grill (gr@grrrr.org) 5 | For information on usage and redistribution, and for a DISCLAIMER OF ALL 6 | WARRANTIES, see the file, "license.txt," in this distribution. 7 | */ 8 | 9 | #include "pyprefix.h" 10 | #include "pysymbol.h" 11 | 12 | inline pySymbol *symbol_newsym(const t_symbol *sym) 13 | { 14 | pySymbol *self = (pySymbol *)pySymbol_Type.tp_alloc(&pySymbol_Type, 0); 15 | if(self) self->sym = sym; 16 | return self; 17 | } 18 | 19 | static PyObject *symbol_new(PyTypeObject *type, PyObject *args, PyObject *kwds) 20 | { 21 | return (PyObject *)symbol_newsym(flext::sym__); 22 | } 23 | 24 | static int symbol_init(PyObject *self, PyObject *args, PyObject *kwds) 25 | { 26 | FLEXT_ASSERT(pySymbol_Check(self)); 27 | 28 | PyObject *arg = PySequence_GetItem(args,0); // new reference 29 | if(!arg) return -1; 30 | 31 | int ret = 0; 32 | 33 | if(pySymbol_Check(arg)) 34 | ((pySymbol *)self)->sym = pySymbol_AS_SYMBOL(arg); 35 | #if PY_MAJOR_VERSION < 3 36 | else if(PyString_Check(arg)) 37 | ((pySymbol *)self)->sym = flext::MakeSymbol(PyString_AS_STRING(arg)); 38 | #else 39 | else if(PyUnicode_Check(arg)) 40 | ((pySymbol *)self)->sym = flext::MakeSymbol(PyUnicode_AsUTF8(arg)); 41 | #endif 42 | else { 43 | PyErr_SetString(PyExc_TypeError, "string, unicode or symbol argument expected"); 44 | ret = -1; 45 | } 46 | Py_DECREF(arg); 47 | 48 | return ret; 49 | } 50 | 51 | static PyObject *symbol_str(PyObject *self) 52 | { 53 | FLEXT_ASSERT(pySymbol_Check(self)); 54 | return (PyObject *) 55 | #if PY_MAJOR_VERSION < 3 56 | PyString_FromString 57 | #else 58 | PyUnicode_FromString 59 | #endif 60 | (pySymbol_AS_STRING(self)); 61 | } 62 | 63 | static PyObject *symbol_repr(PyObject *self) 64 | { 65 | FLEXT_ASSERT(pySymbol_Check(self)); 66 | return (PyObject *) 67 | #if PY_MAJOR_VERSION < 3 68 | PyString_FromFormat 69 | #else 70 | PyUnicode_FromFormat 71 | #endif 72 | ("", pySymbol_AS_STRING(self)); 73 | } 74 | 75 | static PyObject *symbol_richcompare(PyObject *a,PyObject *b,int cmp) 76 | { 77 | if(pySymbol_Check(a) && pySymbol_Check(b)) { 78 | const t_symbol *asym = pySymbol_AS_SYMBOL(a); 79 | const t_symbol *bsym = pySymbol_AS_SYMBOL(b); 80 | 81 | int res = asym == bsym?0:strcmp(flext::GetString(asym),flext::GetString(bsym)); 82 | 83 | bool ret; 84 | switch(cmp) { 85 | case Py_LT: ret = res < 0; break; 86 | case Py_LE: ret = res <= 0; break; 87 | case Py_EQ: ret = res == 0; break; 88 | case Py_NE: ret = res != 0; break; 89 | case Py_GE: ret = res >= 0; break; 90 | case Py_GT: ret = res > 0; break; 91 | default: 92 | FLEXT_ASSERT(false); 93 | } 94 | return PyBool_FromLong(ret); 95 | } 96 | Py_INCREF(Py_NotImplemented); 97 | return Py_NotImplemented; 98 | } 99 | 100 | static long symbol_hash(PyObject *self) 101 | { 102 | FLEXT_ASSERT(pySymbol_Check(self)); 103 | return (long)pySymbol_AS_SYMBOL(self); 104 | } 105 | 106 | 107 | static Py_ssize_t symbol_length(PyObject *s) 108 | { 109 | pySymbol *self = reinterpret_cast(s); 110 | return strlen(flext::GetString(self->sym)); 111 | } 112 | 113 | static PyObject *symbol_item(PyObject *s,Py_ssize_t i) 114 | { 115 | pySymbol *self = reinterpret_cast(s); 116 | const char *str = flext::GetString(self->sym); 117 | int len = strlen(str); 118 | if(i < 0) i += len; 119 | 120 | if(i >= 0 && i < len) 121 | return 122 | #if PY_MAJOR_VERSION < 3 123 | PyString_FromStringAndSize 124 | #else 125 | PyUnicode_FromStringAndSize 126 | #endif 127 | (str+i,1); 128 | else { 129 | Py_INCREF(Py_None); 130 | return Py_None; 131 | } 132 | } 133 | 134 | static PyObject *symbol_slice(PyObject *s,Py_ssize_t ilow = 0,Py_ssize_t ihigh = 1<<(sizeof(int)*8-2)) 135 | { 136 | pySymbol *self = reinterpret_cast(s); 137 | const char *str = flext::GetString(self->sym); 138 | int len = strlen(str); 139 | if(ilow < 0) { 140 | ilow += len; 141 | if(ilow < 0) ilow = 0; 142 | } 143 | if(ihigh < 0) ihigh += len; 144 | if(ihigh >= len) ihigh = len-1; 145 | 146 | return 147 | #if PY_MAJOR_VERSION < 3 148 | PyString_FromStringAndSize 149 | #else 150 | PyUnicode_FromStringAndSize 151 | #endif 152 | (str+ilow, ilow <= ihigh?ihigh-ilow+1:0); 153 | } 154 | 155 | static PyObject *symbol_concat(PyObject *s,PyObject *op) 156 | { 157 | // pySymbol *self = reinterpret_cast(s); 158 | PyObject *nobj = symbol_slice(s); // take all 159 | if(nobj) { 160 | PyObject *ret = PySequence_Concat(nobj,op); 161 | Py_DECREF(nobj); 162 | return ret; 163 | } 164 | else 165 | return NULL; 166 | } 167 | 168 | static PyObject *symbol_repeat(PyObject *s,Py_ssize_t rep) 169 | { 170 | // pySymbol *self = reinterpret_cast(s); 171 | PyObject *nobj = symbol_slice(s); // take all 172 | if(nobj) { 173 | PyObject *ret = PySequence_Repeat(nobj,rep); 174 | Py_DECREF(nobj); 175 | return ret; 176 | } 177 | else 178 | return NULL; 179 | } 180 | 181 | static PySequenceMethods symbol_as_seq = { 182 | symbol_length, /* lenfunc sq_length __len__ */ 183 | symbol_concat, /* binaryfunc sq_concat __add__ */ 184 | symbol_repeat, /* ssizeargfunc sq_repeat __mul__ */ 185 | symbol_item, /* ssizeargfunc sq_item; __getitem__ */ 186 | NULL, /* ssizeobjargproc sq_ass_item __setitem__ */ 187 | NULL, /* objobjproc sq_contains __contains__ */ 188 | NULL, /* binaryfunc sq_inplace_concat __iadd__ */ 189 | NULL /* ssizeargfunc sq_inplace_repeat __imul */ 190 | }; 191 | 192 | static PyObject *symbol_iter(PyObject *s) 193 | { 194 | // pySymbol *self = reinterpret_cast(s); 195 | PyObject *nobj = symbol_slice(s); 196 | if(nobj) { 197 | PyObject *it = PyObject_GetIter(nobj); 198 | Py_DECREF(nobj); 199 | return it; 200 | } 201 | else 202 | return NULL; 203 | } 204 | 205 | 206 | 207 | PyTypeObject pySymbol_Type = { 208 | PyVarObject_HEAD_INIT(NULL, 0) 209 | "Symbol", /* tp_name */ 210 | sizeof(pySymbol), /* tp_basicsize */ 211 | 0, /* tp_itemsize */ 212 | 0, /* tp_dealloc */ 213 | 0, /* tp_print */ 214 | 0, /* tp_getattr */ 215 | 0, /* tp_setattr */ 216 | 0, /* tp_compare */ 217 | symbol_repr, /* tp_repr */ 218 | 0, /* tp_as_number */ 219 | &symbol_as_seq, /* tp_as_sequence */ 220 | 0, /* tp_as_mapping */ 221 | symbol_hash, /* tp_hash */ 222 | 0, /* tp_call */ 223 | symbol_str, /* tp_str */ 224 | 0, /* tp_getattro */ 225 | 0, /* tp_setattro */ 226 | 0, /* tp_as_buffer */ 227 | Py_TPFLAGS_DEFAULT /* | Py_TPFLAGS_BASETYPE*/, /* tp_flags */ 228 | "Symbol objects", /* tp_doc */ 229 | 0, /* tp_traverse */ 230 | 0, /* tp_clear */ 231 | symbol_richcompare, /* tp_richcompare */ 232 | 0, /* tp_weaklistoffset */ 233 | symbol_iter, /* tp_iter */ 234 | 0, /* tp_iternext */ 235 | 0, /* tp_methods */ 236 | 0, /* tp_members */ 237 | 0, /* tp_getset */ 238 | 0, /* tp_base */ 239 | 0, /* tp_dict */ 240 | 0, /* tp_descr_get */ 241 | 0, /* tp_descr_set */ 242 | 0, /* tp_dictoffset */ 243 | symbol_init, /* tp_init */ 244 | 0, /* tp_alloc */ 245 | symbol_new, /* tp_new */ 246 | }; 247 | 248 | pySymbol *pySymbol__; 249 | pySymbol *pySymbol_bang; 250 | pySymbol *pySymbol_list; 251 | pySymbol *pySymbol_symbol; 252 | pySymbol *pySymbol_float; 253 | pySymbol *pySymbol_int; 254 | 255 | 256 | void initsymbol() 257 | { 258 | if(PyType_Ready(&pySymbol_Type) < 0) 259 | return; 260 | 261 | Py_INCREF(&pySymbol_Type); 262 | 263 | // initialize predefined objects 264 | pySymbol__ = symbol_newsym(flext::sym__); 265 | pySymbol_bang = symbol_newsym(flext::sym_bang); 266 | pySymbol_list = symbol_newsym(flext::sym_list); 267 | pySymbol_symbol = symbol_newsym(flext::sym_symbol); 268 | pySymbol_float = symbol_newsym(flext::sym_float); 269 | pySymbol_int = symbol_newsym(flext::sym_int); 270 | } 271 | 272 | 273 | PyObject *pySymbol_FromSymbol(const t_symbol *sym) 274 | { 275 | pySymbol *op; 276 | if(sym == flext::sym__) 277 | Py_INCREF(op = pySymbol__); 278 | else if(sym == flext::sym_bang) 279 | Py_INCREF(op = pySymbol_bang); 280 | else if(sym == flext::sym_list) 281 | Py_INCREF(op = pySymbol_list); 282 | else if(sym == flext::sym_symbol) 283 | Py_INCREF(op = pySymbol_symbol); 284 | else if(sym == flext::sym_float) 285 | Py_INCREF(op = pySymbol_float); 286 | else if(sym == flext::sym_int) 287 | Py_INCREF(op = pySymbol_int); 288 | else 289 | op = symbol_newsym(sym); 290 | return (PyObject *)op; 291 | } 292 | -------------------------------------------------------------------------------- /source/pysymbol.h: -------------------------------------------------------------------------------- 1 | /* 2 | py/pyext - python script object for PD and Max/MSP 3 | 4 | Copyright (c)2002-2019 Thomas Grill (gr@grrrr.org) 5 | For information on usage and redistribution, and for a DISCLAIMER OF ALL 6 | WARRANTIES, see the file, "license.txt," in this distribution. 7 | */ 8 | 9 | #ifndef __PYSYMBOL_H 10 | #define __PYSYMBOL_H 11 | 12 | #include 13 | 14 | #if !defined(FLEXT_VERSION) || (FLEXT_VERSION < 500) 15 | #error You need at least flext version 0.5.0 16 | #endif 17 | 18 | #ifdef PY_USE_FRAMEWORK 19 | #include 20 | #else 21 | #include 22 | #endif 23 | 24 | 25 | #ifdef _MSC_VER 26 | #ifdef PY_EXPORTS 27 | #define PY_EXPORT __declspec(dllexport) 28 | #else 29 | #define PY_EXPORT __declspec(dllimport) 30 | #endif 31 | #else 32 | #define PY_EXPORT 33 | #endif 34 | 35 | typedef struct { 36 | PyObject_HEAD 37 | /* Type-specific fields go here. */ 38 | const t_symbol *sym; 39 | } pySymbol; 40 | 41 | PY_EXPORT extern PyTypeObject pySymbol_Type; 42 | 43 | PY_EXPORT extern pySymbol *pySymbol__; 44 | PY_EXPORT extern pySymbol *pySymbol_bang; 45 | PY_EXPORT extern pySymbol *pySymbol_list; 46 | PY_EXPORT extern pySymbol *pySymbol_symbol; 47 | PY_EXPORT extern pySymbol *pySymbol_float; 48 | PY_EXPORT extern pySymbol *pySymbol_int; 49 | 50 | 51 | #define pySymbol_Check(op) PyObject_TypeCheck(op, &pySymbol_Type) 52 | #define pySymbol_CheckExact(op) ((op)->ob_type == &pySymbol_Type) 53 | 54 | 55 | PY_EXPORT PyObject *pySymbol_FromSymbol(const t_symbol *sym); 56 | 57 | inline PyObject *pySymbol_FromString(const char *str) 58 | { 59 | return pySymbol_FromSymbol(flext::MakeSymbol(str)); 60 | } 61 | 62 | inline PyObject *pySymbol_FromString(PyObject *str) 63 | { 64 | const char *cstr; 65 | #if PY_MAJOR_VERSION < 3 66 | if(PyString_Check(str)) 67 | cstr = PyString_AsString(str); 68 | #else 69 | if(PyUnicode_Check(str)) 70 | cstr = PyUnicode_AsUTF8(str); 71 | #endif 72 | else 73 | PyErr_SetString(PyExc_TypeError, "Type must be string or unicode"); 74 | 75 | return pySymbol_FromString(cstr); 76 | } 77 | 78 | inline const t_symbol *pySymbol_AS_SYMBOL(PyObject *op) 79 | { 80 | return ((pySymbol *)op)->sym; 81 | } 82 | 83 | inline const t_symbol *pySymbol_AsSymbol(PyObject *op) 84 | { 85 | return pySymbol_Check(op)?pySymbol_AS_SYMBOL(op):NULL; 86 | } 87 | 88 | inline const char *pySymbol_AS_STRING(PyObject *op) 89 | { 90 | return flext::GetString(pySymbol_AS_SYMBOL(op)); 91 | } 92 | 93 | inline const t_symbol *pyObject_AsSymbol(PyObject *op) 94 | { 95 | #if PY_MAJOR_VERSION < 3 96 | if(PyString_Check(op)) 97 | return flext::MakeSymbol(PyString_AS_STRING(op)); 98 | #else 99 | if(PyUnicode_Check(op)) 100 | return flext::MakeSymbol(PyUnicode_AsUTF8(op)); 101 | #endif 102 | else 103 | return pySymbol_AsSymbol(op); 104 | } 105 | 106 | #endif 107 | -------------------------------------------------------------------------------- /source/register.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | py/pyext - python external object for PD and MaxMSP 3 | 4 | Copyright (c)2002-2015 Thomas Grill (gr@grrrr.org) 5 | For information on usage and redistribution, and for a DISCLAIMER OF ALL 6 | WARRANTIES, see the file, "license.txt," in this distribution. 7 | */ 8 | 9 | #include "pybase.h" 10 | 11 | #if 1 12 | 13 | PyObject *pybase::GetRegistry(const char *regnm) 14 | { 15 | if(module) { 16 | FLEXT_ASSERT(dict); // module must have a valid dict 17 | 18 | // add this to module registry 19 | PyObject *reg = PyDict_GetItemString(dict,(char *)regnm); // borrowed!!! 20 | if(reg) 21 | FLEXT_ASSERT(PyDict_Check(reg)); 22 | else { 23 | // make a new empty registry 24 | reg = PyDict_New(); 25 | PyDict_SetItemString(dict,(char *)regnm,reg); 26 | } 27 | return reg; 28 | } 29 | else 30 | return NULL; 31 | } 32 | 33 | void pybase::SetRegistry(const char *regnm,PyObject *reg) 34 | { 35 | if(module) { 36 | FLEXT_ASSERT(dict); // module must have a valid dict 37 | FLEXT_ASSERT(reg && PyDict_Check(reg)); 38 | PyDict_SetItemString(dict,(char *)regnm,reg); 39 | } 40 | } 41 | 42 | void pybase::Register(PyObject *reg) 43 | { 44 | if(!module) return; 45 | FLEXT_ASSERT(reg && PyDict_Check(reg)); 46 | 47 | // add this to module registry 48 | Py_INCREF(Py_None); 49 | PyObject *key = PyLong_FromUnsignedLong((size_t)this); 50 | PyDict_SetItem(reg,key,Py_None); 51 | } 52 | 53 | void pybase::Unregister(PyObject *reg) 54 | { 55 | if(!module) return; 56 | FLEXT_ASSERT(reg && PyDict_Check(reg)); 57 | 58 | // remove this from module registry 59 | PyObject *key = PyLong_FromUnsignedLong((size_t)this); 60 | PyObject *item = PyDict_GetItem(reg,key); 61 | if(!item) 62 | post("py/pyext - Internal error: object not found in registry"); 63 | else 64 | PyDict_DelItem(reg,key); 65 | } 66 | 67 | /* 68 | void pybase::RegLoad(PyObject *reg) 69 | { 70 | 71 | } 72 | 73 | void pybase::RegUnload(PyObject *reg) 74 | { 75 | } 76 | */ 77 | 78 | #else 79 | 80 | void pybase::Register(const char *regnm) 81 | { 82 | if(module) { 83 | // add this to module registry 84 | 85 | PyObject *reg = PyDict_GetItemString(dict,(char *)regnm); // borrowed!!! 86 | PyObject *add = Py_BuildValue("[i]",(long)this); 87 | if(!reg || !PyList_Check(reg)) { 88 | if(PyDict_SetItemString(dict,(char *)regnm,add)) { 89 | post("py/pyext - Could not set registry"); 90 | } 91 | } 92 | else { 93 | PySequence_InPlaceConcat(reg,add); 94 | } 95 | } 96 | } 97 | 98 | void pybase::Unregister(const char *regnm) 99 | { 100 | if(module) { 101 | // remove this from module registry 102 | 103 | PyObject *reg = PyDict_GetItemString(dict,(char *)regnm); // borrowed!!! 104 | PyObject *add = Py_BuildValue("i",(int)this); 105 | if(!reg || !PySequence_Check(reg)) 106 | post("py/pyext - Internal error: Registry not found!?"); 107 | else { 108 | int ix = PySequence_Index(reg,add); 109 | if(ix < 0) { 110 | post("py/pyext - Internal error: object not found in registry?!"); 111 | } 112 | else { 113 | PySequence_DelItem(reg,ix); 114 | } 115 | } 116 | Py_DECREF(add); 117 | } 118 | } 119 | 120 | void pybase::Reregister(const char *regnm) 121 | { 122 | if(module) { 123 | // remove this from module registry 124 | 125 | PyObject *reg = PyDict_GetItemString(dict,(char *)regnm); // borrowed!!! 126 | 127 | if(!reg || !PySequence_Check(reg)) 128 | post("py/pyext - Internal error: Registry not found!?"); 129 | else { 130 | int cnt = PySequence_Size(reg); 131 | for(int i = 0; i < cnt; ++i) { 132 | PyObject *it = PySequence_GetItem(reg,i); // new reference 133 | if(!it || !PyInt_Check(it)) { 134 | post("py/pyext - Internal error: Corrupt registry?!"); 135 | } 136 | else { 137 | pybase *th = (pybase *)PyInt_AsLong(it); 138 | th->module = module; 139 | th->dict = dict; 140 | th->Reload(); 141 | } 142 | 143 | Py_XDECREF(it); 144 | } 145 | } 146 | } 147 | } 148 | 149 | #endif 150 | --------------------------------------------------------------------------------