diff --git a/src/config.cpp b/src/config.cpp index 5065d01..9a64f50 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -81,6 +81,8 @@ void CConfig::Load (void) m_bMIDIRXProgramChange = m_Properties.GetNumber ("MIDIRXProgramChange", 1) != 0; m_bIgnoreAllNotesOff = m_Properties.GetNumber ("IgnoreAllNotesOff", 0) != 0; m_bMIDIAutoVoiceDumpOnPC = m_Properties.GetNumber ("MIDIAutoVoiceDumpOnPC", 1) != 0; + m_bHeaderlessSysExVoices = m_Properties.GetNumber ("HeaderlessSysExVoices", 0) != 0; + m_bExpandPCAcrossBanks = m_Properties.GetNumber ("ExpandPCAcrossBanks", 1) != 0; m_bLCDEnabled = m_Properties.GetNumber ("LCDEnabled", 0) != 0; m_nLCDPinEnable = m_Properties.GetNumber ("LCDPinEnable", 4); @@ -194,6 +196,16 @@ bool CConfig::GetMIDIAutoVoiceDumpOnPC (void) const return m_bMIDIAutoVoiceDumpOnPC; } +bool CConfig::GetHeaderlessSysExVoices (void) const +{ + return m_bHeaderlessSysExVoices; +} + +bool CConfig::GetExpandPCAcrossBanks (void) const +{ + return m_bExpandPCAcrossBanks; +} + bool CConfig::GetLCDEnabled (void) const { return m_bLCDEnabled; diff --git a/src/config.h b/src/config.h index 09c0529..c34cdf5 100644 --- a/src/config.h +++ b/src/config.h @@ -78,6 +78,8 @@ public: bool GetMIDIRXProgramChange (void) const; // true if not specified bool GetIgnoreAllNotesOff (void) const; bool GetMIDIAutoVoiceDumpOnPC (void) const; // true if not specified + bool GetHeaderlessSysExVoices (void) const; // false if not specified + bool GetExpandPCAcrossBanks (void) const; // true if not specified // HD44780 LCD // GPIO pin numbers are chip numbers, not header positions @@ -159,6 +161,8 @@ private: bool m_bMIDIRXProgramChange; bool m_bIgnoreAllNotesOff; bool m_bMIDIAutoVoiceDumpOnPC; + bool m_bHeaderlessSysExVoices; + bool m_bExpandPCAcrossBanks; bool m_bLCDEnabled; unsigned m_nLCDPinEnable; diff --git a/src/mididevice.cpp b/src/mididevice.cpp index 4e51056..64fc009 100644 --- a/src/mididevice.cpp +++ b/src/mididevice.cpp @@ -36,7 +36,7 @@ LOGMODULE ("mididevice"); #define MIDI_AFTERTOUCH 0b1010 // TODO #define MIDI_CHANNEL_AFTERTOUCH 0b1101 // right now Synth_Dexed just manage Channel Aftertouch not Polyphonic AT -> 0b1010 #define MIDI_CONTROL_CHANGE 0b1011 - #define MIDI_CC_BANK_SELECT_MSB 0 // TODO + #define MIDI_CC_BANK_SELECT_MSB 0 #define MIDI_CC_MODULATION 1 #define MIDI_CC_BREATH_CONTROLLER 2 #define MIDI_CC_FOOT_PEDAL 4 @@ -283,6 +283,10 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign m_pSynthesizer->SetPan (pMessage[2], nTG); break; + case MIDI_CC_BANK_SELECT_MSB: + m_pSynthesizer->BankSelectMSB (pMessage[2], nTG); + break; + case MIDI_CC_BANK_SELECT_LSB: m_pSynthesizer->BankSelectLSB (pMessage[2], nTG); break; diff --git a/src/minidexed.cpp b/src/minidexed.cpp index dbd32f7..36e7d99 100644 --- a/src/minidexed.cpp +++ b/src/minidexed.cpp @@ -61,6 +61,7 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt, for (unsigned i = 0; i < CConfig::ToneGenerators; i++) { m_nVoiceBankID[i] = 0; + m_nVoiceBankIDMSB[i] = 0; m_nProgram[i] = 0; m_nVolume[i] = 100; m_nPan[i] = 64; @@ -170,7 +171,7 @@ bool CMiniDexed::Initialize (void) return false; } - m_SysExFileLoader.Load (); + m_SysExFileLoader.Load (m_pConfig->GetHeaderlessSysExVoices ()); if (m_SerialMIDI.Initialize ()) { @@ -366,27 +367,81 @@ CSysExFileLoader *CMiniDexed::GetSysExFileLoader (void) return &m_SysExFileLoader; } +void CMiniDexed::BankSelect (unsigned nBank, unsigned nTG) +{ + nBank=constrain((int)nBank,0,16383); + + assert (nTG < CConfig::ToneGenerators); + + if (GetSysExFileLoader ()->IsValidBank(nBank)) + { + // Only change if we have the bank loaded + m_nVoiceBankID[nTG] = nBank; + + m_UI.ParameterChanged (); + } +} + +void CMiniDexed::BankSelectMSB (unsigned nBankMSB, unsigned nTG) +{ + nBankMSB=constrain((int)nBankMSB,0,127); + + assert (nTG < CConfig::ToneGenerators); + // MIDI Spec 1.0 "BANK SELECT" states: + // "The transmitter must transmit the MSB and LSB as a pair, + // and the Program Change must be sent immediately after + // the Bank Select pair." + // + // So it isn't possible to validate the selected bank ID until + // we receive both MSB and LSB so just store the MSB for now. + m_nVoiceBankIDMSB[nTG] = nBankMSB; +} + void CMiniDexed::BankSelectLSB (unsigned nBankLSB, unsigned nTG) { nBankLSB=constrain((int)nBankLSB,0,127); assert (nTG < CConfig::ToneGenerators); - m_nVoiceBankID[nTG] = nBankLSB; + unsigned nBank = m_nVoiceBankID[nTG]; + unsigned nBankMSB = m_nVoiceBankIDMSB[nTG]; + nBank = (nBankMSB << 7) + nBankLSB; - m_UI.ParameterChanged (); + // Now should have both MSB and LSB so enable the BankSelect + BankSelect(nBank, nTG); } void CMiniDexed::ProgramChange (unsigned nProgram, unsigned nTG) { assert (m_pConfig); - nProgram=constrain((int)nProgram,0,31); + unsigned nBankOffset; + bool bPCAcrossBanks = m_pConfig->GetExpandPCAcrossBanks(); + if (bPCAcrossBanks) + { + // Note: This doesn't actually change the bank in use + // but will allow PC messages of 0..127 + // to select across four consecutive banks of voices. + // + // So if the current bank = 5 then: + // PC 0-31 = Bank 5, Program 0-31 + // PC 32-63 = Bank 6, Program 0-31 + // PC 64-95 = Bank 7, Program 0-31 + // PC 96-127 = Bank 8, Program 0-31 + nProgram=constrain((int)nProgram,0,127); + nBankOffset = nProgram >> 5; + nProgram = nProgram % 32; + } + else + { + nBankOffset = 0; + nProgram=constrain((int)nProgram,0,31); + } assert (nTG < CConfig::ToneGenerators); m_nProgram[nTG] = nProgram; uint8_t Buffer[156]; - m_SysExFileLoader.GetVoice (m_nVoiceBankID[nTG], nProgram, Buffer); + m_SysExFileLoader.GetVoice (m_nVoiceBankID[nTG]+nBankOffset, nProgram, Buffer); assert (m_pTG[nTG]); m_pTG[nTG]->loadVoiceParameters (Buffer); @@ -718,7 +773,9 @@ void CMiniDexed::SetTGParameter (TTGParameter Parameter, int nValue, unsigned nT switch (Parameter) { - case TGParameterVoiceBank: BankSelectLSB (nValue, nTG); break; + case TGParameterVoiceBank: BankSelect (nValue, nTG); break; + case TGParameterVoiceBankMSB: BankSelectMSB (nValue, nTG); break; + case TGParameterVoiceBankLSB: BankSelectLSB (nValue, nTG); break; case TGParameterProgram: ProgramChange (nValue, nTG); break; case TGParameterVolume: SetVolume (nValue, nTG); break; case TGParameterPan: SetPan (nValue, nTG); break; @@ -772,6 +829,8 @@ int CMiniDexed::GetTGParameter (TTGParameter Parameter, unsigned nTG) switch (Parameter) { case TGParameterVoiceBank: return m_nVoiceBankID[nTG]; + case TGParameterVoiceBankMSB: return m_nVoiceBankID[nTG] >> 7; + case TGParameterVoiceBankLSB: return m_nVoiceBankID[nTG] & 0x7F; case TGParameterProgram: return m_nProgram[nTG]; case TGParameterVolume: return m_nVolume[nTG]; case TGParameterPan: return m_nPan[nTG]; @@ -1446,7 +1505,7 @@ void CMiniDexed::LoadPerformanceParameters(void) for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++) { - BankSelectLSB (m_PerformanceConfig.GetBankNumber (nTG), nTG); + BankSelect (m_PerformanceConfig.GetBankNumber (nTG), nTG); ProgramChange (m_PerformanceConfig.GetVoiceNumber (nTG), nTG); SetMIDIChannel (m_PerformanceConfig.GetMIDIChannel (nTG), nTG); SetVolume (m_PerformanceConfig.GetVolume (nTG), nTG); diff --git a/src/minidexed.h b/src/minidexed.h index a74a04f..e49e7de 100644 --- a/src/minidexed.h +++ b/src/minidexed.h @@ -63,6 +63,8 @@ public: CSysExFileLoader *GetSysExFileLoader (void); + void BankSelect (unsigned nBank, unsigned nTG); + void BankSelectMSB (unsigned nBankMSB, unsigned nTG); void BankSelectLSB (unsigned nBankLSB, unsigned nTG); void ProgramChange (unsigned nProgram, unsigned nTG); void SetVolume (unsigned nVolume, unsigned nTG); @@ -124,6 +126,7 @@ public: bool GetPerformanceSelectToLoad(void); bool SavePerformance (bool bSaveAsDeault); + // Must match the order in CUIMenu::TParameter enum TParameter { ParameterCompressorEnable, @@ -146,9 +149,12 @@ public: bool DeletePerformance(unsigned nID); bool DoDeletePerformance(void); + // Must match the order in CUIMenu::TGParameter enum TTGParameter { TGParameterVoiceBank, + TGParameterVoiceBankMSB, + TGParameterVoiceBankLSB, TGParameterProgram, TGParameterVolume, TGParameterPan, @@ -227,6 +233,7 @@ private: CDexedAdapter *m_pTG[CConfig::ToneGenerators]; unsigned m_nVoiceBankID[CConfig::ToneGenerators]; + unsigned m_nVoiceBankIDMSB[CConfig::ToneGenerators]; unsigned m_nProgram[CConfig::ToneGenerators]; unsigned m_nVolume[CConfig::ToneGenerators]; unsigned m_nPan[CConfig::ToneGenerators]; diff --git a/src/minidexed.ini b/src/minidexed.ini index 2190277..f58b44c 100644 --- a/src/minidexed.ini +++ b/src/minidexed.ini @@ -19,6 +19,8 @@ MIDIBaudRate=31250 MIDIRXProgramChange=1 IgnoreAllNotesOff=0 MIDIAutoVoiceDumpOnPC=1 +HeaderlessSysExVoices=0 +ExpandPCAcrossBanks=1 # HD44780 LCD LCDEnabled=1 diff --git a/src/sysexfileloader.cpp b/src/sysexfileloader.cpp index ffba8f5..d72bc7d 100644 --- a/src/sysexfileloader.cpp +++ b/src/sysexfileloader.cpp @@ -65,7 +65,7 @@ CSysExFileLoader::CSysExFileLoader (const char *pDirName) : m_DirName (pDirName) { m_DirName += "/voice"; - m_nNumLoadedBanks = 0; + m_nNumHighestBank = 0; for (unsigned i = 0; i <= MaxVoiceBankID; i++) { @@ -81,9 +81,10 @@ CSysExFileLoader::~CSysExFileLoader (void) } } -void CSysExFileLoader::Load (void) +void CSysExFileLoader::Load (bool bHeaderlessSysExVoices) { - m_nNumLoadedBanks = 0; + m_nNumHighestBank = 0; + m_nBanksLoaded = 0; DIR *pDirectory = opendir (m_DirName.c_str ()); if (!pDirectory) @@ -96,71 +97,155 @@ void CSysExFileLoader::Load (void) dirent *pEntry; while ((pEntry = readdir (pDirectory)) != nullptr) { - unsigned nBank; - size_t nLen = strlen (pEntry->d_name); + LoadBank(m_DirName.c_str (), pEntry->d_name, bHeaderlessSysExVoices, 0); + } + LOGDBG ("%u Banks loaded. Highest Bank loaded: #%u", m_nBanksLoaded, m_nNumHighestBank); - if ( nLen < 5 // "[NNNN]N[_name].syx" - || strcasecmp (&pEntry->d_name[nLen-4], ".syx") != 0 - || sscanf (pEntry->d_name, "%u", &nBank) != 1) - { - LOGWARN ("%s: Invalid filename format", pEntry->d_name); + closedir (pDirectory); +} - continue; - } +void CSysExFileLoader::LoadBank (const char * sDirName, const char * sBankName, bool bHeaderlessSysExVoices, unsigned nSubDirCount) +{ + unsigned nBank; + size_t nLen = strlen (sBankName); + + if ( nLen < 5 // "[NNNN]N[_name].syx" + || strcasecmp (&sBankName[nLen-4], ".syx") != 0 + || sscanf (sBankName, "%u", &nBank) != 1) + { + // See if this is a subdirectory... + std::string Dirname (sDirName); + Dirname += "/"; + Dirname += sBankName; - if (nBank > MaxVoiceBankID) + DIR *pDirectory = opendir (Dirname.c_str ()); + if (pDirectory) { - LOGWARN ("Bank #%u is not supported", nBank); + if (nSubDirCount >= MaxSubDirs) + { + LOGWARN ("Too many nested subdirectories: %s", sBankName); + return; + } + + LOGDBG ("Processing subdirectory %s", sBankName); - continue; + dirent *pEntry; + while ((pEntry = readdir (pDirectory)) != nullptr) + { + LoadBank(Dirname.c_str (), pEntry->d_name, bHeaderlessSysExVoices, nSubDirCount+1); + } + closedir (pDirectory); } - - if (m_pVoiceBank[nBank]) + else { - LOGWARN ("Bank #%u already loaded", nBank); - - continue; + LOGWARN ("%s: Invalid filename format", sBankName); } - m_pVoiceBank[nBank] = new TVoiceBank; - assert (m_pVoiceBank[nBank]); + return; + } + + // File and UI handling requires banks to be 1..indexed. + // Internally (and via MIDI) we need 0..indexed. + // Any mention of a BankID internally is assumed to be 0..indexed. + unsigned nBankIdx = nBank - 1; + + // BankIdx goes from 0 to MaxVoiceBankID inclusive + if (nBankIdx > MaxVoiceBankID) + { + LOGWARN ("Bank #%u is not supported", nBank); + + return; + } + + if (m_pVoiceBank[nBankIdx]) + { + LOGWARN ("Bank #%u already loaded", nBank); + + return; + } + + m_pVoiceBank[nBankIdx] = new TVoiceBank; + assert (m_pVoiceBank[nBankIdx]); + assert (sizeof(TVoiceBank) == VoiceSysExHdrSize + VoiceSysExSize); - std::string Filename (m_DirName); - Filename += "/"; - Filename += pEntry->d_name; + std::string Filename (sDirName); + Filename += "/"; + Filename += sBankName; - FILE *pFile = fopen (Filename.c_str (), "rb"); - if (pFile) + FILE *pFile = fopen (Filename.c_str (), "rb"); + if (pFile) + { + bool bBankLoaded = false; + if ( fread (m_pVoiceBank[nBankIdx], VoiceSysExHdrSize+VoiceSysExSize, 1, pFile) == 1 + && m_pVoiceBank[nBankIdx]->StatusStart == 0xF0 + && m_pVoiceBank[nBankIdx]->CompanyID == 0x43 + && m_pVoiceBank[nBankIdx]->Format == 0x09 + && m_pVoiceBank[nBankIdx]->StatusEnd == 0xF7) { - if ( fread (m_pVoiceBank[nBank], sizeof (TVoiceBank), 1, pFile) == 1 - && m_pVoiceBank[nBank]->StatusStart == 0xF0 - && m_pVoiceBank[nBank]->CompanyID == 0x43 - && m_pVoiceBank[nBank]->Format == 0x09 - && m_pVoiceBank[nBank]->StatusEnd == 0xF7) + if (m_nBanksLoaded % 100 == 0) { - LOGDBG ("Bank #%u successfully loaded", nBank); + LOGDBG ("Banks successfully loaded #%u", m_nBanksLoaded); + } + //LOGDBG ("Bank #%u successfully loaded", nBank); - m_BankFileName[nBank] = pEntry->d_name; - m_nNumLoadedBanks++; + m_BankFileName[nBankIdx] = sBankName; + if (nBank > m_nNumHighestBank) + { + // This is the bank ID of the highest loaded bank + m_nNumHighestBank = nBank; } - else + m_nBanksLoaded++; + bBankLoaded = true; + } + else if (bHeaderlessSysExVoices) + { + // Config says to accept headerless SysEx Voice Banks + // so reset file pointer and try again. + fseek (pFile, 0, SEEK_SET); + if (fread (m_pVoiceBank[nBankIdx]->Voice, VoiceSysExSize, 1, pFile) == 1) { - LOGWARN ("%s: Invalid size or format", Filename.c_str ()); - - delete m_pVoiceBank[nBank]; - m_pVoiceBank[nBank] = nullptr; + if (m_nBanksLoaded % 100 == 0) + { + LOGDBG ("Banks successfully loaded #%u", m_nBanksLoaded); + } + //LOGDBG ("Bank #%u successfully loaded (headerless)", nBank); + + // Add in the missing header items. + // Naturally it isn't possible to validate these! + m_pVoiceBank[nBankIdx]->StatusStart = 0xF0; + m_pVoiceBank[nBankIdx]->CompanyID = 0x43; + m_pVoiceBank[nBankIdx]->Format = 0x09; + m_pVoiceBank[nBankIdx]->ByteCountMS = 0x20; + m_pVoiceBank[nBankIdx]->ByteCountLS = 0x00; + m_pVoiceBank[nBankIdx]->Checksum = 0x00; + m_pVoiceBank[nBankIdx]->StatusEnd = 0xF7; + + m_BankFileName[nBankIdx] = sBankName; + if (nBank > m_nNumHighestBank) + { + // This is the bank ID of the highest loaded bank + m_nNumHighestBank = nBank; + } + bBankLoaded = true; + m_nBanksLoaded++; } - - fclose (pFile); } - else + + if (!bBankLoaded) { - delete m_pVoiceBank[nBank]; - m_pVoiceBank[nBank] = nullptr; + LOGWARN ("%s: Invalid size or format", Filename.c_str ()); + + delete m_pVoiceBank[nBankIdx]; + m_pVoiceBank[nBankIdx] = nullptr; } - } - closedir (pDirectory); + fclose (pFile); + } + else + { + delete m_pVoiceBank[nBankIdx]; + m_pVoiceBank[nBankIdx] = nullptr; + } } std::string CSysExFileLoader::GetBankName (unsigned nBankID) @@ -188,9 +273,71 @@ std::string CSysExFileLoader::GetBankName (unsigned nBankID) return "NO NAME"; } -unsigned CSysExFileLoader::GetNumLoadedBanks (void) +unsigned CSysExFileLoader::GetNextBankUp (unsigned nBankID) +{ + // Find the next loaded bank "up" from the provided bank ID + for (unsigned id=nBankID+1; id <= m_nNumHighestBank; id++) + { + if (IsValidBank(id)) + { + return id; + } + } + + // Handle wrap-around + for (unsigned id=0; id= 0; id--) + { + if (IsValidBank((unsigned)id)) + { + return id; + } + } + + // Handle wrap-around + for (unsigned id=m_nNumHighestBank; id>nBankID; id--) + { + if (IsValidBank(id)) + { + return id; + } + } + + // If we get here there are no other banks! + return nBankID; +} + +bool CSysExFileLoader::IsValidBank (unsigned nBankID) +{ + if (m_pVoiceBank[nBankID]) + { + // Use a valid "status start/end" as an indicator of a valid loaded bank + if ((m_pVoiceBank[nBankID]->StatusStart == 0xF0) && + (m_pVoiceBank[nBankID]->StatusEnd == 0xF7)) + { + return true; + } + } + return false; +} + +unsigned CSysExFileLoader::GetNumHighestBank (void) { - return m_nNumLoadedBanks; + return m_nNumHighestBank; } void CSysExFileLoader::GetVoice (unsigned nBankID, unsigned nVoiceID, uint8_t *pVoiceData) @@ -198,7 +345,7 @@ void CSysExFileLoader::GetVoice (unsigned nBankID, unsigned nVoiceID, uint8_t *p if ( nBankID <= MaxVoiceBankID && nVoiceID <= VoicesPerBank) { - if (m_pVoiceBank[nBankID]) + if (IsValidBank(nBankID)) { DecodePackedVoice (m_pVoiceBank[nBankID]->Voice[nVoiceID], pVoiceData); diff --git a/src/sysexfileloader.h b/src/sysexfileloader.h index 5954bfe..4918db6 100644 --- a/src/sysexfileloader.h +++ b/src/sysexfileloader.h @@ -29,10 +29,13 @@ class CSysExFileLoader // Loader for DX7 .syx files { public: - static const unsigned MaxVoiceBankID = 16383; + static const unsigned MaxVoiceBankID = 16383; // i.e. 14-bit MSB/LSB value between 0 and 16383 static const unsigned VoicesPerBank = 32; static const size_t SizePackedVoice = 128; static const size_t SizeSingleVoice = 156; + static const unsigned VoiceSysExHdrSize = 8; // Additional (optional) Header/Footer bytes for bank of 32 voices + static const unsigned VoiceSysExSize = 4096; // Bank of 32 voices as per DX7 MIDI Spec + static const unsigned MaxSubDirs = 3; // Number of nested subdirectories supported. struct TVoiceBank { @@ -54,12 +57,15 @@ public: CSysExFileLoader (const char *pDirName = "/sysex"); ~CSysExFileLoader (void); - void Load (void); + void Load (bool bHeaderlessSysExVoices = false); - std::string GetBankName (unsigned nBankID); // 0 .. 127 - unsigned GetNumLoadedBanks (); // 0 .. MaxVoiceBankID + std::string GetBankName (unsigned nBankID); // 0 .. MaxVoiceBankID + unsigned GetNumHighestBank (); // 0 .. MaxVoiceBankID + bool IsValidBank (unsigned nBankID); + unsigned GetNextBankUp (unsigned nBankID); + unsigned GetNextBankDown (unsigned nBankID); - void GetVoice (unsigned nBankID, // 0 .. 127 + void GetVoice (unsigned nBankID, // 0 .. MaxVoiceBankID unsigned nVoiceID, // 0 .. 31 uint8_t *pVoiceData); // returns unpacked format (156 bytes) @@ -69,12 +75,15 @@ private: private: std::string m_DirName; - unsigned m_nNumLoadedBanks; + unsigned m_nNumHighestBank; + unsigned m_nBanksLoaded; TVoiceBank *m_pVoiceBank[MaxVoiceBankID+1]; std::string m_BankFileName[MaxVoiceBankID+1]; static uint8_t s_DefaultVoice[SizeSingleVoice]; + + void LoadBank (const char * sDirName, const char * sBankName, bool bHeaderlessSysExVoices, unsigned nSubDirCount); }; #endif diff --git a/src/uimenu.cpp b/src/uimenu.cpp index a1d44ce..ee83016 100644 --- a/src/uimenu.cpp +++ b/src/uimenu.cpp @@ -219,6 +219,8 @@ const CUIMenu::TParameter CUIMenu::s_GlobalParameter[CMiniDexed::ParameterUnknow const CUIMenu::TParameter CUIMenu::s_TGParameter[CMiniDexed::TGParameterUnknown] = { {0, CSysExFileLoader::MaxVoiceBankID, 1}, // TGParameterVoiceBank + {0, 0, 0}, // TGParameterVoiceBankMSB (not used in menus) + {0, 0, 0}, // TGParameterVoiceBankLSB (not used in menus) {0, CSysExFileLoader::VoicesPerBank-1, 1}, // TGParameterProgram {0, 127, 8, ToVolume}, // TGParameterVolume {0, 127, 8, ToPan}, // TGParameterPan @@ -249,7 +251,6 @@ const CUIMenu::TParameter CUIMenu::s_TGParameter[CMiniDexed::TGParameterUnknown] {0, 1, 1, ToOnOff}, //AT Pitch {0, 1, 1, ToOnOff}, //AT Amp {0, 1, 1, ToOnOff} //AT EGBias - }; // must match DexedVoiceParameters in Synth_Dexed @@ -486,7 +487,6 @@ void CUIMenu::EditGlobalParameter (CUIMenu *pUIMenu, TMenuEvent Event) void CUIMenu::EditVoiceBankNumber (CUIMenu *pUIMenu, TMenuEvent Event) { unsigned nTG = pUIMenu->m_nMenuStackParameter[pUIMenu->m_nCurrentMenuDepth-1]; - int nLoadedBanks = pUIMenu->m_pMiniDexed->GetSysExFileLoader ()->GetNumLoadedBanks(); int nValue = pUIMenu->m_pMiniDexed->GetTGParameter (CMiniDexed::TGParameterVoiceBank, nTG); @@ -496,19 +496,13 @@ void CUIMenu::EditVoiceBankNumber (CUIMenu *pUIMenu, TMenuEvent Event) break; case MenuEventStepDown: - if (--nValue < 0) - { - nValue = 0; - } + nValue = pUIMenu->m_pMiniDexed->GetSysExFileLoader ()->GetNextBankDown(nValue); pUIMenu->m_pMiniDexed->SetTGParameter ( CMiniDexed::TGParameterVoiceBank, nValue, nTG); break; case MenuEventStepUp: - if (++nValue > (int) nLoadedBanks-1) - { - nValue = nLoadedBanks-1; - } + nValue = pUIMenu->m_pMiniDexed->GetSysExFileLoader ()->GetNextBankUp(nValue); pUIMenu->m_pMiniDexed->SetTGParameter ( CMiniDexed::TGParameterVoiceBank, nValue, nTG); break; @@ -537,7 +531,7 @@ void CUIMenu::EditVoiceBankNumber (CUIMenu *pUIMenu, TMenuEvent Event) void CUIMenu::EditProgramNumber (CUIMenu *pUIMenu, TMenuEvent Event) { unsigned nTG = pUIMenu->m_nMenuStackParameter[pUIMenu->m_nCurrentMenuDepth-1]; - int nLoadedBanks = pUIMenu->m_pMiniDexed->GetSysExFileLoader ()->GetNumLoadedBanks(); + int nHighestBank = pUIMenu->m_pMiniDexed->GetSysExFileLoader ()->GetNumHighestBank(); int nValue = pUIMenu->m_pMiniDexed->GetTGParameter (CMiniDexed::TGParameterProgram, nTG); @@ -555,7 +549,7 @@ void CUIMenu::EditProgramNumber (CUIMenu *pUIMenu, TMenuEvent Event) if (--nVB < 0) { // Wrap around to last loaded bank - nVB = nLoadedBanks-1; + nVB = nHighestBank; } pUIMenu->m_pMiniDexed->SetTGParameter (CMiniDexed::TGParameterVoiceBank, nVB, nTG); } @@ -568,7 +562,7 @@ void CUIMenu::EditProgramNumber (CUIMenu *pUIMenu, TMenuEvent Event) // Switch up a voice bank and reset to voice 0 nValue = 0; int nVB = pUIMenu->m_pMiniDexed->GetTGParameter(CMiniDexed::TGParameterVoiceBank, nTG); - if (++nVB > (int) nLoadedBanks-1) + if (++nVB > (int) nHighestBank) { // Wrap around to first bank nVB = 0; @@ -587,15 +581,29 @@ void CUIMenu::EditProgramNumber (CUIMenu *pUIMenu, TMenuEvent Event) return; } - string TG ("TG"); - TG += to_string (nTG+1); + string voiceName = pUIMenu->m_pMiniDexed->GetVoiceName (nTG); // Skip empty voices + if (voiceName == "EMPTY " + || voiceName == " " + || voiceName == "----------" + || voiceName == "~~~~~~~~~~" ) + { + if (Event == MenuEventStepUp) { + CUIMenu::EditProgramNumber (pUIMenu, MenuEventStepUp); + } + if (Event == MenuEventStepDown) { + CUIMenu::EditProgramNumber (pUIMenu, MenuEventStepDown); + } + } else { + string TG ("TG"); + TG += to_string (nTG+1); - string Value = to_string (nValue+1) + "=" + pUIMenu->m_pMiniDexed->GetVoiceName (nTG); + string Value = to_string (nValue+1) + "=" + pUIMenu->m_pMiniDexed->GetVoiceName (nTG); - pUIMenu->m_pUI->DisplayWrite (TG.c_str (), - pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name, - Value.c_str (), - nValue > 0, nValue < (int) CSysExFileLoader::VoicesPerBank-1); + pUIMenu->m_pUI->DisplayWrite (TG.c_str (), + pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name, + Value.c_str (), + nValue > 0, nValue < (int) CSysExFileLoader::VoicesPerBank-1); + } } void CUIMenu::EditTGParameter (CUIMenu *pUIMenu, TMenuEvent Event) @@ -1474,8 +1482,6 @@ void CUIMenu::EditTGParameterModulation (CUIMenu *pUIMenu, TMenuEvent Event) unsigned nController = pUIMenu->m_nMenuStackParameter[pUIMenu->m_nCurrentMenuDepth-1]; unsigned nParameter = pUIMenu->m_nCurrentParameter + nController; - - CMiniDexed::TTGParameter Param = (CMiniDexed::TTGParameter) nParameter; const TParameter &rParam = s_TGParameter[Param]; diff --git a/submod.sh b/submod.sh new file mode 100755 index 0000000..e941660 --- /dev/null +++ b/submod.sh @@ -0,0 +1,13 @@ +#!/bin/bash +set -ex +git submodule update --init --recursive +cd circle-stdlib/ +git checkout e318f89 # Needed to support Circle develop? +cd - +cd circle-stdlib/libs/circle +git checkout ec09d7e # develop +cd - +cd circle-stdlib/libs/circle-newlib +git checkout 48bf91d # needed for circle ec09d7e +cd - +