Skip to content

Commit

Permalink
add DAW Controller support for Arturia MiniLab 3 and KeyLab 3 Essential
Browse files Browse the repository at this point in the history
based on https://github.com/PrzemekBarski/arturia-keylab-essential-mk3-programming-guide

Tested on a Arturia MiniLab 3

Keylab 3 Essential is not tested
  • Loading branch information
soyersoyer committed Nov 11, 2024
1 parent 2a5d8b9 commit 98788fa
Show file tree
Hide file tree
Showing 13 changed files with 496 additions and 42 deletions.
3 changes: 2 additions & 1 deletion src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
7 changes: 7 additions & 0 deletions src/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -703,6 +705,11 @@ const char *CConfig::GetMIDIButtonActionTGDown (void) const
return m_MIDIButtonActionTGDown.c_str();
}

bool CConfig::GetDAWControllerEnabled (void) const
{
return m_bDAWControllerEnabled;
}

bool CConfig::GetEncoderEnabled (void) const
{
return m_bEncoderEnabled;
Expand Down
4 changes: 4 additions & 0 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,8 @@ class CConfig // Configuration for MiniDexed
const char *GetMIDIButtonActionTGUp (void) const;
const char *GetMIDIButtonActionTGDown (void) const;

bool GetDAWControllerEnabled (void) const;

// KY-040 Rotary Encoder
// GPIO pin numbers are chip numbers, not header positions
bool GetEncoderEnabled (void) const;
Expand Down Expand Up @@ -355,6 +357,8 @@ class CConfig // Configuration for MiniDexed
std::string m_MIDIButtonActionTGUp;
std::string m_MIDIButtonActionTGDown;

bool m_bDAWControllerEnabled;

bool m_bEncoderEnabled;
unsigned m_nEncoderPinClock;
unsigned m_nEncoderPinData;
Expand Down
261 changes: 261 additions & 0 deletions src/dawcontroller.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
//
// dawdisplay.cpp
//
// MiniDexed - Dexed FM synthesizer for bare metal Raspberry Pi
// Copyright (C) 2022 The MiniDexed Team
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <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);
CString line2 (pValue);

size_t nLen = strlen (pParam) + strlen (pMenu);
if (nLen < L1MaxLen)
{
for (unsigned i = L1MaxLen - nLen; i > 0; i--)
{
line1.Append (" ");
}
}

line1.Append (pMenu);

int nLine1Len = strlen (line1);
int nLine2Len = strlen (line2);
int nOffset = 0;

uint8_t pLines[nHdrSize + nLine1Len + 2 + nLine2Len + 2];

memcpy (pLines, pHdr, nHdrSize);
nOffset += nHdrSize;

memcpy (&pLines[nOffset], line1, nLine1Len + 1);
nOffset += nLine1Len + 1;

pLines[nOffset] = 0x02;
nOffset += 1;

memcpy (&pLines[nOffset], line2, nLine2Len + 1);
nOffset += nLine2Len + 1;

pLines[nOffset] = 0xf7;
nOffset += 1;

pKeyboard->SendDebounce (pLines, nOffset, 0);
}

class CMiniLab3DawConnection : public CDAWConnection
{
public:
CMiniLab3DawConnection (CMiniDexed *pSynthesizer, CMIDIKeyboard *pMIDIKeyboard);
void DisplayWrite (CMIDIKeyboard *pMIDIKeyboard, const char *pMenu, const char *pParam, const char *pValue, bool bArrowDown, bool bArrowUp);
void UpdateEncoders (CMiniDexed *pSynthesizer, CMIDIKeyboard *pKeyboard);
private:
void UpdateEncoder (CMIDIKeyboard *pKeyboard, uint8_t ucEncID, uint8_t ucValue);

uint8_t m_pEncoderCache[8];
};

