Add Program/TG Select buttons and choice for Program Change to act on Voices or Performances (#500)

* Initial implementation of Program Up/Down and Tone Generator Up/Down selection and short cuts as buttons and MIDI buttons.

* Add configuration option for Program Change to act on Voices within a TG or Performances.

* Added PC option to Performance menu.  Various bug fixes.  Allow menu updating if on Performance/Load display.  Swapped TG1 and TG3 in Performance 000008 so that TG1 is set to MIDI CH 1 in all Performances.

* Minor fixups in formatting and so on after review.

* Formatting fixups

* Changed config setting to match description in original manuals: Performance Select Channel.  Also now accepts 1-16 or Omni as values.

* Additional fix to ensure GPIO buttons are working correctly on Voices or Performances.

* Changed initialisation order for PerformanceSelectChannel to avoid asset problem on a Pi 4 and updated menu text to fit nicely on a HD44780 display.
print-performance-number^2 2023-08-13
Kevin 1 year ago committed by GitHub
parent 375900b9ec
commit b2c95bda78
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 20
      performance/000008_AmpUpright.ini
  2. 81
      src/config.cpp
  3. 35
      src/config.h
  4. 23
      src/mididevice.cpp
  5. 5
      src/midipin.h
  6. 62
      src/minidexed.cpp
  7. 6
      src/minidexed.h
  8. 47
      src/minidexed.ini
  9. 40
      src/uibuttons.cpp
  10. 32
      src/uibuttons.h
  11. 162
      src/uimenu.cpp
  12. 7
      src/uimenu.h
  13. 30
      src/userinterface.cpp

@ -1,9 +1,9 @@
BankNumber1=2
VoiceNumber1=6
MIDIChannel1=0
Volume1=47
VoiceNumber1=2
MIDIChannel1=1
Volume1=87
Pan1=0
Detune1=0
Detune1=2
Cutoff1=99
Resonance1=0
NoteLimitLow1=0
@ -15,7 +15,7 @@ PitchBendStep1=0
PortamentoMode1=0
PortamentoGlissando1=0
PortamentoTime1=0
VoiceData1=5F 15 00 26 5C 3E 60 00 30 05 0A 03 00 04 00 01 55 00 01 00 07 4D 48 48 22 32 62 00 00 27 00 14 00 01 00 00 04 5F 01 02 00 0A 59 3C 17 23 63 5E 00 00 2E 03 02 03 03 03 00 01 5E 01 00 00 00 5F 1C 06 23 63 59 4B 00 05 00 63 00 01 02 00 06 4F 00 05 00 09 58 5C 47 3F 63 43 5B 5A 27 0C 14 03 00 02 00 01 5A 00 01 00 05 58 1C 1B 32 63 5A 00 00 11 63 05 03 00 02 00 03 63 00 01 00 07 00 00 00 00 32 32 32 32 0A 05 01 0E 05 00 00 00 00 01 18 41 31 31 50 6E 34 4D 46 78 64 74
VoiceData1=43 0B 0B 2A 63 58 00 00 02 00 4E 03 01 06 00 00 50 00 05 00 0E 45 0A 0D 2A 63 58 00 00 22 08 09 03 00 01 00 04 51 00 01 00 0B 4E 49 33 37 63 3A 00 00 33 0C 0B 03 03 01 00 02 5C 01 06 09 06 40 15 07 2C 63 4F 00 00 1E 00 00 00 00 07 00 03 63 00 01 00 08 42 0B 00 27 63 4F 00 00 20 00 09 00 00 03 00 01 4C 00 01 01 00 40 15 07 2C 63 4F 00 00 1E 00 00 00 00 07 00 02 63 00 01 00 03 63 63 63 63 32 32 32 32 08 07 01 23 00 00 00 01 00 02 18 50 69 61 6E 6F 42 6F 20 31 5C 55
MonoMode1=0
ModulationWheelRange1=99
ModulationWheelTarget1=1
@ -53,11 +53,11 @@ BreathControlTarget2=0
AftertouchRange2=99
AftertouchTarget2=0
BankNumber3=2
VoiceNumber3=2
MIDIChannel3=1
Volume3=87
VoiceNumber3=6
MIDIChannel3=0
Volume3=47
Pan3=0
Detune3=2
Detune3=0
Cutoff3=99
Resonance3=0
NoteLimitLow3=0
@ -69,7 +69,7 @@ PitchBendStep3=0
PortamentoMode3=0
PortamentoGlissando3=0
PortamentoTime3=0
VoiceData3=43 0B 0B 2A 63 58 00 00 02 00 4E 03 01 06 00 00 50 00 05 00 0E 45 0A 0D 2A 63 58 00 00 22 08 09 03 00 01 00 04 51 00 01 00 0B 4E 49 33 37 63 3A 00 00 33 0C 0B 03 03 01 00 02 5C 01 06 09 06 40 15 07 2C 63 4F 00 00 1E 00 00 00 00 07 00 03 63 00 01 00 08 42 0B 00 27 63 4F 00 00 20 00 09 00 00 03 00 01 4C 00 01 01 00 40 15 07 2C 63 4F 00 00 1E 00 00 00 00 07 00 02 63 00 01 00 03 63 63 63 63 32 32 32 32 08 07 01 23 00 00 00 01 00 02 18 50 69 61 6E 6F 42 6F 20 31 5C 55
VoiceData3=5F 15 00 26 5C 3E 60 00 30 05 0A 03 00 04 00 01 55 00 01 00 07 4D 48 48 22 32 62 00 00 27 00 14 00 01 00 00 04 5F 01 02 00 0A 59 3C 17 23 63 5E 00 00 2E 03 02 03 03 03 00 01 5E 01 00 00 00 5F 1C 06 23 63 59 4B 00 05 00 63 00 01 02 00 06 4F 00 05 00 09 58 5C 47 3F 63 43 5B 5A 27 0C 14 03 00 02 00 01 5A 00 01 00 05 58 1C 1B 32 63 5A 00 00 11 63 05 03 00 02 00 03 63 00 01 00 07 00 00 00 00 32 32 32 32 0A 05 01 0E 05 00 00 00 00 01 18 41 31 31 50 6E 34 4D 46 78 64 74
MonoMode3=0
ModulationWheelRange3=99
ModulationWheelTarget3=1

@ -119,6 +119,16 @@ void CConfig::Load (void)
m_nDoubleClickTimeout = m_Properties.GetNumber ("DoubleClickTimeout", 400);
m_nLongPressTimeout = m_Properties.GetNumber ("LongPressTimeout", 600);
m_nButtonPinPgmUp = m_Properties.GetNumber ("ButtonPinPgmUp", 0);
m_nButtonPinPgmDown = m_Properties.GetNumber ("ButtonPinPgmDown", 0);
m_nButtonPinTGUp = m_Properties.GetNumber ("ButtonPinTGUp", 0);
m_nButtonPinTGDown = m_Properties.GetNumber ("ButtonPinTGDown", 0);
m_ButtonActionPgmUp = m_Properties.GetString ("ButtonActionPgmUp", "");
m_ButtonActionPgmDown = m_Properties.GetString ("ButtonActionPgmDown", "");
m_ButtonActionTGUp = m_Properties.GetString ("ButtonActionTGUp", "");
m_ButtonActionTGDown = m_Properties.GetString ("ButtonActionTGDown", "");
m_nMIDIButtonCh = m_Properties.GetNumber ("MIDIButtonCh", 0);
m_nMIDIButtonNotes = m_Properties.GetNumber ("MIDIButtonNotes", 0);
m_nMIDIButtonPrev = m_Properties.GetNumber ("MIDIButtonPrev", 0);
@ -127,6 +137,11 @@ void CConfig::Load (void)
m_nMIDIButtonSelect = m_Properties.GetNumber ("MIDIButtonSelect", 0);
m_nMIDIButtonHome = m_Properties.GetNumber ("MIDIButtonHome", 0);
m_nMIDIButtonPgmUp = m_Properties.GetNumber ("MIDIButtonPgmUp", 0);
m_nMIDIButtonPgmDown = m_Properties.GetNumber ("MIDIButtonPgmDown", 0);
m_nMIDIButtonTGUp = m_Properties.GetNumber ("MIDIButtonTGUp", 0);
m_nMIDIButtonTGDown = m_Properties.GetNumber ("MIDIButtonTGDown", 0);
m_bEncoderEnabled = m_Properties.GetNumber ("EncoderEnabled", 0) != 0;
m_nEncoderPinClock = m_Properties.GetNumber ("EncoderPinClock", 10);
m_nEncoderPinData = m_Properties.GetNumber ("EncoderPinData", 9);
@ -134,6 +149,7 @@ void CConfig::Load (void)
m_bMIDIDumpEnabled = m_Properties.GetNumber ("MIDIDumpEnabled", 0) != 0;
m_bProfileEnabled = m_Properties.GetNumber ("ProfileEnabled", 0) != 0;
m_bPerformanceSelectToLoad = m_Properties.GetNumber ("PerformanceSelectToLoad", 1) != 0;
m_bPerformanceSelectChannel = m_Properties.GetNumber ("PerformanceSelectChannel", 0);
}
const char *CConfig::GetSoundDevice (void) const
@ -351,6 +367,46 @@ unsigned CConfig::GetLongPressTimeout (void) const
return m_nLongPressTimeout;
}
unsigned CConfig::GetButtonPinPgmUp (void) const
{
return m_nButtonPinPgmUp;
}
unsigned CConfig::GetButtonPinPgmDown (void) const
{
return m_nButtonPinPgmDown;
}
unsigned CConfig::GetButtonPinTGUp (void) const
{
return m_nButtonPinTGUp;
}
unsigned CConfig::GetButtonPinTGDown (void) const
{
return m_nButtonPinTGDown;
}
const char *CConfig::GetButtonActionPgmUp (void) const
{
return m_ButtonActionPgmUp.c_str();
}
const char *CConfig::GetButtonActionPgmDown (void) const
{
return m_ButtonActionPgmDown.c_str();
}
const char *CConfig::GetButtonActionTGUp (void) const
{
return m_ButtonActionTGUp.c_str();
}
const char *CConfig::GetButtonActionTGDown (void) const
{
return m_ButtonActionTGDown.c_str();
}
unsigned CConfig::GetMIDIButtonCh (void) const
{
return m_nMIDIButtonCh;
@ -386,6 +442,26 @@ unsigned CConfig::GetMIDIButtonHome (void) const
return m_nMIDIButtonHome;
}
unsigned CConfig::GetMIDIButtonPgmUp (void) const
{
return m_nMIDIButtonPgmUp;
}
unsigned CConfig::GetMIDIButtonPgmDown (void) const
{
return m_nMIDIButtonPgmDown;
}
unsigned CConfig::GetMIDIButtonTGUp (void) const
{
return m_nMIDIButtonTGUp;
}
unsigned CConfig::GetMIDIButtonTGDown (void) const
{
return m_nMIDIButtonTGDown;
}
bool CConfig::GetEncoderEnabled (void) const
{
return m_bEncoderEnabled;
@ -416,4 +492,7 @@ bool CConfig::GetPerformanceSelectToLoad (void) const
return m_bPerformanceSelectToLoad;
}
unsigned CConfig::GetPerformanceSelectChannel (void) const
{
return m_bPerformanceSelectChannel;
}

