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/config.cpp b/src/config.cpp
index 7f5d0404..816243f2 100644
--- a/src/config.cpp
+++ b/src/config.cpp
@@ -204,7 +204,9 @@ void CConfig::Load (void)
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);
@@ -751,6 +753,11 @@ 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 97681d48..be311be6 100644
--- a/src/config.h
+++ b/src/config.h
@@ -240,6 +240,8 @@ class CConfig // Configuration for MiniDexed
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;
@@ -371,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..9bf73d6b
--- /dev/null
+++ b/src/dawcontroller.cpp
@@ -0,0 +1,1591 @@
+// 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_ENC_PAGE_PREV 15
+#define MIDI_DAW_ENC_PAGE_NEXT 16
+#define MIDI_DAW_DISPLAY_MODE_TOGGLE 17
+#define MIDI_DAW_ENC_VALUES_SHOW 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,
+ unsigned nLineMaxLen, const bool bFill1, const bool bFill2, const char *pMenu,
+ const char *pParam, const char *pValue,
+ const bool bArrowLeft, const bool bArrowRight)
+{
+ CString line1 (pParam);
+ CString line2;
+
+ if (bFill1)
+ {
+ size_t nLen = strlen (pParam) + strlen (pMenu) + 1;
+ if (nLen < nLineMaxLen)
+ {
+ for (unsigned i = nLineMaxLen - nLen; i > 0; i--)
+ {
+ line1.Append (" ");
+ }
+ }
+ }
+ line1.Append (" ");
+ line1.Append (pMenu);
+
+ if (bArrowLeft)
+ line2.Append("<");
+
+ line2.Append(pValue);
+
+ if (bFill2) {
+ size_t nLen = strlen (line2) + bFill2 ? 1 : 0;
+ if (nLen < nLineMaxLen)
+ {
+ for (unsigned i = nLineMaxLen - nLen; i > 0; i--)
+ {
+ line2.Append (" ");
+ }
+ }
+
+ if (bArrowRight)
+ line2.Append(">");
+ }
+
+ int nLine1Len = strlen (line1);
+ int nLine2Len = strlen (line2);
+ int nOffset = 0;
+
+ uint8_t pLines[nHdrSize + nLine1Len + 2 + nLine2Len + 2];
+
+ memcpy (pLines, pHdr, nHdrSize);
+ nOffset += nHdrSize;
+
+ memcpy (&pLines[nOffset], line1, nLine1Len);
+ nOffset += nLine1Len;
+
+ pLines[nOffset++] = 0x00;
+ pLines[nOffset++] = 0x02;
+
+ memcpy (&pLines[nOffset], line2, nLine2Len);
+ nOffset += nLine2Len;
+
+ 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 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%%", maplong(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%%", maplong(ucValue, 0, 127, 0, 100));
+ ArturiaDisplayInfoWrite (pKeyboard, pDisplayHdr, CT_FADER, ucValue, line1, line2);
+ break;
+ case MIDI_CC_FREQUENCY_CUTOFF:
+ snprintf(line2, LINELEN, "%ld%%", maplong(ucValue, 0, 127, 0, 99));
+ ArturiaDisplayInfoWrite (pKeyboard, pDisplayHdr, CT_KNOB, ucValue, "Cutoff", line2);
+ break;
+ case MIDI_CC_RESONANCE:
+ snprintf(line2, LINELEN, "%ld%%", maplong(ucValue, 0, 127, 0, 99));
+ ArturiaDisplayInfoWrite (pKeyboard, pDisplayHdr, CT_KNOB, ucValue, "Resonance", line2);
+ break;
+ case MIDI_CC_REVERB_LEVEL:
+ snprintf(line2, LINELEN, "%ld%%", maplong(ucValue, 0, 127, 0, 99));
+ ArturiaDisplayInfoWrite (pKeyboard, pDisplayHdr, CT_KNOB, ucValue, "Reverb", line2);
+ break;
+ case MIDI_CC_DETUNE_LEVEL:
+ snprintf(line2, LINELEN, "%ld", maplong(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_MAX;
+ uint8_t g = UINT8_MAX;
+ uint8_t b = UINT8_MAX;
+};
+
+inline bool operator==(const CColor& l, const CColor& r) {
+ return l.r == r.r && l.g ==r.g && l.b == r.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 void s_UpdateDisplay (TKernelTimerHandle hTimer, void *pParam, void *pContext);
+ void QueueUpdateDisplay ();
+ 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_bDisableUpdate = false;
+
+ CUIMenu::TCPageType m_encoderPageType = CUIMenu::PageMain;
+ u8 m_ucEncoderPage = 0;
+ u8 m_ucEncoderOp = 0;
+ CUIMenu::TCParameterInfo *m_pEncoders;
+
+ enum DisplayState {
+ DisplayMenu,
+ DisplayEncoder
+ };
+ DisplayState m_DisplayState = DisplayMenu;
+
+ uint8_t m_pEncoderCache[8] = {};
+ CColor m_pPadColorCache[16] = {};
+
+ CUIMenu::TCParameterInfo m_pGlobalEncoders[4][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::TGParameterPitchBendRange},
+ {CUIMenu::ParameterNone},
+ {CUIMenu::ParameterTG, CMiniDexed::TGParameterMWRange},
+ {CUIMenu::ParameterTG, CMiniDexed::TGParameterMWPitch, "MW Pitch", "MWPi"},
+ {CUIMenu::ParameterTG, CMiniDexed::TGParameterPitchBendStep},
+ {CUIMenu::ParameterNone},
+ {CUIMenu::ParameterTG, CMiniDexed::TGParameterMWEGBias, "MW EG Bias", "MWEGB"},
+ {CUIMenu::ParameterTG, CMiniDexed::TGParameterMWAmplitude, "MW Amp", "MWA"},
+ },
+ {
+ {CUIMenu::ParameterTG, CMiniDexed::TGParameterFCRange},
+ {CUIMenu::ParameterTG, CMiniDexed::TGParameterFCPitch, "FC Pitch", "FCPi"},
+ {CUIMenu::ParameterTG, CMiniDexed::TGParameterBCRange},
+ {CUIMenu::ParameterTG, CMiniDexed::TGParameterBCPitch, "BC Pitch", "BCPi"},
+ {CUIMenu::ParameterTG, CMiniDexed::TGParameterFCEGBias, "FC EG Bias", "FCEGB"},
+ {CUIMenu::ParameterTG, CMiniDexed::TGParameterFCAmplitude, "FC Amp", "FCA"},
+ {CUIMenu::ParameterTG, CMiniDexed::TGParameterBCEGBias, "BC EG Bias", "BCEGB"},
+ {CUIMenu::ParameterTG, CMiniDexed::TGParameterBCAmplitude, "BC Amp", "BCA"},
+ },
+ {
+ {CUIMenu::ParameterTG, CMiniDexed::TGParameterATRange},
+ {CUIMenu::ParameterTG, CMiniDexed::TGParameterATPitch, "AT Pitch", "ATPi"},
+ {CUIMenu::ParameterNone},
+ {CUIMenu::ParameterNone},
+ {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][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_DISPLAY_MODE_TOGGLE, 0xFF, .bGroup=true},
+ {0, 0, MIDI_CONTROL_CHANGE, 27, 0x00, 0, MIDI_DAW_CHANGE, MIDI_DAW_ENC_VALUES_SHOW, 0xFF, .bGroup=true},
+
+ /*
+ {0, 0, MIDI_CONTROL_CHANGE, 86, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_FREQUENCY_CUTOFF, 0xFF}, // Knob1
+ {0, 0, MIDI_CONTROL_CHANGE, 87, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_RESONANCE, 0xFF}, // Knob2
+ {0, 0, MIDI_CONTROL_CHANGE, 89, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_REVERB_LEVEL, 0xFF}, // Knob3
+ {0, 0, MIDI_CONTROL_CHANGE, 90, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_DETUNE_LEVEL, 0xFF}, // Knob4
+ {0, 0, MIDI_CONTROL_CHANGE, 110, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_PORTAMENTO_TIME, 0xFF}, // Knob5
+ {0, 0, MIDI_CONTROL_CHANGE, 111, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_VOICE, 0xFF}, // Knob6
+ {0, 0, MIDI_CONTROL_CHANGE, 116, 0xFF, 0, MIDI_DAW_CHANGE, MIDI_DAW_ALGORITHM, 0xFF}, // Knob7
+ {0, 0, MIDI_CONTROL_CHANGE, 117, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_PAN_POSITION, 0xFF}, // Knob8
+ */
+
+ {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 i = 0; i < sizeof m_pGlobalEncoders / sizeof *m_pGlobalEncoders; ++i)
+ m_pUI->GetParameterInfos (m_pGlobalEncoders[i]);
+ for (unsigned i = 0; i < sizeof m_pEffectEncoders / sizeof *m_pEffectEncoders; ++i)
+ m_pUI->GetParameterInfos (m_pEffectEncoders[i]);
+ for (unsigned i = 0; i < sizeof m_pVoiceEncoders / sizeof *m_pVoiceEncoders; ++i)
+ m_pUI->GetParameterInfos (m_pVoiceEncoders[i]);
+ for (unsigned i = 0; i < sizeof m_pOPEncoders / sizeof *m_pOPEncoders; ++i)
+ m_pUI->GetParameterInfos (m_pOPEncoders[i]);
+
+ UpdateMenu (m_encoderPageType, m_ucEncoderPage, m_ucEncoderOp, 0);
+ QueueUpdateDisplay ();
+}
+
+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 ()
+{
+ if (m_DisplayTimer)
+ CTimer::Get ()->CancelKernelTimer (m_DisplayTimer);
+ m_DisplayTimer = CTimer::Get ()->StartKernelTimer (MSEC2HZ (2000), 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;
+ }
+}
+
+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 < 4; ++i)
+ pParam = pParam + (m_pEncoders[i].Short ?: "...") + " ";
+
+ for (unsigned i = 4; i < 8; ++i)
+ pValue = pValue + (m_pEncoders[i].Short ?: "...") + " ";
+
+ 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 tg, uint8_t op)
+{
+ switch (pInfo->Type)
+ {
+ case CUIMenu::ParameterGlobal:
+ return pSynthesizer->GetParameter (static_cast(pInfo->Parameter));
+ break;
+ case CUIMenu::ParameterTG:
+ return pSynthesizer->GetTGParameter (static_cast(pInfo->Parameter), tg);
+ break;
+ case CUIMenu::ParameterVoice:
+ return pSynthesizer->GetVoiceParameter (pInfo->Parameter, CMiniDexed::NoOP, tg);
+ break;
+ case CUIMenu::ParameterOP:
+ return pSynthesizer->GetVoiceParameter (pInfo->Parameter, op, tg);
+ break;
+ default:
+ return 0;
+ break;
+ }
+}
+
+static std::string GetParameterValueStr (CMiniDexed *pSynthesizer, CUIMenu::TCParameterInfo *pInfo, uint8_t tg, uint8_t op)
+{
+ if (pInfo->Type == CUIMenu::ParameterNone)
+ return "...";
+ uint8_t value = GetParameterValue (pSynthesizer, pInfo, tg, op);
+ 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 < 4; ++i)
+ pParam = pParam + GetParameterValueStr (m_pSynthesizer, &m_pEncoders[i], m_ucFirstTG, m_ucEncoderOp) + " ";
+
+ for (unsigned i=4; i < 8; ++i)
+ pValue = pValue + GetParameterValueStr (m_pSynthesizer, &m_pEncoders[i], m_ucFirstTG, m_ucEncoderOp) + " ";
+
+ 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)
+{
+ uint8_t ucCacheId = PadID + (BankID == BankB ? 8 : 0);
+ if (m_pPadColorCache[ucCacheId] == color)
+ return;
+
+ m_pPadColorCache[ucCacheId] = 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)
+{
+ if (m_pEncoderCache[ucEncID] == ucValue)
+ return;
+
+ m_pEncoderCache[ucEncID] = 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 ()
+{
+ if (m_bDisableUpdate)
+ return;
+
+ 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)
+ {
+ const auto &enc = m_pEncoders[i];
+ switch (enc.Type)
+ {
+ case CUIMenu::ParameterGlobal:
+ UpdateEncoder (i, maplong(m_pSynthesizer->GetParameter (static_cast(enc.Parameter)), enc.Min, enc.Max, 0, 127));
+ break;
+ case CUIMenu::ParameterTG:
+ UpdateEncoder (i, maplong(m_pSynthesizer->GetTGParameter (static_cast(enc.Parameter), m_ucFirstTG), enc.Min, enc.Max, 0, 127));
+ break;
+ case CUIMenu::ParameterVoice:
+ UpdateEncoder (i, maplong(m_pSynthesizer->GetVoiceParameter (enc.Parameter, CMiniDexed::NoOP, m_ucFirstTG), enc.Min, enc.Max, 0, 127));
+ break;
+ case CUIMenu::ParameterOP:
+ UpdateEncoder (i, maplong(m_pSynthesizer->GetVoiceParameter (enc.Parameter, m_ucEncoderOp, m_ucFirstTG), enc.Min, enc.Max, 0, 127));
+ break;
+ default:
+ break;
+ }
+ }
+
+ UpdateMonoColor ();
+ // TODO change the MIDIRouteMap's value also
+ UpdatePortamentoColor ();
+
+ for (unsigned i = 0; i < m_pConfig->GetToneGenerators(); ++i)
+ UpdateTGColor (i);
+
+ UpdateVolumeFaders ();
+}
+
+void CMiniLab3DawConnection::UpdateMenu (CUIMenu::TCPageType Type, s8 ucPage, u8 ucOp, u8 ucTG)
+{
+ switch (Type)
+ {
+ case CUIMenu::PageMain:
+ case CUIMenu::PageTG:
+ ucPage = constrain (ucPage, 0, 3);
+ m_pEncoders = m_pGlobalEncoders[ucPage];
+ break;
+ case CUIMenu::PageEffect:
+ ucPage = constrain (ucPage, 0, 0);
+ m_pEncoders = m_pEffectEncoders[ucPage];
+ break;
+ case CUIMenu::PageVoice:
+ ucPage = constrain (ucPage, 0, 2);
+ m_pEncoders = m_pVoiceEncoders[ucPage];
+ break;
+ case CUIMenu::PageOP:
+ ucPage = constrain (ucPage, 0, 2);
+ m_pEncoders = m_pOPEncoders[ucPage];
+ break;
+ default:
+ assert (false);
+ }
+
+ m_encoderPageType = Type;
+ m_ucEncoderPage = ucPage;
+ m_ucEncoderOp = ucOp;
+ UpdateState ();
+}
+
+void CMiniLab3DawConnection::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);
+
+ 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;
+ }
+ QueueUpdateDisplay ();
+ 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, 0);
+ ShowEncoderDisplay ();
+ break;
+ case MIDI_DAW_MENU_NEXT:
+ UpdateMenu (m_encoderPageType, m_ucEncoderPage + 1, m_ucEncoderOp, 0);
+ ShowEncoderDisplay ();
+ break;
+ case MIDI_DAW_ENC_VALUES_SHOW:
+ ShowValueDisplay ();
+ 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_ENC_PAGE_PREV:
+ UpdateMenu (m_encoderPageType, m_ucEncoderPage - 1, m_ucEncoderOp, 0);
+ ShowEncoderDisplay ();
+ break;
+ case MIDI_DAW_ENC_PAGE_NEXT:
+ UpdateMenu (m_encoderPageType, m_ucEncoderPage + 1, m_ucEncoderOp, 0);
+ ShowEncoderDisplay ();
+ break;
+ case MIDI_DAW_DISPLAY_MODE_TOGGLE:
+ m_DisplayState = m_DisplayState == DisplayEncoder ? 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;
+ }
+ QueueUpdateDisplay ();
+ break;
+ case MIDI_CHANNEL_AFTERTOUCH:
+ SetChannelAT (ucP1);
+ QueueUpdateDisplay ();
+ break;
+ }
+}
+
+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 = maplong (ucValue, 0, 127, encoder->Min, encoder->Max);
+
+ // If we update the encoders during setup, we will get rounding problems, so disable it.
+ m_bDisableUpdate = 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 ||
+ (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:
+ m_pSynthesizer->SetVoiceParameter (encoder->Parameter, value, m_ucEncoderOp, i);
+ break;
+ default:
+ break;
+ }
+ }
+
+ m_bDisableUpdate = 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");
+ UpdateState();
+}
+
+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");
+ }
+ UpdateState();
+}
+
+
+void CMiniLab3DawConnection::SelectChanTG (u8 ucTG)
+{
+ char line1[LINELEN];
+
+ u8 enabled = m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterEnabled, ucTG);
+ u8 channel = m_pKeyboard->GetChannel (ucTG);
+ snprintf(line1, LINELEN, "TGs on Ch %d", channel + 1);
+
+ 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);
+ }
+ }
+ }
+ ArturiaDisplayInfoWrite (m_pKeyboard, m_pEncoder, CT_PAD, 0x7F, line1, enabled ? "Off" : "On");
+ UpdateState();
+}
+
+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, maplong(m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterCutoff, 0), 0, 99, 0, 127));
+ UpdateEncoder (1, maplong(m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterResonance, 0), 0, 99, 0, 127));
+ UpdateEncoder (2, maplong(m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterReverbSend, 0), 0, 99, 0, 127));
+ UpdateEncoder (3, maplong(m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterMasterTune, 0), -99, 99, 1, 127));
+ UpdateEncoder (4, m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterPan, 0));
+ UpdateEncoder (5, maplong(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..dc35d73a
--- /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/mididevice.cpp b/src/mididevice.cpp
index 20215d25..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,32 +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_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
-
// 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.
@@ -82,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++)
{
@@ -218,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:
@@ -240,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 &&
@@ -269,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
{
@@ -285,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;
@@ -295,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:
@@ -307,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);
}
}
}
@@ -345,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;
@@ -365,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;
@@ -380,85 +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 (pMessage[2], 0, 127, 0, 99), nTG);
+ 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 (pMessage[2] >= 64, nTG);
+ m_pSynthesizer->setSostenuto (ucP2 >= 64, nTG);
break;
case MIDI_CC_PORTAMENTO:
- m_pSynthesizer->setPortamentoMode (pMessage[2] >= 64, nTG);
+ m_pSynthesizer->setPortamentoMode (ucP2 >= 64, nTG);
break;
case MIDI_CC_HOLD2:
- m_pSynthesizer->setHoldMode (pMessage[2] >= 64, nTG);
+ 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:
@@ -467,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;
@@ -477,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;
@@ -488,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;
@@ -498,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);
@@ -511,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 ();
}
@@ -572,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;
@@ -700,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];
@@ -716,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..20f00b06 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..6f3209b4 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/minidexed.cpp b/src/minidexed.cpp
index 18ed5371..a7a5828f 100644
--- a/src/minidexed.cpp
+++ b/src/minidexed.cpp
@@ -942,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:
@@ -949,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:
@@ -956,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:
@@ -963,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:
@@ -970,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:
@@ -977,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:
@@ -984,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:
@@ -991,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:
@@ -1144,6 +1152,7 @@ void CMiniDexed::SetVoiceParameter (uint8_t uchOffset, uint8_t uchValue, unsigne
}
m_pTG[nTG]->setOPAll (m_uchOPMask[nTG]);
+ m_UI.ParameterChanged ();
return;
}
@@ -1155,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)
@@ -1808,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);
@@ -1971,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 0c93e543..a96e7ea0 100644
--- a/src/minidexed.h
+++ b/src/minidexed.h
@@ -233,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];
diff --git a/src/minidexed.ini b/src/minidexed.ini
index 9465ae1f..2b409053 100644
--- a/src/minidexed.ini
+++ b/src/minidexed.ini
@@ -151,6 +151,9 @@ 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
EncoderPinClock=10
diff --git a/src/uibuttons.cpp b/src/uibuttons.cpp
index 90e2e1d3..834da4e3 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
@@ -548,21 +549,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),
@@ -447,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, 0);
+ else if (m_pCurrentMenu == s_EditVoiceMenu)
+ m_pMiniDexed->UpdateDAWMenu (PageVoice, 0, 0, 0);
+ else if (m_pCurrentMenu == s_OperatorMenu)
+ m_pMiniDexed->UpdateDAWMenu (PageOP, 0, m_nCurrentParameter, 0);
+ break;
+ default:
+ break;
+ }
}
void CUIMenu::MenuHandler (CUIMenu *pUIMenu, TMenuEvent Event)
@@ -542,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,
@@ -595,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);
@@ -639,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);
@@ -709,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);
@@ -762,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);
@@ -815,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);
@@ -868,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);
@@ -971,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);
@@ -990,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);
@@ -1653,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;
@@ -1686,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);
}
}
@@ -1774,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);
@@ -1887,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;
}
@@ -1938,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);
}
@@ -1992,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..eca7e4ab 100644
--- a/src/uimenu.h
+++ b/src/uimenu.h
@@ -55,11 +55,44 @@ 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;
+ 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 +102,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 +161,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