Buttons or Pads with LEDs: mastering the feedback and software modes (advanced)

Started by azslow3, November 21, 2016, 08:12:52 PM

Previous topic - Next topic

azslow3

Warning: that is an advanced material. I assume the reader has already went throw Quick Start, ACT MIDI Explained and the User Manual. This is not a step by step tutorial, it is an explanation how to use AZ Controller to make complicated configurations.

A part of the tutorial explains general approaches for integrating surfaces into DAWs, with possible tricks, usual problems and workarounds. MIDI Surface integration is never "automatic", if you "plug&play" your controller and it works perfectly, SOMEONE had to spent a lot of time to allow that. This tutorial explain what this SOMEONE had to do with buttons or pads and LEDs (near/under) to make it work smoothly for the end user. With AZ Controller you can dive into the process with minimal or no prior programming experience, dive deep. This tutorial supposed to help you make nice working surface for the time you are back to the music creation, instead of horror memories about useless spent night and a hardware brick wasting your desk space. The explanations are not easy to understand, I know. A button is always just a button, on lamp or on most sophisticated surface. Making in "smart" is not a simple task.

Note: you need AZ Controller v0.5r4b344 or later to use attached preset.

The goal
In "uni-directional" approach, when we want some controller does something inside the DAW, we deal with "what to do when XY is pressed/turned/moved?" question. As soon as our controller has some "feedback", f.e. LEDs, rings around encoders or moving faders, we should define the answer for additional questions: "what to do when something is changed in Sonar?" and "how to correctly glue the indication done inside the device with the indication we want?"

Attached preset defines 8 buttons (Btn1-8) for strips, 2 "bank" (BankUp/BankDown) buttons  and 1 (ModeSwitch) button. It is assumed all buttons are "momentary", so they send "On" when pressed and "Off" when released (either Note or CC). In case you do not have such controller, you can use any MIDI keyboard keys for tests. This tutorial is about the "feedback", but you can use AZ Controller Display (from the Options tab) to see the effect. In case you have some controller with buttons/pads which have LEDs and these LEDs are "reacting" on the same MIDI messages corresponding button/pad is sending (f.e. AKAI MPK Mini, MidiMix, etc.), the preset should work "as is". Otherwise you will need to modify "LED sending" actions, I will explain that later.

The preset functionality
* 8 strip buttons control and indicate Mute status for 8 WAI strips
* strips follow strip focus in Sonar, so if you switch between Tracks and Buses in Sonar, WAI and controls will follow
* short pressing ModeSwitch button switch between Mute and Solo
* long pressing ModeSwitch (more the ~0.5sec) switch 8 buttons to "level monitor" mode for the strip in focus. The rightmost button/LED is supposed to be "CLIP", so it "holds ~2sec" (to better expose the signal clipping). ModeSwitch LED is On in Mute, Blinking in Solo and dark in Level mode.
* BankUp/Down buttons move the WAI region (and so controlled strips) in Mute/Solo modes and change focused strip in the Level mode. If BankUp is pressed with BankDown, the the mode is changed: Mute/Solo/Level/Mute/Solo/etc.

AZ Contoller concept
The preset uses all "advanced" features of AZ Controller, including Functions, Dependent Software State Sets and Monitoring. So before I start explaining the preset, I want outline the "big pictures"of AZ Controller logic.

If you are working with Sonar, even in case you has never "programmed", you have to deal with the "signal processing" concept. When you add several FXes into FX bin, turn on some ProChannel modules or  insert a Software Synth, you establish some signal processing chain.

There are several properties in that concept, and even if you have never thought about them explicitly, you probably perceive them as "natural" and use them all the time:
* audio FXes deal with an audio signal  on there input and produce an audio signal on there output, normally modified. MIDI FXes do the same with MIDI signals. A Soft Synth takes MIDI signals as the input and produce an audio signal as the output. Guitar processors have audio AND MIDI signals on input.
* you organize FXes into FX bins. Each strip (and each clip if required) has a separate FX bin.
* FXes can work in a sequence, in parallel or "cooperative". When you add EQs to 2 independent tracks, they are working parallel and independently with different signals. When you put EQ and then Compressor into the save strip, they are working in a sequence. And you probably have noticed that the order is important, EQ before Compressor and EQ after Compressor, even with the same settings in both, produce different result. When you do side chaining or insert a "console emulator", effect instances in different strip start influence each other, they "cooperate" to produce the sound you want.
* you can group FXes into FX chains and then use such a chain as a FX

When dealing with Conrtrol Surfaces:
* as the input you can use MIDI/OSC signals and many Sonar parameters (strip volumes, transport state, now time, automatable FX and SoftSynth parameters, etc.) and as the output you want to get changes in Sonar parameters (transport start recording, volume on some track is lowered, cut frequency of of FX is increased, etc.), execution of Sonar commands (the same as keyboard shortcuts and menus, like saving the project, undo, zoom, etc) and in case you surface has feedback (LEDs, display, rings on encoders, motor faders) you want some MIDI/OSC is sent back to the surface. There are many limitations in Sonar, so you CAN NOT use as the input/output: any audio signal (except monitoring current level), any information recorded on tracks (recorded MIDI or audio, clip boundaries, FX bins in clips, etc). And while you can send MIDI to external device, you can not "inject" it into MIDI track (without using external MIDI loopback).
* on place of FX for musical content processing, AZ Controller has Actions. The same way as FXes can be simple or complicated, Actions also can be simple (f.e. "Play" command) or complicated (f.e. get current value for strip Volume and adjust it by 1%). You put FXes into FX bins. You put Actions into Action lists.
* Actions inside one Action list are executed in a sequence. As with FXes in a bin, the order is important since next Action can use the result of the previous Action. Unlike FXes, each Action can use any available information as the "input". Different Action lists are "independent". But you can use the result from one Action list in another, as long as it is made persistent. As with side chaining and audio/MIDI loops, you should take care there are no strange dependencies, especially "looping dependencies". The effect will be the same with with a mic pointing toward the monitor once you turn echo on - in case of Control Surface, you will get no crazy sound, but Sonar itself will become "crazy" (and it can crash).
* as with FX chains, it is possible to use a list of Actions as one Action. But implementation is a bit different:

"Functions" in AZ Controller
In Sonar, if you want to use a list of FXes as one FX, you should explicitly put them into "FX chain". You can not just in FX bin of the Track 2 say "please use the whole FX bin from Track 1" (could be handy, is not it?)

CW distinguish between "FX Bin", "FX Chain" and "ProChannel". But in practice, all three are just lists of FXes. In AZ Controller, there is no separate "FX chains" to use as a distinct FX. Any Action List can be used as an Action in another Action List.

There is special Action "Call". As the only parameter it has the name of some other Action List.

