From 094df6bb553a100dc4169c554de4c6fe1ee6a353 Mon Sep 17 00:00:00 2001 From: "Charles K. Neimog" Date: Mon, 27 Nov 2023 15:56:34 -0300 Subject: [PATCH 01/21] remove method pd.pip_install from pd module --- src/module.c | 158 ++++++--------------------------------------------- 1 file changed, 18 insertions(+), 140 deletions(-) diff --git a/src/module.c b/src/module.c index d0dfe10d..1d64a174 100755 --- a/src/module.c +++ b/src/module.c @@ -289,8 +289,8 @@ static PyObject *Py4pdMod_AccumGlobalVar(PyObject *self, PyObject *args) { x->pdcollect = Py4pdMod_CreatePdcollectHash(8); } Py4pdMod_AccumItem(x->pdcollect, key, pValueScript); - x->pdcollect->items[Py4pdMod_HashFunction(x->pdcollect, key)] - ->aCumulative = 1; + x->pdcollect->items[Py4pdMod_HashFunction(x->pdcollect, key)]->aCumulative = + 1; free(key); Py_RETURN_TRUE; @@ -356,8 +356,8 @@ static PyObject *Py4pdMod_GetObjArgs(PyObject *self, PyObject *args) { PyObject *pList = PyList_New(0); for (int i = 0; i < x->objArgsCount; i++) { if (x->pdObjArgs[i].a_type == A_FLOAT) { - int isInt = (int)x->pdObjArgs[i].a_w.w_float == - x->pdObjArgs[i].a_w.w_float; + int isInt = + (int)x->pdObjArgs[i].a_w.w_float == x->pdObjArgs[i].a_w.w_float; if (isInt) { PyObject *Number = PyLong_FromLong(x->pdObjArgs[i].a_w.w_float); PyList_Append(pList, Number); @@ -411,8 +411,7 @@ static PyObject *Py4pdMod_PdRecursiveCall(PyObject *self, PyObject *args) { x->stackLimit) { // seems to be the limit in PureData, x->recursiveObject = pValue; if (x->recursiveClock == NULL) - x->recursiveClock = - clock_new(x, (t_method)Py4pdMod_RecursiveTick); + x->recursiveClock = clock_new(x, (t_method)Py4pdMod_RecursiveTick); Py_INCREF(pValue); // avoid thing to be deleted x->recursiveCalls = 0; clock_delay(x->recursiveClock, 0); @@ -934,7 +933,7 @@ static PyObject *Py4pdMod_PdTabRead(PyObject *self, PyObject *args, double *pArrayData = (double *)PyArray_DATA((PyArrayObject *)pAudio); if (pArrayData == NULL) { pd_error(x, "[Python] pd.tabread: error creating " - "numpy array for vec"); + "numpy array for vec"); return NULL; } for (i = 0; i < vecsize; i++) { @@ -1063,26 +1062,23 @@ static PyObject *Py4pdMod_ShowImage(PyObject *self, PyObject *args) { height = Py4pdUtils_Ntohl(height); x->width = width; x->height = height; - } - - else { - pd_error(x, "[Python] pd.showimage: file format not supported"); - PyErr_SetString(PyExc_TypeError, - "[Python] pd.showimage: file format not supported"); + } else { + PyErr_SetString( + PyExc_TypeError, + "pd.showimage: file format not supported, use .ppm, .gif, or .png"); return NULL; } - if (glist_isvisible(x->glist) && - gobj_shouldvis((t_gobj *)x, x->glist)) { - const char *file_name_open = Py4pdPic_Filepath(x, filename->s_name); - if (access(file_name_open, F_OK) == -1) { + if (glist_isvisible(x->glist) && gobj_shouldvis((t_gobj *)x, x->glist)) { + const char *fileNameOpen = Py4pdPic_Filepath(x, filename->s_name); + if (access(fileNameOpen, F_OK) == -1) { pd_error(x, "[Python] pd.showimage: file not found"); PyErr_SetString(PyExc_TypeError, "[Python] pd.showimage: file not found"); return NULL; } - if (file_name_open) { - x->picFilePath = gensym(file_name_open); + if (fileNameOpen) { + x->picFilePath = gensym(fileNameOpen); if (x->defImg) { x->defImg = 0; } @@ -1093,7 +1089,7 @@ static PyObject *Py4pdMod_ShowImage(PyObject *self, PyObject *args) { sys_vgui("if {[info exists %lx_picname] == 0} {image create " "photo %lx_picname -file \"%s\"\n set %lx_picname " "1\n}\n", - x->picFilePath, x->picFilePath, file_name_open, + x->picFilePath, x->picFilePath, fileNameOpen, x->picFilePath); Py4pdPic_Draw(x, x->glist, 1); } @@ -1254,121 +1250,6 @@ static PyObject *Py4pdMod_ClearPlayer(PyObject *self, PyObject *args) { Py_RETURN_NONE; } -// ================================= -// ============ THREADS ============ -// ================================= -struct pipArgs { - - t_py *x; - int globalInstall; - int detachInstall; - char *package; -}; - -// ================================= - -void *Py4pdMod_PipInstallDetached(void *arg) { - struct pipArgs *pipArgs = (struct pipArgs *)arg; - - char install_path[MAXPDSTRING]; - - if (pipArgs->globalInstall == 1) { - snprintf(install_path, MAXPDSTRING, "%s/py-modules", - pipArgs->x->py4pdPath->s_name); - } else { - snprintf(install_path, MAXPDSTRING, "%s/py-modules", - pipArgs->x->pdPatchPath->s_name); - } - -#ifdef __linux__ - char command[2048]; - snprintf(command, sizeof(command), - "python%d.%d -m pip install %s --target %s --upgrade", - PY_MAJOR_VERSION, PY_MINOR_VERSION, pipArgs->package, install_path); - post(command); - system(command); -#endif - -#ifdef __APPLE__ - // macOS command - char command[MAXPDSTRING]; - snprintf(command, sizeof(command), - "open -a Terminal /usr/local/bin/python%d.%d -m pip install %s " - "--target %s --upgrade", - PY_MAJOR_VERSION, PY_MINOR_VERSION, pipArgs->package, install_path); - system(command); -#endif - -#ifdef _WIN32 - // Windows command - char command[MAXPDSTRING]; - snprintf(command, sizeof(command), - "py -%d.%d -m pip install %s --target %s --upgrade", - PY_MAJOR_VERSION, PY_MINOR_VERSION, pipArgs->package, install_path); - system(command); -#endif - - post("[py4pd] Installation of %s finished\n", pipArgs->package); - pd_error(NULL, "You must restart PureData to use the new module"); - free(pipArgs); - return NULL; -} - -// ================================= -// ============= PIP =============== -// ================================= -static PyObject *Py4pdMod_PipInstall(PyObject *self, PyObject *args, - PyObject *keywords) { - (void)self; - char *pipPackage; - int global = 0; - int detach = 0; - - t_py *x = Py4pdUtils_GetObject(self); - - if (keywords == NULL) { - PyErr_Clear(); - } else { - - if (PyDict_Contains(keywords, PyUnicode_FromString("global_install"))) { - PyObject *global_value = PyDict_GetItemString(keywords, "global_install"); - if (global_value == Py_True) { - global = 1; - } - } - if (PyDict_Contains(keywords, PyUnicode_FromString("detach_install"))) { - PyObject *detach_value = PyDict_GetItemString(keywords, "detach_install"); - if (detach_value == Py_True) { - detach = 1; - } - } - } - if (!PyArg_ParseTuple(args, "s", &pipPackage)) { - PyErr_SetString(PyExc_TypeError, "[Python] pd.pipInstall: wrong arguments"); - return NULL; - } - - struct pipArgs *pipArgs = malloc(sizeof(struct pipArgs)); - pipArgs->globalInstall = 0; - pipArgs->x = x; - pipArgs->detachInstall = detach; - pipArgs->package = pipPackage; - - post("[py4pd] Installing %s, wait!", pipArgs->package); - - pthread_t thread_id; - pthread_create(&thread_id, NULL, Py4pdMod_PipInstallDetached, - (void *)pipArgs); - - if (detach == 1) { - pthread_detach(thread_id); - } else { - pthread_join(thread_id, NULL); - } - - return PyLong_FromLong(0); -} - // ================================= // ========= MODULE INIT =========== // ================================= @@ -1400,7 +1281,8 @@ PyMethodDef PdMethods[] = { {"out", (PyCFunction)Py4pdMod_PdOut, METH_VARARGS | METH_KEYWORDS, "Output in out0 from PureData"}, {"send", Py4pdMod_PdSend, METH_VARARGS, - "Send message to PureData, it can be received with the object [receive]"}, + "Send message to PureData, it can be received with the object " + "[receive]"}, {"print", (PyCFunction)Py4pdMod_PdPrint, METH_VARARGS | METH_KEYWORDS, "Print informations in PureData Console"}, {"logpost", Py4pdMod_PdLogPost, METH_VARARGS, @@ -1453,10 +1335,6 @@ PyMethodDef PdMethods[] = { {"add_object", (PyCFunction)Py4pdLib_AddObj, METH_VARARGS | METH_KEYWORDS, "It adds python functions as objects"}, - // pip install - {"pip_install", (PyCFunction)Py4pdMod_PipInstall, - METH_VARARGS | METH_KEYWORDS, "It installs a pip package"}, - // Others {"get_obj_pointer", Py4pdMod_GetObjPointer, METH_NOARGS, "Get PureData Object Pointer"}, From 86626a87fa145c7c5eaebc55d7b7f05f650b90f4 Mon Sep 17 00:00:00 2001 From: "Charles K. Neimog" Date: Wed, 29 Nov 2023 09:48:51 -0300 Subject: [PATCH 02/21] update website --- resources/README.deken.pd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/README.deken.pd b/resources/README.deken.pd index 14cd2abd..6b3d165a 100644 --- a/resources/README.deken.pd +++ b/resources/README.deken.pd @@ -32,5 +32,5 @@ #X text 107 427 <-- Click on this bang for Linux.; #X obj 59 44 cnv 5 5 22 empty empty Thanks\ for\ downloading\ [py4pd]\ by\ Charles\ K.\ Neimog! 10 12 0 15 #000000 #202020 0; #X text 66 185 [py4pd] is used by third-party libraries. I already upload some objects to deken. You can search for pd-ji (Just Intonation Tools) \, Orchidea (load the free Orchidea samples using midi data) and others. Check the complete list in., f 70; -#X msg 266 239 www.charlesneimog.com/py4pd; +#X msg 266 239 charlesneimog.github.io/py4pd; #X connect 13 0 3 0; From d8a26b9b7aa107a97cd628c1a1d7519a58a49504 Mon Sep 17 00:00:00 2001 From: "Charles K. Neimog" Date: Wed, 29 Nov 2023 18:14:09 -0300 Subject: [PATCH 03/21] try to update pip for mac os --- resources/py4pd-mod/src/pip.py | 69 +++++++++++----------------------- 1 file changed, 21 insertions(+), 48 deletions(-) diff --git a/resources/py4pd-mod/src/pip.py b/resources/py4pd-mod/src/pip.py index 85167716..edf306a8 100755 --- a/resources/py4pd-mod/src/pip.py +++ b/resources/py4pd-mod/src/pip.py @@ -12,51 +12,6 @@ folder = "" -class MacOSpip: - def __init__(self, pippackage, pipfolder): - import tkinter as tk - - self.package = pippackage - self.folder = pipfolder - self.window = tk.Tk() - # icon_file = pd.py4pdfolder() + "/resources/icons/pd.icns" - # self.window.iconbitmap(icon_file) - # self.window.protocol("WM_DELETE_WINDOW", self.close_window) - self._pipinstall() # Renamed to avoid naming conflict - - def drawWarning(self): - from tkinter import Label, LabelFrame, Tk - - # get screen width and height - root = self.window - screen_width = root.winfo_screenwidth() - screen_height = root.winfo_screenheight() - - # calculate x and y coordinates for the Tk root window to center it on the screen - x = (screen_width / 2) - (300 / 2) - y = (screen_height / 2) - (100 / 2) - - root.geometry("300x100+%d+%d" % (x, y)) - root.resizable(False, False) - - # create text - text = LabelFrame( - root, text="Installing " + package + " , please wait...", padx=20, pady=20 - ) - text.pack(fill="both", expand=1) - - # add label inside the label frame - label = Label( - text, - text="Installing " + package + " , please wait...", - anchor="center", - justify="center", - ) - label.pack(fill="both", expand=1) - - # update window - self.window.update() - def _pipinstall(self): self.drawWarning() version = sys.version_info @@ -218,9 +173,27 @@ def pipinstall(mypackage): elif platform.system() == "Darwin": try: - my_window = MacOSpip(package, folder) - my_window.run() - + value = subprocess.run( + [ + f"/usr/local/bin/python{major}.{minor}", + "-m", + "pip", + "install", + "--target", + f"{folder}/py-modules", + package, + "--upgrade", + ], + check=True, + ) + if value.returncode != 0: + pd.logpost(3, "pip return value" + str(value)) + pd.error( + "You need to restart PureData, to check if the installation process worked" + ) + else: + pd.print(f"{package} Installed!") + pd.error("You need to restart PureData") except Exception as e: pd.error(str(e)) else: From c18716c7b47408554420ba305eb3186f2bc7de7e Mon Sep 17 00:00:00 2001 From: "Charles K. Neimog" Date: Thu, 30 Nov 2023 09:34:10 -0300 Subject: [PATCH 04/21] update py4pd internal module --- resources/README.deken.pd | 28 +- resources/localdeps/localdeps.linux.sh | 274 ----------------- resources/localdeps/localdeps.macos.sh | 310 -------------------- resources/localdeps/localdeps.win.sh | 296 ------------------- resources/mac.pd | 146 --------- resources/patch.pd | 11 - resources/py.py | 5 +- resources/py4pd-mod/help/py.collect-help.pd | 17 +- resources/py4pd-mod/help/py.logic-help.pd | 40 +++ resources/py4pd-mod/help/py.loops-help.pd | 60 ++++ resources/py4pd-mod/py4pd.py | 37 ++- resources/py4pd-mod/src/pip.py | 42 --- 12 files changed, 152 insertions(+), 1114 deletions(-) delete mode 100755 resources/localdeps/localdeps.linux.sh delete mode 100755 resources/localdeps/localdeps.macos.sh delete mode 100755 resources/localdeps/localdeps.win.sh delete mode 100644 resources/mac.pd delete mode 100644 resources/patch.pd create mode 100644 resources/py4pd-mod/help/py.logic-help.pd create mode 100644 resources/py4pd-mod/help/py.loops-help.pd diff --git a/resources/README.deken.pd b/resources/README.deken.pd index 6b3d165a..e2d27e9f 100644 --- a/resources/README.deken.pd +++ b/resources/README.deken.pd @@ -1,7 +1,7 @@ -#N canvas 476 73 599 481 12; -#X obj 71 305 py4pd; -#X obj 78 394 bng 23 250 50 0 nutelaOS akdkabdk empty 0 -10 0 12 #dfdfdf #000000 #000000; -#X obj 78 424 bng 23 250 50 0 linux adjhkahdk empty 0 -10 0 12 #dfdfdf #000000 #000000; +#N canvas 476 73 599 565 12; +#X obj 69 366 py4pd; +#X obj 74 488 bng 23 250 50 0 nutelaOS akdkabdk empty 0 -10 0 12 #dfdfdf #000000 #000000; +#X obj 74 518 bng 23 250 50 0 linux adjhkahdk empty 0 -10 0 12 #dfdfdf #000000 #000000; #N canvas 604 170 651 438 links 0; #X obj 108 378 pdcontrol; #X obj 133 192 r linux; @@ -22,15 +22,15 @@ #X connect 7 0 8 0; #X connect 8 0 9 0; #X connect 9 0 0 0; -#X restore 266 270 pd links; -#X obj 78 365 bng 23 250 50 0 howinstallpython howinstallpythonr empty 0 -10 0 12 #dfdfdf #000000 #000000; +#X restore 262 319 pd links; +#X obj 74 459 bng 23 250 50 0 howinstallpython howinstallpythonr empty 0 -10 0 12 #dfdfdf #000000 #000000; #X text 59 83 The [py4pd] external allows you to use Python in Pure Data. It is similar to py/pyext \, but is simpler to maintain \, keep it up to date \, use modules \, lists and other things., f 71; -#X text 60 133 With Python in Pd you can generate scores \, work with AI \, use lots of Audio Analysis tools and OpenMusic functions with libraries like om_py \, music21 \, neoscore and others., f 71; -#X text 116 304 <-- IMPORTANT: If this object failed to load \, Python is not installed in your system. This version uses python 3.11 and you can't use any other version.; -#X text 107 369 <-- Click to see how to install Python!; -#X text 107 396 <-- Click for MacOS OR Windows (go to the bottom of page).; -#X text 107 427 <-- Click on this bang for Linux.; +#X text 103 463 <-- Click to see how to install Python!; +#X text 103 490 <-- Click for MacOS OR Windows (go to the bottom of page).; +#X text 103 521 <-- Click on this bang for Linux.; #X obj 59 44 cnv 5 5 22 empty empty Thanks\ for\ downloading\ [py4pd]\ by\ Charles\ K.\ Neimog! 10 12 0 15 #000000 #202020 0; -#X text 66 185 [py4pd] is used by third-party libraries. I already upload some objects to deken. You can search for pd-ji (Just Intonation Tools) \, Orchidea (load the free Orchidea samples using midi data) and others. Check the complete list in., f 70; -#X msg 266 239 charlesneimog.github.io/py4pd; -#X connect 13 0 3 0; +#X text 59 232 [py4pd] is used by third-party libraries. I already upload some objects to deken. You can search for pd-ji (Just Intonation Tools) \, Orchidea (load the free Orchidea samples using midi data) and others. Check the complete list in., f 71; +#X msg 262 288 charlesneimog.github.io/py4pd; +#X text 59 133 With Python in Pd you can generate scores \, work with AI \, use lots of Audio Analysis tools and OpenMusic functions with libraries like om_py \, music21 \, neoscore and others. It also brings the feature to write Pd externals in pure Python code \, what allows to use all the big Python enviroment in PureData., f 71; +#X text 114 365 <-- IMPORTANT: If this object failed to load \, Python is not installed in your system. This version uses python 3.11 and you CAN'T use any other version.; +#X connect 11 0 3 0; diff --git a/resources/localdeps/localdeps.linux.sh b/resources/localdeps/localdeps.linux.sh deleted file mode 100755 index 5a52d748..00000000 --- a/resources/localdeps/localdeps.linux.sh +++ /dev/null @@ -1,274 +0,0 @@ -#!/bin/sh -# -# creates local copies of all dependencies (dynamic libraries) -# and sets RUNPATH to $ORIGIN on each so they will find -# each other. -# -# usage: $0 - - - -verbose=0 -include_paths= -exclude_paths= - - - -#default exclude/include paths -#exclude_paths="*/libc.so.*:*/libarmmem.*.so.*:*/libdl.so.*:*/libglib-.*.so.*:*/libgomp.so.*:*/libgthread.*.so.*:*/libm.so.*:*/libpthread.*.so.*:*/libpthread.so.*:*/libstdc++.so.*:*/libgcc_s.so.*:*/libz.so.*" -include_paths="/*" - -# UTILITIES -if [ -e "${0%/*}/localdeps.utilities.source" ]; then -. "${0%/*}/localdeps.utilities.source" -else - # the following section (from @BEGIN_UTILITIES@ to @END_UTILITIES@) - # was copied from 'localdeps.utilities.source'. - # changes you make to this section will be lost. -#@BEGIN_UTILITIES@ -verbose=${verbose:-0} - -error() { - echo "$@" 1>&2 -} - -substitute() { - # substitutes literal strings - # usage: echo foo | substitute foo bar g - sed "s/$(echo $1 | sed 's:[]\[^$.*/&]:\\&:g')/$(echo $2 | sed 's:[]\[^$.*/&]:\\&:g')/$3" -} - -check_binaries() { - local cmd - for cmd in "$@"; do - if ! which "${cmd}" > /dev/null; then - error "Could not find '${cmd}'. Is it installed?" - exit 127 - fi - done -} - - -normalize_path() { - # normalize a path specification, e.g. on Windows turn C:\Foo\Bar\ into /c/foo/bar/" - # on most system this doesn't do anything, but override it to your needs... - # e.g. on Windows use: ${CYGPATH} "$1" | tr "[A-Z]" "[a-z]" - echo "$1" -} - -list_dirs() { - # - local IN="$@" - local iter - while [ "$IN" ] ;do - iter=${IN%%:*} - echo "${iter}" - [ "$IN" = "$iter" ] && IN='' || IN="${IN#*:}" - done -} - -check_in_path() { - local needle=$1 - local p - local patterns - shift - patterns="$@" - while [ "${patterns}" ]; do - p=${patterns%%:*} - [ "$patterns" = "$p" ] && patterns='' || patterns="${patterns#*:}" - - case "${needle}" in - ${p}) - echo "${needle}" - break - ;; - esac - done | grep . >/dev/null -} - -check_includedep() { - local path=$(normalize_path "$1") - local p - local result=0 - # exclude non-existing files - if [ ! -e "${path}" ]; then - return 0 - fi - - # skip paths that match one of the patterns in ${exclude_paths} - if check_in_path "${path}" "${exclude_paths}"; then - return 1 - fi - # only include paths that match one of the patterns in ${include_paths} - if check_in_path "${path}" "${include_paths}"; then - echo "${path}" - return 0 - fi - # skip the rest - return 1 -} - -usage() { - cat >/dev/stderr <] [-X ] [ ...] - recursively includes all dependencies of the given binaries - - -I : adds one include path entry - -X : adds one exclude path entry - -v: raise verbosity - -q: lower verbosity - -EOF - - case "$0" in - *win*) - cat >/dev/stderr </dev/stderr < ' \ - | while read _ _ libpath _; do - inc=$(check_includedep "${libpath}") - if [ "x${inc}" != "x" ]; then - echo "${inc}" - fi - done -} - -install_deps () { - # make a local copy of all linked libraries of given binary - # and set RUNPATH to $ORIGIN (exclude "standard" libraries) - # arg1: binary to check - local outdir - outdir="$(dirname "$1")/${arch}" - local outfile - if [ ! -d "${outdir}" ]; then - outdir=. - fi - list_deps "$1" | while read libpath; do - libname=$(basename "${libpath}") - if [ ! -e "${libpath}" ]; then - error "DEP: ${INSTALLDEPS_INDENT} WARNING: could not make copy of '${libpath}'. Not found" - continue - fi - outfile="${outdir}/$(basename ${libpath})" - if [ -e "${outfile}" ]; then - error "DEP: ${INSTALLDEPS_INDENT} ${libpath} SKIPPED" - else - error "DEP: ${INSTALLDEPS_INDENT} ${libpath} -> ${outdir}/" - cp "${libpath}" "${outfile}" - patchelf --set-rpath \$ORIGIN "${outfile}" - fi - done - # remove ./ of ${arch} - local archdir - archdir=$(echo "${arch}" | sed -e 's|^\.*/||') - error "NEW ARCHDIR: ${archdir}" - patchelf --set-rpath \$ORIGIN/amd64 "${1}" -} - - - -# Check dependencies -check_binaries grep ldd patchelf - -for f in "$@"; do - # Check if we can read from given file - if ! ldd "${f}" > /dev/null 2>&1; then - error "Skipping '${f}'. Is it a binary file?" - continue - fi - depdir="$(dirname ${f})/${arch}" - mkdir -p "${depdir}" - install_deps "${f}" -done diff --git a/resources/localdeps/localdeps.macos.sh b/resources/localdeps/localdeps.macos.sh deleted file mode 100755 index cecff022..00000000 --- a/resources/localdeps/localdeps.macos.sh +++ /dev/null @@ -1,310 +0,0 @@ -#!/bin/sh - -## puts dependencies besides the binary -# LATER: put dependencies into a separate folder - -## usage: $0 [...] - -#default exclude/include paths -#exclude_paths="/usr/lib/*:/System/Library/Frameworks/*" -include_paths="/*" -recursion=false - -# UTILITIES -if [ -e "${0%/*}/localdeps.utilities.source" ]; then -. "${0%/*}/localdeps.utilities.source" -else - # the following section (from @BEGIN_UTILITIES@ to @END_UTILITIES@) - # was copied from 'localdeps.utilities.source'. - # changes you make to this section will be lost. -#@BEGIN_UTILITIES@ -verbose=${verbose:-0} - -error() { - echo "$@" 1>&2 -} - -substitute() { - # substitutes literal strings - # usage: echo foo | substitute foo bar g - sed "s/$(echo $1 | sed 's:[]\[^$.*/&]:\\&:g')/$(echo $2 | sed 's:[]\[^$.*/&]:\\&:g')/$3" -} - -check_binaries() { - local cmd - for cmd in "$@"; do - if ! which "${cmd}" > /dev/null; then - error "Could not find '${cmd}'. Is it installed?" - exit 127 - fi - done -} - - -normalize_path() { - # normalize a path specification, e.g. on Windows turn C:\Foo\Bar\ into /c/foo/bar/" - # on most system this doesn't do anything, but override it to your needs... - # e.g. on Windows use: ${CYGPATH} "$1" | tr "[A-Z]" "[a-z]" - echo "$1" -} - -list_dirs() { - # - local IN="$@" - local iter - while [ "$IN" ] ;do - iter=${IN%%:*} - echo "${iter}" - [ "$IN" = "$iter" ] && IN='' || IN="${IN#*:}" - done -} - -check_in_path() { - local needle=$1 - local p - local patterns - shift - patterns="$@" - while [ "${patterns}" ]; do - p=${patterns%%:*} - [ "$patterns" = "$p" ] && patterns='' || patterns="${patterns#*:}" - - case "${needle}" in - ${p}) - echo "${needle}" - break - ;; - esac - done | grep . >/dev/null -} - -check_includedep() { - local path=$(normalize_path "$1") - local p - local result=0 - # exclude non-existing files - if [ ! -e "${path}" ]; then - return 0 - fi - - # skip paths that match one of the patterns in ${exclude_paths} - if check_in_path "${path}" "${exclude_paths}"; then - return 1 - fi - # only include paths that match one of the patterns in ${include_paths} - if check_in_path "${path}" "${include_paths}"; then - echo "${path}" - return 0 - fi - # skip the rest - return 1 -} - -usage() { - cat >/dev/stderr <] [-X ] [ ...] - recursively includes all dependencies of the given binaries - - -I : adds one include path entry - -X : adds one exclude path entry - -v: raise verbosity - -q: lower verbosity - -EOF - - case "$0" in - *win*) - cat >/dev/stderr </dev/stderr < ${outdir}" - cp "${dep}" "${outdir}" - chmod u+w "${outdir}/${depfile}" - - # make sure the dependency announces itself with the local path - install_name_tool -id "${loaderpath}" "${outdir}/${depfile}" - # recursively call ourselves, to resolve higher-order dependencies - INSTALLDEPS_INDENT="${INSTALLDEPS_INDENT} " $0 -r "${outdir}/${depfile}" - fi - done -} - -if [ "x${OTOOL}" = "x" ]; then - check_binaries otool - OTOOL="otool -L" -fi - -for f in "$@"; do - if [ -e "${f}" ]; then - error - install_deps "${f}" - fi -done - -# Code signing -# On Monterey, binaries are automatically codesigned. Modifying them with this script renders the signature -# invalid. When Pd loads an external with an invalid signature, it exits immediately. Thus, we need to make sure -# that we codesign them again _after_ the localdeps process - -# This needs to be the absolutely last step. We don't do it while we're still inside a recursion. -if ! $recursion; then - echo -n "Code signing in progress... " - outdir="$(dirname "$1")/${arch}" - codesign --remove-signature "${ARGS[@]}" ${outdir}/*.dylib - codesign -s - "${ARGS[@]}" ${outdir}/*.dylib - echo "Done" -fi diff --git a/resources/localdeps/localdeps.win.sh b/resources/localdeps/localdeps.win.sh deleted file mode 100755 index 2a56314e..00000000 --- a/resources/localdeps/localdeps.win.sh +++ /dev/null @@ -1,296 +0,0 @@ -#!/bin/sh - -## puts dependencies besides the binary -# LATER: put dependencies into a separate folder - -## usage: $0 [...] - - -########################################### -# WARNING -# -# this uses an ugly hack to allow side-by-side installation of 32bit and 64bit -# dependencies: -# embedded dependencies are renamed from "libfoo.dll" to "libfoo.w32" resp. -# "libfoo.w64", and the files are modified (using 'sed') to reflect this -# renaming. -# this is somewhat brittle and likely to break! - -#default exclude/include paths -exclude_paths="" -include_paths="*mingw*:*/msys/*" - -# UTILITIES -if [ -e "${0%/*}/localdeps.utilities.source" ]; then -. "${0%/*}/localdeps.utilities.source" -else - # the following section (from @BEGIN_UTILITIES@ to @END_UTILITIES@) - # was copied from 'localdeps.utilities.source'. - # changes you make to this section will be lost. -#@BEGIN_UTILITIES@ -verbose=${verbose:-0} - -error() { - echo "$@" 1>&2 -} - -substitute() { - # substitutes literal strings - # usage: echo foo | substitute foo bar g - sed "s/$(echo $1 | sed 's:[]\[^$.*/&]:\\&:g')/$(echo $2 | sed 's:[]\[^$.*/&]:\\&:g')/$3" -} - -check_binaries() { - local cmd - for cmd in "$@"; do - if ! which "${cmd}" > /dev/null; then - error "Could not find '${cmd}'. Is it installed?" - exit 127 - fi - done -} - - -normalize_path() { - # normalize a path specification, e.g. on Windows turn C:\Foo\Bar\ into /c/foo/bar/" - # on most system this doesn't do anything, but override it to your needs... - # e.g. on Windows use: ${CYGPATH} "$1" | tr "[A-Z]" "[a-z]" - echo "$1" -} - -list_dirs() { - # - local IN="$@" - local iter - while [ "$IN" ] ;do - iter=${IN%%:*} - echo "${iter}" - [ "$IN" = "$iter" ] && IN='' || IN="${IN#*:}" - done -} - -check_in_path() { - local needle=$1 - local p - local patterns - shift - patterns="$@" - while [ "${patterns}" ]; do - p=${patterns%%:*} - [ "$patterns" = "$p" ] && patterns='' || patterns="${patterns#*:}" - - case "${needle}" in - ${p}) - echo "${needle}" - break - ;; - esac - done | grep . >/dev/null -} - -check_includedep() { - local path=$(normalize_path "$1") - local p - local result=0 - # exclude non-existing files - if [ ! -e "${path}" ]; then - return 0 - fi - - # skip paths that match one of the patterns in ${exclude_paths} - if check_in_path "${path}" "${exclude_paths}"; then - return 1 - fi - # only include paths that match one of the patterns in ${include_paths} - if check_in_path "${path}" "${include_paths}"; then - echo "${path}" - return 0 - fi - # skip the rest - return 1 -} - -usage() { - cat >/dev/stderr <] [-X ] [ ...] - recursively includes all dependencies of the given binaries - - -I : adds one include path entry - -X : adds one exclude path entry - -v: raise verbosity - -q: lower verbosity - -EOF - - case "$0" in - *win*) - cat >/dev/stderr </dev/stderr </dev/null) -if [ -z "${CYGPATH}" ]; then - CYGPATH=echo -fi - -normalize_path() { - # convert to unix-format (C:\foo\bar\ --> /c/foo/bar/) - # and lower-case everything (because on microsoft-fs, paths are case-insensitive) - ${CYGPATH} "$1" | tr "[A-Z]" "[a-z]" -} - -list_deps() { - local path - local path0 - local inc - "${NTLDD}" "$1" \ - | grep ' => ' \ - | sed -e 's|\\|/|g' -e 's|.* => ||' -e 's| (0.*||' \ - | while read path; do - path0=$(echo $path |sed -e 's|/|\\|g') - inc=$(check_includedep "${path0}") - if [ "x${inc}" != "x" ]; then - echo "${path}" - fi - done -} - -file2arch() { - if file "$1" | grep -w "PE32+" >/dev/null; then - echo "w64" - return - fi - if file "$1" | grep -w "PE32" >/dev/null; then - echo "w32" - return - fi -} - -install_deps () { - local outdir="$2" - local idepfile - local odepfile - local archext - local dep - error "DEP: ${INSTALLDEPS_INDENT}'$1' '$2'" - - if [ "x${outdir}" = "x" ]; then - outdir=${1%/*} - fi - if [ ! -d "${outdir}" ]; then - outdir=. - fi - - list_deps "$1" | while read dep; do - idepfile=$(basename "${dep}") - odepfile=${idepfile} - archext=$(file2arch "${dep}") - if [ "x${archext}" != "x" ]; then - odepfile=$(echo ${idepfile} | sed -e "s|\.dll|.${archext}|") - fi - if [ "x${idepfile}" = "x${odepfile}" ]; then - archext="" - fi - if [ -e "${outdir}/${odepfile}" ]; then - error "DEP: ${INSTALLDEPS_INDENT} ${dep} SKIPPED" - else - error "DEP: ${INSTALLDEPS_INDENT} ${dep} -> ${outdir}/${odepfile}" - cp "${dep}" "${outdir}/${odepfile}" - chmod a-x "${outdir}/${odepfile}" - fi - - if [ "x${archext}" != "x" ]; then - sed -b \ - -e "s|${idepfile}|${odepfile}|g" \ - -i \ - "${outdir}/${odepfile}" "${dep}" "$1" - fi - #recursively resolve dependencies - INSTALLDEPS_INDENT="${INSTALLDEPS_INDENT} " install_deps "${outdir}/${odepfile}" - done -} - -if [ "x${NTLDD}" = "x" ]; then - check_binaries ntldd - NTLDD="ntldd" -fi - -for f in "$@"; do - if [ -e "${f}" ]; then - error - install_deps "${f}" - fi -done \ No newline at end of file diff --git a/resources/mac.pd b/resources/mac.pd deleted file mode 100644 index 1740f851..00000000 --- a/resources/mac.pd +++ /dev/null @@ -1,146 +0,0 @@ -#N canvas 494 62 1228 612 8; -#X msg 25 46 doc, f 6; -#N canvas 282 97 1541 723 embedded 0; -#X obj 9 192 py4pd; -#X msg 47 128 run; -#X msg 52 147 reload; -#X msg 9 216 set \$1; -#X msg 9 240 9; -#X text 6 298 With this you can send values for pd without return \, this seems to be very useful for work with list of lists. And other things; -#X obj 8 269 print pd.out; -#X msg 413 141 run; -#N canvas 0 50 450 250 (subpatch) 0; -#X array test 79 float 3; -#A 0 -0.55 0.24 0.78 0.79 -0.45 -0.82 0.81 -0.23 -0.89 -0.44 0.15 0.41 0.06 0.75 0.41 0.37 0.22 -0.5 -0.2 0.97 0.09 0 -0.46 -0.59 0.93 0.12 0.36 -0.43 -0.2 -0.5 0.03 -0.15 0.96 -0.4 0.95 -0.34 -0.16 0.93 -0.9 0.94 0.65 0.06 0.8 0.82 0 0.98 0.08 -0.04 0.13 -0.76 0.97 0.41 -0.17 -0.27 0.58 -0.37 -0.58 -0.33 0.89 0.03 -0.55 0.5 0.88 -0.25 -0.47 0.16 -0.17 0.39 -0.9 -0.36 -0.67 0.04 -0.22 0.1 0.06 -0.11 -0.55 0.46 0.77; -#X coords 0 1 79 -1 200 140 1; -#X restore 561 12 graph; -#X obj 413 92 tgl 20 0 empty empty empty 0 -10 0 12 #fcfcfc #000000 #000000 0 1; -#X obj 103 244 r py4pdreceiver; -#X obj 103 269 print py4pdreceiver; -#X msg 55 166 editor nvim; -#X msg 32 57 pd_print; -#X msg 9 8 pd_output; -#X msg 9 101 set py \$1; -#X obj 9 79 list; -#X msg 19 35 pd_tempfolder; -#X text 79 6 <= Select the Function to run.; -#X text 67 127 <= Run the Function.; -#X text 94 146 <= Reload the function if you edit it!; -#X text 119 165 <= Set your code editor.; -#X text 445 192 Write of read things in pd arrays using Python.; -#X text 438 93 <= Turn On; -#X obj 413 118 metro 500; -#X msg 425 168 set py pd_tabwrite; -#X obj 413 192 py4pd; -#X text 523 168 <= 1 Set the function; -#X connect 0 0 3 0; -#X connect 0 0 6 0; -#X connect 1 0 0 0; -#X connect 2 0 0 0; -#X connect 3 0 4 0; -#X connect 7 0 26 0; -#X connect 9 0 24 0; -#X connect 10 0 11 0; -#X connect 12 0 0 0; -#X connect 13 0 16 0; -#X connect 14 0 16 0; -#X connect 15 0 0 0; -#X connect 16 0 15 0; -#X connect 17 0 16 0; -#X connect 24 0 7 0; -#X connect 25 0 26 0; -#X restore 14 210 pd embedded module; -#N canvas 0 50 450 250 (subpatch) 0; -#X text 0 0 plugdatainfo ; -#X coords 0 1 100 -1 1 1 1; -#X restore 0 0 graph; -#X obj 13 160 print py4pd; -#N canvas 474 132 1263 711 code 0; -#X obj 7 305 s py4pd; -#X msg 74 154 reload, f 8; -#N canvas 0 50 450 250 (subpatch) 0; -#X text 0 0 plugdatainfo ; -#X coords 0 1 100 -1 1 1 1; -#X restore 0 0 graph; -#X msg 7 28 editor nvim; -#X msg 16 54 editor vscode; -#X text 94 28 Set Nvim as py4pd editor.; -#X text 120 54 Set vscode as py4pd editor.; -#X text 136 129 Open editor if it is in the PATH!; -#X text 142 153 Reload the function \, if you change it on script you need to reload!; -#X msg 29 79 editor emacs; -#X text 133 79 Set emacs as py4pd editor.; -#X msg 83 187 open myscript; -#X text 198 182 If you want to create a new script \, you can run open \, if the files already exits it will not replace it \, just open it.; -#X msg 46 104 editor gvim; -#X text 133 104 Set Gvim as py4pd editor.; -#X msg 57 130 editor sublime; -#X connect 1 0 0 0; -#X connect 3 0 0 0; -#X connect 4 0 0 0; -#X connect 9 0 0 0; -#X connect 11 0 0 0; -#X connect 13 0 0 0; -#X connect 15 0 0 0; -#X restore 15 187 pd code; -#X text 70 186 Code python with IDE.; -#X msg 13 17 set py pdsum; -#X msg 38 69 run 1 10, f 10; -#X obj 296 75 bng 20 250 50 0 mac empty empty 0 -10 0 12 #fcfcfc #000000 #000000; -#X obj 296 100 bng 20 250 50 0 windows empty empty 0 -10 0 12 #fcfcfc #000000 #000000; -#X obj 296 125 bng 20 250 50 0 linux empty empty 0 -10 0 12 #fcfcfc #000000 #000000; -#N canvas 0 0 1375 516 (subpatch) 0; -#X obj 47 189 pdcontrol; -#X obj 36 8 r mac; -#X obj 120 8 r windows; -#X obj 228 9 r linux; -#X msg 229 97 browse https://github.com/charlesneimog/py4pd/wiki/Install-Python; -#X msg 36 58 browse https://www.python.org/downloads/release/python-3114; -#X connect 1 0 5 0; -#X connect 2 0 5 0; -#X connect 3 0 4 0; -#X connect 4 0 0 0; -#X connect 5 0 0 0; -#X restore 295 25 pd; -#X text 320 74 Click on this bang for MacOS | Bottom of page; -#X text 320 101 Click on this bang for Windows | Bottom of page; -#X text 320 124 Click on this bang for Linux; -#X text 292 160 IMPORTANT: This is just simple example \, the exaustive Docs are avaible in www.charlesneimog.com/py4pd; -#X text 80 232 <= Share your Python code as pd objects.; -#X text 109 210 <= Interact with Python from PureData.; -#X text 42 116 <= Click to open the script.; -#X obj 13 138 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -8 0 8 #fcfcfc #000000 #000000 0 256; -#X text 93 69 3 Run the function.; -#X text 64 46 2 See the docs of the function.; -#X text 82 16 1 Load function.; -#X obj 53 91 r py4pd; -#X msg 295 47 YOU NEED TO INSTALL PYTHON3.11; -#N canvas 240 118 658 503 py4pd 0; -#X obj 10 4 py4pd -lib py4pd; -#X obj 10 29 py&& -a 3; -#X obj 10 49 py|| -a 3; -#X obj 9 212 py.list -a 3; -#X obj 10 192 py.nth; -#X obj 10 173 py.len; -#X obj 10 155 py.mklist; -#X obj 10 124 pd2py; -#X obj 10 103 py2pd; -#X obj 10 83 py.print; -#X obj 99 30 py+; -#X obj 99 48 py-; -#X obj 99 67 py*; -#X obj 99 87 py/; -#X obj 225 32 py.iterate; -#X obj 225 53 py.collect; -#X obj 295 31 f2mc; -#X obj 295 51 mc2f; -#X obj 295 71 mc2n; -#X restore 13 261 pd py4pd extra objects; -#X text 131 260 py4pd come with object to use pip inside pd \, and minor others things. Check the subpatch.; -#X obj 13 117 py4pd; -#X connect 0 0 27 0; -#X connect 6 0 27 0; -#X connect 7 0 27 0; -#X connect 19 0 3 0; -#X connect 23 0 27 0; -#X connect 27 0 19 0; diff --git a/resources/patch.pd b/resources/patch.pd deleted file mode 100644 index a8e981d2..00000000 --- a/resources/patch.pd +++ /dev/null @@ -1,11 +0,0 @@ -#N canvas 693 268 867 534 8; -#X obj 8 6 py4pd -lib py4pd; -#X obj 35 129 py.list -a 3; -#X msg 35 50 1 2 3 4 5; -#X msg 63 79 1 2 3 4 5; -#X msg 92 100 1 2 3 4 5; -#X obj 35 150 py.print; -#X connect 1 0 5 0; -#X connect 2 0 1 0; -#X connect 3 0 1 1; -#X connect 4 0 1 2; diff --git a/resources/py.py b/resources/py.py index adacd60f..1d935837 100644 --- a/resources/py.py +++ b/resources/py.py @@ -6,9 +6,8 @@ import numpy as np numpyIsInstalled = True except Exception as e: - pd.pip_install("numpy") - numpyIsInstalled = False - pd.error("You must restart Pure Data to use numpy.") + import py4pd + py4pd.pipinstall(["global", "numpy"]) sys.exit() # ================================================ diff --git a/resources/py4pd-mod/help/py.collect-help.pd b/resources/py4pd-mod/help/py.collect-help.pd index b40c5ca4..9f2a904d 100644 --- a/resources/py4pd-mod/help/py.collect-help.pd +++ b/resources/py4pd-mod/help/py.collect-help.pd @@ -2,16 +2,15 @@ #X obj 27 6 py4pd -lib py4pd; #X obj 53 60 py.iterate, f 15; #X msg 53 38 1 2 3 4 5 6 7; -#X obj 53 119 py.sum; #X msg 80 97 4; #X obj 53 156 py.collect; -#X obj 53 177 print; -#X msg 185 57 reload; -#X msg 157 109 reload; -#X connect 1 0 3 0; +#X msg 63 79 reload; +#X obj 53 119 py+; +#X obj 53 177 py.print; +#X connect 1 0 6 0; +#X connect 1 1 4 0; #X connect 2 0 1 0; -#X connect 3 0 5 0; -#X connect 4 0 3 1; +#X connect 3 0 6 1; +#X connect 4 0 7 0; #X connect 5 0 6 0; -#X connect 7 0 1 0; -#X connect 8 0 3 0; +#X connect 6 0 4 0; diff --git a/resources/py4pd-mod/help/py.logic-help.pd b/resources/py4pd-mod/help/py.logic-help.pd new file mode 100644 index 00000000..ba72a65b --- /dev/null +++ b/resources/py4pd-mod/help/py.logic-help.pd @@ -0,0 +1,40 @@ +#N canvas 974 59 799 799 8; +#X obj 9 6 py4pd -lib py4pd; +#X obj 122 303 py.and -a 4; +#X obj 187 303 py.or -a 4; +#X obj 10 91 py>, f 7; +#X msg 253 331 4; +#X obj 253 358 py== 4 4 4 4 -a 4; +#X msg 42 71 4; +#X obj 10 53 t f b; +#X obj 10 34 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -8 0 8 #fcfcfc #000000 #000000 0 256; +#X obj 10 112 s p; +#X obj 5 341 r p; +#X obj 5 362 py.print; +#X obj 253 379 s p; +#X msg 115 72 4; +#X obj 83 54 t f b; +#X obj 83 35 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -8 0 8 #fcfcfc #000000 #000000 0 256; +#X obj 83 113 s p; +#X obj 83 92 py<, f 7; +#X obj 295 140 py.equal -a 5; +#X obj 30 233 s p; +#X obj 30 212 pyisin None 3; +#X msg 30 160 3; +#X msg 92 190 1 2 3 4 5; +#X connect 3 0 9 0; +#X connect 4 0 5 0; +#X connect 5 0 12 0; +#X connect 6 0 3 1; +#X connect 7 0 3 0; +#X connect 7 1 6 0; +#X connect 8 0 7 0; +#X connect 10 0 11 0; +#X connect 13 0 17 1; +#X connect 14 0 17 0; +#X connect 14 1 13 0; +#X connect 15 0 14 0; +#X connect 17 0 16 0; +#X connect 20 0 19 0; +#X connect 21 0 20 0; +#X connect 22 0 20 1; diff --git a/resources/py4pd-mod/help/py.loops-help.pd b/resources/py4pd-mod/help/py.loops-help.pd new file mode 100644 index 00000000..ce3c3fe6 --- /dev/null +++ b/resources/py4pd-mod/help/py.loops-help.pd @@ -0,0 +1,60 @@ +#N canvas 669 -1 1129 799 8; +#X obj 9 6 py4pd -lib py4pd; +#X obj 7 75 py.iterate; +#X obj 7 120 py.collect; +#N canvas 130 168 450 300 process 0; +#X obj 7 5 inlet; +#X obj 7 131 outlet; +#X obj 7 26 py* None 50; +#X obj 7 88 py+ 30; +#X obj 7 47 t a b, f 16; +#X obj 84 67 expr random(3290 \, 8923); +#X connect 0 0 2 0; +#X connect 2 0 4 0; +#X connect 3 0 1 0; +#X connect 4 0 3 0; +#X connect 4 1 5 0; +#X connect 5 0 3 1; +#X restore 7 96 pd process; +#X obj 7 141 s p; +#X obj 3 359 r p; +#X obj 3 380 py.print; +#X msg 7 45 2 37 84 95 6; +#X obj 225 146 py.or -a 2; +#X obj 334 120 py.equal None 0 -a 2; +#X obj 225 120 py.equal None 1 -a 2; +#X obj 196 168 py.gate -outn 1, f 16; +#X obj 104 36 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -8 0 8 #fcfcfc #000000 #000000 0 256; +#X obj 196 189 py- None 1; +#X obj 167 94 t a a a a, f 18; +#X obj 195 14 r call_again; +#X obj 167 301 s call_again; +#X obj 104 55 t f f; +#X obj 167 225 py*; +#X obj 167 246 py2pd; +#X obj 273 303 f; +#X obj 273 324 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -8 0 8 #fcfcfc #000000 #000000 0 256; +#X obj 321 213; +#X connect 1 0 3 0; +#X connect 1 1 2 0; +#X connect 2 0 4 0; +#X connect 3 0 2 0; +#X connect 5 0 6 0; +#X connect 7 0 1 0; +#X connect 8 0 11 1; +#X connect 9 0 8 1; +#X connect 10 0 8 0; +#X connect 11 0 13 0; +#X connect 11 1 20 0; +#X connect 12 0 17 0; +#X connect 13 0 18 1; +#X connect 14 0 18 0; +#X connect 14 1 11 0; +#X connect 14 2 10 0; +#X connect 14 3 9 0; +#X connect 15 0 14 0; +#X connect 17 1 14 0; +#X connect 18 0 19 0; +#X connect 19 0 20 1; +#X connect 19 0 16 0; +#X connect 20 0 21 0; diff --git a/resources/py4pd-mod/py4pd.py b/resources/py4pd-mod/py4pd.py index f47b1966..c6d31728 100755 --- a/resources/py4pd-mod/py4pd.py +++ b/resources/py4pd-mod/py4pd.py @@ -5,6 +5,8 @@ except Exception as e: pd.error("Error to add py.pip: " + str(e)) +from src.pip import * + try: from src.convertion import * from src.info import * @@ -23,6 +25,20 @@ pd.error("Error loading py4pd objects: " + str(e)) pd.add_object(pipinstall, "py.pip") +from src.convertion import * +from src.info import * +from src.list import * +from src.loop import * +from src.math import * +from src.musicconvertions import * +from src.openmusic import * +from src.operators import * +from src.pip import * +from src.show import * +from src.test import * +from src.tree import * +from src.utils import * + def mysumarg(a=3, b=2, c=5, d=4): pd.print(f"mysumarg: {a} + {b} + {c} + {d}") @@ -39,15 +55,16 @@ def py4pdLoadObjects(): pd.add_object(getObjectArgs, "py.getargs") # Logic Functions - pd.add_object(pyand, "py.and", pyout=True) - pd.add_object(pyand, "py&&", pyout=True) - pd.add_object(pyor, "py||", pyout=True) - pd.add_object(pyequal, "py==", pyout=True) - pd.add_object(pygreater, "py>", pyout=True) - pd.add_object(pylower, "py<", pyout=True) - pd.add_object(py4pdif, "py.if", pyout=True) - pd.add_object(ommod, "py//", pyout=True) - pd.add_object(pyisin, "pyisin") + pd.add_object(pyand, "py.and", pyout=True, helppatch="py.logic-help.pd") + pd.add_object(pyand, "py&&", pyout=True, helppatch="py.logic-help.pd") + pd.add_object(pyor, "py.or", pyout=True, helppatch="py.logic-help.pd") + pd.add_object(pyor, "py||", pyout=True, helppatch="py.logic-help.pd") + pd.add_object(pyequal, "py.equal", pyout=True, helppatch="py.logic-help.pd") + pd.add_object(pyequal, "py==", pyout=True, helppatch="py.logic-help.pd") + pd.add_object(pygreater, "py>", pyout=True, helppatch="py.logic-help.pd") + pd.add_object(pylower, "py<", pyout=True, helppatch="py.logic-help.pd") + pd.add_object(py4pdif, "py.if", pyout=True, helppatch="py.logic-help.pd") + pd.add_object(pyisin, "pyisin", pyout=True, helppatch="py.logic-help.pd") # info pd.add_object(pdprint, "py.print", no_outlet=True) @@ -95,6 +112,8 @@ def py4pdLoadObjects(): pd.add_object(omtimes, "py*") pd.add_object(omdiv, "py/") pd.add_object(omabs, "py.abs") + pd.add_object(ommod, "py//", pyout=True) + pd.add_object(py4pdListComprehension, "py.listcomp", pyout=True) # Rhythm Tree diff --git a/resources/py4pd-mod/src/pip.py b/resources/py4pd-mod/src/pip.py index edf306a8..8f2b0148 100755 --- a/resources/py4pd-mod/src/pip.py +++ b/resources/py4pd-mod/src/pip.py @@ -12,48 +12,6 @@ folder = "" - def _pipinstall(self): - self.drawWarning() - version = sys.version_info - major = version.major # Assuming version is defined - minor = version.minor # Assuming version is defined - folder = self.folder - package = self.package - value = subprocess.run( - [ - f"/usr/local/bin/python{major}.{minor}", - "-m", - "pip", - "install", - "--target", - f"{folder}/py-modules", - package, - "--upgrade", - ], - check=True, - ) - if value.returncode != 0: - pd.logpost(3, "pip return value" + str(value)) - pd.error( - "You need to restart PureData, to check if the installation process worked" - ) - else: - pd.print(f"{package} Installed!") - pd.error("You need to restart PureData") - self.window.protocol("WM_DELETE_WINDOW", self.close_window) - self.window.after(200, self.close_window) - self.window.mainloop() - - return True - - def close_window(self): - self.window.quit() - self.window.destroy() - - def run(self): - self.window.update() - - def pipinstall(mypackage): """Install a Python package from Pd""" global folder From bc8af718aa5c21f411966373dbff67e8e590fac8 Mon Sep 17 00:00:00 2001 From: "Charles K. Neimog" Date: Thu, 30 Nov 2023 10:34:22 -0300 Subject: [PATCH 05/21] split pd class to create objects --- Makefile | 2 +- src/ext-class.c | 392 +++++++++++++++++++++++++++++++++++++++++ src/ext-libraries.c | 412 +++----------------------------------------- src/py4pd.c | 112 +++++------- src/py4pd.h | 2 + src/utils.c | 17 +- 6 files changed, 464 insertions(+), 473 deletions(-) create mode 100644 src/ext-class.c diff --git a/Makefile b/Makefile index d4333cbb..e5f42c33 100755 --- a/Makefile +++ b/Makefile @@ -38,7 +38,7 @@ endif # =================================== Sources =================================== -py4pd.class.sources = src/py4pd.c src/utils.c src/module.c src/pic.c src/ext-libraries.c src/player.c +py4pd.class.sources = src/py4pd.c src/utils.c src/module.c src/pic.c src/ext-libraries.c src/ext-class.c src/player.c # =================================== Data ====================================== datafiles = \ diff --git a/src/ext-class.c b/src/ext-class.c new file mode 100644 index 00000000..2487c016 --- /dev/null +++ b/src/ext-class.c @@ -0,0 +1,392 @@ +#include "py4pd.h" + +#define NPY_NO_DEPRECATED_API NPY_1_25_API_VERSION +#include + +// ============================================================ +// ===================== PDOBJECT CLASS ======================= +// ============================================================ +typedef struct { + PyObject_HEAD const char *objName; + int objType; + int objIsPlayable; + PyObject *objFigSize; + int objOutPyObjs; + int noOutlets; + int auxOutlets; + int requireNofOutlets; + int ignoreNoneOutputs; + const char *objImage; +} py4pdNewObject; + +// ============================================================ +static int Py4pdNewObj_init(py4pdNewObject *self, PyObject *args, + PyObject *kwds) { + (void)self; + static char *keywords[] = {"param_str", NULL}; + char *param_str; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", keywords, ¶m_str)) { + return -1; + } + PyObject *objectName = PyUnicode_FromString(param_str); + if (!objectName) { + PyErr_SetString(PyExc_TypeError, "Object name not valid"); + return -1; + } + self->objName = PyUnicode_AsUTF8(objectName); + self->objType = PY4PD_NORMALOBJ; + return 0; +} + +// ============================================================ +static PyObject *Py4pdNewObj_GetType(py4pdNewObject *self) { + return PyLong_FromLong(self->objType); +} + +static int Py4pdNewObj_SetType(py4pdNewObject *self, PyObject *value) { + int typeValue = PyLong_AsLong(value); + if (typeValue < 0 || typeValue > 3) { + PyErr_SetString(PyExc_TypeError, + "Object type not supported, check the value"); + return -1; + } + self->objType = typeValue; + return 0; +} +// ============================================================ +static PyObject *Py4pdNewObj_GetName(py4pdNewObject *self) { + return PyUnicode_FromString(self->objName); +} + +// ++++++++++ +static int Py4pdNewObj_SetName(py4pdNewObject *self, PyObject *value) { + PyObject *objectName = PyUnicode_FromString(PyUnicode_AsUTF8(value)); + if (!objectName) { + PyErr_SetString(PyExc_TypeError, "Object name not valid"); + return -1; + } + self->objName = PyUnicode_AsUTF8(objectName); + Py_DECREF(objectName); + return 0; +} + +// ============================================================ +static PyObject *Py4pdNewObj_GetPlayable(py4pdNewObject *self) { + return PyLong_FromLong(self->objIsPlayable); +} + +// ++++++++++ +static int Py4pdNewObj_SetPlayable(py4pdNewObject *self, PyObject *value) { + int playableValue = PyObject_IsTrue(value); + if (playableValue < 0 || playableValue > 1) { + PyErr_SetString(PyExc_TypeError, + "Playable value not supported, check the value"); + return -1; + } + self->objIsPlayable = playableValue; + return 0; +} + +// ============================================================ +static PyObject *Py4pdNewObj_GetImage(py4pdNewObject *self) { + return PyUnicode_FromString(self->objImage); +} + +// ++++++++++ +static int Py4pdNewObj_SetImage(py4pdNewObject *self, PyObject *value) { + const char *imageValue = PyUnicode_AsUTF8(value); + if (!imageValue) { + PyErr_SetString(PyExc_TypeError, "Image path not valid"); + return -1; + } + self->objImage = imageValue; + return 0; +} + +// ============================================================ +static PyObject *Py4pdNewObj_GetOutputPyObjs(py4pdNewObject *self) { + return PyLong_FromLong(self->objOutPyObjs); +} + +// ++++++++++ +static int Py4pdNewObj_SetOutputPyObjs(py4pdNewObject *self, PyObject *value) { + int outputPyObjsValue = PyObject_IsTrue(value); + if (!outputPyObjsValue) { + PyErr_SetString(PyExc_TypeError, "Image path not valid"); + return -1; + } + self->objOutPyObjs = outputPyObjsValue; + return 0; +} + +// ============================================================ +static PyObject *Py4pdNewObj_GetFigSize(py4pdNewObject *self) { + return PyLong_FromLong(self->objIsPlayable); +} + +// ++++++++++ +static int Py4pdNewObj_SetFigSize(py4pdNewObject *self, PyObject *value) { + // see if object value is a tuple with two integers + if (!PyTuple_Check(value)) { + PyErr_SetString(PyExc_TypeError, + "Figure size must be a tuple with two integers"); + return -1; // Error handling + } + if (PyTuple_Size(value) != 2) { + PyErr_SetString(PyExc_TypeError, + "Figure size must be a tuple with two integers"); + return -1; // Error handling + } + PyObject *width = PyTuple_GetItem(value, 0); + PyObject *height = PyTuple_GetItem(value, 1); + if (!PyLong_Check(width) || !PyLong_Check(height)) { + PyErr_SetString(PyExc_TypeError, + "Figure size must be a tuple with two integers"); + return -1; // Error handling + } + self->objFigSize = value; + + return 0; // Success +} + +// ============================================================ +static PyObject *Py4pdNewObj_GetNoOutlets(py4pdNewObject *self) { + if (self->noOutlets) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +// ++++++++++ +static int Py4pdNewObj_SetNoOutlets( + py4pdNewObject *self, + PyObject *value) { // see if object value is a tuple with two integers + int outpuNoOutlets = PyObject_IsTrue(value); + self->noOutlets = outpuNoOutlets; + return 0; // Success +} + +// ============================================================ +static PyObject *Py4pdNewObj_GetRequireNofOuts(py4pdNewObject *self) { + if (self->requireNofOutlets) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +// ++++++++++ +static int Py4pdNewObj_SetRequireNofOuts( + py4pdNewObject *self, + PyObject *value) { // see if object value is a tuple with two integers + int outpuNoOutlets = PyObject_IsTrue(value); + self->requireNofOutlets = outpuNoOutlets; + return 0; // Success +} + +// ============================================================ +static PyObject *Py4pdNewObj_GetAuxOutNumbers(py4pdNewObject *self) { + return PyLong_FromLong(self->auxOutlets); +} + +// ++++++++++ +static int Py4pdNewObj_SetAuxOutNumbers( + py4pdNewObject *self, + PyObject *value) { // see if object value is a tuple with two integers + if (!PyLong_Check(value)) { + PyErr_SetString(PyExc_TypeError, "Auxiliary outlets must be an integer"); + return -1; // Error handling + } + self->auxOutlets = PyLong_AsLong(value); + return 0; // Success +} + +// ============================================================ +static PyObject *Py4pdNewObj_GetIgnoreNone(py4pdNewObject *self) { + if (self->ignoreNoneOutputs) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +// ++++++++++ +static int Py4pdNewObj_SetIgnoreNone( + py4pdNewObject *self, + PyObject *value) { // see if object value is a tuple with two integers + int ignoreNone = PyObject_IsTrue(value); + self->ignoreNoneOutputs = ignoreNone; + return 0; // Success +} + +// ========================== GET/SET ========================== +static PyGetSetDef Py4pdNewObj_GetSet[] = { + {"type", (getter)Py4pdNewObj_GetType, (setter)Py4pdNewObj_SetType, + "pd.NORMALOBJ, Type attribute", NULL}, + {"name", (getter)Py4pdNewObj_GetName, (setter)Py4pdNewObj_SetName, + "Name attribute", NULL}, + {"playable", (getter)Py4pdNewObj_GetPlayable, + (setter)Py4pdNewObj_SetPlayable, "Playable attribute", NULL}, + {"figsize", (getter)Py4pdNewObj_GetFigSize, (setter)Py4pdNewObj_SetFigSize, + "Figure size attribute", NULL}, + {"image", (getter)Py4pdNewObj_GetImage, (setter)Py4pdNewObj_SetImage, + "Image patch", NULL}, + {"pyout", (getter)Py4pdNewObj_GetOutputPyObjs, + (setter)Py4pdNewObj_SetOutputPyObjs, "Output or not PyObjs", NULL}, + {"no_outlet", (getter)Py4pdNewObj_GetNoOutlets, + (setter)Py4pdNewObj_SetNoOutlets, "Number of outlets", NULL}, + {"require_n_of_outlets", (getter)Py4pdNewObj_GetRequireNofOuts, + (setter)Py4pdNewObj_SetRequireNofOuts, "Require number of outlets", NULL}, + {"n_extra_outlets", (getter)Py4pdNewObj_GetAuxOutNumbers, + (setter)Py4pdNewObj_SetAuxOutNumbers, "Number of auxiliary outlets", NULL}, + {"ignore_none", (getter)Py4pdNewObj_GetIgnoreNone, + (setter)Py4pdNewObj_SetIgnoreNone, "Ignore None outputs", NULL}, + {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ +}; + +// ============================================================= +static PyObject *Py4pdNewObj_Method_AddObj(py4pdNewObject *self, + PyObject *args) { + (void)args; + + PyObject *objConfigDict = PyDict_New(); + const char *objectName; + objectName = self->objName; + + PyObject *Py_ObjType = PyLong_FromLong(self->objType); + PyDict_SetItemString(objConfigDict, "py4pdOBJType", Py_ObjType); + Py_DECREF(Py_ObjType); + t_class *objClass; + + if (self->objType == PY4PD_NORMALOBJ) { + objClass = class_new(gensym(objectName), (t_newmethod)Py4pdLib_NewObj, + (t_method)Py4pdLib_FreeObj, sizeof(t_py), + CLASS_DEFAULT, A_GIMME, 0); + } else if (self->objType == PY4PD_VISOBJ) { + objClass = class_new(gensym(objectName), (t_newmethod)Py4pdLib_NewObj, + (t_method)Py4pdLib_FreeObj, sizeof(t_py), + CLASS_DEFAULT, A_GIMME, 0); + } else if (self->objType == PY4PD_AUDIOINOBJ) { + objClass = class_new(gensym(objectName), (t_newmethod)Py4pdLib_NewObj, + (t_method)Py4pdLib_FreeObj, sizeof(t_py), + CLASS_MULTICHANNEL, A_GIMME, 0); + } else if (self->objType == PY4PD_AUDIOOUTOBJ) { + objClass = class_new(gensym(objectName), (t_newmethod)Py4pdLib_NewObj, + (t_method)Py4pdLib_FreeObj, sizeof(t_py), + CLASS_MULTICHANNEL, A_GIMME, 0); + } else if (self->objType == PY4PD_AUDIOOBJ) { + objClass = class_new(gensym(objectName), (t_newmethod)Py4pdLib_NewObj, + (t_method)Py4pdLib_FreeObj, sizeof(t_py), + CLASS_MULTICHANNEL, A_GIMME, 0); + } else { + PyErr_SetString(PyExc_TypeError, + "Object type not supported, check the spelling"); + return NULL; + } + + PyObject *Py_ClassLocal = PyLong_FromVoidPtr(objClass); + PyDict_SetItemString(objConfigDict, "py4pdOBJ_CLASS", Py_ClassLocal); + Py_DECREF(Py_ClassLocal); + + if (self->objFigSize != NULL) { + PyObject *Py_Width = PyTuple_GetItem(self->objFigSize, 0); + PyDict_SetItemString(objConfigDict, "py4pdOBJwidth", Py_Width); + Py_DECREF(Py_Width); + PyObject *Py_Height = PyTuple_GetItem(self->objFigSize, 1); + PyDict_SetItemString(objConfigDict, "py4pdOBJheight", Py_Height); + Py_DECREF(Py_Height); + } else { + PyObject *Py_Width = PyLong_FromLong(250); + PyDict_SetItemString(objConfigDict, "py4pdOBJwidth", Py_Width); + Py_DECREF(Py_Width); + PyObject *Py_Height = PyLong_FromLong(250); + PyDict_SetItemString(objConfigDict, "py4pdOBJheight", Py_Height); + Py_DECREF(Py_Height); + } + + PyObject *Py_Playable = PyLong_FromLong(self->objIsPlayable); + PyDict_SetItemString(objConfigDict, "py4pdOBJPlayable", Py_Playable); + Py_DECREF(Py_Playable); + + if (self->objImage != NULL) { + PyObject *Py_GifImage = PyUnicode_FromString(self->objImage); + PyDict_SetItemString(objConfigDict, "py4pdOBJGif", Py_GifImage); + Py_DECREF(Py_GifImage); + } + + PyObject *Py_ObjOuts = PyLong_FromLong(self->objOutPyObjs); + PyDict_SetItemString(objConfigDict, "py4pdOBJpyout", Py_ObjOuts); + Py_DECREF(Py_ObjOuts); + + PyObject *Py_NoOutlet = PyLong_FromLong(self->noOutlets); + PyDict_SetItemString(objConfigDict, "py4pdOBJnooutlet", Py_NoOutlet); + Py_DECREF(Py_NoOutlet); + + PyObject *Py_RequireOutletN = PyLong_FromLong(self->requireNofOutlets); + PyDict_SetItemString(objConfigDict, "py4pdOBJrequireoutletn", + Py_RequireOutletN); + Py_DECREF(Py_RequireOutletN); + + PyObject *Py_auxOutlets = PyLong_FromLong(self->auxOutlets); + PyDict_SetItemString(objConfigDict, "py4pdAuxOutlets", Py_auxOutlets); + Py_DECREF(Py_auxOutlets); + + PyObject *Py_ObjName = PyUnicode_FromString(objectName); + PyDict_SetItemString(objConfigDict, "py4pdOBJname", Py_ObjName); + Py_DECREF(Py_ObjName); + + PyObject *Py_IgnoreNoneReturn = PyLong_FromLong(self->ignoreNoneOutputs); + PyDict_SetItemString(objConfigDict, "py4pdOBJIgnoreNone", + Py_IgnoreNoneReturn); + Py_DECREF(Py_IgnoreNoneReturn); + + PyObject *objectDict = PyDict_New(); + PyDict_SetItemString(objectDict, objectName, objConfigDict); + PyObject *py4pd_capsule = PyCapsule_New(objectDict, objectName, NULL); + char py4pd_objectName[MAXPDSTRING]; + sprintf(py4pd_objectName, "py4pd_ObjectDict_%s", objectName); + + PyObject *pdModule = PyImport_ImportModule("pd"); + PyModule_AddObject(pdModule, py4pd_objectName, py4pd_capsule); + Py_DECREF(pdModule); + + Py_RETURN_TRUE; +} + +// ========================== METHODS ========================== +static PyMethodDef Py4pdNewObj_methods[] = { + {"add_object", (PyCFunction)Py4pdNewObj_Method_AddObj, METH_NOARGS, + "Get the type attribute"}, + + // {"addmethod_float" + // {"addmethod_symbol" + // {"addmethod_list" + // {"addmethod_anything" + // {"addmethod_bang" + // {"addmethod" + + // EXTERN void class_addmethod(t_class *c, t_method fn, t_symbol *sel, + // t_atomtype arg1, ...); + // EXTERN void class_addbang(t_class *c, t_method fn); + // EXTERN void class_addpointer(t_class *c, t_method fn); + // EXTERN void class_doaddfloat(t_class *c, t_method fn); + // EXTERN void class_addsymbol(t_class *c, t_method fn); + // EXTERN void class_addlist(t_class *c, t_method fn); + // EXTERN void class_addanything(t_class *c, t_method fn); + + {NULL, NULL, 0, NULL} // Sentinel +}; + +// ========================== CLASS ============================ +PyTypeObject Py4pdNewObj_Type = { + PyVarObject_HEAD_INIT(NULL, 0).tp_name = "pd.NewObject", + .tp_doc = "It creates new PureData objects", + .tp_basicsize = sizeof(py4pdNewObject), + .tp_itemsize = 0, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_new = PyType_GenericNew, + .tp_init = (initproc)Py4pdNewObj_init, + .tp_methods = Py4pdNewObj_methods, + .tp_getset = Py4pdNewObj_GetSet, // Add the GetSet descriptors here +}; diff --git a/src/ext-libraries.c b/src/ext-libraries.c index 00618861..f4333d7d 100644 --- a/src/ext-libraries.c +++ b/src/ext-libraries.c @@ -655,7 +655,7 @@ static void Py4pdLib_Dsp(t_py *x, t_signal **sp) { // ================ // == CREATE OBJ == // ================ -static void *Py4pdLib_NewObj(t_symbol *s, int argc, t_atom *argv) { +void *Py4pdLib_NewObj(t_symbol *s, int argc, t_atom *argv) { const char *objectName = s->s_name; char py4pd_objectName[MAXPDSTRING]; snprintf(py4pd_objectName, sizeof(py4pd_objectName), "py4pd_ObjectDict_%s", @@ -832,7 +832,6 @@ PyObject *Py4pdLib_AddObj(PyObject *self, PyObject *args, PyObject *keywords) { } const char *helpFolder = "/help/"; - size_t totalLength = strlen(py4pd->libraryFolder->s_name) + strlen(helpFolder) + 1; char *helpFolderCHAR = (char *)malloc(totalLength * sizeof(char)); @@ -888,7 +887,26 @@ PyObject *Py4pdLib_AddObj(PyObject *self, PyObject *args, PyObject *keywords) { if (PyDict_Contains(keywords, PyUnicode_FromString("helppatch"))) { PyObject *helpname = PyDict_GetItemString(keywords, "helppatch"); helpPatch = PyUnicode_AsUTF8(helpname); - personalisedHelp = 1; + const char *suffix = "-help.pd"; + int suffixLen = strlen(suffix); + int helpPatchLen = strlen(helpPatch); + + if (helpPatchLen >= suffixLen) { + if (strcmp(helpPatch + (helpPatchLen - suffixLen), suffix) == 0) { + personalisedHelp = 1; + char helpPatchName[MAXPDSTRING]; + + // Use snprintf to avoid buffer overflow and ensure null-termination + snprintf(helpPatchName, sizeof(helpPatchName), "%.*s", + helpPatchLen - suffixLen, helpPatch); + + helpPatch = helpPatchName; + } else { + pd_error(NULL, "Help patch must end with '-help.pd'"); + } + } else { + pd_error(NULL, "Help patch must end with '-help.pd'"); + } } if (PyDict_Contains(keywords, PyUnicode_FromString("ignore_none_return"))) { PyObject *noneReturn = @@ -1104,391 +1122,3 @@ PyObject *Py4pdLib_AddObj(PyObject *self, PyObject *args, PyObject *keywords) { class_set_extern_dir(&s_); Py_RETURN_TRUE; } - -// ============================================================ -// ===================== PDOBJECT CLASS ======================= -// ============================================================ -typedef struct { - PyObject_HEAD const char *objName; - int objType; - int objIsPlayable; - PyObject *objFigSize; - int objOutPyObjs; - int noOutlets; - int auxOutlets; - int requireNofOutlets; - int ignoreNoneOutputs; - const char *objImage; -} py4pdNewObject; - -// ============================================================ -static int Py4pdNewObj_init(py4pdNewObject *self, PyObject *args, - PyObject *kwds) { - (void)self; - static char *keywords[] = {"param_str", NULL}; - char *param_str; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", keywords, ¶m_str)) { - return -1; - } - PyObject *objectName = PyUnicode_FromString(param_str); - if (!objectName) { - PyErr_SetString(PyExc_TypeError, "Object name not valid"); - return -1; - } - self->objName = PyUnicode_AsUTF8(objectName); - self->objType = PY4PD_NORMALOBJ; - return 0; -} - -// ============================================================ -static PyObject *Py4pdNewObj_GetType(py4pdNewObject *self) { - return PyLong_FromLong(self->objType); -} - -static int Py4pdNewObj_SetType(py4pdNewObject *self, PyObject *value) { - int typeValue = PyLong_AsLong(value); - if (typeValue < 0 || typeValue > 3) { - PyErr_SetString(PyExc_TypeError, - "Object type not supported, check the value"); - return -1; - } - self->objType = typeValue; - return 0; -} -// ============================================================ -static PyObject *Py4pdNewObj_GetName(py4pdNewObject *self) { - return PyUnicode_FromString(self->objName); -} - -// ++++++++++ -static int Py4pdNewObj_SetName(py4pdNewObject *self, PyObject *value) { - PyObject *objectName = PyUnicode_FromString(PyUnicode_AsUTF8(value)); - if (!objectName) { - PyErr_SetString(PyExc_TypeError, "Object name not valid"); - return -1; - } - self->objName = PyUnicode_AsUTF8(objectName); - Py_DECREF(objectName); - return 0; -} - -// ============================================================ -static PyObject *Py4pdNewObj_GetPlayable(py4pdNewObject *self) { - return PyLong_FromLong(self->objIsPlayable); -} - -// ++++++++++ -static int Py4pdNewObj_SetPlayable(py4pdNewObject *self, PyObject *value) { - int playableValue = PyObject_IsTrue(value); - if (playableValue < 0 || playableValue > 1) { - PyErr_SetString(PyExc_TypeError, - "Playable value not supported, check the value"); - return -1; - } - self->objIsPlayable = playableValue; - return 0; -} - -// ============================================================ -static PyObject *Py4pdNewObj_GetImage(py4pdNewObject *self) { - return PyUnicode_FromString(self->objImage); -} - -// ++++++++++ -static int Py4pdNewObj_SetImage(py4pdNewObject *self, PyObject *value) { - const char *imageValue = PyUnicode_AsUTF8(value); - if (!imageValue) { - PyErr_SetString(PyExc_TypeError, "Image path not valid"); - return -1; - } - self->objImage = imageValue; - return 0; -} - -// ============================================================ -static PyObject *Py4pdNewObj_GetOutputPyObjs(py4pdNewObject *self) { - return PyLong_FromLong(self->objOutPyObjs); -} - -// ++++++++++ -static int Py4pdNewObj_SetOutputPyObjs(py4pdNewObject *self, PyObject *value) { - int outputPyObjsValue = PyObject_IsTrue(value); - if (!outputPyObjsValue) { - PyErr_SetString(PyExc_TypeError, "Image path not valid"); - return -1; - } - self->objOutPyObjs = outputPyObjsValue; - return 0; -} - -// ============================================================ -static PyObject *Py4pdNewObj_GetFigSize(py4pdNewObject *self) { - return PyLong_FromLong(self->objIsPlayable); -} - -// ++++++++++ -static int Py4pdNewObj_SetFigSize(py4pdNewObject *self, PyObject *value) { - // see if object value is a tuple with two integers - if (!PyTuple_Check(value)) { - PyErr_SetString(PyExc_TypeError, - "Figure size must be a tuple with two integers"); - return -1; // Error handling - } - if (PyTuple_Size(value) != 2) { - PyErr_SetString(PyExc_TypeError, - "Figure size must be a tuple with two integers"); - return -1; // Error handling - } - PyObject *width = PyTuple_GetItem(value, 0); - PyObject *height = PyTuple_GetItem(value, 1); - if (!PyLong_Check(width) || !PyLong_Check(height)) { - PyErr_SetString(PyExc_TypeError, - "Figure size must be a tuple with two integers"); - return -1; // Error handling - } - self->objFigSize = value; - - return 0; // Success -} - -// ============================================================ -static PyObject *Py4pdNewObj_GetNoOutlets(py4pdNewObject *self) { - if (self->noOutlets) { - Py_RETURN_TRUE; - } else { - Py_RETURN_FALSE; - } -} - -// ++++++++++ -static int Py4pdNewObj_SetNoOutlets( - py4pdNewObject *self, - PyObject *value) { // see if object value is a tuple with two integers - int outpuNoOutlets = PyObject_IsTrue(value); - self->noOutlets = outpuNoOutlets; - return 0; // Success -} - -// ============================================================ -static PyObject *Py4pdNewObj_GetRequireNofOuts(py4pdNewObject *self) { - if (self->requireNofOutlets) { - Py_RETURN_TRUE; - } else { - Py_RETURN_FALSE; - } -} - -// ++++++++++ -static int Py4pdNewObj_SetRequireNofOuts( - py4pdNewObject *self, - PyObject *value) { // see if object value is a tuple with two integers - int outpuNoOutlets = PyObject_IsTrue(value); - self->requireNofOutlets = outpuNoOutlets; - return 0; // Success -} - -// ============================================================ -static PyObject *Py4pdNewObj_GetAuxOutNumbers(py4pdNewObject *self) { - return PyLong_FromLong(self->auxOutlets); -} - -// ++++++++++ -static int Py4pdNewObj_SetAuxOutNumbers( - py4pdNewObject *self, - PyObject *value) { // see if object value is a tuple with two integers - if (!PyLong_Check(value)) { - PyErr_SetString(PyExc_TypeError, "Auxiliary outlets must be an integer"); - return -1; // Error handling - } - self->auxOutlets = PyLong_AsLong(value); - return 0; // Success -} - -// ============================================================ -static PyObject *Py4pdNewObj_GetIgnoreNone(py4pdNewObject *self) { - if (self->ignoreNoneOutputs) { - Py_RETURN_TRUE; - } else { - Py_RETURN_FALSE; - } -} - -// ++++++++++ -static int Py4pdNewObj_SetIgnoreNone( - py4pdNewObject *self, - PyObject *value) { // see if object value is a tuple with two integers - int ignoreNone = PyObject_IsTrue(value); - self->ignoreNoneOutputs = ignoreNone; - return 0; // Success -} - -// ========================== GET/SET ========================== -static PyGetSetDef Py4pdNewObj_GetSet[] = { - {"type", (getter)Py4pdNewObj_GetType, (setter)Py4pdNewObj_SetType, - "pd.NORMALOBJ, Type attribute", NULL}, - {"name", (getter)Py4pdNewObj_GetName, (setter)Py4pdNewObj_SetName, - "Name attribute", NULL}, - {"playable", (getter)Py4pdNewObj_GetPlayable, - (setter)Py4pdNewObj_SetPlayable, "Playable attribute", NULL}, - {"figsize", (getter)Py4pdNewObj_GetFigSize, (setter)Py4pdNewObj_SetFigSize, - "Figure size attribute", NULL}, - {"image", (getter)Py4pdNewObj_GetImage, (setter)Py4pdNewObj_SetImage, - "Image patch", NULL}, - {"pyout", (getter)Py4pdNewObj_GetOutputPyObjs, - (setter)Py4pdNewObj_SetOutputPyObjs, "Output or not PyObjs", NULL}, - {"no_outlet", (getter)Py4pdNewObj_GetNoOutlets, - (setter)Py4pdNewObj_SetNoOutlets, "Number of outlets", NULL}, - {"require_n_of_outlets", (getter)Py4pdNewObj_GetRequireNofOuts, - (setter)Py4pdNewObj_SetRequireNofOuts, "Require number of outlets", NULL}, - {"n_extra_outlets", (getter)Py4pdNewObj_GetAuxOutNumbers, - (setter)Py4pdNewObj_SetAuxOutNumbers, "Number of auxiliary outlets", NULL}, - {"ignore_none", (getter)Py4pdNewObj_GetIgnoreNone, - (setter)Py4pdNewObj_SetIgnoreNone, "Ignore None outputs", NULL}, - {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ -}; - -// ============================================================= -static PyObject *Py4pdNewObj_Method_AddObj(py4pdNewObject *self, - PyObject *args) { - (void)args; - - PyObject *objConfigDict = PyDict_New(); - const char *objectName; - objectName = self->objName; - - PyObject *Py_ObjType = PyLong_FromLong(self->objType); - PyDict_SetItemString(objConfigDict, "py4pdOBJType", Py_ObjType); - Py_DECREF(Py_ObjType); - t_class *objClass; - - if (self->objType == PY4PD_NORMALOBJ) { - objClass = class_new(gensym(objectName), (t_newmethod)Py4pdLib_NewObj, - (t_method)Py4pdLib_FreeObj, sizeof(t_py), - CLASS_DEFAULT, A_GIMME, 0); - } else if (self->objType == PY4PD_VISOBJ) { - objClass = class_new(gensym(objectName), (t_newmethod)Py4pdLib_NewObj, - (t_method)Py4pdLib_FreeObj, sizeof(t_py), - CLASS_DEFAULT, A_GIMME, 0); - } else if (self->objType == PY4PD_AUDIOINOBJ) { - objClass = class_new(gensym(objectName), (t_newmethod)Py4pdLib_NewObj, - (t_method)Py4pdLib_FreeObj, sizeof(t_py), - CLASS_MULTICHANNEL, A_GIMME, 0); - } else if (self->objType == PY4PD_AUDIOOUTOBJ) { - objClass = class_new(gensym(objectName), (t_newmethod)Py4pdLib_NewObj, - (t_method)Py4pdLib_FreeObj, sizeof(t_py), - CLASS_MULTICHANNEL, A_GIMME, 0); - } else if (self->objType == PY4PD_AUDIOOBJ) { - objClass = class_new(gensym(objectName), (t_newmethod)Py4pdLib_NewObj, - (t_method)Py4pdLib_FreeObj, sizeof(t_py), - CLASS_MULTICHANNEL, A_GIMME, 0); - } else { - PyErr_SetString(PyExc_TypeError, - "Object type not supported, check the spelling"); - return NULL; - } - - PyObject *Py_ClassLocal = PyLong_FromVoidPtr(objClass); - PyDict_SetItemString(objConfigDict, "py4pdOBJ_CLASS", Py_ClassLocal); - Py_DECREF(Py_ClassLocal); - - if (self->objFigSize != NULL) { - PyObject *Py_Width = PyTuple_GetItem(self->objFigSize, 0); - PyDict_SetItemString(objConfigDict, "py4pdOBJwidth", Py_Width); - Py_DECREF(Py_Width); - PyObject *Py_Height = PyTuple_GetItem(self->objFigSize, 1); - PyDict_SetItemString(objConfigDict, "py4pdOBJheight", Py_Height); - Py_DECREF(Py_Height); - } else { - PyObject *Py_Width = PyLong_FromLong(250); - PyDict_SetItemString(objConfigDict, "py4pdOBJwidth", Py_Width); - Py_DECREF(Py_Width); - PyObject *Py_Height = PyLong_FromLong(250); - PyDict_SetItemString(objConfigDict, "py4pdOBJheight", Py_Height); - Py_DECREF(Py_Height); - } - - PyObject *Py_Playable = PyLong_FromLong(self->objIsPlayable); - PyDict_SetItemString(objConfigDict, "py4pdOBJPlayable", Py_Playable); - Py_DECREF(Py_Playable); - - if (self->objImage != NULL) { - PyObject *Py_GifImage = PyUnicode_FromString(self->objImage); - PyDict_SetItemString(objConfigDict, "py4pdOBJGif", Py_GifImage); - Py_DECREF(Py_GifImage); - } - - PyObject *Py_ObjOuts = PyLong_FromLong(self->objOutPyObjs); - PyDict_SetItemString(objConfigDict, "py4pdOBJpyout", Py_ObjOuts); - Py_DECREF(Py_ObjOuts); - - PyObject *Py_NoOutlet = PyLong_FromLong(self->noOutlets); - PyDict_SetItemString(objConfigDict, "py4pdOBJnooutlet", Py_NoOutlet); - Py_DECREF(Py_NoOutlet); - - PyObject *Py_RequireOutletN = PyLong_FromLong(self->requireNofOutlets); - PyDict_SetItemString(objConfigDict, "py4pdOBJrequireoutletn", - Py_RequireOutletN); - Py_DECREF(Py_RequireOutletN); - - PyObject *Py_auxOutlets = PyLong_FromLong(self->auxOutlets); - PyDict_SetItemString(objConfigDict, "py4pdAuxOutlets", Py_auxOutlets); - Py_DECREF(Py_auxOutlets); - - PyObject *Py_ObjName = PyUnicode_FromString(objectName); - PyDict_SetItemString(objConfigDict, "py4pdOBJname", Py_ObjName); - Py_DECREF(Py_ObjName); - - PyObject *Py_IgnoreNoneReturn = PyLong_FromLong(self->ignoreNoneOutputs); - PyDict_SetItemString(objConfigDict, "py4pdOBJIgnoreNone", - Py_IgnoreNoneReturn); - Py_DECREF(Py_IgnoreNoneReturn); - - PyObject *objectDict = PyDict_New(); - PyDict_SetItemString(objectDict, objectName, objConfigDict); - PyObject *py4pd_capsule = PyCapsule_New(objectDict, objectName, NULL); - char py4pd_objectName[MAXPDSTRING]; - sprintf(py4pd_objectName, "py4pd_ObjectDict_%s", objectName); - - PyObject *pdModule = PyImport_ImportModule("pd"); - PyModule_AddObject(pdModule, py4pd_objectName, py4pd_capsule); - Py_DECREF(pdModule); - - Py_RETURN_TRUE; -} - -// ========================== METHODS ========================== -static PyMethodDef Py4pdNewObj_methods[] = { - {"add_object", (PyCFunction)Py4pdNewObj_Method_AddObj, METH_NOARGS, - "Get the type attribute"}, - - // {"addmethod_float" - // {"addmethod_symbol" - // {"addmethod_list" - // {"addmethod_anything" - // {"addmethod_bang" - // {"addmethod" - - // EXTERN void class_addmethod(t_class *c, t_method fn, t_symbol *sel, - // t_atomtype arg1, ...); - // EXTERN void class_addbang(t_class *c, t_method fn); - // EXTERN void class_addpointer(t_class *c, t_method fn); - // EXTERN void class_doaddfloat(t_class *c, t_method fn); - // EXTERN void class_addsymbol(t_class *c, t_method fn); - // EXTERN void class_addlist(t_class *c, t_method fn); - // EXTERN void class_addanything(t_class *c, t_method fn); - - {NULL, NULL, 0, NULL} // Sentinel -}; - -// ========================== CLASS ============================ -PyTypeObject Py4pdNewObj_Type = { - PyVarObject_HEAD_INIT(NULL, 0).tp_name = "pd.NewObject", - .tp_doc = "It creates new PureData objects", - .tp_basicsize = sizeof(py4pdNewObject), - .tp_itemsize = 0, - .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_new = PyType_GenericNew, - .tp_init = (initproc)Py4pdNewObj_init, - .tp_methods = Py4pdNewObj_methods, - .tp_getset = Py4pdNewObj_GetSet, // Add the GetSet descriptors here -}; diff --git a/src/py4pd.c b/src/py4pd.c index 55556c90..889a3b0a 100755 --- a/src/py4pd.c +++ b/src/py4pd.c @@ -654,7 +654,7 @@ void Py4pd_SetEditor(t_py *x, t_symbol *s, int argc, t_atom *argv) { void Py4pd_ReloadPy4pdFunction(t_py *x) { PyObject *pName, *pFunc, *pModule, *pReload; if (x->funcCalled == 0) { // if the set method was not called, then we - // can not run the function :) + // can not run the function :) pd_error(x, "To reload the script you need to set the function first!"); return; } @@ -905,11 +905,11 @@ static void Py4pd_RunFunction(t_py *x, t_symbol *s, int argc, t_atom *argv) { x->pFuncName->s_name, (int)x->pArgsCount, argCount); return; } + free(lists); } else { ArgsTuple = PyTuple_New(0); } Py4pdUtils_RunPy(x, ArgsTuple, NULL); - return; } @@ -937,14 +937,10 @@ static void Py4pd_ExecuteFunction(t_py *x, t_symbol *s, int argc, pd_error(x, "[py4pd] You need to call a function before run!"); return; } - Py4pd_RunFunction(x, s, argc, argv); // Implement here the functions - return; } - - // ============================================ /** * @brief This will enable or disable the numpy array support and start numpy @@ -984,9 +980,6 @@ void Py4pd_SetPythonPointersUsage(t_py *x, t_floatarg f) { void *Py4pd_Py4pdNew(t_symbol *s, int argc, t_atom *argv) { int i; t_py *x; - int visMODE = 0; - int audioOUT = 0; - int audioIN = 0; int libraryMODE = 0; int normalMODE = 1; t_symbol *scriptName = NULL; @@ -1002,38 +995,7 @@ void *Py4pd_Py4pdNew(t_symbol *s, int argc, t_atom *argv) { for (i = 0; i < argc; i++) { if (argv[i].a_type == A_SYMBOL) { t_symbol *py4pdArgs = atom_getsymbolarg(i, argc, argv); - if (py4pdArgs == gensym("-picture") || py4pdArgs == gensym("-score") || - py4pdArgs == gensym("-pic") || py4pdArgs == gensym("-canvas")) { - visMODE = 1; - if (argv[i + 1].a_type == A_FLOAT && argv[i + 2].a_type == A_FLOAT) { - pd_error(NULL, "[py4pd] -picture, -score, -pic and -canvas was " - "removed in version 0.8.0"); - pd_error(NULL, "[py4pd] Please transfor your code in one Pd Object, " - "it is simple"); - pd_error(NULL, "[py4pd] Check: " - "https://py4pd.readthedocs.io/en/latest/python-users/" - "#pdaddobject"); - return NULL; - } - } else if (py4pdArgs == gensym("-audio") || - py4pdArgs == gensym("-audioout")) { - pd_error(NULL, "[py4pd] -audio option was removed in version 0.8.0"); - pd_error(NULL, "[py4pd] Please transfor your code in one Pd " - "Object, it is simple"); - pd_error(NULL, "[py4pd] Check: " - "https://py4pd.readthedocs.io/en/latest/" - "python-users/#pdaddobject"); - return NULL; - } else if (py4pdArgs == gensym("-audioin")) { - pd_error(NULL, "[py4pd] -audioin was removed in version 0.8.0"); - pd_error(NULL, "[py4pd] Please transfor your code in one Pd " - "Object, it is simple"); - pd_error(NULL, "[py4pd] Check: " - "https://py4pd.readthedocs.io/en/latest/" - "python-users/#pdaddobject"); - return NULL; - } else if (py4pdArgs == gensym("-library") || - py4pdArgs == gensym("-lib")) { + if (py4pdArgs == gensym("-library") || py4pdArgs == gensym("-lib")) { libraryMODE = 1; normalMODE = 0; scriptName = atom_getsymbolarg(i + 1, argc, argv); @@ -1044,16 +1006,43 @@ void *Py4pd_Py4pdNew(t_symbol *s, int argc, t_atom *argv) { // ================= // INIT PY4PD OBJECT // ================= - if (normalMODE == 1 && visMODE == 0 && audioOUT == 0 && audioIN == 0) { + if (normalMODE == 1) { x = (t_py *)pd_new(py4pd_class); // create a new py4pd object - } else if (libraryMODE == 1 && visMODE == 0 && audioOUT == 0 && - audioIN == 0) { // library + x->canvas = canvas_getcurrent(); + t_canvas *c = x->canvas; + t_symbol *patch_dir = canvas_getdir(c); + x->audioInput = 0; + x->audioOutput = 0; + x->visMode = 0; + x->editorName = NULL; + x->pyObject = 0; + x->vectorSize = 0; + Py4pdUtils_ParseArguments(x, c, argc, argv); // parse arguments + x->pdPatchPath = patch_dir; // set name of the home path + x->pkgPath = patch_dir; // set name of the packages path + x->pArgsCount = 0; + + Py4pdUtils_SetObjConfig( + x); // set the config file (in py4pd.cfg, make this be + + if (object_count == 0) { + Py4pdUtils_AddPathsToPythonPath(x); + } + + if (argc > 1) { // check if there are two arguments + Py4pd_SetFunction(x, s, argc, argv); + Py4pd_ImportNumpyForPy4pd(); + } + object_count++; + return (x); + } else if (libraryMODE == 1) { // library x = (t_py *)pd_new(py4pd_classLibrary); x->canvas = canvas_getcurrent(); t_canvas *c = x->canvas; t_symbol *patch_dir = canvas_getdir(c); x->pdPatchPath = patch_dir; x->pkgPath = patch_dir; + x->visMode = 0; Py4pdUtils_SetObjConfig(x); if (object_count == 0) { Py4pdUtils_AddPathsToPythonPath(x); @@ -1075,32 +1064,6 @@ void *Py4pd_Py4pdNew(t_symbol *s, int argc, t_atom *argv) { "the same time."); return NULL; } - x->canvas = canvas_getcurrent(); - t_canvas *c = x->canvas; - t_symbol *patch_dir = canvas_getdir(c); - x->audioInput = 0; - x->audioOutput = 0; - x->visMode = 0; - x->editorName = NULL; - x->pyObject = 0; - x->vectorSize = 0; - Py4pdUtils_ParseArguments(x, c, argc, argv); // parse arguments - x->pdPatchPath = patch_dir; // set name of the home path - x->pkgPath = patch_dir; // set name of the packages path - - Py4pdUtils_SetObjConfig(x); // set the config file (in py4pd.cfg, make this be - - if (object_count == 0) { - Py4pdUtils_AddPathsToPythonPath(x); - } - - if (argc > 1) { // check if there are two arguments - Py4pd_SetFunction(x, s, argc, argv); - Py4pd_ImportNumpyForPy4pd(); - } - - object_count++; - return (x); } // ==================================================== @@ -1168,8 +1131,13 @@ void py4pd_setup(void) { object_count = 0; post(""); post("[py4pd] by Charles K. Neimog"); - post("[py4pd] Version %d.%d.%d", PY4PD_MAJOR_VERSION, PY4PD_MINOR_VERSION, - PY4PD_MICRO_VERSION); +#ifdef PD_FLAVOR + post("[py4pd] Version %d.%d.%d | %s", PY4PD_MAJOR_VERSION, + PY4PD_MINOR_VERSION, PY4PD_MICRO_VERSION, PD_FLAVOR); +#else + post("[py4pd] Version %d.%d.%d | %s", PY4PD_MAJOR_VERSION, + PY4PD_MINOR_VERSION, PY4PD_MICRO_VERSION, "Pd Vanilla"); +#endif post("[py4pd] Python version %d.%d.%d", PY_MAJOR_VERSION, PY_MINOR_VERSION, PY_MICRO_VERSION); post(""); diff --git a/src/py4pd.h b/src/py4pd.h index dee22f59..5f8513d8 100755 --- a/src/py4pd.h +++ b/src/py4pd.h @@ -295,6 +295,8 @@ KeyValuePair* Py4pdLib_PlayerGetValue(Dictionary* dictionary, int onset); void Py4pdLib_Play(t_py *x, t_symbol *s, int argc, t_atom *argv); void Py4pdLib_Stop(t_py *x); void Py4pdLib_Clear(t_py *x); +void *Py4pdLib_NewObj(t_symbol *s, int argc, t_atom *argv); + // ============= PIC ============= extern t_class *py4pd_class, *pyNewObject_VIS; diff --git a/src/utils.c b/src/utils.c index 4c37aa75..fc7bb73b 100644 --- a/src/utils.c +++ b/src/utils.c @@ -162,8 +162,9 @@ void Py4pdUtils_ParseArguments(t_py *x, t_canvas *c, int argc, t_atom *argv) { } } if (x->audioOutput == 0) { - x->mainOut = outlet_new(&x->obj, - 0); // cria um outlet caso o objeto nao contenha audio + x->mainOut = + outlet_new(&x->obj, + 0); // cria um outlet caso o objeto nao contenha audio } } @@ -192,7 +193,7 @@ void *Py4pdLib_FreeObj(t_py *x) { if (x->pdcollect != NULL) Py4pdMod_FreePdcollectHash(x->pdcollect); - if (x->pArgsCount > 1) { + if (x->pArgsCount > 1 && x->pyObjArgs != NULL) { for (int i = 1; i < x->pArgsCount; i++) { if (!x->pyObjArgs[i]->pdout) Py_DECREF(x->pyObjArgs[i]->pValue); @@ -200,11 +201,9 @@ void *Py4pdLib_FreeObj(t_py *x) { } free(x->pyObjArgs); } - if (x->pdObjArgs != NULL) { free(x->pdObjArgs); } - return NULL; } @@ -1181,8 +1180,8 @@ inline void *Py4pdUtils_ConvertToPd(t_py *x, t_py4pd_pValue *pValueStruct, // not possible to represent None in Pd, so we just skip it } else { pd_error(x, - "[py4pd] py4pd just convert int, float and string! " - "Received: %s", + "[py4pd] py4pd just convert int, float, string, and lists! " + "Received: list of %ss", Py_TYPE(pValue_i)->tp_name); return 0; } @@ -1299,7 +1298,7 @@ PyObject *Py4pdUtils_ConvertToPy(PyObject *listsArrays[], int argc, PyList_Append(listsArrays[listCount], PyUnicode_FromString(str)); PyTuple_SetItem(ArgsTuple, argCount, listsArrays[listCount]); } - // free(str); + free(str); listStarted = 0; listCount++; argCount++; @@ -1322,7 +1321,7 @@ PyObject *Py4pdUtils_ConvertToPy(PyObject *listsArrays[], int argc, } else { PyList_Append(listsArrays[listCount], PyUnicode_FromString(str)); } - // free(str); + free(str); } else { _PyTuple_Resize(&ArgsTuple, argCount + 1); PyTuple_SetItem(ArgsTuple, argCount, From 76cc00c4aa516d4427b5512bbbf74bc1b6e48dbb Mon Sep 17 00:00:00 2001 From: "Charles K. Neimog" Date: Thu, 30 Nov 2023 10:34:40 -0300 Subject: [PATCH 06/21] test updates --- test/01-Install-modules.pd | 20 ++--- test/09-List.pd | 29 +++++++ test/10-deleteObj.pd | 9 ++ test/mem/problem3.pd | 2 +- test/newclass.py | 16 ++++ test/py.py | 40 +++++++-- test/py4pd-help.pd | 171 +++++++++++++++++++++++++++++++++++++ 7 files changed, 267 insertions(+), 20 deletions(-) create mode 100755 test/09-List.pd create mode 100755 test/10-deleteObj.pd create mode 100644 test/newclass.py create mode 100644 test/py4pd-help.pd diff --git a/test/01-Install-modules.pd b/test/01-Install-modules.pd index ed8b025d..623273fb 100755 --- a/test/01-Install-modules.pd +++ b/test/01-Install-modules.pd @@ -11,24 +11,24 @@ #X obj 157 229 t b b; #X obj 135 10 r start-test; #X obj 238 31 bng 15 250 50 0 empty empty empty 0 -8 0 8 #fcfcfc #000000 #000000; -#X obj 157 146 py.pip; -#X msg 157 98 local numpy; #X obj 218 12 bng 15 250 50 0 empty empty empty 0 -8 0 8 #fcfcfc #000000 #000000; #X obj 87 261 delay 10 1 msec; #X obj 37 73 delay 25000 1 msec; -#X connect 1 0 13 0; -#X connect 2 0 16 0; +#X obj 157 146 py4pd; +#X msg 157 98 pipinstall local numpy; +#X connect 1 0 16 0; +#X connect 2 0 14 0; #X connect 2 1 1 0; #X connect 3 0 6 0; #X connect 3 1 5 0; #X connect 5 0 4 0; #X connect 7 0 8 0; -#X connect 9 0 15 0; +#X connect 9 0 13 0; #X connect 9 1 7 0; #X connect 10 0 2 0; #X connect 11 0 1 0; -#X connect 12 0 9 0; -#X connect 13 0 12 0; -#X connect 14 0 2 0; -#X connect 15 0 6 0; -#X connect 16 0 3 0; +#X connect 12 0 2 0; +#X connect 13 0 6 0; +#X connect 14 0 3 0; +#X connect 15 0 9 0; +#X connect 16 0 15 0; diff --git a/test/09-List.pd b/test/09-List.pd new file mode 100755 index 00000000..c90bfea1 --- /dev/null +++ b/test/09-List.pd @@ -0,0 +1,29 @@ +#N canvas 686 36 1001 847 8; +#X obj 5 5 py4pd -lib py4pd; +#X obj 135 10 r start-test; +#X obj 218 12 bng 15 250 50 0 empty empty empty 0 -8 0 8 #fcfcfc #000000 #000000; +#X obj 27 258 sel 0 1; +#X obj 27 298 print ERROR; +#X obj 43 279 print PASS; +#X msg 5 343 \; pd quit; +#X obj 5 238 t b f; +#X obj 135 95 py4pd py sumlist; +#X obj 135 34 t b; +#X msg 87 62 reload; +#X msg 135 63 run [1 2 3 4 5 6] 20; +#X obj 135 116 py.nth None 5; +#X obj 135 137 py2pd; +#X obj 135 158 expr if($f1 == 120 \, 1 \, 0); +#X connect 1 0 9 0; +#X connect 2 0 9 0; +#X connect 3 0 4 0; +#X connect 3 1 5 0; +#X connect 7 0 6 0; +#X connect 7 1 3 0; +#X connect 8 0 12 0; +#X connect 9 0 11 0; +#X connect 10 0 8 0; +#X connect 11 0 8 0; +#X connect 12 0 13 0; +#X connect 13 0 14 0; +#X connect 14 0 7 0; diff --git a/test/10-deleteObj.pd b/test/10-deleteObj.pd new file mode 100755 index 00000000..aec16b07 --- /dev/null +++ b/test/10-deleteObj.pd @@ -0,0 +1,9 @@ +#N canvas 667 314 523 414 8; +#X obj 3 5 namecanvas py4pd-test; +#X obj 135 10 r start-test; +#X obj 218 12 bng 15 250 50 0 empty empty empty 0 -8 0 8 #fcfcfc #000000 #000000; +#X obj 135 95 py4pd py sumlist; +#X msg 135 63 run [1 2 3 4 5 6] 20; +#N canvas 30 68 450 300 (subpatch) 0; +#X restore 56 83 pd; +#X connect 4 0 3 0; diff --git a/test/mem/problem3.pd b/test/mem/problem3.pd index 6b44b60c..c33e0bae 100644 --- a/test/mem/problem3.pd +++ b/test/mem/problem3.pd @@ -1,4 +1,4 @@ -#N canvas 131 169 645 770 8; +#N canvas 131 169 393 577 8; #X obj 6 11 bng 15 250 50 0 empty empty empty 0 -8 0 8 #fcfcfc #000000 #000000; #X obj 6 70 bng 15 250 50 0 empty empty empty 0 -8 0 8 #fcfcfc #000000 #000000; #X msg 6 90 [1 2 3 4 5]; diff --git a/test/newclass.py b/test/newclass.py new file mode 100644 index 00000000..9dc972c8 --- /dev/null +++ b/test/newclass.py @@ -0,0 +1,16 @@ +import pd + + +pd.print(pd.add_object()) + + + +def sumlist(x, valuetimes): + mylist = [] + for i in x: + print(i) + mylist.append(i * valuetimes) + return mylist + + + diff --git a/test/py.py b/test/py.py index d314fddc..be9d5de0 100644 --- a/test/py.py +++ b/test/py.py @@ -1,12 +1,15 @@ -import pd import sys from random import randint +import pd + try: import numpy as np + numpyIsInstalled = True except Exception as e: - pd.pip_install("local", "numpy") + import py4pd + py4pd.pipinstall(["local", "numpy"]) numpyIsInstalled = False pd.error("You must restart Pure Data to use numpy.") sys.exit() @@ -16,6 +19,14 @@ # ================================================ +def sumlist(x, valuetimes): + mylist = [] + for i in x: + print(i) + mylist.append(i * valuetimes) + return mylist + + def pdsum(x, y): "It sums two numbers." x = int(x) @@ -48,6 +59,7 @@ def fibonacci(n): # ================== AUDIO ====================== # ================================================ + def pd_tempfolder(): "It returns the temp folder path." tempfolder = pd.tempfolder() @@ -59,6 +71,7 @@ def pd_output(): for x in range(10): pd.out(x) + def example_pdout(): for x in range(2): pd.out(x, symbol="myloop") @@ -66,11 +79,13 @@ def example_pdout(): pd.out(x, symbol="myloop2") return None + def pdout(x): pd.out(x, symbol="myloop") pd.out(x, symbol="myloop2") return None + def pd_print(): "It sends a message to the py4pd message box." pd.print("Hello from python!") @@ -84,7 +99,7 @@ def pd_error(): def pd_send(): - "It sends a message to the py4pdreceiver receive." + "It sends a message to the py4pdreceiver receive." pd.send("py4pdreceiver", "hello from python!") pd.send("py4pdreceiver", 1) pd.send("py4pdreceiver", [1, 2, 3, 4, 5]) @@ -107,6 +122,7 @@ def printall(x, y): "It sends a message to the py4pd message box." pd.print(str(x + y)) + # ================================================ # ================ Audio ========================= # ================================================ @@ -119,18 +135,22 @@ def generate_sine_wave(frequency, amplitude, phase, num_samples, sampling_rate): def mksenoide(freqs, amps, phases, vectorsize, samplerate): - n = len(freqs) + n = len(freqs) nframes = vectorsize - out = np.zeros((n, nframes), dtype=np.float64) # Modify the shape of the output array + out = np.zeros( + (n, nframes), dtype=np.float64 + ) # Modify the shape of the output array new_phases = np.zeros(n, dtype=np.float64) # Array to store the new phases for i in range(n): - out[i], new_phases[i] = generate_sine_wave(freqs[i], amps[i], phases[i], nframes, samplerate) + out[i], new_phases[i] = generate_sine_wave( + freqs[i], amps[i], phases[i], nframes, samplerate + ) if new_phases[i] > 2 * np.pi: new_phases[i] -= 2 * np.pi return out, new_phases -def sinusoids(freqs, amps): +def sinusoids(freqs, amps): vectorsize = pd.get_vec_size() samplerate = pd.get_sample_rate() if freqs is None or amps is None: @@ -145,8 +165,10 @@ def sinusoids(freqs, amps): return out +# ================================================ +# ================ SETUP ========================= +# ================================================ def py4pdLoadObjects(): - pd.add_object(sinusoids, 'sinusoids~', objtype=pd.AUDIOOUT) - + pd.add_object(sinusoids, "sinusoids~", objtype=pd.AUDIOOUT) diff --git a/test/py4pd-help.pd b/test/py4pd-help.pd new file mode 100644 index 00000000..dd963834 --- /dev/null +++ b/test/py4pd-help.pd @@ -0,0 +1,171 @@ +#N canvas 545 31 1202 670 8; +#X msg 25 46 doc, f 6; +#N canvas 282 97 1541 723 embedded 0; +#X obj 9 192 py4pd; +#X msg 47 128 run; +#X msg 52 147 reload; +#X msg 9 216 set \$1; +#X msg 9 240 9; +#X text 6 298 With this you can send values for pd without return \, this seems to be very useful for work with list of lists. And other things; +#X obj 8 269 print pd.out; +#X msg 413 141 run; +#N canvas 0 50 450 250 (subpatch) 0; +#X array test 79 float 3; +#A 0 -0.55 0.24 0.78 0.79 -0.45 -0.82 0.81 -0.23 -0.89 -0.44 0.15 0.41 0.06 0.75 0.41 0.37 0.22 -0.5 -0.2 0.97 0.09 0 -0.46 -0.59 0.93 0.12 0.36 -0.43 -0.2 -0.5 0.03 -0.15 0.96 -0.4 0.95 -0.34 -0.16 0.93 -0.9 0.94 0.65 0.06 0.8 0.82 0 0.98 0.08 -0.04 0.13 -0.76 0.97 0.41 -0.17 -0.27 0.58 -0.37 -0.58 -0.33 0.89 0.03 -0.55 0.5 0.88 -0.25 -0.47 0.16 -0.17 0.39 -0.9 -0.36 -0.67 0.04 -0.22 0.1 0.06 -0.11 -0.55 0.46 0.77; +#X coords 0 1 79 -1 200 140 1; +#X restore 561 12 graph; +#X obj 413 92 tgl 20 0 empty empty empty 0 -10 0 12 #fcfcfc #000000 #000000 0 1; +#X obj 103 244 r py4pdreceiver; +#X obj 103 269 print py4pdreceiver; +#X msg 55 166 editor nvim; +#X msg 32 57 pd_print; +#X msg 9 8 pd_output; +#X msg 9 101 set py \$1; +#X obj 9 79 list; +#X msg 19 35 pd_tempfolder; +#X text 79 6 <= Select the Function to run.; +#X text 67 127 <= Run the Function.; +#X text 94 146 <= Reload the function if you edit it!; +#X text 119 165 <= Set your code editor.; +#X text 445 192 Write of read things in pd arrays using Python.; +#X text 438 93 <= Turn On; +#X obj 413 118 metro 500; +#X msg 425 168 set py pd_tabwrite; +#X obj 413 192 py4pd; +#X text 523 168 <= 1 Set the function; +#X connect 0 0 3 0; +#X connect 0 0 6 0; +#X connect 1 0 0 0; +#X connect 2 0 0 0; +#X connect 3 0 4 0; +#X connect 7 0 26 0; +#X connect 9 0 24 0; +#X connect 10 0 11 0; +#X connect 12 0 0 0; +#X connect 13 0 16 0; +#X connect 14 0 16 0; +#X connect 15 0 0 0; +#X connect 16 0 15 0; +#X connect 17 0 16 0; +#X connect 24 0 7 0; +#X connect 25 0 26 0; +#X restore 14 210 pd embedded module; +#N canvas 0 50 450 250 (subpatch) 0; +#X text 0 0 plugdatainfo ; +#X coords 0 1 100 -1 1 1 1; +#X restore 0 0 graph; +#X obj 13 160 print py4pd; +#N canvas 474 132 1263 711 code 0; +#X obj 7 305 s py4pd; +#X msg 74 154 reload, f 8; +#N canvas 0 50 450 250 (subpatch) 0; +#X text 0 0 plugdatainfo ; +#X coords 0 1 100 -1 1 1 1; +#X restore 0 0 graph; +#X msg 7 28 editor nvim; +#X msg 16 54 editor vscode; +#X text 94 28 Set Nvim as py4pd editor.; +#X text 120 54 Set vscode as py4pd editor.; +#X text 136 129 Open editor if it is in the PATH!; +#X text 142 153 Reload the function \, if you change it on script you need to reload!; +#X msg 29 79 editor emacs; +#X text 133 79 Set emacs as py4pd editor.; +#X msg 83 187 open myscript; +#X text 198 182 If you want to create a new script \, you can run open \, if the files already exits it will not replace it \, just open it.; +#X msg 46 104 editor gvim; +#X text 133 104 Set Gvim as py4pd editor.; +#X msg 57 130 editor sublime; +#X connect 1 0 0 0; +#X connect 3 0 0 0; +#X connect 4 0 0 0; +#X connect 9 0 0 0; +#X connect 11 0 0 0; +#X connect 13 0 0 0; +#X connect 15 0 0 0; +#X restore 15 187 pd code; +#X text 70 186 Code python with IDE.; +#X msg 13 17 set py pdsum; +#X msg 38 69 run 1 10, f 10; +#X obj 296 75 bng 20 250 50 0 mac empty empty 0 -10 0 12 #fcfcfc #000000 #000000; +#X obj 296 100 bng 20 250 50 0 windows empty empty 0 -10 0 12 #fcfcfc #000000 #000000; +#X obj 296 125 bng 20 250 50 0 linux empty empty 0 -10 0 12 #fcfcfc #000000 #000000; +#N canvas 0 0 1375 516 (subpatch) 0; +#X obj 47 189 pdcontrol; +#X obj 36 8 r mac; +#X obj 120 8 r windows; +#X obj 228 9 r linux; +#X msg 229 97 browse https://github.com/charlesneimog/py4pd/wiki/Install-Python; +#X msg 36 58 browse https://www.python.org/downloads/release/python-3114; +#X connect 1 0 5 0; +#X connect 2 0 5 0; +#X connect 3 0 4 0; +#X connect 4 0 0 0; +#X connect 5 0 0 0; +#X restore 295 25 pd; +#X text 320 74 Click on this bang for MacOS | Bottom of page; +#X text 320 101 Click on this bang for Windows | Bottom of page; +#X text 320 124 Click on this bang for Linux; +#N canvas 861 110 853 614 Libraries 1; +#X text 83 6 <= Load the library of the script called py.py. The script must be located in the patch folder or inside one folder with same of the script that are in the PureData Search patches. For example \, If I have ~/Documents/Pd/pd-ji/pd-ji.py you can use py4pd -lib pd-ji to load the library., f 66; +#X text 96 118 Freqs in Hz.; +#X obj 15 281 dac~; +#X text 104 222 The Python objects do not support Help Patches yet! But inside both libraries there is some Helps patches.; +#X text 103 177 I already upload some Python Libraries for py4pd. Go to Help->Find Externals then "pd-ji" or "orchidea".; +#X obj 10 7 py4pd -lib py; +#X msg 92 157 0.1 0.1 0.1 0.1; +#X obj 15 202 snake~ out 4; +#X obj 15 181 sinusoids~ -ch 4; +#X text 263 97 !!! Start dsp!; +#X msg 247 127 \; pd dsp \$1; +#X obj 247 97 tgl 15 0 empty empty empty 0 -8 0 8 #fcfcfc #ff0400 #000000 0 1; +#X msg 15 118 200 230 310 370; +#X connect 6 0 8 1; +#X connect 7 0 2 0; +#X connect 7 1 2 1; +#X connect 7 2 2 0; +#X connect 7 3 2 1; +#X connect 8 0 7 0; +#X connect 11 0 10 0; +#X connect 12 0 8 0; +#X restore 14 233 pd Libraries; +#X text 80 232 <= Share your Python code as pd objects.; +#X text 109 210 <= Interact with Python from PureData.; +#X text 42 116 <= Click to open the script.; +#X obj 13 138 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -8 0 8 #fcfcfc #000000 #000000 0 256; +#X text 93 69 3 Run the function.; +#X text 64 46 2 See the docs of the function.; +#X text 82 16 1 Load function.; +#X obj 53 91 r py4pd; +#X msg 295 47 YOU NEED TO INSTALL PYTHON3.11; +#N canvas 240 118 658 503 py4pd 0; +#X obj 10 4 py4pd -lib py4pd; +#X obj 10 29 py&& -a 3; +#X obj 10 49 py|| -a 3; +#X obj 9 212 py.list -a 3; +#X obj 10 192 py.nth; +#X obj 10 173 py.len; +#X obj 10 155 py.mklist; +#X obj 10 124 pd2py; +#X obj 10 103 py2pd; +#X obj 10 83 py.print; +#X obj 99 30 py+; +#X obj 99 48 py-; +#X obj 99 67 py*; +#X obj 99 87 py/; +#X obj 225 32 py.iterate; +#X obj 225 53 py.collect; +#X obj 295 31 f2mc; +#X obj 295 51 mc2f; +#X obj 295 71 mc2n; +#X restore 13 261 pd py4pd extra objects; +#X text 131 260 py4pd come with object to use pip inside pd \, and minor others things. Check the subpatch.; +#X obj 13 117 py4pd; +#N canvas 538 32 988 300 MAINFUNCTIONS 0; +#X restore 74 160 pd MAINFUNCTIONS; +#X text 292 160 IMPORTANT: This is just simple example \, the exaustive Docs are avaible in www.charlesneimog.github.io/py4pd; +#X connect 0 0 27 0; +#X connect 6 0 27 0; +#X connect 7 0 27 0; +#X connect 19 0 3 0; +#X connect 23 0 27 0; +#X connect 27 0 19 0; From fe85a111fed2b077bd9061aa275948310760f4d5 Mon Sep 17 00:00:00 2001 From: "Charles K. Neimog" Date: Thu, 30 Nov 2023 10:34:50 -0300 Subject: [PATCH 07/21] help patch minor update --- py4pd-help.pd | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/py4pd-help.pd b/py4pd-help.pd index 72391401..dd963834 100644 --- a/py4pd-help.pd +++ b/py4pd-help.pd @@ -1,4 +1,4 @@ -#N canvas 494 62 1202 670 8; +#N canvas 545 31 1202 670 8; #X msg 25 46 doc, f 6; #N canvas 282 97 1541 723 embedded 0; #X obj 9 192 py4pd; @@ -105,10 +105,10 @@ #X text 320 74 Click on this bang for MacOS | Bottom of page; #X text 320 101 Click on this bang for Windows | Bottom of page; #X text 320 124 Click on this bang for Linux; -#N canvas 861 110 853 614 Libraries 0; +#N canvas 861 110 853 614 Libraries 1; #X text 83 6 <= Load the library of the script called py.py. The script must be located in the patch folder or inside one folder with same of the script that are in the PureData Search patches. For example \, If I have ~/Documents/Pd/pd-ji/pd-ji.py you can use py4pd -lib pd-ji to load the library., f 66; #X text 96 118 Freqs in Hz.; -#X obj 16 281 dac~; +#X obj 15 281 dac~; #X text 104 222 The Python objects do not support Help Patches yet! But inside both libraries there is some Helps patches.; #X text 103 177 I already upload some Python Libraries for py4pd. Go to Help->Find Externals then "pd-ji" or "orchidea".; #X obj 10 7 py4pd -lib py; From 32307ed1a254fb22e5a31dfe1d4484f946ad0df5 Mon Sep 17 00:00:00 2001 From: "Charles K. Neimog" Date: Thu, 30 Nov 2023 10:34:59 -0300 Subject: [PATCH 08/21] remove numba for testes --- .github/workflows/Builder.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Builder.yml b/.github/workflows/Builder.yml index 3f6d1564..85c55988 100644 --- a/.github/workflows/Builder.yml +++ b/.github/workflows/Builder.yml @@ -129,7 +129,7 @@ jobs: continue-on-error: true run: | cd test - python3.11 -m pip install numba numpy -t ./py-modules/ + python3.11 -m pip install numpy -t ./py-modules/ python3.11 ./runTests.py - name: Upload Object From c5727c7b28e3bad6ee5928a61297c701031da15b Mon Sep 17 00:00:00 2001 From: "Charles K. Neimog" Date: Thu, 30 Nov 2023 11:08:39 -0800 Subject: [PATCH 09/21] remove trash --- py.py | 152 ----------------------------------------- py4pd-help.pd | 171 ----------------------------------------------- test.py | 6 -- test/runTests.py | 2 +- 4 files changed, 1 insertion(+), 330 deletions(-) delete mode 100644 py.py delete mode 100644 py4pd-help.pd delete mode 100644 test.py diff --git a/py.py b/py.py deleted file mode 100644 index d314fddc..00000000 --- a/py.py +++ /dev/null @@ -1,152 +0,0 @@ -import pd -import sys -from random import randint - -try: - import numpy as np - numpyIsInstalled = True -except Exception as e: - pd.pip_install("local", "numpy") - numpyIsInstalled = False - pd.error("You must restart Pure Data to use numpy.") - sys.exit() - -# ================================================ -# ============== Functions ===================== -# ================================================ - - -def pdsum(x, y): - "It sums two numbers." - x = int(x) - y = int(y) - return x + y - - -def arithm_ser(begin, end, step): - "It calculates the arithmetic series." - begin = int(begin) - end = int(end) - step = int(step) - list = [] - for x in range(begin, end, step): - list.append(x) - return list - - -def fibonacci(n): - "Calculate the nth fibonacci number." - if n == 0: - return 0 - elif n == 1: - return 1 - else: - return fibonacci(n - 1) + fibonacci(n - 2) - - -# ================================================ -# ================== AUDIO ====================== -# ================================================ - -def pd_tempfolder(): - "It returns the temp folder path." - tempfolder = pd.tempfolder() - pd.print(str(tempfolder)) - - -def pd_output(): - "It sends some output to the py4pd output." - for x in range(10): - pd.out(x) - -def example_pdout(): - for x in range(2): - pd.out(x, symbol="myloop") - for x in range(2): - pd.out(x, symbol="myloop2") - return None - -def pdout(x): - pd.out(x, symbol="myloop") - pd.out(x, symbol="myloop2") - return None - -def pd_print(): - "It sends a message to the py4pd message box." - pd.print("Hello from python!") - return None - - -def pd_error(): - "It sends a message to the py4pd message box." - pd.error("pd error from Python, check the script.") - return None - - -def pd_send(): - "It sends a message to the py4pdreceiver receive." - pd.send("py4pdreceiver", "hello from python!") - pd.send("py4pdreceiver", 1) - pd.send("py4pdreceiver", [1, 2, 3, 4, 5]) - return 0 - - -def pd_tabwrite(): - "It sends a message to the py4pd message box." - list = [] - tablen = randint(10, 200) - i = 0 - while i < tablen: - # gerar aleatoric number between -1 and 1 - list.append(randint(-100, 100) / 100) - i += 1 - pd.tabwrite("test", list, resize=True) - - -def printall(x, y): - "It sends a message to the py4pd message box." - pd.print(str(x + y)) - -# ================================================ -# ================ Audio ========================= -# ================================================ -def generate_sine_wave(frequency, amplitude, phase, num_samples, sampling_rate): - angular_frequency = 2 * np.pi * frequency - t = np.arange(num_samples) / sampling_rate - sine_wave = amplitude * np.sin(angular_frequency * t + phase) - last_phase = phase + angular_frequency * t[-1] - return sine_wave, last_phase - - -def mksenoide(freqs, amps, phases, vectorsize, samplerate): - n = len(freqs) - nframes = vectorsize - out = np.zeros((n, nframes), dtype=np.float64) # Modify the shape of the output array - new_phases = np.zeros(n, dtype=np.float64) # Array to store the new phases - for i in range(n): - out[i], new_phases[i] = generate_sine_wave(freqs[i], amps[i], phases[i], nframes, samplerate) - if new_phases[i] > 2 * np.pi: - new_phases[i] -= 2 * np.pi - return out, new_phases - - -def sinusoids(freqs, amps): - vectorsize = pd.get_vec_size() - samplerate = pd.get_sample_rate() - if freqs is None or amps is None: - return None - if len(freqs) != len(amps): - return None - phases = pd.get_obj_var("PHASE", initial_value=np.zeros(len(freqs))) - freqs = np.array(freqs, dtype=np.float64) - amps = np.array(amps, dtype=np.float64) - out, new_phases = mksenoide(freqs, amps, phases, vectorsize, samplerate) - pd.set_obj_var("PHASE", new_phases) - return out - - - - -def py4pdLoadObjects(): - pd.add_object(sinusoids, 'sinusoids~', objtype=pd.AUDIOOUT) - diff --git a/py4pd-help.pd b/py4pd-help.pd deleted file mode 100644 index dd963834..00000000 --- a/py4pd-help.pd +++ /dev/null @@ -1,171 +0,0 @@ -#N canvas 545 31 1202 670 8; -#X msg 25 46 doc, f 6; -#N canvas 282 97 1541 723 embedded 0; -#X obj 9 192 py4pd; -#X msg 47 128 run; -#X msg 52 147 reload; -#X msg 9 216 set \$1; -#X msg 9 240 9; -#X text 6 298 With this you can send values for pd without return \, this seems to be very useful for work with list of lists. And other things; -#X obj 8 269 print pd.out; -#X msg 413 141 run; -#N canvas 0 50 450 250 (subpatch) 0; -#X array test 79 float 3; -#A 0 -0.55 0.24 0.78 0.79 -0.45 -0.82 0.81 -0.23 -0.89 -0.44 0.15 0.41 0.06 0.75 0.41 0.37 0.22 -0.5 -0.2 0.97 0.09 0 -0.46 -0.59 0.93 0.12 0.36 -0.43 -0.2 -0.5 0.03 -0.15 0.96 -0.4 0.95 -0.34 -0.16 0.93 -0.9 0.94 0.65 0.06 0.8 0.82 0 0.98 0.08 -0.04 0.13 -0.76 0.97 0.41 -0.17 -0.27 0.58 -0.37 -0.58 -0.33 0.89 0.03 -0.55 0.5 0.88 -0.25 -0.47 0.16 -0.17 0.39 -0.9 -0.36 -0.67 0.04 -0.22 0.1 0.06 -0.11 -0.55 0.46 0.77; -#X coords 0 1 79 -1 200 140 1; -#X restore 561 12 graph; -#X obj 413 92 tgl 20 0 empty empty empty 0 -10 0 12 #fcfcfc #000000 #000000 0 1; -#X obj 103 244 r py4pdreceiver; -#X obj 103 269 print py4pdreceiver; -#X msg 55 166 editor nvim; -#X msg 32 57 pd_print; -#X msg 9 8 pd_output; -#X msg 9 101 set py \$1; -#X obj 9 79 list; -#X msg 19 35 pd_tempfolder; -#X text 79 6 <= Select the Function to run.; -#X text 67 127 <= Run the Function.; -#X text 94 146 <= Reload the function if you edit it!; -#X text 119 165 <= Set your code editor.; -#X text 445 192 Write of read things in pd arrays using Python.; -#X text 438 93 <= Turn On; -#X obj 413 118 metro 500; -#X msg 425 168 set py pd_tabwrite; -#X obj 413 192 py4pd; -#X text 523 168 <= 1 Set the function; -#X connect 0 0 3 0; -#X connect 0 0 6 0; -#X connect 1 0 0 0; -#X connect 2 0 0 0; -#X connect 3 0 4 0; -#X connect 7 0 26 0; -#X connect 9 0 24 0; -#X connect 10 0 11 0; -#X connect 12 0 0 0; -#X connect 13 0 16 0; -#X connect 14 0 16 0; -#X connect 15 0 0 0; -#X connect 16 0 15 0; -#X connect 17 0 16 0; -#X connect 24 0 7 0; -#X connect 25 0 26 0; -#X restore 14 210 pd embedded module; -#N canvas 0 50 450 250 (subpatch) 0; -#X text 0 0 plugdatainfo ; -#X coords 0 1 100 -1 1 1 1; -#X restore 0 0 graph; -#X obj 13 160 print py4pd; -#N canvas 474 132 1263 711 code 0; -#X obj 7 305 s py4pd; -#X msg 74 154 reload, f 8; -#N canvas 0 50 450 250 (subpatch) 0; -#X text 0 0 plugdatainfo ; -#X coords 0 1 100 -1 1 1 1; -#X restore 0 0 graph; -#X msg 7 28 editor nvim; -#X msg 16 54 editor vscode; -#X text 94 28 Set Nvim as py4pd editor.; -#X text 120 54 Set vscode as py4pd editor.; -#X text 136 129 Open editor if it is in the PATH!; -#X text 142 153 Reload the function \, if you change it on script you need to reload!; -#X msg 29 79 editor emacs; -#X text 133 79 Set emacs as py4pd editor.; -#X msg 83 187 open myscript; -#X text 198 182 If you want to create a new script \, you can run open \, if the files already exits it will not replace it \, just open it.; -#X msg 46 104 editor gvim; -#X text 133 104 Set Gvim as py4pd editor.; -#X msg 57 130 editor sublime; -#X connect 1 0 0 0; -#X connect 3 0 0 0; -#X connect 4 0 0 0; -#X connect 9 0 0 0; -#X connect 11 0 0 0; -#X connect 13 0 0 0; -#X connect 15 0 0 0; -#X restore 15 187 pd code; -#X text 70 186 Code python with IDE.; -#X msg 13 17 set py pdsum; -#X msg 38 69 run 1 10, f 10; -#X obj 296 75 bng 20 250 50 0 mac empty empty 0 -10 0 12 #fcfcfc #000000 #000000; -#X obj 296 100 bng 20 250 50 0 windows empty empty 0 -10 0 12 #fcfcfc #000000 #000000; -#X obj 296 125 bng 20 250 50 0 linux empty empty 0 -10 0 12 #fcfcfc #000000 #000000; -#N canvas 0 0 1375 516 (subpatch) 0; -#X obj 47 189 pdcontrol; -#X obj 36 8 r mac; -#X obj 120 8 r windows; -#X obj 228 9 r linux; -#X msg 229 97 browse https://github.com/charlesneimog/py4pd/wiki/Install-Python; -#X msg 36 58 browse https://www.python.org/downloads/release/python-3114; -#X connect 1 0 5 0; -#X connect 2 0 5 0; -#X connect 3 0 4 0; -#X connect 4 0 0 0; -#X connect 5 0 0 0; -#X restore 295 25 pd; -#X text 320 74 Click on this bang for MacOS | Bottom of page; -#X text 320 101 Click on this bang for Windows | Bottom of page; -#X text 320 124 Click on this bang for Linux; -#N canvas 861 110 853 614 Libraries 1; -#X text 83 6 <= Load the library of the script called py.py. The script must be located in the patch folder or inside one folder with same of the script that are in the PureData Search patches. For example \, If I have ~/Documents/Pd/pd-ji/pd-ji.py you can use py4pd -lib pd-ji to load the library., f 66; -#X text 96 118 Freqs in Hz.; -#X obj 15 281 dac~; -#X text 104 222 The Python objects do not support Help Patches yet! But inside both libraries there is some Helps patches.; -#X text 103 177 I already upload some Python Libraries for py4pd. Go to Help->Find Externals then "pd-ji" or "orchidea".; -#X obj 10 7 py4pd -lib py; -#X msg 92 157 0.1 0.1 0.1 0.1; -#X obj 15 202 snake~ out 4; -#X obj 15 181 sinusoids~ -ch 4; -#X text 263 97 !!! Start dsp!; -#X msg 247 127 \; pd dsp \$1; -#X obj 247 97 tgl 15 0 empty empty empty 0 -8 0 8 #fcfcfc #ff0400 #000000 0 1; -#X msg 15 118 200 230 310 370; -#X connect 6 0 8 1; -#X connect 7 0 2 0; -#X connect 7 1 2 1; -#X connect 7 2 2 0; -#X connect 7 3 2 1; -#X connect 8 0 7 0; -#X connect 11 0 10 0; -#X connect 12 0 8 0; -#X restore 14 233 pd Libraries; -#X text 80 232 <= Share your Python code as pd objects.; -#X text 109 210 <= Interact with Python from PureData.; -#X text 42 116 <= Click to open the script.; -#X obj 13 138 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -8 0 8 #fcfcfc #000000 #000000 0 256; -#X text 93 69 3 Run the function.; -#X text 64 46 2 See the docs of the function.; -#X text 82 16 1 Load function.; -#X obj 53 91 r py4pd; -#X msg 295 47 YOU NEED TO INSTALL PYTHON3.11; -#N canvas 240 118 658 503 py4pd 0; -#X obj 10 4 py4pd -lib py4pd; -#X obj 10 29 py&& -a 3; -#X obj 10 49 py|| -a 3; -#X obj 9 212 py.list -a 3; -#X obj 10 192 py.nth; -#X obj 10 173 py.len; -#X obj 10 155 py.mklist; -#X obj 10 124 pd2py; -#X obj 10 103 py2pd; -#X obj 10 83 py.print; -#X obj 99 30 py+; -#X obj 99 48 py-; -#X obj 99 67 py*; -#X obj 99 87 py/; -#X obj 225 32 py.iterate; -#X obj 225 53 py.collect; -#X obj 295 31 f2mc; -#X obj 295 51 mc2f; -#X obj 295 71 mc2n; -#X restore 13 261 pd py4pd extra objects; -#X text 131 260 py4pd come with object to use pip inside pd \, and minor others things. Check the subpatch.; -#X obj 13 117 py4pd; -#N canvas 538 32 988 300 MAINFUNCTIONS 0; -#X restore 74 160 pd MAINFUNCTIONS; -#X text 292 160 IMPORTANT: This is just simple example \, the exaustive Docs are avaible in www.charlesneimog.github.io/py4pd; -#X connect 0 0 27 0; -#X connect 6 0 27 0; -#X connect 7 0 27 0; -#X connect 19 0 3 0; -#X connect 23 0 27 0; -#X connect 27 0 19 0; diff --git a/test.py b/test.py deleted file mode 100644 index d4444f51..00000000 --- a/test.py +++ /dev/null @@ -1,6 +0,0 @@ -import pd4web -import pd - - -def sumpd4web(): - pd.print("ok") diff --git a/test/runTests.py b/test/runTests.py index 1f6add13..d4264c15 100755 --- a/test/runTests.py +++ b/test/runTests.py @@ -68,7 +68,7 @@ def runTest(pdpatch): print(f"Patch {pathfile} not found") sys.exit() cmd = ( - '/Applications/Pd-*.app/Contents/Resources/bin/pd -nogui -stderr -send "start-test bang" ' + '/Applications/Pd-*.app/Contents/Resources/bin/pd -stderr -nogui -send "start-test bang" ' + pathfile ) try: From 5e97f7be37886149035d72585c27a61273f56c46 Mon Sep 17 00:00:00 2001 From: "Charles K. Neimog" Date: Thu, 30 Nov 2023 11:09:13 -0800 Subject: [PATCH 10/21] close #69, warning when numpy is not installed --- Makefile | 5 +- resources/py.py | 9 +- resources/py4pd-help.pd | 170 +++++++++++++++------------------ resources/py4pd-mod/src/pip.py | 2 - src/ext-class.c | 3 - src/ext-libraries.c | 49 +++++----- src/module.c | 6 -- src/pic.c | 1 - src/py4pd.c | 66 +++++++------ src/py4pd.h | 7 +- src/utils.c | 9 +- 11 files changed, 154 insertions(+), 173 deletions(-) mode change 100755 => 100644 src/py4pd.c diff --git a/Makefile b/Makefile index e5f42c33..8b42618b 100755 --- a/Makefile +++ b/Makefile @@ -25,12 +25,13 @@ else ifeq (Darwin,$(findstring Darwin,$(uname))) PYTHON_INCLUDE := $(shell $(PYTHON_VERSION) -c 'import sysconfig;print(sysconfig.get_config_var("INCLUDEPY"))') NUMPY_INCLUDE := $(shell $(PYTHON_VERSION) -c 'import numpy; print(numpy.get_include())') ifeq ($(extension),d_arm64) - cflags = -I $(PYTHON_INCLUDE) -I $(NUMPY_INCLUDE) -Wno-cast-function-type -mmacosx-version-min=12 + cflags = -I $(PYTHON_INCLUDE) -I $(NUMPY_INCLUDE) -Wno-bad-function-cast -mmacosx-version-min=12 else - cflags = -I $(PYTHON_INCLUDE) -I $(NUMPY_INCLUDE) -Wno-cast-function-type -mmacosx-version-min=10.9 + cflags = -I $(PYTHON_INCLUDE) -I $(NUMPY_INCLUDE) -Wno-bad-function-cast -mmacosx-version-min=10.9 endif PYTHON_LIB := $(shell $(PYTHON_VERSION) -c 'import sysconfig;print(sysconfig.get_config_var("LIBDIR"))') ldlibs = -l dl -L $(PYTHON_LIB) -l $(PYTHON_VERSION) -Wno-null-pointer-subtraction + else $(error "Unknown system type: $(uname)") $(shell exit 1) diff --git a/resources/py.py b/resources/py.py index 1d935837..462c0dfd 100644 --- a/resources/py.py +++ b/resources/py.py @@ -1,14 +1,7 @@ import pd import sys from random import randint - -try: - import numpy as np - numpyIsInstalled = True -except Exception as e: - import py4pd - py4pd.pipinstall(["global", "numpy"]) - sys.exit() +import numpy as np # ================================================ # ============== Functions ===================== diff --git a/resources/py4pd-help.pd b/resources/py4pd-help.pd index 2af5c679..5c409897 100644 --- a/resources/py4pd-help.pd +++ b/resources/py4pd-help.pd @@ -1,6 +1,6 @@ -#N canvas 494 62 1228 612 8; +#N canvas 552 221 631 353 8; #X msg 25 46 doc, f 6; -#N canvas 282 97 1541 723 embedded 0; +#N canvas 491 261 794 378 embedded 0; #X obj 9 192 py4pd; #X msg 47 128 run; #X msg 52 147 reload; @@ -10,9 +10,9 @@ #X obj 8 269 print pd.out; #X msg 413 141 run; #N canvas 0 50 450 250 (subpatch) 0; -#X array test 79 float 3; -#A 0 -0.55 0.24 0.78 0.79 -0.45 -0.82 0.81 -0.23 -0.89 -0.44 0.15 0.41 0.06 0.75 0.41 0.37 0.22 -0.5 -0.2 0.97 0.09 0 -0.46 -0.59 0.93 0.12 0.36 -0.43 -0.2 -0.5 0.03 -0.15 0.96 -0.4 0.95 -0.34 -0.16 0.93 -0.9 0.94 0.65 0.06 0.8 0.82 0 0.98 0.08 -0.04 0.13 -0.76 0.97 0.41 -0.17 -0.27 0.58 -0.37 -0.58 -0.33 0.89 0.03 -0.55 0.5 0.88 -0.25 -0.47 0.16 -0.17 0.39 -0.9 -0.36 -0.67 0.04 -0.22 0.1 0.06 -0.11 -0.55 0.46 0.77; -#X coords 0 1 79 -1 200 140 1; +#X array test 106 float 3; +#A 0 0.45 -0.67 0.05 -0.61 -0.97 0.63 0.47 -0.44 0.67 0.73 -0.22 -0.35 0.86 -0.41 0.01 -0.43 0.63 0.91 -0.57 0.92 -0.83 -0.2 -0.47 -0.21 -0.94 -0.06 -0.78 -0.52 1 -0.37 -0.41 -0.86 -0.7 0.81 -0.17 0.87 -0.85 0.44 0.05 -0.74 0.59 -0.58 0.09 0.48 0.68 -0.29 -0.16 0.16 0.25 0.47 -0.65 0.83 -0.18 -0.47 0.71 -0.67 1 0.49 0.41 0.56 -0.74 0.05 -0.22 0.56 -0.78 0.3 0.02 -0.82 0.51 -0.74 0.26 0.01 0.68 0.5 -0.07 0.6 -0.97 0.81 -0.49 -0.22 0.65 0.6 0.66 0.45 0.76 0.75 -0.53 -0.61 -0.22 -0.75 -0.9 -0.89 0.5 0.23 0.09 -0.63 0.15 -0.01 0.95 0.06 0.57 -0.95 -0.29 0.84 0.43 0.14; +#X coords 0 1 106 -1 200 140 1; #X restore 561 12 graph; #X obj 413 92 tgl 20 0 empty empty empty 0 -10 0 12 #fcfcfc #000000 #000000 0 1; #X obj 103 244 r py4pdreceiver; @@ -55,7 +55,7 @@ #X coords 0 1 100 -1 1 1 1; #X restore 0 0 graph; #X obj 13 160 print py4pd; -#N canvas 474 132 1263 711 code 0; +#N canvas 236 39 576 375 code 0; #X obj 7 305 s py4pd; #X msg 74 154 reload, f 8; #N canvas 0 50 450 250 (subpatch) 0; @@ -71,7 +71,7 @@ #X msg 29 79 editor emacs; #X text 133 79 Set emacs as py4pd editor.; #X msg 83 187 open myscript; -#X text 198 182 If you want to create a new script \, you can run open \, if the files already exits it will not replace it \, just open it.; +#X text 155 182 If you want to create a new script \, you can run open \, if the files already exits it will not replace it \, just open it.; #X msg 46 104 editor gvim; #X text 133 104 Set Gvim as py4pd editor.; #X msg 57 130 editor sublime; @@ -105,65 +105,71 @@ #X text 320 74 Click on this bang for MacOS | Bottom of page; #X text 320 101 Click on this bang for Windows | Bottom of page; #X text 320 124 Click on this bang for Linux; -#N canvas 861 110 853 614 Libraries 0; +#N canvas 485 102 440 353 Libraries 0; #X text 83 6 <= Load the library of the script called py.py. The script must be located in the patch folder or inside one folder with same of the script that are in the PureData Search patches. For example \, If I have ~/Documents/Pd/pd-ji/pd-ji.py you can use py4pd -lib pd-ji to load the library., f 66; -#X obj 16 281 dac~; -#X text 104 222 The Python objects do not support Help Patches yet! But inside both libraries there is some Helps patches.; -#X text 102 187 I already upload some Python Libraries for py4pd. Go to Help->Find Externals then "pd-ji" or "orchidea".; +#X text 96 83 Freqs in Hz.; +#X obj 15 246 dac~; +#X text 104 187 The Python objects do not support Help Patches yet! But inside both libraries there is some Helps patches.; +#X text 103 142 I already upload some Python Libraries for py4pd. Go to Help->Find Externals then "pd-ji" or "orchidea".; #X obj 10 7 py4pd -lib py; -#X msg 92 157 0.1 0.1 0.1 0.1; -#X obj 15 202 snake~ out 4; -#X obj 15 181 sinusoids~ -ch 4; -#X text 263 97 !!! Start dsp!; -#X msg 247 127 \; pd dsp \$1; -#X obj 247 97 tgl 15 0 empty empty empty 0 -8 0 8 #fcfcfc #ff0400 #000000 0 1; -#X obj 18 73 hsl 100 10 100 2000 0 1 empty empty empty -2 -8 0 8 #fcfcfc #000000 #000000 1750 1; -#X obj 33 87 hsl 100 10 100 2000 0 1 empty empty empty -2 -8 0 8 #fcfcfc #000000 #000000 4350 1; -#X obj 49 101 hsl 100 10 100 2000 0 1 empty empty empty -2 -8 0 8 #fcfcfc #000000 #000000 4050 1; -#X obj 65 115 hsl 100 10 100 2000 0 1 empty empty empty -2 -8 0 8 #fcfcfc #000000 #000000 2650 1; -#N canvas 131 169 450 359 (subpatch) 0; -#X obj 50 18 inlet; -#X obj 50 69 bng 15 250 50 0 empty empty empty 0 -8 0 8 #fcfcfc #000000 #000000; -#X obj 50 39 t b f; -#X text 121 118 Freqs in Hz.; -#X obj 16 118 pack 100 200 300 400; -#X obj 16 140 outlet; -#X obj 16 18 inlet; -#X obj 84 18 inlet; -#X obj 84 39 t b f; -#X obj 115 18 inlet; -#X obj 115 39 t b f; -#X connect 0 0 2 0; -#X connect 1 0 4 0; -#X connect 2 0 1 0; -#X connect 2 1 4 1; -#X connect 4 0 5 0; -#X connect 6 0 4 0; -#X connect 7 0 8 0; -#X connect 8 0 1 0; -#X connect 8 1 4 2; -#X connect 9 0 10 0; -#X connect 10 0 1 0; -#X connect 10 1 4 3; -#X restore 15 128 pd; -#X f 10; -#X obj 92 139 loadbang; -#X text 92 270 !!!!!!!!!THIS IS VERY INSTABLE YET!!!!!!!!!!!!!!; -#X connect 5 0 7 1; -#X connect 6 0 1 0; -#X connect 6 1 1 1; -#X connect 6 2 1 0; -#X connect 6 3 1 1; -#X connect 7 0 6 0; -#X connect 10 0 9 0; -#X connect 11 0 15 0; -#X connect 12 0 15 1; -#X connect 13 0 15 2; -#X connect 14 0 15 3; -#X connect 15 0 7 0; -#X connect 16 0 5 0; +#X msg 92 122 0.1 0.1 0.1 0.1; +#X obj 15 167 snake~ out 4; +#X obj 15 146 sinusoids~ -ch 4; +#X text 263 69 !!! Start dsp!; +#X obj 229 69 tgl 25 0 empty empty empty 0 -8 0 8 #fcfcfc #ff0400 #000000 0 1; +#X msg 15 83 200 230 310 370; +#X obj 54 246 vu 8 80 empty empty -1 -8 0 8 #404040 #000000 0 0; +#N canvas 329 161 625 417 vu 0; +#X obj 59 129 env~ 1024; +#X obj 161 111 abs~; +#X obj 161 213 snapshot~; +#X obj 161 256 rmstodb; +#X obj 161 21 inlet~; +#X obj 161 288 - 100; +#X obj 59 287 - 100; +#X obj 59 361 outlet; +#X obj 161 361 outlet; +#X text 344 307 Check the help file of env~ and slop~ for more details., f 31; +#X obj 239 89 expr 1000 / $f1; +#X floatatom 239 117 5 0 0 0 - - - 0; +#X obj 161 146 slop~ 0 0 0 0 1e+09; +#X obj 239 18 inlet; +#X floatatom 239 57 5 0 0 0 - - - 0; +#X text 359 89 convert to hz; +#X text 282 118 hz; +#X text 284 57 peak decay time in ms; +#X obj 59 171 t f b; +#X text 345 132 The env~ object is dedicated to RMS analysis. Its output is in dB (0-100 range \, pd style) but needs to be converted to dBFS (and we do that by simply subtracting 100)., f 31; +#X text 345 225 The slop~ object is used here for peak detection. We need to convert the output to dB with rmstodb and also subtract by 100 to convert it to dBFS., f 31; +#X connect 0 0 18 0; +#X connect 1 0 12 0; +#X connect 2 0 3 0; +#X connect 3 0 5 0; +#X connect 4 0 0 0; +#X connect 4 0 1 0; +#X connect 5 0 8 0; +#X connect 6 0 7 0; +#X connect 10 0 11 0; +#X connect 11 0 12 3; +#X connect 12 0 2 0; +#X connect 13 0 14 0; +#X connect 14 0 10 0; +#X connect 18 0 6 0; +#X connect 18 1 2 0; +#X restore 53 218 pd vu; +#X obj 229 104 switch~; +#X connect 6 0 8 1; +#X connect 7 0 2 0; +#X connect 7 0 13 0; +#X connect 7 1 2 1; +#X connect 7 2 2 0; +#X connect 7 2 13 0; +#X connect 7 3 2 1; +#X connect 8 0 7 0; +#X connect 10 0 14 0; +#X connect 11 0 8 0; +#X connect 13 0 12 0; #X restore 14 233 pd Libraries; -#X text 292 160 IMPORTANT: This is just simple example \, the exaustive Docs are avaible in www.charlesneimog.com/py4pd; #X text 80 232 <= Share your Python code as pd objects.; #X text 109 210 <= Interact with Python from PureData.; #X text 42 116 <= Click to open the script.; @@ -173,32 +179,14 @@ #X text 82 16 1 Load function.; #X obj 53 91 r py4pd; #X msg 295 47 YOU NEED TO INSTALL PYTHON3.11; -#N canvas 240 118 658 503 py4pd 0; -#X obj 10 4 py4pd -lib py4pd; -#X obj 10 29 py&& -a 3; -#X obj 10 49 py|| -a 3; -#X obj 9 212 py.list -a 3; -#X obj 10 192 py.nth; -#X obj 10 173 py.len; -#X obj 10 155 py.mklist; -#X obj 10 124 pd2py; -#X obj 10 103 py2pd; -#X obj 10 83 py.print; -#X obj 99 30 py+; -#X obj 99 48 py-; -#X obj 99 67 py*; -#X obj 99 87 py/; -#X obj 225 32 py.iterate; -#X obj 225 53 py.collect; -#X obj 295 31 f2mc; -#X obj 295 51 mc2f; -#X obj 295 71 mc2n; -#X restore 13 261 pd py4pd extra objects; -#X text 131 260 py4pd come with object to use pip inside pd \, and minor others things. Check the subpatch.; #X obj 13 117 py4pd; -#X connect 0 0 28 0; -#X connect 6 0 28 0; -#X connect 7 0 28 0; -#X connect 20 0 3 0; -#X connect 24 0 28 0; -#X connect 28 0 20 0; +#X text 292 160 IMPORTANT: This is just simple example \, the exaustive Docs are avaible in www.charlesneimog.github.io/py4pd; +#X msg 397 230 pipinstall global numpy; +#X obj 397 250 py4pd; +#X connect 0 0 25 0; +#X connect 6 0 25 0; +#X connect 7 0 25 0; +#X connect 19 0 3 0; +#X connect 23 0 25 0; +#X connect 25 0 19 0; +#X connect 27 0 28 0; diff --git a/resources/py4pd-mod/src/pip.py b/resources/py4pd-mod/src/pip.py index 8f2b0148..9e90ea7b 100755 --- a/resources/py4pd-mod/src/pip.py +++ b/resources/py4pd-mod/src/pip.py @@ -1,4 +1,3 @@ -import faulthandler import os import platform import subprocess @@ -6,7 +5,6 @@ import pd -faulthandler.enable() package = "" folder = "" diff --git a/src/ext-class.c b/src/ext-class.c index 2487c016..f053fe60 100644 --- a/src/ext-class.c +++ b/src/ext-class.c @@ -1,8 +1,5 @@ #include "py4pd.h" -#define NPY_NO_DEPRECATED_API NPY_1_25_API_VERSION -#include - // ============================================================ // ===================== PDOBJECT CLASS ======================= // ============================================================ diff --git a/src/ext-libraries.c b/src/ext-libraries.c index f4333d7d..63befdfb 100644 --- a/src/ext-libraries.c +++ b/src/ext-libraries.c @@ -1,8 +1,5 @@ #include "py4pd.h" -#define NPY_NO_DEPRECATED_API NPY_1_25_API_VERSION -#include - // =========================================== // ================= PUREDATA ================ // =========================================== @@ -635,21 +632,29 @@ t_int *Py4pdLib_AudioPerform(t_int *w) { // ===================================== static void Py4pdLib_Dsp(t_py *x, t_signal **sp) { - if (x->objType == PY4PD_AUDIOINOBJ) { - x->nChs = sp[0]->s_nchans; - x->vectorSize = sp[0]->s_n; - dsp_add(Py4pdLib_AudioINPerform, 3, x, sp[0]->s_vec, PY4PDSIGTOTAL(sp[0])); - } else if (x->objType == PY4PD_AUDIOOUTOBJ) { - x->vectorSize = sp[0]->s_n; - signal_setmultiout(&sp[0], x->nChs); - dsp_add(Py4pdLib_AudioOUTPerform, 3, x, sp[0]->s_vec, PY4PDSIGTOTAL(sp[0])); - } else if (x->objType == PY4PD_AUDIOOBJ) { - x->nChs = sp[0]->s_nchans; - x->vectorSize = sp[0]->s_n; - signal_setmultiout(&sp[1], sp[0]->s_nchans); - dsp_add(Py4pdLib_AudioPerform, 4, x, sp[0]->s_vec, sp[1]->s_vec, - PY4PDSIGTOTAL(sp[0])); - } + int numpyArrayImported = Py4pd_ImportNumpyForPy4pd(); + if (numpyArrayImported == 1) { + x->numpyImported = 1; + logpost(NULL, 3, "Numpy Loaded"); + if (x->objType == PY4PD_AUDIOINOBJ) { + x->nChs = sp[0]->s_nchans; + x->vectorSize = sp[0]->s_n; + dsp_add(Py4pdLib_AudioINPerform, 3, x, sp[0]->s_vec, PY4PDSIGTOTAL(sp[0])); + } else if (x->objType == PY4PD_AUDIOOUTOBJ) { + x->vectorSize = sp[0]->s_n; + signal_setmultiout(&sp[0], x->nChs); + dsp_add(Py4pdLib_AudioOUTPerform, 3, x, sp[0]->s_vec, PY4PDSIGTOTAL(sp[0])); + } else if (x->objType == PY4PD_AUDIOOBJ) { + x->nChs = sp[0]->s_nchans; + x->vectorSize = sp[0]->s_n; + signal_setmultiout(&sp[1], sp[0]->s_nchans); + dsp_add(Py4pdLib_AudioPerform, 4, x, sp[0]->s_vec, sp[1]->s_vec, + PY4PDSIGTOTAL(sp[0])); + } + } else { + x->numpyImported = 0; + pd_error(NULL, "[%s] Numpy was not imported!", x->objName->s_name); + } } // ================ @@ -781,14 +786,13 @@ void *Py4pdLib_NewObj(t_symbol *s, int argc, t_atom *argv) { int i; if (x->objType > 1) { - int numpyArrayImported = _import_array(); - if (numpyArrayImported == 0) { + int numpyArrayImported = Py4pd_ImportNumpyForPy4pd(); + if (numpyArrayImported == 1) { x->numpyImported = 1; logpost(NULL, 3, "Numpy Loaded"); - } else { x->numpyImported = 0; - pd_error(x, "[%s] Not possible to import numpy array", objectName); + pd_error(NULL, "[%s] Numpy was not imported!", objectName); Py_DECREF(pd_module); Py_DECREF(py4pd_capsule); return NULL; @@ -1120,5 +1124,6 @@ PyObject *Py4pdLib_AddObj(PyObject *self, PyObject *args, PyObject *keywords) { post("[py4pd]: Object {%s} added to PureData", objectName); } class_set_extern_dir(&s_); + post("Object {%s} added to PureData", objectName); Py_RETURN_TRUE; } diff --git a/src/module.c b/src/module.c index 1d64a174..ce3078b8 100755 --- a/src/module.c +++ b/src/module.c @@ -1,9 +1,4 @@ -#include "m_pd.h" #include "py4pd.h" -#include "s_stuff.h" - -#define NPY_NO_DEPRECATED_API NPY_1_25_API_VERSION -#include // ================================= /** @@ -1396,7 +1391,6 @@ const char *createUniqueConstString(const char *prefix, const void *pointer) { // ================================= PyMODINIT_FUNC PyInit_pd() { PyObject *py4pdmodule; - py4pdmodule = PyModule_Create(&pdmodule); if (py4pdmodule == NULL) { diff --git a/src/pic.c b/src/pic.c index cc0d8beb..30ffcd50 100755 --- a/src/pic.c +++ b/src/pic.c @@ -1,4 +1,3 @@ -#include "m_pd.h" #include "py4pd.h" t_widgetbehavior py4pd_widgetbehavior; diff --git a/src/py4pd.c b/src/py4pd.c old mode 100755 new mode 100644 index 889a3b0a..cc404c93 --- a/src/py4pd.c +++ b/src/py4pd.c @@ -3,8 +3,6 @@ // ============================================ t_class *py4pd_class; // For for normal objects, almost unused t_class *py4pd_classLibrary; // For libraries -t_class *py4pd_class; // For for normal objects, almost unused -t_class *py4pd_classLibrary; // For libraries int object_count = 0; // ============================================ @@ -317,7 +315,12 @@ static void Py4pd_PipInstall(t_py *x, t_symbol *s, int argc, t_atom *argv) { PyObject *py4pdModule = PyImport_ImportModule("py4pd"); if (py4pdModule == NULL) { - pd_error(x, "[Python] pipInstall: py4pd module not found"); + PyObject *ptype, *pvalue, *ptraceback; + PyErr_Fetch(&ptype, &pvalue, &ptraceback); + PyErr_NormalizeException(&ptype, &pvalue, &ptraceback); + PyObject *pstr = PyObject_Str(pvalue); + pd_error(x, "[py4pd] Pip failed: %s", PyUnicode_AsUTF8(pstr)); + Py_XDECREF(pstr); return; } PyObject *pipInstallFunction = @@ -616,7 +619,9 @@ static void Py4pd_OpenScript(t_py *x, t_symbol *s, int argc, t_atom *argv) { */ void Py4pd_SetEditor(t_py *x, t_symbol *s, int argc, t_atom *argv) { (void)s; - if (argc != 0) { + // check if argv[0] is a symbol and it is diferent from "float" + if (argc != 0 && argv[0].a_type == A_SYMBOL && + strcmp(argv[0].a_w.w_symbol->s_name, "float") != 0) { x->editorName = atom_getsymbol(argv + 0); post("[py4pd] Editor set to: %s", x->editorName->s_name); char cfgFile[MAXPDSTRING]; @@ -1021,17 +1026,19 @@ void *Py4pd_Py4pdNew(t_symbol *s, int argc, t_atom *argv) { x->pdPatchPath = patch_dir; // set name of the home path x->pkgPath = patch_dir; // set name of the packages path x->pArgsCount = 0; - - Py4pdUtils_SetObjConfig( - x); // set the config file (in py4pd.cfg, make this be + Py4pdUtils_SetObjConfig(x); if (object_count == 0) { Py4pdUtils_AddPathsToPythonPath(x); } + return x; if (argc > 1) { // check if there are two arguments + if (_import_array() < 0) { + pd_error(NULL, "\n!!!!!!\n [py4pd] Unable to import NumPy! Send [pipinstall global numpy] to py4pd object to install it.] \n!!!!!!\n"); + return (x); + } Py4pd_SetFunction(x, s, argc, argv); - Py4pd_ImportNumpyForPy4pd(); } object_count++; return (x); @@ -1046,18 +1053,17 @@ void *Py4pd_Py4pdNew(t_symbol *s, int argc, t_atom *argv) { Py4pdUtils_SetObjConfig(x); if (object_count == 0) { Py4pdUtils_AddPathsToPythonPath(x); + if (_import_array() < 0) { + pd_error(NULL, "\n!!!!!!\n [py4pd] Unable to import NumPy! Send [pipinstall global numpy] to py4pd object to install it.] \n!!!!!!\n"); + return NULL; + } } int libraryLoaded = Py4pd_LibraryLoad(x, argc, argv); if (libraryLoaded == -1) { return NULL; } x->pScriptName = scriptName; - - if (object_count == 0) { - Py4pdUtils_AddPathsToPythonPath(x); - } object_count++; - Py4pd_ImportNumpyForPy4pd(); return (x); } else { pd_error(NULL, "Error in py4pdNew, you can not use more than one flag at " @@ -1072,6 +1078,19 @@ void *Py4pd_Py4pdNew(t_symbol *s, int argc, t_atom *argv) { */ void py4pd_setup(void) { + if (!Py_IsInitialized()) { + object_count = 0; + post(""); + post("[py4pd] by Charles K. Neimog"); + post("[py4pd] Version %d.%d.%d", PY4PD_MAJOR_VERSION, + PY4PD_MINOR_VERSION, PY4PD_MICRO_VERSION); + post("[py4pd] Python version %d.%d.%d", PY_MAJOR_VERSION, PY_MINOR_VERSION, + PY_MICRO_VERSION); + post(""); + PyImport_AppendInittab("pd", PyInit_pd); + Py_Initialize(); + } + py4pd_class = class_new(gensym("py4pd"), // cria o objeto quando escrevemos py4pd (t_newmethod)Py4pd_Py4pdNew, // metodo de criação do objeto @@ -1080,10 +1099,12 @@ void py4pd_setup(void) { 0, // nao há uma GUI especial para esse objeto??? A_GIMME, // os podem ser qualquer coisa 0); // fim de argumentos + py4pd_classLibrary = class_new(gensym("py4pd"), (t_newmethod)Py4pd_Py4pdNew, (t_method)Py4pdLib_FreeObj, sizeof(t_py), CLASS_NOINLET, A_GIMME, 0); + // this is like have lot of objects with the same name, add all methods for class_addmethod(py4pd_class, (t_method)Py4pd_SetPy4pdHomePath, gensym("home"), A_GIMME, 0); // set home path @@ -1126,22 +1147,5 @@ void py4pd_setup(void) { gensym("detach"), 0); #endif - // INIT PYTHON - if (!Py_IsInitialized()) { - object_count = 0; - post(""); - post("[py4pd] by Charles K. Neimog"); -#ifdef PD_FLAVOR - post("[py4pd] Version %d.%d.%d | %s", PY4PD_MAJOR_VERSION, - PY4PD_MINOR_VERSION, PY4PD_MICRO_VERSION, PD_FLAVOR); -#else - post("[py4pd] Version %d.%d.%d | %s", PY4PD_MAJOR_VERSION, - PY4PD_MINOR_VERSION, PY4PD_MICRO_VERSION, "Pd Vanilla"); -#endif - post("[py4pd] Python version %d.%d.%d", PY_MAJOR_VERSION, PY_MINOR_VERSION, - PY_MICRO_VERSION); - post(""); - PyImport_AppendInittab("pd", PyInit_pd); - Py_Initialize(); - } + } diff --git a/src/py4pd.h b/src/py4pd.h index 5f8513d8..ac4285f2 100755 --- a/src/py4pd.h +++ b/src/py4pd.h @@ -11,6 +11,11 @@ #define PY_SSIZE_T_CLEAN // Remove deprecated functions #include +#define PY_ARRAY_UNIQUE_SYMBOL PY4PD_ARRAY_API +#define NPY_NO_DEPRECATED_API NPY_1_25_API_VERSION +#include + + #ifdef __linux__ #define __USE_GNU #endif @@ -228,7 +233,7 @@ typedef struct _py4pdInlet_proxy{ // ===================================== t_py4pd_pValue *Py4pdUtils_Run(t_py *x, PyObject *pArgs, t_py4pd_pValue *pValuePointer); -void *Py4pd_ImportNumpyForPy4pd(); +int Py4pd_ImportNumpyForPy4pd(); // ===================================== // void Py4pd_SetParametersForFunction(t_py *x, t_symbol *s, int argc, t_atom *argv); diff --git a/src/utils.c b/src/utils.c index fc7bb73b..89752ea5 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1,8 +1,5 @@ #include "py4pd.h" -#define NPY_NO_DEPRECATED_API NPY_1_25_API_VERSION -#include - // ==================================================== /* * @brief This function parse the arguments for pd Objects created with the @@ -1474,9 +1471,9 @@ void Py4pdUtils_AddPathsToPythonPath(t_py *x) { * @param NULL * @return It will return NULL. */ -void *Py4pd_ImportNumpyForPy4pd() { - _import_array(); - return NULL; +int Py4pd_ImportNumpyForPy4pd() { + //import_array(); + return 1; } // ========================= PYTHON ============================== From b984ed1f9b239ab58c28b508925b1500e780c062 Mon Sep 17 00:00:00 2001 From: "Charles K. Neimog" Date: Thu, 30 Nov 2023 19:34:03 -0300 Subject: [PATCH 11/21] fix numpy import in Linux, related with #69 too --- src/ext-class.c | 1 + src/ext-libraries.c | 53 +++++++++++++++++++++++---------------------- src/pic.c | 8 ++++--- src/player.c | 1 + src/py4pd.c | 33 ++++++++++++++++------------ src/py4pd.h | 3 +-- src/utils.c | 3 ++- 7 files changed, 56 insertions(+), 46 deletions(-) diff --git a/src/ext-class.c b/src/ext-class.c index f053fe60..ed5fae8b 100644 --- a/src/ext-class.c +++ b/src/ext-class.c @@ -1,3 +1,4 @@ +#define NO_IMPORT_ARRAY #include "py4pd.h" // ============================================================ diff --git a/src/ext-libraries.c b/src/ext-libraries.c index 63befdfb..f4ae8816 100644 --- a/src/ext-libraries.c +++ b/src/ext-libraries.c @@ -548,9 +548,10 @@ void Py4pdLib_Bang(t_py *x) { // ===================================== t_int *Py4pdLib_AudioINPerform(t_int *w) { t_py *x = (t_py *)(w[1]); - if (x->audioError) { + + if (x->audioError || x->numpyImported == 0) return (w + 4); - } + t_sample *in = (t_sample *)(w[2]); int n = (int)(w[3]); x->vectorSize = n; @@ -572,7 +573,8 @@ t_int *Py4pdLib_AudioINPerform(t_int *w) { // ===================================== t_int *Py4pdLib_AudioOUTPerform(t_int *w) { t_py *x = (t_py *)(w[1]); - if (x->audioError) + + if (x->audioError || x->numpyImported == 0) return (w + 4); t_sample *audioOut = (t_sample *)(w[2]); @@ -598,7 +600,7 @@ t_int *Py4pdLib_AudioOUTPerform(t_int *w) { // ===================================== t_int *Py4pdLib_AudioPerform(t_int *w) { t_py *x = (t_py *)(w[1]); - if (x->audioError) + if (x->audioError || x->numpyImported == 0) return (w + 5); t_sample *audioIn = (t_sample *)(w[2]); t_sample *audioOut = (t_sample *)(w[3]); @@ -632,29 +634,28 @@ t_int *Py4pdLib_AudioPerform(t_int *w) { // ===================================== static void Py4pdLib_Dsp(t_py *x, t_signal **sp) { - int numpyArrayImported = Py4pd_ImportNumpyForPy4pd(); - if (numpyArrayImported == 1) { - x->numpyImported = 1; - logpost(NULL, 3, "Numpy Loaded"); - if (x->objType == PY4PD_AUDIOINOBJ) { - x->nChs = sp[0]->s_nchans; - x->vectorSize = sp[0]->s_n; - dsp_add(Py4pdLib_AudioINPerform, 3, x, sp[0]->s_vec, PY4PDSIGTOTAL(sp[0])); - } else if (x->objType == PY4PD_AUDIOOUTOBJ) { - x->vectorSize = sp[0]->s_n; - signal_setmultiout(&sp[0], x->nChs); - dsp_add(Py4pdLib_AudioOUTPerform, 3, x, sp[0]->s_vec, PY4PDSIGTOTAL(sp[0])); - } else if (x->objType == PY4PD_AUDIOOBJ) { - x->nChs = sp[0]->s_nchans; - x->vectorSize = sp[0]->s_n; - signal_setmultiout(&sp[1], sp[0]->s_nchans); - dsp_add(Py4pdLib_AudioPerform, 4, x, sp[0]->s_vec, sp[1]->s_vec, - PY4PDSIGTOTAL(sp[0])); - } + if (_import_array() != 0) { + x->numpyImported = 0; + pd_error(x, "[py4pd] Failed to import numpy"); } else { - x->numpyImported = 0; - pd_error(NULL, "[%s] Numpy was not imported!", x->objName->s_name); - } + x->numpyImported = 1; + } + + if (x->objType == PY4PD_AUDIOINOBJ) { + x->nChs = sp[0]->s_nchans; + x->vectorSize = sp[0]->s_n; + dsp_add(Py4pdLib_AudioINPerform, 3, x, sp[0]->s_vec, PY4PDSIGTOTAL(sp[0])); + } else if (x->objType == PY4PD_AUDIOOUTOBJ) { + x->vectorSize = sp[0]->s_n; + signal_setmultiout(&sp[0], x->nChs); + dsp_add(Py4pdLib_AudioOUTPerform, 3, x, sp[0]->s_vec, PY4PDSIGTOTAL(sp[0])); + } else if (x->objType == PY4PD_AUDIOOBJ) { + x->nChs = sp[0]->s_nchans; + x->vectorSize = sp[0]->s_n; + signal_setmultiout(&sp[1], sp[0]->s_nchans); + dsp_add(Py4pdLib_AudioPerform, 4, x, sp[0]->s_vec, sp[1]->s_vec, + PY4PDSIGTOTAL(sp[0])); + } } // ================ diff --git a/src/pic.c b/src/pic.c index 30ffcd50..0b2195c2 100755 --- a/src/pic.c +++ b/src/pic.c @@ -1,3 +1,4 @@ +#define NO_IMPORT_ARRAY #include "py4pd.h" t_widgetbehavior py4pd_widgetbehavior; @@ -168,8 +169,8 @@ void Py4pdPic_Draw(t_py *x, struct _glist *glist, t_floatarg vis) { sys_vgui("if { [info exists %lx_picname] == 1 } {.x%lx.c create " "rectangle %d %d %d %d -tags %lx_outline -outline black " "-width %d}\n", - x->picFilePath, cv, xpos, ypos, xpos + x->width, ypos + x->height, - x, x->zoom); + x->picFilePath, cv, xpos, ypos, xpos + x->width, + ypos + x->height, x, x->zoom); } } sys_vgui(".x%lx.c create rectangle %d %d %d %d -tags %lx_outline -outline " @@ -328,7 +329,8 @@ void Py4pdPic_Free(t_py *x) { // delete if variable is unset and image is unused // ===================================== void Py4pdPic_PicDefinition(t_py *x) { x->visMode = 1; - sys_vgui("image create photo PY4PD_IMAGE_{%p} -data {%s} \n", x, x->imageBase64); + sys_vgui("image create photo PY4PD_IMAGE_{%p} -data {%s} \n", x, + x->imageBase64); sys_vgui("if {[catch {pd}]} {\n"); sys_vgui("}\n"); diff --git a/src/player.c b/src/player.c index 25886456..c3c245e4 100755 --- a/src/player.c +++ b/src/player.c @@ -1,3 +1,4 @@ +#define NO_IMPORT_ARRAY #include "py4pd.h" // ====================================================== diff --git a/src/py4pd.c b/src/py4pd.c index cc404c93..88c9aced 100644 --- a/src/py4pd.c +++ b/src/py4pd.c @@ -1,3 +1,4 @@ +#define PY_ARRAY_UNIQUE_SYMBOL py4pd_ARRAY_API #include "py4pd.h" // ============================================ @@ -400,6 +401,7 @@ static void Py4pd_PipInstall(t_py *x, t_symbol *s, int argc, t_atom *argv) { Py_DECREF(pValue); Py_DECREF(pipInstallFunction); Py_DECREF(py4pdModule); + outlet_bang(x->mainOut); return; } @@ -1026,19 +1028,21 @@ void *Py4pd_Py4pdNew(t_symbol *s, int argc, t_atom *argv) { x->pdPatchPath = patch_dir; // set name of the home path x->pkgPath = patch_dir; // set name of the packages path x->pArgsCount = 0; - Py4pdUtils_SetObjConfig(x); + Py4pdUtils_SetObjConfig(x); if (object_count == 0) { Py4pdUtils_AddPathsToPythonPath(x); } - return x; - if (argc > 1) { // check if there are two arguments - if (_import_array() < 0) { - pd_error(NULL, "\n!!!!!!\n [py4pd] Unable to import NumPy! Send [pipinstall global numpy] to py4pd object to install it.] \n!!!!!!\n"); - return (x); + if (_import_array() != 0) { + pd_error(NULL, + "\n!!!!!!\n [py4pd] Unable to import NumPy! Send [pipinstall " + "global numpy] to py4pd object to install it.] \n!!!!!!\n"); + x->numpyImported = 0; + return (x); } Py4pd_SetFunction(x, s, argc, argv); + x->numpyImported = 1; } object_count++; return (x); @@ -1053,11 +1057,15 @@ void *Py4pd_Py4pdNew(t_symbol *s, int argc, t_atom *argv) { Py4pdUtils_SetObjConfig(x); if (object_count == 0) { Py4pdUtils_AddPathsToPythonPath(x); - if (_import_array() < 0) { - pd_error(NULL, "\n!!!!!!\n [py4pd] Unable to import NumPy! Send [pipinstall global numpy] to py4pd object to install it.] \n!!!!!!\n"); - return NULL; + if (_import_array() != 0) { + pd_error(NULL, + "\n!!!!!!\n [py4pd] Unable to import NumPy! Send [pipinstall " + "global numpy] to py4pd object to install it.] \n!!!!!!\n"); + x->numpyImported = 0; + return NULL; } } + x->numpyImported = 1; int libraryLoaded = Py4pd_LibraryLoad(x, argc, argv); if (libraryLoaded == -1) { return NULL; @@ -1082,8 +1090,8 @@ void py4pd_setup(void) { object_count = 0; post(""); post("[py4pd] by Charles K. Neimog"); - post("[py4pd] Version %d.%d.%d", PY4PD_MAJOR_VERSION, - PY4PD_MINOR_VERSION, PY4PD_MICRO_VERSION); + post("[py4pd] Version %d.%d.%d", PY4PD_MAJOR_VERSION, PY4PD_MINOR_VERSION, + PY4PD_MICRO_VERSION); post("[py4pd] Python version %d.%d.%d", PY_MAJOR_VERSION, PY_MINOR_VERSION, PY_MICRO_VERSION); post(""); @@ -1104,7 +1112,6 @@ void py4pd_setup(void) { (t_method)Py4pdLib_FreeObj, sizeof(t_py), CLASS_NOINLET, A_GIMME, 0); - // this is like have lot of objects with the same name, add all methods for class_addmethod(py4pd_class, (t_method)Py4pd_SetPy4pdHomePath, gensym("home"), A_GIMME, 0); // set home path @@ -1146,6 +1153,4 @@ void py4pd_setup(void) { class_addmethod(py4pd_class, (t_method)Py4pdUtils_CreatePythonInterpreter, gensym("detach"), 0); #endif - - } diff --git a/src/py4pd.h b/src/py4pd.h index ac4285f2..b790415b 100755 --- a/src/py4pd.h +++ b/src/py4pd.h @@ -11,7 +11,6 @@ #define PY_SSIZE_T_CLEAN // Remove deprecated functions #include -#define PY_ARRAY_UNIQUE_SYMBOL PY4PD_ARRAY_API #define NPY_NO_DEPRECATED_API NPY_1_25_API_VERSION #include @@ -36,7 +35,7 @@ #define PY4PD_MAJOR_VERSION 0 #define PY4PD_MINOR_VERSION 8 -#define PY4PD_MICRO_VERSION 2 +#define PY4PD_MICRO_VERSION 3 #define PYTHON_REQUIRED_VERSION(major, minor) ((major < PY_MAJOR_VERSION) || (major == PY_MAJOR_VERSION && minor <= PY_MINOR_VERSION)) diff --git a/src/utils.c b/src/utils.c index 89752ea5..573115f5 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1,3 +1,4 @@ +#define NO_IMPORT_ARRAY #include "py4pd.h" // ==================================================== @@ -1472,7 +1473,7 @@ void Py4pdUtils_AddPathsToPythonPath(t_py *x) { * @return It will return NULL. */ int Py4pd_ImportNumpyForPy4pd() { - //import_array(); + // import_array(); return 1; } From ba4c6c95fa4f15a41f8159e0856c8324dc04184f Mon Sep 17 00:00:00 2001 From: "Charles K. Neimog" Date: Fri, 1 Dec 2023 10:37:43 -0300 Subject: [PATCH 12/21] add test to check if object can be deleted --- test/06-Objects.pd | 1 - ...ryLeaks_JUSTLINUX.pd => 07-MemoryLeaks.pd} | 0 test/09-List.pd | 18 +- test/10-DeleteObj.pd | 80 ++++++++ test/10-deleteObj.pd | 9 - test/py4pd-help.pd | 171 ------------------ 6 files changed, 89 insertions(+), 190 deletions(-) rename test/{07-MemoryLeaks_JUSTLINUX.pd => 07-MemoryLeaks.pd} (100%) create mode 100755 test/10-DeleteObj.pd delete mode 100755 test/10-deleteObj.pd delete mode 100644 test/py4pd-help.pd diff --git a/test/06-Objects.pd b/test/06-Objects.pd index bc01290f..107f12d0 100755 --- a/test/06-Objects.pd +++ b/test/06-Objects.pd @@ -90,7 +90,6 @@ #X connect 11 0 18 0; #X connect 11 0 27 0; #X connect 15 0 14 0; -#X connect 16 0 13 0; #X connect 16 1 15 0; #X connect 17 0 28 1; #X connect 18 0 28 1; diff --git a/test/07-MemoryLeaks_JUSTLINUX.pd b/test/07-MemoryLeaks.pd similarity index 100% rename from test/07-MemoryLeaks_JUSTLINUX.pd rename to test/07-MemoryLeaks.pd diff --git a/test/09-List.pd b/test/09-List.pd index c90bfea1..c5b1a5db 100755 --- a/test/09-List.pd +++ b/test/09-List.pd @@ -7,23 +7,23 @@ #X obj 43 279 print PASS; #X msg 5 343 \; pd quit; #X obj 5 238 t b f; -#X obj 135 95 py4pd py sumlist; #X obj 135 34 t b; #X msg 87 62 reload; #X msg 135 63 run [1 2 3 4 5 6] 20; #X obj 135 116 py.nth None 5; #X obj 135 137 py2pd; #X obj 135 158 expr if($f1 == 120 \, 1 \, 0); -#X connect 1 0 9 0; -#X connect 2 0 9 0; +#X obj 135 95 py4pd py sumlist; +#X connect 1 0 8 0; +#X connect 2 0 8 0; #X connect 3 0 4 0; #X connect 3 1 5 0; #X connect 7 0 6 0; #X connect 7 1 3 0; -#X connect 8 0 12 0; -#X connect 9 0 11 0; -#X connect 10 0 8 0; -#X connect 11 0 8 0; +#X connect 8 0 10 0; +#X connect 9 0 14 0; +#X connect 10 0 14 0; +#X connect 11 0 12 0; #X connect 12 0 13 0; -#X connect 13 0 14 0; -#X connect 14 0 7 0; +#X connect 13 0 7 0; +#X connect 14 0 11 0; diff --git a/test/10-DeleteObj.pd b/test/10-DeleteObj.pd new file mode 100755 index 00000000..c952d9e9 --- /dev/null +++ b/test/10-DeleteObj.pd @@ -0,0 +1,80 @@ +#N canvas 686 36 1179 936 8; +#X obj 5 5 py4pd -lib py4pd; +#X obj 135 10 r start-test; +#X obj 218 12 bng 15 250 50 0 empty empty empty 0 -8 0 8 #fcfcfc #000000 #000000; +#X obj 27 258 sel 0 1; +#X obj 27 298 print ERROR; +#X obj 43 279 print PASS; +#X msg 5 343 \; pd quit; +#X obj 5 238 t b f; +#X obj 135 34 t b; +#X obj 402 106 s pd-10-DeleteObj.pd; +#X msg 402 81 obj 430 5 pd new_patch \, editmode 1; +#X obj 268 182 s pd-new_patch; +#N canvas 221 259 956 687 create 0; +#X obj 11 6 inlet; +#X msg 124 69 msg 10 10 bang; +#X msg 119 117 obj 10 40 metro 500; +#X msg 119 93 floatatom 100 10 7 50 5000; +#X msg 119 189 obj 10 70 random 127; +#X msg 119 237 floatatom 10 100 5; +#X msg 119 141 connect 0 0 2 0; +#X msg 119 165 connect 1 0 2 1; +#X msg 119 213 connect 2 0 3 0; +#X msg 119 261 connect 3 0 4 0; +#X msg 119 285 obj 10 130 py4pd; +#X obj 10 319 outlet; +#X obj 11 27 t b b b b b b b b b b b, f 25; +#X obj 274 48 del 100 1 msec; +#X obj 274 69 t b b; +#X msg 337 116 clear; +#X msg 274 115 vis 0; +#X connect 0 0 12 0; +#X connect 1 0 11 0; +#X connect 2 0 11 0; +#X connect 3 0 11 0; +#X connect 4 0 11 0; +#X connect 5 0 11 0; +#X connect 6 0 11 0; +#X connect 7 0 11 0; +#X connect 8 0 11 0; +#X connect 9 0 11 0; +#X connect 10 0 11 0; +#X connect 12 0 10 0; +#X connect 12 1 9 0; +#X connect 12 2 5 0; +#X connect 12 3 8 0; +#X connect 12 4 4 0; +#X connect 12 5 7 0; +#X connect 12 6 6 0; +#X connect 12 7 2 0; +#X connect 12 8 3 0; +#X connect 12 9 1 0; +#X connect 12 10 13 0; +#X connect 13 0 14 0; +#X connect 14 0 16 0; +#X connect 14 1 15 0; +#X connect 15 0 11 0; +#X connect 16 0 11 0; +#X restore 268 131 pd create obj; +#X obj 135 113 del 1 1 sec; +#X obj 268 91 del 300 1 msec; +#X obj 135 55 t b b b, f 54; +#N canvas 130 168 450 300 new_patch 0; +#X restore 430 5 pd new_patch; +#X msg 135 160 1; +#X connect 1 0 8 0; +#X connect 2 0 8 0; +#X connect 3 0 4 0; +#X connect 3 1 5 0; +#X connect 7 0 6 0; +#X connect 7 1 3 0; +#X connect 8 0 15 0; +#X connect 10 0 9 0; +#X connect 12 0 11 0; +#X connect 13 0 17 0; +#X connect 14 0 12 0; +#X connect 15 0 13 0; +#X connect 15 1 14 0; +#X connect 15 2 10 0; +#X connect 17 0 7 0; diff --git a/test/10-deleteObj.pd b/test/10-deleteObj.pd deleted file mode 100755 index aec16b07..00000000 --- a/test/10-deleteObj.pd +++ /dev/null @@ -1,9 +0,0 @@ -#N canvas 667 314 523 414 8; -#X obj 3 5 namecanvas py4pd-test; -#X obj 135 10 r start-test; -#X obj 218 12 bng 15 250 50 0 empty empty empty 0 -8 0 8 #fcfcfc #000000 #000000; -#X obj 135 95 py4pd py sumlist; -#X msg 135 63 run [1 2 3 4 5 6] 20; -#N canvas 30 68 450 300 (subpatch) 0; -#X restore 56 83 pd; -#X connect 4 0 3 0; diff --git a/test/py4pd-help.pd b/test/py4pd-help.pd deleted file mode 100644 index dd963834..00000000 --- a/test/py4pd-help.pd +++ /dev/null @@ -1,171 +0,0 @@ -#N canvas 545 31 1202 670 8; -#X msg 25 46 doc, f 6; -#N canvas 282 97 1541 723 embedded 0; -#X obj 9 192 py4pd; -#X msg 47 128 run; -#X msg 52 147 reload; -#X msg 9 216 set \$1; -#X msg 9 240 9; -#X text 6 298 With this you can send values for pd without return \, this seems to be very useful for work with list of lists. And other things; -#X obj 8 269 print pd.out; -#X msg 413 141 run; -#N canvas 0 50 450 250 (subpatch) 0; -#X array test 79 float 3; -#A 0 -0.55 0.24 0.78 0.79 -0.45 -0.82 0.81 -0.23 -0.89 -0.44 0.15 0.41 0.06 0.75 0.41 0.37 0.22 -0.5 -0.2 0.97 0.09 0 -0.46 -0.59 0.93 0.12 0.36 -0.43 -0.2 -0.5 0.03 -0.15 0.96 -0.4 0.95 -0.34 -0.16 0.93 -0.9 0.94 0.65 0.06 0.8 0.82 0 0.98 0.08 -0.04 0.13 -0.76 0.97 0.41 -0.17 -0.27 0.58 -0.37 -0.58 -0.33 0.89 0.03 -0.55 0.5 0.88 -0.25 -0.47 0.16 -0.17 0.39 -0.9 -0.36 -0.67 0.04 -0.22 0.1 0.06 -0.11 -0.55 0.46 0.77; -#X coords 0 1 79 -1 200 140 1; -#X restore 561 12 graph; -#X obj 413 92 tgl 20 0 empty empty empty 0 -10 0 12 #fcfcfc #000000 #000000 0 1; -#X obj 103 244 r py4pdreceiver; -#X obj 103 269 print py4pdreceiver; -#X msg 55 166 editor nvim; -#X msg 32 57 pd_print; -#X msg 9 8 pd_output; -#X msg 9 101 set py \$1; -#X obj 9 79 list; -#X msg 19 35 pd_tempfolder; -#X text 79 6 <= Select the Function to run.; -#X text 67 127 <= Run the Function.; -#X text 94 146 <= Reload the function if you edit it!; -#X text 119 165 <= Set your code editor.; -#X text 445 192 Write of read things in pd arrays using Python.; -#X text 438 93 <= Turn On; -#X obj 413 118 metro 500; -#X msg 425 168 set py pd_tabwrite; -#X obj 413 192 py4pd; -#X text 523 168 <= 1 Set the function; -#X connect 0 0 3 0; -#X connect 0 0 6 0; -#X connect 1 0 0 0; -#X connect 2 0 0 0; -#X connect 3 0 4 0; -#X connect 7 0 26 0; -#X connect 9 0 24 0; -#X connect 10 0 11 0; -#X connect 12 0 0 0; -#X connect 13 0 16 0; -#X connect 14 0 16 0; -#X connect 15 0 0 0; -#X connect 16 0 15 0; -#X connect 17 0 16 0; -#X connect 24 0 7 0; -#X connect 25 0 26 0; -#X restore 14 210 pd embedded module; -#N canvas 0 50 450 250 (subpatch) 0; -#X text 0 0 plugdatainfo ; -#X coords 0 1 100 -1 1 1 1; -#X restore 0 0 graph; -#X obj 13 160 print py4pd; -#N canvas 474 132 1263 711 code 0; -#X obj 7 305 s py4pd; -#X msg 74 154 reload, f 8; -#N canvas 0 50 450 250 (subpatch) 0; -#X text 0 0 plugdatainfo ; -#X coords 0 1 100 -1 1 1 1; -#X restore 0 0 graph; -#X msg 7 28 editor nvim; -#X msg 16 54 editor vscode; -#X text 94 28 Set Nvim as py4pd editor.; -#X text 120 54 Set vscode as py4pd editor.; -#X text 136 129 Open editor if it is in the PATH!; -#X text 142 153 Reload the function \, if you change it on script you need to reload!; -#X msg 29 79 editor emacs; -#X text 133 79 Set emacs as py4pd editor.; -#X msg 83 187 open myscript; -#X text 198 182 If you want to create a new script \, you can run open \, if the files already exits it will not replace it \, just open it.; -#X msg 46 104 editor gvim; -#X text 133 104 Set Gvim as py4pd editor.; -#X msg 57 130 editor sublime; -#X connect 1 0 0 0; -#X connect 3 0 0 0; -#X connect 4 0 0 0; -#X connect 9 0 0 0; -#X connect 11 0 0 0; -#X connect 13 0 0 0; -#X connect 15 0 0 0; -#X restore 15 187 pd code; -#X text 70 186 Code python with IDE.; -#X msg 13 17 set py pdsum; -#X msg 38 69 run 1 10, f 10; -#X obj 296 75 bng 20 250 50 0 mac empty empty 0 -10 0 12 #fcfcfc #000000 #000000; -#X obj 296 100 bng 20 250 50 0 windows empty empty 0 -10 0 12 #fcfcfc #000000 #000000; -#X obj 296 125 bng 20 250 50 0 linux empty empty 0 -10 0 12 #fcfcfc #000000 #000000; -#N canvas 0 0 1375 516 (subpatch) 0; -#X obj 47 189 pdcontrol; -#X obj 36 8 r mac; -#X obj 120 8 r windows; -#X obj 228 9 r linux; -#X msg 229 97 browse https://github.com/charlesneimog/py4pd/wiki/Install-Python; -#X msg 36 58 browse https://www.python.org/downloads/release/python-3114; -#X connect 1 0 5 0; -#X connect 2 0 5 0; -#X connect 3 0 4 0; -#X connect 4 0 0 0; -#X connect 5 0 0 0; -#X restore 295 25 pd; -#X text 320 74 Click on this bang for MacOS | Bottom of page; -#X text 320 101 Click on this bang for Windows | Bottom of page; -#X text 320 124 Click on this bang for Linux; -#N canvas 861 110 853 614 Libraries 1; -#X text 83 6 <= Load the library of the script called py.py. The script must be located in the patch folder or inside one folder with same of the script that are in the PureData Search patches. For example \, If I have ~/Documents/Pd/pd-ji/pd-ji.py you can use py4pd -lib pd-ji to load the library., f 66; -#X text 96 118 Freqs in Hz.; -#X obj 15 281 dac~; -#X text 104 222 The Python objects do not support Help Patches yet! But inside both libraries there is some Helps patches.; -#X text 103 177 I already upload some Python Libraries for py4pd. Go to Help->Find Externals then "pd-ji" or "orchidea".; -#X obj 10 7 py4pd -lib py; -#X msg 92 157 0.1 0.1 0.1 0.1; -#X obj 15 202 snake~ out 4; -#X obj 15 181 sinusoids~ -ch 4; -#X text 263 97 !!! Start dsp!; -#X msg 247 127 \; pd dsp \$1; -#X obj 247 97 tgl 15 0 empty empty empty 0 -8 0 8 #fcfcfc #ff0400 #000000 0 1; -#X msg 15 118 200 230 310 370; -#X connect 6 0 8 1; -#X connect 7 0 2 0; -#X connect 7 1 2 1; -#X connect 7 2 2 0; -#X connect 7 3 2 1; -#X connect 8 0 7 0; -#X connect 11 0 10 0; -#X connect 12 0 8 0; -#X restore 14 233 pd Libraries; -#X text 80 232 <= Share your Python code as pd objects.; -#X text 109 210 <= Interact with Python from PureData.; -#X text 42 116 <= Click to open the script.; -#X obj 13 138 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -8 0 8 #fcfcfc #000000 #000000 0 256; -#X text 93 69 3 Run the function.; -#X text 64 46 2 See the docs of the function.; -#X text 82 16 1 Load function.; -#X obj 53 91 r py4pd; -#X msg 295 47 YOU NEED TO INSTALL PYTHON3.11; -#N canvas 240 118 658 503 py4pd 0; -#X obj 10 4 py4pd -lib py4pd; -#X obj 10 29 py&& -a 3; -#X obj 10 49 py|| -a 3; -#X obj 9 212 py.list -a 3; -#X obj 10 192 py.nth; -#X obj 10 173 py.len; -#X obj 10 155 py.mklist; -#X obj 10 124 pd2py; -#X obj 10 103 py2pd; -#X obj 10 83 py.print; -#X obj 99 30 py+; -#X obj 99 48 py-; -#X obj 99 67 py*; -#X obj 99 87 py/; -#X obj 225 32 py.iterate; -#X obj 225 53 py.collect; -#X obj 295 31 f2mc; -#X obj 295 51 mc2f; -#X obj 295 71 mc2n; -#X restore 13 261 pd py4pd extra objects; -#X text 131 260 py4pd come with object to use pip inside pd \, and minor others things. Check the subpatch.; -#X obj 13 117 py4pd; -#N canvas 538 32 988 300 MAINFUNCTIONS 0; -#X restore 74 160 pd MAINFUNCTIONS; -#X text 292 160 IMPORTANT: This is just simple example \, the exaustive Docs are avaible in www.charlesneimog.github.io/py4pd; -#X connect 0 0 27 0; -#X connect 6 0 27 0; -#X connect 7 0 27 0; -#X connect 19 0 3 0; -#X connect 23 0 27 0; -#X connect 27 0 19 0; From 6aae423ea55fe1ff80034f89c86b38e06f597389 Mon Sep 17 00:00:00 2001 From: "Charles K. Neimog" Date: Fri, 1 Dec 2023 10:55:16 -0300 Subject: [PATCH 13/21] remove unnecessary print --- src/ext-libraries.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ext-libraries.c b/src/ext-libraries.c index f4ae8816..781e8d07 100644 --- a/src/ext-libraries.c +++ b/src/ext-libraries.c @@ -1125,6 +1125,5 @@ PyObject *Py4pdLib_AddObj(PyObject *self, PyObject *args, PyObject *keywords) { post("[py4pd]: Object {%s} added to PureData", objectName); } class_set_extern_dir(&s_); - post("Object {%s} added to PureData", objectName); Py_RETURN_TRUE; } From 84d85b30d3f78b3ba109e955f76ca1b63545439d Mon Sep 17 00:00:00 2001 From: "Charles K. Neimog" Date: Fri, 1 Dec 2023 10:55:35 -0300 Subject: [PATCH 14/21] add new bug --- test/not-work-yet/11-Loop-inside-Loop.pd | 54 ++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100755 test/not-work-yet/11-Loop-inside-Loop.pd diff --git a/test/not-work-yet/11-Loop-inside-Loop.pd b/test/not-work-yet/11-Loop-inside-Loop.pd new file mode 100755 index 00000000..cc4016d3 --- /dev/null +++ b/test/not-work-yet/11-Loop-inside-Loop.pd @@ -0,0 +1,54 @@ +#N canvas 686 36 1179 936 8; +#X obj 5 5 py4pd -lib py4pd; +#X obj 135 10 r start-test; +#X obj 218 12 bng 15 250 50 0 empty empty empty 0 -8 0 8 #fcfcfc #000000 #000000; +#X obj 27 258 sel 0 1; +#X obj 27 298 print ERROR; +#X obj 43 279 print PASS; +#X msg 5 343 \; pd quit; +#X obj 5 238 t b f; +#N canvas 130 168 450 300 new_patch 0; +#X restore 430 5 pd new_patch; +#X obj 135 107 py.iterate; +#N canvas 69 374 626 577 loop 1; +#X obj 8 5 inlet; +#X obj 8 27 py.iterate, f 22; +#X obj 115 224 py.collect; +#X obj 115 255 outlet; +#X obj 8 92 py* None 20; +#X obj 8 48 t a a; +#X obj 72 92 py* None 50; +#X obj 8 113 py.list -a 2; +#X connect 0 0 1 0; +#X connect 1 0 5 0; +#X connect 1 1 2 0; +#X connect 2 0 3 0; +#X connect 4 0 7 0; +#X connect 5 0 4 0; +#X connect 5 1 6 0; +#X connect 6 0 7 1; +#X connect 7 0 2 0; +#X restore 135 143 pd loop; +#X obj 182 178 py.collect; +#X obj 182 199 py.print; +#X obj 135 82 py.list -a 2; +#X msg 135 61 1 2 3 4 5; +#X obj 135 34 t b b; +#X msg 192 61 6 7 8 9 10; +#X obj 86 144 py.print; +#X connect 1 0 15 0; +#X connect 2 0 15 0; +#X connect 3 0 4 0; +#X connect 3 1 5 0; +#X connect 7 0 6 0; +#X connect 7 1 3 0; +#X connect 9 0 10 0; +#X connect 9 0 17 0; +#X connect 9 1 11 0; +#X connect 10 0 11 0; +#X connect 11 0 12 0; +#X connect 13 0 9 0; +#X connect 14 0 13 0; +#X connect 15 0 14 0; +#X connect 15 1 16 0; +#X connect 16 0 13 1; From 8946f21019b149e41697058a3b11e2afb201a50e Mon Sep 17 00:00:00 2001 From: "Charles K. Neimog" Date: Fri, 1 Dec 2023 14:26:25 -0300 Subject: [PATCH 15/21] closes #71 fix loop inside loops and change how the Fake hash table works for now --- src/module.c | 35 +++++--- src/py4pd.h | 1 + test/11-Loop-inside-Loop.pd | 102 +++++++++++++++++++++++ test/not-work-yet/11-Loop-inside-Loop.pd | 54 ------------ 4 files changed, 127 insertions(+), 65 deletions(-) create mode 100755 test/11-Loop-inside-Loop.pd delete mode 100755 test/not-work-yet/11-Loop-inside-Loop.pd diff --git a/src/module.c b/src/module.c index ce3078b8..a03a9420 100755 --- a/src/module.c +++ b/src/module.c @@ -24,12 +24,25 @@ static pdcollectHash *Py4pdMod_CreatePdcollectHash(int size) { static unsigned int Py4pdMod_HashFunction(pdcollectHash *hash_table, char *key) { - unsigned long hash = 5381; - int c; - while ((c = *key++)) { - hash = ((hash << 5) + hash) + c; + // FAKE HASH FUNCTION + int keyAlreadyExists = 0; + for (int i = 0; i < hash_table->size; i++) { + if (hash_table->items[i] != NULL) { + if (strcmp(hash_table->items[i]->key, key) == 0) { + keyAlreadyExists = 1; + return i; + } + } + } + if (keyAlreadyExists == 0) { + for (int i = 0; i < hash_table->size; i++) { + if (hash_table->items[i] == NULL) { + return i; + } + } } - return hash % hash_table->size; + PyErr_SetString(PyExc_MemoryError, "[Python] pd.setglobalvar: memory error"); + return 0; } // ================================= @@ -80,6 +93,7 @@ static void Py4pdMod_InsertItem(pdcollectHash *hash_table, char *key, // ================================= static void Py4pdMod_AccumItem(pdcollectHash *hash_table, char *key, PyObject *obj) { + unsigned int index = Py4pdMod_HashFunction(hash_table, key); pdcollectItem *item = hash_table->items[index]; if (item == NULL && hash_table->count <= hash_table->size) { @@ -94,6 +108,8 @@ static void Py4pdMod_AccumItem(pdcollectHash *hash_table, char *key, } if (item->wasCleaned) item->wasCleaned = 0; + + // print str representation of obj PyList_Append(item->pList, obj); return; } @@ -154,8 +170,6 @@ static void Py4pdMod_FreePdcollectItem(pdcollectItem *item) { item->wasCleaned = 1; free(item->key); - // Free the appropriate object, depending on whether it's a single item or a - // list if (item->pList) { Py_DECREF(item->pList); Py4pdUtils_MemLeakCheck(item->pList, 0, "pList"); @@ -253,8 +267,10 @@ static PyObject *Py4pdMod_GetGlobalVar(PyObject *self, PyObject *args, } if (item->aCumulative) { + Py_INCREF(item->pList); return item->pList; } else { + Py_INCREF(item->pItem); return item->pItem; } } @@ -263,14 +279,12 @@ static PyObject *Py4pdMod_GetGlobalVar(PyObject *self, PyObject *args, static PyObject *Py4pdMod_AccumGlobalVar(PyObject *self, PyObject *args) { (void)self; - t_py *x = Py4pdUtils_GetObject(self); if (x == NULL) { PyErr_SetString(PyExc_RuntimeError, "[Python] pd.setglobalvar: py4pd is NULL"); return NULL; } - char *key; char *varName; PyObject *pValueScript; if (!PyArg_ParseTuple(args, "sO", &varName, &pValueScript)) { @@ -278,7 +292,7 @@ static PyObject *Py4pdMod_AccumGlobalVar(PyObject *self, PyObject *args) { "[Python] pd.setglobalvar: wrong arguments"); return NULL; } - key = malloc(strlen(varName) + 40); + char *key = malloc(strlen(varName) + 40); snprintf(key, strlen(varName) + 40, "%s_%p", varName, x); if (x->pdcollect == NULL) { x->pdcollect = Py4pdMod_CreatePdcollectHash(8); @@ -287,7 +301,6 @@ static PyObject *Py4pdMod_AccumGlobalVar(PyObject *self, PyObject *args) { x->pdcollect->items[Py4pdMod_HashFunction(x->pdcollect, key)]->aCumulative = 1; free(key); - Py_RETURN_TRUE; } diff --git a/src/py4pd.h b/src/py4pd.h index b790415b..1de09fd6 100755 --- a/src/py4pd.h +++ b/src/py4pd.h @@ -109,6 +109,7 @@ typedef struct pdcollectItem{ PyObject* pItem; int wasCleaned; int aCumulative; + int id; } pdcollectItem; // ====================================== diff --git a/test/11-Loop-inside-Loop.pd b/test/11-Loop-inside-Loop.pd new file mode 100755 index 00000000..ef1f5504 --- /dev/null +++ b/test/11-Loop-inside-Loop.pd @@ -0,0 +1,102 @@ +#N canvas 597 -23 1179 972 8; +#X obj 5 5 py4pd -lib py4pd; +#X obj 135 10 r start-test; +#X obj 27 315 sel 0 1; +#X obj 27 355 print ERROR; +#X obj 43 336 print PASS; +#X msg 5 453 \; pd quit; +#X obj 5 295 t b f; +#N canvas 130 168 450 300 new_patch 0; +#X restore 430 5 pd new_patch; +#X obj 135 111 py.iterate; +#N canvas 69 374 626 577 loop 0; +#X obj 8 5 inlet; +#X obj 8 27 py.iterate, f 22; +#X obj 115 224 py.collect; +#X obj 115 255 outlet; +#X obj 8 92 py* None 20; +#X connect 0 0 1 0; +#X connect 1 0 4 0; +#X connect 1 1 2 0; +#X connect 2 0 3 0; +#X connect 4 0 2 0; +#X restore 135 132 pd loop; +#X obj 182 149 py.collect; +#X obj 135 82 py.list -a 2; +#X obj 135 35 t b b; +#X msg 192 61 6 7 8 9 10; +#X msg 135 61 1 2 3 4 5; +#X obj 182 170 t a a, f 37; +#X obj 364 191 py.nth None 1; +#X obj 182 191 py.nth None 0; +#X obj 182 212 py.nth None 2; +#X obj 364 213 py.nth None 2; +#X obj 182 233 py2pd; +#X obj 364 234 py2pd; +#X obj 364 255 expr if($f1 == 160 \, 1 \, 0); +#X obj 364 276 tgl 15 0 empty empty empty 0 -8 0 8 #fcfcfc #000000 #000000 0 1; +#X obj 182 275 tgl 15 0 empty empty empty 0 -8 0 8 #fcfcfc #000000 #000000 0 1; +#X obj 182 254 expr if($f1 == 60 \, 1 \, 0); +#X obj 182 296 &&; +#X obj 182 317 sel 1; +#X obj 182 338 int 1; +#X obj 182 359 + 1; +#X obj 182 380 expr if($f1 > 20000 \, 1 \, 0); +#X obj 182 401 sel 1 0, f 29; +#X obj 253 465 s again; +#X obj 206 10 r again; +#X obj 107 13 bng 15 250 50 0 empty empty empty 0 -8 0 8 #fcfcfc #000000 #000000; +#X obj 253 431 del 0 1 msec; +#X obj 75 147 py.memuse; +#X msg 75 119 pd; +#X obj 75 168 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -8 0 8 #fcfcfc #000000 #000000 0 256; +#X obj 10 249 expr if(($f1 - $f2) < 3 \, 1 \, 0); +#X msg 182 422 pd; +#X obj 10 191 py.memuse; +#X obj 10 212 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -8 0 8 #fcfcfc #000000 #000000 0 256; +#X connect 1 0 12 0; +#X connect 1 0 37 0; +#X connect 2 0 3 0; +#X connect 2 1 4 0; +#X connect 6 0 5 0; +#X connect 6 1 2 0; +#X connect 8 0 9 0; +#X connect 8 1 10 0; +#X connect 9 0 10 0; +#X connect 10 0 15 0; +#X connect 11 0 8 0; +#X connect 12 0 14 0; +#X connect 12 1 13 0; +#X connect 13 0 11 1; +#X connect 14 0 11 0; +#X connect 15 0 17 0; +#X connect 15 1 16 0; +#X connect 16 0 19 0; +#X connect 17 0 18 0; +#X connect 18 0 20 0; +#X connect 19 0 21 0; +#X connect 20 0 25 0; +#X connect 21 0 22 0; +#X connect 22 0 23 0; +#X connect 23 0 26 1; +#X connect 24 0 26 0; +#X connect 25 0 24 0; +#X connect 26 0 27 0; +#X connect 27 0 28 0; +#X connect 28 0 29 0; +#X connect 29 0 28 1; +#X connect 29 0 30 0; +#X connect 30 0 31 0; +#X connect 31 0 40 0; +#X connect 31 1 35 0; +#X connect 33 0 12 0; +#X connect 34 0 12 0; +#X connect 34 0 37 0; +#X connect 35 0 32 0; +#X connect 36 0 38 0; +#X connect 37 0 36 0; +#X connect 38 0 39 1; +#X connect 39 0 6 0; +#X connect 40 0 41 0; +#X connect 41 0 42 0; +#X connect 42 0 39 0; diff --git a/test/not-work-yet/11-Loop-inside-Loop.pd b/test/not-work-yet/11-Loop-inside-Loop.pd deleted file mode 100755 index cc4016d3..00000000 --- a/test/not-work-yet/11-Loop-inside-Loop.pd +++ /dev/null @@ -1,54 +0,0 @@ -#N canvas 686 36 1179 936 8; -#X obj 5 5 py4pd -lib py4pd; -#X obj 135 10 r start-test; -#X obj 218 12 bng 15 250 50 0 empty empty empty 0 -8 0 8 #fcfcfc #000000 #000000; -#X obj 27 258 sel 0 1; -#X obj 27 298 print ERROR; -#X obj 43 279 print PASS; -#X msg 5 343 \; pd quit; -#X obj 5 238 t b f; -#N canvas 130 168 450 300 new_patch 0; -#X restore 430 5 pd new_patch; -#X obj 135 107 py.iterate; -#N canvas 69 374 626 577 loop 1; -#X obj 8 5 inlet; -#X obj 8 27 py.iterate, f 22; -#X obj 115 224 py.collect; -#X obj 115 255 outlet; -#X obj 8 92 py* None 20; -#X obj 8 48 t a a; -#X obj 72 92 py* None 50; -#X obj 8 113 py.list -a 2; -#X connect 0 0 1 0; -#X connect 1 0 5 0; -#X connect 1 1 2 0; -#X connect 2 0 3 0; -#X connect 4 0 7 0; -#X connect 5 0 4 0; -#X connect 5 1 6 0; -#X connect 6 0 7 1; -#X connect 7 0 2 0; -#X restore 135 143 pd loop; -#X obj 182 178 py.collect; -#X obj 182 199 py.print; -#X obj 135 82 py.list -a 2; -#X msg 135 61 1 2 3 4 5; -#X obj 135 34 t b b; -#X msg 192 61 6 7 8 9 10; -#X obj 86 144 py.print; -#X connect 1 0 15 0; -#X connect 2 0 15 0; -#X connect 3 0 4 0; -#X connect 3 1 5 0; -#X connect 7 0 6 0; -#X connect 7 1 3 0; -#X connect 9 0 10 0; -#X connect 9 0 17 0; -#X connect 9 1 11 0; -#X connect 10 0 11 0; -#X connect 11 0 12 0; -#X connect 13 0 9 0; -#X connect 14 0 13 0; -#X connect 15 0 14 0; -#X connect 15 1 16 0; -#X connect 16 0 13 1; From 0a8e6db6a943c78c22602882dc5937aa8cac6342 Mon Sep 17 00:00:00 2001 From: "Charles K. Neimog" Date: Fri, 1 Dec 2023 10:37:33 -0800 Subject: [PATCH 16/21] fix mac edtior --- src/py4pd.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/py4pd.h b/src/py4pd.h index 1de09fd6..72c1993a 100755 --- a/src/py4pd.h +++ b/src/py4pd.h @@ -32,7 +32,6 @@ #define PY4PD_AUDIOOUTOBJ 3 #define PY4PD_AUDIOOBJ 4 - #define PY4PD_MAJOR_VERSION 0 #define PY4PD_MINOR_VERSION 8 #define PY4PD_MICRO_VERSION 3 @@ -48,7 +47,11 @@ // DEFINE STANDARD IDE EDITOR #ifndef PY4PD_EDITOR #ifdef _WIN32 + // Windows #define PY4PD_EDITOR "idle3.11" + #elif defined(__APPLE__) || defined(__MACH__) + // macOS + #define PY4PD_EDITOR "/Library/Frameworks/Python.framework/Versions/3.11/bin/python3 -m idlelib.idle" #else #define PY4PD_EDITOR "idle3.11" #endif From d4f4ea352511b82e00294f1e35820e1cf4f1bda1 Mon Sep 17 00:00:00 2001 From: "Charles K. Neimog" Date: Fri, 1 Dec 2023 11:54:18 -0800 Subject: [PATCH 17/21] fix error with there is no extra outlets --- src/ext-libraries.c | 53 +++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/src/ext-libraries.c b/src/ext-libraries.c index 781e8d07..6774d0ee 100644 --- a/src/ext-libraries.c +++ b/src/ext-libraries.c @@ -741,6 +741,19 @@ void *Py4pdLib_NewObj(t_symbol *s, int argc, t_atom *argv) { return NULL; } + if (x->objType > 1) { + int numpyArrayImported = _import_array(); + if (numpyArrayImported == 1) { + x->numpyImported = 1; + logpost(NULL, 3, "Numpy Loaded"); + } else { + x->numpyImported = 0; + pd_error(NULL, "[%s] Numpy was not imported!", objectName); + if (x->objType == PY4PD_AUDIOOUTOBJ) + x->audioError = 1; + } + } + x->pdObjArgs = malloc(sizeof(t_atom) * argc); for (int i = 0; i < argc; i++) { x->pdObjArgs[i] = argv[i]; @@ -777,34 +790,26 @@ void *Py4pdLib_NewObj(t_symbol *s, int argc, t_atom *argv) { Py_DECREF(pd_module); Py_DECREF(py4pd_capsule); return NULL; - } else - AuxOutlet = x->numOutlets; - } - x->outAUX = (py4pdOuts *)getbytes(AuxOutlet * sizeof(*x->outAUX)); - x->outAUX->u_outletNumber = AuxOutlet; - t_atom defarg[AuxOutlet], *ap; - py4pdOuts *u; - int i; - - if (x->objType > 1) { - int numpyArrayImported = Py4pd_ImportNumpyForPy4pd(); - if (numpyArrayImported == 1) { - x->numpyImported = 1; - logpost(NULL, 3, "Numpy Loaded"); } else { - x->numpyImported = 0; - pd_error(NULL, "[%s] Numpy was not imported!", objectName); - Py_DECREF(pd_module); - Py_DECREF(py4pd_capsule); - return NULL; + post("[%s]: Number of outlets set to %d", objectName, x->numOutlets); + AuxOutlet = x->numOutlets; } - if (x->objType == PY4PD_AUDIOOUTOBJ) - x->audioError = 1; } - for (i = 0, u = x->outAUX, ap = defarg; i < AuxOutlet; i++, u++, ap++) { - u->u_outlet = outlet_new(&x->obj, &s_anything); - } + + if (AuxOutlet > 0) { + x->outAUX = (py4pdOuts *)getbytes(AuxOutlet * sizeof(py4pdOuts)); + x->outAUX->u_outletNumber = AuxOutlet; + t_atom defarg[AuxOutlet]; + t_atom *ap; + py4pdOuts *u; + int i; + for (i = 0, u = x->outAUX, ap = defarg; i < AuxOutlet; i++, u++, ap++) { + u->u_outlet = outlet_new(&x->obj, &s_anything); + } + } + + object_count++; // To clear memory when closing the patch Py_DECREF(pd_module); Py_DECREF(py4pd_capsule); From 92aff46795147fd0be620e97d8b2856c3b426985 Mon Sep 17 00:00:00 2001 From: "Charles K. Neimog" Date: Fri, 1 Dec 2023 11:54:51 -0800 Subject: [PATCH 18/21] fix function getMemoryUse for no Linux OSs --- resources/py4pd-mod/src/test.py | 37 +++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/resources/py4pd-mod/src/test.py b/resources/py4pd-mod/src/test.py index 83c3e980..69227e20 100644 --- a/resources/py4pd-mod/src/test.py +++ b/resources/py4pd-mod/src/test.py @@ -1,6 +1,7 @@ import pd import time import subprocess +import platform def py4pdtimer(message): @@ -12,22 +13,26 @@ def py4pdtimer(message): else: pd.error("Bad args to py4pdtimer") - - - - def getMemoryUse(programName): - # execute pidof to get the pid of the program using programName - pid = subprocess.check_output(["pidof", programName]) - # convert the byte string to a string and remove the newline character - pid = pid.decode("utf-8").strip() - if isinstance(pid, list): - pd.error("More than one pid found for " + programName) - return 0 - memoryUse = int(subprocess.check_output(["ps", "-o", "rss=", pid]).strip()) - memoryUse = int(memoryUse / 1024) - # return the memory usage - return memoryUse - + # Check if the platform is Linux + if platform.system() == 'Linux': + try: + # Execute pidof to get the pid of the program using programName + pid = subprocess.check_output(["pidof", programName]) + # Convert the byte string to a string and remove the newline character + pid = pid.decode("utf-8").strip() + if isinstance(pid, list): + pd.error("More than one pid found for " + programName) + return 0 + memoryUse = int(subprocess.check_output(["ps", "-o", "rss=", pid]).strip()) + memoryUse = int(memoryUse / 1024) + # Return the memory usage + return memoryUse + except subprocess.CalledProcessError as e: + pd.error(f"Error retrieving memory usage: {e}") + return 0 + else: + # Return 1 for non-Linux platforms (Mac and Windows) + return 1 From 3e6dffd2c56abea6befb43426d547c563dc85577 Mon Sep 17 00:00:00 2001 From: "Charles K. Neimog" Date: Fri, 1 Dec 2023 12:55:48 -0800 Subject: [PATCH 19/21] fix memuse for mac os --- resources/py4pd-mod/src/test.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/resources/py4pd-mod/src/test.py b/resources/py4pd-mod/src/test.py index 69227e20..b58b9de6 100644 --- a/resources/py4pd-mod/src/test.py +++ b/resources/py4pd-mod/src/test.py @@ -3,7 +3,6 @@ import subprocess import platform - def py4pdtimer(message): if (message == "start"): pd.set_obj_var("py4pd_timer", time.time()) @@ -14,25 +13,29 @@ def py4pdtimer(message): pd.error("Bad args to py4pdtimer") def getMemoryUse(programName): - # Check if the platform is Linux if platform.system() == 'Linux': try: - # Execute pidof to get the pid of the program using programName pid = subprocess.check_output(["pidof", programName]) - # Convert the byte string to a string and remove the newline character pid = pid.decode("utf-8").strip() if isinstance(pid, list): pd.error("More than one pid found for " + programName) return 0 memoryUse = int(subprocess.check_output(["ps", "-o", "rss=", pid]).strip()) memoryUse = int(memoryUse / 1024) - # Return the memory usage return memoryUse except subprocess.CalledProcessError as e: pd.error(f"Error retrieving memory usage: {e}") return 0 + elif platform.system() == 'Darwin': + try: + command = 'top -l 1 -stats pid,command,cpu,mem | grep Pd' + result = subprocess.check_output(command, shell=True, text=True) + result = result.split()[3] + result = result[:-1] + return result + except subprocess.CalledProcessError: + return None else: - # Return 1 for non-Linux platforms (Mac and Windows) return 1 From a0c5b05ceb32e9acaeef7aa14116537a3e0332c4 Mon Sep 17 00:00:00 2001 From: "Charles K. Neimog" Date: Fri, 1 Dec 2023 12:56:58 -0800 Subject: [PATCH 20/21] remove nogui on macos for test --- test/runTests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/runTests.py b/test/runTests.py index d4264c15..e8bcb372 100755 --- a/test/runTests.py +++ b/test/runTests.py @@ -68,7 +68,7 @@ def runTest(pdpatch): print(f"Patch {pathfile} not found") sys.exit() cmd = ( - '/Applications/Pd-*.app/Contents/Resources/bin/pd -stderr -nogui -send "start-test bang" ' + '/Applications/Pd-*.app/Contents/Resources/bin/pd -stderr -send "start-test bang" ' + pathfile ) try: From 2a2af7c90ae54c67ee226766cf9577029d74663c Mon Sep 17 00:00:00 2001 From: "Charles K. Neimog" Date: Fri, 1 Dec 2023 12:57:42 -0800 Subject: [PATCH 21/21] minor fixes for tests patches --- test/06-Objects.pd | 3 +- test/08-Gui.pd | 4 ++- test/10-DeleteObj.pd | 6 ++-- test/11-Loop-inside-Loop.pd | 72 ++++++++++++++++++------------------- 4 files changed, 42 insertions(+), 43 deletions(-) diff --git a/test/06-Objects.pd b/test/06-Objects.pd index 107f12d0..8fc9b877 100755 --- a/test/06-Objects.pd +++ b/test/06-Objects.pd @@ -1,4 +1,4 @@ -#N canvas 583 -8 740 918 8; +#N canvas 583 25 740 918 8; #X obj 12 7 py4pd -lib multichannel; #X obj 135 7 r start-test; #X msg 211 129 \; pd dsp 1; @@ -90,6 +90,7 @@ #X connect 11 0 18 0; #X connect 11 0 27 0; #X connect 15 0 14 0; +#X connect 16 0 13 0; #X connect 16 1 15 0; #X connect 17 0 28 1; #X connect 18 0 28 1; diff --git a/test/08-Gui.pd b/test/08-Gui.pd index 9aeb49f6..76c43b61 100755 --- a/test/08-Gui.pd +++ b/test/08-Gui.pd @@ -27,6 +27,7 @@ #X obj 45 279 print PASS; #X msg 5 343 \; pd quit; #X obj 5 238 t b f; +#X obj 137 93 del 1 1 sec; #X connect 1 0 16 0; #X connect 2 0 16 0; #X connect 4 0 3 0; @@ -44,7 +45,7 @@ #X connect 14 0 12 0; #X connect 15 0 6 0; #X connect 16 0 20 0; -#X connect 16 1 14 0; +#X connect 16 1 28 0; #X connect 16 2 15 0; #X connect 17 0 22 0; #X connect 18 0 21 0; @@ -56,3 +57,4 @@ #X connect 23 1 25 0; #X connect 27 0 26 0; #X connect 27 1 23 0; +#X connect 28 0 14 0; diff --git a/test/10-DeleteObj.pd b/test/10-DeleteObj.pd index c952d9e9..4c861b72 100755 --- a/test/10-DeleteObj.pd +++ b/test/10-DeleteObj.pd @@ -60,8 +60,6 @@ #X obj 135 113 del 1 1 sec; #X obj 268 91 del 300 1 msec; #X obj 135 55 t b b b, f 54; -#N canvas 130 168 450 300 new_patch 0; -#X restore 430 5 pd new_patch; #X msg 135 160 1; #X connect 1 0 8 0; #X connect 2 0 8 0; @@ -72,9 +70,9 @@ #X connect 8 0 15 0; #X connect 10 0 9 0; #X connect 12 0 11 0; -#X connect 13 0 17 0; +#X connect 13 0 16 0; #X connect 14 0 12 0; #X connect 15 0 13 0; #X connect 15 1 14 0; #X connect 15 2 10 0; -#X connect 17 0 7 0; +#X connect 16 0 7 0; diff --git a/test/11-Loop-inside-Loop.pd b/test/11-Loop-inside-Loop.pd index ef1f5504..f4631bfd 100755 --- a/test/11-Loop-inside-Loop.pd +++ b/test/11-Loop-inside-Loop.pd @@ -1,4 +1,4 @@ -#N canvas 597 -23 1179 972 8; +#N canvas 597 25 1179 954 8; #X obj 5 5 py4pd -lib py4pd; #X obj 135 10 r start-test; #X obj 27 315 sel 0 1; @@ -6,8 +6,6 @@ #X obj 43 336 print PASS; #X msg 5 453 \; pd quit; #X obj 5 295 t b f; -#N canvas 130 168 450 300 new_patch 0; -#X restore 430 5 pd new_patch; #X obj 135 111 py.iterate; #N canvas 69 374 626 577 loop 0; #X obj 8 5 inlet; @@ -54,49 +52,49 @@ #X msg 182 422 pd; #X obj 10 191 py.memuse; #X obj 10 212 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -8 0 8 #fcfcfc #000000 #000000 0 256; -#X connect 1 0 12 0; -#X connect 1 0 37 0; +#X connect 1 0 11 0; +#X connect 1 0 36 0; #X connect 2 0 3 0; #X connect 2 1 4 0; #X connect 6 0 5 0; #X connect 6 1 2 0; +#X connect 7 0 8 0; +#X connect 7 1 9 0; #X connect 8 0 9 0; -#X connect 8 1 10 0; -#X connect 9 0 10 0; -#X connect 10 0 15 0; -#X connect 11 0 8 0; -#X connect 12 0 14 0; -#X connect 12 1 13 0; -#X connect 13 0 11 1; -#X connect 14 0 11 0; -#X connect 15 0 17 0; -#X connect 15 1 16 0; -#X connect 16 0 19 0; -#X connect 17 0 18 0; +#X connect 9 0 14 0; +#X connect 10 0 7 0; +#X connect 11 0 13 0; +#X connect 11 1 12 0; +#X connect 12 0 10 1; +#X connect 13 0 10 0; +#X connect 14 0 16 0; +#X connect 14 1 15 0; +#X connect 15 0 18 0; +#X connect 16 0 17 0; +#X connect 17 0 19 0; #X connect 18 0 20 0; -#X connect 19 0 21 0; -#X connect 20 0 25 0; +#X connect 19 0 24 0; +#X connect 20 0 21 0; #X connect 21 0 22 0; -#X connect 22 0 23 0; -#X connect 23 0 26 1; -#X connect 24 0 26 0; -#X connect 25 0 24 0; +#X connect 22 0 25 1; +#X connect 23 0 25 0; +#X connect 24 0 23 0; +#X connect 25 0 26 0; #X connect 26 0 27 0; #X connect 27 0 28 0; +#X connect 28 0 27 1; #X connect 28 0 29 0; -#X connect 29 0 28 1; #X connect 29 0 30 0; -#X connect 30 0 31 0; -#X connect 31 0 40 0; -#X connect 31 1 35 0; -#X connect 33 0 12 0; -#X connect 34 0 12 0; -#X connect 34 0 37 0; -#X connect 35 0 32 0; -#X connect 36 0 38 0; -#X connect 37 0 36 0; -#X connect 38 0 39 1; -#X connect 39 0 6 0; +#X connect 30 0 39 0; +#X connect 30 1 34 0; +#X connect 32 0 11 0; +#X connect 33 0 11 0; +#X connect 33 0 36 0; +#X connect 34 0 31 0; +#X connect 35 0 37 0; +#X connect 36 0 35 0; +#X connect 37 0 38 1; +#X connect 38 0 6 0; +#X connect 39 0 40 0; #X connect 40 0 41 0; -#X connect 41 0 42 0; -#X connect 42 0 39 0; +#X connect 41 0 38 0;