markusschloesser / MackieC4_P3

A Mackie C4 Midi Remote Script for Ableton 11
16 stars 2 forks source link

"Function" mode features #65

Open markusschloesser opened 3 years ago

markusschloesser commented 3 years ago

Function mode is now declared a kind of "Global" mode. Currently implemented are:

To implement / ideas:

Let's update this issue with further functionality for Function mode

BilldarBagdar commented 3 years ago

I pushed an update of the shared-dev branch to origin. I was able to implement the full-on full-off LED ring toggling, it works for the first 3 encoders in function mode, but only just kind of for some and not at all for others. It looks like maybe some of your new code needs a bit of TLC, and I thought you would like to peel your own potatoes in that regard.

I suspect Record Arm and Mute encoders could also use that "blinking" treatment in channel mode, but didn't go there yet.

markusschloesser commented 3 years ago

I pushed an update of the shared-dev branch to origin. I was able to implement the full-on full-off LED ring toggling,

great! Will test tomorrow, the C4 is currently detached as I received the VESA arm, but unfortunately the drills I had, have the wrong size, so I gotta wait for Amazon prime's delivery tomorrow (shops closed here).

it works for the first 3 encoders in function mode, but only just kind of for some and not at all for others.

which one's? can't test right now

It looks like maybe some of your new code needs a bit of TLC, and I thought you would like to peel your own potatoes in that regard.

they definitely need some tweaking, as I said, I implemented stuff the easiest way possible :-). I'm afraid this will stay for a while, as all the "cool" implementations use decorators and super and rely on the LOM framework etc , and I'm not there yet 🙄😜

I suspect Record Arm and Mute encoders could also use that "blinking" treatment in channel mode, but didn't go there yet.

The other Mackie controllers do not blink, and I definitely prefer it that way ;-)

BilldarBagdar commented 3 years ago

"blinking" is the wrong word I should have used "binary", the whole ring is lit or the whole ring is dark and it changes when you press the button. Or is supposed to. encoders 1, 2, 3 in "function" mode do, and the others should as soon as the logic toggles. The "arrangement view" toggles in Live, for example, but not the logic to change the LED ring. Or so it seems. I could go in and figure it out. It kind of depends on how much more you want to "learn for yourself" about programming and Python. You're already pretty close with the functionality.

I will also mention this though, and show a more Python example you can run with.

            if encoder_index == encoder_07_index:
                song_util.unmute_all(self)
                for track in tuple(self.song().tracks) + tuple(self.song().return_tracks):
                    if track.mute:
                        s.show_full_enlighted_poti()
                    else:
                        s.unlight_vpot_leds()