After some degree of complexity, "functions" (as FX Chains) can be time and brain savers. As an exercise, you can "unwrap" (copy the function content into caller's list) f.e. "Btn1" control. You will see that while avoiding such thing as a "function", that makes the preset almost unreadable.

For programmers: "functions" in AZ Controller have no parameters and return no value (except "success" when the function execution was ended with an Action with "final" flag). I still name them with "parameters", f.e. "_fBtnSetValue(_Ch)", just to comment that this function uses "_Ch" Software Set.

Action lists and Logical Controls
In AZ Controller, you can find Action Lists at two different places. In the Logic tab and in the Feedback tab. I will explain the different later, once I clarify what is Monitoring, but it is worse to mention already now that an Action list is either a property of Logical Control or a Monitor. And only Logical Control Action List can be used as a "function" (even when called from the Monitor Action List!)

Logical Control IS an Action List. It can have an assignment (MIDI and/or OSC) and it can be attached to some Hardware control (throw Hardware Context). Why Hardware Control is not the same as Hardware Context and Logical Control is yet another "thing" is explained in the Manual (in short, physical Surface Hardware Control can send different MIDI signals in different Hardware presets, but for many operations it is required to know that both signals are coming from the same knob/slider). In this preset, that is not the topic, all Hardware Controls have exactly one Hardware Context with Logical Control attached to it.

The "primary" purpose of Logical Control is to define the reaction on incoming MIDI signal (so what to do when you operate some control on your surface). For readability, such controls are named without "_" at the beginning, f.e. "Btn1".

The "secondary" purpose of Logical Control is to be used as a "function". In the preset such controls are named starting from "_f", f.e. "_fModeSwitch()".

Logical Controls can be used for yet another purpose, for Monitoring. In this case, I name them starting from "_" but without "f" following, f.e. "_BtnMode".

Important, that you should assign some MIDI to all control WITHOUT "_" and you should NOT assign MIDI to any control WITH "_".

Continued in the next post...

azslow3

Monitoring concept in AZ Controller
Monitoring in AZ Controller is difficult to explain and understand. "Monitors" and how they work have no direct analogs in conventional programming, so programming experience will not help to understand it (till you have programmed in Forth, Prolog and worked with LabVIEW).

Unfortunately, the concept is essential to create complicated presets in AZ Controller. So, please read this part twice, ask questions, try some simple monitors till you are convinced that you have more or less learned it.

Fortunately, the concept just mimics "common sense". It is not coming from any "theoretical Informatica"/"the theory of controlling systems" (as some other "projects" I have to deal with...). So I will start with musical examples.

Let say you want record some vocal with a mic. You first LOCATE the mic, then you CHECK that the mic is connected and in case it is not connected, you CONNECT it. You can not check the mic connection before you have found the mic and you do nothing (will not reconnect) in case it is already connected. So the sequence in which you operate is fixed (here it is obvious, but in AZ Controller you just see some "text" which describe location, check, conditions to do something and what you want to do. Too easy to put things in wrong order and then spend hours looking at the result...)
Then you start recording the vocal and adjusting the input gain. You first OBSERVE current level, then CHECK it is ok and if required CORRECT it. The difference with the first example is that you do this PERIODICALLY. Usually with several ITERATIONS of several OBSERVE/CHECK/CORRECT sequence.
When you are about to physically change the gain, you OBSERVE the current value of the gain for the channel to which you mic is connected and then ADJUST  the current value of the gain for the channel to which you mic is connected

Another example. You want cut some resonance frequency on snare. You OBSERVE the sound while TEMPORARY busting narrow band change the frequency to LOCATE required spot, CHECK that is what you was looking for and then ADJUST PERMANENTLY by cutting 1-2dB, widening the band. Interesting there not only OBSERVE/LOCATE/CHECK/ADJUST sequence but also the fact that during this process involves TEMPORARY EQ parameters modification, which you do not want see persistent inside your project.


Lets look at that examples from Control Surface perspective. What we want it does for us?
First we want to see current level, which we can not adjust directly. So, in case we have some level indicator on our surface, we want:
1) LOCATE the current level for the channel to which you mic is connected
2) CHECK it is different from what we have shown the last time on our surface (as with re-connecting mic, we do not want "redo" things)
3) (if the level is different) SEND new level to the surface

In case we have an encoder on our surface for digital gain control, with a ring indicator, we probably want:
1) LOCATE the current value of the gain for the channel to which you mic is connected
2) CHECK it is different from what our ring already shows
3) (if the value is different) SEND new ring value to the surface

And once we want adjust the gain, when turning the encoder we want:
1) LOCATE the current value of the gain for the channel to which you mic is connected
2) ADJUST that value, using direction information sent from the encoder

So we have 2 cases: we just want monitor something and we want monitor and adjust something.

Now look at lengthy text in italic in the LOCATE section of the last 2 lists. Lets combine both lists:
1) LOCATE the current value of the gain for the channel to which you mic is connected (if we are REACTING on encoder, we want that location is done for "real", remember the example with EQ, if we are just OBSERVING, we want the location is TEMPORARY)
2) FOR OBSERVATION ONLY: CHECK it is different from what our ring already shows, if yes -> SEND new ring value
3) FOR REACTION ONLY: ADJUST that value, using direction information sent from the encoder

In AZ Controller, you can use exactly that sequence (look at "Btn1" Logical Control definition in the preset). How AZ Controller "knows" either we want OBSERVING or REACTING? It depends from WHERE this list is called:
* When it is called as the REACTION on incoming MIDI signal from the encoder, the list is executed in REACTION mode (saving LOCATE result permanently and ignoring CHECK and corresponding SEND).
* When it is called PERIODICALLY during the monitoring cycle (see later), the list is executed in OBSERVING mode, not saving LOCATE result and ignoring ADJUST section (even if you put it before CHECK).

Execution modes
Lets look at execution modes more systematically. Two of them I have already mentioned, there there is the third one... In examples before, "SEND" is executed in the FEEDBACK mode. Corresponding Action Lists are defined int the Feedback tab, but "functions" (which are Logical Control Action Lists) can be called from there. Important that ALL Actions are executed using current mode, independent where these Actions are defined.

So we have:
1) REACTION mode. Used when the execution is triggered by incoming MIDI signal
2) OBSERVATION mode. Used during monitoring cycle for actions which stay before the CHECK.
3) FEEDBACK mode. When the check is passed and the Feedback list is called.

Logical Control Action List can theoretically be executed in ALL 3 modes: as the REACTION on MIDI or as a function called from such reaction list; as OBSERVATION when CHECK (the Action name is "Monitor") is defined inside the list; as FEEDBACK when called as a function from the feedback section.

Feedback Action List is ALWAYS executed in the FEEDBACK mode. Feedback lists can not be called as functions and there is no MIDI assignments for them.

Every defined in AZ Controller Action behave differently in different execution modes. The rules are:
1) in REACTION mode, all Actions except "Monitor" have effect, Location and Software Set State changes are permanent. "Monitor" (CHECK) Action is ignored in this mode.
2) in OBSERVATION mode: "Monitor" Action is special, since OBSERVATION is called for each Monitor separately, only Actions which stay BEFORE the Monitor is question are executed. All other "Monitors" are ignored. All Actions, except Temporary Location selection, Temporary Current State changes and Temporary Text are ignored.
3) FEEDBACK mode is similar to OBSERVATION, but some additional Actions are allowed (like MIDI send). In general, only Actions which modify something within AZ Controller or send MIDI/OSC are allowed and all Actions which set something in Sonar are ignored.

