Initial commit for MIDI user interface button support.

Status: first successful build.
pull/365/head
Kevin 3 years ago committed by diyelectromusic
parent aa5a7c7450
commit 242455b104
  1. 2
      src/Makefile
  2. 37
      src/config.cpp
  3. 15
      src/config.h
  4. 14
      src/mididevice.cpp
  5. 55
      src/midipin.cpp
  6. 54
      src/midipin.h
  7. 11
      src/minidexed.ini
  8. 53
      src/uibuttons.cpp
  9. 20
      src/uibuttons.h
  10. 20
      src/userinterface.cpp
  11. 5
      src/userinterface.h

@ -9,7 +9,7 @@ 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
effect_compressor.o effect_platervbstereo.o uibuttons.o midipin.o
OPTIMIZE = -O3

@ -104,6 +104,13 @@ void CConfig::Load (void)
m_nDoubleClickTimeout = m_Properties.GetNumber ("DoubleClickTimeout", 400);
m_nLongPressTimeout = m_Properties.GetNumber ("LongPressTimeout", 600);
m_nMIDIButtonCh = m_Properties.GetNumber ("MIDIButtonCh", 255);
m_nMIDIButtonPrev = m_Properties.GetNumber ("MIDIButtonPrev", 0);
m_nMIDIButtonNext = m_Properties.GetNumber ("MIDIButtonNext", 0);
m_nMIDIButtonBack = m_Properties.GetNumber ("MIDIButtonBack", 0);
m_nMIDIButtonSelect = m_Properties.GetNumber ("MIDIButtonSelect", 0);
m_nMIDIButtonHome = m_Properties.GetNumber ("MIDIButtonHome", 0);
m_bEncoderEnabled = m_Properties.GetNumber ("EncoderEnabled", 0) != 0;
m_nEncoderPinClock = m_Properties.GetNumber ("EncoderPinClock", 10);
m_nEncoderPinData = m_Properties.GetNumber ("EncoderPinData", 9);
@ -293,6 +300,36 @@ unsigned CConfig::GetLongPressTimeout (void) const
return m_nLongPressTimeout;
}
unsigned CConfig::GetMIDIButtonCh (void) const
{
return m_nMIDIButtonCh;
}
unsigned CConfig::GetMIDIButtonPrev (void) const
{
return m_nMIDIButtonPrev;
}
unsigned CConfig::GetMIDIButtonNext (void) const
{
return m_nMIDIButtonNext;
}
unsigned CConfig::GetMIDIButtonBack (void) const
{
return m_nMIDIButtonBack;
}
unsigned CConfig::GetMIDIButtonSelect (void) const
{
return m_nMIDIButtonSelect;
}
unsigned CConfig::GetMIDIButtonHome (void) const
{
return m_nMIDIButtonHome;
}
bool CConfig::GetEncoderEnabled (void) const
{
return m_bEncoderEnabled;

@ -117,6 +117,14 @@ public:
unsigned GetDoubleClickTimeout (void) const;
unsigned GetLongPressTimeout (void) const;
// MIDI Button Navigation
unsigned GetMIDIButtonCh (void) const;
unsigned GetMIDIButtonPrev (void) const;
unsigned GetMIDIButtonNext (void) const;
unsigned GetMIDIButtonBack (void) const;
unsigned GetMIDIButtonSelect (void) const;
unsigned GetMIDIButtonHome (void) const;
// KY-040 Rotary Encoder
// GPIO pin numbers are chip numbers, not header positions
bool GetEncoderEnabled (void) const;
@ -177,6 +185,13 @@ private:
unsigned m_nDoubleClickTimeout;
unsigned m_nLongPressTimeout;
unsigned m_nMIDIButtonCh;
unsigned m_nMIDIButtonPrev;
unsigned m_nMIDIButtonNext;
unsigned m_nMIDIButtonBack;
unsigned m_nMIDIButtonSelect;
unsigned m_nMIDIButtonHome;
bool m_bEncoderEnabled;
unsigned m_nEncoderPinClock;
unsigned m_nEncoderPinData;

@ -27,6 +27,7 @@
#include "config.h"
#include <stdio.h>
#include <assert.h>
#include "userinterface.h"
LOGMODULE ("mididevice");
@ -181,6 +182,19 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
}
else
{
// Perform any MiniDexed level MIDI handling before specific Tone Generators
switch (ucType)
{
case MIDI_CONTROL_CHANGE:
if (nLength < 3)
{
break;
}
CUserInterface::UIMIDICCHandler (ucChannel, pMessage[1], pMessage[2]);
break;
}
// Process MIDI for each Tone Generator
for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++)
{
if (ucStatus == MIDI_SYSTEM_EXCLUSIVE_BEGIN)

@ -0,0 +1,55 @@
//
// midipin.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 "midipin.h"
#include <circle/logger.h>
#include <assert.h>
LOGMODULE ("midipin");
CMIDIPin::CMIDIPin (unsigned nPinNumber)
: m_nPinNumber (nPinNumber),
m_nValue (HIGH)
{
}
CMIDIPin::~CMIDIPin (void)
{
}
unsigned CMIDIPin::read (void)
{
return m_nValue;
}
void CMIDIPin::write (unsigned nValue)
{
// Takes values in the MIDI controller range 0 to 127
// and OFF < 64 < ON.
// Simulates a PULLUP IO pin, so "true" is LOW (0)
if (nValue >= 64) {
// "on"
m_nValue = LOW;
} else {
// "off"
m_nValue = HIGH;
}
return;
}

@ -0,0 +1,54 @@
//
// midipin.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 _midipin_h
#define _midipin_h
#include <circle/gpiopin.h>
#include <circle/types.h>
// MIDI CC numbers go 0 to 127.
// Normal GPIO pins are below 100.
// So use a "pin number" of 128 + MIDI CC message for a "MIDI Pin"
#define MIDI_PINS 128
#define ccToMidiPin(c) ((c)+MIDI_PINS)
#define MidiPinToCC(p) ((p)-MIDI_PINS)
#define isMidiPin(p) (((p)>=MIDI_PINS)?1:0)
class CMIDIPin : public CGPIOPin
{
public:
CMIDIPin (unsigned nPinNumber); // pinNumber = ccToMidiPin (MIDI CC number)
~CMIDIPin (void);
// Will return MP_HIGH or MP_LOW.
// Should be treated as a PULLED UP IO pin
// i.e. treated as "active low" (LOW) when pressed.
unsigned read (void);
// MIDI CC values >=64 will set the MIDI pin to LOW ("on")
// MIDI CC values <= 63 will set the MIDI pin to HIGH ("off")
void write (unsigned nValue);
private:
unsigned m_nPinNumber;
unsigned m_nValue;
};
#endif

@ -57,6 +57,17 @@ ButtonPinShortcut=11
DoubleClickTimeout=400
LongPressTimeout=400
# MIDI Button Navigation
# Specify MIDI CC to act as a button
# NB: Off < 64 < ON
# CC channel: 0=OMNI; 1-16 MIDI Ch; >16 OFF
MIDIButtonCh=255
MIDIButtonPrev=85
MIDIButtonNext=86
MIDIButtonHome=87
MIDIButtonSelect=89
MIDIButtonBack=90
# KY-040 Rotary Encoder
EncoderEnabled=1
EncoderPinClock=10

@ -4,9 +4,6 @@
// MiniDexed - Dexed FM synthesizer for bare metal Raspberry Pi
// Copyright (C) 2022 The MiniDexed Team
//
// Original author of this class:
// R. Stange <rsta2@o2online.de>
//
// 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
@ -72,7 +69,12 @@ boolean CUIButton::Initialize (unsigned pinNumber, unsigned doubleClickTimeout,
if (m_pinNumber != 0)
{
m_pin = new CGPIOPin (m_pinNumber, GPIOModeInputPullUp);
if (isMidiPin(m_pinNumber))
{
m_pin = new CMIDIPin (m_pinNumber);
} else {
m_pin = new CGPIOPin (m_pinNumber, GPIOModeInputPullUp);
}
}
return TRUE;
}
@ -233,7 +235,8 @@ CUIButtons::CUIButtons (
unsigned backPin, const char *backAction,
unsigned selectPin, const char *selectAction,
unsigned homePin, const char *homeAction,
unsigned doubleClickTimeout, unsigned longPressTimeout
unsigned doubleClickTimeout, unsigned longPressTimeout,
unsigned prevMidi, unsigned nextMidi, unsigned backMidi, unsigned selectMidi, unsigned homeMidi
)
: m_doubleClickTimeout(doubleClickTimeout),
m_longPressTimeout(longPressTimeout),
@ -247,6 +250,11 @@ CUIButtons::CUIButtons (
m_selectAction(CUIButton::triggerTypeFromString(selectAction)),
m_homePin(homePin),
m_homeAction(CUIButton::triggerTypeFromString(homeAction)),
m_prevMidi(prevMidi),
m_nextMidi(nextMidi),
m_backMidi(backMidi),
m_selectMidi(selectMidi),
m_homeMidi(homeMidi),
m_eventHandler (0),
m_lastTick (0)
{
@ -274,15 +282,27 @@ boolean CUIButtons::Initialize (void)
longPressTimeout = doubleClickTimeout;
}
// Each button can be assigned up to 3 actions: click, doubleclick and
// longpress. We may not initialise all of the buttons
// Each normal button can be assigned up to 3 actions: click, doubleclick and
// longpress. We may not initialise all of the buttons.
// MIDI buttons only support a single click.
unsigned pins[MAX_BUTTONS] = {
m_prevPin, m_nextPin, m_backPin, m_selectPin, m_homePin
m_prevPin, m_nextPin, m_backPin, m_selectPin, m_homePin,
m_prevMidi, m_nextMidi, m_backMidi, m_selectMidi, m_homeMidi
};
CUIButton::BtnTrigger triggers[MAX_BUTTONS] = {
m_prevAction, m_nextAction, m_backAction, m_selectAction, m_homeAction
// Normal buttons
m_prevAction, m_nextAction, m_backAction, m_selectAction, m_homeAction,
// MIDI Buttons only support a single click (at present)
CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick
};
CUIButton::BtnEvent events[MAX_BUTTONS] = {
// Normal buttons
CUIButton::BtnEventPrev,
CUIButton::BtnEventNext,
CUIButton::BtnEventBack,
CUIButton::BtnEventSelect,
CUIButton::BtnEventHome,
// MIDI buttons
CUIButton::BtnEventPrev,
CUIButton::BtnEventNext,
CUIButton::BtnEventBack,
@ -290,7 +310,8 @@ boolean CUIButtons::Initialize (void)
CUIButton::BtnEventHome
};
for (unsigned i=0; i<MAX_BUTTONS; i++) {
// Setup normal GPIO buttons first
for (unsigned i=0; i<MAX_GPIO_BUTTONS; i++) {
// if this pin is 0 it means it's disabled - so continue
if (pins[i] == 0) {
continue;
@ -311,6 +332,18 @@ boolean CUIButtons::Initialize (void)
}
}
}
// Now setup the MIDI buttons.
// Note: the configuration is simpler as the only trigger supported is a single, short press
for (unsigned i=MAX_GPIO_BUTTONS; i<MAX_BUTTONS; i++) {
// if this pin is 0 it means it's disabled - so continue
if (pins[i] == 0) {
continue;
}
// doubleClickTimeout and longPressTimeout are ignored for MIDI buttons at present
m_buttons[i].Initialize(pins[i], doubleClickTimeout, longPressTimeout);
}
// All of the buttons are now initialised, they just need to have their
// events assigned to them

@ -4,9 +4,6 @@
// MiniDexed - Dexed FM synthesizer for bare metal Raspberry Pi
// Copyright (C) 2022 The MiniDexed Team
//
// Original author of this class:
// R. Stange <rsta2@o2online.de>
//
// 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
@ -25,11 +22,14 @@
#include <circle/gpiopin.h>
#include <circle/types.h>
#include "midipin.h"
#include "config.h"
#define BUTTONS_UPDATE_NUM_TICKS 100
#define DEBOUNCE_TIME 100
#define MAX_BUTTONS 5
#define MAX_GPIO_BUTTONS 5
#define MAX_MIDI_BUTTONS 5
#define MAX_BUTTONS (MAX_GPIO_BUTTONS+MAX_MIDI_BUTTONS)
class CUIButtons;
@ -110,7 +110,8 @@ public:
unsigned backPin, const char *backAction,
unsigned selectPin, const char *selectAction,
unsigned homePin, const char *homeAction,
unsigned doubleClickTimeout, unsigned longPressTimeout
unsigned doubleClickTimeout, unsigned longPressTimeout,
unsigned prevMidi, unsigned nextMidi, unsigned backMidi, unsigned selectMidi, unsigned homeMidi
);
~CUIButtons (void);
@ -123,7 +124,7 @@ public:
void ResetButton (unsigned pinNumber);
private:
// Array of 5 buttons
// Array of normal GPIO buttons and "MIDI buttons"
CUIButton m_buttons[MAX_BUTTONS];
// Timeout for double click in tenths of a millisecond
@ -142,6 +143,13 @@ private:
CUIButton::BtnTrigger m_selectAction;
unsigned m_homePin;
CUIButton::BtnTrigger m_homeAction;
// MIDI button configuration
unsigned m_prevMidi;
unsigned m_nextMidi;
unsigned m_backMidi;
unsigned m_selectMidi;
unsigned m_homeMidi;
BtnEventHandler *m_eventHandler;
void *m_eventParam;

@ -27,6 +27,8 @@
LOGMODULE ("ui");
unsigned CUserInterface::nMIDIButtonCh = 255;
CUserInterface::CUserInterface (CMiniDexed *pMiniDexed, CGPIOManager *pGPIOManager, CI2CMaster *pI2CMaster, CConfig *pConfig)
: m_pMiniDexed (pMiniDexed),
m_pGPIOManager (pGPIOManager),
@ -114,7 +116,13 @@ bool CUserInterface::Initialize (void)
m_pConfig->GetButtonPinHome (),
m_pConfig->GetButtonActionHome (),
m_pConfig->GetDoubleClickTimeout (),
m_pConfig->GetLongPressTimeout () );
m_pConfig->GetLongPressTimeout (),
m_pConfig->GetMIDIButtonNext (),
m_pConfig->GetMIDIButtonPrev (),
m_pConfig->GetMIDIButtonBack (),
m_pConfig->GetMIDIButtonSelect (),
m_pConfig->GetMIDIButtonHome ()
);
assert (m_pUIButtons);
if (!m_pUIButtons->Initialize ())
@ -123,6 +131,7 @@ bool CUserInterface::Initialize (void)
}
m_pUIButtons->RegisterEventHandler (UIButtonsEventStub, this);
nMIDIButtonCh = m_pConfig->GetMIDIButtonCh ();
LOGDBG ("Button User Interface initialized");
@ -322,3 +331,12 @@ void CUserInterface::UIButtonsEventStub (CUIButton::BtnEvent Event, void *pParam
pThis->UIButtonsEventHandler (Event);
}
void CUserInterface::UIMIDICCHandler (unsigned nMidiCh, unsigned nMidiCC, unsigned nMidiData)
{
if ((nMIDIButtonCh != nMidiCh) && (nMIDIButtonCh != 0))
{
// Message not on the MIDI Button channel and MIDI buttons not in OMNI mode
return;
}
}

@ -52,6 +52,9 @@ public:
void DisplayWrite (const char *pMenu, const char *pParam, const char *pValue,
bool bArrowDown, bool bArrowUp);
// To be called from the MIDI device on reception of a MIDI CC message
static void UIMIDICCHandler (unsigned nMidiCh, unsigned nMidiCC, unsigned nMidiData);
private:
void LCDWrite (const char *pString); // Print to optional HD44780 display
@ -73,6 +76,8 @@ private:
CUIButtons *m_pUIButtons;
static unsigned nMIDIButtonCh;
CKY040 *m_pRotaryEncoder;
bool m_bSwitchPressed;

Loading…
Cancel
Save