Initial Unison support

Note that this doesn't increase the total number of TGs, the other ones will just stay silent.
Menu integration still to be improved.
pull/901/head
probonopd 2 weeks ago
parent 83ea7ee551
commit ce6810a8b7
  1. 97
      src/minidexed.cpp
  2. 8
      src/minidexed.h
  3. 39
      src/performanceconfig.cpp
  4. 9
      src/performanceconfig.h
  5. 8
      src/uimenu.cpp

@ -31,6 +31,9 @@
#include <assert.h>
#include "arm_float_to_q23.h"
// Forward declaration for getPhysicalTG
static unsigned getPhysicalTG(unsigned logicalTG, unsigned unisonVoice, unsigned unisonVoices);
const char WLANFirmwarePath[] = "SD:firmware/";
const char WLANConfigFile[] = "SD:wpa_supplicant.conf";
#define FTPUSERNAME "admin"
@ -126,6 +129,9 @@ 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;
}
unsigned nUSBGadgetPin = pConfig->GetUSBGadgetPin();
@ -848,9 +854,34 @@ 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, unisonVoices);
if (physicalTG >= m_nToneGenerators) break;
// Ensure virtual TG plays the same voice as logical TG
if (physicalTG != nTG) {
uint8_t voiceData[156];
m_pTG[nTG]->getVoiceData(voiceData);
m_pTG[physicalTG]->loadVoiceParameters(voiceData);
}
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 +893,30 @@ 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;
unsigned maxLogicalTGs = m_nToneGenerators / unisonVoices;
if (nTG >= maxLogicalTGs) return; // Don't exceed available physical TGs
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, unisonVoices);
if (physicalTG >= m_nToneGenerators) break;
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);
}
}
@ -1130,10 +1182,20 @@ void CMiniDexed::SetTGParameter (TTGParameter Parameter, int nValue, unsigned nT
case TGParameterReverbSend: SetReverbSend (nValue, nTG); break;
default:
assert (0);
case TGParameterUnisonVoices:
m_nUnisonVoices[nTG] = constrain(nValue, 1, 4);
break;
case TGParameterUnisonDetune:
m_nUnisonDetune[nTG] = constrain(nValue, 0, 99);
break;
case TGParameterUnisonSpread:
m_nUnisonSpread[nTG] = constrain(nValue, 0, 99);
break;
case TGParameterUnknown:
// No action needed for unknown parameter
break;
}
}
int CMiniDexed::GetTGParameter (TTGParameter Parameter, unsigned nTG)
@ -1180,7 +1242,12 @@ 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;
@ -2025,7 +2092,6 @@ 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);
@ -2062,7 +2128,10 @@ void CMiniDexed::LoadPerformanceParameters(void)
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
@ -2493,3 +2562,9 @@ bool CMiniDexed::InitNetwork()
return false;
}
}
// Forward declaration and definition for getPhysicalTG
static unsigned getPhysicalTG(unsigned logicalTG, unsigned unisonVoice, unsigned unisonVoices);
static unsigned getPhysicalTG(unsigned logicalTG, unsigned unisonVoice, unsigned unisonVoices) {
return logicalTG * unisonVoices + unisonVoice;
}

@ -222,6 +222,10 @@ public:
TGParameterATAmplitude,
TGParameterATEGBias,
TGParameterUnisonVoices,
TGParameterUnisonDetune,
TGParameterUnisonSpread,
TGParameterUnknown
};
@ -305,7 +309,9 @@ 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];

@ -210,6 +210,13 @@ 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;
@ -328,6 +335,13 @@ 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);
@ -1328,3 +1342,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;

@ -81,6 +81,9 @@ const CUIMenu::TMenuItem CUIMenu::s_TGMenu[] =
{"Detune", EditTGParameter, 0, CMiniDexed::TGParameterMasterTune},
{"Cutoff", EditTGParameter, 0, CMiniDexed::TGParameterCutoff},
{"Resonance", EditTGParameter, 0, CMiniDexed::TGParameterResonance},
{"Unison Voices", EditTGParameter, 0, CMiniDexed::TGParameterUnisonVoices},
{"Unison Detune", EditTGParameter, 0, CMiniDexed::TGParameterUnisonDetune},
{"Unison Spread", EditTGParameter, 0, CMiniDexed::TGParameterUnisonSpread},
{"Pitch Bend", MenuHandler, s_EditPitchBendMenu},
{"Portamento", MenuHandler, s_EditPortamentoMenu},
{"Poly/Mono", EditTGParameter, 0, CMiniDexed::TGParameterMonoMode},
@ -265,7 +268,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

Loading…
Cancel
Save