The codebase to run the *spark d-fuser controller www.sparkav.co.uk/dvimixer

Dependencies:   SPK-TVOne DMX DmxArtNet NetServicesMin OSC PinDetect mRotaryEncoder iniparser mbed spk_oled_ssd1305 filter

Revision:
49:16309f39cead
Parent:
48:c0fedfa8c525
Child:
50:e98220a71065
--- a/main.cpp	Mon Dec 10 16:45:03 2012 +0000
+++ b/main.cpp	Tue Dec 11 18:05:32 2012 +0000
@@ -39,8 +39,9 @@
  * v20 - Keying values and resolutions load from USB mass storage - September'12
  * v21 - Mixing behaviour upgrade: blend-additive as continuum, test cards on startup if no valid source - October'12
  * v22 - EDID passthrough override and EDID upload from USB mass storage
- * v23 - Set keying values from controller, requires a guided, step-through process for user
+ * v23 - Set keying values from controller
  * v24 - Conform uploads SIS image; now once firmware is loaded controller is all that is required
+ * v25 - UX work
  * vxx - TODO: Writes back to .ini on USB mass storage: keyer updates, comms, hdcp, edid internal/passthrough, ...?
  * vxx - TODO: EDID creation from resolution
  */
@@ -59,7 +60,7 @@
 #include "DMX.h"
 #include "filter.h"
 
-#define kSPKDFSoftwareVersion "24.3"
+#define kSPKDFSoftwareVersion "25"
 
 // MBED PINS
 
@@ -161,16 +162,11 @@
 
 SPKMenu mixModeMenu;
 SPKMenu mixModeAdditiveMenu;
-SPKMenu mixModeKeyerMenuUpdate; 
-SPKMenu mixModeKeyerMenuMinY;
-SPKMenu mixModeKeyerMenuMaxY;
-SPKMenu mixModeKeyerMenuMinU;
-SPKMenu mixModeKeyerMenuMaxU;
-SPKMenu mixModeKeyerMenuMinV;
-SPKMenu mixModeKeyerMenuMaxV;
+SPKMenu mixModeUpdateKeyMenu; 
 enum { mixBlend, mixAdditive, mixKey };
 int mixKeyStartIndex = 1; // need this hard coded as mixBlend and mixAdditive are now combined into the same menu item
 int mixMode = mixBlend; // Start with safe mix mode, and test to get out of it. Safe mode will work with inputs missing and without hold frames.
+int mixModeOld = mixMode;
 float fadeCurve = 0.0f; // 0 = "X", ie. as per blend, 1 = "/\", ie. as per additive  <-- pictograms!
 
 SPKMenu commsMenu;
@@ -178,6 +174,8 @@
 int commsMode = commsNone;
 
 SPKMenu advancedMenu;
+SPKMenu advancedMenuHDCP;
+SPKMenu advancedMenuEDID;
 enum { advancedHDCPOn, advancedHDCPOff, advancedEDIDPassthrough, advancedEDIDInternal, advancedTestSources, advancedConformProcessor, advancedConformUploadProcessor, advancedLoadDefaults, advancedSetResolutions };
 
 // RJ45 Comms
@@ -351,6 +349,8 @@
 
 bool handleTVOneSources()
 {
+    static int notOKCounter = 0;
+    
     bool ok = true;
 
     int32_t payload = 0;
@@ -381,7 +381,7 @@
         tvOneDetectString += " R: ";
         tvOneDetectString += right;
     }
-    
+        
     tvOneStatusMessage.addMessage(tvOneDetectString, 0);
     
     // Assign appropriate source depending on whether DVI input is good
@@ -400,11 +400,19 @@
         if (ok && RGB2) tvOneRGB2Stable = true;
     } 
     
-    // It seemed best to conform / power on at 100% and only update fade levels if controls moved.
-    // However this doesn't seem to be entirely reliable in practice, so lets force the fade levels to the controls regardless
-    tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustWindowsMaxFadeLevel, fadeAPercent);
-    tvOne.command(0, kTV1WindowIDB, kTV1FunctionAdjustWindowsMaxFadeLevel, fadeBPercent);
-        
+    // It seems there is an occasional RS232 choke around power-on of the processor.
+    // Hard to reproduce, doubly so when in debug mode, this may fix. 
+    if (ok)
+    {
+        notOKCounter = 0;
+        tvOne.resetCommandPeriods();
+    }
+    else if (++notOKCounter == 5) 
+    {
+        printf("Handling 5x Not OK: increasing command periods\r\n");
+        tvOne.increaseCommandPeriods(1500);
+    }
+    
     return ok;       
 }   
 
@@ -466,12 +474,17 @@
     else
     {
         int index = mixModeMenu.selectedIndex() - mixKeyStartIndex;
-
+        
+        // Turn off Additive Mixing on output
+        ok = tvOne.command(0, kTV1WindowIDA, 0x298, 0);
+        // Turn on Keyer
+        ok = ok && setKeyParamsTo(index);
         ok = ok && tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustKeyerEnable, true);
-        ok = ok && setKeyParamsTo(index);
- 
+        
         snprintf(sentMSGBuffer, kStringBufferLength, "Keyer On with %i", index);
     }
+    
+    mixModeOld = mixMode;
 
     if (ok) sentOK = "Sent:";
     else sentOK = "Send Error:";
@@ -479,6 +492,53 @@
     tvOneStatusMessage.addMessage(sentOK + sentMSGBuffer, kTVOneStatusMessageHoldTime);
 }
 