This for-loop is iterating over a collection of tracks + returns and checking each one on a single condition. Think through the whole loop the way it is coded here. When No tracks are muted, and when one track is muted several times; when the first track is muted (index 0), when the last track is muted (index ``len() - 1"), and when a track somewhere in the middle is muted. Under which of those conditions would you ever (have time to) see the LED ring "fully enlightened"?

Also, this code reads oddly. I mean it calls a method named unmute_all() and then immediately proceeds to check if every track is indeed unmuted. However, ignoring that apparent oddness, a more Python way of doing that same check more accurately would be

song_util.unmute_all(self)
muted_remnant = next(track for track in (tuple(self.song().tracks) + tuple(self.song().return_tracks)) if track.mute)
if muted_remnant:
    s.show_full_enlighted_poti()
else:
    s.unlight_vpot_leds()

I haven't actually tried the code I just typed, but I copy/pasted and substituted from working code.

As I was thinking about this, it occurred to me it would definitely be possible to "pre-check" the "are any tracks muted" condition when we go to "function" mode and then that "unmute all" encoder ring could be "pre-lit" whenever there are any "muted tracks to unmute". One could also check for "muted tracks" every time the "display update" method runs in "function" mode and "light it up" any time some track is muted.

markusschloesser commented 3 years ago

As I was thinking about this, it occurred to me it would definitely be possible to "pre-check" the "are any tracks muted" condition when we go to "function" mode and then that "unmute all" encoder ring could be "pre-lit" whenever there are any "muted tracks to unmute".

That was the idea, but I obviously didn't think it through 😂

One could also check for "muted tracks" every time the "display update" method runs in "function" mode and "light it up" any time some track is muted.

That sounds expensive. Actually implementing logic like that, there, does not feel good anyway. I mean currently, I implement stuff (logic) somewhere (vpot press), than do encoder ring lighting somewhere else and THAN do display stuff in on_display_timer. Ideally one would implement logic AND displaying once in ONE place. I tried to come up with that, but until now couldn't figure it out

BilldarBagdar commented 3 years ago

That sounds expensive.

One of the "rules" of software development is to avoid "premature optimization". It's based on the "proven fact" that humans are really bad at recognizing code and situations where that kind of problem manifests. The more general principal that applies is "don't fix what ain't broke".

You are correct to recognize and be concerned about "processing expense", and also correct to be wary of making changes in the "log vomit" method. But in general, unless you are doing something you "know" could "blow up the stack" like multiplying two matrices, or recursively solving the "Towers of Hanoi" problem, a developer shouldn't really be concerned with "processing expense" until it is a problem AND evidence has been collected from the running program that pinpoints the program "processing bottleneck" areas. Also, in general, the "biggest numbers" we are dealing with in this script are pretty small in computer science terms.

I mean, even if some project has 128 total tracks and return tracks (the maximum our script "list db" can process), 128 is not a "big number" in that respect. Iterating through 128 element of an array once and checking one property of each element is a comparatively "cheap" operation. It would be another story for example, if the code also had to dive into each of those 128 "top-level" elements and iterate through another list of 128 elements, and then another 128 elements in another list; and then compare that 128x128x128'th item against every other "top-level" element (two lists down) for some matching value.

There exists the concept of "Big-O" notation in computer science and it represents the operational complexity of any given operation like sorting or searching a list, for example. (roughly equal to "processor cycles needed"). Searching a list for an element with a certain property or not (list of tracks for any muted track) is an O(n) operation, meaning the "cost" of searching the list scales linearly with the number of elements in the list. Searching a list for an element with a certain property where that property itself has a property in a list, and that property has another list is an O(n^3) operation, meaning the "cost" of that operation scales exponentially with the number of elements in the "top-level" list (technically the exponential scale scales with the number of elements in each list "dimension"). The only Big-O complexity cost that is better than "linear" is "constant". Finding the "length" of a list is an example of O(1) - a constant complexity cost. It generally doesn't matter how big a list is, it always takes the same number of processor cycles to determine the length of that list.

As a rough rule-of-thumb, one can equate "nested loops" with increasing Big-O complexity. Every new "inner loop" adds 1 to the Big-O exponent. So, generally, a "single" loop cost is O(n), a "double nested" loop cost is O(n^2), and a "triple nested" loop is O(n^3).

The reason I even thought about "on display update" for checking the "mute status" is because if the device is already in "function" mode, and the "track mute" status changes from "none are muted" to "not-none are muted" somewhere else (like another controller or the GUI), the encoder ring would only "light up" in response after the device switched away from "function" mode and back. But a change in "on display update" would make the script behave more responsively.

I don't think it's very necessary, but we could code in a counter or something that would implement only actually checking the "track mute status" once every 10th time "on display update" runs. That would probably still be "responsive enough" for the human eye. If one is watching carefully, one could spot 1 second of latency before the ring lights up, but that's probably "fast enough" for our purposes. Only the most pedantic of nit-picky users might complain that the encoder ring takes too long to respond and light up (or turn off) when the mute status changes elsewhere.

Edit: However, with that said, this "button pressed" handler and/or a "track mute listener" callback method would also be a good place to simply store a class level flag value that could be checked in "on display update" where the LED ring "display" would be "updated" ON or OFF accordingly. So, you only "pay" (exponential cost) to search the list of tracks when an event indicates the need, and only "pay" the much less expensive (constant) cost of a "flag value lookup" when the much more frequently called "on display update" method runs.

markusschloesser commented 3 years ago

That sounds expensive.

One of the "rules" of software development is to avoid "premature optimization".

Got it

You are correct to recognize and be concerned about "processing expense", and also correct to be wary of making changes in the "log vomit" method.

That's exactly my main concerns. BUT, see below

The reason I even thought about "on display update" for checking the "mute status" is because if the device is already in "function" mode, and the "track mute" status changes from "none are muted" to "not-none are muted" somewhere else (like another controller or the GUI), the encoder ring would only "light up" in response after the device switched away from "function" mode and back. But a change in "on display update" would make the script behave more responsively.

2 things:

  1. Constantly "checking if somethings changed" is imho a lot inferior to "get notified when things have changed" (see https://github.com/markusschloesser/MackieC4_P3/issues/66#issuecomment-822071055 this is for track, but there are similar things for devices (https://github.com/markusschloesser/AbletonRemoteScriptsPY/blob/main/ableton/v2/control_surface/components/device_parameters.py), and one for mixer_device which would be the one to listen to in your example)
  2. as on_update_display_timer keeps fucking with us (had a blow-up during a recording session today), I don't wanna expand that section, but reduce it to the smallest size possible. I checked a lot of other scripts and ALL the newer ones do not have this part. It seems more and more an archaic, badly done, error prone thing.

I don't think it's very necessary, but we could code in a counter or something that would implement only actually checking the "track mute status" once every 10th time "on display update" runs. That would probably still be "responsive enough" for the human eye. If one is watching carefully, one could spot 1 second of latency before the ring lights up, but that's probably "fast enough" for our purposes. Only the most pedantic of nit-picky users might complain that the encoder ring takes too long to respond and light up (or turn off) when the mute status changes elsewhere.

If the scripts/C4 were to get notified, there wouldn't be a delay.

Edit: However, with that said, this "button pressed" handler and/or a "track mute listener" callback method would also be a good place to simply store a class level flag value that could be checked in "on display update" where the LED ring "display" would be "updated" ON or OFF accordingly. So, you only "pay" (exponential cost) to search the list of tracks when an event indicates the need, and only "pay" the much less expensive (constant) cost of a "flag value lookup" when the much more frequently called "on display update" method runs.

I think goes into the direction of what I was writing about above, but I wanna take a step further ;-)

markusschloesser commented 3 years ago

"blinking" is the wrong word I should have used "binary", the whole ring is lit or the whole ring is dark and it changes when you press the button.

Looks fantastic!

I will also mention this though, and show a more Python example you can run with.

            if encoder_index == encoder_07_index:
                song_util.unmute_all(self)
                for track in tuple(self.song().tracks) + tuple(self.song().return_tracks):
                    if track.mute:
                        s.show_full_enlighted_poti()
                    else:
                        s.unlight_vpot_leds()

This for-loop is iterating over a collection of tracks + returns and checking each one on a single condition. Think through the whole loop the way it is coded here. When No tracks are muted, and when one track is muted several times; when the first track is muted (index 0), when the last track is muted (index ``len() - 1"), and when a track somewhere in the middle is muted. Under which of those conditions would you ever (have time to) see the LED ring "fully enlightened"?

Also, this code reads oddly. I mean it calls a method named unmute_all() and then immediately proceeds to check if every track is indeed unmuted. However, ignoring that apparent oddness, a more Python way of doing that same check more accurately would be

song_util.unmute_all(self)
muted_remnant = next(track for track in (tuple(self.song().tracks) + tuple(self.song().return_tracks)) if track.mute)
if muted_remnant:
    s.show_full_enlighted_poti()
else:
    s.unlight_vpot_leds()

Funnily enough, the stuff you deemed unpythonic was exactly the part I borrowed from somewhere else 😂 (Launchpad SpecialMixerComponent)

BilldarBagdar commented 3 years ago

The "problem" is that "on display update" already is an event notification "callback method", that's how the "changing" volume or "pan" is reflected in the LCD for example. Every time "on display update" runs in "channel" mode, the volume level might be changing because the user is dragging the mouse in the GUI. If the script isn't updating the LCD with that value (or any changing value it is displaying) when the event occurs (in "on display update"), then you aren't updating the LCD at all until something else triggers the script to update that cell of that LCD. and also, at the "top level" there are only 32 encoders, and generally "on display update" is doing "constant time" lookups. In device mode, for example, for each encoder, we get it's DeviceParameter and then "print" it to the LCD (segment upper, and lower). That's a "constant time" operation, as good as it gets in terms of computational efficiency. (it's true, iterating the list of 32 encoders is a "linear time" operation. But there are always 32 encoders so that "linear time" cost becomes a fixed amount, and 32 is a really small number as a "list size")

Sorry to hear about your busted recording session.

IMHO, ideally EncoderController would be a lot simpler as a class with as much behavior and as many smarts as possible pushed down into, for example, the Encoder and the EncoderDisplaySegment classes (Or "EncoderRow" representations). But the way it is coded, most of the "smarts" written into EncoderController are related to the modes, and that makes the behavior more difficult to "push down". All of the behaviorally significant methods in EncoderController are more-or-less structured around what each encoder should do in each mode.

if self.__assignment_mode == C4M_CHANNEL_STRIP:
elif self.__assignment_mode == C4M_PLUGINS:
elif self.__assignment_mode == C4M_FUNCTION:
elif self.__assignment_mode == C4M_USER:

The reason EncoderController maintains, for example, self.__t_d_p_current[] list and friends is so that the script "remembers where you were" when you come back. I mean, on track 2 you go to the second parameter page of the third device on the track and make an adjustment. Then you switch to channel mode, switch to 3 other tracks and change various settings in various modes. Then when you come back to track 2 and switch to device mode, the script displays for you where you were, the second parameter page of the third device on the track because the script remembered you were there. That kind of "memory" seems like it belongs at the EncoderController level, because I sense a lot of code duplication trying to implement "modal behavior" like that at the "encoder level". I could be wrong of course, and it wouldn't be the first time. But so far every time I think I'm getting an idea about what the "blueprint" should look like going forward, I'm drawing blanks instead.

It is indeed funny that you copied that for-loop code from another script. Typically, when you code a for-loop like that to check some status, you would "break out" of the loop as soon as, in this case, you found the first muted track. There's no need to keep searching for more muted tracks at that point. But without any guard rails during the actual looping, unless the very last (return?) track is muted, that LED ring might not even have enough time to completely respond physically to the ON signal before all the subsequent OFF signals arrive. A human staring right at the ring might have a chance to notice it flash briefly.

markusschloesser commented 3 years ago

The "problem" is that "on display update" already is an event notification "callback method", that's how the "changing" volume or "pan" is reflected in the LCD for example. Every time "on display update" runs in "channel" mode, the volume level might be changing because the user is dragging the mouse in the GUI. If the script isn't updating the LCD with that value (or any changing value it is displaying) when the event occurs (in "on display update"), then you aren't updating the LCD at all until something else triggers the script to update that cell of that LCD. and also, at the "top level" there are only 32 encoders, and generally "on display update" is doing "constant time" lookups. In device mode, for example, for each encoder, we get it's DeviceParameter and then "print" it to the LCD (segment upper, and lower). That's a "constant time" operation, as good as it gets in terms of computational efficiency. (it's true, iterating the list of 32 encoders is a "linear time" operation. But there are always 32 encoders so that "linear time" cost becomes a fixed amount, and 32 is a really small number as a "list size")

