Performance file handling (#581)

Implements #580

* Initial update in performance file handling.  This change makes the 6-digit number in the filename indicate a performance "voice number" in MiniDexed.  The external filename numbers will now match any Program Change messages using the common MIDI concept of user selecting 1..128 whilst internally they are treated as 0..127.  Note: in the case of performances, performance 1 (index 0) is the Default "performance.ini" file for backwards compatibility.

Also note that in this version, new performances, when saved, cannot occupy free slots between other performances - they are added to the end.

Even though the filename standard gives 6 digit numbers, the actual number of performances is still limited to 256.

* Start of subdirectory implementation for performance banks.

* Initial version with performance banks, selectable over MIDI only.

* Initial implementation of performance bank switching in the UI menu.

* Remove debug information, fix few bugs, including PgmUpDown handling and performance numbers out of range.

* Bugfixes for legacy cases when no performance directory exists plus some extra checks for saving and deleting performances.

* Remove verbose debug options (doh!)

* Fix a minor off-by-one error found in review.

* Bugfix - removed redundant legacy check that results in out of order performance files being skipped on load.

* Fix bug in MIDI button handling commands.

* Fix for issue where wrong performance is selected [L] on new save.

* Suggested update to UI to show bank/performance numbers.

* Make performance bank select asynchronous to MIDI and UI to stop corruptions on loading performances.

* Fix an assert that should be a run-time test.

* Ensure bank selection works when PCCH is not enabled, and that UI remains consistent when changing banks.

---------

Co-authored-by: Kevin <68612569+diyelectromusic@users.noreply.github.com>
pull/607/head
probonopd 3 months ago committed by GitHub
parent 753c205991
commit 4755c5d861
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 28
      src/mididevice.cpp
  2. 169
      src/minidexed.cpp
  3. 21
      src/minidexed.h
  4. 533
      src/performanceconfig.cpp
  5. 33
      src/performanceconfig.h
  6. 186
      src/uimenu.cpp
  7. 2
      src/uimenu.h
  8. 3
      src/userinterface.cpp

@ -184,9 +184,35 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
else
{
// Perform any MiniDexed level MIDI handling before specific Tone Generators
unsigned nPerfCh = m_pSynthesizer->GetPerformanceSelectChannel();
switch (ucType)
{
case MIDI_CONTROL_CHANGE:
// Check for performance PC messages
if (nPerfCh != Disabled)
{
if ((ucChannel == nPerfCh) || (nPerfCh == OmniMode))
{
if (pMessage[1] == MIDI_CC_BANK_SELECT_MSB)
{
m_pSynthesizer->BankSelectMSBPerformance (pMessage[2]);
}
else if (pMessage[1] == MIDI_CC_BANK_SELECT_LSB)
{
m_pSynthesizer->BankSelectLSBPerformance (pMessage[2]);
}
else
{
// Ignore any other CC messages at this time
}
}
}
if (nLength == 3)
{
m_pUI->UIMIDICmdHandler (ucChannel, ucStatus & 0xF0, pMessage[1], pMessage[2]);
}
break;
case MIDI_NOTE_OFF:
case MIDI_NOTE_ON:
if (nLength < 3)
@ -195,11 +221,11 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
}
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))

