From 8bfba864c0e8cd0a28be8d3ed15cf0979baef212 Mon Sep 17 00:00:00 2001 From: Gergo Koteles Date: Tue, 5 Nov 2024 16:51:23 +0100 Subject: [PATCH] add DAW Controller support for various Arturia controllers MiniLab 3 KeyLab 3 Essential KeyLab mkII based on https://github.com/PrzemekBarski/arturia-keylab-essential-mk3-programming-guide Tested on a Arturia MiniLab 3 and KeyLab mkII Keylab 3 Essential is not tested --- src/Makefile | 3 +- src/config.cpp | 64 ++++- src/config.h | 42 ++- src/dawcontroller.cpp | 616 ++++++++++++++++++++++++++++++++++++++++++ src/dawcontroller.h | 52 ++++ src/mididevice.cpp | 163 ++++++----- src/mididevice.h | 54 ++++ src/midikeyboard.cpp | 40 ++- src/midikeyboard.h | 11 + src/minidexed.cpp | 36 +++ src/minidexed.h | 5 + src/minidexed.ini | 3 + src/uibuttons.cpp | 16 +- src/uimenu.cpp | 32 +-- src/userinterface.cpp | 26 +- src/userinterface.h | 2 + 16 files changed, 1049 insertions(+), 116 deletions(-) create mode 100644 src/dawcontroller.cpp create mode 100644 src/dawcontroller.h 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..1f789a5d 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); @@ -666,6 +668,36 @@ unsigned CConfig::GetMIDIButtonHome (void) const return m_nMIDIButtonHome; } +void CConfig::SetMIDIButtonCh (unsigned v) +{ + m_nMIDIButtonCh = v; +} + +void CConfig::SetMIDIButtonPrev (unsigned v) +{ + m_nMIDIButtonPrev = v; +} + +void CConfig::SetMIDIButtonNext (unsigned v) +{ + m_nMIDIButtonNext = v; +} + +void CConfig::SetMIDIButtonBack (unsigned v) +{ + m_nMIDIButtonBack = v; +} + +void CConfig::SetMIDIButtonSelect (unsigned v) +{ + m_nMIDIButtonSelect = v; +} + +void CConfig::SetMIDIButtonHome (unsigned v) +{ + m_nMIDIButtonHome = v; +} + const char *CConfig::GetMIDIButtonActionPrev (void) const { return m_MIDIButtonActionPrev.c_str(); @@ -691,6 +723,31 @@ const char *CConfig::GetMIDIButtonActionHome (void) const return m_MIDIButtonActionHome.c_str(); } +void CConfig::SetMIDIButtonActionPrev (const char *v) +{ + m_MIDIButtonActionPrev = v; +} + +void CConfig::SetMIDIButtonActionNext (const char *v) +{ + m_MIDIButtonActionNext = v; +} + +void CConfig::SetMIDIButtonActionBack (const char *v) +{ + m_MIDIButtonActionBack = v; +} + +void CConfig::SetMIDIButtonActionSelect (const char *v) +{ + m_MIDIButtonActionSelect = v; +} + +void CConfig::SetMIDIButtonActionHome (const char *v) +{ + m_MIDIButtonActionHome = v; +} + unsigned CConfig::GetMIDIButtonPgmUp (void) const { return m_nMIDIButtonPgmUp; @@ -751,6 +808,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..f72c32f8 100644 --- a/src/config.h +++ b/src/config.h @@ -217,6 +217,14 @@ class CConfig // Configuration for MiniDexed unsigned GetMIDIButtonSelect (void) const; unsigned GetMIDIButtonHome (void) const; + void SetMIDIButtonCh (unsigned v); + + void SetMIDIButtonPrev (unsigned v); + void SetMIDIButtonNext (unsigned v); + void SetMIDIButtonBack (unsigned v); + void SetMIDIButtonSelect (unsigned v); + void SetMIDIButtonHome (unsigned v); + // Action type for Midi buttons: "click", "doubleclick", "longpress", "dec", "inc", "" const char *GetMIDIButtonActionPrev (void) const; const char *GetMIDIButtonActionNext (void) const; @@ -224,6 +232,12 @@ class CConfig // Configuration for MiniDexed const char *GetMIDIButtonActionSelect (void) const; const char *GetMIDIButtonActionHome (void) const; + void SetMIDIButtonActionPrev (const char *v); + void SetMIDIButtonActionNext (const char *v); + void SetMIDIButtonActionBack (const char *v); + void SetMIDIButtonActionSelect (const char *v); + void SetMIDIButtonActionHome (const char *v); + // MIDI Button Program and TG Selection unsigned GetMIDIButtonPgmUp (void) const; unsigned GetMIDIButtonPgmDown (void) const; @@ -240,6 +254,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; @@ -341,18 +357,6 @@ class CConfig // Configuration for MiniDexed std::string m_ButtonActionBankDown; std::string m_ButtonActionTGUp; std::string m_ButtonActionTGDown; - - std::string m_MIDIButtonActionPrev; - std::string m_MIDIButtonActionNext; - std::string m_MIDIButtonActionBack; - std::string m_MIDIButtonActionSelect; - std::string m_MIDIButtonActionHome; - std::string m_MIDIButtonActionPgmUp; - std::string m_MIDIButtonActionPgmDown; - std::string m_MIDIButtonActionBankUp; - std::string m_MIDIButtonActionBankDown; - std::string m_MIDIButtonActionTGUp; - std::string m_MIDIButtonActionTGDown; unsigned m_nDoubleClickTimeout; unsigned m_nLongPressTimeout; @@ -371,6 +375,20 @@ class CConfig // Configuration for MiniDexed unsigned m_nMIDIButtonTGUp; unsigned m_nMIDIButtonTGDown; + std::string m_MIDIButtonActionPrev; + std::string m_MIDIButtonActionNext; + std::string m_MIDIButtonActionBack; + std::string m_MIDIButtonActionSelect; + std::string m_MIDIButtonActionHome; + std::string m_MIDIButtonActionPgmUp; + std::string m_MIDIButtonActionPgmDown; + std::string m_MIDIButtonActionBankUp; + std::string m_MIDIButtonActionBankDown; + std::string m_MIDIButtonActionTGUp; + std::string m_MIDIButtonActionTGDown; + + 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..5635fa6e --- /dev/null +++ b/src/dawcontroller.cpp @@ -0,0 +1,616 @@ +// 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" + +static void ArturiaDisplayWrite (CMIDIKeyboard *pKeyboard, const u8 *pHdr, const unsigned nHdrSize, unsigned L1MaxLen, const char *pMenu, const char *pParam, const char *pValue) +{ + CString line1 (pParam); + + 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 (pValue); + 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], pValue, nLine2Len + 1); + nOffset += nLine2Len + 1; + + pLines[nOffset] = 0xf7; + nOffset += 1; + + // block character (0xFF) is not supported, change to = + for (unsigned i = 0; i < sizeof pLines; ++i) + if (pLines[i] == 0xFF) + pLines[i] = '='; + + pKeyboard->SendDebounce (pLines, nOffset, 0); +} + +enum ControlType +{ + CT_KNOB = 3, + CT_FADER, + CT_PAD, +}; + +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, 0x02, 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 + 1); + nOffset += nLine1Len + 1; + + pLines[nOffset] = 0x02; + nOffset += 1; + + memcpy (pLines + nOffset, pValue, nLine2Len + 1); + nOffset += nLine2Len + 1; + + pLines[nOffset] = 0xf7; + nOffset += 1; + + pKeyboard->SendDebounce (pLines, nOffset, 0); +} + +static void ArturiaShowNewCCValue (CMIDIKeyboard *pKeyboard, const uint8_t pDisplayHdr[3], u8 ucCh, u8 ucCC, u8 ucValue) +{ + #define LINELEN 18 + + 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 %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_ALL_SOUND_OFF: + ArturiaDisplayInfoWrite (pKeyboard, pDisplayHdr, CT_PAD, ucValue, "All Sound Off", ""); + 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 ShowNewCCValue (u8 ucCh, u8 ucCC, u8 ucValue) = 0; + virtual ~CDAWConnection (void) = default; +}; + +struct CColor +{ + uint8_t r; + uint8_t g; + uint8_t b; +}; + +static CColor colors[CMIDIDevice::TChannel::Disabled + 1] = { + {0x7F, 0x00, 0x00}, + {0x7F, 0x16, 0x00}, + {0x7F, 0x32, 0x00}, + {0x7F, 0x48, 0x00}, + {0x7F, 0x64, 0x00}, + {0x7F, 0x7F, 0x00}, + {0x64, 0x7F, 0x00}, + {0x48, 0x7F, 0x00}, + {0x00, 0x7F, 0x00}, + {0x00, 0x7F, 0x16}, + {0x00, 0x7F, 0x32}, + {0x00, 0x7F, 0x48}, + {0x00, 0x7F, 0x64}, + {0x00, 0x7F, 0x7F}, + {0x7F, 0x00, 0x16}, + {0x7F, 0x00, 0x32}, + {0x7F, 0x7F, 0x7F}, + {0x00, 0x00, 0x00}, +}; + +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 ShowNewCCValue (u8 ucCh, u8 ucCC, u8 ucValue) override; +private: + enum TPadID { + PortamentoPad = 1, + SostenutoPad = 2, + SustainPad = 3, + SoundOffPad = 4, + }; + enum TBankID { + BankA = 0x34, + BankB = 0x44, + }; + + void UpdateEncoder (uint8_t ucEncID, uint8_t ucValue); + void UpdateTGColor (uint8_t nTG); + void SetPadColor (TBankID BankID, TPadID PadID, uint8_t r, uint8_t g, uint8_t b); + + static void s_EnableTG (void *data, u8 ucCable, u8 ucChannel, u8 ucType, u8 ucP1, u8 ucP2); + void EnableTG (u8 ucCable, u8 ucChannel, u8 ucType, u8 ucP1, u8 ucP2); + + CMiniDexed *m_pSynthesizer; + CMIDIKeyboard *m_pKeyboard; +}; + +CMiniLab3DawConnection::CMiniLab3DawConnection (CMiniDexed *pSynthesizer, CMIDIKeyboard *pKeyboard, CConfig *pConfig, CUserInterface *pUI) + :m_pSynthesizer (pSynthesizer), m_pKeyboard (pKeyboard) +{ + static const uint8_t pInit[] = {0xF0, 0x00, 0x20, 0x6B, 0x7F, 0x42, 0x02, 0x00, 0x40, 0x6A, 0x21, 0xF7}; + static TMIDIRouteMap map[] = { + {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, 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_CONTROL_CHANGE, MIDI_CC_DETUNE_LEVEL, 0xFF}, // Knob6 + // {0, 0, MIDI_CONTROL_CHANGE, 116, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_DETUNE_LEVEL, 0xFF}, // Knob7 + {0, 0, MIDI_CONTROL_CHANGE, 117, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_PAN_POSITION, 0xFF}, // Knob8 + {0, 9, MIDI_NOTE_ON, 37, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_PORTAMENTO, 0x7F, .bToggle=true}, // BankA Pad2 + {0, 9, MIDI_NOTE_OFF, 37, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_PORTAMENTO, 0x00, .bSkip=true}, // BankA Pad2 + {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, 0, MIDI_CONTROL_CHANGE, MIDI_CC_SOSTENUTO, 0x00, .bSkip=true}, // BankA Pad3 + {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, 0, MIDI_CONTROL_CHANGE, MIDI_CC_BANK_SUSTAIN, 0x00, .bSkip=true}, // BankA Pad4 + {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}, // BankA Pad5 + {0, 9, MIDI_NOTE_ON, 44, 0xFF, 0, MIDI_NOTE_ON, 0, 0x7F, .bSkip=true, .bToggle=true, .pFun=s_EnableTG, .pData=this}, // BankB Pad1 + {0, 9, MIDI_NOTE_OFF, 44, 0xFF, 0, MIDI_NOTE_ON, 0, 0xFF, .bSkip=true}, // BankB Pad1 + {0, 9, MIDI_NOTE_ON, 45, 0xFF, 0, MIDI_NOTE_ON, 1, 0x7F, .bSkip=true, .bToggle=true, .pFun=s_EnableTG, .pData=this}, // BankB Pad2 + {0, 9, MIDI_NOTE_OFF, 45, 0xFF, 0, MIDI_NOTE_ON, 1, 0xFF, .bSkip=true}, // BankB Pad2 + {0, 9, MIDI_NOTE_ON, 46, 0xFF, 0, MIDI_NOTE_ON, 2, 0x7F, .bSkip=true, .bToggle=true, .pFun=s_EnableTG, .pData=this}, // BankB Pad3 + {0, 9, MIDI_NOTE_OFF, 46, 0xFF, 0, MIDI_NOTE_ON, 2, 0xFF, .bSkip=true}, // BankB Pad3 + {0, 9, MIDI_NOTE_ON, 47, 0xFF, 0, MIDI_NOTE_ON, 3, 0x7F, .bSkip=true, .bToggle=true, .pFun=s_EnableTG, .pData=this}, // BankB Pad4 + {0, 9, MIDI_NOTE_OFF, 47, 0xFF, 0, MIDI_NOTE_ON, 3, 0xFF, .bSkip=true}, // BankB Pad4 + {0, 9, MIDI_NOTE_ON, 48, 0xFF, 0, MIDI_NOTE_ON, 4, 0x7F, .bSkip=true, .bToggle=true, .pFun=s_EnableTG, .pData=this}, // BankB Pad5 + {0, 9, MIDI_NOTE_OFF, 48, 0xFF, 0, MIDI_NOTE_ON, 4, 0xFF, .bSkip=true}, // BankB Pad5 + {0, 9, MIDI_NOTE_ON, 49, 0xFF, 0, MIDI_NOTE_ON, 5, 0x7F, .bSkip=true, .bToggle=true, .pFun=s_EnableTG, .pData=this}, // BankB Pad6 + {0, 9, MIDI_NOTE_OFF, 49, 0xFF, 0, MIDI_NOTE_ON, 5, 0xFF, .bSkip=true}, // BankB Pad6 + {0, 9, MIDI_NOTE_ON, 50, 0xFF, 0, MIDI_NOTE_ON, 6, 0x7F, .bSkip=true, .bToggle=true, .pFun=s_EnableTG, .pData=this}, // BankB Pad7 + {0, 9, MIDI_NOTE_OFF, 50, 0xFF, 0, MIDI_NOTE_ON, 6, 0xFF, .bSkip=true}, // BankB Pad7 + {0, 9, MIDI_NOTE_ON, 51, 0xFF, 0, MIDI_NOTE_ON, 7, 0x7F, .bSkip=true, .bToggle=true, .pFun=s_EnableTG, .pData=this}, // BankB Pad8 + {0, 9, MIDI_NOTE_OFF, 51, 0xFF, 0, MIDI_NOTE_ON, 7, 0xFF, .bSkip=true}, // BankB Pad8 + {0xFF}, // Sentine + }; + + m_pKeyboard->SetMIDIRouteMap (map); + + m_pKeyboard->Send (pInit, sizeof pInit, 0); + DisplayWrite ("MiniDexed", "", "On MiniLab 3", 0, 0); + + SetPadColor (BankA, PortamentoPad, 0x11, 0x11, 0x3f); + SetPadColor (BankA, SostenutoPad, 0x3f, 0x11, 0x3f); + SetPadColor (BankA, SustainPad, 0x11, 0x3f, 0x11); + SetPadColor (BankA, SoundOffPad, 0x3f, 0x11, 0x11); + + UpdateState (); + + pConfig->SetMIDIButtonCh (1); + pConfig->SetMIDIButtonPrev (28); + pConfig->SetMIDIButtonActionPrev("dec"); + pConfig->SetMIDIButtonNext (28); + pConfig->SetMIDIButtonActionNext("inc"); + pConfig->SetMIDIButtonBack (118); + pConfig->SetMIDIButtonActionBack("longpress"); + pConfig->SetMIDIButtonSelect (118); + pConfig->SetMIDIButtonActionSelect("click"); + pConfig->SetMIDIButtonHome (119); + pConfig->SetMIDIButtonActionHome("click"); + + pUI->InitButtonsWithConfig(pConfig); +} + +void CMiniLab3DawConnection::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, 0x02, 0x60, 0x12, 0x01}; + ArturiaDisplayWrite (m_pKeyboard, pHdr, sizeof pHdr, 18, pMenu, pParam, pValue); +} + +void CMiniLab3DawConnection::SetPadColor (TBankID BankID, TPadID PadID, uint8_t r, uint8_t g, uint8_t b) +{ + const uint8_t pSetPadColor[] = {0xF0, 0x00, 0x20, 0x6B, 0x7F, 0x42, 0x02, 0x02, 0x16, (uint8_t)(PadID + BankID), r, g, b, 0xF7}; + m_pKeyboard->Send (pSetPadColor, sizeof pSetPadColor, 0); +} + +void CMiniLab3DawConnection::UpdateEncoder (uint8_t ucEncID, uint8_t ucValue) +{ + uint8_t pUpdateEncoder[] = {0xF0, 0x00, 0x20, 0x6B, 0x7F, 0x42, 0x21, 0x10, 0x00, ucEncID+=7, 0x00, ucValue, 0xF7}; + m_pKeyboard->Send (pUpdateEncoder, sizeof pUpdateEncoder, 0); +} + +void CMiniLab3DawConnection::UpdateTGColor (uint8_t nTG) +{ + CColor color = colors[m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterMIDIChannel, nTG)]; + if (m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterEnabled, nTG) == 0) + { + color.r /= 4; + color.g /= 4; + color.b /= 4; + } + SetPadColor (BankB, (TPadID)nTG, color.r, color.g, color.b); +} + +void CMiniLab3DawConnection::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, maplong(m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterPortamentoTime, 0), 0, 99, 0, 127)); + UpdateEncoder (7, m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterPan, 0)); + + // TODO change the MIDIRouteMap's value also + SetPadColor (BankA, PortamentoPad, 0x11, 0x11, m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterPortamentoMode, 0) ? 0x7f : 0x3f); + + for (unsigned i = 0; i < CConfig::AllToneGenerators; ++i) + UpdateTGColor (i); +} + +void CMiniLab3DawConnection::ShowNewCCValue (u8 ucCh, u8 ucCC, u8 ucValue) +{ + static const uint8_t pEncoder[] = {0x04, 0x02, 0x60}; + ArturiaShowNewCCValue (m_pKeyboard, pEncoder, ucCh, ucCC, ucValue); + + switch (ucCC) + { + case MIDI_CC_PORTAMENTO: + SetPadColor (BankA, PortamentoPad, 0x11, 0x11, ucValue ? 0x7f : 0x3f); + break; + case MIDI_CC_SOSTENUTO: + SetPadColor (BankA, SostenutoPad, ucValue ? 0x7f : 0x3f, 0x11, ucValue ? 0x7f : 0x3f); + break; + case MIDI_CC_BANK_SUSTAIN: + SetPadColor (BankA, SustainPad, 0x11, ucValue ? 0x7f : 0x3f, 0x11); + break; + case MIDI_CC_ALL_SOUND_OFF: + SetPadColor (BankA, SoundOffPad, ucValue ? 0x7f : 0x3f, 0x11, 0x11); + break; + } +} + +void CMiniLab3DawConnection::s_EnableTG (void *data, u8 ucCable, u8 ucChannel, u8 ucType, u8 ucP1, u8 ucP2) +{ + static_cast(data)->EnableTG (ucCable, ucChannel, ucType, ucP1, ucP2); +} + +void CMiniLab3DawConnection::EnableTG (u8 ucCable, u8 ucChannel, u8 ucType, u8 ucP1, u8 ucP2) +{ + m_pSynthesizer->setEnabled (ucP2, ucP1); + m_pSynthesizer->panic (ucP2, ucP1); + + UpdateTGColor (ucP1); +} + +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 ShowNewCCValue (u8 ucCh, u8 ucCC, u8 ucValue) override; +private: + void UpdateEncoder (uint8_t ucEncID, uint8_t ucValue); + + CMiniDexed *m_pSynthesizer; + CMIDIKeyboard *m_pKeyboard; +}; + +CKeyLabEs3DawConnection::CKeyLabEs3DawConnection (CMiniDexed *pSynthesizer, CMIDIKeyboard *pKeyboard, CConfig *pConfig, CUserInterface *pUI) + :m_pSynthesizer (pSynthesizer), m_pKeyboard (pKeyboard) +{ + static const uint8_t pInit[] = {0xF0, 0x00, 0x20, 0x6B, 0x7F, 0x42, 0x02, 0x00, 0x40, 0x6A, 0x21, 0xF7}; + static TMIDIRouteMap map[] = { + {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 + }; + + m_pKeyboard->SetMIDIRouteMap (map); + + m_pKeyboard->Send (pInit, sizeof pInit, 0); + DisplayWrite ("MiniDexed", "", "On KeyLab 3 Essential", 0, 0); + + UpdateState (); + + pConfig->SetMIDIButtonCh (1); + pConfig->SetMIDIButtonPrev (116); + pConfig->SetMIDIButtonActionPrev("dec"); + pConfig->SetMIDIButtonNext (116); + pConfig->SetMIDIButtonActionNext("inc"); + pConfig->SetMIDIButtonBack (117); + pConfig->SetMIDIButtonActionBack("longpress"); + pConfig->SetMIDIButtonSelect (117); + pConfig->SetMIDIButtonActionSelect("click"); + pConfig->SetMIDIButtonHome (44); + pConfig->SetMIDIButtonActionHome("click"); + + pUI->InitButtonsWithConfig(pConfig); +} + +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, pMenu, pParam, pValue); +} + +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::ShowNewCCValue (u8 ucCh, u8 ucCC, u8 ucValue) +{ + static const uint8_t pEncoder[] = {0x04, 0x01, 0x60}; + ArturiaShowNewCCValue (m_pKeyboard, pEncoder, ucCh, ucCC, ucValue); +} + +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 ShowNewCCValue (u8 ucCh, u8 ucCC, u8 ucValue) override; +private: + CMiniDexed *m_pSynthesizer; + CMIDIKeyboard *m_pKeyboard; +}; + +CKeyLab2DawConnection::CKeyLab2DawConnection (CMiniDexed *pSynthesizer, CMIDIKeyboard *pKeyboard, CConfig *pConfig, CUserInterface *pUI) + :m_pSynthesizer (pSynthesizer), m_pKeyboard (pKeyboard) +{ + static const uint8_t pInit[] = {0xF0, 0x00, 0x20, 0x6B, 0x7F, 0x42, 0x02, 0x00, 0x40, 0x52, 0x00, 0xF7}; + static TMIDIRouteMap map[] = { + {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, 0, MIDI_CONTROL_CHANGE, 60, 0xFF, 0, MIDI_CONTROL_CHANGE, 60, 0xFF}, // Main rotate + {1, 0, MIDI_NOTE_ON, 84, 0xFF, 0, MIDI_CONTROL_CHANGE, 63, 0xFF}, // Main click + {1, 0xFF, 0xFF, 0xFF, 0xFF, .bSkip = true}, // skip other messages on DAW cable + {0xFF}, // Sentinel + }; + + m_pKeyboard->SetMIDIRouteMap (map); + + m_pKeyboard->Send (pInit, sizeof pInit, 0); + DisplayWrite ("MiniDexed", "", "On KeyLab 2", 0, 0); + + UpdateState (); + + pConfig->SetMIDIButtonCh (1); + pConfig->SetMIDIButtonPrev (60); + pConfig->SetMIDIButtonActionPrev("inc"); + pConfig->SetMIDIButtonNext (60); + pConfig->SetMIDIButtonActionNext("dec"); + pConfig->SetMIDIButtonBack (63); + pConfig->SetMIDIButtonActionBack("longpress"); + pConfig->SetMIDIButtonSelect (63); + pConfig->SetMIDIButtonActionSelect("click"); + pConfig->SetMIDIButtonHome (63); + pConfig->SetMIDIButtonActionHome("doubleclick"); + + pUI->InitButtonsWithConfig(pConfig); +} + +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, pMenu, pParam, pValue); +} + +void CKeyLab2DawConnection::UpdateState () +{ +} + +void CKeyLab2DawConnection::ShowNewCCValue (u8 ucCh, u8 ucCC, u8 ucValue) +{ + //static const uint8_t pEncoder[] = {0x04, 0x01, 0x60}; + //ArturiaShowNewCCValue (pKeyboard, pEncoder, ucCh, ucCC, ucValue); +} + + +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 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}; + 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}; + + 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 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); + } + 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); + } +} + +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::ShowNewCCValue (u8 ucCh, u8 ucCC, u8 ucValue) +{ + if (m_pDAWConnection) + m_pDAWConnection->ShowNewCCValue (ucCh, ucCC, ucValue); +} diff --git a/src/dawcontroller.h b/src/dawcontroller.h new file mode 100644 index 00000000..8f451f3f --- /dev/null +++ b/src/dawcontroller.h @@ -0,0 +1,52 @@ +// 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 + +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 ShowNewCCValue (u8 ucCh, u8 ucCC, u8 ucValue); + +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 64297e1f..92bb4757 100644 --- a/src/mididevice.cpp +++ b/src/mididevice.cpp @@ -31,31 +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_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_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. @@ -81,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_pMIDIRouteMap () { for (unsigned nTG = 0; nTG < CConfig::AllToneGenerators; nTG++) { @@ -179,6 +155,7 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign } break; } + fflush (stdout); } // Only for debugging: @@ -216,12 +193,28 @@ 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; + TMidiFun *pFun = NULL; + void *pData = NULL; + + if (m_pMIDIRouteMap) + GetRoutedMIDI (m_pMIDIRouteMap, &ucCable, &ucChannel, &ucType, &ucP1, &ucP2, &bSkip, &pFun, &pData); + + if (pFun) + pFun(pData, ucCable, ucChannel, ucType, ucP1, ucP2); + if (bSkip) + { + // skip (and release mutex at the end) + } // GLOBAL MIDI SYSEX - if (pMessage[0] == MIDI_SYSTEM_EXCLUSIVE_BEGIN && pMessage[3] == 0x04 && pMessage[4] == 0x01 && pMessage[nLength-1] == MIDI_SYSTEM_EXCLUSIVE_END) // MASTER VOLUME + else if (pMessage[0] == MIDI_SYSTEM_EXCLUSIVE_BEGIN && pMessage[3] == 0x04 && pMessage[4] == 0x01 && pMessage[nLength-1] == MIDI_SYSTEM_EXCLUSIVE_END) // MASTER VOLUME { float32_t nMasterVolume=((pMessage[5] & 0x7c) & ((pMessage[6] & 0x7c) <<7))/(1<<14); LOGNOTE("Master volume: %f",nMasterVolume); @@ -239,13 +232,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 { @@ -255,7 +248,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; @@ -265,7 +258,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: @@ -277,7 +270,7 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign if ((ucChannel == nPerfCh) || (nPerfCh == OmniMode)) { //printf("Performance Select Channel %d\n", nPerfCh); - m_pSynthesizer->ProgramChangePerformance (pMessage[1]); + m_pSynthesizer->ProgramChangePerformance (ucP1); } } } @@ -315,17 +308,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; @@ -335,12 +328,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; @@ -350,81 +343,81 @@ 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_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: @@ -433,7 +426,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; @@ -443,18 +436,19 @@ 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; } + MIDICCHandler (ucChannel, ucP1, ucP2); break; case MIDI_PROGRAM_CHANGE: // 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; @@ -464,8 +458,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); @@ -538,6 +532,11 @@ bool CMIDIDevice::HandleMIDISystemCC(const u8 ucCC, const u8 ucCCval) return false; } +void CMIDIDevice::SetMIDIRouteMap (TMIDIRouteMap *pMIDIRouteMap) +{ + m_pMIDIRouteMap = pMIDIRouteMap; +} + void CMIDIDevice::HandleSystemExclusive(const uint8_t* pMessage, const size_t nLength, const unsigned nCable, const uint8_t nTG) { int16_t sysex_return; @@ -666,6 +665,11 @@ void CMIDIDevice::HandleSystemExclusive(const uint8_t* pMessage, const size_t nL } } +void CMIDIDevice::MIDICCHandler (u8 ucCh, u8 ucCC, u8 ucValue) +{ +} + + void CMIDIDevice::SendSystemExclusiveVoice(uint8_t nVoice, const unsigned nCable, uint8_t nTG) { uint8_t voicedump[163]; @@ -682,3 +686,32 @@ void CMIDIDevice::SendSystemExclusiveVoice(uint8_t nVoice, const unsigned nCable // LOGDBG("Send SYSEX voice dump %u to \"%s\"",nVoice,Iterator->first.c_str()); } } + +void GetRoutedMIDI (TMIDIRouteMap *map, u8 *pCable, u8 *pCh, u8 *pType, u8 *pP1, u8 *pP2, bool *bSkip, TMidiFun **pFun, void **pData) +{ + assert (map); + for (TMIDIRouteMap *r = map; 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 > 127)) + { + *pCh = r->ucDCh; + *pType = r->ucDType; + if (r->ucDP1 <= 127) + *pP1 = r->ucDP1; + if (r->ucDP2 <= 127) + *pP2 = r->ucDP2; + *bSkip = r->bSkip; + if (r->bToggle) + r->ucDP2 = r->ucDP2 ? 0x0 : 0x7F; + if (r->pFun) + *pFun = r->pFun; + if (r->pData) + *pData = r->pData; + return; + } + } +} diff --git a/src/mididevice.h b/src/mididevice.h index 44f16916..a4b30839 100644 --- a/src/mididevice.h +++ b/src/mididevice.h @@ -33,8 +33,54 @@ #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_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; +typedef void TMidiFun (void *data, u8 ucCable, u8 ucChannel, u8 ucType, u8 ucP1, u8 ucP2); + +typedef struct +{ + u8 ucSCable; + u8 ucSCh; + u8 ucSType; + u8 ucSP1; + u8 ucSP2; + u8 ucDCh; + u8 ucDType; + u8 ucDP1; + u8 ucDP2; + bool bSkip; + bool bToggle; + TMidiFun *pFun; + void *pData; +} TMIDIRouteMap; + +void GetRoutedMIDI (TMIDIRouteMap *m_pRouteMap, u8 *pCable, u8 *pChannel, u8 *pType, u8 *pP1, u8 *pP2, bool *bSkip, TMidiFun **pFun, void **pData); + class CMIDIDevice { public: @@ -46,6 +92,8 @@ class CMIDIDevice ChannelUnknown }; + + public: CMIDIDevice (CMiniDexed *pSynthesizer, CConfig *pConfig, CUserInterface *pUI); virtual ~CMIDIDevice (void); @@ -53,6 +101,8 @@ class CMIDIDevice void SetChannel (u8 ucChannel, unsigned nTG); u8 GetChannel (unsigned nTG) const; + void SetMIDIRouteMap (TMIDIRouteMap *pMIDIRouteMap); + virtual void Send (const u8 *pMessage, size_t nLength, unsigned nCable = 0) {} virtual void SendSystemExclusiveVoice(uint8_t nVoice, const unsigned nCable, uint8_t nTG); @@ -61,6 +111,8 @@ class CMIDIDevice void AddDevice (const char *pDeviceName); void HandleSystemExclusive(const uint8_t* pMessage, const size_t nLength, const unsigned nCable, const uint8_t nTG); + virtual void MIDICCHandler (u8 ucCh, u8 ucCC, u8 ucValue); + private: bool HandleMIDISystemCC(const u8 ucCC, const u8 ucCCval); @@ -78,6 +130,8 @@ class CMIDIDevice std::string m_DeviceName; + TMIDIRouteMap *m_pMIDIRouteMap; + typedef std::unordered_map TDeviceMap; static TDeviceMap s_DeviceMap; diff --git a/src/midikeyboard.cpp b/src/midikeyboard.cpp index db71168d..06d979ba 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,22 @@ 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::MIDICCHandler (u8 ucCh, u8 ucCC, u8 ucValue) +{ + if (m_pDAWController) + m_pDAWController->ShowNewCCValue (ucCh, ucCC, ucValue); +} diff --git a/src/midikeyboard.h b/src/midikeyboard.h index bf626890..86e44865 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,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 UpdateDAWState (void); private: static void MIDIPacketHandler (unsigned nCable, u8 *pPacket, unsigned nLength, unsigned nDevice, void *pParam); @@ -51,6 +58,8 @@ class CMIDIKeyboard : public CMIDIDevice void USBMIDIMessageHandler (u8 *pPacket, unsigned nLength, unsigned nCable, unsigned nDevice); + void MIDICCHandler (u8 ucCh, u8 ucCC, u8 ucValue) override; + private: struct TSendQueueEntry { @@ -68,6 +77,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 0beea890..804c3316 100644 --- a/src/minidexed.cpp +++ b/src/minidexed.cpp @@ -1059,6 +1059,21 @@ void CMiniDexed::SetTGParameter (TTGParameter Parameter, int nValue, unsigned nT assert (0); break; } + + switch (Parameter) + { + case TGParameterCutoff: + case TGParameterResonance: + case TGParameterPan: + case TGParameterReverbSend: + case TGParameterMasterTune: + case TGParameterPortamentoTime: + case TGParameterPortamentoMode: + UpdateDAWState (); + break; + default: + break; + } } int CMiniDexed::GetTGParameter (TTGParameter Parameter, unsigned nTG) @@ -1799,6 +1814,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::UpdateDAWState () +{ + for (unsigned i = 0; i < CConfig::MaxUSBMIDIDevices; i++) + { + m_pMIDIKeyboard[i]->UpdateDAWState (); + } +} + std::string CMiniDexed::GetPerformanceFileName(unsigned nID) { return m_PerformanceConfig.GetPerformanceFileName(nID); @@ -1985,6 +2019,8 @@ void CMiniDexed::LoadPerformanceParameters(void) SetParameter (ParameterReverbLowPass, m_PerformanceConfig.GetReverbLowPass ()); SetParameter (ParameterReverbDiffusion, m_PerformanceConfig.GetReverbDiffusion ()); SetParameter (ParameterReverbLevel, m_PerformanceConfig.GetReverbLevel ()); + + UpdateDAWState (); } std::string CMiniDexed::GetNewPerformanceDefaultName(void) diff --git a/src/minidexed.h b/src/minidexed.h index 72f43c7f..866bd032 100644 --- a/src/minidexed.h +++ b/src/minidexed.h @@ -232,6 +232,11 @@ class CMiniDexed void setMasterVolume (float32_t vol); + void DisplayWrite (const char *pMenu, const char *pParam, const char *pValue, + bool bArrowDown, bool bArrowUp); + + void UpdateDAWState (); + 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..2ba9fd06 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 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; im_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, @@ -594,7 +594,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); @@ -638,7 +638,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); @@ -708,7 +708,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); @@ -761,7 +761,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); @@ -814,7 +814,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); @@ -867,7 +867,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); @@ -970,7 +970,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); @@ -989,7 +989,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); @@ -1652,7 +1652,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; @@ -1685,13 +1685,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); } } @@ -1773,7 +1773,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); @@ -1886,7 +1886,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; } @@ -1937,7 +1937,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); } @@ -1991,7 +1991,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/userinterface.cpp b/src/userinterface.cpp index 32222a1f..7320caea 100644 --- a/src/userinterface.cpp +++ b/src/userinterface.cpp @@ -162,16 +162,7 @@ bool CUserInterface::Initialize (void) LOGDBG ("LCD initialized"); } - m_pUIButtons = new CUIButtons ( m_pConfig ); - assert (m_pUIButtons); - - if (!m_pUIButtons->Initialize ()) - { - return false; - } - - m_pUIButtons->RegisterEventHandler (UIButtonsEventStub, this); - UISetMIDIButtonChannel (m_pConfig->GetMIDIButtonCh ()); + InitButtonsWithConfig (m_pConfig); LOGDBG ("Button User Interface initialized"); @@ -198,6 +189,17 @@ bool CUserInterface::Initialize (void) return true; } +void CUserInterface::InitButtonsWithConfig (CConfig *pConfig) +{ + delete m_pUIButtons; + m_pUIButtons = new CUIButtons (pConfig); + assert (m_pUIButtons); + + m_pUIButtons->Initialize (); + m_pUIButtons->RegisterEventHandler (UIButtonsEventStub, this); + UISetMIDIButtonChannel (pConfig->GetMIDIButtonCh ()); +} + void CUserInterface::Process (void) { if (m_pLCDBuffered) @@ -396,7 +398,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 +413,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..24f5763c 100644 --- a/src/userinterface.h +++ b/src/userinterface.h @@ -57,6 +57,8 @@ 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 InitButtonsWithConfig (CConfig *pConfig); + private: void LCDWrite (const char *pString); // Print to optional HD44780 display