how to get dynamically updated values into Pd from Python
First off thank you all for your help and suggestions!
I managed to find a solution with a little help from my friends.
So, my main problem turned out to be that I needed to create my socket BEFORE defining my function. Then only after both of these are done I could set up the string to send to Pd (because Python does this sort of weird recursivey thing).
Also as some of you suggested my string was named badly, first I was using a the wrong variable data type ("b") then I tried to make a string using "str" as a name which confuses Python as "str" is the string data type. So for anyone else dealing with this be sure to call your string something like "data" or "lsdfdjkhgs" but not "str"!
Anyway, it works now and I can send data over to [netrecieve]
incidentally @whale-av 's suggestion to use Pd's built in messaging scripts located in the application folder (pdsend & pdreceive) brought up a lot of interesting possibilities that I will be exploring later and for anybody wanting to send messages between Pd and other programs this looks like a cool way to do it.
anyway, I will share the code here in hopes it will help someone else:
import socket
from obspy.clients.seedlink.easyseedlink import create_client
# first get all the socket declaration out of the way
s = socket.socket()
host = 'localhost'
port = #put (your) open port here, this is also part of Pd's [netreceive] argument
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
# then we define our callback which happens when we receive data
# out socket is already set up so we can reference it here in the callback
def handle_data(trace):
# trace is an obspy.trace class so we must convert to string
# I have to mess around with this "trace" because it is part of the class my client code is using
data = str(trace)
# we can then neatly append a ; with a "f-string" to make it work with Pd's FUDI protocol (Pd messages end in ";")
data = f'{data};'
# or
# data = str(trace) + ';'
# if you like
# and send it over our socket
s.sendall(data.encode('utf-8'))
#then AFTER all this my code connecting to the server (that returns the data as a string "trace" above, but the entire part for communicating with Pd is up there, and surprisingly simple despite the 'strange loops'!
Logarithmic glissando
One of the very early lessons that I teach in my interactive multimedia class is range mapping, where I derive the formulas and then leave them with ready-to-use patch structures for them.
-
If it's based on incoming data, first normalize (0 to 1, or -1 to +1, range). If you're generating a control signal, generate a normal range (e.g. [phasor~] is already 0 to 1).
-
For both linear and exponential mapping, there's a low value
lo
and a high valuehi
. (Or, if the normalized range is bipolar -1 to +1, a center value instead oflo
.) -
The "width" of the range is: linear
hi - lo
, exponentialhi / lo
. -
Apply the width to the control signal by: linear, multiplying (
(hi - lo) * signal
); exponential, raising to the power of the control signal =(hi / lo) ** signal
. -
Then (linear) add the lo or center; (exponential) multiply by the lo or center.
One way to remember this is that the exponential formula "promotes" operators to the "next level up": +
--> *
, -
--> /
, *
--> power-of (and /
--> log, but that would only be needed for normalizing arbitrary exponential data from an external source). So if you know the linear formula and the operator-promotion rule, then you have everything.
- linear: (width * signal) + lo
- exponential: (width ** signal) * lo
(Then the "super-exponential" that bocanegra was hypothesizing would exponentiate twice: ((width ** signal) ** signal) * lo
= (width ** (signal * signal)) * lo
.)
[mtof~] is a great shortcut, of course, but -- I drill this pretty hard with my students because if you understand this, then you can map any values onto any range, not only MIDI note numbers. IMO this is basic vocabulary -- you'll get much further with, say, western music theory if you know what is a major triad, and you can go much further with electronic music programming if you learn how to map numeric ranges.
hjh
Scripting Purr Data - with JavaScript?
Hi all,
Has been a while since I've used Pure Data. Now I'm about to do a small project, which I should share with collaborators, which do not have Pure Data background, however, are comfortable with JavaScript. For this, I'm considering doing some patches in Purr Data.
Back in the days, I was aware of the following:
(via https://newcome.wordpress.com/2013/12/29/scripting-pure-data/ ; https://forum.pdpatchrepo.info/topic/9650/best-way-to-write-code-in-pd):
py
/pyext
( http://puredata.info/Members/thomas/py/ ) - allows using the Python language to define/implement a Pure Data objectpdlua
- ( https://github.com/agraef/pd-lua ) allows using the Lua language to define/implement a Pure Data objectpdj
( http://puredata.info/downloads/pdj ) - "PDJ enables you to write java code to interact with pure-data objects"
I have tried these in latest Purr Data on Windows - pdlua
is still there; however, no trace of pyext
/py
.
Now, I was wondering - I am aware that Purr Data is partially based on JavaScript - and also, my collaborators are mostly comfortable with JavaScript too; so, I'd just like to confirm:
- is there a way to define/implement a Pure Data object using JavaScript as a scripting language (say, through something like a
[pdjs myscript.js]
object)? Just to specify, I do not really need to do audio-rate calculations with this, only control-rate calculations of data on inlets ...
I guess there isn't such a thing/object, as I would have probably found it by now - however, this with Purr Data being somewhat based on JavaScript just keeps staying in the back of my head, so it would be nice to get an explicit "no" if there isn't.
Thanks in advance for any answers!
Taking and saving an image to a file every 3 seconds
@Still said:
I was wondering if there are any examples of using PD to take images from a USB camera every 3 seconds or so and saving the image out to individual filenames that includes the time stamp in the filename.
My goal is to create a poor mans DAQ (data acquisition) using PD, a multi-meter, and image recognition.
My thought process:
PD plays audio signal that increases over time for a duration of 3 second, then appends the signal value that was played to a text file with a time stamp.
Have usb take camera shot every 3 seconds of a digital multi-meter screen and output that to a file with the time stamp in the filename.
Use machine learning / OCR on image files and append the decoded OCR numerical data to another text file that includes a time stamped text value from the filename so I can sync up the original signal played and the value that comes from the multi-meter output.
Plot the values from text file showing input signal vs output signal
PS I'm using Ubuntu 20.04 Linux.
If anyone has another way of doing this please let me know.Thanks
@whale-av said:
@Still What is the multi-meter reading?
If it is an audio level then you could bring the data back into Pd from a microphone and plot the level against the level of output from Pd.
Image capture could be done in Pd with GEM but the data will be huge and OCR is not a Pd function that I know of.
Using a microphone instead would bypass those stages....... and the plot could even be presented in real time.
David.
It's a voltage level around 200+ volts. It's measuring non-linear vortex inductor coils. When you input different audio signals it outputs very high voltages and very low voltages .1 to 200+ volts (very non linear). So I don't think I could bring that data back in easily. I would do the OCR using python or Octave.
JASS, Just Another Synth...Sort-of, codename: Gemini (UPDATED: esp with midi fixes)
JASS, Just Another Synth...Sort-of, codename: Gemini (UPDATED TO V-1.0.1)
jass-v1.0.1( esp with midi fixes).zip
1.0.1-CHANGES:
- Fixed issues with midi routing, re the mode selector (mentioned below)
- Upgraded the midi mode "fetch" abstraction to be less granular
- Fix (for midi) so changing cc["14","15","16"] to "rnd" outputs a random wave (It has always done this for non-midi.)
- Added a midi-mode-tester.pd (connect PD's midi out to PD's midi in to use it)
- Upgrade: cc-56 and cc-58 can now change pbend-cc and mod-cc in all modes
- Update: the (this) readme
INFO: Values setting to 0 on initial cc changes is (given midi) to be expected.
JASS is a clone-based, three wavetable, 16 voice polyphonic, Dual-channel synth.
With...
- The initial, two wavetables combined in 1 of 5 possible ways per channel and then adding those two channels. Example: additive+frequency modulation, phase+pulse-modulation, pulse-modulation+amplitude modulation, fm+fm, etc
- The third wavetable is a ring modulator, embedded inside each mod type
- 8 wave types, including a random with a settable number of partials and a square with a settable dutycycle
- A vcf~ filter embedded inside each modulation type
- The attack-decay-release, cutoff, and resonance ranges settable so they immediately and globally recalculate all relevant values
- Four parameters /mod type: p1,p2, cutoff, and resonance
- State-saving, at both the global level (wavetables, env, etc.), as well as, multiple "substates" of for-each-mod-type settings.
- Distortion, reverb
- Midiin, paying special attention to the use of 8-knob, usb, midi controllers (see below for details)
- zexy-limiters, for each channel, after the distortion, and just before dac~
Instructions
Requires: zexy
for-entire-state
- O: Open preset. "default.txt" is loaded by...default
- S: Save preset (all values incl. the multiple substates) (Note: I have Not included any presets, besides the default with 5 substates.)
- SA: Save as
- TEST: A sample player
- symbol: The filename of the currently loaded preset
- CL: Clear, sets all but a few values to 0
- U: Undo CL
- distortion,reverb,MASTER: operate on the total out, just before the limiter.
- MIDI (Each selection corresponds to a pgmin, 123,124,125,126,127, respectively, see below for more information)
- X: Default midi config, cc[1,7,8-64] available
- M: Modulators;cc[10-17] routed to ch1&ch2: p1,p2,cutoff,q controls
- E: Envelopes; cc[10-17] routed to filter- and amp-env controls
- R: Ranges; cc[10-17] routed to adr-min/max,cut-off min/max, resonance min/max, distortion, and reverb
- O: Other; cc[10-17] routed to rngmod controls, 3 wavetypes, and crossfade
- symbol: you may enter 8 cc#'s here to replace the default [10-17] from above to suit your midi-controller's knob configuration; these settings are saved to file upon entry
- vu: for total out to dac~
for-all-mod-types
- /wavetable
- graph: of the chosen wavetype
- part: partials, # of partials to use for the "rn" wavetype; the resulting, random sinesum is saved with the preset
- duty: dutycycle for the "du" wavetype
- type: sin | square | triangle | saw | random | duty | pink (pink-noise: a random sinesum with 128 partials, it is not saved with the preset) | noise (a random sinesum with 2051 partials, also not saved)
- filter-env: (self-explanatory)
- amp-env: (self-explanatory)
- rngmod: self-explanatory, except "sign" is to the modulated signal just before going into the vcf~
- adr-range: min,max[0-10000]; changing these values immediately recalculates all values for the filter- and amp-env's scaled to the new range
- R: randomizes all for-all-mod-types values, but excludes wavetype "noise"; rem: you must S or SA the preset to save the results
- U: Undoes R
for-each-mod-type
- mod-type-1: (In all cases, wavetable1 is the carrier and wavetable2 is the modulator); additive | frequency | phase | pulse | amplitude modulation
- mod-type-2: Same as above; mod-type-2 May be the same type as mod-type-1
- crossfade: Between ch1 and ch2
- detune: Applied to the midi pitch going into ch2
- for-each-clone-type controls:
- p1,p2: (self-explanatory)
- cutoff, resonance: (self-explanatory)
- navigation: Cycles through the saved substates of for-each-mod-type settings (note: they are lines on the end of a [text])
- CP: Copy the current settings, ie. add a line to the end of the [text] identical to the current substate
- -: Delete the current substate
- R: Randomize all (but only a few) substate settings
- U: Undo R
- cut-rng: min,max[0-20000] As adr-range above, this immediately recalculates all cutoff values
- res-rng: min,max[0-100], same as previously but for q
- pbend: cc,rng: the pitchwheel may be assigned to a control by setting this to a value >7 (see midi table below for possibilities); rng is in midi pitches (+/- the value you enter)
- mod-cc: the mod-wheel may be assigned to a control [7..64] by setting this value
midi-implementation
name | --- | Description |
---|---|---|
sysex | not supported | |
pgmin | 123,124,125,126,127; They set midi mode | |
notein | 0-127 | |
bendin | pbend-cc=7>pitchbend; otherwise to the cc# from below | |
touch | not supported | |
polytouch | not supported |
cc - basic (for all midi-configs)
# | name | --- | desciption |
---|---|---|---|
1 | mod-wheel | (assignable) | |
7 | volume | Master |
cc - "X" mode/pgmin=123
cc | --- | parameter |
---|---|---|
8 | wavetype1 | |
9 | partials 1 | |
10 | duty 1 | |
11 | wavetype2 | |
12 | partials 2 | |
13 | duty 2 | |
14 | wavetype3 | |
15 | partials 3 | |
16 | duty 3 | |
17 | filter-att | |
18 | filter-dec | |
19 | filter-sus | |
20 | filter-rel | |
21 | amp-att | |
22 | amp-dec | |
23 | amp-sus | |
24 | amp-rel | |
25 | rngmod-freq | |
26 | rngmod-sig | |
27 | rngmod-filt | |
28 | rngmod-amp | |
29 | distortion | |
30 | reverb | |
31 | master | |
32 | mod-type 1 | |
33 | mod-type 2 | |
34 | crossfade | |
35 | detune | |
36 | p1-1 | |
37 | p2-1 | |
38 | cutoff-1 | |
39 | q-1 | |
40 | p1-2 | |
41 | p2-2 | |
42 | cutoff-2 | |
43 | q-2 | |
44 | p1-3 | |
45 | p2-3 | |
46 | cutoff-3 | |
47 | q-3 | |
48 | p1-4 | |
49 | p2-4 | |
50 | cutoff-4 | |
51 | q-4 | |
52 | p1-5 | |
53 | p2-5 | |
54 | cutoff-5 | |
55 | q-5 | |
56 | pbend-cc | |
57 | pbend-rng | |
58 | mod-cc | |
59 | adr-rng-min | |
60 | adr-rng-max | |
61 | cut-rng-min | |
62 | cut-rng-max | |
63 | res-rng-min | |
64 | res-rng-max |
cc - Modes M, E, R, O
Jass is designed so that single knobs may be used for multiple purposes without reentering the previous value when you turn the knob, esp. as it pertains to, 8-knob controllers.
Thus, for instance, when in Mode M(pgm=124) your cc send the signals as listed below. When you switch modes, that knob will then change the values for That mode.
In order to do this, you must turn the knob until it hits the previously stored value for that mode-knob.
After hitting that previous value, it will begin to change the current value.
cc - Modes M, E, R, O assignments
Where [10..17] may be the midi cc #'s you enter in the MIDI symbol field (as mentioned above) aligned to your particular midi controller.
cc# | --- | M/pgm=124 | --- | E/pgm=125 | --- | R/pgm=126 | --- | O/pgm=127 |
---|---|---|---|---|---|---|---|---|
10 | ch1:p1 | filter-env:att | adr-rng-min | rngmod:freq | ||||
11 | ch1:p2 | filter-env:dec | adr-rng-max | rngmod:sig | ||||
12 | ch1:cutoff | filter-env:sus | cut-rng-min | rngmod:filter | ||||
13 | ch1:q | filter-env:re | cut-rng-max | rngmod:amp | ||||
14 | ch2:p1 | amp-env:att | res-rng-min | wavetype1 | ||||
15 | ch2:p2 | amp-env:dec | res-rng-max | wavetype2 | ||||
16 | ch2:cutoff | amp-env:sus | distortion | wavetype3 | ||||
17 | ch2:q | amp-env:rel | reverb | crossfade |
In closing
If you have anywhere close to as much fun (using, experimenting with, trying out, etc.) this patch, as I had making it, I will consider it a success.
For while an arduous learning curve (the first synth I ever built), it has been an Enormous pleasure to listen to as I worked on it. Getting better and better sounding at each pass.
Rather, than say to much, I will say this:
Enjoy. May it bring a smile to your face.
Peace through love of creating and sharing.
Sincerely,
Scott
Windowed-sync oscillator: Style questions
@jameslo "Since many programmers seem to want to avoid [fexpr~] at all costs, maybe you could rewrite your [fexpr~] with [expr~ $v1 > $v2] and [rzero_rev~ 0]"
Hmm, yeah. I had thought that both [expr~] and [fexpr~] were said to be bad for performance.
I suppose this might be another way to do a signal-rate comparison:
There's a very small chance of the [-~] result being between 0 and 1e-30, where the clip~ value would be between zero and one but not exactly either. With typical signal magnitudes, that's unlikely, so this wouldn't work in cases where only 0 and 1 are acceptable.
+1 RE the mystifying omissions and irregularities in vanilla. Since I started thinking of it as a scripting language (and stopped comparing it to c, c++, c#, Java...) everything became happy and easy-going.
Yes and no... Pd is an important and valuable tool and it's good to make friends with it as it is. At the same time, lack of some core operators can be seen as a usability issue.
I definitely wouldn't compare it to C and such. Miller Puckette himself acknowledges[1] that many algorithms are straightforward to write in procedural languages (and maybe even more straightforward in functional languages), but rip-your-hair-out painful in dataflow patchers. E.g., quicksort, which in Haskell goes:
quicksort :: Ord a => [a] -> [a]
quicksort [] = []
quicksort (p:xs) = (quicksort lesser) ++ [p] ++ (quicksort greater)
where
lesser = filter (< p) xs
greater = filter (>= p) xs
and in C is... somewhat longer but still a fairly straightforward recursive implementation.
I can literally not even imagine how to implement a quicksort in Pd.
In SuperCollider, I'm doing most of my performances using a sequence-scripting language of my own design, where SC code is parsing the expressions and translating them into SC patterns. That's been a long project but within reach of an object-oriented language with a full suite of data structures. Pd... again, I can't imagine where to begin.
Puckette points out in that interview that Max and Pd are designed to react to input events, and they are very elegant for this. In Pd, you can connect a slider to a number box. In SC, the same is:
(
var number, slider;
w = Window("slider", Rect(800, 200, 500, 400)).front;
w.layout = VLayout(
nil,
HLayout(
number = NumberBox().fixedWidth_(80),
slider = Slider().orientation_(\horizontal)
),
nil
);
slider.action = { |view|
number.value = view.value;
};
)
In that case, definitely, Pd's expression of the idea of a value flowing from an interface object toward the display object is as concise as you can imagine, and SC's version is verbose and puzzling until you get used to it. Use the tool that's right for the job.
lacuna:
There are [vphasor~], [vphasor2~] and [vsamphold~] from @Maelstorm .
Oh that's good.
It's hard to find extensions like this, if you don't know where it is.
In your patch [rpole~ 1] is working with a signal-inlet, isn't it?
Yes. The idea is, while the (signal) coefficient is 1, then the left-hand signal gets integrated. If, for a single sample, both the signal and coefficient are 0, then the output is 0, and it will start integrating again as soon as the coefficient flips back to 1.
Which is that other forum you mentioned?
TBH I think SC wins in terms of clear expression of this synthesis algorithm:
(
{
var sync = LFTri.ar(SinOsc.kr(0.2).exprange(100, 400));
var phase = Sweep.ar(sync, SinOsc.kr(0.12743).exprange(700/3, 2100));
var synced = SinOsc.ar(0, (phase % 1) * 2pi);
dup(LeakDC.ar(synced * sync) * 0.1)
}.play;
)
This is part of what I mean by "usability issues" -- Pd: 7 objects for the triangle wave vs SC: LFTri.ar(freq)
, or the automatic exponential scaling exprange
, and Sweep has a signal-rate retrigger built-in (there's also a range-rewrapping Phasor, and it has an audio-rate trigger too)... SC's initial learning curve is steeper but once you know it well, it took a couple of minutes to write that, vs 30-40 minutes (including head-scratching time) in Pd. (Admittedly I'm less fluent in Pd.)
hjh
[1] https://omny.fm/shows/future-of-coding/47-maxmsp-pure-data-miller-puckette
To Purr or not to Purr?
@Duckett You should start with Pure Data Vanilla first. When you know the programming language environment, then you can switch to Pure Data extended or Purr Data or Pure Data on Raspberry etc. I do not recommend jumping into a fork version of Pure Data full of many libraries because this can lead to confusion. With Pure Data Vanilla you can build externals, so you can add very complicated objects by yourself to do anything you want (but this is for a later objective). Also i recommend that you first try to use only Pure Data. Connecting Pure Data to other tools is possible but this is not fundamental. Start small and simple then go crazy and optimize and change what you know. A little note about the GUI of Pure Data Vanilla. The idea of the GUI is very smart. You can build your own GUI in a very fancy way but it requires a lot of UI knowledge (not fundamental). Here is an example snapshot of a GUI that i made with Pure Data Vanilla.
Here is something for you start with in Pure Data :
Here is a more advance GUI using Pure Data extended
You could also make this in Pure Data if you want :
[array] / [text] Pointer
@Jona From what I've saw, i'd say that, arrays pointing to data structures are "data structure" efficient; maybe arrays are actualy data structures. The text object is realy slow compared.
The fastest combo is:
- data structures
- array get -s -f, the data structure flags / get /set /element to call and write the data,
- list store, to store the pointer, to move among the scalars, if there is no solution to set the pointers at creation time.
I call it the pointer's combo.
I only use text for:
1- write and open text file ;
2- store a set of data of variable size, since creating and deleting data structures can lead to audio crack, memory access problem maybe. But i don't count on accessing those data very often.
When I create a patch that computes a lot of data, the engine core and the display should be made with the pointer's combo , and only the slow parts should use text. Input gui elements should be used as inputs, not outputs . NO GUI ELEMENTS SHOULD BE ON THE PATH OF DATA COMPUTING.
The usage of data structure is justified for data computation as much as it is for data visualisation, because they are "sound safe" : they won't cause audio click.
Dynamic allocated Stack structure in Pure Data ??
@whale-av Yes. It is working with floats but when i try to mess with the data types it gets slower. I would really like to have a more universal stack structure to hold various types of data. The idea is to have a simple structure with complex data. Do you have any idea of some external library to handle different data types then Pure Data ? or i just need somehow to stick with Pure Data data types ? I was also thinking about data type conversion. Like if i need a complex data type maybe i can brake it down to a bunch of simple data types using an idea about data type symmetry. Many thanks anyway. I need to learn more about Pure Data stuff. I don't have extensive knowledge about Pure Data but the concept is brilliant.
i/o-errors in pd
Has this problem been solved in any of pds "forks" yet?
In Purr Data we've addressed a few of the GUI updating problems:
- data structures use Pd's extant guiqueue system to keep from sending unnecessary graphical update messages to the GUI. I noticed that the pacdata game (pacman using data structures) runs smoothly on Purr Data for this reason. It probably still uses lots of CPU and would cause dropouts during complex audio computation, though.
- data structure array elements can react to mouse events very efficiently. This is because they don't constantly recalculate their bounding rectangle on every single mouse motion like everything else in Pd does.
- moving a selection of objects in Purr Data only requires a single message between core and GUI, whereas in Pd it requires a separate message for each object
- complex data structures cache their bound rectangle
- some small optimizations for iemgui messages that get sent to the GUI
- text box edition (e.g., typing stuff inside an object/message box) happens completely inside the GUI. So you can paste an enormous amount of text and it won't get sent Pd until you're ready to instantiate the object.
One of the biggest pains for users is the constant polling of bounding rectangles for objects-- it happens any time you move the mouse in a patch window. Someone made an introductory patch to solve this but it's not complete so it hasn't been merged.
I've got an idea about how to move all the logic into the GUI. Still, it's a big undertaking and I'm not sure I have sufficient time to properly implement atm. And notice that all that hard work would only solve 1/4 of your issues. Dropouts from resizing a table are a separate issue. Message-passing overhead is a separate issue, too.