So does this mean we can finally customize our control surfaces, as promised in the original Max 4 Live press releases, without resorting to Python scripting or other convoluted hackery?

Because while I understand that resources are tight and the LOM documentation almost didn't make it in at all, frankly this was one of the most disappointing things about the M4L release and the biggest reason why I haven't actually bought M4L yet (and therefore can't test this to answer the question myself).

We're starting to close the gap here. Congratulations Peter, the LOM Navigator is a great tool for exploring the functions and properties defined in the python remote scripts. Until such time as Ableton provides documentation for the ControlSurface object and its children, however, it is my opinion that the single best way to understand how to actually use these functions and properties is to study the remote scripts themselves (yeah, yeah, I know - wrong forum ;).

I'll admit that without some knowledge of python, it's not easy to decipher the scripts, but I've done my best to explain the basics on my blog and there is a link to documentation for the Framework classes there (the "Framework" is Ableton's function library for control surfaces). At very least, the results of LOM Navigator CS exploration may be easier to understand when cross-referenced against the Framework docs (and decompiled remote scripts).

Best,

Hanz

PS: From what I gather, there may be offical documentation for Control Surfaces in the works (although it may be months away). I've been tempted to put together some kind of unofficial Control Surface "programmer's guide", but I can't help feeling that it would be a waste of time, especially if Ableton is already working on something official.

PPS: @S4racen: disconnect is basically a python destructor call. If you call it on an important component (such as Session), it will also destroy (disconnect) all of that component's sub-components. Better to call set_enabled with a value of False if you only want to temporarliy suspend functionality of a component or control, and call sent_enabled True to re-enable.

Working on it. There are ways to do it, but you have to add some functions to the main Python script that you are working with. If you check out the LCD patch I just released, there are some functions inserted in it that allow communication back to m4l via the components in the script.

I've been putting off posting about this lately (been too busy with other things), but I want to get an idea of what people are expecting out of the control surface scripts and come to a consensus on how to accomplish those things. Its easy enough to do some basic editing to the LiveOSC script and channel things back into m4l instead of using OSC, but this doesn't solve the problem of tackling the _Framework scripts, which have embedded functions in the base classes that we need to get to.

I'm really curious to know whether or not this kind of functionality is something that Abes/Cycling intends for us to have, or if its hack-town for everyone that wants to do this. (?)

The _Framework scripts provide an easy way to setup groups of built-in functions that do predetermined things, and they work faster than any calls we can presently make with m4l Max/js objects. It is my intention to start using Python for working with anything that is control-surface-centric (basically everything I script at present), and implement functions that allow access via both MIDI (from the control surface itself) and m4l. In this way, you can modify the way a particular component works via m4l if need be, and send values from m4l objects to the components themselves, but still use the control surface objects to perform their built in functions relative to the control surface you have attached to it.

For instance: we can already create a user defined "Generic" control surface via Python without even knowing anything about Python, merely by creating a new folder and editing the Generic script definitions (MIDI channel, notes, CC's, etc). This works great for any MIDI input to the script, and we can even send/receive values to/from the controller (to turn lights on/off, what have you); but what we can't do is send values to the components directly to use the script to send values directly to Live.

I've been working with this for the last week or so, and its definitely possible to do, but not in an obvious way. For some reason, when you hijack the "receive_midi" function, you can change the value of the controller assigned to a specific function, but that value is not forwarded to Live (e.g. you can update the value of a controller like a fader that is assigned to the volume of Track 1, and it will change the scripts internal value, but it won't update the volume for Track 1 in Live). I've gotten around this by adding methods that call the corresponding function directly to Live, but I'm this isn't the best way to go about things really. Still, its a solution. The problem with this, though, is that you can't use these methods with existing scripts included with Live, and any work done towards this end now will probably be broken with the next major revision of Live, as I'm guessing some of this sort of thing will be included with future versions.

The _Framework classes need to have this functionality built into each class. If you want a quick fix now, I can probably get you started, or you can take a look at what I did with the LCD patch and figure it out yourself. I'm guessing most people don't want to learn Python in order to do this, though. And I wouldn't recommend it anyway, its kind of slow writing code this way, and there's a lot of guesswork involved due to a lot of things being hidden in the Python portion of the Live API.

