add DAW Controller support for various Arturia controllers

MiniLab 3
Keylab Essential
KeyLab Essential mk3
KeyLab mkII

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

Tested on a Arturia MiniLab 3 and KeyLab mkII

Keylab Essential and Keylab Essential mk3 is not tested
pull/743/head
Gergo Koteles 5 months ago
parent 98e9815c47
commit b99658c63b
  1. 3
      src/Makefile
  2. 8
      src/common.h
  3. 7
      src/config.cpp
  4. 4
      src/config.h
  5. 1648
      src/dawcontroller.cpp
  6. 54
      src/dawcontroller.h
  7. 219
      src/mididevice.cpp
  8. 68
      src/mididevice.h
  9. 46
      src/midikeyboard.cpp
  10. 12
      src/midikeyboard.h
  11. 38
      src/minidexed.cpp
  12. 6
      src/minidexed.h
  13. 3
      src/minidexed.ini
  14. 16
      src/uibuttons.cpp
  15. 253
      src/uimenu.cpp
  16. 38
      src/uimenu.h
  17. 15
      src/userinterface.cpp
  18. 4
      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

@ -2,6 +2,8 @@
#ifndef _common_h
#define _common_h
#define ARRAY_LENGTH(x) (sizeof(x) / sizeof((x)[0]))
inline long maplong(long x, long in_min, long in_max, long out_min, long out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
@ -16,6 +18,12 @@ inline float32_t mapfloat(int val, int in_min, int in_max, float32_t out_min, fl
return (val - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
inline long mapfloatr(int val, int in_min, int in_max, float32_t out_min, float32_t out_max)
{
return lround((val - in_min) * (out_max - out_min) / (in_max - in_min) + out_min);
}
#define constrain(amt, low, high) ({ \
__typeof__(amt) _amt = (amt); \
__typeof__(low) _low = (low); \

@ -205,6 +205,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);
@ -751,6 +753,11 @@ const char *CConfig::GetMIDIButtonActionTGDown (void) const
return m_MIDIButtonActionTGDown.c_str();
}
bool CConfig::GetDAWControllerEnabled (void) const
{
return m_bDAWControllerEnabled;
}
bool CConfig::GetEncoderEnabled (void) const
{
return m_bEncoderEnabled;

@ -240,6 +240,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;
@ -371,6 +373,8 @@ private:
unsigned m_nMIDIButtonTGUp;
unsigned m_nMIDIButtonTGDown;
bool m_bDAWControllerEnabled;
bool m_bEncoderEnabled;
unsigned m_nEncoderPinClock;
unsigned m_nEncoderPinData;

File diff suppressed because it is too large Load Diff

@ -0,0 +1,54 @@
// MiniDexed - Dexed FM synthesizer for bare metal Raspberry Pi
// Copyright (C) 2024 The MiniDexed Team
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#ifndef _dawcontroller_h
#define _dawcontroller_h
#include <circle/types.h>
#include "uimenu.h"
class CMIDIKeyboard;
class CMiniDexed;
class CDAWConnection;
class CConfig;
class CUserInterface;
class CDAWController
{
public:
CDAWController (CMiniDexed *pSynthesizer, CMIDIKeyboard *pKeyboard, CConfig *pConfig, CUserInterface *pUI);
~CDAWController (void);
void OnConnect (void);
void MIDISysexHandler (u8 *pPacket, unsigned nLength, unsigned nCable);
void DisplayWrite (const char *pMenu, const char *pParam, const char *pValue,
bool bArrowDown, bool bArrowUp);
void UpdateState (void);
void UpdateMenu (CUIMenu::TCPageType Type, s8 ucPage, u8 ucOP, u8 ucTG);
void MIDIListener (u8 ucCable, u8 ucChannel, u8 ucType, u8 ucP1, u8 ucP2);
private:
CMiniDexed *m_pSynthesizer;
CMIDIKeyboard *m_pKeyboard;
CConfig *m_pConfig;
CUserInterface *m_pUI;
CDAWConnection *m_pDAWConnection;
};
#endif

@ -22,6 +22,7 @@
//
#include <circle/logger.h>
#include <circle/timer.h>
#include "mididevice.h"
#include "minidexed.h"
#include "config.h"
@ -31,32 +32,6 @@
LOGMODULE ("mididevice");
#define MIDI_NOTE_OFF 0b1000
#define MIDI_NOTE_ON 0b1001
#define MIDI_AFTERTOUCH 0b1010 // TODO
#define MIDI_CHANNEL_AFTERTOUCH 0b1101 // right now Synth_Dexed just manage Channel Aftertouch not Polyphonic AT -> 0b1010
#define MIDI_CONTROL_CHANGE 0b1011
#define MIDI_CC_BANK_SELECT_MSB 0
#define MIDI_CC_MODULATION 1
#define MIDI_CC_BREATH_CONTROLLER 2
#define MIDI_CC_FOOT_PEDAL 4
#define MIDI_CC_PORTAMENTO_TIME 5
#define MIDI_CC_VOLUME 7
#define MIDI_CC_PAN_POSITION 10
#define MIDI_CC_BANK_SELECT_LSB 32
#define MIDI_CC_BANK_SUSTAIN 64
#define MIDI_CC_PORTAMENTO 65
#define MIDI_CC_SOSTENUTO 66
#define MIDI_CC_HOLD2 69
#define MIDI_CC_RESONANCE 71
#define MIDI_CC_FREQUENCY_CUTOFF 74
#define MIDI_CC_REVERB_LEVEL 91
#define MIDI_CC_DETUNE_LEVEL 94
#define MIDI_CC_ALL_SOUND_OFF 120
#define MIDI_CC_ALL_NOTES_OFF 123
#define MIDI_PROGRAM_CHANGE 0b1100
#define MIDI_PITCH_BEND 0b1110
// MIDI "System" level (i.e. all TG) custom CC maps
// Note: Even if number of TGs is not 8, there are only 8
// available to be used in the mappings here.
@ -82,7 +57,8 @@ CMIDIDevice::TDeviceMap CMIDIDevice::s_DeviceMap;
CMIDIDevice::CMIDIDevice (CMiniDexed *pSynthesizer, CConfig *pConfig, CUserInterface *pUI)
: m_pSynthesizer (pSynthesizer),
m_pConfig (pConfig),
m_pUI (pUI)
m_pUI (pUI),
m_pRouteMap ()
{
for (unsigned nTG = 0; nTG < CConfig::AllToneGenerators; nTG++)
{
@ -218,10 +194,21 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
m_MIDISpinLock.Acquire ();
u8 ucCable = nCable;
u8 ucStatus = pMessage[0];
u8 ucChannel = ucStatus & 0x0F;
u8 ucType = ucStatus >> 4;
u8 ucP1 = pMessage[1];
u8 ucP2 = nLength >= 3 ? pMessage[2] : 0xFF;
bool bSkip = false;
if (m_pRouteMap)
GetRoutedMIDI (m_pRouteMap, this, &ucCable, &ucChannel, &ucType, &ucP1, &ucP2, &bSkip);
if (bSkip)
{
// skip (and release mutex at the end)
}
// GLOBAL MIDI SYSEX
//
// Master Volume is set using a MIDI SysEx message as follows:
@ -240,7 +227,7 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
// Need to scale the volume parameter to fit
// a 14-bit value: 0..16383
// and then split into LSB/MSB.
if (nLength == 8 &&
else if (nLength == 8 &&
pMessage[0] == MIDI_SYSTEM_EXCLUSIVE_BEGIN &&
pMessage[1] == 0x7F &&
pMessage[2] == 0x7F &&
@ -269,13 +256,13 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
{
if ((ucChannel == nPerfCh) || (nPerfCh == OmniMode))
{
if (pMessage[1] == MIDI_CC_BANK_SELECT_MSB)
if (ucP1 == MIDI_CC_BANK_SELECT_MSB)
{
m_pSynthesizer->BankSelectMSBPerformance (pMessage[2]);
m_pSynthesizer->BankSelectMSBPerformance (ucP2);
}
else if (pMessage[1] == MIDI_CC_BANK_SELECT_LSB)
else if (ucP1 == MIDI_CC_BANK_SELECT_LSB)
{
m_pSynthesizer->BankSelectLSBPerformance (pMessage[2]);
m_pSynthesizer->BankSelectLSBPerformance (ucP2);
}
else
{
@ -285,7 +272,7 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
}
if (nLength == 3)
{
m_pUI->UIMIDICmdHandler (ucChannel, ucStatus & 0xF0, pMessage[1], pMessage[2]);
m_pUI->UIMIDICmdHandler (ucChannel, ucType, ucP1, ucP2);
}
break;
@ -295,7 +282,7 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
{
break;
}
m_pUI->UIMIDICmdHandler (ucChannel, ucStatus & 0xF0, pMessage[1], pMessage[2]);
m_pUI->UIMIDICmdHandler (ucChannel, ucType, ucP1, ucP2);
break;
case MIDI_PROGRAM_CHANGE:
@ -307,7 +294,7 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
if ((ucChannel == nPerfCh) || (nPerfCh == OmniMode))
{
//printf("Performance Select Channel %d\n", nPerfCh);
m_pSynthesizer->ProgramChangePerformance (pMessage[1]);
m_pSynthesizer->ProgramChangePerformance (ucP1);
}
}
}
@ -345,17 +332,17 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
break;
}
if (pMessage[2] > 0)
if (ucP2 > 0)
{
if (pMessage[2] <= 127)
if (ucP2 <= 127)
{
m_pSynthesizer->keydown (pMessage[1],
pMessage[2], nTG);
m_pSynthesizer->keydown (ucP1,
ucP2, nTG);
}
}
else
{
m_pSynthesizer->keyup (pMessage[1], nTG);
m_pSynthesizer->keyup (ucP1, nTG);
}
break;
@ -365,12 +352,12 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
break;
}
m_pSynthesizer->keyup (pMessage[1], nTG);
m_pSynthesizer->keyup (ucP1, nTG);
break;
case MIDI_CHANNEL_AFTERTOUCH:
m_pSynthesizer->setAftertouch (pMessage[1], nTG);
m_pSynthesizer->setAftertouch (ucP1, nTG);
m_pSynthesizer->ControllersRefresh (nTG);
break;
@ -380,85 +367,85 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
break;
}
switch (pMessage[1])
switch (ucP1)
{
case MIDI_CC_MODULATION:
m_pSynthesizer->setModWheel (pMessage[2], nTG);
m_pSynthesizer->setModWheel (ucP2, nTG);
m_pSynthesizer->ControllersRefresh (nTG);
break;
case MIDI_CC_FOOT_PEDAL:
m_pSynthesizer->setFootController (pMessage[2], nTG);
m_pSynthesizer->setFootController (ucP2, nTG);
m_pSynthesizer->ControllersRefresh (nTG);
break;
case MIDI_CC_PORTAMENTO_TIME:
m_pSynthesizer->setPortamentoTime (maplong (pMessage[2], 0, 127, 0, 99), nTG);
m_pSynthesizer->setPortamentoTime (maplong (ucP2, 0, 127, 0, 99), nTG);
break;
case MIDI_CC_BREATH_CONTROLLER:
m_pSynthesizer->setBreathController (pMessage[2], nTG);
m_pSynthesizer->setBreathController (ucP2, nTG);
m_pSynthesizer->ControllersRefresh (nTG);
break;
case MIDI_CC_VOLUME:
m_pSynthesizer->SetVolume (pMessage[2], nTG);
m_pSynthesizer->SetVolume (ucP2, nTG);
break;
case MIDI_CC_PAN_POSITION:
m_pSynthesizer->SetPan (pMessage[2], nTG);
m_pSynthesizer->SetPan (ucP2, nTG);
break;
case MIDI_CC_BANK_SELECT_MSB:
m_pSynthesizer->BankSelectMSB (pMessage[2], nTG);
m_pSynthesizer->BankSelectMSB (ucP2, nTG);
break;
case MIDI_CC_BANK_SELECT_LSB:
m_pSynthesizer->BankSelectLSB (pMessage[2], nTG);
m_pSynthesizer->BankSelectLSB (ucP2, nTG);
break;
case MIDI_CC_BANK_SUSTAIN:
m_pSynthesizer->setSustain (pMessage[2] >= 64, nTG);
m_pSynthesizer->setSustain (ucP2 >= 64, nTG);
break;
case MIDI_CC_SOSTENUTO:
m_pSynthesizer->setSostenuto (pMessage[2] >= 64, nTG);
m_pSynthesizer->setSostenuto (ucP2 >= 64, nTG);
break;
case MIDI_CC_PORTAMENTO:
m_pSynthesizer->setPortamentoMode (pMessage[2] >= 64, nTG);
m_pSynthesizer->setPortamentoMode (ucP2 >= 64, nTG);
break;
case MIDI_CC_HOLD2:
m_pSynthesizer->setHoldMode (pMessage[2] >= 64, nTG);
m_pSynthesizer->setHoldMode (ucP2 >= 64, nTG);
break;
case MIDI_CC_RESONANCE:
m_pSynthesizer->SetResonance (maplong (pMessage[2], 0, 127, 0, 99), nTG);
m_pSynthesizer->SetResonance (maplong (ucP2, 0, 127, 0, 99), nTG);
break;
case MIDI_CC_FREQUENCY_CUTOFF:
m_pSynthesizer->SetCutoff (maplong (pMessage[2], 0, 127, 0, 99), nTG);
m_pSynthesizer->SetCutoff (maplong (ucP2, 0, 127, 0, 99), nTG);
break;
case MIDI_CC_REVERB_LEVEL:
m_pSynthesizer->SetReverbSend (maplong (pMessage[2], 0, 127, 0, 99), nTG);
m_pSynthesizer->SetReverbSend (maplong (ucP2, 0, 127, 0, 99), nTG);
break;
case MIDI_CC_DETUNE_LEVEL:
if (pMessage[2] == 0)
if (ucP2 == 0)
{
// "0 to 127, with 0 being no celeste (detune) effect applied at all."
m_pSynthesizer->SetMasterTune (0, nTG);
}
else
{
m_pSynthesizer->SetMasterTune (maplong (pMessage[2], 1, 127, -99, 99), nTG);
m_pSynthesizer->SetMasterTune (maplong (ucP2, 1, 127, -99, 99), nTG);
}
break;
case MIDI_CC_ALL_SOUND_OFF:
m_pSynthesizer->panic (pMessage[2], nTG);
m_pSynthesizer->panic (ucP2, nTG);
break;
case MIDI_CC_ALL_NOTES_OFF:
@ -467,7 +454,7 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
// "Receivers should ignore an All Notes Off message while Omni is on (Modes 1 & 2)"
if (!m_pConfig->GetIgnoreAllNotesOff () && m_ChannelMap[nTG] != OmniMode)
{
m_pSynthesizer->notesOff (pMessage[2], nTG);
m_pSynthesizer->notesOff (ucP2, nTG);
}
break;
@ -477,7 +464,7 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
// so it is possible to break out of the main TG loop too.
// Note: We handle this here so we get the TG MIDI channel checking.
if (!bSystemCCChecked) {
bSystemCCHandled = HandleMIDISystemCC(pMessage[1], pMessage[2]);
bSystemCCHandled = HandleMIDISystemCC(ucP1, ucP2);
bSystemCCChecked = true;
}
break;
@ -488,7 +475,7 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
// do program change only if enabled in config and not in "Performance Select Channel" mode
if( m_pConfig->GetMIDIRXProgramChange() && ( m_pSynthesizer->GetPerformanceSelectChannel() == Disabled) ) {
//printf("Program Change to %d (%d)\n", ucChannel, m_pSynthesizer->GetPerformanceSelectChannel());
m_pSynthesizer->ProgramChange (pMessage[1], nTG);
m_pSynthesizer->ProgramChange (ucP1, nTG);
}
break;
@ -498,8 +485,8 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
break;
}
s16 nValue = pMessage[1];
nValue |= (s16) pMessage[2] << 7;
s16 nValue = ucP1;
nValue |= (s16) ucP2 << 7;
nValue -= 0x2000;
m_pSynthesizer->setPitchbend (nValue, nTG);
@ -511,6 +498,9 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
}
}
}
if (m_pRouteMap)
MIDIListener(ucCable, ucChannel, ucType, ucP1, ucP2);
}
m_MIDISpinLock.Release ();
}
@ -572,6 +562,11 @@ bool CMIDIDevice::HandleMIDISystemCC(const u8 ucCC, const u8 ucCCval)
return false;
}
void CMIDIDevice::SetRouteMap (TMIDIRoute *pRouteMap)
{
m_pRouteMap = pRouteMap;
}
void CMIDIDevice::HandleSystemExclusive(const uint8_t* pMessage, const size_t nLength, const unsigned nCable, const uint8_t nTG)
{
int16_t sysex_return;
@ -700,6 +695,11 @@ void CMIDIDevice::HandleSystemExclusive(const uint8_t* pMessage, const size_t nL
}
}
void CMIDIDevice::MIDIListener (u8 ucCable, u8 ucChannel, u8 ucType, u8 ucP1, u8 ucP2)
{
}
void CMIDIDevice::SendSystemExclusiveVoice(uint8_t nVoice, const unsigned nCable, uint8_t nTG)
{
uint8_t voicedump[163];
@ -716,3 +716,90 @@ void CMIDIDevice::SendSystemExclusiveVoice(uint8_t nVoice, const unsigned nCable
// LOGDBG("Send SYSEX voice dump %u to \"%s\"",nVoice,Iterator->first.c_str());
}
}
void CMIDIDevice::s_HandleTimerTimeout(TKernelTimerHandle hTimer, void *pParam, void *pContext)
{
CMIDIDevice *pDevice = static_cast<CMIDIDevice*>(pParam);
TMIDIRoute *pRoute = static_cast<TMIDIRoute*>(pContext);
TMIDIRoute *pTarget = pRoute + pRoute->ucTimerTarget;
pRoute->hTimer = 0;
pRoute->bGroupActive = false;
pRoute->bGroupHold = false;
if (!pTarget->bSkip)
pDevice->MIDIListener (pTarget->ucSCable, pTarget->ucDCh, pTarget->ucDType, pTarget->ucDP1, pTarget->ucDP2);
}
void GetRoutedMIDI (TMIDIRoute *pRouteMap, CMIDIDevice *pDevice, u8 *pCable, u8 *pCh, u8 *pType, u8 *pP1, u8 *pP2, bool *bSkip)
{
assert (pRouteMap);
for (TMIDIRoute *r = pRouteMap; r->ucSCable != 0xFF ; r++)
{
if (r->ucSCable == *pCable &&
(r->ucSCh == *pCh || r->ucSCh >= 16) &&
(r->ucSType == *pType || r->ucSType >= 16) &&
(r->ucSP1 == *pP1 || r->ucSP1 > 127) &&
(r->ucSP2 == *pP2 || r->ucSP2 == 0xFF ||
r->ucSP2 == TMIDIRoute::LtCenter && *pP2 < 64 ||
r->ucSP2 == TMIDIRoute::GtCenter && *pP2 > 64)
)
{
if (r->usTimerExpire)
r->hTimer = CTimer::Get ()->StartKernelTimer (MSEC2HZ(r->usTimerExpire), CMIDIDevice::s_HandleTimerTimeout, pDevice, r);
if (r->usTimerExpire || r->bGroupHead)
r->bGroupActive = true;
if (r->bSkip) {
*bSkip = true;
return;
}
if (r->bGroup) {
TMIDIRoute *parent = r - 1;
for (; parent > pRouteMap && parent->bGroup; parent--);
if (parent->hTimer) {
CTimer::Get ()->CancelKernelTimer (parent->hTimer);
parent->hTimer = 0;
}
if (!r->bGroupHold)
parent->bGroupHold = false;
if (!parent->bGroupActive && !parent->bGroupHold) {
// skip at the end if not captured by other routes
*bSkip = true;
continue;
}
// hold the group for bGroupHold routes
if (r->bGroupHold)
parent->bGroupHold = true;
parent->bGroupActive = false;
}
*pCh = r->ucDCh;
*pType = r->ucDType;
if (r->ucDP1 <= 127)
*pP1 = r->ucDP1;
if (r->ucDP1 == TMIDIRoute::P2)
*pP1 = *pP2;
if (r->ucDP2 <= 127)
*pP2 = r->ucDP2;
if (r->bToggle)
r->ucDP2 = r->ucDP2 ? 0x0 : 0x7F;
*bSkip = false;
return;
}
}
}
TMIDIRoute::~TMIDIRoute ()
{
if (hTimer)
CTimer::Get ()->CancelKernelTimer (hTimer);
hTimer = 0;
}

