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:
30:873979018850
Parent:
29:95a7efe30527
Child:
31:01845a2347ff
--- a/main.cpp	Sun Oct 28 19:55:35 2012 +0000
+++ b/main.cpp	Mon Oct 29 13:18:52 2012 +0000
@@ -36,9 +36,11 @@
  * v17 - RJ45 - May'12
  * v18 - DMX - July'12
  * v19 - TVOne mixing comms further optimised - August'12
- * vxx - TODO: Keying values load from USB mass storage
+ * 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
  * vxx - TODO: Set keying values from controller, requires a guided, step-through process for user
  * vxx - TODO: Defaults load/save from USB mass storage
+ * vxx - TODO: EDID passthrough override
  * vxx - TODO: EDID upload from USB mass storage
  * vxx - TODO: EDID creation from resolution
  */
@@ -57,7 +59,7 @@
 #include "DMX.h"
 #include "filter.h"
 
-#define kSPKDFSoftwareVersion "beta.19"
+#define kSPKDFSoftwareVersion "21"
 
 // MBED PINS
 
@@ -157,8 +159,8 @@
 
 SPKMenu mixModeMenu;
 SPKMenu mixModeAdditiveMenu; 
-enum { mixSafe, mixBlend, mixAdditive, mixKey }; // additive will require custom TVOne firmware.
-int mixMode = mixBlend;
+enum { mixBlend, mixAdditive, mixKey }; // additive will require custom TVOne firmware.
+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.
 float fadeCurve = 0.0f; // 0 = "X", ie. as per blend, 1 = "/\", ie. as per additive  <-- pictograms!
 
 SPKMenu commsMenu;
@@ -166,7 +168,7 @@
 int commsMode = commsNone;
 
 SPKMenu advancedMenu;
-enum { advancedHDCPOn, advancedHDCPOff, advancedMixModeSafe, advancedConformProcessor, advancedLoadDefaults, advancedSelfTest, advancedSetResolutions };
+enum { advancedHDCPOn, advancedHDCPOff, advancedEDIDPassthrough, advancedEDIDInternal, advancedTestSources, advancedConformProcessor, advancedLoadDefaults, advancedSelfTest, advancedSetResolutions };
 
 // RJ45 Comms
 enum { rj45Ethernet = 0, rj45DMX = 1}; // These values from circuit
@@ -193,6 +195,10 @@
 // Key mode parameters
 int keyerParamsSet = -1; // last keyParams index uploaded to unit 
 
+// TVOne input sources stable flag
+bool tvOneRGB1Stable = true; // init true as this is conformed state of mixer, ie. RGB1 not SIS1
+bool tvOneRGB2Stable = true;
+
 void processOSC(float &xFade, float &fadeUp) {
     string statusMessage;
     
@@ -293,6 +299,52 @@
     return pos;
 }
 
+bool handleTVOneSources()
+{
+    bool ok = true;
+
+    int32_t payload = 0;
+    
+    ok = ok && tvOne.readCommand(kTV1SourceRGB1, kTV1WindowIDA, kTV1FunctionAdjustSourceSourceStable, payload);
+    bool RGB1 = (payload == 1);
+    
+    ok = ok && tvOne.readCommand(kTV1SourceRGB2, kTV1WindowIDA, kTV1FunctionAdjustSourceSourceStable, payload);
+    bool RGB2 = (payload == 1);
+   
+    string tvOneDetectString;
+    if (!ok)
+    {
+        tvOneDetectString = "TVOne: link failed";
+    }
+    else if (!RGB1 || !RGB2) 
+    {
+        if (!RGB1 && !RGB2) tvOneDetectString = "TVOne: no sources";
+        else if (!RGB1)     tvOneDetectString = "TVOne: no right source";
+        else if (!RGB2)     tvOneDetectString = "TVOne: no left source";
+    }
+    else
+    {
+        tvOneDetectString = "TVOne: OK";
+    }
+    
+    if (RGB1 && !tvOneRGB1Stable) tvOne.command(0, kTV1WindowIDB, kTV1FunctionAdjustWindowsWindowSource, kTV1SourceRGB1);
+    if (RGB2 && !tvOneRGB2Stable) tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustWindowsWindowSource, kTV1SourceRGB2);
+    if (!RGB1 && tvOneRGB1Stable) tvOne.command(0, kTV1WindowIDB, kTV1FunctionAdjustWindowsWindowSource, kTV1SourceSIS1);
+    if (!RGB2 && tvOneRGB2Stable) tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustWindowsWindowSource, kTV1SourceSIS2);
+    
+    if (RGB1 != tvOneRGB1Stable || RGB2 != tvOneRGB2Stable)
+    {
+        screen.clearBufferRow(kTVOneStatusLine);
+        screen.textToBuffer(tvOneDetectString, kTVOneStatusLine); 
+        screen.sendBuffer();
+    }
+    
+    tvOneRGB1Stable = RGB1;
+    tvOneRGB2Stable = RGB2;
+        
+    return ok;       
+}   
+
 bool setKeyParamsTo(int index) 
 {   
     // Only spend the time uploading six parameters if we need to
@@ -328,7 +380,7 @@
     // Set Keyer
     if (mixMode < mixKey)
     {
-        if (mixMode == mixBlend || mixMode == mixSafe)
+        if (mixMode == mixBlend)
         {
             // Waiting on TV One...
         }
@@ -368,6 +420,7 @@
     int32_t off = 0;
     
     // Independent output
+    ok = ok && tvOne.command(0, kTV1WindowIDA, kTV1FunctionMode, 2);
     ok = ok && tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustOutputsOutputEnable, on);
     ok = ok && tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustOutputsLockMethod, off);
                     
