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

pull/500/head
diyelectromusic 2 years ago
parent 8593dc955f
commit a234380539
  1. 6
      src/config.cpp
  2. 2
      src/config.h
  3. 2
      src/mididevice.cpp
  4. 1
      src/midipin.h
  5. 41
      src/minidexed.cpp
  6. 2
      src/minidexed.h
  7. 45
      src/minidexed.ini
  8. 2
      src/uibuttons.cpp
  9. 116
      src/uimenu.cpp

@ -139,6 +139,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_bPerformanceProgramChange = m_Properties.GetNumber ("PerformanceProgramChange", 0) != 0;
}
const char *CConfig::GetSoundDevice (void) const
@ -475,3 +476,8 @@ bool CConfig::GetPerformanceSelectToLoad (void) const
{
return m_bPerformanceSelectToLoad;
}
bool CConfig::GetPerformanceProgramChange (void) const
{
return m_bPerformanceProgramChange;
}

@ -162,6 +162,7 @@ public:
// Load performance mode. 0 for load just rotating encoder, 1 load just when Select is pushed
bool GetPerformanceSelectToLoad (void) const;
bool GetPerformanceProgramChange (void) const;
private:
CPropertiesFatFsFile m_Properties;
@ -243,6 +244,7 @@ private:
bool m_bMIDIDumpEnabled;
bool m_bProfileEnabled;
bool m_bPerformanceSelectToLoad;
bool m_bPerformanceProgramChange;
};
#endif

@ -338,7 +338,7 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
case MIDI_PROGRAM_CHANGE:
// do program change only if enabled in config
if( m_pConfig->GetMIDIRXProgramChange() )
m_pSynthesizer->ProgramChange (pMessage[1], nTG);
m_pSynthesizer->ProgramChangePerformance (pMessage[1], nTG);
break;
case MIDI_PITCH_BEND: {

@ -24,6 +24,7 @@
#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

@ -412,6 +412,8 @@ void CMiniDexed::BankSelectLSB (unsigned nBankLSB, unsigned nTG)
void CMiniDexed::ProgramChange (unsigned nProgram, unsigned nTG)
{
assert (m_pConfig);
// Program Change messages change voice on specified TG
unsigned nBankOffset;
bool bPCAcrossBanks = m_pConfig->GetExpandPCAcrossBanks();
@ -458,6 +460,39 @@ void CMiniDexed::ProgramChange (unsigned nProgram, unsigned nTG)
m_UI.ParameterChanged ();
}
void CMiniDexed::ProgramChangePerformance (unsigned nProgram, unsigned nTG)
{
assert (m_pConfig);
if (m_pConfig-GetPerformanceProgramChange())
{
// Program Change messages change Performances.
//
// Only pay attention to PCs received on the MIDI channel
// associated with TG1.
//
// Note: as performances store the MIDI channnel, if
// the MIDI channel for TG1 changes, then further
// PCs will only be actioned on the new channel number...
if (nTG == 0)
{
unsigned nLastPerformance = m_PerformanceConfig.GetLastPerformance();
// GetLastPerformance actually returns 1-indexed, number of performances
if (nProgram < nLastPerformance - 1)
{
SetNewPerformance(nProgram);
}
}
}
else
{
ProgramChange(nProgram, nTG);
}
m_UI.ParameterChanged ();
}
void CMiniDexed::SetVolume (unsigned nVolume, unsigned nTG)
{
nVolume=constrain((int)nVolume,0,127);
@ -1431,7 +1466,11 @@ unsigned CMiniDexed::GetLastPerformance()
return m_PerformanceConfig.GetLastPerformance();
}
bool CMiniDexed::GetPerformanceProgramChange (void)
{
assert (m_pConfig);
return m_pConfig->GetPerformanceProgramChange();
}
unsigned CMiniDexed::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, unsigned nTG);
void SetVolume (unsigned nVolume, unsigned nTG);
void SetPan (unsigned nPan, unsigned nTG); // 0 .. 127
void SetMasterTune (int nMasterTune, unsigned nTG); // -99 .. 99
@ -125,6 +126,7 @@ public:
bool DoSetNewPerformance (void);
bool GetPerformanceSelectToLoad(void);
bool SavePerformance (bool bSaveAsDeault);
bool GetPerformanceProgramChange (void);
// Must match the order in CUIMenu::TParameter
enum TParameter

@ -18,7 +18,20 @@ MIDIRXProgramChange=1
IgnoreAllNotesOff=0
MIDIAutoVoiceDumpOnPC=1
HeaderlessSysExVoices=0
# Program Change mode
# 0 = Only recognise Program Change 0-31.
# 1 = Support 0-127 across four consecutive banks.
# NB: Ignored if PerformanceProgramChange=1
ExpandPCAcrossBanks=1
# Program Change action:
# 0 = Select voices
# 1 = Select performances
#
# NB: If Perfromances, then only reacts to Program Change
# messages received on the MIDI channel for TG1.
# Warning: Make sure all perfromances use the same MIDI
# channel for TG1 and that MIDI is enabled.
PerformanceProgramChange=0
# HD44780 LCD
LCDEnabled=1
@ -59,24 +72,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);

@ -1203,57 +1203,85 @@ void CUIMenu::OPShortcutHandler (TMenuEvent Event)
void CUIMenu::PgmUpDownHandler (TMenuEvent Event)
{
// If we're on anything apart from the root menu,
// then find the current TG number. Otherwise assume TG1 (nTG=0).
unsigned nTG = 0;
if (m_MenuStackMenu[0] == s_MainMenu) {
nTG = m_nMenuStackSelection[0];
}
assert (nTG < CConfig::ToneGenerators);
int nPgm = m_pMiniDexed->GetTGParameter (CMiniDexed::TGParameterProgram, nTG);
assert (Event == MenuEventPgmDown || Event == MenuEventPgmUp);
if (Event == MenuEventPgmDown)
if (m_pMiniDexed->GetPerformanceProgramChange())
{
//LOGNOTE("PgmDown");
if (--nPgm < 0)
// 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)
{
// 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);
if (nPerformance > 0)
{
m_pMiniDexed->SetNewPerformance(nPerformance-1);
LOGNOTE("Performance new=%d, last=%d", nPerformance-1, nLastPerformance);
}
}
else
{
if (nPerformance < nLastPerformance-1)
{
m_pMiniDexed->SetNewPerformance(nPerformance+1);
LOGNOTE("Performance new=%d, last=%d", nPerformance+1, nLastPerformance);
}
}
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);
// Program Up/Down acts on voices within a TG.
// If we're on anything apart from the root menu,
// then find the current TG number. Otherwise assume TG1 (nTG=0).
unsigned nTG = 0;
if (m_MenuStackMenu[0] == s_MainMenu) {
nTG = m_nMenuStackSelection[0];
}
m_pMiniDexed->SetTGParameter (CMiniDexed::TGParameterProgram, nPgm, nTG);
}
assert (nTG < CConfig::ToneGenerators);
// 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);
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);
}
if (Event == MenuEventPgmDown) {
PgmUpDownHandler (MenuEventPgmDown);
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);
}
}
}
}
@ -1268,9 +1296,9 @@ void CUIMenu::TGUpDownHandler (TMenuEvent Event)
return;
}
// If we're on anything apart from the root menu,
// 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) {
if (m_MenuStackMenu[0] == s_MainMenu && (m_pCurrentMenu == s_TGMenu) || (m_MenuStackMenu[1] == s_TGMenu)) {
nTG = m_nMenuStackSelection[0];
}

Loading…
Cancel
Save