@ -33,7 +33,67 @@
#define MAX_DX7_SYSEX_LENGTH 4104
#define MAX_MIDI_MESSAGE MAX_DX7_SYSEX_LENGTH
#define MIDI_NOTE_OFF 0b1000
#define MIDI_NOTE_ON 0b1001
#define MIDI_AFTERTOUCH 0b1010 // TODO
#define MIDI_CHANNEL_AFTERTOUCH 0b1101 // right now Synth_Dexed just manage Channel Aftertouch not Polyphonic AT -> 0b1010
#define MIDI_CONTROL_CHANGE 0b1011
#define MIDI_CC_BANK_SELECT_MSB 0
#define MIDI_CC_MODULATION 1
#define MIDI_CC_BREATH_CONTROLLER 2
#define MIDI_CC_FOOT_PEDAL 4
#define MIDI_CC_PORTAMENTO_TIME 5
#define MIDI_CC_VOLUME 7
#define MIDI_CC_PAN_POSITION 10
#define MIDI_CC_BANK_SELECT_LSB 32
#define MIDI_CC_BANK_SUSTAIN 64
#define MIDI_CC_PORTAMENTO 65
#define MIDI_CC_SOSTENUTO 66
#define MIDI_CC_HOLD2 69
#define MIDI_CC_RESONANCE 71
#define MIDI_CC_FREQUENCY_CUTOFF 74
#define MIDI_CC_REVERB_LEVEL 91
#define MIDI_CC_DETUNE_LEVEL 94
#define MIDI_CC_ALL_SOUND_OFF 120
#define MIDI_CC_ALL_NOTES_OFF 123
#define MIDI_PROGRAM_CHANGE 0b1100
#define MIDI_PITCH_BEND 0b1110
class CMiniDexed;
class CMIDIDevice;
struct TMIDIRoute
{
~TMIDIRoute ();
enum TRouteOP
{
P2 = 0x82,
LtCenter = 0x90,
GtCenter = 0x91,
};
u8 ucSCable;
u8 ucSCh;
u8 ucSType;
u8 ucSP1;
u8 ucSP2;
u8 ucDCh;
u8 ucDType;
u8 ucDP1;
u8 ucDP2;
u8 ucTimerTarget;
unsigned usTimerExpire;
TKernelTimerHandle hTimer;
bool bSkip;
bool bToggle;
bool bGroup;
bool bGroupHead;
bool bGroupActive;
bool bGroupHold; // hold flag for GroupHeads, or set hold the group
};
void GetRoutedMIDI (TMIDIRoute *pRouteMap, CMIDIDevice *pDevice, u8 *pCable, u8 *pChannel, u8 *pType, u8 *pP1, u8 *pP2, bool *bSkip);
class CMIDIDevice
{
@ -53,14 +113,20 @@ public:
void SetChannel (u8 ucChannel, unsigned nTG);
u8 GetChannel (unsigned nTG) const;
void SetRouteMap (TMIDIRoute *pRouteMap);
virtual void Send (const u8 *pMessage, size_t nLength, unsigned nCable = 0) {}
virtual void SendSystemExclusiveVoice(uint8_t nVoice, const unsigned nCable, uint8_t nTG);
static void s_HandleTimerTimeout(TKernelTimerHandle hTimer, void *pParam, void *pContext);
protected:
void MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsigned nCable = 0);
void AddDevice (const char *pDeviceName);
void HandleSystemExclusive(const uint8_t* pMessage, const size_t nLength, const unsigned nCable, const uint8_t nTG);
virtual void MIDIListener (u8 ucCable, u8 ucChannel, u8 ucType, u8 ucP1, u8 ucP2);
private:
bool HandleMIDISystemCC(const u8 ucCC, const u8 ucCCval);
@ -78,6 +144,8 @@ private:
std::string m_DeviceName;
TMIDIRoute *m_pRouteMap;
typedef std::unordered_map<std::string, CMIDIDevice *> TDeviceMap;
static TDeviceMap s_DeviceMap;