The API is documented, and the basic functions are the same as everyone is already using in m4l (which is basically just a bridge to the Python LiveAPI...when you create a m4l API object, you are calling a Live function in the same way that the Python Remote Script calls it, only with a little bit different syntax).

That's my longwinded approach at trying to get something done, which probably isn't that effective. I'm still working on this, though, and when I have some worthwhile results that are more easily usable for the general user, everyone will know about it. I'd love to hear some input.

Hanz_Petrov wrote:
PPS: @S4racen: disconnect is basically a python destructor call. If you call it on an important component (such as Session), it will also destroy (disconnect) all of that component's sub-components. Better to call set_enabled with a value of False if you only want to temporarliy suspend functionality of a component or control, and call sent_enabled True to re-enable.

I've been successful on disabling everything on the APC40 by finding which component a button was a part of and calling set_enabled id 0 then to turn it back on id xx with xx being the original id of the element...

Some are grouped as pairs and max can handle id 0 id 0 but anything larger than that and Max can't handle tupples so you can turn the button matrix off for example but not turn it back on using the Control Surface......

@ S4racen: The functions that are larger than two are expecting tuples....if the function is written differently, it can handle individual arguments. Its just coincidental that the functions that are looking for two buttons at once are written like:

def function_blah(self, blah_a, blah_b)

That's another place we can write a simple function in the main class that can handle this:

....and then call the original function that we wanted to call that wants a tuple, and we have it defined as the "holder" that we can pass the function we want to redefine elements for.

We can't pass a tuple from Max, but we can pass multiple arguments to the script, compile them as a tuple in Python, and then use that stored tuple somewhere else. We just need to come up with a plan of stuff that we need to be able to do, and then come up with a clever way of implementing it in a simple, general way that is constructive and NOT destructive to the _Framework that already exists.

For the record, I think I'm doing things roughly the way you are...I don't think I'm calling set_disabled unless set_blah_button is looking for a tuple, then I disable it and reenable it later. I never completely destruct the class, as, obviously, then you can't get it back again.

@ Hanz:I'm still having trouble wrapping my head around how the _Framework classes handle add_value_listener methods; I can add them manually myself, no problem, but can't figure out how to hijack the listeners that are already created by the scripts. It seems like the only component with a listenable property is the ModeSelectorComponent, and I'm obviously just missing something about how it operates.

I think what can be done here is to create a super-class to wrap the original c_s class in, and define some basic utility functions inside that class that aren't necessary to the _Framework stuff, but can operate on top of it. Basic listener functions to be initiated after the main c_s class is constructed, some tuple_wrappers, the sysex_out stuff we already figured out for talking directly to the control surface (mainly for control_surfaces with LCD's or other unordinaries), and whatever else might be needed.

I don't even know if that is possible...theoretically, I think it is though. If it works like that, there would be no need to modify any of the existing _Framework docs or compiled scripts...all the additional functionality could be attained in the top Class, and other sub-classes could be added to deal with API stuff that's not included in the _Framework already. Thoughts?

@ ShelLuser: I've tried your script again...good work I tried it a while ago and it kept crashing/not working, which was a bummer because it looked promising. Its nice to have it in the format you've made. I've been using Mathieu's since beta, but it takes up too much real estate on the screen.

Suggestions: make it windowable. If you are working on c_s scripts (as it seems everyone suddenly wants to do) it will be disappearing a good deal if its tied to a track (like every time you call a function that takes you away from that track).

There needs to be a text box that you can type arguments into for "Call function"....it's next to useless without this to test things out (mathieu's was largely the same), as most functions require an argument to do anything, and most of them want something that's not simply an int/float.

I've noticed that if you add/remove a c_s, the umenu doesn't update this...it just keeps adding the same one to the end of the list. I'm looking to see if anything can be done about this....I've had issues with this and my own patches, so maybe there's nothing to be done.

[quote="amounra93"]If you are working on c_s scripts (as it seems everyone suddenly wants to do) .../quote]

I don't think it was terribly sudden. for instance, i have been interested since before M4L was released. i followed LiveAPI and we were elated at what frameworks looked to promise. But I chose to focus on M4L because I had already spent over a year working on a script that was broken by an update. I figure, the more I can accomplish in the supported realm, the better. But i have always been a little frustrated by the lack of basic things that control surface offer and i now feel like i am at the edge of what i can accomplish with 'just' M4L.