@ -123,6 +123,19 @@ public:
unsigned GetDoubleClickTimeout (void) const;
unsigned GetLongPressTimeout (void) const;
// GPIO Button Program and TG Selection
// GPIO pin numbers are chip numbers, not header positions
unsigned GetButtonPinPgmUp (void) const;
unsigned GetButtonPinPgmDown (void) const;
unsigned GetButtonPinTGUp (void) const;
unsigned GetButtonPinTGDown (void) const;
// Action type for buttons: "click", "doubleclick", "longpress", ""
const char *GetButtonActionPgmUp (void) const;
const char *GetButtonActionPgmDown (void) const;
const char *GetButtonActionTGUp (void) const;
const char *GetButtonActionTGDown (void) const;
// MIDI Button Navigation
unsigned GetMIDIButtonCh (void) const;
unsigned GetMIDIButtonNotes (void) const;
@ -131,6 +144,12 @@ public:
unsigned GetMIDIButtonBack (void) const;
unsigned GetMIDIButtonSelect (void) const;
unsigned GetMIDIButtonHome (void) const;
// MIDI Button Program and TG Selection
unsigned GetMIDIButtonPgmUp (void) const;
unsigned GetMIDIButtonPgmDown (void) const;
unsigned GetMIDIButtonTGUp (void) const;
unsigned GetMIDIButtonTGDown (void) const;
// KY-040 Rotary Encoder
// GPIO pin numbers are chip numbers, not header positions
@ -144,6 +163,7 @@ public:
// Load performance mode. 0 for load just rotating encoder, 1 load just when Select is pushed
bool GetPerformanceSelectToLoad (void) const;
unsigned GetPerformanceSelectChannel (void) const;
private:
CPropertiesFatFsFile m_Properties;
@ -189,12 +209,20 @@ private:
unsigned m_nButtonPinSelect;
unsigned m_nButtonPinHome;
unsigned m_nButtonPinShortcut;
unsigned m_nButtonPinPgmUp;
unsigned m_nButtonPinPgmDown;
unsigned m_nButtonPinTGUp;
unsigned m_nButtonPinTGDown;
std::string m_ButtonActionPrev;
std::string m_ButtonActionNext;
std::string m_ButtonActionBack;
std::string m_ButtonActionSelect;
std::string m_ButtonActionHome;
std::string m_ButtonActionPgmUp;
std::string m_ButtonActionPgmDown;
std::string m_ButtonActionTGUp;
std::string m_ButtonActionTGDown;
unsigned m_nDoubleClickTimeout;
unsigned m_nLongPressTimeout;
@ -206,6 +234,10 @@ private:
unsigned m_nMIDIButtonBack;
unsigned m_nMIDIButtonSelect;
unsigned m_nMIDIButtonHome;
unsigned m_nMIDIButtonPgmUp;
unsigned m_nMIDIButtonPgmDown;
unsigned m_nMIDIButtonTGUp;
unsigned m_nMIDIButtonTGDown;
bool m_bEncoderEnabled;
unsigned m_nEncoderPinClock;
@ -214,8 +246,7 @@ private:
bool m_bMIDIDumpEnabled;
bool m_bProfileEnabled;
bool m_bPerformanceSelectToLoad;
unsigned m_bPerformanceSelectChannel;
};
#endif

