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_bMIDIDumpEnabled = m_Properties.GetNumber ("MIDIDumpEnabled", 0) != 0;
m_bProfileEnabled = m_Properties.GetNumber ("ProfileEnabled", 0) != 0; m_bProfileEnabled = m_Properties.GetNumber ("ProfileEnabled", 0) != 0;
m_bPerformanceSelectToLoad = m_Properties.GetNumber ("PerformanceSelectToLoad", 1) != 0; m_bPerformanceSelectToLoad = m_Properties.GetNumber ("PerformanceSelectToLoad", 1) != 0;
m_bPerformanceProgramChange = m_Properties.GetNumber ("PerformanceProgramChange", 0) != 0;
} }
const char *CConfig::GetSoundDevice (void) const const char *CConfig::GetSoundDevice (void) const
@ -475,3 +476,8 @@ bool CConfig::GetPerformanceSelectToLoad (void) const
{ {
return m_bPerformanceSelectToLoad; 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 // Load performance mode. 0 for load just rotating encoder, 1 load just when Select is pushed
bool GetPerformanceSelectToLoad (void) const; bool GetPerformanceSelectToLoad (void) const;
bool GetPerformanceProgramChange (void) const;
private: private:
CPropertiesFatFsFile m_Properties; CPropertiesFatFsFile m_Properties;
@ -243,6 +244,7 @@ private:
bool m_bMIDIDumpEnabled; bool m_bMIDIDumpEnabled;
bool m_bProfileEnabled; bool m_bProfileEnabled;
bool m_bPerformanceSelectToLoad; bool m_bPerformanceSelectToLoad;
bool m_bPerformanceProgramChange;
}; };
#endif #endif

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

@ -24,6 +24,7 @@
#include <circle/types.h> #include <circle/types.h>
// MIDI CC numbers go 0 to 127. // 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. // Normal GPIO pins are below 100.
// So use a "pin number" of 128 + MIDI CC message for a "MIDI Pin" // So use a "pin number" of 128 + MIDI CC message for a "MIDI Pin"
#define MIDI_PINS 128 #define MIDI_PINS 128

