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
pull/42/head
probonopd 3 years ago
parent 7707f90d46
commit f784824254
  1. 3
      build.sh
  2. 23
      src/kernel.cpp
  3. 228
      src/minidexed.cpp
  4. 61
      src/minidexed.h

@ -18,6 +18,9 @@ fi
cd circle-stdlib/ cd circle-stdlib/
make mrproper || true make mrproper || true
./configure -r ${RPI} --prefix "${TOOLCHAIN_PREFIX}" ./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 += -DUSE_PWM_AUDIO_ON_ZERO" >> libs/circle/Config.mk
echo "DEFINE += -DSAVE_VFP_REGS_ON_IRQ" >> libs/circle/Config.mk echo "DEFINE += -DSAVE_VFP_REGS_ON_IRQ" >> libs/circle/Config.mk
echo "DEFINE += -DREALTIME" >> libs/circle/Config.mk echo "DEFINE += -DREALTIME" >> libs/circle/Config.mk

@ -18,7 +18,6 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
#include "kernel.h" #include "kernel.h"
#include <string.h>
#include <circle/logger.h> #include <circle/logger.h>
#include <circle/synchronize.h> #include <circle/synchronize.h>
#include <assert.h> #include <assert.h>
@ -65,27 +64,7 @@ bool CKernel::Initialize (void)
m_Config.Load (); m_Config.Load ();
// select the sound device m_pDexed = new CMiniDexed (&m_Config, &mInterrupt, &m_GPIOManager, &m_I2CMaster);
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);
}
assert (m_pDexed); assert (m_pDexed);
if (!m_pDexed->Initialize ()) if (!m_pDexed->Initialize ())

