Add support for 8 channel I2S mono audio output on RPi 5 (#657)

* First implementation of using four PCM5102 I2S sound devices for 8-channel mono sound output on a Raspberry Pi 5.  Requires latest develop branch of circle.

* Update to required develop branch of circle

* Adjusted default chunk sizes to correctly support number of channels.

* Update queue size as per Rene's suggestion.

---------

Co-authored-by: probonopd <probonopd@users.noreply.github.com>
probonopd-try-USBGadgetPin^2
Kevin 4 months ago committed by GitHub
parent afa72d21aa
commit 98b5274cf3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 18
      src/config.cpp
  2. 2
      src/config.h
  3. 222
      src/minidexed.cpp
  4. 1
      src/minidexed.h
  5. 2
      submod.sh

@ -41,15 +41,22 @@ void CConfig::Load (void)
m_SoundDevice = m_Properties.GetString ("SoundDevice", "pwm"); m_SoundDevice = m_Properties.GetString ("SoundDevice", "pwm");
m_nSampleRate = m_Properties.GetNumber ("SampleRate", 48000); m_nSampleRate = m_Properties.GetNumber ("SampleRate", 48000);
m_bQuadDAC8Chan = m_Properties.GetNumber ("QuadDAC8Chan", 0) != 0;
if (m_SoundDevice == "hdmi") {
m_nChunkSize = m_Properties.GetNumber ("ChunkSize", 384*6);
}
else
{
#ifdef ARM_ALLOW_MULTI_CORE #ifdef ARM_ALLOW_MULTI_CORE
m_nChunkSize = m_Properties.GetNumber ("ChunkSize", m_SoundDevice == "hdmi" ? 384*6 : 256); m_nChunkSize = m_Properties.GetNumber ("ChunkSize", m_bQuadDAC8Chan ? 1024 : 256); // 128 per channel
#else #else
m_nChunkSize = m_Properties.GetNumber ("ChunkSize", m_SoundDevice == "hdmi" ? 384*6 : 1024); m_nChunkSize = m_Properties.GetNumber ("ChunkSize", 1024);
#endif #endif
}
m_nDACI2CAddress = m_Properties.GetNumber ("DACI2CAddress", 0); m_nDACI2CAddress = m_Properties.GetNumber ("DACI2CAddress", 0);
m_bChannelsSwapped = m_Properties.GetNumber ("ChannelsSwapped", 0) != 0; m_bChannelsSwapped = m_Properties.GetNumber ("ChannelsSwapped", 0) != 0;
unsigned newEngineType = m_Properties.GetNumber ("EngineType", 1); unsigned newEngineType = m_Properties.GetNumber ("EngineType", 1);
if (newEngineType == 2) { if (newEngineType == 2) {
m_EngineType = MKI; m_EngineType = MKI;
} else if (newEngineType == 3) { } else if (newEngineType == 3) {
@ -243,6 +250,11 @@ bool CConfig::GetExpandPCAcrossBanks (void) const
return m_bExpandPCAcrossBanks; return m_bExpandPCAcrossBanks;
} }
bool CConfig::GetQuadDAC8Chan (void) const
{
return m_bQuadDAC8Chan;
}
bool CConfig::GetLCDEnabled (void) const bool CConfig::GetLCDEnabled (void) const
{ {
return m_bLCDEnabled; return m_bLCDEnabled;

@ -87,6 +87,7 @@ public:
bool GetMIDIAutoVoiceDumpOnPC (void) const; // false if not specified bool GetMIDIAutoVoiceDumpOnPC (void) const; // false if not specified
bool GetHeaderlessSysExVoices (void) const; // false if not specified bool GetHeaderlessSysExVoices (void) const; // false if not specified
bool GetExpandPCAcrossBanks (void) const; // true if not specified bool GetExpandPCAcrossBanks (void) const; // true if not specified
bool GetQuadDAC8Chan (void) const; // false if not specified
// HD44780 LCD // HD44780 LCD
// GPIO pin numbers are chip numbers, not header positions // GPIO pin numbers are chip numbers, not header positions
@ -208,6 +209,7 @@ private:
bool m_bMIDIAutoVoiceDumpOnPC; bool m_bMIDIAutoVoiceDumpOnPC;
bool m_bHeaderlessSysExVoices; bool m_bHeaderlessSysExVoices;
bool m_bExpandPCAcrossBanks; bool m_bExpandPCAcrossBanks;
bool m_bQuadDAC8Chan;
bool m_bLCDEnabled; bool m_bLCDEnabled;
unsigned m_nLCDPinEnable; unsigned m_nLCDPinEnable;

@ -42,6 +42,7 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt,
m_PCKeyboard (this, pConfig, &m_UI), m_PCKeyboard (this, pConfig, &m_UI),
m_SerialMIDI (this, pInterrupt, pConfig, &m_UI), m_SerialMIDI (this, pInterrupt, pConfig, &m_UI),
m_bUseSerial (false), m_bUseSerial (false),
m_bQuadDAC8Chan (false),
m_pSoundDevice (0), m_pSoundDevice (0),
m_bChannelsSwapped (pConfig->GetChannelsSwapped ()), m_bChannelsSwapped (pConfig->GetChannelsSwapped ()),
#ifdef ARM_ALLOW_MULTI_CORE #ifdef ARM_ALLOW_MULTI_CORE
@ -125,10 +126,26 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt,
if (strcmp (pDeviceName, "i2s") == 0) if (strcmp (pDeviceName, "i2s") == 0)
{ {
LOGNOTE ("I2S mode"); LOGNOTE ("I2S mode");
#if RASPPI==5
m_pSoundDevice = new CI2SSoundBaseDevice (pInterrupt, pConfig->GetSampleRate (), // Quad DAC 8-channel mono only an option for RPI 5
pConfig->GetChunkSize (), false, m_bQuadDAC8Chan = pConfig->GetQuadDAC8Chan ();
pI2CMaster, pConfig->GetDACI2CAddress ()); #endif
if (m_bQuadDAC8Chan) {
LOGNOTE ("Configured for Quad DAC 8-channel Mono audio");
m_pSoundDevice = new CI2SSoundBaseDevice (pInterrupt, pConfig->GetSampleRate (),
pConfig->GetChunkSize (), false,
pI2CMaster, pConfig->GetDACI2CAddress (),
CI2SSoundBaseDevice::DeviceModeTXOnly,
8); // 8 channels - L+R x4 across 4 I2S lanes
}
else
{
m_pSoundDevice = new CI2SSoundBaseDevice (pInterrupt, pConfig->GetSampleRate (),
pConfig->GetChunkSize (), false,
pI2CMaster, pConfig->GetDACI2CAddress (),
CI2SSoundBaseDevice::DeviceModeTXOnly,
2); // 2 channels - L+R
}
} }
else if (strcmp (pDeviceName, "hdmi") == 0) else if (strcmp (pDeviceName, "hdmi") == 0)
{ {
@ -251,18 +268,30 @@ bool CMiniDexed::Initialize (void)
} }
// setup and start the sound device // setup and start the sound device
if (!m_pSoundDevice->AllocateQueueFrames (m_pConfig->GetChunkSize ())) int Channels = 1; // 16-bit Mono
#ifdef ARM_ALLOW_MULTI_CORE
if (m_bQuadDAC8Chan)
{
Channels = 8; // 16-bit 8-channel mono
}
else
{
Channels = 2; // 16-bit Stereo
}
#endif
// Need 2 x ChunkSize / Channel queue frames as the audio driver uses
// two DMA channels each of ChunkSize and one single single frame
// contains a sample for each of all the channels.
//
// See discussion here: https://github.com/rsta2/circle/discussions/453
if (!m_pSoundDevice->AllocateQueueFrames (2 * m_pConfig->GetChunkSize () / Channels))
{ {
LOGERR ("Cannot allocate sound queue"); LOGERR ("Cannot allocate sound queue");
return false; return false;
} }
#ifndef ARM_ALLOW_MULTI_CORE m_pSoundDevice->SetWriteFormat (SoundFormatSigned16, Channels);
m_pSoundDevice->SetWriteFormat (SoundFormatSigned16, 1); // 16-bit Mono
#else
m_pSoundDevice->SetWriteFormat (SoundFormatSigned16, 2); // 16-bit Stereo
#endif
m_nQueueSizeFrames = m_pSoundDevice->GetQueueSizeFrames (); m_nQueueSizeFrames = m_pSoundDevice->GetQueueSizeFrames ();
@ -1128,85 +1157,130 @@ void CMiniDexed::ProcessSound (void)
assert (CConfig::ToneGenerators == 8); assert (CConfig::ToneGenerators == 8);
uint8_t indexL=0, indexR=1; if (m_bQuadDAC8Chan) {
// No mixing is performed by MiniDexed, sound is output in 8 channels.
// BEGIN TG mixing // Note: one TG per audio channel; output=mono; no processing.
float32_t tmp_float[nFrames*2]; const int Channels = 8; // One TG per channel
int16_t tmp_int[nFrames*2]; float32_t tmp_float[nFrames*Channels];
int16_t tmp_int[nFrames*Channels];
if(nMasterVolume > 0.0) if(nMasterVolume > 0.0)
{
for (uint8_t i = 0; i < CConfig::ToneGenerators; i++)
{ {
tg_mixer->doAddMix(i,m_OutputLevel[i]); // Convert dual float array (8 chan) to single int16 array (8 chan)
reverb_send_mixer->doAddMix(i,m_OutputLevel[i]); for(uint16_t i=0; i<nFrames;i++)
{
// TGs will alternate on L/R channels for each output
// reading directly from the TG OutputLevel buffer with
// no additional processing.
for (uint8_t tg = 0; tg < Channels; tg++)
{
if(nMasterVolume >0.0 && nMasterVolume <1.0)
{
tmp_float[(i*Channels)+tg]=m_OutputLevel[tg][i] * nMasterVolume;
}
else if(nMasterVolume == 1.0)
{
tmp_float[(i*Channels)+tg]=m_OutputLevel[tg][i];
}
}
}
arm_float_to_q15(tmp_float,tmp_int,nFrames*Channels);
} }
// END TG mixing else
// BEGIN create SampleBuffer for holding audio data
float32_t SampleBuffer[2][nFrames];
// END create SampleBuffer for holding audio data
// get the mix of all TGs
tg_mixer->getMix(SampleBuffer[indexL], SampleBuffer[indexR]);
// BEGIN adding reverb
if (m_nParameter[ParameterReverbEnable])
{ {
float32_t ReverbBuffer[2][nFrames]; arm_fill_q15(0, tmp_int, nFrames*Channels);
float32_t ReverbSendBuffer[2][nFrames];
arm_fill_f32(0.0f, ReverbBuffer[indexL], nFrames);
arm_fill_f32(0.0f, ReverbBuffer[indexR], nFrames);
arm_fill_f32(0.0f, ReverbSendBuffer[indexR], nFrames);
arm_fill_f32(0.0f, ReverbSendBuffer[indexL], nFrames);
m_ReverbSpinLock.Acquire ();
reverb_send_mixer->getMix(ReverbSendBuffer[indexL], ReverbSendBuffer[indexR]);
reverb->doReverb(ReverbSendBuffer[indexL],ReverbSendBuffer[indexR],ReverbBuffer[indexL], ReverbBuffer[indexR],nFrames);
// scale down and add left reverb buffer by reverb level
arm_scale_f32(ReverbBuffer[indexL], reverb->get_level(), ReverbBuffer[indexL], nFrames);
arm_add_f32(SampleBuffer[indexL], ReverbBuffer[indexL], SampleBuffer[indexL], nFrames);
// scale down and add right reverb buffer by reverb level
arm_scale_f32(ReverbBuffer[indexR], reverb->get_level(), ReverbBuffer[indexR], nFrames);
arm_add_f32(SampleBuffer[indexR], ReverbBuffer[indexR], SampleBuffer[indexR], nFrames);
m_ReverbSpinLock.Release ();
} }
// END adding reverb
if (m_pSoundDevice->Write (tmp_int, sizeof(tmp_int)) != (int) sizeof(tmp_int))
// swap stereo channels if needed prior to writing back out
if (m_bChannelsSwapped)
{ {
indexL=1; LOGERR ("Sound data dropped");
indexR=0;
} }
}
else
{
// Mix everything down to stereo
uint8_t indexL=0, indexR=1;
// BEGIN TG mixing
float32_t tmp_float[nFrames*2];
int16_t tmp_int[nFrames*2];
// Convert dual float array (left, right) to single int16 array (left/right) if(nMasterVolume > 0.0)
for(uint16_t i=0; i<nFrames;i++)
{ {
if(nMasterVolume >0.0 && nMasterVolume <1.0) for (uint8_t i = 0; i < CConfig::ToneGenerators; i++)
{ {
tmp_float[i*2]=SampleBuffer[indexL][i] * nMasterVolume; tg_mixer->doAddMix(i,m_OutputLevel[i]);
tmp_float[(i*2)+1]=SampleBuffer[indexR][i] * nMasterVolume; reverb_send_mixer->doAddMix(i,m_OutputLevel[i]);
} }
else if(nMasterVolume == 1.0) // END TG mixing
// BEGIN create SampleBuffer for holding audio data
float32_t SampleBuffer[2][nFrames];
// END create SampleBuffer for holding audio data
// get the mix of all TGs
tg_mixer->getMix(SampleBuffer[indexL], SampleBuffer[indexR]);
// BEGIN adding reverb
if (m_nParameter[ParameterReverbEnable])
{ {
tmp_float[i*2]=SampleBuffer[indexL][i]; float32_t ReverbBuffer[2][nFrames];
tmp_float[(i*2)+1]=SampleBuffer[indexR][i]; float32_t ReverbSendBuffer[2][nFrames];
arm_fill_f32(0.0f, ReverbBuffer[indexL], nFrames);
arm_fill_f32(0.0f, ReverbBuffer[indexR], nFrames);
arm_fill_f32(0.0f, ReverbSendBuffer[indexR], nFrames);
arm_fill_f32(0.0f, ReverbSendBuffer[indexL], nFrames);
m_ReverbSpinLock.Acquire ();
reverb_send_mixer->getMix(ReverbSendBuffer[indexL], ReverbSendBuffer[indexR]);
reverb->doReverb(ReverbSendBuffer[indexL],ReverbSendBuffer[indexR],ReverbBuffer[indexL], ReverbBuffer[indexR],nFrames);
// scale down and add left reverb buffer by reverb level
arm_scale_f32(ReverbBuffer[indexL], reverb->get_level(), ReverbBuffer[indexL], nFrames);
arm_add_f32(SampleBuffer[indexL], ReverbBuffer[indexL], SampleBuffer[indexL], nFrames);
// scale down and add right reverb buffer by reverb level
arm_scale_f32(ReverbBuffer[indexR], reverb->get_level(), ReverbBuffer[indexR], nFrames);
arm_add_f32(SampleBuffer[indexR], ReverbBuffer[indexR], SampleBuffer[indexR], nFrames);
m_ReverbSpinLock.Release ();
} }
// END adding reverb
// swap stereo channels if needed prior to writing back out
if (m_bChannelsSwapped)
{
indexL=1;
indexR=0;
}
// Convert dual float array (left, right) to single int16 array (left/right)
for(uint16_t i=0; i<nFrames;i++)
{
if(nMasterVolume >0.0 && nMasterVolume <1.0)
{
tmp_float[i*2]=SampleBuffer[indexL][i] * nMasterVolume;
tmp_float[(i*2)+1]=SampleBuffer[indexR][i] * nMasterVolume;
}
else if(nMasterVolume == 1.0)
{
tmp_float[i*2]=SampleBuffer[indexL][i];
tmp_float[(i*2)+1]=SampleBuffer[indexR][i];
}
}
arm_float_to_q15(tmp_float,tmp_int,nFrames*2);
}
else
{
arm_fill_q15(0, tmp_int, nFrames * 2);
} }
arm_float_to_q15(tmp_float,tmp_int,nFrames*2);
}
else
arm_fill_q15(0, tmp_int, nFrames * 2);
if (m_pSoundDevice->Write (tmp_int, sizeof(tmp_int)) != (int) sizeof(tmp_int)) if (m_pSoundDevice->Write (tmp_int, sizeof(tmp_int)) != (int) sizeof(tmp_int))
{ {
LOGERR ("Sound data dropped"); LOGERR ("Sound data dropped");
} }
} // End of Stereo mixing
if (m_bProfileEnabled) if (m_bProfileEnabled)
{ {

@ -298,6 +298,7 @@ private:
CPCKeyboard m_PCKeyboard; CPCKeyboard m_PCKeyboard;
CSerialMIDIDevice m_SerialMIDI; CSerialMIDIDevice m_SerialMIDI;
bool m_bUseSerial; bool m_bUseSerial;
bool m_bQuadDAC8Chan;
CSoundBaseDevice *m_pSoundDevice; CSoundBaseDevice *m_pSoundDevice;
bool m_bChannelsSwapped; bool m_bChannelsSwapped;

@ -12,7 +12,7 @@ cd -
# #
# Optional update submodules explicitly # Optional update submodules explicitly
cd circle-stdlib/libs/circle cd circle-stdlib/libs/circle
git checkout 4155f43 git checkout fff3764
cd - cd -
cd circle-stdlib/libs/circle-newlib cd circle-stdlib/libs/circle-newlib
#git checkout develop #git checkout develop

Loading…
Cancel
Save