|
Post by dollanaire on Apr 4, 2022 20:33:10 GMT -5
Hey guys. Could someone help develop a way of using PLAY RNDC to not repeat a clip once its triggered? Also, maybe have a reset action that will reset the clip playing status in case you want to include the already played clips again? That would be extremely useful for me and I'm sure others. I'd pay for your time if someone wants to take a stab at it and DM me. Thanks!!
|
|
syph
Junior Member

https://www.youtube.com/SyphMusic
Posts: 84
|
Post by syph on Apr 5, 2022 1:55:36 GMT -5
DM'ed 
|
|
|
Post by ChainsawArt on Apr 8, 2022 0:15:29 GMT -5
syph dollanaire Very cool idea for a User Action. Here's my take on it so far: www.codepile.net/pile/0LdlExLQMy idea was to use a class dictionary which creates a key for each track addressed by the action. Each key would contain a list which would start off empty and then fill with the index numbers of the played clips for that track. This way, there would be a separate memory for what clips have played on each track and resetting could either be done just for the track, or globally. Examples of the 2 actions: 1/RNDCSOLE ##Primary action that plays random clip, will not play an empty clipslot, will not play clip if it has already been played by using this action 3/RNDCRESET ##Resets play history for track 3 1/RNDCRESET ALL ##Optional 'ALL' modifier resets play history for all tracks. (Probably should switch this to a global action so it's not confusing. Any track name or number works for now) Next Up: After using the action, I realized that I would like a visual of the clips that have already been played. I would like to add to the existing dictionary another layer.. so a dict of dicts where each track addressed would be added as it's own dict with key, value pairs being index and clip color. This way clip colors could change to something like white as they are played with this action, and then restored to their original color after the resetting action. I'll report back here if I'm successful. EDIT: Also.. just realized that I wrote this action with f-strings for Live 11. I think that's the only thing I did that was Python3 exclusive. Nothing special that the old string-formatting method can't do here.
|
|
syph
Junior Member

https://www.youtube.com/SyphMusic
Posts: 84
|
Post by syph on Apr 8, 2022 4:00:26 GMT -5
Dang dude, your python chops keep getting better each time I see them.
Nice use of the choice() method, I didn't know that one. I was thinking I would have to abstract that one another layer by putting keys to a dictionary into a list to then run a random method on.
Line 16 is also quite interesting to me. If I understand that right, that's a list whose contents are generated by a for loop for the tracks clip slots dependent on whether the clip slot has a clip. Does that return a list of clip slot addresses that have clips, or does it return an index of the clip slots that have clips? If it's the former, then that should take care of deleting and replacing clips on a track, right?
|
|
|
Post by ChainsawArt on Apr 8, 2022 8:56:55 GMT -5
Yeah, choice takes all the work out of the Random part. Line 16 does most of the work. It builds a list of all clip_slot indexes that have a clip, and haven't already been played. This is the bucket the choice method picks from. I don't understand 'deleting or replacing clips'. I think I must be misunderstanding you on the last question. syph, you still rocking Live 9? BTW.. I checked out your video: youtu.be/_1pITN00CrQ. Your bass chops are very good. Nice work!
|
|
syph
Junior Member