@ -29,15 +29,20 @@ CMIDIKeyboard::CMIDIKeyboard (CMiniDexed *pSynthesizer, CConfig *pConfig, CUserI
: CMIDIDevice (pSynthesizer, pConfig, pUI),
m_nSysExIdx (0),
m_nInstance (nInstance),
m_pMIDIDevice (0)
m_pMIDIDevice (0),
m_pDAWController (0)
{
m_DeviceName.Format ("umidi%u", nInstance+1);
AddDevice (m_DeviceName);
if (pConfig->GetDAWControllerEnabled ())
m_pDAWController = new CDAWController (pSynthesizer, this, pConfig, pUI);
}
CMIDIKeyboard::~CMIDIKeyboard (void)
{
delete m_pDAWController;
}
void CMIDIKeyboard::Process (boolean bPlugAndPlayUpdated)
@ -69,6 +74,9 @@ void CMIDIKeyboard::Process (boolean bPlugAndPlayUpdated)
m_pMIDIDevice->RegisterPacketHandler (MIDIPacketHandler, this);
m_pMIDIDevice->RegisterRemovedHandler (DeviceRemovedHandler, this);
if (m_pDAWController)
m_pDAWController->OnConnect();
}
}
}
@ -85,6 +93,13 @@ void CMIDIKeyboard::Send (const u8 *pMessage, size_t nLength, unsigned nCable)
m_SendQueue.push (Entry);
}
void CMIDIKeyboard::SendDebounce (const u8 *pMessage, size_t nLength, unsigned nCable)
{
TSendQueueEntry Entry = m_SendQueue.back ();
if (Entry.nLength != nLength || Entry.nCable != nCable || memcmp (Entry.pMessage, pMessage, nLength) != 0)
Send (pMessage, nLength, nCable);
}
// Most packets will be passed straight onto the main MIDI message handler
// but SysEx messages are multiple USB packets and so will need building up
// before parsing.
@ -122,6 +137,10 @@ void CMIDIKeyboard::USBMIDIMessageHandler (u8 *pPacket, unsigned nLength, unsign
m_SysEx[m_nSysExIdx++] = pPacket[i];
//printf ("SysEx End Idx=%d\n", m_nSysExIdx);
MIDIMessageHandler (m_SysEx, m_nSysExIdx, nCable);
if (m_pDAWController)
m_pDAWController->MIDISysexHandler (m_SysEx, m_nSysExIdx, nCable);
// Reset ready for next time
m_nSysExIdx = 0;
}
@ -160,3 +179,28 @@ void CMIDIKeyboard::DeviceRemovedHandler (CDevice *pDevice, void *pContext)
pThis->m_pMIDIDevice = 0;
}
void CMIDIKeyboard::DisplayWrite (const char *pMenu, const char *pParam, const char *pValue,
bool bArrowDown, bool bArrowUp)
{
if (m_pMIDIDevice && m_pDAWController)
m_pDAWController->DisplayWrite (pMenu, pParam, pValue, bArrowDown, bArrowUp);
}
void CMIDIKeyboard::UpdateDAWState (void)
{
if (m_pMIDIDevice && m_pDAWController)
m_pDAWController->UpdateState ();
}
void CMIDIKeyboard::UpdateDAWMenu (CUIMenu::TCPageType Type, s8 ucPage, u8 ucOP, u8 ucTG)
{
if (m_pMIDIDevice && m_pDAWController)
m_pDAWController->UpdateMenu (Type, ucPage, ucOP, ucTG);
}
void CMIDIKeyboard::MIDIListener (u8 ucCable, u8 ucChannel, u8 ucType, u8 ucP1, u8 ucP2)
{
if (m_pDAWController)
m_pDAWController->MIDIListener (ucCable, ucChannel, ucType, ucP1, ucP2);
}

