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
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 }
Generated on Tue Jul 12 2022 21:39:05 by 1.7.2