diff --git a/src/Makefile b/src/Makefile index 540ae684..a8a8ccad 100644 --- a/src/Makefile +++ b/src/Makefile @@ -9,7 +9,8 @@ CMSIS_DIR = ../CMSIS_5/CMSIS OBJS = main.o kernel.o minidexed.o config.o userinterface.o uimenu.o \ mididevice.o midikeyboard.o serialmididevice.o pckeyboard.o \ sysexfileloader.o performanceconfig.o perftimer.o \ - effect_compressor.o effect_platervbstereo.o uibuttons.o midipin.o + effect_compressor.o effect_platervbstereo.o uibuttons.o midipin.o \ + dawcontroller.o OPTIMIZE = -O3 diff --git a/src/common.h b/src/common.h index 902f74b7..d982c821 100644 --- a/src/common.h +++ b/src/common.h @@ -2,6 +2,8 @@ #ifndef _common_h #define _common_h +#define ARRAY_LENGTH(x) (sizeof(x) / sizeof((x)[0])) + inline long maplong(long x, long in_min, long in_max, long out_min, long out_max) { return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; } @@ -16,6 +18,12 @@ inline float32_t mapfloat(int val, int in_min, int in_max, float32_t out_min, fl return (val - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; } +inline long mapfloatr(int val, int in_min, int in_max, float32_t out_min, float32_t out_max) +{ + return lround((val - in_min) * (out_max - out_min) / (in_max - in_min) + out_min); +} + + #define constrain(amt, low, high) ({ \ __typeof__(amt) _amt = (amt); \ __typeof__(low) _low = (low); \ diff --git a/src/config.cpp b/src/config.cpp index 482b2b28..816243f2 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -178,19 +178,35 @@ void CConfig::Load (void) m_nMIDIButtonCh = m_Properties.GetNumber ("MIDIButtonCh", 0); m_nMIDIButtonNotes = m_Properties.GetNumber ("MIDIButtonNotes", 0); + m_nMIDIButtonPrev = m_Properties.GetNumber ("MIDIButtonPrev", 0); m_nMIDIButtonNext = m_Properties.GetNumber ("MIDIButtonNext", 0); m_nMIDIButtonBack = m_Properties.GetNumber ("MIDIButtonBack", 0); m_nMIDIButtonSelect = m_Properties.GetNumber ("MIDIButtonSelect", 0); m_nMIDIButtonHome = m_Properties.GetNumber ("MIDIButtonHome", 0); + m_MIDIButtonActionPrev = m_Properties.GetString ("MIDIButtonActionPrev", ""); + m_MIDIButtonActionNext = m_Properties.GetString ("MIDIButtonActionNext", ""); + m_MIDIButtonActionBack = m_Properties.GetString ("MIDIButtonActionBack", ""); + m_MIDIButtonActionSelect = m_Properties.GetString ("MIDIButtonActionSelect", ""); + m_MIDIButtonActionHome = m_Properties.GetString ("MIDIButtonActionHome", ""); + m_nMIDIButtonPgmUp = m_Properties.GetNumber ("MIDIButtonPgmUp", 0); m_nMIDIButtonPgmDown = m_Properties.GetNumber ("MIDIButtonPgmDown", 0); m_nMIDIButtonBankUp = m_Properties.GetNumber ("MIDIButtonBankUp", 0); m_nMIDIButtonBankDown = m_Properties.GetNumber ("MIDIButtonBankDown", 0); m_nMIDIButtonTGUp = m_Properties.GetNumber ("MIDIButtonTGUp", 0); m_nMIDIButtonTGDown = m_Properties.GetNumber ("MIDIButtonTGDown", 0); - + + m_MIDIButtonActionPgmUp = m_Properties.GetString ("MIDIButtonActionPgmUp", ""); + m_MIDIButtonActionPgmDown = m_Properties.GetString ("MIDIButtonActionPgmDown", ""); + m_MIDIButtonActionBankUp = m_Properties.GetString ("MIDIButtonActionBankUp", ""); + m_MIDIButtonActionBankDown = m_Properties.GetString ("MIDIButtonActionBankDown", ""); + m_MIDIButtonActionTGUp = m_Properties.GetString ("MIDIButtonActionTGUp", ""); + m_MIDIButtonActionTGDown = m_Properties.GetString ("MIDIButtonActionTGDown", ""); + + m_bDAWControllerEnabled = m_Properties.GetNumber ("DAWControllerEnabled", 0) != 0; + m_bEncoderEnabled = m_Properties.GetNumber ("EncoderEnabled", 0) != 0; m_nEncoderPinClock = m_Properties.GetNumber ("EncoderPinClock", 10); m_nEncoderPinData = m_Properties.GetNumber ("EncoderPinData", 9); @@ -652,6 +668,31 @@ unsigned CConfig::GetMIDIButtonHome (void) const return m_nMIDIButtonHome; } +const char *CConfig::GetMIDIButtonActionPrev (void) const +{ + return m_MIDIButtonActionPrev.c_str(); +} + +const char *CConfig::GetMIDIButtonActionNext (void) const +{ + return m_MIDIButtonActionNext.c_str(); +} + +const char *CConfig::GetMIDIButtonActionBack (void) const +{ + return m_MIDIButtonActionBack.c_str(); +} + +const char *CConfig::GetMIDIButtonActionSelect (void) const +{ + return m_MIDIButtonActionSelect.c_str(); +} + +const char *CConfig::GetMIDIButtonActionHome (void) const +{ + return m_MIDIButtonActionHome.c_str(); +} + unsigned CConfig::GetMIDIButtonPgmUp (void) const { return m_nMIDIButtonPgmUp; @@ -682,6 +723,41 @@ unsigned CConfig::GetMIDIButtonTGDown (void) const return m_nMIDIButtonTGDown; } +const char *CConfig::GetMIDIButtonActionPgmUp (void) const +{ + return m_MIDIButtonActionPgmUp.c_str(); +} + +const char *CConfig::GetMIDIButtonActionPgmDown (void) const +{ + return m_MIDIButtonActionPgmDown.c_str(); +} + +const char *CConfig::GetMIDIButtonActionBankUp (void) const +{ + return m_MIDIButtonActionBankUp.c_str(); +} + +const char *CConfig::GetMIDIButtonActionBankDown (void) const +{ + return m_MIDIButtonActionBankDown.c_str(); +} + +const char *CConfig::GetMIDIButtonActionTGUp (void) const +{ + return m_MIDIButtonActionTGUp.c_str(); +} + +const char *CConfig::GetMIDIButtonActionTGDown (void) const +{ + return m_MIDIButtonActionTGDown.c_str(); +} + +bool CConfig::GetDAWControllerEnabled (void) const +{ + return m_bDAWControllerEnabled; +} + bool CConfig::GetEncoderEnabled (void) const { return m_bEncoderEnabled; diff --git a/src/config.h b/src/config.h index 5d0cbc15..be311be6 100644 --- a/src/config.h +++ b/src/config.h @@ -210,12 +210,20 @@ class CConfig // Configuration for MiniDexed // MIDI Button Navigation unsigned GetMIDIButtonCh (void) const; unsigned GetMIDIButtonNotes (void) const; + unsigned GetMIDIButtonPrev (void) const; unsigned GetMIDIButtonNext (void) const; unsigned GetMIDIButtonBack (void) const; unsigned GetMIDIButtonSelect (void) const; unsigned GetMIDIButtonHome (void) const; + // Action type for Midi buttons: "click", "doubleclick", "longpress", "dec", "inc", "" + const char *GetMIDIButtonActionPrev (void) const; + const char *GetMIDIButtonActionNext (void) const; + const char *GetMIDIButtonActionBack (void) const; + const char *GetMIDIButtonActionSelect (void) const; + const char *GetMIDIButtonActionHome (void) const; + // MIDI Button Program and TG Selection unsigned GetMIDIButtonPgmUp (void) const; unsigned GetMIDIButtonPgmDown (void) const; @@ -224,6 +232,16 @@ class CConfig // Configuration for MiniDexed unsigned GetMIDIButtonTGUp (void) const; unsigned GetMIDIButtonTGDown (void) const; + // Action type for buttons: "click", "doubleclick", "longpress", "dec", "inc", "" + const char *GetMIDIButtonActionPgmUp (void) const; + const char *GetMIDIButtonActionPgmDown (void) const; + const char *GetMIDIButtonActionBankUp (void) const; + const char *GetMIDIButtonActionBankDown (void) const; + const char *GetMIDIButtonActionTGUp (void) const; + const char *GetMIDIButtonActionTGDown (void) const; + + bool GetDAWControllerEnabled (void) const; + // KY-040 Rotary Encoder // GPIO pin numbers are chip numbers, not header positions bool GetEncoderEnabled (void) const; @@ -326,6 +344,18 @@ class CConfig // Configuration for MiniDexed std::string m_ButtonActionTGUp; std::string m_ButtonActionTGDown; + std::string m_MIDIButtonActionPrev; + std::string m_MIDIButtonActionNext; + std::string m_MIDIButtonActionBack; + std::string m_MIDIButtonActionSelect; + std::string m_MIDIButtonActionHome; + std::string m_MIDIButtonActionPgmUp; + std::string m_MIDIButtonActionPgmDown; + std::string m_MIDIButtonActionBankUp; + std::string m_MIDIButtonActionBankDown; + std::string m_MIDIButtonActionTGUp; + std::string m_MIDIButtonActionTGDown; + unsigned m_nDoubleClickTimeout; unsigned m_nLongPressTimeout; @@ -343,6 +373,8 @@ class CConfig // Configuration for MiniDexed unsigned m_nMIDIButtonTGUp; unsigned m_nMIDIButtonTGDown; + bool m_bDAWControllerEnabled; + bool m_bEncoderEnabled; unsigned m_nEncoderPinClock; unsigned m_nEncoderPinData; diff --git a/src/dawcontroller.cpp b/src/dawcontroller.cpp new file mode 100644 index 00000000..bc0d5471 --- /dev/null +++ b/src/dawcontroller.cpp @@ -0,0 +1,1648 @@ +// MiniDexed - Dexed FM synthesizer for bare metal Raspberry Pi +// Copyright (C) 2024 The MiniDexed Team +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +#include + +#include "dawcontroller.h" +#include "midikeyboard.h" +#include "minidexed.h" + +#define LINELEN 18 + +#define MIDI_DAW_CHANGE 0b10000 +#define MIDI_DAW_VOICE 1 +#define MIDI_DAW_TOGGLE_MONO 3 +#define MIDI_DAW_TOGGLE_PORTA_GLISS 4 +#define MIDI_DAW_TOGGLE_TG 5 +#define MIDI_DAW_SELECT_TG 6 +#define MIDI_DAW_SELECT_CHAN_TG 7 +#define MIDI_DAW_MENU_SELECT 8 +#define MIDI_DAW_MENU_BACK 9 +#define MIDI_DAW_MENU_PREV 10 +#define MIDI_DAW_MENU_NEXT 11 +#define MIDI_DAW_MENU_PRESS_PREV 12 +#define MIDI_DAW_MENU_PRESS_NEXT 13 +#define MIDI_DAW_MENU_HOME 14 +#define MIDI_DAW_DISPLAY_MODE_TOGGLE 17 +#define MIDI_DAW_ENC_VALUES_TOGGLE 18 +#define MIDI_DAW_ENC_0 20 +#define MIDI_DAW_ENC_1 21 +#define MIDI_DAW_ENC_2 22 +#define MIDI_DAW_ENC_3 23 +#define MIDI_DAW_ENC_4 24 +#define MIDI_DAW_ENC_5 25 +#define MIDI_DAW_ENC_6 26 +#define MIDI_DAW_ENC_7 27 + + +static void ArturiaDisplayWrite (CMIDIKeyboard *pKeyboard, const u8 *pHdr, const unsigned nHdrSize, + size_t nLineMaxLen, const bool bFill1, const bool bFill2, const char *pMenu, + const char *pParam, const char *pValue, + const bool bArrowLeft, const bool bArrowRight) +{ + size_t nParamLen = std::min (nLineMaxLen, strlen (pParam)); + size_t nMenuLen = strlen (pMenu); + size_t nFill1Len = bFill1 && nLineMaxLen > nParamLen + nMenuLen ? + nLineMaxLen - nParamLen - nMenuLen : 1; + + nFill1Len = std:: min (nLineMaxLen - nParamLen, nFill1Len); + nMenuLen = std::min (nLineMaxLen - nParamLen - nFill1Len, nMenuLen); + + size_t nLine1Len = nParamLen + nFill1Len + nMenuLen; + + size_t nValueLen = std::min (nLineMaxLen - 2, strlen (pValue)); + size_t nFill2Len = bFill2 ? nLineMaxLen - 2 - nValueLen : 0; + size_t nLine2Len = 1 + nValueLen + nFill2Len + 1; + + size_t nOffset = 0; + + uint8_t pLines[nHdrSize + nLine1Len + 2 + nLine2Len + 2]; + + memcpy (pLines, pHdr, nHdrSize); + nOffset += nHdrSize; + + memcpy (&pLines[nOffset], pParam, nParamLen); + nOffset += nParamLen; + + memset (&pLines[nOffset], ' ', nFill1Len); + nOffset += nFill1Len; + + memcpy (&pLines[nOffset], pMenu, nMenuLen); + nOffset += nMenuLen; + + pLines[nOffset++] = 0x00; + pLines[nOffset++] = 0x02; + + pLines[nOffset++] = bArrowLeft ? '<' : ' '; + + memcpy (&pLines[nOffset], pValue, nValueLen); + nOffset += nValueLen; + + memset (&pLines[nOffset], ' ', nFill2Len); + nOffset += nFill2Len; + + pLines[nOffset++] = bArrowRight ? '>' : ' '; + + pLines[nOffset++] = 0x00; + pLines[nOffset++] = 0xF7; + + // block character (0xFF) is not supported over MIDI, change to 0x7f + for (unsigned i = 0; i < sizeof pLines; ++i) + if (pLines[i] == 0xFF) + pLines[i] = 0x7F; + + pKeyboard->SendDebounce (pLines, nOffset, 0); +} + +enum ControlType +{ + CT_KNOB = 3, + CT_FADER, + CT_PAD, +}; + +enum HideAfter +{ + HA_NO = 0, + HA_YES = 2, +}; + +static std::string to_percent (int nValue) +{ + return std::to_string(mapfloatr (nValue, 0, 127, 0, 100)) + "%"; +} + +std::string to_midi_channel (int nValue) +{ + switch (nValue) + { + case CMIDIDevice::OmniMode: return "Omni"; + case CMIDIDevice::Disabled: return "Off"; + default: return std::to_string (nValue + 1); + } +} + +static void ArturiaDisplayInfoWrite (CMIDIKeyboard *pKeyboard, const uint8_t pDisplayHdr[3], ControlType Type, u8 uValue, const char *pName, const char *pValue) +{ + const uint8_t pHdr[] = {0xF0, 0x00, 0x20, 0x6B, 0x7F, 0x42, pDisplayHdr[0], pDisplayHdr[1], pDisplayHdr[2], 0x1F, Type, HA_NO, uValue, 0x00, 0x00, 0x01}; + + int nLine1Len = strlen (pName); + int nLine2Len = strlen (pValue); + int nOffset = 0; + + uint8_t pLines[sizeof pHdr + nLine1Len + 2 + nLine2Len + 2]; + + memcpy (pLines, pHdr, sizeof pHdr); + nOffset += sizeof pHdr; + + memcpy (pLines + nOffset, pName, nLine1Len); + nOffset += nLine1Len; + + pLines[nOffset++] = 0x00; + pLines[nOffset++] = 0x02; + + memcpy (pLines + nOffset, pValue, nLine2Len); + nOffset += nLine2Len; + + pLines[nOffset++] = 0x00; + pLines[nOffset++] = 0xf7; + + pKeyboard->SendDebounce (pLines, nOffset, 0); +} + +static void ArturiaShowNewCCValue (CMIDIKeyboard *pKeyboard, const uint8_t pDisplayHdr[3], u8 ucCh, u8 ucCC, u8 ucValue) +{ + char line1[LINELEN]; + char line2[LINELEN]; + + switch (ucCC) + { + case MIDI_CC_PORTAMENTO_TIME: + snprintf(line2, LINELEN, "%ld%%", mapfloatr (ucValue, 0, 127, 0, 99)); + ArturiaDisplayInfoWrite (pKeyboard, pDisplayHdr, CT_KNOB, ucValue, "Portamento Time", line2); + break; + case MIDI_CC_VOLUME: + snprintf(line1, LINELEN, "Volume Ch %d", ucCh + 1); + snprintf(line2, LINELEN, "%ld%%", mapfloatr (ucValue, 0, 127, 0, 100)); + ArturiaDisplayInfoWrite (pKeyboard, pDisplayHdr, CT_FADER, ucValue, line1, line2); + break; + case MIDI_CC_FREQUENCY_CUTOFF: + snprintf(line2, LINELEN, "%ld%%", mapfloatr (ucValue, 0, 127, 0, 99)); + ArturiaDisplayInfoWrite (pKeyboard, pDisplayHdr, CT_KNOB, ucValue, "Cutoff", line2); + break; + case MIDI_CC_RESONANCE: + snprintf(line2, LINELEN, "%ld%%", mapfloatr (ucValue, 0, 127, 0, 99)); + ArturiaDisplayInfoWrite (pKeyboard, pDisplayHdr, CT_KNOB, ucValue, "Resonance", line2); + break; + case MIDI_CC_REVERB_LEVEL: + snprintf(line2, LINELEN, "%ld%%", mapfloatr (ucValue, 0, 127, 0, 99)); + ArturiaDisplayInfoWrite (pKeyboard, pDisplayHdr, CT_KNOB, ucValue, "Reverb", line2); + break; + case MIDI_CC_DETUNE_LEVEL: + snprintf(line2, LINELEN, "%ld", mapfloatr (ucValue, 1, 127, -99, 99)); + ArturiaDisplayInfoWrite (pKeyboard, pDisplayHdr, CT_KNOB, ucValue, "Detune", line2); + break; + case MIDI_CC_PAN_POSITION: + snprintf(line2, LINELEN, "%d", ucValue); + ArturiaDisplayInfoWrite (pKeyboard, pDisplayHdr, CT_KNOB, ucValue, "Pan", line2); + break; + case MIDI_CC_BANK_SUSTAIN: + ArturiaDisplayInfoWrite (pKeyboard, pDisplayHdr, CT_PAD, ucValue, "Sustain", ucValue > 64 ? "On" : "Off"); + break; + case MIDI_CC_PORTAMENTO: + ArturiaDisplayInfoWrite (pKeyboard, pDisplayHdr, CT_PAD, ucValue, "Portamento", ucValue > 64 ? "On" : "Off"); + break; + case MIDI_CC_SOSTENUTO: + ArturiaDisplayInfoWrite (pKeyboard, pDisplayHdr, CT_PAD, ucValue, "Sostenuto", ucValue > 64 ? "On" : "Off"); + break; + case MIDI_CC_HOLD2: + ArturiaDisplayInfoWrite (pKeyboard, pDisplayHdr, CT_PAD, ucValue, "Hold", ucValue > 64 ? "On" : "Off"); + break; + case MIDI_CC_ALL_SOUND_OFF: + ArturiaDisplayInfoWrite (pKeyboard, pDisplayHdr, CT_PAD, ucValue, "All Sound Off", ""); + break; + } +} + +static void HandleMenuEvents (CUserInterface *pUI, u8 ucDC) +{ + switch (ucDC) + { + case MIDI_DAW_MENU_SELECT: + pUI->MIDIEventHandler (CUIMenu::MenuEventSelect); + break; + case MIDI_DAW_MENU_BACK: + pUI->MIDIEventHandler (CUIMenu::MenuEventBack); + break; + case MIDI_DAW_MENU_PREV: + pUI->MIDIEventHandler (CUIMenu::MenuEventStepDown); + break; + case MIDI_DAW_MENU_NEXT: + pUI->MIDIEventHandler (CUIMenu::MenuEventStepUp); + break; + case MIDI_DAW_MENU_PRESS_PREV: + pUI->MIDIEventHandler (CUIMenu::MenuEventPressAndStepDown); + break; + case MIDI_DAW_MENU_PRESS_NEXT: + pUI->MIDIEventHandler (CUIMenu::MenuEventPressAndStepUp); + break; + case MIDI_DAW_MENU_HOME: + pUI->MIDIEventHandler (CUIMenu::MenuEventHome); + break; + } +} + +class CDAWConnection +{ +public: + virtual void DisplayWrite (const char *pMenu, const char *pParam, + const char *pValue, bool bArrowDown, bool bArrowUp) = 0; + virtual void UpdateState () = 0; + virtual void UpdateMenu (CUIMenu::TCPageType Type, s8 ucPage, u8 ucOP, u8 ucTG) = 0; + virtual void MIDIListener (u8 ucCable, u8 ucChannel, u8 ucType, u8 ucP1, u8 ucP2) = 0; + virtual ~CDAWConnection (void) = default; +}; + +struct CColor +{ + uint8_t r; + uint8_t g; + uint8_t b; +}; + +static CColor padColors[8] = { + {0x3F, 0x3F, 0x11}, + {0x11, 0x11, 0x3F}, + {0x3F, 0x11, 0x3F}, + {0x11, 0x3F, 0x11}, + {0x3F, 0x11, 0x11}, + {0x11, 0x3F, 0x3F}, + {0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00}, +}; + +static CColor altPadColors[8] = { + {0x3F, 0x3F, 0x11}, + {0x11, 0x21, 0x3F}, + {0x3F, 0x11, 0x3F}, + {0x11, 0x3F, 0x11}, + {0x3F, 0x11, 0x11}, + {0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00}, +}; + +static CColor chColors[CMIDIDevice::TChannel::Disabled + 1] = { + {0x7F, 0x00, 0x00}, // 1 + {0x7F, 0x40, 0x00}, // 2 + {0x7F, 0x40, 0x40}, // 3 + {0x7F, 0x40, 0x7F}, // 4 + {0x7F, 0x7F, 0x00}, // 5 + {0x7F, 0x7F, 0x40}, // 6 + {0x7F, 0x7F, 0x7F}, // 7 + {0x40, 0x00, 0x40}, // 8 + {0x40, 0x40, 0x00}, // 9 + {0x40, 0x40, 0x40}, // 10 + {0x40, 0x40, 0x7F}, // 11 + {0x40, 0x7F, 0x00}, // 12 + {0x40, 0x7F, 0x40}, // 13 + {0x40, 0x7F, 0x7F}, // 14 + {0x00, 0x00, 0x40}, // 15 + {0x00, 0x40, 0x00}, // 16 + {0x7F, 0x7F, 0x7F}, // Omni + {0x00, 0x00, 0x00}, // Disabled +}; + +class CMiniLab3DawConnection : public CDAWConnection +{ +public: + CMiniLab3DawConnection (CMiniDexed *pSynthesizer, CMIDIKeyboard *pKeyboard, CConfig *pConfig, CUserInterface *pUI); + void DisplayWrite (const char *pMenu, const char *pParam, const char *pValue, bool bArrowDown, bool bArrowUp) override; + void UpdateState () override; + void UpdateMenu (CUIMenu::TCPageType Type, s8 ucPage, u8 ucOP, u8 ucTG) override; + void MIDIListener (u8 ucCable, u8 ucChannel, u8 ucType, u8 ucP1, u8 ucP2) override; +private: + enum TPadID { + MonoPad = 0, + PortamentoPad = 1, + SostenutoPad = 2, + SustainPad = 3, + SoundOffPad = 4, + HoldPad = 5, + TBDPad7 = 6, + ATPad = 7, + }; + enum TBankID { + BankA = 0x34, + BankB = 0x44, + }; + + static const u8 AllOP = 8; + static const unsigned nDefaultDisplayUpdateDelay = 2000; + + static void s_UpdateDisplay (TKernelTimerHandle hTimer, void *pParam, void *pContext); + void QueueUpdateDisplay (unsigned msec); + void UpdateDisplay (); + void ShowEncoderDisplay (); + void ShowValueDisplay (); + + void UpdateEncoder (uint8_t ucEncID, uint8_t ucValue); + void UpdateTGColor (uint8_t nTG); + void UpdateMonoColor (); + void UpdatePortamentoColor (); + void UpdateATColor (u8 ucAT); + void UpdateVolumeFaders (); + + void SetPadColor (TBankID BankID, TPadID PadID, u8 state); + void SetPadColor (TBankID BankID, TPadID PadID, u8 state, u8 state2); + void SetPadColor (TBankID BankID, TPadID PadID, CColor color, u8 state); + void SetPadColor (TBankID BankID, TPadID PadID, CColor color); + + void SetChannelAT (u8 ucValue); + void SetVoice (u8 ucChannel, u8 ucVoice); + void SetAlgorithm (u8 ucChannel, u8 ucAlgorithm); + void SetEncoder (u8 ucChannel, u8 ucEncId, u8 ucVoice); + void ToggleMonoMode (u8 ucChannel); + void TogglePortamentoGlisssando (u8 ucChannel); + void ToggleTG (u8 ucTG); + void SelectTG (u8 ucTG); + void SelectChanTG (u8 ucTG); + + CMiniDexed *m_pSynthesizer; + CMIDIKeyboard *m_pKeyboard; + CConfig *m_pConfig; + CUserInterface *m_pUI; + + bool m_bDisableEncoderUpdate = false; + + CUIMenu::TCPageType m_encoderPageType = CUIMenu::PageMain; + u8 m_ucEncoderPage = 0; + u8 m_ucEncoderOP = 0; + u8 m_ucEncoderTG = 0; + CUIMenu::TCParameterInfo *m_pEncoders; + + enum DisplayState { + DisplayMenu, + DisplayEncoder, + DisplayValues, + }; + DisplayState m_DisplayState = DisplayMenu; + + CUIMenu::TCParameterInfo m_pTGEncoders[4 + 3 + 22][8] = { + { + {CUIMenu::ParameterTG, CMiniDexed::TGParameterCutoff}, + {CUIMenu::ParameterTG, CMiniDexed::TGParameterResonance}, + {CUIMenu::ParameterTG, CMiniDexed::TGParameterReverbSend}, + {CUIMenu::ParameterTG, CMiniDexed::TGParameterMasterTune}, + {CUIMenu::ParameterTG, CMiniDexed::TGParameterPortamentoTime}, + {CUIMenu::ParameterTG, CMiniDexed::TGParameterProgram}, + {CUIMenu::ParameterNone}, + {CUIMenu::ParameterTG, CMiniDexed::TGParameterPan, .ToString=std::to_string}, + }, + { + {CUIMenu::ParameterTG, CMiniDexed::TGParameterMIDIChannel}, + {CUIMenu::ParameterTG, CMiniDexed::TGParameterVolume, .ToString=to_percent}, + {CUIMenu::ParameterNone}, + {CUIMenu::ParameterTG, CMiniDexed::TGParameterPitchBendRange}, + {CUIMenu::ParameterTG, CMiniDexed::TGParameterPortamentoGlissando}, + {CUIMenu::ParameterTG, CMiniDexed::TGParameterMonoMode}, + {CUIMenu::ParameterNone}, + {CUIMenu::ParameterTG, CMiniDexed::TGParameterPitchBendStep}, + }, + { + {CUIMenu::ParameterTG, CMiniDexed::TGParameterMWRange}, + {CUIMenu::ParameterTG, CMiniDexed::TGParameterMWPitch, "MW Pitch", "MWPi"}, + {CUIMenu::ParameterTG, CMiniDexed::TGParameterFCRange}, + {CUIMenu::ParameterTG, CMiniDexed::TGParameterFCPitch, "FC Pitch", "FCPi"}, + {CUIMenu::ParameterTG, CMiniDexed::TGParameterMWEGBias, "MW EG Bias", "MWEGB"}, + {CUIMenu::ParameterTG, CMiniDexed::TGParameterMWAmplitude, "MW Amp", "MWA"}, + {CUIMenu::ParameterTG, CMiniDexed::TGParameterFCEGBias, "FC EG Bias", "FCEGB"}, + {CUIMenu::ParameterTG, CMiniDexed::TGParameterFCAmplitude, "FC Amp", "FCA"}, + + }, + { + {CUIMenu::ParameterTG, CMiniDexed::TGParameterBCRange}, + {CUIMenu::ParameterTG, CMiniDexed::TGParameterBCPitch, "BC Pitch", "BCPi"}, + {CUIMenu::ParameterTG, CMiniDexed::TGParameterATRange}, + {CUIMenu::ParameterTG, CMiniDexed::TGParameterATPitch, "AT Pitch", "ATPi"}, + {CUIMenu::ParameterTG, CMiniDexed::TGParameterBCEGBias, "BC EG Bias", "BCEGB"}, + {CUIMenu::ParameterTG, CMiniDexed::TGParameterBCAmplitude, "BC Amp", "BCA"}, + {CUIMenu::ParameterTG, CMiniDexed::TGParameterATEGBias, "AT EG Bias", "ATEGB"}, + {CUIMenu::ParameterTG, CMiniDexed::TGParameterATAmplitude, "AT Amp", "ATA"}, + }, + }; + + CUIMenu::TCParameterInfo m_pEffectEncoders[1][8] = { + { + {CUIMenu::ParameterGlobal, CMiniDexed::ParameterCompressorEnable}, + {CUIMenu::ParameterGlobal, CMiniDexed::ParameterReverbEnable, "Reverb"}, + {CUIMenu::ParameterGlobal, CMiniDexed::ParameterReverbSize, "Rev Size"}, + {CUIMenu::ParameterGlobal, CMiniDexed::ParameterReverbHighDamp, "Rev High Damp"}, + {CUIMenu::ParameterGlobal, CMiniDexed::ParameterReverbLowDamp, "Rev Low Damp"}, + {CUIMenu::ParameterGlobal, CMiniDexed::ParameterReverbLowPass, "Rev Low Pass"}, + {CUIMenu::ParameterGlobal, CMiniDexed::ParameterReverbDiffusion, "Rev Diffusion"}, + {CUIMenu::ParameterGlobal, CMiniDexed::ParameterReverbLevel, "Rev Level"}, + }, + }; + + CUIMenu::TCParameterInfo m_pVoiceEncoders[3 + 22][8] = { + { + {CUIMenu::ParameterVoice, DEXED_ALGORITHM}, + {CUIMenu::ParameterVoice, DEXED_FEEDBACK}, + {CUIMenu::ParameterVoice, DEXED_TRANSPOSE}, + }, + { + {CUIMenu::ParameterVoice, DEXED_PITCH_EG_R1}, + {CUIMenu::ParameterVoice, DEXED_PITCH_EG_R2}, + {CUIMenu::ParameterVoice, DEXED_PITCH_EG_R3}, + {CUIMenu::ParameterVoice, DEXED_PITCH_EG_R4}, + {CUIMenu::ParameterVoice, DEXED_PITCH_EG_L1}, + {CUIMenu::ParameterVoice, DEXED_PITCH_EG_L2}, + {CUIMenu::ParameterVoice, DEXED_PITCH_EG_L3}, + {CUIMenu::ParameterVoice, DEXED_PITCH_EG_L4}, + }, + { + {CUIMenu::ParameterVoice, DEXED_OSC_KEY_SYNC}, + {CUIMenu::ParameterVoice, DEXED_LFO_SPEED}, + {CUIMenu::ParameterVoice, DEXED_LFO_DELAY}, + {CUIMenu::ParameterVoice, DEXED_LFO_PITCH_MOD_DEP}, + {CUIMenu::ParameterVoice, DEXED_LFO_SYNC}, + {CUIMenu::ParameterVoice, DEXED_LFO_WAVE}, + {CUIMenu::ParameterVoice, DEXED_LFO_PITCH_MOD_SENS}, + {CUIMenu::ParameterVoice, DEXED_LFO_AMP_MOD_DEP}, + }, + }; + + CUIMenu::TCParameterInfo m_pOPEncoders[3][8] = { + { + {CUIMenu::ParameterOP, DEXED_OP_OUTPUT_LEV}, + {CUIMenu::ParameterOP, DEXED_OP_FREQ_COARSE}, + {CUIMenu::ParameterOP, DEXED_OP_FREQ_FINE}, + {CUIMenu::ParameterOP, DEXED_OP_OSC_DETUNE}, + {CUIMenu::ParameterOP, DEXED_OP_OSC_MODE}, + {CUIMenu::ParameterOP, DEXED_OP_ENABLE}, + }, + { + {CUIMenu::ParameterOP, DEXED_OP_EG_R1}, + {CUIMenu::ParameterOP, DEXED_OP_EG_R2}, + {CUIMenu::ParameterOP, DEXED_OP_EG_R3}, + {CUIMenu::ParameterOP, DEXED_OP_EG_R4}, + {CUIMenu::ParameterOP, DEXED_OP_EG_L1}, + {CUIMenu::ParameterOP, DEXED_OP_EG_L2}, + {CUIMenu::ParameterOP, DEXED_OP_EG_L3}, + {CUIMenu::ParameterOP, DEXED_OP_EG_L4}, + }, + { + {CUIMenu::ParameterOP, DEXED_OP_LEV_SCL_BRK_PT}, + {CUIMenu::ParameterOP, DEXED_OP_SCL_LEFT_DEPTH}, + {CUIMenu::ParameterOP, DEXED_OP_SCL_RGHT_DEPTH}, + {CUIMenu::ParameterOP, DEXED_OP_AMP_MOD_SENS}, + {CUIMenu::ParameterOP, DEXED_OP_OSC_RATE_SCALE}, + {CUIMenu::ParameterOP, DEXED_OP_SCL_LEFT_CURVE}, + {CUIMenu::ParameterOP, DEXED_OP_SCL_RGHT_CURVE}, + {CUIMenu::ParameterOP, DEXED_OP_KEY_VEL_SENS}, + }, + }; + + TKernelTimerHandle m_DisplayTimer = 0; + + u8 m_ucFirstTG = 0; + + const uint8_t m_pEncoder[3] = {0x04, 0x02, 0x60}; + TMIDIRoute m_pRouteMap[75] = { + {0, 0, MIDI_CONTROL_CHANGE, 14, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader1 + {0, 0, MIDI_CONTROL_CHANGE, 15, 0xFF, 1, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader2 + {0, 0, MIDI_CONTROL_CHANGE, 30, 0xFF, 2, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader3 + {0, 0, MIDI_CONTROL_CHANGE, 31, 0xFF, 3, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader4 + + {0, 0, MIDI_CONTROL_CHANGE, 118, 0x7F, .ucTimerTarget=2, .usTimerExpire=m_pConfig->GetLongPressTimeout (), .bSkip=true}, // Main knob click + {0, 0, MIDI_CONTROL_CHANGE, 118, 0x00, 0, MIDI_DAW_CHANGE, MIDI_DAW_MENU_SELECT, 0, .bGroup=true}, + {0, 0, MIDI_CONTROL_CHANGE, 118, 0x00, 0, MIDI_DAW_CHANGE, MIDI_DAW_MENU_BACK, 0, .bGroup=true}, + {0, 0, MIDI_CONTROL_CHANGE, 28, TMIDIRoute::LtCenter, 0, MIDI_DAW_CHANGE, MIDI_DAW_MENU_PRESS_PREV, 0xFF, .bGroup=true, .bGroupHold=true}, // Main knob click + rotate + {0, 0, MIDI_CONTROL_CHANGE, 28, TMIDIRoute::GtCenter, 0, MIDI_DAW_CHANGE, MIDI_DAW_MENU_PRESS_NEXT, 0xFF, .bGroup=true, .bGroupHold=true}, + + {0, 0, MIDI_CONTROL_CHANGE, 119, 0x7F, .bSkip=true}, // Shift + Main knob click + {0, 0, MIDI_CONTROL_CHANGE, 119, 0x00, 0, MIDI_DAW_CHANGE, MIDI_DAW_MENU_HOME, 0}, + + {0, 0, MIDI_CONTROL_CHANGE, 28, TMIDIRoute::LtCenter, 0, MIDI_DAW_CHANGE, MIDI_DAW_MENU_PREV, 0xFF}, // Main knob + {0, 0, MIDI_CONTROL_CHANGE, 28, TMIDIRoute::GtCenter, 0, MIDI_DAW_CHANGE, MIDI_DAW_MENU_NEXT, 0xFF}, + + {0, 0, MIDI_CONTROL_CHANGE, 27, 0x7F, .ucTimerTarget=2, .usTimerExpire=m_pConfig->GetLongPressTimeout (), .bSkip=true}, // Shift + {0, 0, MIDI_CONTROL_CHANGE, 27, 0x00, 0, MIDI_DAW_CHANGE, MIDI_DAW_ENC_VALUES_TOGGLE, 0xFF, .bGroup=true}, + {0, 0, MIDI_CONTROL_CHANGE, 27, 0x00, 0, MIDI_DAW_CHANGE, MIDI_DAW_DISPLAY_MODE_TOGGLE, 0xFF, .bGroup=true}, + + {0, 0, MIDI_CONTROL_CHANGE, 86, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_ENC_0, 0xFF}, // Knob1 + {0, 0, MIDI_CONTROL_CHANGE, 87, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_ENC_1, 0xFF}, // Knob2 + {0, 0, MIDI_CONTROL_CHANGE, 89, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_ENC_2, 0xFF}, // Knob3 + {0, 0, MIDI_CONTROL_CHANGE, 90, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_ENC_3, 0xFF}, // Knob4 + {0, 0, MIDI_CONTROL_CHANGE, 110, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_ENC_4, 0xFF}, // Knob5 + {0, 0, MIDI_CONTROL_CHANGE, 111, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_ENC_5, 0xFF}, // Knob6 + {0, 0, MIDI_CONTROL_CHANGE, 116, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_ENC_6, 0xFF}, // Knob7 + {0, 0, MIDI_CONTROL_CHANGE, 117, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_ENC_7, 0xFF}, // Knob8 + + {0, 9, MIDI_NOTE_ON, 36, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_TOGGLE_MONO, 0x7F}, // BankA Pad1 + {0, 9, MIDI_NOTE_OFF, 36, 0xFF, .bSkip=true}, + + {0, 9, MIDI_NOTE_ON, 37, 0xFF, .bSkip=true, .bGroupHead=true}, // BankA Pad2 + {0, 9, MIDI_NOTE_OFF, 37, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_PORTAMENTO, 0x7F, .bToggle=true, .bGroup=true}, + {0, 9, MIDI_AFTERTOUCH, 37, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_TOGGLE_PORTA_GLISS, 0x7F, .bGroup=true}, + + {0, 9, MIDI_NOTE_ON, 38, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_SOSTENUTO, 0x7F, .bToggle=true}, // BankA Pad3 + {0, 9, MIDI_NOTE_OFF, 38, 0xFF, .bSkip=true}, + + {0, 9, MIDI_NOTE_ON, 39, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_BANK_SUSTAIN, 0x7F, .bToggle=true}, // BankA Pad4 + {0, 9, MIDI_NOTE_OFF, 39, 0xFF, .bSkip=true}, + + {0, 9, MIDI_NOTE_ON, 40, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_ALL_SOUND_OFF, 0x7F}, // BankA Pad5 + {0, 9, MIDI_NOTE_OFF, 40, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_ALL_SOUND_OFF, 0x00}, + + {0, 9, MIDI_NOTE_ON, 41, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_HOLD2, 0x7F, .bToggle=true}, // BankA Pad6 + {0, 9, MIDI_NOTE_OFF, 41, 0xFF, .bSkip=true}, + + {0, 9, MIDI_NOTE_ON, 42, 0xFF, .bSkip=true}, // BankA Pad7 + {0, 9, MIDI_NOTE_OFF, 42, 0xFF, .bSkip=true}, + + {0, 9, MIDI_NOTE_ON, 43, 0xFF, .bSkip=true}, // BankA Pad8 + {0, 9, MIDI_NOTE_OFF, 43, 0xFF, 0, MIDI_CHANNEL_AFTERTOUCH, 0x00, 0xFF}, + {0, 9, MIDI_AFTERTOUCH, 43, 0xFF, 0, MIDI_CHANNEL_AFTERTOUCH, TMIDIRoute::P2, 0xFF}, + + {0, 9, MIDI_NOTE_ON, 44, 0xFF, .ucTimerTarget=2, .usTimerExpire=m_pConfig->GetLongPressTimeout (), .bSkip=true}, // BankB Pad1 + {0, 9, MIDI_NOTE_OFF, 44, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_TOGGLE_TG, 0, .bGroup=true}, + {0, 9, MIDI_NOTE_OFF, 44, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_SELECT_CHAN_TG, 0, .bGroup=true}, + {0, 9, MIDI_AFTERTOUCH, 44, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_SELECT_TG, 0, .bGroup=true}, + + {0, 9, MIDI_NOTE_ON, 45, 0xFF, .ucTimerTarget=2, .usTimerExpire=m_pConfig->GetLongPressTimeout (), .bSkip=true}, // BankB Pad2 + {0, 9, MIDI_NOTE_OFF, 45, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_TOGGLE_TG, 1, .bGroup=true}, + {0, 9, MIDI_NOTE_OFF, 45, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_SELECT_CHAN_TG, 1, .bGroup=true}, + {0, 9, MIDI_AFTERTOUCH, 45, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_SELECT_TG, 1, .bGroup=true}, + + {0, 9, MIDI_NOTE_ON, 46, 0xFF, .ucTimerTarget=2, .usTimerExpire=m_pConfig->GetLongPressTimeout (), .bSkip=true}, // BankB Pad3 + {0, 9, MIDI_NOTE_OFF, 46, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_TOGGLE_TG, 2, .bGroup=true}, + {0, 9, MIDI_NOTE_OFF, 46, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_SELECT_CHAN_TG, 2, .bGroup=true}, + {0, 9, MIDI_AFTERTOUCH, 46, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_SELECT_TG, 2, .bGroup=true}, + + {0, 9, MIDI_NOTE_ON, 47, 0xFF, .ucTimerTarget=2, .usTimerExpire=m_pConfig->GetLongPressTimeout (), .bSkip=true}, // BankB Pad4 + {0, 9, MIDI_NOTE_OFF, 47, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_TOGGLE_TG, 3, .bGroup=true}, + {0, 9, MIDI_NOTE_OFF, 47, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_SELECT_CHAN_TG, 3, .bGroup=true}, + {0, 9, MIDI_AFTERTOUCH, 47, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_SELECT_TG, 3, .bGroup=true}, + + {0, 9, MIDI_NOTE_ON, 48, 0xFF, .ucTimerTarget=2, .usTimerExpire=m_pConfig->GetLongPressTimeout (), .bSkip=true}, // BankB Pad5 + {0, 9, MIDI_NOTE_OFF, 48, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_TOGGLE_TG, 4, .bGroup=true}, + {0, 9, MIDI_NOTE_OFF, 48, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_SELECT_CHAN_TG, 4, .bGroup=true}, + {0, 9, MIDI_AFTERTOUCH, 48, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_SELECT_TG, 4, .bGroup=true}, + + {0, 9, MIDI_NOTE_ON, 49, 0xFF, .ucTimerTarget=2, .usTimerExpire=m_pConfig->GetLongPressTimeout (), .bSkip=true}, // BankB Pad6 + {0, 9, MIDI_NOTE_OFF, 49, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_TOGGLE_TG, 5, .bGroup=true}, + {0, 9, MIDI_NOTE_OFF, 49, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_SELECT_CHAN_TG, 5, .bGroup=true}, + {0, 9, MIDI_AFTERTOUCH, 49, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_SELECT_TG, 5, .bGroup=true}, + + {0, 9, MIDI_NOTE_ON, 50, 0xFF, .ucTimerTarget=2, .usTimerExpire=m_pConfig->GetLongPressTimeout (), .bSkip=true}, // BankB Pad7 + {0, 9, MIDI_NOTE_OFF, 50, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_TOGGLE_TG, 6, .bGroup=true}, + {0, 9, MIDI_NOTE_OFF, 50, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_SELECT_CHAN_TG, 6, .bGroup=true}, + {0, 9, MIDI_AFTERTOUCH, 50, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_SELECT_TG, 6, .bGroup=true}, + + {0, 9, MIDI_NOTE_ON, 51, 0xFF, .ucTimerTarget=2, .usTimerExpire=m_pConfig->GetLongPressTimeout (), .bSkip=true}, // BankB Pad8 + {0, 9, MIDI_NOTE_OFF, 51, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_TOGGLE_TG, 7, .bGroup=true}, + {0, 9, MIDI_NOTE_OFF, 51, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_SELECT_CHAN_TG, 7, .bGroup=true}, + {0, 9, MIDI_AFTERTOUCH, 51, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_SELECT_TG, 7, .bGroup=true}, + + {0xFF}, // Sentinel + }; +}; + +CMiniLab3DawConnection::CMiniLab3DawConnection (CMiniDexed *pSynthesizer, CMIDIKeyboard *pKeyboard, CConfig *pConfig, CUserInterface *pUI) + :m_pSynthesizer (pSynthesizer), m_pKeyboard (pKeyboard), m_pConfig (pConfig), m_pUI (pUI) +{ + static const uint8_t pInit[] = {0xF0, 0x00, 0x20, 0x6B, 0x7F, 0x42, 0x02, 0x00, 0x40, 0x6A, 0x21, 0xF7}; + + m_pKeyboard->SetRouteMap (m_pRouteMap); + + m_pKeyboard->Send (pInit, sizeof pInit, 0); + DisplayWrite ("MiniDexed", "", "On MiniLab 3", 0, 0); + + SetPadColor (BankA, MonoPad, 0); + SetPadColor (BankA, PortamentoPad, 0); + SetPadColor (BankA, SostenutoPad, 0); + SetPadColor (BankA, SustainPad, 0); + SetPadColor (BankA, SoundOffPad, 0); + SetPadColor (BankA, HoldPad, 0); + SetPadColor (BankA, TBDPad7, 0); + UpdateATColor (0); + + for (unsigned vIdx = 3,i = 0; i < ARRAY_LENGTH (m_pOPEncoders); ++i) + for (unsigned j = 0; j < ARRAY_LENGTH (*m_pOPEncoders); ++j) + { + CUIMenu::TCParameterInfo *pInfo = &m_pOPEncoders[i][j]; + if (!pInfo->Type) + continue; + + for (unsigned k = 0; k < 6; ++k) + m_pVoiceEncoders[vIdx][k] = {pInfo->Type, pInfo->Parameter, .OP=static_cast(k+1)}; + + m_pVoiceEncoders[vIdx][7] = {pInfo->Type, pInfo->Parameter, .OP=AllOP}; + + vIdx++; + } + + assert ((sizeof m_pTGEncoders - sizeof *m_pTGEncoders * 4 ) == sizeof m_pVoiceEncoders); + memcpy (m_pTGEncoders + 4, m_pVoiceEncoders, sizeof m_pVoiceEncoders); + + + for (unsigned i = 0; i < ARRAY_LENGTH (m_pTGEncoders); ++i) + m_pUI->GetParameterInfos (m_pTGEncoders[i]); + for (unsigned i = 0; i < ARRAY_LENGTH (m_pEffectEncoders); ++i) + m_pUI->GetParameterInfos (m_pEffectEncoders[i]); + for (unsigned i = 0; i < ARRAY_LENGTH (m_pVoiceEncoders); ++i) + m_pUI->GetParameterInfos (m_pVoiceEncoders[i]); + for (unsigned i = 0; i < ARRAY_LENGTH (m_pOPEncoders); ++i) + m_pUI->GetParameterInfos (m_pOPEncoders[i]); + + UpdateMenu (m_encoderPageType, m_ucEncoderPage, m_ucEncoderOP, m_ucEncoderTG); + QueueUpdateDisplay (nDefaultDisplayUpdateDelay); +} + +void CMiniLab3DawConnection::DisplayWrite (const char *pMenu, const char *pParam, const char *pValue, bool bArrowDown, bool bArrowUp) +{ + const uint8_t page = bArrowDown == bArrowUp ? 0x11 : bArrowDown ? 0x10 : 0x00; + const uint8_t pHdr[] = {0xF0, 0x00, 0x20, 0x6B, 0x7F, 0x42, 0x04, 0x02, 0x60, 0x1f, 0x06, 0x00, 0x00, page, 0x00, 0x11, 0x00, 0x01}; + ArturiaDisplayWrite (m_pKeyboard, pHdr, sizeof pHdr, 18, true, false, pMenu, pParam, pValue, false, false); +} + +void CMiniLab3DawConnection::QueueUpdateDisplay (unsigned msec) +{ + if (m_DisplayTimer) + CTimer::Get ()->CancelKernelTimer (m_DisplayTimer); + m_DisplayTimer = CTimer::Get ()->StartKernelTimer (MSEC2HZ (msec), s_UpdateDisplay, this, NULL); +} + +void CMiniLab3DawConnection::s_UpdateDisplay (TKernelTimerHandle hTimer, void *pParam, void *pContext) +{ + assert (pParam != NULL); + static_cast(pParam)->UpdateDisplay (); +} + +void CMiniLab3DawConnection::UpdateDisplay () +{ + switch (m_DisplayState) + { + case DisplayMenu: + m_pUI->MIDIEventHandler (CUIMenu::MenuEventUpdate); + break; + case DisplayEncoder: + ShowEncoderDisplay (); + break; + case DisplayValues: + ShowValueDisplay (); + break; + } +} + +void CMiniLab3DawConnection::ShowEncoderDisplay () +{ + static const uint8_t pHdr[] = {0xF0, 0x00, 0x20, 0x6B, 0x7F, 0x42, 0x04, 0x02, 0x60, 0x1f, 0x07, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01}; + std::string pParam = ""; + std::string pValue = ""; + for (unsigned i = 0; i < 8; ++i) + { + const char *pShort = m_pEncoders[i].Short ?: "..."; + if (i < 4) + pParam = pParam + pShort + " "; + else + pValue = pValue + pShort + " "; + } + + ArturiaDisplayWrite (m_pKeyboard, pHdr, sizeof pHdr, 18, false, false, "", pParam.c_str(), pValue.c_str(), false, false); +} + +static uint8_t GetParameterValue (CMiniDexed *pSynthesizer, CUIMenu::TCParameterInfo *pInfo, uint8_t ucOP, uint8_t ucTG) +{ + switch (pInfo->Type) + { + case CUIMenu::ParameterGlobal: + return pSynthesizer->GetParameter (static_cast(pInfo->Parameter)); + break; + case CUIMenu::ParameterTG: + return pSynthesizer->GetTGParameter (static_cast(pInfo->Parameter), ucTG); + break; + case CUIMenu::ParameterVoice: + return pSynthesizer->GetVoiceParameter (pInfo->Parameter, CMiniDexed::NoOP, ucTG); + break; + case CUIMenu::ParameterOP: + return pSynthesizer->GetVoiceParameter (pInfo->Parameter, ucOP, ucTG); + break; + default: + return 0; + break; + } +} + +static std::string GetParameterValueStr (CMiniDexed *pSynthesizer, CUIMenu::TCParameterInfo *pInfo, uint8_t ucOP, uint8_t ucTG) +{ + if (pInfo->Type == CUIMenu::ParameterNone) + return "..."; + uint8_t value = GetParameterValue (pSynthesizer, pInfo, ucOP, ucTG); + if (pInfo->ToString) + return pInfo->ToString (value); + return std::to_string (value); +} + + +void CMiniLab3DawConnection::ShowValueDisplay () +{ + static const uint8_t pHdr[] = {0xF0, 0x00, 0x20, 0x6B, 0x7F, 0x42, 0x04, 0x02, 0x60, 0x1f, 0x07, 0x01, 0x02, 0x02, 0x01, 0x00, 0x01}; + std::string pParam = ""; + std::string pValue = ""; + + for (unsigned i = 0; i < 8; ++i) + { + CUIMenu::TCParameterInfo *enc = &m_pEncoders[i]; + uint8_t ucOP = enc->OP ? enc->OP - 1 : m_ucEncoderOP; + if (enc->OP == AllOP) + ucOP = 0; + uint8_t ucTG = m_ucEncoderTG ? m_ucEncoderTG - 1 : m_ucFirstTG; + + std::string sValue = GetParameterValueStr (m_pSynthesizer, enc, ucOP, ucTG); + + if (i < 4) + pParam = pParam + sValue + " "; + else + pValue = pValue + sValue + " "; + } + + ArturiaDisplayWrite (m_pKeyboard, pHdr, sizeof pHdr, 18, false, false, "", pParam.c_str(), pValue.c_str(), false, false); +} + +void CMiniLab3DawConnection::SetPadColor (TBankID BankID, TPadID PadID, u8 state) +{ + SetPadColor (BankID, PadID, padColors[PadID], state); +} + +void CMiniLab3DawConnection::SetPadColor (TBankID BankID, TPadID PadID, u8 state, u8 state2) +{ + SetPadColor (BankID, PadID, state2 ? altPadColors[PadID] : padColors[PadID], state); +} + +void CMiniLab3DawConnection::SetPadColor (TBankID BankID, TPadID PadID, CColor color, u8 state) +{ + if (state == 0) + { + color.r /= 32; + color.g /= 32; + color.b /= 32; + } + SetPadColor (BankID, PadID, color); +} + +void CMiniLab3DawConnection::SetPadColor (TBankID BankID, TPadID PadID, CColor color) +{ + const uint8_t pSetPadColor[] = {0xF0, 0x00, 0x20, 0x6B, 0x7F, 0x42, 0x02, 0x02, 0x16, (uint8_t)(PadID + BankID), color.r, color.g, color.b, 0xF7}; + m_pKeyboard->Send (pSetPadColor, sizeof pSetPadColor, 0); +} + +void CMiniLab3DawConnection::UpdateEncoder (uint8_t ucEncID, uint8_t ucValue) +{ + uint8_t pUpdateEncoder[] = {0xF0, 0x00, 0x20, 0x6B, 0x7F, 0x42, 0x21, 0x10, 0x00, ucEncID+=7, 0x00, ucValue, 0xF7}; + m_pKeyboard->Send (pUpdateEncoder, sizeof pUpdateEncoder, 0); +} + +void CMiniLab3DawConnection::UpdateTGColor (uint8_t nTG) +{ + u8 ch = m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterMIDIChannel, nTG); + u8 enabled = m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterEnabled, nTG); + SetPadColor (BankB, (TPadID)nTG, chColors[ch], enabled); +} + +void CMiniLab3DawConnection::UpdateMonoColor () +{ + SetPadColor (BankA, MonoPad, m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterMonoMode, m_ucFirstTG)); +} + +void CMiniLab3DawConnection::UpdatePortamentoColor () +{ + u8 mode = m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterPortamentoMode, m_ucFirstTG); + u8 mode2 = m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterPortamentoGlissando, m_ucFirstTG); + SetPadColor (BankA, PortamentoPad, mode, mode2); +} + +void CMiniLab3DawConnection::UpdateATColor (u8 ucAT) +{ + u8 c = ucAT ?: 1; + SetPadColor (BankA, ATPad, CColor {c, c, c}); +} + + +void CMiniLab3DawConnection::UpdateVolumeFaders () +{ + u8 chan_map[4] = { + CMIDIDevice::TChannel::Disabled, + CMIDIDevice::TChannel::Disabled, + CMIDIDevice::TChannel::Disabled, + CMIDIDevice::TChannel::Disabled, + }; + + for (unsigned i = 0; i < m_pConfig->GetToneGenerators(); ++i) + { + int channel = m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterMIDIChannel, i); + + if (channel == CMIDIDevice::ChannelUnknown || channel == CMIDIDevice::Disabled) + continue; + + if (channel == CMIDIDevice::OmniMode) + channel = 0; + + for (unsigned i = 0; i < sizeof chan_map; ++i) + { + if (chan_map[i] == channel) + break; + + if (chan_map[i] == CMIDIDevice::Disabled) { + chan_map[i] = channel; + break; + } + } + } + + for (unsigned i = 0; i < sizeof chan_map; ++i) + { + if (chan_map[i] == CMIDIDevice::Disabled) + m_pRouteMap[i].bSkip = true; + else { + m_pRouteMap[i].bSkip = false; + m_pRouteMap[i].ucDCh = chan_map[i]; + } + } +} + +void CMiniLab3DawConnection::UpdateState () +{ + for (unsigned i = 0; i < m_pConfig->GetToneGenerators(); ++i) + if (m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterEnabled, i)) { + u8 ucChannel = m_pKeyboard->GetChannel (i); + + if (ucChannel == CMIDIDevice::ChannelUnknown || ucChannel == CMIDIDevice::Disabled) + continue; + + if (ucChannel == CMIDIDevice::OmniMode) + ucChannel = 0; + + for (TMIDIRoute *r = m_pRouteMap; r->ucSCable != 0xFF; r++) + r->ucDCh = ucChannel; + + m_ucFirstTG = i; + + break; + } + + for (unsigned i = 0; i < 8; ++i) + { + if (m_bDisableEncoderUpdate) + continue; + + CUIMenu::TCParameterInfo *enc = &m_pEncoders[i]; + u8 ucTG = m_ucEncoderTG ? m_ucEncoderTG - 1 : m_ucFirstTG; + u8 ucOP = enc->OP ? enc->OP - 1 : m_ucEncoderOP; + if (enc->OP == AllOP) + ucOP = 0; + int value; + + switch (enc->Type) + { + case CUIMenu::ParameterGlobal: + value = m_pSynthesizer->GetParameter (static_cast(enc->Parameter)); + break; + case CUIMenu::ParameterTG: + value = m_pSynthesizer->GetTGParameter (static_cast(enc->Parameter), ucTG); + break; + case CUIMenu::ParameterVoice: + value = m_pSynthesizer->GetVoiceParameter (enc->Parameter, CMiniDexed::NoOP, ucTG); + break; + case CUIMenu::ParameterOP: + value = m_pSynthesizer->GetVoiceParameter (enc->Parameter, ucOP, ucTG); + break; + default: + continue; + } + UpdateEncoder (i, mapfloatr (value, enc->Min, enc->Max, 0, 127)); + } + + UpdateMonoColor (); + // TODO change the MIDIRouteMap's value also + UpdatePortamentoColor (); + + for (unsigned i = 0; i < m_pConfig->GetToneGenerators(); ++i) + { + if (m_bDisableEncoderUpdate) + continue; + + UpdateTGColor (i); + } + + UpdateVolumeFaders (); +} + +void CMiniLab3DawConnection::UpdateMenu (CUIMenu::TCPageType Type, s8 ucPage, u8 ucOP, u8 ucTG) +{ + m_encoderPageType = Type; + m_ucEncoderOP = ucOP; + m_ucEncoderTG = ucTG; + + switch (Type) + { + case CUIMenu::PageMain: + m_ucEncoderPage = constrain (ucPage, 0, (s8)ARRAY_LENGTH (m_pTGEncoders) - 1); + m_pEncoders = m_pTGEncoders[m_ucEncoderPage]; + m_ucEncoderTG = 0; // 0 -> first active TG + break; + case CUIMenu::PageTG: + m_ucEncoderPage = constrain (ucPage, 0, (s8)ARRAY_LENGTH (m_pTGEncoders) - 1); + m_pEncoders = m_pTGEncoders[m_ucEncoderPage]; + break; + case CUIMenu::PageEffect: + m_ucEncoderPage = constrain (ucPage, 0, (s8)ARRAY_LENGTH (m_pEffectEncoders) - 1); + m_pEncoders = m_pEffectEncoders[m_ucEncoderPage]; + break; + case CUIMenu::PageVoice: + m_ucEncoderPage = constrain (ucPage, 0, (s8)ARRAY_LENGTH (m_pVoiceEncoders) - 1); + m_pEncoders = m_pVoiceEncoders[m_ucEncoderPage]; + break; + case CUIMenu::PageOP: + m_ucEncoderPage = constrain (ucPage, 0, (s8)ARRAY_LENGTH (m_pOPEncoders) - 1); + m_pEncoders = m_pOPEncoders[m_ucEncoderPage]; + break; + default: + assert (false); + } + + UpdateState (); +} + +void CMiniLab3DawConnection::MIDIListener (u8 ucCable, u8 ucChannel, u8 ucType, u8 ucP1, u8 ucP2) +{ + unsigned nDisplayUpdateDelay = nDefaultDisplayUpdateDelay; + switch (ucType) + { + case MIDI_CONTROL_CHANGE: + ArturiaShowNewCCValue (m_pKeyboard, m_pEncoder, ucChannel, ucP1, ucP2); + + switch (ucP1) + { + case MIDI_CC_PORTAMENTO: + UpdatePortamentoColor (); + break; + case MIDI_CC_SOSTENUTO: + SetPadColor (BankA, SostenutoPad, ucP2); + break; + case MIDI_CC_HOLD2: + SetPadColor (BankA, HoldPad, ucP2); + break; + case MIDI_CC_BANK_SUSTAIN: + SetPadColor (BankA, SustainPad, ucP2); + break; + case MIDI_CC_ALL_SOUND_OFF: + SetPadColor (BankA, SoundOffPad, ucP2); + break; + } + break; + case MIDI_DAW_CHANGE: + switch (m_DisplayState) + { + case DisplayMenu: + HandleMenuEvents (m_pUI, ucP1); + break; + case DisplayEncoder: + switch (ucP1) + { + case MIDI_DAW_MENU_PREV: + UpdateMenu (m_encoderPageType, m_ucEncoderPage - 1, m_ucEncoderOP, m_ucEncoderTG); + ShowEncoderDisplay (); + break; + case MIDI_DAW_MENU_NEXT: + UpdateMenu (m_encoderPageType, m_ucEncoderPage + 1, m_ucEncoderOP, m_ucEncoderTG); + ShowEncoderDisplay (); + break; + case MIDI_DAW_ENC_VALUES_TOGGLE: + m_DisplayState = DisplayValues; + UpdateDisplay (); + break; + } + break; + case DisplayValues: + switch (ucP1) + { + case MIDI_DAW_MENU_PREV: + UpdateMenu (m_encoderPageType, m_ucEncoderPage - 1, m_ucEncoderOP, m_ucEncoderTG); + ShowEncoderDisplay (); + nDisplayUpdateDelay = 500; + break; + case MIDI_DAW_MENU_NEXT: + UpdateMenu (m_encoderPageType, m_ucEncoderPage + 1, m_ucEncoderOP, m_ucEncoderTG); + ShowEncoderDisplay (); + nDisplayUpdateDelay = 500; + break; + case MIDI_DAW_ENC_VALUES_TOGGLE: + m_DisplayState = DisplayEncoder; + UpdateDisplay (); + break; + } + break; + } + + switch (ucP1) + { + case MIDI_DAW_VOICE: + SetVoice (ucChannel, ucP2); + break; + case MIDI_DAW_TOGGLE_MONO: + ToggleMonoMode (ucChannel); + break; + case MIDI_DAW_TOGGLE_PORTA_GLISS: + TogglePortamentoGlisssando (ucChannel); + break; + case MIDI_DAW_TOGGLE_TG: + ToggleTG (ucP2); + break; + case MIDI_DAW_SELECT_TG: + SelectTG (ucP2); + break; + case MIDI_DAW_SELECT_CHAN_TG: + SelectChanTG (ucP2); + break; + case MIDI_DAW_DISPLAY_MODE_TOGGLE: + m_DisplayState = m_DisplayState != DisplayMenu ? DisplayMenu : DisplayEncoder; + UpdateDisplay (); + break; + case MIDI_DAW_ENC_0: + case MIDI_DAW_ENC_1: + case MIDI_DAW_ENC_2: + case MIDI_DAW_ENC_3: + case MIDI_DAW_ENC_4: + case MIDI_DAW_ENC_5: + case MIDI_DAW_ENC_6: + case MIDI_DAW_ENC_7: + SetEncoder (ucChannel, ucP1 - MIDI_DAW_ENC_0, ucP2); + break; + } + break; + case MIDI_CHANNEL_AFTERTOUCH: + SetChannelAT (ucP1); + break; + } + QueueUpdateDisplay (nDisplayUpdateDelay); +} + +void CMiniLab3DawConnection::SetChannelAT (u8 ucValue) +{ + char line2[LINELEN]; + snprintf(line2, LINELEN, "%d", ucValue); + + ArturiaDisplayInfoWrite (m_pKeyboard, m_pEncoder, CT_PAD, ucValue, "Channel AT", line2); + + UpdateATColor (ucValue); +} + +void CMiniLab3DawConnection::SetVoice (u8 ucChannel, u8 ucVoice) +{ + std::string line2; + for (unsigned i = 0; i < m_pConfig->GetToneGenerators(); ++i) + { + if (m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterEnabled, i) == 0 || + (m_pKeyboard->GetChannel (i) != ucChannel && m_pKeyboard->GetChannel (i) != CMIDIDevice::OmniMode)) + continue; + m_pSynthesizer->ProgramChange (ucVoice, i); + if (line2.length() == 0) { + std::string sVoiceName = m_pSynthesizer->GetVoiceName (i); + if (sVoiceName.length() > 0) + line2 = std::to_string (ucVoice + 1) + "=" + sVoiceName; + } + } + + ArturiaDisplayInfoWrite (m_pKeyboard, m_pEncoder, CT_KNOB, ucVoice, "Voice", line2.c_str()); +} + +void CMiniLab3DawConnection::SetEncoder (u8 ucChannel, u8 ucEncId, u8 ucValue) +{ + char line2[LINELEN]; + + CUIMenu::TCParameterInfo *encoder = &m_pEncoders[ucEncId]; + + if (encoder->Type == CUIMenu::ParameterNone) + return; + + int value = mapfloatr (ucValue, 0, 127, encoder->Min, encoder->Max); + u8 ucOP = encoder->OP ? encoder->OP - 1 : m_ucEncoderOP; + + // If we update the encoders during setup, we will get rounding problems, so disable it. + m_bDisableEncoderUpdate = true; + + if (encoder->Type == CUIMenu::ParameterGlobal) + m_pSynthesizer->SetParameter (static_cast(encoder->Parameter), value); + else + for (unsigned i = 0; i < m_pConfig->GetToneGenerators(); ++i) + { + if (m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterEnabled, i) == 0) + continue; + + if (m_ucEncoderTG && m_ucEncoderTG - 1u != i) + continue; + + if (m_ucEncoderTG == 0 && + (m_pKeyboard->GetChannel (i) != ucChannel && m_pKeyboard->GetChannel (i) != CMIDIDevice::OmniMode)) + continue; + + switch (encoder->Type) + { + case CUIMenu::ParameterTG: + m_pSynthesizer->SetTGParameter (static_cast(encoder->Parameter), value, i); + break; + case CUIMenu::ParameterVoice: + m_pSynthesizer->SetVoiceParameter (encoder->Parameter, value, CMiniDexed::NoOP, i); + break; + case CUIMenu::ParameterOP: + for (unsigned j = 0; j < 6; ++j) + { + if (encoder->OP != AllOP && j != ucOP) + continue; + + m_pSynthesizer->SetVoiceParameter (encoder->Parameter, value, j, i); + } + break; + default: + break; + } + } + + m_bDisableEncoderUpdate = false; + + if (encoder->ToString) { + std::string sValue = encoder->ToString(value); + snprintf(line2, LINELEN, "%s", sValue.c_str()); + } + else + snprintf(line2, LINELEN, "%d", value); + + ArturiaDisplayInfoWrite (m_pKeyboard, m_pEncoder, CT_KNOB, ucValue, encoder->Name, line2); +} + +void CMiniLab3DawConnection::ToggleMonoMode (u8 ucChannel) +{ + u8 ucValue = m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterMonoMode, m_ucFirstTG) ? 0x00 : 0x7F; + + for (unsigned i = 0; i < m_pConfig->GetToneGenerators(); ++i) + { + if (m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterEnabled, i) == 0 || + (m_pKeyboard->GetChannel (i) != ucChannel && m_pKeyboard->GetChannel (i) != CMIDIDevice::OmniMode)) + continue; + m_pSynthesizer->setMonoMode (ucValue, i); + } + + ArturiaDisplayInfoWrite (m_pKeyboard, m_pEncoder, CT_PAD, ucValue, "Mono Mode", ucValue > 64 ? "On" : "Off"); + UpdateMonoColor (); +} + +void CMiniLab3DawConnection::TogglePortamentoGlisssando (u8 ucChannel) +{ + u8 ucValue = m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterPortamentoGlissando, m_ucFirstTG) ? 0x00 : 0x7F; + + for (unsigned i = 0; i < m_pConfig->GetToneGenerators(); ++i) + { + if (m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterEnabled, i) == 0 && + (m_pKeyboard->GetChannel (i) != ucChannel && m_pKeyboard->GetChannel (i) != CMIDIDevice::OmniMode)) + continue; + m_pSynthesizer->setPortamentoGlissando (ucValue, i); + } + + ArturiaDisplayInfoWrite (m_pKeyboard, m_pEncoder, CT_PAD, ucValue, "Porta Gliss", ucValue > 64 ? "On" : "Off"); + UpdatePortamentoColor (); +} + +void CMiniLab3DawConnection::ToggleTG (u8 ucTG) +{ + char line1[LINELEN]; + + u8 value = m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterEnabled, ucTG) ? 0x00 : 0x7F; + + m_pSynthesizer->setEnabled (value, ucTG); + m_pSynthesizer->panic (value, ucTG); + + snprintf(line1, LINELEN, "TG %d", ucTG + 1); + ArturiaDisplayInfoWrite (m_pKeyboard, m_pEncoder, CT_PAD, value, line1, value > 64 ? "On" : "Off"); +} + +void CMiniLab3DawConnection::SelectTG (u8 ucTG) +{ + char line1[LINELEN]; + + u8 enabledOne = true; + for (unsigned i = 0; i < m_pConfig->GetToneGenerators(); ++i) + { + if (i == ucTG) + continue; + + if (m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterEnabled, i)) { + enabledOne = false; + break; + } + } + + if (enabledOne) { + for (unsigned i = 0; i < m_pConfig->GetToneGenerators(); ++i) { + m_pSynthesizer->setEnabled (true, i); + } + ArturiaDisplayInfoWrite (m_pKeyboard, m_pEncoder, CT_PAD, 0x7F, "TG All", "On"); + } else { + for (unsigned i = 0; i < m_pConfig->GetToneGenerators(); ++i) { + if (i == ucTG) { + m_pSynthesizer->setEnabled (true, i); + } else { + m_pSynthesizer->setEnabled (false, i); + m_pSynthesizer->panic (false, i); + } + } + snprintf(line1, LINELEN, "TG %d", ucTG + 1); + ArturiaDisplayInfoWrite (m_pKeyboard, m_pEncoder, CT_PAD, 0x7F, line1, "Selected"); + } +} + + +void CMiniLab3DawConnection::SelectChanTG (u8 ucTG) +{ + char line1[LINELEN]; + + u8 enabled = m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterEnabled, ucTG); + u8 channel = m_pKeyboard->GetChannel (ucTG); + + for (unsigned i = 0; i < m_pConfig->GetToneGenerators(); ++i) { + if (m_pKeyboard->GetChannel (i) == channel) { + if (enabled) { + m_pSynthesizer->setEnabled (false, i); + m_pSynthesizer->panic (false, i); + } else { + m_pSynthesizer->setEnabled (true, i); + } + } + } + + snprintf(line1, LINELEN, "TGs on Ch %s", to_midi_channel(channel).c_str()); + + // this doesn't work well with Minilab 3 firmware 1.2.0 + ArturiaDisplayInfoWrite (m_pKeyboard, m_pEncoder, CT_PAD, 0x7F, line1, enabled ? "Off" : "On"); +} + +class CKeyLabEs3DawConnection : public CDAWConnection +{ +public: + CKeyLabEs3DawConnection (CMiniDexed *pSynthesizer, CMIDIKeyboard *pKeyboard, CConfig *pConfig, CUserInterface *pUI); + void DisplayWrite ( const char *pMenu, const char *pParam, const char *pValue, bool bArrowDown, bool bArrowUp) override; + void UpdateState () override; + void UpdateMenu (CUIMenu::TCPageType Type, s8 ucPage, u8 ucOP, u8 ucTG) override; + void MIDIListener (u8 ucCable, u8 ucChannel, u8 ucType, u8 ucP1, u8 ucP2) override; +private: + void UpdateEncoder (uint8_t ucEncID, uint8_t ucValue); + + CMiniDexed *m_pSynthesizer; + CMIDIKeyboard *m_pKeyboard; + CConfig *m_pConfig; + CUserInterface *m_pUI; + + const uint8_t m_pEncoder[3] = {0x04, 0x01, 0x60}; + TMIDIRoute m_pRouteMap[25] = { + {0, 0, MIDI_CONTROL_CHANGE, 117, 0x7F, .ucTimerTarget=2, .usTimerExpire=m_pConfig->GetLongPressTimeout (), .bSkip=true}, // Main knob click + {0, 0, MIDI_CONTROL_CHANGE, 117, 0x00, 0, MIDI_DAW_CHANGE, MIDI_DAW_MENU_SELECT, 0, .bGroup=true}, + {0, 0, MIDI_CONTROL_CHANGE, 117, 0x00, 0, MIDI_DAW_CHANGE, MIDI_DAW_MENU_BACK, 0, .bGroup=true}, + {0, 0, MIDI_CONTROL_CHANGE, 116, TMIDIRoute::LtCenter, 0, MIDI_DAW_CHANGE, MIDI_DAW_MENU_PRESS_PREV, 0xFF, .bGroup=true, .bGroupHold=true}, // Main knob click + rotate + {0, 0, MIDI_CONTROL_CHANGE, 116, TMIDIRoute::GtCenter, 0, MIDI_DAW_CHANGE, MIDI_DAW_MENU_PRESS_NEXT, 0xFF, .bGroup=true, .bGroupHold=true}, + + {0, 0, MIDI_CONTROL_CHANGE, 44, 0x7F, .bSkip=true}, // Home + {0, 0, MIDI_CONTROL_CHANGE, 44, 0x00, 0, MIDI_DAW_CHANGE, MIDI_DAW_MENU_HOME, 0}, + + {0, 0, MIDI_CONTROL_CHANGE, 116, TMIDIRoute::LtCenter, 0, MIDI_DAW_CHANGE, MIDI_DAW_MENU_PREV, 0xFF}, // Main knob + {0, 0, MIDI_CONTROL_CHANGE, 116, TMIDIRoute::GtCenter, 0, MIDI_DAW_CHANGE, MIDI_DAW_MENU_NEXT, 0xFF}, + + {0, 0, MIDI_CONTROL_CHANGE, 105, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader1 + {0, 0, MIDI_CONTROL_CHANGE, 106, 0xFF, 1, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader2 + {0, 0, MIDI_CONTROL_CHANGE, 107, 0xFF, 2, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader3 + {0, 0, MIDI_CONTROL_CHANGE, 108, 0xFF, 3, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader4 + {0, 0, MIDI_CONTROL_CHANGE, 109, 0xFF, 4, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader5 + {0, 0, MIDI_CONTROL_CHANGE, 110, 0xFF, 5, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader6 + {0, 0, MIDI_CONTROL_CHANGE, 111, 0xFF, 6, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader7 + {0, 0, MIDI_CONTROL_CHANGE, 112, 0xFF, 7, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader8 + {0, 0, MIDI_CONTROL_CHANGE, 113, 0xFF, 8, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader9 + + {0, 0, MIDI_CONTROL_CHANGE, 96, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_FREQUENCY_CUTOFF, 0xFF}, // Knob1 + {0, 0, MIDI_CONTROL_CHANGE, 97, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_RESONANCE, 0xFF}, // Knob2 + {0, 0, MIDI_CONTROL_CHANGE, 98, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_REVERB_LEVEL, 0xFF}, // Knob3 + {0, 0, MIDI_CONTROL_CHANGE, 99, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_DETUNE_LEVEL, 0xFF}, // Knob4 + {0, 0, MIDI_CONTROL_CHANGE, 100, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_PAN_POSITION, 0xFF}, // Knob5 + {0, 0, MIDI_CONTROL_CHANGE, 101, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_PORTAMENTO_TIME, 0xFF}, // Knob6 + // {0, 0, MIDI_CONTROL_CHANGE, 102, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_DETUNE_LEVEL, 0xFF}, // Knob7 + // {0, 0, MIDI_CONTROL_CHANGE, 103, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_PAN_POSITION, 0xFF}, // Knob8 + // {0, 0, MIDI_CONTROL_CHANGE, 104, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_PAN_POSITION, 0xFF}, // Knob9 + {0xFF}, // Sentinel + }; +}; + +CKeyLabEs3DawConnection::CKeyLabEs3DawConnection (CMiniDexed *pSynthesizer, CMIDIKeyboard *pKeyboard, CConfig *pConfig, CUserInterface *pUI) + :m_pSynthesizer (pSynthesizer), m_pKeyboard (pKeyboard), m_pConfig (pConfig), m_pUI (pUI) +{ + static const uint8_t pInit[] = {0xF0, 0x00, 0x20, 0x6B, 0x7F, 0x42, 0x02, 0x00, 0x40, 0x6A, 0x21, 0xF7}; + + m_pKeyboard->SetRouteMap (m_pRouteMap); + + m_pKeyboard->Send (pInit, sizeof pInit, 0); + DisplayWrite ("MiniDexed", "", "On KeyLab 3 Essential", 0, 0); + + UpdateState (); +} + +void CKeyLabEs3DawConnection::DisplayWrite (const char *pMenu, const char *pParam, const char *pValue, bool bArrowDown, bool bArrowUp) +{ + static const uint8_t pHdr[] = {0xF0, 0x00, 0x20, 0x6B, 0x7F, 0x42, 0x04, 0x01, 0x60, 0x12, 0x01}; + ArturiaDisplayWrite (m_pKeyboard, pHdr, sizeof pHdr, 18, true, true, pMenu, pParam, pValue, bArrowDown, bArrowUp); +} + +void CKeyLabEs3DawConnection::UpdateEncoder (uint8_t ucEncID, uint8_t ucValue) +{ + uint8_t pUpdateEncoder[] = {0xF0, 0x00, 0x20, 0x6B, 0x7F, 0x42, 0x02, 0x0F, 0x40, ucEncID += 3, ucValue, 0xF7}; + m_pKeyboard->Send (pUpdateEncoder, sizeof pUpdateEncoder, 0); +} + +void CKeyLabEs3DawConnection::UpdateState () +{ + UpdateEncoder (0, mapfloatr (m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterCutoff, 0), 0, 99, 0, 127)); + UpdateEncoder (1, mapfloatr (m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterResonance, 0), 0, 99, 0, 127)); + UpdateEncoder (2, mapfloatr (m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterReverbSend, 0), 0, 99, 0, 127)); + UpdateEncoder (3, mapfloatr (m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterMasterTune, 0), -99, 99, 1, 127)); + UpdateEncoder (4, m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterPan, 0)); + UpdateEncoder (5, mapfloatr (m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterPortamentoTime, 0), 0, 99, 0, 127)); +} + +void CKeyLabEs3DawConnection::UpdateMenu (CUIMenu::TCPageType Type, s8 ucPage, u8 ucOP, u8 ucTG) +{ + +} + +void CKeyLabEs3DawConnection::MIDIListener (u8 ucCable, u8 ucChannel, u8 ucType, u8 ucP1, u8 ucP2) +{ + switch (ucType) + { + case MIDI_CONTROL_CHANGE: + ArturiaShowNewCCValue (m_pKeyboard, m_pEncoder, ucChannel, ucP1, ucP2); + break; + case MIDI_DAW_CHANGE: + HandleMenuEvents (m_pUI, ucP1); + break; + } +} + +class CKeyLab2DawConnection : public CDAWConnection +{ +public: + CKeyLab2DawConnection (CMiniDexed *pSynthesizer, CMIDIKeyboard *pKeyboard, CConfig *pConfig, CUserInterface *pUI); + void DisplayWrite (const char *pMenu, const char *pParam, const char *pValue, bool bArrowDown, bool bArrowUp) override; + void UpdateState () override; + void UpdateMenu (CUIMenu::TCPageType Type, s8 ucPage, u8 ucOP, u8 ucTG) override; + void MIDIListener (u8 ucCable, u8 ucChannel, u8 ucType, u8 ucP1, u8 ucP2) override; +private: + CMiniDexed *m_pSynthesizer; + CMIDIKeyboard *m_pKeyboard; + CConfig *m_pConfig; + CUserInterface *m_pUI; + + TMIDIRoute m_pRouteMap[18] = { + {1, 0, MIDI_NOTE_ON, 0x54, 0x7F, .ucTimerTarget=2, .usTimerExpire=m_pConfig->GetLongPressTimeout (), .bSkip=true}, // Main knob click + {1, 0, MIDI_NOTE_ON, 0x54, 0x00, 0, MIDI_DAW_CHANGE, MIDI_DAW_MENU_SELECT, 0, .bGroup=true}, + {1, 0, MIDI_NOTE_ON, 0x54, 0x00, 0, MIDI_DAW_CHANGE, MIDI_DAW_MENU_BACK, 0, .bGroup=true}, + {0, 0, MIDI_CONTROL_CHANGE, 0x3C, TMIDIRoute::LtCenter, 0, MIDI_DAW_CHANGE, MIDI_DAW_MENU_PRESS_PREV, 0xFF, .bGroup=true, .bGroupHold=true}, // Main knob click + rotate + {0, 0, MIDI_CONTROL_CHANGE, 0x3C, TMIDIRoute::GtCenter, 0, MIDI_DAW_CHANGE, MIDI_DAW_MENU_PRESS_NEXT, 0xFF, .bGroup=true, .bGroupHold=true}, + + {1, 0, MIDI_CONTROL_CHANGE, 0x3C, TMIDIRoute::GtCenter, 0, MIDI_DAW_CHANGE, MIDI_DAW_MENU_PREV, 0xFF}, // Main knob + {1, 0, MIDI_CONTROL_CHANGE, 0x3C, TMIDIRoute::LtCenter, 0, MIDI_DAW_CHANGE, MIDI_DAW_MENU_NEXT, 0xFF}, + + {1, 0, MIDI_PITCH_BEND, 0xFF, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader1 + {1, 1, MIDI_PITCH_BEND, 0xFF, 0xFF, 1, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader2 + {1, 2, MIDI_PITCH_BEND, 0xFF, 0xFF, 2, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader3 + {1, 3, MIDI_PITCH_BEND, 0xFF, 0xFF, 3, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader4 + {1, 4, MIDI_PITCH_BEND, 0xFF, 0xFF, 4, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader5 + {1, 5, MIDI_PITCH_BEND, 0xFF, 0xFF, 5, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader6 + {1, 6, MIDI_PITCH_BEND, 0xFF, 0xFF, 6, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader7 + {1, 7, MIDI_PITCH_BEND, 0xFF, 0xFF, 7, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader8 + {1, 8, MIDI_PITCH_BEND, 0xFF, 0xFF, 8, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader9 + + /*{0, 0, MIDI_CONTROL_CHANGE, 96, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_FREQUENCY_CUTOFF, 0xFF}, // Knob1 + {0, 0, MIDI_CONTROL_CHANGE, 97, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_RESONANCE, 0xFF}, // Knob2 + {0, 0, MIDI_CONTROL_CHANGE, 98, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_REVERB_LEVEL, 0xFF}, // Knob3 + {0, 0, MIDI_CONTROL_CHANGE, 99, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_DETUNE_LEVEL, 0xFF}, // Knob4 + {0, 0, MIDI_CONTROL_CHANGE, 100, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_PAN_POSITION, 0xFF}, // Knob5 + {0, 0, MIDI_CONTROL_CHANGE, 101, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_PORTAMENTO_TIME, 0xFF}, // Knob6 + // {0, 0, MIDI_CONTROL_CHANGE, 102, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_DETUNE_LEVEL, 0xFF}, // Knob7 + // {0, 0, MIDI_CONTROL_CHANGE, 103, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_PAN_POSITION, 0xFF}, // Knob8 + // {0, 0, MIDI_CONTROL_CHANGE, 104, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_PAN_POSITION, 0xFF}, // Knob9*/ + {1, 0xFF, 0xFF, 0xFF, 0xFF, .bSkip = true}, // skip other messages on DAW cable + {0xFF}, // Sentinel + }; +}; + +CKeyLab2DawConnection::CKeyLab2DawConnection (CMiniDexed *pSynthesizer, CMIDIKeyboard *pKeyboard, CConfig *pConfig, CUserInterface *pUI) + :m_pSynthesizer (pSynthesizer), m_pKeyboard (pKeyboard), m_pConfig (pConfig), m_pUI (pUI) +{ + static const uint8_t pInit[] = {0xF0, 0x00, 0x20, 0x6B, 0x7F, 0x42, 0x02, 0x00, 0x40, 0x52, 0x00, 0xF7}; + m_pKeyboard->SetRouteMap (m_pRouteMap); + + m_pKeyboard->Send (pInit, sizeof pInit, 0); + DisplayWrite ("MiniDexed", "", "On KeyLab 2", 0, 0); + + UpdateState (); +} + +void CKeyLab2DawConnection::DisplayWrite (const char *pMenu, const char *pParam, const char *pValue, bool bArrowDown, bool bArrowUp) +{ + static const uint8_t pHdr[] = {0xF0, 0x00, 0x20, 0x6B, 0x7F, 0x42, 0x04, 0x00, 0x60, 0x01}; + ArturiaDisplayWrite (m_pKeyboard, pHdr, sizeof pHdr, 16, true, true, pMenu, pParam, pValue, bArrowDown, bArrowUp); +} + +void CKeyLab2DawConnection::UpdateState () +{ +} + +void CKeyLab2DawConnection::UpdateMenu (CUIMenu::TCPageType Type, s8 ucPage, u8 ucOP, u8 ucTG) +{ +} + +void CKeyLab2DawConnection::MIDIListener (u8 ucCable, u8 ucChannel, u8 ucType, u8 ucP1, u8 ucP2) +{ + //static const uint8_t pEncoder[] = {0x04, 0x01, 0x60}; + //ArturiaShowNewCCValue (pKeyboard, pEncoder, ucCh, ucCC, ucValue); + switch (ucType) + { + case MIDI_DAW_CHANGE: + HandleMenuEvents (m_pUI, ucP1); + break; + } +} + +class CKeyLabEsDawConnection : public CDAWConnection +{ +public: + CKeyLabEsDawConnection (CMiniDexed *pSynthesizer, CMIDIKeyboard *pKeyboard, CConfig *pConfig, CUserInterface *pUI); + void DisplayWrite (const char *pMenu, const char *pParam, const char *pValue, bool bArrowDown, bool bArrowUp) override; + void UpdateState () override; + void UpdateMenu (CUIMenu::TCPageType Type, s8 ucPage, u8 ucOP, u8 ucTG) override; + void MIDIListener (u8 ucCable, u8 ucChannel, u8 ucType, u8 ucP1, u8 ucP2) override; +private: + CMiniDexed *m_pSynthesizer; + CMIDIKeyboard *m_pKeyboard; + CConfig *m_pConfig; + CUserInterface *m_pUI; + + TMIDIRoute m_pRouteMap[18] = { + {1, 0, MIDI_NOTE_ON, 0x54, 0x7F, .ucTimerTarget=2, .usTimerExpire=m_pConfig->GetLongPressTimeout (), .bSkip=true}, // Main knob click + {1, 0, MIDI_NOTE_ON, 0x54, 0x00, 0, MIDI_DAW_CHANGE, MIDI_DAW_MENU_SELECT, 0, .bGroup=true}, + {1, 0, MIDI_NOTE_ON, 0x54, 0x00, 0, MIDI_DAW_CHANGE, MIDI_DAW_MENU_BACK, 0, .bGroup=true}, + {0, 0, MIDI_CONTROL_CHANGE, 0x3C, TMIDIRoute::LtCenter, 0, MIDI_DAW_CHANGE, MIDI_DAW_MENU_PRESS_PREV, 0xFF, .bGroup=true, .bGroupHold=true}, // Main knob click + rotate + {0, 0, MIDI_CONTROL_CHANGE, 0x3C, TMIDIRoute::GtCenter, 0, MIDI_DAW_CHANGE, MIDI_DAW_MENU_PRESS_NEXT, 0xFF, .bGroup=true, .bGroupHold=true}, + + {1, 0, MIDI_CONTROL_CHANGE, 0x3C, TMIDIRoute::GtCenter, 0, MIDI_DAW_CHANGE, MIDI_DAW_MENU_PREV, 0xFF}, // Main knob + {1, 0, MIDI_CONTROL_CHANGE, 0x3C, TMIDIRoute::LtCenter, 0, MIDI_DAW_CHANGE, MIDI_DAW_MENU_NEXT, 0xFF}, + + {1, 0, MIDI_PITCH_BEND, 0xFF, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader1 + {1, 1, MIDI_PITCH_BEND, 0xFF, 0xFF, 1, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader2 + {1, 2, MIDI_PITCH_BEND, 0xFF, 0xFF, 2, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader3 + {1, 3, MIDI_PITCH_BEND, 0xFF, 0xFF, 3, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader4 + {1, 4, MIDI_PITCH_BEND, 0xFF, 0xFF, 4, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader5 + {1, 5, MIDI_PITCH_BEND, 0xFF, 0xFF, 5, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader6 + {1, 6, MIDI_PITCH_BEND, 0xFF, 0xFF, 6, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader7 + {1, 7, MIDI_PITCH_BEND, 0xFF, 0xFF, 7, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader8 + {1, 8, MIDI_PITCH_BEND, 0xFF, 0xFF, 8, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader9 + /*{0, 0, MIDI_CONTROL_CHANGE, 96, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_FREQUENCY_CUTOFF, 0xFF}, // Knob1 + {0, 0, MIDI_CONTROL_CHANGE, 97, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_RESONANCE, 0xFF}, // Knob2 + {0, 0, MIDI_CONTROL_CHANGE, 98, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_REVERB_LEVEL, 0xFF}, // Knob3 + {0, 0, MIDI_CONTROL_CHANGE, 99, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_DETUNE_LEVEL, 0xFF}, // Knob4 + {0, 0, MIDI_CONTROL_CHANGE, 100, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_PAN_POSITION, 0xFF}, // Knob5 + {0, 0, MIDI_CONTROL_CHANGE, 101, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_PORTAMENTO_TIME, 0xFF}, // Knob6 + // {0, 0, MIDI_CONTROL_CHANGE, 102, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_DETUNE_LEVEL, 0xFF}, // Knob7 + // {0, 0, MIDI_CONTROL_CHANGE, 103, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_PAN_POSITION, 0xFF}, // Knob8 + // {0, 0, MIDI_CONTROL_CHANGE, 104, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_PAN_POSITION, 0xFF}, // Knob9*/ + {1, 0xFF, 0xFF, 0xFF, 0xFF, .bSkip = true}, // skip other messages on DAW cable + {0xFF}, // Sentinel + }; +}; + +CKeyLabEsDawConnection::CKeyLabEsDawConnection (CMiniDexed *pSynthesizer, CMIDIKeyboard *pKeyboard, CConfig *pConfig, CUserInterface *pUI) + :m_pSynthesizer (pSynthesizer), m_pKeyboard (pKeyboard), m_pConfig (pConfig), m_pUI (pUI) +{ + static const uint8_t pInit[] = {0xF0, 0x00, 0x20, 0x6B, 0x7F, 0x42, 0x02, 0x00, 0x40, 0x51, 0x00, 0xF7}; // init DAW to Mackie mode + m_pKeyboard->SetRouteMap (m_pRouteMap); + + m_pKeyboard->Send (pInit, sizeof pInit, 0); + DisplayWrite ("MiniDexed", "", "On KeyLab Essential", 0, 0); + + UpdateState (); +} + +void CKeyLabEsDawConnection::DisplayWrite (const char *pMenu, const char *pParam, const char *pValue, bool bArrowDown, bool bArrowUp) +{ + static const uint8_t pHdr[] = {0xF0, 0x00, 0x20, 0x6B, 0x7F, 0x42, 0x04, 0x00, 0x60, 0x01}; + ArturiaDisplayWrite (m_pKeyboard, pHdr, sizeof pHdr, 16, true, true, pMenu, pParam, pValue, bArrowDown, bArrowUp); +} + +void CKeyLabEsDawConnection::UpdateState () +{ +} + +void CKeyLabEsDawConnection::UpdateMenu (CUIMenu::TCPageType Type, s8 ucPage, u8 ucOP, u8 ucTG) +{ +} + +void CKeyLabEsDawConnection::MIDIListener (u8 ucCable, u8 ucChannel, u8 ucType, u8 ucP1, u8 ucP2) +{ + //static const uint8_t pEncoder[] = {0x04, 0x01, 0x60}; + //ArturiaShowNewCCValue (pKeyboard, pEncoder, ucCh, ucCC, ucValue); + switch (ucType) + { + case MIDI_DAW_CHANGE: + HandleMenuEvents (m_pUI, ucP1); + break; + } +} + +CDAWController::CDAWController (CMiniDexed *pSynthesizer, CMIDIKeyboard *pKeyboard, CConfig *pConfig, CUserInterface *pUI) +: m_pSynthesizer (pSynthesizer), + m_pKeyboard (pKeyboard), + m_pConfig (pConfig), + m_pUI (pUI), + m_pDAWConnection (0) +{ +} + +CDAWController::~CDAWController (void) +{ + delete m_pDAWConnection; +} + +void CDAWController::OnConnect (void) +{ + static const uint8_t inquiry[] = {0xF0, 0x7E, 0x7F, 0x06, 0x01, 0xF7}; + + delete m_pDAWConnection; + m_pDAWConnection = 0; + + m_pKeyboard->Send (inquiry, sizeof inquiry, 0); +} + +void CDAWController::MIDISysexHandler (u8 *pPacket, unsigned nLength, unsigned nCable) +{ + static const uint8_t pMiniLab3[] = {0xF0, 0x7E, 0x7F, 0x06, 0x02, 0x00, 0x20, 0x6B, 0x02, 0x00, 0x04, 0x04}; + static const uint8_t pKeyLabEs_49[] = {0xF0, 0x7E, 0x7F, 0x06, 0x02, 0x00, 0x20, 0x6B, 0x02, 0x00, 0x05, 0x52}; + static const uint8_t pKeyLabEs_61[] = {0xF0, 0x7E, 0x7F, 0x06, 0x02, 0x00, 0x20, 0x6B, 0x02, 0x00, 0x05, 0x54}; + static const uint8_t pKeyLabEs_88[] = {0xF0, 0x7E, 0x7F, 0x06, 0x02, 0x00, 0x20, 0x6B, 0x02, 0x00, 0x05, 0x58}; + static const uint8_t pKeyLab2_49[] = {0xF0, 0x7E, 0x7F, 0x06, 0x02, 0x00, 0x20, 0x6B, 0x02, 0x00, 0x05, 0x62}; + static const uint8_t pKeyLab2_61[] = {0xF0, 0x7E, 0x7F, 0x06, 0x02, 0x00, 0x20, 0x6B, 0x02, 0x00, 0x05, 0x64}; + static const uint8_t pKeyLab2_88[] = {0xF0, 0x7E, 0x7F, 0x06, 0x02, 0x00, 0x20, 0x6B, 0x02, 0x00, 0x05, 0x68}; + static const uint8_t pKeyLabEs3_49[] = {0xF0, 0x7E, 0x7F, 0x06, 0x02, 0x00, 0x20, 0x6B, 0x02, 0x00, 0x05, 0x72}; + static const uint8_t pKeyLabEs3_61[] = {0xF0, 0x7E, 0x7F, 0x06, 0x02, 0x00, 0x20, 0x6B, 0x02, 0x00, 0x05, 0x74}; + static const uint8_t pKeyLabEs3_88[] = {0xF0, 0x7E, 0x7F, 0x06, 0x02, 0x00, 0x20, 0x6B, 0x02, 0x00, 0x05, 0x78}; + + if (nLength > sizeof pMiniLab3 && memcmp (pPacket, pMiniLab3, sizeof pMiniLab3) == 0) + { + m_pDAWConnection = new CMiniLab3DawConnection (m_pSynthesizer, m_pKeyboard, m_pConfig, m_pUI); + } + else if (nLength > sizeof pKeyLabEs_49 && ( + memcmp (pPacket, pKeyLabEs_49, sizeof pKeyLabEs_49) == 0 || + memcmp (pPacket, pKeyLabEs_61, sizeof pKeyLabEs_61) == 0 || + memcmp (pPacket, pKeyLabEs_88, sizeof pKeyLabEs_88) == 0)) + { + m_pDAWConnection = new CKeyLabEsDawConnection (m_pSynthesizer, m_pKeyboard, m_pConfig, m_pUI); + } + else if (nLength > sizeof pKeyLab2_61 && ( + memcmp (pPacket, pKeyLab2_49, sizeof pKeyLab2_49) == 0 || + memcmp (pPacket, pKeyLab2_61, sizeof pKeyLab2_61) == 0 || + memcmp (pPacket, pKeyLab2_88, sizeof pKeyLab2_88) == 0)) + { + m_pDAWConnection = new CKeyLab2DawConnection (m_pSynthesizer, m_pKeyboard, m_pConfig, m_pUI); + } + else if (nLength > sizeof pKeyLabEs3_49 && ( + memcmp (pPacket, pKeyLabEs3_49, sizeof pKeyLabEs3_49) == 0 || + memcmp (pPacket, pKeyLabEs3_61, sizeof pKeyLabEs3_61) == 0 || + memcmp (pPacket, pKeyLabEs3_88, sizeof pKeyLabEs3_88) == 0)) + { + m_pDAWConnection = new CKeyLabEs3DawConnection (m_pSynthesizer, m_pKeyboard, m_pConfig, m_pUI); + } +} + +void CDAWController::DisplayWrite (const char *pMenu, const char *pParam, const char *pValue, + bool bArrowDown, bool bArrowUp) +{ + if (m_pDAWConnection) + m_pDAWConnection->DisplayWrite (pMenu, pParam, pValue, bArrowDown, bArrowUp); +} + +void CDAWController::UpdateState (void) +{ + if (m_pDAWConnection) + m_pDAWConnection->UpdateState (); +} + +void CDAWController::UpdateMenu (CUIMenu::TCPageType Type, s8 ucPage, u8 ucOP, u8 ucTG) +{ + if (m_pDAWConnection) + m_pDAWConnection->UpdateMenu (Type, ucPage, ucOP, ucTG); +} + +void CDAWController::MIDIListener (u8 ucCable, u8 ucChannel, u8 ucType, u8 ucP1, u8 ucP2) +{ + if (m_pDAWConnection) + m_pDAWConnection->MIDIListener (ucCable, ucChannel, ucType, ucP1, ucP2); +} diff --git a/src/dawcontroller.h b/src/dawcontroller.h new file mode 100644 index 00000000..0b1f0051 --- /dev/null +++ b/src/dawcontroller.h @@ -0,0 +1,54 @@ +// MiniDexed - Dexed FM synthesizer for bare metal Raspberry Pi +// Copyright (C) 2024 The MiniDexed Team +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +#ifndef _dawcontroller_h +#define _dawcontroller_h + +#include +#include "uimenu.h" + +class CMIDIKeyboard; +class CMiniDexed; +class CDAWConnection; +class CConfig; +class CUserInterface; + +class CDAWController +{ +public: + CDAWController (CMiniDexed *pSynthesizer, CMIDIKeyboard *pKeyboard, CConfig *pConfig, CUserInterface *pUI); + ~CDAWController (void); + + void OnConnect (void); + void MIDISysexHandler (u8 *pPacket, unsigned nLength, unsigned nCable); + + void DisplayWrite (const char *pMenu, const char *pParam, const char *pValue, + bool bArrowDown, bool bArrowUp); + + void UpdateState (void); + void UpdateMenu (CUIMenu::TCPageType Type, s8 ucPage, u8 ucOP, u8 ucTG); + + void MIDIListener (u8 ucCable, u8 ucChannel, u8 ucType, u8 ucP1, u8 ucP2); + +private: + CMiniDexed *m_pSynthesizer; + CMIDIKeyboard *m_pKeyboard; + CConfig *m_pConfig; + CUserInterface *m_pUI; + CDAWConnection *m_pDAWConnection; +}; + +#endif diff --git a/src/effect_compressor.cpp b/src/effect_compressor.cpp index d2aaec7c..1b35c26d 100644 --- a/src/effect_compressor.cpp +++ b/src/effect_compressor.cpp @@ -12,6 +12,7 @@ MIT License. use at your own risk. */ +#include #include #include #include "effect_compressor.h" @@ -203,7 +204,7 @@ void Compressor::setPreGain_dB(float32_t gain_dB) void Compressor::setCompressionRatio(float32_t cr) { - comp_ratio = max(0.001f, cr); //limit to positive values + comp_ratio = std::max(0.001f, cr); //limit to positive values updateThresholdAndCompRatioConstants(); } @@ -213,7 +214,7 @@ void Compressor::setAttack_sec(float32_t a, float32_t fs_Hz) attack_const = expf(-1.0f / (attack_sec * fs_Hz)); //expf() is much faster than exp() //also update the time constant for the envelope extraction - setLevelTimeConst_sec(min(attack_sec,release_sec) / 5.0, fs_Hz); //make the level time-constant one-fifth the gain time constants + setLevelTimeConst_sec(std::min(attack_sec,release_sec) / 5.0, fs_Hz); //make the level time-constant one-fifth the gain time constants } void Compressor::setRelease_sec(float32_t r, float32_t fs_Hz) @@ -222,13 +223,13 @@ void Compressor::setRelease_sec(float32_t r, float32_t fs_Hz) release_const = expf(-1.0f / (release_sec * fs_Hz)); //expf() is much faster than exp() //also update the time constant for the envelope extraction - setLevelTimeConst_sec(min(attack_sec,release_sec) / 5.0, fs_Hz); //make the level time-constant one-fifth the gain time constants + setLevelTimeConst_sec(std::min(attack_sec,release_sec) / 5.0, fs_Hz); //make the level time-constant one-fifth the gain time constants } void Compressor::setLevelTimeConst_sec(float32_t t_sec, float32_t fs_Hz) { const float32_t min_t_sec = 0.002f; //this is the minimum allowed value - level_lp_sec = max(min_t_sec,t_sec); + level_lp_sec = std::max(min_t_sec,t_sec); level_lp_const = expf(-1.0f / (level_lp_sec * fs_Hz)); //expf() is much faster than exp() } diff --git a/src/mididevice.cpp b/src/mididevice.cpp index 216b4663..bb7e4275 100644 --- a/src/mididevice.cpp +++ b/src/mididevice.cpp @@ -22,6 +22,7 @@ // #include +#include #include "mididevice.h" #include "minidexed.h" #include "config.h" @@ -31,28 +32,6 @@ LOGMODULE ("mididevice"); -#define MIDI_NOTE_OFF 0b1000 -#define MIDI_NOTE_ON 0b1001 -#define MIDI_AFTERTOUCH 0b1010 // TODO -#define MIDI_CHANNEL_AFTERTOUCH 0b1101 // right now Synth_Dexed just manage Channel Aftertouch not Polyphonic AT -> 0b1010 -#define MIDI_CONTROL_CHANGE 0b1011 - #define MIDI_CC_BANK_SELECT_MSB 0 - #define MIDI_CC_MODULATION 1 - #define MIDI_CC_BREATH_CONTROLLER 2 - #define MIDI_CC_FOOT_PEDAL 4 - #define MIDI_CC_VOLUME 7 - #define MIDI_CC_PAN_POSITION 10 - #define MIDI_CC_BANK_SELECT_LSB 32 - #define MIDI_CC_BANK_SUSTAIN 64 - #define MIDI_CC_RESONANCE 71 - #define MIDI_CC_FREQUENCY_CUTOFF 74 - #define MIDI_CC_REVERB_LEVEL 91 - #define MIDI_CC_DETUNE_LEVEL 94 - #define MIDI_CC_ALL_SOUND_OFF 120 - #define MIDI_CC_ALL_NOTES_OFF 123 -#define MIDI_PROGRAM_CHANGE 0b1100 -#define MIDI_PITCH_BEND 0b1110 - // MIDI "System" level (i.e. all TG) custom CC maps // Note: Even if number of TGs is not 8, there are only 8 // available to be used in the mappings here. @@ -78,7 +57,8 @@ CMIDIDevice::TDeviceMap CMIDIDevice::s_DeviceMap; CMIDIDevice::CMIDIDevice (CMiniDexed *pSynthesizer, CConfig *pConfig, CUserInterface *pUI) : m_pSynthesizer (pSynthesizer), m_pConfig (pConfig), - m_pUI (pUI) + m_pUI (pUI), + m_pRouteMap () { for (unsigned nTG = 0; nTG < CConfig::AllToneGenerators; nTG++) { @@ -214,10 +194,21 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign m_MIDISpinLock.Acquire (); + u8 ucCable = nCable; u8 ucStatus = pMessage[0]; u8 ucChannel = ucStatus & 0x0F; u8 ucType = ucStatus >> 4; + u8 ucP1 = pMessage[1]; + u8 ucP2 = nLength >= 3 ? pMessage[2] : 0xFF; + bool bSkip = false; + + if (m_pRouteMap) + GetRoutedMIDI (m_pRouteMap, this, &ucCable, &ucChannel, &ucType, &ucP1, &ucP2, &bSkip); + if (bSkip) + { + // skip (and release mutex at the end) + } // GLOBAL MIDI SYSEX // // Master Volume is set using a MIDI SysEx message as follows: @@ -236,7 +227,7 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign // Need to scale the volume parameter to fit // a 14-bit value: 0..16383 // and then split into LSB/MSB. - if (nLength == 8 && + else if (nLength == 8 && pMessage[0] == MIDI_SYSTEM_EXCLUSIVE_BEGIN && pMessage[1] == 0x7F && pMessage[2] == 0x7F && @@ -265,13 +256,13 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign { if ((ucChannel == nPerfCh) || (nPerfCh == OmniMode)) { - if (pMessage[1] == MIDI_CC_BANK_SELECT_MSB) + if (ucP1 == MIDI_CC_BANK_SELECT_MSB) { - m_pSynthesizer->BankSelectMSBPerformance (pMessage[2]); + m_pSynthesizer->BankSelectMSBPerformance (ucP2); } - else if (pMessage[1] == MIDI_CC_BANK_SELECT_LSB) + else if (ucP1 == MIDI_CC_BANK_SELECT_LSB) { - m_pSynthesizer->BankSelectLSBPerformance (pMessage[2]); + m_pSynthesizer->BankSelectLSBPerformance (ucP2); } else { @@ -281,7 +272,7 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign } if (nLength == 3) { - m_pUI->UIMIDICmdHandler (ucChannel, ucStatus & 0xF0, pMessage[1], pMessage[2]); + m_pUI->UIMIDICmdHandler (ucChannel, ucType, ucP1, ucP2); } break; @@ -291,7 +282,7 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign { break; } - m_pUI->UIMIDICmdHandler (ucChannel, ucStatus & 0xF0, pMessage[1], pMessage[2]); + m_pUI->UIMIDICmdHandler (ucChannel, ucType, ucP1, ucP2); break; case MIDI_PROGRAM_CHANGE: @@ -303,7 +294,7 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign if ((ucChannel == nPerfCh) || (nPerfCh == OmniMode)) { //printf("Performance Select Channel %d\n", nPerfCh); - m_pSynthesizer->ProgramChangePerformance (pMessage[1]); + m_pSynthesizer->ProgramChangePerformance (ucP1); } } } @@ -315,6 +306,9 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign bool bSystemCCChecked = false; for (unsigned nTG = 0; nTG < m_pConfig->GetToneGenerators() && !bSystemCCHandled; nTG++) { + if (m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterEnabled, nTG) == 0) + continue; + if (ucStatus == MIDI_SYSTEM_EXCLUSIVE_BEGIN) { // MIDI SYSEX per MIDI channel @@ -338,17 +332,17 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign break; } - if (pMessage[2] > 0) + if (ucP2 > 0) { - if (pMessage[2] <= 127) + if (ucP2 <= 127) { - m_pSynthesizer->keydown (pMessage[1], - pMessage[2], nTG); + m_pSynthesizer->keydown (ucP1, + ucP2, nTG); } } else { - m_pSynthesizer->keyup (pMessage[1], nTG); + m_pSynthesizer->keyup (ucP1, nTG); } break; @@ -358,12 +352,12 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign break; } - m_pSynthesizer->keyup (pMessage[1], nTG); + m_pSynthesizer->keyup (ucP1, nTG); break; case MIDI_CHANNEL_AFTERTOUCH: - m_pSynthesizer->setAftertouch (pMessage[1], nTG); + m_pSynthesizer->setAftertouch (ucP1, nTG); m_pSynthesizer->ControllersRefresh (nTG); break; @@ -373,69 +367,85 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign break; } - switch (pMessage[1]) + switch (ucP1) { case MIDI_CC_MODULATION: - m_pSynthesizer->setModWheel (pMessage[2], nTG); + m_pSynthesizer->setModWheel (ucP2, nTG); m_pSynthesizer->ControllersRefresh (nTG); break; case MIDI_CC_FOOT_PEDAL: - m_pSynthesizer->setFootController (pMessage[2], nTG); + m_pSynthesizer->setFootController (ucP2, nTG); m_pSynthesizer->ControllersRefresh (nTG); break; + case MIDI_CC_PORTAMENTO_TIME: + m_pSynthesizer->setPortamentoTime (maplong (ucP2, 0, 127, 0, 99), nTG); + break; + case MIDI_CC_BREATH_CONTROLLER: - m_pSynthesizer->setBreathController (pMessage[2], nTG); + m_pSynthesizer->setBreathController (ucP2, nTG); m_pSynthesizer->ControllersRefresh (nTG); break; case MIDI_CC_VOLUME: - m_pSynthesizer->SetVolume (pMessage[2], nTG); + m_pSynthesizer->SetVolume (ucP2, nTG); break; case MIDI_CC_PAN_POSITION: - m_pSynthesizer->SetPan (pMessage[2], nTG); + m_pSynthesizer->SetPan (ucP2, nTG); break; case MIDI_CC_BANK_SELECT_MSB: - m_pSynthesizer->BankSelectMSB (pMessage[2], nTG); + m_pSynthesizer->BankSelectMSB (ucP2, nTG); break; case MIDI_CC_BANK_SELECT_LSB: - m_pSynthesizer->BankSelectLSB (pMessage[2], nTG); + m_pSynthesizer->BankSelectLSB (ucP2, nTG); break; case MIDI_CC_BANK_SUSTAIN: - m_pSynthesizer->setSustain (pMessage[2] >= 64, nTG); + m_pSynthesizer->setSustain (ucP2 >= 64, nTG); + break; + + case MIDI_CC_SOSTENUTO: + m_pSynthesizer->setSostenuto (ucP2 >= 64, nTG); + break; + + case MIDI_CC_PORTAMENTO: + m_pSynthesizer->setPortamentoMode (ucP2 >= 64, nTG); + break; + + case MIDI_CC_HOLD2: + m_pSynthesizer->setHoldMode (ucP2 >= 64, nTG); break; case MIDI_CC_RESONANCE: - m_pSynthesizer->SetResonance (maplong (pMessage[2], 0, 127, 0, 99), nTG); + m_pSynthesizer->SetResonance (maplong (ucP2, 0, 127, 0, 99), nTG); break; case MIDI_CC_FREQUENCY_CUTOFF: - m_pSynthesizer->SetCutoff (maplong (pMessage[2], 0, 127, 0, 99), nTG); + m_pSynthesizer->SetCutoff (maplong (ucP2, 0, 127, 0, 99), nTG); break; case MIDI_CC_REVERB_LEVEL: - m_pSynthesizer->SetReverbSend (maplong (pMessage[2], 0, 127, 0, 99), nTG); + m_pSynthesizer->SetReverbSend (maplong (ucP2, 0, 127, 0, 99), nTG); break; case MIDI_CC_DETUNE_LEVEL: - if (pMessage[2] == 0) + if (ucP2 == 0) { // "0 to 127, with 0 being no celeste (detune) effect applied at all." m_pSynthesizer->SetMasterTune (0, nTG); } else { - m_pSynthesizer->SetMasterTune (maplong (pMessage[2], 1, 127, -99, 99), nTG); + m_pSynthesizer->SetMasterTune (maplong (ucP2, 1, 127, -99, 99), nTG); } break; case MIDI_CC_ALL_SOUND_OFF: - m_pSynthesizer->panic (pMessage[2], nTG); + m_pSynthesizer->panic (ucP2, nTG); break; case MIDI_CC_ALL_NOTES_OFF: @@ -444,7 +454,7 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign // "Receivers should ignore an All Notes Off message while Omni is on (Modes 1 & 2)" if (!m_pConfig->GetIgnoreAllNotesOff () && m_ChannelMap[nTG] != OmniMode) { - m_pSynthesizer->notesOff (pMessage[2], nTG); + m_pSynthesizer->notesOff (ucP2, nTG); } break; @@ -454,7 +464,7 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign // so it is possible to break out of the main TG loop too. // Note: We handle this here so we get the TG MIDI channel checking. if (!bSystemCCChecked) { - bSystemCCHandled = HandleMIDISystemCC(pMessage[1], pMessage[2]); + bSystemCCHandled = HandleMIDISystemCC(ucP1, ucP2); bSystemCCChecked = true; } break; @@ -465,7 +475,7 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign // do program change only if enabled in config and not in "Performance Select Channel" mode if( m_pConfig->GetMIDIRXProgramChange() && ( m_pSynthesizer->GetPerformanceSelectChannel() == Disabled) ) { //printf("Program Change to %d (%d)\n", ucChannel, m_pSynthesizer->GetPerformanceSelectChannel()); - m_pSynthesizer->ProgramChange (pMessage[1], nTG); + m_pSynthesizer->ProgramChange (ucP1, nTG); } break; @@ -475,8 +485,8 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign break; } - s16 nValue = pMessage[1]; - nValue |= (s16) pMessage[2] << 7; + s16 nValue = ucP1; + nValue |= (s16) ucP2 << 7; nValue -= 0x2000; m_pSynthesizer->setPitchbend (nValue, nTG); @@ -488,6 +498,9 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign } } } + + if (m_pRouteMap) + MIDIListener(ucCable, ucChannel, ucType, ucP1, ucP2); } m_MIDISpinLock.Release (); } @@ -549,6 +562,11 @@ bool CMIDIDevice::HandleMIDISystemCC(const u8 ucCC, const u8 ucCCval) return false; } +void CMIDIDevice::SetRouteMap (TMIDIRoute *pRouteMap) +{ + m_pRouteMap = pRouteMap; +} + void CMIDIDevice::HandleSystemExclusive(const uint8_t* pMessage, const size_t nLength, const unsigned nCable, const uint8_t nTG) { int16_t sysex_return; @@ -677,6 +695,11 @@ void CMIDIDevice::HandleSystemExclusive(const uint8_t* pMessage, const size_t nL } } +void CMIDIDevice::MIDIListener (u8 ucCable, u8 ucChannel, u8 ucType, u8 ucP1, u8 ucP2) +{ +} + + void CMIDIDevice::SendSystemExclusiveVoice(uint8_t nVoice, const unsigned nCable, uint8_t nTG) { uint8_t voicedump[163]; @@ -693,3 +716,90 @@ void CMIDIDevice::SendSystemExclusiveVoice(uint8_t nVoice, const unsigned nCable // LOGDBG("Send SYSEX voice dump %u to \"%s\"",nVoice,Iterator->first.c_str()); } } + +void CMIDIDevice::s_HandleTimerTimeout(TKernelTimerHandle hTimer, void *pParam, void *pContext) +{ + CMIDIDevice *pDevice = static_cast(pParam); + TMIDIRoute *pRoute = static_cast(pContext); + TMIDIRoute *pTarget = pRoute + pRoute->ucTimerTarget; + + pRoute->hTimer = 0; + pRoute->bGroupActive = false; + pRoute->bGroupHold = false; + if (!pTarget->bSkip) + pDevice->MIDIListener (pTarget->ucSCable, pTarget->ucDCh, pTarget->ucDType, pTarget->ucDP1, pTarget->ucDP2); +} + + +void GetRoutedMIDI (TMIDIRoute *pRouteMap, CMIDIDevice *pDevice, u8 *pCable, u8 *pCh, u8 *pType, u8 *pP1, u8 *pP2, bool *bSkip) +{ + assert (pRouteMap); + for (TMIDIRoute *r = pRouteMap; r->ucSCable != 0xFF ; r++) + { + if (r->ucSCable == *pCable && + (r->ucSCh == *pCh || r->ucSCh >= 16) && + (r->ucSType == *pType || r->ucSType >= 16) && + (r->ucSP1 == *pP1 || r->ucSP1 > 127) && + (r->ucSP2 == *pP2 || r->ucSP2 == 0xFF || + r->ucSP2 == TMIDIRoute::LtCenter && *pP2 < 64 || + r->ucSP2 == TMIDIRoute::GtCenter && *pP2 > 64) + ) + { + if (r->usTimerExpire) + r->hTimer = CTimer::Get ()->StartKernelTimer (MSEC2HZ(r->usTimerExpire), CMIDIDevice::s_HandleTimerTimeout, pDevice, r); + + if (r->usTimerExpire || r->bGroupHead) + r->bGroupActive = true; + + if (r->bSkip) { + *bSkip = true; + return; + } + + if (r->bGroup) { + TMIDIRoute *parent = r - 1; + for (; parent > pRouteMap && parent->bGroup; parent--); + + if (parent->hTimer) { + CTimer::Get ()->CancelKernelTimer (parent->hTimer); + parent->hTimer = 0; + } + + if (!r->bGroupHold) + parent->bGroupHold = false; + + if (!parent->bGroupActive && !parent->bGroupHold) { + // skip at the end if not captured by other routes + *bSkip = true; + continue; + } + + // hold the group for bGroupHold routes + if (r->bGroupHold) + parent->bGroupHold = true; + + parent->bGroupActive = false; + } + + *pCh = r->ucDCh; + *pType = r->ucDType; + if (r->ucDP1 <= 127) + *pP1 = r->ucDP1; + if (r->ucDP1 == TMIDIRoute::P2) + *pP1 = *pP2; + if (r->ucDP2 <= 127) + *pP2 = r->ucDP2; + if (r->bToggle) + r->ucDP2 = r->ucDP2 ? 0x0 : 0x7F; + *bSkip = false; + return; + } + } +} + +TMIDIRoute::~TMIDIRoute () +{ + if (hTimer) + CTimer::Get ()->CancelKernelTimer (hTimer); + hTimer = 0; +} diff --git a/src/mididevice.h b/src/mididevice.h index 44f16916..6e9a43bf 100644 --- a/src/mididevice.h +++ b/src/mididevice.h @@ -33,7 +33,67 @@ #define MAX_DX7_SYSEX_LENGTH 4104 #define MAX_MIDI_MESSAGE MAX_DX7_SYSEX_LENGTH +#define MIDI_NOTE_OFF 0b1000 +#define MIDI_NOTE_ON 0b1001 +#define MIDI_AFTERTOUCH 0b1010 // TODO +#define MIDI_CHANNEL_AFTERTOUCH 0b1101 // right now Synth_Dexed just manage Channel Aftertouch not Polyphonic AT -> 0b1010 +#define MIDI_CONTROL_CHANGE 0b1011 + #define MIDI_CC_BANK_SELECT_MSB 0 + #define MIDI_CC_MODULATION 1 + #define MIDI_CC_BREATH_CONTROLLER 2 + #define MIDI_CC_FOOT_PEDAL 4 + #define MIDI_CC_PORTAMENTO_TIME 5 + #define MIDI_CC_VOLUME 7 + #define MIDI_CC_PAN_POSITION 10 + #define MIDI_CC_BANK_SELECT_LSB 32 + #define MIDI_CC_BANK_SUSTAIN 64 + #define MIDI_CC_PORTAMENTO 65 + #define MIDI_CC_SOSTENUTO 66 + #define MIDI_CC_HOLD2 69 + #define MIDI_CC_RESONANCE 71 + #define MIDI_CC_FREQUENCY_CUTOFF 74 + #define MIDI_CC_REVERB_LEVEL 91 + #define MIDI_CC_DETUNE_LEVEL 94 + #define MIDI_CC_ALL_SOUND_OFF 120 + #define MIDI_CC_ALL_NOTES_OFF 123 +#define MIDI_PROGRAM_CHANGE 0b1100 +#define MIDI_PITCH_BEND 0b1110 + class CMiniDexed; +class CMIDIDevice; + +struct TMIDIRoute +{ + ~TMIDIRoute (); + + enum TRouteOP + { + P2 = 0x82, + LtCenter = 0x90, + GtCenter = 0x91, + }; + + u8 ucSCable; + u8 ucSCh; + u8 ucSType; + u8 ucSP1; + u8 ucSP2; + u8 ucDCh; + u8 ucDType; + u8 ucDP1; + u8 ucDP2; + u8 ucTimerTarget; + unsigned usTimerExpire; + TKernelTimerHandle hTimer; + bool bSkip; + bool bToggle; + bool bGroup; + bool bGroupHead; + bool bGroupActive; + bool bGroupHold; // hold flag for GroupHeads, or set hold the group +}; + +void GetRoutedMIDI (TMIDIRoute *pRouteMap, CMIDIDevice *pDevice, u8 *pCable, u8 *pChannel, u8 *pType, u8 *pP1, u8 *pP2, bool *bSkip); class CMIDIDevice { @@ -53,14 +113,20 @@ class CMIDIDevice void SetChannel (u8 ucChannel, unsigned nTG); u8 GetChannel (unsigned nTG) const; + void SetRouteMap (TMIDIRoute *pRouteMap); + virtual void Send (const u8 *pMessage, size_t nLength, unsigned nCable = 0) {} virtual void SendSystemExclusiveVoice(uint8_t nVoice, const unsigned nCable, uint8_t nTG); + static void s_HandleTimerTimeout(TKernelTimerHandle hTimer, void *pParam, void *pContext); + protected: void MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsigned nCable = 0); void AddDevice (const char *pDeviceName); void HandleSystemExclusive(const uint8_t* pMessage, const size_t nLength, const unsigned nCable, const uint8_t nTG); + virtual void MIDIListener (u8 ucCable, u8 ucChannel, u8 ucType, u8 ucP1, u8 ucP2); + private: bool HandleMIDISystemCC(const u8 ucCC, const u8 ucCCval); @@ -78,6 +144,8 @@ class CMIDIDevice std::string m_DeviceName; + TMIDIRoute *m_pRouteMap; + typedef std::unordered_map TDeviceMap; static TDeviceMap s_DeviceMap; diff --git a/src/midikeyboard.cpp b/src/midikeyboard.cpp index db71168d..f81cc7cb 100644 --- a/src/midikeyboard.cpp +++ b/src/midikeyboard.cpp @@ -29,15 +29,20 @@ CMIDIKeyboard::CMIDIKeyboard (CMiniDexed *pSynthesizer, CConfig *pConfig, CUserI : CMIDIDevice (pSynthesizer, pConfig, pUI), m_nSysExIdx (0), m_nInstance (nInstance), - m_pMIDIDevice (0) + m_pMIDIDevice (0), + m_pDAWController (0) { m_DeviceName.Format ("umidi%u", nInstance+1); AddDevice (m_DeviceName); + + if (pConfig->GetDAWControllerEnabled ()) + m_pDAWController = new CDAWController (pSynthesizer, this, pConfig, pUI); } CMIDIKeyboard::~CMIDIKeyboard (void) { + delete m_pDAWController; } void CMIDIKeyboard::Process (boolean bPlugAndPlayUpdated) @@ -69,6 +74,9 @@ void CMIDIKeyboard::Process (boolean bPlugAndPlayUpdated) m_pMIDIDevice->RegisterPacketHandler (MIDIPacketHandler, this); m_pMIDIDevice->RegisterRemovedHandler (DeviceRemovedHandler, this); + + if (m_pDAWController) + m_pDAWController->OnConnect(); } } } @@ -85,6 +93,13 @@ void CMIDIKeyboard::Send (const u8 *pMessage, size_t nLength, unsigned nCable) m_SendQueue.push (Entry); } +void CMIDIKeyboard::SendDebounce (const u8 *pMessage, size_t nLength, unsigned nCable) +{ + TSendQueueEntry Entry = m_SendQueue.back (); + if (Entry.nLength != nLength || Entry.nCable != nCable || memcmp (Entry.pMessage, pMessage, nLength) != 0) + Send (pMessage, nLength, nCable); +} + // Most packets will be passed straight onto the main MIDI message handler // but SysEx messages are multiple USB packets and so will need building up // before parsing. @@ -122,6 +137,10 @@ void CMIDIKeyboard::USBMIDIMessageHandler (u8 *pPacket, unsigned nLength, unsign m_SysEx[m_nSysExIdx++] = pPacket[i]; //printf ("SysEx End Idx=%d\n", m_nSysExIdx); MIDIMessageHandler (m_SysEx, m_nSysExIdx, nCable); + + if (m_pDAWController) + m_pDAWController->MIDISysexHandler (m_SysEx, m_nSysExIdx, nCable); + // Reset ready for next time m_nSysExIdx = 0; } @@ -160,3 +179,28 @@ void CMIDIKeyboard::DeviceRemovedHandler (CDevice *pDevice, void *pContext) pThis->m_pMIDIDevice = 0; } + +void CMIDIKeyboard::DisplayWrite (const char *pMenu, const char *pParam, const char *pValue, + bool bArrowDown, bool bArrowUp) +{ + if (m_pMIDIDevice && m_pDAWController) + m_pDAWController->DisplayWrite (pMenu, pParam, pValue, bArrowDown, bArrowUp); +} + +void CMIDIKeyboard::UpdateDAWState (void) +{ + if (m_pMIDIDevice && m_pDAWController) + m_pDAWController->UpdateState (); +} + +void CMIDIKeyboard::UpdateDAWMenu (CUIMenu::TCPageType Type, s8 ucPage, u8 ucOP, u8 ucTG) +{ + if (m_pMIDIDevice && m_pDAWController) + m_pDAWController->UpdateMenu (Type, ucPage, ucOP, ucTG); +} + +void CMIDIKeyboard::MIDIListener (u8 ucCable, u8 ucChannel, u8 ucType, u8 ucP1, u8 ucP2) +{ + if (m_pDAWController) + m_pDAWController->MIDIListener (ucCable, ucChannel, ucType, ucP1, ucP2); +} diff --git a/src/midikeyboard.h b/src/midikeyboard.h index bf626890..66f9001f 100644 --- a/src/midikeyboard.h +++ b/src/midikeyboard.h @@ -25,6 +25,7 @@ #include "mididevice.h" #include "config.h" +#include "dawcontroller.h" #include #include #include @@ -44,6 +45,13 @@ class CMIDIKeyboard : public CMIDIDevice void Process (boolean bPlugAndPlayUpdated); void Send (const u8 *pMessage, size_t nLength, unsigned nCable = 0) override; + void SendDebounce (const u8 *pMessage, size_t nLength, unsigned nCable = 0); + + void DisplayWrite (const char *pMenu, const char *pParam, const char *pValue, + bool bArrowDown, bool bArrowUp); + + void UpdateDAWState (void); + void UpdateDAWMenu (CUIMenu::TCPageType Type, s8 ucPage, u8 ucOP, u8 ucTG); private: static void MIDIPacketHandler (unsigned nCable, u8 *pPacket, unsigned nLength, unsigned nDevice, void *pParam); @@ -51,6 +59,8 @@ class CMIDIKeyboard : public CMIDIDevice void USBMIDIMessageHandler (u8 *pPacket, unsigned nLength, unsigned nCable, unsigned nDevice); + void MIDIListener (u8 ucCable, u8 ucChannel, u8 ucType, u8 ucP1, u8 ucP2) override; + private: struct TSendQueueEntry { @@ -68,6 +78,8 @@ class CMIDIKeyboard : public CMIDIDevice CUSBMIDIDevice * volatile m_pMIDIDevice; std::queue m_SendQueue; + + CDAWController *m_pDAWController; }; #endif diff --git a/src/midipin.cpp b/src/midipin.cpp index d03d73aa..b63b79b4 100644 --- a/src/midipin.cpp +++ b/src/midipin.cpp @@ -25,7 +25,8 @@ LOGMODULE ("midipin"); CMIDIPin::CMIDIPin (unsigned nPinNumber) : m_nPinNumber (nPinNumber), - m_nValue (HIGH) + m_nValue (HIGH), + m_nRawValue (MIDIPIN_CENTER) { } @@ -38,6 +39,11 @@ unsigned CMIDIPin::Read (void) return m_nValue; } +unsigned CMIDIPin::ReadRaw (void) +{ + return m_nRawValue; +} + void CMIDIPin::Write (unsigned nValue) { // Takes values in the MIDI controller range 0 to 127 @@ -50,6 +56,10 @@ void CMIDIPin::Write (unsigned nValue) // "off" m_nValue = HIGH; } + + // Save the raw value for INC and DEC + m_nRawValue = nValue; + return; } diff --git a/src/midipin.h b/src/midipin.h index 7fa3f1db..0764966d 100644 --- a/src/midipin.h +++ b/src/midipin.h @@ -32,6 +32,8 @@ #define MidiPinToCC(p) (((p)>=MIDI_PINS)?((p)-MIDI_PINS):0) #define isMidiPin(p) (((p)>=MIDI_PINS)?1:0) +#define MIDIPIN_CENTER 64 + class CMIDIPin { public: @@ -42,7 +44,10 @@ class CMIDIPin // Should be treated as a PULLED UP IO pin // i.e. treated as "active low" (LOW) when pressed. unsigned Read (void); - + + // returns the raw CC value + unsigned ReadRaw (void); + // MIDI CC values >=64 will set the MIDI pin to LOW ("on") // MIDI CC values <= 63 will set the MIDI pin to HIGH ("off") void Write (unsigned nValue); @@ -50,6 +55,7 @@ class CMIDIPin private: unsigned m_nPinNumber; unsigned m_nValue; + unsigned m_nRawValue; }; #endif diff --git a/src/minidexed.cpp b/src/minidexed.cpp index 1a714fc8..f45c9009 100644 --- a/src/minidexed.cpp +++ b/src/minidexed.cpp @@ -98,6 +98,8 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt, m_nReverbSend[i] = 0; + m_bEnabled[i] = 1; + // Active the required number of active TGs if (isetSustain (sustain); } +void CMiniDexed::setSostenuto(bool sostenuto, unsigned nTG) +{ + assert (nTG < CConfig::AllToneGenerators); + if (nTG >= m_nToneGenerators) return; // Not an active TG + + assert (m_pTG[nTG]); + m_pTG[nTG]->setSostenuto (sostenuto); +} + +void CMiniDexed::setHoldMode(bool holdmode, unsigned nTG) +{ + assert (nTG < CConfig::AllToneGenerators); + if (nTG >= m_nToneGenerators) return; // Not an active TG + + assert (m_pTG[nTG]); + m_pTG[nTG]->setHold (holdmode); +} + void CMiniDexed::panic(uint8_t value, unsigned nTG) { assert (nTG < CConfig::AllToneGenerators); @@ -922,6 +942,7 @@ void CMiniDexed::SetParameter (TParameter Parameter, int nValue) assert (m_pTG[nTG]); m_pTG[nTG]->setCompressor (!!nValue); } + m_UI.ParameterChanged (); break; case ParameterReverbEnable: @@ -929,6 +950,7 @@ void CMiniDexed::SetParameter (TParameter Parameter, int nValue) m_ReverbSpinLock.Acquire (); reverb->set_bypass (!nValue); m_ReverbSpinLock.Release (); + m_UI.ParameterChanged (); break; case ParameterReverbSize: @@ -936,6 +958,7 @@ void CMiniDexed::SetParameter (TParameter Parameter, int nValue) m_ReverbSpinLock.Acquire (); reverb->size (nValue / 99.0f); m_ReverbSpinLock.Release (); + m_UI.ParameterChanged (); break; case ParameterReverbHighDamp: @@ -943,6 +966,7 @@ void CMiniDexed::SetParameter (TParameter Parameter, int nValue) m_ReverbSpinLock.Acquire (); reverb->hidamp (nValue / 99.0f); m_ReverbSpinLock.Release (); + m_UI.ParameterChanged (); break; case ParameterReverbLowDamp: @@ -950,6 +974,7 @@ void CMiniDexed::SetParameter (TParameter Parameter, int nValue) m_ReverbSpinLock.Acquire (); reverb->lodamp (nValue / 99.0f); m_ReverbSpinLock.Release (); + m_UI.ParameterChanged (); break; case ParameterReverbLowPass: @@ -957,6 +982,7 @@ void CMiniDexed::SetParameter (TParameter Parameter, int nValue) m_ReverbSpinLock.Acquire (); reverb->lowpass (nValue / 99.0f); m_ReverbSpinLock.Release (); + m_UI.ParameterChanged (); break; case ParameterReverbDiffusion: @@ -964,6 +990,7 @@ void CMiniDexed::SetParameter (TParameter Parameter, int nValue) m_ReverbSpinLock.Acquire (); reverb->diffusion (nValue / 99.0f); m_ReverbSpinLock.Release (); + m_UI.ParameterChanged (); break; case ParameterReverbLevel: @@ -971,6 +998,7 @@ void CMiniDexed::SetParameter (TParameter Parameter, int nValue) m_ReverbSpinLock.Acquire (); reverb->level (nValue / 99.0f); m_ReverbSpinLock.Release (); + m_UI.ParameterChanged (); break; case ParameterPerformanceSelectChannel: @@ -1015,6 +1043,7 @@ void CMiniDexed::SetTGParameter (TTGParameter Parameter, int nValue, unsigned nT case TGParameterPortamentoGlissando: setPortamentoGlissando (nValue, nTG); break; case TGParameterPortamentoTime: setPortamentoTime (nValue, nTG); break; case TGParameterMonoMode: setMonoMode (nValue , nTG); break; + case TGParameterEnabled: setEnabled (nValue, nTG); break; case TGParameterMWRange: setModController(0, 0, nValue, nTG); break; case TGParameterMWPitch: setModController(0, 1, nValue, nTG); break; @@ -1072,6 +1101,7 @@ int CMiniDexed::GetTGParameter (TTGParameter Parameter, unsigned nTG) case TGParameterPortamentoGlissando: return m_nPortamentoGlissando[nTG]; case TGParameterPortamentoTime: return m_nPortamentoTime[nTG]; case TGParameterMonoMode: return m_bMonoMode[nTG] ? 1 : 0; + case TGParameterEnabled: return m_bEnabled[nTG] ? 1 : 0; case TGParameterMWRange: return getModController(0, 0, nTG); case TGParameterMWPitch: return getModController(0, 1, nTG); @@ -1122,6 +1152,7 @@ void CMiniDexed::SetVoiceParameter (uint8_t uchOffset, uint8_t uchValue, unsigne } m_pTG[nTG]->setOPAll (m_uchOPMask[nTG]); + m_UI.ParameterChanged (); return; } @@ -1133,6 +1164,7 @@ void CMiniDexed::SetVoiceParameter (uint8_t uchOffset, uint8_t uchValue, unsigne assert (uchOffset < 156); m_pTG[nTG]->setVoiceDataElement (uchOffset, uchValue); + m_UI.ParameterChanged (); } uint8_t CMiniDexed::GetVoiceParameter (uint8_t uchOffset, unsigned nOP, unsigned nTG) @@ -1501,6 +1533,17 @@ void CMiniDexed::setMonoMode(uint8_t mono, uint8_t nTG) m_UI.ParameterChanged (); } +void CMiniDexed::setEnabled (uint8_t enabled, uint8_t nTG) +{ + assert (nTG < CConfig::AllToneGenerators); + if (nTG >= m_nToneGenerators) return; // Not an active TG + assert (m_pTG[nTG]); + + m_bEnabled[nTG] = enabled != 0; + + m_UI.ParameterChanged (); +} + void CMiniDexed::setPitchbendRange(uint8_t range, uint8_t nTG) { range = constrain (range, 0, 12); @@ -1775,6 +1818,33 @@ void CMiniDexed::setMasterVolume (float32_t vol) nMasterVolume=vol; } +void CMiniDexed::DisplayWrite (const char *pMenu, const char *pParam, const char *pValue, + bool bArrowDown, bool bArrowUp) +{ + m_UI.DisplayWrite (pMenu, pParam, pValue, bArrowDown, bArrowUp); + + for (unsigned i = 0; i < CConfig::MaxUSBMIDIDevices; i++) + { + m_pMIDIKeyboard[i]->DisplayWrite (pMenu, pParam, pValue, bArrowDown, bArrowUp); + } +} + +void CMiniDexed::UpdateDAWState () +{ + for (unsigned i = 0; i < CConfig::MaxUSBMIDIDevices; i++) + { + m_pMIDIKeyboard[i]->UpdateDAWState (); + } +} + +void CMiniDexed::UpdateDAWMenu (CUIMenu::TCPageType Type, s8 ucPage, u8 ucOP, u8 ucTG) +{ + for (unsigned i = 0; i < CConfig::MaxUSBMIDIDevices; i++) + { + m_pMIDIKeyboard[i]->UpdateDAWMenu (Type, ucPage, ucOP, ucTG); + } +} + std::string CMiniDexed::GetPerformanceFileName(unsigned nID) { return m_PerformanceConfig.GetPerformanceFileName(nID); @@ -1938,6 +2008,7 @@ void CMiniDexed::LoadPerformanceParameters(void) m_pTG[nTG]->loadVoiceParameters(tVoiceData); } setMonoMode(m_PerformanceConfig.GetMonoMode(nTG) ? 1 : 0, nTG); + setEnabled(1, nTG); SetReverbSend (m_PerformanceConfig.GetReverbSend (nTG), nTG); setModWheelRange (m_PerformanceConfig.GetModulationWheelRange (nTG), nTG); diff --git a/src/minidexed.h b/src/minidexed.h index bb6290b2..ccc2aad3 100644 --- a/src/minidexed.h +++ b/src/minidexed.h @@ -84,6 +84,8 @@ class CMiniDexed void keydown (int16_t pitch, uint8_t velocity, unsigned nTG); void setSustain (bool sustain, unsigned nTG); + void setSostenuto (bool sostenuto, unsigned nTG); + void setHoldMode(bool holdmode, unsigned nTG); void panic (uint8_t value, unsigned nTG); void notesOff (uint8_t value, unsigned nTG); void setModWheel (uint8_t value, unsigned nTG); @@ -97,6 +99,7 @@ class CMiniDexed void SetReverbSend (unsigned nReverbSend, unsigned nTG); // 0 .. 127 void setMonoMode(uint8_t mono, uint8_t nTG); + void setEnabled(uint8_t enabled, uint8_t nTG); void setPitchbendRange(uint8_t range, uint8_t nTG); void setPitchbendStep(uint8_t step, uint8_t nTG); void setPortamentoMode(uint8_t mode, uint8_t nTG); @@ -189,7 +192,9 @@ class CMiniDexed TGParameterPortamentoGlissando, TGParameterPortamentoTime, TGParameterMonoMode, - + + TGParameterEnabled, + TGParameterMWRange, TGParameterMWPitch, TGParameterMWAmplitude, @@ -228,6 +233,12 @@ class CMiniDexed void setMasterVolume (float32_t vol); + void DisplayWrite (const char *pMenu, const char *pParam, const char *pValue, + bool bArrowDown, bool bArrowUp); + + void UpdateDAWState (); + void UpdateDAWMenu (CUIMenu::TCPageType Type, s8 ucPage, u8 ucOP, u8 ucTG); + private: int16_t ApplyNoteLimits (int16_t pitch, unsigned nTG); // returns < 0 to ignore note uint8_t m_uchOPMask[CConfig::AllToneGenerators]; @@ -272,7 +283,8 @@ class CMiniDexed unsigned m_nPortamentoGlissando[CConfig::AllToneGenerators]; unsigned m_nPortamentoTime[CConfig::AllToneGenerators]; bool m_bMonoMode[CConfig::AllToneGenerators]; - + bool m_bEnabled[CConfig::AllToneGenerators]; + unsigned m_nModulationWheelRange[CConfig::AllToneGenerators]; unsigned m_nModulationWheelTarget[CConfig::AllToneGenerators]; unsigned m_nFootControlRange[CConfig::AllToneGenerators]; diff --git a/src/minidexed.ini b/src/minidexed.ini index 5b6b13d8..8f872686 100644 --- a/src/minidexed.ini +++ b/src/minidexed.ini @@ -116,7 +116,8 @@ LongPressTimeout=400 # MIDI Button Navigation # Specify MIDI CC to act as a button (0 = ununsed, so don't use CC 0) -# NB: Off < 64 < ON +# NB: Off < 64 < ON for click / doubleclick / longpress actions +# DEC < 64 < INC for dec / inc actions # CC channel: 0=OFF; 1-16 MIDI Ch; >16 Omni # If MIDIButtonNotes>0 then treat MIDIButton numbers as MIDI # Note numbers, triggered with NoteOn/NoteOff, not CC numbers. @@ -124,20 +125,34 @@ MIDIButtonCh=17 MIDIButtonNotes=0 # Arrow left MIDIButtonPrev=46 +MIDIButtonActionPrev=click # Arrow right MIDIButtonNext=47 +MIDIButtonActionNext=click # Arrow up MIDIButtonBack=48 +MIDIButtonActionBack=click # Arrow down MIDIButtonSelect=49 +MIDIButtonActionSelect=click # Home button MIDIButtonHome=50 +MIDIButtonActionHome=click MIDIButtonPgmUp=51 +MIDIButtonActionPgmUp=click MIDIButtonPgmDown=52 +MIDIButtonActionPgmDown=click MIDIButtonBankUp=53 +MIDIButtonActionBankUp=click MIDIButtonBankDown=54 +MIDIButtonActionBankDown=click MIDIButtonTGUp=55 +MIDIButtonActionTGUp=click MIDIButtonTGDown=56 +MIDIButtonActionTGDown=click + +# DAW Controller (Arturia MiniLab 3, KeyLab Essential, KeyLab Essential 3, Keylab mkII) +DAWControllerEnabled=0 # KY-040 Rotary Encoder EncoderEnabled=1 diff --git a/src/uibuttons.cpp b/src/uibuttons.cpp index ae206dcc..bc5681dd 100644 --- a/src/uibuttons.cpp +++ b/src/uibuttons.cpp @@ -18,6 +18,7 @@ // along with this program. If not, see . // #include "uibuttons.h" +#include "mididevice.h" #include #include #include @@ -36,6 +37,8 @@ CUIButton::CUIButton (void) m_clickEvent(BtnEventNone), m_doubleClickEvent(BtnEventNone), m_longPressEvent(BtnEventNone), + m_decEvent(BtnEventNone), + m_incEvent(BtnEventNone), m_doubleClickTimeout(0), m_longPressTimeout(0) { @@ -101,6 +104,16 @@ void CUIButton::setLongPressEvent(BtnEvent longPressEvent) m_longPressEvent = longPressEvent; } +void CUIButton::setDecEvent(BtnEvent decEvent) +{ + m_decEvent = decEvent; +} + +void CUIButton::setIncEvent(BtnEvent incEvent) +{ + m_incEvent = incEvent; +} + unsigned CUIButton::getPinNumber(void) { return m_pinNumber; @@ -109,6 +122,7 @@ unsigned CUIButton::getPinNumber(void) CUIButton::BtnTrigger CUIButton::ReadTrigger (void) { unsigned value; + if (isMidiPin(m_pinNumber)) { if (!m_midipin) @@ -117,6 +131,18 @@ CUIButton::BtnTrigger CUIButton::ReadTrigger (void) return BtnTriggerNone; } value = m_midipin->Read(); + + if (m_decEvent || m_incEvent) + { + unsigned raw = m_midipin->ReadRaw(); + + if (raw == MIDIPIN_CENTER) + return BtnTriggerNone; + + // reset value to trigger only once + m_midipin->Write(MIDIPIN_CENTER); + return raw < MIDIPIN_CENTER ? BtnTriggerDec : BtnTriggerInc; + } } else { @@ -230,6 +256,12 @@ CUIButton::BtnEvent CUIButton::Read (void) { else if (trigger == BtnTriggerLongPress) { return m_longPressEvent; } + else if (trigger == BtnTriggerDec) { + return m_decEvent; + } + else if (trigger == BtnTriggerInc) { + return m_incEvent; + } assert (trigger == BtnTriggerNone); @@ -250,6 +282,12 @@ CUIButton::BtnTrigger CUIButton::triggerTypeFromString(const char* triggerString else if (strcmp(triggerString, "longpress") == 0) { return BtnTriggerLongPress; } + else if (strcmp(triggerString, "dec") == 0) { + return BtnTriggerDec; + } + else if (strcmp(triggerString, "inc") == 0) { + return BtnTriggerInc; + } LOGERR("Invalid action: %s", triggerString); @@ -299,17 +337,28 @@ boolean CUIButtons::Initialize (void) m_TGDownAction = CUIButton::triggerTypeFromString( m_pConfig->GetButtonActionTGDown ()); m_notesMidi = ccToMidiPin( m_pConfig->GetMIDIButtonNotes ()); m_prevMidi = ccToMidiPin( m_pConfig->GetMIDIButtonPrev ()); + m_prevMidiAction = CUIButton::triggerTypeFromString( m_pConfig->GetMIDIButtonActionPrev ()); m_nextMidi = ccToMidiPin( m_pConfig->GetMIDIButtonNext ()); + m_nextMidiAction = CUIButton::triggerTypeFromString( m_pConfig->GetMIDIButtonActionNext ()); m_backMidi = ccToMidiPin( m_pConfig->GetMIDIButtonBack ()); + m_backMidiAction = CUIButton::triggerTypeFromString( m_pConfig->GetMIDIButtonActionBack ()); m_selectMidi = ccToMidiPin( m_pConfig->GetMIDIButtonSelect ()); + m_selectMidiAction = CUIButton::triggerTypeFromString( m_pConfig->GetMIDIButtonActionSelect ()); m_homeMidi = ccToMidiPin( m_pConfig->GetMIDIButtonHome ()); + m_homeMidiAction = CUIButton::triggerTypeFromString( m_pConfig->GetMIDIButtonActionHome ()); m_pgmUpMidi = ccToMidiPin( m_pConfig->GetMIDIButtonPgmUp ()); + m_pgmUpMidiAction = CUIButton::triggerTypeFromString( m_pConfig->GetMIDIButtonActionPgmUp ()); m_pgmDownMidi = ccToMidiPin( m_pConfig->GetMIDIButtonPgmDown ()); + m_pgmDownMidiAction = CUIButton::triggerTypeFromString( m_pConfig->GetMIDIButtonActionPgmDown ()); m_BankUpMidi = ccToMidiPin( m_pConfig->GetMIDIButtonBankUp ()); + m_BankUpMidiAction = CUIButton::triggerTypeFromString( m_pConfig->GetMIDIButtonActionBankUp ()); m_BankDownMidi = ccToMidiPin( m_pConfig->GetMIDIButtonBankDown ()); + m_BankDownMidiAction = CUIButton::triggerTypeFromString( m_pConfig->GetMIDIButtonActionBankDown ()); m_TGUpMidi = ccToMidiPin( m_pConfig->GetMIDIButtonTGUp ()); + m_TGUpMidiAction = CUIButton::triggerTypeFromString( m_pConfig->GetMIDIButtonActionTGUp ()); m_TGDownMidi = ccToMidiPin( m_pConfig->GetMIDIButtonTGDown ()); - + m_TGDownMidiAction = CUIButton::triggerTypeFromString( m_pConfig->GetMIDIButtonActionTGDown ()); + // First sanity check and convert the timeouts: // Internally values are in tenths of a millisecond, but config values // are in milliseconds @@ -328,7 +377,7 @@ boolean CUIButtons::Initialize (void) // Each normal button can be assigned up to 3 actions: click, doubleclick and // longpress. We may not initialise all of the buttons. - // MIDI buttons only support a single click. + // MIDI buttons can be assigned to click, doubleclick, longpress, dec, inc unsigned pins[MAX_BUTTONS] = { m_prevPin, m_nextPin, m_backPin, m_selectPin, m_homePin, m_pgmUpPin, m_pgmDownPin, m_BankUpPin, m_BankDownPin, m_TGUpPin, m_TGDownPin, m_prevMidi, m_nextMidi, m_backMidi, m_selectMidi, m_homeMidi, m_pgmUpMidi, m_pgmDownMidi, m_BankUpMidi, m_BankDownMidi, m_TGUpMidi, m_TGDownMidi @@ -337,9 +386,9 @@ boolean CUIButtons::Initialize (void) // Normal buttons m_prevAction, m_nextAction, m_backAction, m_selectAction, m_homeAction, m_pgmUpAction, m_pgmDownAction, m_BankUpAction, m_BankDownAction, m_TGUpAction, m_TGDownAction, - // MIDI Buttons only support a single click (at present) - CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick, - CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick + // MIDI buttons + m_prevMidiAction, m_nextMidiAction, m_backMidiAction, m_selectMidiAction, m_homeMidiAction, + m_pgmUpMidiAction, m_pgmDownMidiAction, m_BankUpMidiAction, m_BankDownMidiAction, m_TGUpMidiAction, m_TGDownMidiAction, }; CUIButton::BtnEvent events[MAX_BUTTONS] = { // Normal buttons @@ -439,6 +488,12 @@ void CUIButtons::bindButton(unsigned pinNumber, CUIButton::BtnTrigger trigger, C else if (trigger == CUIButton::BtnTriggerLongPress) { m_buttons[i].setLongPressEvent(event); } + else if (trigger == CUIButton::BtnTriggerDec) { + m_buttons[i].setDecEvent(event); + } + else if (trigger == CUIButton::BtnTriggerInc) { + m_buttons[i].setIncEvent(event); + } else { assert (trigger == CUIButton::BtnTriggerNone); } @@ -490,21 +545,20 @@ void CUIButtons::ResetButton (unsigned pinNumber) } } -void CUIButtons::BtnMIDICmdHandler (unsigned nMidiCmd, unsigned nMidiData1, unsigned nMidiData2) +void CUIButtons::BtnMIDICmdHandler (unsigned nMidiType, unsigned nMidiData1, unsigned nMidiData2) { if (m_notesMidi > 0) { -// LOGDBG("BtnMIDICmdHandler (notes): %x %x %x)", nMidiCmd, nMidiData1, nMidiData2); +// LOGDBG("BtnMIDICmdHandler (notes): %x %x %x)", nMidiType, nMidiData1, nMidiData2); // Using MIDI Note messages for MIDI buttons unsigned midiPin = ccToMidiPin(nMidiData1); for (unsigned i=0; iType) + { + case ParameterGlobal: + pSources = globalSources; + pInfo = s_GlobalParameter; + break; + case ParameterTG: + pSources = tgSources; + pInfo = s_TGParameter; + break; + case ParameterVoice: + pSources = voiceSources; + pInfo = s_VoiceParameter; + break; + case ParameterOP: + pSources = opSources; + pInfo = s_OPParameter; + break; + default: + return; + } + + pParam->Min = pInfo[pParam->Parameter].Minimum; + pParam->Max = pInfo[pParam->Parameter].Maximum; + pParam->Increment = pInfo[pParam->Parameter].Increment; + if (!pParam->ToString) + pParam->ToString = pInfo[pParam->Parameter].ToString; + + // There are some parameters without entry eg TGParameterMWRange + // skip them + if (pParam->Name && pParam->Short) + return; + + for (const CUIMenu::TMenuItem **source = pSources; *source; ++source) + for (const CUIMenu::TMenuItem *m = *source; m->Name; ++m) + if (m->Parameter == pParam->Parameter) + { + if (!pParam->Name) + pParam->Name = m->Name; + if (!pParam->Short) + pParam->Short = m->Short; + return; + } + + pParam->Type = ParameterNone; + pParam->Parameter = 0; +} + +const void CUIMenu::GetParameterInfos (CUIMenu::TCParameterInfo pParamInfo[8]) +{ + for (size_t i = 0; i < 8; ++i) + GetParameterInfo(&pParamInfo[i]); +} CUIMenu::CUIMenu (CUserInterface *pUI, CMiniDexed *pMiniDexed, CConfig *pConfig) : m_pUI (pUI), @@ -445,6 +514,26 @@ void CUIMenu::EventHandler (TMenuEvent Event) (*m_pParentMenu[m_nCurrentMenuItem].Handler) (this, Event); break; } + + switch (Event) + { + case MenuEventBack: + case MenuEventHome: + case MenuEventSelect: + if (m_pCurrentMenu == s_MainMenu) + m_pMiniDexed->UpdateDAWMenu (PageMain, 0, 0, 0); + else if (m_pCurrentMenu == s_EffectsMenu) + m_pMiniDexed->UpdateDAWMenu (PageEffect, 0, 0, 0); + else if (m_pCurrentMenu == s_TGMenu) + m_pMiniDexed->UpdateDAWMenu (PageTG, 0, 0, m_nCurrentParameter + 1); + else if (m_pCurrentMenu == s_EditVoiceMenu) + m_pMiniDexed->UpdateDAWMenu (PageVoice, 0, 0, m_nMenuStackParameter[m_nCurrentMenuDepth - 1] + 1); + else if (m_pCurrentMenu == s_OperatorMenu) + m_pMiniDexed->UpdateDAWMenu (PageOP, 0, m_nCurrentParameter, m_nMenuStackParameter[m_nCurrentMenuDepth - 2] + 1); + break; + default: + break; + } } void CUIMenu::MenuHandler (CUIMenu *pUIMenu, TMenuEvent Event) @@ -540,7 +629,7 @@ void CUIMenu::MenuHandler (CUIMenu *pUIMenu, TMenuEvent Event) if (pUIMenu->m_pCurrentMenu) // if this is another menu? { bool bIsMainMenu = pUIMenu->m_pCurrentMenu == s_MainMenu; - pUIMenu->m_pUI->DisplayWrite ( + pUIMenu->m_pMiniDexed->DisplayWrite ( pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name, "", pUIMenu->m_pCurrentMenu[pUIMenu->m_nCurrentSelection].Name, @@ -593,7 +682,7 @@ void CUIMenu::EditGlobalParameter (CUIMenu *pUIMenu, TMenuEvent Event) string Value = GetGlobalValueString (Param, pUIMenu->m_pMiniDexed->GetParameter (Param)); - pUIMenu->m_pUI->DisplayWrite (pMenuName, + pUIMenu->m_pMiniDexed->DisplayWrite (pMenuName, pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name, Value.c_str (), nValue > rParam.Minimum, nValue < rParam.Maximum); @@ -637,7 +726,7 @@ void CUIMenu::EditVoiceBankNumber (CUIMenu *pUIMenu, TMenuEvent Event) string Value = to_string (nValue+1) + "=" + pUIMenu->m_pMiniDexed->GetSysExFileLoader ()->GetBankName (nValue); - pUIMenu->m_pUI->DisplayWrite (TG.c_str (), + pUIMenu->m_pMiniDexed->DisplayWrite (TG.c_str (), pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name, Value.c_str (), nValue > 0, nValue < (int) CSysExFileLoader::MaxVoiceBankID); @@ -707,7 +796,7 @@ void CUIMenu::EditProgramNumber (CUIMenu *pUIMenu, TMenuEvent Event) string Value = to_string (nValue+1) + "=" + pUIMenu->m_pMiniDexed->GetVoiceName (nTG); - pUIMenu->m_pUI->DisplayWrite (TG.c_str (), + pUIMenu->m_pMiniDexed->DisplayWrite (TG.c_str (), pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name, Value.c_str (), nValue > 0, nValue < (int) CSysExFileLoader::VoicesPerBank-1); @@ -760,7 +849,7 @@ void CUIMenu::EditTGParameter (CUIMenu *pUIMenu, TMenuEvent Event) string Value = GetTGValueString (Param, pUIMenu->m_pMiniDexed->GetTGParameter (Param, nTG)); - pUIMenu->m_pUI->DisplayWrite (TG.c_str (), + pUIMenu->m_pMiniDexed->DisplayWrite (TG.c_str (), pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name, Value.c_str (), nValue > rParam.Minimum, nValue < rParam.Maximum); @@ -813,7 +902,7 @@ void CUIMenu::EditTGParameter2 (CUIMenu *pUIMenu, TMenuEvent Event) // second me string Value = GetTGValueString (Param, pUIMenu->m_pMiniDexed->GetTGParameter (Param, nTG)); - pUIMenu->m_pUI->DisplayWrite (TG.c_str (), + pUIMenu->m_pMiniDexed->DisplayWrite (TG.c_str (), pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name, Value.c_str (), nValue > rParam.Minimum, nValue < rParam.Maximum); @@ -866,7 +955,7 @@ void CUIMenu::EditVoiceParameter (CUIMenu *pUIMenu, TMenuEvent Event) string Value = GetVoiceValueString (nParam, nValue); - pUIMenu->m_pUI->DisplayWrite (TG.c_str (), + pUIMenu->m_pMiniDexed->DisplayWrite (TG.c_str (), pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name, Value.c_str (), nValue > rParam.Minimum, nValue < rParam.Maximum); @@ -969,7 +1058,7 @@ void CUIMenu::EditOPParameter (CUIMenu *pUIMenu, TMenuEvent Event) Value = GetOPValueString (nParam, nValue); } - pUIMenu->m_pUI->DisplayWrite (OP.c_str (), + pUIMenu->m_pMiniDexed->DisplayWrite (OP.c_str (), pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name, Value.c_str (), nValue > rParam.Minimum, nValue < rParam.Maximum); @@ -988,7 +1077,7 @@ void CUIMenu::SavePerformance (CUIMenu *pUIMenu, TMenuEvent Event) pUIMenu->m_MenuStackParent[pUIMenu->m_nCurrentMenuDepth-1] [pUIMenu->m_nMenuStackItem[pUIMenu->m_nCurrentMenuDepth-1]].Name; - pUIMenu->m_pUI->DisplayWrite (pMenuName, + pUIMenu->m_pMiniDexed->DisplayWrite (pMenuName, pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name, bOK ? "Completed" : "Error", false, false); @@ -1651,7 +1740,7 @@ void CUIMenu::PerformanceMenu (CUIMenu *pUIMenu, TMenuEvent Event) { pUIMenu->m_nSelectedPerformanceID = 0; pUIMenu->m_bConfirmDeletePerformance=false; - pUIMenu->m_pUI->DisplayWrite ("", "Delete", pUIMenu->m_pMiniDexed->DeletePerformance(nValue) ? "Completed" : "Error", false, false); + pUIMenu->m_pMiniDexed->DisplayWrite ("", "Delete", pUIMenu->m_pMiniDexed->DeletePerformance(nValue) ? "Completed" : "Error", false, false); pUIMenu->m_bSplashShow=true; CTimer::Get ()->StartKernelTimer (MSEC2HZ (1500), TimerHandlerNoBack, 0, pUIMenu); return; @@ -1684,13 +1773,13 @@ void CUIMenu::PerformanceMenu (CUIMenu *pUIMenu, TMenuEvent Event) nPSelected += " [L]"; } - pUIMenu->m_pUI->DisplayWrite (pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name, nPSelected.c_str(), + pUIMenu->m_pMiniDexed->DisplayWrite (pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name, nPSelected.c_str(), Value.c_str (), true, true); // (int) nValue > 0, (int) nValue < (int) pUIMenu->m_pMiniDexed->GetLastPerformance()); } else { - pUIMenu->m_pUI->DisplayWrite ("", "Delete?", pUIMenu->m_bConfirmDeletePerformance ? "Yes" : "No", false, false); + pUIMenu->m_pMiniDexed->DisplayWrite ("", "Delete?", pUIMenu->m_bConfirmDeletePerformance ? "Yes" : "No", false, false); } } @@ -1772,7 +1861,7 @@ void CUIMenu::EditPerformanceBankNumber (CUIMenu *pUIMenu, TMenuEvent Event) nPSelected += " [L]"; } - pUIMenu->m_pUI->DisplayWrite (pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name, nPSelected.c_str(), + pUIMenu->m_pMiniDexed->DisplayWrite (pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name, nPSelected.c_str(), Value.c_str (), nValue > 0, nValue < pUIMenu->m_pMiniDexed->GetLastPerformanceBank()-1); @@ -1885,7 +1974,7 @@ void CUIMenu::InputTxt (CUIMenu *pUIMenu, TMenuEvent Event) pUIMenu->m_pMiniDexed->SetNewPerformanceName(pUIMenu->m_InputText); bOK = pUIMenu->m_pMiniDexed->SavePerformanceNewFile (); MsgOk=bOK ? "Completed" : "Error"; - pUIMenu->m_pUI->DisplayWrite (OkTitleR.c_str(), OkTitleL.c_str(), MsgOk.c_str(), false, false); + pUIMenu->m_pMiniDexed->DisplayWrite (OkTitleR.c_str(), OkTitleL.c_str(), MsgOk.c_str(), false, false); CTimer::Get ()->StartKernelTimer (MSEC2HZ (1500), TimerHandler, 0, pUIMenu); return; } @@ -1936,7 +2025,7 @@ void CUIMenu::InputTxt (CUIMenu *pUIMenu, TMenuEvent Event) } Value = Value + " " + escCursor ; - pUIMenu->m_pUI->DisplayWrite (MenuTitleR.c_str(),MenuTitleL.c_str(), Value.c_str(), false, false); + pUIMenu->m_pMiniDexed->DisplayWrite (MenuTitleR.c_str(),MenuTitleL.c_str(), Value.c_str(), false, false); } @@ -1990,7 +2079,7 @@ void CUIMenu::EditTGParameterModulation (CUIMenu *pUIMenu, TMenuEvent Event) string Value = GetTGValueString (Param, pUIMenu->m_pMiniDexed->GetTGParameter (Param, nTG)); - pUIMenu->m_pUI->DisplayWrite (TG.c_str (), + pUIMenu->m_pMiniDexed->DisplayWrite (TG.c_str (), pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name, Value.c_str (), nValue > rParam.Minimum, nValue < rParam.Maximum); diff --git a/src/uimenu.h b/src/uimenu.h index d9dc3ee5..2737d8fa 100644 --- a/src/uimenu.h +++ b/src/uimenu.h @@ -55,11 +55,45 @@ class CUIMenu MenuEventUnknown }; + typedef std::string TToString (int nValue); + + enum TCPageType + { + PageMain, + PageEffect, + PageTG, + PageVoice, + PageOP, + }; + + enum TCParameterType + { + ParameterNone, + ParameterGlobal, + ParameterTG, + ParameterVoice, + ParameterOP, + }; + + struct TCParameterInfo { + TCParameterType Type; + unsigned Parameter; + const char* Name; + const char* Short; + u8 OP; + int Min; + int Max; + int Increment; + TToString *ToString; + }; + public: CUIMenu (CUserInterface *pUI, CMiniDexed *pMiniDexed, CConfig *pConfig); void EventHandler (TMenuEvent Event); + const void GetParameterInfos (CUIMenu::TCParameterInfo pParamInfo[8]); + private: typedef void TMenuHandler (CUIMenu *pUIMenu, TMenuEvent Event); @@ -69,10 +103,9 @@ class CUIMenu TMenuHandler *Handler; const TMenuItem *MenuItem; unsigned Parameter; + const char *Short; }; - typedef std::string TToString (int nValue); - struct TParameter { int Minimum; @@ -129,6 +162,7 @@ class CUIMenu static void InputTxt (CUIMenu *pUIMenu, TMenuEvent Event); static void TimerHandlerNoBack (TKernelTimerHandle hTimer, void *pParam, void *pContext); + const void GetParameterInfo (TCParameterInfo *pParam); private: CUserInterface *m_pUI; CMiniDexed *m_pMiniDexed; diff --git a/src/userinterface.cpp b/src/userinterface.cpp index 32222a1f..3fb8e6f1 100644 --- a/src/userinterface.cpp +++ b/src/userinterface.cpp @@ -198,6 +198,16 @@ bool CUserInterface::Initialize (void) return true; } +void CUserInterface::MIDIEventHandler (CUIMenu::TMenuEvent Event) +{ + m_Menu.EventHandler (Event); +} + +const void CUserInterface::GetParameterInfos (CUIMenu::TCParameterInfo pParamInfo[8]) +{ + m_Menu.GetParameterInfos (pParamInfo); +} + void CUserInterface::Process (void) { if (m_pLCDBuffered) @@ -213,6 +223,7 @@ void CUserInterface::Process (void) void CUserInterface::ParameterChanged (void) { m_Menu.EventHandler (CUIMenu::MenuEventUpdate); + m_pMiniDexed->UpdateDAWState (); } void CUserInterface::DisplayWrite (const char *pMenu, const char *pParam, const char *pValue, @@ -396,7 +407,7 @@ void CUserInterface::UIButtonsEventStub (CUIButton::BtnEvent Event, void *pParam pThis->UIButtonsEventHandler (Event); } -void CUserInterface::UIMIDICmdHandler (unsigned nMidiCh, unsigned nMidiCmd, unsigned nMidiData1, unsigned nMidiData2) +void CUserInterface::UIMIDICmdHandler (unsigned nMidiCh, unsigned nMidiType, unsigned nMidiData1, unsigned nMidiData2) { if (m_nMIDIButtonCh == CMIDIDevice::Disabled) { @@ -411,7 +422,7 @@ void CUserInterface::UIMIDICmdHandler (unsigned nMidiCh, unsigned nMidiCmd, unsi if (m_pUIButtons) { - m_pUIButtons->BtnMIDICmdHandler (nMidiCmd, nMidiData1, nMidiData2); + m_pUIButtons->BtnMIDICmdHandler (nMidiType, nMidiData1, nMidiData2); } } diff --git a/src/userinterface.h b/src/userinterface.h index a8026db9..dd9c5769 100644 --- a/src/userinterface.h +++ b/src/userinterface.h @@ -57,6 +57,10 @@ class CUserInterface // To be called from the MIDI device on reception of a MIDI CC message void UIMIDICmdHandler (unsigned nMidiCh, unsigned nMidiCmd, unsigned nMidiData1, unsigned nMidiData2); + void MIDIEventHandler (CUIMenu::TMenuEvent Event); + + const void GetParameterInfos (CUIMenu::TCParameterInfo pParamInfo[8]); + private: void LCDWrite (const char *pString); // Print to optional HD44780 display diff --git a/submod.sh b/submod.sh index f9524a32..22391e24 100755 --- a/submod.sh +++ b/submod.sh @@ -20,5 +20,5 @@ cd - # # Use fixed master branch of Synth_Dexed cd Synth_Dexed/ -git checkout c9f5274 +git checkout ab0feb94 cd -