Looking for dark layout - is it possible?
@ejoo Hi, currently vanilla doesn't support tk color hooks. However, I have a branch which does that is only a few commits behind HEAD.
https://github.com/sebshader/pure-data/tree/colors
(The names are somewhat different than in extended, as discussed in this pr:)
https://github.com/pure-data/pure-data/pull/196
you have to compile yourself.. there is an example scheme in doc/7.stuff/colors-plugin.txt (change the extension to .tcl and put it in your path, and you can then edit (or remove for defaults) the values below line 43:
array set ::pd_colors {
e.g.
array set ::pd_colors {
canvas_fill "Black"
signal_cord "IndianRed"
signal_iolet "IndianRed"
msg_cord "DodgerBlue"
msg_iolet "DodgerBlue"
atom_box_fill "MintCream"
msg_box_fill "MintCream"
obj_box_fill "MintCream"
obj_box_outline "Black"
msg_box_outline "Black"
atom_box_outline "Black"
graph_outline "Black"
}
there are also more colors than were settable in extended including colors for GOP box, array values, console window, help browser, and [text] object windows
I have an osx binary btw if anyone wants one
edit: graph_outline as black? .. maybe that was different in extended but it doesn't show up against the black canvas
& I would also recommend setting "comment" to MintCream or something
Possible debugger of sorts to find red messages that can not be found with Cntrl + Mouse 1
@RetroMaximus Unfortunately there is no "index" of objects in the patch file.
When Pd opens a patch it gives them an index in their order in the list...... which is also the order in which they were put in the patch...... because that will determine the order of operations.
They appear as "obj x y" where x and y are where they appear in the patch window.... coordinates, not index.
BUT beware..... it is not just "obj" objects that count..... "floatatom" "msg"..... anything that has connections is counted.
The connect message is connect a b x y....... you see a load of them already at the bottom of the text file.
"a" is the object number to connect from starting at zero.
"b" is the outlet number of that object starting at zero on the left outlet.
"x" is the object number to connect to starting at zero.
"y" is the inlet number of that object to connect to starting at zero on the left inlet.
So with 2 objects only..... say [f] and [print]...... [connect 0 0 1 0(
With [route 1 2] and 2 [print] objects [connect 0 0 1 0( for the print of the left outlet of route and [connect 0 1 2 0( for the middle outlet.
That assumes you created [route] first and the prints afterwards.
Each object number starting at 0 is assigned in the order in which it was put in the patch.
You are going to have to count the objects in the patch to test before you start because the first [print] you put will have the next object number.
Actually you could just put a bigger number into my patch below...... a guess, but big.... and it will just throw more errors as it tries to connect [print] s together when it runs out of other objects to connect to.
debugger.zip
Of course it gets more complicated by an enormous factor when you want a [print] on all outlets of all the objects, but again, you could repeat connect messages for each object up to 10? outlets and suffer the console errors as it tries to connect to non existent outlets....... but then your print numbers will not match up with the object numbers.... hum.
David.
Spaghettis: Yet another fork of Pure Data
QWERTY keyboard! Does it works if you add following code?
event add <<NewObject>> <$mod-Key-1>
event add <<NewMessage>> <$mod-Key-2>
event add <<NewAtom>> <$mod-Key-3>
event add <<NewSymbol>> <$mod-Key-4>
event add <<NewComment>> <$mod-Key-5>
event add <<NewBang>> <$mod-Key-6>
event add <<NewToggle>> <$mod-Key-7>
event add <<NewDial>> <$mod-Key-8>
event add <<NewArray>> <$mod-Key-9>
PD's scheduler, timing, control-rate, audio-rate, block-size, (sub)sample accuracy,
Hello, 
this is going to be a long one.
After years of using PD, I am still confused about its' timing and schedueling.
I have collected many snippets from here and there about this topic,
-wich all together are really confusing to me.
*I think it is very important to understand how timing works in detail for low-level programming … *
(For example the number of heavy jittering sequencers in hard and software make me wonder what sequencers are made actually for ? lol )
This is a collection of my findings regarding this topic, a bit messy and with confused questions.
I hope we can shed some light on this.
- a)
The first time, I had issues with the PD-scheduler vs. how I thought my patch should work is described here:
https://forum.pdpatchrepo.info/topic/11615/bang-bug-when-block-1-1-1-bang-on-every-sample
The answers where:
„
[...] it's just that messages actually only process every 64 samples at the least. You can get a bang every sample with [metro 1 1 samp] but it should be noted that most pd message objects only interact with each other at 64-sample boundaries, there are some that use the elapsed logical time to get times in between though (like vsnapshot~)
also this seems like a very inefficient way to do per-sample processing..
https://github.com/sebshader/shadylib http://www.openprocessing.org/user/29118
seb-harmonik.ar posted about a year ago , last edited by seb-harmonik.ar about a year ago
• 1
whale-av
@lacuna An excellent simple explanation from @seb-harmonik.ar.
Chapter 2.5 onwards for more info....... http://puredata.info/docs/manuals/pd/x2.htm
David.
“
There is written: http://puredata.info/docs/manuals/pd/x2.htm
„2.5. scheduling
Pd uses 64-bit floating point numbers to represent time, providing sample accuracy and essentially never overflowing. Time appears to the user in milliseconds.
2.5.1. audio and messages
Audio and message processing are interleaved in Pd. Audio processing is scheduled every 64 samples at Pd's sample rate; at 44100 Hz. this gives a period of 1.45 milliseconds. You may turn DSP computation on and off by sending the "pd" object the messages "dsp 1" and "dsp 0."
In the intervals between, delays might time out or external conditions might arise (incoming MIDI, mouse clicks, or whatnot). These may cause a cascade of depth-first message passing; each such message cascade is completely run out before the next message or DSP tick is computed. Messages are never passed to objects during a DSP tick; the ticks are atomic and parameter changes sent to different objects in any given message cascade take effect simultaneously.
In the middle of a message cascade you may schedule another one at a delay of zero. This delayed cascade happens after the present cascade has finished, but at the same logical time.
2.5.2. computation load
The Pd scheduler maintains a (user-specified) lead on its computations; that is, it tries to keep ahead of real time by a small amount in order to be able to absorb unpredictable, momentary increases in computation time. This is specified using the "audiobuffer" or "frags" command line flags (see getting Pd to run ).
If Pd gets late with respect to real time, gaps (either occasional or frequent) will appear in both the input and output audio streams. On the other hand, disk strewaming objects will work correctly, so that you may use Pd as a batch program with soundfile input and/or output. The "-nogui" and "-send" startup flags are provided to aid in doing this.
Pd's "realtime" computations compete for CPU time with its own GUI, which runs as a separate process. A flow control mechanism will be provided someday to prevent this from causing trouble, but it is in any case wise to avoid having too much drawing going on while Pd is trying to make sound. If a subwindow is closed, Pd suspends sending the GUI update messages for it; but not so for miniaturized windows as of version 0.32. You should really close them when you aren't using them.
2.5.3. determinism
All message cascades that are scheduled (via "delay" and its relatives) to happen before a given audio tick will happen as scheduled regardless of whether Pd as a whole is running on time; in other words, calculation is never reordered for any real-time considerations. This is done in order to make Pd's operation deterministic.
If a message cascade is started by an external event, a time tag is given it. These time tags are guaranteed to be consistent with the times at which timeouts are scheduled and DSP ticks are computed; i.e., time never decreases. (However, either Pd or a hardware driver may lie about the physical time an input arrives; this depends on the operating system.) "Timer" objects which meaure time intervals measure them in terms of the logical time stamps of the message cascades, so that timing a "delay" object always gives exactly the theoretical value. (There is, however, a "realtime" object that measures real time, with nondeterministic results.)
If two message cascades are scheduled for the same logical time, they are carried out in the order they were scheduled.
“
[block~ smaller then 64] doesn't change the interval of message-control-domain-calculation?,
Only the size of the audio-samples calculated at once is decreased?
Is this the reason [block~] should always be … 128 64 32 16 8 4 2 1, nothing inbetween, because else it would mess with the calculation every 64 samples?
How do I know which messages are handeled inbetween smaller blocksizes the 64 and which are not?
How does [vline~] execute?
Does it calculate between sample 64 and 65 a ramp of samples with a delay beforehand, calculated in samples, too - running like a "stupid array" in audio-rate?
While sample 1-64 are running, PD does audio only?
[metro 1 1 samp]
How could I have known that? The helpfile doesn't mention this. EDIT: yes, it does.
(Offtopic: actually the whole forum is full of pd-vocabular-questions)
How is this calculation being done?
But you can „use“ the metro counts every 64 samples only, don't you?
Is the timing of [metro] exact? Will the milliseconds dialed in be on point or jittering with the 64 samples interval?
Even if it is exact the upcoming calculation will happen in that 64 sample frame!?
- b )
There are [phasor~], [vphasor~] and [vphasor2~] … and [vsamphold~]
https://forum.pdpatchrepo.info/topic/10192/vphasor-and-vphasor2-subsample-accurate-phasors
“Ive been getting back into Pd lately and have been messing around with some granular stuff. A few years ago I posted a [vphasor.mmb~] abstraction that made the phase reset of [phasor~] sample-accurate using vanilla objects. Unfortunately, I'm finding that with pitch-synchronous granular synthesis, sample accuracy isn't accurate enough. There's still a little jitter that causes a little bit of noise. So I went ahead and made an external to fix this issue, and I know a lot of people have wanted this so I thought I'd share.
[vphasor~] acts just like [phasor~], except the phase resets with subsample accuracy at the moment the message is sent. I think it's about as accurate as Pd will allow, though I don't pretend to be an expert C programmer or know Pd's api that well. But it seems to be about as accurate as [vline~]. (Actually, I've found that [vline~] starts its ramp a sample early, which is some unexpected behavior.)
[…]
“
- c)
Later I discovered that PD has jittery Midi because it doesn't handle Midi at a higher priority then everything else (GUI, OSC, message-domain ect.)
EDIT:
Tryed roundtrip-midi-messages with -nogui flag:
still some jitter.
Didn't try -nosleep flag yet (see below)
- d)
So I looked into the sources of PD:
scheduler with m_mainloop()
https://github.com/pure-data/pure-data/blob/master/src/m_sched.c
And found this paper
Scheduler explained (in German):
https://iaem.at/kurse/ss19/iaa/pdscheduler.pdf/view
wich explains the interleaving of control and audio domain as in the text of @seb-harmonik.ar with some drawings
plus the distinction between the two (control vs audio / realtime vs logical time / xruns vs burst batch processing).
And the "timestamping objects" listed below.
And the mainloop:
Loop
- messages (var.duration)
- dsp (rel.const.duration)
- sleep
With
[block~ 1 1 1]
calculations in the control-domain are done between every sample? But there is still a 64 sample interval somehow?
Why is [block~ 1 1 1] more expensive? The amount of data is the same!? Is this the overhead which makes the difference? Calling up operations ect.?
Timing-relevant objects
from iemlib:
[...]
iem_blocksize~ blocksize of a window in samples
iem_samplerate~ samplerate of a window in Hertz
------------------ t3~ - time-tagged-trigger --------------------
-- inputmessages allow a sample-accurate access to signalshape --
t3_sig~ time tagged trigger sig~
t3_line~ time tagged trigger line~
--------------- t3 - time-tagged-trigger ---------------------
----------- a time-tag is prepended to each message -----------
----- so these objects allow a sample-accurate access to ------
---------- the signal-objects t3_sig~ and t3_line~ ------------
t3_bpe time tagged trigger break point envelope
t3_delay time tagged trigger delay
t3_metro time tagged trigger metronom
t3_timer time tagged trigger timer
[...]
What are different use-cases of [line~] [vline~] and [t3_line~]?
And of [phasor~] [vphasor~] and [vphasor2~]?
When should I use [block~ 1 1 1] and when shouldn't I?
[line~] starts at block boundaries defined with [block~] and ends in exact timing?
[vline~] starts the line within the block?
and [t3_line~]???? Are they some kind of interrupt? Shortcutting within sheduling???
- c) again)
https://forum.pdpatchrepo.info/topic/1114/smooth-midi-clock-jitter/2
I read this in the html help for Pd:
„
MIDI and sleepgrain
In Linux, if you ask for "pd -midioutdev 1" for instance, you get /dev/midi0 or /dev/midi00 (or even /dev/midi). "-midioutdev 45" would be /dev/midi44. In NT, device number 0 is the "MIDI mapper", which is the default MIDI device you selected from the control panel; counting from one, the device numbers are card numbers as listed by "pd -listdev."
The "sleepgrain" controls how long (in milliseconds) Pd sleeps between periods of computation. This is normally the audio buffer divided by 4, but no less than 0.1 and no more than 5. On most OSes, ingoing and outgoing MIDI is quantized to this value, so if you care about MIDI timing, reduce this to 1 or less.
„
Why is there the „sleep-time“ of PD? For energy-saving??????
This seems to slow down the whole process-chain?
Can I control this with a startup flag or from withing PD? Or only in the sources?
There is a startup-flag for loading a different scheduler, wich is not documented how to use.
- e)
[pd~] helpfile says:
ATTENTION: DSP must be running in this process for the sub-process to run. This is because its clock is slaved to audio I/O it gets from us!
Doesn't [pd~] work within a Camomile plugin!?
How are things scheduled in Camomile? How is the communication with the DAW handled?
- f)
and slightly off-topic:
There is a batch mode:
https://forum.pdpatchrepo.info/topic/11776/sigmund-fiddle-or-helmholtz-faster-than-realtime/9
EDIT:
- g)
I didn't look into it, but there is:
https://grrrr.org/research/software/
clk – Syncable clocking objects for Pure Data and Max
This library implements a number of objects for highly precise and persistently stable timing, e.g. for the control of long-lasting sound installations or other complex time-related processes.
Sorry for the mess!
Could you please help me to sort things a bit? Mabye some real-world examples would help, too.
Ofelia images -> variables
[ofRequire] loads the «M» variable from an [ofelia] object.
Let' say you have this [ofelia function] with name $0-script:
ofelia function $0-script;
M.x = 10;
You could use [ofRequire $0-script] to get the «M» variable defined in the [ofelia] object with name $0-script.
If you do this:
[ofRequire $0-script]
↓
ofelia function $0-another-script;
print(a.x);
This will print «10». What could be confusing is that I now use the «a» variable to access the «M» variable. That's because every float, symbol, pointer is received in the [ofelia function] with the name «a». Like:
[100(
↓
ofelia function $0-another-script;
print(a);
This will print the number «100». Now, since [ofImage] is actually an [ofelia define] object which cointains the variable M.image, if you require this script you will be able to access the image in an [ofelia function] using a.image, like this:
[...]
↓
[ofImage $0-nice-image]
and then:
[ofRequire $0-nice-image]
↓
ofelia function;
print(a.image:getWidth())
This will print the width of the image.
Hope this helps!
ofelia GLSL shader loader
i tried to make a minimalistic example (that does not work):
if type(window) ~= "userdata" then;
window = ofWindow();
end;
;
local canvas = pdCanvas(this);
local clock = pdClock(this, "setup");
local shaderDir = canvas:getDir() .. "/data/";
local shader = ofShader();
local imageDir = canvas:getDir() .. "/data/";
local image1 = ofImage();
local image2 = ofImage();
;
function ofelia.new();
ofWindow.addListener("setup", this);
ofWindow.addListener("draw", this);
ofWindow.addListener("exit", this);
window:setPosition(30, 100);
window:setSize(800 + 40, 600 + 40);
window:create();
if ofWindow.exists then;
clock:delay(0);
end;
end;
;
function ofelia.free();
window:destroy();
ofWindow.removeListener("setup", this);
ofWindow.removeListener("draw", this);
ofWindow.removeListener("exit", this);
end;
;
function ofelia.setup();
ofSetWindowTitle("Add Image");
ofBackground(0, 0, 0, 255);
image1:load(imageDir .. "image1.jpg");
image2:load(imageDir .. "image2.jpg");
shader:load(shaderDir .. "addImage");
end;
;
function ofelia.draw();
shader:beginShader();
ofSetColor(255);
shader:setUniformTexture("Texture0", image1:getTexture(), 0);
shader:setUniformTexture("Texture1", image2:getTexture(), 1);
image1:draw(20, 20, 800, 600);
image2:draw(20, 20, 800, 600);
shader:endShader();
end;
;
function ofelia.exit();
image1:clear();
image2:clear();
shader:unload();
end;
Ofelia - videoPlayer and GLSL Effects
i think it is possible after trying a bit.
what seems important:
-use photo JPG (or something similar) as the codec for the video. a good program for converting is http://www.squared5.com/
-use setPosition and not setFrame for scrubbing or playing reverse through the video (it is much faster)
-playing with setPosition is without sound
-for playing with setPosition the video needs to be paused
it is adapted from the video example and can be optimized a lot to work like the [pixfilm] object...
video4.pd
if type(window) ~= "userdata" then;
window = ofWindow();
end;
;
local canvas = pdCanvas(this);
local clock = pdClock(this, "setup");
local videoPlayer = ofVideoPlayer();
;
function ofelia.bang();
ofWindow.addListener("setup", this);
ofWindow.addListener("update", this);
ofWindow.addListener("draw", this);
ofWindow.addListener("exit", this);
window:setPosition(50, 100);
window:setSize(800 + 40, 600 + 40);
if type(window) ~= "userdata" then;
window = ofWindow();
end;
;
window:create();
if ofWindow.exists then;
clock:delay(0);
end;
end;
;
function ofelia.free();
window:destroy();
ofWindow.removeListener("setup", this);
ofWindow.removeListener("update", this);
ofWindow.removeListener("draw", this);
ofWindow.removeListener("exit", this);
end;
;
function ofelia.setup();
ofSetWindowTitle("Video Player");
ofBackground(0, 0, 0, 255);
end;
;
function ofelia.moviefile(path);
videoPlayer:load(path);
videoPlayer:setLoopState(OF_LOOP_NORMAL);
videoPlayer:setPaused(true);
end;
;
function ofelia.setSpeed(f);
videoPlayer:setSpeed(f / 100);
end;
;
function ofelia.play();
videoPlayer:play();
end;
;
function ofelia.stop();
videoPlayer:setFrame(0);
videoPlayer:setPaused(true);
end;
;
function ofelia.setPaused(f);
if f == 1 then pause = true;
else pause = false;
end;
videoPlayer:setPaused(pause);
end;
;
function ofelia.setFrame(f);
videoPlayer:setFrame(f);
end;
;
function ofelia.setPosition(f);
videoPlayer:setPosition(f);
end;
;
function ofelia.setVolume(f);
videoPlayer:setVolume(f / 100);
end;
;
function ofelia.update();
videoPlayer:update();
end;
;
function ofelia.draw();
ofSetHexColor(0xFFFFFF);
videoPlayer:draw(20, 20, 800, 600);
local outputList = {};
outputList[1] = videoPlayer:getPosition();
outputList[2] = videoPlayer:getCurrentFrame();
outputList[3] = videoPlayer:getTotalNumFrames();
return outputList;
end;
;
function ofelia.exit();
videoPlayer:close();
end;
new vanilla list sort
@ingox If you are looking for source code for any old extended externals they are all in https://sourceforge.net/projects/pure-data/files/pd-extended/0.43.4/Pd-extended_0.43.4-source.tar.bz2/download
Worth grabbing a copy while it remains available.
All the "makefile"s are included.
Useful for compiling externals for 64-bit (when they work).
I have seen the sort message almost hidden in a subpatch in 12-tut.pd in a tutorial on scalars here...... https://puredata.info/community/projects/convention04/lectures/tk-barknecht/tut.tgz
and it is mentioned (again... sort of.... with no explanation of the message call) as a function in Chapter 2.9.1 here...... http://puredata.info/docs/manuals/pd/x2.htm
and so it is also in Pd's \doc\1.manual\x2.htm
Zexy sort below. But it looks like the canvas sort is in g.graph.c.
There is an "if scalar sort" statement in there.
However g.graph.c has been disappeared from 0.49...... so......?
David.
.... sort.c.... (zexy)
In/*
* sort : sort a list of floats
*
* (c) 1999-2011 IOhannes m zmölnig, forum::für::umläute, institute of electronic music and acoustics (iem)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "zexy.h"
/* ------------------------- sort ------------------------------- */
/*
SHELL SORT: simple and easy
*/
static t_class *sort_class;
typedef struct _sort
{
t_object x_obj;
int bufsize;
t_float *buffer;
t_int *indices;
int ascending;
t_outlet*indexOut, *sortedOut;
} t_sort;
static void sort_dir(t_sort *x, t_float f)
{
x->ascending = (f < 0.f)?0:1;
}
static void sort_buffer(t_sort *x, int argc, t_atom *argv)
{
int n = argc;
t_float *buf;
t_atom *atombuf = argv;
if (argc != x->bufsize) {
if (x->buffer) freebytes(x->buffer, x->bufsize * sizeof(t_float));
if (x->indices)freebytes(x->indices, x->bufsize * sizeof(t_int));
x->bufsize = argc;
x->buffer = getbytes(x->bufsize * sizeof(t_float));
x->indices = getbytes(x->bufsize * sizeof(t_int));
}
buf = x->buffer;
while (n--){
*buf++ = atom_getfloat(atombuf++);
x->indices[n] = n;
}
}
static void sort_list(t_sort *x, t_symbol *s, int argc, t_atom *argv)
{
int step = argc, n;
t_atom *atombuf = (t_atom *)getbytes(sizeof(t_atom) * argc);
t_float *buf;
t_int *idx;
int i, loops = 1;
sort_buffer(x, argc, argv);
buf = x->buffer;
idx = x->indices;
while (step > 1) {
step = (step % 2)?(step+1)/2:step/2;
i = loops;
loops += 2;
while(i--) { /* there might be some optimization in here */
for (n=0; n<(argc-step); n++) {
if (buf[n] > buf[n+step]) {
t_int i_tmp = idx[n];
t_float f_tmp = buf[n];
buf[n] = buf[n+step];
buf[n+step] = f_tmp;
idx[n] = idx[n+step];
idx[n+step] = i_tmp;
}
}
}
}
if (x->ascending)
for (n = 0; n < argc; n++) SETFLOAT(&atombuf[n], idx[n]);
else
for (n = 0, i=argc-1; n < argc; n++, i--) SETFLOAT(&atombuf[n], idx[i]);
outlet_list(x->indexOut , gensym("list"), n, atombuf);
if (x->ascending)
for (n = 0; n < argc; n++) SETFLOAT(&atombuf[n], buf[n]);
else
for (n = 0, i=argc-1; n < argc; n++, i--) SETFLOAT(&atombuf[n], buf[i]);
outlet_list(x->sortedOut, gensym("list"), n, atombuf);
freebytes(atombuf, argc*sizeof(t_atom));
}
static void *sort_new(t_floatarg f)
{
t_sort *x = (t_sort *)pd_new(sort_class);
x->ascending = (f < 0.f)?0:1;
x->sortedOut=outlet_new(&x->x_obj, gensym("list"));
x->indexOut=outlet_new(&x->x_obj, gensym("list"));
x->bufsize = 0;
x->buffer = NULL;
inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("direction"));
return (x);
}
static void sort_help(t_sort*x)
{
post("\n%c sort\t\t:: sort a list of numbers", HEARTSYMBOL);
}
void sort_setup(void)
{
sort_class = class_new(gensym("sort"), (t_newmethod)sort_new,
0, sizeof(t_sort), 0, A_DEFFLOAT, 0);
class_addlist (sort_class, sort_list);
class_addmethod (sort_class, (t_method)sort_dir, gensym("direction"), A_DEFFLOAT, 0);
class_addmethod(sort_class, (t_method)sort_help, gensym("help"), A_NULL);
zexy_register("sort");
}
`
webpd slider onchange
I`ve just started using webpd and I would be happy if someone showed me how to properly write a very simple HTML & javascript code connected to the pd patch, which has a slider that fires the moment when the value of the element is changed.
<!doctype HTML>
<html>
<head>
<meta charset="utf-8">
<script type="text/javascript" src="../js/jquery-2.1.0.min.js"></script>
<script type="text/javascript" src="../js/webpd-latest.js"></script>
<script type="text/javascript" src="../js/elindit.js"></script>
</head>
<body>
<button id="startButton">Start</button>
<div id="controls">
<form>
<input type="text" id="freqInput" />
<input type="submit" value="Set frequency" />
</form>
</div>
<script type="text/javascript">
webPdLali.init()
$('form').submit(function(event) {
event.preventDefault()
Pd.send('freq', [parseFloat($('#freqInput').val())])
})
var patch
$.get('pd/main.pd', function(mainStr) {
// Loading the patch
patch = Pd.loadPatch(mainStr)
webPdLali.patchLoaded(mainStr)
})
</script>
</body>
</html>
pointer evaluation in Pd
Hello,
I tried to post this to the Pd mailing list but for some reason it didn't go through. I'll repost it here.
Hi list,
[namecanvas foo]
[traverse foo, bang(
|
[pointer]
|
[$1, $1 two(
|
[print]
Currently, evaluating the binbuf "$1, $1 two" will silently fail to output anything. That's clearly wrong.
Now, consider this:
[namecanvas foo]
[traverse foo, bang(
|
[pointer]
|
[print]
Here, the outgoing pointer calls the pointer method of the [print] object. There are many other objects which have a defined pointer method.
So if we want the behavior to be the least surprising as well as the least likely to cause bugs, the "$1" above should be equivalent to just sending the output from [pointer].
That would mean that a message box that expands to a single gpointer should trigger the pointer method for the target object (i.e., the object the message box connects to). That will ensure it triggers the pointer methods for classes which define one, as well as triggering the default Pd pointer handling for classes which don't.
This change seems pretty needed. I see all kinds of patches in the wild where users are doing weird things in object chains to handle pointers. It appears these users are hitting this bug and just pentesting the message until the gpointer finally flows out of the message box. (Which can happen with "list $1", or even "wtf $1" with a list split and trim following it.)
The remaining question is what to do about "$1 two."
It would be nice to convert it to a list in that case. That would match the behavior of Pd messages with leading numbers, as well as keeping with the common knowledge that Pd messages begin with a symbolic selector (or at least an implied one). However, gpointers break that pattern because there was no "pointer" selector reserved, implied or otherwise, in Pd. Thus, arbitrary messages beginning with the selector "pointer" can be followed by arbitrary arguments which in no way imply a gpointer payload. For example, "pointer 50 25" can be sent to [route pointer] which will happily output "50 25".
That means that going forward there's no way to reserve "pointer" in the way that, say, "float" is reserved. For example, if you try to type "float boat" in a message box Pd will tell you that "boat" is a bad argument for the "float" message. It's not allowed. And if we made a requirement that only a gpointer arg may follow the "pointer" selector it would probably break some existing patches in subtle ways.
So there are only two sensible behaviors left:
- Error out in message boxes for a multi-arg message that has a gpointer selector
- Call pd_anything to handle the "$1 two" example above.
I guess the determining factor would be whether multi-arg messages with interspersed gpointers are currently being used at all. Unfortunately, they probably are since [struct] will output messages like "click (gpointer) 50". Simply using a [route click] will then output a multi-arg message that has a gpointer selector.
That means Pd is already implicitly supports sending around "(gpointer) arg1 arg2 etc." messages to arbitrary objects. And I assume that any future crashers from that would be fixed. So it's probably no worse to allow message boxes to forward on such messages using pd_anything.
Thoughts? If not I'm probably going to start building some tests for this and
implement it in Purr Data after the next release.
-Jonathan