#pragma once #ifdef LAUNCHPAD #include "communicator.hpp" // The interface for driving the Novation Launchpad Pro class ILaunchpadPro { public: static LaunchpadKey RC2Key(int r, int c) { return (LaunchpadKey)(R8C1 + c + 10 * (7 - r)); } static bool IsPlayKey(LaunchpadKey key) // true if key is a squared key (not a function key!) { if(key >= R8C1 && key <= R1C8) { int v = ((int)key - 1) % 10; return v < 8; } return false; } static bool Key2RC(LaunchpadKey key, int *r, int *c) { if(IsPlayKey(key)) { int v = (int)key - 1; int n = v / 10; *c = v % 10; *r = 8 - n; return true; } return false; } static bool IsValidkey(LaunchpadKey key) { return key >= RECORD_ARM && key != reserved_unused0 && key != reserved_unused1; } static LaunchpadMessage *Led(LaunchpadMessage *dest, LaunchpadKey key, LaunchpadLed led) { switch(led.status) { case ButtonColorType::Normal: LedColor(dest, key, led.r_color); break; case ButtonColorType::RGB: LedRGB(dest, key, led.r_color, led.g, led.b); break; case ButtonColorType::Flash: LedFlash(dest, key, led.r_color); break; case ButtonColorType::Pulse: LedPulse(dest, key, led.r_color); break; } return dest; } static LaunchpadMessage *Led(LaunchpadMessage *dest, int r, int c, LaunchpadLed led) { return Led(dest, RC2Key(r, c), led); } static LaunchpadMessage *LedOff(LaunchpadMessage *dest, int r, int c) { return LedOff(dest, RC2Key(r, c)); } static LaunchpadMessage *LedOff(LaunchpadMessage *dest, LaunchpadKey key) { return LedColor(dest, key, 0); } static LaunchpadMessage *LedColor(LaunchpadMessage *dest, LaunchpadKey key, int color) { dest->key = key; dest->cmd = KEYON; dest->param0 = color & 0x3f; return dest; } static LaunchpadMessage *LedColor(LaunchpadMessage *dest, int r, int c, int color) { return LedColor(dest, RC2Key(r, c), color); } static LaunchpadMessage *RegisterScene(LaunchpadMessage *dest, LaunchpadScene scene, bool registr) { dest->cmd = REGISTERSCENE; dest->currentScene = SceneAll; dest->param0 = registr ? 1 : 0; dest->param1 = scene; return dest; } static LaunchpadMessage *SetScene(LaunchpadMessage *dest, LaunchpadScene scene) { dest->cmd = SETSCENE; dest->currentScene = scene; return dest; } static LaunchpadMessage *GetNumLaunchpads(LaunchpadMessage *dest) { dest->currentScene = SceneAll; dest->cmd = GETNUMLAUNCHPADS; return dest; } static LaunchpadMessage *SetStandaloneMode(LaunchpadMessage *dest, LaunchpadMode mode) { dest->cmd = SET_STANDALONE_MODE; dest->param0 = mode; return dest; } static LaunchpadMessage *SetLiveMode(LaunchpadMessage *dest, LaunchpadLiveMode mode) { dest->cmd = SET_LIVE_MODE; dest->param0 = mode; return dest; } static LaunchpadMessage *SetStatus(LaunchpadMessage *dest, LaunchpadStatus status) { dest->cmd = SETSTATUS; dest->param0 = status; return dest; } static LaunchpadMessage *SideLed(LaunchpadMessage *dest, int color) { dest->cmd = SIDE_LED; dest->param0 = color & 0x3f; return dest; } static LaunchpadMessage *LightAll(LaunchpadMessage *dest, int color) { dest->cmd = LED_ALL; dest->param0 = color & 0x3f; return dest; } static LaunchpadMessage *LedFlash(LaunchpadMessage *dest, LaunchpadKey key, int color) { dest->key = key; dest->cmd = FLASH_KEY; dest->param0 = color & 0x3f; return dest; } static LaunchpadMessage *LedFlash(LaunchpadMessage *dest, int r, int c, int color) { return LedFlash(dest, RC2Key(r, c), color); } static LaunchpadMessage *LedPulse(LaunchpadMessage *dest, LaunchpadKey key, int color) { dest->key = key; dest->cmd = PULSE_KEY; dest->param0 = color & 0x3f; return dest; } static LaunchpadMessage *LedPulse(LaunchpadMessage *dest, int r, int c, int color) { return LedPulse(dest, RC2Key(r, c), color); } static LaunchpadMessage *LedRGB(LaunchpadMessage *dest, LaunchpadKey key, int c_r, int c_g, int c_b) { dest->key = key; dest->cmd = LED_RGB; dest->param0 = ((c_r & 0x3f) << 8) | (c_g & 0x3f); // R,G dest->param1 = (c_b & 0x3f); // B return dest; } static LaunchpadMessage *LedRGB(LaunchpadMessage *dest, int r, int c, int c_r, int c_g, int c_b) { return LedRGB(dest, RC2Key(r, c), c_r, c_g, c_b); } }; class launchpadDriver { // uses keys SESSION, NOTE, DEVICE, USER // as page keys public: static const int ALL_PAGES = -1; launchpadDriver(LaunchpadScene scene, int maxPage) { comm = new communicator(); numPages = maxPage; myScene = scene; Reset(ALL_LAUNCHPADS); lastCheck = 0; currentPage = 0; registerMyScene(true); } ~launchpadDriver() { registerMyScene(false); delete comm; } bool Connected() { return comm->Connected(); } void LedRGB(int lp, int page, int r, int c, int c_r, int c_g, int c_b) { return LedRGB(lp, page, ILaunchpadPro::RC2Key(r, c), c_r, c_g, c_b); } void LedPulse(int lp, int page, int r, int c, int color) { return LedPulse(lp, page, ILaunchpadPro::RC2Key(r, c), color); } void LedColor(int lp, int page, int r, int c, int color) { return LedColor(lp, page, ILaunchpadPro::RC2Key(r, c), color); } void LedFlash(int lp, int page, int r, int c, int color) { return LedFlash(lp, page, ILaunchpadPro::RC2Key(r, c), color); } void Led(int lp, int page, int r, int c, LaunchpadLed led) { return Led(lp, page, ILaunchpadPro::RC2Key(r, c), led); } void Led(int lp, int page, LaunchpadKey key, LaunchpadLed led) { setValue(lp, page, key, led); } void LedColor(int lp, int page, LaunchpadKey key, int colr) { LaunchpadLed led; led.status = ButtonColorType::Normal; led.r_color = colr; Led(lp, page, key, led); } void LedFlash(int lp, int page, LaunchpadKey key, int colr) { LaunchpadLed led; led.status = ButtonColorType::Flash; led.r_color = colr; Led(lp, page, key, led); } void LedPulse(int lp, int page, LaunchpadKey key, int colr) { LaunchpadLed led; led.status = ButtonColorType::Pulse; led.r_color = colr; Led(lp, page, key, led); } void LedRGB(int lp, int page, LaunchpadKey key, int c_r, int c_g, int c_b) { LaunchpadLed led; led.status = ButtonColorType::RGB; led.r_color = c_r; led.g = c_g; led.b = c_b; Led(lp, page, key, led); } int GetPage() { return currentPage; } void SetPage(int page) { if(page >= 0 && page < numPages && currentPage != page) { currentPage = page; Clear(ALL_LAUNCHPADS); redrawCache(); } } void Clear(int lp) { LaunchpadMessage dest; ILaunchpadPro::LightAll(&dest, 0); dest.currentScene = myScene; dest.lpNumber = lp; comm->Write(dest); } void Reset(int lp) { SetPage(0); Clear(lp); comm->clear(); } int GetNumLaunchpads() // SYNC read { LaunchpadMessage dest; ILaunchpadPro::GetNumLaunchpads(&dest); comm->Write(dest); LaunchpadMessage rcv; if(syncRead(&rcv, LaunchpadCommand::GETNUMLAUNCHPADS)) return rcv.lpNumber; return 0; } void SetAutoPageKey(LaunchpadKey key, int page) { if(page >= 0 && page < numPages) { autoPages[key] = page; } else if(page == -1) // remove key? { auto it = autoPages.find(key); if(it != autoPages.end()) autoPages.erase(it); } } void drive_led(int lp, LaunchpadKey key, LaunchpadLed led) { LaunchpadMessage dest; ILaunchpadPro::Led(&dest, key, led); dest.currentScene = myScene; dest.lpNumber = lp; comm->Write(dest); } protected: int numPages; int currentPage; LaunchpadScene myScene; communicator *comm; virtual void redrawCache() { drive_autopage(); }; void setValue(int lp, int page, LaunchpadKey key, LaunchpadLed led) { if(page == currentPage || page == launchpadDriver::ALL_PAGES) drive_led(lp, key, led); } int isAutoPageKey(LaunchpadMessage *msg) { if(msg->cmd == LaunchpadCommand::KEYON) { auto it = autoPages.find(msg->key); if(it != autoPages.end()) return it->second; } return -1; } bool syncRead(LaunchpadMessage *rv, LaunchpadCommand waitCmd, int timeout = 1000) { DWORD now = GetTickCount(); LaunchpadMessage msg; do { msg = comm->Read(); if(msg.cmd == waitCmd) { memcpy(rv, &msg, sizeof(msg)); return true; } } while((GetTickCount() - now) <= (DWORD)timeout); return false; } void registerMyScene(bool registr) { lastCheck = GetTickCount(); LaunchpadMessage dest; ILaunchpadPro::RegisterScene(&dest, myScene, registr); dest.lpNumber = ALL_LAUNCHPADS; if(comm->Open()) { comm->Write(dest); if(registr) SetPage(0); comm->Write(dest); } } uint32_t lastCheck; private: void drive_autopage() { for(std::map::iterator it = autoPages.begin(); it != autoPages.end(); ++it) { for(int k = 0; k < numPages; k++) { LaunchpadMessage dest; ILaunchpadPro::Led(&dest, it->first, LaunchpadLed::Color(it->second == currentPage ? 57 : 11)); comm->Write(dest); } } } private: std::map autoPages; }; struct launchpadControl { public: virtual ~launchpadControl() {}; void Draw(launchpadDriver *drv, bool force = false) { if((force || m_dirty) && (drv->GetPage() == m_page || m_page == launchpadDriver::ALL_PAGES)) { draw(drv); } m_dirty = false; m_lastDrawnValue = getValue(); } bool Intersect(int lp, int page, LaunchpadKey key, bool shift) { if(m_shifted == shift) { if(page == m_page || page == launchpadDriver::ALL_PAGES || m_page == launchpadDriver::ALL_PAGES) { if(lp == m_lpNumber || lp == ALL_LAUNCHPADS || m_lpNumber == ALL_LAUNCHPADS) return intersect(key); } } return false; } void ChangeFromGUI(launchpadDriver *drv) // gui updated: the new value is already in the binded parameter { m_dirty = true; Draw(drv); } virtual void onLaunchpadKey(Module *pModule, LaunchpadMessage msg) = 0; bool DetectGUIChanges() { return getValue() != m_lastDrawnValue; } int ID() { return is_light ? pBindedLight->firstLightId : pBindedParam->paramId; } void bindWidget(ModuleLightWidget *p) { pBindedLight = p; is_light = true; } void bindWidget(ParamWidget *p) { pBindedParam = p; } protected: virtual void draw(launchpadDriver *drv) = 0; virtual bool intersect(LaunchpadKey key) { return false; } launchpadControl(int lp, int page, LaunchpadKey key, bool shifted) { m_lpNumber = lp; is_light = false; m_page = page; m_key = key; m_shifted = shifted; pBindedLight = NULL; pBindedParam = NULL; m_dirty = true; m_lastDrawnValue = -10202020; } float getValue() { return is_light ? pBindedLight->module->lights[pBindedLight->firstLightId].getBrightness() : pBindedParam->value; } void setValue(Module *pModule, float v) { if(v != getValue()) { if(is_light) pBindedLight->module->lights[pBindedLight->firstLightId].value = v; else { SVGKnob *pk = (SVGKnob *)dynamic_cast(pBindedParam); if(pk != NULL) { pModule->params[pBindedParam->paramId].value = pBindedParam->value = v; pk->dirty = true; } else { SVGFader *pk1 = (SVGFader *)dynamic_cast(pBindedParam); if(pk1 != NULL) { pModule->params[pBindedParam->paramId].value = pBindedParam->value = v; pk1->dirty = true; } else pBindedParam->setValue(v); } } m_dirty = true; } } int m_lpNumber; int m_page; bool is_light; LaunchpadKey m_key; bool m_shifted; ModuleLightWidget *pBindedLight; ParamWidget *pBindedParam; bool m_dirty; float m_lastDrawnValue; }; struct LaunchpadBindingDriver : public launchpadDriver { public: LaunchpadBindingDriver(Module *pMod, LaunchpadScene scene, int maxPage) : launchpadDriver(scene, maxPage) { pModule = pMod; } virtual ~LaunchpadBindingDriver() { for(std::map::iterator it = m_bindings.begin(); it != m_bindings.end(); ++it) { if(it->second != NULL) delete it->second; } m_bindings.clear(); } void Add(launchpadControl *ctrl, ParamWidget *p) { ctrl->bindWidget(p); int id = ctrl->ID(); m_bindings[id] = ctrl; #ifdef DEBUG info("binded param %i ", id); #endif } void Add(launchpadControl *ctrl, ModuleLightWidget *p) { ctrl->bindWidget(p); int id = ctrl->ID(); m_bindings[0x8000 | id] = ctrl; #ifdef DEBUG info("binded light %i ", 0x8000 | id); #endif } void ProcessLaunchpad() { processGUI(); processLaunchpadKeys(); if(!Connected() && (GetTickCount() - lastCheck) >= 2000) { registerMyScene(true); } } protected: virtual void redrawCache() override { for(std::map::iterator it = m_bindings.begin(); it != m_bindings.end(); ++it) { it->second->Draw(this, true); } launchpadDriver::redrawCache(); } private: Module *pModule; std::mapm_bindings; void processGUI() { for(std::map::iterator it = m_bindings.begin(); it != m_bindings.end(); ++it) { if(it->second->DetectGUIChanges()) { it->second->ChangeFromGUI(this); } } } void processLaunchpadKeys() { LaunchpadMessage msg; do { msg = comm->Read(); if(msg.status != LaunchpadKeyStatus::keyNone && (msg.currentScene == SceneAll || msg.currentScene == myScene)) { int page = isAutoPageKey(&msg); #ifdef DEBUG info("MSG: from LP=%i %i scene=%i ispage=%i key=%i", msg.lpNumber, msg.cmd, msg.currentScene, page, msg.key); #endif if(page >= 0 && msg.status == LaunchpadKeyStatus::keyDown && !msg.shiftDown) { SetPage(page); } else if(msg.cmd == LaunchpadCommand::RESET) { SetPage(currentPage); } else if(msg.cmd == LaunchpadCommand::SETSCENE) { #ifdef DEBUG info("MSG: set scene=%i myscene=%i", msg.param1, myScene); #endif if(myScene == msg.param1) { redrawCache(); } } else { for(std::map::iterator it = m_bindings.begin(); it != m_bindings.end(); ++it) { if(it->second->Intersect(msg.lpNumber, GetPage(), msg.key, msg.shiftDown)) { #ifdef DEBUG info("MSG: lp#=%i page=%i, key=%i shift=%i detected: %i", msg.lpNumber, GetPage(), msg.key, msg.shiftDown, it->first); #endif it->second->onLaunchpadKey(pModule, msg); } } } } } while(msg.status != LaunchpadKeyStatus::keyNone); } }; #endif