Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: SPK-TVOne DMX DmxArtNet NetServicesMin OSC PinDetect mRotaryEncoder iniparser mbed spk_oled_ssd1305 filter
main.cpp
- Committer:
- tobyspark
- Date:
- 2012-08-02
- Revision:
- 8:d46cc49f0f37
- Parent:
- 7:e6717468c18d
- Child:
- 9:f83eadd8917a
File content as of revision 8:d46cc49f0f37:
/* *SPARK D-FUSER
* A project by Toby Harris
*
* 'DJ' controller styke RS232 Control for TV-One products
* Good for 1T-C2-750, others will need some extra work
*
* www.sparkav.co.uk/dvimixer
*/
/* Copyright (c) 2011 Toby Harris, MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* ROADMAP / HISTORY
* v10 - Port to mBed, keying redux - Apr'11
* v11 - Sign callbacks, code clean-up - Apr'11
* 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
* v13 - Menu system for Resolution + Keying implemented, it writing to debug, it sending TVOne commands - Apr'11
* v14 - Fixes for new PCB - Oct'11
* v15 - TBZ PCB, OLED - Mar'12
* v16 - Comms menu, OSC, ArtNet - April'12
* v17 - RJ45 - May'12
* v18 - DMX - July'12
* vxx - TODO: Keying values load from USB mass storage
* vxx - TODO: Defaults load/save from USB mass storage
* vxx - TODO: EDID upload from USB mass storage
* vxx - TODO: EDID creation from resolution
*/
#include "mbed.h"
#include "spk_tvone_mbed.h"
#include "spk_utils.h"
#include "spk_mRotaryEncoder.h"
#include "spk_oled_ssd1305.h"
#include "spk_oled_gfx.h"
#include "EthernetNetIf.h"
#include "mbedOSC.h"
#include "DmxArtNet.h"
#include "DMX.h"
#include <sstream>
// MBED PINS
#define kMBED_AIN_XFADE p20
#define kMBED_AIN_FADEUP p19
#define kMBED_DIN_TAP_L p24
#define kMBED_DIN_TAP_R p23
#define kMBED_ENC_SW p15
#define kMBED_ENC_A p16
#define kMBED_ENC_B p17
#define kMBED_RS232_TTLTX p13
#define kMBED_RS232_TTLRX p14
#define kMBED_OLED_MOSI p5
#define kMBED_OLED_SCK p7
#define kMBED_OLED_CS p8
#define kMBED_OLED_RES p9
#define kMBED_OLED_DC p10
#define kMBED_DIN_ETHLO_DMXHI p30
#define kMBED_DOUT_RS485_TXHI_RXLO p29
#define kMBED_RS485_TTLTX p28
#define kMBED_RS485_TTLRX p27
// DISPLAY
#define kMenuLine1 3
#define kMenuLine2 4
#define kCommsStatusLine 6
#define kTVOneStatusLine 7
// NETWORKING
#define kOSCMbedPort 10000
#define kOSCMbedIPAddress 10,0,0,2
#define kOSCMbedSubnetMask 255,255,255,0
#define kOSCMbedGateway 10,0,0,1
#define kOSCMbedDNS 10,0,0,1
#define kArtNetBindIPAddress 2,0,0,100
#define kArtNetBroadcastAddress 2,255,255,255
#define kDMXInChannelXFade 0
#define kDMXInChannelFadeUp 1
#define kDMXOutChannelXFade 0
#define kDMXOutChannelFadeUp 1
//// DEBUG
// Comment out one or the other...
//Serial *debug = new Serial(USBTX, USBRX); // For debugging via USB serial
Serial *debug = NULL; // For release (no debugging)
//// mBED PIN ASSIGNMENTS
// Inputs
AnalogIn xFadeAIN(kMBED_AIN_XFADE);
AnalogIn fadeUpAIN(kMBED_AIN_FADEUP);
DigitalIn tapLeftDIN(kMBED_DIN_TAP_L);
DigitalIn tapRightDIN(kMBED_DIN_TAP_R);
SPKRotaryEncoder menuEnc(kMBED_ENC_A, kMBED_ENC_B, kMBED_ENC_SW);
DigitalIn rj45ModeDIN(kMBED_DIN_ETHLO_DMXHI);
// Outputs
PwmOut fadeAPO(LED1);
PwmOut fadeBPO(LED2);
DigitalOut dmxDirectionDOUT(kMBED_DOUT_RS485_TXHI_RXLO);
// SPKTVOne(PinName txPin, PinName rxPin, PinName signWritePin, PinName signErrorPin, Serial *debugSerial)
SPKTVOne tvOne(kMBED_RS232_TTLTX, kMBED_RS232_TTLRX, LED3, LED4, debug);
// SPKDisplay(PinName mosi, PinName clk, PinName cs, PinName dc, PinName res, Serial *debugSerial = NULL);
SPKDisplay screen(kMBED_OLED_MOSI, kMBED_OLED_SCK, kMBED_OLED_CS, kMBED_OLED_DC, kMBED_OLED_RES, debug);
// Menu
SPKMenu *selectedMenu;
SPKMenu *lastSelectedMenu;
SPKMenuOfMenus mainMenu;
SPKMenuPayload resolutionMenu;
SPKMenuPayload mixModeMenu;
enum { blend, additive, lumaKey, chromaKey1, chromaKey2, chromaKey3 }; // additive will require custom TVOne firmware.
int mixMode = blend;
SPKMenuPayload commsMenu;
enum { commsNone, commsOSC, commsArtNet, commsDMXIn, commsDMXOut};
int commsMode = commsNone;
// RJ45 Comms
enum { rj45Ethernet = 0, rj45DMX = 1}; // These values from circuit
int rj45Mode = -1;
EthernetNetIf *ethernet = NULL;
OSCClass *osc = NULL;
OSCMessage recMessage;
DmxArtNet *artNet = NULL;
DMX *dmx = NULL;
// Fade logic constants
const float xFadeTolerance = 0.05;
const float fadeUpTolerance = 0.05;
// A&B Fade as resolved percent
int fadeAPercent = 0;
int fadeBPercent = 0;
// Tap button states
bool tapLeftWasFirstPressed = false;
// Key mode parameters
int keyerParamsSet = -1; // last keyParams index uploaded to unit
// {lumakey, chroma on blue [, to be extended as needed] }
// {minY, maxY, minU, maxU, minV, maxV }
int keyerParams[2][6] =
{
{0, 18, 128, 129, 128, 129}, // lumakey
{41, 42, 240, 241, 109, 110} // chroma on blue
// ...
};
void processOSC(float &xFade, float &fadeUp) {
std::stringstream statusMessage;
statusMessage.setf(ios::fixed,ios::floatfield);
statusMessage.precision(2);
if (!strcmp( recMessage.getTopAddress() , "dvimxr" ))
{
statusMessage << "OSC: /dvimxr";
if (!strcmp( recMessage.getSubAddress() , "xFade" ))
if (recMessage.getArgNum() == 1)
if (recMessage.getTypeTag(0) == 'f')
{
double newXFade = recMessage.getArgFloat(0);
statusMessage << "/xFade " << newXFade;
xFade = newXFade;
}
else if (!strcmp( recMessage.getSubAddress() , "fadeUp" ))
if (recMessage.getArgNum() == 1)
if (recMessage.getTypeTag(0) == 'f')
{
double newFadeUp = recMessage.getArgFloat(0);
statusMessage << "/fadeUp " << newFadeUp;
xFade = newFadeUp;
}
else statusMessage << recMessage.getSubAddress() << " - Ignoring";
}
else
{
statusMessage << "OSC: " << recMessage.getTopAddress() << " - Ignoring";
}
screen.clearBufferRow(kCommsStatusLine);
screen.textToBuffer(statusMessage.str(), kCommsStatusLine);
screen.sendBuffer();
if (debug) debug->printf("%s \r\n", statusMessage.str().c_str());
}
void processArtNet(float &xFade, float &fadeUp)
{
screen.clearBufferRow(kCommsStatusLine);
screen.textToBuffer("ArtNet activity", kCommsStatusLine);
screen.sendBuffer();
if (debug) debug->printf("ArtNet activity");
}
void processDMXIn(float &xFade, float &fadeUp)
{
std::stringstream statusMessage;
int xFadeDMX = dmx->get(kDMXInChannelXFade);
int fadeUpDMX = dmx->get(kDMXInChannelFadeUp);
xFade = (float)xFadeDMX/255;
fadeUp = (float)fadeUpDMX/255;
screen.clearBufferRow(kCommsStatusLine);
statusMessage << "DMX In: xF " << xFadeDMX << " fUp " << fadeUpDMX;
screen.textToBuffer(statusMessage.str(), kCommsStatusLine);
screen.sendBuffer();
if (debug) debug->printf(statusMessage.str().c_str());
}
void processDMXOut(float &xFade, float &fadeUp)
{
std::stringstream statusMessage;
int xFadeDMX = xFade*255;
int fadeUpDMX = fadeUp*255;
dmx->put(kDMXOutChannelXFade, xFadeDMX);
dmx->put(kDMXOutChannelFadeUp, fadeUpDMX);
screen.clearBufferRow(kCommsStatusLine);
statusMessage << "DMX Out: xF " << xFadeDMX << " fUp " << fadeUpDMX;
screen.textToBuffer(statusMessage.str(), kCommsStatusLine);
screen.sendBuffer();
if (debug) debug->printf(statusMessage.str().c_str());
}
inline float fadeCalc (const float AIN, const float tolerance)
{
float pos ;
if (AIN < tolerance) pos = 0;
else if (AIN > 1.0 - tolerance) pos = 1;
else pos = (AIN - tolerance) / (1 - 2*tolerance);
if (debug && false) debug->printf("fadeCalc in: %f out: %f \r\n", AIN, pos);
return pos;
}
bool setKeyParamsTo(int index)
{
// Only spend the time uploading six parameters if we need to
// Might want to bounds check here
bool ok = false;
if (index != keyerParamsSet)
{
ok = tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustKeyerMinY, keyerParams[index][0]);
ok = ok && tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustKeyerMaxY, keyerParams[index][1]);
ok = ok && tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustKeyerMinU, keyerParams[index][2]);
ok = ok && tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustKeyerMaxU, keyerParams[index][3]);
ok = ok && tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustKeyerMinV, keyerParams[index][4]);
ok = ok && tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustKeyerMaxV, keyerParams[index][5]);
keyerParamsSet = index;
}
return ok;
}
int main()
{
if (debug)
{
debug->printf("\r\n\r\n");
debug->printf("*spark d-fuser -----------\r\n");
debug->printf(" debug channel\r\n");
}
// Set display font
screen.fontStartCharacter = &characterBytesStartChar;
screen.fontEndCharacter = &characterBytesEndChar;
screen.fontCharacters = characterBytes;
// Splash screen
screen.imageToBuffer(spkDisplayLogo);
screen.textToBuffer("SPK:D-Fuser",0);
screen.textToBuffer("SW beta.18",1);
screen.sendBuffer();
// Set menu structure
mixModeMenu.title = "Mix Mode";
mixModeMenu.addMenuItem("Blend", blend, 0);
mixModeMenu.addMenuItem("LumaKey", lumaKey, 0);
mixModeMenu.addMenuItem("ChromaKey - Blue", chromaKey1, 0);
resolutionMenu.title = "Resolution";
resolutionMenu.addMenuItem(kTV1ResolutionDescriptionVGA, kTV1ResolutionVGA, 5);
resolutionMenu.addMenuItem(kTV1ResolutionDescriptionSVGA, kTV1ResolutionSVGA, 5);
resolutionMenu.addMenuItem(kTV1ResolutionDescriptionXGAp60, kTV1ResolutionXGAp60, 5);
resolutionMenu.addMenuItem(kTV1ResolutionDescriptionWSXGAPLUSp60, kTV1ResolutionWSXGAPLUSp60, 5);
resolutionMenu.addMenuItem(kTV1ResolutionDescriptionWUXGAp60, kTV1ResolutionWUXGAp60, 5);
resolutionMenu.addMenuItem(kTV1ResolutionDescription720p60, kTV1Resolution720p60, 5);
resolutionMenu.addMenuItem(kTV1ResolutionDescription1080p60, kTV1Resolution1080p60, 5);
resolutionMenu.addMenuItem(kTV1ResolutionDescriptionDualHeadSVGAp60, kTV1ResolutionDualHeadSVGAp60, 0);
resolutionMenu.addMenuItem(kTV1ResolutionDescriptionDualHeadXGAp60, kTV1ResolutionDualHeadXGAp60, 0);
resolutionMenu.addMenuItem(kTV1ResolutionDescriptionTripleHeadVGAp60, kTV1ResolutionTripleHeadVGAp60, 0);
commsMenu.title = "Network Mode";
commsMenu.addMenuItem("None", commsNone, 0);
commsMenu.addMenuItem("OSC", commsOSC, 0);
commsMenu.addMenuItem("ArtNet", commsArtNet, 0);
commsMenu.addMenuItem("DMX In", commsDMXIn, 0);
commsMenu.addMenuItem("DMX Out", commsDMXOut, 0);
mainMenu.title = "Main Menu";
mainMenu.addMenuItem(&mixModeMenu);
mainMenu.addMenuItem(&resolutionMenu);
mainMenu.addMenuItem(&commsMenu);
selectedMenu = &mainMenu;
lastSelectedMenu = &mainMenu;
// Misc I/O stuff
fadeAPO.period(0.001);
fadeBPO.period(0.001);
// TVOne setup
bool ok = false;
// horrid, horrid HDCP
ok = tvOne.setHDCPOff();
std::string sendOK = ok ? "Sent: HDCP Off" : "Send Error: HDCP Off";
// display menu and framing lines
screen.horizLineToBuffer(kMenuLine1*pixInPage - 1);
screen.clearBufferRow(kMenuLine1);
screen.textToBuffer(selectedMenu->title, kMenuLine1);
screen.clearBufferRow(kMenuLine2);
screen.textToBuffer(selectedMenu->selectedString(), kMenuLine2);
screen.horizLineToBuffer(kMenuLine2*pixInPage + pixInPage);
screen.horizLineToBuffer(kCommsStatusLine*pixInPage - 1);
screen.clearBufferRow(kCommsStatusLine);
screen.textToBuffer(commsMenu.selectedString(), kCommsStatusLine);
screen.clearBufferRow(kTVOneStatusLine);
screen.textToBuffer(sendOK, kTVOneStatusLine);
screen.sendBuffer();
//// CONTROLS TEST
while (0) {
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());
}
//// MIXER RUN
while (1)
{
//// Task background things
if (ethernet && rj45Mode == rj45Ethernet)
{
if (debug) debug->printf("net poll");
Net::poll();
}
//// RJ45 SWITCH
if (rj45ModeDIN != rj45Mode)
{
// update state
rj45Mode = rj45ModeDIN;
if (rj45Mode == rj45Ethernet) commsMenu.title = "Network Mode [Ethernet]";
if (rj45Mode == rj45DMX) commsMenu.title = "Network Mode [DMX]";
// cancel old comms
commsMode = commsNone;
commsMenu = commsMode;
// refresh display
if (selectedMenu == &commsMenu) screen.textToBuffer(selectedMenu->title, kMenuLine1);
if (rj45Mode == rj45Ethernet) screen.textToBuffer("RJ45: Ethernet Engaged", kCommsStatusLine);
if (rj45Mode == rj45DMX) screen.textToBuffer("RJ45: DMX Engaged", kCommsStatusLine);
}
//// MENU
int menuChange = menuEnc.getChange();
// Update GUI
if (menuChange != 0)
{
if (debug) debug->printf("Menu changed by %i\r\n", menuChange);
*selectedMenu = selectedMenu->selectedIndex() + menuChange;
// update OLED line 2 here
screen.clearBufferRow(kMenuLine2);
screen.textToBuffer(selectedMenu->selectedString(), kMenuLine2);
if (debug) debug->printf("%s \r\n", selectedMenu->selectedString().c_str());
}
// Action menu item
if (menuEnc.hasPressed())
{
if (debug) debug->printf("Action Menu Item!\r\n");
// Are we changing menus?
if (selectedMenu->type() == menuOfMenus)
{
// point selected menu to the new menu
// FIXME. Make this function abstract virtual of base class or get dynamic_cast working. BTW: C++ sucks / Obj-c rocks / Right now.
if (selectedMenu == &mainMenu) selectedMenu = mainMenu.selectedMenu();
else if (debug) debug->printf("FIXME: You've missed a SPKMenuOfMenus");
// reset the selection within that menu to the first position
(*selectedMenu) = 0;
// update OLED lines 1&2
screen.clearBufferRow(kMenuLine1);
screen.clearBufferRow(kMenuLine2);
screen.textToBuffer(selectedMenu->title, kMenuLine1);
screen.textToBuffer(selectedMenu->selectedString(), kMenuLine2);
if (debug)
{
debug->printf("\r\n");
debug->printf("%s \r\n", selectedMenu->title.c_str());
debug->printf("%s \r\n", selectedMenu->selectedString().c_str());
}
}
// Are we cancelling?
else if (selectedMenu->type() == payload && selectedMenu->selectedIndex() == 0)
{
selectedMenu = lastSelectedMenu;
// update OLED lines 1&2
screen.clearBufferRow(kMenuLine1);
screen.clearBufferRow(kMenuLine2);
screen.textToBuffer(selectedMenu->title, kMenuLine1);
screen.textToBuffer(selectedMenu->selectedString(), kMenuLine2);
if (debug)
{
debug->printf("\r\n");
debug->printf("%s \r\n", selectedMenu->title.c_str());
debug->printf("%s \r\n", selectedMenu->selectedString().c_str());
}
}
// With that out of the way, we should be actioning a specific menu's payload?
else if (selectedMenu == &mixModeMenu)
{
mixMode = mixModeMenu.selectedPayload1();
bool ok = false;
std::string sentOK;
std::stringstream sentMSG;
// Set keying parameters
switch (mixModeMenu.selectedPayload1()) {
case lumaKey:
ok = setKeyParamsTo(0);
sentMSG << "Keyer Params 0, ";
break;
case chromaKey1:
ok = setKeyParamsTo(1);
sentMSG << "Keyer Params 1, ";
break;
}
// Set keying on or off
switch (mixModeMenu.selectedPayload1()) {
case blend:
case additive:
ok = ok && tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustKeyerEnable, false);
sentMSG << "Keyer Off";
break;
case lumaKey:
case chromaKey1:
case chromaKey2:
case chromaKey3:
ok = ok && tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustKeyerEnable, true);
sentMSG << "Keyer On";
break;
}
if (ok) sentOK = "Sent:";
else sentOK = "Send Error:";
screen.clearBufferRow(kTVOneStatusLine);
screen.textToBuffer(sentOK + sentMSG.str(), kTVOneStatusLine);
if (debug) { debug->printf("Changing mix mode"); }
}
else if (selectedMenu == &resolutionMenu)
{
bool ok = false;
ok = tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustOutputsOutputResolution, resolutionMenu.selectedPayload1());
ok = ok && tvOne.command(kTV1SourceRGB1, kTV1WindowIDA, kTV1FunctionAdjustSourceEDID, resolutionMenu.selectedPayload2());
ok = ok && tvOne.command(kTV1SourceRGB2, kTV1WindowIDA, kTV1FunctionAdjustSourceEDID, resolutionMenu.selectedPayload2());
std::string sentOK;
if (ok) sentOK = "Sent: ";
else sentOK = "Send Error: ";
std::stringstream sentMSG;
sentMSG << "Res " << resolutionMenu.selectedPayload1() << ", EDID " << resolutionMenu.selectedPayload2();
screen.clearBufferRow(kTVOneStatusLine);
screen.textToBuffer(sentOK + sentMSG.str(), kTVOneStatusLine);
if (debug) { debug->printf("Changing resolution"); }
}
else if (selectedMenu == &commsMenu)
{
std::string commsTypeString = "Network: --";
std::stringstream commsStatus;
// Tear down any existing comms
// This is the action of commsNone
// And also clears the way for other comms actions
if (osc) {delete osc; osc = NULL;}
if (ethernet) {delete ethernet; ethernet = NULL;}
if (artNet) {delete artNet; artNet = NULL;}
if (dmx) {delete dmx; dmx = NULL;}
// Ensure we can't change to comms modes the hardware isn't switched to
if (rj45Mode == rj45DMX && (commsMenu.selectedPayload1() == commsOSC || commsMenu.selectedPayload1() == commsArtNet))
{
commsTypeString = "RJ45 not in Ethernet mode";
}
else if (rj45Mode == rj45Ethernet && (commsMenu.selectedPayload1() == commsDMXIn || commsMenu.selectedPayload1() == commsDMXOut))
{
commsTypeString = "RJ45 not in DMX mode";
}
// Action!
else if (commsMenu.selectedPayload1() == commsOSC)
{
commsMode = commsOSC;
commsTypeString = "OSC: ";
ethernet = new EthernetNetIf(
IpAddr(kOSCMbedIPAddress),
IpAddr(kOSCMbedSubnetMask),
IpAddr(kOSCMbedGateway),
IpAddr(kOSCMbedDNS)
);
EthernetErr ethError = ethernet->setup();
if(ethError)
{
if (debug) debug->printf("Ethernet setup error, %d", ethError);
commsStatus << "Ethernet setup failed";
commsMenu = commsNone;
// break out of here. this setup should be a function that returns a boolean
}
osc = new OSCClass();
osc->setReceiveMessage(&recMessage);
osc->begin(kOSCMbedPort);
commsStatus << "Listening on " << kOSCMbedPort;
}
else if (commsMenu.selectedPayload1() == commsArtNet)
{
commsMode = commsArtNet;
commsTypeString = "ArtNet: ";
artNet = new DmxArtNet();
artNet->BindIpAddress = IpAddr(kArtNetBindIPAddress);
artNet->BCastAddress = IpAddr(kArtNetBroadcastAddress);
artNet->InitArtPollReplyDefaults();
artNet->ArtPollReply.PortType[0] = 128; // output
artNet->ArtPollReply.PortType[2] = 64; // input
artNet->ArtPollReply.GoodInput[2] = 4;
artNet->Init();
artNet->SendArtPollReply(); // announce to art-net nodes
commsStatus << "Listening";
}
else if (commsMenu.selectedPayload1() == commsDMXIn)
{
commsMode = commsDMXIn;
commsTypeString = "DMX In: ";
dmxDirectionDOUT = 0;
dmx = new DMX(kMBED_RS485_TTLTX, kMBED_RS485_TTLRX);
}
else if (commsMenu.selectedPayload1() == commsDMXOut)
{
commsMode = commsDMXOut;
commsTypeString = "DMX Out: ";
dmxDirectionDOUT = 1;
dmx = new DMX(kMBED_RS485_TTLTX, kMBED_RS485_TTLRX);
}
screen.clearBufferRow(kCommsStatusLine);
screen.textToBuffer(commsTypeString + commsStatus.str(), kCommsStatusLine);
}
else
{
if (debug) { debug->printf("Warning: No action identified"); }
}
}
// Send any updates to the display
screen.sendBuffer();
//// MIX MIX MIX MIX MIX MIX MIX MIX MIX MIX MIX MIXMIX MIX MIXMIX MIX MIX MIX MIX MIXMIX MIX MIX
bool updateFade = false;
float xFade = 0;
float fadeUp = 1;
//// TASK: Process control surface
// Get new states of tap buttons, remembering at end of loop() assign these current values to the previous variables
const bool tapLeft = !tapLeftDIN;
const bool tapRight = !tapRightDIN;
// We're going to cache the analog in reads, as have seen wierdness otherwise
const float xFadeAINCached = 1-xFadeAIN.read();
const float fadeUpAINCached = fadeUpAIN.read();
// When a tap is depressed, we can ignore any move of the crossfader but not fade to black
if (tapLeft || tapRight)
{
// If both are pressed, take to the one that is new, ie. not the first pressed.
if (tapLeft && tapRight)
{
xFade = tapLeftWasFirstPressed ? 1 : 0;
}
// If just one is pressed, take to that and remember which is pressed
else if (tapLeft)
{
xFade = 0;
tapLeftWasFirstPressed = 1;
}
else if (tapRight)
{
xFade = 1;
tapLeftWasFirstPressed = 0;
}
}
else xFade = fadeCalc(xFadeAINCached, xFadeTolerance);
fadeUp = 1.0 - fadeCalc(fadeUpAINCached, fadeUpTolerance);
//// TASK: Process Network Comms
if (commsMode == commsOSC)
{
if (osc->newMessage)
{
osc->newMessage = false; // fixme!
processOSC(xFade, fadeUp);
}
}
if (commsMode == commsArtNet)
{
if (artNet->Work()) processArtNet(xFade, fadeUp);
}
if (commsMode == commsDMXIn)
{
processDMXIn(xFade, fadeUp);
}
if (commsMode == commsDMXOut)
{
processDMXOut(xFade, fadeUp);
}
// WISH: Really, we should have B at 100% and A fading in over that, with fade to black implemented as a fade in black layer on top of that correct mix.
// There is no way to implement that though, and the alphas get messy, so this is the only way (afaik).
// Calculate new A&B fade percents
int newFadeAPercent = 0;
int newFadeBPercent = 0;
switch (mixMode) {
case blend:
case additive:
newFadeAPercent = (1.0-xFade) * fadeUp * 100.0;
newFadeBPercent = xFade * fadeUp * 100.0;
break;
case lumaKey:
case chromaKey1:
case chromaKey2:
case chromaKey3:
newFadeAPercent = (1.0-xFade) * fadeUp * 100.0;
newFadeBPercent = fadeUp * 100.0;
break;
}
// Send to TVOne if percents have changed
if (newFadeAPercent != fadeAPercent)
{
fadeAPercent = newFadeAPercent;
updateFade = true;
fadeAPO = fadeAPercent / 100.0;
tvOne.command(0, kTV1WindowIDA, kTV1FunctionAdjustWindowsMaxFadeLevel, fadeAPercent);
}
if (newFadeBPercent != fadeBPercent)
{
fadeBPercent = newFadeBPercent;
updateFade = true;
fadeBPO = fadeBPercent / 100.0;
tvOne.command(0, kTV1WindowIDB, kTV1FunctionAdjustWindowsMaxFadeLevel, fadeBPercent);
}
if (updateFade && debug)
{
debug->printf("\r\n");
//debug->printf("xFade = %3f fadeUp = %3f \r\n", xFadeAIN.read(), fadeUpAIN.read());
debug->printf("xFade = %3f fadeUp = %3f \r\n", xFadeAINCached, fadeUpAINCached);
debug->printf("xFade = %3f fadeUp = %3f fadeA% = %i fadeB% = %i \r\n", xFade, fadeUp, fadeAPercent, fadeBPercent);
}
}
}