https://www.youtube.com/SyphMusic
Posts: 84
|
Post by syph on Apr 8, 2022 12:06:01 GMT -5
Alrighty, how to explain this... My first thought when thinking of how to make this was to run a for loop to check each clip slot and identify which clip slots are occupied and write the clip slot numbers to a list, then cross reference that list against a dictionary/list of clip slots that have already been played on that track to create a list of clips that have yet to be played. If I understand right, I think that's what you are doing there. The problem regarding deleting and new clips is in the following sequence of actions: (lets just assume this is all happening on track 1) 1) play random clip which launches a clip on scene 1 2) delete clip 1 after it plays, then record or copy a new clip into the slot on scene 1 So when a clip is deleted, there is no way for the random clip action to know since the random clip play action is just based on slots that have been played and not clips that have been played. I hope I explained that well enough... Anywho, so I was going to do some testing when I have some free time after the weekend to try and find a solution for that. I have some ideas for it, but that's why I was asking if your action creates an index list of clip slots that have clips in them, like, is the list that is generated just a series of integers that point at the clip slots, or are you generating a list of what I think are memory address that refer to the clip slots directly? (I am clearly pushing the limits of my python and programming skills in asking that, so sorry if it's incomprehensible...) And yes, I am still on Ableton 9, and I don't have the time right now to play with your script to make it python 2 friendly (seriously, the heck is a F-string?), hence asking the questions. Regarding the BTW, thanks man Most the videos on my channel are just open mic performances at my local bar. Partly vlogging the rig development process, partly learning how to make videos, and partly (reluctantly) making online content for the obvious self promotional reasons. That video is pretty boring in regards to the rig itself, just some basic looping and drum sequencing on display. I like to think it's a nice little ditty though.
|
|
|
Post by ChainsawArt on Apr 8, 2022 12:46:54 GMT -5
syph Ahhh Now I get it. The action I wrote does accommodate for adding clips to empty slots as it generates a list of clip_slot indexes (integers) that have clips and that haven't been played each time the action is run. BUT! One thing you bring up is what happens when a clip in a played slot is deleted and then a new clip is added to that same slot? That, my action didn't account for. I was hoping to create a comprehensive solution that didn't involve a listener. I am not sure how to do that without creating a special action like 1/delplayedclip that deletes clips like a normal delete clip, but also pops the index for that slot out of the 'already played' list in the dictionary. This is probably not ideal as deleting the clip any other way, using Push for example, wouldn't help here. Hmm. maybe the best way really is to generate a list of unique clip ids instead of clip_slot indexes each time the action is called. That way, the action would catch if a new clip has been added each time and the current_options list would always reflect reality, not just played indexes. This is probably what you were referring to originally. Nice catch! I'll play around with the idea and see if it's possible. I'm sure it is.. Python is so amazingly flexible. F-strings are fantastic. They make string formatting extremely simple and more readable. I'll make a Python 2 version as well if I'm able to figure this out.
|
|
syph
Junior Member

https://www.youtube.com/SyphMusic
Posts: 84
|
Post by syph on Apr 8, 2022 13:10:15 GMT -5
Yay! I successfully achieved the communicates!
*ahem*
But uh, yes. That is indeed the problem I was getting at. It might be overkill for a lot of peoples needs, it might actually not even be desirable for some peoples work flows, but it is annoying me and I doubt I would be happy if a solution isn't found.
Creating a list with the unique clip ID's does seem the most bullet proof approach. I'll be doing some trial and error on that at the start of next week I think, if for no other reason than that this has become a surprisingly interesting project for me. I may have spent a few hours the last few night playing with and learning about data structures, dictionaries, converting data types, etc. Good times! The other approach I was considering is something I stole from you regarding clip colors for played vs unplayed clips. Using text identifiers in clip names could also be effective in identifying played clips, though that could have unintended consequences for some peoples work flows.
A python 2 version to compare to would be really interesting for me as I have little (no) experience with python 3. That said, know that I know about it I can do what all programmers do and google the heck out of it, so uh, no stress there.
|
|
|
Post by ChainsawArt on Apr 8, 2022 14:09:09 GMT -5
I agree, this is a particularly interesting problem. I have found that having genuinely interesting problems makes up at least half of what is necessary to learn User Actions without a background in coding. That was the impetus for trying to start the User Action Challenges. Doesn't seem to have taken off yet. dollanaire has posted some great prompts on this forum.
|
|
|
Post by dollanaire on Apr 9, 2022 0:00:35 GMT -5
Wow you guys rock! I was telling syph I really wish I understood what you all do. It amazes me every time a solution to a problem is found. Thanks for tackling this as well ChainsawArt. I can't wait until you convert it to python 2 so I can try it out in 10. The deleting clip is an interesting point. I particularly wouldn't need to worry about it for my workflow currently but I'm sure others may. To turn this up a notch: If possible could you incorporate the Randges action you did for me with this so I could do 1/RNDCSOLE 1 25 50 100 ... That action is a lifesaver for me !!!! But when that is done it would most likely need to reset the clips (in that range only) automatically too depending on the non-played clips left in that range. For example: I have 100 clips on track 1 and I trigger 1/RNDCSOLE 1 10 10 times which plays every clip then the 11th time should reset range 1-10 only and leave clips 11-100 as they are until I trigger 1/RNDCSOLE 101 times which should reset everything. Another scenario would be 100 clips in track 1 and 1/RNDCSOLE triggers 5 times playing clips 4, 8, 2, 30, 14... I then trigger 1/RNDCSOLE 1 10 7 more times which should have played all clips in range of 1-10 and the next 1/RNDCSOLE 1 10 resets clips 1-10 only to being unplayed leaving clips 30, 14 as played still. Whew, I hope I'm explaining it correctly because it gave me a headache trying to figure out how to ask the question hahahaha Either way thanks guys so much for the work and effort!
|
|
|
Post by ChainsawArt on Apr 10, 2022 23:00:55 GMT -5
syph Ok.. so dictionaries are not the most straightforward things to work with at first. A lot of head-banging.. but I wasn't aware of how powerful they were until this exercise. The biggest hurdle here was double-checking at each call that the clip in each played slot was the actual clip that was there when the slot was marked as played. In M4L, every object has an easily obtained id that is static for the most part (a few exceptions, but not important here). I couldn't find a way to do the same in the Python API. I was mistaken in thinking that an "<object> @ 122o3iuoiu1r4" or whatever gets returned when referencing an object in Python was static. It is not. Every call to the same object returns a different group of characters following the "object at". I couldn't find a single bit of info anywhere online that talked about persistent ids in the Python API for objects. Out of curiosity, I returned the _live_ptr property relating to a clip class and behold! a 15-digit number appeared. I moved the clip to a different clip_slot and called it again.. same number came back. I can't say for sure, but I think this is an id of sorts. Well, it functions as one anyway and is how I solved the problem. I changed the reset all action to a global action. I was also able to incorporate the color coding feature. Everytime a clip has played via this action, the clip's current color is stored in the dictionary and then the clip's color is changed to white. When either of the reset actions are called, all clips relevant to the action are restored to their original color. The updated version of the code works in both Live 10 and 11.. so hopefully it's good in 9. Let me know if you have any questions or if I missed anything that we discussed about the original problem. Here's the new code: EDIT: Code has been finished and can be accessed here: www.codepile.net/pile/yvdzMk2Mdollanaire What a monster of a challenge you have unleashed upon the world! After getting this first phase done, I need to take a break for a bit. I'm sure my curiosity will bring me back to see if I can add the RANDGES features to the action.
|
|
|
Post by ChainsawArt on Apr 15, 2022 14:09:20 GMT -5
syph dollanaire I was able to add the Randges functionality into the action as well. The action now functions as before but resets automatically and can accept range arguments. Adding ranges as arguments after RNDCSOLE will limit the selections to the clip slots within those ranges. Note that if you want to have a single clip slot as a range, you would add that clip slot twice in the arguments. The arguments need to be written in multiples of 2. Example: RNDCSOLE 1 3 5 10 14 14 The above action will limit the available slots to clip slots 1, 2, 3, 5, 6, 7, 8, 9, 10, 14 if those slots exist and have clips. If a new clip is added, even to an existing slot since the last time the action was run, it will be added to available slots to play. Once slots within the standard action(without ranges) or action with ranges have been exhausted, all eligible slots will become available again and the selection process will automatically start over. If using multiple ranges, I didn't add resets for specific ranges yet. For the time being, there is only only an auto-reset that triggers if there are no more options to play when the action is called. Here is the code: www.codepile.net/pile/yvdzMk2MNote there is another import up top for chain from itertools. One last thing, I wouldn't mix non-range and ranged actions together for now except for testing as it will screw up clip color resetting. I tested on Live 10 and 11. Hopefully this all makes sense. Let me know if you have questions. Cheers!
|
|
|
Post by ChainsawArt on Apr 15, 2022 17:46:41 GMT -5
dollanaireI got it.. The link to the code one post up ^^^ has been updated. The new action does everything you outlined above in the Randges request. The only change is that the Randges reset after the last clip slot in the list has been fired so that there is no dead fire when resetting. Example RNDCSOLE 1 10 will reset after the 10th trigger. All other cli pslots will remain in their current played/unplayed state. Let me know if this works for you.
|
|
|
Post by dollanaire on Apr 16, 2022 7:28:13 GMT -5
WOOOOOOOOOOW! You the man!! Thanks ChainsawArt!! This is so useful for me man and I greatly appreciate it. DM me your info and I'll send you something for your time. I am however having an issue with large ranges compared to small ones. If I do 1/rndcsole 1 1000 it will hang for about 7 seconds before playing. My set is pretty big (around 3000 rows) Hopefully it's an easy fix but I'm happy that this works nonetheless. Thanks again!
|
|
|
Post by ChainsawArt on Apr 16, 2022 9:45:21 GMT -5
No need for payment. I'm still very much in the learning phase here. I see what you mean about the time problem. I only tested on a set with 40 clip slots. No time problem there. I think the main culprit is all the lookups that occur before each action call. I'm thinking there could be a solution but it would require removing a few lookups. It may also involve an indexing action, similar to a snap, that would file the clip slot info into a dictionary for more efficient action calls. This would require those tracks being static though, clips added to the track, similar to snaps with devices being added, might break the action in the sense that they wouldn't be added to the pool of available slots to play until the indexing action is called again. If you don't need the checking if new clips were added functionality, that should speed things up a bit as well. dollanaire, are the tracks that you wish to use this on static? syph, any ideas on how to speed this up?
|
|