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);