Special is the interpretation of "Set engine state" flag, for Actions which have it:
1) in REACTION mode it is completely ignored
2) in OBSERVATION mode, Actions with this flag set ARE IGNORED (without that flag, they modify Temporary Current State)
3) in FEEDBACK mode, Actions with this flag set modify PERMANENT Current State (while still using Temporary Current State on the Input and modifying it as the result).

Except rare cases, everything should work "as expected". For example, in the LOCATE/CHECK (and may be SEND)/ADJUST list mentioned before, AZ Controller will do what was described in the brackets without "extra steering". "Set engine state" in the FEEDBACK will be usual "manual" adjustment to the automatic (the system just can not decide on itself either some State change should be permanent or not), but I had only several very special configurations where something else was required (like "Set ending state" in OBSERVATION or "looping back" MIDI in FEEDBACK to execute Sonar Commands as the reaction on monitoring).

Yet another controlling case
There is the third case of OBSERVING and ADJUSTING I want to mention: when we want monitor something but adjust something else. F.e. when we send some command which produce no feedback ("undo"), but do not want "waste" LED and use it for different purpose (as a part of level monitoring). In the preset there is such case with the ModeSwitch: it indicates its own "status" and it changes other buttons mode. ModeSwitch status depends from BtnMode, but they are not the same. Such case can be seen as a "control without feedback" combined with "monitoring only". And as such is not "different" from the first (just monitoring) case. It can be confusing since there can be 2 different LOCATEs:
1) LOCATE (for REACTION)
2) ADJUST (for REACTION)
3) LOCATE (for OBSERVATION)
4) FEEDBACK (for OBSERVATION)
In fact (1+2) are independent from (3+4). The only common part is the relation to physically close to each other control and LED (and so, most of the time, MIDI signal used).

Continued in the next post...

azslow3

Monitoring cycle
How fast you can check parameters in the DAW? Do you able to read number when they are changing once per second? What about 10 times per second? Keep the answers in mind while interpreting following numbers. Also note that it is not about the audio discretization frequency, not even about reaction on your keys/controls (the REACTION is executed is "semi real time").

Monitoring happens PERIODICALLY. It happens every 75ms (or whatever set in Sonar preferences, with lower limit of 50ms. But PLEASE do not change the default. That can confuse ALL Control Surface plug-ins). When you define a Monitor, you also specify its "speed". The "Fastest" speed is "Ultra", so every 75ms or ~13 times per second. Lets call such period a monitoring cycle.

Each cycle, AZ Controller checks either its time to execute OBSERVATION/FEEDBACK for each defined Monitor. All Monitors except "Timer once" (executed on special request with "Reset monitor" and defined delay) and "State monitor" (executed at next possible occasion in case the Current State of the specified Software Set is changed) have the "Speed" parameter. "Ultra" means every cycle. All other "speeds" are slower.  In reality, with modern computers and what we do with control surfaces, that is negligible compare to any single FX in your project. So in the preset you can see that all Monitors are defined with "Ultra" speed.

If it is time to execute the monitor, AZ Controller start the OBSERVATION execution of the Logical Control Action List where the Monitor is define, till the Monitor Action in question is reached. Then monitor conditions are checked and the Monitor Feedback Action List is executed when desired (using FEEDBACK execution mode).

For each Monitor the OBSERVATION execution happens in "sane" environment,  reverting changes in the Temorary Current States (Text/Location) may be made by observations/feedbacks already checked Monitors to the current Permanent Current States (Text/Location).

Each Monitor is checked/executed only ONCE during one "cycle". So if some Monitor is already checked (and may be executed), it will not be checked again during the same cycle, even in case its "monitoring conditions" are changed. So the next important topic is:

Monitor priority
This parameter defines in which order monitors will be checked/executed during ONE CYCLE. Again, the "speed" defines how often (in cycles) the monitor is executed and the priority order monitors within one cycle.

All Monitors with priority "0" are checked/executed before Monitors with priority "1", and so on. You would normally organize Monitor Priority depending from which Software Sets are used during Monitoring and either these Sets can be changed by other Monitors.

F.e. in the preset we are monitoring currently focused in Sonar strip type. And we save the result into "Strip" Set (permanently!). This monitor has priority "0" since there are no dependencies. Then we use "Level" monitor to set "Level(_Ch)" Set. Which strip we look at depends from "Stip" Set, so this monitor has priority "1". Finally, "BtnX" Monitor depends from "Strip" (in Mute/Solo mode) AND from "Level(_Ch)" (in the Level mode) Sets, so we should set its priority to "2".

Monitors with equal priority are executed in "random" order. If you do not set the priority correctly, sometimes your preset will work correctly and sometimes not. Think about side-chaining wrong track, when it is almost in sync with intended (but not always).

Monitor CHECK procedure
So far I have described that when its time to check the Monitor (depending from its Speed in cycles and the Priority withing a cycle), Logical Control Action List where the Monitor is defined is executed up to the Monitor Action using OBSERVATION mode. What happens as next?

The system should decide either we should execute Monitor's Feedback Action List or not, depending from the observation results and the Monitor specification.

First of all, Action Conditions for the Monitor Action are checked. Actions during the OBSERVATION could change current States for Sets in question (as explained before, these changes are TEMPORARY and DISCARDED after monitoring). If some condition is not met, the Monitor CHECK is not executed. The procedure ends (for this Monitor) like the OBSERVATION was not executed at all, so nothing is "saved".

If the Action Conditions are OK, the Monitor enters the CHECK procedure. What is really checked is define in the Monitor options:
* for "Timers" nothing is checked, the decision is always "EXECUTE FEEDBACK".
* other Monitors check specified values, either parameter values, names or Automation flags, the text (for Command Monitor) or strip level. The check is done for CHANGES. If the value in question is CHANGED (compare to the last time it was CHECKED), the decision is "EXECUTE FEEDBACK". If it not CHANGED, the FEEDBACK is not executed.

CHANGED means the following:
1) if that is the first time the Monitor is CHECKED, the value IS CHANGED. That correspond to the "initialization". The Monitor can be "reset" at any time (with Monitor Reset Action) to "forget" the result of previous checks.
2) if Sonar parameter in question is different, the result IS CHANGED. Even in case the value is the same. F.e. if you monitor focused track Solo and you switch focus to another track, with the same status for Solo, the result is still IS CHANGED. The same in case ACT Dynamic Mapping focus is change to different plug-in.
3) if the type of the Monitor is changed (used in the example preset), the result is always IS CHANGED.
4) finally, if the the value is different, the result is also IS CHANGED.

Reversed: the CHECK is negative and the FEEDBACK is not executed in case the type of the Monitor, the Parameter (including originating Strip/FX/etc) and the value are the same as during the last CHECK.