@ -53,8 +53,11 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt,
m_bSavePerformance (false),
m_bSavePerformanceNewFile (false),
m_bSetNewPerformance (false),
m_bSetNewPerformanceBank (false),
m_bSetFirstPerformance (false),
m_bDeletePerformance (false),
m_bLoadPerformanceBusy(false)
m_bLoadPerformanceBusy(false),
m_bLoadPerformanceBankBusy(false)
{
assert (m_pConfig);
@ -170,6 +173,8 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt,
SetParameter (ParameterCompressorEnable, 1);
SetPerformanceSelectChannel(m_pConfig->GetPerformanceSelectChannel());
SetParameter (ParameterPerformanceBank, 0);
};
bool CMiniDexed::Initialize (void)
@ -227,6 +232,7 @@ bool CMiniDexed::Initialize (void)
reverb_send_mixer->gain(i,mapfloat(m_nReverbSend[i],0,99,0.0f,1.0f));
}
m_PerformanceConfig.Init();
if (m_PerformanceConfig.Load ())
{
LoadPerformanceParameters();
@ -236,12 +242,6 @@ bool CMiniDexed::Initialize (void)
SetMIDIChannel (CMIDIDevice::OmniMode, 0);
}
// load performances file list, and attempt to create the performance folder
if (!m_PerformanceConfig.ListPerformances())
{
LOGERR ("Cannot create internal Performance folder, new performances can't be created");
}
// setup and start the sound device
if (!m_pSoundDevice->AllocateQueueFrames (m_pConfig->GetChunkSize ()))
{
@ -305,14 +305,30 @@ void CMiniDexed::Process (bool bPlugAndPlayUpdated)
m_bSavePerformanceNewFile = false;
}
if (m_bSetNewPerformance && !m_bLoadPerformanceBusy)
if (m_bSetNewPerformanceBank && !m_bLoadPerformanceBusy && !m_bLoadPerformanceBankBusy)
{
DoSetNewPerformanceBank ();
if (m_nSetNewPerformanceBankID == GetActualPerformanceBankID())
{
m_bSetNewPerformanceBank = false;
}
// If there is no pending SetNewPerformance already, then see if we need to find the first performance to load
// NB: If called from the UI, then there will not be a SetNewPerformance, so load the first existing one.
// If called from MIDI, there will probably be a SetNewPerformance alongside the Bank select.
if (!m_bSetNewPerformance && m_bSetFirstPerformance)
{
DoSetFirstPerformance();
}
}
if (m_bSetNewPerformance && !m_bSetNewPerformanceBank && !m_bLoadPerformanceBusy && !m_bLoadPerformanceBankBusy)
{
DoSetNewPerformance ();
if (m_nSetNewPerformanceID == GetActualPerformanceID())
{
m_bSetNewPerformance = false;
}
}
if(m_bDeletePerformance)
@ -392,6 +408,11 @@ CSysExFileLoader *CMiniDexed::GetSysExFileLoader (void)
return &m_SysExFileLoader;
}
CPerformanceConfig *CMiniDexed::GetPerformanceConfig (void)
{
return &m_PerformanceConfig;
}
void CMiniDexed::BankSelect (unsigned nBank, unsigned nTG)
{
nBank=constrain((int)nBank,0,16383);
@ -407,6 +428,20 @@ void CMiniDexed::BankSelect (unsigned nBank, unsigned nTG)
}
}
void CMiniDexed::BankSelectPerformance (unsigned nBank)
{
nBank=constrain((int)nBank,0,16383);
if (GetPerformanceConfig ()->IsValidPerformanceBank(nBank))
{
// Only change if we have the bank loaded
m_nVoiceBankIDPerformance = nBank;
SetNewPerformanceBank (nBank);
m_UI.ParameterChanged ();
}
}
void CMiniDexed::BankSelectMSB (unsigned nBankMSB, unsigned nTG)
{
nBankMSB=constrain((int)nBankMSB,0,127);
@ -422,6 +457,12 @@ void CMiniDexed::BankSelectMSB (unsigned nBankMSB, unsigned nTG)
m_nVoiceBankIDMSB[nTG] = nBankMSB;
}
void CMiniDexed::BankSelectMSBPerformance (unsigned nBankMSB)
{
nBankMSB=constrain((int)nBankMSB,0,127);
m_nVoiceBankIDMSBPerformance = nBankMSB;
}
void CMiniDexed::BankSelectLSB (unsigned nBankLSB, unsigned nTG)
{
nBankLSB=constrain((int)nBankLSB,0,127);
@ -435,6 +476,18 @@ void CMiniDexed::BankSelectLSB (unsigned nBankLSB, unsigned nTG)
BankSelect(nBank, nTG);
}
void CMiniDexed::BankSelectLSBPerformance (unsigned nBankLSB)
{
nBankLSB=constrain((int)nBankLSB,0,127);
unsigned nBank = m_nVoiceBankIDPerformance;
unsigned nBankMSB = m_nVoiceBankIDMSBPerformance;
nBank = (nBankMSB << 7) + nBankLSB;
// Now should have both MSB and LSB so enable the BankSelect
BankSelectPerformance(nBank);
}
void CMiniDexed::ProgramChange (unsigned nProgram, unsigned nTG)
{
assert (m_pConfig);
@ -489,10 +542,7 @@ 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)
if (m_PerformanceConfig.IsValidPerformance(nProgram))
{
SetNewPerformance(nProgram);
}
@ -800,6 +850,10 @@ void CMiniDexed::SetParameter (TParameter Parameter, int nValue)
// Nothing more to do
break;
case ParameterPerformanceBank:
BankSelectPerformance(nValue);
break;
default:
assert (0);
break;
@ -1181,10 +1235,17 @@ void CMiniDexed::SetPerformanceSelectChannel (unsigned uCh)
bool CMiniDexed::SavePerformance (bool bSaveAsDeault)
{
m_bSavePerformance = true;
m_bSaveAsDeault=bSaveAsDeault;
if (m_PerformanceConfig.GetInternalFolderOk())
{
m_bSavePerformance = true;
m_bSaveAsDeault=bSaveAsDeault;
return true;
return true;
}
else
{
return false;
}
}
bool CMiniDexed::DoSavePerformance (void)
@ -1501,6 +1562,16 @@ unsigned CMiniDexed::GetLastPerformance()
return m_PerformanceConfig.GetLastPerformance();
}
unsigned CMiniDexed::GetPerformanceBank()
{
return m_PerformanceConfig.GetPerformanceBank();
}
unsigned CMiniDexed::GetLastPerformanceBank()
{
return m_PerformanceConfig.GetLastPerformanceBank();
}
unsigned CMiniDexed::GetActualPerformanceID()
{
return m_PerformanceConfig.GetActualPerformanceID();
@ -1511,6 +1582,16 @@ void CMiniDexed::SetActualPerformanceID(unsigned nID)
m_PerformanceConfig.SetActualPerformanceID(nID);
}
unsigned CMiniDexed::GetActualPerformanceBankID()
{
return m_PerformanceConfig.GetActualPerformanceBankID();
}
void CMiniDexed::SetActualPerformanceBankID(unsigned nBankID)
{
m_PerformanceConfig.SetActualPerformanceBankID(nBankID);
}
bool CMiniDexed::SetNewPerformance(unsigned nID)
{
m_bSetNewPerformance = true;
@ -1519,6 +1600,20 @@ bool CMiniDexed::SetNewPerformance(unsigned nID)
return true;
}
bool CMiniDexed::SetNewPerformanceBank(unsigned nBankID)
{
m_bSetNewPerformanceBank = true;
m_nSetNewPerformanceBankID = nBankID;
return true;
}
void CMiniDexed::SetFirstPerformance(void)
{
m_bSetFirstPerformance = true;
return;
}
bool CMiniDexed::DoSetNewPerformance (void)
{
m_bLoadPerformanceBusy = true;
@ -1540,6 +1635,25 @@ bool CMiniDexed::DoSetNewPerformance (void)
}
}
bool CMiniDexed::DoSetNewPerformanceBank (void)
{
m_bLoadPerformanceBankBusy = true;
unsigned nBankID = m_nSetNewPerformanceBankID;
m_PerformanceConfig.SetNewPerformanceBank(nBankID);
m_bLoadPerformanceBankBusy = false;
return true;
}
void CMiniDexed::DoSetFirstPerformance(void)
{
unsigned nID = m_PerformanceConfig.FindFirstPerformance();
SetNewPerformance(nID);
m_bSetFirstPerformance = false;
return;
}
bool CMiniDexed::SavePerformanceNewFile ()
{
m_bSavePerformanceNewFile = m_PerformanceConfig.GetInternalFolderOk() && m_PerformanceConfig.CheckFreePerformanceSlot();
@ -1631,6 +1745,16 @@ void CMiniDexed::SetNewPerformanceName(std::string nName)
m_PerformanceConfig.SetNewPerformanceName(nName);
}
bool CMiniDexed::IsValidPerformance(unsigned nID)
{
return m_PerformanceConfig.IsValidPerformance(nID);
}
bool CMiniDexed::IsValidPerformanceBank(unsigned nBankID)
{
return m_PerformanceConfig.IsValidPerformanceBank(nBankID);
}
void CMiniDexed::SetVoiceName (std::string VoiceName, unsigned nTG)
{
assert (nTG < CConfig::ToneGenerators);
@ -1642,10 +1766,17 @@ void CMiniDexed::SetVoiceName (std::string VoiceName, unsigned nTG)
bool CMiniDexed::DeletePerformance(unsigned nID)
{
m_bDeletePerformance = true;
m_nDeletePerformanceID = nID;
if (m_PerformanceConfig.IsValidPerformance(nID) && m_PerformanceConfig.GetInternalFolderOk())
{
m_bDeletePerformance = true;
m_nDeletePerformanceID = nID;
return true;
return true;
}
else
{
return false;
}
}
bool CMiniDexed::DoDeletePerformance(void)

@ -62,10 +62,14 @@ public:
#endif
CSysExFileLoader *GetSysExFileLoader (void);
CPerformanceConfig *GetPerformanceConfig (void);
void BankSelect (unsigned nBank, unsigned nTG);
void BankSelectPerformance (unsigned nBank);
void BankSelectMSB (unsigned nBankMSB, unsigned nTG);
void BankSelectMSBPerformance (unsigned nBankMSB);
void BankSelectLSB (unsigned nBankLSB, unsigned nTG);
void BankSelectLSBPerformance (unsigned nBankLSB);
void ProgramChange (unsigned nProgram, unsigned nTG);
void ProgramChangePerformance (unsigned nProgram);
void SetVolume (unsigned nVolume, unsigned nTG);
@ -117,17 +121,27 @@ public:
std::string GetPerformanceFileName(unsigned nID);
std::string GetPerformanceName(unsigned nID);
unsigned GetLastPerformance();
unsigned GetPerformanceBank();
unsigned GetLastPerformanceBank();
unsigned GetActualPerformanceID();
void SetActualPerformanceID(unsigned nID);
unsigned GetActualPerformanceBankID();
void SetActualPerformanceBankID(unsigned nBankID);
bool SetNewPerformance(unsigned nID);
bool SetNewPerformanceBank(unsigned nBankID);
void SetFirstPerformance(void);
void DoSetFirstPerformance(void);
bool SavePerformanceNewFile ();
bool DoSavePerformanceNewFile (void);
bool DoSetNewPerformance (void);
bool DoSetNewPerformanceBank (void);
bool GetPerformanceSelectToLoad(void);
bool SavePerformance (bool bSaveAsDeault);
unsigned GetPerformanceSelectChannel (void);
void SetPerformanceSelectChannel (unsigned uCh);
bool IsValidPerformance(unsigned nID);
bool IsValidPerformanceBank(unsigned nBankID);
// Must match the order in CUIMenu::TParameter
enum TParameter
@ -141,6 +155,7 @@ public:
ParameterReverbDiffusion,
ParameterReverbLevel,
ParameterPerformanceSelectChannel,
ParameterPerformanceBank,
ParameterUnknown
};
@ -238,6 +253,8 @@ private:
unsigned m_nVoiceBankID[CConfig::ToneGenerators];
unsigned m_nVoiceBankIDMSB[CConfig::ToneGenerators];
unsigned m_nVoiceBankIDPerformance;
unsigned m_nVoiceBankIDMSBPerformance;
unsigned m_nProgram[CConfig::ToneGenerators];
unsigned m_nVolume[CConfig::ToneGenerators];
unsigned m_nPan[CConfig::ToneGenerators];
@ -305,9 +322,13 @@ private:
bool m_bSavePerformanceNewFile;
bool m_bSetNewPerformance;
unsigned m_nSetNewPerformanceID;
bool m_bSetNewPerformanceBank;
unsigned m_nSetNewPerformanceBankID;
bool m_bSetFirstPerformance;
bool m_bDeletePerformance;
unsigned m_nDeletePerformanceID;
bool m_bLoadPerformanceBusy;
bool m_bLoadPerformanceBankBusy;
bool m_bSaveAsDeault;
};

