From f784824254a3eb1b053cdc42f1cdaa62b7ba2ee4 Mon Sep 17 00:00:00 2001 From: probonopd Date: Fri, 11 Mar 2022 18:36:22 +0100 Subject: [PATCH] Use the CSoundBaseDevice::Write() method from a secondary CPU core Should result in a smaller IRQ latency on CPU core 0 and in a better USB handling. It still works on the RPi 1 too, but the MIDI dump and profiling has to be disabled there, otherwise one will hear drops, when the screen scrolls. https://github.com/probonopd/MiniDexed/issues/39#issuecomment-1062604728 Thanks @rsta2 --- build.sh | 3 + src/kernel.cpp | 23 +---- src/minidexed.cpp | 246 ++++++++++++++++++---------------------------- src/minidexed.h | 61 ++++-------- 4 files changed, 119 insertions(+), 214 deletions(-) diff --git a/build.sh b/build.sh index ef9ce685..de4d9f14 100755 --- a/build.sh +++ b/build.sh @@ -18,6 +18,9 @@ fi cd circle-stdlib/ make mrproper || true ./configure -r ${RPI} --prefix "${TOOLCHAIN_PREFIX}" +if [ "${RPI}" -gt "1" ]; then + echo "DEFINE += -DARM_ALLOW_MULTI_CORE" >> libs/circle/Config.mk +fi echo "DEFINE += -DUSE_PWM_AUDIO_ON_ZERO" >> libs/circle/Config.mk echo "DEFINE += -DSAVE_VFP_REGS_ON_IRQ" >> libs/circle/Config.mk echo "DEFINE += -DREALTIME" >> libs/circle/Config.mk diff --git a/src/kernel.cpp b/src/kernel.cpp index 2ad706d3..dde24f06 100644 --- a/src/kernel.cpp +++ b/src/kernel.cpp @@ -18,7 +18,6 @@ // along with this program. If not, see . // #include "kernel.h" -#include #include #include #include @@ -65,27 +64,7 @@ bool CKernel::Initialize (void) m_Config.Load (); - // select the sound device - const char *pSoundDevice = m_Config.GetSoundDevice (); - if (strcmp (pSoundDevice, "i2s") == 0) - { - LOGNOTE ("I2S mode"); - - m_pDexed = new CMiniDexedI2S (&m_Config, &mInterrupt, &m_GPIOManager, &m_I2CMaster); - } - else if (strcmp (pSoundDevice, "hdmi") == 0) - { - LOGNOTE ("HDMI mode"); - - m_pDexed = new CMiniDexedHDMI (&m_Config, &mInterrupt, &m_GPIOManager); - } - else - { - LOGNOTE ("PWM mode"); - - m_pDexed = new CMiniDexedPWM (&m_Config, &mInterrupt, &m_GPIOManager); - } - + m_pDexed = new CMiniDexed (&m_Config, &mInterrupt, &m_GPIOManager, &m_I2CMaster); assert (m_pDexed); if (!m_pDexed->Initialize ()) diff --git a/src/minidexed.cpp b/src/minidexed.cpp index 476a8f3e..23f64cb1 100644 --- a/src/minidexed.cpp +++ b/src/minidexed.cpp @@ -19,18 +19,28 @@ // #include "minidexed.h" #include +#include +#include +#include +#include +#include #include #include LOGMODULE ("minidexed"); -CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt, CGPIOManager *pGPIOManager) +CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt, + CGPIOManager *pGPIOManager, CI2CMaster *pI2CMaster) : CDexedAdapter (CConfig::MaxNotes, pConfig->GetSampleRate ()), +#ifdef ARM_ALLOW_MULTI_CORE + CMultiCoreSupport (CMemorySystem::Get ()), +#endif m_pConfig (pConfig), m_UI (this, pGPIOManager, pConfig), m_PCKeyboard (this), m_SerialMIDI (this, pInterrupt, pConfig), m_bUseSerial (false), + m_pSoundDevice (0), m_GetChunkTimer ("GetChunk", 1000000U * pConfig->GetChunkSize ()/2 / pConfig->GetSampleRate ()), m_bProfileEnabled (m_pConfig->GetProfileEnabled ()) @@ -40,10 +50,38 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt, CGPIOMan 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 ()); + } + else + { + LOGNOTE ("PWM mode"); + + m_pSoundDevice = new CPWMSoundBaseDevice (pInterrupt, pConfig->GetSampleRate (), + pConfig->GetChunkSize ()); + } }; bool CMiniDexed::Initialize (void) { + assert (m_pConfig); + assert (m_pSoundDevice); + if (!m_UI.Initialize ()) { return false; @@ -63,11 +101,37 @@ bool CMiniDexed::Initialize (void) ProgramChange (0); setTranspose (24); + // setup and start the sound device + if (!m_pSoundDevice->AllocateQueueFrames (m_pConfig->GetChunkSize ()/2)) + { + LOGERR ("Cannot allocate sound queue"); + + return false; + } + + m_pSoundDevice->SetWriteFormat (SoundFormatSigned16, 1); // 16-bit Mono + + 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]); @@ -89,6 +153,21 @@ void CMiniDexed::Process (bool bPlugAndPlayUpdated) } } +#ifdef ARM_ALLOW_MULTI_CORE + +void CMiniDexed::Run (unsigned nCore) +{ + if (nCore == 1) + { + while (1) + { + ProcessSound (); + } + } +} + +#endif + CSysExFileLoader *CMiniDexed::GetSysExFileLoader (void) { return &m_SysExFileLoader; @@ -120,159 +199,30 @@ void CMiniDexed::ProgramChange (unsigned nProgram) m_UI.ProgramChanged (nProgram); } -//// PWM ////////////////////////////////////////////////////////////////////// - -CMiniDexedPWM::CMiniDexedPWM (CConfig *pConfig, CInterruptSystem *pInterrupt, - CGPIOManager *pGPIOManager) -: CMiniDexed (pConfig, pInterrupt, pGPIOManager), - CPWMSoundBaseDevice (pInterrupt, pConfig->GetSampleRate (), - pConfig->GetChunkSize ()) +void CMiniDexed::ProcessSound (void) { -} + assert (m_pSoundDevice); -bool CMiniDexedPWM::Initialize (void) -{ - if (!CMiniDexed::Initialize ()) - { - return false; - } - - return Start (); -} - -unsigned CMiniDexedPWM::GetChunk (u32 *pBuffer, unsigned nChunkSize) -{ - if (m_bProfileEnabled) + unsigned nFrames = m_nQueueSizeFrames - m_pSoundDevice->GetQueueFramesAvail (); + if (nFrames >= m_nQueueSizeFrames/2) { - m_GetChunkTimer.Start (); - } - - unsigned nResult = nChunkSize; - - int16_t SampleBuffer[nChunkSize/2]; - getSamples (nChunkSize/2, SampleBuffer); - - for (unsigned i = 0; nChunkSize > 0; nChunkSize -= 2) // fill the whole buffer - { - s32 nSample = SampleBuffer[i++]; - nSample += 32768; - nSample *= GetRangeMax()/2; - nSample /= 32768; - - *pBuffer++ = nSample; // 2 stereo channels - *pBuffer++ = nSample; - } - - if (m_bProfileEnabled) - { - m_GetChunkTimer.Stop (); - } - - return nResult; -}; - -//// I2S ////////////////////////////////////////////////////////////////////// - -CMiniDexedI2S::CMiniDexedI2S (CConfig *pConfig, CInterruptSystem *pInterrupt, - CGPIOManager *pGPIOManager, CI2CMaster *pI2CMaster) -: CMiniDexed (pConfig, pInterrupt, pGPIOManager), - CI2SSoundBaseDevice (pInterrupt, pConfig->GetSampleRate (), - pConfig->GetChunkSize (), false, pI2CMaster, - pConfig->GetDACI2CAddress ()) -{ -} - -bool CMiniDexedI2S::Initialize (void) -{ - if (!CMiniDexed::Initialize ()) - { - return false; - } - - return Start (); -} - -unsigned CMiniDexedI2S::GetChunk (u32 *pBuffer, unsigned nChunkSize) -{ - if (m_bProfileEnabled) - { - m_GetChunkTimer.Start (); - } - - unsigned nResult = nChunkSize; - - int16_t SampleBuffer[nChunkSize/2]; - getSamples (nChunkSize/2, SampleBuffer); - - for (unsigned i = 0; nChunkSize > 0; nChunkSize -= 2) // fill the whole buffer - { - s32 nSample = SampleBuffer[i++]; - nSample <<= 8; - - *pBuffer++ = nSample; // 2 stereo channels - *pBuffer++ = nSample; - } - - if (m_bProfileEnabled) - { - m_GetChunkTimer.Stop (); - } - - return nResult; -}; - -//// HDMI ///////////////////////////////////////////////////////////////////// - -CMiniDexedHDMI::CMiniDexedHDMI (CConfig *pConfig, CInterruptSystem *pInterrupt, - CGPIOManager *pGPIOManager) -: CMiniDexed (pConfig, pInterrupt, pGPIOManager), - CHDMISoundBaseDevice (pInterrupt, pConfig->GetSampleRate (), - pConfig->GetChunkSize ()) -{ -} - -bool CMiniDexedHDMI::Initialize (void) -{ - if (!CMiniDexed::Initialize ()) - { - return false; - } - - return Start (); -} - -unsigned CMiniDexedHDMI::GetChunk(u32 *pBuffer, unsigned nChunkSize) -{ - if (m_bProfileEnabled) - { - m_GetChunkTimer.Start (); - } - - unsigned nResult = nChunkSize; + if (m_bProfileEnabled) + { + m_GetChunkTimer.Start (); + } - int16_t SampleBuffer[nChunkSize/2]; - getSamples (nChunkSize/2, SampleBuffer); + int16_t SampleBuffer[nFrames]; + getSamples (nFrames, SampleBuffer); - unsigned nFrame = 0; - for (unsigned i = 0; nChunkSize > 0; nChunkSize -= 2) // fill the whole buffer - { - s32 nSample = SampleBuffer[i++]; - nSample <<= 8; - - nSample = ConvertIEC958Sample (nSample, nFrame); - if (++nFrame == IEC958_FRAMES_PER_BLOCK) + if ( m_pSoundDevice->Write (SampleBuffer, sizeof SampleBuffer) + != (int) sizeof SampleBuffer) { - nFrame = 0; + LOGERR ("Sound data dropped"); } - *pBuffer++ = nSample; // 2 stereo channels - *pBuffer++ = nSample; - } - - if (m_bProfileEnabled) - { - m_GetChunkTimer.Stop(); + if (m_bProfileEnabled) + { + m_GetChunkTimer.Stop (); + } } - - return nResult; -}; +} diff --git a/src/minidexed.h b/src/minidexed.h index d817f87d..ae0eff65 100644 --- a/src/minidexed.h +++ b/src/minidexed.h @@ -33,25 +33,34 @@ #include #include #include -#include -#include -#include +#include +#include class CMiniDexed : public CDexedAdapter +#ifdef ARM_ALLOW_MULTI_CORE + , public CMultiCoreSupport +#endif { public: CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt, - CGPIOManager *pGPIOManager); + CGPIOManager *pGPIOManager, CI2CMaster *pI2CMaster); - virtual bool Initialize (void); + bool Initialize (void); void Process (bool bPlugAndPlayUpdated); +#ifdef ARM_ALLOW_MULTI_CORE + void Run (unsigned nCore); +#endif + CSysExFileLoader *GetSysExFileLoader (void); void BankSelectLSB (unsigned nBankLSB); void ProgramChange (unsigned nProgram); +private: + void ProcessSound (void); + private: CConfig *m_pConfig; @@ -63,48 +72,12 @@ class CMiniDexed : public CDexedAdapter CSerialMIDIDevice m_SerialMIDI; bool m_bUseSerial; + CSoundBaseDevice *m_pSoundDevice; + unsigned m_nQueueSizeFrames; + protected: CPerformanceTimer m_GetChunkTimer; bool m_bProfileEnabled; }; -//// PWM ////////////////////////////////////////////////////////////////////// - -class CMiniDexedPWM : public CMiniDexed, public CPWMSoundBaseDevice -{ -public: - CMiniDexedPWM (CConfig *pConfig, CInterruptSystem *pInterrupt, - CGPIOManager *pGPIOManager); - - bool Initialize (void); - - unsigned GetChunk (u32 *pBuffer, unsigned nChunkSize); -}; - -//// I2S ////////////////////////////////////////////////////////////////////// - -class CMiniDexedI2S : public CMiniDexed, public CI2SSoundBaseDevice -{ -public: - CMiniDexedI2S (CConfig *pConfig, CInterruptSystem *pInterrupt, - CGPIOManager *pGPIOManager, CI2CMaster *pI2CMaster); - - bool Initialize (void); - - unsigned GetChunk (u32 *pBuffer, unsigned nChunkSize); -}; - -//// HDMI ///////////////////////////////////////////////////////////////////// - -class CMiniDexedHDMI : public CMiniDexed, public CHDMISoundBaseDevice -{ -public: - CMiniDexedHDMI (CConfig *pConfig, CInterruptSystem *pInterrupt, - CGPIOManager *pGPIOManager); - - bool Initialize (void); - - unsigned GetChunk (u32 *pBuffer, unsigned nChunkSize); -}; - #endif