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 98497d4b..0e973e72 100644
--- a/src/config.cpp
+++ b/src/config.cpp
@@ -197,6 +197,8 @@ void CConfig::Load (void)
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);
@@ -703,6 +705,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 f85ef36c..3f9665da 100644
--- a/src/config.h
+++ b/src/config.h
@@ -232,6 +232,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;
@@ -355,6 +357,8 @@ class CConfig // Configuration for MiniDexed
std::string m_MIDIButtonActionTGUp;
std::string m_MIDIButtonActionTGDown;
+ 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..75685ab3
--- /dev/null
+++ b/src/dawcontroller.cpp
@@ -0,0 +1,261 @@
+//
+// dawdisplay.cpp
+//
+// MiniDexed - Dexed FM synthesizer for bare metal Raspberry Pi
+// Copyright (C) 2022 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"
+
+static void ArturiaDisplayWrite (CMIDIKeyboard *pKeyboard, const u8 *pHdr, const unsigned nHdrSize, const char *pMenu, const char *pParam, const char *pValue)
+{
+ static unsigned L1MaxLen = 18;
+
+ CString line1 (pParam);
+ CString line2 (pValue);
+
+ size_t nLen = strlen (pParam) + strlen (pMenu);
+ if (nLen < L1MaxLen)
+ {
+ for (unsigned i = L1MaxLen - nLen; i > 0; i--)
+ {
+ line1.Append (" ");
+ }
+ }
+
+ line1.Append (pMenu);
+
+ 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 + 1);
+ nOffset += nLine1Len + 1;
+
+ pLines[nOffset] = 0x02;
+ nOffset += 1;
+
+ memcpy (&pLines[nOffset], line2, nLine2Len + 1);
+ nOffset += nLine2Len + 1;
+
+ pLines[nOffset] = 0xf7;
+ nOffset += 1;
+
+ pKeyboard->SendDebounce (pLines, nOffset, 0);
+}
+
+class CMiniLab3DawConnection : public CDAWConnection
+{
+public:
+ CMiniLab3DawConnection (CMiniDexed *pSynthesizer, CMIDIKeyboard *pMIDIKeyboard);
+ void DisplayWrite (CMIDIKeyboard *pMIDIKeyboard, const char *pMenu, const char *pParam, const char *pValue, bool bArrowDown, bool bArrowUp);
+ void UpdateEncoders (CMiniDexed *pSynthesizer, CMIDIKeyboard *pKeyboard);
+private:
+ void UpdateEncoder (CMIDIKeyboard *pKeyboard, uint8_t ucEncID, uint8_t ucValue);
+
+ uint8_t m_pEncoderCache[8];
+};
+
+CMiniLab3DawConnection::CMiniLab3DawConnection (CMiniDexed *pSynthesizer, CMIDIKeyboard *pMIDIKeyboard)
+{
+ static const uint8_t pInit[] = {0xF0, 0x00, 0x20, 0x6B, 0x7F, 0x42, 0x02, 0x00, 0x40, 0x6A, 0x21, 0xF7};
+ static const CMIDIDevice::TMIDICCRouteMap map[] = {
+ {0, 14, 0, MIDI_CC_VOLUME}, // Fader1
+ {0, 15, 1, MIDI_CC_VOLUME}, // Fader2
+ {0, 30, 2, MIDI_CC_VOLUME}, // Fader3
+ {0, 31, 3, MIDI_CC_VOLUME}, // Fader4
+ {0, 86, 0, MIDI_CC_FREQUENCY_CUTOFF}, // Knob1
+ {0, 87, 0, MIDI_CC_RESONANCE}, // Knob2
+ {0, 89, 0, MIDI_CC_REVERB_LEVEL}, // Knob3
+ {0, 90, 0, MIDI_CC_DETUNE_LEVEL}, // Knob4
+ // {0, 110, 0, MIDI_CC_DETUNE_LEVEL}, // Knob5
+ // {0, 111, 0, MIDI_CC_DETUNE_LEVEL}, // Knob6
+ // {0, 116, 0, MIDI_CC_DETUNE_LEVEL}, // Knob7
+ {0, 117, 0, MIDI_CC_PAN_POSITION}, // Knob8
+ {255, 0, 0, 0}, // Sentinel
+ };
+
+ memset (m_pEncoderCache, 128, sizeof m_pEncoderCache);
+
+ pMIDIKeyboard->SetCCRouteMap (map);
+
+ pMIDIKeyboard->Send (pInit, sizeof pInit, 0);
+ DisplayWrite (pMIDIKeyboard, "MiniDexed", "", "On MiniLab 3", 0, 0);
+
+ UpdateEncoders (pSynthesizer, pMIDIKeyboard);
+}
+
+void CMiniLab3DawConnection::DisplayWrite (CMIDIKeyboard *pMIDIKeyboard, 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, 0x02, 0x60, 0x12, 0x01};
+ ArturiaDisplayWrite (pMIDIKeyboard, pHdr, sizeof pHdr, pMenu, pParam, pValue);
+}
+
+void CMiniLab3DawConnection::UpdateEncoder (CMIDIKeyboard *pKeyboard, 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};
+ pKeyboard->Send (pUpdateEncoder, sizeof pUpdateEncoder, 0);
+}
+
+void CMiniLab3DawConnection::UpdateEncoders (CMiniDexed *pSynthesizer, CMIDIKeyboard *pKeyboard)
+{
+ UpdateEncoder (pKeyboard, 0, maplong(pSynthesizer->GetTGParameter (CMiniDexed::TGParameterCutoff, 0), 0, 99, 0, 127));
+ UpdateEncoder (pKeyboard, 1, maplong(pSynthesizer->GetTGParameter (CMiniDexed::TGParameterResonance, 0), 0, 99, 0, 127));
+ UpdateEncoder (pKeyboard, 2, maplong(pSynthesizer->GetTGParameter (CMiniDexed::TGParameterReverbSend, 0), 0, 99, 0, 127));
+ UpdateEncoder (pKeyboard, 3, maplong(pSynthesizer->GetTGParameter (CMiniDexed::TGParameterMasterTune, 0), -99, 99, 1, 127));
+ UpdateEncoder (pKeyboard, 7, pSynthesizer->GetTGParameter (CMiniDexed::TGParameterPan, 0));
+}
+
+class CKeyLabEs3DawConnection : public CDAWConnection
+{
+public:
+ CKeyLabEs3DawConnection (CMiniDexed *pSynthesizer, CMIDIKeyboard *pMIDIKeyboard);
+ void DisplayWrite (CMIDIKeyboard *pMIDIKeyboard, const char *pMenu, const char *pParam, const char *pValue, bool bArrowDown, bool bArrowUp);
+ void UpdateEncoders (CMiniDexed *pSynthesizer, CMIDIKeyboard *pKeyboard);
+private:
+ void UpdateEncoder (CMIDIKeyboard *pKeyboard, uint8_t ucEncID, uint8_t ucValue);
+
+ uint8_t m_pEncoderCache[8];
+};
+
+CKeyLabEs3DawConnection::CKeyLabEs3DawConnection (CMiniDexed *pSynthesizer, CMIDIKeyboard *pMIDIKeyboard)
+{
+ static const uint8_t pInit[] = {0xF0, 0x00, 0x20, 0x6B, 0x7F, 0x42, 0x02, 0x00, 0x40, 0x6A, 0x21, 0xF7};
+ static const CMIDIDevice::TMIDICCRouteMap map[] = {
+ {0, 105, 0, MIDI_CC_VOLUME}, // Fader1
+ {0, 106, 1, MIDI_CC_VOLUME}, // Fader2
+ {0, 107, 2, MIDI_CC_VOLUME}, // Fader3
+ {0, 108, 3, MIDI_CC_VOLUME}, // Fader4
+ {0, 109, 4, MIDI_CC_VOLUME}, // Fader5
+ {0, 110, 5, MIDI_CC_VOLUME}, // Fader6
+ {0, 111, 6, MIDI_CC_VOLUME}, // Fader7
+ {0, 112, 7, MIDI_CC_VOLUME}, // Fader8
+ //{0, 113, 8, MIDI_CC_VOLUME}, // Fader9
+ {0, 96, 0, MIDI_CC_FREQUENCY_CUTOFF}, // Knob1
+ {0, 97, 0, MIDI_CC_RESONANCE}, // Knob2
+ {0, 98, 0, MIDI_CC_REVERB_LEVEL}, // Knob3
+ {0, 99, 0, MIDI_CC_DETUNE_LEVEL}, // Knob4
+ {0, 100, 0, MIDI_CC_PAN_POSITION}, // Knob5
+ // {0, 101, 0, MIDI_CC_DETUNE_LEVEL}, // Knob6
+ // {0, 102, 0, MIDI_CC_DETUNE_LEVEL}, // Knob7
+ // {0, 103, 0, MIDI_CC_PAN_POSITION}, // Knob8
+ // {0, 104, 0, MIDI_CC_PAN_POSITION}, // Knob9
+ {255, 0, 0, 0}, // Sentinel
+ };
+
+ memset (m_pEncoderCache, 128, sizeof m_pEncoderCache);
+
+ pMIDIKeyboard->SetCCRouteMap (map);
+
+ pMIDIKeyboard->Send (pInit, sizeof pInit, 0);
+ DisplayWrite (pMIDIKeyboard, "MiniDexed", "", "On KeyLab 3 Essential", 0, 0);
+
+ UpdateEncoders (pSynthesizer, pMIDIKeyboard);
+}
+
+void CKeyLabEs3DawConnection::DisplayWrite (CMIDIKeyboard *pMIDIKeyboard, 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 (pMIDIKeyboard, pHdr, sizeof pHdr, pMenu, pParam, pValue);
+}
+
+void CKeyLabEs3DawConnection::UpdateEncoder (CMIDIKeyboard *pKeyboard, 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, 0x02, 0x0F, 0x40, ucEncID += 3, ucValue, 0xF7};
+ pKeyboard->Send (pUpdateEncoder, sizeof pUpdateEncoder, 0);
+}
+
+void CKeyLabEs3DawConnection::UpdateEncoders (CMiniDexed *pSynthesizer, CMIDIKeyboard *pKeyboard)
+{
+ UpdateEncoder (pKeyboard, 0, maplong(pSynthesizer->GetTGParameter (CMiniDexed::TGParameterCutoff, 0), 0, 99, 0, 127));
+ UpdateEncoder (pKeyboard, 1, maplong(pSynthesizer->GetTGParameter (CMiniDexed::TGParameterResonance, 0), 0, 99, 0, 127));
+ UpdateEncoder (pKeyboard, 2, maplong(pSynthesizer->GetTGParameter (CMiniDexed::TGParameterReverbSend, 0), 0, 99, 0, 127));
+ UpdateEncoder (pKeyboard, 3, maplong(pSynthesizer->GetTGParameter (CMiniDexed::TGParameterMasterTune, 0), -99, 99, 1, 127));
+ UpdateEncoder (pKeyboard, 4, pSynthesizer->GetTGParameter (CMiniDexed::TGParameterPan, 0));
+}
+
+CDAWController::CDAWController (CMiniDexed *pSynthesizer, CMIDIKeyboard *pMIDIKeyboard)
+: m_pSynthesizer (pSynthesizer),
+ m_pMIDIKeyboard (pMIDIKeyboard),
+ 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_pMIDIKeyboard->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 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_pMIDIKeyboard);
+ }
+ 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_pMIDIKeyboard);
+ }
+}
+
+void CDAWController::DisplayWrite (const char *pMenu, const char *pParam, const char *pValue,
+ bool bArrowDown, bool bArrowUp)
+{
+ if (m_pDAWConnection)
+ m_pDAWConnection->DisplayWrite (m_pMIDIKeyboard, pMenu, pParam, pValue, bArrowDown, bArrowUp);
+}
+
+void CDAWController::UpdateEncoders (void)
+{
+ if (m_pDAWConnection)
+ m_pDAWConnection->UpdateEncoders (m_pSynthesizer, m_pMIDIKeyboard);
+}
diff --git a/src/dawcontroller.h b/src/dawcontroller.h
new file mode 100644
index 00000000..c0a55875
--- /dev/null
+++ b/src/dawcontroller.h
@@ -0,0 +1,57 @@
+//
+// dawcontroller.h
+//
+// MiniDexed - Dexed FM synthesizer for bare metal Raspberry Pi
+// Copyright (C) 2022 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
+
+class CMIDIKeyboard;
+class CMiniDexed;
+
+class CDAWConnection
+{
+public:
+ virtual void DisplayWrite (CMIDIKeyboard *pKeyboard, const char *pMenu, const char *pParam,
+ const char *pValue, bool bArrowDown, bool bArrowUp) = 0;
+ virtual void UpdateEncoders (CMiniDexed *pSynthesizer, CMIDIKeyboard *pKeyboard) = 0;
+ virtual ~CDAWConnection (void) = default;
+};
+
+class CDAWController
+{
+public:
+ CDAWController (CMiniDexed *pSynthesizer, CMIDIKeyboard *pKeyboard);
+ ~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 UpdateEncoders (void);
+
+private:
+ CMiniDexed *m_pSynthesizer;
+ CMIDIKeyboard *m_pMIDIKeyboard;
+ CDAWConnection *m_pDAWConnection;
+};
+
+#endif
diff --git a/src/mididevice.cpp b/src/mididevice.cpp
index f2b51def..41c96768 100644
--- a/src/mididevice.cpp
+++ b/src/mididevice.cpp
@@ -31,28 +31,6 @@
LOGMODULE ("mididevice");
-#define MIDI_NOTE_OFF 0b1000
-#define MIDI_NOTE_ON 0b1001
-#define MIDI_AFTERTOUCH 0b1010 // TODO
-#define MIDI_CHANNEL_AFTERTOUCH 0b1101 // right now Synth_Dexed just manage Channel Aftertouch not Polyphonic AT -> 0b1010
-#define MIDI_CONTROL_CHANGE 0b1011
- #define MIDI_CC_BANK_SELECT_MSB 0
- #define MIDI_CC_MODULATION 1
- #define MIDI_CC_BREATH_CONTROLLER 2
- #define MIDI_CC_FOOT_PEDAL 4
- #define MIDI_CC_VOLUME 7
- #define MIDI_CC_PAN_POSITION 10
- #define MIDI_CC_BANK_SELECT_LSB 32
- #define MIDI_CC_BANK_SUSTAIN 64
- #define MIDI_CC_RESONANCE 71
- #define MIDI_CC_FREQUENCY_CUTOFF 74
- #define MIDI_CC_REVERB_LEVEL 91
- #define MIDI_CC_DETUNE_LEVEL 94
- #define MIDI_CC_ALL_SOUND_OFF 120
- #define MIDI_CC_ALL_NOTES_OFF 123
-#define MIDI_PROGRAM_CHANGE 0b1100
-#define MIDI_PITCH_BEND 0b1110
-
// MIDI "System" level (i.e. all TG) custom CC maps
// Note: Even if number of TGs is not 8, there are only 8
// available to be used in the mappings here.
@@ -78,7 +56,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_pMIDICCRoute ()
{
for (unsigned nTG = 0; nTG < CConfig::AllToneGenerators; nTG++)
{
@@ -284,6 +263,15 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
// Process MIDI for each active Tone Generator
bool bSystemCCHandled = false;
bool bSystemCCChecked = false;
+
+ u8 ucCC = 0;
+ if (ucType == MIDI_CONTROL_CHANGE && nLength >= 3)
+ {
+ ucCC = pMessage[1];
+ if (m_pMIDICCRoute)
+ GetRoutedCC (&ucChannel, &ucCC);
+ }
+
for (unsigned nTG = 0; nTG < m_pConfig->GetToneGenerators() && !bSystemCCHandled; nTG++)
{
if (ucStatus == MIDI_SYSTEM_EXCLUSIVE_BEGIN)
@@ -344,7 +332,7 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
break;
}
- switch (pMessage[1])
+ switch (ucCC)
{
case MIDI_CC_MODULATION:
m_pSynthesizer->setModWheel (pMessage[2], nTG);
@@ -520,6 +508,23 @@ bool CMIDIDevice::HandleMIDISystemCC(const u8 ucCC, const u8 ucCCval)
return false;
}
+void CMIDIDevice::SetCCRouteMap (const TMIDICCRouteMap *pMIDICCRoute)
+{
+ m_pMIDICCRoute = pMIDICCRoute;
+}
+
+void CMIDIDevice::GetRoutedCC (u8 *pChannel, u8 *pCC) const
+{
+ for (const TMIDICCRouteMap *r = m_pMIDICCRoute;r->ucSChannel < 16; r++)
+ {
+ if (r->ucSChannel == *pChannel && r->ucSCC == *pCC)
+ {
+ *pChannel = r->ucDChannel;
+ *pCC = r->ucDCC;
+ }
+ }
+}
+
void CMIDIDevice::HandleSystemExclusive(const uint8_t* pMessage, const size_t nLength, const unsigned nCable, const uint8_t nTG)
{
int16_t sysex_return;
diff --git a/src/mididevice.h b/src/mididevice.h
index 44f16916..92c5229d 100644
--- a/src/mididevice.h
+++ b/src/mididevice.h
@@ -33,6 +33,28 @@
#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_VOLUME 7
+ #define MIDI_CC_PAN_POSITION 10
+ #define MIDI_CC_BANK_SELECT_LSB 32
+ #define MIDI_CC_BANK_SUSTAIN 64
+ #define MIDI_CC_RESONANCE 71
+ #define MIDI_CC_FREQUENCY_CUTOFF 74
+ #define MIDI_CC_REVERB_LEVEL 91
+ #define MIDI_CC_DETUNE_LEVEL 94
+ #define MIDI_CC_ALL_SOUND_OFF 120
+ #define MIDI_CC_ALL_NOTES_OFF 123
+#define MIDI_PROGRAM_CHANGE 0b1100
+#define MIDI_PITCH_BEND 0b1110
+
class CMiniDexed;
class CMIDIDevice
@@ -46,6 +68,14 @@ class CMIDIDevice
ChannelUnknown
};
+ typedef struct
+ {
+ u8 ucSChannel;
+ u8 ucSCC;
+ u8 ucDChannel;
+ u8 ucDCC;
+ } TMIDICCRouteMap;
+
public:
CMIDIDevice (CMiniDexed *pSynthesizer, CConfig *pConfig, CUserInterface *pUI);
virtual ~CMIDIDevice (void);
@@ -53,6 +83,8 @@ class CMIDIDevice
void SetChannel (u8 ucChannel, unsigned nTG);
u8 GetChannel (unsigned nTG) const;
+ void SetCCRouteMap (const TMIDICCRouteMap *pMIDICCRoute);
+
virtual void Send (const u8 *pMessage, size_t nLength, unsigned nCable = 0) {}
virtual void SendSystemExclusiveVoice(uint8_t nVoice, const unsigned nCable, uint8_t nTG);
@@ -64,6 +96,8 @@ class CMIDIDevice
private:
bool HandleMIDISystemCC(const u8 ucCC, const u8 ucCCval);
+ void GetRoutedCC (u8 *pChannel, u8 *pCC) const;
+
private:
CMiniDexed *m_pSynthesizer;
CConfig *m_pConfig;
@@ -78,6 +112,8 @@ class CMIDIDevice
std::string m_DeviceName;
+ const TMIDICCRouteMap *m_pMIDICCRoute;
+
typedef std::unordered_map TDeviceMap;
static TDeviceMap s_DeviceMap;
diff --git a/src/midikeyboard.cpp b/src/midikeyboard.cpp
index 2eae9d81..1db09539 100644
--- a/src/midikeyboard.cpp
+++ b/src/midikeyboard.cpp
@@ -39,7 +39,8 @@ 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)
{
assert (m_nInstance < MaxInstances);
s_pThis[m_nInstance] = this;
@@ -47,12 +48,16 @@ CMIDIKeyboard::CMIDIKeyboard (CMiniDexed *pSynthesizer, CConfig *pConfig, CUserI
m_DeviceName.Format ("umidi%u", nInstance+1);
AddDevice (m_DeviceName);
+
+ if (pConfig->GetDAWControllerEnabled ())
+ m_pDAWController = new CDAWController (pSynthesizer, this);
}
CMIDIKeyboard::~CMIDIKeyboard (void)
{
assert (m_nInstance < MaxInstances);
s_pThis[m_nInstance] = 0;
+ delete m_pDAWController;
}
void CMIDIKeyboard::Process (boolean bPlugAndPlayUpdated)
@@ -85,6 +90,9 @@ void CMIDIKeyboard::Process (boolean bPlugAndPlayUpdated)
m_pMIDIDevice->RegisterPacketHandler (s_pMIDIPacketHandler[m_nInstance]);
m_pMIDIDevice->RegisterRemovedHandler (DeviceRemovedHandler, this);
+
+ if (m_pDAWController)
+ m_pDAWController->OnConnect();
}
}
}
@@ -101,6 +109,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.
@@ -136,6 +151,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;
}
@@ -190,3 +209,16 @@ 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::UpdateEncoders (void)
+{
+ if (m_pMIDIDevice && m_pDAWController)
+ m_pDAWController->UpdateEncoders ();
+}
diff --git a/src/midikeyboard.h b/src/midikeyboard.h
index 047fa523..e56153e0 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
@@ -47,6 +48,12 @@ 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 UpdateEncoders (void);
private:
static void MIDIPacketHandler0 (unsigned nCable, u8 *pPacket, unsigned nLength);
@@ -76,6 +83,8 @@ class CMIDIKeyboard : public CMIDIDevice
std::queue m_SendQueue;
+ CDAWController *m_pDAWController;
+
static CMIDIKeyboard *s_pThis[MaxInstances];
static TMIDIPacketHandler * const s_pMIDIPacketHandler[MaxInstances];
diff --git a/src/minidexed.cpp b/src/minidexed.cpp
index 2e45f46b..d057aaab 100644
--- a/src/minidexed.cpp
+++ b/src/minidexed.cpp
@@ -1047,6 +1047,19 @@ void CMiniDexed::SetTGParameter (TTGParameter Parameter, int nValue, unsigned nT
assert (0);
break;
}
+
+ switch (Parameter)
+ {
+ case TGParameterCutoff:
+ case TGParameterResonance:
+ case TGParameterPan:
+ case TGParameterReverbSend:
+ case TGParameterMasterTune:
+ UpdateEncoders ();
+ break;
+ default:
+ break;
+ }
}
int CMiniDexed::GetTGParameter (TTGParameter Parameter, unsigned nTG)
@@ -1775,6 +1788,25 @@ 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::UpdateEncoders ()
+{
+ for (unsigned i = 0; i < CConfig::MaxUSBMIDIDevices; i++)
+ {
+ m_pMIDIKeyboard[i]->UpdateEncoders ();
+ }
+}
+
std::string CMiniDexed::GetPerformanceFileName(unsigned nID)
{
return m_PerformanceConfig.GetPerformanceFileName(nID);
@@ -1961,6 +1993,8 @@ void CMiniDexed::LoadPerformanceParameters(void)
SetParameter (ParameterReverbLowPass, m_PerformanceConfig.GetReverbLowPass ());
SetParameter (ParameterReverbDiffusion, m_PerformanceConfig.GetReverbDiffusion ());
SetParameter (ParameterReverbLevel, m_PerformanceConfig.GetReverbLevel ());
+
+ UpdateEncoders ();
}
std::string CMiniDexed::GetNewPerformanceDefaultName(void)
diff --git a/src/minidexed.h b/src/minidexed.h
index 69dcf9ce..997b576e 100644
--- a/src/minidexed.h
+++ b/src/minidexed.h
@@ -228,6 +228,11 @@ class CMiniDexed
void setMasterVolume (float32_t vol);
+ void DisplayWrite (const char *pMenu, const char *pParam, const char *pValue,
+ bool bArrowDown, bool bArrowUp);
+
+ void UpdateEncoders ();
+
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 d04d9988..ce82f69f 100644
--- a/src/minidexed.ini
+++ b/src/minidexed.ini
@@ -139,6 +139,9 @@ MIDIButtonActionTGUp=
MIDIButtonTGDown=0
MIDIButtonActionTGDown=
+# DAW Controller (Arturia MiniLab 3, KeyLab Essential 3)
+DAWControllerEnabled=0
+
# KY-040 Rotary Encoder
EncoderEnabled=1
EncoderPinClock=10
diff --git a/src/uimenu.cpp b/src/uimenu.cpp
index 8b724250..8ece5779 100644
--- a/src/uimenu.cpp
+++ b/src/uimenu.cpp
@@ -534,7 +534,7 @@ void CUIMenu::MenuHandler (CUIMenu *pUIMenu, TMenuEvent Event)
if (pUIMenu->m_pCurrentMenu) // if this is another menu?
{
- pUIMenu->m_pUI->DisplayWrite (
+ pUIMenu->m_pMiniDexed->DisplayWrite (
pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name,
"",
pUIMenu->m_pCurrentMenu[pUIMenu->m_nCurrentSelection].Name,
@@ -587,7 +587,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);
@@ -631,7 +631,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);
@@ -701,7 +701,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);
@@ -754,7 +754,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);
@@ -807,7 +807,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);
@@ -860,7 +860,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);
@@ -963,7 +963,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);
@@ -982,7 +982,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);
@@ -1564,7 +1564,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;
@@ -1597,13 +1597,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);
}
}
@@ -1685,7 +1685,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);
@@ -1798,7 +1798,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;
}
@@ -1849,7 +1849,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);
}
@@ -1903,7 +1903,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);