@ -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>
@ -44,6 +45,13 @@ 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 UpdateDAWState (void);
void UpdateDAWMenu (CUIMenu::TCPageType Type, s8 ucPage, u8 ucOP, u8 ucTG);
private:
static void MIDIPacketHandler (unsigned nCable, u8 *pPacket, unsigned nLength, unsigned nDevice, void *pParam);
@ -51,6 +59,8 @@ private:
void USBMIDIMessageHandler (u8 *pPacket, unsigned nLength, unsigned nCable, unsigned nDevice);
void MIDIListener (u8 ucCable, u8 ucChannel, u8 ucType, u8 ucP1, u8 ucP2) override;
private:
struct TSendQueueEntry
{
@ -68,6 +78,8 @@ private:
CUSBMIDIDevice * volatile m_pMIDIDevice;
std::queue<TSendQueueEntry> m_SendQueue;
CDAWController *m_pDAWController;
};
#endif

@ -942,6 +942,7 @@ void CMiniDexed::SetParameter (TParameter Parameter, int nValue)
assert (m_pTG[nTG]);
m_pTG[nTG]->setCompressor (!!nValue);
}
m_UI.ParameterChanged ();
break;
case ParameterReverbEnable:
@ -949,6 +950,7 @@ void CMiniDexed::SetParameter (TParameter Parameter, int nValue)
m_ReverbSpinLock.Acquire ();
reverb->set_bypass (!nValue);
m_ReverbSpinLock.Release ();
m_UI.ParameterChanged ();
break;
case ParameterReverbSize:
@ -956,6 +958,7 @@ void CMiniDexed::SetParameter (TParameter Parameter, int nValue)
m_ReverbSpinLock.Acquire ();
reverb->size (nValue / 99.0f);
m_ReverbSpinLock.Release ();
m_UI.ParameterChanged ();
break;
case ParameterReverbHighDamp:
@ -963,6 +966,7 @@ void CMiniDexed::SetParameter (TParameter Parameter, int nValue)
m_ReverbSpinLock.Acquire ();
reverb->hidamp (nValue / 99.0f);
m_ReverbSpinLock.Release ();
m_UI.ParameterChanged ();
break;
case ParameterReverbLowDamp:
@ -970,6 +974,7 @@ void CMiniDexed::SetParameter (TParameter Parameter, int nValue)
m_ReverbSpinLock.Acquire ();
reverb->lodamp (nValue / 99.0f);
m_ReverbSpinLock.Release ();
m_UI.ParameterChanged ();
break;
case ParameterReverbLowPass:
@ -977,6 +982,7 @@ void CMiniDexed::SetParameter (TParameter Parameter, int nValue)
m_ReverbSpinLock.Acquire ();
reverb->lowpass (nValue / 99.0f);
m_ReverbSpinLock.Release ();
m_UI.ParameterChanged ();
break;
case ParameterReverbDiffusion:
@ -984,6 +990,7 @@ void CMiniDexed::SetParameter (TParameter Parameter, int nValue)
m_ReverbSpinLock.Acquire ();
reverb->diffusion (nValue / 99.0f);
m_ReverbSpinLock.Release ();
m_UI.ParameterChanged ();
break;
case ParameterReverbLevel:
@ -991,6 +998,7 @@ void CMiniDexed::SetParameter (TParameter Parameter, int nValue)
m_ReverbSpinLock.Acquire ();
reverb->level (nValue / 99.0f);
m_ReverbSpinLock.Release ();
m_UI.ParameterChanged ();
break;
case ParameterPerformanceSelectChannel:
@ -1144,6 +1152,7 @@ void CMiniDexed::SetVoiceParameter (uint8_t uchOffset, uint8_t uchValue, unsigne
}
m_pTG[nTG]->setOPAll (m_uchOPMask[nTG]);
m_UI.ParameterChanged ();
return;
}
@ -1155,6 +1164,7 @@ void CMiniDexed::SetVoiceParameter (uint8_t uchOffset, uint8_t uchValue, unsigne
assert (uchOffset < 156);
m_pTG[nTG]->setVoiceDataElement (uchOffset, uchValue);
m_UI.ParameterChanged ();
}
uint8_t CMiniDexed::GetVoiceParameter (uint8_t uchOffset, unsigned nOP, unsigned nTG)
@ -1808,6 +1818,33 @@ void CMiniDexed::setMasterVolume (float32_t vol)
nMasterVolume=vol;
}
void CMiniDexed::DisplayWrite (const char *pMenu, const char *pParam, const char *pValue,
bool bArrowDown, bool bArrowUp)
{
m_UI.DisplayWrite (pMenu, pParam, pValue, bArrowDown, bArrowUp);
for (unsigned i = 0; i < CConfig::MaxUSBMIDIDevices; i++)
{
m_pMIDIKeyboard[i]->DisplayWrite (pMenu, pParam, pValue, bArrowDown, bArrowUp);
}
}
void CMiniDexed::UpdateDAWState ()
{
for (unsigned i = 0; i < CConfig::MaxUSBMIDIDevices; i++)
{
m_pMIDIKeyboard[i]->UpdateDAWState ();
}
}
void CMiniDexed::UpdateDAWMenu (CUIMenu::TCPageType Type, s8 ucPage, u8 ucOP, u8 ucTG)
{
for (unsigned i = 0; i < CConfig::MaxUSBMIDIDevices; i++)
{
m_pMIDIKeyboard[i]->UpdateDAWMenu (Type, ucPage, ucOP, ucTG);
}
}
std::string CMiniDexed::GetPerformanceFileName(unsigned nID)
{
return m_PerformanceConfig.GetPerformanceFileName(nID);
@ -1971,6 +2008,7 @@ void CMiniDexed::LoadPerformanceParameters(void)
m_pTG[nTG]->loadVoiceParameters(tVoiceData);
}
setMonoMode(m_PerformanceConfig.GetMonoMode(nTG) ? 1 : 0, nTG);
setEnabled(1, nTG);
SetReverbSend (m_PerformanceConfig.GetReverbSend (nTG), nTG);
setModWheelRange (m_PerformanceConfig.GetModulationWheelRange (nTG), nTG);

