Why does Pd look so much worse on linux/windows than in macOS?
Howdy all,
I just found this and want to respond from my perspective as someone who has spent by now a good amount of time (paid & unpaid) working on the Pure Data source code itself.
I'm just writing for myself and don't speak for Miller or anyone else.
Mac looks good
The antialiasing on macOS is provided by the system and utilized by Tk. It's essentially "free" and you can enable or disable it on the canvas. This is by design as I believe Apple pushed antialiasing at the system level starting with Mac OS X 1.
There are even some platform-specific settings to control the underlying CoreGraphics settings which I think Hans tried but had issues with: https://github.com/pure-data/pure-data/blob/master/tcl/apple_events.tcl#L16. As I recall, I actually disabled the font antialiasing as people complained that the canvas fonts on mac were "too fuzzy" while Linux was "nice and crisp."
In addition, the last few versions of Pd have had support for "Retina" high resolution displays enabled and the macOS compositor does a nice job of handling the point to pixel scaling for you, for free, in the background. Again, Tk simply uses the system for this and you can enable/disable via various app bundle plist settings and/or app defaults keys.
This is why the macOS screenshots look so good: antialiasing is on and it's likely the rendering is at double the resolution of the Linux screenshot.
IMO a fair comparison is: normal screen size in Linux vs normal screen size in Mac.
Nope. See above. 
It could also just be Apple holding back a bit of the driver code from the open source community to make certain linux/BSD never gets quite as nice as OSX on their hardware, they seem to like to play such games, that one key bit of code that is not free and you must license from them if you want it and they only license it out in high volume and at high cost.
Nah. Apple simply invested in antialiasing via its accelerated compositor when OS X was released. I doubt there are patents or licensing on common antialiasing algorithms which go back to the 60s or even earlier.
tkpath exists, why not use it?
Last I checked, tkpath is long dead. Sure, it has a website and screenshots (uhh Mac OS X 10.2 anyone?) but the latest (and only?) Sourceforge download is dated 2005. I do see a mirror repo on Github but it is archived and the last commit was 5 years ago.
And I did check on this, in fact I spent about a day (unpaid) seeing if I could update the tkpath mac implementation to move away from the ATSU (Apple Type Support) APIs which were not available in 64 bit. In the end, I ran out of energy and stopped as it would be too much work, too many details, and likely to not be maintained reliably by probably anyone.
It makes sense to help out a thriving project but much harder to justify propping something up that is barely active beyond "it still works" on a couple of platforms.
Why aren't the fonts all the same yet?!
I also despise how linux/windows has 'bold' for default
I honestly don't really care about this... but I resisted because I know so many people do and are used to it already. We could clearly and easily make the change but then we have to deal with all the pushback. If you went to the Pd list and got an overwhelming consensus and Miller was fine with it, then ok, that would make sense. As it was, "I think it should be this way because it doesn't make sense to me" was not enough of a carrot for me to personally make and support the change.
Maybe my problem is that I feel a responsibility for making what seems like a quick and easy change to others? 
And this view is after having put an in ordinate amount of time just getting (almost) the same font on all platforms, including writing and debugging a custom C Tcl extension just to load arbitrary TTF files on Windows.
Why don't we add abz, 123 to Pd? xyzzy already has it?!
What I've learned is that it's much easier to write new code than it is to maintain it. This is especially true for cross platform projects where you have to figure out platform intricacies and edge cases even when mediated by a common interface like Tk. It's true for any non-native wrapper like QT, WXWidgets, web browsers, etc.
Actually, I am pretty happy that Pd's only core dependencies a Tcl/Tk, PortAudio, and PortMidi as it greatly lowers the amount of vectors for bitrot. That being said, I just spent about 2 hours fixing the help browser for mac after trying Miller's latest 0.52-0test2 build. The end result is 4 lines of code.
For a software community to thrive over the long haul, it needs to attract new users. If new users get turned off by an outdated surface presentation, then it's harder to retain new users.
Yes, this is correct, but first we have to keep the damn thing working at all.
I think most people agree with you, including me when I was teaching with Pd.
I've observed, at times, when someone points out a deficiency in Pd, the Pd community's response often downplays, or denies, or gets defensive about the deficiency. (Not always, but often enough for me to mention it.) I'm seeing that trend again here. Pd is all about lines, and the lines don't look good -- and some of the responses are "this is not important" or (oid) "I like the fact that it never changed." That's... thoroughly baffling to me.
I read this as "community" = "active developers." It's true, some people tend to poo poo the same reoccurring ideas but this is largely out of years of hearing discussions and decisions and treatises on the list or the forum or facebook or whatever but nothing more. In the end, code talks, even better, a working technical implementation that is honed with input from people who will most likely end up maintaining it, without probably understanding it completely at first.
This was very hard back on Sourceforge as people had to submit patches(!) to the bug tracker. Thanks to moving development to Github and the improvement of tools and community, I'm happy to see the new engagement over the last 5-10 years. This was one of the pushes for me to help overhaul the build system to make it possible and easy for people to build Pd itself, then they are much more likely to help contribute as opposed to waiting for binary builds and unleashing an unmanageable flood of bug reports and feature requests on the mailing list.
I know it's not going to change anytime soon, because the current options are a/ wait for Tcl/Tk to catch up with modern rendering or b/ burn Pd developer cycles implementing something that Tcl/Tk will(?) eventually implement or c/ rip the guts out of the GUI and rewrite the whole thing using a modern graphics framework like Qt. None of those is good (well, c might be a viable investment in the future -- SuperCollider, around 2010-2011, ripped out the Cocoa GUIs and went to Qt, and the benefits have been massive -- but I know the developer resources aren't there for Pd to dump Tcl/Tk).
A couple of points:
-
Your point (c) already happened... you can use Purr Data (or the new Pd-L2ork etc). The GUI is implemented in Node/Electron/JS (I'm not sure of the details). Is it tracking Pd vanilla releases?... well that's a different issue.
-
As for updating Tk, it's probably not likely to happen as advanced graphics are not their focus. I could be wrong about this.
I agree that updating the GUI itself is the better solution for the long run. I also agree that it's a big undertaking when the current implementation is essentially still working fine after over 20 years, especially since Miller's stated goal was for 50 year project support, ie. pieces composed in the late 90s should work in 2040. This is one reason why we don't just "switch over to QT or Juce so the lines can look like Max." At this point, Pd is aesthetically more Max than Max, at least judging by looking at the original Ircam Max documentation in an archive closet at work. 
A way forward: libpd?
I my view, the best way forward is to build upon Jonathan Wilke's work in Purr Data for abstracting the GUI communication. He essentially replaced the raw Tcl commands with abstracted drawing commands such as "draw rectangle here of this color and thickness" or "open this window and put it here."
For those that don't know, "Pd" is actually two processes, similar to SuperCollider, where the "core" manages the audio, patch dsp/msg graph, and most of the canvas interaction event handling (mouse, key). The GUI is a separate process which communicates with the core over a localhost loopback networking connection. The GUI is basically just opening windows, showing settings, and forwarding interaction events to the core. When you open the audio preferences dialog, the core sends the current settings to the GUI, the GUI then sends everything back to the core after you make your changes and close the dialog. The same for working on a patch canvas: your mouse and key events are forwarded to the core, then drawing commands are sent back like "draw object outline here, draw osc~ text here inside. etc."
So basically, the core has almost all of the GUI's logic while the GUI just does the chrome like scroll bars and windows. This means it could be trivial to port the GUI to other toolkits or frameworks as compared to rewriting an overly interconnected monolithic application (trust me, I know...).
Basically, if we take Jonathan's approach, I feel adding a GUI communication abstraction layer to libpd would allow for making custom GUIs much easier. You basically just have to respond to the drawing and windowing commands and forward the input events.
Ideally, then each fork could use the same Pd core internally and implement their own GUIs or platform specific versions such as a pure Cocoa macOS Pd. There is some other re-organization that would be needed in the C core, but we've already ported a number of improvements from extended and Pd-L2ork, so it is indeed possible.
Also note: the libpd C sources are now part of the pure-data repo as of a couple months ago...
Discouraging Initiative?!
But there's a big difference between "we know it's a problem but can't do much about it" vs "it's not a serious problem." The former may invite new developers to take some initiative. The latter discourages initiative. A healthy open source software community should really be careful about the latter.
IMO Pd is healthier now than it has been as long as I've know it (2006). We have so many updates and improvements over every release the last few years, with many contributions by people in this thread. Thank you! THAT is how we make the project sustainable and work toward finding solutions for deep issues and aesthetic issues and usage issues and all of that.
We've managed to integrate a great many changes from Pd-Extended into vanilla and open up/decentralize the externals and in a collaborative manner. For this I am also grateful when I install an external for a project.
At this point, I encourage more people to pitch in. If you work at a university or institution, consider sponsoring some student work on specific issues which volunteering developers could help supervise, organize a Pd conference or developer meetup (this are super useful!), or consider some sort of paid residency or focused project for artists using Pd. A good amount of my own work on Pd and libpd has been sponsored in many of these ways and has helped encourage me to continue.
This is likely to be more positive toward the community as a whole than banging back and forth on the list or the forum. Besides, I'd rather see cool projects made with Pd than keep talking about working on Pd.
That being said, I know everyone here wants to see the project continue and improve and it will. We are still largely opening up the development and figuring how to support/maintain it. As with any such project, this is an ongoing process.
Out
Ok, that was long and rambly and it's way past my bed time.
Good night all.
Having lots of switches into Pd
@alexandros now pwm and digital output work fine, but when I try to merge with digital and analog inputs it goes wrong. the leds flicker randomly. If you remove the code below
// do input pins: flicker stops. :/
int outPins[3] = {2, 4, 7};
int pwmPins[6] = {3, 5, 6, 9, 10, 11};
// a global variable to hold which LED we want to control (either digital or PWM)
int channel = 0;
int inPins[2] = {12, 13};
int analogPins[8] = {0, 1, 2, 3, 4, 5, 6, 7};
void setup() {
for (int i = 0; i < 3; i++) {
pinMode(outPins[i], OUTPUT);
}
for (int i = 0; i < 6; i++) {
pinMode(pwmPins[i], OUTPUT);
}
for(int i = 0; i < 2; i++) {
pinMode(inPins[i], INPUT);
}
pinMode(A0, INPUT_PULLUP);
pinMode(A1, INPUT_PULLUP);
pinMode(A2, INPUT_PULLUP);
pinMode(A3, INPUT_PULLUP);
pinMode(A4, INPUT_PULLUP);
pinMode(A5, INPUT_PULLUP);
pinMode(A6, INPUT);
pinMode(A7, INPUT);
//DEFAULT works with thermistors,
//INTERNAL with transitor thermostats
analogReference(DEFAULT);
Serial.begin(115200);
}
void loop() {
//do output pins:
if (Serial.available()) {
static int temp;
byte in = Serial.read();
if (isDigit(in)) temp = temp * 10 + in - '0';
else if (in == 'c') {
channel = temp;
temp = 0;
}
else if (in == 'd') {
digitalWrite(outPins[channel], temp);
temp = 0;
}
else if (in == 'p') {
analogWrite(pwmPins[channel], temp);
temp = 0;
}
}
// do input pins:
Serial.print("analog"); // use "knobs" as a keyword so you can receive
// the knob values as a list with a [r analog] in Pd
for(int i = 0; i < 8; i++){
unsigned int analogVal = analogRead (analogPins[i]);
Serial.print(" "); // first print a white space to separate the "knob" keyword from the values
// and the values from each other
Serial.print(analogVal); // then print the actual knob value
}
Serial.println(); // finally print a newline character to denote end of data for keyword "knobs"
Serial.print("digital");
for(int i = 0; i < 2; i++) {
unsigned int digitalVal = digitalRead(inPins[i]);
Serial.print(" ");
Serial.print(digitalVal);
}
Serial.println();
}
Having lots of switches into Pd
First of all, don't separate functions for reading the serial data, even if you want to read data for different kinds of stuff.
Combining digital with PWM pins should be sort of straight forward. Here's some example code:
int digitalPins[4] = {2, 4, 7, 10};
// use an array for all the pins of similar type, e.g. PWM pins, like below
int pwmPins[6] = {3, 5, 6, 9, 11, 12}; // can't remember the exact pin numbers with PWM, change accordingly
// a global variable to hold which LED we want to control (either digital or PWM)
int channel = 0;
void setup() {
for (int i = 0; i < 4; i++) {
pinMode(digitalPins[i], OUTPUT);
pinMode(pwmPins[i], OUTPUT);
Serial.begin(115200);
}
void loop() {
if (Serial.available()) {
static int temp;
byte in = Serial.read();
if (isDigit(in)) temp = temp * 10 + in - '0';
else if (in == 'c') {
channel = temp;
temp = 0;
}
else if (in == 'd') {
digitalWrite(digitalPins[channel], temp);
temp = 0;
}
else if (in == 'a') {
analogWrite(pwmPins[channel], temp);
temp = 0;
}
}
Check this code with Pd by sending messages of the type print $1c$2d for digital values and print $1c$2a for analog values. $1 is the led number (incrementing, starting from 0), and $2 is the value you want to write, 0-1 for digital and 0-255 for PWM.
Having lots of switches into Pd
@alexandros
This code sort of works with wip_multiple_PWM.pd
// merging works but pwm leds are choppy.
// number of elements in arrays need to
// match for() cycles in void setup and void loop
int pinsIn[2] = {2, 4};
int pinsAnalog[8] = {0, 1, 2, 3, 4, 5, 6, 7};
int pin = 0;
int val = 0;
int pinsOut[2] = {7, 12};
//TMP setup pwm:
// variables to hold pin numbers
int pwmLED1 = 3;
int pwmLED2 = 5;
int pwmLED3 = 6;
int pwmLED4 = 9;
int pwmLED5 = 10;
int pwmLED6 = 11;
// variables to hold pin states
int pwmLEDvalue1;
int pwmLEDvalue2;
int pwmLEDvalue3;
int pwmLEDvalue4;
int pwmLEDvalue5;
int pwmLEDvalue6;
//should this be omitted and use the a
// variable to hold and assemble incoming data
int temporary;
//END TMP pwm setup
void setup()
{
//set up a total of pins for digital input (has to match number of elements in array)
for(int i = 0; i < 2; i++)
pinMode(pinsIn[i], INPUT);
for (int i = 0; i < 2; i++) {
pinMode(pinsOut[i], OUTPUT);
digitalWrite(pinsOut[i], LOW);
}
//DEFAULT works with thermistors,
//INTERNAL with transitor thermostats
analogReference(DEFAULT);
pinMode(A0, INPUT_PULLUP);
pinMode(A1, INPUT_PULLUP);
pinMode(A2, INPUT_PULLUP);
pinMode(A3, INPUT_PULLUP);
pinMode(A4, INPUT_PULLUP);
pinMode(A5, INPUT_PULLUP);
pinMode(A6, INPUT);
pinMode(A7, INPUT);
//TMP test pwm setup:
pinMode(pwmLED1, OUTPUT);
pinMode(pwmLED2, OUTPUT);
pinMode(pwmLED3, OUTPUT);
pinMode(pwmLED4, OUTPUT);
pinMode(pwmLED5, OUTPUT);
pinMode(pwmLED6, OUTPUT);
Serial.begin(115200); // perhaps use a faster baud rate
}
void loop()
{
Serial.print("knobs"); // use "knobs" as a keyword so you can receive
// the knob values as a list with a [r knobs] in Pd
for(int i = 0; i < 8; i++){
unsigned int knob = analogRead (pinsAnalog[i]);
Serial.print(" "); // first print a white space to separate the "knob" keyword from the values
// and the values from each other
Serial.print(knob); // then print the actual knob value
}
Serial.println(); // finally print a newline character to denote end of data for keyword "knobs"
// the same technique applies to the switches too
// receive the switch values as a list with [r switches]
Serial.print("switches");
for(int i = 0; i < 2; i++) {
int switchVal = digitalRead(pinsIn[i]);
Serial.print(" ");
Serial.print(switchVal);
}
Serial.println();
//handle digital outputs
if (Serial.available()) {
static int temp;
byte in = Serial.read();
if (isDigit(in)) {
temp = temp * 10 + in - '0';
}
else if (in == 'p') {
pin = temp;
temp = 0;
}
else if (in == 'v') {
val = temp;
temp = 0;
digitalWrite(pinsOut[pin], val);
}
}
//TMP merge test PWMs:
while(Serial.available()){
byte inByte = Serial.read();
if((inByte >= '0') && (inByte <= '9'))
temporary = 10 * temporary + inByte - '0';
else{
if(inByte == 'p'){
pwmLEDvalue1 = temporary;
temporary = 0;
}
else if(inByte == 'q'){
pwmLEDvalue2 = temporary;
temporary = 0;
}
else if(inByte == 'r'){
pwmLEDvalue3 = temporary;
temporary = 0;
}
else if(inByte == 's'){
pwmLEDvalue4 = temporary;
temporary = 0;
}
else if(inByte == 't'){
pwmLEDvalue5 = temporary;
temporary = 0;
}
else if(inByte == 'u'){
pwmLEDvalue6 = temporary;
temporary = 0;
}
}
analogWrite(pwmLED1, pwmLEDvalue1);
analogWrite(pwmLED2, pwmLEDvalue2);
analogWrite(pwmLED3, pwmLEDvalue3);
analogWrite(pwmLED4, pwmLEDvalue4);
analogWrite(pwmLED5, pwmLEDvalue5);
analogWrite(pwmLED6, pwmLEDvalue6);
//digitalWrite(dspLED, dspLEDstate);
}
}
This is the code without PWM control. It works fine.
//number of elements in arrays need to match for() cycles in void setup
int pinsIn[4] = {6, 7, 8, 9};
int pinsAnalog[8] = {0, 1, 2, 3, 4, 5, 6, 7};
int pin = 0;
int val = 0;
int pinsOut[4] = {2, 3, 4, 5};
void setup()
{
//set up a total of pins for digital input (has to match number of elements in array)
for(int i = 0; i < 4; i++)
pinMode(pinsIn[i], INPUT);
for (int i = 0; i < 4; i++) {
pinMode(pinsOut[i], OUTPUT);
digitalWrite(pinsOut[i], LOW);
}
//DEFAULT works with thermistors,
//INTERNAL with transitor thermostats
// ELLER var det tvartom???
analogReference(DEFAULT);
pinMode(A0, INPUT_PULLUP);
pinMode(A1, INPUT_PULLUP);
pinMode(A2, INPUT_PULLUP);
pinMode(A3, INPUT_PULLUP);
pinMode(A4, INPUT_PULLUP);
pinMode(A5, INPUT_PULLUP);
pinMode(A6, INPUT);
pinMode(A7, INPUT);
Serial.begin(115200); // perhaps use a faster baud rate
}
void loop()
{
Serial.print("knobs"); // use "knobs" as a keyword so you can receive
// the knob values as a list with a [r knobs] in Pd
for(int i = 0; i < 8; i++){
unsigned int knob = analogRead (pinsAnalog[i]);
Serial.print(" "); // first print a white space to separate the "knob" keyword from the values
// and the values from each other
Serial.print(knob); // then print the actual knob value
}
Serial.println(); // finally print a newline character to denote end of data for keyword "knobs"
// the same technique applies to the switches too
// receive the switch values as a list with [r switches]
Serial.print("switches");
for(int i = 0; i < 4; i++) {
int switchVal = digitalRead(pinsIn[i]);
Serial.print(" ");
Serial.print(switchVal);
}
Serial.println();
//handle digital outputs
if (Serial.available()) {
static int temp;
byte in = Serial.read();
if (isDigit(in)) {
temp = temp * 10 + in - '0';
}
else if (in == 'p') {
pin = temp;
temp = 0;
}
else if (in == 'v') {
val = temp;
temp = 0;
digitalWrite(pinsOut[pin], val);
}
}
}
and here is the code from tutorial5 from Arduino for Pd'ers. It goes with arduinoforpdrs_tut5.pd
// variables to hold pin numbers
int pwmLED = 9;
int dspLED = 2;
// variables to hold pin states
int pwmLEDvalue;
int dspLEDstate;
//variable to hold and assemble incoming data
int temporary;
void setup()
{
pinMode(pwmLED, OUTPUT);
pinMode(dspLED, OUTPUT);
Serial.begin(9600);
}
void loop()
{
while(Serial.available()){
byte inByte = Serial.read();
if((inByte >= '0') && (inByte <= '9'))
temporary = 10 * temporary + inByte - '0';
else{
if(inByte == 'p'){
pwmLEDvalue = temporary;
temporary = 0;
}
else if(inByte == 'd'){
dspLEDstate = temporary;
temporary = 0;
}
}
analogWrite(pwmLED, pwmLEDvalue);
digitalWrite(dspLED, dspLEDstate);
}
}
I am aiming at using same type of array handling as for the digital outs.
Thanks a lot
Paradigms useful for teaching Pd
@ingox Ha, I guess this highlights the number 0 paradigm for Pd-- always measure first!
It looks to me like message-passing overhead dwarfs the time it takes to copy a list once into [list store]. That single full list-copy operation (a for loop if you look in the source) is still significant-- about 10% of total computation time when testing with a list of 100,000 elements.
However, if you remove all message-passing overhead and just code up [list drip] as a core class, you consistently get an order of magnitude better performance.
So we have:
- original list-drip: slow, but I can't find the code for it. But I hope it was just copying a list once before iteration, and not on every pass.
- matju's insane recursive non-copy list-drip: faster than the original implementation
list store]implementation of[list-drip]: looks to be consistently 3 times faster than number 2 above, lending credence to the theory that message-passing overhead is the significant factor in performance. (Well, as long as you aren't copying the entire list on every pass...)- Add core class
[list drip]to x_list.c and use a for loop to output each element from the incoming vector of atoms without copying anything. I quickly coded this up and saw a consistent order of magnitude performance improvement over the[list store]implementation. (Didn't test with gpointers, which will probably slow down every single implementation listed here.)
I think Matt Barber even had a case where removing a [trigger] object and replacing it with bare wires (and just depending on creation order) generated enough of a performance difference in a complex patch to warrant doing it. (Edit: this may have been in an abstraction, in which case the speedup would be multiplied by the total number of abstractions used...)
It's too bad that message-passing overhead makes that much of a difference. And while I can think of a potential speedup for the case of [trigger], I'm not sure there's a simple way to boost message passing performance across the board.
Purr Data GSoC and Dictionaries in Pd
@jancsika said:
The point is that Pd doesn't have a properly optimized associative table implementation in vanilla.
Well, it does-- it is the global symbol table which t_symbol hooks into.
That's good -- SC also has a global symbol table, so that symbols can be matched just by comparing the internal ID.
That would speed up the string matching in [text search], but [text]'s sequential storage means that the search can never be better than O(n).
SC's dictionary is fast because storage is by hash. A 100 item dictionary would be stored in a 256 item array (where even indices are for keys and odd indices are for values), and the position of a key is key.hash % (array size / 2) * 2. Hash collisions just push an item later in the array (and the array expands as needed, rearranging the items). If the hash is well designed, it may need to search only a few items to find the right one (and IdentityDictionary further optimizes by doing the whole hash search in C). SC's implementation expands to the next power of two, hence, logarithmic time.
There are all these leaky parts between the language and the implementation-- I think it's what you are noticing when a solution gets produced from "on high." The user either has pentested potential workarounds enough to know which constructions are likely to be performant, or they've looked under the hood to see how things are implemented.
This is also just part of fluency (when you've already made and discarded dozens of mistakes).
Yeah, I'd be curious to know after jumping into Pd if you have any opinions on what they could look like or at what level they could be implemented. IMO a decent associative array implementation ought to fill most niches.
Yes, a hash table is one that I miss. (Lua tables become available through Ofelia, but I've noticed some overhead in converting data between Lua and Pd.)
One usage of hash tables in SC is an Event, which is a bridge between arbitrary, named musical data and a concrete sounding result -- (degree: 0) represents a tonic quarter note; adding instrument: \synthdefname plays it using a specific synth, but you can also add type: \midi, midiout: aMIDIOutObject to play the same on an external MIDI device/program without rewriting logic. It's rich enough to support even strummed chords within one event, but lightweight enough to generate dozens of them per second ([text] doesn't fly there). Very powerful.
I'm hard-pressed to think of others. Multidimensional arrays perhaps (which you can already hack together, but re-engineering this wastes users' time).
hjh
Purr Data GSoC and Dictionaries in Pd
If you're measuring search, last I looked it's just a simple linear search with [text] classes.
The point is that Pd doesn't have a properly optimized associative table implementation in vanilla.
Well, it does-- it is the global symbol table which t_symbol hooks into.
There are all these leaky parts between the language and the implementation-- I think it's what you are noticing when a solution gets produced from "on high." The user either has pentested potential workarounds enough to know which constructions are likely to be performant, or they've looked under the hood to see how things are implemented.
For example, when I said this in the other thread:
At least when I look at OP's problem, I see global receiver names followed by messages.
It's because I know that the global receiver names are a) created using a simple, effective hashing algo, and
their associations are already cached by the time a symbol becomes part of a message. So the cost of forwarding a message to a receiver is just the cost of message evaluation plus the cost of invoking the relevant receiver's function in pd_typedmess. Since the user obviously knows how many arguments each receiver expects, they can skip iterating over the args in order to forward the next message. No lookups or data copying is needed.
TL;DR I would love to see more robust and well-performing data structures in Pd.
Yeah, I'd be curious to know after jumping into Pd if you have any opinions on what they could look like or at what level they could be implemented. IMO a decent associative array implementation ought to fill most niches. The demo solution I wrote that @jona found could be expanded to make things more expressive. (It just reimplements Pd's t_symbol hashing algo in a subpatch.) But of course it would be limited to that particular class/object-- you couldn't use it to pass around an associative array. (I guess gpointers could be used to do this, but that's not a great UX IMO.)
It's also possible to add something at the level of a native data type, at the cost of all the thinking about new syntax and compatibility that requires.
Edit: my letter b above got converted to a sunglasses-smily emoji. I like it. 
PDuino - simultaneous analog input and digital output problems
Hello!
I'm trying to get PDuino working in a setup where I have Arduino reading an analog input and also controlling a relay on the digital output. I have tried the StandardFirmata sketch for Arduino but it doesn't seem to be able to read the analog input with this sketch (it controls the digital output no problem with this however). When I use the AllInputsFirmata instead, it reads the analog inputs just fine but I'm getting no response on the digital outputs.
Is there perhaps something where you can't use analog inputs and digital outputs at the same time? Could I be doing something else wrong? I've tried all the other Firmata sketches that come with the Pduino setup files (out of desperation!) but with no success. Any help greatly appreciated!
ofelia test grid
@Cuinjune thanks a lot for the update 
inspired by the conway implementation from @weightless https://forum.pdpatchrepo.info/topic/10916/conway-s-game-of-life-implementation-with-data-structures i tried to implement the conway algorhithm. if you select the first preset and click the conway toggle you can see an example.
Ofelia2_GridXConway.pd
it works inside the grid, but i cant figure out the upper and lower border logic yet. i think it is a disadvantage of the one dimensional table for this case, perhaps a 2 dimensional table would make things easier? or i just dont get the logic
it could be more difficult to implement the existing functions (like shift or shuffle) into the 2 dimensional table, that was quite easy with the one dimensional table. @weightless also used "2 dimensional arrays" (the [text] object) in his conway patch. something like this: https://github.com/syntruth/Lua-Grid sounds great for the grid logic, but still to complex for me to understand...
dbtorms
@pomolo This needed re-writing.
Dynamic range is meaningless in Pd (or any computer digital processing), in that it cannot be reproduced in the real world. The dynamic range is determined by the ratio of the biggest possible number to the smallest. Even in Pd 32-bit the biggest possible number is massive compared to the smallest.
Of course for FFT the dynamic range is constrained, as it depends on the sample-rate and bit depth.......
https://en.wikipedia.org/wiki/Audio_bit_depth
dB(something) becomes fixed, with real measurable (but weighted) values, when it is measured as sound waves in the air....... and it is fixed then because it is universally agreed that 0dB is the smallest pressure wave that can be heard by a human.
Only then can you define +100dB
RMS..... volts do not "exist" either until the [dac~] does it's work
How you scale the numbers to match your soundcard is up to you.
As Pierre says above 1V = +100dB has been "chosen" as sensible for Pd.
But as we all know, a traditional VU meter will show 0dB as "maximum".... not +100dB.
Hence the dB-100 part of the formula above.
+1...-1 (about zero) is considered safe for [dac~]
If the soundcard sees 1 as max for its conversion to analog all is well.
At 1.00001 it will clip. This value is fixed and known. There is nothing to be done about it.
The dynamic range of your card is not from 0dB to +94dB.
(Well....... it is, but that is what confuses the mind).
Its dynamic range is determined by its noise floor, and your card has a noise floor 94dB "below" the maximum that it can output before clipping.
The dynamic range of your card is from -94dB to 0dB........ if we agree that it will "clip" above 0dB.
So if Pd +100dB is 1 (in fact shown on the VU Gui in Pd as 0dB) then when the Pd Gui shows -95dB any Pd output will be masked by the noise floor of the soundcard....... (or more likely by dither if the soundcard is any good), as the very small numbers cannot be properly resolved, and that results in unpleasant audible artefacts.
And then there is the metering of the soundcard (if it has any).
Because it was traditional to overload magnetic tape, many of the first pro digital systems actually showed 0dB (the max number without clipping) as +18dB..... and showed red bars from +12dB upwards. Trying to make sure you would not clip your recording.
This has most of the info you are looking for I think........
https://sound.stackexchange.com/questions/25529/what-is-0-db-in-digital-audio
As with any analog or digital system you need to know "where you are" with the audio chain before you start work. Set an oscillator into a [dac~] with a VU driven by [pvu~]. You will see 0dB on the meter (1V RMS in Pd).
What does your soundcard show?
Do you need to trim the output of all your patches with a [*~ ?].?
Probably not.
Are there standards
No, not for metering.
If it shows +18dB or +12dB that is probably correct.
0dB on the Pd VU should show as the last bar on your soundcard before the red bars, but the soundcard metering might be lying..... to save you from yourself.
You will only get clipping where the maximum possible value is exceeded.
Volts, RMS or peak, are measures on a linear scale.
But our ears hear "power" and so the dB scale of relative power is a logarithmic scale.
The formula above equates the linear scale to the log scale, but they can "slide" against each other.
Putting an amplifier increases the volts, but how much more volume (dB) you get depends on the efficiency of the speaker...... losses in cables etc.
Only when you have decided 0dB can you say what is 100dB.
Or when you have decided 100dB you can say "how much" is 0dB.
Because it is all relative.
David.



