Signal chain is now float32_t with much more support of CMSIS5.

pull/73/head
Holger Wirtz 3 years ago
parent fcafaea364
commit 1942cc95ad
  1. 2
      README.md
  2. 2
      Synth_Dexed
  3. 21
      src/common.h
  4. 9
      src/dexedadapter.h
  5. 24
      src/effect_platervbstereo.cpp
  6. 40
      src/effect_platervbstereo.h
  7. 89
      src/minidexed.cpp
  8. 9
      src/minidexed.h
  9. 87
      src/mixer.cpp
  10. 79
      src/mixer.h
  11. 2
      src/uimenu.cpp

@ -152,7 +152,7 @@ sudo losetup -d "${DEV}"
rm -r boot
# Write to SD card
sudo dd if="${IMG}" of=/dev/mmcblk0 bs=512k status=progress
sudo dd if="${IMG}" of=/dev/mmcblk0 bs=512k status=progress && sync
```
## Acknowledgements

@ -1 +1 @@
Subproject commit 70293ae5998643c706244b090504dde8b4097851
Subproject commit e414a8718300815aefc3fe0acd8df5c12ad0b58a

@ -0,0 +1,21 @@
#ifndef _common_h
#define _common_h
inline long maplong(long x, long in_min, long in_max, long out_min, long out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
inline float32_t mapfloat(float32_t val, float32_t in_min, float32_t in_max, float32_t out_min, float32_t out_max)
{
return (val - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
#define constrain(amt, low, high) ({ \
__typeof__(amt) _amt = (amt); \
__typeof__(low) _low = (low); \
__typeof__(high) _high = (high); \
(_amt < _low) ? _low : ((_amt > _high) ? _high : _amt); \
})
#endif

@ -17,6 +17,7 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#ifndef _dexedadapter_h
#define _dexedadapter_h
@ -34,10 +35,6 @@ public:
: Dexed (maxnotes, rate)
{
Dexed::setCompressor(true);
if(Dexed::getCompressor()==true)
printf("Dexed-Compressor: enabled\n");
else
printf("Dexed-Compressor: disabled\n");
}
void loadVoiceParameters (uint8_t* data)
@ -61,10 +58,10 @@ public:
m_SpinLock.Release ();
}
void getSamples (uint16_t n_samples, int16_t* buffer)
void getSamples (float32_t* buffer, uint16_t n_samples)
{
m_SpinLock.Acquire ();
Dexed::getSamples (n_samples, buffer);
Dexed::getSamples (buffer, n_samples);
m_SpinLock.Release ();
}

@ -153,12 +153,12 @@ AudioEffectPlateReverb::AudioEffectPlateReverb(float32_t samplerate)
lfo2_phase_acc = 0;
lfo2_adder = (UINT32_MAX + 1)/(samplerate * LFO2_FREQ_HZ);
send_level = 0.0;
reverb_level = 0.0;
}
// #define sat16(n, rshift) signed_saturate_rshift((n), 16, (rshift))
void AudioEffectPlateReverb::doReverb(uint16_t len, int16_t audioblock[][2])
void AudioEffectPlateReverb::doReverb(float32_t* audioblockL, float32_t* audioblockR, uint16_t len)
{
int i;
float32_t input, acc, temp1, temp2;
@ -236,7 +236,7 @@ void AudioEffectPlateReverb::doReverb(uint16_t len, int16_t audioblock[][2])
y += (int64_t)y1 * idx;
lfo2_out_cos = (int32_t) (y >> (32-8)); // 16bit output
input = (float32_t(audioblock[i][0])/32767.0f) * input_attn;
input = audioblockL[i] * input_attn;
// chained input allpasses, channel L
acc = in_allp1_bufL[in_allp1_idxL] + input * in_allp_k;
@ -259,7 +259,7 @@ void AudioEffectPlateReverb::doReverb(uint16_t len, int16_t audioblock[][2])
in_allp_out_L = acc;
if (++in_allp4_idxL >= sizeof(in_allp4_bufL)/sizeof(float32_t)) in_allp4_idxL = 0;
input = (float32_t(audioblock[i][1])/32767.0f) * input_attn;
input = audioblockR[i] * input_attn;
// chained input allpasses, channel R
acc = in_allp1_bufR[in_allp1_idxR] + input * in_allp_k;
@ -406,13 +406,7 @@ void AudioEffectPlateReverb::doReverb(uint16_t len, int16_t audioblock[][2])
temp1 = acc - master_lowpass_l;
master_lowpass_l += temp1 * master_lowpass_f;
int32_t out = audioblock[i][0] + int16_t(master_lowpass_l * 32767.0f * send_level);
if(out > INT16_MAX)
audioblock[i][0] = INT16_MAX;
else if(out < INT16_MIN)
audioblock[i][0] = INT16_MIN;
else
audioblock[i][0] = out;
audioblockL[i]+=master_lowpass_l * reverb_level;
// Channel R
#ifdef TAP1_MODULATED
@ -456,12 +450,6 @@ void AudioEffectPlateReverb::doReverb(uint16_t len, int16_t audioblock[][2])
temp1 = acc - master_lowpass_r;
master_lowpass_r += temp1 * master_lowpass_f;
out = audioblock[i][1] + int16_t(master_lowpass_l * 32767.0f * send_level);
if(out > INT16_MAX)
audioblock[i][1] = INT16_MAX;
else if(out < INT16_MIN)
audioblock[i][1] = INT16_MIN;
else
audioblock[i][1] = out;
audioblockR[i]+=master_lowpass_r * reverb_level;
}
}

@ -45,37 +45,9 @@
#ifndef _EFFECT_PLATERVBSTEREO_H
#define _EFFECT_PLATERVBSTEREO_H
#include "arm_math.h"
#include <stdint.h>
#define constrain(amt, low, high) ({ \
__typeof__(amt) _amt = (amt); \
__typeof__(low) _low = (low); \
__typeof__(high) _high = (high); \
(_amt < _low) ? _low : ((_amt > _high) ? _high : _amt); \
})
/*
template<typename T>
inline static T min(const T& a, const T& b) {
return a < b ? a : b;
}
template<typename T>
inline static T max(const T& a, const T& b) {
return a > b ? a : b;
}
inline long maplong(long x, long in_min, long in_max, long out_min, long out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
*/
inline float32_t mapfloat(float32_t val, float32_t in_min, float32_t in_max, float32_t out_min, float32_t out_max)
{
return (val - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
#include <arm_math.h>
#include "common.h"
/***
* Loop delay modulation: comment/uncomment to switch sin/cos
@ -89,7 +61,7 @@ class AudioEffectPlateReverb
{
public:
AudioEffectPlateReverb(float32_t samplerate);
void doReverb(uint16_t len, int16_t audioblock[][2]);
void doReverb(float32_t* audioblockL, float32_t* audioblockR, uint16_t len);
void size(float n)
{
@ -136,9 +108,9 @@ public:
//__enable_irq();
}
void send(float n)
void level(float n)
{
send_level = constrain(n, 0.0f, 1.0f);
reverb_level = constrain(n, 0.0f, 1.0f);
}
float32_t get_size(void) {return rv_time_k;}
@ -147,7 +119,7 @@ public:
void tgl_bypass(void) {bypass ^=1;}
private:
bool bypass = false;
float32_t send_level;
float32_t reverb_level;
float32_t input_attn;
float32_t in_allp_k; // input allpass coeff

@ -58,6 +58,7 @@ 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;
@ -113,6 +114,10 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt,
}
#endif
// BEGIN setup tg_mixer
tg_mixer = new AudioStereoMixer<8>();
// END setup tg_mixer
// BEGIN setup reverb
reverb = new AudioEffectPlateReverb(pConfig->GetSampleRate());
SetParameter (ParameterReverbSize, 70);
@ -120,7 +125,7 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt,
SetParameter (ParameterReverbLowDamp, 50);
SetParameter (ParameterReverbLowPass, 30);
SetParameter (ParameterReverbDiffusion, 65);
SetParameter (ParameterReverbSend, 80);
SetParameter (ParameterReverbLevel, 80);
// END setup reverb
};
@ -285,7 +290,7 @@ void CMiniDexed::Run (unsigned nCore)
for (unsigned i = 0; i < CConfig::TGsCore23; i++, nTG++)
{
assert (m_pTG[nTG]);
m_pTG[nTG]->getSamples (m_nFramesToProcess, m_OutputLevel[nTG]);
m_pTG[nTG]->getSamples (m_OutputLevel[nTG], m_nFramesToProcess);
}
}
}
@ -348,13 +353,11 @@ void CMiniDexed::SetVolume (unsigned nVolume, unsigned nTG)
void CMiniDexed::SetPan (unsigned nPan, unsigned nTG)
{
if (nPan > 127)
{
return;
}
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 ();
}
@ -502,7 +505,7 @@ void CMiniDexed::SetParameter (TParameter Parameter, int nValue)
case ParameterReverbLowDamp: reverb->lodamp (fValue); break;
case ParameterReverbLowPass: reverb->lowpass (fValue); break;
case ParameterReverbDiffusion: reverb->diffusion (fValue); break;
case ParameterReverbSend: reverb->send (fValue); break;
case ParameterReverbLevel: reverb->level (fValue); break;
default:
assert (0);
@ -622,8 +625,8 @@ void CMiniDexed::ProcessSound (void)
m_GetChunkTimer.Start ();
}
int16_t SampleBuffer[nFrames];
m_pTG[0]->getSamples (nFrames, SampleBuffer);
//int16_t SampleBuffer[nFrames]; // TODO float->int
m_pTG[0]->getSamples (SampleBuffer, nFrames);
if ( m_pSoundDevice->Write (SampleBuffer, sizeof SampleBuffer)
!= (int) sizeof SampleBuffer)
@ -666,7 +669,7 @@ void CMiniDexed::ProcessSound (void)
for (unsigned i = 0; i < CConfig::TGsCore1; i++)
{
assert (m_pTG[i]);
m_pTG[i]->getSamples (nFrames, m_OutputLevel[i]);
m_pTG[i]->getSamples (m_OutputLevel[i], nFrames);
}
// wait for cores 2 and 3 to complete their work
@ -678,51 +681,53 @@ void CMiniDexed::ProcessSound (void)
}
}
//
// Audio signal path after tone generators starts here
//
// now mix the output of all TGs
int16_t SampleBuffer[nFrames][2];
float32_t SampleBuffer[2][nFrames];
uint8_t indexL=0, indexR=1;
assert (SampleBuffer[0]!=NULL);
arm_fill_f32(0.0, SampleBuffer[0], nFrames);
assert (SampleBuffer[1]!=NULL);
arm_fill_f32(0.0, SampleBuffer[1], nFrames);
if (m_bChannelsSwapped)
{
indexL=1;
indexR=0;
}
assert (CConfig::ToneGenerators == 8);
for (unsigned i = 0; i < nFrames; i++)
for (uint16_t i = 0; i < nFrames; i++)
{
int32_t nLeft = m_OutputLevel[0][i] * (127-m_nPan[0])
+ m_OutputLevel[1][i] * (127-m_nPan[1])
+ m_OutputLevel[2][i] * (127-m_nPan[2])
+ m_OutputLevel[3][i] * (127-m_nPan[3])
+ m_OutputLevel[4][i] * (127-m_nPan[4])
+ m_OutputLevel[5][i] * (127-m_nPan[5])
+ m_OutputLevel[6][i] * (127-m_nPan[6])
+ m_OutputLevel[7][i] * (127-m_nPan[7]);
nLeft >>= m_nActiveTGsLog2 + 7;
int32_t nRight = m_OutputLevel[0][i] * m_nPan[0]
+ m_OutputLevel[1][i] * m_nPan[1]
+ m_OutputLevel[2][i] * m_nPan[2]
+ m_OutputLevel[3][i] * m_nPan[3]
+ m_OutputLevel[4][i] * m_nPan[4]
+ m_OutputLevel[5][i] * m_nPan[5]
+ m_OutputLevel[6][i] * m_nPan[6]
+ m_OutputLevel[7][i] * m_nPan[7];
nRight >>= m_nActiveTGsLog2 + 7;
if (!m_bChannelsSwapped)
{
SampleBuffer[i][0] = (int16_t) nLeft;
SampleBuffer[i][1] = (int16_t) nRight;
}
else
for(uint8_t n=0; n<CConfig::ToneGenerators; n++)
{
SampleBuffer[i][0] = (int16_t) nRight;
SampleBuffer[i][1] = (int16_t) nLeft;
SampleBuffer[indexL][i] += m_OutputLevel[n][i] * 1.0f-pan_float[n];
SampleBuffer[indexR][i] += m_OutputLevel[n][i] * pan_float[n];
}
}
// BEGIN adding reverb
m_ReverbSpinLock.Acquire ();
reverb->doReverb(nFrames,SampleBuffer);
reverb->doReverb(SampleBuffer[0],SampleBuffer[1],nFrames);
m_ReverbSpinLock.Release ();
// END adding reverb
if (m_pSoundDevice->Write (SampleBuffer, sizeof SampleBuffer) != (int) sizeof SampleBuffer)
// Convert float to int16 array
float32_t tmp_float[nFrames*2];
int16_t tmp_int[nFrames*2];
for(uint16_t i=0; i<nFrames;i++)
{
tmp_float[i*2]=SampleBuffer[0][i];
tmp_float[(i*2)+1]=SampleBuffer[1][i];
}
arm_float_to_q15(tmp_float,(q15_t*)tmp_int,nFrames*2);
if (m_pSoundDevice->Write (tmp_int, sizeof tmp_int) != (int) sizeof tmp_int)
{
LOGERR ("Sound data dropped");
}

@ -39,7 +39,9 @@
#include <circle/multicore.h>
#include <circle/soundbasedevice.h>
#include <circle/spinlock.h>
#include "common.h"
#include "effect_platervbstereo.h"
#include "mixer.h"
class CMiniDexed
#ifdef ARM_ALLOW_MULTI_CORE
@ -82,7 +84,7 @@ public:
ParameterReverbLowDamp,
ParameterReverbLowPass,
ParameterReverbDiffusion,
ParameterReverbSend,
ParameterReverbLevel,
ParameterUnknown
};
@ -137,6 +139,7 @@ private:
unsigned m_nProgram[CConfig::ToneGenerators];
unsigned m_nVolume[CConfig::ToneGenerators];
unsigned m_nPan[CConfig::ToneGenerators];
unsigned pan_float[CConfig::ToneGenerators];
int m_nMasterTune[CConfig::ToneGenerators];
unsigned m_nMIDIChannel[CConfig::ToneGenerators];
@ -161,13 +164,15 @@ private:
unsigned m_nActiveTGsLog2;
volatile TCoreStatus m_CoreStatus[CORES];
volatile unsigned m_nFramesToProcess;
int16_t m_OutputLevel[CConfig::ToneGenerators][CConfig::MaxChunkSize];
float32_t m_OutputLevel[CConfig::ToneGenerators][CConfig::MaxChunkSize];
#endif
CPerformanceTimer m_GetChunkTimer;
bool m_bProfileEnabled;
AudioEffectPlateReverb* reverb;
AudioStereoMixer<8>* tg_mixer;
CSpinLock m_ReverbSpinLock;
};

@ -0,0 +1,87 @@
// Taken from https://github.com/manicken/Audio/tree/templateMixer
// Adapted for MiniDexed by Holger Wirtz <dcoredump@googlemail.com>
/* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <cstdlib>
#include <stdint.h>
#include <assert.h>
#include "arm_math.h"
#include "mixer.h"
template <int NN> void AudioMixer<NN>::gain(uint8_t channel, float32_t gain)
{
if (channel >= NN) return;
if (gain > MAX_GAIN)
gain = MAX_GAIN;
else if (gain < MIN_GAIN)
gain = MIN_GAIN;
multiplier[channel] = gain;
}
template <int NN> void AudioMixer<NN>::gain(float32_t gain)
{
for (uint8_t i = 0; i < NN; i++)
{
if (gain > MAX_GAIN)
gain = MAX_GAIN;
else if (gain < MIN_GAIN)
gain = MIN_GAIN;
multiplier[i] = gain;
}
}
template <int NN> void AudioMixer<NN>::doAddMix(uint8_t channel, float32_t* in, float32_t* out, uint16_t len)
{
float32_t* tmp=malloc(sizeof(float32_t)*len);
assert(tmp!=NULL);
arm_scale_f32(in,multiplier[channel],tmp,len);
arm_add_f32(out, tmp, out, len);
free(tmp);
}
template <int NN> void AudioStereoMixer<NN>::doAddMix(uint8_t channel, float32_t* in[], float32_t* out[], uint16_t len)
{
float32_t* tmp=malloc(sizeof(float32_t)*len);
assert(tmp!=NULL);
// panorama
for(uint16_t i=0;i<len;i++)
{
// left
arm_scale_f32(in+(i*2),multiplier[channel],tmp+(i*2),len);
arm_add_f32(out(i*2), tmp(i*2), out(i*2), len*2);
// right
}
free(tmp);
}

@ -0,0 +1,79 @@
// Taken from https://github.com/manicken/Audio/tree/templateMixer
// Adapted for MiniDexed by Holger Wirtz <dcoredump@googlemail.com>
/* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef template_mixer_h_
#define template_mixer_h_
#include "arm_math.h"
#include <stdint.h>
#define UNITYGAIN 1.0f
#define MAX_GAIN 1.0f
#define MIN_GAIN 0.0f
template <int NN> class AudioMixer
{
public:
AudioMixer(void)
{
for (uint8_t i=0; i<NN; i++)
multiplier[i] = UNITYGAIN;
}
void doAddMix(uint8_t channel, float32_t* in, float32_t* out, uint16_t len);
/**
* this sets the individual gains
* @param channel
* @param gain
*/
void gain(uint8_t channel, float32_t gain);
/**
* set all channels to specified gain
* @param gain
*/
void gain(float32_t gain);
protected:
float32_t multiplier[NN];
};
template <int NN> class AudioStereoMixer : public AudioMixer<NN>
{
public:
AudioStereoMixer(void)
{
AudioMixer<NN>();
for (uint8_t i=0; i<NN; i++)
panorama[i] = 0.0;
}
void doAddMix(uint8_t channel, float32_t* in[], float32_t* out[], uint16_t len);
protected:
float32_t panorama[NN];
};
#endif

@ -76,7 +76,7 @@ const CUIMenu::TMenuItem CUIMenu::s_ReverbMenu[] =
{"Low damp", EditGlobalParameter, 0, CMiniDexed::ParameterReverbLowDamp},
{"Low pass", EditGlobalParameter, 0, CMiniDexed::ParameterReverbLowPass},
{"Diffusion", EditGlobalParameter, 0, CMiniDexed::ParameterReverbDiffusion},
{"Send", EditGlobalParameter, 0, CMiniDexed::ParameterReverbSend},
{"Level", EditGlobalParameter, 0, CMiniDexed::ParameterReverbLevel},
{0}
};

Loading…
Cancel
Save