Skip to content

Commit

Permalink
Performance file handling (#581)
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
probonopd and diyelectromusic authored Jan 27, 2024
1 parent 753c205 commit 4755c5d
Show file tree
Hide file tree
Showing 8 changed files with 842 additions and 133 deletions.
28 changes: 27 additions & 1 deletion src/mididevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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))
Expand Down
169 changes: 150 additions & 19 deletions src/minidexed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -170,6 +173,8 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt,
SetParameter (ParameterCompressorEnable, 1);

SetPerformanceSelectChannel(m_pConfig->GetPerformanceSelectChannel());

SetParameter (ParameterPerformanceBank, 0);
};

bool CMiniDexed::Initialize (void)
Expand Down Expand Up @@ -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();
Expand All @@ -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 ()))
{
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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();
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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();
Expand Down Expand Up @@ -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);
Expand All @@ -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)
Expand Down
Loading

0 comments on commit 4755c5d

Please sign in to comment.