+bool checkTVOneMixStatus()
+{
+    bool ok = true;
+    
+    int32_t payload;
+    
+    // Mix Mode
+    bool mixModeNeedsAction = false;
+    bool additiveOn = false, keyerOn = false;
+    
+    if (mixMode == mixBlend)    { additiveOn = false; keyerOn = false;}
+    if (mixMode == mixAdditive) { additiveOn = true;  keyerOn = false;}
+    if (mixMode >= mixKey)      { additiveOn = false; keyerOn = true; }
+    
+    payload = -1;
+    ok = ok && tvOne.readCommand(0, kTV1WindowIDA, 0x298, payload);
+    if (payload != additiveOn) mixModeNeedsAction = true;
+
+    payload = -1;
+    ok = ok && tvOne.readCommand(0, kTV1WindowIDA, kTV1FunctionAdjustKeyerEnable, payload);
+    if (payload != keyerOn) mixModeNeedsAction = true;
+
+    if (ok && mixModeNeedsAction) 
+    {
+        if (debug) debug->printf("Check TVOne Mix Status requiring mixMode action. mixMode: %i \r\n", mixMode);
+        actionMixMode();
+    }
+    
+    // Check Fade
+    payload = -1;
+    ok = ok && tvOne.readCommand(0, kTV1WindowIDA, kTV1FunctionAdjustWindowsMaxFadeLevel, payload);
+    if (ok && (payload != fadeAPercent))
+    {
+        if (debug) debug->printf("Check TVOne Mix Status requiring fadeA action");
+        ok = ok && tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustWindowsMaxFadeLevel, fadeAPercent);
+    }
+    
+    payload = -1;
+    ok = ok && tvOne.readCommand(0, kTV1WindowIDB, kTV1FunctionAdjustWindowsMaxFadeLevel, payload);
+    if (ok && (payload != fadeBPercent))
+    {
+        if (debug) debug->printf("Check TVOne Mix Status requiring fadeB action");
+        ok = ok && tvOne.command(0, kTV1WindowIDB, kTV1FunctionAdjustWindowsMaxFadeLevel, fadeBPercent);
+    }
+    
+    return ok;
+}
 
 bool conformProcessor()
 {
@@ -616,7 +676,7 @@
     
     for (int i=0; i < settings.keyerSetCount(); i++)
     {
-        mixModeMenu.addMenuItem(SPKMenuItem(settings.keyerParamName(i), &mixModeKeyerMenuUpdate));
+        mixModeMenu.addMenuItem(SPKMenuItem(settings.keyerParamName(i), mixKeyStartIndex + i));
     }
     mixModeMenu.addMenuItem(SPKMenuItem("Back to Main Menu", &mainMenu));
 }
@@ -645,6 +705,374 @@
     }
 }
 