@@ -395,10 +448,15 @@
     ok = ok && tvOne.command(kTV1SourceRGB1, kTV1WindowIDA, kTV1FunctionAdjustSourceOnSourceLoss, freeze);
     ok = ok && tvOne.command(kTV1SourceRGB2, kTV1WindowIDA, kTV1FunctionAdjustSourceOnSourceLoss, freeze);
     
-    // Finally, autoset to sources?
-    //int32_t start = 1;
-    //ok = ok && tvOne.command(kTV1SourceRGB1, kTV1WindowIDA, kTV1FunctionAdjustSourceAutoSet, start);
-    //ok = ok && tvOne.command(kTV1SourceRGB2, kTV1WindowIDA, kTV1FunctionAdjustSourceAutoSet, start);
+    // Set resolution and fade levels for maximum chance of being seen
+    ok = ok && tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustOutputsOutputResolution, kTV1ResolutionVGA);
+    ok = ok && tvOne.command(kTV1SourceRGB1, kTV1WindowIDA, kTV1FunctionAdjustSourceEDID, 7);
+    ok = ok && tvOne.command(kTV1SourceRGB2, kTV1WindowIDA, kTV1FunctionAdjustSourceEDID, 7);
+    ok = ok && tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustWindowsMaxFadeLevel, 100);
+    ok = ok && tvOne.command(0, kTV1WindowIDB, kTV1FunctionAdjustWindowsMaxFadeLevel, 100);
+    
+    // Save current state for power on
+    ok = ok && tvOne.command(0, kTV1WindowIDA, kTV1FunctionPowerOnPresetStore, 1);
     
     return ok;
 }
@@ -564,22 +622,28 @@
     screen.fontCharacters = characterBytes;
     
     // Splash screen
+    string softwareLine = "SW ";
+    softwareLine += kSPKDFSoftwareVersion;
     screen.imageToBuffer(spkDisplayLogo);
     screen.textToBuffer("SPK:D-Fuser",0);
-    screen.textToBuffer(string("SW ") + kSPKDFSoftwareVersion,1);
+    screen.textToBuffer(softwareLine,1);
     screen.sendBuffer();
     
     // Load saved settings
     bool settingsAreCustom = false;
     settingsAreCustom = settings.load(kSPKDFSettingsFilename);
-    if (settingsAreCustom) {screen.textToBuffer("SPKDF.ini OK", 0); screen.sendBuffer();}
-
+    if (settingsAreCustom) 
+    {
+        softwareLine += "; ini OK";
+        screen.textToBuffer(softwareLine, 1); 
+    }
+    
     // Set menu structure
     mixModeMenu.title = "Mix Mode";
     setMixModeMenuItems();
     
     mixModeAdditiveMenu.title = "Crossfade";
-    mixModeAdditiveMenu.addMenuItem(SPKMenuItem("Twist then click", &mixModeMenu, true));
+    mixModeAdditiveMenu.addMenuItem(SPKMenuItem("[title overridden]", &mixModeMenu, true));
  
     resolutionMenu.title = "Resolution";
     setResolutionMenuItems();
@@ -590,9 +654,11 @@
     advancedMenu.title = "Troubleshooting"; 
     advancedMenu.addMenuItem(SPKMenuItem("HDCP Off", advancedHDCPOff));
     advancedMenu.addMenuItem(SPKMenuItem("HDCP On", advancedHDCPOn));