Monitor FEEDBACK execution
If the decision during the CHECK was "EXECUTE FEEDBACK", Monitors Feedback Action List is executed in FEEDBACK mode. Note that Temorary Current States / Text / Location are NOT reverted between OBSERVING and the FEEDBACK. They are reverted AFTER the FEEDBACK.

Are you puzzled by my monitoring explanation? Please take a break, may be read the text one more time next day. Read the rest, check the example preset.

Dependent Software Set
The last "advanced" feature used in the preset. Simple Sets have only "one Current State". Dependent Sets have different "Current State" for each State in another Set.

For programmers: these are one dimensional arrays  8)

For other: let say you want define "Plugged" Set with States "Yes" and "No", which describe either you instrument is plugged or not. You can do this by defining different Sets: "GuitarPlugged", "MicPlugged", "'KeyboardPlugged". Each from these Sets can have own current State. But you also can define the Set "Instruments", with "Guitar", "Mic" and "Keyboard" and say the (one) "Plugged" set depends from the Instrument. So you can then use:
Insturment -> Guitar
Plugged -> Yes
Instrument -> Mic
Plugged -> No

That approach reduce the number of Sets and simplify function definitions, as you can see in the example preset.


Continued in the next post...

azslow3

The explanation for the preset

As I have written, I am not going to describe everything in "click by click" mode, assuming you already know how to create controls, states, add actions, etc. But I will try to explain in which sequence I have created this preset and WHY I have defined things that way.

This example is not of kind "do it like me". That is just one from possible approached to achieve the goal stated at the beginning.

I normally start by defining the "Strip" Set, with "Track" and "Bus" States.
Then I define "_Strip" Control with "Timer" and the "Strip" State Monitor, first with "0" priority, second with priority "1" (it depends from the first) and "Ultra" speed. All Monitors in that preset have "Ultra" speed, so I will not mention that again (when you add Monitors they have "Normal" speed by default). 

In the Feedback for the Timer, I "Recall" "focused" Strip, saving its strip type into the "Strip" set (do not forget "Set engine state", we are in the FEEDBACK mode!). With that definition, "Strip" (Track/Bus, Sonar does not expose hardware output focusing to Control Surfaces) is always the same as currently focused in Sonar strip type, which can be changed by Mouse, this or other Surface.

Save/Recall Action is a powerful tool which can be used for "scratch ACT" and other "on the fly" control assignments. Here it is used just to follow the strip type focus.

In the "_Strip::State monitor" Feedback, I set WAI following the Strip type. Note that both Actions just set the WIDTH, but not the position of the WAI. Also the first WAI is "initial" (not really required since the State monitor will be called after loading, but historically I set it...). Without WAI indication, it is hard to predict what your surface is currently controlling. Note that you can control ANY strip, not only WAI strips. But that is a good idea either use "Focused" or "WAI related" strips since both can be quickly changed by mouse.

In this preset we will use several Sets
"_Ch" set to simplify the configuration using "functions". With States 1-8 (or whatever number of strips you are going to control in parallel). The name prefixed with "_" to remind me the use of this Set. Such Sets, while they have permanent Current State, I use locally only (I do not use permanent Current State)

"Level(_Ch)" with "Off"/"On" States. Note that it DEPENDS from "_Ch". So we effectively have the place to save current LED situation for each button.  Usually it is not an issue to just update LEDs when required, even if they already have "correct" lighting. But unlike "stable" parameters like Solo or Mute, the level is normally changed all the time during recording/playback. And I do not want to update all LEDs all the time. If you have some old big surface with hardware MIDI connection, like old Digital Mixer, updating all controls all the time can be a big issue, not only MIDI throughput is limited but also internal hardware/firmware can be saturated by incoming messaged.

The next Set is "BtnMode". As specified in the Goal, with "Mute", "Solo" and the "Level". It can be extended with "Echo", "Rec.Arm", etc. with minor modifications in the preset.

"BtnMonMode" is special. When it has States, the always have the meaning "Value", "A.Read", "A.Write", "Command" and "Level", in that order. Names of states are not important, just the position. This Set will control which TYPE button monitors have at any particular moment. I use "Value" for Mute/Solo and "Command" for Level (not "Level" type, as I have started to explain for "Level(_Ch)" Set). If States are fixed, you can ask why this Set is not pre-defined. In this preset I use one "button group" only. But in more complete presets there can be several groups, and each can have own Monitor type.

"_LED" "Off"/"On" is used temporary to decide either turn some LED on or off. It is possible to write all related Action Lists without using this Set, but there will be more Actions. Again, just "code optimization".

"ModeSwitch" "Off"/"On" is used to detect "long press" of the ModeSwitch button (explained later).
"ModeSwitchLED" "Off"/"On"/"Blink_On"/"Blink_Off" is what ModeSwitch LED should do. It will correspond to "Level" (no light), "Mute" (continuous light) and "Blink_On" (solo). "Blink_Off" will be used later in the ModeSwitch implementation.
"BankDown" "Off"/"On" is used for yet another way to switch modes.

Continued in the next post...

azslow3

"_Level" Control
This control is for monitoring only, it has no MIDI assigned and it is not used as a function. The same as with "_Strip". But it has 3(!) different Monitors.

In the LOCATION section (used by 2 of 3 monitors) I choose Strip "Strip" <current> Volume. The Set "Strip" has currently focused strip type (as defined before). So it is going to be always in sync with focused strip in Sonar.

"Parameter Name Monitor" (priority "1", since it has dependency from "Strip" changed in "0" priority monitor) is there to detect focused strip changes. "Parameter Name" is always "Volume", but as I have already explains, the Monitor will detect strip changes. Note that we can not use "Parameter Volume Monitor", since we do not want triggering on volume change. In its feedback list I "reset" "Level(_Ch)" for "_Ch" = 8 to "Off". The reason is: I keep LED on button 8 lighting for 2 seconds, even when current level is lower. Without this monitor, when you change strips and previous strip was clipping at the time of switch, "CLIP" will still light for up to 2 seconds. Not nice.
I put "BtnMode:Level" condition for this monitor since we need it during Level mode only (preventing "useless running" all the time). When the strip is changed after we left Level mode, this Monitor is still activated once we enter Level mode again. Remember CHECK explanation, if Monitor Conditions are not met, nothing is remembered, so this Monitor will still have the old Strip as "last checked" all the time till the mode is Level again. That is yet another example of "logical" behavior of AZ Controller, to reduce the number of tweaks you have to do manually. Unfortunately, Sonar is not logical in many cases, as you can see in the:

