diff --git a/src/minidexed.cpp b/src/minidexed.cpp
index 2ddab6d..8f71675 100644
--- a/src/minidexed.cpp
+++ b/src/minidexed.cpp
@@ -58,7 +58,6 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt,
m_nProgram[i] = 0;
m_nVolume[i] = 100;
m_nPan[i] = 64;
- pan_float[i]=0.0f;
m_nMasterTune[i] = 0;
m_nMIDIChannel[i] = CMIDIDevice::Disabled;
@@ -114,10 +113,6 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt,
}
#endif
- // BEGIN setup tg_mixer
- //tg_mixer = new AudioStereoMixer<8>();
- // END setup tg_mixer
-
SetParameter (ParameterCompressorEnable, 1);
// BEGIN setup reverb
@@ -127,7 +122,7 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt,
SetParameter (ParameterReverbHighDamp, 50);
SetParameter (ParameterReverbLowDamp, 50);
SetParameter (ParameterReverbLowPass, 30);
- SetParameter (ParameterReverbDiffusion, 20);
+ SetParameter (ParameterReverbDiffusion, 65);
SetParameter (ParameterReverbLevel, 80);
// END setup reverb
};
@@ -188,7 +183,7 @@ bool CMiniDexed::Initialize (void)
SetParameter (ParameterReverbLowDamp, m_PerformanceConfig.GetReverbLowDamp ());
SetParameter (ParameterReverbLowPass, m_PerformanceConfig.GetReverbLowPass ());
SetParameter (ParameterReverbDiffusion, m_PerformanceConfig.GetReverbDiffusion ());
- SetParameter (ParameterReverbSend, m_PerformanceConfig.GetReverbSend ());
+ SetParameter (ParameterReverbLevel, m_PerformanceConfig.GetReverbLevel ());
}
else
{
@@ -303,7 +298,7 @@ void CMiniDexed::Run (unsigned nCore)
for (unsigned i = 0; i < CConfig::TGsCore23; i++, nTG++)
{
assert (m_pTG[nTG]);
- m_pTG[nTG]->getSamples (m_OutputLevel[nTG], m_nFramesToProcess);
+ m_pTG[nTG]->getSamples (m_OutputLevel[nTG],m_nFramesToProcess);
}
}
}
@@ -366,11 +361,13 @@ void CMiniDexed::SetVolume (unsigned nVolume, unsigned nTG)
void CMiniDexed::SetPan (unsigned nPan, unsigned nTG)
{
- constrain(nPan,-1.0f,1.0f);
+ if (nPan > 127)
+ {
+ return;
+ }
assert (nTG < CConfig::ToneGenerators);
m_nPan[nTG] = nPan;
- pan_float[nTG]=mapfloat(nPan,0,127,-1.0,1.0);
m_UI.ParameterChanged ();
}
@@ -509,14 +506,6 @@ void CMiniDexed::SetParameter (TParameter Parameter, int nValue)
switch (Parameter)
{
-<<<<<<< HEAD
- case ParameterReverbSize: reverb->size (fValue); break;
- case ParameterReverbHighDamp: reverb->hidamp (fValue); break;
- case ParameterReverbLowDamp: reverb->lodamp (fValue); break;
- case ParameterReverbLowPass: reverb->lowpass (fValue); break;
- case ParameterReverbDiffusion: reverb->diffusion (fValue); break;
- case ParameterReverbLevel: reverb->level (fValue); break;
-=======
case ParameterCompressorEnable:
for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++)
{
@@ -561,9 +550,9 @@ void CMiniDexed::SetParameter (TParameter Parameter, int nValue)
m_ReverbSpinLock.Release ();
break;
- case ParameterReverbSend:
+ case ParameterReverbLevel:
m_ReverbSpinLock.Acquire ();
- reverb->send (nValue / 99.0);
+ reverb->level (nValue / 99.0);
m_ReverbSpinLock.Release ();
break;
@@ -683,8 +672,8 @@ void CMiniDexed::ProcessSound (void)
m_GetChunkTimer.Start ();
}
- //int16_t SampleBuffer[nFrames]; // TODO float->int
- m_pTG[0]->getSamples (SampleBuffer, nFrames);
+ int16_t SampleBuffer[nFrames];
+ m_pTG[0]->getSamples (nFrames, SampleBuffer);
if ( m_pSoundDevice->Write (SampleBuffer, sizeof SampleBuffer)
!= (int) sizeof SampleBuffer)
@@ -847,7 +836,7 @@ bool CMiniDexed::SavePerformance (void)
m_PerformanceConfig.SetReverbLowDamp (m_nParameter[ParameterReverbLowDamp]);
m_PerformanceConfig.SetReverbLowPass (m_nParameter[ParameterReverbLowPass]);
m_PerformanceConfig.SetReverbDiffusion (m_nParameter[ParameterReverbDiffusion]);
- m_PerformanceConfig.SetReverbSend (m_nParameter[ParameterReverbSend]);
+ m_PerformanceConfig.SetReverbLevel (m_nParameter[ParameterReverbLevel]);
return m_PerformanceConfig.Save ();
}
diff --git a/src/minidexed.cpp.O b/src/minidexed.cpp.O
new file mode 100644
index 0000000..17a9c77
--- /dev/null
+++ b/src/minidexed.cpp.O
@@ -0,0 +1,853 @@
+//
+// minidexed.cpp
+//
+// MiniDexed - Dexed FM synthesizer for bare metal Raspberry Pi
+// Copyright (C) 2022 The MiniDexed Team
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+#include "minidexed.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+LOGMODULE ("minidexed");
+
+CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt,
+ CGPIOManager *pGPIOManager, CI2CMaster *pI2CMaster, FATFS *pFileSystem)
+:
+#ifdef ARM_ALLOW_MULTI_CORE
+ CMultiCoreSupport (CMemorySystem::Get ()),
+#endif
+ m_pConfig (pConfig),
+ m_UI (this, pGPIOManager, pConfig),
+ m_PerformanceConfig (pFileSystem),
+ m_PCKeyboard (this, pConfig),
+ m_SerialMIDI (this, pInterrupt, pConfig),
+ m_bUseSerial (false),
+ m_pSoundDevice (0),
+ m_bChannelsSwapped (pConfig->GetChannelsSwapped ()),
+#ifdef ARM_ALLOW_MULTI_CORE
+ m_nActiveTGsLog2 (0),
+#endif
+ m_GetChunkTimer ("GetChunk",
+ 1000000U * pConfig->GetChunkSize ()/2 / pConfig->GetSampleRate ()),
+ m_bProfileEnabled (m_pConfig->GetProfileEnabled ())
+{
+ assert (m_pConfig);
+
+ for (unsigned i = 0; i < CConfig::ToneGenerators; i++)
+ {
+ m_nVoiceBankID[i] = 0;
+ m_nProgram[i] = 0;
+ m_nVolume[i] = 100;
+ m_nPan[i] = 64;
+ pan_float[i]=0.0f;
+ m_nMasterTune[i] = 0;
+ m_nMIDIChannel[i] = CMIDIDevice::Disabled;
+
+ m_nNoteLimitLow[i] = 0;
+ m_nNoteLimitHigh[i] = 127;
+ m_nNoteShift[i] = 0;
+
+ m_pTG[i] = new CDexedAdapter (CConfig::MaxNotes, pConfig->GetSampleRate ());
+ assert (m_pTG[i]);
+
+ m_pTG[i]->activate ();
+ }
+
+ for (unsigned i = 0; i < CConfig::MaxUSBMIDIDevices; i++)
+ {
+ m_pMIDIKeyboard[i] = new CMIDIKeyboard (this, pConfig, i);
+ assert (m_pMIDIKeyboard[i]);
+ }
+
+ // select the sound device
+ const char *pDeviceName = pConfig->GetSoundDevice ();
+ if (strcmp (pDeviceName, "i2s") == 0)
+ {
+ LOGNOTE ("I2S mode");
+
+ m_pSoundDevice = new CI2SSoundBaseDevice (pInterrupt, pConfig->GetSampleRate (),
+ pConfig->GetChunkSize (), false,
+ pI2CMaster, pConfig->GetDACI2CAddress ());
+ }
+ else if (strcmp (pDeviceName, "hdmi") == 0)
+ {
+ LOGNOTE ("HDMI mode");
+
+ m_pSoundDevice = new CHDMISoundBaseDevice (pInterrupt, pConfig->GetSampleRate (),
+ pConfig->GetChunkSize ());
+
+ // The channels are swapped by default in the HDMI sound driver.
+ // TODO: Remove this line, when this has been fixed in the driver.
+ m_bChannelsSwapped = !m_bChannelsSwapped;
+ }
+ else
+ {
+ LOGNOTE ("PWM mode");
+
+ m_pSoundDevice = new CPWMSoundBaseDevice (pInterrupt, pConfig->GetSampleRate (),
+ pConfig->GetChunkSize ());
+ }
+
+#ifdef ARM_ALLOW_MULTI_CORE
+ for (unsigned nCore = 0; nCore < CORES; nCore++)
+ {
+ m_CoreStatus[nCore] = CoreStatusInit;
+ }
+#endif
+
+ // BEGIN setup tg_mixer
+ //tg_mixer = new AudioStereoMixer<8>();
+ // END setup tg_mixer
+
+ SetParameter (ParameterCompressorEnable, 1);
+
+ // BEGIN setup reverb
+ reverb = new AudioEffectPlateReverb(pConfig->GetSampleRate());
+ SetParameter (ParameterReverbEnable, 1);
+ SetParameter (ParameterReverbSize, 70);
+ SetParameter (ParameterReverbHighDamp, 50);
+ SetParameter (ParameterReverbLowDamp, 50);
+ SetParameter (ParameterReverbLowPass, 30);
+ SetParameter (ParameterReverbDiffusion, 20);
+ SetParameter (ParameterReverbLevel, 80);
+ // END setup reverb
+};
+
+bool CMiniDexed::Initialize (void)
+{
+ assert (m_pConfig);
+ assert (m_pSoundDevice);
+
+ if (!m_UI.Initialize ())
+ {
+ return false;
+ }
+
+ m_SysExFileLoader.Load ();
+
+ if (m_SerialMIDI.Initialize ())
+ {
+ LOGNOTE ("Serial MIDI interface enabled");
+
+ m_bUseSerial = true;
+ }
+
+ for (unsigned i = 0; i < CConfig::ToneGenerators; i++)
+ {
+ assert (m_pTG[i]);
+
+ SetVolume (100, i);
+ ProgramChange (0, i);
+
+ m_pTG[i]->setTranspose (24);
+
+ m_pTG[i]->setPBController (12, 1);
+ m_pTG[i]->setMWController (99, 7, 0);
+ }
+
+ if (m_PerformanceConfig.Load ())
+ {
+ for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++)
+ {
+ BankSelectLSB (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);
+
+ m_nNoteLimitLow[nTG] = m_PerformanceConfig.GetNoteLimitLow (nTG);
+ m_nNoteLimitHigh[nTG] = m_PerformanceConfig.GetNoteLimitHigh (nTG);
+ m_nNoteShift[nTG] = m_PerformanceConfig.GetNoteShift (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 ());
+ }
+ else
+ {
+ SetMIDIChannel (CMIDIDevice::OmniMode, 0);
+ }
+
+ // setup and start the sound device
+ if (!m_pSoundDevice->AllocateQueueFrames (m_pConfig->GetChunkSize ()))
+ {
+ LOGERR ("Cannot allocate sound queue");
+
+ return false;
+ }
+
+#ifndef ARM_ALLOW_MULTI_CORE
+ m_pSoundDevice->SetWriteFormat (SoundFormatSigned16, 1); // 16-bit Mono
+#else
+ m_pSoundDevice->SetWriteFormat (SoundFormatSigned16, 2); // 16-bit Stereo
+#endif
+
+ m_nQueueSizeFrames = m_pSoundDevice->GetQueueSizeFrames ();
+
+ m_pSoundDevice->Start ();
+
+#ifdef ARM_ALLOW_MULTI_CORE
+ // start secondary cores
+ if (!CMultiCoreSupport::Initialize ())
+ {
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+void CMiniDexed::Process (bool bPlugAndPlayUpdated)
+{
+#ifndef ARM_ALLOW_MULTI_CORE
+ ProcessSound ();
+#endif
+
+ for (unsigned i = 0; i < CConfig::MaxUSBMIDIDevices; i++)
+ {
+ assert (m_pMIDIKeyboard[i]);
+ m_pMIDIKeyboard[i]->Process (bPlugAndPlayUpdated);
+ }
+
+ m_PCKeyboard.Process (bPlugAndPlayUpdated);
+
+ if (m_bUseSerial)
+ {
+ m_SerialMIDI.Process ();
+ }
+
+ m_UI.Process ();
+
+ if (m_bProfileEnabled)
+ {
+ m_GetChunkTimer.Dump ();
+ }
+}
+
+#ifdef ARM_ALLOW_MULTI_CORE
+
+void CMiniDexed::Run (unsigned nCore)
+{
+ assert (1 <= nCore && nCore < CORES);
+
+ if (nCore == 1)
+ {
+ m_CoreStatus[nCore] = CoreStatusIdle; // core 1 ready
+
+ // wait for cores 2 and 3 to be ready
+ for (unsigned nCore = 2; nCore < CORES; nCore++)
+ {
+ while (m_CoreStatus[nCore] != CoreStatusIdle)
+ {
+ // just wait
+ }
+ }
+
+ while (m_CoreStatus[nCore] != CoreStatusExit)
+ {
+ ProcessSound ();
+ }
+ }
+ else // core 2 and 3
+ {
+ while (1)
+ {
+ m_CoreStatus[nCore] = CoreStatusIdle; // ready to be kicked
+ while (m_CoreStatus[nCore] == CoreStatusIdle)
+ {
+ // just wait
+ }
+
+ // now kicked from core 1
+
+ if (m_CoreStatus[nCore] == CoreStatusExit)
+ {
+ m_CoreStatus[nCore] = CoreStatusUnknown;
+
+ break;
+ }
+
+ assert (m_CoreStatus[nCore] == CoreStatusBusy);
+
+ // process the TGs, assigned to this core (2 or 3)
+
+ assert (m_nFramesToProcess <= CConfig::MaxChunkSize);
+ unsigned nTG = CConfig::TGsCore1 + (nCore-2)*CConfig::TGsCore23;
+ for (unsigned i = 0; i < CConfig::TGsCore23; i++, nTG++)
+ {
+ assert (m_pTG[nTG]);
+ m_pTG[nTG]->getSamples (m_OutputLevel[nTG], m_nFramesToProcess);
+ }
+ }
+ }
+}
+
+#endif
+
+CSysExFileLoader *CMiniDexed::GetSysExFileLoader (void)
+{
+ return &m_SysExFileLoader;
+}
+
+void CMiniDexed::BankSelectLSB (unsigned nBankLSB, unsigned nTG)
+{
+ if (nBankLSB > 127)
+ {
+ return;
+ }
+
+ assert (nTG < CConfig::ToneGenerators);
+ m_nVoiceBankID[nTG] = nBankLSB;
+
+ m_UI.ParameterChanged ();
+}
+
+void CMiniDexed::ProgramChange (unsigned nProgram, unsigned nTG)
+{
+ if (nProgram > 31)
+ {
+ return;
+ }
+
+ assert (nTG < CConfig::ToneGenerators);
+ m_nProgram[nTG] = nProgram;
+
+ uint8_t Buffer[156];
+ m_SysExFileLoader.GetVoice (m_nVoiceBankID[nTG], nProgram, Buffer);
+
+ assert (m_pTG[nTG]);
+ m_pTG[nTG]->loadVoiceParameters (Buffer);
+
+ m_UI.ParameterChanged ();
+}
+
+void CMiniDexed::SetVolume (unsigned nVolume, unsigned nTG)
+{
+ if (nVolume > 127)
+ {
+ return;
+ }
+
+ assert (nTG < CConfig::ToneGenerators);
+ m_nVolume[nTG] = nVolume;
+
+ assert (m_pTG[nTG]);
+ m_pTG[nTG]->setGain (nVolume / 127.0);
+
+ m_UI.ParameterChanged ();
+}
+
+void CMiniDexed::SetPan (unsigned nPan, unsigned nTG)
+{
+ constrain(nPan,-1.0f,1.0f);
+
+ assert (nTG < CConfig::ToneGenerators);
+ m_nPan[nTG] = nPan;
+ pan_float[nTG]=mapfloat(nPan,0,127,-1.0,1.0);
+
+ m_UI.ParameterChanged ();
+}
+
+void CMiniDexed::SetMasterTune (int nMasterTune, unsigned nTG)
+{
+ if (!(-99 <= nMasterTune && nMasterTune <= 99))
+ {
+ return;
+ }
+
+ assert (nTG < CConfig::ToneGenerators);
+ m_nMasterTune[nTG] = nMasterTune;
+
+ assert (m_pTG[nTG]);
+ m_pTG[nTG]->setMasterTune ((int8_t) nMasterTune);
+
+ m_UI.ParameterChanged ();
+}
+
+void CMiniDexed::SetMIDIChannel (uint8_t uchChannel, unsigned nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+ m_nMIDIChannel[nTG] = uchChannel;
+
+ for (unsigned i = 0; i < CConfig::MaxUSBMIDIDevices; i++)
+ {
+ assert (m_pMIDIKeyboard[i]);
+ m_pMIDIKeyboard[i]->SetChannel (uchChannel, nTG);
+ }
+
+ m_PCKeyboard.SetChannel (uchChannel, nTG);
+
+ if (m_bUseSerial)
+ {
+ m_SerialMIDI.SetChannel (uchChannel, nTG);
+ }
+
+#ifdef ARM_ALLOW_MULTI_CORE
+ unsigned nActiveTGs = 0;
+ for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++)
+ {
+ if (m_nMIDIChannel[nTG] != CMIDIDevice::Disabled)
+ {
+ nActiveTGs++;
+ }
+ }
+
+ assert (nActiveTGs <= 8);
+ static const unsigned Log2[] = {0, 0, 1, 2, 2, 3, 3, 3, 3};
+ m_nActiveTGsLog2 = Log2[nActiveTGs];
+#endif
+
+ m_UI.ParameterChanged ();
+}
+
+void CMiniDexed::keyup (int16_t pitch, unsigned nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+ assert (m_pTG[nTG]);
+
+ pitch = ApplyNoteLimits (pitch, nTG);
+ if (pitch >= 0)
+ {
+ m_pTG[nTG]->keyup (pitch);
+ }
+}
+
+void CMiniDexed::keydown (int16_t pitch, uint8_t velocity, unsigned nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+ assert (m_pTG[nTG]);
+
+ pitch = ApplyNoteLimits (pitch, nTG);
+ if (pitch >= 0)
+ {
+ m_pTG[nTG]->keydown (pitch, velocity);
+ }
+}
+
+int16_t CMiniDexed::ApplyNoteLimits (int16_t pitch, unsigned nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+
+ if ( pitch < (int16_t) m_nNoteLimitLow[nTG]
+ || pitch > (int16_t) m_nNoteLimitHigh[nTG])
+ {
+ return -1;
+ }
+
+ pitch += m_nNoteShift[nTG];
+
+ if ( pitch < 0
+ || pitch > 127)
+ {
+ return -1;
+ }
+
+ return pitch;
+}
+
+void CMiniDexed::setSustain(bool sustain, unsigned nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+ assert (m_pTG[nTG]);
+ m_pTG[nTG]->setSustain (sustain);
+}
+
+void CMiniDexed::setModWheel (uint8_t value, unsigned nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+ assert (m_pTG[nTG]);
+ m_pTG[nTG]->setModWheel (value);
+}
+
+void CMiniDexed::setPitchbend (int16_t value, unsigned nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+ assert (m_pTG[nTG]);
+ m_pTG[nTG]->setPitchbend (value);
+}
+
+void CMiniDexed::ControllersRefresh (unsigned nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+ assert (m_pTG[nTG]);
+ m_pTG[nTG]->ControllersRefresh ();
+}
+
+void CMiniDexed::SetParameter (TParameter Parameter, int nValue)
+{
+ assert (reverb);
+
+ assert (Parameter < ParameterUnknown);
+ m_nParameter[Parameter] = nValue;
+
+ switch (Parameter)
+ {
+<<<<<<< HEAD
+ case ParameterReverbSize: reverb->size (fValue); break;
+ case ParameterReverbHighDamp: reverb->hidamp (fValue); break;
+ case ParameterReverbLowDamp: reverb->lodamp (fValue); break;
+ case ParameterReverbLowPass: reverb->lowpass (fValue); break;
+ case ParameterReverbDiffusion: reverb->diffusion (fValue); break;
+ case ParameterReverbLevel: reverb->level (fValue); break;
+=======
+ case ParameterCompressorEnable:
+ for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++)
+ {
+ assert (m_pTG[nTG]);
+ m_pTG[nTG]->setCompressor (!!nValue);
+ }
+ break;
+
+ case ParameterReverbEnable:
+ m_ReverbSpinLock.Acquire ();
+ reverb->set_bypass (!nValue);
+ m_ReverbSpinLock.Release ();
+ break;
+
+ case ParameterReverbSize:
+ m_ReverbSpinLock.Acquire ();
+ reverb->size (nValue / 99.0);
+ m_ReverbSpinLock.Release ();
+ break;
+
+ case ParameterReverbHighDamp:
+ m_ReverbSpinLock.Acquire ();
+ reverb->hidamp (nValue / 99.0);
+ m_ReverbSpinLock.Release ();
+ break;
+
+ case ParameterReverbLowDamp:
+ m_ReverbSpinLock.Acquire ();
+ reverb->lodamp (nValue / 99.0);
+ m_ReverbSpinLock.Release ();
+ break;
+
+ case ParameterReverbLowPass:
+ m_ReverbSpinLock.Acquire ();
+ reverb->lowpass (nValue / 99.0);
+ m_ReverbSpinLock.Release ();
+ break;
+
+ case ParameterReverbDiffusion:
+ m_ReverbSpinLock.Acquire ();
+ reverb->diffusion (nValue / 99.0);
+ m_ReverbSpinLock.Release ();
+ break;
+
+ case ParameterReverbLevel:
+ m_ReverbSpinLock.Acquire ();
+ reverb->level (nValue / 99.0);
+ m_ReverbSpinLock.Release ();
+ break;
+
+ default:
+ assert (0);
+ break;
+ }
+}
+
+int CMiniDexed::GetParameter (TParameter Parameter)
+{
+ assert (Parameter < ParameterUnknown);
+ return m_nParameter[Parameter];
+}
+
+void CMiniDexed::SetTGParameter (TTGParameter Parameter, int nValue, unsigned nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+
+ switch (Parameter)
+ {
+ case TGParameterVoiceBank: BankSelectLSB (nValue, nTG); break;
+ case TGParameterProgram: ProgramChange (nValue, nTG); break;
+ case TGParameterVolume: SetVolume (nValue, nTG); break;
+ case TGParameterPan: SetPan (nValue, nTG); break;
+ case TGParameterMasterTune: SetMasterTune (nValue, nTG); break;
+
+ case TGParameterMIDIChannel:
+ assert (0 <= nValue && nValue <= 255);
+ SetMIDIChannel ((uint8_t) nValue, nTG);
+ break;
+
+ default:
+ assert (0);
+ break;
+ }
+}
+
+int CMiniDexed::GetTGParameter (TTGParameter Parameter, unsigned nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+
+ switch (Parameter)
+ {
+ case TGParameterVoiceBank: return m_nVoiceBankID[nTG];
+ case TGParameterProgram: return m_nProgram[nTG];
+ case TGParameterVolume: return m_nVolume[nTG];
+ case TGParameterPan: return m_nPan[nTG];
+ case TGParameterMasterTune: return m_nMasterTune[nTG];
+ case TGParameterMIDIChannel: return m_nMIDIChannel[nTG];
+
+ default:
+ assert (0);
+ return 0;
+ }
+}
+
+void CMiniDexed::SetVoiceParameter (uint8_t uchOffset, uint8_t uchValue, unsigned nOP, unsigned nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+ assert (m_pTG[nTG]);
+ assert (nOP <= 6);
+
+ if (nOP < 6)
+ {
+ nOP = 5 - nOP; // OPs are in reverse order
+ }
+
+ 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::ToneGenerators);
+ assert (m_pTG[nTG]);
+ assert (nOP <= 6);
+
+ if (nOP < 6)
+ {
+ nOP = 5 - nOP; // OPs are in reverse order
+ }
+
+ uchOffset += nOP * 21;
+ assert (uchOffset < 156);
+
+ return m_pTG[nTG]->getVoiceDataElement (uchOffset);
+}
+
+std::string CMiniDexed::GetVoiceName (unsigned nTG)
+{
+ char VoiceName[11];
+ memset (VoiceName, 0, sizeof VoiceName);
+
+ assert (nTG < CConfig::ToneGenerators);
+ assert (m_pTG[nTG]);
+ m_pTG[nTG]->setName (VoiceName);
+
+ std::string Result (VoiceName);
+
+ return Result;
+}
+
+#ifndef ARM_ALLOW_MULTI_CORE
+
+void CMiniDexed::ProcessSound (void)
+{
+ assert (m_pSoundDevice);
+
+ unsigned nFrames = m_nQueueSizeFrames - m_pSoundDevice->GetQueueFramesAvail ();
+ if (nFrames >= m_nQueueSizeFrames/2)
+ {
+ if (m_bProfileEnabled)
+ {
+ m_GetChunkTimer.Start ();
+ }
+
+ //int16_t SampleBuffer[nFrames]; // TODO float->int
+ m_pTG[0]->getSamples (SampleBuffer, nFrames);
+
+ if ( m_pSoundDevice->Write (SampleBuffer, sizeof SampleBuffer)
+ != (int) sizeof SampleBuffer)
+ {
+ LOGERR ("Sound data dropped");
+ }
+
+ if (m_bProfileEnabled)
+ {
+ m_GetChunkTimer.Stop ();
+ }
+ }
+}
+
+#else // #ifdef ARM_ALLOW_MULTI_CORE
+
+void CMiniDexed::ProcessSound (void)
+{
+ assert (m_pSoundDevice);
+
+ unsigned nFrames = m_nQueueSizeFrames - m_pSoundDevice->GetQueueFramesAvail ();
+ if (nFrames >= m_nQueueSizeFrames/2)
+ {
+ if (m_bProfileEnabled)
+ {
+ m_GetChunkTimer.Start ();
+ }
+
+ m_nFramesToProcess = nFrames;
+
+ // kick secondary cores
+ for (unsigned nCore = 2; nCore < CORES; nCore++)
+ {
+ assert (m_CoreStatus[nCore] == CoreStatusIdle);
+ m_CoreStatus[nCore] = CoreStatusBusy;
+ }
+
+ // process the TGs assigned to core 1
+ assert (nFrames <= CConfig::MaxChunkSize);
+ for (unsigned i = 0; i < CConfig::TGsCore1; i++)
+ {
+ assert (m_pTG[i]);
+ m_pTG[i]->getSamples (m_OutputLevel[i], nFrames);
+ }
+
+ // wait for cores 2 and 3 to complete their work
+ for (unsigned nCore = 2; nCore < CORES; nCore++)
+ {
+ while (m_CoreStatus[nCore] != CoreStatusIdle)
+ {
+ // just wait
+ }
+ }
+
+ //
+ // Audio signal path after tone generators starts here
+ //
+
+ // now mix the output of all TGs
+ float32_t SampleBuffer[2][nFrames];
+ uint8_t indexL=0, indexR=1;
+
+ if (m_bChannelsSwapped)
+ {
+ indexL=1;
+ indexR=0;
+ }
+
+ // init left sum output
+ assert (SampleBuffer[0]!=NULL);
+ arm_fill_f32(0.0, SampleBuffer[0], nFrames);
+ // init right sum output
+ assert (SampleBuffer[1]!=NULL);
+ arm_fill_f32(0.0, SampleBuffer[1], nFrames);
+
+ assert (CConfig::ToneGenerators == 8);
+
+ // BEGIN stereo panorama
+ for (uint8_t i = 0; i < CConfig::ToneGenerators; i++)
+ {
+ float32_t tmpBuffer[nFrames];
+
+ m_PanoramaSpinLock.Acquire ();
+ // calculate left panorama of this TG
+ arm_scale_f32(m_OutputLevel[i], 1.0f-pan_float[i], tmpBuffer, nFrames);
+ // add left panorama output of this TG to sum output
+ arm_add_f32(SampleBuffer[indexL], tmpBuffer, SampleBuffer[indexL], nFrames);
+
+ // calculate right panorama of this TG
+ arm_scale_f32(m_OutputLevel[i], pan_float[i], tmpBuffer, nFrames);
+ // add right panaorama output of this TG to sum output
+ arm_add_f32(SampleBuffer[indexR], tmpBuffer, SampleBuffer[indexR], nFrames);
+
+ m_PanoramaSpinLock.Release ();
+ }
+ // END stereo panorama
+
+ // BEGIN adding reverb
+ if (m_nParameter[ParameterReverbEnable])
+ {
+ float32_t ReverbBuffer[2][nFrames];
+
+ m_ReverbSpinLock.Acquire ();
+ reverb->doReverb(SampleBuffer[indexL],SampleBuffer[indexR],ReverbBuffer[0], ReverbBuffer[1],nFrames);
+ m_ReverbSpinLock.Release ();
+
+ // scale down and add left reverb buffer by reverb level
+ arm_scale_f32(ReverbBuffer[0], reverb->get_level(), ReverbBuffer[0], nFrames);
+ arm_add_f32(SampleBuffer[indexL], ReverbBuffer[0], SampleBuffer[indexL], nFrames);
+ // scale down and add right reverb buffer by reverb level
+ arm_scale_f32(ReverbBuffer[1], reverb->get_level(), ReverbBuffer[1], nFrames);
+ arm_add_f32(SampleBuffer[indexR], ReverbBuffer[1], SampleBuffer[indexR], nFrames);
+ }
+ // END adding reverb
+
+ // Convert dual float array (left, right) to single int16 array (left/right)
+ float32_t tmp_float[nFrames*2];
+ int16_t tmp_int[nFrames*2];
+ for(uint16_t i=0; iWrite (tmp_int, sizeof(tmp_int)) != (int) sizeof(tmp_int))
+ {
+ LOGERR ("Sound data dropped");
+ }
+
+ if (m_bProfileEnabled)
+ {
+ m_GetChunkTimer.Stop ();
+ }
+ }
+}
+
+#endif
+
+bool CMiniDexed::SavePerformance (void)
+{
+ for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++)
+ {
+ m_PerformanceConfig.SetBankNumber (m_nVoiceBankID[nTG], nTG);
+ m_PerformanceConfig.SetVoiceNumber (m_nProgram[nTG], nTG);
+ m_PerformanceConfig.SetMIDIChannel (m_nMIDIChannel[nTG], nTG);
+ m_PerformanceConfig.SetVolume (m_nVolume[nTG], nTG);
+ m_PerformanceConfig.SetPan (m_nPan[nTG], nTG);
+ m_PerformanceConfig.SetDetune (m_nMasterTune[nTG], nTG);
+
+ m_PerformanceConfig.SetNoteLimitLow (m_nNoteLimitLow[nTG], nTG);
+ m_PerformanceConfig.SetNoteLimitHigh (m_nNoteLimitHigh[nTG], nTG);
+ m_PerformanceConfig.SetNoteShift (m_nNoteShift[nTG], nTG);
+ }
+
+ m_PerformanceConfig.SetCompressorEnable (!!m_nParameter[ParameterCompressorEnable]);
+ m_PerformanceConfig.SetReverbEnable (!!m_nParameter[ParameterReverbEnable]);
+ m_PerformanceConfig.SetReverbSize (m_nParameter[ParameterReverbSize]);
+ m_PerformanceConfig.SetReverbHighDamp (m_nParameter[ParameterReverbHighDamp]);
+ m_PerformanceConfig.SetReverbLowDamp (m_nParameter[ParameterReverbLowDamp]);
+ m_PerformanceConfig.SetReverbLowPass (m_nParameter[ParameterReverbLowPass]);
+ m_PerformanceConfig.SetReverbDiffusion (m_nParameter[ParameterReverbDiffusion]);
+ m_PerformanceConfig.SetReverbLevel (m_nParameter[ParameterReverbLevel]);
+
+ return m_PerformanceConfig.Save ();
+}
diff --git a/src/performance.ini b/src/performance.ini
index 837fae7..979c30c 100644
--- a/src/performance.ini
+++ b/src/performance.ini
@@ -109,7 +109,7 @@ NoteShift8=0
#ReverbLowDamp=50 # 0 .. 99
#ReverbLowPass=30 # 0 .. 99
#ReverbDiffusion=65 # 0 .. 99
-#ReverbSend=80 # 0 .. 99
+#ReverbLevel=80 # 0 .. 99
# Effects
CompressorEnable=1
@@ -119,4 +119,4 @@ ReverbHighDamp=50
ReverbLowDamp=50
ReverbLowPass=30
ReverbDiffusion=65
-ReverbSend=80
+ReverbLevel=80
diff --git a/src/performanceconfig.cpp b/src/performanceconfig.cpp
index ac1a47b..b4241d4 100644
--- a/src/performanceconfig.cpp
+++ b/src/performanceconfig.cpp
@@ -99,7 +99,7 @@ bool CPerformanceConfig::Load (void)
m_nReverbLowDamp = m_Properties.GetNumber ("ReverbLowDamp", 50);
m_nReverbLowPass = m_Properties.GetNumber ("ReverbLowPass", 30);
m_nReverbDiffusion = m_Properties.GetNumber ("ReverbDiffusion", 65);
- m_nReverbSend = m_Properties.GetNumber ("ReverbSend", 80);
+ m_nReverbLevel = m_Properties.GetNumber ("ReverbLevel", 80);
return bResult;
}
@@ -161,7 +161,7 @@ bool CPerformanceConfig::Save (void)
m_Properties.SetNumber ("ReverbLowDamp", m_nReverbLowDamp);
m_Properties.SetNumber ("ReverbLowPass", m_nReverbLowPass);
m_Properties.SetNumber ("ReverbDiffusion", m_nReverbDiffusion);
- m_Properties.SetNumber ("ReverbSend", m_nReverbSend);
+ m_Properties.SetNumber ("ReverbLevel", m_nReverbLevel);
return m_Properties.Save ();
}
@@ -309,9 +309,9 @@ unsigned CPerformanceConfig::GetReverbDiffusion (void) const
return m_nReverbDiffusion;
}
-unsigned CPerformanceConfig::GetReverbSend (void) const
+unsigned CPerformanceConfig::GetReverbLevel (void) const
{
- return m_nReverbSend;
+ return m_nReverbLevel;
}
void CPerformanceConfig::SetCompressorEnable (bool bValue)
@@ -349,7 +349,7 @@ void CPerformanceConfig::SetReverbDiffusion (unsigned nValue)
m_nReverbDiffusion = nValue;
}
-void CPerformanceConfig::SetReverbSend (unsigned nValue)
+void CPerformanceConfig::SetReverbLevel (unsigned nValue)
{
- m_nReverbSend = nValue;
+ m_nReverbLevel = nValue;
}
diff --git a/src/performanceconfig.h b/src/performanceconfig.h
index 3a03219..c616e42 100644
--- a/src/performanceconfig.h
+++ b/src/performanceconfig.h
@@ -66,7 +66,7 @@ public:
unsigned GetReverbLowDamp (void) const; // 0 .. 99
unsigned GetReverbLowPass (void) const; // 0 .. 99
unsigned GetReverbDiffusion (void) const; // 0 .. 99
- unsigned GetReverbSend (void) const; // 0 .. 99
+ unsigned GetReverbLevel (void) const; // 0 .. 99
void SetCompressorEnable (bool bValue);
void SetReverbEnable (bool bValue);
@@ -75,7 +75,7 @@ public:
void SetReverbLowDamp (unsigned nValue);
void SetReverbLowPass (unsigned nValue);
void SetReverbDiffusion (unsigned nValue);
- void SetReverbSend (unsigned nValue);
+ void SetReverbLevel (unsigned nValue);
private:
CPropertiesFatFsFile m_Properties;
@@ -97,7 +97,7 @@ private:
unsigned m_nReverbLowDamp;
unsigned m_nReverbLowPass;
unsigned m_nReverbDiffusion;
- unsigned m_nReverbSend;
+ unsigned m_nReverbLevel;
};
#endif
diff --git a/src/uimenu.cpp b/src/uimenu.cpp
index a76794e..450023a 100644
--- a/src/uimenu.cpp
+++ b/src/uimenu.cpp
@@ -166,7 +166,7 @@ const CUIMenu::TParameter CUIMenu::s_GlobalParameter[CMiniDexed::ParameterUnknow
{0, 99, 1}, // ParameterReverbLowDamp
{0, 99, 1}, // ParameterReverbLowPass
{0, 99, 1}, // ParameterReverbDiffusion
- {0, 99, 1} // ParameterReverbSend
+ {0, 99, 1} // ParameterReverbLevel
};
// must match CMiniDexed::TTGParameter