-    advancedMenu.addMenuItem(SPKMenuItem("Basic mix mode", advancedMixModeSafe));
+    advancedMenu.addMenuItem(SPKMenuItem("Test Processor Sources", advancedTestSources));
     advancedMenu.addMenuItem(SPKMenuItem("Conform Processor", advancedConformProcessor));
-    if (settingsAreCustom) advancedMenu.addMenuItem(SPKMenuItem("Revert to defaults", advancedLoadDefaults));
+    //advancedMenu.addMenuItem(SPKMenuItem("EDID Passthrough", advancedEDIDPassthrough)); // have global setting of passthrough that overrides resolution sets and is saved with conform processor
+    //advancedMenu.addMenuItem(SPKMenuItem("EDID Internal", advancedEDIDInternal));
+    if (settingsAreCustom) advancedMenu.addMenuItem(SPKMenuItem("Revert Controller", advancedLoadDefaults));
     advancedMenu.addMenuItem(SPKMenuItem("Start Self-Test", advancedSelfTest));
     advancedMenu.addMenuItem(SPKMenuItem("Back to Main Menu", &mainMenu));
     
@@ -610,18 +676,13 @@
     fadeBPO.period(0.001);
     
     // Test for TV One connectivity and determine unit type
-    int32_t testConnectionPayload = 0;
-    
+    // TODO: Determine and fall back if not dfuser firmware?
+    // TODO: Use software version to select resolution slots?
+    // TODO: Use product / board type to select TVOne conform type?
     // kTV1FunctionReadSoftwareVersion
     // kTV1FunctionReadProductType
     // kTV1FunctionReadBoardType
-    bool ok = tvOne.readCommand(0, kTV1WindowIDA, kTV1FunctionReadSoftwareVersion, testConnectionPayload);
     
-    string tvOneDetectString = ok ? "TVOne link ok" : "TVOne link failed";
-    
-    // TODO: Use software version to select resolution slots etc?
-    // TODO: Use product / board type to select TVOne conform type?
- 
     // Display menu and framing lines
     screen.horizLineToBuffer(kMenuLine1*pixInPage - 1);
     screen.clearBufferRow(kMenuLine1);
@@ -631,7 +692,13 @@
     screen.horizLineToBuffer(kMenuLine2*pixInPage + pixInPage);
     screen.horizLineToBuffer(kCommsStatusLine*pixInPage - 1);
     screen.clearBufferRow(kTVOneStatusLine);
-    screen.textToBuffer(tvOneDetectString, kTVOneStatusLine);
+    screen.textToBuffer("TVOne: OK", kTVOneStatusLine); // handleTVOneSources may update this
+    
+    // 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.
+    // This will update kTVOneStatusLine if necessary
+    bool ok = handleTVOneSources();
+
     screen.sendBuffer();
 
     //// CONTROLS TEST
@@ -892,11 +959,9 @@
                     screen.clearBufferRow(kTVOneStatusLine);
                     screen.textToBuffer(sendOK, kTVOneStatusLine);
                 }
-                else if (advancedMenu.selectedItem().payload.command[0] == advancedMixModeSafe)
+                else if (advancedMenu.selectedItem().payload.command[0] == advancedTestSources)
                 {
-                    mixMode = mixSafe;
-                    
-                    actionMixMode();
+                    handleTVOneSources();
                 }
                 else if (advancedMenu.selectedItem().payload.command[0] == advancedConformProcessor)
                 {
@@ -1021,15 +1086,20 @@
         int newFadeAPercent = 0;
         int newFadeBPercent = 0;
 
-        if (mixMode == mixSafe) 
+        if (mixMode == mixBlend) 
         {
-            newFadeAPercent = (1.0-xFade) * fadeUp * 100.0;
-            newFadeBPercent = xFade * fadeUp * 100.0;
-        }
-        else if (mixMode == mixBlend) 
-        {
-            newFadeAPercent = (1.0-xFade) * fadeUp * 100.0;
-            newFadeBPercent = fadeUp * 100.0;
+            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;
+            }
         }
         else if (mixMode == mixAdditive)
         {
@@ -1097,5 +1167,12 @@
             debug->printf("xFade = %3f   fadeUp = %3f   fadeA% = %i   fadeB% = %i \r\n", xFade, fadeUp, fadeAPercent, fadeBPercent);
             debug->printf("\r\n"); 
         }
+        
+        // If we're not actively mixing, we can do any housekeeping...
+        if (!updateFade)
+        {
+            // We should check up on any source flagged unstable 
+            if (!tvOneRGB1Stable || !tvOneRGB2Stable) handleTVOneSources();
+        }
     }
-}
+}
\ No newline at end of file