Momentary button to send effect with max value

4.35K viewsCSS Questions
0

I have a momentary button and I wanna send my send1 to max value (127). I also have a Knob1 that controls the send1. But additionally I want to have a that momentary button. For some reason I can’t manage to send the value of the send1 to max when pressed.

I tried with reaction, but without success (but maybe there is a easier way):

Listener: Button 1o was pressed
Action Block1:
Condition: Button 10 latest velocity value is equal – 127
Action: MIDI Controller – send MIDI velocity value to Controller input Knob1 Value 127
Action Block2:
Condition: Button 10 latest velocity value is not equal – 127
Action: MIDI Controller – send MIDI velocity value to Controller input Knob1 Value 0

Maybe it’s wront to send the velocity value to Knob1, but I couldn’t find an option how to set the send1 directly.

Any ideas on how I can achieve this?
Any help is appreciated

Rouz Answered question
Attached Files:
0

I don’t think Lists are bound to channels. Channels are just a way to separate the MIDI controllers with, so that the same CC values don’t interfere with each other (?). Or did you mean Mode, instead of Channel? I also don’t think a List is bound to a Mode; a List is just a storage container to store multiple items in, that you could use however you want. If I recall correctly, JohnC once commented that they are basically Python Dictionaries. But I’m not sure if you can address any key to a value, I’ll have to check it out myself.

There are 3 ways to use Custom Code. Whatever suits you best in the moment.

There’s this function in Ableton self.schedule_message(tick, callback_function) that puts a function back into Ableton’s program loop to be called again. The first argument tick determines how many ticks (which you could translate as frames) it takes before the function is called again. Fill in 1 to make it call the function on every tick. The second argument is the function you want to see called again (don’t add parentheses).

The way you could approach it here is (example using block level coding in a Reaction):

  • Create a Reaction that only gets called when a button is pressed down, not when it’s released. You could use the “exit reaction” method, that I explained before, inside a first Action Block, to exit when the Condition of button.cur_val = 0 is met.
  • Inside the second Action Block you can then set up an Action to do the block level custom code in. Here’s an example (untested).

first_time = True
send_value_storage = 0

send = self.song().tracks[0].mixer_device.sends[0]
example_button = self.midi_cc_ch_0_val_61

def falcon_power():

 if example_button.cur_val = 127:
   if first_time:
     send_value_storage = send.value
     send.value = 1
     first_time = False

   self.schedule_message(1, falcon_power)

 else:
   send.value = send_value_storage

As you can see in the code, self.schedule_message is called within the if statement that checks if the button is still held down. So, as long as the button is held down, this function will be rescheduled.

The variables outside the function let us store data, for as long as the rescheduling is happening. We only need certain Actions to happen when the button is pressed (and not on rescheduling), that’s why I’ve implemented the first_time variable, to control this. We can also store references to the Send or Button (or anything else that uses long lines of code) inside variables, to make the code inside the function a bit more readable.

Once the button is released, the first if statement becomes False, triggering the else statement, which doesn’t have a schedule_message call. This sets the Send’s value back to what was stored and ends the Reaction.

You could combine this with the Listener Number method I mentioned earlier. One way to do this is to store a python list of button references outside the falcon_power function (sorry for the name, couldn’t come up with anything relevant). Then insert each button reference, in the same order as the Listeners. Then use listener_number-1 as the list index to get the relevant button reference, and store this in a variable outside the function (although the listener_number might also stay the same upon rescheduling, so it probably wouldn’t be a big deal to use it inside the function as well).

Glenn V. Answered question
0

Thanks Glenn, will give it a try sounds straight forward.

Only the Listener Numbers I am not fully sure about now:

If Button1 is referring to Listener Number is 1 to target Send 1 and respectively .. I would somehow still need to map each button to the channel of the mixer, right?
I didn’t mean channel in the sense of a MIDI channel, but for example channel 1 of the mixer. Because I don’t want to update the send value for every mixer channel

