espd - tutorial
Hi all! 
I had some time during vacations and I wanted to try running Miller's espd version. Here's a small tutorial.
The development board I bought: ESP32-LyraT > Mouser | Aliexpress

INSTALL ESP-IDF (IoT development framework)
mkdir ~/esp && cd ~/esp
git clone -b v4.4.2 --recursive https://github.com/espressif/esp-idf.git
cd esp-idf
./install.sh esp32
INSTALL ESP-ADF (audio development kit)
cd ~/esp && git clone --recursive https://github.com/espressif/esp-adf.git
SETUP ENV VAR
export ADF_PATH=~/esp/esp-adf && . ~/esp/esp-idf/export.sh
ESPD
download espd: http://msp.ucsd.edu/ideas/espd/
cd espd
git clone https://github.com/pure-data/pure-data.git pd
cd pd
git checkout 05bf346fa32510fd191fe77de24b3ea1c481f5ff
git apply ../patches/*.patch
Edit main/espd.h put your wifi credentials and the IP of the computer that will control espd:
#define CONFIG_ESP_WIFI_SSID "..."
#define CONFIG_ESP_WIFI_PASSWORD "..."
#define CONFIG_ESP_WIFI_SENDADDR "...."
mv sdkconfig.lyrat sdkconfig
idf.py build
idf.py -p /dev/ttyUSB0 flash (hold boot and then press reset on lyrat)
idf.py -p /dev/ttyUSB0 monitor (Ctrl+] to exit)
HOST
- open pd installed on (SENDADDR)
- open test-patch/host-patch.pd
If connected this message (ESPD: sendtcp: waiting for socket) will stop and you will see the mac address in the host patch.
1- click on [send pf begin-new poodle .<
2- click on [line 0, auto< to send the defined patch (esp-patch.pd)
3 - click [send pd end-new<

4 - connect headphone, play with [send f 440< and [send f 660<
Custom patch:
- Use mono [dac~ 1] only
- Add this to your patch:

- in host-patch.pd change [read your-patch< -> [text define patch] redo step 1 to 3

TODO
Would love to play more with the code, right now I am not able to load complex vanilla patch. Also using the AUX (or built-in microphone) would be awesome (but I'm wondering about the round-trip latency (would it be under 15ms)).
Stop Stream of Bangs after threshold
@dkeller Even working with some of the Jazz "greats".... or should that be "grates"?..... I have met almost none that can truly improvise. A huge learned repertoire of perfect riffs seems to inhibit their ability to really innovate.
This could perhaps be a great learning tool.
Pd looks in its standard directory (Pd/bin) and the externals folder (Pd/extra) and the patch folder as it opens a patch. You can add other paths to search in Pd preferences.
If you put an external or an abstraction (like [once]... abstraction because just a patch... not compiled) in the same folder as the patch it will work just with that patch.
If you put it in a sub-folder then in the patch you will have to tell Pd where to look..... so [subfolder/once] in your patch instead of [once]....... or declare the path in the patch by placing an object [declare -path subfolder] in the patch.
If you want to use it everywhere then dump it in the extra folder...... which means you can put it in every patch..... BUT..... when you share the patch you will need to remember to bundle it or the recipient will not be able to create the object.
David.
Controlling Pd from another programme
@Worik Not sure what you are looking for.....
If you want to open a patch from the command line you can open Pd and a patch with a batch file...... in windows it would look something like this....
"C:\Program Files (x86)\pd\bin\pd.com" -path C:\Users\David\Desktop\PDMusic\Minx -path C:\Program_Files_(x86)\pd\extra\readanysf~\ -r 44100 -asio -nomidi -audioindev 20 -audiooutdev 15 -inchannels 32 -outchannels 22 -audiobuf 2 -blocksize 64 -callback -nosleep -open Minx_Run.pd
exit`
which on my system opens Minx_run.Pd and tells Pd where to find it........ \PdMusic\Minx
It also sets a load of command line switches...... to set up audio for this very specific instance (useful!).
There will be similar methods in other os's.
When you want to close the patch (and Pd) you kill the Dos window that the batch file created.
Messages can be sent to Pd to close a specific patch..... but another patch will have to be open (probably the one receiving the message) or Pd will terminate.
It can be done from within the patch but its complicated........ https://forum.pdpatchrepo.info/topic/11710/closing-patches-without-pd-crashing-hopefully-in-an-elegant-way ...... and not easy.
If you want to keep Pd open and control from another program........ then as @seb-harmonik.ar describes........ but the menuclose message might crash Pd if another patch (any patch) is not open........ https://forum.pdpatchrepo.info/topic/4063/close-patch-window-command-object
Anyway, it is cleaner to use a second patch to receive your messages and close the desired patch.
David.
Can't install purr-data on Manjaro
Hey, I'm trying to get purr-data to work on my fresh Manjaro XFCE install, but it does not work. Any help is greatly appreciated!
$ yay purr-data
7 aur/purrdata-faust 0.18-1 (+0 0.00)
Run Faust signal processors in Pd, Purr-Data version
6 aur/purrdata-pure 0.26-1 (+0 0.00)
Loader plugin for the Pure programming language, Purr-Data version
5 aur/purrdata-lv2plugin-git 25.e00f302-1 (+0 0.00)
LV2 plugin host for Pd, Purr-Data version
4 aur/purrdata-mdnsbrowser-git 8.88d2b0c-1 (+0 0.00)
Zeroconf service advertising and discovery for Pd, Purr-Data version
3 aur/purrdata-touchosc-git 44.443c793-1 (+0 0.00)
A TouchOSC MIDI bridge for Pd, Purr-Data version
2 aur/purr-data-git 2.12.0.r4366.6d94e10b-1 (+2 0.00)
Jonathan Wilkes' nw.js variant of Pd-L2Ork (git version)
1 aur/purr-data 2.12.0.r4346.aeb24d89-1 (+6 0.03) (Out-of-date: 2021-06-17)
Jonathan Wilkes' nw.js variant of Pd-L2Ork (git version)
==> Packages to install (eg: 1 2 3, 1-3 or ^4)
==> 2
:: There are 2 providers available for gconf:
:: Repository AUR
1) gconf 2) gconf-gtk2
Enter a number (default=1): ==>
:: Checking for conflicts...
:: Checking for inner conflicts...
[Aur:2] gconf-3.2.6+11+g07808097-10 purr-data-git-2.12.0.r4366.6d94e10b-1
2 gconf (Build Files Exist)
1 purr-data-git
==> Packages to cleanBuild?
==> [N]one [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)
==> a
:: Deleting (1/1): /home/olav/.cache/yay/gconf
:: (1/2) Downloaded PKGBUILD: gconf
:: (2/2) Downloaded PKGBUILD: purr-data-git
2 gconf (Build Files Exist)
1 purr-data-git (Build Files Exist)
==> Diffs to show?
==> [N]one [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)
==>
:: (1/2) Parsing SRCINFO: gconf
:: (2/2) Parsing SRCINFO: purr-data-git
==> Making package: gconf 3.2.6+11+g07808097-10 (Wed 09 Feb 2022 10:23:23 PM CET)
==> Retrieving sources...
-> Cloning gconf git repo...
Cloning into bare repository '/home/olav/.cache/yay/gconf/gconf'...
==> Making package: purr-data-git 2.12.0.r4366.6d94e10b-1 (Wed 09 Feb 2022 10:23:23 PM CET)
==> Retrieving sources...
-> Cloning purr-data-git git repo...
Cloning into bare repository '/home/olav/.cache/yay/purr-data-git/purr-data-git'...
remote: Enumerating objects: 21531, done.
remote: Enumerating objects: 63399, done.
remote: Counting objects: 100% (1852/1852), done.
remote: Compressing objects: 100% (641/641), done.
remote: Total 21531 (delta 0), reused 0 (delta 0), pack-reused 21531
Receiving objects: 100% (21531/21531), 10.88 MiB | 9.46 MiB/s, done.
Resolving deltas: 100% (17681/17681), done.1 KiB | 704.00 KiB/s
-> Found 01_xml-gettext-domain.patch388.01 KiB | 704.00 KiB/s
-> Found gconf-reload.patch
-> Found gconf-merge-schema
-> Found gconfpkg
-> Found gconf-install.hook
-> Found gconf-remove.hook
==> Validating source files with sha256sums...
gconf ... Skipped
01_xml-gettext-domain.patch ... Passed
gconf-reload.patch ... Passed
gconf-merge-schema ... Passed
gconfpkg ... Passed
gconf-install.hook ... Passed
gconf-remove.hook ... Passed
remote: Total 63399 (delta 1106), reused 1786 (delta 1052), pack-reused 61547
Receiving objects: 100% (63399/63399), 177.19 MiB | 5.71 MiB/s, done.
Resolving deltas: 100% (33721/33721), done.
-> Downloading nwjs-sdk-v0.24.4-linux-x64.tar.gz...
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 86.7M 100 86.7M 0 0 3966k 0 0:00:22 0:00:22 --:--:-- 8404k
==> Validating source files with md5sums...
purr-data-git ... Skipped
==> Validating source_x86_64 files with md5sums...
nwjs-sdk-v0.24.4-linux-x64.tar.gz ... Passed
==> Making package: gconf 3.2.6+11+g07808097-10 (Wed 09 Feb 2022 10:24:19 PM CET)
==> Checking runtime dependencies...
==> Checking buildtime dependencies...
==> Retrieving sources...
-> Updating gconf git repo...
Fetching origin
-> Found 01_xml-gettext-domain.patch
-> Found gconf-reload.patch
-> Found gconf-merge-schema
-> Found gconfpkg
-> Found gconf-install.hook
-> Found gconf-remove.hook
==> Validating source files with sha256sums...
gconf ... Skipped
01_xml-gettext-domain.patch ... Passed
gconf-reload.patch ... Passed
gconf-merge-schema ... Passed
gconfpkg ... Passed
gconf-install.hook ... Passed
gconf-remove.hook ... Passed
==> Removing existing $srcdir/ directory...
==> Extracting sources...
-> Creating working copy of gconf git repo...
Cloning into 'gconf'...
done.
Switched to a new branch 'makepkg'
==> Starting prepare()...
/home/olav/.cache/yay/gconf/PKGBUILD: line 30: patch: command not found
==> ERROR: A failure occurred in prepare().
Aborting...
-> error making: gconf
And
yay purr-data
7 aur/purrdata-faust 0.18-1 (+0 0.00)
Run Faust signal processors in Pd, Purr-Data version
6 aur/purrdata-pure 0.26-1 (+0 0.00)
Loader plugin for the Pure programming language, Purr-Data version
5 aur/purrdata-lv2plugin-git 25.e00f302-1 (+0 0.00)
LV2 plugin host for Pd, Purr-Data version
4 aur/purrdata-mdnsbrowser-git 8.88d2b0c-1 (+0 0.00)
Zeroconf service advertising and discovery for Pd, Purr-Data version
3 aur/purrdata-touchosc-git 44.443c793-1 (+0 0.00)
A TouchOSC MIDI bridge for Pd, Purr-Data version
2 aur/purr-data-git 2.12.0.r4366.6d94e10b-1 (+2 0.00)
Jonathan Wilkes' nw.js variant of Pd-L2Ork (git version)
1 aur/purr-data 2.12.0.r4346.aeb24d89-1 (+6 0.03) (Out-of-date: 2021-06-17)
Jonathan Wilkes' nw.js variant of Pd-L2Ork (git version)
==> Packages to install (eg: 1 2 3, 1-3 or ^4)
==> 2
:: There are 2 providers available for gconf:
:: Repository AUR
1) gconf 2) gconf-gtk2
Enter a number (default=1): ==>
:: Checking for conflicts...
:: Checking for inner conflicts...
[Aur:2] gconf-3.2.6+11+g07808097-10 purr-data-git-2.12.0.r4366.6d94e10b-1
2 gconf (Build Files Exist)
1 purr-data-git
==> Packages to cleanBuild?
==> [N]one [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)
==> a
:: Deleting (1/1): /home/olav/.cache/yay/gconf
:: (1/2) Downloaded PKGBUILD: gconf
:: (2/2) Downloaded PKGBUILD: purr-data-git
2 gconf (Build Files Exist)
1 purr-data-git (Build Files Exist)
==> Diffs to show?
==> [N]one [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)
==>
:: (1/2) Parsing SRCINFO: gconf
:: (2/2) Parsing SRCINFO: purr-data-git
==> Making package: gconf 3.2.6+11+g07808097-10 (Wed 09 Feb 2022 10:23:23 PM CET)
==> Retrieving sources...
-> Cloning gconf git repo...
Cloning into bare repository '/home/olav/.cache/yay/gconf/gconf'...
==> Making package: purr-data-git 2.12.0.r4366.6d94e10b-1 (Wed 09 Feb 2022 10:23:23 PM CET)
==> Retrieving sources...
-> Cloning purr-data-git git repo...
Cloning into bare repository '/home/olav/.cache/yay/purr-data-git/purr-data-git'...
remote: Enumerating objects: 21531, done.
remote: Enumerating objects: 63399, done.
remote: Counting objects: 100% (1852/1852), done.
remote: Compressing objects: 100% (641/641), done.
remote: Total 21531 (delta 0), reused 0 (delta 0), pack-reused 21531
Receiving objects: 100% (21531/21531), 10.88 MiB | 9.46 MiB/s, done.
Resolving deltas: 100% (17681/17681), done.1 KiB | 704.00 KiB/s
-> Found 01_xml-gettext-domain.patch388.01 KiB | 704.00 KiB/s
-> Found gconf-reload.patch
-> Found gconf-merge-schema
-> Found gconfpkg
-> Found gconf-install.hook
-> Found gconf-remove.hook
==> Validating source files with sha256sums...
gconf ... Skipped
01_xml-gettext-domain.patch ... Passed
gconf-reload.patch ... Passed
gconf-merge-schema ... Passed
gconfpkg ... Passed
gconf-install.hook ... Passed
gconf-remove.hook ... Passed
remote: Total 63399 (delta 1106), reused 1786 (delta 1052), pack-reused 61547
Receiving objects: 100% (63399/63399), 177.19 MiB | 5.71 MiB/s, done.
Resolving deltas: 100% (33721/33721), done.
-> Downloading nwjs-sdk-v0.24.4-linux-x64.tar.gz...
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 86.7M 100 86.7M 0 0 3966k 0 0:00:22 0:00:22 --:--:-- 8404k
==> Validating source files with md5sums...
purr-data-git ... Skipped
==> Validating source_x86_64 files with md5sums...
nwjs-sdk-v0.24.4-linux-x64.tar.gz ... Passed
==> Making package: gconf 3.2.6+11+g07808097-10 (Wed 09 Feb 2022 10:24:19 PM CET)
==> Checking runtime dependencies...
==> Checking buildtime dependencies...
==> Retrieving sources...
-> Updating gconf git repo...
Fetching origin
-> Found 01_xml-gettext-domain.patch
-> Found gconf-reload.patch
-> Found gconf-merge-schema
-> Found gconfpkg
-> Found gconf-install.hook
-> Found gconf-remove.hook
==> Validating source files with sha256sums...
gconf ... Skipped
01_xml-gettext-domain.patch ... Passed
gconf-reload.patch ... Passed
gconf-merge-schema ... Passed
gconfpkg ... Passed
gconf-install.hook ... Passed
gconf-remove.hook ... Passed
==> Removing existing $srcdir/ directory...
==> Extracting sources...
-> Creating working copy of gconf git repo...
Cloning into 'gconf'...
done.
Switched to a new branch 'makepkg'
==> Starting prepare()...
/home/olav/.cache/yay/gconf/PKGBUILD: line 30: patch: command not found
==> ERROR: A failure occurred in prepare().
Aborting...
-> error making: gconf
[olav@AMD-Computer ~]$ yay purr-data
7 aur/purrdata-faust 0.18-1 (+0 0.00)
Run Faust signal processors in Pd, Purr-Data version
6 aur/purrdata-pure 0.26-1 (+0 0.00)
Loader plugin for the Pure programming language, Purr-Data version
5 aur/purrdata-lv2plugin-git 25.e00f302-1 (+0 0.00)
LV2 plugin host for Pd, Purr-Data version
4 aur/purrdata-mdnsbrowser-git 8.88d2b0c-1 (+0 0.00)
Zeroconf service advertising and discovery for Pd, Purr-Data version
3 aur/purrdata-touchosc-git 44.443c793-1 (+0 0.00)
A TouchOSC MIDI bridge for Pd, Purr-Data version
2 aur/purr-data-git 2.12.0.r4366.6d94e10b-1 (+2 0.00)
Jonathan Wilkes' nw.js variant of Pd-L2Ork (git version)
1 aur/purr-data 2.12.0.r4346.aeb24d89-1 (+6 0.03) (Out-of-date: 2021-06-17)
Jonathan Wilkes' nw.js variant of Pd-L2Ork (git version)
==> Packages to install (eg: 1 2 3, 1-3 or ^4)
==> 2
:: There are 2 providers available for gconf:
:: Repository AUR
1) gconf 2) gconf-gtk2
Enter a number (default=1): ==> 2
:: Checking for conflicts...
:: Checking for inner conflicts...
[Aur:2] gconf-gtk2-3.2.6-5 purr-data-git-2.12.0.r4366.6d94e10b-1
2 gconf-gtk2 (Build Files Exist)
1 purr-data-git (Build Files Exist)
==> Packages to cleanBuild?
==> [N]one [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)
==> a
:: Deleting (1/2): /home/olav/.cache/yay/gconf-gtk2
:: Deleting (2/2): /home/olav/.cache/yay/purr-data-git
:: (1/2) Downloaded PKGBUILD: purr-data-git
:: (2/2) Downloaded PKGBUILD: gconf-gtk2
2 gconf-gtk2 (Build Files Exist)
1 purr-data-git (Build Files Exist)
==> Diffs to show?
==> [N]one [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)
==>
:: (1/2) Parsing SRCINFO: gconf-gtk2
:: (2/2) Parsing SRCINFO: purr-data-git
==> Making package: purr-data-git 2.12.0.r4366.6d94e10b-1 (Wed 09 Feb 2022 10:40:58 PM CET)
==> Retrieving sources...
==> Making package: gconf-gtk2 3.2.6-5 (Wed 09 Feb 2022 10:40:58 PM CET)
==> Retrieving sources...
-> Cloning purr-data-git git repo...
Cloning into bare repository '/home/olav/.cache/yay/purr-data-git/purr-data-git'...
-> Downloading GConf-3.2.6.tar.xz...
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
100 1523k 100 1523k 0 0 1610k 0 --:--:-- --:--:-- --:--:-- 1610k
-> Found gconf-merge-schema
-> Found gconfpkg
-> Found gconf-reload.patch
-> Found gconf-install.hook
-> Found gconf-remove.hook
-> Found 01_xml-gettext-domain.patch
-> Found dbus-dontspew.patch
-> Found gsettings-data-convert-fix-invalid-schema-path.patch
==> Validating source files with sha256sums...
GConf-3.2.6.tar.xz ... Passed
gconf-merge-schema ... Passed
gconfpkg ... Passed
gconf-reload.patch ... Passed
gconf-install.hook ... Passed
gconf-remove.hook ... Passed
01_xml-gettext-domain.patch ... Passed
dbus-dontspew.patch ... Passed
gsettings-data-convert-fix-invalid-schema-path.patch ... Passed
remote: Enumerating objects: 63399, done.
remote: Counting objects: 100% (1852/1852), done.
remote: Compressing objects: 100% (641/641), done.
remote: Total 63399 (delta 1106), reused 1786 (delta 1052), pack-reused 61547
Receiving objects: 100% (63399/63399), 177.19 MiB | 9.76 MiB/s, done.
Resolving deltas: 100% (33721/33721), done.
-> Downloading nwjs-sdk-v0.24.4-linux-x64.tar.gz...
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 86.7M 100 86.7M 0 0 11.7M 0 0:00:07 0:00:07 --:--:-- 14.4M
==> Validating source files with md5sums...
purr-data-git ... Skipped
==> Validating source_x86_64 files with md5sums...
nwjs-sdk-v0.24.4-linux-x64.tar.gz ... Passed
==> Making package: gconf-gtk2 3.2.6-5 (Wed 09 Feb 2022 10:41:26 PM CET)
==> Checking runtime dependencies...
==> Checking buildtime dependencies...
==> Retrieving sources...
-> Found GConf-3.2.6.tar.xz
-> Found gconf-merge-schema
-> Found gconfpkg
-> Found gconf-reload.patch
-> Found gconf-install.hook
-> Found gconf-remove.hook
-> Found 01_xml-gettext-domain.patch
-> Found dbus-dontspew.patch
-> Found gsettings-data-convert-fix-invalid-schema-path.patch
==> Validating source files with sha256sums...
GConf-3.2.6.tar.xz ... Passed
gconf-merge-schema ... Passed
gconfpkg ... Passed
gconf-reload.patch ... Passed
gconf-install.hook ... Passed
gconf-remove.hook ... Passed
01_xml-gettext-domain.patch ... Passed
dbus-dontspew.patch ... Passed
gsettings-data-convert-fix-invalid-schema-path.patch ... Passed
==> Removing existing $srcdir/ directory...
==> Extracting sources...
-> Extracting GConf-3.2.6.tar.xz with bsdtar
==> Starting prepare()...
/home/olav/.cache/yay/gconf-gtk2/PKGBUILD: line 39: patch: command not found
==> ERROR: A failure occurred in prepare().
Aborting...
-> error making: gconf-gtk2
Dynamically patched vslider with local send symbol
@oid This is a problem from my 2nd ever dynamic patch, so I wouldn't try to read deep meaning into my questions
. Like my first dynamic patch, it's just a patch that generates the repetitive, static parts of other patches--a way to reduce the tedium of having to edit multiple send/receive names by hand, that's all. I'm expecting to have to cut and paste from this helper patch into the patch I'm really trying to write, so the value of $0 in the generating patch is irrelevant. Is that reasonable? In a patch that needed its UI to be dynamically generated, your way would make total sense, but I'm def not there yet. Also, regarding your other topic, I think the message that dynamically generates a vslider can itself be static, so I'm not sure that add2 technique is applicable.
@ingox OK, now I'm getting the same results as you, which is odd because the patch I've presented is just a small example from a larger patch that was giving me trouble. After discovering the $\$0 syntax I applied it to that larger patch, saw that it too was working, and then posted this topic. But now my larger patch is broken, which makes me believe that the system is behaving differently since running your patches (and that I wasn't previously hallucinating). But that can't be true, right?
So here's another small patch to demonstrate how my larger patch is (now) broken: dynamicPatchVerticalSlider2.pd

See how \$0 is just generating a 0 in the send symbol? When I save and reload it to test the fader (which doesn't work), the backslash gets dropped from the creation message!

So next I try falling back to the syntax I got working earlier, and the generated vslider send symbol looks good

and on reloading it I can confirm that it is good, but now I can't use my generating patch because $1 is backslash escaped!

Paths and organizing .pd files for use.
@raynovich I use the [declare] object instead of paths, keeps the paths list shorter and more manageable and also gives you a nice list of dependencies right in the patch so you never have to wade through the patch trying to figure out what it needs if you want to share the patch or copy it to a different machine. If you need a sub-folder you just declare the sub-folder. Use absolute paths when you declare, this way you can move the patch and it will still find what it needs. I generally have a sub-patch in each patch for declares and I avoid declaring folders, just declare the abstraction directly so I never have to try and figure out which abstractions a given patch uses, just open the declare sub-patch and it is all there. In my pd folder I have all my current patches, an abstractions folder, a folder for patches I am not currently working on and a folder for reference patches. I do my best to avoid making abstractions which only have use in a single patch, I will figure out how to generalize it to make it more useful for other patches so it can live in my abstraction folder without cluttering it up and helps avoid needing folders for patches. When a patch must have a folder it goes in the pd directory with the patch and I declare it instead of keeping the patch in it's own directory, this is mainly to minimize having to navigate folders as much.
Using [declare] to explicitly declare individual abstractions also makes it easier to clean your abstraction folder, a simple shell script can check for abstractions which are not used by any patch or ones that just get used by a single patch so you can check them out to see if they are worth keeping or should be moved too the folder of that one patch which uses them.
Ofelia - using addons, GL_TEXTURE_3D and binding openGL functions
i think i learned how to integrate addons (not everything is working yet...).
this is what i added to ofxOfeliaPdBindings.h to integrate ofxVolumetrics:
class pdTexture3d
{
public:
pdTexture3d(){};
void allocate(int w, int h, int d, int internalGlDataType)
{
texture3d.allocate(w, h, d, internalGlDataType);
}
void loadData(unsigned char * data, int w, int h, int d, int xOffset, int yOffset, int zOffset, int glFormat)
{
texture3d.loadData(data, w, h, d, xOffset, yOffset, zOffset, glFormat);
}
void loadData(float* data, int w, int h, int d, int xOffset, int yOffset, int zOffset, int glFormat)
{
texture3d.loadData(data, w, h, d, xOffset, yOffset, zOffset, glFormat);
}
void loadData(unsigned short* data, int w, int h, int d, int xOffset, int yOffset, int zOffset, int glFormat)
{
texture3d.loadData(data, w, h, d, xOffset, yOffset, zOffset, glFormat);
}
void loadData(ofPixels & pix, int d, int xOffset, int yOffset, int zOffset)
{
texture3d.loadData(pix, d, xOffset, yOffset, zOffset);
}
void loadData(ofShortPixels & pix, int d, int xOffset, int yOffset, int zOffset)
{
texture3d.loadData(pix, d, xOffset, yOffset, zOffset);
}
void loadData(ofFloatPixels & pix, int d, int xOffset, int yOffset, int zOffset)
{
texture3d.loadData(pix, d, xOffset, yOffset, zOffset);
}
void bind()
{
texture3d.bind();
}
void unbind()
{
texture3d.unbind();
}
void clear()
{
texture3d.clear();
}
ofxTextureData3d getTextureData()
{
return texture3d.getTextureData();
}
private:
ofxTexture3d texture3d;
};
class pdVolumetrics
{
public:
pdVolumetrics(){};
void setup(int w, int h, int d, ofVec3f voxelSize, bool usePowerOfTwoTexSize=false)
{
volumetrics.setup(w, h, d, voxelSize, usePowerOfTwoTexSize);
}
void destroy()
{
volumetrics.destroy();
}
void updateVolumeData(unsigned char * data, int w, int h, int d, int xOffset, int yOffset, int zOffset)
{
volumetrics.updateVolumeData(data, w, h, d, xOffset, yOffset, zOffset);
}
void drawVolume(float x, float y, float z, float size, int zTexOffset)
{
volumetrics.drawVolume(x, y, z, size, zTexOffset);
}
void drawVolume(float x, float y, float z, float w, float h, float d, int zTexOffset)
{
volumetrics.drawVolume(x, y, z, w, h, d, zTexOffset);
}
bool isInitialized()
{
return volumetrics.isInitialized();
}
int getVolumeWidth()
{
return volumetrics.getVolumeWidth();
}
int getVolumeHeight()
{
return volumetrics.getVolumeHeight();
}
int getVolumeDepth()
{
return volumetrics.getVolumeDepth();
}
ofFbo & getFboReference()
{
return volumetrics.getFboReference();
}
int getRenderWidth()
{
return volumetrics.getRenderWidth();
}
int getRenderHeight()
{
return volumetrics.getRenderHeight();
}
float getXyQuality()
{
return volumetrics.getXyQuality();
}
float getZQuality()
{
return volumetrics.getZQuality();
}
float getThreshold()
{
return volumetrics.getThreshold();
}
float getDensity()
{
return volumetrics.getDensity();
}
void setXyQuality(float q)
{
volumetrics.setXyQuality(q);
}
void setZQuality(float q)
{
volumetrics.setZQuality(q);
}
void setThreshold(float t)
{
volumetrics.setThreshold(t);
}
void setDensity(float d)
{
volumetrics.setDensity(d);
}
void setRenderSettings(float xyQuality, float zQuality, float dens, float thresh)
{
volumetrics.setRenderSettings(xyQuality, zQuality, dens, thresh);
}
void setVolumeTextureFilterMode(GLint filterMode)
{
volumetrics.setVolumeTextureFilterMode(filterMode);
}
private:
ofxVolumetrics volumetrics;
};
class pdImageSequencePlayer
{
public:
pdImageSequencePlayer(){};
void init(std::string prefix, int digits, std::string extension, int start)
{
imageSequencePlayer.init(prefix, digits, extension, start);
}
int getWidth()
{
return imageSequencePlayer.getWidth();
}
int getHeight()
{
return imageSequencePlayer.getHeight();
}
ofPixels_<unsigned char> getPixels()
{
return imageSequencePlayer.getPixels();
}
int getSequenceLength()
{
return imageSequencePlayer.getSequenceLength();
}
bool loadNextFrame()
{
return imageSequencePlayer.loadNextFrame();
}
bool loadPreviousFrame()
{
return imageSequencePlayer.loadPreviousFrame();
}
bool loadFrame(int n)
{
return imageSequencePlayer.loadFrame(n);
}
int getCurrentFrameNumber()
{
return imageSequencePlayer.getCurrentFrameNumber();
}
void setCurrentFrameNumber(int i)
{
imageSequencePlayer.setCurrentFrameNumber(i);
}
bool isInitialized()
{
return imageSequencePlayer.isInitialized();
}
private:
ofxImageSequencePlayer imageSequencePlayer;
};
Closing patches without Pd crashing, hopefully in an elegant way...
EUREKA! I solved the following problem:
Problem: provide a mechanism for allowing patches to self-close without Pd crashing.
Requirements: (1) uses only Pd vanilla; (2) action to close patch starts on the very patch that will be closed.
Solution: copy patch killer.pd in the same folder as the patch to be closed, and send a message containing ";pd open killer.pd <dir>;kill_me symbol patch_to_be_closed.pd":

killer.pd
patch_to_be_closed.pd
Comments: (documented in the killer patch) Pd doesn't deal well with menuclose requests that originate (in a direct chain reaction) from the same patch that wants to be closed, so a separate killer patch is needed, which can be opened by the same patch requesting to be closed. In order to break the direct chain reaction, the solution is to postpone this request and make it appear as being originated inside the killer patch. This is done by storing the received symbol and delaying the message menuclose by 0 ms, which is enough to issue a new chain of events. The killer patch stays alive (but invisible), and any new instances of the killer patch will silently kill the previous instances so that only one killer patch is alive at any given time. [EDIT: I included a safeguard [pipe 1] in the killer patch, check the inline comments]. This implementation fixes the problems with my previous solution and also with the 3-patches method by @ingox, both of which didn't work through a send/receive pair. It also does not depend on dynamic patching.
This has been tested in Pd 0.50-2 and Ubuntu 20.04. I appreciate feedback to confirm if it works in other platforms/versions.
@whale-av Thanks again David for the suggestions (hcs, mouse clicks)! I wasn't too keen on the idea of depending on external libraries, and I also had to abandom the idea of having the killer patch embedded in the patch to be closed.
RMS vs FFT complex magnitudes
I'm up to the beginning of Katja V's Fourier Transform section and I've already found a few answers to my questions. I also managed to get the sum of FFT term amplitudes to match the RMS value for arbitrary input. Here's the patch:
Inside [pd computerMagnitudes]: 
compareTimeFreqAmpl2.pd
All the things on the left are just tools to fill the input table, but you can also just draw. Once you have your signal, bang computeMagnitudes to measure its amplitude both ways.
I made a couple of simplifications that not only got the test working but also gave me more confidence that I was comparing apples to apples:
- I'm computing RMS and the FFT from a single static 1024 vector, so I'm now comparing two views of the exact same signal and there's no need for averaging.
- I learned from Katja that if you perform a complex FFT on a real signal, you don't have to worry about which terms to double because the FFT gives you those terms's double in the upper half of the output explicitly. The real FFT skips the upper half for efficiency because it's related to the lower half.
- I also learned that even the cosine and sine components of each harmonic are uncorrelated signals, so I now sum their magnitudes individually across all harmonics. There's no need to compute the magnitude of each FFT term first.
So I think the issue I was having with noise was just an artifact of a badly programmed test, probably having to do with the way I was averaging term magnitudes, but I don't really know.
7/18/2020 update: I've found info in Katja's blog that suggests that this patch is wrong (or maybe even not possible). Exhibit A:
IMHO, this contradicts what she spent so much effort establishing on the prior two pages (http://www.katjaas.nl/sinusoids2/sinusoids2.html
http://www.katjaas.nl/correlation/correlation.html), that the cos and sine components of all FFT terms are orthogonal. If they're orthogonal, how could they cancel each other out?
She raises a another point on the FFT output page that really makes me wonder why my patch seems to work:
In this case I agree--Fourier coefficents are really the peak amplitudes of the cos and sine components--but my confusion over this is what made me program the patch the way that I did. So why is it working?
sending / receiving variables with emscripten ofelia?
i managed to send and receive values (numbers) with EM_ASM between java script and c++
here is an example: http://emasm.handmadeproductions.de/
EMASM.zip
but it works only with regular open frameworks programs yet, not with ofelia...
#include "ofApp.h"
#include "emscripten.h"
//--------------------------------------------------------------
void ofApp::setup(){
}
//--------------------------------------------------------------
void ofApp::update(){
}
//--------------------------------------------------------------
void ofApp::draw(){
EM_ASM_({
console.log('I send: ' + $0);
document.getElementById("display_time").textContent = Number($0).toFixed(2);
}, ofGetElapsedTimef());
int size = EM_ASM_INT({
console.log('I received size: ' + document.getElementById("slider_size").value);
return document.getElementById("slider_size").value;
});
int blue = EM_ASM_INT({
console.log('I received blue: ' + document.getElementById("slider_blue").value);
return document.getElementById("slider_blue").value;
});
int t = ofGetElapsedTimef();
ofSetColor(t % 255, t % 255, blue);
ofRectangle rect;
rect.x = 10;
rect.y = 10;
rect.width = size * 1.344827586;
rect.height = size;
ofDrawRectangle(rect);
}
//--------------------------------------------------------------
void ofApp::keyPressed(int key){
}
//--------------------------------------------------------------
void ofApp::keyReleased(int key){
}
//--------------------------------------------------------------
void ofApp::mouseMoved(int x, int y ){
}
//--------------------------------------------------------------
void ofApp::mouseDragged(int x, int y, int button){
}
//--------------------------------------------------------------
void ofApp::mousePressed(int x, int y, int button){
}
//--------------------------------------------------------------
void ofApp::mouseReleased(int x, int y, int button){
}
//--------------------------------------------------------------
void ofApp::mouseEntered(int x, int y){
}
//--------------------------------------------------------------
void ofApp::mouseExited(int x, int y){
}
//--------------------------------------------------------------
void ofApp::windowResized(int w, int h){
}
//--------------------------------------------------------------
void ofApp::gotMessage(ofMessage msg){
}
//--------------------------------------------------------------
void ofApp::dragEvent(ofDragInfo dragInfo){
}
@Cuinjune i wonder if it is possible to access ofelia variables from ofApp.cpp,
or if it could be possible to use EM_ASM in an future update inside an ofelia script?
Or is it possible to already use EM_ASM inside an ofelia script and I just need to know how to use it with Lua?