@ -412,6 +412,8 @@ void CMiniDexed::BankSelectLSB (unsigned nBankLSB, unsigned nTG)
void CMiniDexed::ProgramChange (unsigned nProgram, unsigned nTG) void CMiniDexed::ProgramChange (unsigned nProgram, unsigned nTG)
{ {
assert (m_pConfig); assert (m_pConfig);
// Program Change messages change voice on specified TG
unsigned nBankOffset; unsigned nBankOffset;
bool bPCAcrossBanks = m_pConfig->GetExpandPCAcrossBanks(); bool bPCAcrossBanks = m_pConfig->GetExpandPCAcrossBanks();
@ -458,6 +460,39 @@ void CMiniDexed::ProgramChange (unsigned nProgram, unsigned nTG)
m_UI.ParameterChanged (); 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) void CMiniDexed::SetVolume (unsigned nVolume, unsigned nTG)
{ {
nVolume=constrain((int)nVolume,0,127); nVolume=constrain((int)nVolume,0,127);
@ -1431,7 +1466,11 @@ unsigned CMiniDexed::GetLastPerformance()
return m_PerformanceConfig.GetLastPerformance(); return m_PerformanceConfig.GetLastPerformance();
} }
bool CMiniDexed::GetPerformanceProgramChange (void)
{
assert (m_pConfig);
return m_pConfig->GetPerformanceProgramChange();
}
unsigned CMiniDexed::GetActualPerformanceID() unsigned CMiniDexed::GetActualPerformanceID()
{ {

@ -67,6 +67,7 @@ public:
void BankSelectMSB (unsigned nBankMSB, unsigned nTG); void BankSelectMSB (unsigned nBankMSB, unsigned nTG);
void BankSelectLSB (unsigned nBankLSB, unsigned nTG); void BankSelectLSB (unsigned nBankLSB, unsigned nTG);
void ProgramChange (unsigned nProgram, unsigned nTG); void ProgramChange (unsigned nProgram, unsigned nTG);
void ProgramChangePerformance (unsigned nProgram, unsigned nTG);
void SetVolume (unsigned nVolume, unsigned nTG); void SetVolume (unsigned nVolume, unsigned nTG);
void SetPan (unsigned nPan, unsigned nTG); // 0 .. 127 void SetPan (unsigned nPan, unsigned nTG); // 0 .. 127
void SetMasterTune (int nMasterTune, unsigned nTG); // -99 .. 99 void SetMasterTune (int nMasterTune, unsigned nTG); // -99 .. 99
@ -125,6 +126,7 @@ public:
bool DoSetNewPerformance (void); bool DoSetNewPerformance (void);
bool GetPerformanceSelectToLoad(void); bool GetPerformanceSelectToLoad(void);
bool SavePerformance (bool bSaveAsDeault); bool SavePerformance (bool bSaveAsDeault);
bool GetPerformanceProgramChange (void);
// Must match the order in CUIMenu::TParameter // Must match the order in CUIMenu::TParameter
enum TParameter enum TParameter

@ -18,7 +18,20 @@ MIDIRXProgramChange=1
IgnoreAllNotesOff=0 IgnoreAllNotesOff=0
MIDIAutoVoiceDumpOnPC=1 MIDIAutoVoiceDumpOnPC=1
HeaderlessSysExVoices=0 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 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 # HD44780 LCD
LCDEnabled=1 LCDEnabled=1
@ -59,24 +72,38 @@ ButtonActionHome=doubleclick
ButtonPinShortcut=11 ButtonPinShortcut=11
# (Shortcut doesn't have an action) # (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 # Timeouts in milliseconds for double click and long press
DoubleClickTimeout=400 DoubleClickTimeout=400
LongPressTimeout=400 LongPressTimeout=400
# MIDI Button Navigation # 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 # NB: Off < 64 < ON
# CC channel: 0=OFF; 1-16 MIDI Ch; >16 Omni # CC channel: 0=OFF; 1-16 MIDI Ch; >16 Omni
# If MIDIButtonNotes>0 then treat MIDIButton # If MIDIButtonNotes>0 then treat MIDIButton numbers as MIDI
# numbers as MIDI Note numbers note CC numbers. # Note numbers, triggered with NoteOn/NoteOff, not CC numbers.
MIDIButtonCh=0 MIDIButtonCh=0
MIDIButtonNotes=0 MIDIButtonNotes=0
MIDIButtonNotes=1 MIDIButtonPrev=0
MIDIButtonPrev=00 MIDIButtonNext=0
MIDIButtonNext=02 MIDIButtonBack=0
MIDIButtonBack=03 MIDIButtonSelect=0
MIDIButtonSelect=04 MIDIButtonHome=0
MIDIButtonHome=06 MIDIButtonPgmUp=0
MIDIButtonPgmDown=0
MIDIButtonTGUp=0
MIDIButtonTGDown=0
# KY-040 Rotary Encoder # KY-040 Rotary Encoder
EncoderEnabled=1 EncoderEnabled=1

@ -76,7 +76,7 @@ boolean CUIButton::Initialize (unsigned pinNumber, unsigned doubleClickTimeout,
{ {
if (isMidiPin(m_pinNumber)) 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); m_midipin = new CMIDIPin (m_pinNumber);
} else { } else {
LOGDBG("GPIO Button on pin: %d (%x)", m_pinNumber, m_pinNumber); 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) void CUIMenu::PgmUpDownHandler (TMenuEvent Event)
{ {
// If we're on anything apart from the root menu, if (m_pMiniDexed->GetPerformanceProgramChange())
// 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)
{ {
//LOGNOTE("PgmDown"); // Program Up/Down acts on performances
if (--nPgm < 0) 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 if (nPerformance > 0)
nPgm = CSysExFileLoader::VoicesPerBank-1; {
int nVB = m_pMiniDexed->GetTGParameter(CMiniDexed::TGParameterVoiceBank, nTG); m_pMiniDexed->SetNewPerformance(nPerformance-1);
nVB = m_pMiniDexed->GetSysExFileLoader ()->GetNextBankDown(nVB); LOGNOTE("Performance new=%d, last=%d", nPerformance-1, nLastPerformance);
m_pMiniDexed->SetTGParameter (CMiniDexed::TGParameterVoiceBank, nVB, nTG); }
}
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 else
{ {
//LOGNOTE("PgmUp"); // Program Up/Down acts on voices within a TG.
if (++nPgm > (int) CSysExFileLoader::VoicesPerBank-1)
{ // If we're on anything apart from the root menu,
// Switch up a voice bank and reset to voice 0 // then find the current TG number. Otherwise assume TG1 (nTG=0).
nPgm = 0; unsigned nTG = 0;
int nVB = m_pMiniDexed->GetTGParameter(CMiniDexed::TGParameterVoiceBank, nTG); if (m_MenuStackMenu[0] == s_MainMenu) {
nVB = m_pMiniDexed->GetSysExFileLoader ()->GetNextBankUp(nVB); nTG = m_nMenuStackSelection[0];
m_pMiniDexed->SetTGParameter (CMiniDexed::TGParameterVoiceBank, nVB, nTG);
} }
m_pMiniDexed->SetTGParameter (CMiniDexed::TGParameterProgram, nPgm, nTG); assert (nTG < CConfig::ToneGenerators);
}
// Skip empty voices. int nPgm = m_pMiniDexed->GetTGParameter (CMiniDexed::TGParameterProgram, nTG);
// Use same criteria in EditProgramNumber () too.
string voiceName = m_pMiniDexed->GetVoiceName (nTG); assert (Event == MenuEventPgmDown || Event == MenuEventPgmUp);
if (voiceName == "EMPTY " if (Event == MenuEventPgmDown)
|| voiceName == " " {
|| voiceName == "----------" //LOGNOTE("PgmDown");
|| voiceName == "~~~~~~~~~~" ) if (--nPgm < 0)
{ {
if (Event == MenuEventPgmUp) { // Switch down a voice bank and set to the last voice
PgmUpDownHandler (MenuEventPgmUp); 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) { else
PgmUpDownHandler (MenuEventPgmDown); {
//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; 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). // 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]; nTG = m_nMenuStackSelection[0];
} }

Loading…
Cancel
Save