add DAW Controller support for Arturia MiniLab 3 and KeyLab 3 Essential

based on https://github.com/PrzemekBarski/arturia-keylab-essential-mk3-programming-guide

Tested on a Arturia MiniLab 3

Keylab 3 Essential is not tested
pull/743/head
Gergo Koteles 2 weeks ago
parent 2a5d8b91a2
commit a520bf05a1
  1. 3
      src/Makefile
  2. 62
      src/config.cpp
  3. 18
      src/config.h
  4. 432
      src/dawcontroller.cpp
  5. 55
      src/dawcontroller.h
  6. 137
      src/mididevice.cpp
  7. 46
      src/mididevice.h
  8. 40
      src/midikeyboard.cpp
  9. 11
      src/midikeyboard.h
  10. 34
      src/minidexed.cpp
  11. 5
      src/minidexed.h
  12. 3
      src/minidexed.ini
  13. 16
      src/uibuttons.cpp
  14. 32
      src/uimenu.cpp
  15. 104
      src/userinterface.cpp
  16. 2
      src/userinterface.h

@ -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

@ -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);
@ -638,6 +640,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();
@ -663,6 +695,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;
@ -703,6 +760,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;

@ -213,6 +213,14 @@ public:
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;
@ -220,6 +228,12 @@ public:
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;
@ -232,6 +246,8 @@ public:
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 +371,8 @@ private:
std::string m_MIDIButtonActionTGUp;
std::string m_MIDIButtonActionTGDown;
bool m_bDAWControllerEnabled;
bool m_bEncoderEnabled;
unsigned m_nEncoderPinClock;
unsigned m_nEncoderPinData;