@ -28,8 +28,15 @@
LOGMODULE ("Performance");
//#define VERBOSE_DEBUG
#define PERFORMANCE_DIR "performance"
#define DEFAULT_PERFORMANCE_FILENAME "performance.ini"
#define DEFAULT_PERFORMANCE_NAME "Default"
#define DEFAULT_PERFORMANCE_BANK_NAME "Default"
CPerformanceConfig::CPerformanceConfig (FATFS *pFileSystem)
: m_Properties ("performance.ini", pFileSystem)
: m_Properties (DEFAULT_PERFORMANCE_FILENAME, pFileSystem)
{
m_pFileSystem = pFileSystem;
}
@ -38,6 +45,47 @@ CPerformanceConfig::~CPerformanceConfig (void)
{
}
bool CPerformanceConfig::Init (void)
{
// Check intermal performance directory exists
DIR Directory;
FRESULT Result;
//Check if internal "performance" directory exists
Result = f_opendir (&Directory, "SD:/" PERFORMANCE_DIR);
if (Result == FR_OK)
{
m_bPerformanceDirectoryExists=true;
Result = f_closedir (&Directory);
}
else
{
m_bPerformanceDirectoryExists = false;
}
// List banks if present
ListPerformanceBanks();
#ifdef VERBOSE_DEBUG
#warning "PerformanceConfig in verbose debug printing mode"
LOGNOTE("Testing loading of banks");
for (unsigned i=0; i<NUM_PERFORMANCE_BANKS; i++)
{
if (!m_PerformanceBankName[i].empty())
{
SetNewPerformanceBank(i);
SetNewPerformance(0);
}
}
#endif
// Set to default initial bank
SetNewPerformanceBank(0);
SetNewPerformance(0);
LOGNOTE ("Loaded Default Performance Bank - Last Performance: %d", m_nLastPerformance + 1); // Show "user facing" index
return true;
}
bool CPerformanceConfig::Load (void)
{
if (!m_Properties.Load ())
@ -715,44 +763,116 @@ bool CPerformanceConfig::VoiceDataFilled(unsigned nTG)
std::string CPerformanceConfig::GetPerformanceFileName(unsigned nID)
{
return m_nPerformanceFileName[nID];
assert (nID < NUM_PERFORMANCES);
std::string FileN = "";
if ((m_nPerformanceBank==0) && (nID == 0)) // in order to assure retrocompatibility
{
FileN += DEFAULT_PERFORMANCE_FILENAME;
}
else
{
// Build up from the index, "_", performance name, and ".ini"
// NB: Index on disk = index in memory + 1
std::string nIndex = "000000";
nIndex += std::to_string(nID+1);
nIndex = nIndex.substr(nIndex.length()-6,6);
FileN += nIndex;
FileN += "_";
FileN += m_PerformanceFileName[nID];
FileN += ".ini";
}
return FileN;
}
std::string CPerformanceConfig::GetPerformanceFullFilePath(unsigned nID)
{
assert (nID < NUM_PERFORMANCES);
std::string FileN = "SD:/";
if ((m_nPerformanceBank == 0) && (nID == 0))
{
// Special case for the legacy Bank 1/Default performance
FileN += DEFAULT_PERFORMANCE_FILENAME;
}
else
{
if (m_bPerformanceDirectoryExists)
{
FileN += PERFORMANCE_DIR;
FileN += AddPerformanceBankDirName(m_nPerformanceBank);
FileN += "/";
FileN += GetPerformanceFileName(nID);
}
}
return FileN;
}
std::string CPerformanceConfig::GetPerformanceName(unsigned nID)
{
if(nID == 0) // in order to assure retrocompatibility
assert (nID < NUM_PERFORMANCES);
if ((m_nPerformanceBank==0) && (nID == 0)) // in order to assure retrocompatibility
{
return "Default";
return DEFAULT_PERFORMANCE_NAME;
}
else
{
return m_nPerformanceFileName[nID].substr(0,m_nPerformanceFileName[nID].length()-4).substr(7,14);
return m_PerformanceFileName[nID];
}
}
unsigned CPerformanceConfig::GetLastPerformance()
{
return nLastPerformance;
return m_nLastPerformance;
}
unsigned CPerformanceConfig::GetLastPerformanceBank()
{
return m_nLastPerformanceBank;
}
unsigned CPerformanceConfig::GetActualPerformanceID()
{
return nActualPerformance;
return m_nActualPerformance;
}
void CPerformanceConfig::SetActualPerformanceID(unsigned nID)
{
nActualPerformance = nID;
assert (nID < NUM_PERFORMANCES);
m_nActualPerformance = nID;
}
unsigned CPerformanceConfig::GetActualPerformanceBankID()
{
return m_nActualPerformanceBank;
}
void CPerformanceConfig::SetActualPerformanceBankID(unsigned nBankID)
{
assert (nBankID < NUM_PERFORMANCE_BANKS);
m_nActualPerformanceBank = nBankID;
}
bool CPerformanceConfig::GetInternalFolderOk()
{
return nInternalFolderOk;
return m_bPerformanceDirectoryExists;
}
bool CPerformanceConfig::IsValidPerformance(unsigned nID)
{
if (nID < NUM_PERFORMANCES)
{
if (!m_PerformanceFileName[nID].empty())
{
return true;
}
}
return false;
}
bool CPerformanceConfig::CheckFreePerformanceSlot(void)
{
if (nLastPerformance < NUM_PERFORMANCES)
if (m_nLastPerformance < NUM_PERFORMANCES-1)
{
// There is a free slot...
return true;
@ -765,19 +885,36 @@ bool CPerformanceConfig::CheckFreePerformanceSlot(void)
bool CPerformanceConfig::CreateNewPerformanceFile(void)
{
if (nLastPerformance >= NUM_PERFORMANCES) {
if (!m_bPerformanceDirectoryExists)
{
// Nothing can be done if there is no performance directory
LOGNOTE("Performance directory does not exist");
return false;
}
if (m_nLastPerformance >= NUM_PERFORMANCES) {
// No space left for new performances
LOGWARN ("No space left for new performance");
return false;
}
// Note: New performances are created at the end of the currently selected bank.
// Sould we default to a new bank just for user-performances?
//
// There is the possibility of MIDI changing the Bank Number and the user
// not spotting the bank has changed...
//
// Another option would be to find empty slots in the current bank
// rather than always add at the end.
//
// Sorting this out is left for a future update.
std::string sPerformanceName = NewPerformanceName;
NewPerformanceName="";
nActualPerformance=nLastPerformance;
unsigned nNewPerformance = m_nLastPerformance + 1;
std::string nFileName;
std::string nPath;
std::string nIndex = "000000";
nIndex += std::to_string(++nLastFileIndex);
nIndex += std::to_string(nNewPerformance+1); // Index on disk = index in memory+1
nIndex = nIndex.substr(nIndex.length()-6,6);
@ -793,10 +930,11 @@ bool CPerformanceConfig::CreateNewPerformanceFile(void)
nFileName +=sPerformanceName.substr(0,14);
}
nFileName += ".ini";
m_nPerformanceFileName[nLastPerformance]= nFileName;
m_PerformanceFileName[nNewPerformance]= sPerformanceName;
nPath = "SD:/" ;
nPath += PERFORMANCE_DIR;
nPath += AddPerformanceBankDirName(m_nPerformanceBank);
nPath += "/";
nFileName = nPath + nFileName;
@ -804,17 +942,18 @@ bool CPerformanceConfig::CreateNewPerformanceFile(void)
FRESULT Result = f_open (&File, nFileName.c_str(), FA_WRITE | FA_CREATE_ALWAYS);
if (Result != FR_OK)
{
m_nPerformanceFileName[nLastPerformance]=nullptr;
m_PerformanceFileName[nNewPerformance]=nullptr;
return false;
}
if (f_close (&File) != FR_OK)
{
m_nPerformanceFileName[nLastPerformance]=nullptr;
m_PerformanceFileName[nNewPerformance]=nullptr;
return false;
}
nLastPerformance++;
m_nLastPerformance = nNewPerformance;
m_nActualPerformance = nNewPerformance;
new (&m_Properties) CPropertiesFatFsFile(nFileName.c_str(), m_pFileSystem);
return true;
@ -822,88 +961,124 @@ bool CPerformanceConfig::CreateNewPerformanceFile(void)
bool CPerformanceConfig::ListPerformances()
{
nInternalFolderOk=false;
nExternalFolderOk=false; // for future USB implementation
nLastPerformance=0;
nLastFileIndex=0;
m_nPerformanceFileName[nLastPerformance++]="performance.ini"; // in order to assure retrocompatibility
unsigned nPIndex;
DIR Directory;
FILINFO FileInfo;
FRESULT Result;
//Check if internal "performance" directory exists
Result = f_opendir (&Directory, "SD:/" PERFORMANCE_DIR);
if (Result == FR_OK)
// Clear any existing lists of performances
for (unsigned i=0; i<NUM_PERFORMANCES; i++)
{
nInternalFolderOk=true;
// Result = f_closedir (&Directory);
m_PerformanceFileName[i].clear();
}
else
m_nLastPerformance=0;
if (m_nPerformanceBank == 0)
{
// attenpt to create the folder
Result = f_mkdir("SD:/" PERFORMANCE_DIR);
nInternalFolderOk = (Result == FR_OK);
// The first bank is the default performance directory
m_PerformanceFileName[0]=DEFAULT_PERFORMANCE_NAME; // in order to assure retrocompatibility
}
if (nInternalFolderOk)
if (m_bPerformanceDirectoryExists)
{
Result = f_findfirst (&Directory, &FileInfo, "SD:/" PERFORMANCE_DIR, "*.ini");
DIR Directory;
FILINFO FileInfo;
FRESULT Result;
std::string PerfDir = "SD:/" PERFORMANCE_DIR + AddPerformanceBankDirName(m_nPerformanceBank);
#ifdef VERBOSE_DEBUG
LOGNOTE("Listing Performances from %s", PerfDir.c_str());
#endif
Result = f_opendir (&Directory, PerfDir.c_str());
if (Result != FR_OK)
{
return false;
}
unsigned nPIndex;
Result = f_findfirst (&Directory, &FileInfo, PerfDir.c_str(), "*.ini");
for (unsigned i = 0; Result == FR_OK && FileInfo.fname[0]; i++)
{
if (nLastPerformance >= NUM_PERFORMANCES) {
LOGNOTE ("Skipping performance %s", FileInfo.fname);
} else {
if (!(FileInfo.fattrib & (AM_HID | AM_SYS)))
if (!(FileInfo.fattrib & (AM_HID | AM_SYS)))
{
std::string OriFileName = FileInfo.fname;
size_t nLen = OriFileName.length();
if ( nLen > 8 && nLen <26 && strcmp(OriFileName.substr(6,1).c_str(), "_")==0)
{
std::string FileName = FileInfo.fname;
size_t nLen = FileName.length();
if ( nLen > 8 && nLen <26 && strcmp(FileName.substr(6,1).c_str(), "_")==0)
{
nPIndex=stoi(FileName.substr(0,6));
if(nPIndex > nLastFileIndex)
// Note: m_nLastPerformance - refers to the number (index) of the last performance in memory,
// which includes a default performance.
//
// Filenames on the disk start from 1 to match what the user might see in MIDI.
// So this means that actually file 000001_ will correspond to index position [0].
// For the default bank though, ID 1 is the default performance, so will already exist.
// m_PerformanceFileName[0] = default performance (file 000001)
// m_PerformanceFileName[1] = first available on-disk performance (file 000002)
//
// Note2: filenames assume 6 digits, underscore, name, finally ".ini"
// i.e. 123456_Performance Name.ini
//
nPIndex=stoi(OriFileName.substr(0,6));
if ((nPIndex < 1) || (nPIndex >= (NUM_PERFORMANCES+1)))
{
// Index is out of range - skip to next file
LOGNOTE ("Performance number out of range: %s (%d to %d)", FileInfo.fname, 1, NUM_PERFORMANCES);
}
else
{
// Convert from "user facing" 1..indexed number to internal 0..indexed
nPIndex = nPIndex-1;
if (m_PerformanceFileName[nPIndex].empty())
{
nLastFileIndex=nPIndex;
}
if(nPIndex > m_nLastPerformance)
{
m_nLastPerformance=nPIndex;
}
m_nPerformanceFileName[nLastPerformance++]= FileName;
}
std::string FileName = OriFileName.substr(0,OriFileName.length()-4).substr(7,14);
m_PerformanceFileName[nPIndex] = FileName;
#ifdef VERBOSE_DEBUG
LOGNOTE ("Loading performance %s (%d, %s)", OriFileName.c_str(), nPIndex, FileName.c_str());
#endif
}
else
{
LOGNOTE ("Duplicate performance %s", OriFileName.c_str());
}
}
}
}
Result = f_findnext (&Directory, &FileInfo);
}
// sort by performance number-name
if (nLastPerformance > 2)
{
sort (m_nPerformanceFileName+1, m_nPerformanceFileName + nLastPerformance); // default is always on first place. %%%%%%%%%%%%%%%%
}
f_closedir (&Directory);
}
LOGNOTE ("Number of Performances: %d", nLastPerformance);
return nInternalFolderOk;
}
return true;
}
void CPerformanceConfig::SetNewPerformance (unsigned nID)
{
nActualPerformance=nID;
std::string FileN = "";
if (nID != 0) // in order to assure retrocompatibility
assert (nID < NUM_PERFORMANCES);
m_nActualPerformance=nID;
std::string FileN = GetPerformanceFullFilePath(nID);
new (&m_Properties) CPropertiesFatFsFile(FileN.c_str(), m_pFileSystem);
#ifdef VERBOSE_DEBUG
LOGNOTE("Selecting Performance: %d (%s)", nID+1, FileN.c_str());
#endif
}
unsigned CPerformanceConfig::FindFirstPerformance (void)
{
for (int nID=0; nID < NUM_PERFORMANCES; nID++)
{
if (IsValidPerformance(nID))
{
FileN += PERFORMANCE_DIR;
FileN += "/";
return nID;
}
FileN += m_nPerformanceFileName[nID];
new (&m_Properties) CPropertiesFatFsFile(FileN.c_str(), m_pFileSystem);
}
return 0; // Even though 0 is a valid performance, not much else to do
}
std::string CPerformanceConfig::GetNewPerformanceDefaultName(void)
{
std::string nIndex = "000000";
nIndex += std::to_string(nLastFileIndex+1);
nIndex += std::to_string(m_nLastPerformance+1+1); // Convert from internal 0.. index to a file-based 1.. index to show the user
nIndex = nIndex.substr(nIndex.length()-6,6);
return "Perf" + nIndex;
}
@ -923,31 +1098,229 @@ void CPerformanceConfig::SetNewPerformanceName(std::string nName)
bool CPerformanceConfig::DeletePerformance(unsigned nID)
{
if (!m_bPerformanceDirectoryExists)
{
// Nothing can be done if there is no performance directory
LOGNOTE("Performance directory does not exist");
return false;
}
bool bOK = false;
if(nID == 0){return bOK;} // default (performance.ini at root directory) can't be deleted
if((m_nPerformanceBank == 0) && (nID == 0)){return bOK;} // default (performance.ini at root directory) can't be deleted
DIR Directory;
FILINFO FileInfo;
std::string FileN = "SD:/";
FileN += PERFORMANCE_DIR;
FileN += AddPerformanceBankDirName(m_nPerformanceBank);
FRESULT Result = f_findfirst (&Directory, &FileInfo, FileN.c_str(), m_nPerformanceFileName[nID].c_str());
FRESULT Result = f_findfirst (&Directory, &FileInfo, FileN.c_str(), GetPerformanceFileName(nID).c_str());
if (Result == FR_OK && FileInfo.fname[0])
{
FileN += "/";
FileN += m_nPerformanceFileName[nID];
FileN += GetPerformanceFileName(nID);
Result=f_unlink (FileN.c_str());
if (Result == FR_OK)
{
SetNewPerformance(0);
nActualPerformance =0;
m_nActualPerformance =0;
//nMenuSelectedPerformance=0;
m_nPerformanceFileName[nID]="ZZZZZZ";
sort (m_nPerformanceFileName+1, m_nPerformanceFileName + nLastPerformance); // test si va con -1 o no
--nLastPerformance;
m_nPerformanceFileName[nLastPerformance]=nullptr;
m_PerformanceFileName[nID].clear();
// If this was the last performance in the bank...
if (nID == m_nLastPerformance)
{
do
{
// Find the new last performance
m_nLastPerformance--;
} while (!IsValidPerformance(m_nLastPerformance) && (m_nLastPerformance > 0));
}
bOK=true;
}
else
{
LOGNOTE ("Failed to delete %s", FileN.c_str());
}
}
return bOK;
}
bool CPerformanceConfig::ListPerformanceBanks()
{
m_nPerformanceBank = 0;
m_nLastPerformance = 0;
m_nLastPerformanceBank = 0;
// Open performance directory
DIR Directory;
FILINFO FileInfo;
FRESULT Result;
Result = f_opendir (&Directory, "SD:/" PERFORMANCE_DIR);
if (Result != FR_OK)
{
// No performance directory, so no performance banks.
// So nothing else to do here
LOGNOTE ("No performance banks detected");
m_bPerformanceDirectoryExists = false;
return false;
}
unsigned nNumBanks = 0;
// Add in the default performance directory as the first bank
m_PerformanceBankName[0] = DEFAULT_PERFORMANCE_BANK_NAME;
nNumBanks = 1;
m_nLastPerformanceBank = 0;
// List directories with names in format 01_Perf Bank Name
Result = f_findfirst (&Directory, &FileInfo, "SD:/" PERFORMANCE_DIR, "*");
for (unsigned i = 0; Result == FR_OK && FileInfo.fname[0]; i++)
{
// Check to see if it is a directory
if ((FileInfo.fattrib & AM_DIR) != 0)
{
// Looking for Performance banks of the form: 01_Perf Bank Name
// So positions 0,1,2 = decimal digit
// position 3 = "_"
// positions 4.. = actual name
//
std::string OriFileName = FileInfo.fname;
size_t nLen = OriFileName.length();
if ( nLen > 4 && nLen <26 && strcmp(OriFileName.substr(3,1).c_str(), "_")==0)
{
unsigned nBankIndex = stoi(OriFileName.substr(0,3));
// Recall user index numbered 002..NUM_PERFORMANCE_BANKS
// NB: Bank 001 is reserved for the default performance directory
if ((nBankIndex > 0) && (nBankIndex <= NUM_PERFORMANCE_BANKS))
{
// Convert from "user facing" 1..indexed number to internal 0..indexed
nBankIndex = nBankIndex-1;
if (m_PerformanceBankName[nBankIndex].empty())
{
std::string BankName = OriFileName.substr(4,nLen);
m_PerformanceBankName[nBankIndex] = BankName;
#ifdef VERBOSE_DEBUG
LOGNOTE ("Found performance bank %s (%d, %s)", OriFileName.c_str(), nBankIndex, BankName.c_str());
#endif
nNumBanks++;
if (nBankIndex > m_nLastPerformanceBank)
{
m_nLastPerformanceBank = nBankIndex;
}
}
else
{
LOGNOTE ("Duplicate Performance Bank: %s", FileInfo.fname);
if (nBankIndex==0)
{
LOGNOTE ("(Bank 001 is the default performance directory)");
}
}
}
else
{
LOGNOTE ("Performance Bank number out of range: %s (%d to %d)", FileInfo.fname, 1, NUM_PERFORMANCE_BANKS);
}
}
else
{
#ifdef VERBOSE_DEBUG
LOGNOTE ("Skipping: %s", FileInfo.fname);
#endif
}
}
Result = f_findnext (&Directory, &FileInfo);
}
if (nNumBanks > 0)
{
LOGNOTE ("Number of Performance Banks: %d (last = %d)", nNumBanks, m_nLastPerformanceBank+1);
}
f_closedir (&Directory);
return true;
}
void CPerformanceConfig::SetNewPerformanceBank(unsigned nBankID)
{
assert (nBankID < NUM_PERFORMANCE_BANKS);
if (IsValidPerformanceBank(nBankID))
{
#ifdef VERBOSE_DEBUG
LOGNOTE("Selecting Performance Bank: %d", nBankID+1);
#endif
m_nPerformanceBank = nBankID;
m_nActualPerformanceBank = nBankID;
ListPerformances();
}
else
{
#ifdef VERBOSE_DEBUG
LOGNOTE("Not selecting invalid Performance Bank: %d", nBankID+1);
#endif
}
}
unsigned CPerformanceConfig::GetPerformanceBank(void)
{
return m_nPerformanceBank;
}
std::string CPerformanceConfig::GetPerformanceBankName(unsigned nBankID)
{
assert (nBankID < NUM_PERFORMANCE_BANKS);
if (IsValidPerformanceBank(nBankID))
{
return m_PerformanceBankName[nBankID];
}
else
{
return DEFAULT_PERFORMANCE_BANK_NAME;
}
}
std::string CPerformanceConfig::AddPerformanceBankDirName(unsigned nBankID)
{
assert (nBankID < NUM_PERFORMANCE_BANKS);
if (IsValidPerformanceBank(nBankID))
{
// Performance Banks directories in format "001_Bank Name"
std::string Index;
if (nBankID == 0)
{
// Legacy: Bank 1 is the default performance directory
return "";
}
if (nBankID < 9)
{
Index = "00" + std::to_string(nBankID+1);
}
else if (nBankID < 99)
{
Index = "0" + std::to_string(nBankID+1);
}
else
{
Index = std::to_string(nBankID+1);
}
return "/" + Index + "_" + m_PerformanceBankName[nBankID];
}
else
{
return "";
}
}
bool CPerformanceConfig::IsValidPerformanceBank(unsigned nBankID)
{
if (nBankID >= NUM_PERFORMANCE_BANKS) {
return false;
}
if (m_PerformanceBankName[nBankID].empty())
{
return false;
}
return true;
}

@ -27,14 +27,16 @@
#include <fatfs/ff.h>
#include <Properties/propertiesfatfsfile.h>
#define NUM_VOICE_PARAM 156
#define PERFORMANCE_DIR "performance"
#define NUM_PERFORMANCES 256
#define NUM_PERFORMANCES 128
#define NUM_PERFORMANCE_BANKS 128
class CPerformanceConfig // Performance configuration
{
public:
CPerformanceConfig (FATFS *pFileSystem);
~CPerformanceConfig (void);
bool Init (void);
bool Load (void);
@ -122,17 +124,30 @@ public:
bool ListPerformances();
//std::string m_DirName;
void SetNewPerformance (unsigned nID);
unsigned FindFirstPerformance (void);
std::string GetPerformanceFileName(unsigned nID);
std::string GetPerformanceFullFilePath(unsigned nID);
std::string GetPerformanceName(unsigned nID);
unsigned GetLastPerformance();
unsigned GetLastPerformanceBank();
void SetActualPerformanceID(unsigned nID);
unsigned GetActualPerformanceID();
void SetActualPerformanceBankID(unsigned nBankID);
unsigned GetActualPerformanceBankID();
bool CreateNewPerformanceFile(void);
bool GetInternalFolderOk();
std::string GetNewPerformanceDefaultName(void);
void SetNewPerformanceName(std::string nName);
bool DeletePerformance(unsigned nID);
bool CheckFreePerformanceSlot(void);
std::string AddPerformanceBankDirName(unsigned nBankID);
bool IsValidPerformance(unsigned nID);
bool ListPerformanceBanks(void);
void SetNewPerformanceBank(unsigned nBankID);
unsigned GetPerformanceBank(void);
std::string GetPerformanceBankName(unsigned nBankID);
bool IsValidPerformanceBank(unsigned nBankID);
private:
CPropertiesFatFsFile m_Properties;
@ -166,15 +181,17 @@ private:
unsigned m_nAftertouchRange[CConfig::ToneGenerators];
unsigned m_nAftertouchTarget[CConfig::ToneGenerators];
unsigned nLastPerformance;
unsigned nLastFileIndex;
unsigned nActualPerformance = 0;
unsigned m_nLastPerformance;
unsigned m_nActualPerformance = 0;
unsigned m_nActualPerformanceBank = 0;
unsigned m_nPerformanceBank;
unsigned m_nLastPerformanceBank;
bool m_bPerformanceDirectoryExists;
//unsigned nMenuSelectedPerformance = 0;
std::string m_nPerformanceFileName[NUM_PERFORMANCES];
std::string m_PerformanceFileName[NUM_PERFORMANCES];
std::string m_PerformanceBankName[NUM_PERFORMANCE_BANKS];
FATFS *m_pFileSystem;
bool nInternalFolderOk=false;
bool nExternalFolderOk=false; // for future USB implementation
std::string NewPerformanceName="";
bool m_bCompressorEnable;

@ -214,7 +214,8 @@ const CUIMenu::TParameter CUIMenu::s_GlobalParameter[CMiniDexed::ParameterUnknow
{0, 99, 1}, // ParameterReverbLowPass
{0, 99, 1}, // ParameterReverbDiffusion
{0, 99, 1}, // ParameterReverbLevel
{0, CMIDIDevice::ChannelUnknown-1, 1, ToMIDIChannel} // ParameterPerformanceSelectChannel
{0, CMIDIDevice::ChannelUnknown-1, 1, ToMIDIChannel}, // ParameterPerformanceSelectChannel
{0, NUM_PERFORMANCE_BANKS, 1} // ParameterPerformanceBank
};
// must match CMiniDexed::TTGParameter
@ -327,6 +328,7 @@ const CUIMenu::TMenuItem CUIMenu::s_PerformanceMenu[] =
{"Load", PerformanceMenu, 0, 0},
{"Save", MenuHandler, s_SaveMenu},
{"Delete", PerformanceMenu, 0, 1},
{"Bank", EditPerformanceBankNumber, 0, 0},
{"PCCH", EditGlobalParameter, 0, CMiniDexed::ParameterPerformanceSelectChannel},
{0}
};
@ -1211,24 +1213,43 @@ void CUIMenu::PgmUpDownHandler (TMenuEvent Event)
// Program Up/Down acts on performances
unsigned nLastPerformance = m_pMiniDexed->GetLastPerformance();
unsigned nPerformance = m_pMiniDexed->GetActualPerformanceID();
unsigned nStart = nPerformance;
//LOGNOTE("Performance actual=%d, last=%d", nPerformance, nLastPerformance);
if (Event == MenuEventPgmDown)
{
if (nPerformance > 0)
do
{
m_nSelectedPerformanceID = nPerformance-1;
m_pMiniDexed->SetNewPerformance(m_nSelectedPerformanceID);
//LOGNOTE("Performance new=%d, last=%d", m_nSelectedPerformanceID, nLastPerformance);
}
if (nPerformance == 0)
{
// Wrap around
nPerformance = nLastPerformance;
}
else if (nPerformance > 0)
{
--nPerformance;
}
} while ((m_pMiniDexed->IsValidPerformance(nPerformance) != true) && (nPerformance != nStart));
m_nSelectedPerformanceID = nPerformance;
m_pMiniDexed->SetNewPerformance(m_nSelectedPerformanceID);
//LOGNOTE("Performance new=%d, last=%d", m_nSelectedPerformanceID, nLastPerformance);
}
else
else // MenuEventPgmUp
{
if (nPerformance < nLastPerformance-1)
do
{
m_nSelectedPerformanceID = nPerformance+1;
m_pMiniDexed->SetNewPerformance(m_nSelectedPerformanceID);
//LOGNOTE("Performance new=%d, last=%d", m_nSelectedPerformanceID, nLastPerformance);
}
if (nPerformance == nLastPerformance)
{
// Wrap around
nPerformance = 0;
}
else if (nPerformance < nLastPerformance)
{
++nPerformance;
}
} while ((m_pMiniDexed->IsValidPerformance(nPerformance) != true) && (nPerformance != nStart));
m_nSelectedPerformanceID = nPerformance;
m_pMiniDexed->SetNewPerformance(m_nSelectedPerformanceID);
//LOGNOTE("Performance new=%d, last=%d", m_nSelectedPerformanceID, nLastPerformance);
}
}
else
@ -1366,7 +1387,15 @@ void CUIMenu::TimerHandlerNoBack (TKernelTimerHandle hTimer, void *pParam, void
void CUIMenu::PerformanceMenu (CUIMenu *pUIMenu, TMenuEvent Event)
{
bool bPerformanceSelectToLoad = pUIMenu->m_pMiniDexed->GetPerformanceSelectToLoad();
unsigned nLastPerformance = pUIMenu->m_pMiniDexed->GetLastPerformance();
unsigned nValue = pUIMenu->m_nSelectedPerformanceID;
unsigned nStart = nValue;
if (pUIMenu->m_pMiniDexed->IsValidPerformance(nValue) != true)
{
// A bank change has left the selected performance out of sync
nValue = pUIMenu->m_pMiniDexed->GetActualPerformanceID();
pUIMenu->m_nSelectedPerformanceID = nValue;
}
std::string Value;
if (Event == MenuEventUpdate)
@ -1380,17 +1409,25 @@ void CUIMenu::PerformanceMenu (CUIMenu *pUIMenu, TMenuEvent Event)
}
if(!pUIMenu->m_bPerformanceDeleteMode)
{
{
switch (Event)
{
case MenuEventUpdate:
break;
case MenuEventStepDown:
if (nValue > 0)
do
{
--nValue;
}
if (nValue == 0)
{
// Wrap around
nValue = nLastPerformance;
}
else if (nValue > 0)
{
--nValue;
}
} while ((pUIMenu->m_pMiniDexed->IsValidPerformance(nValue) != true) && (nValue != nStart));
pUIMenu->m_nSelectedPerformanceID = nValue;
if (!bPerformanceSelectToLoad && pUIMenu->m_nCurrentParameter==0)
{
@ -1399,10 +1436,18 @@ void CUIMenu::PerformanceMenu (CUIMenu *pUIMenu, TMenuEvent Event)
break;
case MenuEventStepUp:
if (++nValue > (unsigned) pUIMenu->m_pMiniDexed->GetLastPerformance()-1)
do
{
nValue = pUIMenu->m_pMiniDexed->GetLastPerformance()-1;
}
if (nValue == nLastPerformance)
{
// Wrap around
nValue = 0;
}
else if (nValue < nLastPerformance)
{
++nValue;
}
} while ((pUIMenu->m_pMiniDexed->IsValidPerformance(nValue) != true) && (nValue != nStart));
pUIMenu->m_nSelectedPerformanceID = nValue;
if (!bPerformanceSelectToLoad && pUIMenu->m_nCurrentParameter==0)
{
@ -1421,7 +1466,7 @@ void CUIMenu::PerformanceMenu (CUIMenu *pUIMenu, TMenuEvent Event)
break;
case 1:
if (pUIMenu->m_nSelectedPerformanceID != 0)
if (pUIMenu->m_pMiniDexed->IsValidPerformance(pUIMenu->m_nSelectedPerformanceID))
{
pUIMenu->m_bPerformanceDeleteMode=true;
pUIMenu->m_bConfirmDeletePerformance=false;
@ -1474,17 +1519,24 @@ void CUIMenu::PerformanceMenu (CUIMenu *pUIMenu, TMenuEvent Event)
if(!pUIMenu->m_bPerformanceDeleteMode)
{
Value = pUIMenu->m_pMiniDexed->GetPerformanceName(nValue);
unsigned nBankNum = pUIMenu->m_pMiniDexed->GetPerformanceBank();
std::string nPSelected = "";
std::string nPSelected = "000";
nPSelected += std::to_string(nBankNum+1); // Convert to user-facing bank number rather than index
nPSelected = nPSelected.substr(nPSelected.length()-3,3);
std::string nPPerf = "000";
nPPerf += std::to_string(nValue+1); // Convert to user-facing performance number rather than index
nPPerf = nPPerf.substr(nPPerf.length()-3,3);
nPSelected += ":"+nPPerf;
if(nValue == pUIMenu->m_pMiniDexed->GetActualPerformanceID())
{
nPSelected= "[L]";
nPSelected += " [L]";
}
pUIMenu->m_pUI->DisplayWrite (pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name, nPSelected.c_str(),
Value.c_str (),
(int) nValue > 0, (int) nValue < (int) pUIMenu->m_pMiniDexed->GetLastPerformance()-1);
Value.c_str (), true, true);
// (int) nValue > 0, (int) nValue < (int) pUIMenu->m_pMiniDexed->GetLastPerformance());
}
else
{
@ -1492,6 +1544,90 @@ void CUIMenu::PerformanceMenu (CUIMenu *pUIMenu, TMenuEvent Event)
}
}
void CUIMenu::EditPerformanceBankNumber (CUIMenu *pUIMenu, TMenuEvent Event)
{
bool bPerformanceSelectToLoad = pUIMenu->m_pMiniDexed->GetPerformanceSelectToLoad();
unsigned nLastPerformanceBank = pUIMenu->m_pMiniDexed->GetLastPerformanceBank();
unsigned nValue = pUIMenu->m_nSelectedPerformanceBankID;
unsigned nStart = nValue;
std::string Value;
switch (Event)
{
case MenuEventUpdate:
break;
case MenuEventStepDown:
do
{
if (nValue == 0)
{
// Wrap around
nValue = nLastPerformanceBank;
}
else if (nValue > 0)
{
--nValue;
}
} while ((pUIMenu->m_pMiniDexed->IsValidPerformanceBank(nValue) != true) && (nValue != nStart));
pUIMenu->m_nSelectedPerformanceBankID = nValue;
if (!bPerformanceSelectToLoad)
{
// Switch to the new bank and select the first performance voice
pUIMenu->m_pMiniDexed->SetParameter (CMiniDexed::ParameterPerformanceBank, nValue);
pUIMenu->m_pMiniDexed->SetFirstPerformance();
}
break;
case MenuEventStepUp:
do
{
if (nValue == nLastPerformanceBank)
{
// Wrap around
nValue = 0;
}
else if (nValue < nLastPerformanceBank)
{
++nValue;
}
} while ((pUIMenu->m_pMiniDexed->IsValidPerformanceBank(nValue) != true) && (nValue != nStart));
pUIMenu->m_nSelectedPerformanceBankID = nValue;
if (!bPerformanceSelectToLoad)
{
pUIMenu->m_pMiniDexed->SetParameter (CMiniDexed::ParameterPerformanceBank, nValue);
pUIMenu->m_pMiniDexed->SetFirstPerformance();
}
break;
case MenuEventSelect:
if (bPerformanceSelectToLoad)
{
pUIMenu->m_pMiniDexed->SetParameter (CMiniDexed::ParameterPerformanceBank, nValue);
pUIMenu->m_pMiniDexed->SetFirstPerformance();
}
break;
default:
return;
}
Value = pUIMenu->m_pMiniDexed->GetPerformanceConfig ()->GetPerformanceBankName(nValue);
std::string nPSelected = "000";
nPSelected += std::to_string(nValue+1); // Convert to user-facing number rather than index
nPSelected = nPSelected.substr(nPSelected.length()-3,3);
if(nValue == (unsigned)pUIMenu->m_pMiniDexed->GetParameter (CMiniDexed::ParameterPerformanceBank))
{
nPSelected += " [L]";
}
pUIMenu->m_pUI->DisplayWrite (pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name, nPSelected.c_str(),
Value.c_str (),
nValue > 0,
nValue < pUIMenu->m_pMiniDexed->GetLastPerformanceBank()-1);
}
void CUIMenu::InputTxt (CUIMenu *pUIMenu, TMenuEvent Event)
{
unsigned nTG=0;

@ -91,6 +91,7 @@ private:
static void EditTGParameterModulation (CUIMenu *pUIMenu, TMenuEvent Event);
static void PerformanceMenu (CUIMenu *pUIMenu, TMenuEvent Event);
static void SavePerformanceNewFile (CUIMenu *pUIMenu, TMenuEvent Event);
static void EditPerformanceBankNumber (CUIMenu *pUIMenu, TMenuEvent Event);
static std::string GetGlobalValueString (unsigned nParameter, int nValue);
static std::string GetTGValueString (unsigned nTGParameter, int nValue);
@ -169,6 +170,7 @@ private:
bool m_bPerformanceDeleteMode=false;
bool m_bConfirmDeletePerformance=false;
unsigned m_nSelectedPerformanceID =0;
unsigned m_nSelectedPerformanceBankID =0;
bool m_bSplashShow=false;
};

@ -388,13 +388,16 @@ void CUserInterface::UISetMIDIButtonChannel (unsigned uCh)
if (uCh == 0)
{
m_nMIDIButtonCh = CMIDIDevice::Disabled;
LOGNOTE("MIDI Button channel not set");
}
else if (uCh < CMIDIDevice::Channels)
{
m_nMIDIButtonCh = uCh - 1;
LOGNOTE("MIDI Button channel set to: %d", m_nMIDIButtonCh);
}
else
{
m_nMIDIButtonCh = CMIDIDevice::OmniMode;
LOGNOTE("MIDI Button channel set to: OMNI");
}
}
Loading…
Cancel
Save