CMiniLab3DawConnection::CMiniLab3DawConnection (CMiniDexed *pSynthesizer, CMIDIKeyboard *pMIDIKeyboard)
{
static const uint8_t pInit[] = {0xF0, 0x00, 0x20, 0x6B, 0x7F, 0x42, 0x02, 0x00, 0x40, 0x6A, 0x21, 0xF7};
static const CMIDIDevice::TMIDICCRouteMap map[] = {
{0, 14, 0, MIDI_CC_VOLUME}, // Fader1
{0, 15, 1, MIDI_CC_VOLUME}, // Fader2
{0, 30, 2, MIDI_CC_VOLUME}, // Fader3
{0, 31, 3, MIDI_CC_VOLUME}, // Fader4
{0, 86, 0, MIDI_CC_FREQUENCY_CUTOFF}, // Knob1
{0, 87, 0, MIDI_CC_RESONANCE}, // Knob2
{0, 89, 0, MIDI_CC_REVERB_LEVEL}, // Knob3
{0, 90, 0, MIDI_CC_DETUNE_LEVEL}, // Knob4
// {0, 110, 0, MIDI_CC_DETUNE_LEVEL}, // Knob5
// {0, 111, 0, MIDI_CC_DETUNE_LEVEL}, // Knob6
// {0, 116, 0, MIDI_CC_DETUNE_LEVEL}, // Knob7
{0, 117, 0, MIDI_CC_PAN_POSITION}, // Knob8
{255, 0, 0, 0}, // Sentinel
};

memset (m_pEncoderCache, 128, sizeof m_pEncoderCache);

pMIDIKeyboard->SetCCRouteMap (map);

pMIDIKeyboard->Send (pInit, sizeof pInit, 0);
DisplayWrite (pMIDIKeyboard, "MiniDexed", "", "On MiniLab 3", 0, 0);

UpdateEncoders (pSynthesizer, pMIDIKeyboard);
}

void CMiniLab3DawConnection::DisplayWrite (CMIDIKeyboard *pMIDIKeyboard, const char *pMenu, const char *pParam, const char *pValue, bool bArrowDown, bool bArrowUp)
{
static const uint8_t pHdr[] = {0xF0, 0x00, 0x20, 0x6B, 0x7F, 0x42, 0x04, 0x02, 0x60, 0x12, 0x01};
ArturiaDisplayWrite (pMIDIKeyboard, pHdr, sizeof pHdr, pMenu, pParam, pValue);
}

void CMiniLab3DawConnection::UpdateEncoder (CMIDIKeyboard *pKeyboard, uint8_t ucEncID, uint8_t ucValue)
{
if (m_pEncoderCache[ucEncID] == ucValue)
return;

m_pEncoderCache[ucEncID] = ucValue;

uint8_t pUpdateEncoder[] = {0xF0, 0x00, 0x20, 0x6B, 0x7F, 0x42, 0x21, 0x10, 0x00, ucEncID+=7, 0x00, ucValue, 0xF7};
pKeyboard->Send (pUpdateEncoder, sizeof pUpdateEncoder, 0);
}

void CMiniLab3DawConnection::UpdateEncoders (CMiniDexed *pSynthesizer, CMIDIKeyboard *pKeyboard)
{
UpdateEncoder (pKeyboard, 0, maplong(pSynthesizer->GetTGParameter (CMiniDexed::TGParameterCutoff, 0), 0, 99, 0, 127));
UpdateEncoder (pKeyboard, 1, maplong(pSynthesizer->GetTGParameter (CMiniDexed::TGParameterResonance, 0), 0, 99, 0, 127));
UpdateEncoder (pKeyboard, 2, maplong(pSynthesizer->GetTGParameter (CMiniDexed::TGParameterReverbSend, 0), 0, 99, 0, 127));
UpdateEncoder (pKeyboard, 3, maplong(pSynthesizer->GetTGParameter (CMiniDexed::TGParameterMasterTune, 0), -99, 99, 1, 127));
UpdateEncoder (pKeyboard, 7, pSynthesizer->GetTGParameter (CMiniDexed::TGParameterPan, 0));
}

class CKeyLabEs3DawConnection : public CDAWConnection
{
public:
CKeyLabEs3DawConnection (CMiniDexed *pSynthesizer, CMIDIKeyboard *pMIDIKeyboard);
void DisplayWrite (CMIDIKeyboard *pMIDIKeyboard, const char *pMenu, const char *pParam, const char *pValue, bool bArrowDown, bool bArrowUp);
void UpdateEncoders (CMiniDexed *pSynthesizer, CMIDIKeyboard *pKeyboard);
private:
void UpdateEncoder (CMIDIKeyboard *pKeyboard, uint8_t ucEncID, uint8_t ucValue);

uint8_t m_pEncoderCache[8];
};

