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 diff --git a/Makefile b/Makefile index d4333cbb..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) @@ -38,7 +39,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/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 72391401..00000000 --- a/py4pd-help.pd +++ /dev/null @@ -1,171 +0,0 @@ -#N canvas 494 62 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 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 text 96 118 Freqs in Hz.; -#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 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/resources/README.deken.pd b/resources/README.deken.pd index 14cd2abd..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 www.charlesneimog.com/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..462c0dfd 100644 --- a/resources/py.py +++ b/resources/py.py @@ -1,15 +1,7 @@ import pd import sys from random import randint - -try: - 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.") - 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/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 85167716..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,99 +5,11 @@ import pd -faulthandler.enable() package = "" 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 - 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 @@ -218,9 +129,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: diff --git a/resources/py4pd-mod/src/test.py b/resources/py4pd-mod/src/test.py index 83c3e980..b58b9de6 100644 --- a/resources/py4pd-mod/src/test.py +++ b/resources/py4pd-mod/src/test.py @@ -1,7 +1,7 @@ import pd import time import subprocess - +import platform def py4pdtimer(message): if (message == "start"): @@ -12,22 +12,30 @@ 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 - + if platform.system() == 'Linux': + try: + pid = subprocess.check_output(["pidof", programName]) + 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 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 diff --git a/src/ext-class.c b/src/ext-class.c new file mode 100644 index 00000000..ed5fae8b --- /dev/null +++ b/src/ext-class.c @@ -0,0 +1,390 @@ +#define NO_IMPORT_ARRAY +#include "py4pd.h" + +// ============================================================ +// ===================== 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..6774d0ee 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 ================ // =========================================== @@ -551,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; @@ -575,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]); @@ -601,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]); @@ -635,6 +634,13 @@ t_int *Py4pdLib_AudioPerform(t_int *w) { // ===================================== static void Py4pdLib_Dsp(t_py *x, t_signal **sp) { + if (_import_array() != 0) { + x->numpyImported = 0; + pd_error(x, "[py4pd] Failed to import numpy"); + } else { + x->numpyImported = 1; + } + if (x->objType == PY4PD_AUDIOINOBJ) { x->nChs = sp[0]->s_nchans; x->vectorSize = sp[0]->s_n; @@ -655,7 +661,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", @@ -735,6 +741,19 @@ static 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]; @@ -771,35 +790,26 @@ static 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 = _import_array(); - if (numpyArrayImported == 0) { - x->numpyImported = 1; - logpost(NULL, 3, "Numpy Loaded"); - } else { - x->numpyImported = 0; - pd_error(x, "[%s] Not possible to import numpy array", 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); @@ -832,7 +842,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 +897,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 +1132,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/module.c b/src/module.c index d0dfe10d..a03a9420 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 // ================================= /** @@ -29,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; + } + } } - return hash % hash_table->size; + if (keyAlreadyExists == 0) { + for (int i = 0; i < hash_table->size; i++) { + if (hash_table->items[i] == NULL) { + return i; + } + } + } + PyErr_SetString(PyExc_MemoryError, "[Python] pd.setglobalvar: memory error"); + return 0; } // ================================= @@ -85,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) { @@ -99,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; } @@ -159,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"); @@ -258,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; } } @@ -268,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)) { @@ -283,16 +292,15 @@ 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); } 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 +364,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 +419,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 +941,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 +1070,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 +1097,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 +1258,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 +1289,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 +1343,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"}, @@ -1518,7 +1404,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..0b2195c2 100755 --- a/src/pic.c +++ b/src/pic.c @@ -1,4 +1,4 @@ -#include "m_pd.h" +#define NO_IMPORT_ARRAY #include "py4pd.h" t_widgetbehavior py4pd_widgetbehavior; @@ -169,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 " @@ -329,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 old mode 100755 new mode 100644 index 55556c90..88c9aced --- a/src/py4pd.c +++ b/src/py4pd.c @@ -1,10 +1,9 @@ +#define PY_ARRAY_UNIQUE_SYMBOL py4pd_ARRAY_API #include "py4pd.h" // ============================================ 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 +316,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 = @@ -397,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; } @@ -616,7 +621,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]; @@ -654,7 +661,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 +912,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 +944,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 +987,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 +1002,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,63 +1013,71 @@ 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); + + if (object_count == 0) { + Py4pdUtils_AddPathsToPythonPath(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"); + x->numpyImported = 0; + return (x); + } + Py4pd_SetFunction(x, s, argc, argv); + x->numpyImported = 1; + } + 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); + 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; } 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 " "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); } // ==================================================== @@ -1109,6 +1086,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 @@ -1117,6 +1107,7 @@ 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); @@ -1162,18 +1153,4 @@ void py4pd_setup(void) { class_addmethod(py4pd_class, (t_method)Py4pdUtils_CreatePythonInterpreter, gensym("detach"), 0); #endif - - // INIT PYTHON - 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(); - } } diff --git a/src/py4pd.h b/src/py4pd.h index dee22f59..72c1993a 100755 --- a/src/py4pd.h +++ b/src/py4pd.h @@ -11,6 +11,10 @@ #define PY_SSIZE_T_CLEAN // Remove deprecated functions #include +#define NPY_NO_DEPRECATED_API NPY_1_25_API_VERSION +#include + + #ifdef __linux__ #define __USE_GNU #endif @@ -28,10 +32,9 @@ #define PY4PD_AUDIOOUTOBJ 3 #define PY4PD_AUDIOOBJ 4 - #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)) @@ -44,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 @@ -105,6 +112,7 @@ typedef struct pdcollectItem{ PyObject* pItem; int wasCleaned; int aCumulative; + int id; } pdcollectItem; // ====================================== @@ -228,7 +236,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); @@ -295,6 +303,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..573115f5 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1,8 +1,6 @@ +#define NO_IMPORT_ARRAY #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 @@ -162,8 +160,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 +191,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 +199,9 @@ void *Py4pdLib_FreeObj(t_py *x) { } free(x->pyObjArgs); } - if (x->pdObjArgs != NULL) { free(x->pdObjArgs); } - return NULL; } @@ -1181,8 +1178,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 +1296,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 +1319,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, @@ -1475,9 +1472,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 ============================== 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/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/06-Objects.pd b/test/06-Objects.pd index bc01290f..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; 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/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/09-List.pd b/test/09-List.pd new file mode 100755 index 00000000..c5b1a5db --- /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 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 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 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 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..4c861b72 --- /dev/null +++ b/test/10-DeleteObj.pd @@ -0,0 +1,78 @@ +#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; +#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 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 16 0 7 0; diff --git a/test/11-Loop-inside-Loop.pd b/test/11-Loop-inside-Loop.pd new file mode 100755 index 00000000..f4631bfd --- /dev/null +++ b/test/11-Loop-inside-Loop.pd @@ -0,0 +1,100 @@ +#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; +#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; +#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 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 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 24 0; +#X connect 20 0 21 0; +#X connect 21 0 22 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 30 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 38 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/runTests.py b/test/runTests.py index 1f6add13..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 -nogui -stderr -send "start-test bang" ' + '/Applications/Pd-*.app/Contents/Resources/bin/pd -stderr -send "start-test bang" ' + pathfile ) try: