pull/892/merge
probonopd 3 days ago committed by GitHub
commit 0c859792c1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 12
      src/config.h
  2. 16
      src/effect_mixer.hpp
  3. 489
      src/minidexed.cpp
  4. 9
      src/minidexed.h
  5. 43
      src/performanceconfig.cpp
  6. 9
      src/performanceconfig.h
  7. 69
      src/uimenu.cpp
  8. 20
      src/uimenu.h

@ -47,17 +47,17 @@ public:
#if (RASPPI==4 || RASPPI==5)
// Pi 4 and 5 quad core
// These are max values, default is to support 8 in total with optional 16 TGs
static const unsigned TGsCore1 = 2; // process 2 TGs on core 1
static const unsigned TGsCore23 = 3; // process 3 TGs on core 2 and 3 each
static const unsigned TGsCore1Opt = 2; // process optional additional 2 TGs on core 1
static const unsigned TGsCore23Opt = 3; // process optional additional 3 TGs on core 2 and 3 each
static const unsigned TGsCore1 = 2*4; // process 2 TGs on core 1
static const unsigned TGsCore23 = 3*4; // process 3 TGs on core 2 and 3 each
static const unsigned TGsCore1Opt = 2*4; // process optional additional 2 TGs on core 1
static const unsigned TGsCore23Opt = 3*4; // process optional additional 3 TGs on core 2 and 3 each
static const unsigned MinToneGenerators = TGsCore1 + 2*TGsCore23;
static const unsigned AllToneGenerators = TGsCore1 + TGsCore1Opt + 2*TGsCore23 + 2*TGsCore23Opt;
static const unsigned DefToneGenerators = MinToneGenerators;
#else
// Pi 2 or 3 quad core
static const unsigned TGsCore1 = 2; // process 2 TGs on core 1
static const unsigned TGsCore23 = 3; // process 3 TGs on core 2 and 3 each
static const unsigned TGsCore1 = 2*4; // process 2 TGs on core 1
static const unsigned TGsCore23 = 3*4; // process 3 TGs on core 2 and 3 each
static const unsigned TGsCore1Opt = 0;
static const unsigned TGsCore23Opt = 0;
static const unsigned MinToneGenerators = TGsCore1 + 2*TGsCore23;

@ -55,6 +55,22 @@ public:
multiplier[channel] = powf(gain, 4); // see: https://www.dr-lex.be/info-stuff/volumecontrols.html#ideal2
}
// Add a raw parameter to allow bypassing the powf(gain, 4) curve
void gain(uint8_t channel, float32_t gain, bool raw)
{
if (channel >= NN) return;
if (gain > MAX_GAIN)
gain = MAX_GAIN;
else if (gain < MIN_GAIN)
gain = MIN_GAIN;
if (raw) {
multiplier[channel] = gain;
} else {
multiplier[channel] = powf(gain, 4); // see: https://www.dr-lex.be/info-stuff/volumecontrols.html#ideal2
}
}
void gain(float32_t gain)
{
for (uint8_t i = 0; i < NN; i++)

@ -30,6 +30,24 @@
#include <stdio.h>
#include <assert.h>
#include "arm_float_to_q23.h"
#include "common.h"
// --- Macro for propagating parameter changes to all unison voices ---
#define FOR_AUX_UNISON_TGS(nTG, code) \
{ \
unsigned unisonVoices = m_nUnisonVoices[nTG]; \
if (unisonVoices < 1) unisonVoices = 1; \
for (unsigned v = 0; v < unisonVoices; ++v) { \
unsigned physicalTG = getPhysicalTG(nTG, v, maxUnisonVoices); \
if (physicalTG == nTG || physicalTG >= m_nToneGenerators) continue; \
code; \
}}
// Forward declaration for getPhysicalTG
static constexpr unsigned maxUnisonVoices = 4;
static unsigned getPhysicalTG(unsigned logicalTG, unsigned unisonVoice, unsigned /*unisonVoices*/) {
return logicalTG * maxUnisonVoices + unisonVoice;
}
const char WLANFirmwarePath[] = "SD:firmware/";
const char WLANConfigFile[] = "SD:wpa_supplicant.conf";
@ -126,6 +144,10 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt,
m_pTG[i]->setEngineType(pConfig->GetEngineType ());
m_pTG[i]->activate ();
}
m_nUnisonVoices[i] = 1;
m_nUnisonDetune[i] = 0;
m_nUnisonSpread[i] = 0;
memset(m_noteOnCount[i], 0, sizeof(m_noteOnCount[i]));
}
unsigned nUSBGadgetPin = pConfig->GetUSBGadgetPin();
@ -651,6 +673,8 @@ void CMiniDexed::ProgramChange (unsigned nProgram, unsigned nTG)
assert (nTG < CConfig::AllToneGenerators);
if (nTG >= m_nToneGenerators) return; // Not an active TG
LOGNOTE("Voice change: logicalTG=%u, program=%u", nTG, nProgram);
m_nProgram[nTG] = nProgram;
uint8_t Buffer[156];
@ -660,6 +684,30 @@ void CMiniDexed::ProgramChange (unsigned nProgram, unsigned nTG)
m_pTG[nTG]->loadVoiceParameters (Buffer);
setOPMask(0b111111, nTG);
// Propagate to auxiliary TGs for unison
unsigned unisonVoices = m_nUnisonVoices[nTG];
if (unisonVoices < 1) unisonVoices = 1;
int baseDetune = m_nMasterTune[nTG];
unsigned basePan = m_nPan[nTG];
unsigned unisonDetune = m_nUnisonDetune[nTG];
unsigned unisonSpread = m_nUnisonSpread[nTG];
for (unsigned v = 0; v < unisonVoices; ++v) {
unsigned physicalTG = getPhysicalTG(nTG, v, maxUnisonVoices);
LOGNOTE(" affected physicalTG=%u (logicalTG=%u, unisonVoice=%u)", physicalTG, nTG, v);
if (physicalTG == nTG || physicalTG >= m_nToneGenerators) continue;
m_pTG[physicalTG]->loadVoiceParameters(Buffer);
setOPMask(0b111111, physicalTG);
// Apply per-voice detune and pan
float detuneOffset = ((float)v - (unisonVoices - 1) / 2.0f) * (float)unisonDetune;
float panOffset = ((float)v - (unisonVoices - 1) / 2.0f) * (float)unisonSpread;
int detune = baseDetune + (int)detuneOffset;
unsigned pan = basePan + (int)panOffset;
detune = constrain(detune, -99, 99);
pan = constrain((int)pan, 0, 127);
m_pTG[physicalTG]->setMasterTune((int8_t)detune);
tg_mixer->pan(physicalTG, mapfloat(pan, 0, 127, 0.0f, 1.0f));
}
if (m_pConfig->GetMIDIAutoVoiceDumpOnPC())
{
// Only do the voice dump back out over MIDI if we have a specific
@ -721,15 +769,11 @@ void CMiniDexed::SetExpression (unsigned nExpression, unsigned nTG)
void CMiniDexed::SetPan (unsigned nPan, unsigned nTG)
{
nPan=constrain((int)nPan,0,127);
assert (nTG < CConfig::AllToneGenerators);
if (nTG >= m_nToneGenerators) return; // Not an active TG
if (nTG >= m_nToneGenerators) return;
m_nPan[nTG] = nPan;
tg_mixer->pan(nTG,mapfloat(nPan,0,127,0.0f,1.0f));
reverb_send_mixer->pan(nTG,mapfloat(nPan,0,127,0.0f,1.0f));
m_UI.ParameterChanged ();
}
@ -750,50 +794,42 @@ void CMiniDexed::SetReverbSend (unsigned nReverbSend, unsigned nTG)
void CMiniDexed::SetMasterTune (int nMasterTune, unsigned nTG)
{
nMasterTune=constrain((int)nMasterTune,-99,99);
assert (nTG < CConfig::AllToneGenerators);
if (nTG >= m_nToneGenerators) return; // Not an active TG
if (nTG >= m_nToneGenerators) return;
m_nMasterTune[nTG] = nMasterTune;
assert (m_pTG[nTG]);
m_pTG[nTG]->setMasterTune ((int8_t) nMasterTune);
m_UI.ParameterChanged ();
}
void CMiniDexed::SetCutoff (int nCutoff, unsigned nTG)
{
nCutoff = constrain (nCutoff, 0, 99);
assert (nTG < CConfig::AllToneGenerators);
if (nTG >= m_nToneGenerators) return; // Not an active TG
if (nTG >= m_nToneGenerators) return;
m_nCutoff[nTG] = nCutoff;
assert (m_pTG[nTG]);
m_pTG[nTG]->setFilterCutoff (mapfloat (nCutoff, 0, 99, 0.0f, 1.0f));
FOR_AUX_UNISON_TGS(nTG, {
m_pTG[physicalTG]->setFilterCutoff(mapfloat(nCutoff, 0, 99, 0.0f, 1.0f));
})
m_UI.ParameterChanged ();
}
void CMiniDexed::SetResonance (int nResonance, unsigned nTG)
{
nResonance = constrain (nResonance, 0, 99);
assert (nTG < CConfig::AllToneGenerators);
if (nTG >= m_nToneGenerators) return; // Not an active TG
if (nTG >= m_nToneGenerators) return;
m_nResonance[nTG] = nResonance;
assert (m_pTG[nTG]);
m_pTG[nTG]->setFilterResonance (mapfloat (nResonance, 0, 99, 0.0f, 1.0f));
FOR_AUX_UNISON_TGS(nTG, {
m_pTG[physicalTG]->setFilterResonance(mapfloat(nResonance, 0, 99, 0.0f, 1.0f));
})
m_UI.ParameterChanged ();
}
void CMiniDexed::SetMIDIChannel (uint8_t uchChannel, unsigned nTG)
{
assert (nTG < CConfig::AllToneGenerators);
@ -848,9 +884,29 @@ void CMiniDexed::keyup (int16_t pitch, unsigned nTG)
assert (m_pTG[nTG]);
pitch = ApplyNoteLimits (pitch, nTG);
if (pitch >= 0)
{
m_pTG[nTG]->keyup (pitch);
if (pitch < 0) return;
unsigned unisonVoices = m_nUnisonVoices[nTG];
if (unisonVoices < 1) unisonVoices = 1;
int baseDetune = m_nMasterTune[nTG];
unsigned basePan = m_nPan[nTG];
unsigned unisonDetune = m_nUnisonDetune[nTG];
unsigned unisonSpread = m_nUnisonSpread[nTG];
for (unsigned v = 0; v < unisonVoices; ++v) {
unsigned physicalTG = getPhysicalTG(nTG, v, maxUnisonVoices);
if (physicalTG >= m_nToneGenerators) break;
// Per-voice detune and pan
float detuneOffset = ((float)v - (unisonVoices - 1) / 2.0f) * (float)unisonDetune;
float panOffset = ((float)v - (unisonVoices - 1) / 2.0f) * (float)unisonSpread;
int detune = baseDetune + (int)detuneOffset;
unsigned pan = basePan + (int)panOffset;
detune = constrain(detune, -99, 99);
pan = constrain((int)pan, 0, 127);
m_pTG[physicalTG]->setMasterTune((int8_t)detune);
tg_mixer->pan(physicalTG, mapfloat(pan, 0, 127, 0.0f, 1.0f));
m_pTG[physicalTG]->keyup(pitch);
}
}
@ -862,9 +918,31 @@ void CMiniDexed::keydown (int16_t pitch, uint8_t velocity, unsigned nTG)
assert (m_pTG[nTG]);
pitch = ApplyNoteLimits (pitch, nTG);
if (pitch >= 0)
{
m_pTG[nTG]->keydown (pitch, velocity);
if (pitch < 0) return;
unsigned unisonVoices = m_nUnisonVoices[nTG];
if (unisonVoices < 1) unisonVoices = 1;
int baseDetune = m_nMasterTune[nTG];
unsigned basePan = m_nPan[nTG];
unsigned unisonDetune = m_nUnisonDetune[nTG];
unsigned unisonSpread = m_nUnisonSpread[nTG];
LOGNOTE("Note ON: pitch=%d, velocity=%u, logicalTG=%u, unisonVoices=%u", pitch, velocity, nTG, unisonVoices);
for (unsigned v = 0; v < unisonVoices; ++v) {
unsigned physicalTG = getPhysicalTG(nTG, v, maxUnisonVoices);
LOGNOTE(" playing physicalTG=%u (logicalTG=%u, unisonVoice=%u)", physicalTG, nTG, v);
if (physicalTG >= m_nToneGenerators) break;
// Per-voice detune and pan
float detuneOffset = ((float)v - (unisonVoices - 1) / 2.0f) * (float)unisonDetune;
float panOffset = ((float)v - (unisonVoices - 1) / 2.0f) * (float)unisonSpread;
int detune = baseDetune + (int)detuneOffset;
unsigned pan = basePan + (int)panOffset;
detune = constrain(detune, -99, 99);
pan = constrain((int)pan, 0, 127);
m_pTG[physicalTG]->setMasterTune((int8_t)detune);
tg_mixer->pan(physicalTG, mapfloat(pan, 0, 127, 0.0f, 1.0f));
m_pTG[physicalTG]->keydown(pitch, velocity);
}
}
@ -1123,16 +1201,61 @@ void CMiniDexed::SetTGParameter (TTGParameter Parameter, int nValue, unsigned nT
case TGParameterATAmplitude: setModController(3, 2, nValue, nTG); break;
case TGParameterATEGBias: setModController(3, 3, nValue, nTG); break;
case TGParameterUnisonVoices: {
unsigned oldUnisonVoices = m_nUnisonVoices[nTG];
unsigned newUnisonVoices = constrain(nValue, 1, maxUnisonVoices);
if (newUnisonVoices < oldUnisonVoices) {
// For each note and each physical TG that is no longer mapped, send keyup if needed
for (unsigned midiNote = 0; midiNote < 128; ++midiNote) {
for (unsigned v = newUnisonVoices; v < oldUnisonVoices; ++v) {
unsigned physicalTG = getPhysicalTG(nTG, v, maxUnisonVoices);
if (physicalTG >= m_nToneGenerators) continue;
m_pTG[physicalTG]->keyup(midiNote);
}
}
} else if (newUnisonVoices > oldUnisonVoices) {
// Configure new auxiliary TGs with the current voice data
uint8_t voiceData[161];
m_pTG[nTG]->getVoiceData(&voiceData[6]); // getVoiceData expects pointer to voice data (skipping 6 bytes header)
for (unsigned v = oldUnisonVoices; v < newUnisonVoices; ++v) {
unsigned physicalTG = getPhysicalTG(nTG, v, maxUnisonVoices);
if (physicalTG >= m_nToneGenerators) continue;
m_pTG[physicalTG]->loadVoiceParameters(&voiceData[6]);
m_pTG[physicalTG]->doRefreshVoice();
setOPMask(0b111111, physicalTG);
// Apply per-voice detune and pan
int baseDetune = m_nMasterTune[nTG];
unsigned basePan = m_nPan[nTG];
unsigned unisonDetune = m_nUnisonDetune[nTG];
unsigned unisonSpread = m_nUnisonSpread[nTG];
float detuneOffset = ((float)v - (newUnisonVoices - 1) / 2.0f) * (float)unisonDetune;
float panOffset = ((float)v - (newUnisonVoices - 1) / 2.0f) * (float)unisonSpread;
int detune = baseDetune + (int)detuneOffset;
unsigned pan = basePan + (int)panOffset;
detune = constrain(detune, -99, 99);
pan = constrain((int)pan, 0, 127);
m_pTG[physicalTG]->setMasterTune((int8_t)detune);
tg_mixer->pan(physicalTG, mapfloat(pan, 0, 127, 0.0f, 1.0f));
}
}
m_nUnisonVoices[nTG] = newUnisonVoices;
break;
}
case TGParameterUnisonDetune:
m_nUnisonDetune[nTG] = constrain(nValue, 0, 99);
break;
case TGParameterUnisonSpread:
m_nUnisonSpread[nTG] = constrain(nValue, 0, 99);
break;
case TGParameterMIDIChannel:
assert (0 <= nValue && nValue <= 255);
SetMIDIChannel ((uint8_t) nValue, nTG);
SetMIDIChannel(nValue, nTG);
break;
case TGParameterReverbSend: SetReverbSend (nValue, nTG); break;
default:
assert (0);
case TGParameterReverbSend:
SetReverbSend(nValue, nTG);
break;
case TGParameterUnknown:
// No action needed for unknown parameter
break;
}
}
@ -1181,69 +1304,69 @@ int CMiniDexed::GetTGParameter (TTGParameter Parameter, unsigned nTG)
case TGParameterATAmplitude: return getModController(3, 2, nTG);
case TGParameterATEGBias: return getModController(3, 3, nTG);
case TGParameterUnisonVoices:
return m_nUnisonVoices[nTG];
case TGParameterUnisonDetune:
return m_nUnisonDetune[nTG];
case TGParameterUnisonSpread:
return m_nUnisonSpread[nTG];
default:
assert (0);
return 0;
}
}
void CMiniDexed::SetVoiceParameter (uint8_t uchOffset, uint8_t uchValue, unsigned nOP, unsigned nTG)
{
assert (nTG < CConfig::AllToneGenerators);
if (nTG >= m_nToneGenerators) return; // Not an active TG
assert (m_pTG[nTG]);
assert (nOP <= 6);
if (nOP < 6)
{
nOP = 5 - nOP; // OPs are in reverse order
if (uchOffset == DEXED_OP_ENABLE)
{
if (uchValue)
{
setOPMask(m_uchOPMask[nTG] | 1 << nOP, nTG);
}
else
{
setOPMask(m_uchOPMask[nTG] & ~(1 << nOP), nTG);
}
return;
}
}
uchOffset += nOP * 21;
assert (uchOffset < 156);
m_pTG[nTG]->setVoiceDataElement (uchOffset, uchValue);
}
uint8_t CMiniDexed::GetVoiceParameter (uint8_t uchOffset, unsigned nOP, unsigned nTG)
{
assert (nTG < CConfig::AllToneGenerators);
if (nTG >= m_nToneGenerators) return 0; // Not an active TG
assert (m_pTG[nTG]);
assert (nOP <= 6);
if (nOP < 6)
{
nOP = 5 - nOP; // OPs are in reverse order
if (uchOffset == DEXED_OP_ENABLE)
{
return !!(m_uchOPMask[nTG] & (1 << nOP));
}
}
uchOffset += nOP * 21;
assert (uchOffset < 156);
return m_pTG[nTG]->getVoiceDataElement (uchOffset);
void CMiniDexed::SetVoiceParameter(uint8_t uchOffset, uint8_t uchValue, unsigned nOP, unsigned nTG)
{
assert(nTG < CConfig::AllToneGenerators);
if (nTG >= m_nToneGenerators) return; // Not an active TG
unsigned unisonVoices = m_nUnisonVoices[nTG];
if (unisonVoices < 1) unisonVoices = 1;
LOGNOTE("SetVoiceParameter: logicalTG=%u, unisonVoices=%u", nTG, unisonVoices);
for (unsigned v = 0; v < unisonVoices; ++v) {
unsigned physicalTG = getPhysicalTG(nTG, v, maxUnisonVoices);
LOGNOTE(" -> physicalTG=%u (logicalTG=%u, unisonVoice=%u)", physicalTG, nTG, v);
if (physicalTG >= m_nToneGenerators) break;
assert(m_pTG[physicalTG]);
unsigned op = nOP;
if (op < 6) {
op = 5 - op; // OPs are in reverse order
if (uchOffset == DEXED_OP_ENABLE) {
if (uchValue) {
setOPMask(m_uchOPMask[physicalTG] | 1 << op, physicalTG);
} else {
setOPMask(m_uchOPMask[physicalTG] & ~(1 << op), physicalTG);
}
continue;
}
}
uint8_t offset = uchOffset + op * 21;
assert(offset < 156);
m_pTG[physicalTG]->setVoiceDataElement(offset, uchValue);
}
}
uint8_t CMiniDexed::GetVoiceParameter(uint8_t uchOffset, unsigned nOP, unsigned nTG)
{
assert(nTG < CConfig::AllToneGenerators);
if (nTG >= m_nToneGenerators) return 0; // Not an active TG
unsigned unisonVoices = m_nUnisonVoices[nTG];
if (unisonVoices < 1) unisonVoices = 1;
unsigned physicalTG = getPhysicalTG(nTG, 0, maxUnisonVoices); // Use first physical TG for this logical TG
LOGNOTE("GetVoiceParameter: logicalTG=%u, unisonVoices=%u, physicalTG=%u", nTG, unisonVoices, physicalTG);
assert(m_pTG[physicalTG]);
unsigned op = nOP;
if (op < 6) {
op = 5 - op; // OPs are in reverse order
if (uchOffset == DEXED_OP_ENABLE) {
return !!(m_uchOPMask[physicalTG] & (1 << op));
}
}
uint8_t offset = uchOffset + op * 21;
assert(offset < 156);
return m_pTG[physicalTG]->getVoiceDataElement(offset);
}
std::string CMiniDexed::GetVoiceName (unsigned nTG)
@ -1408,7 +1531,22 @@ void CMiniDexed::ProcessSound (void)
{
for (uint8_t i = 0; i < m_nToneGenerators; i++)
{
unsigned unisonVoices = 1;
// Find which logical TG this physical TG belongs to
for (unsigned lTG = 0; lTG < m_nToneGenerators; ++lTG) {
unsigned uv = m_nUnisonVoices[lTG];
if (uv < 1) uv = 1;
unsigned start = lTG * uv;
unsigned end = start + uv;
if (i >= start && i < end) {
unisonVoices = uv;
break;
}
}
float gainComp = 1.0f / sqrtf((float)unisonVoices);
tg_mixer->gain(i, gainComp, true);
tg_mixer->doAddMix(i,m_OutputLevel[i]);
reverb_send_mixer->gain(i, gainComp * mapfloat(m_nReverbSend[i],0,99,0.0f,1.0f), true);
reverb_send_mixer->doAddMix(i,m_OutputLevel[i]);
}
// END TG mixing
@ -1548,14 +1686,14 @@ bool CMiniDexed::DoSavePerformance (void)
m_PerformanceConfig.SetCutoff (m_nCutoff[nTG], nTG);
m_PerformanceConfig.SetResonance (m_nResonance[nTG], nTG);
m_PerformanceConfig.SetPitchBendRange (m_nPitchBendRange[nTG], nTG);
m_PerformanceConfig.SetPitchBendStep (m_nPitchBendStep[nTG], nTG);
m_PerformanceConfig.SetPitchBendStep (m_nPitchBendStep[nTG], nTG);
m_PerformanceConfig.SetPortamentoMode (m_nPortamentoMode[nTG], nTG);
m_PerformanceConfig.SetPortamentoGlissando (m_nPortamentoGlissando[nTG], nTG);
m_PerformanceConfig.SetPortamentoTime (m_nPortamentoTime[nTG], nTG);
m_PerformanceConfig.SetNoteLimitLow (m_nNoteLimitLow[nTG], nTG);
m_PerformanceConfig.SetNoteLimitHigh (m_nNoteLimitHigh[nTG], nTG);
m_PerformanceConfig.SetNoteShift (m_nNoteShift[nTG], nTG);
m_nNoteLimitLow[nTG] = m_PerformanceConfig.GetNoteLimitLow (nTG);
m_nNoteLimitHigh[nTG] = m_PerformanceConfig.GetNoteLimitHigh (nTG);
m_nNoteShift[nTG] = m_PerformanceConfig.GetNoteShift (nTG);
if (nTG < m_pConfig->GetToneGenerators())
{
m_pTG[nTG]->getVoiceData(m_nRawVoiceData);
@ -1574,8 +1712,11 @@ bool CMiniDexed::DoSavePerformance (void)
m_PerformanceConfig.SetBreathControlTarget (m_nBreathControlTarget[nTG], nTG);
m_PerformanceConfig.SetAftertouchRange (m_nAftertouchRange[nTG], nTG);
m_PerformanceConfig.SetAftertouchTarget (m_nAftertouchTarget[nTG], nTG);
m_PerformanceConfig.SetReverbSend (m_nReverbSend[nTG], nTG);
m_PerformanceConfig.SetUnisonVoices(m_nUnisonVoices[nTG], nTG);
m_PerformanceConfig.SetUnisonDetune(m_nUnisonDetune[nTG], nTG);
m_PerformanceConfig.SetUnisonSpread(m_nUnisonSpread[nTG], nTG);
}
m_PerformanceConfig.SetCompressorEnable (!!m_nParameter[ParameterCompressorEnable]);
@ -1605,6 +1746,10 @@ void CMiniDexed::setMonoMode(uint8_t mono, uint8_t nTG)
m_bMonoMode[nTG]= mono != 0;
m_pTG[nTG]->setMonoMode(constrain(mono, 0, 1));
m_pTG[nTG]->doRefreshVoice();
FOR_AUX_UNISON_TGS(nTG, {
m_pTG[physicalTG]->setMonoMode(constrain(mono, 0, 1));
m_pTG[physicalTG]->doRefreshVoice();
})
m_UI.ParameterChanged ();
}
@ -1807,16 +1952,47 @@ void CMiniDexed::loadVoiceParameters(const uint8_t* data, uint8_t nTG)
memcpy(voice, data, sizeof(uint8_t)*161);
// fix voice name
for (uint8_t i = 0; i < 10; i++)
for (uint8_t i = 0; i <10; i++)
{
if (voice[151 + i] > 126) // filter characters
voice[151 + i] = 32;
voice[151 +i] = 32;
}
m_pTG[nTG]->loadVoiceParameters(&voice[6]);
m_pTG[nTG]->doRefreshVoice();
setOPMask(0b111111, nTG);
// Propagate to auxiliary TGs for unison
unsigned unisonVoices = m_nUnisonVoices[nTG];
if (unisonVoices < 1) unisonVoices = 1;
int baseDetune = m_nMasterTune[nTG];
unsigned basePan = m_nPan[nTG];
unsigned unisonDetune = m_nUnisonDetune[nTG];
unsigned unisonSpread = m_nUnisonSpread[nTG];
for (unsigned v = 0; v < unisonVoices; ++v) {
unsigned physicalTG = getPhysicalTG(nTG, v, maxUnisonVoices);
if (physicalTG == nTG || physicalTG >= m_nToneGenerators) continue;
m_pTG[physicalTG]->loadVoiceParameters(&voice[6]);
m_pTG[physicalTG]->doRefreshVoice();
setOPMask(0b111111, physicalTG);
// Apply per-voice detune and pan
float detuneOffset = ((float)v - (unisonVoices - 1) / 2.0f) * (float)unisonDetune;
float panOffset = ((float)v - (unisonVoices - 1) / 2.0f) * (float)unisonSpread;
int detune = baseDetune + (int)detuneOffset;
unsigned pan = basePan + (int)panOffset;
detune = constrain(detune, -99, 99);
pan = constrain((int)pan, 0, 127);
m_pTG[physicalTG]->setMasterTune((int8_t)detune);
tg_mixer->pan(physicalTG, mapfloat(pan, 0, 127, 0.0f, 1.0f));
}
// Silence any unused auxiliary TGs (to prevent leftover sound/crackle)
for (unsigned v = unisonVoices; v < maxUnisonVoices; ++v) { // maxUnisonVoices is the max unison voices
unsigned physicalTG = getPhysicalTG(nTG, v, maxUnisonVoices);
if (physicalTG >= m_nToneGenerators) continue;
m_pTG[physicalTG]->notesOff();
m_pTG[physicalTG]->panic();
}
m_UI.ParameterChanged ();
}
@ -1838,6 +2014,15 @@ int16_t CMiniDexed::checkSystemExclusive(const uint8_t* pMessage,const uint16_t
assert (m_pTG[nTG]);
// Log which TGs are affected by SysEx voice load
unsigned unisonVoices = m_nUnisonVoices[nTG];
if (unisonVoices < 1) unisonVoices = 1;
LOGNOTE("SysEx voice load: logicalTG=%u, unisonVoices=%u", nTG, unisonVoices);
for (unsigned v = 0; v < unisonVoices; ++v) {
unsigned physicalTG = getPhysicalTG(nTG, v, maxUnisonVoices);
LOGNOTE(" affected physicalTG=%u (logicalTG=%u, unisonVoice=%u)", physicalTG, nTG, v);
}
return(m_pTG[nTG]->checkSystemExclusive(pMessage, nLength));
}
@ -1877,6 +2062,10 @@ void CMiniDexed::setOPMask(uint8_t uchOPMask, uint8_t nTG)
{
m_uchOPMask[nTG] = uchOPMask;
m_pTG[nTG]->setOPAll (m_uchOPMask[nTG]);
FOR_AUX_UNISON_TGS(nTG, {
m_uchOPMask[physicalTG] = uchOPMask;
m_pTG[physicalTG]->setOPAll(uchOPMask);
})
}
void CMiniDexed::setMasterVolume(float32_t vol)
@ -2029,58 +2218,60 @@ bool CMiniDexed::DoSavePerformanceNewFile (void)
void CMiniDexed::LoadPerformanceParameters(void)
{
for (unsigned nTG = 0; nTG < CConfig::AllToneGenerators; 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);
SetPan (m_PerformanceConfig.GetPan (nTG), nTG);
SetMasterTune (m_PerformanceConfig.GetDetune (nTG), nTG);
SetCutoff (m_PerformanceConfig.GetCutoff (nTG), nTG);
SetResonance (m_PerformanceConfig.GetResonance (nTG), nTG);
setPitchbendRange (m_PerformanceConfig.GetPitchBendRange (nTG), nTG);
setPitchbendStep (m_PerformanceConfig.GetPitchBendStep (nTG), nTG);
setPortamentoMode (m_PerformanceConfig.GetPortamentoMode (nTG), nTG);
setPortamentoGlissando (m_PerformanceConfig.GetPortamentoGlissando (nTG), nTG);
setPortamentoTime (m_PerformanceConfig.GetPortamentoTime (nTG), nTG);
m_nNoteLimitLow[nTG] = m_PerformanceConfig.GetNoteLimitLow (nTG);
m_nNoteLimitHigh[nTG] = m_PerformanceConfig.GetNoteLimitHigh (nTG);
m_nNoteShift[nTG] = m_PerformanceConfig.GetNoteShift (nTG);
if(m_PerformanceConfig.VoiceDataFilled(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);
SetPan (m_PerformanceConfig.GetPan (nTG), nTG);
SetMasterTune (m_PerformanceConfig.GetDetune (nTG), nTG);
SetCutoff (m_PerformanceConfig.GetCutoff (nTG), nTG);
SetResonance (m_PerformanceConfig.GetResonance (nTG), nTG);
setPitchbendRange (m_PerformanceConfig.GetPitchBendRange (nTG), nTG);
setPitchbendStep (m_PerformanceConfig.GetPitchBendStep (nTG), nTG);
setPortamentoMode (m_PerformanceConfig.GetPortamentoMode (nTG), nTG);
setPortamentoGlissando (m_PerformanceConfig.GetPortamentoGlissando (nTG), nTG);
setPortamentoTime (m_PerformanceConfig.GetPortamentoTime (nTG), nTG);
m_nNoteLimitLow[nTG] = m_PerformanceConfig.GetNoteLimitLow (nTG);
m_nNoteLimitHigh[nTG] = m_PerformanceConfig.GetNoteLimitHigh (nTG);
m_nNoteShift[nTG] = m_PerformanceConfig.GetNoteShift (nTG);
if(m_PerformanceConfig.VoiceDataFilled(nTG))
{
uint8_t* tVoiceData = m_PerformanceConfig.GetVoiceDataFromTxt(nTG);
m_pTG[nTG]->loadVoiceParameters(tVoiceData);
setOPMask(0b111111, nTG);
}
setMonoMode(m_PerformanceConfig.GetMonoMode(nTG) ? 1 : 0, nTG);
SetReverbSend (m_PerformanceConfig.GetReverbSend (nTG), nTG);
setModWheelRange (m_PerformanceConfig.GetModulationWheelRange (nTG), nTG);
setModWheelTarget (m_PerformanceConfig.GetModulationWheelTarget (nTG), nTG);
setFootControllerRange (m_PerformanceConfig.GetFootControlRange (nTG), nTG);
setFootControllerTarget (m_PerformanceConfig.GetFootControlTarget (nTG), nTG);
setBreathControllerRange (m_PerformanceConfig.GetBreathControlRange (nTG), nTG);
setBreathControllerTarget (m_PerformanceConfig.GetBreathControlTarget (nTG), nTG);
setAftertouchRange (m_PerformanceConfig.GetAftertouchRange (nTG), nTG);
setAftertouchTarget (m_PerformanceConfig.GetAftertouchTarget (nTG), nTG);
uint8_t* tVoiceData = m_PerformanceConfig.GetVoiceDataFromTxt(nTG);
m_pTG[nTG]->loadVoiceParameters(tVoiceData);
setOPMask(0b111111, nTG);
}
// Effects
SetParameter (ParameterCompressorEnable, m_PerformanceConfig.GetCompressorEnable () ? 1 : 0);
SetParameter (ParameterReverbEnable, m_PerformanceConfig.GetReverbEnable () ? 1 : 0);
SetParameter (ParameterReverbSize, m_PerformanceConfig.GetReverbSize ());
SetParameter (ParameterReverbHighDamp, m_PerformanceConfig.GetReverbHighDamp ());
SetParameter (ParameterReverbLowDamp, m_PerformanceConfig.GetReverbLowDamp ());
SetParameter (ParameterReverbLowPass, m_PerformanceConfig.GetReverbLowPass ());
SetParameter (ParameterReverbDiffusion, m_PerformanceConfig.GetReverbDiffusion ());
SetParameter (ParameterReverbLevel, m_PerformanceConfig.GetReverbLevel ());
m_UI.DisplayChanged ();
setMonoMode(m_PerformanceConfig.GetMonoMode(nTG) ? 1 : 0, nTG);
SetReverbSend (m_PerformanceConfig.GetReverbSend (nTG), nTG);
setModWheelRange (m_PerformanceConfig.GetModulationWheelRange (nTG), nTG);
setModWheelTarget (m_PerformanceConfig.GetModulationWheelTarget (nTG), nTG);
setFootControllerRange (m_PerformanceConfig.GetFootControlRange (nTG), nTG);
setFootControllerTarget (m_PerformanceConfig.GetFootControlTarget (nTG), nTG);
setBreathControllerRange (m_PerformanceConfig.GetBreathControlRange (nTG), nTG);
setBreathControllerTarget (m_PerformanceConfig.GetBreathControlTarget (nTG), nTG);
setAftertouchRange (m_PerformanceConfig.GetAftertouchRange (nTG), nTG);
setAftertouchTarget (m_PerformanceConfig.GetAftertouchTarget (nTG), nTG);
// Unison parameters
m_nUnisonVoices[nTG] = m_PerformanceConfig.GetUnisonVoices(nTG);
m_nUnisonDetune[nTG] = m_PerformanceConfig.GetUnisonDetune(nTG);
m_nUnisonSpread[nTG] = m_PerformanceConfig.GetUnisonSpread(nTG);
}
// Effects
SetParameter (ParameterCompressorEnable, m_PerformanceConfig.GetCompressorEnable () ? 1 : 0);
SetParameter (ParameterReverbEnable, m_PerformanceConfig.GetReverbEnable () ? 1 : 0);
SetParameter (ParameterReverbSize, m_PerformanceConfig.GetReverbSize ());
SetParameter (ParameterReverbHighDamp, m_PerformanceConfig.GetReverbHighDamp ());
SetParameter (ParameterReverbLowDamp, m_PerformanceConfig.GetReverbLowDamp ());
SetParameter (ParameterReverbLowPass, m_PerformanceConfig.GetReverbLowPass ());
SetParameter (ParameterReverbDiffusion, m_PerformanceConfig.GetReverbDiffusion ());
SetParameter (ParameterReverbLevel, m_PerformanceConfig.GetReverbLevel ());
m_UI.DisplayChanged ();
}
std::string CMiniDexed::GetNewPerformanceDefaultName(void)

@ -222,6 +222,10 @@ public:
TGParameterATAmplitude,
TGParameterATEGBias,
TGParameterUnisonVoices,
TGParameterUnisonDetune,
TGParameterUnisonSpread,
TGParameterUnknown
};
@ -305,9 +309,12 @@ private:
int m_nNoteShift[CConfig::AllToneGenerators];
unsigned m_nReverbSend[CConfig::AllToneGenerators];
unsigned m_nUnisonVoices[CConfig::AllToneGenerators];
unsigned m_nUnisonDetune[CConfig::AllToneGenerators];
unsigned m_nUnisonSpread[CConfig::AllToneGenerators];
uint8_t m_nRawVoiceData[156];
uint8_t m_noteOnCount[CConfig::AllToneGenerators][128];
float32_t nMasterVolume;

@ -209,7 +209,14 @@ bool CPerformanceConfig::Load (void)
PropertyName.Format ("AftertouchTarget%u", nTG+1);
m_nAftertouchTarget[nTG] = m_Properties.GetNumber (PropertyName, 0);
}
// Unison parameters
PropertyName.Format ("UnisonVoices%u", nTG+1);
m_nUnisonVoices[nTG] = m_Properties.GetNumber(PropertyName, 1);
PropertyName.Format ("UnisonDetune%u", nTG+1);
m_nUnisonDetune[nTG] = m_Properties.GetNumber(PropertyName, 0);
PropertyName.Format ("UnisonSpread%u", nTG+1);
m_nUnisonSpread[nTG] = m_Properties.GetNumber(PropertyName, 0);
}
m_bCompressorEnable = m_Properties.GetNumber ("CompressorEnable", 1) != 0;
@ -327,7 +334,14 @@ bool CPerformanceConfig::Save (void)
PropertyName.Format ("AftertouchTarget%u", nTG+1);
m_Properties.SetNumber (PropertyName, m_nAftertouchTarget[nTG]);
}
// Unison parameters
PropertyName.Format ("UnisonVoices%u", nTG+1);
m_Properties.SetNumber(PropertyName, m_nUnisonVoices[nTG]);
PropertyName.Format ("UnisonDetune%u", nTG+1);
m_Properties.SetNumber(PropertyName, m_nUnisonDetune[nTG]);
PropertyName.Format ("UnisonSpread%u", nTG+1);
m_Properties.SetNumber(PropertyName, m_nUnisonSpread[nTG]);
}
m_Properties.SetNumber ("CompressorEnable", m_bCompressorEnable ? 1 : 0);
@ -1314,3 +1328,28 @@ bool CPerformanceConfig::IsValidPerformanceBank(unsigned nBankID)
}
return true;
}
unsigned CPerformanceConfig::GetUnisonVoices(unsigned nTG) const {
assert(nTG < CConfig::AllToneGenerators);
return m_nUnisonVoices[nTG];
}
unsigned CPerformanceConfig::GetUnisonDetune(unsigned nTG) const {
assert(nTG < CConfig::AllToneGenerators);
return m_nUnisonDetune[nTG];
}
unsigned CPerformanceConfig::GetUnisonSpread(unsigned nTG) const {
assert(nTG < CConfig::AllToneGenerators);
return m_nUnisonSpread[nTG];
}
void CPerformanceConfig::SetUnisonVoices(unsigned nValue, unsigned nTG) {
assert(nTG < CConfig::AllToneGenerators);
m_nUnisonVoices[nTG] = nValue;
}
void CPerformanceConfig::SetUnisonDetune(unsigned nValue, unsigned nTG) {
assert(nTG < CConfig::AllToneGenerators);
m_nUnisonDetune[nTG] = nValue;
}
void CPerformanceConfig::SetUnisonSpread(unsigned nValue, unsigned nTG) {
assert(nTG < CConfig::AllToneGenerators);
m_nUnisonSpread[nTG] = nValue;
}

@ -70,6 +70,12 @@ public:
unsigned GetBreathControlTarget (unsigned nTG) const; // 0 .. 7
unsigned GetAftertouchRange (unsigned nTG) const; // 0 .. 99
unsigned GetAftertouchTarget (unsigned nTG) const; // 0 .. 7
unsigned GetUnisonVoices(unsigned nTG) const; // 1..4
unsigned GetUnisonDetune(unsigned nTG) const; // 0..99
unsigned GetUnisonSpread(unsigned nTG) const; // 0..99
void SetUnisonVoices(unsigned nValue, unsigned nTG);
void SetUnisonDetune(unsigned nValue, unsigned nTG);
void SetUnisonSpread(unsigned nValue, unsigned nTG);
void SetBankNumber (unsigned nValue, unsigned nTG);
void SetVoiceNumber (unsigned nValue, unsigned nTG);
@ -182,6 +188,9 @@ private:
unsigned m_nBreathControlTarget[CConfig::AllToneGenerators];
unsigned m_nAftertouchRange[CConfig::AllToneGenerators];
unsigned m_nAftertouchTarget[CConfig::AllToneGenerators];
unsigned m_nUnisonVoices[CConfig::AllToneGenerators];
unsigned m_nUnisonDetune[CConfig::AllToneGenerators];
unsigned m_nUnisonSpread[CConfig::AllToneGenerators];
unsigned m_nLastPerformance;
unsigned m_nActualPerformance = 0;

@ -71,22 +71,31 @@ const CUIMenu::TMenuItem CUIMenu::s_MainMenu[] =
const CUIMenu::TMenuItem CUIMenu::s_TGMenu[] =
{
{"Voice", EditProgramNumber},
{"Bank", EditVoiceBankNumber},
{"Volume", EditTGParameter, 0, CMiniDexed::TGParameterVolume},
{"Voice", EditProgramNumber},
{"Bank", EditVoiceBankNumber},
{"Volume", EditTGParameter, 0, CMiniDexed::TGParameterVolume},
#ifdef ARM_ALLOW_MULTI_CORE
{"Pan", EditTGParameter, 0, CMiniDexed::TGParameterPan},
{"Reverb-Send", EditTGParameter, 0, CMiniDexed::TGParameterReverbSend},
{"Pan", EditTGParameter, 0, CMiniDexed::TGParameterPan},
{"Reverb-Send", EditTGParameter, 0, CMiniDexed::TGParameterReverbSend},
#endif
{"Detune", EditTGParameter, 0, CMiniDexed::TGParameterMasterTune},
{"Cutoff", EditTGParameter, 0, CMiniDexed::TGParameterCutoff},
{"Resonance", EditTGParameter, 0, CMiniDexed::TGParameterResonance},
{"Pitch Bend", MenuHandler, s_EditPitchBendMenu},
{"Portamento", MenuHandler, s_EditPortamentoMenu},
{"Poly/Mono", EditTGParameter, 0, CMiniDexed::TGParameterMonoMode},
{"Modulation", MenuHandler, s_ModulationMenu},
{"Channel", EditTGParameter, 0, CMiniDexed::TGParameterMIDIChannel},
{"Edit Voice", MenuHandler, s_EditVoiceMenu},
{"Detune", EditTGParameter, 0, CMiniDexed::TGParameterMasterTune},
{"Cutoff", EditTGParameter, 0, CMiniDexed::TGParameterCutoff},
{"Resonance", EditTGParameter, 0, CMiniDexed::TGParameterResonance},
{"Unison", MenuHandler, s_UnisonMenu},
{"Pitch Bend", MenuHandler, s_EditPitchBendMenu},
{"Portamento", MenuHandler, s_EditPortamentoMenu},
{"Poly/Mono", EditTGParameter, 0, CMiniDexed::TGParameterMonoMode},
{"Modulation", MenuHandler, s_ModulationMenu},
{"Channel", EditTGParameter, 0, CMiniDexed::TGParameterMIDIChannel},
{"Edit Voice", MenuHandler, s_EditVoiceMenu},
{0}
};
const CUIMenu::TMenuItem CUIMenu::s_UnisonMenu[] =
{
{"Voices", EditTGParameter, 0, CMiniDexed::TGParameterUnisonVoices},
{"Detune", EditTGParameter, 0, CMiniDexed::TGParameterUnisonDetune},
{"Spread", EditTGParameter, 0, CMiniDexed::TGParameterUnisonSpread},
{0}
};
@ -265,7 +274,10 @@ const CUIMenu::TParameter CUIMenu::s_TGParameter[CMiniDexed::TGParameterUnknown]
{0, 99, 1}, //AT Range
{0, 1, 1, ToOnOff}, //AT Pitch
{0, 1, 1, ToOnOff}, //AT Amp
{0, 1, 1, ToOnOff} //AT EGBias
{0, 1, 1, ToOnOff}, //AT EGBias
{1, 4, 1}, // Unison Voices
{0, 99, 1}, // Unison Detune
{0, 99, 1}, // Unison Spread
};
// must match DexedVoiceParameters in Synth_Dexed
@ -734,9 +746,16 @@ void CUIMenu::EditProgramNumber (CUIMenu *pUIMenu, TMenuEvent Event)
}
}
void CUIMenu::EditTGParameter (CUIMenu *pUIMenu, TMenuEvent Event)
void CUIMenu::EditTGParameter(CUIMenu *pUIMenu, TMenuEvent Event)
{
unsigned nTG = pUIMenu->m_nMenuStackParameter[pUIMenu->m_nCurrentMenuDepth-1];
unsigned nTG = 0;
// Always determine the correct logical TG from the menu stack
if (pUIMenu->m_nCurrentMenuDepth >= 2) {
nTG = pUIMenu->m_nMenuStackParameter[1];
} else if (pUIMenu->m_nCurrentMenuDepth >= 1) {
nTG = pUIMenu->m_nMenuStackParameter[0];
}
if (nTG >= pUIMenu->m_nToneGenerators) nTG = 0;
CMiniDexed::TTGParameter Param = (CMiniDexed::TTGParameter) pUIMenu->m_nCurrentParameter;
const TParameter &rParam = s_TGParameter[Param];
@ -776,15 +795,15 @@ void CUIMenu::EditTGParameter (CUIMenu *pUIMenu, TMenuEvent Event)
return;
}
string TG ("TG");
TG += to_string (nTG+1);
std::string TG ("TG");
TG += std::to_string (nTG+1);
string Value = GetTGValueString (Param, pUIMenu->m_pMiniDexed->GetTGParameter (Param, nTG));
std::string Value = GetTGValueString (Param, pUIMenu->m_pMiniDexed->GetTGParameter (Param, nTG));
pUIMenu->m_pUI->DisplayWrite (TG.c_str (),
pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name,
Value.c_str (),
nValue > rParam.Minimum, nValue < rParam.Maximum);
pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name,
Value.c_str (),
nValue > rParam.Minimum, nValue < rParam.Maximum);
}
void CUIMenu::EditTGParameter2 (CUIMenu *pUIMenu, TMenuEvent Event) // second menu level. Redundant code but in order to not modified original code
@ -1098,7 +1117,7 @@ string CUIMenu::GetOPValueString (unsigned nOPParameter, int nValue)
string CUIMenu::ToVolume (int nValue)
{
static const size_t MaxChars = CConfig::LCDColumns-2;
static const std::size_t MaxChars = CConfig::LCDColumns-2;
char VolumeBar[MaxChars+1];
memset (VolumeBar, 0xFF, sizeof VolumeBar); // 0xFF is the block character
VolumeBar[nValue * MaxChars / 127] = '\0';
@ -1109,7 +1128,7 @@ string CUIMenu::ToVolume (int nValue)
string CUIMenu::ToPan (int nValue)
{
assert (CConfig::LCDColumns == 16);
static const size_t MaxChars = CConfig::LCDColumns-3;
static const std::size_t MaxChars = CConfig::LCDColumns-3;
char PanMarker[MaxChars+1] = "......:......";
unsigned nIndex = nValue * MaxChars / 127;
if (nIndex == MaxChars)

@ -57,11 +57,6 @@ public:
};
public:
CUIMenu (CUserInterface *pUI, CMiniDexed *pMiniDexed, CConfig *pConfig);
void EventHandler (TMenuEvent Event);
private:
typedef void TMenuHandler (CUIMenu *pUIMenu, TMenuEvent Event);
struct TMenuItem
@ -72,6 +67,17 @@ private:
unsigned Parameter;
};
static const TMenuItem s_UnisonMenu[];
static void EditTGParameter(CUIMenu *pUIMenu, TMenuEvent Event);
static void EditVoiceParameter(CUIMenu *pUIMenu, TMenuEvent Event);
static void EditOPParameter(CUIMenu *pUIMenu, TMenuEvent Event);
static void SavePerformance(CUIMenu *pUIMenu, TMenuEvent Event);
public:
CUIMenu(CUserInterface *pUI, CMiniDexed *pMiniDexed, CConfig *pConfig);
void EventHandler(TMenuEvent Event);
private:
typedef std::string TToString (int nValue);
struct TParameter
@ -87,10 +93,6 @@ private:
static void EditGlobalParameter (CUIMenu *pUIMenu, TMenuEvent Event);
static void EditVoiceBankNumber (CUIMenu *pUIMenu, TMenuEvent Event);
static void EditProgramNumber (CUIMenu *pUIMenu, TMenuEvent Event);
static void EditTGParameter (CUIMenu *pUIMenu, TMenuEvent Event);
static void EditVoiceParameter (CUIMenu *pUIMenu, TMenuEvent Event);
static void EditOPParameter (CUIMenu *pUIMenu, TMenuEvent Event);
static void SavePerformance (CUIMenu *pUIMenu, TMenuEvent Event);
static void EditTGParameter2 (CUIMenu *pUIMenu, TMenuEvent Event);
static void EditTGParameterModulation (CUIMenu *pUIMenu, TMenuEvent Event);
static void PerformanceMenu (CUIMenu *pUIMenu, TMenuEvent Event);

Loading…
Cancel
Save