@ -0,0 +1,432 @@
//
// 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 <http://www.gnu.org/licenses/>.
//
#include <circle/string.h>
#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);
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;
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_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_ALL_SOUND_OFF:
ArturiaDisplayInfoWrite (pKeyboard, pDisplayHdr, CT_PAD, ucValue, "All Sound Off", "");
break;
}
}
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 void ShowNewCCValue (CMIDIKeyboard *pKeyboard, u8 ucCh, u8 ucCC, u8 ucValue) = 0;
virtual ~CDAWConnection (void) = default;
};
class CMiniLab3DawConnection : public CDAWConnection
{
public:
CMiniLab3DawConnection (CMiniDexed *pSynthesizer, CMIDIKeyboard *pKeyboard, CConfig *pConfig, CUserInterface *pUI);
void DisplayWrite (CMIDIKeyboard *pKeyboard, const char *pMenu, const char *pParam, const char *pValue, bool bArrowDown, bool bArrowUp) override;
void UpdateEncoders (CMiniDexed *pSynthesizer, CMIDIKeyboard *pKeyboard) override;
void ShowNewCCValue (CMIDIKeyboard *pKeyboard, u8 ucCh, u8 ucCC, u8 ucValue) override;
private:
enum TPadID {
SustainPad = 3,
SoundOffPad = 4,
};
enum TBankID {
BankA = 0x34,
BankB = 0x44,
};
void UpdateEncoder (CMIDIKeyboard *pKeyboard, uint8_t ucEncID, uint8_t ucValue);
void SetPadColor (CMIDIKeyboard *pKeyBoard, TBankID BankID, TPadID PadID, uint8_t r, uint8_t g, uint8_t b);
uint8_t m_pEncoderCache[8];
};
CMiniLab3DawConnection::CMiniLab3DawConnection (CMiniDexed *pSynthesizer, CMIDIKeyboard *pKeyboard, CConfig *pConfig, CUserInterface *pUI)
{
static const uint8_t pInit[] = {0xF0, 0x00, 0x20, 0x6B, 0x7F, 0x42, 0x02, 0x00, 0x40, 0x6A, 0x21, 0xF7};
static TMIDIRouteMap map[] = {
{0, MIDI_CONTROL_CHANGE, 14, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader1
{0, MIDI_CONTROL_CHANGE, 15, 0xFF, 1, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader2
{0, MIDI_CONTROL_CHANGE, 30, 0xFF, 2, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader3
{0, MIDI_CONTROL_CHANGE, 31, 0xFF, 3, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader4
{0, MIDI_CONTROL_CHANGE, 86, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_FREQUENCY_CUTOFF, 0xFF}, // Knob1
{0, MIDI_CONTROL_CHANGE, 87, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_RESONANCE, 0xFF}, // Knob2
{0, MIDI_CONTROL_CHANGE, 89, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_REVERB_LEVEL, 0xFF}, // Knob3
{0, MIDI_CONTROL_CHANGE, 90, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_DETUNE_LEVEL, 0xFF}, // Knob4
// {0, MIDI_CONTROL_CHANGE, 110, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_DETUNE_LEVEL, 0xFF}, // Knob5
// {0, MIDI_CONTROL_CHANGE, 111, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_DETUNE_LEVEL, 0xFF}, // Knob6
// {0, MIDI_CONTROL_CHANGE, 116, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_DETUNE_LEVEL, 0xFF}, // Knob7
{0, MIDI_CONTROL_CHANGE, 117, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_PAN_POSITION, 0xFF}, // Knob8
{9, MIDI_NOTE_ON, 39, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_BANK_SUSTAIN, 0x7F, .bToggle=true}, // BankA Pad4
{9, MIDI_NOTE_OFF, 39, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_BANK_SUSTAIN, 0x00, .bSkip=true}, // BankA Pad4
{9, MIDI_NOTE_ON, 40, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_ALL_SOUND_OFF, 0x7f}, // BankA Pad5
{9, MIDI_NOTE_OFF, 40, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_ALL_SOUND_OFF, 0x00}, // BankA Pad5
{0xFF}, // Sentinel
};
memset (m_pEncoderCache, 128, sizeof m_pEncoderCache);
pKeyboard->SetMIDIRouteMap (map);
pKeyboard->Send (pInit, sizeof pInit, 0);
DisplayWrite (pKeyboard, "MiniDexed", "", "On MiniLab 3", 0, 0);
SetPadColor (pKeyboard, BankA, SustainPad, 0x11, 0x3f, 0x11);
SetPadColor (pKeyboard, BankA, SoundOffPad, 0x3f, 0x11, 0x11);
UpdateEncoders (pSynthesizer, pKeyboard);
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 (CMIDIKeyboard *pKeyboard, 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 (pKeyboard, pHdr, sizeof pHdr, pMenu, pParam, pValue);
}
void CMiniLab3DawConnection::SetPadColor (CMIDIKeyboard *pKeyboard, 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};
pKeyboard->Send (pSetPadColor, sizeof pSetPadColor, 0);
}
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));
}
void CMiniLab3DawConnection::ShowNewCCValue (CMIDIKeyboard *pKeyboard, u8 ucCh, u8 ucCC, u8 ucValue)
{
static const uint8_t pEncoder[] = {0x04, 0x02, 0x60};
ArturiaShowNewCCValue (pKeyboard, pEncoder, ucCh, ucCC, ucValue);
switch (ucCC)
{
case MIDI_CC_BANK_SUSTAIN:
SetPadColor (pKeyboard, BankA, SustainPad, 0x11, ucValue ? 0x7f : 0x3f, 0x11);
break;
case MIDI_CC_ALL_SOUND_OFF:
SetPadColor (pKeyboard, BankA, SoundOffPad, ucValue ? 0x7f : 0x3f, 0x11, 0x11);
break;
}
}
class CKeyLabEs3DawConnection : public CDAWConnection
{
public:
CKeyLabEs3DawConnection (CMiniDexed *pSynthesizer, CMIDIKeyboard *pKeyboard, CConfig *pConfig, CUserInterface *pUI);
void DisplayWrite (CMIDIKeyboard *pKeyboard, const char *pMenu, const char *pParam, const char *pValue, bool bArrowDown, bool bArrowUp) override;
void UpdateEncoders (CMiniDexed *pSynthesizer, CMIDIKeyboard *pKeyboard) override;
void ShowNewCCValue (CMIDIKeyboard *pKeyboard, u8 ucCh, u8 ucCC, u8 ucValue) override;
private:
void UpdateEncoder (CMIDIKeyboard *pKeyboard, uint8_t ucEncID, uint8_t ucValue);
uint8_t m_pEncoderCache[8];
};
CKeyLabEs3DawConnection::CKeyLabEs3DawConnection (CMiniDexed *pSynthesizer, CMIDIKeyboard *pKeyboard, CConfig *pConfig, CUserInterface *pUI)
{
static const uint8_t pInit[] = {0xF0, 0x00, 0x20, 0x6B, 0x7F, 0x42, 0x02, 0x00, 0x40, 0x6A, 0x21, 0xF7};
static TMIDIRouteMap map[] = {
{0, MIDI_CONTROL_CHANGE, 105, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader1
{0, MIDI_CONTROL_CHANGE, 106, 0xFF, 1, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader2
{0, MIDI_CONTROL_CHANGE, 107, 0xFF, 2, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader3
{0, MIDI_CONTROL_CHANGE, 108, 0xFF, 3, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader4
{0, MIDI_CONTROL_CHANGE, 109, 0xFF, 4, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader5
{0, MIDI_CONTROL_CHANGE, 110, 0xFF, 5, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader6
{0, MIDI_CONTROL_CHANGE, 111, 0xFF, 6, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader7
{0, MIDI_CONTROL_CHANGE, 112, 0xFF, 7, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader8
//{0, MIDI_CONTROL_CHANGE, 113, 0xFF, 8, MIDI_CONTROL_CHANGE, MIDI_CC_VOLUME, 0xFF}, // Fader9
{0, MIDI_CONTROL_CHANGE, 96, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_FREQUENCY_CUTOFF, 0xFF}, // Knob1
{0, MIDI_CONTROL_CHANGE, 97, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_RESONANCE, 0xFF}, // Knob2
{0, MIDI_CONTROL_CHANGE, 98, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_REVERB_LEVEL, 0xFF}, // Knob3
{0, MIDI_CONTROL_CHANGE, 99, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_DETUNE_LEVEL, 0xFF}, // Knob4
{0, MIDI_CONTROL_CHANGE, 100, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_PAN_POSITION, 0xFF}, // Knob5
// {0, MIDI_CONTROL_CHANGE, 101, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_DETUNE_LEVEL, 0xFF}, // Knob6
// {0, MIDI_CONTROL_CHANGE, 102, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_DETUNE_LEVEL, 0xFF}, // Knob7
// {0, MIDI_CONTROL_CHANGE, 103, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_PAN_POSITION, 0xFF}, // Knob8
// {0, MIDI_CONTROL_CHANGE, 104, 0xFF, 0, MIDI_CONTROL_CHANGE, MIDI_CC_PAN_POSITION, 0xFF}, // Knob9
{0xFF}, // Sentinel
};
memset (m_pEncoderCache, 128, sizeof m_pEncoderCache);
pKeyboard->SetMIDIRouteMap (map);
pKeyboard->Send (pInit, sizeof pInit, 0);
DisplayWrite (pKeyboard, "MiniDexed", "", "On KeyLab 3 Essential", 0, 0);
UpdateEncoders (pSynthesizer, pKeyboard);
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 (CMIDIKeyboard *pKeyboard, 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 (pKeyboard, 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));
}
void CKeyLabEs3DawConnection::ShowNewCCValue (CMIDIKeyboard *pKeyboard, 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};
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);
}
}
void CDAWController::DisplayWrite (const char *pMenu, const char *pParam, const char *pValue,
bool bArrowDown, bool bArrowUp)
{
if (m_pDAWConnection)
m_pDAWConnection->DisplayWrite (m_pKeyboard, pMenu, pParam, pValue, bArrowDown, bArrowUp);
}
void CDAWController::UpdateEncoders (void)
{
if (m_pDAWConnection)
m_pDAWConnection->UpdateEncoders (m_pSynthesizer, m_pKeyboard);
}
void CDAWController::ShowNewCCValue (u8 ucCh, u8 ucCC, u8 ucValue)
{
if (m_pDAWConnection)
m_pDAWConnection->ShowNewCCValue (m_pKeyboard, ucCh, ucCC, ucValue);
}

@ -0,0 +1,55 @@
//
// 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 <http://www.gnu.org/licenses/>.
//
#ifndef _dawcontroller_h
#define _dawcontroller_h
#include <circle/types.h>
class CMIDIKeyboard;
class CMiniDexed;
class CDAWConnection;
class CConfig;
class CUserInterface;
class CDAWController
{
public:
CDAWController (CMiniDexed *pSynthesizer, CMIDIKeyboard *pKeyboard, CConfig *pConfig, CUserInterface *pUI);
~CDAWController (void);
void OnConnect (void);
void MIDISysexHandler (u8 *pPacket, unsigned nLength, unsigned nCable);
void DisplayWrite (const char *pMenu, const char *pParam, const char *pValue,
bool bArrowDown, bool bArrowUp);
void UpdateEncoders (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

@ -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_pMIDIRouteMap ()
{
for (unsigned nTG = 0; nTG < CConfig::AllToneGenerators; nTG++)
{
@ -216,9 +195,19 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
u8 ucStatus = pMessage[0];
u8 ucChannel = ucStatus & 0x0F;
u8 ucType = ucStatus >> 4;
u8 ucP1 = pMessage[1];
u8 ucP2 = nLength >= 3 ? pMessage[2] : 0xFF;
bool bSkip = false;
if (m_pMIDIRouteMap)
GetRoutedMIDI (m_pMIDIRouteMap, &ucChannel, &ucType, &ucP1, &ucP2, &bSkip);
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);
@ -236,13 +225,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
{
@ -252,7 +241,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;
@ -262,7 +251,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:
@ -274,7 +263,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);
}
}
}
@ -309,17 +298,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;
@ -329,12 +318,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;
@ -344,69 +333,69 @@ 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_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_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:
@ -415,7 +404,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;
@ -425,18 +414,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;
@ -446,8 +436,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);
@ -520,6 +510,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;
@ -648,6 +643,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];
@ -664,3 +664,24 @@ 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 *pCh, u8 *pType, u8 *pP1, u8 *pP2, bool *bSkip)
{
assert (map);
for (TMIDIRouteMap *r = map; r->ucSCh < 16 ; r++)
{
if (r->ucSCh == *pCh && r->ucSType == *pType && (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->ucDType == MIDI_CONTROL_CHANGE && r->bToggle)
r->ucDP2 = r->ucDP2 ? 0x0 : 0x7F;
return;
}
}
}

@ -33,8 +33,46 @@
#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;
typedef struct
{
u8 ucSCh;
u8 ucSType;
u8 ucSP1;
u8 ucSP2;
u8 ucDCh;
u8 ucDType;
u8 ucDP1;
u8 ucDP2;
bool bSkip;
bool bToggle;
} TMIDIRouteMap;
void GetRoutedMIDI (TMIDIRouteMap *m_pRouteMap, u8 *pChannel, u8 *pType, u8 *pP1, u8 *pP2, bool *bSkip);
class CMIDIDevice
{
public:
@ -46,6 +84,8 @@ public:
ChannelUnknown
};
public:
CMIDIDevice (CMiniDexed *pSynthesizer, CConfig *pConfig, CUserInterface *pUI);
virtual ~CMIDIDevice (void);
@ -53,6 +93,8 @@ public:
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 +103,8 @@ protected:
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 +122,8 @@ private:
std::string m_DeviceName;
TMIDIRouteMap *m_pMIDIRouteMap;
typedef std::unordered_map<std::string, CMIDIDevice *> TDeviceMap;
static TDeviceMap s_DeviceMap;

@ -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, pConfig, pUI);
}
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,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::UpdateEncoders (void)
{
if (m_pMIDIDevice && m_pDAWController)
m_pDAWController->UpdateEncoders ();
}
void CMIDIKeyboard::MIDICCHandler (u8 ucCh, u8 ucCC, u8 ucValue)
{
if (m_pDAWController)
m_pDAWController->ShowNewCCValue (ucCh, ucCC, ucValue);
}

@ -25,6 +25,7 @@
#include "mididevice.h"
#include "config.h"
#include "dawcontroller.h"
#include <circle/usb/usbmidi.h>
#include <circle/device.h>
#include <circle/string.h>
@ -47,6 +48,12 @@ public:
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);
@ -58,6 +65,8 @@ private:
void USBMIDIMessageHandler (u8 *pPacket, unsigned nLength, unsigned nCable);
void MIDICCHandler (u8 ucCh, u8 ucCC, u8 ucValue) override;
private:
struct TSendQueueEntry
{
@ -76,6 +85,8 @@ private:
std::queue<TSendQueueEntry> m_SendQueue;
CDAWController *m_pDAWController;
static CMIDIKeyboard *s_pThis[MaxInstances];
static TMIDIPacketHandler * const s_pMIDIPacketHandler[MaxInstances];

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

@ -228,6 +228,11 @@ public:
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];

