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

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 /* *SPARK D-FUSER
00002  * A project by Toby Harris
00003  *
00004  * 'DJ' controller style RS232 Control for TV-One products
00005  * Good for 1T-C2-750, others will need some extra work
00006  *
00007  * www.tobyz.net/projects/dvi-mixer
00008  */
00009 
00010 /* Copyright (c) 2011 Toby Harris, MIT License
00011  *
00012  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
00013  * and associated documentation files (the "Software"), to deal in the Software without restriction, 
00014  * including without limitation the rights to use, copy, modify, merge, publish, distribute, 
00015  * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 
00016  * furnished to do so, subject to the following conditions:
00017  *
00018  * The above copyright notice and this permission notice shall be included in all copies or 
00019  * substantial portions of the Software.
00020  *
00021  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 
00022  * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
00023  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
00024  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
00025  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00026  */
00027  
00028 /* ROADMAP / HISTORY
00029  * v10 - Port to mBed, keying redux - Apr'11
00030  * v11 - Sign callbacks, code clean-up - Apr'11
00031  * v12 - TVOne header split into two: defines and mbed class. v002 header updates pulled down. Removed sign callbacks, rewrite of debug and signing. - Apr'11
00032  * v13 - Menu system for Resolution + Keying implemented, it writing to debug, it sending TVOne commands - Apr'11
00033  * v14 - Fixes for new PCB - Oct'11
00034  * v15 - TBZ PCB, OLED - Mar'12
00035  * v16 - Comms menu, OSC, ArtNet - April'12
00036  * v17 - RJ45 - May'12
00037  * v18 - DMX - July'12
00038  * v19 - TVOne mixing comms further optimised - August'12
00039  * v20 - Keying values and resolutions load from USB mass storage - September'12
00040  * v21 - Mixing behaviour upgrade: blend-additive as continuum, test cards on startup if no valid source - October'12
00041  * v22 - EDID passthrough override and EDID upload from USB mass storage
00042  * v23 - Set keying values from controller
00043  * v24 - Conform uploads SIS image; now once firmware is loaded controller is all that is required
00044  * v25 - UX work
00045  * v26 - Tweaks: Network in works with hands-on controls, EDID Change message, Fit/Fill
00046  * v27 - Rework Keying UX, having current key saved in processor and loading in presets.
00047  * v28 - Network tweaks. Reads OSC and ArtNet network info from .ini
00048  * v29 - Keying: Can change between Left over Right and Right over Left
00049  * v30 - Matrox Dual/Triplehead - support for analogue edition timings (currently only analog DH2Go XGA)
00050  * vxx - TODO: Writes back to .ini on USB mass storage: keyer updates, comms, hdcp, edid internal/passthrough, ...?
00051  * vxx - TODO: EDID creation from resolution
00052  */
00053  
00054 #include "mbed.h"
00055 
00056 #include "spk_tvone_mbed.h"
00057 #include "spk_utils.h"
00058 #include "spk_mRotaryEncoder.h"
00059 #include "spk_oled_ssd1305.h"
00060 #include "spk_oled_gfx.h"
00061 #include "spk_settings.h"
00062 #include "EthernetNetIf.h"
00063 #include "mbedOSC.h"
00064 #include "DmxArtNet.h"
00065 #include "DMX.h"
00066 #include "filter.h"
00067 
00068 #define kSPKDFSoftwareVersion "30"
00069 
00070 // MBED PINS
00071 
00072 #define kMBED_AIN_XFADE     p20
00073 #define kMBED_AIN_FADEUP    p19
00074 #define kMBED_DIN_TAP_L     p24
00075 #define kMBED_DIN_TAP_R     p23
00076 #define kMBED_ENC_SW        p15
00077 #define kMBED_ENC_A         p16
00078 #define kMBED_ENC_B         p17
00079 
00080 #define kMBED_RS232_TTLTX   p13
00081 #define kMBED_RS232_TTLRX   p14
00082 
00083 #define kMBED_OLED_MOSI     p5
00084 #define kMBED_OLED_SCK      p7
00085 #define kMBED_OLED_CS       p8
00086 #define kMBED_OLED_RES      p9
00087 #define kMBED_OLED_DC       p10
00088 
00089 #define kMBED_DIN_ETHLO_DMXHI       p30
00090 #define kMBED_DOUT_RS485_TXHI_RXLO  p29
00091 #define kMBED_RS485_TTLTX           p28
00092 #define kMBED_RS485_TTLRX           p27
00093 
00094 // DISPLAY
00095 
00096 #define kMenuLine1 3
00097 #define kMenuLine2 4
00098 #define kCommsStatusLine 6
00099 #define kTVOneStatusLine 7
00100 #define kTVOneStatusMessageHoldTime 5
00101 
00102 // 8.3 format filename only, no subdirs
00103 #define kSPKDFSettingsFilename "SPKDF.ini"
00104 
00105 #define kStringBufferLength 30
00106 
00107 //// DEBUG
00108 
00109 // Comment out one or the other...
00110 //Serial *debug = new Serial(USBTX, USBRX); // For debugging via USB serial
00111 Serial *debug = NULL; // For release (no debugging)
00112 
00113 //// SOFT RESET
00114 
00115 extern "C" void mbed_reset();
00116 
00117 //// mBED PIN ASSIGNMENTS
00118 
00119 // Inputs
00120 AnalogIn xFadeAIN(kMBED_AIN_XFADE);    
00121 AnalogIn fadeUpAIN(kMBED_AIN_FADEUP);
00122 DigitalIn tapLeftDIN(kMBED_DIN_TAP_L);
00123 DigitalIn tapRightDIN(kMBED_DIN_TAP_R);
00124 medianFilter xFadeFilter(9);
00125 medianFilter fadeUpFilter(9);
00126 
00127 SPKRotaryEncoder menuEnc(kMBED_ENC_A, kMBED_ENC_B, kMBED_ENC_SW);
00128 
00129 DigitalIn rj45ModeDIN(kMBED_DIN_ETHLO_DMXHI);
00130 
00131 // Outputs
00132 PwmOut fadeAPO(LED1);
00133 PwmOut fadeBPO(LED2);
00134 
00135 DigitalOut dmxDirectionDOUT(kMBED_DOUT_RS485_TXHI_RXLO);
00136 
00137 // SPKTVOne(PinName txPin, PinName rxPin, PinName signWritePin, PinName signErrorPin, Serial *debugSerial)
00138 SPKTVOne tvOne(kMBED_RS232_TTLTX, kMBED_RS232_TTLRX, LED3, LED4, debug);
00139 
00140 // SPKDisplay(PinName mosi, PinName clk, PinName cs, PinName dc, PinName res, Serial *debugSerial = NULL);
00141 SPKDisplay screen(kMBED_OLED_MOSI, kMBED_OLED_SCK, kMBED_OLED_CS, kMBED_OLED_DC, kMBED_OLED_RES, debug);
00142 SPKMessageHold tvOneStatusMessage;
00143 
00144 // Saved Settings
00145 SPKSettings settings;
00146 
00147 // Menu 
00148 SPKMenu *selectedMenu;
00149 SPKMenu mainMenu;
00150 SPKMenu resolutionMenu;
00151 
00152 SPKMenu mixModeMenu;
00153 SPKMenu mixModeAdditiveMenu;
00154 SPKMenu mixModeUpdateKeyMenu; 
00155 enum { mixBlend, mixAdditive, mixKeyLeft, mixKeyRight };
00156 int mixKeyPresetStartIndex = 100; // need this hard coded as mixBlend and mixAdditive are now combined into the same menu item
00157 int mixKeyWindow = kTV1WindowIDA; // The window we've last used to key
00158 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.
00159 int mixModeOld = mixMode;
00160 float fadeCurve = 0.0f; // 0 = "X", ie. as per blend, 1 = "/\", ie. as per additive  <-- pictograms!
00161 
00162 SPKMenu commsMenu;
00163 enum { commsNone, commsOSC, commsArtNet, commsDMXIn, commsDMXOut};
00164 int commsMode = commsNone;
00165 
00166 SPKMenu troubleshootingMenu;
00167 SPKMenu troubleshootingMenuHDCP;
00168 SPKMenu troubleshootingMenuEDID;
00169 SPKMenu troubleshootingMenuAspect;
00170 SPKMenu troubleshootingMenuMatrox;
00171 SPKMenu troubleshootingMenuReset;
00172 
00173 SPKMenu advancedMenu;
00174 enum { advancedConformUploadProcessor, advancedSetResolutions };
00175 
00176 // RJ45 Comms
00177 enum { rj45Ethernet = 0, rj45DMX = 1}; // These values from circuit
00178 int rj45Mode = -1;
00179 EthernetNetIf *ethernet = NULL;
00180 OSCClass *osc = NULL;
00181 OSCMessage sendMessage;
00182 OSCMessage receiveMessage;
00183 DmxArtNet *artNet = NULL;
00184 DMX *dmx = NULL;
00185 
00186 // Fade logic constants
00187 const float xFadeTolerance = 0.05;
00188 const float fadeUpTolerance = 0.05;
00189 
00190 // A&B Fade as resolved percent
00191 int fadeAPercent = 0;
00192 int fadeBPercent = 0;
00193 int oldFadeAPercent = 0;
00194 int oldFadeBPercent = 0;
00195 
00196 // Tap button states
00197 bool tapLeftWasFirstPressed = false;
00198 
00199 // Comms In fade state
00200 float commsXFade = -1;
00201 float commsFadeUp = -1;
00202 float oldXFade = 0;
00203 float oldFadeUp = 0;
00204 bool  commsInActive = false;
00205 
00206 // TVOne input sources stable flag
00207 bool tvOneRGB1Stable = false;
00208 bool tvOneRGB2Stable = false;
00209 
00210 // TVOne behaviour flags
00211 bool tvOneHDCPOn = false;
00212 bool tvOneEDIDPassthrough = false;
00213 const int32_t EDIDPassthroughSlot = 7;
00214 
00215 bool processOSCIn() 
00216 {
00217     bool updateFade = false;
00218 
00219     if (osc->newMessage) 
00220     {
00221         osc->newMessage = false; // fixme!
00222         
00223         string statusMessage;
00224         
00225         if (!strcmp( receiveMessage.getTopAddress() , "dvimxr" )) 
00226         {
00227             statusMessage = "OSC: /dvimxr";
00228             if (!strcmp( receiveMessage.getSubAddress() , "xFade" )) 
00229             {
00230                 if (receiveMessage.getArgNum() == 1)
00231                     if (receiveMessage.getTypeTag(0) == 'f')
00232                     {
00233                         commsXFade = receiveMessage.getArgFloat(0);
00234                         updateFade = true;
00235                         
00236                         char buffer[15];
00237                         snprintf(buffer, 15, "/xFade %1.2f", commsXFade);
00238                         statusMessage += buffer;
00239                     }
00240             }
00241             else if (!strcmp( receiveMessage.getSubAddress() , "fadeUp" ))
00242             {
00243                 if (receiveMessage.getArgNum() == 1)
00244                     if (receiveMessage.getTypeTag(0) == 'f')
00245                     {
00246                         commsFadeUp = receiveMessage.getArgFloat(0);
00247                         updateFade = true;
00248                         
00249                         char buffer[15];
00250                         snprintf(buffer, 15, "/fadeUp %1.2f", commsFadeUp);
00251                         statusMessage += buffer;
00252                     }
00253             }
00254             else if (!strcmp( receiveMessage.getSubAddress() , "xFadeFadeUp" ))
00255             {
00256                 if (receiveMessage.getArgNum() == 2)
00257                     if (receiveMessage.getTypeTag(0) == 'f' && receiveMessage.getTypeTag(1) == 'f')
00258                     {
00259                         commsXFade  = receiveMessage.getArgFloat(0);
00260                         commsFadeUp = receiveMessage.getArgFloat(1);
00261                         updateFade = true;
00262                         
00263                         char buffer[15];
00264                         snprintf(buffer, 15, "/... %1.2f %1.2f", commsXFade, commsFadeUp);
00265                         statusMessage += buffer;
00266                     }
00267             }
00268             else 
00269             {
00270                 statusMessage += receiveMessage.getSubAddress();
00271                 statusMessage += " - Ignoring";
00272             }
00273         }
00274         else
00275         {
00276             statusMessage = "OSC: ";
00277             statusMessage += receiveMessage.getTopAddress();
00278             statusMessage += " - Ignoring";
00279         }
00280         
00281         screen.clearBufferRow(kCommsStatusLine);
00282         screen.textToBuffer(statusMessage, kCommsStatusLine);
00283     
00284         if (debug) debug->printf("%s \r\n", statusMessage.c_str());
00285     }
00286     
00287     return updateFade;
00288 }
00289 
00290 void processOSCOut(const float &xFade, const float &fadeUp) 
00291 {
00292     sendMessage.setAddress("dvimxr", "xFadeFadeUp");
00293     sendMessage.setArgs("ff", &xFade, &fadeUp);
00294     osc->sendOsc(&sendMessage);
00295     
00296     char statusMessageBuffer[kStringBufferLength];
00297     snprintf(statusMessageBuffer, kStringBufferLength, "OSC Out: xF %.2f fUp %.2f", xFade, fadeUp);
00298     screen.clearBufferRow(kCommsStatusLine);
00299     screen.textToBuffer(statusMessageBuffer, kCommsStatusLine);
00300 
00301     if (debug) debug->printf(statusMessageBuffer);
00302 }
00303 
00304 bool processArtNetIn() 
00305 {
00306     if (artNet->Work()) 
00307     {
00308         int xFadeDMX = artNet->DmxIn[settings.artNet.universe][settings.dmx.inChannelXFade];
00309         int fadeUpDMX = artNet->DmxIn[settings.artNet.universe][settings.dmx.inChannelFadeUp];
00310     
00311         commsXFade  = (float)xFadeDMX/255;
00312         commsFadeUp = (float)fadeUpDMX/255;
00313     
00314         char statusMessageBuffer[kStringBufferLength];
00315         snprintf(statusMessageBuffer, kStringBufferLength, "A'Net In: xF%3i fUp %3i", xFadeDMX, fadeUpDMX);
00316         screen.clearBufferRow(kCommsStatusLine);
00317         screen.textToBuffer(statusMessageBuffer, kCommsStatusLine);
00318     
00319         if (debug) debug->printf("ArtNet activity");
00320         
00321         return true;
00322     }
00323     
00324     return false;
00325 }
00326 
00327 void processArtNetOut(const float &xFade, const float &fadeUp) 
00328 {
00329     int xFadeDMX = xFade*255;
00330     int fadeUpDMX = fadeUp*255;
00331     
00332     // Create array for all 512 DMX channels in the universe
00333     char dmxData[512];
00334     
00335     // Don't set every other channel to 0, rather channel values we already have
00336     memcpy(dmxData, artNet->DmxIn[settings.artNet.universe], 512);
00337         
00338     // Set fade channels
00339     dmxData[settings.dmx.outChannelXFade] = xFadeDMX;
00340     dmxData[settings.dmx.outChannelFadeUp] = fadeUpDMX;
00341     
00342     // Send
00343     artNet->Send_ArtDmx(settings.artNet.universe, 0, dmxData, 512);
00344 
00345     char statusMessageBuffer[kStringBufferLength];
00346     snprintf(statusMessageBuffer, kStringBufferLength, "A'Net Out: xF%3i fUp %3i", xFadeDMX, fadeUpDMX);
00347     screen.clearBufferRow(kCommsStatusLine);
00348     screen.textToBuffer(statusMessageBuffer, kCommsStatusLine);
00349 
00350     if (debug) debug->printf(statusMessageBuffer);
00351 }
00352 
00353 bool processDMXIn() 
00354 {
00355     int xFadeDMX = dmx->get(settings.dmx.inChannelXFade);
00356     int fadeUpDMX = dmx->get(settings.dmx.inChannelFadeUp);
00357 
00358     if (((float)xFadeDMX/255 != commsXFade) && ((float)fadeUpDMX/255 != commsFadeUp))
00359     {
00360         commsXFade = (float)xFadeDMX/255;
00361         commsFadeUp = (float)fadeUpDMX/255;
00362     
00363         char statusMessageBuffer[kStringBufferLength];
00364         snprintf(statusMessageBuffer, kStringBufferLength, "DMX In: xF %3i fUp %3i", xFadeDMX, fadeUpDMX);
00365         screen.clearBufferRow(kCommsStatusLine);
00366         screen.textToBuffer(statusMessageBuffer, kCommsStatusLine);
00367     
00368         if (debug) debug->printf(statusMessageBuffer);
00369         
00370         return true;
00371     }
00372     
00373     return false;
00374 }
00375 
00376 void processDMXOut(const float &xFade, const float &fadeUp) 
00377 {
00378     int xFadeDMX = xFade*255;
00379     int fadeUpDMX = fadeUp*255;
00380     
00381     dmx->put(settings.dmx.outChannelXFade, xFadeDMX);
00382     dmx->put(settings.dmx.outChannelFadeUp, fadeUpDMX);
00383     
00384     char statusMessageBuffer[kStringBufferLength];
00385     snprintf(statusMessageBuffer, kStringBufferLength, "DMX Out: xF %3i fUp %3i", xFadeDMX, fadeUpDMX);
00386     screen.clearBufferRow(kCommsStatusLine);
00387     screen.textToBuffer(statusMessageBuffer, kCommsStatusLine);
00388 
00389     if (debug) debug->printf(statusMessageBuffer);
00390 }
00391 
00392 inline float fadeCalc (const float AIN, const float tolerance) 
00393 {
00394     float pos ;
00395     if (AIN < tolerance) pos = 0;
00396     else if (AIN > 1.0 - tolerance) pos = 1;
00397     else pos = (AIN - tolerance) / (1 - 2*tolerance);
00398     if (debug && false) debug->printf("fadeCalc in: %f out: %f \r\n", AIN, pos);
00399     return pos;
00400 }
00401 
00402 bool handleTVOneSources()
00403 {
00404     static int notOKCounter = 0;
00405     
00406     bool ok = true;
00407 
00408     int32_t payload = 0;
00409     
00410     ok = ok && tvOne.readCommand(kTV1SourceRGB1, kTV1WindowIDA, kTV1FunctionAdjustSourceSourceStable, payload);
00411     bool RGB1 = (payload == 1);    
00412     
00413     ok = ok && tvOne.readCommand(kTV1SourceRGB2, kTV1WindowIDA, kTV1FunctionAdjustSourceSourceStable, payload);
00414     bool RGB2 = (payload == 1);
00415    
00416     ok = ok && tvOne.readCommand(0, kTV1WindowIDA, kTV1FunctionAdjustWindowsWindowSource, payload);
00417     int sourceA = payload;
00418    
00419     ok = ok && tvOne.readCommand(0, kTV1WindowIDB, kTV1FunctionAdjustWindowsWindowSource, payload);
00420     int sourceB = payload;
00421    
00422     if (debug) debug->printf("HandleTVOneSources: RGB1: %i, RGB2: %i, sourceA: %#x, sourceB: %#x \r\n", RGB1, RGB2, sourceA, sourceB);
00423     
00424     string tvOneDetectString = "TVOne: ";
00425 
00426     if (ok)
00427     {
00428         string right = RGB1 ? "Live" : (tvOneRGB1Stable ? "Hold" : "Logo");
00429         string left  = RGB2 ? "Live" : (tvOneRGB2Stable ? "Hold" : "Logo");
00430         
00431         tvOneDetectString += "L: ";
00432         tvOneDetectString += left;
00433         tvOneDetectString += " R: ";
00434         tvOneDetectString += right;
00435     }
00436         
00437     tvOneStatusMessage.addMessage(tvOneDetectString);
00438     
00439     // Assign appropriate source depending on whether DVI input is good
00440     // If that assign command completes ok, and the DVI input is good, finally flag the unit has had a live source
00441     // Note any further losses on this input will be handled by the unit holding the last frame, so we don't need to switch back to SIS.
00442     if (ok && !tvOneRGB1Stable)
00443     {
00444         if (RGB1 && (sourceB != kTV1SourceRGB1)) ok = ok && tvOne.command(0, kTV1WindowIDB, kTV1FunctionAdjustWindowsWindowSource, kTV1SourceRGB1);
00445         if (!RGB1 && (sourceB != kTV1SourceSIS2)) ok = ok && tvOne.command(0, kTV1WindowIDB, kTV1FunctionAdjustWindowsWindowSource, kTV1SourceSIS2); // Wierd: Can't set to SIS1 sometimes.
00446         if (ok && RGB1) tvOneRGB1Stable = true;
00447     }
00448     if (ok && !tvOneRGB2Stable)
00449     {
00450         if (RGB2 && (sourceA != kTV1SourceRGB2)) ok = ok && tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustWindowsWindowSource, kTV1SourceRGB2);
00451         if (!RGB2 && (sourceA != kTV1SourceSIS2)) ok = ok && tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustWindowsWindowSource, kTV1SourceSIS2);
00452         if (ok && RGB2) tvOneRGB2Stable = true;
00453     } 
00454     
00455     // It seems there is an occasional RS232 choke around power-on of the processor.
00456     // Hard to reproduce, doubly so when in debug mode, this may fix. 
00457     if (ok)
00458     {
00459         notOKCounter = 0;
00460         tvOne.resetCommandPeriods();
00461     }
00462     else 
00463     {
00464         notOKCounter++;
00465         if (notOKCounter == 5)
00466         {
00467             tvOne.increaseCommandPeriods(200);
00468         }
00469         if (notOKCounter == 6)
00470         {
00471             tvOne.resetCommandPeriods();
00472         }
00473         if (notOKCounter % 15 == 0)
00474         {
00475             tvOneStatusMessage.addMessage("TVOne: Resetting link", 2.0f);
00476             screen.textToBuffer(tvOneStatusMessage.message(), kTVOneStatusLine);
00477             screen.sendBuffer();
00478             
00479             tvOne.increaseCommandPeriods(1500);
00480         }
00481         if (notOKCounter % 15 == 1)
00482         {
00483             tvOne.resetCommandPeriods();
00484         }
00485     }
00486     
00487     return ok;
00488 }   
00489 
00490 void actionMixMode(bool reset = false)
00491 {
00492     if (debug) debug->printf("Changing mix mode \r\n");
00493 
00494     bool ok = true;
00495     string sentOK;
00496     char* sentMSGBuffer = "Blend";
00497 
00498     // Perform unset before set, in case mixMode = mixModeOld
00499 
00500     if (mixModeOld == mixAdditive || reset)
00501     {
00502         // Turn off Additive Mixing on output
00503         if (tvOne.getProcessorType().version == 423)
00504         {
00505             ok = ok && tvOne.command(0, kTV1WindowIDA, 0x298, 0);
00506         }
00507     }
00508     if (mixMode == mixAdditive) 
00509     {   
00510         sentMSGBuffer = "Additive";
00511     
00512         // First set B to what you'd expect for additive; it may be left at 100 if optimised blend mixing was previous mixmode.
00513         ok = ok && tvOne.command(0, kTV1WindowIDB, kTV1FunctionAdjustWindowsMaxFadeLevel, fadeBPercent);
00514         // Then turn on Additive Mixing
00515         if (tvOne.getProcessorType().version == 423)
00516         {
00517             ok = ok && tvOne.command(0, kTV1WindowIDA, 0x298, 1);
00518         }
00519     }
00520 
00521     if (mixModeOld == mixKeyLeft || reset)
00522     {
00523         // Turn off Keyer
00524         tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustKeyerEnable, false);
00525     }
00526     
00527     if (mixMode == mixKeyLeft)
00528     {
00529         sentMSGBuffer = "Key L over R";
00530         mixKeyWindow = kTV1WindowIDA;
00531         
00532         // Turn on Keyer
00533         ok = ok && tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustKeyerEnable, true);
00534     }
00535 
00536     if (mixModeOld == mixKeyRight || reset)
00537     {
00538         // Restore window positions
00539         ok = ok && tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustWindowsLayerPriority, 0);
00540         
00541         // Turn off Keyer
00542         tvOne.command(0, kTV1WindowIDB, kTV1FunctionAdjustKeyerEnable, false); // Checkme: if check for success, returns failure errantly?
00543     }    
00544     if (mixMode == mixKeyRight)
00545     {
00546         sentMSGBuffer = "Key R over L";
00547         mixKeyWindow = kTV1WindowIDB;
00548         
00549         // Turn on Keyer
00550         ok = ok && tvOne.command(0, kTV1WindowIDB, kTV1FunctionAdjustKeyerEnable, true);
00551         
00552         
00553         // Set window B above window A
00554         ok = ok && tvOne.command(0, kTV1WindowIDB, kTV1FunctionAdjustWindowsLayerPriority, 0);
00555     }
00556     
00557     if (ok) 
00558     {
00559         mixModeOld = mixMode;
00560         sentOK = "Sent: ";
00561     }
00562     else 
00563     {
00564         mixMode = mixModeOld;
00565         sentOK = "Send Error: ";
00566     }
00567     
00568     tvOneStatusMessage.addMessage(sentOK + sentMSGBuffer, kTVOneStatusMessageHoldTime);
00569 }
00570 
00571 bool checkTVOneMixStatus()
00572 {
00573     bool ok = true;
00574     
00575     int32_t payload;
00576     
00577     // Mix Mode
00578     bool mixModeNeedsAction = false;
00579     bool additiveOn = false, keyLeftOn = false, keyRightOn = false;
00580     int  windowAPriority = -1;
00581     
00582     if (mixMode == mixBlend)    { additiveOn = false; keyLeftOn = false; keyRightOn = false; windowAPriority = 0;}
00583     if (mixMode == mixAdditive) { additiveOn = true;  keyLeftOn = false; keyRightOn = false; windowAPriority = 0;}
00584     if (mixMode == mixKeyLeft)  { additiveOn = false; keyLeftOn = true;  keyRightOn = false; windowAPriority = 0;}
00585     if (mixMode == mixKeyRight) { additiveOn = false; keyLeftOn = false; keyRightOn = true;  windowAPriority = 1;}
00586     
00587     if (tvOne.getProcessorType().version == 423)
00588     {
00589         payload = -1;
00590         ok = ok && tvOne.readCommand(0, kTV1WindowIDA, 0x298, payload);
00591         if (payload != additiveOn) mixModeNeedsAction = true;
00592     }
00593     
00594     payload = -1;
00595     ok = ok && tvOne.readCommand(0, kTV1WindowIDA, kTV1FunctionAdjustKeyerEnable, payload);
00596     if (payload != keyLeftOn) mixModeNeedsAction = true;
00597 
00598     payload = -1;
00599     ok = ok && tvOne.readCommand(0, kTV1WindowIDB, kTV1FunctionAdjustKeyerEnable, payload);
00600     if (payload != keyRightOn) mixModeNeedsAction = true;
00601     
00602     payload = -1;
00603     ok = ok && tvOne.readCommand(0, kTV1WindowIDA, kTV1FunctionAdjustWindowsLayerPriority, payload);
00604     if (payload != windowAPriority) mixModeNeedsAction = true;
00605 
00606     if (ok && mixModeNeedsAction) 
00607     {
00608         if (debug) debug->printf("Check TVOne Mix Status requiring mixMode action. mixMode: %i \r\n", mixMode);
00609         actionMixMode(true);
00610     }
00611     
00612     // Check Fade
00613     payload = -1;
00614     ok = ok && tvOne.readCommand(0, kTV1WindowIDA, kTV1FunctionAdjustWindowsMaxFadeLevel, payload);
00615     if (ok && (payload != fadeAPercent))
00616     {
00617         if (debug) debug->printf("Check TVOne Mix Status requiring fadeA action");
00618         ok = ok && tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustWindowsMaxFadeLevel, fadeAPercent);
00619     }
00620     
00621     payload = -1;
00622     ok = ok && tvOne.readCommand(0, kTV1WindowIDB, kTV1FunctionAdjustWindowsMaxFadeLevel, payload);
00623     if (ok && (payload != fadeBPercent))
00624     {
00625         if (debug) debug->printf("Check TVOne Mix Status requiring fadeB action");
00626         ok = ok && tvOne.command(0, kTV1WindowIDB, kTV1FunctionAdjustWindowsMaxFadeLevel, fadeBPercent);
00627     }
00628     
00629     return ok;
00630 }
00631 
00632 bool conformProcessor()
00633 {
00634     bool ok;
00635                     
00636     int32_t on = 1;
00637     int32_t off = 0;
00638     
00639     for (int i=0; i < 3; i++)
00640     {
00641         // Independent output
00642         ok =       tvOne.command(0, kTV1WindowIDA, kTV1FunctionMode, 2);
00643         ok = ok && tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustOutputsOutputEnable, on);
00644         ok = ok && tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustOutputsLockMethod, off);
00645                         
00646         // Make sure our windows exist
00647         ok = ok && tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustWindowsEnable, on);
00648         ok = ok && tvOne.command(0, kTV1WindowIDB, kTV1FunctionAdjustWindowsEnable, on);
00649         ok = ok && tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustWindowsLayerPriority, 0);
00650         ok = ok && tvOne.command(0, kTV1WindowIDB, kTV1FunctionAdjustWindowsLayerPriority, 1);
00651         
00652         // Turn off borders on those windows                
00653         ok = ok && tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustBorderEnable, off);
00654         ok = ok && tvOne.command(0, kTV1WindowIDB, kTV1FunctionAdjustBorderEnable, off);
00655             
00656         // Assign inputs to windows, so that left on the crossfader is left on the processor viewed from front
00657         ok = ok && tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustWindowsWindowSource, kTV1SourceRGB2);
00658         ok = ok && tvOne.command(0, kTV1WindowIDB, kTV1FunctionAdjustWindowsWindowSource, kTV1SourceRGB1);
00659         
00660         // Set scaling to fit source within output, maintaining aspect ratio
00661         ok = ok && tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustWindowsZoomLevel, 100);
00662         ok = ok && tvOne.command(0, kTV1WindowIDB, kTV1FunctionAdjustWindowsZoomLevel, 100);
00663         ok = ok && tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustWindowsShrinkEnable, off);
00664         ok = ok && tvOne.command(0, kTV1WindowIDB, kTV1FunctionAdjustWindowsShrinkEnable, off);
00665         ok = ok && tvOne.command(kTV1SourceRGB1, kTV1WindowIDA, kTV1FunctionAdjustSourceAspectCorrect, SPKTVOne::aspectFit);
00666         ok = ok && tvOne.command(kTV1SourceRGB2, kTV1WindowIDA, kTV1FunctionAdjustSourceAspectCorrect, SPKTVOne::aspectFit);
00667         ok = ok && tvOne.command(kTV1SourceSIS1, kTV1WindowIDA, kTV1FunctionAdjustSourceTestCard, 1);
00668         ok = ok && tvOne.command(kTV1SourceSIS1, kTV1WindowIDA, kTV1FunctionAdjustSourceAspectCorrect, SPKTVOne::aspect1to1);
00669         ok = ok && tvOne.command(kTV1SourceSIS2, kTV1WindowIDA, kTV1FunctionAdjustSourceTestCard, 1);
00670         ok = ok && tvOne.command(kTV1SourceSIS2, kTV1WindowIDA, kTV1FunctionAdjustSourceAspectCorrect, SPKTVOne::aspect1to1);
00671         
00672         // On source loss, hold on the last frame received.
00673         int32_t freeze = 1;
00674         ok = ok && tvOne.command(kTV1SourceRGB1, kTV1WindowIDA, kTV1FunctionAdjustSourceOnSourceLoss, freeze);
00675         ok = ok && tvOne.command(kTV1SourceRGB2, kTV1WindowIDA, kTV1FunctionAdjustSourceOnSourceLoss, freeze);
00676         
00677         // Set resolution and fade levels for maximum chance of being seen
00678         ok = ok && tvOne.setResolution(kTV1ResolutionVGA, 5);
00679         ok = ok && tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustWindowsMaxFadeLevel, 50);
00680         ok = ok && tvOne.command(0, kTV1WindowIDB, kTV1FunctionAdjustWindowsMaxFadeLevel, 100);
00681         
00682         // Set evil, evil HDCP off
00683         ok = ok && tvOne.setHDCPOn(false);
00684     
00685         if (ok) break;
00686         else tvOne.increaseCommandPeriods(500);
00687     }
00688     
00689     if (ok)
00690     {
00691         // Save current state in preset one
00692         tvOne.command(0, kTV1WindowIDA, kTV1FunctionPreset, 1);          // Set Preset 1
00693         tvOne.command(0, kTV1WindowIDA, kTV1FunctionPresetStore, 1);     // Store
00694         
00695         // Save current state for power on
00696         tvOne.command(0, kTV1WindowIDA, kTV1FunctionPowerOnPresetStore, 1);
00697     }
00698     
00699     tvOne.resetCommandPeriods();
00700     
00701     return ok;
00702 }
00703 
00704 bool uploadToProcessor()
00705 {
00706     bool ok = true;
00707 
00708     LocalFileSystem local("local");
00709     FILE *file;
00710     
00711     // Upload EDIDs
00712     if (tvOne.getProcessorType().version < 415)
00713     {
00714         if (debug) debug->printf("Skipping EDID upload as unsupported on detected TV One firmware\r\n");
00715     }
00716     else
00717     {
00718         // Upload Matrox EDID to mem4 (ie. index 3). Use this EDID slot when setting Matrox resolutions.
00719         file = fopen("/local/matroxe.did", "r"); // 8.3, avoid .bin as mbed executable extension
00720         if (file)
00721         {
00722             ok = ok && tvOne.uploadEDID(file, 3);   
00723             fclose(file);
00724         }
00725         else
00726         {
00727             if (debug) debug->printf("Could not open Matrox EDID file 'matroxe.did'\r\n");
00728         }
00729         
00730         // Upload Datapath X4 EDID to mem3 (ie. index 2). Use this EDID slot when setting X4 resolutions.
00731         file = fopen("/local/x4e.did", "r"); // 8.3, avoid .bin as mbed executable extension
00732         if (file)
00733         {
00734             ok = ok && tvOne.uploadEDID(file, 2);   
00735             fclose(file);
00736         }
00737         else
00738         {
00739             if (debug) debug->printf("Could not open X4 EDID file 'x4e.did'\r\n");
00740         }
00741     }
00742 
00743     // Upload Logo to SIS2. Use this (minimal) image when no sources are connected.
00744     {
00745         file = fopen("/local/spark.dat", "r"); // 8.3, avoid .bin as mbed executable extension
00746         if (file)
00747         {
00748             ok = ok && tvOne.uploadImage(file, 0);   
00749             fclose(file);
00750         }
00751         else
00752         {
00753             if (debug) debug->printf("Could not open image file 'spark.dat'");
00754         }
00755     }    
00756     
00757     return ok;
00758 }
00759 
00760 void setResolutionMenuItems()
00761 {
00762     resolutionMenu.clearMenuItems();
00763     for (int i=0; i < settings.resolutionsCount(); i++)
00764     {
00765         resolutionMenu.addMenuItem(SPKMenuItem(settings.resolutionName(i), settings.resolutionIndex(i), settings.resolutionEDIDIndex(i)));
00766     }
00767     resolutionMenu.addMenuItem(SPKMenuItem("Back to Main Menu", &mainMenu));
00768 }
00769 
00770 void setMixModeMenuItems()
00771 {
00772     mixModeMenu.clearMenuItems();
00773     
00774     if (tvOne.getProcessorType().version == 423 || tvOne.getProcessorType().version == -1)
00775     {
00776         mixModeAdditiveMenu.title = "Crossfade";
00777         mixModeMenu.addMenuItem(SPKMenuItem(mixModeAdditiveMenu.title, &mixModeAdditiveMenu));
00778     }
00779     else
00780     {
00781         mixModeMenu.addMenuItem(SPKMenuItem("Blend", mixBlend));
00782     }
00783     
00784     mixModeMenu.addMenuItem(SPKMenuItem("Key: Left over Right", mixKeyLeft));
00785     mixModeMenu.addMenuItem(SPKMenuItem("Key: Right over Left", mixKeyRight));
00786     mixModeMenu.addMenuItem(SPKMenuItem("Key: Tweak key values", &mixModeUpdateKeyMenu));
00787     
00788     // Load in presets from settings. Index 0 is the "live" key read from TVOne, so we ignore here.
00789     if (settings.keyerSetCount() > 1)
00790         for (int i=1; i < settings.keyerSetCount(); i++)
00791         {
00792             mixModeMenu.addMenuItem(SPKMenuItem("Key Preset: " + settings.keyerParamName(i), mixKeyPresetStartIndex + i));
00793         }
00794     
00795     mixModeMenu.addMenuItem(SPKMenuItem("Back to Main Menu", &mainMenu));
00796 }
00797 
00798 void setCommsMenuItems()
00799 {
00800     if (rj45Mode == rj45Ethernet) 
00801     {
00802         commsMenu.title = "Network Mode [Ethernet]";
00803         commsMenu.clearMenuItems();
00804         switch (commsMode) // Cannot switch between OSC and Artnet once one is selected (crash in EthernetIf deconstructor?), so this.
00805         {
00806             case commsNone:        
00807                 commsMenu.addMenuItem(SPKMenuItem("None", commsNone));
00808                 commsMenu.addMenuItem(SPKMenuItem("OSC", commsOSC));
00809                 commsMenu.addMenuItem(SPKMenuItem("ArtNet", commsArtNet));
00810                 break;
00811             case commsOSC:
00812                 commsMenu.addMenuItem(SPKMenuItem("OSC", commsOSC));
00813                 break;
00814             case commsArtNet:
00815                 commsMenu.addMenuItem(SPKMenuItem("ArtNet", commsArtNet));
00816                 break;
00817         }
00818         commsMenu.addMenuItem(SPKMenuItem("Back to Main Menu", &mainMenu));
00819         commsMenu = 0;
00820     }
00821     else if (rj45Mode == rj45DMX) 
00822     {
00823         commsMenu.title = "Network Mode [DMX]";
00824         commsMenu.clearMenuItems();
00825         commsMenu.addMenuItem(SPKMenuItem("None", commsNone));
00826         commsMenu.addMenuItem(SPKMenuItem("DMX In", commsDMXIn));
00827         commsMenu.addMenuItem(SPKMenuItem("DMX Out", commsDMXOut));
00828         commsMenu.addMenuItem(SPKMenuItem("Back to Main Menu", &mainMenu));
00829         commsMenu = 0;
00830     }
00831 }
00832 
00833 void mixModeAdditiveMenuHandler(int change, bool action)
00834 {
00835     fadeCurve += change * 0.05f;
00836     if (fadeCurve > 1.0f) fadeCurve = 1.0f;
00837     if (fadeCurve < 0.0f) fadeCurve = 0.0f;
00838     
00839     mixMode = (fadeCurve > 0.001f) ? mixAdditive: mixBlend;
00840 
00841     screen.clearBufferRow(kMenuLine2);
00842     screen.textToBuffer("Blend [ ----- ] Add", kMenuLine2);
00843     screen.characterToBuffer('X', 38 + fadeCurve*20.0f, kMenuLine2);
00844     
00845     if (debug) debug->printf("Fade curve changed by %i to %f \r\n", change, fadeCurve);
00846     
00847     if (action)
00848     {
00849         selectedMenu = &mixModeMenu;
00850         
00851         screen.clearBufferRow(kMenuLine1);
00852         screen.clearBufferRow(kMenuLine2);
00853         screen.textToBuffer(selectedMenu->title, kMenuLine1);
00854         screen.textToBuffer(selectedMenu->selectedString(), kMenuLine2);
00855     }
00856 }
00857 
00858 void troubleshootingMenuHDCPHandler(int change, bool action)
00859 {
00860     static int currentHDCP;
00861     static int state = 0;
00862 
00863     if (change == 0 && !action)
00864     {
00865         // We check the control not the status, as status depends on connection etc.
00866         
00867         int32_t payloadOutput = -1;
00868         tvOne.readCommand(0, kTV1WindowIDA, kTV1FunctionAdjustOutputsHDCPRequired, payloadOutput);
00869         
00870         int32_t payload1 = -1;
00871         tvOne.readCommand(kTV1SourceRGB1, kTV1WindowIDA, kTV1FunctionAdjustSourceHDCPAdvertize, payload1);
00872         
00873         int32_t payload2 = -1;
00874         tvOne.readCommand(kTV1SourceRGB2, kTV1WindowIDA, kTV1FunctionAdjustSourceHDCPAdvertize, payload2);
00875    
00876         if ((payloadOutput == payload1) && (payload1 == payload2) && (payload2 == 0)) 
00877         {
00878             currentHDCP = 0; // Change to on
00879         }
00880         else if ((payloadOutput == payload1) && (payload1 == payload2) && (payload2 == 1))
00881         {
00882             currentHDCP = 1; // Change to off
00883         }
00884         else
00885         {
00886             currentHDCP = -1; // Change to off
00887         }
00888         
00889         if (debug) debug->printf("HDCP detected O: %i 1: %i 2: %i", payloadOutput, payload1, payload2);
00890     }
00891     
00892     state += change;
00893     if (state > 1) state = 1;
00894     if (state < 0) state = 0;
00895     
00896     char paramLine[kStringBufferLength];
00897     screen.clearBufferRow(kMenuLine2);
00898     
00899     const char* current = currentHDCP == -1 ? "Mixed" : ( currentHDCP == 1 ? "On" : "Off");
00900     
00901     if (state == 0) snprintf(paramLine, kStringBufferLength, "%s. Set: [%s/      ]?", current, currentHDCP == 0 ? "On " : "Off" );
00902     else           snprintf(paramLine, kStringBufferLength, "%s. Set: [   /Cancel]?", current);
00903     screen.textToBuffer(paramLine, kMenuLine2);
00904 
00905     if (action)
00906     {
00907         if (state == 0)
00908         {
00909             screen.clearBufferRow(kTVOneStatusLine);
00910             screen.textToBuffer("Setting HDCP...", kTVOneStatusLine);
00911             screen.sendBuffer();
00912         
00913             // Do the action
00914             bool ok = tvOne.setHDCPOn(currentHDCP == 0);
00915             
00916             if (ok) tvOne.command(0, kTV1WindowIDA, kTV1FunctionPowerOnPresetStore, 1);
00917             
00918             std::string sendOK = ok ? "Sent: HDCP " : "Send Error: HDCP ";
00919             sendOK += currentHDCP == 0 ? "On" : "Off";
00920             
00921             tvOneStatusMessage.addMessage(sendOK, kTVOneStatusMessageHoldTime);
00922         }
00923         
00924         // Get back to menu
00925         selectedMenu = &troubleshootingMenu;
00926         
00927         screen.clearBufferRow(kMenuLine1);
00928         screen.clearBufferRow(kMenuLine2);
00929         screen.textToBuffer(selectedMenu->title, kMenuLine1);
00930         screen.textToBuffer(selectedMenu->selectedString(), kMenuLine2);
00931     }
00932 }
00933 
00934 void troubleshootingMenuEDIDHandler(int change, bool action)
00935 {
00936     static int currentEDIDPassthrough;
00937     static int currentEDID;
00938     static int state = 0;
00939     
00940     if (change == 0 && !action)
00941     {
00942         currentEDID = tvOne.getEDID();
00943         
00944         if (currentEDID == -1) currentEDIDPassthrough = -1;
00945         else currentEDIDPassthrough = (currentEDID == EDIDPassthroughSlot) ? 1 : 0;
00946     }
00947     
00948     state += change;
00949     if (state > 1) state = 1;
00950     if (state < 0) state = 0;
00951         
00952     char paramLine[kStringBufferLength];
00953     screen.clearBufferRow(kMenuLine2);
00954     
00955     const char* current = currentEDIDPassthrough == -1 ? "Mixed" : ( currentEDIDPassthrough == 1 ? "Thru" : "Internal");
00956     
00957     if (state == 0) snprintf(paramLine, kStringBufferLength, "%s. Set: [%s/      ]?", current, currentEDIDPassthrough == 0 ? "Thru" : "Int");
00958     else           snprintf(paramLine, kStringBufferLength, "%s. Set: [   /Cancel]?", current);      
00959     screen.textToBuffer(paramLine, kMenuLine2);
00960     
00961     if (action)
00962     {
00963         if (state == 0)
00964         {
00965             screen.clearBufferRow(kTVOneStatusLine);
00966             screen.textToBuffer("Setting EDID...", kTVOneStatusLine);
00967             screen.sendBuffer();
00968         
00969             // Do the action
00970             tvOneEDIDPassthrough = currentEDIDPassthrough == 0;
00971             
00972             bool ok = true;
00973             std::string message;
00974             
00975             int newEDID = tvOneEDIDPassthrough ? EDIDPassthroughSlot : resolutionMenu.selectedItem().payload.command[1];
00976             
00977             if (newEDID != currentEDID)
00978             {
00979                 ok = ok && tvOne.command(kTV1SourceRGB1, kTV1WindowIDA, kTV1FunctionAdjustSourceEDID, newEDID);
00980                 ok = ok && tvOne.command(kTV1SourceRGB2, kTV1WindowIDA, kTV1FunctionAdjustSourceEDID, newEDID);
00981                 if (ok) message = "Sent: EDID";
00982                 else    message = "Send Error: EDID";
00983             }
00984             else        message = "EDID already set";
00985         
00986             if (ok) tvOne.command(0, kTV1WindowIDA, kTV1FunctionPowerOnPresetStore, 1);
00987             
00988             tvOneStatusMessage.addMessage(message, kTVOneStatusMessageHoldTime);
00989             
00990             // This is WHACK. Can't believe it's both needed and officially in the 1T-C2-750 manual
00991             if ((newEDID != currentEDID) && ok) tvOneStatusMessage.addMessage("EDID: Processor Off+On?", kTVOneStatusMessageHoldTime);
00992         }
00993             
00994         // Get back to menu
00995         selectedMenu = &troubleshootingMenu;
00996         
00997         screen.clearBufferRow(kMenuLine1);
00998         screen.clearBufferRow(kMenuLine2);
00999         screen.textToBuffer(selectedMenu->title, kMenuLine1);
01000         screen.textToBuffer(selectedMenu->selectedString(), kMenuLine2);
01001     }
01002 }
01003 
01004 void troubleshootingMenuAspectHandler(int change, bool action)
01005 {
01006     static int state = 0;
01007 
01008     if (change == 0 && !action)
01009     {
01010         switch (tvOne.getAspect())
01011         {
01012             case SPKTVOne::aspectFit : state = 0; break;
01013             case SPKTVOne::aspectHFill : state = 1; break;
01014             case SPKTVOne::aspectVFill : state = 1; break;
01015             case SPKTVOne::aspectSPKFill : state = 1; break;
01016             case SPKTVOne::aspect1to1 : state = 2; break;
01017         }
01018     }
01019     
01020     state += change;
01021     if (state > 3) state = 3;
01022     if (state < 0) state = 0;
01023     
01024     screen.clearBufferRow(kMenuLine2);
01025     switch (state) 
01026     {
01027         case 0: screen.textToBuffer("Set: [Fit/    /   /      ]", kMenuLine2); break;
01028         case 1: screen.textToBuffer("Set: [   /Fill/   /      ]", kMenuLine2); break;
01029         case 2: screen.textToBuffer("Set: [   /    /1:1/      ]", kMenuLine2); break;
01030         case 3: screen.textToBuffer("Set: [   /    /   /Cancel]", kMenuLine2); break;
01031     }
01032       
01033     if (action)
01034     {
01035         if (state != 3)
01036         {
01037             screen.clearBufferRow(kTVOneStatusLine);
01038             screen.textToBuffer("Setting Aspect...", kTVOneStatusLine);
01039             screen.sendBuffer();
01040         
01041             // Do the action
01042             bool ok = false;
01043             switch (state) 
01044             {
01045                 case 0: ok = tvOne.setAspect(SPKTVOne::aspectFit); break;
01046                 case 1: ok = tvOne.setAspect(SPKTVOne::aspectSPKFill); break;
01047                 case 2: ok = tvOne.setAspect(SPKTVOne::aspect1to1); break;
01048             }
01049             if (ok) tvOne.command(0, kTV1WindowIDA, kTV1FunctionPowerOnPresetStore, 1);
01050             
01051             std::string sendOK = ok ? "Sent: " : "Send Error: ";
01052             switch (state) 
01053             {
01054                 case 0: sendOK += "Aspect Fit "; break;
01055                 case 1: sendOK += "Aspect Fill"; break;
01056                 case 2: sendOK += "Aspect 1:1"; break;
01057             }
01058             tvOneStatusMessage.addMessage(sendOK, kTVOneStatusMessageHoldTime);
01059         }
01060             
01061         // Get back to menu
01062         selectedMenu = &troubleshootingMenu;
01063         
01064         screen.clearBufferRow(kMenuLine1);
01065         screen.clearBufferRow(kMenuLine2);
01066         screen.textToBuffer(selectedMenu->title, kMenuLine1);
01067         screen.textToBuffer(selectedMenu->selectedString(), kMenuLine2);
01068     }
01069 }
01070 
01071 void troubleshootingMenuMatroxHandler(int change, bool action)
01072 {
01073     static int state = 0;
01074 
01075     if (change == 0 && !action)
01076     {
01077         // TODO: Find processor state. Test for a known timing value?
01078     }
01079     
01080     state += change;
01081     if (state > 2) state = 2;
01082     if (state < 0) state = 0;
01083     
01084     screen.clearBufferRow(kMenuLine2);
01085     switch (state) 
01086     {
01087         case 0: screen.textToBuffer("Set: [Digital/      /      ]", kMenuLine2); break;
01088         case 1: screen.textToBuffer("Set: [       /Analog/      ]", kMenuLine2); break;
01089         case 2: screen.textToBuffer("Set: [       /      /Cancel]", kMenuLine2); break;
01090     }
01091       
01092     if (action)
01093     {
01094         if (state != 2)
01095         {
01096             screen.clearBufferRow(kTVOneStatusLine);
01097             screen.textToBuffer("Configuring...", kTVOneStatusLine);
01098             screen.sendBuffer();
01099         
01100             // Do the action
01101             bool ok = false;
01102             switch (state) 
01103             {
01104                 case 0: ok = tvOne.setMatroxResolutions(true); break;
01105                 case 1: ok = tvOne.setMatroxResolutions(false); break;
01106             }
01107             
01108             std::string sendOK = ok ? "Sent: " : "Send Error: ";
01109             switch (state) 
01110             {
01111                 case 0: sendOK += "Digital Timings"; break;
01112                 case 1: sendOK += "Analogue Timings"; break;
01113             }
01114             tvOneStatusMessage.addMessage(sendOK, kTVOneStatusMessageHoldTime);
01115         }
01116             
01117         // Get back to menu
01118         selectedMenu = &troubleshootingMenu;
01119         
01120         screen.clearBufferRow(kMenuLine1);
01121         screen.clearBufferRow(kMenuLine2);
01122         screen.textToBuffer(selectedMenu->title, kMenuLine1);
01123         screen.textToBuffer(selectedMenu->selectedString(), kMenuLine2);
01124     }
01125 }
01126 
01127 void mixModeUpdateKeyMenuHandler(int menuChange, bool action)
01128 {
01129     static int actionCount = 0;
01130     static int state = 0;
01131     
01132     if (mixMode == mixKeyLeft)  mixKeyWindow = kTV1WindowIDA;
01133     if (mixMode == mixKeyRight) mixKeyWindow = kTV1WindowIDB;
01134     
01135     if (action) actionCount++;
01136     
01137     if (actionCount == 0) 
01138     {
01139         settings.editingKeyerSetIndex = 0;
01140         
01141         bool ok;
01142         int minY, maxY, minU, maxU, minV, maxV;
01143     
01144         ok =       tvOne.readCommand(0, mixKeyWindow, kTV1FunctionAdjustKeyerMinY, minY); 
01145         ok = ok && tvOne.readCommand(0, mixKeyWindow, kTV1FunctionAdjustKeyerMaxY, maxY); 
01146         ok = ok && tvOne.readCommand(0, mixKeyWindow, kTV1FunctionAdjustKeyerMinU, minU); 
01147         ok = ok && tvOne.readCommand(0, mixKeyWindow, kTV1FunctionAdjustKeyerMaxU, maxU); 
01148         ok = ok && tvOne.readCommand(0, mixKeyWindow, kTV1FunctionAdjustKeyerMinV, minV); 
01149         ok = ok && tvOne.readCommand(0, mixKeyWindow, kTV1FunctionAdjustKeyerMaxV, maxV);
01150         
01151         if (ok)
01152         {
01153             settings.setEditingKeyerSetValue(SPKSettings::minY, minY);
01154             settings.setEditingKeyerSetValue(SPKSettings::maxY, maxY);
01155             settings.setEditingKeyerSetValue(SPKSettings::minU, minU);
01156             settings.setEditingKeyerSetValue(SPKSettings::maxU, maxU);
01157             settings.setEditingKeyerSetValue(SPKSettings::minV, minV);
01158             settings.setEditingKeyerSetValue(SPKSettings::maxV, maxV);
01159         }
01160         else
01161         {
01162         printf("failed to load\r\n");
01163             tvOneStatusMessage.addMessage("Failed to read key values", kTVOneStatusMessageHoldTime);
01164         }
01165         
01166         actionCount = 1;
01167     }
01168     if (actionCount == 1)
01169     {
01170         state += menuChange;
01171 
01172         screen.clearBufferRow(kMenuLine1);
01173         screen.clearBufferRow(kMenuLine2);
01174         screen.textToBuffer("Tweak or start over?", kMenuLine1);
01175         
01176         char paramLine[kStringBufferLength];
01177         
01178         if (state % 2)  snprintf(paramLine, kStringBufferLength, "[%3i/%3i][%3i/%3i][%3i/%3i]", 0, 
01179                                                                                                 255, 
01180                                                                                                 0, 
01181                                                                                                 255,
01182                                                                                                 0, 
01183                                                                                                 255);
01184         else            snprintf(paramLine, kStringBufferLength, "[%3i/%3i][%3i/%3i][%3i/%3i]", settings.editingKeyerSetValue(SPKSettings::minY), 
01185                                                                                                 settings.editingKeyerSetValue(SPKSettings::maxY), 
01186                                                                                                 settings.editingKeyerSetValue(SPKSettings::minU), 
01187                                                                                                 settings.editingKeyerSetValue(SPKSettings::maxU),
01188                                                                                                 settings.editingKeyerSetValue(SPKSettings::minV), 
01189                                                                                                 settings.editingKeyerSetValue(SPKSettings::maxV));
01190         screen.textToBuffer(paramLine, kMenuLine2); 
01191     }
01192     if (actionCount == 2)
01193     {
01194         if (state % 2)
01195         {
01196             settings.setEditingKeyerSetValue(SPKSettings::minY,0);
01197             settings.setEditingKeyerSetValue(SPKSettings::maxY,255);
01198             settings.setEditingKeyerSetValue(SPKSettings::minU,0);
01199             settings.setEditingKeyerSetValue(SPKSettings::maxU,255);
01200             settings.setEditingKeyerSetValue(SPKSettings::minV,0);
01201             settings.setEditingKeyerSetValue(SPKSettings::maxV,255);
01202             tvOne.command(0, mixKeyWindow, kTV1FunctionAdjustKeyerMinY, 0);
01203             tvOne.command(0, mixKeyWindow, kTV1FunctionAdjustKeyerMaxY, 255);
01204             tvOne.command(0, mixKeyWindow, kTV1FunctionAdjustKeyerMinU, 0);
01205             tvOne.command(0, mixKeyWindow, kTV1FunctionAdjustKeyerMaxU, 255);
01206             tvOne.command(0, mixKeyWindow, kTV1FunctionAdjustKeyerMinV, 0);
01207             tvOne.command(0, mixKeyWindow, kTV1FunctionAdjustKeyerMaxV, 255);
01208         }
01209         
01210         actionCount = 3;
01211     }
01212     if (actionCount == 3)
01213     {
01214         int value = settings.editingKeyerSetValue(SPKSettings::maxY);
01215         value += menuChange;
01216         if (value < 0) value = 0;
01217         if (value > 255) value = 255;
01218         settings.setEditingKeyerSetValue(SPKSettings::maxY, value);
01219 
01220         screen.clearBufferRow(kMenuLine1);
01221         screen.clearBufferRow(kMenuLine2);
01222         screen.textToBuffer("Down until unmasked", kMenuLine1);
01223         
01224         char paramLine[kStringBufferLength];
01225         snprintf(paramLine, kStringBufferLength, "[   /%3i][   /   ][   /   ]", value);
01226         screen.textToBuffer(paramLine, kMenuLine2);
01227         
01228         tvOne.command(0, mixKeyWindow, kTV1FunctionAdjustKeyerMaxY, value);   
01229     }
01230     else if (actionCount == 4)
01231     {
01232         int value = settings.editingKeyerSetValue(SPKSettings::minY);
01233         value += menuChange;
01234         if (value < 0) value = 0;
01235         if (value > settings.editingKeyerSetValue(SPKSettings::maxY)) value = settings.editingKeyerSetValue(SPKSettings::maxY);
01236         settings.setEditingKeyerSetValue(SPKSettings::minY, value);
01237 
01238         screen.clearBufferRow(kMenuLine1);
01239         screen.clearBufferRow(kMenuLine2);
01240         screen.textToBuffer("Up until unmasked", kMenuLine1);
01241         
01242         char paramLine[kStringBufferLength];
01243         snprintf(paramLine, kStringBufferLength, "[%3i/%3i][   /   ][   /   ]", value,
01244                                                                                 settings.editingKeyerSetValue(SPKSettings::maxY));
01245         screen.textToBuffer(paramLine, kMenuLine2);
01246         
01247         tvOne.command(0, mixKeyWindow, kTV1FunctionAdjustKeyerMinY, value); 
01248     }
01249     else if (actionCount == 5)
01250     {
01251         int value = settings.editingKeyerSetValue(SPKSettings::maxU);
01252         value += menuChange;
01253         if (value < 0) value = 0;
01254         if (value > 255) value = 255;
01255         settings.setEditingKeyerSetValue(SPKSettings::maxU, value);
01256 
01257         screen.clearBufferRow(kMenuLine1);
01258         screen.clearBufferRow(kMenuLine2);
01259         screen.textToBuffer("Down until unmasked", kMenuLine1);
01260         
01261         char paramLine[kStringBufferLength];
01262         snprintf(paramLine, kStringBufferLength, "[%3i/%3i][   /%3i][   /   ]", settings.editingKeyerSetValue(SPKSettings::minY), 
01263                                                                                 settings.editingKeyerSetValue(SPKSettings::maxY),  
01264                                                                                 value);
01265         screen.textToBuffer(paramLine, kMenuLine2);
01266         
01267         tvOne.command(0, mixKeyWindow, kTV1FunctionAdjustKeyerMaxU, value); 
01268     }
01269     else if (actionCount == 6)
01270     {
01271         int value = settings.editingKeyerSetValue(SPKSettings::minU);
01272         value += menuChange;
01273         if (value < 0) value = 0;
01274         if (value > settings.editingKeyerSetValue(SPKSettings::maxU)) value = settings.editingKeyerSetValue(SPKSettings::maxU);
01275         settings.setEditingKeyerSetValue(SPKSettings::minU, value);
01276 
01277         screen.clearBufferRow(kMenuLine1);
01278         screen.clearBufferRow(kMenuLine2);
01279         screen.textToBuffer("Up until unmasked", kMenuLine1);
01280         
01281         char paramLine[kStringBufferLength];
01282         snprintf(paramLine, kStringBufferLength, "[%3i/%3i][%3i/%3i][   /   ]", settings.editingKeyerSetValue(SPKSettings::minY), 
01283                                                                                 settings.editingKeyerSetValue(SPKSettings::maxY), 
01284                                                                                 value,
01285                                                                                 settings.editingKeyerSetValue(SPKSettings::maxU));
01286         screen.textToBuffer(paramLine, kMenuLine2);
01287         
01288         tvOne.command(0, mixKeyWindow, kTV1FunctionAdjustKeyerMinU, value);
01289     }
01290     else if (actionCount == 7)
01291     {
01292         int value = settings.editingKeyerSetValue(SPKSettings::maxV);
01293         value += menuChange;
01294         if (value < 0) value = 0;
01295         if (value > 255) value = 255;
01296         settings.setEditingKeyerSetValue(SPKSettings::maxV, value);
01297 
01298         screen.clearBufferRow(kMenuLine1);
01299         screen.clearBufferRow(kMenuLine2);
01300         screen.textToBuffer("Down until unmasked", kMenuLine1);
01301         
01302         char paramLine[kStringBufferLength];
01303         snprintf(paramLine, kStringBufferLength, "[%3i/%3i][%3i/%3i][   /%3i]", settings.editingKeyerSetValue(SPKSettings::minY), 
01304                                                                                 settings.editingKeyerSetValue(SPKSettings::maxY), 
01305                                                                                 settings.editingKeyerSetValue(SPKSettings::minU), 
01306                                                                                 settings.editingKeyerSetValue(SPKSettings::maxU),  
01307                                                                                 value);
01308         screen.textToBuffer(paramLine, kMenuLine2);
01309         
01310         tvOne.command(0, mixKeyWindow, kTV1FunctionAdjustKeyerMaxV, value);    
01311     }
01312     else if (actionCount == 8)
01313     {
01314         int value = settings.editingKeyerSetValue(SPKSettings::minV);
01315         value += menuChange;
01316         if (value < 0) value = 0;
01317         if (value > settings.editingKeyerSetValue(SPKSettings::maxV)) value = settings.editingKeyerSetValue(SPKSettings::maxV);
01318         settings.setEditingKeyerSetValue(SPKSettings::minV, value);
01319 
01320         screen.clearBufferRow(kMenuLine1);
01321         screen.clearBufferRow(kMenuLine2);
01322         screen.textToBuffer("Up until unmasked", kMenuLine1);
01323         
01324         char paramLine[kStringBufferLength];
01325         snprintf(paramLine, kStringBufferLength, "[%3i/%3i][%3i/%3i][%3i/%3i]", settings.editingKeyerSetValue(SPKSettings::minY), 
01326                                                                                 settings.editingKeyerSetValue(SPKSettings::maxY), 
01327                                                                                 settings.editingKeyerSetValue(SPKSettings::minU), 
01328                                                                                 settings.editingKeyerSetValue(SPKSettings::maxU),
01329                                                                                 value, 
01330                                                                                 settings.editingKeyerSetValue(SPKSettings::maxV));
01331         screen.textToBuffer(paramLine, kMenuLine2);
01332         
01333         tvOne.command(0, mixKeyWindow, kTV1FunctionAdjustKeyerMinV, value);    
01334     }
01335     else if (actionCount == 9)
01336     {
01337         // Save settings
01338         tvOne.command(0, mixKeyWindow, kTV1FunctionPowerOnPresetStore, 1);
01339     
01340         // Get back to menu
01341         actionCount = 0;
01342         state = 0;
01343         selectedMenu = &mixModeMenu;
01344         screen.clearBufferRow(kMenuLine1);
01345         screen.clearBufferRow(kMenuLine2);
01346         screen.textToBuffer(selectedMenu->title, kMenuLine1);
01347         screen.textToBuffer(selectedMenu->selectedString(), kMenuLine2);
01348     }
01349 }
01350 
01351 void troubleshootingMenuResetHandler(int menuChange, bool action)
01352 {
01353     static int actionCount = 0;
01354     
01355     if (action) actionCount++;
01356     
01357     if (actionCount == 0)
01358     {
01359         screen.clearBufferRow(kMenuLine2);
01360         screen.textToBuffer("Follow instructions... [+]", kMenuLine2);  
01361     }
01362     if (actionCount == 1)
01363     {
01364         screen.clearBufferRow(kMenuLine2);
01365         screen.textToBuffer("On Processor find...   [+]", kMenuLine2);  
01366     }
01367     if (actionCount == 2)
01368     {
01369         screen.clearBufferRow(kMenuLine2);
01370         screen.textToBuffer("MENU+STANDBY buttons[+]", kMenuLine2);
01371     }
01372     if (actionCount == 3)
01373     {
01374         Timer timer;
01375         timer.start();
01376         
01377         while (timer.read_ms() < 16000)
01378         {
01379             screen.clearBufferRow(kMenuLine2);
01380             char messageBuffer[kStringBufferLength];
01381             snprintf(messageBuffer, kStringBufferLength,"Hold buttons for [%i s]", 15 - (timer.read_ms() / 1000));
01382             screen.textToBuffer(messageBuffer, kMenuLine2);
01383             screen.sendBuffer();
01384         }
01385         
01386         screen.clearBufferRow(kMenuLine2);
01387         screen.textToBuffer("Hold buttons for [+]", kMenuLine2);
01388     }
01389     if (actionCount == 4)
01390     {
01391         screen.clearBufferRow(kMenuLine2);
01392         screen.textToBuffer("Updating processor [-]", kMenuLine2);
01393         screen.clearBufferRow(kTVOneStatusLine);
01394         screen.textToBuffer("Sending...", kTVOneStatusLine);
01395         screen.sendBuffer();
01396     
01397         bool ok = conformProcessor();
01398         
01399         std::string sendOK = ok ? "TVOne: Reset success" : "Send Error: Reset";
01400     
01401         tvOneStatusMessage.addMessage(sendOK, kTVOneStatusMessageHoldTime);
01402         
01403         tvOneRGB1Stable = false;
01404         tvOneRGB2Stable = false;
01405         handleTVOneSources();
01406         
01407         actionCount++;
01408     }
01409     if (actionCount == 5)
01410     {
01411         screen.clearBufferRow(kMenuLine2);
01412         screen.textToBuffer("Reset complete [DONE]", kMenuLine2);
01413     }
01414     if (actionCount == 6)
01415     {
01416         // Get back to menu
01417         actionCount = 0;
01418         selectedMenu = &troubleshootingMenu;
01419         screen.clearBufferRow(kMenuLine1);
01420         screen.clearBufferRow(kMenuLine2);
01421         screen.textToBuffer(selectedMenu->title, kMenuLine1);
01422         screen.textToBuffer(selectedMenu->selectedString(), kMenuLine2);
01423     }
01424 }
01425 
01426 int main() 
01427 {
01428     if (debug) 
01429     { 
01430         debug->printf("\r\n\r\n");
01431         debug->printf("*spark d-fuser -----------\r\n");
01432         debug->printf(" debug channel\r\n");
01433     }
01434     
01435     // Set display font
01436     screen.fontStartCharacter = &characterBytesStartChar;
01437     screen.fontEndCharacter = &characterBytesEndChar;
01438     screen.fontCharacters = characterBytes;
01439     
01440     // Splash screen
01441     string softwareLine = "SW ";
01442     softwareLine += kSPKDFSoftwareVersion;
01443     screen.imageToBuffer(spkDisplayLogo);
01444     screen.textToBuffer("SPK:D-Fuser",0);
01445     screen.textToBuffer(softwareLine,1);
01446     screen.sendBuffer();
01447     
01448     // Load saved settings
01449     bool settingsAreCustom = false;
01450     settingsAreCustom = settings.load(kSPKDFSettingsFilename);
01451     if (settingsAreCustom) 
01452     {
01453         softwareLine += "; ini OK";
01454         screen.textToBuffer(softwareLine, 1); 
01455     }
01456     
01457     // Set menu structure
01458     mixModeMenu.title = "Mix Mode";
01459     mixModeAdditiveMenu.addMenuItem(SPKMenuItem(&mixModeAdditiveMenuHandler));
01460     mixModeUpdateKeyMenu.addMenuItem(SPKMenuItem(&mixModeUpdateKeyMenuHandler));
01461 
01462     setMixModeMenuItems();
01463 
01464     resolutionMenu.title = "Resolution";
01465     setResolutionMenuItems();
01466 
01467     commsMenu.title = "Network Mode";
01468     setCommsMenuItems();
01469     
01470     advancedMenu.title = "Advanced Commands";
01471     advancedMenu.addMenuItem(SPKMenuItem("Processor full conform", advancedConformUploadProcessor));
01472     advancedMenu.addMenuItem(SPKMenuItem("Back to Troubleshooting Menu", &troubleshootingMenu));
01473     
01474     troubleshootingMenu.title = "Troubleshooting"; 
01475     troubleshootingMenuHDCP.title = "HDCP - Can Block DVI";
01476     troubleshootingMenuHDCP.addMenuItem(SPKMenuItem(&troubleshootingMenuHDCPHandler));
01477     troubleshootingMenu.addMenuItem(SPKMenuItem(troubleshootingMenuHDCP.title, &troubleshootingMenuHDCP));
01478     troubleshootingMenuEDID.title = "EDID - Advertises Res's";
01479     troubleshootingMenuEDID.addMenuItem(SPKMenuItem(&troubleshootingMenuEDIDHandler));
01480     troubleshootingMenu.addMenuItem(SPKMenuItem(troubleshootingMenuEDID.title, &troubleshootingMenuEDID));
01481     troubleshootingMenuAspect.title = "Aspect - Mismatched Res";
01482     troubleshootingMenuAspect.addMenuItem(SPKMenuItem(&troubleshootingMenuAspectHandler));
01483     troubleshootingMenu.addMenuItem(SPKMenuItem(troubleshootingMenuAspect.title, &troubleshootingMenuAspect));
01484     troubleshootingMenuMatrox.title = "Matrox - Red Light";
01485     troubleshootingMenuMatrox.addMenuItem(SPKMenuItem(&troubleshootingMenuMatroxHandler));
01486     troubleshootingMenu.addMenuItem(SPKMenuItem(troubleshootingMenuMatrox.title, &troubleshootingMenuMatrox));
01487     troubleshootingMenuReset.title = "Output - Mixing Wrong";
01488     troubleshootingMenuReset.addMenuItem(SPKMenuItem(&troubleshootingMenuResetHandler));
01489     troubleshootingMenu.addMenuItem(SPKMenuItem(troubleshootingMenuReset.title, &troubleshootingMenuReset));
01490     troubleshootingMenu.addMenuItem(SPKMenuItem(advancedMenu.title, &advancedMenu));
01491     troubleshootingMenu.addMenuItem(SPKMenuItem("Back to Main Menu", &mainMenu));
01492     
01493     mainMenu.title = "Main Menu";
01494     mainMenu.addMenuItem(SPKMenuItem(mixModeMenu.title, &mixModeMenu));
01495     mainMenu.addMenuItem(SPKMenuItem(resolutionMenu.title, &resolutionMenu));
01496     mainMenu.addMenuItem(SPKMenuItem(commsMenu.title, &commsMenu));
01497     mainMenu.addMenuItem(SPKMenuItem(troubleshootingMenu.title, &troubleshootingMenu));
01498       
01499     selectedMenu = &mainMenu;
01500       
01501     // Misc I/O stuff
01502     
01503     fadeAPO.period(0.001);
01504     fadeBPO.period(0.001);
01505 
01506     // If we do not have two solid sources, act on this as we rely on the window having a source for crossfade behaviour
01507     // 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.
01508     handleTVOneSources();
01509     
01510     // Processor can have been power-on saved with a keyer on, lets revert
01511     checkTVOneMixStatus();
01512     
01513     // Display menu and framing lines
01514     screen.horizLineToBuffer(kMenuLine1*pixInPage - 1);
01515     screen.clearBufferRow(kMenuLine1);
01516     screen.textToBuffer(selectedMenu->title, kMenuLine1);
01517     screen.clearBufferRow(kMenuLine2);
01518     screen.textToBuffer(selectedMenu->selectedString(), kMenuLine2);
01519     screen.horizLineToBuffer(kMenuLine2*pixInPage + pixInPage);
01520     screen.horizLineToBuffer(kCommsStatusLine*pixInPage - 1);
01521     screen.clearBufferRow(kTVOneStatusLine);
01522     screen.textToBuffer(tvOneStatusMessage.message(), kTVOneStatusLine);
01523     screen.sendBuffer();
01524     
01525     //// CONTROLS TEST
01526 
01527     while (0) {
01528         if (debug) debug->printf("xFade: %f, fadeOut: %f, tapLeft %i, tapRight: %i encPos: %i encChange:%i encHasPressed:%i \r\n" , xFadeAIN.read(), fadeUpAIN.read(), tapLeftDIN.read(), tapRightDIN.read(), menuEnc.getPos(), menuEnc.getChange(), menuEnc.hasPressed());
01529     }
01530 
01531     //// MIXER RUN
01532 
01533     while (1) 
01534     {    
01535         //// Task background things
01536         if ((osc || artNet) && rj45Mode == rj45Ethernet)
01537         {
01538             Net::poll();
01539         }
01540 
01541         //// RJ45 SWITCH
01542         
01543         if (rj45ModeDIN != rj45Mode)
01544         {
01545             if (debug) debug->printf("Handling RJ45 mode change\r\n");   
01546 
01547             // update state
01548             rj45Mode = rj45ModeDIN;
01549             
01550             setCommsMenuItems();
01551             
01552             // cancel old comms
01553             commsMode = commsNone;
01554             commsMenu = commsMode;
01555             
01556             // refresh display
01557             if (selectedMenu == &commsMenu) 
01558             {
01559                 screen.clearBufferRow(kMenuLine1);
01560                 screen.clearBufferRow(kMenuLine2);
01561                 screen.textToBuffer(selectedMenu->title, kMenuLine1);
01562                 screen.textToBuffer(selectedMenu->selectedString(), kMenuLine2);
01563             }
01564             if (rj45Mode == rj45Ethernet) screen.textToBuffer("RJ45: Ethernet Mode", kCommsStatusLine);
01565             if (rj45Mode == rj45DMX) screen.textToBuffer("RJ45: DMX Mode", kCommsStatusLine);
01566         }
01567 
01568         //// MENU
01569         
01570         int menuChange = menuEnc.getChange();
01571         
01572         // Update GUI
01573         if (menuChange != 0)
01574         {
01575             if (selectedMenu->selectedItem().type == SPKMenuItem::hasHandler)
01576             {
01577                 selectedMenu->selectedItem().payload.handler(menuChange, false);
01578             }
01579             else
01580             {
01581                 if (debug) debug->printf("Menu changed by %i\r\n", menuChange);
01582                 
01583                 *selectedMenu = selectedMenu->selectedIndex() + menuChange;
01584                 
01585                 // update OLED line 2 here
01586                 screen.clearBufferRow(kMenuLine2);
01587                 screen.textToBuffer(selectedMenu->selectedString(), kMenuLine2);
01588                 
01589                 if (debug) debug->printf("%s \r\n", selectedMenu->selectedString().c_str());
01590             }    
01591         }
01592         
01593         // Action menu item
01594         if (menuEnc.hasPressed()) 
01595         {
01596             if (debug) debug->printf("Action Menu Item!\r\n");
01597                     
01598             // Are we changing menus?
01599             if (selectedMenu->selectedItem().type == SPKMenuItem::changesToMenu) 
01600             {
01601                 // If we're exiting the menu, we should set its selected index back to the menu's beginning...
01602                 SPKMenu* menuToReset = selectedMenu->selectedItem().payload.menu == &mainMenu? selectedMenu : NULL;
01603              
01604                 // point selected menu pointer to the new menu pointer
01605                 selectedMenu = selectedMenu->selectedItem().payload.menu;
01606                 
01607                 // ...doing this, of course, after we've used the value
01608                 if (menuToReset) *menuToReset = 0;
01609                 
01610                 // update OLED lines 1&2
01611                 screen.clearBufferRow(kMenuLine1);
01612                 screen.clearBufferRow(kMenuLine2);
01613                 screen.textToBuffer(selectedMenu->title, kMenuLine1);
01614                 screen.textToBuffer(selectedMenu->selectedString(), kMenuLine2);
01615                 
01616                 if (selectedMenu->selectedItem().type == SPKMenuItem::hasHandler)
01617                 {
01618                     selectedMenu->selectedItem().payload.handler(0, false);
01619                 }
01620                 
01621                 if (debug)
01622                 {
01623                     debug->printf("\r\n");
01624                     debug->printf("%s \r\n", selectedMenu->title.c_str());
01625                     debug->printf("%s \r\n", selectedMenu->selectedString().c_str());
01626                 }
01627             }    
01628             else if (selectedMenu->selectedItem().type == SPKMenuItem::hasHandler)
01629             {
01630                 selectedMenu->selectedItem().payload.handler(0, true);
01631             }
01632             // With that out of the way, we should be actioning a specific menu's payload?
01633             else if (selectedMenu == &mixModeMenu)
01634             {
01635                 int mixModeMenuPayload = mixModeMenu.selectedItem().payload.command[0];
01636 
01637                 if (mixModeMenuPayload < mixKeyPresetStartIndex)
01638                 {
01639                     mixMode = mixModeMenuPayload;
01640                     
01641                     if (mixMode == mixModeOld) tvOneStatusMessage.addMessage("Mix mode already active", kTVOneStatusMessageHoldTime);
01642                 }
01643                 else
01644                 { 
01645                     int keySetIndex = mixModeMenuPayload - mixKeyPresetStartIndex;
01646                     
01647                     if (keySetIndex > 0) // Key set 0 is now the "live" set, we read from processor rather than write to it.
01648                     {
01649                         bool ok;
01650                         ok =       tvOne.command(0, mixKeyWindow, kTV1FunctionAdjustKeyerMinY, settings.keyerParamSet(keySetIndex)[SPKSettings::minY]); 
01651                         ok = ok && tvOne.command(0, mixKeyWindow, kTV1FunctionAdjustKeyerMaxY, settings.keyerParamSet(keySetIndex)[SPKSettings::maxY]); 
01652                         ok = ok && tvOne.command(0, mixKeyWindow, kTV1FunctionAdjustKeyerMinU, settings.keyerParamSet(keySetIndex)[SPKSettings::minU]); 
01653                         ok = ok && tvOne.command(0, mixKeyWindow, kTV1FunctionAdjustKeyerMaxU, settings.keyerParamSet(keySetIndex)[SPKSettings::maxU]); 
01654                         ok = ok && tvOne.command(0, mixKeyWindow, kTV1FunctionAdjustKeyerMinV, settings.keyerParamSet(keySetIndex)[SPKSettings::minV]); 
01655                         ok = ok && tvOne.command(0, mixKeyWindow, kTV1FunctionAdjustKeyerMaxV, settings.keyerParamSet(keySetIndex)[SPKSettings::maxV]);
01656                         
01657                         tvOne.command(0, kTV1WindowIDA, kTV1FunctionPowerOnPresetStore, 1);
01658                         
01659                         tvOneStatusMessage.addMessage(ok ? "Loaded: " + settings.keyerParamName(keySetIndex) + " values" : "Send error: keyer values", kTVOneStatusMessageHoldTime);
01660                     }
01661                 }
01662             }
01663             else if (selectedMenu == &resolutionMenu)
01664             {
01665                 screen.clearBufferRow(kTVOneStatusLine);
01666                 screen.textToBuffer("Setting Resolution...", kTVOneStatusLine);
01667                 screen.sendBuffer();
01668                 
01669                 bool ok;
01670                 int oldEDID = tvOne.getEDID();
01671                 int newEDID = tvOneEDIDPassthrough ? EDIDPassthroughSlot : resolutionMenu.selectedItem().payload.command[1];
01672                 
01673                 ok = tvOne.setResolution(resolutionMenu.selectedItem().payload.command[0], newEDID);
01674                 
01675                 // Save new resolution and EDID into TV One unit for power-on. Cycling TV One power sometimes needed for EDID. Pffft.
01676                 if (ok) tvOne.command(0, kTV1WindowIDA, kTV1FunctionPowerOnPresetStore, 1);
01677                 
01678                 string message;
01679                 if (ok)
01680                 {
01681                     if (oldEDID == newEDID) message = "Sent: Resolution";
01682                     else                    message = "Sent: Resolution + EDID";
01683                 }
01684                 else                        message = "Send Error: Resolution";
01685                 tvOneStatusMessage.addMessage(message, kTVOneStatusMessageHoldTime, kTVOneStatusMessageHoldTime);
01686                 
01687                 // This is WHACK. Can't believe it's both needed and officially in the 1T-C2-750 manual
01688                 if ((oldEDID != newEDID) && ok) tvOneStatusMessage.addMessage("EDID: Processor Off+On?", kTVOneStatusMessageHoldTime);
01689                 
01690                 if (debug) { debug->printf("Changing resolution"); }
01691             }
01692             else if (selectedMenu == &commsMenu)
01693             {
01694                 string commsTypeString = "Network:";
01695                 char commsStatusBuffer[kStringBufferLength] = "--";
01696             
01697                 // Tear down any existing comms
01698                 // This is the action of commsNone
01699                 // And also clears the way for other comms actions
01700                 commsMode = commsNone;
01701                 /* // These don't take well to being destroyed. So while it was a nice idea to be able to swap between None, OSC and Artnet, now the GUI won't allow it, and we don't need this.
01702                 if (osc)        {delete osc; osc = NULL;}  
01703                 if (ethernet)   {delete ethernet; ethernet = NULL;}
01704                 if (artNet)     
01705                 {
01706                     artNet->ArtPollReply.NumPorts = 0; 
01707                     strcpy(artNet->ArtPollReply.NodeReport, "Shutdown");
01708                     artNet->SendArtPollReply();
01709                     artNet->Done(); 
01710                     delete artNet; 
01711                     artNet = NULL;
01712                 }
01713                 */
01714                 if (dmx)        {delete dmx; dmx = NULL;}
01715                 
01716                 // Ensure we can't change to comms modes the hardware isn't switched to
01717                 if (rj45Mode == rj45DMX && (commsMenu.selectedItem().payload.command[0] == commsOSC || commsMenu.selectedItem().payload.command[0] == commsArtNet))
01718                 {
01719                     commsTypeString = "RJ45 not in Ethernet mode";
01720                 }
01721                 else if (rj45Mode == rj45Ethernet && (commsMenu.selectedItem().payload.command[0] == commsDMXIn || commsMenu.selectedItem().payload.command[0] == commsDMXOut))
01722                 {
01723                     commsTypeString = "RJ45 not in DMX mode";
01724                 }
01725                 // Action!
01726                 else if (commsMenu.selectedItem().payload.command[0] == commsOSC) 
01727                 {
01728                     commsMode = commsOSC;
01729                     commsTypeString = "OSC: ";
01730                     
01731                     if (!ethernet)
01732                     {
01733                         if (settings.osc.DHCP)
01734                         {
01735                             ethernet = new EthernetNetIf("dvimxr");
01736                         }
01737                         else
01738                         {
01739                             ethernet = new EthernetNetIf(
01740                                 settings.osc.controllerAddress, 
01741                                 settings.osc.controllerSubnetMask, 
01742                                 settings.osc.controllerGateway, 
01743                                 settings.osc.controllerDNS  
01744                             );
01745                         }
01746                         
01747                         EthernetErr ethError = ethernet->setup();
01748                         if(ethError)
01749                         {
01750                             if (debug) debug->printf("Ethernet setup error, %d", ethError);
01751                             snprintf(commsStatusBuffer, kStringBufferLength, "Ethernet setup failed");
01752                             commsMenu = commsNone;
01753                             // break out of here. this setup should be a function that returns a boolean
01754                         }
01755                     }
01756                     
01757                     if (!osc)
01758                     {
01759                         osc = new OSCClass();
01760                         osc->setReceiveMessage(&receiveMessage);
01761                         osc->begin(settings.osc.sendPort);
01762                         
01763                         uint8_t sendAddress[] = {settings.osc.sendAddress[0], settings.osc.sendAddress[1], settings.osc.sendAddress[2], settings.osc.sendAddress[3]};
01764                         sendMessage.setIp( sendAddress );
01765                         sendMessage.setPort( settings.osc.sendPort );
01766                     }
01767                     
01768                     IpAddr ethIP = ethernet->getIp();
01769                     snprintf(commsStatusBuffer, kStringBufferLength, "In %u.%u.%u.%u:%u", ethIP[0], ethIP[1], ethIP[2], ethIP[3], settings.osc.controllerPort);
01770                 
01771                     setCommsMenuItems(); // remove non-OSC from menu. we're locked in.
01772                 }
01773                 else if (commsMenu.selectedItem().payload.command[0] == commsArtNet) 
01774                 {
01775                     commsMode = commsArtNet;
01776                     commsTypeString = "ArtNet: ";                    
01777 
01778                     artNet = new DmxArtNet();
01779                     
01780                     artNet->BindIpAddress = settings.artNet.controllerAddress;
01781                     artNet->BCastAddress = settings.artNet.broadcastAddress;
01782                 
01783                     artNet->InitArtPollReplyDefaults();
01784                 
01785                     artNet->ArtPollReply.PortType[0] = 128; // Bit 7 = Set is this channel can output data from the Art-Net Network.
01786                     artNet->ArtPollReply.GoodOutput[0] = 128; // Bit 7 = Set – Data is being transmitted.
01787                     artNet->ArtPollReply.PortType[2] = 64; // Bit 6 = Set if this channel can input onto the Art-NetNetwork.
01788                     artNet->ArtPollReply.GoodInput[2] = 128; // Bit 7 = Set – Data received.
01789                 
01790                     artNet->Init();
01791                     artNet->SendArtPollReply(); // announce to art-net nodes
01792                     
01793                     snprintf(commsStatusBuffer, kStringBufferLength, "Listening");
01794                     
01795                     setCommsMenuItems(); // remove non-ArtNet from menu. we're locked in.
01796                 }
01797                 else if (commsMenu.selectedItem().payload.command[0] == commsDMXIn) 
01798                 {
01799                     commsMode = commsDMXIn;
01800                     commsTypeString = "DMX In: ";
01801                     
01802                     dmxDirectionDOUT = 0;
01803                     
01804                     dmx = new DMX(kMBED_RS485_TTLTX, kMBED_RS485_TTLRX);
01805                 }
01806                 else if (commsMenu.selectedItem().payload.command[0] == commsDMXOut) 
01807                 {
01808                     commsMode = commsDMXOut;
01809                     commsTypeString = "DMX Out: ";
01810                     
01811                     dmxDirectionDOUT = 1;
01812                     
01813                     dmx = new DMX(kMBED_RS485_TTLTX, kMBED_RS485_TTLRX);
01814                 }
01815                                 
01816                 screen.clearBufferRow(kCommsStatusLine);
01817                 screen.textToBuffer(commsTypeString + commsStatusBuffer, kCommsStatusLine);
01818             }
01819             else if (selectedMenu == &advancedMenu)
01820             {
01821                 if (advancedMenu.selectedItem().payload.command[0] == advancedConformUploadProcessor)
01822                 {
01823                     bool ok = true;
01824                 
01825                     screen.clearBufferRow(kTVOneStatusLine);
01826                     screen.textToBuffer("Uploading...", kTVOneStatusLine);
01827                     screen.sendBuffer();
01828                     
01829                     ok = ok && uploadToProcessor();                    
01830                     
01831                     screen.clearBufferRow(kTVOneStatusLine);
01832                     screen.textToBuffer("Conforming...", kTVOneStatusLine);
01833                     screen.sendBuffer();
01834                     
01835                     ok = ok && conformProcessor();
01836                     
01837                     std::string sendOK = ok ? "Conform success" : "Send Error: Conform";
01838                     
01839                     tvOneStatusMessage.addMessage(sendOK, kTVOneStatusMessageHoldTime, 600);
01840                 }
01841 //                else if (advancedMenu.selectedItem().payload.command[0] == advancedSetResolutions)
01842 //                {
01843 //                    bool ok;
01844 //                    ok = tvOne.uploadCustomResolutions();
01845 //                    
01846 //                    tvOneStatusMessage.addMessage(ok ? "Resolutions set" : "Res' could not be set", kTVOneStatusMessageHoldTime);
01847 //                }
01848             }
01849             else
01850             {
01851                 if (debug) { debug->printf("Warning: No action identified"); }
01852             }
01853         }
01854 
01855         // Send any updates to the display
01856         screen.clearBufferRow(kTVOneStatusLine);
01857         screen.textToBuffer(tvOneStatusMessage.message(), kTVOneStatusLine);
01858         screen.sendBuffer();
01859         
01860         //// MIX MIX MIX MIX MIX MIX MIX MIX MIX MIX MIX MIXMIX MIX MIXMIX MIX MIX MIX MIX MIXMIX MIX MIX
01861 
01862         bool updateFade = false;
01863         float xFade = 0;
01864         float fadeUp = 1;
01865         
01866         //// TASK: Process control surface
01867         
01868         // Get new states of tap buttons, remembering at end of loop() assign these current values to the previous variables
01869         const bool tapLeft = !tapLeftDIN;
01870         const bool tapRight = !tapRightDIN;
01871         
01872         // We're taking a further median of the AINs on top of mbed libs v29.
01873         // This takes some values from last passes and most from now. With debug off, seem to need median size > 5
01874         xFadeFilter.process(xFadeAIN.read());
01875         fadeUpFilter.process(fadeUpAIN.read());
01876         xFadeFilter.process(xFadeAIN.read());
01877         fadeUpFilter.process(fadeUpAIN.read());
01878         xFadeFilter.process(xFadeAIN.read());
01879         fadeUpFilter.process(fadeUpAIN.read());
01880         xFadeFilter.process(xFadeAIN.read());
01881         fadeUpFilter.process(fadeUpAIN.read());
01882         const float xFadeAINCached = xFadeFilter.process(xFadeAIN.read());
01883         const float fadeUpAINCached = fadeUpFilter.process(fadeUpAIN.read());
01884         
01885         // When a tap is depressed, we can ignore any move of the crossfader but not fade to black
01886         if (tapLeft || tapRight) 
01887         {
01888             // If both are pressed, take to the one that is new, ie. not the first pressed.
01889             if (tapLeft && tapRight) 
01890             {
01891                 xFade = tapLeftWasFirstPressed ? 1 : 0;
01892             }
01893             // If just one is pressed, take to that and remember which is pressed
01894             else if (tapLeft) 
01895             {
01896                 xFade = 0;
01897                 tapLeftWasFirstPressed = 1;
01898             }
01899             else if (tapRight) 
01900             {
01901                 xFade = 1;
01902                 tapLeftWasFirstPressed = 0;
01903             }
01904         }
01905         else xFade = 1.0 - fadeCalc(xFadeAINCached, xFadeTolerance);
01906 
01907         fadeUp = 1.0 - fadeCalc(fadeUpAINCached, fadeUpTolerance);
01908 
01909         //// TASK: Process Network Comms In, allowing hands-on controls to override
01910         if ((commsMode == commsOSC) || (commsMode == commsArtNet) || (commsMode == commsDMXIn))
01911         {
01912             bool commsIn = false;
01913 
01914             switch (commsMode)
01915             {
01916                 case commsOSC:      commsIn = processOSCIn(); break;
01917                 case commsArtNet:   commsIn = processArtNetIn(); break;
01918                 case commsDMXIn:    commsIn = processDMXIn(); break;
01919             }
01920         
01921             if (commsIn)
01922             {
01923                 // Store hands-on control positions to compare for change later
01924                 commsInActive = true;
01925                 oldXFade = xFade;
01926                 oldFadeUp = fadeUp;
01927             }
01928             else if (commsInActive)
01929             {
01930                 // If no comms in update this loop, hold to the last unless hands-on controls have moved significantly
01931                 bool movement = (fabs(oldXFade-xFade) > 0.1) || (fabs(oldFadeUp-fadeUp) > 0.1);
01932                 if (movement) 
01933                 {   
01934                     commsInActive = false;
01935                     commsXFade = -1;
01936                     commsFadeUp = -1;
01937                 }
01938             }
01939         
01940             if (commsInActive)
01941             {
01942                 if (commsXFade >= 0)    xFade = commsXFade;
01943                 if (commsFadeUp >= 0)   fadeUp = commsFadeUp;   
01944             }
01945         }
01946 
01947         // Calculate new A&B fade percents
01948         int newFadeAPercent = 0;
01949         int newFadeBPercent = 0;
01950 
01951         if (mixMode == mixBlend) 
01952         {
01953             // This is the correct algorithm for blend where window A occludes B.
01954             // Who knew a crossfade could be so tricky. The level of B has to be factored by what A is letting through.
01955             // ie. if fully faded up, top window = xfade, bottom window = 100%
01956             // This will however look very wrong if A is not occluding B, ie. mismatched aspect ratios.
01957             if (xFade > 0) // avoids div by zero (if xFade = 0 and fadeUp = 1, B sum = 0 / 0)
01958             {
01959                 newFadeAPercent = (1.0-xFade) * fadeUp * 100.0;
01960                 newFadeBPercent = (xFade*fadeUp) / (1.0 - fadeUp + xFade*fadeUp) * 100.0;
01961             }
01962             else
01963             {
01964                 newFadeAPercent = fadeUp * 100.0;
01965                 newFadeBPercent = 0;
01966             }
01967         }
01968         else if (mixMode == mixAdditive)
01969         {
01970             // we need to set fade level of both windows according to the fade curve profile
01971             float newFadeA = (1.0-xFade) * (1.0 + fadeCurve);
01972             float newFadeB = xFade * (1 + fadeCurve);
01973             if (newFadeA > 1.0) newFadeA = 1.0;
01974             if (newFadeB > 1.0) newFadeB = 1.0;
01975             
01976             newFadeAPercent = newFadeA * fadeUp * 100.0;
01977             newFadeBPercent = newFadeB * fadeUp * 100.0;
01978         }
01979         else if (mixMode == mixKeyLeft)
01980         {
01981             newFadeAPercent = (1.0-xFade) * fadeUp * 100.0;
01982             newFadeBPercent = fadeUp * 100.0;
01983         }
01984         else if (mixMode == mixKeyRight)
01985         {
01986             newFadeAPercent = fadeUp * 100.0;
01987             newFadeBPercent = xFade * fadeUp * 100.0;
01988         }
01989         
01990         //// TASK: Send to TVOne if percents have changed
01991         
01992         // No amount of median filtering is stopping flipflopping between two adjacent percents, so...
01993         bool fadeAPercentHasChanged;
01994         bool fadeBPercentHasChanged;
01995         if (oldFadeAPercent == newFadeAPercent && (newFadeAPercent == fadeAPercent - 1 || newFadeAPercent == fadeAPercent + 1))
01996             fadeAPercentHasChanged = false;
01997         else
01998             fadeAPercentHasChanged = newFadeAPercent != fadeAPercent;
01999         if (oldFadeBPercent == newFadeBPercent && (newFadeBPercent == fadeBPercent - 1 || newFadeBPercent == fadeBPercent + 1))
02000             fadeBPercentHasChanged = false;
02001         else
02002             fadeBPercentHasChanged = newFadeBPercent != fadeBPercent;
02003         
02004         // If changing mixMode from additive, we want to do this before updating fade values
02005         if (mixMode != mixModeOld && mixModeOld == mixAdditive) actionMixMode();
02006         
02007         // We want to send the higher first, otherwise black flashes can happen on taps
02008         if (fadeAPercentHasChanged && newFadeAPercent >= newFadeBPercent) 
02009         {
02010             oldFadeAPercent = fadeAPercent;
02011             fadeAPercent = newFadeAPercent;
02012             updateFade = true;
02013             
02014             fadeAPO = fadeAPercent / 100.0;
02015             tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustWindowsMaxFadeLevel, fadeAPercent);
02016         }
02017         if (fadeBPercentHasChanged) 
02018         {
02019             oldFadeBPercent = fadeBPercent;
02020             fadeBPercent = newFadeBPercent;
02021             updateFade = true;
02022             
02023             fadeBPO = fadeBPercent / 100.0;
02024             tvOne.command(0, kTV1WindowIDB, kTV1FunctionAdjustWindowsMaxFadeLevel, fadeBPercent);
02025         }
02026         if (fadeAPercentHasChanged && newFadeAPercent < newFadeBPercent) 
02027         {
02028             oldFadeAPercent = fadeAPercent; 
02029             fadeAPercent = newFadeAPercent;
02030             updateFade = true;
02031             
02032             fadeAPO = fadeAPercent / 100.0;
02033             tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustWindowsMaxFadeLevel, fadeAPercent);
02034         }
02035         if (updateFade && debug) 
02036         {
02037             //debug->printf("xFade = %3f   fadeUp = %3f \r\n", xFadeAIN.read(), fadeUpAIN.read());
02038             debug->printf("xFade = %3f   fadeUp = %3f \r\n", xFadeAINCached, fadeUpAINCached);
02039             debug->printf("xFade = %3f   fadeUp = %3f   fadeA% = %i   fadeB% = %i \r\n", xFade, fadeUp, fadeAPercent, fadeBPercent);
02040             debug->printf("\r\n"); 
02041         }
02042         
02043         // If changing mixMode to additive, we want to do this after updating fade values
02044         if (mixMode != mixModeOld) actionMixMode();
02045                 
02046         //// TASK: Process Network Comms Out, ie. send out any fade updates
02047         if (commsMode == commsOSC && updateFade && !commsInActive)
02048         {
02049             processOSCOut(xFade, fadeUp);
02050         }
02051 
02052         if (commsMode == commsArtNet && updateFade && !commsInActive)
02053         {
02054             processArtNetOut(xFade, fadeUp);
02055         }
02056 
02057         if (commsMode == commsDMXOut && updateFade && !commsInActive)
02058         {
02059             processDMXOut(xFade, fadeUp);
02060         }
02061         
02062         //// TASK: Housekeeping
02063         
02064         if (tvOne.millisSinceLastCommandSent() > tvOne.getCommandTimeoutPeriod() + 1000)
02065         {
02066             // Lets check on our sources
02067             handleTVOneSources();
02068             
02069             // Lets check on our fade levels
02070             checkTVOneMixStatus();
02071         }
02072     }
02073 }