-
oid
@cfry Been playing with this today and reread the thread, I think we are mostly on the same track, in that previous post I just took a leap to a next step which seemed illogical to you but makes sense to me and might be a step you are not even interested in taking but it is almost free because of the stack. From what I understand, you want to have a sequence of commands which you want to step through, such as your valadd example in post 10, each time it gets a bang it adds that value to val and then loops infinitely the last three valadds; valAdd -1, valAdd -0.5, valAdd 1, goto 7, valAdd -1, valAdd -0.5, valAdd 1, goto 7, valAdd -1 etc. This would be like your [pi-cy] abstraction, sets val to 60, and then steps through each valAdd and finishing with that infinite loop between line 7 and line 9 with each step sending the result of val + valAdd's argument to SynthY's pitch? The loop command would be closer to [pi-cy] than this goto? or maybe loop would be the [cy%...] abstractions? Hopefully it is close enough for this example.
Assuming I got this down, the leap to the next step I took was to to just stick the entirety of your previous screen shot into one instance of this abstraction so you don't need dozens of text files to keep track of jumping between editing the patch and the texts, and things like sharing data between them, your groups, and a whole lot more, become trivial. The difference between the single instances for each channel and putting all this into one instance is just adding a few commands, the stack makes it easy to expand and develop things like this. But you may not want to go that way and may not even need the stack, a different data structure like a list or an array might suit your needs better and it is easy to switch this all over to a different data structure, pop and push just set and get a value from the list or array instead of literally popping and pushing. Or we can implement lists/arrays in the stack based paradigm and that is when things get fun, but that might be more than you want.
I worked out a few variations but I will wait to post them until I am certain I understand what you are going for so I can make any needed adjustments and hopefully avoid more confusion.
-
oid
@cfry I don't think you did, but maybe? Either way, no need to apologize, I have gotten a lot of good ideas for my own projects from this, simplifying my way of doing this stuff for you has been very productive for me. Is loop now working? it looks good but I did not test it. Also, sending 200 to $0pop is not a good way to clear the stack, if the last command run was something which pushes onto the stack you will end up with something still on the stack, just connect a [r $0-dump] to the right inlet of the [list store] and you can send the dump command to clear it.
-
oid
@cfry Slow is fine with me and I am generally not going to exceed a post a day anyways unless I can just give a quick answer that I don't have to think about much. So the output of [r chs-$0] is a two element list, channel number and a one or a zero? What is the right outlet of your once? bang when reset or something else? are they how you are implementing groups? this screen shot is one group with two synths? I think I got a bit hungup on the tracker aspect and was not quite seeing what you were trying to do. We should get this sorted out quite nicely after a bit of a Q&A and get you a more flexible and dynamic setup in the process.
-
oid
@cfry I think variables might be the best way to go about this even though they do not give you your easy to read argument, but you can just use the name of the text to show that and have the text as an argument? You could also use a single text for all instances, have each one execute just the code that is relevant to them, this has the advantage of keeping it all in one place and you will never have to remember what was in the other texts but you could end up having to scroll a whole lot more. Could also go with a startup script which all instances in a patch would run at startup, a way to setup the environment and create globals, setup initial state of each, load programs into them, etc. Having a master instance which controls the rest might is also a possible solution, like the control track some trackers have, it would be able to feed each instance shared values and then they would react on that data as their code tells them. Other solutions I have come up with move things away from the tracker style and towards it being a scripting language, so it would greatly change how you interact with the abstractions and your entire workflow, which I am not sure you want to do?
I should have time to get my computer setup tomorrow so I can get back to patching, on Friday or Saturday (assuming thing go well with the computer) I will throw a few demos together for you to play with so you can get a better idea.
Or maybe just have arguments be code, at startup each instance runs their arguments, then you could do numbered groups with an if statements or a case statement, that would probably work best with the startup script idea so you don't have to have the exact same thing repeated in every instance.
-
oid
@soundproofskin That makes sense since pd's text editor is the Tk text widget and it has those features, have not used the editor since I made my abstraction to get around other issues with the text editor.
I am not sure if we can add this in with a plugin, the binding is not an issue but we also need to retrieve the text of the object currently being edited so we can find that next space to navigate too when shift-right is pressed, and I have not found a way to do that, only the last object clicked on, which means this would not play well with autopatching. If we can get the current object these features would be simple to implement, maybe @seb-harmonik.ar knows of a variable holding the value of the current object being edited or an array holding the text currently being edited? I have a vague memory of once finding such an array, but it is not in my notes and the one time I tried to find it again I came up empty.
You could easily bind it to move the insertion cursor a fixed number of characters, shift-right moves the cursor 4 or 5 characters to the right, but I don't think that would be much use since to get good with it you would have to constantly count characters. One of the other flavors of pd (plugdata, purrdata, pd-ceammc, pd-l2ork) might provide this if using one of them is an option but as you progress and get better with pd, i am not sure you will find much utility in navigating text this way; years ago I also used to try and shit-arrow my way through objects but as I became more familiar with pd it became rare there was a need, anytime I want the cursor somewhere other than at the end is when I am editing an object which is not currently selected so I have to use the mouse anyways and putting the cursor where I need it is less work than keyboard navigation.
-
oid
@soundproofskin Pd's built in text editor seems a low priority and just a convienence that is not meant to replace a proper editor. I have an abstraction which you use in place of [text define] and instead of opening the internal editor it opens the text in an external editor and reloads the text anytime it is saved, I will upload it later this week after I get some computer stuff sorted out.
-
oid
@cfry The variables are much simpler than they look and the general technique I used to implement them is a very useful one, you can also use it to implement arrays and even functions.
Completely forgot about midi groups, your idlewait subpatch now makes sense. Believe I am going to think on this one a bit, the way I deal with such things would be to use tracky for generating the modulation tracks ahead of time instead of playing/creating them in realtime so I can do everything in a single instance, but I don't think this is the solution for you? you want something you can interact with more directly? You seem to be going more towards a sequencer/programming language hybrid, which seems fun, especially when you bring in multiple instances. I think implementing basic namespaces is probably ideal, and this would allow you to change which group something is a member of on the fly or have them be members of multiple groups, redefine groupes, lots of stuff. I will play around with this at work tonight, you have given me lots of fun ideas.
Wrong select on your loop, you had that right the first time. Put a [t b f] on the right outlet of [sel 0 -1], float outlet goes to [sel] and bang outlet bangs the float. Make sure to click on that [0( message if you are not starting from the freshly opened patch, who knows what the value is otherwise. And remove your [>=] logic, but there is something to be said about using [>=] instead of the plain select as I did, you would not have to worry about clicking the [0( if things get out of whack but once you have it working it should never get out of whack so I tend towards going with the slightly more efficient option in this case. Also make sure the counter is wired proper, it needs to do the +1 before the [selec 0 -1] or it won't work, a [t f f] is not a terrible idea here, old habits. I probably was not as clear as I could have been on that, have yet to get my new computer setup and have been using my tablet or phone, which are not great for this stuff, especially since I can't get the linux VM on my tablet to share files, so can't get patches in or out of the VM where pd lives without jumping through hoops. Should be up and running this week.
-
oid
@cfry said:
The problem that arise is that when I start to improvise I kind of "break the (your) concept". And I would like to avoid ending up in another patch that is so messy that I can not use it if I bring it up after a half a year or so. Lets continue working on it!
You are definitely getting parts of it and seeing how to develop it but there are parts you don't quite have yet and I can't quite identify what those are so I can explain things. I made a sizable patch that adds a lot of commands but last night I realized I went to far and it would probably confuse things for you, so I will reduce it down to just the things you mentioned, I will follow your lead.
Passing the text name as an argument is just a matter of using your dollar arguments, [text get $1], but you will want to tweak your input for text symbols some.
So our right inlet for text symbols goes to a send now so we can easily access that text anywhere in the patch, like in our commands, and we also reset the counter which uses a value for its float for the same reason, we will want to be able to access and change its value from commands. "pc" is short for program counter and it is important that we increment its value before sending a float to the text get otherwise if we change its value in some command it will get overwritten, so having a [t f f] here is almost a must. This also means that [v $0pc] points to the next line to be run and not the one that is currently being run, this is important, fairly useful, and occasionally irksome.The left inlet has change some as well, we have a [route bang float], bangs and floats go to the counter so we can increment the program or set the next line to be run, the right outlet sends to our [list-drip] which enables us to run commands from the parent patch so when things don't work you can run that print command to print the stack and get some insight or just run commands from a listbox to test things out or whatnot. We also have [r $0bang] on the counter, this lets us increment the counter immediately from a command and start the next line. And finally we have a new outlet that bangs when we reach the end of the text file so we can turn off the metro in the parent patch which is doing the banging, reset the counter to zero, load a new program, or what ever you want to do when the program completes. Middle outlet I did away with, globals can be done as a command, as can most everything.
Variables we can implement with some dynamic patching and a simple abstraction to create the commands for setting and getting the value of any variable.
This looks more complicated than it is. If we have the linevar val1 10
in our program it runs thevar
command which bangs [v $0pc] and subtracts 1 from it to get the current line from the text holding our program and then appends our $0 to it giving us the listvar val1 10 $0
. The first message it goes to creates an instance of the var.pd abstraction in [pd $0var] with the second and fourth elements of the list as arguments, val1 and $0. Second message sends $3 to $4->$2, 10 to $0->val1. To finish off we use that new $0bang receive to bang [v $0pc] so we don't execute the rest of the line which would runval1
pushing 10 to the stack and then push another 10 to the stack. Variable name with a>
prepended to it is the command for setting the value of a variable,22 >val1
in your program would set the value of val1 to 22,val1
would push 22 to the stack. If we could see how pd expands all those dollar arguments in the abstraction it would look like this:
Now you can create as many variables as you would like in your programs and a couple tweaks you can have the abstraction global.pd and subpatch [pd $0globals] so you can have the lineglobal val2 0
and get a global variable that all instances of tracky can read and write from. There are a couple catches, each variable definition must appear on its own line with nothing else after it and with our simple parsing there is nothing to stop you from creating multiple instances of the same variable, if you run the linevar val1 10
a second time it will create a second abstraction so when you runval1
it will bang both and each will push 10 to the stack giving you an extra 10 and screw up your program. We can fix this with adding a registry to the var command which searches a [text] to see if it has been created already, something like this after the [list $0] should do it:
And you will want to create a command to clear all variables (and the text if you use it) by sending [clear( to [pd-$0var], or get fancy and add another command in var.pd which bangs [iemguts/canvasdelete] so you can delete individual variables. Using [canvasdelete] has the advantage of not needing a registry for variables, you can just always run ```delete-<variable name>, or what ever you name your delete command, before creating a new variable. Each method has advantages.Your loop does not work because the unpack needs to go into the left inlet of the float, that triggers the first loop and causes it to go back, each time the program gets back up to the loop command it increments [v $0loopi] until the select hits the target number of loops which sets $0loopi to -1 which ends the looping.
Not sure what you mean by groups/exclusive groups, can you elaborate or show it with a patch?
None of the above has been tested, but I did think them through better than I did the loop, fairly certain all is well but there might be a bug or two for you to find. Letting you patch them since we think about how things work more when we patch than when we use a patch. Try and sort out and how they work from the pictures and then patch them together without the pictures, following your understanding of them instead of your memory of how I did it. And change them as needed to suit your needs.
-
oid
@whale-av said:
A recent change (since the doc was written).
That doc was written 20 years ago, back when pd was 9 years old, not sure I could call it recent but it gave me a chuckle, getting old. it goes back to at least 0.47 when I returned to pd and finally started to learn it proper and not treat it like digital modular synth. I don't think we could use this in dynamic patching until they sorted out escapes, vague memory that back when scriptit was released and i really dug into dynamic patching, this did not work.
-
oid
@lacuna in the case of dynamic patching there is at least one nuance, we have the f message which sets object width,
[msg 10 10 bang, f 10(
will create the message box 10 characters wide despite only having 4 characters. Still another message but a special case, any others? -
oid
@cfry Doing two arguments is just changing how many arguments you pop off the stack and replacing the [route bang float] after the command receive to [route bang list]. So a loop command could be made like this (untested and not even fully thought through):
I used [value] because add works off the last value that was output and [value] keeps things neater than tying lots of stuff to a single [float]. In this case you don't need to use [v $0val] to get the output for val, it just stores the float for add, same, and any other command you want to use it for, sending the float to both [v $0val] and [s $0out] is exactly what I would do in this case. I don't think $0-same will work as you expect, there is no need to pop for that and out is getting a bang instead of a float, just need to do [route bang] -> [v $0val] -> [s $0out].Your comments look to be correct. [r $0stack] is used for the trace but it is useful for other things, you can have a print command which just bangs the message [send $0print( to [s $0stack( and then a [r $0print] connected to a [print] so you can check the stack's condition in those cases when things did not work as planned and you did not have the trace on. Notice the send message is to $0print and not $0-print, receives with the $0 hyphen prefix are only for commands and you will get into trouble if you use that prefix for non-commands. In this case we send the print to $0print instead of $0-print because if the stack is empty it would output a bang which would bang the message again and send you into an infinite loop. Remember to save before testing new commands, it is easy to miss an obvious loop like this.
We prepend to stack instead of append because that means we always get to do [delete 0 $1(, if we appended we would have to keep track of stack size and then subtract how many to delete so we can do a [delete $1 $2(, just more efficient to do it this way in pd land. There is a bit of a flaw here as well, think about what happens if you run the line
1 2 3
, one is pushed, then two, then three, so three is top of the stack and the contents of the stack is reversed as3 2 1
, as it should be, the issues is if we have a command which pushes more than one value onto the stack. If you had the command which just bangs the message [1 2 3( to [s $0push] you would have one as top of the stack instead of three so in commands you need to send your lists backwards. Slightly annoying and could be fixed but would be less efficient, so I just remember that when patching commands which push a list, the list needs to be backwards or it will cause confusion elsewhere.I will do another post later tonight or tomorrow going into the rest and elaborating on a few things.
Edit: Suppose that loop command only works for looping to a point earlier in the program, which is probably workable but perhaps not ideal. We will deal with that in the next post and start really exploiting that this is a programming language and will save you work as any programming language should. Trying not to dump too much on you all at once, next post will be a big one. Or maybe you are already ahead of me? can't quite tell.
Edit2: No, that loop will not quite work, for got to resend the float to $0-goto on each iteration, only loops once.
-
oid
@CalBassist Follow it with a [pack f 50] and then a [line] or [line~] depending on if you need message rate or audio rate, if you are doing message rate, be sure to set the grain size of [line] to suit your needs. Pd's UI has to quantize to pixels so there will always be gaps even if you drag slowly. Replace the 50 in [pack f 50] with what ever rate works for your need, 50 is good for most situations but occasionally 50 is too slow or fast.
Edit: I did not read closely enough, if [line]/[line~] is not working for you, can you give an example patch of when it fails for you? I can't think of a situation where this solution will not work, but maybe someone else can.
-
oid
@cfry Took care of the dollar args for you and fixed a small oversight in the previous which made it not work for commands with more than one argument. Also added a simple trace which prints stack size, command, and the top 10 elements on the stack, should help you in figuring what is going on. The [s $0fpush] is the same as $0push, nothing fancy there, it was just a simple way to make the trace slightly easier to read.
trackthing.pdEdit: fixed a bug.
I think list-abs could use a rewrite using the 'new' [list] objects.
I started in on that but realized that the overall design of list-abs is an issue, often what takes 5 list-abs abstractions can be done with a single [list store] and some glue, some can be replaced with [list store] and a message and have no need to be abstracted away. I think a good [list store] tutorial and a much smaller (more thought out and planed) replacement for list-abs would be a better idea. List-abs is fine as it is for keeping old patches alive.
-
oid
@cfry The abstraction part is supposed to be run with $0 as it's argument to pass the parents dollar zero so those commands in the parent patch can communicate with it. Change those $1s to $0s if you want everything in one patch, or the $0s into $1s if you want commands in both the abstraction and the parent patch and all should be well. It will be atleast a few hours before i can offer patches, computer died right after yesterday's post so have to setup the new one and do all that stuff which goes with a new computer. Once you get the basic functionality working it will probably start making sense.
-
oid
@cfry I would avoid things like 10x4, as time goes on and you think up new commands for this little abstraction you will likely come up with ones which need more arguments and writing/reading/breaking up these symbols will get tiring. Just parsing the line itself will be easier, just need to run it through a [list-drip] and some logic which decides how to act on each element one at a time, if you make a few concessions like arguments coming before commands and symbols are always commands, floats are always arguments it turns out quite simple and surprisingly powerful.
On the left of the abstraction we have the parser and on the right we have a simple stack for storing arguments, and in the patch we have two commands. If we send it the line1 val 7 goto
it drips the list,1
goes out the right outlet of the [route symbol] and is pushed onto the stack,val
is then dripped and packed with $1 ($0 of parent patch) to set two send symbols, first the send on the output of the stack and then the one which works just like the send in the previous example but is always banged, $0-val receives the bang which runs the command, it pops a value off the stack and sets $0val to that value. On down the line. While sticking that stuff on the stack might seem roundabout and pointless it means commands get their arguments from the stack instead having their arguments passed too them, which is very powerful and what makes all those stack based programming languages like Forth work. So, if you decide you want to setval
to a random number you can just patch up a simple command for it:
Now you just feed it the line127 random val
and it pushes 127 onto the stack, sends a bang to $0-random which pops 127 off the stack and then pushes a random number between 0 and 127 onto the stack soval
can get it and set $0val. This random can be used with any function which accepts an argument with no extra patching required, you can do50 random 10 random loop
to loop from a random position a random number of times or what ever. You develop a little custom programming language instead of a random collection of adhoc functions.My tracker thing started out almost exactly as yours did with and eventually evolved into this and finally into something rather close to a proper programming language. You might want to put the commands in the abstraction instead of the patch for ease of use or you can have library abstraction(s) that you can stick in the patch or dynamically load into the patch or abstraction through its arguments, or you might just continue on as you were and split your 10x4 symbol into floats, each have their advantages and disadvantages.
-
oid
@cfry To get around each command needing the same format the easiest is just to make them all the same, have a null argument like bang.
Now we can just skip the commas and have as many commands per line as you want. If you want to run the command "stop" you just make the message "stop bang" and it sends a bang to [r stop]. Would probably want to add a [list prepend $1] between the [list split] and the message and make the message [; $1$2 $3( so you can pass the parent patch's $0 and prepend it to the symbol but that might not be needed for your use?If you want commands with more than one argument things get more difficult since you don't want to always have to type in all those null characters or symbols, but it is not that difficult to create a simple editor which does it for you, especially now that we got [list box), I have one somewhere from when I made a tracker like thing years ago, I will see if I can find it later if you want to check it out.
If you really want commands with an arbitrary number of arguments and no nulls, use commas and exploit the right outlet of [text get]. This does complicate things regarding line number vs message number but making that [sel 1] into a [sel 0 1] and using the 0 outlet to bang a second counter for the line number might be enough?
-
oid
@cfry I was thinking you were using [list split] in place of [unpack] to deal with hex being made up of both floats like 11 and symbols like a1. I can't quite figure out what you are doing with the [list split],. I suspect you just need to develop a more complex counter and maybe tweak how your data is stored and you will be able to make things cleaner. Providing some more information might help in offering alternatives, the format of your data and how you need to access it, perhaps a simple mockup of your patch.
-
oid
@cfry Dealing with hex is almost always irritating in pd, I generally try and make the hex UI only and keep it as floats under the hood, but that is not always practical. The comma behavior is fairly useful, just need to format your data to exploit it and it makes sense when you think about it since if it did not work that way the object the [text get] is connected to would see a line like
1, 2, 3, 4;
as four separate messages and not the single message1, 2, 3, 4
, it would be the same as [1, 2, 3, 4( into a [list] which generally will not give the desired result . That one caused me some grief at first until I learned to use it to my advantage. -
oid
@cfry Don't think you will find an external or abstraction which will be easier/better for this than [text] unless someone made exactly what you were looking for and even then it will only be easier until you decide to change things and start banging your head against the limitations of the external or abstraction. This is a pretty simple thing to throw together using [text] and [text get] or [text sequence] depending on your needs, a simple looping [text get] sequencer is not complex and easy to expand to a full tracker like interface even.
[text sequence] is even simpler but has some limitations, it is great when your needs align with it but almost everytime I use it my needs grow beyond its limitations and I switch to [text get]. [text get] also tends to result in neater, easier to read patches since you don't have all those messages going to one inlet.Edit: Must not have been quite awake when I patched that moses in, bet I thought I was being clever.
-
oid
@atux This should work for parsing the file, but you will need to add the stuff for resetting the counters/moses/max and before running it you will want to zero the arrays. Not 100% certain I got the order on the array sizing correct, the summer heat is melting my brain.
parse.pd
plot the four graphs on the same canvas?
Not completely certain about your intention here, do you mean canvas literal or graph? Are you trying to get it to look like your screen shot? or just a standard pd graph? If you want it to look like your screen shot but don't need it to be interactive you can easily use dynamic patching, if you need it to be interactive then data structures are probably your best bet but it can be managed without by hiding some sliders under a canvas. Need more info to offer help here.