I am absolutely fine with that, but: we currently on one hand constantly check for changes where we should/could choose not to ("has a track been muted" "scene listener" "tempo" etc) and the way the script does at least seems unelegant (if tempo not current tempo than change tempo). Instead we could utilize what the framework offers so for example for "track mute"

@listens_group('mute')
    def __on_track_mute_state_changed(self, mixable):
        self._update_mixable_color(self.tracks.index(mixable), mixable)

And on the other hand we have lots of things where we don't check constantly and therefor the C4 does not know of changes and displays wrong or outdated values or devices, which is the case for

  1. "track change via GUI" which results in the wrong device being selected/device selection not updated
  2. amount of device params change (edit: there is code for that "__on_parameter_list_of_chosen_plugin_changed", but it doesn't work)
  3. my new stuff in song_util
  4. there are others

The reason EncoderController maintains, for example, self.__t_d_p_current[] list and friends is so that the script "remembers where you were" when you come back. I mean, on track 2 you go to the second parameter page of the third device on the track and make an adjustment. Then you switch to channel mode, switch to 3 other tracks and change various settings in various modes. Then when you come back to track 2 and switch to device mode, the script displays for you where you were, the second parameter page of the third device on the track because the script remembered you were there. That kind of "memory" seems like it belongs at the EncoderController level, because I sense a lot of code duplication trying to implement "modal behavior" like that at the "encoder level".

Thats a great feature btw! The remembering part

I could be wrong of course, and it wouldn't be the first time. But so far every time I think I'm getting an idea about what the "blueprint" should look like going forward, I'm drawing blanks instead.

I have an idea, but cannot implement it 😂

It is indeed funny that you copied that for-loop code from another script. Typically, when you code a for-loop like that to check some status, you would "break out" of the loop as soon as, in this case, you found the first muted track. There's no need to keep searching for more muted tracks at that point. But without any guard rails during the actual looping, unless the very last (return?) track is muted, that LED ring might not even have enough time to completely respond physically to the ON signal before all the subsequent OFF signals arrive. A human staring right at the ring might have a chance to notice it flash briefly.

Misunderstanding: I thought you were referring to the method in song_util. That was copied

markusschloesser commented 3 years ago

And because I explained the issue to my wife with a, IMHO rather nice, analogy, I wanna share this with you as well. I want to move from a client pull /Poll (pop3) to a push (IMAP) implementation. I don't think fully relying on Live and getting rid of the internal db means that we loose the "remember where we were" part. But instead of using an index for everything, we use an index only for the remembering part

markusschloesser commented 3 years ago

As I was thinking about this, it occurred to me it would definitely be possible to "pre-check" the "are any tracks muted" condition when we go to "function" mode and then that "unmute all" encoder ring could be "pre-lit" whenever there are any "muted tracks to unmute". One could also check for "muted tracks" every time the "display update" method runs in "function" mode and "light it up" any time some track is muted.

I tried to do exactly that, but it doesn't work. Will write a separate discussion for all my naive questions

BilldarBagdar commented 3 years ago

I just pushed an update to the shared-dev branch. The "function" mode "unmute all" encoder LED ring now always reflects the correct mute status for the entire song. If there is anything to "unmute" the ring is ON. Otherwise it's OFF.

I know this isn't the most efficient implementation, we can do better with a "listener based" implementation. But I didn't find the right place for that yet, and this was pretty easy.

markusschloesser commented 3 years ago

great! Will check. I also implemented and fixed a bit in the last couple of days. Will merge. I also massively and (semi-)intentionally fucked around with lots of stuff, which helped me gain some further insight. But that's for another thread :-)

markusschloesser commented 3 years ago

merge done, was a bigger hassle than expected. Unfortunately function mode currently has display issues in row 2 on the C4. Worked on that, but still funky. Didn't wanna fall too far behind, that's why I wanted to merge into shared-dev.

markusschloesser commented 3 years ago

Unfortunately function mode currently has display issues in row 2 on the C4.

fixed

BilldarBagdar commented 3 years ago

If you didn't deliberately remove it, it was accidental, but I had to put back some code that I guess disappeared during the "merge hassle". Now I bet you are starting to see why I generally do whatever I can to avoid merge conflicts, because unwinding them can be fraught with peril and/or frustration. About six lines in "on display update" disappeared. The new method I added in song_util was still there, but the code that uses that method to toggle the LED ring disappeared. So, I doubt it was purposeful, but if so, why?

Hope you don't mind, I refactored the way the "function mode" display is done. It should all work exactly the same, except maybe the undo and redo displays. The upper text alternates between, for example, 'undo' and '------', but it's not clear to me how the text and LED ring combine to tell a user like me the difference between "there is or is not some event to undo". So maybe I screwed up the translation. I pushed up these changes in the shared-dev branch again.

I hope you can see the logic in the refactoring changes I made, particularly in how the various methods interact to support the functionality. The updated "function mode" is now a little more similar in structure to "channel" and "device" mode.

markusschloesser commented 3 years ago

If you didn't deliberately remove it, it was accidental, but I had to put back some code that I guess disappeared during the "merge hassle". Now I bet you are starting to see why I generally do whatever I can to avoid merge conflicts, because unwinding them can be fraught with peril and/or frustration. About six lines in "on display update" disappeared. The new method I added in song_util was still there, but the code that uses that method to toggle the LED ring disappeared. So, I doubt it was purposeful, but if so, why?

So sorry, not intentional!

Hope you don't mind, I refactored the way the "function mode" display is done.

Of course not!

It should all work exactly the same, except maybe the undo and redo displays. The upper text alternates between, for example, 'undo' and '------', but it's not clear to me how the text and LED ring combine to tell a user like me the difference between "there is or is not some event to undo". So maybe I screwed up the translation. I pushed up these changes in the shared-dev branch again.

IMHO Logic should be: If there's something to undo or redo, light up vpot, show undo and or redo. If there is nothing to do, unlight and don't show "call to action" in display

I hope you can see the logic in the refactoring changes I made, particularly in how the various methods interact to support the functionality. The updated "function mode" is now a little more similar in structure to "channel" and "device" mode.

will have a look later (and also comment on your other suggestions, my current focus is too come up with a way to split up the huge classes and introduce new, manageable ones, which rather risky when one is just starting to finally understand OOP a little bit 😂)

BilldarBagdar commented 3 years ago

The refactoring is/was kind of OOPy. I mean mostly, the refactoring moved "hard coded strings" directly referenced in "on display update" to "hard coded strings" indirectly referenced in "on display update". The indirect references are instances of EncoderDisplaySegment. It's kind of subtle, but changing to use the same object type for display related behavior that "channel" and "device" modes also use is a change toward "object orientation"

I still don't really understand Python well enough to know if inheritance works similar enough to my previous experience in other languages to be of use. But class hierarchies are very OOP. Theoretically we could have an abstract EncoderController base class and concrete instance classes for each mode. We would have a ChannelModeEncoderController and a DeviceModeEncoderController, etc. and then each sub-class could handle its own version of "on display update" for example. Rather than having a big chunk of if self.__assignment_mode == C4M_CHANNEL_STRIP: code in a method; all of that code chunk would be in the concrete instance class. I'm not sure a "class hierarchy" project like that would be worth any more than the academic exercise though.

I'll take a look at that undo/redo logic again. What you say makes sense, what I saw last night (was it last night?) didn't.

What if it said "nothing" over "2 undo" and no ring lights or "Undo" over "<<<<<<" with full ring?

BilldarBagdar commented 3 years ago

added "overdub toggle" next to "Play" button in function mode

markusschloesser commented 1 year ago

@BilldarBagdar added a couple of things here in the last weeks, also cleaned up code a bit to properly reflect the states. Would love your feedback on this! Also if you have further ideas :-)