"Parameter Level Monitor" (priority "2", "Strip" dependency, but more important I want it AFTER "Parameter Name" monitor possibly reset CLIP, also it should be after "BtnMode" Set monitor). Here we monitor the "level" of LOCATED (so currently focused) strip. In the feedback list, I use "Compare" Action to decide either some LED should light from the "Value" System Set (I write a bit more about "Compare" Action result in the "fBtnSendLED(_Ch)" explanation). The "Value" is mapped to monitored parameter value, in this case to the level value. You can adjust numbers as you wish. Simpler approach, shown in the Level monitoring tutorial, is less tunable. Note that "Value" is set to 0 in case monitored parameter is invalid (f.e. if there are no strips at all in the project), which works fine in our case. In more complicated case (f.e. to display "---" for strip name when there is no strip), "Selection:Invalid" condition can be used (or even better by failed status for parameter value mapping, as I explain for the button feedback later). For "_Ch":8 Actions are a bit different. We do not set "Level( 8 )" to "Off" in case it is "On", we keep "CLIP" holding. And in case the level is still "clipping", we reset the "Timer" described later to trigger after ~2 seconds. In case during these 2 seconds we again have "clipping" condition, we again reset the "Timer" to 2 seconds. Monitors in AZ Controller do not have "time memory" (on purpose), so the "Timer" will not trigger after the "first" 2 seconds, it will trigger only 2 seconds after we reset it the last time (in case we do not reset it again).
As with strip change detection, the first idea will be set "BtnMode:Level" condition. But with Level monitor the result from that condition is strange. When set, once we leave Level mode, AZ Controller immediately stop asking Sonar about the level. Interesting that Sonar "remembers" the level we have asked the last time, and "slowly drop it", the same way as in the Sonar level monitor, once we start to ask for it again. Independent from the real current value! Let say we have clipping on Track 1, which we can see on our Surface. If we switch to Mute mode, then move Track 1 fader all the way down (so the level is now <<-48db) and then turn Level mode again, our Surface will show us CLIP! Slowly dropping to silence... I do not like that, but that is how Sonar is working. So I monitor the level all the time. That in turn creates a "side effect": not only we ask Sonar for level all the time (which as I have explained we can not avoid), but we also set "Level(_Ch)" all the time, trigger timer, etc. I do not like that. So I check the mode INSIDE feedback (first 2 feedback actions), and do nothing except in the Level mode. Do you understand the difference between setting the Monitor Action Condition and checking the same condition inside the feedback? In the first case we are not CHECKING monitored parameter, so not Asking Sonar and not saving the result. In the second case, we do all that, but do nothing as the feedback. In the workaround complete? Unfortunately now.... since with do real parameter CHECK and THEN ignoring changes, our "Level(_Ch)" is "out of sync" but "last checked value" for the monitor is in sync. And when we enter Level mode again, in case the level is not changing much, surface will show incorrect information. The solution is to "reset" level monitor once we enter Level mode. We will do this inside next control, "_BtnMode".

Finally, "Timer" (once, so executed on special request only) monitor (priority 0) clean our "Level( 8 )" to "Off".

"_BtnMode" Control
It has one monitor, for "BtnMode" Set (priority "0", it it will be executed before Level and Btn, where the result is used).
For each BtnMode, we set correct type for "BtnX" monitors steered by "BtnMonMode" Set and correct ModeSwitch LED indication with "ModeSwitchLED" set.
And as explained before, we reset Level monitor (but only when entering "Level" mode, irrelevant otherwise).

Continued in the next post...

azslow3

From previous definitions, we have:
* all required Software State Sets
* we get "BtnMonMode" set correctly when we changed modes (btw., Sets Surrent State can be changed on the Overview tab manually, the only way to test something till proper mapping to surface is done)
* in the Level mode, "Level(_Ch)" Set is updated from desired strip Level, with "Level( 8 )" implementing 2sec CLIP HOLD functionality.
So we can proceed with our 8 buttons. But to make the preset more organized, I have defined some functions:

_fBtnSelectPar(_Ch)
The LOCATE section, so I define what I want to monitor/control depending from the "_Ch".
All Actions are defined with "Note:Any", in this preset I need it working when the button is pressed AND when it is released. The reason is explained when we come to LEDs. When you do not need that workaround required for some controller, you can keep all Actions with default "Note:On" condition.

I first choose Strip "Strip" WAI + "<_Ch>" , "Mute" parameter. "Strip" defined currently focused pane (Track/Bus), WAI we see in Sonar, +"<_Ch>" will make the trick to select the strip depending from the button number. Note that what is counted is the State Number, starting from Zero (0). State name has no influence. So, when we set "_Ch" to "1" and then use "WAI" + "_Ch", we choose the first strip in WAI ("WAI" + 0). You can think differently, to avoid the confusion with mathematic. "Rename" States for "_Ch" from "1", "2", "3"... to "a", "b", "c", ... etc. When you say "WAI" + "a", you assume it is going to be the first strip in WAI, right? And it is so! If we use numbers "as is", "WAI" + 1 will be the SECOND strip in WAI. Mathematically correct, but not what we expect to get  :o

To master advanced configurations in AZ Controller, you are going to use "state shifts" often.
Arithmetic: ShowHide
People have a tradition to associate "the first" element with number "1", that is what you normally also see on surfaces/tracks/inputs/etc. But in arithmetic that creates a confusion between absolute position and "shifting". After you locate "the first" strip in WAI, if you ADD "1" to it, it is going to be the second strip. Only in case you add zero ("0") it STILL will be the first strip.
Programmers have "solved" that paradox counting from zero. So, "the first" track is "track zero". But for "other people", they are normally "converting to and from" both conventions. For example, MIDI Channel "1" is really transfered as "0". But I have not seen "MIDI Channel Zero" in any text so far. In practice, it is not even possible to transfer "MIDI Channel 16" as "16" since the place reserved in the protocol for the channel is limited, and maximal number which can be transfered there is "15".

State shifts in AZ Controller are always using State position and completely ignoring state names. You could define "_Ch" states as "1","1","1","X","Hello!","Hm...". That will NOT change the preset functionality. And States are counted from the top, starting from Zero (0). NAMING states from "1" invisibly makes "common human numbering" working arithmetically correct. So that "_Ch" = '1', WAI + "_Ch" in the TEXT form looks logical and produce expected result. But in case you specify the shift manually, f.e. "WAI" "+1", that is the SECOND WAI strip. The first strip is "+0" (default value when you configure). Keep that in mind.


For Solo mode I change "Mute" parameter to "Solo", while keeping located strip the same.
Finally for Level mode I put Current State of "Level(_Ch)" (it is "_Ch" dependent, so correct for "_Ch" in question) into Text. I will explain why and how it works later.

Note that in the Level mode, LOCATION with such definition is still "Strip Mute" (Level is not Solo, and the first action had no conditions). That is not a problem till we by mistake try to use it, and we do not.

_fBtnSendLED(_Ch)
That function is our feedback to the surface. It should indicate correct for current mode status for "_Ch" button. First we set "_LED" to "On" or "Off", depending from mode. I set it from LOCATION value first (we always call this function after previous). (almost) All parameters in Sonar are numbers between 0.0 and 1.0. "Mute off" is 0.0, "Mute On" is 1.0 ( "Pan center" is 0.5). So if we "map" a Set with 2 States (again, everything is calculated with the State position, not name), it will get the first State for values under 0.5, and the second State for values over 0.5. With Solo/Mute, "Off" in Sonar -> value 0.0 -> the first State in "_LED" -> "Off". In AZ Controller, it LOOKS like we map Sonar "Mute Off" to "_LED" "Off" directly, but behind the scene it takes described conversion route.