@ -19,18 +19,28 @@
// //
#include "minidexed.h" #include "minidexed.h"
#include <circle/logger.h> #include <circle/logger.h>
#include <circle/memory.h>
#include <circle/pwmsoundbasedevice.h>
#include <circle/i2ssoundbasedevice.h>
#include <circle/hdmisoundbasedevice.h>
#include <string.h>
#include <stdio.h> #include <stdio.h>
#include <assert.h> #include <assert.h>
LOGMODULE ("minidexed"); 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 ()), : CDexedAdapter (CConfig::MaxNotes, pConfig->GetSampleRate ()),
#ifdef ARM_ALLOW_MULTI_CORE
CMultiCoreSupport (CMemorySystem::Get ()),
#endif
m_pConfig (pConfig), m_pConfig (pConfig),
m_UI (this, pGPIOManager, pConfig), m_UI (this, pGPIOManager, pConfig),
m_PCKeyboard (this), m_PCKeyboard (this),
m_SerialMIDI (this, pInterrupt, pConfig), m_SerialMIDI (this, pInterrupt, pConfig),
m_bUseSerial (false), m_bUseSerial (false),
m_pSoundDevice (0),
m_GetChunkTimer ("GetChunk", m_GetChunkTimer ("GetChunk",
1000000U * pConfig->GetChunkSize ()/2 / pConfig->GetSampleRate ()), 1000000U * pConfig->GetChunkSize ()/2 / pConfig->GetSampleRate ()),
m_bProfileEnabled (m_pConfig->GetProfileEnabled ()) m_bProfileEnabled (m_pConfig->GetProfileEnabled ())
@ -40,10 +50,38 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt, CGPIOMan
m_pMIDIKeyboard[i] = new CMIDIKeyboard (this, pConfig, i); m_pMIDIKeyboard[i] = new CMIDIKeyboard (this, pConfig, i);
assert (m_pMIDIKeyboard[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) bool CMiniDexed::Initialize (void)
{ {
assert (m_pConfig);
assert (m_pSoundDevice);
if (!m_UI.Initialize ()) if (!m_UI.Initialize ())
{ {
return false; return false;
@ -63,11 +101,37 @@ bool CMiniDexed::Initialize (void)
ProgramChange (0); ProgramChange (0);
setTranspose (24); 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; return true;
} }
void CMiniDexed::Process (bool bPlugAndPlayUpdated) void CMiniDexed::Process (bool bPlugAndPlayUpdated)
{ {
#ifndef ARM_ALLOW_MULTI_CORE
ProcessSound ();
#endif
for (unsigned i = 0; i < CConfig::MaxUSBMIDIDevices; i++) for (unsigned i = 0; i < CConfig::MaxUSBMIDIDevices; i++)
{ {
assert (m_pMIDIKeyboard[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) CSysExFileLoader *CMiniDexed::GetSysExFileLoader (void)
{ {
return &m_SysExFileLoader; return &m_SysExFileLoader;
@ -120,159 +199,30 @@ void CMiniDexed::ProgramChange (unsigned nProgram)
m_UI.ProgramChanged (nProgram); m_UI.ProgramChanged (nProgram);
} }
//// PWM ////////////////////////////////////////////////////////////////////// void CMiniDexed::ProcessSound (void)
CMiniDexedPWM::CMiniDexedPWM (CConfig *pConfig, CInterruptSystem *pInterrupt,
CGPIOManager *pGPIOManager)
: CMiniDexed (pConfig, pInterrupt, pGPIOManager),
CPWMSoundBaseDevice (pInterrupt, pConfig->GetSampleRate (),
pConfig->GetChunkSize ())
{ {
} assert (m_pSoundDevice);
bool CMiniDexedPWM::Initialize (void) unsigned nFrames = m_nQueueSizeFrames - m_pSoundDevice->GetQueueFramesAvail ();
{ if (nFrames >= m_nQueueSizeFrames/2)
if (!CMiniDexed::Initialize ())
{
return false;
}
return Start ();
}
unsigned CMiniDexedPWM::GetChunk (u32 *pBuffer, unsigned nChunkSize)
{ {
if (m_bProfileEnabled) if (m_bProfileEnabled)
{ {
m_GetChunkTimer.Start (); m_GetChunkTimer.Start ();
} }
unsigned nResult = nChunkSize; int16_t SampleBuffer[nFrames];
getSamples (nFrames, SampleBuffer);
int16_t SampleBuffer[nChunkSize/2];
getSamples (nChunkSize/2, SampleBuffer);
for (unsigned i = 0; nChunkSize > 0; nChunkSize -= 2) // fill the whole buffer if ( m_pSoundDevice->Write (SampleBuffer, sizeof SampleBuffer)
!= (int) sizeof SampleBuffer)
{ {
s32 nSample = SampleBuffer[i++]; LOGERR ("Sound data dropped");
nSample += 32768;
nSample *= GetRangeMax()/2;
nSample /= 32768;
*pBuffer++ = nSample; // 2 stereo channels
*pBuffer++ = nSample;
} }
if (m_bProfileEnabled) if (m_bProfileEnabled)
{ {
m_GetChunkTimer.Stop (); 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;
int16_t SampleBuffer[nChunkSize/2];
getSamples (nChunkSize/2, 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)
{
nFrame = 0;
}
*pBuffer++ = nSample; // 2 stereo channels
*pBuffer++ = nSample;
}
if (m_bProfileEnabled)
{
m_GetChunkTimer.Stop();
}
return nResult;
};

@ -33,25 +33,34 @@
#include <circle/interrupt.h> #include <circle/interrupt.h>
#include <circle/gpiomanager.h> #include <circle/gpiomanager.h>
#include <circle/i2cmaster.h> #include <circle/i2cmaster.h>
#include <circle/pwmsoundbasedevice.h> #include <circle/multicore.h>
#include <circle/i2ssoundbasedevice.h> #include <circle/soundbasedevice.h>
#include <circle/hdmisoundbasedevice.h>
class CMiniDexed : public CDexedAdapter class CMiniDexed : public CDexedAdapter
#ifdef ARM_ALLOW_MULTI_CORE
, public CMultiCoreSupport
#endif
{ {
public: public:
CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt, CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt,
CGPIOManager *pGPIOManager); CGPIOManager *pGPIOManager, CI2CMaster *pI2CMaster);
virtual bool Initialize (void); bool Initialize (void);
void Process (bool bPlugAndPlayUpdated); void Process (bool bPlugAndPlayUpdated);
#ifdef ARM_ALLOW_MULTI_CORE
void Run (unsigned nCore);
#endif
CSysExFileLoader *GetSysExFileLoader (void); CSysExFileLoader *GetSysExFileLoader (void);
void BankSelectLSB (unsigned nBankLSB); void BankSelectLSB (unsigned nBankLSB);
void ProgramChange (unsigned nProgram); void ProgramChange (unsigned nProgram);
private:
void ProcessSound (void);
private: private:
CConfig *m_pConfig; CConfig *m_pConfig;
@ -63,48 +72,12 @@ private:
CSerialMIDIDevice m_SerialMIDI; CSerialMIDIDevice m_SerialMIDI;
bool m_bUseSerial; bool m_bUseSerial;
CSoundBaseDevice *m_pSoundDevice;
unsigned m_nQueueSizeFrames;
protected: protected:
CPerformanceTimer m_GetChunkTimer; CPerformanceTimer m_GetChunkTimer;
bool m_bProfileEnabled; 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 #endif

Loading…
Cancel
Save