BilldarBagdar commented 1 year ago

@markusschloesser I'll fetch and merge your updates and check them out later today probably. I ran into another issue with CSS and the developer remains unresponsive on the support forum. (However, even broken, CSS is a good "teacher by example". I think I will be able to go into the Python files and "bug fix" my scripts and/or learn how to use the same event handling "style" from scratch, and/or how to enhance existing scripts using the same "style".)

Also the "algoarts.com" website went dark recently, possibly less than 10 days ago. The domain is linked to a GoDaddy landing page now. Just as I was (re?)discovering how cool of an app Musicwonk is as a MIDI tinker-toy application. Working with CSS, it's super easy (and very time consuming [always?]) to create fully customized software based control surfaces for Live in Musicwonk. But now, I can't really share what I create (or teach how) because no one can download the Musicwonk app from the real source anymore. People can still find Artwonk and Musicwonk offered at various questionable "freeware" download sites, but I can't in good conscience recommend anyone take that risk.

However, man, the sky is the limit. For example, I just had an idea overnight to create a Musicwonk patch that acts like a Control Surface device Akai never made, an APC400, featuring 64 pads (8 per "track" versus 5 for the APC40) and 32 dedicated device control knobs (versus 8 for the APC40) among other expansions. But that's actually probably overkill except as a demonstration (and who would I demo to?). One can also create custom "control surface software" scripts on a per set, session, song, or any combination basis. So if you only want to algorithmically modulate, for example, 5 targets in your set/session/song using remote control DJs; then you only need to create a control surface patch in Musicwonk containing those 5 "encoders" (, the controlling DJs/algorithm(s); ) and a companion control script in CSS.

