Shell object, maybe bugged
Does any command work in the [shell] object that you are using? If you run something simple, like 'ls' or 'pwd' will it output the file list of the directory? If so, then there could likely be a formating issue with the text that you are sending to the object. The way that puredata handles sending certain symbols and characters through messages can sometimes throw a command askew when translating to bash. To make things harder, the front slash that is typically used as an escape symbol to send symbols as literals is not allowed in messages within pd... So if the [shell] object is functional, but your particular command just doesn't seem to want to work properly, then you can try the following:
1 - make use of objects that convert text in messages to and from symbols, like [makesymbol] [cyclone/tosymbol] [cyclone/fromsymbol] [s2l] [l2s] etc.
2 - try sending commands to shell via [text] or [coll].
3- when all else fails, or a script is just too complex or uses many odd characters, open a code editor like kate (or textmate on mac,) and type the code in there. Try running it in a terminal to make sure it works. Save the file, and then in terminal run
sudo chmod +x "/path/to/file/filename"
enter your password and now that file will be marked by the OS as executable. You can call it from the [shell] object by simply sending it the path to the file, or cd <path>; ./<filename>
If your script requires variables that change in pd, like $1 $2, then you can set that up in your script by defining variables and then sending those values as in separate messages. for example, in your code, you could start by defining a variable like
foo=0;
then in pd
[nbx]
|
[foo=$1(
|
[shell]
After you send the change in value to the variable, call the script again and it will output with the updated values.
In sticky situations in shell, this trick has saved the day for me many times. Hope that helps.
Playing sound files based on numbers/sets of numbers?
@whale-av Hello again, thank you very much for replying so quickly!
Ok so, the computer will always be a PC and will always have Pd installed so that makes things a lot easier I suppose (not sure what version it will be though, but since all those computers have only recently been equipped with Pd I'm assuming it's either vanilla or extended so it should be ok).
I just put my 17 sound files in the same folder and named them 1.wav ...... 17.wav. They are in the same folder as your patch now too.
Well, the numbers are not exactly "set", in fact, each sound file corresponds to a certain range of numbers. Ugh, I did it again, forgot to mention the range... Sorry..... I'd probably need a different object for a range... It's still kinda difficult for me to think in "computer" language about things related to music so that's why I'm forgetting to mention tons of stuff, but I'll try to fix that in this reply, sorry again!
I will just post the table to make it easier for you to understand.
But first, I would just like to explain how the table works. First I need to ask if it's possible to attribute the same numbers/range to different sound files, depending on the number's "place" within the combination? (You'll understand once you see the table.) Maybe it isn't and in that case I should think of a way to change the table according to what can be done?
So, there should be 13 numbers in total within the combination (13 numbers to type, like 13 empty slots waiting to be filled with numbers), but I said there are five sub-combinations. The first, second and fourth are 2 digit numbers, the third is a 3 digit number and the fifth is a 4 digit number. Obviously, three of these are 2 digit numbers, but the thing is I can't really choose the ranges (I have a set range for each sub-combination and can only try to figure out the easiest way to work with them) and the ranges are actually intertwined, So some numbers can be repeated, that's why I'm asking if the same numbers can be attributed to different sound files based on whether it's the first, second or fourth sub-combination. If that's too complicated or even impossible, I can only think of sacrificing a couple of sound files and just let the program play the same file twice instead.
Also important: is it possible to "condition" to program into playing a sound file based on the first combination, the first 2 digit number? Simply put through simple chords, say in the first combination you can type either 01 or 02, where 01 is A major and 02 is B major. Next, you can type in either 02 or 03, where 03 is E major (irrelevant right now), but 02 can be either D major or F sharp major (not A major like in the first combination), based on what the previous number was. If it was 01 (A major), then this time 02 will be D major, but if the first number was 02 (B major) then the 02 in the second combination should be F sharp major. IS something like this even possible, or do I need to figure out a way to go around this and alter my table? I CAN alter the table, but I can't work with the ranges of the combinations, or the number of the combinations, it must be 5.
Lastly, can different numbers be attributed to the same sound file based on their position within the combination?
Ok so finally here's the table, and I will just give you my current table so you can tell me if it's possible to do. I understand if it's too complicated and I need to change it! Maybe you can give me some tips on how to change it to make it the easiest to do in Pd.
1st SUB-COMBINATION
01-19 - 1.wav
20-22 - 2.wav
23-31 - 3.wav
2nd SUB-COMB.
01-03 - 4.wav (if the first was 01-19 or 20-22; or shorter, 01-22)
01-03 - 5.wav (if the first was 23-31)
04-06 - 6.wav
07-09 - 7.wav (if the first was 01-19)
07-09 - 8.wav (if the first was 20-31)
10-12 - 9.wav (if the first was 01-19)
10-12 - 10.wav (if the first was 20-22)
10-22 - 11.wav (if the first was 23-31)
3rd SUB-COMB.
900-975 - 4.wav (if the first was 01-19)
900-975 - 2.wav (if the first was 20-22)
900-975 - 12.wav (if the first was 23-31)
976-999 and 000-020 - 1.wav (if the first was 10-22)
976-999 and 000-020 - 13.wav (if the first was 23-31)
4th SUB-COMB.
01-09 - 7.wav (if the first was 01-19)
01-09 - 8.wav (if the first was 20-31)
10-69 - 14.wav (if the first was 01-19)
10-69 - 3.wav (if the first was 20-31)
70-99 - 6.wav (if the first was 01-22)
70-99 - 11.wav (if the first was 23-31)
5th SUB-COMB.
(this one is arbitrary so no matter what the last 4 numbers are, the sound file that is triggered should be based on the first combination, or something like that, so...)
xxxx - 15.wav (if the first was 01-19)
xxxx - 16.wav (if the first was 20-22)
xxxx - 17.wav (if the first was 23-31)
Now, I need to thank you if you had the patience to actually read all that and think about it..... I can only hope you could understand the idea, but please, if this is just too complicated, do tell me to change the table. Maybe, if you can or think I will understand, try to explain how the ranges work when playing sound files and how I can use that to my advantage to still keep all the sound files (or as many as possible) but make the thing easier to program.
Now I know I had a few more questions but I can't remember right now and I don't want to bombard you with everything all at once, I realize even this is way more than I could have asked for, you have been a huge help, really! Even if I don't entirely understand every single element of your patches, I did study them and used the help option like you advised me to, so I really am trying to get the hang of this. I completely understand if you just want to take a break from this if it's too complicated and time-consuming, I just ask that you give me some kind of feedback so I know what to expect!
Anyway, thank you for all your help and guidance until now, and for the guidance you'll provide in the future if you so choose!
Pure Data noob
Ok...... So I have been meaning to do this for a very long time......
I don't think it is the perfect "show_me_dollars" and so I will change it from time to time.
3rd attempt.........
show_me_dollars.zip
Here is a really terrifying screenshot, but I have also tried to explain it in words.
In many ways I think words ( below the screenshot) are easier to understand.
David.
Dollar $ variables in Pure Data patches.
A dollar variable is a thing that can be given a new value.
The new value can be a float or a symbol.
- If the Dollar variable is in an [object] box
A Pd patch can be saved and used inside another patch. We then call it an abstraction.... and it is just like a programming sub-routine.
If you want to use it many times then you have a problem, that they are all the same, so if you put an object [receive woof] they will all receive any message that you send with [send woof].
That might well be what you want to do.
But what if you want to send the message to only one of them?
You can give it an [inlet], but your patch will get messy, and what if your patch needs to make its own mind up about which abstraction it wants to send the message to, maybe depending on which midi note it received?
The solution is to give the abstraction arguments... some parameters that define it and make it different to the other copies.
For example [my_abstraction]
Let’s give it some arguments [my_abstraction 5 9 woof]
Inside the abstraction, as it is created (you open its parent patch) the dollar variables will be replaced. Wherever you see $1 written IN AN OBJECT it has been replaced by the number 5.
Number 5 because 5 is the first argument and has actually replaced the $1. You still see $1, but if you bang a [$1] object it will output 5.
[f $2] will output 9
[symbol $3] will output woof
So if you have an object [receive $1-$3] then it has now become [receive 5-woof]
And if you want to send it a message from outside, from another patch or abstraction, you will need to use [send 5-woof]
Every Pd patch, which remember includes your abstractions, also has a secret number. The number is unique and greater than 1000. As Pd opens each patch it gives it the number, increased by one from the last number it gave.
That number will replace $0 as the patch is created. You can find out what the number is by banging a [$0] object and connecting its output to a number box, or [print] object.
$0 can be used in any object as part of the name or the address, which means that a message cannot escape from the abstraction. A sub-patch like [pd my-subpatch] will be given the same number.
But from outside your abstraction you don’t know what it will be when the patch is created, so it is not useful. (A lie, you can find out, but as it can change every time you open your patch it is not worth the bother).
Use it to send messages within your patch [send $0-reset] to [receive $0-reset] for example, because the message is absolutely unique to its window, so you know it cannot interfere with other abstractions.
Use it also for objects like [delwrite~ $0-buffer 100] or for an array name [array $0-array] so that in each abstraction they have a different name and you will not have problems with their being "multiply defined"...... as each name can only exist once in your patch.
- If the Dollar $ variable is in a [message( box
Dollar $ variables are also replaced, but not as the patch is created (drawn by Pd as you open it).
Dollar zero $0 has no meaning in a message box. It will produce a zero if the message is banged, but that is it.
It is a mistake, a patching error, to put a $0 in a message box.
$1 $2 $3 $4 etc. in a message box are replaced by incoming atoms (individual floats or symbols or whatever) when they arrive. $1 will be replaced by the first atom in the list, $2 the second etc.
So if you have a message box [$1 $2 $3( ..... and you send into it a list [3 48 lala( .....then it will output 3 48 lala
That is not really very useful.
But it is actually very powerful.
Make a list in a message box........ [33 12 wav(
And bang it into a message box [open my-track$2-$1.$3( and you will get the output.........
open my-track12-33.wav
Which could be just the message that you want to send to [soundfiler]
P.S. If the first item in the incoming list is a symbol then it will be dropped causing errors.
You can fix that by making the message a list by passing it through the object [list].
Unfortunately only messages starting with a float are automatically recognised as lists.
getting the sent symbol of a number atom
If you want to send a text message with a number you just make a message box containing the info you want to send and connect it to [send xyz] and then bang the message at the same time you send the variable, then you can [receive xyz] at the other end. (if you want to do that automatically whenever you change a number then just connect the output of the number box to the input of the message box).
You can't send both a number and text in the same send & receive box unless you start packing them into lists... probably simpler just to use 1 send & receive pair for the number and 1 send & receive pair for the text.
It's not that elegant!
In writing sequences I haven't found a need to send text with numbers. Just label stuff with comments to help you see where it's going.
I haven't seen your patch but possibly a way to make it more efficient is to bear in mind that if the numbers for each step are stored in the number boxes or sliders used to edit the sequencer, then they don't need to be additionally stored anywhere else unless you are saving/loading patterns. If you want pattern storage then, yes, you will need some pretty intense messaging to exchange the data between the displayed/currently played sequence and the memory banks.
Ewolverine 4 U
New version 7 (currently testing)
- added automatic loosening of minimum fitness limit for the case that a population of sounds gets stuck in a local maximum in the fitness landscape (really nerdy jabbering, but trust me, it's useful ^^)
- added automatic "jumping" out of local maximums after a certain number of fruitless climbing-trials
EWOLVERINE v.7 by Henry Dalcke.pd
plans:
• bugfix: prevent a newly audible sound from being selected after manually stopping the target drive
• simulated annealing in target drive mode: span "temperature" value onto fitnesslandscape and decrease step length (modwheel) and probability value in the splice-pattern-generator the closer the fitness gets to optimal fitness value
• interactive mode: automatic narrowing of the range of generated parameter values around a mean value that's derived from the repeated selection of similar values of individual parameters throughout the generations (increases the number of similar sounds per generation that are located around a certain coordinate in parameter space; increases the likelihood of the generation of the desired sound in a smaller amount of time)
• stop-condition for automatic stopping of target drive
• make default settings for modwheel-position, splice-pattern-generator's probability, anti-stuck and allowed minimum fitness value in target drive adjustable from GUI
• adjustable MIDI output message blocker (useful for instruments with a fixed MIDI implementation, for instance: If you want to breed a bass drum in a drum synth with multiple instruments, you may not want to ruin the parameter adjustments of the snare drum meanwhile you're selecting for good bass drums)
• storage for self-created splicer patterns (maybe in connection to the MIDI output message blocker)
• low-value-weighted probability for the generation of MIDI-CC-values in new populations; switchable per MIDI-CC either manually or randomly (increases the probability for the generation of short attack and decay values in synth's envelopes)
• bigger populations for each sound-set: 4 more random sounds per set (A/B) to select from
• discontinuous MIDI messaging interrupted by assignment switching CC events (special build for FM-Heaven) - low priority
• possibility to interpolate between new random population's sounds to smoothly re-direct the modwheel-morphing path while morphing
• selection-history recorder that one can use to re-load the selected sounds of each past generation
• a visualizer that generates a "tree of life" from directions (keys C,D,E,F) and steplengths (modwheel) of formerly selected individuals and their respective distances to their parent sounds
Simulate LoadBang
@jjegede01 I see what you want for loadbangs. Sometimes it is handy to have a master loadbang that sends bangs to other parts of the patch using [send bang1] and [send bang2] etc. (for example). That way you can put delays before the sends, and control the order in which parts of your patch are activated. Sometimes that can be absolutely vital especially with dynamic patch building.
Sends and receives are absolutely reliable. I have never ever had a fail. For Pd it is as if you had connected with wires. There is no similarity with "wireless". If you use [send~] and [receive~] or [throw~] and [catch~] for audio then you have a "one block" delay, but that is much too small to notice.
David.
how does [list drip] actually work?
it's a recursive thing: I'll walk through part of an example:
say the list has 5 elements.
The list comes in, and goes into the open spigot.
so at this point the [t a a a a] is sending a list of 5 elements. the following is what happens to the right [list-split] at this moment:
the first 2 elements of the list are sent back to the upper trigger, and go back into the spigot, where they are split again into lists of length 1. element 1 goes back into the upper trigger and out of the outlet, and then the second does as well.
All of this happens before the list of 5 elements is even sent to the left [list split], because of the trigger. A similar thing occurs:
the last 3 elements of the 5-element list are sent to the upper trigger and into the open spigot, and then the first element of that is split off and sent out the outlet. Then the last 2 elements are sent through the spigot and split, and are sent out in order.
The crucial thing to understand is the recursive nature; it has to do with the depth-first message passing in pd.
The first half of the list is split off before the second half, and sent to be split in half again before the second half of the original list is even processed. So it goes the first half of the first half of the first half, etc. until the first element is sent to the outlet. Then it goes back up the "stack" to the second halves of the lists that it missed in doing the first half every time (this happens because of the triggers). So after the first element is sent, the second element is sent. After that, elements 3 and 4 (in a list since they weren't even processed to be split yet) are split and sent out in order. then elements 5-8, which are still in a list, are sent back and processed in the same manner.
Hopefully that's somewhat clear..
(rambling extra stuff:)
From a "computer science" perspective, you could say that you process the first half and put the second half on a "stack" , and then go into the first half, split it in half, put the second half on the "stack", etc. And then after the 1st element is put out, repeat that process for each thing on the top of the stack until you end up with 1 element:
in "pseudocode" (with a datatype "list" and an assumed stack):
function splitlist (list inlist) {
list firsthalf;
list secondhalf;
if length(inlist) == 1 then {
output(inlist); -- pass the element
if stack exists then {
secondhalf = popstack(); -- get the next thing on the stack
splitlist(secondhalf); -- call the function recursively
}
} else {
firsthalf, secondhalf = split(inlist); -- split the list, pass to variables
pushstack(second half); --- push the second half onto the stack
splitlist(firsthalf); -- call the function recursively
}
}
I think that in reality most of this is taken care of in the computer's call stack, though not completely sure on how the memory for the lists is dealt with
Pd/rjdj skillshare @ Eyebeam, NYC, Dec 5th
http://eyebeam.org/events/rjdj-skillshare
December 5, 2009
12:00 -- 1:30 PM : Introductory workshop on Pd with Hans-Christoph Steiner
2:00 -- 6:00 PM : SkillShare w/Steiner and members of RjDj programming team
Free, capacity for up to 30 participants
RSVP HERE: http://tinyurl.com/ykaq3l3
Hans-Christoph Steiner returns to Eyebeam with members of the RjDj programming team from Europe to help turn your iPhone or iPod-Touch into a programmable, generative, and interactive sound-processor! Create a variable echo, whose timing varies according to the phone's tilt-sensor or an audio synthesizer that responds to your gestures, accelerations and touches. Abuse the extensive sound capabilities of the Pure Data programming language to blend generative music, audio analysis, and synthy goodness. If you're familiar with the awesome RjDj, then you already know the possibilities of Pure Data on the iPhone or iPod Touch (2nd and 3rd generation Touch only).
Creating and uploading your own sound-processing and sound-generating patches can be as easy as copying a text file to your device! In this 4-hour hands-on SkillShare, interactive sound whiz and Pure Data developer Hans-Christoph Steiner and several of the original RjDj programers will lead you through all the steps necessary to turn your phone into a pocket synth.
How Eyebeam SkillShares work
Eyebeam's SkillShares are Peer-to-Peer working/learning sessions that provide an informal context to develop new skills alongside leading developers and artists. They are for all levels and start with an introduction and overview of the topic, after which participants with similar projects or skill levels break off into small groups to work on their project while getting feedback and additional instruction and ideas from their group. It's a great way to level-up your skills and meet like-minded people. This SkillShare is especially well-suited for electronic musicians and other people who have experience programming sound. Some knowledge of sound analysis and synthesis techniques will go a long way.
We'll also take a lunch break in the afternoon including a special informal meeting about how to jailbreak your iPhone!
Your Skill Level
All levels of skill are OK as long as you have done something with Pd or Max/MSP before. If you consider yourself a beginner It would help a lot to run through the Pd audio tutorials before attending.
NOTE: On the day of the SkillShare we will hold an introductory workshop from 12:00 until 1:30 PM, led by Steiner, for those who want to make sure they're up-to-speed before the actual SkillShare starts at 2:00. The introductory workshop is for people who have some done something in Pd or Max/MSP but are still relative beginners in the area of electronic sound programming.
What You Should Bring
You'll need to bring your iPhone or iPod Touch (2nd or 3rd generation Touch only), your own laptop, a headset with a built-in mic (especially if using an iPod Touch) and the data cable you use to connect your device to your laptop. Owing to a terrific hack, you won't even need an Apple Developer License for your device!
More Information
RjDj is an augmented reality app that uses the power of the new generation personal music players like iPhone and iPod Touch to create mind blowing hearing sensations. The RjDj app makes a number of downloadable scenes from different artists available as well as the opportunity to make your own and share them with other users. RjDj.me
Pd (aka Pure Data) is a real-time graphical programming environment for audio, video, and graphical processing. Pd is free software, and works on multiple platforms, and therefore is quite portable; versions exist for Win32, IRIX, GNU/Linux, BSD, and MacOS X running on anything from a PocketPC to an old Mac to a brand new PC. Recent developments include a system of abstractions for building performance environments, and a library of objects for physical modeling for sound synthesis.
kill your television
"Morphine" - fx-morphing engine
for some reason, the list of error messages became even longer......
mix.switch.nice 101
... couldn't create
init.post.dollarg
... couldn't create
flow.receive
... couldn't create
flow.send
... couldn't create
list.split 1
... couldn't create
flow.send
... couldn't create
flow.send
... couldn't create
flow.receive
... couldn't create
[makesymbol] part of zexy-2.2.3 (compiled: Feb 25 2009)
Copyright (l) 1999-2008 IOhannes m zmölnig, forum::für::umläute & IEM
flow.@parse
... couldn't create
flow.receive
... couldn't create
init.dollar.zero.top
... couldn't create
flow.receive
... couldn't create
flow.receive
... couldn't create
list.build
... couldn't create
[demultiplex] part of zexy-2.2.3 (compiled: Feb 25 2009)
Copyright (l) 1999-2008 IOhannes m zmölnig, forum::für::umläute & IEM
init.make.unique
... couldn't create
flow.receive
... couldn't create
flow.receive
... couldn't create
flow.send
... couldn't create
list.split 1
... couldn't create
list.split 1
... couldn't create
list.length
... couldn't create
flow.send
... couldn't create
flow.send
... couldn't create
wahwah~: an audio wahwah, version 0.1 (ydegoyon@free.fr)
expr, expr~, fexpr~ version 0.4 under GNU General Public License
mix.switch.nice 101
... couldn't create
init.post.dollarg
... couldn't create
flow.receive
... couldn't create
flow.send
... couldn't create
list.split 1
... couldn't create
flow.send
... couldn't create
flow.send
... couldn't create
flow.receive
... couldn't create
flow.@parse
... couldn't create
flow.receive
... couldn't create
init.dollar.zero.top
... couldn't create
flow.receive
... couldn't create
flow.receive
... couldn't create
list.build
... couldn't create
init.make.unique
... couldn't create
flow.receive
... couldn't create
flow.receive
... couldn't create
flow.send
... couldn't create
list.split 1
... couldn't create
list.split 1
... couldn't create
list.length
... couldn't create
flow.send
... couldn't create
flow.send
... couldn't create
mix.switch.nice 101
... couldn't create
init.post.dollarg
... couldn't create
flow.receive
... couldn't create
flow.send
... couldn't create
list.split 1
... couldn't create
flow.send
... couldn't create
flow.send
... couldn't create
flow.receive
... couldn't create
flow.@parse
... couldn't create
flow.receive
... couldn't create
init.dollar.zero.top
... couldn't create
flow.receive
... couldn't create
flow.receive
... couldn't create
list.build
... couldn't create
init.make.unique
... couldn't create
flow.receive
... couldn't create
flow.receive
... couldn't create
flow.send
... couldn't create
list.split 1
... couldn't create
list.split 1
... couldn't create
list.length
... couldn't create
flow.send
... couldn't create
flow.send
... couldn't create
mix.switch.nice 101
... couldn't create
init.post.dollarg
... couldn't create
flow.receive
... couldn't create
flow.send
... couldn't create
list.split 1
... couldn't create
flow.send
... couldn't create
flow.send
... couldn't create
flow.receive
... couldn't create
flow.@parse
... couldn't create
flow.receive
... couldn't create
init.dollar.zero.top
... couldn't create
flow.receive
... couldn't create
flow.receive
... couldn't create
list.build
... couldn't create
init.make.unique
... couldn't create
flow.receive
... couldn't create
flow.receive
... couldn't create
flow.send
... couldn't create
list.split 1
... couldn't create
list.split 1
... couldn't create
list.length
... couldn't create
flow.send
... couldn't create
flow.send
... couldn't create
error: inlet: expected '' but got 'symbol'
... you might be able to track this down from the Find menu.
error: inlet: expected '' but got 'symbol'
error: inlet: expected '' but got 'symbol'
error: inlet: expected '' but got 'list'
error: inlet: expected '' but got 'symbol'
error: inlet: expected '' but got 'symbol'
error: inlet: expected '' but got 'list'
error: inlet: expected '' but got 'symbol'
error: inlet: expected '' but got 'symbol'
error: inlet: expected '' but got 'symbol'
error: inlet: expected '' but got 'list'
error: inlet: expected '' but got 'symbol'
error: inlet: expected '' but got 'symbol'
error: inlet: expected '' but got 'list'
error: inlet: expected '' but got 'symbol'
error: inlet: expected '' but got 'symbol'
error: inlet: expected '' but got 'symbol'
error: inlet: expected '' but got 'list'
error: inlet: expected '' but got 'symbol'
error: inlet: expected '' but got 'symbol'
error: inlet: expected '' but got 'list'
error: inlet: expected '' but got 'symbol'
error: inlet: expected '' but got 'symbol'
error: inlet: expected '' but got 'symbol'
error: inlet: expected '' but got 'list'
error: inlet: expected '' but got 'symbol'
error: inlet: expected '' but got 'symbol'
error: inlet: expected '' but got 'list'
i'd really love to check out, what you have build there!
\#DEFINE
you could just use symbols, and instead of sending directly to logic from every subpatch, send to a master receiver that converts from the names into the messages you want to send to logic
something like:
subpatch1: ...--"lp1RecOn"--[send LOGIC]
subpatch2: ...--"lp1RecOn"--[send LOGIC]
subpatch3: ...--"lp2RecOn"--[send LOGIC]
subpatch4: ...--"lp2RecOn"--[send LOGIC]
etc
master:
[receive LOGIC]
|
[route lp1RecOn lp2RecOn etc]
| |
"1 1 1" "6 24 100"
| |
... whatever you need to send to logic
(I use [route] instead of [select] here because otherwise you need to have all the identifiers be symbols, like "symbol lp1RecOn", which quickly gets tedious to type)
side note: be careful with the "shifting multiple values into one number" trick, it works until you get to large enough output numbers (about 16 million) at which point rounding errors creep in and it gets ugly, not to mention Pd likes to truncate numbers even more roughly when converting between floats and strings...