"Last action:Failed" is a fall-back case, I have promised to explain it before. Out LOCATION could be unsuccessful. F.e. we try to select "WAI + 5" track in a project which has only 1 track. "Undefined" location has "Undefined" MUTE value. So the previous Actions can not map the value since the value is unknown. So the previous action is "failing". That result is saved inside "Last action" System Set and is available for following Action Conditions. Note that it will be overwritten as soon as some other Action will be called, so you can use this Set IMMEDIATELY after the Action in question only.... With one exception: the Set is not overwritten in case there was NO attempt to execute an Action, that can happened if Action Condition was not met. And I have used that in the "_Level :: Level Monitor" feedback list. I was checking that comparison was successful first and set "Level(_Ch)" to "On" in this case. Then I am checking it was not successful, and set it to "Off". The trick is that in case "Compare" was unsuccessful, there was no attempt to "Set to On" and so "Last action" was not changed, staying "Failed". In case it was successful, "Set to On" was called and it has OVERWRITTEN "Last action". But setting some Set to concrete State is ALWAYS successful, so while overwritten, "Last action" will be still "Ok". If I change the order, so I first test for "Failed" and then for "Ok", it will NOT work! If "Compare" fail, "Set to Off" will be executed and it will succeed! Next check will be for "Ok", and it will ALWAYS succeed: either from Compare or from "Set of Off". So in practice, if you use "Last action" check try to do this immediately after the Action is question, till you 100% sure what you are doing.

Last action:Failed vs Selection:Invalid: ShowHide

Why have I mentioned before that "Last action:Failed" in some situations is better then "Selection:Invalid"? The second looks like more "solid" solution and is not position dependent... "Last action" is just more "bullet proof". Even in case LOCATION is correct, so "Selection" is "Valid", the Action in question theoretically CAN fail. Depending on the Action, it can call Sonar (f.e. to get the value). And Sonar can refuse to return the result... With existing parameters that happens rare, but happens. But what can happened more often is that AZ Controller "think" the parameter is valid (so "Selection" is "Valid"), but in reality is is not. There are several reasons for that, partially Sonar can report inaccurate information, especially during project loading/unloading, partially AZ Controller, to not disturb Sonar too much, is not collecting all possible information about parameters availability.

So, the practical consequence: "Selection:Invalid" and "Last action:Failed" are equivalent when used in the FEEDBACK (Sonar is asked during CHECK phase in this case, setting "Selection:Invalid" if that was not possible), while "Last action:Failed" is preferred method in REACTION. In reality the difference is so subtle, that I use "Selection:Invalid" even in REACTION, when "Last action" will significantly complicate the Action List.


Next two Actions work in Level mode and just map "Level(_Ch)" State to "_LED" State (overwriting whatever was set in the previous actions).

After that, depending from "_LED", i SEND MIDI to the surface. I use a "trick" with "<Use Ctrl MIDI>". It will only work in case LEDs expect the same MIDI messages as corresponding button sends. It is almost always the case, but there are exceptions (f.e. Presonus Faderport). The "Control" is the topmost control from which the execution is started. For REACTION, it is the control which is called on incoming MIDI, so we will use exactly that MIDI message (we obviously modify the value which we "send back"...). For FEEDBACK, that is the control where the Monitor is defined. Again, that is going to be our "Btn" with MIDI assigned. So, it will work inside this "function" Control correctly (which has no MIDI assignment by itself)

Notice "Note:Any" conditions for all these Actions. I have mentioned before that I implement a workaround needed for many surfaces. And I will explain that, but not yet.

Next Actions duplicate "_LED" indication to AZ Controller internal Display. Here you can see possible solution to send different "On" messages in different modes. In case your buttons/pads have multicolor LEDs, you can use that logic to let say show "Green" on muted, "Grey" on solo, "Red" on record armed. Internal Display has no indication problem, so no workaround is needed and these actions have usual "Note:On" condition.

_fBtnSetValue(_Ch)
Here we first set the value in Sonar (the function is called after "_fBtnSelectPar(_Ch)"), but only for Mute and Solo modes.
And then we call already described "_fBtnSendLED(_Ch)". We call is as a workaround here, but there is not yet time to describe why.

Notice that Set Value Actions have "Note:On", while "SendLED" has "Note:Any" condition. We set the value only when the Button is pressed (and do not set is when the buttons is released).

Continued in the next post...

azslow3

It is time to put all our effort together and define "Btn1" Control.

Btn1
As the first, we set "_Ch" for the button. For Btn1 it is "1".
Then we call our "SelectPar" function as LOCATE section.

Then we define "<BtnMonMode>" type Monitor, with priority 3. We want it is working AFTER all other monitors we have defined, since we use the result from all of them. In its feedback list, we call "SendLED" function.
With (parameter)"Value" type monitor, which we set in Solo and Mute modes, we control LOCATED parameter, Soloe and Mute, as desired.
With "Command" type monitor which we use in the Level mode, there is a trick. "Command" monitor detect changes in Text! Commands set the text, originally I was using this type of monitor to display command named. So the type name is historical, it is really "Text" monitor. In "SelectPar" function we put current "Level(_Ch)" value into the text. So, in the Level mode, the monitor will trigger only when corresponding "Level(_Ch)" Current State is changed. So only in case we really have to update THIS LED, and not trigger when we only need to update some other LED. Since on type changing monitor is always triggering, when we change the mode from/to Level, all LEDs are updated.

Then we call "SetValue".

In the "theory" part, I have mentioned "LOCATE"/"CHECK"/"ADJUST". And here it is how it looks in the working preset.

Finally, we reset the monitor...
And it is time to explain why I did all that "Note:Any", LED sending in "Set" and reseting the monitor.

All dedicated DAW controllers like VS, MCU, etc. have LEDs and buttons completely decoupled. So operating buttons has no influence on LEDs, it is up to the DAW only to make them light when needed.

MIDI masterboards and "simple" controllers, while have LEDs, are normally was thought to be used without feedback... LEDs there are "just for fun". When you press the button/pad, LED is turned on. When you release the pad it is turned off. Looks "great", imitate some "feedback" and shows that the device has these LEDs (which are proudly announced in the product specification, you have paid for them, you want to see them!). And that make the integration with DAW a "nightmare". Why?

Let say you assigned a pad to solo track 1. It is not soloed yet. LED is dark. Everything is in sync. You press the pad... LED lit (from pressing operation, not from the DAW!), DAW solo the track, so it is now on, LED should light, everything is sync! Now you RELEASE the pad, LED is dark! Releasing has switched it off automatically! Even in case the DAW has explicitly set it MUST light!