@ -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

@ -18,6 +18,7 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#include "uibuttons.h"
#include "mididevice.h"
#include <circle/logger.h>
#include <assert.h>
#include <circle/timer.h>
@ -552,21 +553,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; i<MAX_BUTTONS; i++) {
if (m_buttons[i].getPinNumber() == midiPin) {
if (nMidiCmd == 0x80) {
// NoteOff = Button OFF
if (nMidiType == MIDI_NOTE_OFF) {
m_buttons[i].Write (0);
} else if ((nMidiCmd == 0x90) && (nMidiData2 == 0)) {
} else if ((nMidiType == MIDI_NOTE_ON) && (nMidiData2 == 0)) {
// NoteOn with Vel == 0 = Button OFF
m_buttons[i].Write (0);
} else if (nMidiCmd == 0x90) {
} else if (nMidiType == MIDI_NOTE_ON) {
// NoteOn = Button ON
m_buttons[i].Write (127);
} else {
@ -575,9 +575,9 @@ void CUIButtons::BtnMIDICmdHandler (unsigned nMidiCmd, unsigned nMidiData1, unsi
}
}
} else {
// LOGDBG("BtnMIDICmdHandler (CC): %x %x %x)", nMidiCmd, nMidiData1, nMidiData2);
// LOGDBG("BtnMIDICmdHandler (CC): %x %x %x)", nMidiType, nMidiData1, nMidiData2);
// Using MIDI CC messages for MIDI buttons
if (nMidiCmd == 0xB0) { // Control Message
if (nMidiType == MIDI_CONTROL_CHANGE) { // Control Message
unsigned midiPin = ccToMidiPin(nMidiData1);
for (unsigned i=0; i<MAX_BUTTONS; i++) {
if (m_buttons[i].getPinNumber() == midiPin) {

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

@ -162,55 +162,7 @@ bool CUserInterface::Initialize (void)
LOGDBG ("LCD initialized");
}
m_pUIButtons = new CUIButtons ( m_pConfig->GetButtonPinPrev (),
m_pConfig->GetButtonActionPrev (),
m_pConfig->GetButtonPinNext (),
m_pConfig->GetButtonActionNext (),
m_pConfig->GetButtonPinBack (),
m_pConfig->GetButtonActionBack (),
m_pConfig->GetButtonPinSelect (),
m_pConfig->GetButtonActionSelect (),
m_pConfig->GetButtonPinHome (),
m_pConfig->GetButtonActionHome (),
m_pConfig->GetButtonPinPgmUp (),
m_pConfig->GetButtonActionPgmUp (),
m_pConfig->GetButtonPinPgmDown (),
m_pConfig->GetButtonActionPgmDown (),
m_pConfig->GetButtonPinTGUp (),
m_pConfig->GetButtonActionTGUp (),
m_pConfig->GetButtonPinTGDown (),
m_pConfig->GetButtonActionTGDown (),
m_pConfig->GetDoubleClickTimeout (),
m_pConfig->GetLongPressTimeout (),
m_pConfig->GetMIDIButtonNotes (),
m_pConfig->GetMIDIButtonPrev (),
m_pConfig->GetMIDIButtonActionPrev (),
m_pConfig->GetMIDIButtonNext (),
m_pConfig->GetMIDIButtonActionNext (),
m_pConfig->GetMIDIButtonBack (),
m_pConfig->GetMIDIButtonActionBack (),
m_pConfig->GetMIDIButtonSelect (),
m_pConfig->GetMIDIButtonActionSelect (),
m_pConfig->GetMIDIButtonHome (),
m_pConfig->GetMIDIButtonActionHome (),
m_pConfig->GetMIDIButtonPgmUp (),
m_pConfig->GetMIDIButtonActionPgmUp (),
m_pConfig->GetMIDIButtonPgmDown (),
m_pConfig->GetMIDIButtonActionPgmDown (),
m_pConfig->GetMIDIButtonTGUp (),
m_pConfig->GetMIDIButtonActionTGUp (),
m_pConfig->GetMIDIButtonTGDown (),
m_pConfig->GetMIDIButtonActionTGDown ()
);
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");
@ -237,6 +189,56 @@ bool CUserInterface::Initialize (void)
return true;
}
void CUserInterface::InitButtonsWithConfig (CConfig *pConfig)
{
delete m_pUIButtons;
m_pUIButtons = new CUIButtons ( pConfig->GetButtonPinPrev (),
pConfig->GetButtonActionPrev (),
pConfig->GetButtonPinNext (),
pConfig->GetButtonActionNext (),
pConfig->GetButtonPinBack (),
pConfig->GetButtonActionBack (),
pConfig->GetButtonPinSelect (),
pConfig->GetButtonActionSelect (),
pConfig->GetButtonPinHome (),
pConfig->GetButtonActionHome (),
pConfig->GetButtonPinPgmUp (),
pConfig->GetButtonActionPgmUp (),
pConfig->GetButtonPinPgmDown (),
pConfig->GetButtonActionPgmDown (),
pConfig->GetButtonPinTGUp (),
pConfig->GetButtonActionTGUp (),
pConfig->GetButtonPinTGDown (),
pConfig->GetButtonActionTGDown (),
pConfig->GetDoubleClickTimeout (),
pConfig->GetLongPressTimeout (),
pConfig->GetMIDIButtonNotes (),
pConfig->GetMIDIButtonPrev (),
pConfig->GetMIDIButtonActionPrev (),
pConfig->GetMIDIButtonNext (),
pConfig->GetMIDIButtonActionNext (),
pConfig->GetMIDIButtonBack (),
pConfig->GetMIDIButtonActionBack (),
pConfig->GetMIDIButtonSelect (),
pConfig->GetMIDIButtonActionSelect (),
pConfig->GetMIDIButtonHome (),
pConfig->GetMIDIButtonActionHome (),
pConfig->GetMIDIButtonPgmUp (),
pConfig->GetMIDIButtonActionPgmUp (),
pConfig->GetMIDIButtonPgmDown (),
pConfig->GetMIDIButtonActionPgmDown (),
pConfig->GetMIDIButtonTGUp (),
pConfig->GetMIDIButtonActionTGUp (),
pConfig->GetMIDIButtonTGDown (),
pConfig->GetMIDIButtonActionTGDown ()
);
assert (m_pUIButtons);
m_pUIButtons->Initialize ();
m_pUIButtons->RegisterEventHandler (UIButtonsEventStub, this);
UISetMIDIButtonChannel (pConfig->GetMIDIButtonCh ());
}
void CUserInterface::Process (void)
{
if (m_pLCDBuffered)
@ -427,7 +429,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)
{
@ -442,7 +444,7 @@ void CUserInterface::UIMIDICmdHandler (unsigned nMidiCh, unsigned nMidiCmd, unsi
if (m_pUIButtons)
{
m_pUIButtons->BtnMIDICmdHandler (nMidiCmd, nMidiData1, nMidiData2);
m_pUIButtons->BtnMIDICmdHandler (nMidiType, nMidiData1, nMidiData2);
}
}

@ -57,6 +57,8 @@ public:
// 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

Loading…
Cancel
Save