@ -233,6 +233,12 @@ public:
void setMasterVolume (float32_t vol);
void DisplayWrite (const char *pMenu, const char *pParam, const char *pValue,
bool bArrowDown, bool bArrowUp);
void UpdateDAWState ();
void UpdateDAWMenu (CUIMenu::TCPageType Type, s8 ucPage, u8 ucOP, u8 ucTG);
private:
int16_t ApplyNoteLimits (int16_t pitch, unsigned nTG); // returns < 0 to ignore note
uint8_t m_uchOPMask[CConfig::AllToneGenerators];

@ -151,6 +151,9 @@ MIDIButtonActionTGUp=click
MIDIButtonTGDown=56
MIDIButtonActionTGDown=click
# DAW Controller (Arturia MiniLab 3, KeyLab Essential, KeyLab Essential 3, Keylab mkII)
DAWControllerEnabled=0
# KY-040 Rotary Encoder
EncoderEnabled=1
EncoderPinClock=10

@ -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>
@ -544,21 +545,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 {
@ -567,9 +567,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) {

@ -69,29 +69,29 @@ const CUIMenu::TMenuItem CUIMenu::s_MainMenu[] =
const CUIMenu::TMenuItem CUIMenu::s_TGMenu[] =
{
{"Voice", EditProgramNumber},
{"Voice", EditProgramNumber, 0, CMiniDexed::TGParameterProgram, "Voice"},
{"Bank", EditVoiceBankNumber},
{"Volume", EditTGParameter, 0, CMiniDexed::TGParameterVolume},
{"Volume", EditTGParameter, 0, CMiniDexed::TGParameterVolume, "Vol"},
#ifdef ARM_ALLOW_MULTI_CORE
{"Pan", EditTGParameter, 0, CMiniDexed::TGParameterPan},
{"Pan", EditTGParameter, 0, CMiniDexed::TGParameterPan, "Pan"},
#endif
{"Reverb-Send", EditTGParameter, 0, CMiniDexed::TGParameterReverbSend},
{"Detune", EditTGParameter, 0, CMiniDexed::TGParameterMasterTune},
{"Cutoff", EditTGParameter, 0, CMiniDexed::TGParameterCutoff},
{"Resonance", EditTGParameter, 0, CMiniDexed::TGParameterResonance},
{"Reverb-Send", EditTGParameter, 0, CMiniDexed::TGParameterReverbSend, "RevS"},
{"Detune", EditTGParameter, 0, CMiniDexed::TGParameterMasterTune, "DeT"},
{"Cutoff", EditTGParameter, 0, CMiniDexed::TGParameterCutoff, "Cut"},
{"Resonance", EditTGParameter, 0, CMiniDexed::TGParameterResonance, "Res"},
{"Pitch Bend", MenuHandler, s_EditPitchBendMenu},
{"Portamento", MenuHandler, s_EditPortamentoMenu},
{"Poly/Mono", EditTGParameter, 0, CMiniDexed::TGParameterMonoMode},
{"Poly/Mono", EditTGParameter, 0, CMiniDexed::TGParameterMonoMode, "P/M"},
{"Enabled", EditTGParameter, 0, CMiniDexed::TGParameterEnabled},
{"Modulation", MenuHandler, s_ModulationMenu},
{"Channel", EditTGParameter, 0, CMiniDexed::TGParameterMIDIChannel},
{"Channel", EditTGParameter, 0, CMiniDexed::TGParameterMIDIChannel, "Chan"},
{"Edit Voice", MenuHandler, s_EditVoiceMenu},
{0}
};
const CUIMenu::TMenuItem CUIMenu::s_EffectsMenu[] =
{
{"Compress", EditGlobalParameter, 0, CMiniDexed::ParameterCompressorEnable},
{"Compress", EditGlobalParameter, 0, CMiniDexed::ParameterCompressorEnable, "CrEn"},
#ifdef ARM_ALLOW_MULTI_CORE
{"Reverb", MenuHandler, s_ReverbMenu},
#endif
@ -100,25 +100,25 @@ const CUIMenu::TMenuItem CUIMenu::s_EffectsMenu[] =
const CUIMenu::TMenuItem CUIMenu::s_EditPitchBendMenu[] =
{
{"Bend Range", EditTGParameter2, 0, CMiniDexed::TGParameterPitchBendRange},
{"Bend Step", EditTGParameter2, 0, CMiniDexed::TGParameterPitchBendStep},
{"Bend Range", EditTGParameter2, 0, CMiniDexed::TGParameterPitchBendRange, "PiBR"},
{"Bend Step", EditTGParameter2, 0, CMiniDexed::TGParameterPitchBendStep, "PiBS"},
{0}
};
const CUIMenu::TMenuItem CUIMenu::s_EditPortamentoMenu[] =
{
{"Mode", EditTGParameter2, 0, CMiniDexed::TGParameterPortamentoMode},
{"Glissando", EditTGParameter2, 0, CMiniDexed::TGParameterPortamentoGlissando},
{"Time", EditTGParameter2, 0, CMiniDexed::TGParameterPortamentoTime},
{"Mode", EditTGParameter2, 0, CMiniDexed::TGParameterPortamentoMode, "PorM"},
{"Glissando", EditTGParameter2, 0, CMiniDexed::TGParameterPortamentoGlissando, "PorG"},
{"Time", EditTGParameter2, 0, CMiniDexed::TGParameterPortamentoTime, "PorT"},
{0}
};
const CUIMenu::TMenuItem CUIMenu::s_ModulationMenu[] =
{
{"Mod. Wheel", MenuHandler, s_ModulationMenuParameters, CMiniDexed::TGParameterMWRange},
{"Foot Control", MenuHandler, s_ModulationMenuParameters, CMiniDexed::TGParameterFCRange},
{"Breath Control", MenuHandler, s_ModulationMenuParameters, CMiniDexed::TGParameterBCRange},
{"Aftertouch", MenuHandler, s_ModulationMenuParameters, CMiniDexed::TGParameterATRange},
{"Mod. Wheel", MenuHandler, s_ModulationMenuParameters, CMiniDexed::TGParameterMWRange, "MWRa"},
{"Foot Control", MenuHandler, s_ModulationMenuParameters, CMiniDexed::TGParameterFCRange, "FCRa"},
{"Breath Control", MenuHandler, s_ModulationMenuParameters, CMiniDexed::TGParameterBCRange, "BCRa"},
{"Aftertouch", MenuHandler, s_ModulationMenuParameters, CMiniDexed::TGParameterATRange, "ATRa"},
{0}
};
@ -135,13 +135,13 @@ const CUIMenu::TMenuItem CUIMenu::s_ModulationMenuParameters[] =
const CUIMenu::TMenuItem CUIMenu::s_ReverbMenu[] =
{
{"Enable", EditGlobalParameter, 0, CMiniDexed::ParameterReverbEnable},
{"Size", EditGlobalParameter, 0, CMiniDexed::ParameterReverbSize},
{"High damp", EditGlobalParameter, 0, CMiniDexed::ParameterReverbHighDamp},
{"Low damp", EditGlobalParameter, 0, CMiniDexed::ParameterReverbLowDamp},
{"Low pass", EditGlobalParameter, 0, CMiniDexed::ParameterReverbLowPass},
{"Diffusion", EditGlobalParameter, 0, CMiniDexed::ParameterReverbDiffusion},
{"Level", EditGlobalParameter, 0, CMiniDexed::ParameterReverbLevel},
{"Enable", EditGlobalParameter, 0, CMiniDexed::ParameterReverbEnable, "RvEn"},
{"Size", EditGlobalParameter, 0, CMiniDexed::ParameterReverbSize, "RvS"},
{"High damp", EditGlobalParameter, 0, CMiniDexed::ParameterReverbHighDamp, "RvHD"},
{"Low damp", EditGlobalParameter, 0, CMiniDexed::ParameterReverbLowDamp, "RvLD"},
{"Low pass", EditGlobalParameter, 0, CMiniDexed::ParameterReverbLowPass, "RvLP"},
{"Diffusion", EditGlobalParameter, 0, CMiniDexed::ParameterReverbDiffusion, "RvDi"},
{"Level", EditGlobalParameter, 0, CMiniDexed::ParameterReverbLevel, "RvLv"},
{0}
};
@ -156,53 +156,53 @@ const CUIMenu::TMenuItem CUIMenu::s_EditVoiceMenu[] =
{"OP4", MenuHandler, s_OperatorMenu, 3},
{"OP5", MenuHandler, s_OperatorMenu, 4},
{"OP6", MenuHandler, s_OperatorMenu, 5},
{"Algorithm", EditVoiceParameter, 0, DEXED_ALGORITHM},
{"Feedback", EditVoiceParameter, 0, DEXED_FEEDBACK},
{"P EG Rate 1", EditVoiceParameter, 0, DEXED_PITCH_EG_R1},
{"P EG Rate 2", EditVoiceParameter, 0, DEXED_PITCH_EG_R2},
{"P EG Rate 3", EditVoiceParameter, 0, DEXED_PITCH_EG_R3},
{"P EG Rate 4", EditVoiceParameter, 0, DEXED_PITCH_EG_R4},
{"P EG Level 1",EditVoiceParameter, 0, DEXED_PITCH_EG_L1},
{"P EG Level 2",EditVoiceParameter, 0, DEXED_PITCH_EG_L2},
{"P EG Level 3",EditVoiceParameter, 0, DEXED_PITCH_EG_L3},
{"P EG Level 4",EditVoiceParameter, 0, DEXED_PITCH_EG_L4},
{"Osc Key Sync",EditVoiceParameter, 0, DEXED_OSC_KEY_SYNC},
{"LFO Speed", EditVoiceParameter, 0, DEXED_LFO_SPEED},
{"LFO Delay", EditVoiceParameter, 0, DEXED_LFO_DELAY},
{"LFO PMD", EditVoiceParameter, 0, DEXED_LFO_PITCH_MOD_DEP},
{"LFO AMD", EditVoiceParameter, 0, DEXED_LFO_AMP_MOD_DEP},
{"LFO Sync", EditVoiceParameter, 0, DEXED_LFO_SYNC},
{"LFO Wave", EditVoiceParameter, 0, DEXED_LFO_WAVE},
{"P Mod Sens.", EditVoiceParameter, 0, DEXED_LFO_PITCH_MOD_SENS},
{"Transpose", EditVoiceParameter, 0, DEXED_TRANSPOSE},
{"Algorithm", EditVoiceParameter, 0, DEXED_ALGORITHM, "Alg"},
{"Feedback", EditVoiceParameter, 0, DEXED_FEEDBACK, "FB"},
{"P EG Rate 1", EditVoiceParameter, 0, DEXED_PITCH_EG_R1, "PR1"},
{"P EG Rate 2", EditVoiceParameter, 0, DEXED_PITCH_EG_R2, "PR2"},
{"P EG Rate 3", EditVoiceParameter, 0, DEXED_PITCH_EG_R3, "PR3"},
{"P EG Rate 4", EditVoiceParameter, 0, DEXED_PITCH_EG_R4, "PR4"},
{"P EG Level 1",EditVoiceParameter, 0, DEXED_PITCH_EG_L1, "PL1"},
{"P EG Level 2",EditVoiceParameter, 0, DEXED_PITCH_EG_L2, "PL2"},
{"P EG Level 3",EditVoiceParameter, 0, DEXED_PITCH_EG_L3, "PL3"},
{"P EG Level 4",EditVoiceParameter, 0, DEXED_PITCH_EG_L4, "PL4"},
{"Osc Key Sync",EditVoiceParameter, 0, DEXED_OSC_KEY_SYNC, "OKS"},
{"LFO Speed", EditVoiceParameter, 0, DEXED_LFO_SPEED, "LSP"},
{"LFO Delay", EditVoiceParameter, 0, DEXED_LFO_DELAY, "LDE"},
{"LFO PMD", EditVoiceParameter, 0, DEXED_LFO_PITCH_MOD_DEP, "LPMD"},
{"LFO AMD", EditVoiceParameter, 0, DEXED_LFO_AMP_MOD_DEP, "LAMD"},
{"LFO Sync", EditVoiceParameter, 0, DEXED_LFO_SYNC, "LSYN"},
{"LFO Wave", EditVoiceParameter, 0, DEXED_LFO_WAVE, "LWAV"},
{"P Mod Sens.", EditVoiceParameter, 0, DEXED_LFO_PITCH_MOD_SENS, "LPMS"},
{"Transpose", EditVoiceParameter, 0, DEXED_TRANSPOSE, "TRP"},
{"Name", InputTxt,0 , 3},
{0}
};
const CUIMenu::TMenuItem CUIMenu::s_OperatorMenu[] =
{
{"Output Level",EditOPParameter, 0, DEXED_OP_OUTPUT_LEV},
{"Freq Coarse", EditOPParameter, 0, DEXED_OP_FREQ_COARSE},
{"Freq Fine", EditOPParameter, 0, DEXED_OP_FREQ_FINE},
{"Osc Detune", EditOPParameter, 0, DEXED_OP_OSC_DETUNE},
{"Osc Mode", EditOPParameter, 0, DEXED_OP_OSC_MODE},
{"EG Rate 1", EditOPParameter, 0, DEXED_OP_EG_R1},
{"EG Rate 2", EditOPParameter, 0, DEXED_OP_EG_R2},
{"EG Rate 3", EditOPParameter, 0, DEXED_OP_EG_R3},
{"EG Rate 4", EditOPParameter, 0, DEXED_OP_EG_R4},
{"EG Level 1", EditOPParameter, 0, DEXED_OP_EG_L1},
{"EG Level 2", EditOPParameter, 0, DEXED_OP_EG_L2},
{"EG Level 3", EditOPParameter, 0, DEXED_OP_EG_L3},
{"EG Level 4", EditOPParameter, 0, DEXED_OP_EG_L4},
{"Break Point", EditOPParameter, 0, DEXED_OP_LEV_SCL_BRK_PT},
{"L Key Depth", EditOPParameter, 0, DEXED_OP_SCL_LEFT_DEPTH},
{"R Key Depth", EditOPParameter, 0, DEXED_OP_SCL_RGHT_DEPTH},
{"L Key Scale", EditOPParameter, 0, DEXED_OP_SCL_LEFT_CURVE},
{"R Key Scale", EditOPParameter, 0, DEXED_OP_SCL_RGHT_CURVE},
{"Rate Scaling",EditOPParameter, 0, DEXED_OP_OSC_RATE_SCALE},
{"A Mod Sens.", EditOPParameter, 0, DEXED_OP_AMP_MOD_SENS},
{"K Vel. Sens.",EditOPParameter, 0, DEXED_OP_KEY_VEL_SENS},
{"Enable", EditOPParameter, 0, DEXED_OP_ENABLE},
{"Output Level",EditOPParameter, 0, DEXED_OP_OUTPUT_LEV, "OutL"},
{"Freq Coarse", EditOPParameter, 0, DEXED_OP_FREQ_COARSE, "FrC"},
{"Freq Fine", EditOPParameter, 0, DEXED_OP_FREQ_FINE, "FrF"},
{"Osc Detune", EditOPParameter, 0, DEXED_OP_OSC_DETUNE, "OsDT"},
{"Osc Mode", EditOPParameter, 0, DEXED_OP_OSC_MODE, "OsM"},
{"EG Rate 1", EditOPParameter, 0, DEXED_OP_EG_R1, "R1"},
{"EG Rate 2", EditOPParameter, 0, DEXED_OP_EG_R2, "R2"},
{"EG Rate 3", EditOPParameter, 0, DEXED_OP_EG_R3, "R3"},
{"EG Rate 4", EditOPParameter, 0, DEXED_OP_EG_R4, "R4"},
{"EG Level 1", EditOPParameter, 0, DEXED_OP_EG_L1, "L1"},
{"EG Level 2", EditOPParameter, 0, DEXED_OP_EG_L2, "L2"},
{"EG Level 3", EditOPParameter, 0, DEXED_OP_EG_L3, "L3"},
{"EG Level 4", EditOPParameter, 0, DEXED_OP_EG_L4, "L4"},
{"Break Point", EditOPParameter, 0, DEXED_OP_LEV_SCL_BRK_PT, "BP"},
{"L Key Depth", EditOPParameter, 0, DEXED_OP_SCL_LEFT_DEPTH, "LDep"},
{"R Key Depth", EditOPParameter, 0, DEXED_OP_SCL_RGHT_DEPTH, "RDep"},
{"L Key Scale", EditOPParameter, 0, DEXED_OP_SCL_LEFT_CURVE, "LSca"},
{"R Key Scale", EditOPParameter, 0, DEXED_OP_SCL_RGHT_CURVE, "RSca"},
{"Rate Scaling",EditOPParameter, 0, DEXED_OP_OSC_RATE_SCALE, "RS"},
{"A Mod Sens.", EditOPParameter, 0, DEXED_OP_AMP_MOD_SENS, "AMS"},
{"K Vel. Sens.",EditOPParameter, 0, DEXED_OP_KEY_VEL_SENS, "KVS"},
{"Enable", EditOPParameter, 0, DEXED_OP_ENABLE, "OPEn"},
{0}
};
@ -345,6 +345,73 @@ const CUIMenu::TMenuItem CUIMenu::s_PerformanceMenu[] =
{0}
};
const void CUIMenu::GetParameterInfo (CUIMenu::TCParameterInfo *pParam)
{
static const CUIMenu::TMenuItem *globalSources[] = {
s_EffectsMenu,
#ifdef ARM_ALLOW_MULTI_CORE
s_ReverbMenu,
#endif
0};
static const CUIMenu::TMenuItem *tgSources[] = {s_TGMenu, s_EditPortamentoMenu, s_EditPitchBendMenu, s_ModulationMenu, 0};
static const CUIMenu::TMenuItem *voiceSources[] = {s_EditVoiceMenu + 6, 0}; // skip Operators
static const CUIMenu::TMenuItem *opSources[] = {s_OperatorMenu, 0};
const CUIMenu::TMenuItem **pSources = NULL;
const CUIMenu::TParameter *pInfo = NULL;
switch (pParam->Type)
{
case ParameterGlobal:
pSources = globalSources;
pInfo = s_GlobalParameter;
break;
case ParameterTG:
pSources = tgSources;
pInfo = s_TGParameter;
break;
case ParameterVoice:
pSources = voiceSources;
pInfo = s_VoiceParameter;
break;
case ParameterOP:
pSources = opSources;
pInfo = s_OPParameter;
break;
default:
return;
}
pParam->Min = pInfo[pParam->Parameter].Minimum;
pParam->Max = pInfo[pParam->Parameter].Maximum;
pParam->Increment = pInfo[pParam->Parameter].Increment;
if (!pParam->ToString)
pParam->ToString = pInfo[pParam->Parameter].ToString;
// There are some parameters without entry eg TGParameterMWRange
// skip them
if (pParam->Name && pParam->Short)
return;
for (const CUIMenu::TMenuItem **source = pSources; *source; ++source)
for (const CUIMenu::TMenuItem *m = *source; m->Name; ++m)
if (m->Parameter == pParam->Parameter)
{
if (!pParam->Name)
pParam->Name = m->Name;
if (!pParam->Short)
pParam->Short = m->Short;
return;
}
pParam->Type = ParameterNone;
pParam->Parameter = 0;
}
const void CUIMenu::GetParameterInfos (CUIMenu::TCParameterInfo pParamInfo[8])
{
for (size_t i = 0; i < 8; ++i)
GetParameterInfo(&pParamInfo[i]);
}
CUIMenu::CUIMenu (CUserInterface *pUI, CMiniDexed *pMiniDexed, CConfig *pConfig)
: m_pUI (pUI),
@ -447,6 +514,26 @@ void CUIMenu::EventHandler (TMenuEvent Event)
(*m_pParentMenu[m_nCurrentMenuItem].Handler) (this, Event);
break;
}
switch (Event)
{
case MenuEventBack:
case MenuEventHome:
case MenuEventSelect:
if (m_pCurrentMenu == s_MainMenu)
m_pMiniDexed->UpdateDAWMenu (PageMain, 0, 0, 0);
else if (m_pCurrentMenu == s_EffectsMenu)
m_pMiniDexed->UpdateDAWMenu (PageEffect, 0, 0, 0);
else if (m_pCurrentMenu == s_TGMenu)
m_pMiniDexed->UpdateDAWMenu (PageTG, 0, 0, m_nCurrentParameter + 1);
else if (m_pCurrentMenu == s_EditVoiceMenu)
m_pMiniDexed->UpdateDAWMenu (PageVoice, 0, 0, m_nMenuStackParameter[m_nCurrentMenuDepth - 1] + 1);
else if (m_pCurrentMenu == s_OperatorMenu)
m_pMiniDexed->UpdateDAWMenu (PageOP, 0, m_nCurrentParameter, m_nMenuStackParameter[m_nCurrentMenuDepth - 2] + 1);
break;
default:
break;
}
}
void CUIMenu::MenuHandler (CUIMenu *pUIMenu, TMenuEvent Event)
@ -542,7 +629,7 @@ void CUIMenu::MenuHandler (CUIMenu *pUIMenu, TMenuEvent Event)
if (pUIMenu->m_pCurrentMenu) // if this is another menu?
{
bool bIsMainMenu = pUIMenu->m_pCurrentMenu == s_MainMenu;
pUIMenu->m_pUI->DisplayWrite (
pUIMenu->m_pMiniDexed->DisplayWrite (
pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name,
"",
pUIMenu->m_pCurrentMenu[pUIMenu->m_nCurrentSelection].Name,
@ -595,7 +682,7 @@ void CUIMenu::EditGlobalParameter (CUIMenu *pUIMenu, TMenuEvent Event)
string Value = GetGlobalValueString (Param, pUIMenu->m_pMiniDexed->GetParameter (Param));
pUIMenu->m_pUI->DisplayWrite (pMenuName,
pUIMenu->m_pMiniDexed->DisplayWrite (pMenuName,
pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name,
Value.c_str (),
nValue > rParam.Minimum, nValue < rParam.Maximum);
@ -639,7 +726,7 @@ void CUIMenu::EditVoiceBankNumber (CUIMenu *pUIMenu, TMenuEvent Event)
string Value = to_string (nValue+1) + "="
+ pUIMenu->m_pMiniDexed->GetSysExFileLoader ()->GetBankName (nValue);
pUIMenu->m_pUI->DisplayWrite (TG.c_str (),
pUIMenu->m_pMiniDexed->DisplayWrite (TG.c_str (),
pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name,
Value.c_str (),
nValue > 0, nValue < (int) CSysExFileLoader::MaxVoiceBankID);
@ -709,7 +796,7 @@ void CUIMenu::EditProgramNumber (CUIMenu *pUIMenu, TMenuEvent Event)
string Value = to_string (nValue+1) + "=" + pUIMenu->m_pMiniDexed->GetVoiceName (nTG);
pUIMenu->m_pUI->DisplayWrite (TG.c_str (),
pUIMenu->m_pMiniDexed->DisplayWrite (TG.c_str (),
pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name,
Value.c_str (),
nValue > 0, nValue < (int) CSysExFileLoader::VoicesPerBank-1);
@ -762,7 +849,7 @@ void CUIMenu::EditTGParameter (CUIMenu *pUIMenu, TMenuEvent Event)
string Value = GetTGValueString (Param, pUIMenu->m_pMiniDexed->GetTGParameter (Param, nTG));
pUIMenu->m_pUI->DisplayWrite (TG.c_str (),
pUIMenu->m_pMiniDexed->DisplayWrite (TG.c_str (),
pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name,
Value.c_str (),
nValue > rParam.Minimum, nValue < rParam.Maximum);
@ -815,7 +902,7 @@ void CUIMenu::EditTGParameter2 (CUIMenu *pUIMenu, TMenuEvent Event) // second me
string Value = GetTGValueString (Param, pUIMenu->m_pMiniDexed->GetTGParameter (Param, nTG));
pUIMenu->m_pUI->DisplayWrite (TG.c_str (),
pUIMenu->m_pMiniDexed->DisplayWrite (TG.c_str (),
pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name,
Value.c_str (),
nValue > rParam.Minimum, nValue < rParam.Maximum);
@ -868,7 +955,7 @@ void CUIMenu::EditVoiceParameter (CUIMenu *pUIMenu, TMenuEvent Event)
string Value = GetVoiceValueString (nParam, nValue);
pUIMenu->m_pUI->DisplayWrite (TG.c_str (),
pUIMenu->m_pMiniDexed->DisplayWrite (TG.c_str (),
pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name,
Value.c_str (),
nValue > rParam.Minimum, nValue < rParam.Maximum);
@ -971,7 +1058,7 @@ void CUIMenu::EditOPParameter (CUIMenu *pUIMenu, TMenuEvent Event)
Value = GetOPValueString (nParam, nValue);
}
pUIMenu->m_pUI->DisplayWrite (OP.c_str (),
pUIMenu->m_pMiniDexed->DisplayWrite (OP.c_str (),
pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name,
Value.c_str (),
nValue > rParam.Minimum, nValue < rParam.Maximum);
@ -990,7 +1077,7 @@ void CUIMenu::SavePerformance (CUIMenu *pUIMenu, TMenuEvent Event)
pUIMenu->m_MenuStackParent[pUIMenu->m_nCurrentMenuDepth-1]
[pUIMenu->m_nMenuStackItem[pUIMenu->m_nCurrentMenuDepth-1]].Name;
pUIMenu->m_pUI->DisplayWrite (pMenuName,
pUIMenu->m_pMiniDexed->DisplayWrite (pMenuName,
pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name,
bOK ? "Completed" : "Error",
false, false);
@ -1653,7 +1740,7 @@ void CUIMenu::PerformanceMenu (CUIMenu *pUIMenu, TMenuEvent Event)
{
pUIMenu->m_nSelectedPerformanceID = 0;
pUIMenu->m_bConfirmDeletePerformance=false;
pUIMenu->m_pUI->DisplayWrite ("", "Delete", pUIMenu->m_pMiniDexed->DeletePerformance(nValue) ? "Completed" : "Error", false, false);
pUIMenu->m_pMiniDexed->DisplayWrite ("", "Delete", pUIMenu->m_pMiniDexed->DeletePerformance(nValue) ? "Completed" : "Error", false, false);
pUIMenu->m_bSplashShow=true;
CTimer::Get ()->StartKernelTimer (MSEC2HZ (1500), TimerHandlerNoBack, 0, pUIMenu);
return;
@ -1686,13 +1773,13 @@ void CUIMenu::PerformanceMenu (CUIMenu *pUIMenu, TMenuEvent Event)
nPSelected += " [L]";
}
pUIMenu->m_pUI->DisplayWrite (pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name, nPSelected.c_str(),
pUIMenu->m_pMiniDexed->DisplayWrite (pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name, nPSelected.c_str(),
Value.c_str (), true, true);
// (int) nValue > 0, (int) nValue < (int) pUIMenu->m_pMiniDexed->GetLastPerformance());
}
else
{
pUIMenu->m_pUI->DisplayWrite ("", "Delete?", pUIMenu->m_bConfirmDeletePerformance ? "Yes" : "No", false, false);
pUIMenu->m_pMiniDexed->DisplayWrite ("", "Delete?", pUIMenu->m_bConfirmDeletePerformance ? "Yes" : "No", false, false);
}
}
@ -1774,7 +1861,7 @@ void CUIMenu::EditPerformanceBankNumber (CUIMenu *pUIMenu, TMenuEvent Event)
nPSelected += " [L]";
}
pUIMenu->m_pUI->DisplayWrite (pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name, nPSelected.c_str(),
pUIMenu->m_pMiniDexed->DisplayWrite (pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name, nPSelected.c_str(),
Value.c_str (),
nValue > 0,
nValue < pUIMenu->m_pMiniDexed->GetLastPerformanceBank()-1);
@ -1887,7 +1974,7 @@ void CUIMenu::InputTxt (CUIMenu *pUIMenu, TMenuEvent Event)
pUIMenu->m_pMiniDexed->SetNewPerformanceName(pUIMenu->m_InputText);
bOK = pUIMenu->m_pMiniDexed->SavePerformanceNewFile ();
MsgOk=bOK ? "Completed" : "Error";
pUIMenu->m_pUI->DisplayWrite (OkTitleR.c_str(), OkTitleL.c_str(), MsgOk.c_str(), false, false);
pUIMenu->m_pMiniDexed->DisplayWrite (OkTitleR.c_str(), OkTitleL.c_str(), MsgOk.c_str(), false, false);
CTimer::Get ()->StartKernelTimer (MSEC2HZ (1500), TimerHandler, 0, pUIMenu);
return;
}
@ -1938,7 +2025,7 @@ void CUIMenu::InputTxt (CUIMenu *pUIMenu, TMenuEvent Event)
}
Value = Value + " " + escCursor ;
pUIMenu->m_pUI->DisplayWrite (MenuTitleR.c_str(),MenuTitleL.c_str(), Value.c_str(), false, false);
pUIMenu->m_pMiniDexed->DisplayWrite (MenuTitleR.c_str(),MenuTitleL.c_str(), Value.c_str(), false, false);
}
@ -1992,7 +2079,7 @@ void CUIMenu::EditTGParameterModulation (CUIMenu *pUIMenu, TMenuEvent Event)
string Value = GetTGValueString (Param, pUIMenu->m_pMiniDexed->GetTGParameter (Param, nTG));
pUIMenu->m_pUI->DisplayWrite (TG.c_str (),
pUIMenu->m_pMiniDexed->DisplayWrite (TG.c_str (),
pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name,
Value.c_str (),
nValue > rParam.Minimum, nValue < rParam.Maximum);

@ -55,11 +55,45 @@ public:
MenuEventUnknown
};
typedef std::string TToString (int nValue);
enum TCPageType
{
PageMain,
PageEffect,
PageTG,
PageVoice,
PageOP,
};
enum TCParameterType
{
ParameterNone,
ParameterGlobal,
ParameterTG,
ParameterVoice,
ParameterOP,
};
struct TCParameterInfo {
TCParameterType Type;
unsigned Parameter;
const char* Name;
const char* Short;
u8 OP;
int Min;
int Max;
int Increment;
TToString *ToString;
};
public:
CUIMenu (CUserInterface *pUI, CMiniDexed *pMiniDexed, CConfig *pConfig);
void EventHandler (TMenuEvent Event);
const void GetParameterInfos (CUIMenu::TCParameterInfo pParamInfo[8]);
private:
typedef void TMenuHandler (CUIMenu *pUIMenu, TMenuEvent Event);
@ -69,10 +103,9 @@ private:
TMenuHandler *Handler;
const TMenuItem *MenuItem;
unsigned Parameter;
const char *Short;
};
typedef std::string TToString (int nValue);
struct TParameter
{
int Minimum;
@ -129,6 +162,7 @@ private:
static void InputTxt (CUIMenu *pUIMenu, TMenuEvent Event);
static void TimerHandlerNoBack (TKernelTimerHandle hTimer, void *pParam, void *pContext);
const void GetParameterInfo (TCParameterInfo *pParam);
private:
CUserInterface *m_pUI;
CMiniDexed *m_pMiniDexed;

@ -198,6 +198,16 @@ bool CUserInterface::Initialize (void)
return true;
}
void CUserInterface::MIDIEventHandler (CUIMenu::TMenuEvent Event)
{
m_Menu.EventHandler (Event);
}
const void CUserInterface::GetParameterInfos (CUIMenu::TCParameterInfo pParamInfo[8])
{
m_Menu.GetParameterInfos (pParamInfo);
}
void CUserInterface::Process (void)
{
if (m_pLCDBuffered)
@ -213,6 +223,7 @@ void CUserInterface::Process (void)
void CUserInterface::ParameterChanged (void)
{
m_Menu.EventHandler (CUIMenu::MenuEventUpdate);
m_pMiniDexed->UpdateDAWState ();
}
void CUserInterface::DisplayWrite (const char *pMenu, const char *pParam, const char *pValue,
@ -396,7 +407,7 @@ void CUserInterface::UIButtonsEventStub (CUIButton::BtnEvent Event, void *pParam
pThis->UIButtonsEventHandler (Event);
}
void CUserInterface::UIMIDICmdHandler (unsigned nMidiCh, unsigned nMidiCmd, unsigned nMidiData1, unsigned nMidiData2)
void CUserInterface::UIMIDICmdHandler (unsigned nMidiCh, unsigned nMidiType, unsigned nMidiData1, unsigned nMidiData2)
{
if (m_nMIDIButtonCh == CMIDIDevice::Disabled)
{
@ -411,7 +422,7 @@ void CUserInterface::UIMIDICmdHandler (unsigned nMidiCh, unsigned nMidiCmd, unsi
if (m_pUIButtons)
{
m_pUIButtons->BtnMIDICmdHandler (nMidiCmd, nMidiData1, nMidiData2);
m_pUIButtons->BtnMIDICmdHandler (nMidiType, nMidiData1, nMidiData2);
}
}

@ -57,6 +57,10 @@ 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 MIDIEventHandler (CUIMenu::TMenuEvent Event);
const void GetParameterInfos (CUIMenu::TCParameterInfo pParamInfo[8]);
private:
void LCDWrite (const char *pString); // Print to optional HD44780 display

Loading…
Cancel
Save