+void mixModeAdditiveMenuHandler(int change, bool action)
+{
+    fadeCurve += change * 0.05f;
+    if (fadeCurve > 1.0f) fadeCurve = 1.0f;
+    if (fadeCurve < 0.0f) fadeCurve = 0.0f;
+    
+    mixMode = (fadeCurve > 0.001f) ? mixAdditive: mixBlend;
+
+    screen.clearBufferRow(kMenuLine2);
+    screen.textToBuffer("Blend [ ----- ] Add", kMenuLine2);
+    screen.characterToBuffer('X', 38 + fadeCurve*20.0f, kMenuLine2);
+    
+    if (debug) debug->printf("Fade curve changed by %i to %f \r\n", change, fadeCurve);
+    
+    if (action)
+    {
+        selectedMenu = &mixModeMenu;
+        
+        screen.clearBufferRow(kMenuLine1);
+        screen.clearBufferRow(kMenuLine2);
+        screen.textToBuffer(selectedMenu->title, kMenuLine1);
+        screen.textToBuffer(selectedMenu->selectedString(), kMenuLine2);
+    }
+}
+
+void advancedMenuHDCPHandler(int change, bool action)
+{
+    static int currentHDCP;
+    static unsigned int state = 1;
+
+    if (change == 0 && !action)
+    {
+        // We check the control not the status, as status depends on connection etc.
+        
+        int32_t payloadOutput = -1;
+        tvOne.readCommand(0, kTV1WindowIDA, kTV1FunctionAdjustOutputsHDCPRequired, payloadOutput);
+        
+        int32_t payload1 = -1;
+        tvOne.readCommand(kTV1SourceRGB1, kTV1WindowIDA, kTV1FunctionAdjustSourceHDCPAdvertize, payload1);
+        
+        int32_t payload2 = -1;
+        tvOne.readCommand(kTV1SourceRGB2, kTV1WindowIDA, kTV1FunctionAdjustSourceHDCPAdvertize, payload2);
+   
+        if ((payloadOutput == payload1) && (payload1 == payload2) && (payload2 == 0)) 
+        {
+            currentHDCP = 0; // Change to on
+        }
+        else if ((payloadOutput == payload1) && (payload1 == payload2) && (payload2 == 1))
+        {
+            currentHDCP = 1; // Change to off
+        }
+        else
+        {
+            currentHDCP = -1; // Change to off
+        }
+        
+        if (debug) debug->printf("HDCP detected O: %i 1: %i 2: %i", payloadOutput, payload1, payload2);
+    }
+    
+    state += change;
+    
+    char paramLine[kStringBufferLength];
+    screen.clearBufferRow(kMenuLine2);
+    
+    const char* current = currentHDCP == -1 ? "Mixed" : ( currentHDCP == 1 ? "On" : "Off");
+    
+    if (state % 2) snprintf(paramLine, kStringBufferLength, "%s. Set: [%s/      ]?", current, currentHDCP == 0 ? "On " : "Off" );
+    else           snprintf(paramLine, kStringBufferLength, "%s. Set: [   /Cancel]?", current);
+    screen.textToBuffer(paramLine, kMenuLine2);
+
+    if (action)
+    {
+        if (state % 2)
+        {
+            screen.clearBufferRow(kTVOneStatusLine);
+            screen.textToBuffer("Setting HDCP...", kTVOneStatusLine);
+            screen.sendBuffer();
+        
+            // Do the action
+            bool ok = tvOne.setHDCPOn(currentHDCP == 0);
+            
+            if (ok) tvOne.command(0, kTV1WindowIDA, kTV1FunctionPowerOnPresetStore, 1);
+            
+            std::string sendOK = ok ? "Sent: HDCP " : "Send Error: HDCP ";
+            sendOK += currentHDCP == 0 ? "On" : "Off";
+            
+            tvOneStatusMessage.addMessage(sendOK, kTVOneStatusMessageHoldTime);
+        }
+        
+        // Get back to menu
+        selectedMenu = &advancedMenu;
+        
+        screen.clearBufferRow(kMenuLine1);
+        screen.clearBufferRow(kMenuLine2);
+        screen.textToBuffer(selectedMenu->title, kMenuLine1);
+        screen.textToBuffer(selectedMenu->selectedString(), kMenuLine2);
+    }
+}
+
+void advancedMenuEDIDHandler(int change, bool action)
+{
+    static int currentEDIDPassthrough;
+    static unsigned int state = 1;
+
+    if (change == 0 && !action)
+    {
+        int32_t payload1 = -1;
+        tvOne.readCommand(kTV1SourceRGB1, kTV1WindowIDA, kTV1FunctionAdjustSourceEDID, payload1);
+        
+        int32_t payload2 = -1;
+        tvOne.readCommand(kTV1SourceRGB2, kTV1WindowIDA, kTV1FunctionAdjustSourceEDID, payload2);
+        
+        if ((payload1 == payload2) && (payload2 == EDIDPassthroughSlot)) 
+        {
+            currentEDIDPassthrough = 1;
+        }
+        else if ((payload1 == payload2) && (payload2 != EDIDPassthroughSlot))
+        {
+            currentEDIDPassthrough = 0;
+        }
+        else
+        {
+            currentEDIDPassthrough = -1;
+        }
+    }
+    
+    state += change;
+        
+    char paramLine[kStringBufferLength];
+    screen.clearBufferRow(kMenuLine2);
+    
+    const char* current = currentEDIDPassthrough == -1 ? "Mixed" : ( currentEDIDPassthrough == 1 ? "Thru" : "Int");
+    
+    if (state % 2) snprintf(paramLine, kStringBufferLength, "%s. Set: [%s/      ]?", current, currentEDIDPassthrough == 0 ? "Thru" : "Int");
+    else           snprintf(paramLine, kStringBufferLength, "%s. Set: [   /Cancel]?", current);      
+    screen.textToBuffer(paramLine, kMenuLine2);
+    
+    if (action)
+    {
+        if (state % 2)
+        {
+            screen.clearBufferRow(kTVOneStatusLine);
+            screen.textToBuffer("Setting EDID...", kTVOneStatusLine);
+            screen.sendBuffer();
+        
+            // Do the action
+            tvOneEDIDPassthrough = currentEDIDPassthrough == 0;
+            
+            bool ok = true;
+            
+            int32_t slot = tvOneEDIDPassthrough ? EDIDPassthroughSlot : resolutionMenu.selectedItem().payload.command[1];
+        
+            ok = ok && tvOne.command(kTV1SourceRGB1, kTV1WindowIDA, kTV1FunctionAdjustSourceEDID, slot);
+            ok = ok && tvOne.command(kTV1SourceRGB2, kTV1WindowIDA, kTV1FunctionAdjustSourceEDID, slot);
+            
+            if (ok) tvOne.command(0, kTV1WindowIDA, kTV1FunctionPowerOnPresetStore, 1);
+            
+            std::string sendOK = ok ? "Sent: EDID " : "Send Error: EDID ";
+            sendOK += tvOneEDIDPassthrough ? "Passthrough" : "Internal";
+            
+            tvOneStatusMessage.addMessage(sendOK, kTVOneStatusMessageHoldTime);
+        }
+            
+        // Get back to menu
+        selectedMenu = &advancedMenu;
+        
+        screen.clearBufferRow(kMenuLine1);
+        screen.clearBufferRow(kMenuLine2);
+        screen.textToBuffer(selectedMenu->title, kMenuLine1);
+        screen.textToBuffer(selectedMenu->selectedString(), kMenuLine2);
+    }
+}
+
+void mixModeUpdateKeyMenuHandler(int menuChange, bool action)
+{
+    static int actionCount = 0;
+    static unsigned int state = 0;
+    
+    if (action) actionCount++;
+    
+    if (actionCount == 0)
+    {
+        screen.clearBufferRow(kMenuLine1);
+        screen.clearBufferRow(kMenuLine2);
+        screen.textToBuffer("Edit current key?", kMenuLine1);
+        
+        state += menuChange;
+        switch (state % 3) 
+        {
+            case 0: screen.textToBuffer("[Tweak/          /      ]", kMenuLine2); break;
+            case 1: screen.textToBuffer("[     /Start Over/      ]", kMenuLine2); break;
+            case 2: screen.textToBuffer("[     /          /Cancel]", kMenuLine2); break;
+        }
+    }
+    if (actionCount == 1)
+    {
+        state = state % 3;
+        
+        if (state == 0) 
+        {
+            settings.editingKeyerSetIndex = mixModeMenu.selectedIndex() - mixKeyStartIndex;
+            actionCount++;
+        }
+        else if (state == 1)
+        {
+            settings.editingKeyerSetIndex = mixModeMenu.selectedIndex() - mixKeyStartIndex; 
+            settings.setEditingKeyerSetValue(SPKSettings::minY, 0);
+            settings.setEditingKeyerSetValue(SPKSettings::maxY, 255);
+            settings.setEditingKeyerSetValue(SPKSettings::minU, 0);
+            settings.setEditingKeyerSetValue(SPKSettings::maxU, 255);
+            settings.setEditingKeyerSetValue(SPKSettings::minV, 0);
+            settings.setEditingKeyerSetValue(SPKSettings::maxV, 255);
+            actionCount++;
+            state = 0;
+        }
+        else if (state == 2)
+        {
+            settings.editingKeyerSetIndex = -1;
+                    
+            // Get back to menu
+            actionCount = 0;
+            state = 0;
+            selectedMenu = &mixModeMenu;
+            screen.clearBufferRow(kMenuLine1);
+            screen.clearBufferRow(kMenuLine2);
+            screen.textToBuffer(selectedMenu->title, kMenuLine1);
+            screen.textToBuffer(selectedMenu->selectedString(), kMenuLine2);
+            
+            return;
+        }
+    }
+    if (actionCount == 2)
+    {
+        int value = settings.editingKeyerSetValue(SPKSettings::maxY);
+        value += menuChange;
+        if (value < 0) value = 0;
+        if (value > 255) value = 255;
+        settings.setEditingKeyerSetValue(SPKSettings::maxY, value);
+
+        screen.clearBufferRow(kMenuLine1);
+        screen.clearBufferRow(kMenuLine2);
+        screen.textToBuffer("Down until unmasked", kMenuLine1);
+        
+        char paramLine[kStringBufferLength];
+        snprintf(paramLine, kStringBufferLength, "[   /%3i][   /   ][   /   ]", value);
+        screen.textToBuffer(paramLine, kMenuLine2);
+        
+        tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustKeyerMaxY, value);   
+    }
+    else if (actionCount == 3)
+    {
+        int value = settings.editingKeyerSetValue(SPKSettings::minY);
+        value += menuChange;
+        if (value < 0) value = 0;
+        if (value > settings.editingKeyerSetValue(SPKSettings::maxY)) value = settings.editingKeyerSetValue(SPKSettings::maxY);
+        settings.setEditingKeyerSetValue(SPKSettings::minY, value);
+
+        screen.clearBufferRow(kMenuLine1);
+        screen.clearBufferRow(kMenuLine2);
+        screen.textToBuffer("Up until unmasked", kMenuLine1);
+        
+        char paramLine[kStringBufferLength];
+        snprintf(paramLine, kStringBufferLength, "[%3i/%3i][   /   ][   /   ]", value,
+                                                                                settings.editingKeyerSetValue(SPKSettings::maxY));
+        screen.textToBuffer(paramLine, kMenuLine2);
+        
+        tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustKeyerMinY, value); 
+    }
+    else if (actionCount == 4)
+    {
+        int value = settings.editingKeyerSetValue(SPKSettings::maxU);
+        value += menuChange;
+        if (value < 0) value = 0;
+        if (value > 255) value = 255;
+        settings.setEditingKeyerSetValue(SPKSettings::maxU, value);
+
+        screen.clearBufferRow(kMenuLine1);
+        screen.clearBufferRow(kMenuLine2);
+        screen.textToBuffer("Down until unmasked", kMenuLine1);
+        
+        char paramLine[kStringBufferLength];
+        snprintf(paramLine, kStringBufferLength, "[%3i/%3i][   /%3i][   /   ]", settings.editingKeyerSetValue(SPKSettings::minY), 
+                                                                                settings.editingKeyerSetValue(SPKSettings::maxY),  
+                                                                                value);
+        screen.textToBuffer(paramLine, kMenuLine2);
+        
+        tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustKeyerMaxU, value); 
+    }
+    else if (actionCount == 5)
+    {
+        int value = settings.editingKeyerSetValue(SPKSettings::minU);
+        value += menuChange;
+        if (value < 0) value = 0;
+        if (value > settings.editingKeyerSetValue(SPKSettings::maxU)) value = settings.editingKeyerSetValue(SPKSettings::maxU);
+        settings.setEditingKeyerSetValue(SPKSettings::minU, value);
+
+        screen.clearBufferRow(kMenuLine1);
+        screen.clearBufferRow(kMenuLine2);
+        screen.textToBuffer("Up until unmasked", kMenuLine1);
+        
+        char paramLine[kStringBufferLength];
+        snprintf(paramLine, kStringBufferLength, "[%3i/%3i][%3i/%3i][   /   ]", settings.editingKeyerSetValue(SPKSettings::minY), 
+                                                                                settings.editingKeyerSetValue(SPKSettings::maxY), 
+                                                                                value,
+                                                                                settings.editingKeyerSetValue(SPKSettings::maxU));
+        screen.textToBuffer(paramLine, kMenuLine2);
+        
+        tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustKeyerMinU, value);
+    }
+    else if (actionCount == 6)
+    {
+        int value = settings.editingKeyerSetValue(SPKSettings::maxV);
+        value += menuChange;
+        if (value < 0) value = 0;
+        if (value > 255) value = 255;
+        settings.setEditingKeyerSetValue(SPKSettings::maxV, value);
+
+        screen.clearBufferRow(kMenuLine1);
+        screen.clearBufferRow(kMenuLine2);
+        screen.textToBuffer("Down until unmasked", kMenuLine1);
+        
+        char paramLine[kStringBufferLength];
+        snprintf(paramLine, kStringBufferLength, "[%3i/%3i][%3i/%3i][   /%3i]", settings.editingKeyerSetValue(SPKSettings::minY), 
+                                                                                settings.editingKeyerSetValue(SPKSettings::maxY), 
+                                                                                settings.editingKeyerSetValue(SPKSettings::minU), 
+                                                                                settings.editingKeyerSetValue(SPKSettings::maxU),  
+                                                                                value);
+        screen.textToBuffer(paramLine, kMenuLine2);
+        
+        tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustKeyerMaxV, value);    
+    }
+    else if (actionCount == 7)
+    {
+        int value = settings.editingKeyerSetValue(SPKSettings::minV);
+        value += menuChange;
+        if (value < 0) value = 0;
+        if (value > settings.editingKeyerSetValue(SPKSettings::maxV)) value = settings.editingKeyerSetValue(SPKSettings::maxV);
+        settings.setEditingKeyerSetValue(SPKSettings::minV, value);
+
+        screen.clearBufferRow(kMenuLine1);
+        screen.clearBufferRow(kMenuLine2);
+        screen.textToBuffer("Up until unmasked", kMenuLine1);
+        
+        char paramLine[kStringBufferLength];
+        snprintf(paramLine, kStringBufferLength, "[%3i/%3i][%3i/%3i][%3i/%3i]", settings.editingKeyerSetValue(SPKSettings::minY), 
+                                                                                settings.editingKeyerSetValue(SPKSettings::maxY), 
+                                                                                settings.editingKeyerSetValue(SPKSettings::minU), 
+                                                                                settings.editingKeyerSetValue(SPKSettings::maxU),
+                                                                                value, 
+                                                                                settings.editingKeyerSetValue(SPKSettings::maxV));
+        screen.textToBuffer(paramLine, kMenuLine2);
+        
+        tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustKeyerMinV, value);    
+    }
+    else if (actionCount == 8)
+    {
+        // A save dialog would go here
+        
+        // Get back to menu
+        actionCount = 0;
+        selectedMenu = &mixModeMenu;
+        screen.clearBufferRow(kMenuLine1);
+        screen.clearBufferRow(kMenuLine2);
+        screen.textToBuffer(selectedMenu->title, kMenuLine1);
+        screen.textToBuffer(selectedMenu->selectedString(), kMenuLine2);
+    }
+}
+
 int main() 
 {
     if (debug) 
@@ -681,30 +1109,11 @@
     setMixModeMenuItems();
     
     mixModeAdditiveMenu.title = "Crossfade";
-    mixModeAdditiveMenu.addMenuItem(SPKMenuItem("[title overridden]", &mixModeMenu, true));
-
-    mixModeKeyerMenuUpdate.title = "Update Keyer Settings?";
-    mixModeKeyerMenuUpdate.addMenuItem(SPKMenuItem("[title overridden]", &mixModeKeyerMenuMaxY, true));
-
-    mixModeKeyerMenuMaxY.title = "UP until lighter gone";
-    mixModeKeyerMenuMaxY.addMenuItem(SPKMenuItem("[title overridden]", &mixModeKeyerMenuMinY, true)); 
- 
-    mixModeKeyerMenuMinY.title = "UP to bring back darker";
-    mixModeKeyerMenuMinY.addMenuItem(SPKMenuItem("[title overridden]", &mixModeKeyerMenuMaxU, true)); 
+    mixModeAdditiveMenu.addMenuItem(SPKMenuItem(&mixModeAdditiveMenuHandler));
 
-    mixModeKeyerMenuMaxU.title = "UP until lighter gone";
-    mixModeKeyerMenuMaxU.addMenuItem(SPKMenuItem("[title overridden]", &mixModeKeyerMenuMinU, true)); 
- 
-    mixModeKeyerMenuMinU.title = "UP to bring back darker";
-    mixModeKeyerMenuMinU.addMenuItem(SPKMenuItem("[title overridden]", &mixModeKeyerMenuMaxV, true)); 
+    mixModeUpdateKeyMenu.title = "Update Keyer Settings?";
+    mixModeUpdateKeyMenu.addMenuItem(SPKMenuItem(mixModeUpdateKeyMenuHandler));
 
-    mixModeKeyerMenuMaxV.title = "UP until lighter gone";
-    mixModeKeyerMenuMaxV.addMenuItem(SPKMenuItem("[title overridden]", &mixModeKeyerMenuMinV, true)); 
- 
-    mixModeKeyerMenuMinV.title = "UP to bring back darker";
-    mixModeKeyerMenuMinV.addMenuItem(SPKMenuItem("[title overridden]", &mixModeMenu, true)); 
- 
- 
     resolutionMenu.title = "Resolution";
     setResolutionMenuItems();
 
@@ -712,10 +1121,12 @@
     setCommsMenuItems();
     
     advancedMenu.title = "Troubleshooting"; 
-    advancedMenu.addMenuItem(SPKMenuItem("HDCP Off", advancedHDCPOff));
-    advancedMenu.addMenuItem(SPKMenuItem("HDCP On", advancedHDCPOn));
-    advancedMenu.addMenuItem(SPKMenuItem("EDID Passthrough", advancedEDIDPassthrough));
-    advancedMenu.addMenuItem(SPKMenuItem("EDID Internal", advancedEDIDInternal));
+    advancedMenuHDCP.title = "HDCP - Can Block DVI";
+    advancedMenuHDCP.addMenuItem(SPKMenuItem(&advancedMenuHDCPHandler));
+    advancedMenu.addMenuItem(SPKMenuItem(advancedMenuHDCP.title, &advancedMenuHDCP));
+    advancedMenuEDID.title = "EDID - Advertises Res's";
+    advancedMenuEDID.addMenuItem(SPKMenuItem(advancedMenuEDIDHandler));
+    advancedMenu.addMenuItem(SPKMenuItem(advancedMenuEDID.title, &advancedMenuEDID));
     //advancedMenu.addMenuItem(SPKMenuItem("Test Processor Sources", advancedTestSources));
     //if (settingsAreCustom) advancedMenu.addMenuItem(SPKMenuItem("Revert Controller", advancedLoadDefaults));
     advancedMenu.addMenuItem(SPKMenuItem("Revert Processor", advancedConformProcessor));
@@ -737,7 +1148,10 @@
 
     // If we do not have two solid sources, act on this as we rely on the window having a source for crossfade behaviour
     // Once we've had two solid inputs, don't check any more as we're ok as the unit is set to hold on last frame.
-    bool ok = handleTVOneSources();
+    handleTVOneSources();
+    
+    // Processor can have been power-on saved with a keyer on, lets revert
+    actionMixMode();
     
     // Display menu and framing lines
     screen.horizLineToBuffer(kMenuLine1*pixInPage - 1);
@@ -760,9 +1174,7 @@
     //// MIXER RUN
 
     while (1) 
-    {
-        bool updateMixMode = false;
-    
+    {    
         //// Task background things
         if ((osc || artNet) && rj45Mode == rj45Ethernet)
         {
@@ -803,152 +1215,9 @@
         // Update GUI
         if (menuChange != 0)
         {
-            if (selectedMenu->selectedItem().handlingControls)
+            if (selectedMenu->selectedItem().type == SPKMenuItem::hasHandler)
             {
-                if (selectedMenu == &mixModeAdditiveMenu)
-                {
-                    fadeCurve += menuChange * 0.05;
-                    if (fadeCurve > 1.0f) fadeCurve = 1.0f;
-                    if (fadeCurve < 0.0f) fadeCurve = 0.0f;
-                    
-                    int newMixMode = (fadeCurve > 0.0f) ? mixAdditive: mixBlend;
-
-                    if (newMixMode != mixMode)
-                    {
-                        mixMode = newMixMode;
-                        updateMixMode = true;
-                    }
-
-                    screen.clearBufferRow(kMenuLine2);
-                    screen.textToBuffer("Blend [ ----- ] Add", kMenuLine2);
-                    screen.characterToBuffer('X', 38 + fadeCurve*20.0f, kMenuLine2);
-                    
-                    if (debug) debug->printf("Fade curve changed by %i to %f \r\n", menuChange, fadeCurve);
-                }
-                else if (selectedMenu == &mixModeKeyerMenuUpdate)
-                {
-                    if (menuChange < 0)
-                    {
-                        settings.editingKeyerSetIndex = mixModeMenu.selectedIndex() - mixKeyStartIndex;
-                        screen.textToBuffer("[Yes/  ]", kMenuLine2);
-                    }
-                    else
-                    {
-                        settings.editingKeyerSetIndex = -1;
-                        screen.textToBuffer("[   /No]", kMenuLine2);
-                    }
-                }
-                else if (selectedMenu == &mixModeKeyerMenuMinY)
-                {
-                        int value = settings.editingKeyerSetValue(SPKSettings::minY);
-                        value += menuChange;
-                        if (value < 0) value = 0;
-                        if (value > settings.editingKeyerSetValue(SPKSettings::maxY)) value = settings.editingKeyerSetValue(SPKSettings::maxY);
-                        settings.setEditingKeyerSetValue(SPKSettings::minY, value);
-                
-                        screen.clearBufferRow(kMenuLine2);
-                        
-                        char paramLine[kStringBufferLength];
-                        snprintf(paramLine, kStringBufferLength, "[%3i/%3i][   /   ][   /   ]", value,
-                                                                                                settings.editingKeyerSetValue(SPKSettings::maxY));
-                        screen.textToBuffer(paramLine, kMenuLine2);
-                        
-                        tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustKeyerMinY, value);              
-                }
-                else if (selectedMenu == &mixModeKeyerMenuMaxY)
-                {
-                        int value = settings.editingKeyerSetValue(SPKSettings::maxY);
-                        value += menuChange;
-                        if (value < 0) value = 0;
-                        if (value > 255) value = 255;
-                        settings.setEditingKeyerSetValue(SPKSettings::maxY, value);
-                
-                        screen.clearBufferRow(kMenuLine2);
-                        
-                        char paramLine[kStringBufferLength];
-                        snprintf(paramLine, kStringBufferLength, "[   /%3i][   /   ][   /   ]", value);
-                        screen.textToBuffer(paramLine, kMenuLine2);
-                        
-                        tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustKeyerMaxY, value);              
-                }
-                else if (selectedMenu == &mixModeKeyerMenuMinU)
-                {
-                        int value = settings.editingKeyerSetValue(SPKSettings::minU);
-                        value += menuChange;
-                        if (value < 0) value = 0;
-                        if (value > settings.editingKeyerSetValue(SPKSettings::maxU)) value = settings.editingKeyerSetValue(SPKSettings::maxU);
-                        settings.setEditingKeyerSetValue(SPKSettings::minU, value);
-                
-                        screen.clearBufferRow(kMenuLine2);
-                        
-                        char paramLine[kStringBufferLength];
-                        snprintf(paramLine, kStringBufferLength, "[%3i/%3i][%3i/%3i][   /   ]", settings.editingKeyerSetValue(SPKSettings::minY), 
-                                                                                                settings.editingKeyerSetValue(SPKSettings::maxY), 
-                                                                                                value,
-                                                                                                settings.editingKeyerSetValue(SPKSettings::maxU));
-                        screen.textToBuffer(paramLine, kMenuLine2);
-                        
-                        tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustKeyerMinU, value);              
-                }
-                else if (selectedMenu == &mixModeKeyerMenuMaxU)
-                {
-                        int value = settings.editingKeyerSetValue(SPKSettings::maxU);
-                        value += menuChange;
-                        if (value < 0) value = 0;
-                        if (value > 255) value = 255;
-                        settings.setEditingKeyerSetValue(SPKSettings::maxU, value);
-                
-                        screen.clearBufferRow(kMenuLine2);
-                        
-                        char paramLine[kStringBufferLength];
-                        snprintf(paramLine, kStringBufferLength, "[%3i/%3i][   /%3i][   /   ]", settings.editingKeyerSetValue(SPKSettings::minY), 
-                                                                                                settings.editingKeyerSetValue(SPKSettings::maxY),  
-                                                                                                value);
-                        screen.textToBuffer(paramLine, kMenuLine2);
-                        
-                        tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustKeyerMaxU, value);              
-                }
-                else if (selectedMenu == &mixModeKeyerMenuMinV)
-                {
-                        int value = settings.editingKeyerSetValue(SPKSettings::minV);
-                        value += menuChange;
-                        if (value < 0) value = 0;
-                        if (value > settings.editingKeyerSetValue(SPKSettings::maxV)) value = settings.editingKeyerSetValue(SPKSettings::maxV);
-                        settings.setEditingKeyerSetValue(SPKSettings::minV, value);
-                
-                        screen.clearBufferRow(kMenuLine2);
-                        
-                        char paramLine[kStringBufferLength];
-                        snprintf(paramLine, kStringBufferLength, "[%3i/%3i][%3i/%3i][%3i/%3i]", settings.editingKeyerSetValue(SPKSettings::minY), 
-                                                                                                settings.editingKeyerSetValue(SPKSettings::maxY), 
-                                                                                                settings.editingKeyerSetValue(SPKSettings::minU), 
-                                                                                                settings.editingKeyerSetValue(SPKSettings::maxU),
-                                                                                                value, 
-                                                                                                settings.editingKeyerSetValue(SPKSettings::maxV));
-                        screen.textToBuffer(paramLine, kMenuLine2);
-                        
-                        tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustKeyerMinV, value);              
-                }
-                else if (selectedMenu == &mixModeKeyerMenuMaxV)
-                {
-                        int value = settings.editingKeyerSetValue(SPKSettings::maxV);
-                        value += menuChange;
-                        if (value < 0) value = 0;
-                        if (value > 255) value = 255;
-                        settings.setEditingKeyerSetValue(SPKSettings::maxV, value);
-                
-                        screen.clearBufferRow(kMenuLine2);
-                        
-                        char paramLine[kStringBufferLength];
-                        snprintf(paramLine, kStringBufferLength, "[%3i/%3i][%3i/%3i][   /%3i]", settings.editingKeyerSetValue(SPKSettings::minY), 
-                                                                                                settings.editingKeyerSetValue(SPKSettings::maxY), 
-                                                                                                settings.editingKeyerSetValue(SPKSettings::minU), 
-                                                                                                settings.editingKeyerSetValue(SPKSettings::maxU),  
-                                                                                                value);
-                        screen.textToBuffer(paramLine, kMenuLine2);
-                        
-                        tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustKeyerMaxV, value);    
-                }
+                selectedMenu->selectedItem().payload.handler(menuChange, false);
             }
             else
             {
@@ -987,116 +1256,51 @@
                 screen.textToBuffer(selectedMenu->title, kMenuLine1);
                 screen.textToBuffer(selectedMenu->selectedString(), kMenuLine2);
                 
+                if (selectedMenu->selectedItem().type == SPKMenuItem::hasHandler)
+                {
+                    selectedMenu->selectedItem().payload.handler(0, false);
+                }
+                
                 if (debug)
                 {
                     debug->printf("\r\n");
                     debug->printf("%s \r\n", selectedMenu->title.c_str());
                     debug->printf("%s \r\n", selectedMenu->selectedString().c_str());
                 }
-                
-                // Are we changing menus that should have a command attached?
-                if (selectedMenu == &mixModeAdditiveMenu)
-                {
-                    screen.clearBufferRow(kMenuLine2);
-                    screen.textToBuffer("Blend [ ----- ] Add", kMenuLine2);
-                    screen.characterToBuffer('X', 38 + fadeCurve*20.0f, kMenuLine2);
-                    
-                    mixMode = fadeCurve > 0 ? mixAdditive : mixBlend;
-                    updateMixMode = true;
-                }
-                else if (selectedMenu == &mixModeKeyerMenuUpdate)
-                {
-                    screen.clearBufferRow(kMenuLine2);
-                    if (settings.editingKeyerSetIndex == mixModeMenu.selectedIndex() - mixKeyStartIndex)
-                        screen.textToBuffer("[Yes/  ]", kMenuLine2);
-                    else
-                        screen.textToBuffer("[   /No]", kMenuLine2);
-                    
-                    mixMode = mixKey;
-                    updateMixMode = true;
-                }
-                else if (selectedMenu == &mixModeKeyerMenuMinY)
-                {                    
-                    char paramLine[kStringBufferLength];
-                    screen.clearBufferRow(kMenuLine2);
-                    snprintf(paramLine, kStringBufferLength, "[%3i/%3i][   /   ][   /   ]", settings.editingKeyerSetValue(SPKSettings::minY), 
-                                                                                            settings.editingKeyerSetValue(SPKSettings::maxY));
-                    screen.textToBuffer(paramLine, kMenuLine2);
-                }
-                else if (selectedMenu == &mixModeKeyerMenuMaxY)
-                {
-                    // First, check outcome of menu that got us here: mixModeKeyerMenuUpdate
-                    if (settings.editingKeyerSetIndex == -1)
-                    {
-                        selectedMenu = &mixModeMenu;
-                        
-                        // update OLED lines 1&2
-                        screen.clearBufferRow(kMenuLine1);
-                        screen.clearBufferRow(kMenuLine2);
-                        screen.textToBuffer(selectedMenu->title, kMenuLine1);
-                        screen.textToBuffer(selectedMenu->selectedString(), kMenuLine2);
-                    }
-                    else
-                    {
-                        char paramLine[kStringBufferLength];
-                        screen.clearBufferRow(kMenuLine2);
-                        snprintf(paramLine, kStringBufferLength, "[   /%3i][   /   ][   /   ]", settings.editingKeyerSetValue(SPKSettings::maxY));
-                        screen.textToBuffer(paramLine, kMenuLine2);
-                    }
-                }
-                else if (selectedMenu == &mixModeKeyerMenuMinU)
-                {
-                    char paramLine[kStringBufferLength];
-                    screen.clearBufferRow(kMenuLine2);
-                    snprintf(paramLine, kStringBufferLength, "[%3i/%3i][%3i/%3i][   /   ]", settings.editingKeyerSetValue(SPKSettings::minY), 
-                                                                                            settings.editingKeyerSetValue(SPKSettings::maxY), 
-                                                                                            settings.editingKeyerSetValue(SPKSettings::minU), 
-                                                                                            settings.editingKeyerSetValue(SPKSettings::maxU));
-                    screen.textToBuffer(paramLine, kMenuLine2);
-                }
-                else if (selectedMenu == &mixModeKeyerMenuMaxU)
-                {
-                    char paramLine[kStringBufferLength];
-                    screen.clearBufferRow(kMenuLine2);
-                    snprintf(paramLine, kStringBufferLength, "[%3i/%3i][   /%3i][   /   ]", settings.editingKeyerSetValue(SPKSettings::minY), 
-                                                                                            settings.editingKeyerSetValue(SPKSettings::maxY), 
-                                                                                            settings.editingKeyerSetValue(SPKSettings::maxU));
-                    screen.textToBuffer(paramLine, kMenuLine2);                
-                }
-                else if (selectedMenu == &mixModeKeyerMenuMinV)
-                {
-                    char paramLine[kStringBufferLength];
-                    screen.clearBufferRow(kMenuLine2);
-                    snprintf(paramLine, kStringBufferLength, "[%3i/%3i][%3i/%3i][%3i/%3i]", settings.editingKeyerSetValue(SPKSettings::minY), 
-                                                                                            settings.editingKeyerSetValue(SPKSettings::maxY), 
-                                                                                            settings.editingKeyerSetValue(SPKSettings::minU), 
-                                                                                            settings.editingKeyerSetValue(SPKSettings::maxU), 
-                                                                                            settings.editingKeyerSetValue(SPKSettings::minV),
-                                                                                            settings.editingKeyerSetValue(SPKSettings::maxV));
-                    screen.textToBuffer(paramLine, kMenuLine2);
-                }
-                else if (selectedMenu == &mixModeKeyerMenuMaxV)
-                {
-                    char paramLine[kStringBufferLength];
-                    screen.clearBufferRow(kMenuLine2);
-                    snprintf(paramLine, kStringBufferLength, "[%3i/%3i][%3i/%3i][   /%3i]", settings.editingKeyerSetValue(SPKSettings::minY), 
-                                                                                            settings.editingKeyerSetValue(SPKSettings::maxY), 
-                                                                                            settings.editingKeyerSetValue(SPKSettings::minU), 
-                                                                                            settings.editingKeyerSetValue(SPKSettings::maxU),
-                                                                                            settings.editingKeyerSetValue(SPKSettings::maxV));
-                    screen.textToBuffer(paramLine, kMenuLine2);
-                }
+            }    
+            else if (selectedMenu->selectedItem().type == SPKMenuItem::hasHandler)
+            {
+                selectedMenu->selectedItem().payload.handler(0, true);
             }
             // With that out of the way, we should be actioning a specific menu's payload?
             else if (selectedMenu == &mixModeMenu)
             {
-                // mixAdditive and mixKeyXYZ are now SPKMenus so are handled above.
-                // This should only ever do mixBlend for non d-fuser firmware processors
                 mixMode = mixModeMenu.selectedItem().payload.command[0];
-                updateMixMode = true;
+                
+                // the spanner in the works: mixBlend and mixAdditive are now both index 0 in the menu
+                if (mixMode >= mixKeyStartIndex) 
+                {
+                    // adjust for the two-into-one spanner
+                    mixMode += 1; 
+                    
+                    // if its the second click on the keying mode, lets edit the parameters
+                    if (mixMode == mixModeOld)
+                    {
+                        selectedMenu = &mixModeUpdateKeyMenu;
+                        
+                        screen.clearBufferRow(kMenuLine1);
+                        screen.textToBuffer(selectedMenu->title, kMenuLine1);
+                        
+                        selectedMenu->selectedItem().payload.handler(0, false);
+                    }
+                }
             }
             else if (selectedMenu == &resolutionMenu)
             {
+                screen.clearBufferRow(kTVOneStatusLine);
+                screen.textToBuffer("Setting Resolution...", kTVOneStatusLine);
+                screen.sendBuffer();
+                
                 bool ok;
                 int32_t slot = tvOneEDIDPassthrough ? EDIDPassthroughSlot : resolutionMenu.selectedItem().payload.command[1];
                 
@@ -1223,53 +1427,19 @@
             {
                 if (advancedMenu.selectedItem().payload.command[0] == advancedHDCPOff)
                 {
-                    bool ok;
-                    
-                    ok = tvOne.setHDCPOn(false);
-                    
-                    std::string sendOK = ok ? "Sent: HDCP Off" : "Send Error: HDCP Off";
-                    
-                    tvOneStatusMessage.addMessage(sendOK, kTVOneStatusMessageHoldTime);
+                    // Has handler
                 }
                 else if (advancedMenu.selectedItem().payload.command[0] == advancedHDCPOn)
                 {
-                    bool ok;
-                    
-                    ok = tvOne.setHDCPOn(true);
-                    
-                    std::string sendOK = ok ? "Sent: HDCP On" : "Send Error: HDCP On";
-                    
-                    tvOneStatusMessage.addMessage(sendOK, kTVOneStatusMessageHoldTime);
+                    // Has handler
                 }
                 else if (advancedMenu.selectedItem().payload.command[0] == advancedEDIDPassthrough)
                 {
-                    tvOneEDIDPassthrough = true;
-                    
-                    bool ok = true;
-                    
-                    int32_t slot = tvOneEDIDPassthrough ? EDIDPassthroughSlot : resolutionMenu.selectedItem().payload.command[1];
-                
-                    ok = ok && tvOne.command(kTV1SourceRGB1, kTV1WindowIDA, kTV1FunctionAdjustSourceEDID, slot);
-                    ok = ok && tvOne.command(kTV1SourceRGB2, kTV1WindowIDA, kTV1FunctionAdjustSourceEDID, slot);
-                    
-                    std::string sendOK = ok ? "Sent: EDID. Next:conform?" : "Send Error: EDID";
-                    
-                    tvOneStatusMessage.addMessage(sendOK, kTVOneStatusMessageHoldTime);
+                    // Has handler
                 }
                 else if (advancedMenu.selectedItem().payload.command[0] == advancedEDIDInternal)
                 {
-                    tvOneEDIDPassthrough = false;
-                    
-                    bool ok = true;
-                    
-                    int32_t slot = tvOneEDIDPassthrough ? EDIDPassthroughSlot : resolutionMenu.selectedItem().payload.command[1];
-                
-                    ok = ok && tvOne.command(kTV1SourceRGB1, kTV1WindowIDA, kTV1FunctionAdjustSourceEDID, slot);
-                    ok = ok && tvOne.command(kTV1SourceRGB2, kTV1WindowIDA, kTV1FunctionAdjustSourceEDID, slot);
-                    
-                    std::string sendOK = ok ? "Sent: EDID. Next:conform?" : "Send Error: EDID";
-                    
-                    tvOneStatusMessage.addMessage(sendOK, kTVOneStatusMessageHoldTime);
+                    // Has handler
                 }
                 else if (advancedMenu.selectedItem().payload.command[0] == advancedTestSources)
                 {   
@@ -1416,18 +1586,9 @@
 
         if (mixMode == mixBlend) 
         {
-            if (fadeUp < 1.0)
-            {
-                // we need to set fade level of both windows as there is no way AFAIK to implement fade to black as a further window on top of A&B
-                newFadeAPercent = (1.0-xFade) * fadeUp * 100.0;
-                newFadeBPercent = xFade * fadeUp * 100.0;
-            }
-            else
-            {
-                // we can optimise and just fade A in and out over a fully up B, doubling the rate of fadeA commands sent.
-                newFadeAPercent = (1.0-xFade) * 100.0;
-                newFadeBPercent = 100.0;
-            }
+            // window A occludes B. this is fast as only A changes with xFade.
+            newFadeAPercent = (1.0-xFade) * fadeUp * 100.0;
+            newFadeBPercent = fadeUp * 100.0;
         }
         else if (mixMode == mixAdditive)
         {
@@ -1461,7 +1622,7 @@
             fadeBPercentHasChanged = newFadeBPercent != fadeBPercent;
         
         // If changing mixMode from additive, we want to do this before updating fade values
-        if (updateMixMode && mixMode != mixAdditive) actionMixMode();
+        if (mixMode != mixModeOld && mixModeOld == mixAdditive) actionMixMode();
         
         // We want to send the higher first, otherwise black flashes can happen on taps
         if (fadeAPercentHasChanged && newFadeAPercent >= newFadeBPercent) 
@@ -1499,8 +1660,8 @@
             debug->printf("\r\n"); 
         }
         
-        // If changing mixMode to additive, we want to do this before updating fade values
-        if (updateMixMode && mixMode == mixAdditive) actionMixMode();
+        // If changing mixMode to additive, we want to do this after updating fade values
+        if (mixMode != mixModeOld) actionMixMode();
                 
         //// TASK: Process Network Comms Out, ie. send out any fade updates
         if (commsMode == commsOSC && updateFade)
@@ -1520,10 +1681,13 @@
         
         //// TASK: Housekeeping
         
-        if (tvOne.millisSinceLastCommandSent() > 1000)
+        if (tvOne.millisSinceLastCommandSent() > tvOne.getCommandTimeoutPeriod() + 1000)
         {
             // Lets check on our sources
             handleTVOneSources();
+            
+            // Lets check on our fade levels
+            checkTVOneMixStatus();
         }
     }
 }
\ No newline at end of file