|
Post by Stray on May 23, 2013 22:00:11 GMT -5
As mentioned in the ClyphX manual, you can create your own actions (called UserActions) that will work just like any other ClyphX actions. The file you will create these in (called ClyphXUserActions.py) includes a bunch of instructions, examples and references. However, I think it would help some of you to see a practical example of the steps involved in creating your own action.
For this example, we’ll be creating an action that allows Looper’s Speed parameter to be controlled more precisely than standard ClyphX Device Actions allow for.
The following posts will provide a general overview of action development and then get specifically into developing our example action.
|
|
|
Post by Stray on May 23, 2013 22:03:56 GMT -5
Action Development OverviewStep 1:Install an IDE (integrated development environment). This is similar to a text editor, but includes features that make writing code easier. One of the easiest to use (and somewhat limited in terms of features) of the free IDEs for Python is Wing IDE. Also, ClyphXUserActions.py provides some handy links. You’ll probably want to bookmark these in your browser for easy access. Step 2:Get a clear mental picture of what you want your action to do and then figure out the steps needed to make that happen (I call this a specification or spec). The clearer the image, the easier it will be to code. Unfortunately, it can be somewhat difficult to know what steps will be involved if you’re new to developing actions. The more you do it, the better you’ll get at it. Also, you can figure things out by searching for solutions online. Python is used for many, many things and so there is lots of info about it online. In most cases though, you’ll need to figure out how to generalize the problem you’re having. For example, suppose you needed to sort a list of notes in a clip in some fashion. Searching for something like “sorting MIDI clip notes in Live MIDI Remote Scripts” is not going to yield very many (if any) results. Instead, search for something more general like “sorting lists in Python” and work from there. Step 3:Code the action. You will probably make mistakes at this stage and that’s perfectly fine. Coding involves a lot of trial and error and testing. Sometimes you didn’t realize you needed a step in your spec. In that case, you should refactor your spec, which means that you’ll change the steps involved in the spec without changing what the spec attempts to accomplish. And then you should refactor your code, which means that you’ll change the code without changing what the code attempts to accomplish. During this step, taking a look at what is going on in the code is extremely useful. For MIDI Remote Scripts, you’ll use Live’s Log.txt file. ClyphX’s manual describes how to locate this in the Troubleshooting section. We’ll see examples of this as we’re developing our example action.
|
|
|
Post by Stray on May 23, 2013 22:17:58 GMT -5
Developing Our Action
Step 1 So first we need the spec. Again, this action is going to allow for precise adjustment of Looper’s Speed parameter. What are the steps needed to make this happen?
A. We need a way of specifying which Looper device we want to operate on. B. We need a way of finding the parameter within Looper that we want to operate on. C. We need to know the range of the parameter we want to control. D. We need to figure out how we’ll specify the value we want to set the parameter to. The value range shown in Live is -36.0 to 36.0.
Step 2 Next we need a name for our action. LOOPERSPD x (where x is the speed to set) should work.
Step 3 Next we need to define the basic action in ClyphXUserActions.py. ClyphXUserActions.py covers how to do this, so I’ll just post the code snippets. First we need to edit the dictionary to include our action:
self._action_dict = {#<--DO NOTE REMOVE THIS 'EX_ACTION_1' : 'example_action_one', 'EX_ACTION_2' : 'example_action_two', 'LOOPERSPD' : 'set_looper_speed' }#<--DO NOTE REMOVE THIS This defines that our action name will call the function named set_looper_speed. Now we need to define that function. We’ll just create a skeleton (function that doesn’t really do anything) to start with.
def set_looper_speed(self, track, args): self._parent.log_message('set_looper_speed called for track=' + str(track) + ' with args=' + args) This will simply write to Live’s log file when the action is triggered from an X-Trigger. You’ll want to test this out to make sure that it works. To do that, load a new set in Live, create an X-Clip named LOOPERSPD 36.0, trigger it and then look in Live’s log file. Also, save the set. From here on out, every time we change our function, we’ll need to reload the set to test our changes.
Step 4 Now let’s tackle the A part of the spec. ClyphX does part of the work for us by passing the track that the action should apply to. When accessing our action from an X-Trigger, we can use all the common options for specifying the track to operate on. For example, 4/LOOPERSPD 36.0 or “My Track”/LOOPERSPD 36.0. Next, we need to find the Looper device on the track. For this, we’ll assume that Looper is not inside a rack and that we want to operate on the first Looper found on the track. Let’s modify our function to do that:
def set_looper_speed(self, track, args): self._parent.log_message('set_looper_speed called for track=' + str(track) + ' with args=' + args) looper = None for device in track.devices: if device.class_name == 'Looper': looper = device break self._parent.log_message('set_looper_speed, looper=' + str(looper)) As a side note, if we planned on creating several actions that applied to Looper, we would break out this Looper locating section into its own function so we could reuse it.
Step 5 Now let’s tackle the B part of the spec. Assuming we don’t know where the Speed parameter is within Looper’s parameter list, we’d have to find it. We actually might as well tackle the C part of the spec at this point too. Let’s modify our function to do that:
def set_looper_speed(self, track, args): self._parent.log_message('set_looper_speed called for track=' + str(track) + ' with args=' + args) looper = None for device in track.devices: if device.class_name == 'Looper': looper = device break self._parent.log_message('set_looper_speed, looper=' + str(looper)) if looper: for index in range(len(looper.parameters)): param = looper.parameters[index] self._parent.log_message(str(index) + ': ' + str(param.name) + ' range=' + str(param.min) + '-' + str(param.max)) We actually got lucky here because the range of the Speed parameter in the API is exactly the same as the range shown in Looper itself. This isn’t always the case. Anyhow, now we’ve got the range (-36.0 – 36.0) and the index of the parameter (5).
Step 6 The D part of the spec is simple since the range in the API is the same as the range shown in Looper, so it’s obvious how we’ll specify the value. Now, we could do some error checking to assert that the value (argument) passed to our function is in the correct range, but it isn’t necessary because we have to use a try/except anyway. We need/want to use a try/except because the argument will be a string (text) and we need to convert it to a float (number). So the function will simply silently fail if an incorrect value or string is passed. This methodology is used a lot in ClyphX, so there are plenty examples. Anyhow, let’s get rid of all the logging and finish up the function:
def set_looper_speed(self, track, args): looper = None for device in track.devices: if device.class_name == 'Looper': looper = device break if looper: try: looper.parameters[5].value = float(args.strip()) except: pass
Step 7 Test the action with a variety of correct (like LOOPERSPD -14.5) and incorrect (like LOOPERSPD 37.8 or LOOPERSPD breakit) arguments to confirm that it works as expected.
|
|
|
Post by akamed on May 24, 2013 8:21:16 GMT -5
Great tutorial! It´s easy to get lost...but now, I have learned how to get parameters and devices to work with. It´s working fine in my project. I will upload it as soon as I´m sure it works as I desire. Thanks, Stray, for your time and for yor great work.
|
|
|
Post by Stray on May 24, 2013 12:32:09 GMT -5
You're welcome. I was hoping this would help you achieve what you were looking for it, but also hope that I presented it in a way that will be useful for other people/purposes too.
|
|
|
Post by akamed on May 24, 2013 14:49:59 GMT -5
Yes, that´s perfect for my case, and perfect for learning about Pyhton and ClyphX (because this is the only place we could find info about ClyphX) 
|
|
|
Post by jorgalad on Mar 2, 2015 6:34:10 GMT -5
Hey Stray,
This post is verg useful to me, pretty much the only thing out there that explains this stuff, along with the UserAction file, so thanks for taking the time. I wanted to edit this function and actually make it a bit easier. All I want to do is select the looper device (or any other device), so I looked at the LOM documents to find out how to select a device, which is in the song class. This is what I came up with:
def selbus_one(self, track, args): self._parent.log_message('selbus_one called for track=' + str(track) + ' with args=' + args) looper = None for device in track.devices: if device.class_name =='Looper': looper = device break if looper: try: looper.select_device except: pass
It probably shows that I have no idea what I'm doing, but I keep trying! Any insight on this? Do I need to mention the song class somewhere? In the future I want to be able to select my device racks on 3 different busses so i can easily see which FX I'm tweaking, so this would be the first step on understanding how that works.
|
|
|
Post by Stray on Mar 2, 2015 10:38:10 GMT -5
ClyphX has an action that does this already. It's called DEV SET and is one of the Device Actions. You can find the code for it in ClyphXDeviceActions.py.
|
|
|
Post by jorgalad on Mar 3, 2015 2:46:06 GMT -5
I've looked at the Dev Set but it doesn't look like something I can use in this situation. I just want to view a device, like MIDI learning the name of a track so that it switches to that track. Also in the manual it says that the Dev Track can only be used from an X-Clip but I want to map it to a button through the preferences file.
|
|
|
Post by jorgalad on Mar 3, 2015 2:47:52 GMT -5
Wait I get it, you where probably talking about Dev Sel, I'm gonna check that out! Can't believe I missed out on this function (twice)
|
|
|
Post by Stray on Mar 3, 2015 9:17:09 GMT -5
Ooops...yeah, I meant DEV SEL. Sorry about that.
|
|
|
Post by jorgalad on Mar 6, 2015 3:57:58 GMT -5
I've done this tutorial a few times just to get some practice, I'm having some problem setting up a list of actions though. What I want to do is create an user action that clears the looper, resets the FX racks and triggers the selected scene. Would be great if this could be quantized too. However all i'm getting inside my log file is this:
12148 ms. RemoteScriptMessage: (ClyphX) looper_multi called for track=<Track.Track object at 0x1be10f30> with args=12 12148 ms. RemoteScriptMessage: (ClyphX) looper_multi, looper=<Device.Device object at 0x1be10270> 12149 ms. RemoteScriptMessage: (ClyphX) 0: Device On range=0.0-1.0 12149 ms. RemoteScriptMessage: (ClyphX) 1: State range=0.0-3.0 12149 ms. RemoteScriptMessage: (ClyphX) 2: Feedback range=0.0-1.0 12149 ms. RemoteScriptMessage: (ClyphX) 3: Reverse range=0.0-1.0 12149 ms. RemoteScriptMessage: (ClyphX) 4: Monitor range=0.0-3.0 12149 ms. RemoteScriptMessage: (ClyphX) 5: Speed range=-36.0-36.0 12149 ms. RemoteScriptMessage: (ClyphX) 6: Quantization range=0.0-14.0 12149 ms. RemoteScriptMessage: (ClyphX) 7: Song Control range=0.0-2.0 12150 ms. RemoteScriptMessage: (ClyphX) 8: Tempo Control range=0.0-2.0 So I can't find the clear button, is it even possible or should I add something to my code?
Thanks!
|
|
|
Post by Stray on Mar 6, 2015 21:32:15 GMT -5
Not possible AFAIK.
|
|
|
Post by peeveebee on Jun 25, 2017 6:41:35 GMT -5
Hi, this question relates to LED feedback on the Push controller.
Using X-controls I've managed to assign velocity colours to Push's pads in User mode i.e.
16 = NOTE, 1, 92, 1/CLIP LOOP 0.0625, MIDI 144 92 72 8 = NOTE, 1, 84, 1/CLIP LOOP 0.125, MIDI 144 84 72 4 = NOTE, 1, 76, 1/CLIP LOOP 0.25, MIDI 144 76 72 2 = NOTE, 1, 68, 1/CLIP LOOP 0.5, MIDI 144 68 72 1 = NOTE, 1, 60, 1/CLIP LOOP 1, MIDI 144 60 72
I've been trying to figure out how to develop this by assigning two colours to one pad so that I can differentiate between playing/non-playing pads.
Do you know if this can be implemented in ClyphXUserActions.py? I just thought I'd ask before I begin looking into it.
Thanks.
|
|
|
Post by Stray on Jun 25, 2017 12:04:46 GMT -5
Sorry, I'm not sure what you mean by "playing/non-playing pads". Can you clarify and give me an example?
|
|