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