BilldarBagdar commented 1 year ago

I was able to pull the latest updates and confirm in Live 10, only needed to commit one change, guarding for the "import from builtins" syntax in TimeDisplay.py. I clicked around on the C4 to confirm script functionality seems to still be present.

I didn't look at the diffs yet to see what changed and how which is probably the feedback you're looking for. I'll make some time for that after lunch (strongly implying I'll actually get to it today...)

I'm kind of thinking about making "user mode" into another "device mode" except using all 32 encoders for device parameters; but I'm also thinking that would probably be a good case for "modifier button" behavior. The top row of encoders would "shift" to parameter control for example, temporarily revealing device parameters 24 - 31 on the bottom row.

BilldarBagdar commented 1 year ago

I looked at diffs until my eyes got tired. There are seemingly a lot of changes and my familiarity with the code has waned since spring 2021. Generally everything looks as good as it ever has. Do you have any specific areas in mind?

Why did you remove the "Slot/Clip/Scene Navigation" behavior? Are you planning to add it back a different way?

An idea that occurred to me for the "Split" button in Channel Strip (Track) mode would be mapping it to Crossfader A, B, None selection. Each time you press the Split button the "crossfader assignment" on the current track cycles from A to B to None back to A, and the corresponding LEDs also cycle. (But that would mean an LED is lit to mean "no assignment", kind of counter intuitive)