If the pad is responsible for not existing track, LED will still lit when you press it! (the "status" of solo was and is "undefined", so LED is not updated from the DAW).

In total: as long as you operate such pad on device, it is not showing correct Solo status from the DAW. Even in case the DAW sends correct status. Funny? Looks "great"? Not at all, it is a mess...

Easy workaround is that last "Reset monitor" with "Note:Any" alone. Every time button is pressed OR release, this monitor will resend correct LED status. But... it can take up to 75ms till the monitor does that (remember Monitor Cycle?). During this time we can get "flickering". To avoid it, we update LED IMMEDIATELY once we receive MIDI signal. The REACTION is called in "real time", so LED is updated sufficiently fast to not show incorrect temporary value (at least on my MPK Mini). But to implement that, we had to set many Actions to "Note:Any" and add "SendLED" to the REACTION.

Is "Reset monitor" still needed if we send LEDs directly? Yes. When we send LEDs in REACTION, we BELIEVE we send it correctly. In case we have change that status (f.e. turned off Solo), AZ Controller is sufficiently "smart" to send the status it think is correct (in the example case "Off"). But AZ Controller can not check that: when changing parameter value in Sonar, that effect the FUTURE value. Sonar also operate in "cycles" internally. These cycles are on the audio level, so instead of 75ms for Control Surface, that is just several ms (the interface buffer dependent). But it is not "0". So, if we ask Sonar about Solo status IMMEDIATELY after we just ask it to turn it off, Sonar STILL returns "old" (really current) value, Solo is ON! And so, if Sonar has "accepted" the value change but has not (was unable) really change it (I have seen that with other parameter, I am not sure that can happened with Solo), AZ Controller in REACTION will send incorrect status. Se we still need the monitor to "correct" it. There will be flickering in this case, but that is not something we can change. And with monitor, after 75ms the indication will be correct in ALL cases.


[Btn2 - Btn8]
In the Option Tab there is very nice "Dup" button ;) After selecting Btn1 and pressing it 7 times, Btn2 - Btn8 are almost defined.
Only "Ch -> X" and targeted monitor in the "Reset" should be corrected for each button separately, thanks "_Ch" based design and functions.

Also with show design, we will never have to touch Btn1-Btn8 definitions again till we want some drastic changes in the overall behavior. We just have to change the functions which work for ALL these buttons.

Continued in the next post...

azslow3

Software modes in AZ Controller
Before I continue with the preset, I want explain how you can define "Software modes" in AZ Controller in general.

Many controllers have more the one "hardware preset". Usually there are several for different DAWs and some additional, for controlling instruments, freely user defined. What these presets really are? They define which MIDI signal each control sends. F.e. in one preset, some knob sends "CC 1". In other preset the same knob sends "CC 5". And so on.

For computer keyboard, you use "shortcuts" which you can define with "modifier keys". So "Ctrl+A" is not the same as "A". We can look at that using "preset" terminology. As long as you are pressing "Shift", the keyboard is switched to different "preset", the same keys start producing capital characters or completely different characters (for numeric keys). You can also "permanently" shift the keyboard to "Shift preset" with "Caps lock" key.

For computer keyboard and for Control Surface we can speak about "software presets". But the word "preset" is not the best choice here. First, it is easy to mix with "hardware preset". Second, there are "Control Surface Plug-in Presets" (as with any plug-ins in Sonar). And finally, you may want to switch only a part of controls to do different things, while under "preset" we normally understand something "global".

I am going to call "software presets" as "software modes". For me, "the mode for knobs" is changed sounds better then "the preset for knobs" is changed.

The difference between "hardware preset" and "software mode" is WHEN/WHERE an operation is interpreted differently. In "hardware preset" the interpretation happens inside the surface, so between you press the button (which still produce the same electrical signal all the time) and MIDI signal is sent. In "software mode" the interpretation happens during the MIDI signal processing (MIDI signal is the same in all software modes).

AZ Controller has special support for "hardware presets", but that is a different topic. For "software modes" there is nothing special. Since we can define arbitrary interpretation for each MIDI signal, we can define as many "modes" for each control as we need.

Since in different modes we want do different things inside the same Action List, the only way to execute/skip some Action is throw Action Conditions and all Action Conditions are defined in terms of Software Sets, "software mode" is also defined in terms of Software Sets. We can define any number of Software Sets and use any combination of them in Action Conditions. So AZ Controller supports unlimited number of software modes and there combinations. Each Software Set, including System Sets, can be used a set of "software modes".

Example: if we define "EncoderMode" Set as "Coarse" and "Fine", with the System Set "Transport" : "Stop", "Play", "Rec" we can define in  Encoder reaction:
"EncoderMode":"Coarse", "Transport":"Stop" - ACT R1, Endless ,+-5%
"EncoderMode":"Fine", "Transport":"Stop" - ACT R1, Endless ,+-1%
"EncoderMode":"Coarse", "Transport":"Play" - Volume, Endless ,+-1%
... etc.
When the transport is stopped, we will change current plug-in parameter "R1", coarse of fine defined by "EncoderMode". But as soon as we start playing, the same knob will start control Volume. Is that useful? Probably not. But that is just an example  ;)


AZ Controller imply no rules how you use Software Set as "software modes". But I personally follow several rules:

1) if some modes are mutual exclusive, define them in one Set. F.e. I could define "BtnModeSolo": "Yes","No" ; "BtnModeMute":"Yes","No" and "BtnModeLevel":"Yes","No". And I could make the preset using them, with absolutely the same behavior for End User. But what to do if "BtnModeSolo" = "Yes" and "BtnModeMute" = "Yes"? Does not make sense. The preset will do something strange. So I have defined "BtnMode": "Mute", "Solo", "Level". First, buttons are always in "some" mode. Second, there is less chance to set incorrect conditions. Third, to modify the mode I just have to change Current State of one Set, not thinking I have to change some other Set (more on that in (3))

Note that I do not mean you have to combing all possible modes for something into one Set. F.e. if I want several banks for knobs, I usually define separately "KnobMode": "Volume", "Pan", "SendLvl", etc. and "KnobBank": 1, 2. Modes "Volume"+"Bank1", "Volume"+"Bank2", "Pan"+"Bank1", etc are mutual exclusive, but "KnobMode" and "KnobBank" are not: "Volume" has 2 banks and "Pan" has 2 banks.
If only one "major" mode has 2 banks, it is situation dependent. Sometimes I define "Volume_B1", "Volume_B2", "Pan", "SendLvl" in this case. Sometimes I separate them. Note there is no violation for the basic rule here, "Pan"+"Bank2" just make no sense (but if "Bank"=2 when we set "Pan" there is no confusion if "Pan" processing does not include "Bank" conditions).

2) if there is a chance you are going to "split" some mode later, define "combined" Set for current use but defined everything in terms of "concrete" Sets. For example if I have "Mode":"Mix","FX" for faders and knobs, but I think in the future I want be able switch only knobs to "FX" leaving faders in "Mix", I define "ModeKnob":"Mix","FX" and "ModeFader":"Mix","FX" IN ADDITION to "Mode":"Mix","FX". But see (3)