Another question:

Is it possible to add the full code (including the button listeners) to users.py file?

Rouz Edited answer
0

Ah, now I see. What you mean by channel is the mixer_device of a Track. As you can see here, self.song().tracks[0].mixer_device.sends[0], each track has its own mixer_device. If you want to access sends from other tracks you just have to change the index of tracks in that line of code.

If you use the UI from CSS to select a send, like you did in the pictures you posted here, and then use the path menu, you’ll find different options that you can select for track number when you click on the downwards pointing arrow next to the input field (e.g. selected_track). Once you’ve selected an option, you’ll see the code that’s used, which you can then use within your own Custom Code.

Is it possible to add the full code (including the button listeners) to users.py file?

I’m pretty sure you can just create a method in the user.py file, copy the code inside that method and then just call self.user.method() within the Action of a Reaction to access it. Although, if you’re going to use the listener_number inside the code, then you should insert that as an argument into the method, self.user.method(listener_number).

Out of curiosity, what kind of MIDI Controller do you want to use for this?

Glenn V. Changed status to publish
0

Ah ok I see. Will try again tonight to see if I get any further.

I use the Livid DS-1 and want to use the buttons above every channel fader to trigger Sends 1/2 and in shift mode Sends 3/4.

Rouz Answered question
1

Hey Rouz, I made a user.py version today. The code I presented earlier wasn’t without some bugs. Also, to make it work in the user.py file I had to do a few adjustments. Here’s a link to the file on my google drive.

Some notes

To be able to access functions and objects from the Control Surface Script itself, it’s necessary to make a class variable from c_instance (which is an instance of the script and all it’s functions). Simply using self. inside the User class with the objects from that script wouldn’t work; in the way I implemented it you need to put self.c_instance before those objects.

This will become more obvious once you read the code. For example, the button references are part of the Control Surface Script so we need self.c_instance to access those.

The function I made for your request is at the bottom. In this user.py file there are a few other functions, most are unused in this case (but might prove useful for future projects). If you rather copy the function I made for you to your own user.py script, then you should also copy the following:

  • the self.c_instance variable inside the __init__ (that I mentioned above)
  • the log method, which I’ve used in the error handling of the function (or you could delete or change all the times log is called upon). I prefer this log method because I can add a little descriptor before a message, which makes it easier to find certain messages in the log.

The function takes at least 1 argument, the listener number. The variable for the listener number inside a reaction is called reaction_listener_number. The keyword arguments are not necessary to fill in, they will default to the following:

  • track_num will be the position of the selected track
  • send_num will be listener_num – 1 (because listener_num starts at 1 but send index starts at 0)

You’ll still need to change the channel and value of the button references inside the function to match those of your buttons. At the moment they are set to the buttons on my device.

How to set up a Reaction with the function

  • Add the Listeners you want to use, in order, to the Reaction
  • Create an Action, convert it to a Custom Code Block and add: self.user.full_on_press_reset_on_release(reaction_listener_number)
  • Save and generate the script. If the user.py file is in the correct position, then it should work.

However…

You’ve mentioned that you want to use a shift mode, which means that depending on the mode, a button might have to control a different send. The current state of the code doesn’t take this in account.

One way you could alter the code is adding an offset to the send index when you’re in shift mode. Each mode is part of a list. You can retrieve the position of a mode in that list via self.get_active_mode_number() (although, if you want to use that code inside the user.py file, you’ll need self.c_instance again. Once you know the position number of the shift mode, you can do something like the following:

shift_mode_num = ... # this should hold the number of the shift_mode in your script
current_mode = self.c_instance.get_active_mode_number()

if send_num is None:
 send_num = listener_num - 1

 if shift_mode_num == current_mode:
  send_num += 2 # I'm using 2 here because you mentioned that sends 3 and 4 were on the shift mode

Let me know how it works on your end.

Glenn V. Edited answer