CKeyLabEs3DawConnection::CKeyLabEs3DawConnection (CMiniDexed *pSynthesizer, CMIDIKeyboard *pMIDIKeyboard)
{
static const uint8_t pInit[] = {0xF0, 0x00, 0x20, 0x6B, 0x7F, 0x42, 0x02, 0x00, 0x40, 0x6A, 0x21, 0xF7};
static const CMIDIDevice::TMIDICCRouteMap map[] = {
{0, 105, 0, MIDI_CC_VOLUME}, // Fader1
{0, 106, 1, MIDI_CC_VOLUME}, // Fader2
{0, 107, 2, MIDI_CC_VOLUME}, // Fader3
{0, 108, 3, MIDI_CC_VOLUME}, // Fader4
{0, 109, 4, MIDI_CC_VOLUME}, // Fader5
{0, 110, 5, MIDI_CC_VOLUME}, // Fader6
{0, 111, 6, MIDI_CC_VOLUME}, // Fader7
{0, 112, 7, MIDI_CC_VOLUME}, // Fader8
//{0, 113, 8, MIDI_CC_VOLUME}, // Fader9
{0, 96, 0, MIDI_CC_FREQUENCY_CUTOFF}, // Knob1
{0, 97, 0, MIDI_CC_RESONANCE}, // Knob2
{0, 98, 0, MIDI_CC_REVERB_LEVEL}, // Knob3
{0, 99, 0, MIDI_CC_DETUNE_LEVEL}, // Knob4
{0, 100, 0, MIDI_CC_PAN_POSITION}, // Knob5
// {0, 101, 0, MIDI_CC_DETUNE_LEVEL}, // Knob6
// {0, 102, 0, MIDI_CC_DETUNE_LEVEL}, // Knob7
// {0, 103, 0, MIDI_CC_PAN_POSITION}, // Knob8
// {0, 104, 0, MIDI_CC_PAN_POSITION}, // Knob9
{255, 0, 0, 0}, // Sentinel
};

memset (m_pEncoderCache, 128, sizeof m_pEncoderCache);

pMIDIKeyboard->SetCCRouteMap (map);

pMIDIKeyboard->Send (pInit, sizeof pInit, 0);
DisplayWrite (pMIDIKeyboard, "MiniDexed", "", "On KeyLab 3 Essential", 0, 0);

UpdateEncoders (pSynthesizer, pMIDIKeyboard);
}

void CKeyLabEs3DawConnection::DisplayWrite (CMIDIKeyboard *pMIDIKeyboard, const char *pMenu, const char *pParam, const char *pValue, bool bArrowDown, bool bArrowUp)
{
static const uint8_t pHdr[] = {0xF0, 0x00, 0x20, 0x6B, 0x7F, 0x42, 0x04, 0x01, 0x60, 0x12, 0x01};
ArturiaDisplayWrite (pMIDIKeyboard, pHdr, sizeof pHdr, pMenu, pParam, pValue);
}

void CKeyLabEs3DawConnection::UpdateEncoder (CMIDIKeyboard *pKeyboard, uint8_t ucEncID, uint8_t ucValue)
{
if (m_pEncoderCache[ucEncID] == ucValue)
return;

m_pEncoderCache[ucEncID] = ucValue;

uint8_t pUpdateEncoder[] = {0xF0, 0x00, 0x20, 0x6B, 0x7F, 0x42, 0x02, 0x0F, 0x40, ucEncID += 3, ucValue, 0xF7};
pKeyboard->Send (pUpdateEncoder, sizeof pUpdateEncoder, 0);
}

void CKeyLabEs3DawConnection::UpdateEncoders (CMiniDexed *pSynthesizer, CMIDIKeyboard *pKeyboard)
{
UpdateEncoder (pKeyboard, 0, maplong(pSynthesizer->GetTGParameter (CMiniDexed::TGParameterCutoff, 0), 0, 99, 0, 127));
UpdateEncoder (pKeyboard, 1, maplong(pSynthesizer->GetTGParameter (CMiniDexed::TGParameterResonance, 0), 0, 99, 0, 127));
UpdateEncoder (pKeyboard, 2, maplong(pSynthesizer->GetTGParameter (CMiniDexed::TGParameterReverbSend, 0), 0, 99, 0, 127));
UpdateEncoder (pKeyboard, 3, maplong(pSynthesizer->GetTGParameter (CMiniDexed::TGParameterMasterTune, 0), -99, 99, 1, 127));
UpdateEncoder (pKeyboard, 4, pSynthesizer->GetTGParameter (CMiniDexed::TGParameterPan, 0));
}