3) try to make preset such a way that switching modes can be done but setting corresponding Set only. F.e. in the preset we have "BtnMode" and "BtnMonMode". "BtnMonMode" must be set correctly as soon as with switch "BtnMode". I could decide that "as soon as something is switching BtnMode, it should switch BtnMonMode as well". Instead, I have defined "BtnMode" monitor where are set BtnMonMode. The same approach can be used to set "ModeKnob" and "ModeFader" on "Mode" switch from the example in (2).

The same as with (1), there are less mistakes when you use this approach. Also "mode switch" Action List looks simpler (just one Set State Action). And while we could solve that by defining a "function" instead of the monitor, I can use Overview Tab to correctly switch mode, without searching and executing the function in the Logic list.

Continued in the next post...

azslow3

_fModeSwitchSendLED
Does the same for ModeSwitch LED as other Send does for 1-8 buttons. The "ModeSwitchLED" Set defines exactly what we should send. Since the "Mode" is AZ Controller internal "thing", Sonar is not involved in this process.

_fModeSwitch()
With "Note:Off" condition (we are switching on button release), it set required "BtnMode" depending from the previous "BtnMode" and either timeout for "long press" is over (indicated with "ModeSwitch:Off"). Explained later.

_ModeSwitchBlink
has timer triggered on request with priority "1" (it should be after _BtnMode monitor which set ModeSwitchLED correctly). In this timer, I flip "Blink_On" and "Blink_Off", used in

ModeSwitch
Here I have implemented an "advanced" logic, just to show it. I could do Next "BtnMode" (loop) without all the dance. But let say I want switch between 2 modes quickly, with a possibility to enter the 3d mode by "long press".

I prefer "Long press" over "Double click" (also possible with close to the same approach). In the first case, you just have to wait a bit. In the second you have to be fast, which usually creates more noise. We are working with Music, so I try to keep silence  8)

The timer on request (priority 0, no dependencies from other monitors) always set "ModeSwitch" to "Off".

Next "ModeSwitchLED" State monitor (priority 2, should be after _ModeSwitchBlink timer) sends required LED status. And in case we are in "Blinking" states, it arms _ModeSwitchBlink timer with the interval we want the LED blink. I set "1sec ON / 1sec OFF", but all values are allowed. F.e. you can set "next" and "1sec" to get "short ON / pause" effect. So, we send "Blink_On" or "Blink_Off" are immediately arm the Blink timer, which flips the mode, which trigger this monitor again, and so on. Till we set "ModeSwitchLED" into some "stable" State.

We set "ModeSwitch" to "On" (only when we press the button, "Note:On") and then reset our timer to trigger after "long press" period (~0.5sec). As the result, if we release the button before timer, we still see "ModeSwitch":"On". If we release the button after the timer, we see "ModeSwitch:Off". And so we can detect either on release we was pressing sufficiently long.

On release ("Note:Off") we call our "_fModeSwitch()". The only goal of which is to set "BtnMode" correctly. The reset (including ModeSwitch LED Set) is done "automagically". As explained before, I can switch BtnMode in the Overview Tab and still everything will work correctly, including ModeSwitch LED.

The last Action is the same workaround as for other buttons to avoid LED showing something incorrectly during/after pressing. But here we are SURE that what we send is correct, "BtnMode" is AZ Controller internal. If we set it, it is set. Sonar is not involved. So there is no extra monitor reset.

Continued in the next post...

azslow3

And now the easy part. Buttons without feedback (you can try to add it as an exercise).

Bank buttons in that preset can switch the mode, switch Banks and switch focused channel.

BankDown
The REACTION is done where you release the button. When you press it, it is not unknown yet what you want to do.
So the first and the only Action when you press it ("Note:On") is remembering the fact it is pressed by setting "BankDown" Set to "On".

All following Actions have "Note:Off" condition (could be "Any" since there are only 2 possible cases and one we have already configured).

As the first, I check that "BankDown" is still "On", that means nothing set it to "Off" in between press and release. If it is "Off", it is already "used", so I do nothing (final Action).

Then I save the fact it is released (set "BankDown" to "Off").

Now the REACTION section. When "BtnState" is "Level", I LOCATE previous strip from current focus and focus it. In other cases ("Mute"/"Solo") I move the "bank", which is WAI.
The trick here is to find the place to move. I first try to LOCATE current WAI minus 8 strips. In case that fails (f.e. currently WAI starts at strip 4), I LOCATE the very first strip.
Then I ask WAI to be moved to this LOCATION. Note that if LOCATION is still invalid (f.e. there is no strips at all), focusing or moving WAI does nothing. It will not "crash Sonar" or do anything nasty.

BankUp
Here I know everything I need right on press. So I do not wait till the button is released.

If "BankDown" is "On", which means that BankDown buttons is still pressed, I rotate "BtnMode" Set. I also indicate that "BtnDown" is "used" and it should not produce usual reaction (WAI/focus move).

Otherwise I do the save as in BankDown, but in reversed direction (next strip, WAI + 8). There is no check for WAI+8 failure, in case such strip does not exists, unlike in BankDown, we have no place to move (independent from how many strips exists in the current WAI, 0 up to 8, they are all "covered").




azslow3

Q&A

  • If your buttons produce MIDI signals when they are pressed only, but not when they are released (on many surfaces, transport buttons are by default in MMC mode, sending SysEx when there are pressed, other send ProgramChange, which also has no "Off"), which from mentioned controls in this preset will not work? Correct answer: ModeSwitch and BankDown. Do you understand why? If yes, you can also understand why it is a good idea to switch all buttons/pads on your surface into Note/CC/PolyAftertouch mode. And also why there is NO difference between Note/CC/PA in respect to controlling the DAW
  • If your buttons are in "switch" mode, so then send "On" when they pressed once (nothing on release) and "Off" next time (nothing on release). What does that mean for the preset? Correct answer: the same problem as in (1), in addition all actions in REACTION should be set to "Note:Any" since "Note:On" and "Note:Off" mean the same operation on hardware side
  • If the surface has LEDs near/under buttons, but they do not react on the same MIDI. What to do? Ups...: read the documentation, google is your friend, if everything else fail, try to send all possible messages to see if something works  ;)

Some ideas about buttons and LEDs
* if you have color LEDs: you can try to use different colors in different modes, also you can try to make the "level" indication from one such LED (level -> color).
* there are MANY combinations you can organize with just 2 buttons (in my case, leftmost and middle button on my Digital Piano), using "long press", the first is pressed when the second is still pressed separate from the second is pressed when the first is still pressed, in addition to "normal" pressed/released for each. Combined with the transport, that opens even more possibilities (f.e. controlling the volume in Play/Rec with "long press", just stop by the first, stop+undo+rec again by the second in the rec mode while "move to the next marker" in the play mode).

Please do not hesitate to ask questions, correct my bad English or comment. You are welcome!  :)