markusschloesser commented 1 year ago

I looked at diffs until my eyes got tired. There are seemingly a lot of changes and my familiarity with the code has waned since spring 2021. Generally everything looks as good as it ever has. Do you have any specific areas in mind?

Check the commit notes! 😊 Also I commented a lot in the code, but also removed a lot, no longer relevant comments. Also I added stuff here in this ticket as well as new tickets

So the question are:

  1. does everything new work for you? (jog dial, loop start and length etc)
  2. Do you "like" it?
  3. Does the kicking out of the earlier commits this year (metering etc) have any unwanted side effects?

Why did you remove the "Slot/Clip/Scene Navigation" behavior? Are you planning to add it back a different way?

  1. Iirc I removed that very early, March 2021 or so
  2. It did not work / threw errors (again iirc)

An idea that occurred to me for the "Split" button in Channel Strip (Track) mode would be mapping it to Crossfader A, B, None selection. Each time you press the Split button the "crossfader assignment" on the current track cycles from A to B to None back to A, and the corresponding LEDs also cycle. (But that would mean an LED is lit to mean "no assignment", kind of counter intuitive)

Imho easier to do with a vpot and ring cut /boost As I now know on how to forward cc to the script (that's how jog dial etc was done), it should be quite easy to implement.

Btw currently the forward cc to function is not as nice as it could be (should forward to vpot rotation in encoder controller), but hey it works 😊

BilldarBagdar commented 1 year ago

Lol, yes commit notes is what I was going by. But I soon realized in some cases I was trying to understand diffs of diffs. In other words trying to make sense of code that had changed maybe several times since I had last seen it, and trying to grok based only on the most recent diff was challenging. The eyeballs began murmuring mutiny and the captain exercised skillful judgement. I also haven't checked any of the ticket updates here.

I've been in "programming mode" recently more than "music making mode", so I haven't played with the updated script enough to know if I "like" it any more or less than I already did in the "live with Live" sense under my control in flight. I clicked around on the C4 controlling Live, but "programming mode", I didn't even have a saved project loaded just the (custom) default New set.

Ha, I was wondering how accurate my memory was when I couldn't find the metering that was apparently kicked earlier this year. I liked that feature.

Good work figuring out forwarding CCs outside of the regular "invisible" feedback mapping system like that. Again, I haven't necessarily seen the code and understood the meaning yet, so this is helpful to hear. I might have a better shot at seeing the code for what it really is next time.

markusschloesser commented 1 year ago

Ha, I was wondering how accurate my memory was when I couldn't find the metering that was apparently kicked earlier this year. I liked that feature.

there wasn't even any code left to show the actual metering, was it?

Good work figuring out forwarding CCs outside of the regular "invisible" feedback mapping system like that. Again, I haven't necessarily seen the code and understood the meaning yet, so this is helpful to hear. I might have a better shot at seeing the code for what it really is next time.

Thanks, took me fucking ages! :-) (it's basically this for setting things: https://github.com/markusschloesser/MackieC4_P3/blob/7d4904dc0fe5054040c959de5ca8e56d6d80c646/wip/MackieC4/MackieC4.py#L228 and this for showing it: https://github.com/markusschloesser/MackieC4_P3/blob/7d4904dc0fe5054040c959de5ca8e56d6d80c646/wip/MackieC4/EncoderController.py#L1530

trying to improve further but got stuck with this https://github.com/markusschloesser/MackieC4_P3/issues/98 , can you help?

I just implemented Scroll/Zoom on vpot 16 and could possibly implement the crossfade one you like, BUT that would make more sense in track mode than Function mode, and it would be really cool with the vpot display mode boost/cut ;-)