CDAWController::CDAWController (CMiniDexed *pSynthesizer, CMIDIKeyboard *pMIDIKeyboard)
: m_pSynthesizer (pSynthesizer),
m_pMIDIKeyboard (pMIDIKeyboard),
m_pDAWConnection (0)
{
}

CDAWController::~CDAWController (void)
{
delete m_pDAWConnection;
}

void CDAWController::OnConnect (void)
{
static const uint8_t inquiry[] = {0xF0, 0x7E, 0x7F, 0x06, 0x01, 0xF7};

delete m_pDAWConnection;
m_pDAWConnection = 0;

m_pMIDIKeyboard->Send (inquiry, sizeof inquiry, 0);
}

void CDAWController::MIDISysexHandler (u8 *pPacket, unsigned nLength, unsigned nCable)
{
static const uint8_t pMiniLab3[] = {0xF0, 0x7E, 0x7F, 0x06, 0x02, 0x00, 0x20, 0x6B, 0x02, 0x00, 0x04, 0x04};
static const uint8_t pKeyLabEs3_49[] = {0xF0, 0x7E, 0x7F, 0x06, 0x02, 0x00, 0x20, 0x6B, 0x02, 0x00, 0x05, 0x72};
static const uint8_t pKeyLabEs3_61[] = {0xF0, 0x7E, 0x7F, 0x06, 0x02, 0x00, 0x20, 0x6B, 0x02, 0x00, 0x05, 0x74};
static const uint8_t pKeyLabEs3_88[] = {0xF0, 0x7E, 0x7F, 0x06, 0x02, 0x00, 0x20, 0x6B, 0x02, 0x00, 0x05, 0x78};

if (nLength > sizeof pMiniLab3 && memcmp (pPacket, pMiniLab3, sizeof pMiniLab3) == 0)
{
m_pDAWConnection = new CMiniLab3DawConnection (m_pSynthesizer, m_pMIDIKeyboard);
}
else if (nLength > sizeof pKeyLabEs3_49 && (
memcmp (pPacket, pKeyLabEs3_49, sizeof pKeyLabEs3_49) == 0 ||
memcmp (pPacket, pKeyLabEs3_61, sizeof pKeyLabEs3_61) == 0 ||
memcmp (pPacket, pKeyLabEs3_88, sizeof pKeyLabEs3_88) == 0))
{
m_pDAWConnection = new CKeyLabEs3DawConnection (m_pSynthesizer, m_pMIDIKeyboard);
}
}

void CDAWController::DisplayWrite (const char *pMenu, const char *pParam, const char *pValue,
bool bArrowDown, bool bArrowUp)
{
if (m_pDAWConnection)
m_pDAWConnection->DisplayWrite (m_pMIDIKeyboard, pMenu, pParam, pValue, bArrowDown, bArrowUp);
}

void CDAWController::UpdateEncoders (void)
{
if (m_pDAWConnection)
m_pDAWConnection->UpdateEncoders (m_pSynthesizer, m_pMIDIKeyboard);
}
57 changes: 57 additions & 0 deletions src/dawcontroller.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//
// dawcontroller.h
//
// MiniDexed - Dexed FM synthesizer for bare metal Raspberry Pi
// Copyright (C) 2022 The MiniDexed Team
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#ifndef _dawcontroller_h
#define _dawcontroller_h

#include <circle/types.h>

class CMIDIKeyboard;
class CMiniDexed;

class CDAWConnection
{
public:
virtual void DisplayWrite (CMIDIKeyboard *pKeyboard, const char *pMenu, const char *pParam,
const char *pValue, bool bArrowDown, bool bArrowUp) = 0;
virtual void UpdateEncoders (CMiniDexed *pSynthesizer, CMIDIKeyboard *pKeyboard) = 0;
virtual ~CDAWConnection (void) = default;
};

class CDAWController
{
public:
CDAWController (CMiniDexed *pSynthesizer, CMIDIKeyboard *pKeyboard);
~CDAWController (void);

void OnConnect (void);
void MIDISysexHandler (u8 *pPacket, unsigned nLength, unsigned nCable);

void DisplayWrite (const char *pMenu, const char *pParam, const char *pValue,
bool bArrowDown, bool bArrowUp);

void UpdateEncoders (void);

private:
CMiniDexed *m_pSynthesizer;
CMIDIKeyboard *m_pMIDIKeyboard;
CDAWConnection *m_pDAWConnection;
};

#endif
Loading

0 comments on commit 98788fa

Please sign in to comment.