@ -191,10 +191,25 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
case MIDI_NOTE_ON:
if (nLength < 3)
{
break;
break;
}
m_pUI->UIMIDICmdHandler (ucChannel, ucStatus & 0xF0, pMessage[1], pMessage[2]);
break;
case MIDI_PROGRAM_CHANGE:
// Check for performance PC messages
if( m_pConfig->GetMIDIRXProgramChange() )
{
unsigned nPerfCh = m_pSynthesizer->GetPerformanceSelectChannel();
if( nPerfCh != Disabled)
{
if ((ucChannel == nPerfCh) || (nPerfCh == OmniMode))
{
//printf("Performance Select Channel %d\n", nPerfCh);
m_pSynthesizer->ProgramChangePerformance (pMessage[1]);
}
}
}
break;
}
// Process MIDI for each Tone Generator
@ -336,9 +351,11 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
break;
case MIDI_PROGRAM_CHANGE:
// do program change only if enabled in config
if( m_pConfig->GetMIDIRXProgramChange() )
// 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);
}
break;
case MIDI_PITCH_BEND: {

@ -24,11 +24,12 @@
#include <circle/types.h>
// MIDI CC numbers go 0 to 127.
// NB: 0 is treated as "unused" so CC=0 won't work
// 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 ccToMidiPin(c) (((c)==0)?0:((c)+MIDI_PINS))
#define MidiPinToCC(p) (((p)>=MIDI_PINS)?((p)-MIDI_PINS):0)
#define isMidiPin(p) (((p)>=MIDI_PINS)?1:0)
class CMIDIPin

@ -159,6 +159,8 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt,
// END setup reverb
SetParameter (ParameterCompressorEnable, 1);
SetPerformanceSelectChannel(m_pConfig->GetPerformanceSelectChannel());
};
bool CMiniDexed::Initialize (void)
@ -179,6 +181,20 @@ bool CMiniDexed::Initialize (void)
m_bUseSerial = true;
}
if (m_pConfig->GetMIDIRXProgramChange())
{
int nPerfCh = GetParameter(ParameterPerformanceSelectChannel);
if (nPerfCh == CMIDIDevice::Disabled) {
LOGNOTE("Program Change: Enabled for Voices");
} else if (nPerfCh == CMIDIDevice::OmniMode) {
LOGNOTE("Program Change: Enabled for Performances (Omni)");
} else {
LOGNOTE("Program Change: Enabled for Performances (CH %d)", nPerfCh+1);
}
} else {
LOGNOTE("Program Change: Disabled");
}
for (unsigned i = 0; i < CConfig::ToneGenerators; i++)
{
@ -459,6 +475,22 @@ void CMiniDexed::ProgramChange (unsigned nProgram, unsigned nTG)
m_UI.ParameterChanged ();
}
void CMiniDexed::ProgramChangePerformance (unsigned nProgram)
{
if (m_nParameter[ParameterPerformanceSelectChannel] != CMIDIDevice::Disabled)
{
// Program Change messages change Performances.
unsigned nLastPerformance = m_PerformanceConfig.GetLastPerformance();
// GetLastPerformance actually returns 1-indexed, number of performances
if (nProgram < nLastPerformance - 1)
{
SetNewPerformance(nProgram);
}
m_UI.ParameterChanged ();
}
}
void CMiniDexed::SetVolume (unsigned nVolume, unsigned nTG)
{
nVolume=constrain((int)nVolume,0,127);
@ -755,6 +787,10 @@ void CMiniDexed::SetParameter (TParameter Parameter, int nValue)
m_ReverbSpinLock.Release ();
break;
case ParameterPerformanceSelectChannel:
// Nothing more to do
break;
default:
assert (0);
break;
@ -1110,6 +1146,30 @@ void CMiniDexed::ProcessSound (void)
#endif
unsigned CMiniDexed::GetPerformanceSelectChannel (void)
{
// Stores and returns Select Channel using MIDI Device Channel definitions
return (unsigned) GetParameter (ParameterPerformanceSelectChannel);
}
void CMiniDexed::SetPerformanceSelectChannel (unsigned uCh)
{
// Turns a configuration setting to MIDI Device Channel definitions
// Mirrors the logic in Performance Config for handling MIDI channel configuration
if (uCh == 0)
{
SetParameter (ParameterPerformanceSelectChannel, CMIDIDevice::Disabled);
}
else if (uCh < CMIDIDevice::Channels)
{
SetParameter (ParameterPerformanceSelectChannel, uCh - 1);
}
else
{
SetParameter (ParameterPerformanceSelectChannel, CMIDIDevice::OmniMode);
}
}
bool CMiniDexed::SavePerformance (bool bSaveAsDeault)
{
m_bSavePerformance = true;
@ -1432,8 +1492,6 @@ unsigned CMiniDexed::GetLastPerformance()
return m_PerformanceConfig.GetLastPerformance();
}
unsigned CMiniDexed::GetActualPerformanceID()
{
return m_PerformanceConfig.GetActualPerformanceID();

@ -67,6 +67,7 @@ public:
void BankSelectMSB (unsigned nBankMSB, unsigned nTG);
void BankSelectLSB (unsigned nBankLSB, unsigned nTG);
void ProgramChange (unsigned nProgram, unsigned nTG);
void ProgramChangePerformance (unsigned nProgram);
void SetVolume (unsigned nVolume, unsigned nTG);
void SetPan (unsigned nPan, unsigned nTG); // 0 .. 127
void SetMasterTune (int nMasterTune, unsigned nTG); // -99 .. 99
@ -125,7 +126,9 @@ public:
bool DoSetNewPerformance (void);
bool GetPerformanceSelectToLoad(void);
bool SavePerformance (bool bSaveAsDeault);
unsigned GetPerformanceSelectChannel (void);
void SetPerformanceSelectChannel (unsigned uCh);
// Must match the order in CUIMenu::TParameter
enum TParameter
{
@ -137,6 +140,7 @@ public:
ParameterReverbLowPass,
ParameterReverbDiffusion,
ParameterReverbLevel,
ParameterPerformanceSelectChannel,
ParameterUnknown
};

@ -16,11 +16,24 @@ EngineType=1
# MIDI
MIDIBaudRate=31250
#MIDIThru=umidi1,ttyS1
MIDIRXProgramChange=1
IgnoreAllNotesOff=0
MIDIAutoVoiceDumpOnPC=1
HeaderlessSysExVoices=0
# Program Change enable
# 0 = Ignore all Program Change messages.
# 1 = Respond to Program Change messages.
MIDIRXProgramChange=1
# Program Change mode
# 0 = Only recognise Program Change 0-31.
# 1 = Support 0-127 across four consecutive banks.
# NB: Only relevant if PerformanceSelectChannel=0
ExpandPCAcrossBanks=1
# Program Change action:
# 0 = Program Change messages select voices on the channel associated with each TG.
# 1-16 = Program Change messages on this channel select performances.
# >16 = Program Change messages on ANY channel select performances.
# NB: In performance mode, all Program Change messages on other channels are ignored.
PerformanceSelectChannel=0
# HD44780 LCD
LCDEnabled=1
@ -61,24 +74,38 @@ ButtonActionHome=doubleclick
ButtonPinShortcut=11
# (Shortcut doesn't have an action)
# GPIO Program/TG Selection
# Any buttons set to 0 will be ignored
ButtonPinPgmUp=0
ButtonActionPgmUp=
ButtonPinPgmDown=0
ButtonActionPgmDown=
ButtonPinTGUp=0
ButtonActionTGUp=
ButtonPinTGDown=0
ButtonActionTGDown=
# Timeouts in milliseconds for double click and long press
DoubleClickTimeout=400
LongPressTimeout=400
# MIDI Button Navigation
# Specify MIDI CC to act as a button
# Specify MIDI CC to act as a button (0 = ununsed, so don't use CC 0)
# NB: Off < 64 < ON
# CC channel: 0=OFF; 1-16 MIDI Ch; >16 Omni
# If MIDIButtonNotes>0 then treat MIDIButton
# numbers as MIDI Note numbers note CC numbers.
# If MIDIButtonNotes>0 then treat MIDIButton numbers as MIDI
# Note numbers, triggered with NoteOn/NoteOff, not CC numbers.
MIDIButtonCh=0
MIDIButtonNotes=0
MIDIButtonNotes=1
MIDIButtonPrev=00
MIDIButtonNext=02
MIDIButtonBack=03
MIDIButtonSelect=04
MIDIButtonHome=06
MIDIButtonPrev=0
MIDIButtonNext=0
MIDIButtonBack=0
MIDIButtonSelect=0
MIDIButtonHome=0
MIDIButtonPgmUp=0
MIDIButtonPgmDown=0
MIDIButtonTGUp=0
MIDIButtonTGDown=0
# KY-040 Rotary Encoder
EncoderEnabled=1

@ -76,7 +76,7 @@ boolean CUIButton::Initialize (unsigned pinNumber, unsigned doubleClickTimeout,
{
if (isMidiPin(m_pinNumber))
{
LOGDBG("MIDI Button on pin: %d (%x)", m_pinNumber, m_pinNumber);
LOGDBG("MIDI Button on msg: %d (%x)", MidiPinToCC(m_pinNumber), MidiPinToCC(m_pinNumber));
m_midipin = new CMIDIPin (m_pinNumber);
} else {
LOGDBG("GPIO Button on pin: %d (%x)", m_pinNumber, m_pinNumber);
@ -263,8 +263,13 @@ CUIButtons::CUIButtons (
unsigned backPin, const char *backAction,
unsigned selectPin, const char *selectAction,
unsigned homePin, const char *homeAction,
unsigned pgmUpPin, const char *pgmUpAction,
unsigned pgmDownPin, const char *pgmDownAction,
unsigned TGUpPin, const char *TGUpAction,
unsigned TGDownPin, const char *TGDownAction,
unsigned doubleClickTimeout, unsigned longPressTimeout,
unsigned notesMidi, unsigned prevMidi, unsigned nextMidi, unsigned backMidi, unsigned selectMidi, unsigned homeMidi
unsigned notesMidi, unsigned prevMidi, unsigned nextMidi, unsigned backMidi, unsigned selectMidi, unsigned homeMidi,
unsigned pgmUpMidi, unsigned pgmDownMidi, unsigned TGUpMidi, unsigned TGDownMidi
)
: m_doubleClickTimeout(doubleClickTimeout),
m_longPressTimeout(longPressTimeout),
@ -278,12 +283,24 @@ CUIButtons::CUIButtons (
m_selectAction(CUIButton::triggerTypeFromString(selectAction)),
m_homePin(homePin),
m_homeAction(CUIButton::triggerTypeFromString(homeAction)),
m_pgmUpPin(pgmUpPin),
m_pgmUpAction(CUIButton::triggerTypeFromString(pgmUpAction)),
m_pgmDownPin(pgmDownPin),
m_pgmDownAction(CUIButton::triggerTypeFromString(pgmDownAction)),
m_TGUpPin(TGUpPin),
m_TGUpAction(CUIButton::triggerTypeFromString(TGUpAction)),
m_TGDownPin(TGDownPin),
m_TGDownAction(CUIButton::triggerTypeFromString(TGDownAction)),
m_notesMidi(notesMidi),
m_prevMidi(ccToMidiPin(prevMidi)),
m_nextMidi(ccToMidiPin(nextMidi)),
m_backMidi(ccToMidiPin(backMidi)),
m_selectMidi(ccToMidiPin(selectMidi)),
m_homeMidi(ccToMidiPin(homeMidi)),
m_pgmUpMidi(ccToMidiPin(pgmUpMidi)),
m_pgmDownMidi(ccToMidiPin(pgmDownMidi)),
m_TGUpMidi(ccToMidiPin(TGUpMidi)),
m_TGDownMidi(ccToMidiPin(TGDownMidi)),
m_eventHandler (0),
m_lastTick (0)
{
@ -315,14 +332,16 @@ boolean CUIButtons::Initialize (void)
// 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_prevMidi, m_nextMidi, m_backMidi, m_selectMidi, m_homeMidi
m_prevPin, m_nextPin, m_backPin, m_selectPin, m_homePin, m_pgmUpPin, m_pgmDownPin, m_TGUpPin, m_TGDownPin,
m_prevMidi, m_nextMidi, m_backMidi, m_selectMidi, m_homeMidi, m_pgmUpMidi, m_pgmDownMidi, m_TGUpMidi, m_TGDownMidi
};
CUIButton::BtnTrigger triggers[MAX_BUTTONS] = {
// Normal buttons
m_prevAction, m_nextAction, m_backAction, m_selectAction, m_homeAction,
m_pgmUpAction, m_pgmDownAction, m_TGUpAction, m_TGDownAction,
// MIDI Buttons only support a single click (at present)
CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick
CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick,
CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick
};
CUIButton::BtnEvent events[MAX_BUTTONS] = {
// Normal buttons
@ -331,12 +350,20 @@ boolean CUIButtons::Initialize (void)
CUIButton::BtnEventBack,
CUIButton::BtnEventSelect,
CUIButton::BtnEventHome,
CUIButton::BtnEventPgmUp,
CUIButton::BtnEventPgmDown,
CUIButton::BtnEventTGUp,
CUIButton::BtnEventTGDown,
// MIDI buttons
CUIButton::BtnEventPrev,
CUIButton::BtnEventNext,
CUIButton::BtnEventBack,
CUIButton::BtnEventSelect,
CUIButton::BtnEventHome
CUIButton::BtnEventHome,
CUIButton::BtnEventPgmUp,
CUIButton::BtnEventPgmDown,
CUIButton::BtnEventTGUp,
CUIButton::BtnEventTGDown
};
// Setup normal GPIO buttons first
@ -352,6 +379,7 @@ boolean CUIButtons::Initialize (void)
for (unsigned j=0; j<MAX_BUTTONS; j++) {
if (m_buttons[j].getPinNumber() == pins[i]) {
// This pin is already assigned
LOGNOTE("Note: GPIO pin %d is already assigned", pins[i]);
break;
}
else if (m_buttons[j].getPinNumber() == 0) {

@ -27,8 +27,8 @@
#define BUTTONS_UPDATE_NUM_TICKS 100
#define DEBOUNCE_TIME 100
#define MAX_GPIO_BUTTONS 5
#define MAX_MIDI_BUTTONS 5
#define MAX_GPIO_BUTTONS 9 // 5 UI buttons, 4 Program/TG Select buttons
#define MAX_MIDI_BUTTONS 9
#define MAX_BUTTONS (MAX_GPIO_BUTTONS+MAX_MIDI_BUTTONS)
class CUIButtons;
@ -52,7 +52,11 @@ public:
BtnEventBack = 3,
BtnEventSelect = 4,
BtnEventHome = 5,
BtnEventUnknown = 6
BtnEventPgmUp = 6,
BtnEventPgmDown = 7,
BtnEventTGUp = 8,
BtnEventTGDown = 9,
BtnEventUnknown = 10
};
CUIButton (void);
@ -113,8 +117,13 @@ public:
unsigned backPin, const char *backAction,
unsigned selectPin, const char *selectAction,
unsigned homePin, const char *homeAction,
unsigned pgmUpPin, const char *pgmUpAction,
unsigned pgmDownPin, const char *pgmDownAction,
unsigned TGUpPin, const char *TGUpAction,
unsigned TGDownPin, const char *TGDownAction,
unsigned doubleClickTimeout, unsigned longPressTimeout,
unsigned notesMidi, unsigned prevMidi, unsigned nextMidi, unsigned backMidi, unsigned selectMidi, unsigned homeMidi
unsigned notesMidi, unsigned prevMidi, unsigned nextMidi, unsigned backMidi, unsigned selectMidi, unsigned homeMidi,
unsigned pgmUpMidi, unsigned pgmDownMidi, unsigned TGUpMidi, unsigned TGDownMidi
);
~CUIButtons (void);
@ -149,6 +158,16 @@ private:
unsigned m_homePin;
CUIButton::BtnTrigger m_homeAction;
// Program and TG Selection buttons
unsigned m_pgmUpPin;
CUIButton::BtnTrigger m_pgmUpAction;
unsigned m_pgmDownPin;
CUIButton::BtnTrigger m_pgmDownAction;
unsigned m_TGUpPin;
CUIButton::BtnTrigger m_TGUpAction;
unsigned m_TGDownPin;
CUIButton::BtnTrigger m_TGDownAction;
// MIDI button configuration
unsigned m_notesMidi;
unsigned m_prevMidi;
@ -156,6 +175,11 @@ private:
unsigned m_backMidi;
unsigned m_selectMidi;
unsigned m_homeMidi;
unsigned m_pgmUpMidi;
unsigned m_pgmDownMidi;
unsigned m_TGUpMidi;
unsigned m_TGDownMidi;
BtnEventHandler *m_eventHandler;
void *m_eventParam;

@ -31,6 +31,7 @@
#include <assert.h>
using namespace std;
LOGMODULE ("uimenu");
const CUIMenu::TMenuItem CUIMenu::s_MenuRoot[] =
{
@ -212,7 +213,8 @@ const CUIMenu::TParameter CUIMenu::s_GlobalParameter[CMiniDexed::ParameterUnknow
{0, 99, 1}, // ParameterReverbLowDamp
{0, 99, 1}, // ParameterReverbLowPass
{0, 99, 1}, // ParameterReverbDiffusion
{0, 99, 1} // ParameterReverbLevel
{0, 99, 1}, // ParameterReverbLevel
{0, CMIDIDevice::ChannelUnknown-1, 1, ToMIDIChannel} // ParameterPerformanceSelectChannel
};
// must match CMiniDexed::TTGParameter
@ -323,7 +325,8 @@ const CUIMenu::TMenuItem CUIMenu::s_PerformanceMenu[] =
{
{"Load", PerformanceMenu, 0, 0},
{"Save", MenuHandler, s_SaveMenu},
{"Delete", PerformanceMenu, 0, 1},
{"Delete", PerformanceMenu, 0, 1},
{"PCCH", EditGlobalParameter, 0, CMiniDexed::ParameterPerformanceSelectChannel},
{0}
};
@ -402,6 +405,16 @@ void CUIMenu::EventHandler (TMenuEvent Event)
EventHandler (MenuEventUpdate);
break;
case MenuEventPgmUp:
case MenuEventPgmDown:
PgmUpDownHandler(Event);
break;
case MenuEventTGUp:
case MenuEventTGDown:
TGUpDownHandler(Event);
break;
default:
(*m_pParentMenu[m_nCurrentMenuItem].Handler) (this, Event);
break;
@ -604,7 +617,9 @@ void CUIMenu::EditProgramNumber (CUIMenu *pUIMenu, TMenuEvent Event)
return;
}
string voiceName = pUIMenu->m_pMiniDexed->GetVoiceName (nTG); // Skip empty voices
// Skip empty voices.
// Use same criteria in PgmUpDownHandler() too.
string voiceName = pUIMenu->m_pMiniDexed->GetVoiceName (nTG);
if (voiceName == "EMPTY "
|| voiceName == " "
|| voiceName == "----------"
@ -1188,6 +1203,147 @@ void CUIMenu::OPShortcutHandler (TMenuEvent Event)
}
}
void CUIMenu::PgmUpDownHandler (TMenuEvent Event)
{
if (m_pMiniDexed->GetParameter (CMiniDexed::ParameterPerformanceSelectChannel) != CMIDIDevice::Disabled)
{
// Program Up/Down acts on performances
unsigned nLastPerformance = m_pMiniDexed->GetLastPerformance();
unsigned nPerformance = m_pMiniDexed->GetActualPerformanceID();
//LOGNOTE("Performance actual=%d, last=%d", nPerformance, nLastPerformance);
if (Event == MenuEventPgmDown)
{
if (nPerformance > 0)
{
m_nSelectedPerformanceID = nPerformance-1;
m_pMiniDexed->SetNewPerformance(m_nSelectedPerformanceID);
//LOGNOTE("Performance new=%d, last=%d", m_nSelectedPerformanceID, nLastPerformance);
}
}
else
{
if (nPerformance < nLastPerformance-1)
{
m_nSelectedPerformanceID = nPerformance+1;
m_pMiniDexed->SetNewPerformance(m_nSelectedPerformanceID);
//LOGNOTE("Performance new=%d, last=%d", m_nSelectedPerformanceID, nLastPerformance);
}
}
}
else
{
// Program Up/Down acts on voices within a TG.
// If we're not in the root menu, then see if we are already in a TG menu,
// then find the current TG number. Otherwise assume TG1 (nTG=0).
unsigned nTG = 0;
if (m_MenuStackMenu[0] == s_MainMenu && (m_pCurrentMenu == s_TGMenu) || (m_MenuStackMenu[1] == s_TGMenu)) {
nTG = m_nMenuStackSelection[0];
}
assert (nTG < CConfig::ToneGenerators);
int nPgm = m_pMiniDexed->GetTGParameter (CMiniDexed::TGParameterProgram, nTG);
assert (Event == MenuEventPgmDown || Event == MenuEventPgmUp);
if (Event == MenuEventPgmDown)
{
//LOGNOTE("PgmDown");
if (--nPgm < 0)
{
// Switch down a voice bank and set to the last voice
nPgm = CSysExFileLoader::VoicesPerBank-1;
int nVB = m_pMiniDexed->GetTGParameter(CMiniDexed::TGParameterVoiceBank, nTG);
nVB = m_pMiniDexed->GetSysExFileLoader ()->GetNextBankDown(nVB);
m_pMiniDexed->SetTGParameter (CMiniDexed::TGParameterVoiceBank, nVB, nTG);
}
m_pMiniDexed->SetTGParameter (CMiniDexed::TGParameterProgram, nPgm, nTG);
}
else
{
//LOGNOTE("PgmUp");
if (++nPgm > (int) CSysExFileLoader::VoicesPerBank-1)
{
// Switch up a voice bank and reset to voice 0
nPgm = 0;
int nVB = m_pMiniDexed->GetTGParameter(CMiniDexed::TGParameterVoiceBank, nTG);
nVB = m_pMiniDexed->GetSysExFileLoader ()->GetNextBankUp(nVB);
m_pMiniDexed->SetTGParameter (CMiniDexed::TGParameterVoiceBank, nVB, nTG);
}
m_pMiniDexed->SetTGParameter (CMiniDexed::TGParameterProgram, nPgm, nTG);
}
// Skip empty voices.
// Use same criteria in EditProgramNumber () too.
string voiceName = m_pMiniDexed->GetVoiceName (nTG);
if (voiceName == "EMPTY "
|| voiceName == " "
|| voiceName == "----------"
|| voiceName == "~~~~~~~~~~" )
{
if (Event == MenuEventPgmUp) {
PgmUpDownHandler (MenuEventPgmUp);
}
if (Event == MenuEventPgmDown) {
PgmUpDownHandler (MenuEventPgmDown);
}
}
}
}
void CUIMenu::TGUpDownHandler (TMenuEvent Event)
{
// This will update the menus to position it for the next TG up or down
unsigned nTG = 0;
if (CConfig::ToneGenerators <= 1) {
// Nothing to do if only a single TG
return;
}
// If we're not in the root menu, then see if we are already in a TG menu,
// then find the current TG number. Otherwise assume TG1 (nTG=0).
if (m_MenuStackMenu[0] == s_MainMenu && (m_pCurrentMenu == s_TGMenu) || (m_MenuStackMenu[1] == s_TGMenu)) {
nTG = m_nMenuStackSelection[0];
}
assert (nTG < CConfig::ToneGenerators);
assert (Event == MenuEventTGDown || Event == MenuEventTGUp);
if (Event == MenuEventTGDown)
{
//LOGNOTE("TGDown");
if (nTG > 0) {
nTG--;
}
}
else
{
//LOGNOTE("TGUp");
if (nTG < CConfig::ToneGenerators - 1) {
nTG++;
}
}
// Set menu to the appropriate TG menu as follows:
// Top = Root
// Menu [0] = Main
// Menu [1] = TG Menu
m_pParentMenu = s_MainMenu;
m_pCurrentMenu = s_TGMenu;
m_nCurrentMenuItem = nTG;
m_nCurrentSelection = 0;
m_nCurrentParameter = nTG;
m_nCurrentMenuDepth = 1;
// Place the main menu on the stack with Root as the parent
m_MenuStackParent[0] = s_MenuRoot;
m_MenuStackMenu[0] = s_MainMenu;
m_nMenuStackItem[0] = 0;
m_nMenuStackSelection[0] = nTG;
m_nMenuStackParameter[0] = 0;
EventHandler (MenuEventUpdate);
}
void CUIMenu::TimerHandler (TKernelTimerHandle hTimer, void *pParam, void *pContext)
{
CUIMenu *pThis = static_cast<CUIMenu *> (pContext);

@ -45,6 +45,10 @@ public:
MenuEventStepUp,
MenuEventPressAndStepDown,
MenuEventPressAndStepUp,
MenuEventPgmUp,
MenuEventPgmDown,
MenuEventTGUp,
MenuEventTGDown,
MenuEventUnknown
};
@ -112,6 +116,9 @@ private:
void TGShortcutHandler (TMenuEvent Event);
void OPShortcutHandler (TMenuEvent Event);
void PgmUpDownHandler (TMenuEvent Event);
void TGUpDownHandler (TMenuEvent Event);
static void TimerHandler (TKernelTimerHandle hTimer, void *pParam, void *pContext);
static void InputTxt (CUIMenu *pUIMenu, TMenuEvent Event);

@ -115,6 +115,14 @@ bool CUserInterface::Initialize (void)
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 (),
@ -122,7 +130,11 @@ bool CUserInterface::Initialize (void)
m_pConfig->GetMIDIButtonNext (),
m_pConfig->GetMIDIButtonBack (),
m_pConfig->GetMIDIButtonSelect (),
m_pConfig->GetMIDIButtonHome ()
m_pConfig->GetMIDIButtonHome (),
m_pConfig->GetMIDIButtonPgmUp (),
m_pConfig->GetMIDIButtonPgmDown (),
m_pConfig->GetMIDIButtonTGUp (),
m_pConfig->GetMIDIButtonTGDown ()
);
assert (m_pUIButtons);
@ -320,6 +332,22 @@ void CUserInterface::UIButtonsEventHandler (CUIButton::BtnEvent Event)
m_Menu.EventHandler (CUIMenu::MenuEventHome);
break;
case CUIButton::BtnEventPgmUp:
m_Menu.EventHandler (CUIMenu::MenuEventPgmUp);
break;
case CUIButton::BtnEventPgmDown:
m_Menu.EventHandler (CUIMenu::MenuEventPgmDown);
break;
case CUIButton::BtnEventTGUp:
m_Menu.EventHandler (CUIMenu::MenuEventTGUp);
break;
case CUIButton::BtnEventTGDown:
m_Menu.EventHandler (CUIMenu::MenuEventTGDown);
break;
default:
break;
}

Loading…
Cancel
Save