using the new v2 framework and inheriting from ControlSurface #73
Replies: 13 comments 78 replies
-
Nice job figuring out that much. Does 5 mean you already tried making that more or less the only change? and you got stuck at that error? What was the full error "trace" around that RemoteScriptError? Believe it or not, I saw a very, very similar error several times last night. While I was trying to figure out how to write Generally, I would guess you are headed in the correct direction for where you want to go. But now that I've said that and looked at a little of the code both some of the examples in the other scripts as well as the C4, and I think I can fairly certainly say, it would probably be easier for you to abandon the existing classes and structure, and build up what functionality you want new "from scratch" (from the library examples). I mean just decide what functionality you want and "how" you want to represent it. I mean as, for example, 4 rows of 8 encoders, or maybe 8 columns of 4 encoders, or maybe a button matrix, both, or all. There are lots of examples of scripts for devices with a group of 8 LED-ring encoders. The C4 has four such groups, and lots of buttons. The two Akai scripts I've looked at most are _Framework based, APC40 and APC40MkII (because I own one of each). so most of what I "know" comes from those scripts. They both inherit from OptimizedControlSurface, not just ControlSurface. I don't know the difference, but otherwise your example looks nearly identical to those two init methods. The giant difference between the C4 and basically any other control surface I've seen though is the LCD screens. Encoders and Buttons come in all shapes and sizes, but sending sysex to those LCDs is likely going to be unique. (and kind of where I would probably have started, figuring out some way to model the LCD screens with _Framework classes, like maybe NumericalDisplaySegment. If it represents a typical "10 segment display" (8?), then that could represent a single character "position" on an LCD, and the "LCD Screen" in code could be a class with 2 arrays (upper and lower text) of the 55 "text bytes" in each LCD. Do we need to go to the trouble of representing each character as a "NumericalDisplaySegment" when an array of 55 bytes would be just as suitable to the C4 and the sysex message? Depends on what that representation buys us.) The other concept that I suspect is going to be challenging is the different "modes" in the current script. I suspect part of the reason the "Arsenal" scripts are commercially viable is because Stray figured out how to implement "modes" in a common way across all the "Arsensal" scripts his company produced. So I would think if modes were "easy" to implement more scripts would make more use of them. One things that kind of stands in the way of my better understanding of Live scripting is... I can "see" where the hand-off between Live and our script happens in the MackieC4 class, and I can follow the "execution path" with my eyes and in my mind down into the script code and I know "where I am" when I get there. But over in the framework classes and scripts, the same cannot be said. I have yet to find a script where I can start at the top (or bottom) and work my way to the other end, down into the guts where the details are. (or up from the guts to the roof) It's like everything is abstracted away so you can't understand the execution path without first understanding the abstraction model. (and there are no documents detailing the abstraction model) I haven't really been burning any cycles studying the examples, so I've been happy to just work on the existing script. |
Beta Was this translation helpful? Give feedback.
-
have a look into https://github.com/markusschloesser/AbletonRemoteScriptsPY/blob/main/Akai_Force_MPC/elements.py https://github.com/markusschloesser/AbletonRemoteScriptsPY/blob/main/ATOMSQ/elements.py |
Beta Was this translation helpful? Give feedback.
-
Rather than a new thread. This one seems to be on topic anyway... (My answer doesn't necessarily sound like it, but I'm talking to myself in the future a lot)
This is one of the things I wanted to write down. All this does end up in ControlSurface anyway, but the only behavior mapped by default is no behavior. Whatever behavior you want your script to invoke, you need to customize the associated "control surface behavior modification" class by using it directly and giving the default behavior "values to work with" (like ButtonElement objects) or by inheriting from it to add customized behavior code. When researching other Control Surface scripts, I suspect the key to understanding how they do what they do is to carefully review the import statements at the top of the main script class file before trying to read the For example, in AxiomPro, they don't customize any behavior for these three components.
If they are doing more than providing appropriate data values, they just connect generic ControlElement objects like ButtonElement. For the behavior that is the most specific to the Axiom Pro hardware and software, they do customize behavior in these classes (technically I think the files are called "modules" because more than one class per file is allowed. That's what the import syntax says to me, "from (current directory).(module) import (class))
4 "ModeSelector", 2 "Component", and 1 "Element", that's 7 behavior customizers. The SelectButtonModeSelector class, for example, is used to make 4 "dedicated" hardware buttons control the mappings associated with a group of 8 hardware encoders. In other words... There are four buttons in "mixer mode" that change the function of the 8 encoders between controlling Pan, Send Level A, Send Level B, or Send Level C. I think those same 4 buttons in "device mode" map 8 "device parameters" per "page" of parameters to those same hardware encoders. These is special code for handling devices with more than "4 pages" of parameters (more than 32 parameters). On that note, the PageableDeviceComponent and PeekableEncoderElement are related custom behavior implementations. The "paging" and "peeking" aspects are not default behavior. In addition to handling "regular devices" normally, PageableDeviceComponent appears to "specifically enhance remote control" of whatever VST instruments or effects were bundled with the Axiom Pro. (maybe?) |
Beta Was this translation helpful? Give feedback.
-
btw to bring every info into this issue: #47 (comment) did you see this last year? |
Beta Was this translation helpful? Give feedback.
-
I think I did see that post back then, but 18 months of river have passed under the bridge since then... good resources. I'm thinking we can use one or more versions of that kind of "select button mode selector" specialization. But for the C4, the 4 selector buttons would be the 4 Modifier buttons. Then we could, for example, make all 32 encoder buttons implement 5 "behaviors" each (default, shift, option, control, alt) This would be 5 "short press" selections. "long pressing" (holding) modifier buttons could still control "one off" alternative behaviors in various modes. And we could also implement other kinds of "select button mode selector" specializations for those modifier buttons, for example, each of the 4 modifiers could be linked to a specific row of 8 encoders and each modifier button press could cycle through the same set of alternate behaviors. One row could have the encoder buttons mapped to "jump to previous saved value" (value saved upon "long press" of encoder button?), and another row could have its encoder buttons mapped to "Jump to 0", "Jump to 127", "Jump to 63", whatever. And, that's probably also how we can easily get more than just 4 modes. Another specialization could map the 4 modifier buttons to the 4 assignment buttons similarly altering their behavior ("long press" Chan Strip button, for example, to go back to "default Chan Strip" mode. Otherwise click Shift, then Chan Strip to get "shift + chan strip" mode; Option, then Chan Strip to get "opt + chan strip", etc. I don't know that we would actually have a use for all those options. But if a script starts with the idea of such "unknown options" in play, accommodating those same or other options later can be a much easier egg to fry. |
Beta Was this translation helpful? Give feedback.
-
No "dependency injection" stuff (yet?) LOL, I started with Axiom Pro, not ATOMSQ. The code I pushed up tonight still barely works, but I suspect it barely works from a more solid foundation. The only thing working on the LCD screens is the "clear all" message. The script starts off in "channel strip" mode and the Track Volume feedback is working (but the data is jacked) but the encoder isn't working (Change Track volume with the mouse and LED ring display updates). You can now click the bottom row encoder buttons to Mute, Arm, Activate the selected track and Play, Stop, and Record the transport. I also tried to get the same "device handshake" going that Axiom has, but I don't think the C4 consistently responds to "firmware request" midi messages. It seems like the C4 will only respond to a firmware request once after power up. If the C4 is powered up and Live app cycles, I'm not seeing a C4 SYSEX response and all the "script components" stay disabled until you power cycle the C4. If you happen to click the Marker button, the script tries to load a device and barfs all over the log... It REALLY seems like I could stand to go to like a 3-day "Control Surface Writers" workshop with Ableton "subject matter experts". I'm betting I could learn in 3 days what I might not get on my own in 3 months... |
Beta Was this translation helpful? Give feedback.
-
I think I've reached an early "false summit" developing this V2C4 script "from scratch". The script is still more or less broken, but now it seems like the whole car is up on blocks in my driveway with only a few parts or connections missing versus still being at the factory on an assembly line staffed by one barely trained monkey. (that's me :)) I checked in a commit just now that works only a very little bit better than it did before, but now I think all the correct bones are more or less in the correct places. There are basically 3 custom component classes C4ChannelStripComponent, C4DeviceComponent, and C4EncodersComponent that interact with Live and the C4ModeSelector manages the connections between the "control surface element model" objects like Button and Physical Display Elements and the various component objects. I repurposed the file named C4Controller into the one named C4ControlSurfaceComponent. But I abandoned that approach as soon as I realized that by introducing a customized base ControlSurfaceComponent I was taking on all of the tasks and responsibilities previously handled by each of the default ControlSurfaceComponent subclasses. Or at least I think that's what happened. I didn't do a lot of experimenting while zoomed in on that issue. I ended up making the C4EncodersComponent inherit from C4ChannelStripComponent instead of C4ControlSurfaceComponent, and that's what kind of working now. The encoders are kind of bound to the selected track anyway. IDK how closely you've been watching the commits progression if at all, but the branch as of this commit might be worth reviewing |
Beta Was this translation helpful? Give feedback.
-
I saw you started a new branch off of the C4Modeling branch, your one commit looks like you've made a good start using v2 imports instead of _Framework. I banged my head against the wall I hit one or two more times today, but then I went back to the drawing board to update the V2C4 design again. In a nutshell I think my yesterday's problem was fairly fundamental, the C4EncoderComponent class should not exist. At a high level I think _Framework classes with "component" in the name model LOM stuff and _Framework class with "element" in the name model the midi hardware you want to connect to and control the LOM stuff. I think I probably should have realized remembered that fact before I diverted so far up this dead end route. I was misled by my first reaction to the first error I saw that looked something like I went back to using "C4Encoders" class object on the "InputElement" side where they now clearly seem to belong. I think I learned a lot being stumped by this impasse so many times. But even after cutting over to the Element side, the issue that is dogging me is "inheriting" from an EncoderElement. None of the examples I've tried to implement have been successfully implemented. Not even the examples in the EncoderElement module itself. I've got an APC40 with encoder rings and it's script works, but that "_Framework code APC40 script" example doesn't work when I copy it to make a "C4Encoder". The reason I think we need to inherit from an EncoderElement rather than just using it is so we can override this method in InputControlElement base class
and only return a tuple of the "legal" led ring values for any ring display mode. We also want to be able to update the ring display mode depending on what we connect to. But the furthest along I seem to be able to get the script is almost all the way through initializing everything, and then...
I've reached this same dead end three different ways. `InputControlElement.install_connections() is a method that takes other methods as arguments and then runs them using instance values as inputs. Line 700 in ControlSurface where the assertion actually throws, seems to be around line 600 in my _Framework file, but there is more than one candidate assertion... The other problem is I can't find the code where an APC40 script, for example, implements those callback methods. RingedEncoderElement has an overloaded implementation of install_connections() where the input variable names contain "callback". But I can't find the place where that method is called to see what the callback inputs are, how the methods are implemented. |
Beta Was this translation helpful? Give feedback.
-
I "might be" about done banging my head into this wall though. I expected my "Arsenal C4" script to fail somehow due to something I didn't understand about the "base classes" like any kind of protection against random people like me coding "new" Arsenal scripts. Even though all I really did was provide C4 specific midi information to the "midi map" class like channel 0 instead of channel 15 and, of course, changing map mode to RSB from absolute, the new "Arsenal_C4_A" script is broken in the same way we've been experiencing using the Framework classes (v1 and v2) in our "simple scripts". All the button elements just work "both midi directions", so the new "Arsenal_C4_A" script does all the "Arsenal magic" in response to button clicks switching out all the various "encoder modes", for example, exactly like the "BCR2000 script" settings I had previously defined and copied over as the C4 script's settings. But the C4 script's Encoders are just as broken as we've been seeing. The feedback side works "normally", the LED rings update with shizzle when the encoder modes change and when you mouse drag connected parameters in Live. But the inbound encoder messages still don't impact (update) those connected parameters. (and, of course, no "feedback" associated with knob turns because no input midi received and processed) I haven't added a bunch of "investigative logging" to the new script, but I'm not sure I see a point. The new "Arsenal_C4_A" script's main The only thing different between the two scripts of any substantive merit to me is RSB map mode in the C4 script, and if Arsenal is showing the same problem we've been seeing in our scripting, then I'm no longer sure we're looking at a solvable problem. If (Stray) the Python coding wizard who wrote the "Arsenal framework" didn't solve this issue already, I am a mere Python coding acolyte in comparison. I no longer feel much confidence in my own ability to find any solution-route up this coding-cliff. Maybe an answer seems so hard to find because an answer doesn't exist. Maybe the overhang is too far beyond vertical to get around. |
Beta Was this translation helpful? Give feedback.
-
Writing this out to help me remember what Internally to the ControlSurface class, the
is called from the
The
Notice how the Given all of that and then some... It looks like one way to implement a solution is to override
I realize you might not be back from vacation yet, but I just pushed up a commit implementing this solution on the "C4 Modeling" branch. It's still a mostly not working very well Noobie Nuberson kind of script, but you can switch tracks left and right and the volume and pan encoders work correctly. The "Marker" button will even toggle between "device mode" and "channel strip mode" too. I didn't test much of anything, happy enough for today just to see "real encoder action"... You'll be able to check it out whenever you return from vacation. |
Beta Was this translation helpful? Give feedback.
-
Minimum custom code required for C4 Encoders to work in Framework scripts: As noted in a previous post, override the main ControlSurface script's
Notice BOTH To get feedback data to the LED rings working properly, you'll need to make a custom class that inherits from EncoderElement and add more specialized code.
That should be all the changes necessary for minimal encoder functionality with "single dot" feedback. For more details, see any associated code such as C4EncoderElement and C4EncoderMixin classes in the V2C4 script folder in this repository. But note that the above is only minimal code. Typically, for example, you would at least want to save a reference to the selected "LED Ring display mode", the generated tuple of mapping feedback values, or both in your custom C4EncoderElement class. The above example code as shared will only ever show "single dot" display mode feedback. EDIT: added links, fixed typos |
Beta Was this translation helpful? Give feedback.
-
This post kind of takes a tangent from recent topics in the discussion thread, but I think still "belongs" on topic of using the new v2 framework in new or updated scripts. Even in the new v2 framework (and later), there seems to be evidence of a difference between an "old way" and a "new way" of implementing custom event handling in control surface scripts. The "old way" would be "directly registering event listener methods with event producing objects" and the "new way" would be "implementing the "Observer Pattern" in custom code to produce the same outcome, to get the same "the event of interest happened now do this" code to run. (specifically note the accepted SO answer links to 3 other "Python ways" that could also be used to implement "Observer" before describing a "first class functions" way with examples, and that most of the comments under the question also contain good info) (Other python specific ways to "python implement observer pattern" are easily searchable) In the v2 framework, for example, we have the NotifyingControlElement class implementation that InputControlElement inherits from. NotifyingControlElement itself inherits from both EventObject and ControlElement classes. The EventObject class is a concrete CompoundDisconnectable, and the Slot class is a concrete Disconnectable. All of the docstring comments are worth reading (again?) within the code just mentioned. But these in particular stand out:
TLDR, for now. In other words, the "old v2 way" is to "manually" handle appropriately registering and unregistering "listener methods" in custom scripts; and the "new way" is to use annotations (decorators?) like |
Beta Was this translation helpful? Give feedback.
-
never mind 3. fixed that by changing / to // for line 27 and 28 (remembered that 💪😎)
I thought I quickly check out your work, but it is getting late her, so will have to postpone till tomorrow |
Beta Was this translation helpful? Give feedback.
-
This thread is a collection of info around the topic:
class MackieC4(ControlSurface):
you cannot have adef song
anymore (imho cos song is now an object passed down by the super class)Beta Was this translation helpful? Give feedback.
All reactions