major update and fixes

pull/2/head
pio 8 months ago
parent f1f8aa711d
commit 07d5e7e9dc
  1. 62
      keywords.txt
  2. 42
      src/basic_DSPutils.cpp
  3. 42
      src/basic_DSPutils.h
  4. 1
      src/basic_components.h
  5. 18
      src/basic_delay.h
  6. 5
      src/basic_shelvFilter.h
  7. 37
      src/basic_tempBuffer.h
  8. 16
      src/control_SGTL5000_F32.cpp
  9. 40
      src/control_SGTL5000_F32.h
  10. 333
      src/control_WM8731_F32.cpp
  11. 69
      src/control_WM8731_F32.h
  12. 447
      src/effect_compressorStereo_F32.h
  13. 208
      src/effect_delaystereo_F32.cpp
  14. 78
      src/effect_delaystereo_F32.h
  15. 79
      src/effect_gainStereo_F32.h
  16. 339
      src/effect_guitarBooster_F32.cpp
  17. 175
      src/effect_guitarBooster_F32.h
  18. 139
      src/effect_noiseGateStereo_F32.h
  19. 63
      src/effect_platereverb_F32.cpp
  20. 161
      src/effect_platereverb_F32.h
  21. 152
      src/effect_reverbsc_F32.cpp
  22. 73
      src/effect_reverbsc_F32.h
  23. 60
      src/effect_springreverb_F32.cpp
  24. 35
      src/effect_springreverb_F32.h
  25. 114
      src/effect_xfaderStereo_F32.h
  26. 2
      src/filter_3bandeq.h
  27. 59
      src/filter_biquadStereo_F32.cpp
  28. 258
      src/filter_biquadStereo_F32.h
  29. 4
      src/filter_equalizer_F32.h
  30. 40
      src/filter_ir_cabsim_F32.cpp
  31. 14
      src/filter_ir_cabsim_F32.h
  32. 11
      src/hexefx_audio_F32.h
  33. 212
      src/input_i2s2_F32.cpp
  34. 14
      src/input_i2s2_F32.h
  35. 171
      src/output_i2s2_F32.cpp
  36. 18
      src/output_i2s2_F32.h
  37. 93
      src/switch_selectorStereo_F32.h
  38. 1
      src/wavetables.c

@ -27,6 +27,8 @@ setMix KEYWORD2
AudioFilterShelvingLPHP KEYWORD1 AudioFilterShelvingLPHP KEYWORD1
AudioFilterLP KEYWORD1 AudioFilterLP KEYWORD1
AudioBasicTempBuffer_F32 KEYWORD1
AudioEffectInfinitePhaser_F32 KEYWORD1 AudioEffectInfinitePhaser_F32 KEYWORD1
depth KEYWORD2 depth KEYWORD2
depth_top KEYWORD2 depth_top KEYWORD2
@ -43,7 +45,6 @@ AudioEffectMonoToStereo_F32 KEYWORD1
stereo_set KEYWORD2 stereo_set KEYWORD2
pan_set KEYWORD2 pan_set KEYWORD2
AudioEffectPhaserStereo_F32 KEYWORD1 AudioEffectPhaserStereo_F32 KEYWORD1
top KEYWORD2 top KEYWORD2
btm KEYWORD2 btm KEYWORD2
@ -99,7 +100,7 @@ bass_cut KEYWORD2
AudioEffectDelayStereo_F32 KEYWORD1 AudioEffectDelayStereo_F32 KEYWORD1
delay KEYWORD2 delay KEYWORD2
feedback KEYWORD2 feedback KEYWORD2
inertia inertia KEYWORD2
mod_rateHz KEYWORD2 mod_rateHz KEYWORD2
mod_rate KEYWORD2 mod_rate KEYWORD2
mod_depth KEYWORD2 mod_depth KEYWORD2
@ -108,9 +109,66 @@ tap_tempo KEYWORD2
AudioEffectReverbSc_F32 KEYWORD1 AudioEffectReverbSc_F32 KEYWORD1
lowpass KEYWORD2 lowpass KEYWORD2
AudioEffectCompressorStereo_F32 KEYWORD1
setDefaultValues KEYWORD2
calcAudioLevel_dB KEYWORD2
calcGain KEYWORD2
calcInstantaneousTargetGain KEYWORD2
calcSmoothedGain_dB KEYWORD2
resetStates KEYWORD2
setPreGain KEYWORD2
setPreGain_dB KEYWORD2
setPostGain KEYWORD2
setPostGain_dB KEYWORD2
setCompressionRatio KEYWORD2
setAttack_sec KEYWORD2
setRelease_sec KEYWORD2
setLevelTimeConst_sec KEYWORD2
setThresh_dBFS KEYWORD2
enableHPFilter KEYWORD2
getPreGain_dB KEYWORD2
getAttack_sec KEYWORD2
getRelease_sec KEYWORD2
getLevelTimeConst_sec KEYWORD2
getThresh_dBFS KEYWORD2
getCompressionRatio KEYWORD2
getCurrentLevel_dBFS KEYWORD2
getCurrentGain_dB KEYWORD2
setHPFilterCoeff_N2IIR_Matlab
bypass_get KEYWORD2
bypass_set KEYWORD2
bypass_tgl KEYWORD2
setSideChainMode KEYWORD2
AudioEffectGainStereo_F32 KEYWORD1
setGain KEYWORD2
setGain_dB KEYWORD2
getGain KEYWORD2
getGain_dB KEYWORD2
setPan KEYWORD2
getPan KEYWORD2
AudioEffectGuitarBooster_F32 KEYWORD1
drive KEYWORD2
bottom KEYWORD2
tone KEYWORD2
bias KEYWORD2
mix KEYWORD2
volume KEYWORD2
octave_get KEYWORD2
octave_set KEYWORD2
octave_tgl KEYWORD2
memcpyInterleave_f32 KEYWORD2 memcpyInterleave_f32 KEYWORD2
memcpyDeinterleave_f32 KEYWORD2 memcpyDeinterleave_f32 KEYWORD2
AudioInputI2S2_F32 KEYWORD1 AudioInputI2S2_F32 KEYWORD1
AudioOutputI2S2_F32 KEYWORD1 AudioOutputI2S2_F32 KEYWORD1
AudioControlSGTL5000_Ext KEYWORD1
AudioControlWM8731_Ext KEYWORD1
dac_mute KEYWORD2
HPfilter KEYWORD2

@ -0,0 +1,42 @@
#include "basic_DSPutils.h"
/**
* @brief scale a float vector (range -1.0 - 1.0) to a new float vector
* in range of int32_t + saturation.
* based on arm_float_to_31
*
* @param pSrc pointer to the source vector
* @param pDst pointer to the destination verctor
* @param blockSize
*/
void scale_float_to_int32range(const float32_t *pSrc, float32_t *pDst, uint32_t blockSize)
{
uint32_t blkCnt; /* Loop counter */
const float32_t *pIn = pSrc; /* Source pointer */
/* Loop unrolling: Compute 4 outputs at a time */
blkCnt = blockSize >> 2U;
while (blkCnt > 0U)
{
/* C = A * 2147483648 */
/* Convert from float to Q31 and then store the results in the destination buffer */
*pDst++ = (float32_t)clip_q63_to_q31((q63_t)(*pIn++ * 2147483648.0f));
*pDst++ = (float32_t)clip_q63_to_q31((q63_t)(*pIn++ * 2147483648.0f));
*pDst++ = (float32_t)clip_q63_to_q31((q63_t)(*pIn++ * 2147483648.0f));
*pDst++ = (float32_t)clip_q63_to_q31((q63_t)(*pIn++ * 2147483648.0f));
blkCnt--;
}
/* Loop unrolling: Compute remaining outputs */
blkCnt = blockSize % 0x4U;
while (blkCnt > 0U)
{
/* C = A * 2147483648 */
*pDst++ = (float32_t)clip_q63_to_q31((q63_t)(*pIn++ * 2147483648.0f));
/* Decrement loop counter */
blkCnt--;
}
}

@ -1,14 +1,19 @@
#ifndef _BASIC_DSPUTILS_H_ #ifndef _BASIC_DSPUTILS_H_
#define _BASIC_DSPUTILS_H_ #define _BASIC_DSPUTILS_H_
#include <Arduino.h>
#include <arm_math.h> #include <arm_math.h>
#define F32_TO_I32_NORM_FACTOR (2147483647) // which is 2^31-1
#define I32_TO_F32_NORM_FACTOR (4.656612875245797e-10) //which is 1/(2^31 - 1)
static inline void mix_pwr(float32_t mix, float32_t *wetMix, float32_t *dryMix); static inline void mix_pwr(float32_t mix, float32_t *wetMix, float32_t *dryMix);
static inline void mix_pwr(float32_t mix, float32_t *wetMix, float32_t *dryMix) static inline void mix_pwr(float32_t mix, float32_t *wetMix, float32_t *dryMix)
{ {
// Calculate mix parameters // Calculate mix parameters
// A cheap mostly energy constant crossfade from SignalSmith Blog // A cheap mostly energy constant crossfade from SignalSmith Blog
// https://signalsmith-audio.co.uk/writing/2021/cheap-energy-crossfade/ // https://signalsmith-audio.co.uk/writing/2021/cheap-energy-crossfade/
mix = constrain(mix, 0.0f, 1.0f);
float32_t x2 = 1.0f - mix; float32_t x2 = 1.0f - mix;
float32_t A = mix*x2; float32_t A = mix*x2;
float32_t B = A * (1.0f + 1.4186f * A); float32_t B = A * (1.0f + 1.4186f * A);
@ -19,4 +24,41 @@ static inline void mix_pwr(float32_t mix, float32_t *wetMix, float32_t *dryMix)
*dryMix = D * D; *dryMix = D * D;
} }
void scale_float_to_int32range(const float32_t *pSrc, float32_t *pDst, uint32_t blockSize);
/**
* @brief combine two separate buffers into interleaved one
* @param sz - samples per output buffer (divisible by 2)
* @param dst - pointer to source buffer
* @param srcA - pointer to A source buffer (even samples)
* @param srcB - pointer to B source buffer (odd samples)
* @retval none
*/
inline void memcpyInterleave_f32(float32_t *srcA, float32_t *srcB, float32_t *dst, int16_t sz)
{
while(sz)
{
*dst++ = *srcA++;
*dst++ = *srcB++;
sz--;
*dst++ = *srcA++;
*dst++ = *srcB++;
sz--;
}
}
inline void memcpyInterleave_f32(float32_t *srcA, float32_t *srcB, float32_t *dst, int16_t sz);
inline void memcpyDeinterleave_f32(float32_t *src, float32_t *dstA, float32_t *dstB, int16_t sz)
{
while(sz)
{
*dstA++ = *src++;
*dstB++ = *src++;
sz--;
*dstA++ = *src++;
*dstB++ = *src++;
sz--;
}
}
inline void memcpyDeinterleave_f32(float32_t *src, float32_t *dstA, float32_t *dstB, int16_t sz);
#endif // _BASIC_DSPUTILS_H_ #endif // _BASIC_DSPUTILS_H_

@ -7,5 +7,6 @@
#include "basic_shelvFilter.h" #include "basic_shelvFilter.h"
#include "basic_pitch.h" #include "basic_pitch.h"
#include "basic_DSPutils.h" #include "basic_DSPutils.h"
#include "basic_tempBuffer.h"
#endif // _BASIC_COMPONENTS_H_ #endif // _BASIC_COMPONENTS_H_

@ -30,9 +30,10 @@ public:
bool init(uint32_t size_samples, bool psram=false) bool init(uint32_t size_samples, bool psram=false)
{ {
if(bf) free(bf); if(bf) free(bf);
use_psram = psram;
size = size_samples; size = size_samples;
if (psram) bf = (float *)extmem_malloc(size * sizeof(float)); // allocate buffer if (use_psram) bf = (float *)extmem_malloc(size * sizeof(float)); // allocate buffer in PSRAM
else bf = (float *)malloc(size * sizeof(float)); // allocate buffer else bf = (float *)malloc(size * sizeof(float)); // allocate buffer in DMARAM
if (!bf) return false; if (!bf) return false;
idx = 0; idx = 0;
reset(); reset();
@ -40,7 +41,17 @@ public:
} }
void reset() void reset()
{ {
memset(bf, 0, size * sizeof(float)); memset(bf, 0, size * sizeof(float32_t));
if (use_psram) arm_dcache_flush_delete(&bf[0], size * sizeof(float32_t));
}
void reset(uint32_t startAddr, uint32_t endAddr)
{
if (startAddr > endAddr) return;
if (endAddr > (uint32_t)size) endAddr = size;
float32_t* memPtr = &bf[0]+startAddr;
uint32_t l = (endAddr - startAddr) * sizeof(float32_t);
memset(memPtr, 0, l);
if (use_psram) arm_dcache_flush_delete(memPtr, l);
} }
/** /**
* @brief get the tap from the delay buffer * @brief get the tap from the delay buffer
@ -106,6 +117,7 @@ private:
int32_t size; int32_t size;
float *bf; float *bf;
int32_t idx; int32_t idx;
bool use_psram = false;
}; };
#endif // _BASIC_DELAY_H_ #endif // _BASIC_DELAY_H_

@ -59,6 +59,11 @@ public:
hpreg += tmp1 * hp_f; hpreg += tmp1 * hp_f;
return (lpreg + hidamp*tmp2 + lodamp * hpreg); return (lpreg + hidamp*tmp2 + lodamp * hpreg);
} }
void reset()
{
lpreg = 0.0f;
hpreg = 0.0f;
}
private: private:
float lpreg; float lpreg;
float hpreg; float hpreg;

@ -0,0 +1,37 @@
#ifndef _BASIC_TEMPBUFFER_H_
#define _BASIC_TEMPBUFFER_H_
#include <Arduino.h>
#include "AudioStream_F32.h"
class AudioBasicTempBuffer_F32 : public AudioStream_F32
{
public:
AudioBasicTempBuffer_F32() : AudioStream_F32(1, inputQueueArray_f32)
{
blockSize = AUDIO_BLOCK_SAMPLES;
memset(&data[0], 0, AUDIO_BLOCK_SAMPLES * sizeof(float32_t));
dataPtr = &data[0];
};
AudioBasicTempBuffer_F32(const AudioSettings_F32 &settings) : AudioStream_F32(1, inputQueueArray_f32)
{
blockSize = settings.audio_block_samples;
memset(&data[0], 0, AUDIO_BLOCK_SAMPLES * sizeof(float32_t));
dataPtr = &data[0];
};
~AudioBasicTempBuffer_F32(){};
void update(void)
{
audio_block_f32_t* block = AudioStream_F32::receiveReadOnly_f32();
if (!block) return;
memcpy(&data[0], block->data, blockSize * sizeof(float32_t));
AudioStream_F32::release(block);
}
float32_t* dataPtr;
private:
audio_block_f32_t *inputQueueArray_f32[1];
uint16_t blockSize = AUDIO_BLOCK_SAMPLES;
float32_t data[AUDIO_BLOCK_SAMPLES];
};
#endif // _BASIC_TEMPBUFFER_H_

@ -0,0 +1,16 @@
#include "control_SGTL5000_F32.h"
#include <Wire.h>
#define CHIP_I2S_CTRL 0x0006
#define CHIP_ADCDAC_CTRL 0x000E
void AudioControlSGTL5000_F32::set_bitDepth(bit_depth_t bits)
{
uint16_t regTmp = read(CHIP_I2S_CTRL);
regTmp &= ~(0x30); // clear bit 5:4 (DLEN)
regTmp |= ((uint8_t)bits << 4) & 0x30; // update DLEN
write(CHIP_ADCDAC_CTRL, 0x000C); // mute DAC
write(CHIP_I2S_CTRL, regTmp); // write new config
write(CHIP_ADCDAC_CTRL, 0x0000); // unmute DAC
}

@ -0,0 +1,40 @@
#ifndef _CONTROL_SGTL5000_F32_H_
#define _CONTROL_SGTL5000_F32_H_
/**
* @file control_SGTL5000_ext.h
* @author Piotr Zapart
* @brief enables the bit depth setting for the SGTL5000 codec chip
* @version 0.1
* @date 2024-03-20
*
* @copyright Copyright (c) 2024 www.hexefx.com
* 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 <https://www.gnu.org/licenses/>."
*/
#include <Arduino.h>
#include <control_sgtl5000.h>
class AudioControlSGTL5000_F32 : public AudioControlSGTL5000
{
//GUI: inputs:0, outputs:0 //this line used for automatic generation of GUI node
public:
AudioControlSGTL5000_F32(void) {};
typedef enum
{
I2S_BITS_32 = 0,
I2S_BITS_24,
I2S_BITS_20,
I2S_BITS_16
}bit_depth_t;
void set_bitDepth(bit_depth_t bits);
};
#endif // _CONTROL_SGTL5000_EXT_H_

@ -0,0 +1,333 @@
#include "control_WM8731_F32.h"
#include <Wire.h>
#define WM8731_I2C_ADDR_A 0x1A
#define WM8731_I2C_ADDR_B 0x1B
#define WM8731_MUTE_ON (1)
#define WM8731_MUTE_OFF (0)
#define WM8731_REG_LLINEIN (0)
#define WM8731_BITS_LINVOL_SHIFT (0)
#define WM8731_BITS_LINVOL_MASK (0x1F)
#define WM8731_BITS_LINVOL(x) (((x)<<WM8731_BITS_LINVOL_SHIFT)&WM8731_BITS_LINVOL_MASK)
#define WM8731_BITS_LINMUTE_SHIFT (7)
#define WM8731_BITS_LINMUTE_MASK (1<<WM8731_BITS_LINMUTE_SHIFT)
#define WM8731_BITS_LINMUTE(x) (((x)<<WM8731_BITS_LINMUTE_SHIFT)&WM8731_BITS_LINMUTE_MASK)
#define WM8731_BITS_LRINBOTH_SHIFT (8)
#define WM8731_BITS_LRINBOTH_MASK (1<<WM8731_BITS_LRINBOTH_SHIFT)
#define WM8731_BITS_LRINBOTH(x) (((x)<<WM8731_BITS_LRINBOTH_SHIFT)&WM8731_BITS_LRINBOTH_MASK)
#define WM8731_REG_RLINEIN (1)
#define WM8731_BITS_RINVOL_SHIFT (0)
#define WM8731_BITS_RINVOL_MASK (0x1F)
#define WM8731_BITS_RINVOL(x) (((x)<<WM8731_BITS_RINVOL_SHIFT)&WM8731_BITS_RINVOL_MASK)
#define WM8731_BITS_RINMUTE_SHIFT (7)
#define WM8731_BITS_RINMUTE_MASK (1<<WM8731_BITS_RINMUTE_SHIFT)
#define WM8731_BITS_RINMUTE(x) (((x)<<WM8731_BITS_RINMUTE_SHIFT)&WM8731_BITS_RINMUTE_MASK)
#define WM8731_BITS_RLINBOTH_SHIFT (8)
#define WM8731_BITS_RLINBOTH_MASK (1<<WM8731_BITS_RLINBOTH_SHIFT)
#define WM8731_BITS_RLINBOTH(x) (((x)<<WM8731_BITS_RLINBOTH_SHIFT)&WM8731_BITS_RLINBOTH_MASK)
#define WM8731_REG_LHEADOUT (2)
#define WM8731_BITS_LHPVOL_SHIFT (0)
#define WM8731_BITS_LHPVOL_MASK (0x7F)
#define WM8731_BITS_LHPVOL(x) (((x))<<WM8731_BITS_LHPVOL_SHIFT)&WM8731_BITS_LHPVOL_MASK)
#define WM8731_BITS_LZCEN_SHIFT (7)
#define WM8731_BITS_LZCEN_MASK (1<<WM8731_BITS_LZCEN_SHIFT)
#define WM8731_BITS_LZCEN(x) (((x)<<WM8731_BITS_LZCEN_SHIFT)&WM8731_BITS_LZCEN_MASK)
#define WM8731_BITS_LRHPBOTH_SHIFT (8)
#define WM8731_BITS_LRHPBOTH_MASK (1<<WM8731_BITS_LRHPBOTH_SHIFT)
#define WM8731_BITS_LRHPBOTH(x) (((x)<<WM8731_BITS_LRHPBOTH_SHIFT)&WM8731_BITS_LRHPBOTH_MASK)
#define WM8731_REG_RHEADOUT (3)
#define WM8731_BITS_RHPVOL_SHIFT (0)
#define WM8731_BITS_RHPVOL_MASK (0x7F)
#define WM8731_BITS_RHPVOL(x) (((x))<<WM8731_BITS_RHPVOL_SHIFT)&WM8731_BITS_RHPVOL_MASK)
#define WM8731_BITS_RZCEN_SHIFT (7)
#define WM8731_BITS_RZCEN_MASK (1<<WM8731_BITS_RZCEN_SHIFT)
#define WM8731_BITS_RZCEN(x) (((x)<<WM8731_BITS_RZCEN_SHIFT)&WM8731_BITS_RZCEN_MASK)
#define WM8731_BITS_RLHPBOTH_SHIFT (8)
#define WM8731_BITS_RLHPBOTH_MASK (1<<WM8731_BITS_RLHPBOTH_SHIFT)
#define WM8731_BITS_RLHPBOTH(x) (((x)<<WM8731_BITS_RLHPBOTH_SHIFT)&WM8731_BITS_RLHPBOTH_MASK)
#define WM8731_REG_ANALOG (4)
#define WM8731_BITS_MICBOOST_SHIFT (0)
#define WM8731_BITS_MICBOOST_MASK (1<<WM8731_BITS_MICBOOST_SHIFT)
#define WM8731_BITS_MICBOOST(x) (((x)<<WM8731_BITS_MICBOOST_SHIFT)&WM8731_BITS_MICBOOST_MASK)
#define WM8731_BITS_MUTEMIC_SHIFT (1)
#define WM8731_BITS_MUTEMIC_MASK (1<<WM8731_BITS_MUTEMIC_SHIFT)
#define WM8731_BITS_MUTEMIC(x) (((x)<<WM8731_BITS_MUTEMIC_SHIFT)&WM8731_BITS_MUTEMIC_MASK)
#define WM8731_BITS_INSEL_SHIFT (2)
#define WM8731_BITS_INSEL_MASK (1<<WM8731_BITS_INSEL_SHIFT)
#define WM8731_BITS_INSEL(x) (((x)<<WM8731_BITS_INSEL_SHIFT)&WM8731_BITS_INSEL_MASK)
#define WM8731_BITS_BYPASS_SHIFT (3)
#define WM8731_BITS_BYPASS_MASK (1<<WM8731_BITS_BYPASS_SHIFT)
#define WM8731_BITS_BYPASS(x) (((x)<<WM8731_BITS_BYPASS_SHIFT)&WM8731_BITS_BYPASS_MASK)
#define WM8731_BITS_DACSEL_SHIFT (4)
#define WM8731_BITS_DACSEL_MASK (1<<WM8731_BITS_DACSEL_SHIFT)
#define WM8731_BITS_DACSEL(x) (((x)<<WM8731_BITS_DACSEL_SHIFT)&WM8731_BITS_DACSEL_MASK)
#define WM8731_BITS_SIDETONE_SHIFT (5)
#define WM8731_BITS_SIDETONE_MASK (1<<WM8731_BITS_SIDETONE_SHIFT)
#define WM8731_BITS_SIDETONE(x) (((x)<<WM8731_BITS_SIDETONE_SHIFT)&WM8731_BITS_SIDETONE_MASK)
#define WM8731_BITS_SIDEATT_SHIFT (6)
#define WM8731_BITS_SIDEATT_MASK (0xC0)
#define WM8731_BITS_SIDEATT(x) (((x)<<WM8731_BITS_SIDEATT_SHIFT)&WM8731_BITS_SIDEATT_MASK)
#define WM8731_SIDEATT_M6_DB (0x00)
#define WM8731_SIDEATT_M9_DB (0x01)
#define WM8731_SIDEATT_M12_DB (0x02)
#define WM8731_SIDEATT_M15_DB (0x03)
#define WM8731_REG_DIGITAL (5)
#define WM8731_BITS_ADCHPD_SHIFT (0)
#define WM8731_BITS_ADCHPD_MASK (1<<WM8731_BITS_ADCHPD_SHIFT)
#define WM8731_BITS_ADCHPD(x) (((x)<<WM8731_BITS_ADCHPD_SHIFT)&WM8731_BITS_ADCHPD_MASK)
#define WM8731_BITS_DEEMP_SHIFT (6)
#define WM8731_BITS_DEEMP_MASK (0x06)
#define WM8731_BITS_DEEMP(x) (((x)<<WM8731_BITS_DEEMP_SHIFT)&WM8731_BITS_DEEMP_MASK)
#define WM8731_DEEMP_OFF (0x00)
#define WM8731_DEEMP_32KHZ (0x01)
#define WM8731_DEEMP_44_1KHZ (0x02)
#define WM8731_DEEMP_48KHZ (0x03)
#define WM8731_BITS_DACMU_SHIFT (3)
#define WM8731_BITS_DACMU_MASK (1<<WM8731_BITS_DACMU_SHIFT)
#define WM8731_BITS_DACMU(x) (((x)<<WM8731_BITS_DACMU_SHIFT)&WM8731_BITS_DACMU_MASK)
#define WM8731_BITS_HPOR_SHIFT (4)
#define WM8731_BITS_HPOR_MASK (1<<WM8731_BITS_HPOR_SHIFT)
#define WM8731_BITS_HPOR(x) (((x)<<WM8731_BITS_HPOR_SHIFT)&WM8731_BITS_HPOR_MASK)
#define WM8731_REG_POWERDOWN (6)
#define WM8731_BITS_LINEINPD_SHIFT (0)
#define WM8731_BITS_LINEINPD_MASK (1<<WM8731_BITS_LINEINPD_SHIFT)
#define WM8731_BITS_LINEINPD(x) (((x)<<WM8731_BITS_LINEINPD_SHIFT)&WM8731_BITS_LINEINPD_MASK)
#define WM8731_BITS_MICPD_SHIFT (1)
#define WM8731_BITS_MICPD_MASK (1<<WM8731_BITS_MICPD_SHIFT)
#define WM8731_BITS_MICPD(x) (((x)<<WM8731_BITS_MICPD_SHIFT)&WM8731_BITS_MICPD_MASK)
#define WM8731_BITS_ADCPD_SHIFT (2)
#define WM8731_BITS_ADCPD_MASK (1<<WM8731_BITS_ADCPD_SHIFT)
#define WM8731_BITS_ADCPD(x) (((x)<<WM8731_BITS_ADCPD_SHIFT)&WM8731_BITS_ADCPD_MASK)
#define WM8731_BITS_DACPD_SHIFT (3)
#define WM8731_BITS_DACPD_MASK (1<<WM8731_BITS_DACPD_SHIFT)
#define WM8731_BITS_DACPD(x) (((x)<<WM8731_BITS_DACPD_SHIFT)&WM8731_BITS_DACPD_MASK)
#define WM8731_BITS_OUTPD_SHIFT (4)
#define WM8731_BITS_OUTPD_MASK (1<<WM8731_BITS_OUTPD_SHIFT)
#define WM8731_BITS_OUTPD(x) (((x)<<WM8731_BITS_OUTPD_SHIFT)&WM8731_BITS_OUTPD_MASK)
#define WM8731_BITS_OSCPD_SHIFT (5)
#define WM8731_BITS_OSCPD_MASK (1<<WM8731_BITS_OSCPD_SHIFT)
#define WM8731_BITS_OSCPD(x) (((x)<<WM8731_BITS_OSCPD_SHIFT)&WM8731_BITS_OSCPD_MASK)
#define WM8731_BITS_CLKOUTPD_SHIFT (6)
#define WM8731_BITS_CLKOUTPD_MASK (1<<WM8731_BITS_CLKOUTPD_SHIFT)
#define WM8731_BITS_CLKOUTPD(x) (((x)<<WM8731_BITS_CLKOUTPD_SHIFT)&WM8731_BITS_CLKOUTPD_MASK)
#define WM8731_BITS_POWEROFF_SHIFT (7)
#define WM8731_BITS_POWEROFF_MASK (1<<WM8731_BITS_POWEROFF_SHIFT)
#define WM8731_BITS_POWEROFF(x) (((x)<<WM8731_BITS_POWEROFF_SHIFT)&WM8731_BITS_POWEROFF_MASK)
#define WM8731_REG_INTERFACE (7)
#define WM8731_BITS_FORMAT_SHIFT (0)
#define WM8731_BITS_FORMAT_MASK (0x03)
#define WM8731_BITS_FORMAT(x) (((x)<<WM8731_BITS_FORMAT_SHIFT)&WM8731_BITS_FORMAT_MASK)
#define WM8731_FORMAT_DSP_MODE (0x03)
#define WM8731_FORMAT_I2S_MSB_LEFT (0x02)
#define WM8731_FORMAT_MSB_LEFT (0x01)
#define WM8731_FORMAT_MSB_RIGHT (0x00)
#define WM8731_BITS_IWL_SHIFT (2)
#define WM8731_BITS_IWL_MASK (0x0C)
#define WM8731_BITS_IWL(x) ((x<<WM8731_BITS_IWL_SHIFT)&WM8731_BITS_IWL_MASK)
#define WM8731_BITS_LRP_SHIFT (4)
#define WM8731_BITS_LRP_MASK (1<<WM8731_BITS_LRP_SHIFT)
#define WM8731_BITS_LRP(x) (((x)<<WM8731_BITS_LRP_SHIFT)&WM8731_BITS_LRP_MASK)
#define WM8731_BITS_LRSWAP_SHIFT (5)
#define WM8731_BITS_LRSWAP_MASK (1<<WM8731_BITS_LRSWAP_SHIFT)
#define WM8731_BITS_LRSWAP(x) (((x)<<WM8731_BITS_LRSWAP_SHIFT)&WM8731_BITS_LRSWAP_MASK)
#define WM8731_BITS_MS_SHIFT (6)
#define WM8731_BITS_MS_MASK (1<<WM8731_BITS_MS_SHIFT)
#define WM8731_BITS_MS(x) (((x)<<WM8731_BITS_MS_SHIFT)&WM8731_BITS_MS_MASK)
#define WM8731_BITS_BCLKINV_SHIFT (7)
#define WM8731_BITS_BCLKINV_MASK (1<<WM8731_BITS_BCLKINV_SHIFT)
#define WM8731_BITS_BCLKINV(x) (((x)<<WM8731_BITS_BCLKINV_SHIFT)&WM8731_BITS_BCLKINV_MASK)
#define WM8731_REG_SAMPLING (8)
#define WM8731_BITS_USB_NORMAL_SHIFT (0)
#define WM8731_BITS_USB_NORMAL_MASK (1<<WM8731_BITS_USB_NORMAL_SHIFT)
#define WM8731_BITS_USB_NORMAL(x) (((x)<<WM8731_BITS_USB_NORMAL_SHIFT)&WM8731_BITS_USB_NORMAL_MASK)
#define WM8731_BITS_BOSR_SHIFT (1)
#define WM8731_BITS_BOSR_MASK (1<<WM8731_BITS_BOSR_SHIFT)
#define WM8731_BITS_BOSR(x) (((x)<<WM8731_BITS_BOSR_SHIFT)&WM8731_BITS_BOSR_MASK)
#define WM8731_BITS_SR_SHIFT (2)
#define WM8731_BITS_SR_MASK (0x3C)
#define WM8731_BITS_SR(x) (((x)<<WM8731_BITS_SR_SHIFT)&WM8731_BITS_SR_MASK)
#define WM8731_BITS_CLKIDIV2_SHIFT (6)
#define WM8731_BITS_CLKIDIV2_MASK (1<<WM8731_BITS_CLKIDIV2_SHIFT)
#define WM8731_BITS_CLKIDIV2(x) (((x)<<WM8731_BITS_CLKIDIV2_SHIFT)&WM8731_BITS_CLKIDIV2_MASK)
#define WM8731_BITS_CLKODIV2_SHIFT (7)
#define WM8731_BITS_CLKODIV2_MASK (1<<WM8731_BITS_CLKODIV2_SHIFT)
#define WM8731_BITS_CLKODIV2(x) (((x)<<WM8731_BITS_CLKODIV2_SHIFT)&WM8731_BITS_CLKODIV2_MASK)
#define WM8731_REG_ACTIVE (9)
#define WM8731_REG_RESET (15)
bool AudioControlWM8731_F32::enable(bit_depth_t bits, uint8_t addr)
{
i2c_addr = addr;
Wire.begin();
delay(5);
if (!write(WM8731_REG_RESET, 0))
{
return false; // no WM8731 chip responding
}
write(WM8731_REG_INTERFACE, WM8731_BITS_FORMAT(WM8731_FORMAT_I2S_MSB_LEFT) |
WM8731_BITS_IWL(bits)); // I2S, x bit, MCLK slave
write(WM8731_REG_SAMPLING, 0x20); // 256*Fs, 44.1 kHz, MCLK/1
// In order to prevent pops, the DAC should first be soft-muted (DACMU),
// the output should then be de-selected from the line and headphone output
// (DACSEL), then the DAC powered down (DACPD).
write(WM8731_REG_DIGITAL, 0x08); // DAC soft mute
write(WM8731_REG_ANALOG, 0x00); // disable all
write(WM8731_REG_POWERDOWN, 0x00); // codec powerdown
write(WM8731_REG_LHEADOUT, 0x80); // volume off
write(WM8731_REG_RHEADOUT, 0x80);
delay(100); // how long to power up?
write(WM8731_REG_ACTIVE, 1);
delay(5);
write(WM8731_REG_DIGITAL, 0x00); // DAC unmuted
write(WM8731_REG_ANALOG, 0x10); // DAC selected
return true;
}
void AudioControlWM8731_F32::dac_mute(bool m)
{
write(WM8731_REG_DIGITAL, m ? WM8731_BITS_DACMU(1) : WM8731_BITS_DACMU(0)); // DAC soft mute
DACmute = m;
}
void AudioControlWM8731_F32::HPfilter(bool state)
{
write(WM8731_REG_DIGITAL, WM8731_BITS_DACMU(DACmute) | WM8731_BITS_ADCHPD(state));
}
bool AudioControlWM8731_F32::write(unsigned int reg, unsigned int val)
{
int attempt = 0;
while (1)
{
attempt++;
Wire.beginTransmission(i2c_addr);
Wire.write((reg << 1) | ((val >> 8) & 1));
Wire.write(val & 0xFF);
int status = Wire.endTransmission();
if (status == 0) return true;
if (attempt >= 12) return false;
delayMicroseconds(80);
}
}
bool AudioControlWM8731_F32::volumeInteger(unsigned int n)
{
// n = 127 for max volume (+6 dB)
// n = 48 for min volume (-73 dB)
// n = 0 to 47 for mute
if (n > 127)
n = 127;
// Serial.print("volumeInteger, n = ");
// Serial.println(n);
write(WM8731_REG_LHEADOUT, n | 0x180);
write(WM8731_REG_RHEADOUT, n | 0x80);
return true;
}
bool AudioControlWM8731_F32::inputLevel(float n)
{
// range is 0x00 (min) - 0x1F (max)
int _level = int(n * 31.f);
_level = _level > 0x1F ? 0x1F : _level;
write(WM8731_REG_LLINEIN, _level);
write(WM8731_REG_RLINEIN, _level);
return true;
}
bool AudioControlWM8731_F32::inputSelect(input_select_t n)
{
if (n == INPUT_SELECT_LINEIN) write(WM8731_REG_ANALOG, 0x12);
else if (n == INPUT_SELECT_MIC) write(WM8731_REG_ANALOG, 0x15);
else return false;
return true;
}
/******************************************************************/
bool AudioControlWM8731_F32_master::enable(bit_depth_t bits, uint8_t addr)
{
i2c_addr = addr;
Wire.begin();
delay(5);
// write(WM8731_REG_RESET, 0);
write(WM8731_REG_INTERFACE,
WM8731_BITS_FORMAT(WM8731_FORMAT_I2S_MSB_LEFT) |
WM8731_BITS_IWL(bits)|
WM8731_BITS_MS(1)); // I2S, x bit, MCLK slave
write(WM8731_REG_SAMPLING, 0x20); // 256*Fs, 44.1 kHz, MCLK/1
// In order to prevent pops, the DAC should first be soft-muted (DACMU),
// the output should then be de-selected from the line and headphone output
// (DACSEL), then the DAC powered down (DACPD).
write(WM8731_REG_DIGITAL, 0x08); // DAC soft mute
write(WM8731_REG_ANALOG, 0x00); // disable all
write(WM8731_REG_POWERDOWN, 0x00); // codec powerdown
write(WM8731_REG_LHEADOUT, 0x80); // volume off
write(WM8731_REG_RHEADOUT, 0x80);
delay(100); // how long to power up?
write(WM8731_REG_ACTIVE, 1);
delay(5);
write(WM8731_REG_DIGITAL, 0x00); // DAC unmuted
write(WM8731_REG_ANALOG, 0x10); // DAC selected
return true;
}

@ -0,0 +1,69 @@
/**
* @file control_WM8731_ext.h
* @author Piotr Zapart
* @brief Alternative WM8731 driver with configurable bit depth
* @version 1.0
* @date 2024-03-21
*
* @copyright Copyright (c) 2024 www.hexefx.com
* 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 <https://www.gnu.org/licenses/>."
*/
#ifndef _CONTROL_WM8731_F32_H_
#define _CONTROL_WM8731_F32_H_
#include <Arduino.h>
#define WM8731_I2C_ADDR_CSB0 0x1A
#define WM8731_I2C_ADDR_CSB1 0x1B
class AudioControlWM8731_F32
{
public:
AudioControlWM8731_F32(){};
typedef enum
{
I2S_BITS_16 = 0,
I2S_BITS_20,
I2S_BITS_24,
I2S_BITS_32
}bit_depth_t;
typedef enum
{
INPUT_SELECT_LINEIN = 0,
INPUT_SELECT_MIC
}input_select_t;
bool enable(bit_depth_t bits = I2S_BITS_16, uint8_t addr=WM8731_I2C_ADDR_CSB0);
bool disable(void) { return false; }
bool volume(float n) { return volumeInteger(n * 80.0f + 47.499f); }
bool inputLevel(float n); // range: 0.0f to 1.0f
bool inputSelect(input_select_t n=INPUT_SELECT_LINEIN);
void dac_mute(bool m);
void HPfilter(bool state);
protected:
bool write(unsigned int reg, unsigned int val);
bool volumeInteger(unsigned int n); // range: 0x2F to 0x7F
private:
uint8_t bit_depth = I2S_BITS_16;
uint8_t i2c_addr;
bool DACmute = false;
};
class AudioControlWM8731_F32_master : public AudioControlWM8731_F32
{
public:
bool enable(bit_depth_t bits = I2S_BITS_16, uint8_t addr=WM8731_I2C_ADDR_CSB0);
private:
uint8_t i2c_addr;
};
#endif // _CONTROL_WM8731_EXTENDED_H_

@ -0,0 +1,447 @@
/*
AudioEffectCompressor
Created: Chip Audette, Dec 2016 - Jan 2017
Purpose; Apply dynamic range compression to the audio stream.
Assumes floating-point data.
This processes a single stream fo audio data (ie, it is mono)
MIT License. use at your own risk.
Stereo version - Piotr Zapart www.hexefx.com 03.2024
*/
#ifndef _EFFECT_COMPRESSORSTEREO_F32
#define _EFFECT_COMPRESSORSTEREO_F32
#include <arm_math.h> //ARM DSP extensions. https://www.keil.com/pack/doc/CMSIS/DSP/html/index.html
#include <AudioStream_F32.h>
class AudioEffectCompressorStereo_F32 : public AudioStream_F32
{
// GUI: inputs:1, outputs:1 //this line used for automatic generation of GUI node
public:
// constructor
AudioEffectCompressorStereo_F32(void) : AudioStream_F32(2, inputQueueArray_f32)
{
setDefaultValues(AUDIO_SAMPLE_RATE);
resetStates();
};
AudioEffectCompressorStereo_F32(const AudioSettings_F32 &settings) : AudioStream_F32(2, inputQueueArray_f32)
{
setDefaultValues(settings.sample_rate_Hz);
resetStates();
};
typedef enum
{
COMP_SIDECHAIN_SRC_LR, // l + r separate
COMP_SIDECHAIN_SRC_LRSUM // l + r sum / 2
}sideChainMode_t;
void setDefaultValues(const float sample_rate_Hz)
{
fs_Hz = sample_rate_Hz;
setThresh_dBFS(-20.0f); // set the default value for the threshold for compression
setCompressionRatio(5.0f); // set the default copression ratio
setAttack_sec(0.005f); // default to this value
setRelease_sec(0.200f); // default to this value
setHPFilterCoeff();
enableHPFilter(true); // enable the HP filter to remove any DC offset from the audio
sidechainMode = COMP_SIDECHAIN_SRC_LRSUM;
}
// here's the method that does all the work
void update(void)
{
audio_block_f32_t *blockL, *blockR;
if (bp) // handle bypass
{
blockL = AudioStream_F32::receiveReadOnly_f32(0);
blockR = AudioStream_F32::receiveReadOnly_f32(1);
if (!blockL || !blockR)
{
if (blockL) AudioStream_F32::release(blockL);
if (blockR) AudioStream_F32::release(blockR);
return;
}
AudioStream_F32::transmit(blockL, 0);
AudioStream_F32::transmit(blockR, 1);
AudioStream_F32::release(blockL);
AudioStream_F32::release(blockR);
return;
}
blockL = AudioStream_F32::receiveWritable_f32(0);
blockR = AudioStream_F32::receiveWritable_f32(1);
if (!blockL || !blockR)
{
if (blockL) AudioStream_F32::release(blockL);
if (blockR) AudioStream_F32::release(blockR);
return;
}
// allocate blocks required for gain calculations
audio_block_f32_t* audio_level_dB_blockL = AudioStream_F32::allocate_f32();
audio_block_f32_t* audio_level_dB_blockR = AudioStream_F32::allocate_f32();
audio_block_f32_t *gain_blockL = AudioStream_F32::allocate_f32();
audio_block_f32_t *gain_blockR = AudioStream_F32::allocate_f32();
// no memory for the audio gain blocks
if ( !audio_level_dB_blockL || !audio_level_dB_blockR || !gain_blockL || !gain_blockL)
{
if (audio_level_dB_blockL) AudioStream_F32::release(audio_level_dB_blockL);
if (audio_level_dB_blockR) AudioStream_F32::release(audio_level_dB_blockR);
if (gain_blockL) AudioStream_F32::release(gain_blockL);
if (gain_blockR) AudioStream_F32::release(gain_blockR);
AudioStream_F32::release(blockL);
AudioStream_F32::release(blockR);
return;
}
// apply a high-pass filter to get rid of the DC offset
if (use_HP_prefilter)
{
arm_biquad_cascade_df1_f32(&hp_filt_structL, blockL->data, blockL->data, blockL->length);
arm_biquad_cascade_df1_f32(&hp_filt_structR, blockR->data, blockR->data, blockR->length);
}
// apply the pre-gain...a negative gain value will disable
if (pre_gain > 0.0f)
{
arm_scale_f32(blockL->data, pre_gain, blockL->data, blockL->length); // use ARM DSP for speed!
arm_scale_f32(blockR->data, pre_gain, blockR->data, blockR->length);
}
// Side chain processing
switch (sidechainMode)
{
case COMP_SIDECHAIN_SRC_LR: // l + r separate
calcAudioLevel_dB(blockL, audio_level_dB_blockL);
calcAudioLevel_dB(blockR, audio_level_dB_blockR);
calcGain(audio_level_dB_blockL, gain_blockL);
calcGain(audio_level_dB_blockR, gain_blockR);
arm_mult_f32(blockL->data, gain_blockL->data, blockL->data, blockL->length);
arm_mult_f32(blockR->data, gain_blockR->data, blockR->data, blockR->length);
break;
case COMP_SIDECHAIN_SRC_LRSUM: // l + r sum / 2
arm_add_f32(blockL->data, blockR->data, audio_level_dB_blockL->data, audio_level_dB_blockL->length); // L+R -> db_L
arm_scale_f32(audio_level_dB_blockL->data, 0.5f, audio_level_dB_blockL->data, audio_level_dB_blockL->length); // L+R / 2
calcAudioLevel_dB(audio_level_dB_blockL, audio_level_dB_blockL); // chn L used for L&R
calcGain(audio_level_dB_blockL, gain_blockL);
arm_mult_f32(blockL->data, gain_blockL->data, blockL->data, blockL->length);
arm_mult_f32(blockR->data, gain_blockL->data, blockR->data, blockR->length);
break;
default:
break;
}
if (post_gain > 0.0f)
{
arm_scale_f32(blockL->data, post_gain, blockL->data, blockL->length); // use ARM DSP for speed!
arm_scale_f32(blockR->data, post_gain, blockR->data, blockR->length);
}
// transmit the block and release memory
AudioStream_F32::transmit(blockL, 0);
AudioStream_F32::transmit(blockR, 1);
AudioStream_F32::release(blockL);
AudioStream_F32::release(blockR);
AudioStream_F32::release(gain_blockL);
AudioStream_F32::release(gain_blockR);
AudioStream_F32::release(audio_level_dB_blockL);
AudioStream_F32::release(audio_level_dB_blockR);
}
// Here's the method that estimates the level of the audio (in dB)
// It squares the signal and low-pass filters to get a time-averaged
// signal power. It then
void calcAudioLevel_dB(audio_block_f32_t *wav_block, audio_block_f32_t *level_dB_block)
{
// calculate the instantaneous signal power (square the signal)
audio_block_f32_t *wav_pow_block = AudioStream_F32::allocate_f32();
arm_mult_f32(wav_block->data, wav_block->data, wav_pow_block->data, wav_block->length);
// low-pass filter and convert to dB
float c1 = level_lp_const, c2 = 1.0f - c1; // prepare constants
for (int i = 0; i < wav_pow_block->length; i++)
{
// first-order low-pass filter to get a running estimate of the average power
wav_pow_block->data[i] = c1 * prev_level_lp_pow + c2 * wav_pow_block->data[i];
// save the state of the first-order low-pass filter
prev_level_lp_pow = wav_pow_block->data[i];
// now convert the signal power to dB (but not yet multiplied by 10.0)
level_dB_block->data[i] = log10f_approx(wav_pow_block->data[i]);
}
// limit the amount that the state of the smoothing filter can go toward negative infinity
if (prev_level_lp_pow < (1.0E-13))
prev_level_lp_pow = 1.0E-13; // never go less than -130 dBFS
// scale the wav_pow_block by 10.0 to complete the conversion to dB
arm_scale_f32(level_dB_block->data, 10.0f, level_dB_block->data, level_dB_block->length); // use ARM DSP for speed!
// release memory and return
AudioStream_F32::release(wav_pow_block);
return; // output is passed through level_dB_block
}
// This method computes the desired gain from the compressor, given an estimate
// of the signal level (in dB)
void calcGain(audio_block_f32_t *audio_level_dB_block, audio_block_f32_t *gain_block)
{
// first, calculate the instantaneous target gain based on the compression ratio
audio_block_f32_t *inst_targ_gain_dB_block = AudioStream_F32::allocate_f32();
calcInstantaneousTargetGain(audio_level_dB_block, inst_targ_gain_dB_block);
// second, smooth in time (attack and release) by stepping through each sample
audio_block_f32_t *gain_dB_block = AudioStream_F32::allocate_f32();
calcSmoothedGain_dB(inst_targ_gain_dB_block, gain_dB_block);
// finally, convert from dB to linear gain: gain = 10^(gain_dB/20); (ie this takes care of the sqrt, too!)
arm_scale_f32(gain_dB_block->data, 1.0f / 20.0f, gain_dB_block->data, gain_dB_block->length); // divide by 20
for (int i = 0; i < gain_dB_block->length; i++)
gain_block->data[i] = pow10f(gain_dB_block->data[i]); // do the 10^(x)
// release memory and return
AudioStream_F32::release(gain_dB_block);
AudioStream_F32::release(inst_targ_gain_dB_block);
return; // output is passed through gain_block
}
// Compute the instantaneous desired gain, including the compression ratio and
// threshold for where the comrpession kicks in
void calcInstantaneousTargetGain(audio_block_f32_t *audio_level_dB_block, audio_block_f32_t *inst_targ_gain_dB_block)
{
// how much are we above the compression threshold?
audio_block_f32_t *above_thresh_dB_block = AudioStream_F32::allocate_f32();
arm_offset_f32(audio_level_dB_block->data, // CMSIS DSP for "add a constant value to all elements"
-thresh_dBFS, // this is the value to be added
above_thresh_dB_block->data, // this is the output
audio_level_dB_block->length);
// scale by the compression ratio...this is what the output level should be (this is our target level)
arm_scale_f32(above_thresh_dB_block->data, // CMSIS DSP for "multiply all elements by a constant value"
1.0f / comp_ratio, // this is the value to be multiplied
inst_targ_gain_dB_block->data, // this is the output
above_thresh_dB_block->length);
// compute the instantaneous gain...which is the difference between the target level and the original level
arm_sub_f32(inst_targ_gain_dB_block->data, // CMSIS DSP for "subtract two vectors element-by-element"
above_thresh_dB_block->data, // this is the vector to be subtracted
inst_targ_gain_dB_block->data, // this is the output
inst_targ_gain_dB_block->length);
// limit the target gain to attenuation only (this part of the compressor should not make things louder!)
for (int i = 0; i < inst_targ_gain_dB_block->length; i++)
{
if (inst_targ_gain_dB_block->data[i] > 0.0f)
inst_targ_gain_dB_block->data[i] = 0.0f;
}
// release memory before returning
AudioStream_F32::release(above_thresh_dB_block);
return; // output is passed through inst_targ_gain_dB_block
}
// this method applies the "attack" and "release" constants to smooth the
// target gain level through time.
void calcSmoothedGain_dB(audio_block_f32_t *inst_targ_gain_dB_block, audio_block_f32_t *gain_dB_block)
{
float32_t gain_dB;
float32_t one_minus_attack_const = 1.0f - attack_const;
float32_t one_minus_release_const = 1.0f - release_const;
for (int i = 0; i < inst_targ_gain_dB_block->length; i++)
{
gain_dB = inst_targ_gain_dB_block->data[i];
// smooth the gain using the attack or release constants
if (gain_dB < prev_gain_dB)
{ // are we in the attack phase?
gain_dB_block->data[i] = attack_const * prev_gain_dB + one_minus_attack_const * gain_dB;
}
else
{ // or, we're in the release phase
gain_dB_block->data[i] = release_const * prev_gain_dB + one_minus_release_const * gain_dB;
}
// save value for the next time through this loop
prev_gain_dB = gain_dB_block->data[i];
}
// return
return; // the output here is gain_block
}
// methods to set parameters of this module
void resetStates(void)
{
prev_level_lp_pow = 1.0f;
prev_gain_dB = 0.0f;
// initialize the HP filter. (This also resets the filter states,)
arm_biquad_cascade_df1_init_f32(&hp_filt_structL, hp_nstages, hp_coeff, hp_stateL);
arm_biquad_cascade_df1_init_f32(&hp_filt_structR, hp_nstages, hp_coeff, hp_stateR);
}
void setPreGain(float g) { pre_gain = g; }
void setPreGain_dB(float gain_dB) { setPreGain(pow(10.0f, gain_dB / 20.0f)); }
void setPostGain(float g) { post_gain = g; }
void setPostGain_dB(float gain_dB) { setPostGain(pow(10.0f, gain_dB / 20.0f)); }
void setCompressionRatio(float cr)
{
comp_ratio = max(0.001f, cr); // limit to positive values
updateThresholdAndCompRatioConstants();
}
void setAttack_sec(float a)
{
attack_sec = a;
attack_const = expf(-1.0f / (attack_sec * fs_Hz)); // expf() is much faster than exp()
// also update the time constant for the envelope extraction
setLevelTimeConst_sec(min(attack_sec, release_sec) / 5.0f); // make the level time-constant one-fifth the gain time constants
}
void setRelease_sec(float r)
{
release_sec = r;
release_const = expf(-1.0f / (release_sec * fs_Hz)); // expf() is much faster than exp()
// also update the time constant for the envelope extraction
setLevelTimeConst_sec(min(attack_sec, release_sec) / 5.0f); // make the level time-constant one-fifth the gain time constants
}
void setLevelTimeConst_sec(float t_sec)
{
const float min_t_sec = 0.002f; // this is the minimum allowed value
level_lp_sec = max(min_t_sec, t_sec);
level_lp_const = expf(-1.0f / (level_lp_sec * fs_Hz)); // expf() is much faster than exp()
}
void setThresh_dBFS(float val)
{
thresh_dBFS = val;
setThreshPow(pow(10.0f, thresh_dBFS / 10.0f));
}
void enableHPFilter(boolean flag) { use_HP_prefilter = flag; };
// methods to return information about this module
float32_t getPreGain_dB(void) { return 20.0 * log10f_approx(pre_gain); }
float32_t getAttack_sec(void) { return attack_sec; }
float32_t getRelease_sec(void) { return release_sec; }
float32_t getLevelTimeConst_sec(void) { return level_lp_sec; }
float32_t getThresh_dBFS(void) { return thresh_dBFS; }
float32_t getCompressionRatio(void) { return comp_ratio; }
float32_t getCurrentLevel_dBFS(void) { return 10.0 * log10f_approx(prev_level_lp_pow); }
float32_t getCurrentGain_dB(void) { return prev_gain_dB; }
void setHPFilterCoeff_N2IIR_Matlab(float32_t b[], float32_t a[])
{
// https://www.keil.com/pack/doc/CMSIS/DSP/html/group__BiquadCascadeDF1.html#ga8e73b69a788e681a61bccc8959d823c5
// Use matlab to compute the coeff for HP at 20Hz: [b,a]=butter(2,20/(44100/2),'high'); %assumes fs_Hz = 44100
hp_coeff[0] = b[0];
hp_coeff[1] = b[1];
hp_coeff[2] = b[2]; // here are the matlab "b" coefficients
hp_coeff[3] = -a[1];
hp_coeff[4] = -a[2]; // the DSP needs the "a" terms to have opposite sign vs Matlab
}
bool bypass_get(void) {return bp;}
void bypass_set(bool state) {bp = state;}
bool bypass_tgl(void)
{
bp ^= 1;
return bp;
}
void setSideChainMode(sideChainMode_t newMode) {sidechainMode = newMode;}
private:
// state-related variables
audio_block_f32_t *inputQueueArray_f32[2]; // memory pointer for the input to this module
float32_t prev_level_lp_pow = 1.0f;
float32_t prev_gain_dB = 0.0f; // last gain^2 used
float32_t fs_Hz = AUDIO_SAMPLE_RATE_EXACT;
bool bp = true; // bypass flag
sideChainMode_t sidechainMode = COMP_SIDECHAIN_SRC_LRSUM;
// HP filter state-related variables
arm_biquad_casd_df1_inst_f32 hp_filt_structL;
arm_biquad_casd_df1_inst_f32 hp_filt_structR;
static const uint8_t hp_nstages = 1;
float32_t hp_coeff[5 * hp_nstages] = {1.0f, 0.0f, 0.0f, 0.0f, 0.0f}; // no filtering. actual filter coeff set later
float32_t hp_stateL[4 * hp_nstages];
float32_t hp_stateR[4 * hp_nstages];
void setHPFilterCoeff(void)
{
// https://www.keil.com/pack/doc/CMSIS/DSP/html/group__BiquadCascadeDF1.html#ga8e73b69a788e681a61bccc8959d823c5
// Use matlab to compute the coeff for HP at 20Hz: [b,a]=butter(2,20/(44100/2),'high'); %assumes fs_Hz = 44100
const float32_t b[] = {9.979871156751189e-01, -1.995974231350238e+00, 9.979871156751189e-01}; // from Matlab
const float32_t a[] = {1.000000000000000e+00, -1.995970179642828e+00, 9.959782830576472e-01}; // from Matlab
setHPFilterCoeff_N2IIR_Matlab((float32_t *)b, (float32_t *)a);
}
// private parameters related to gain calculation
float32_t attack_const, release_const, level_lp_const; // used in calcGain(). set by setAttack_sec() and setRelease_sec();
float32_t comp_ratio_const, thresh_pow_FS_wCR; // used in calcGain(); set in updateThresholdAndCompRatioConstants()
void updateThresholdAndCompRatioConstants(void)
{
comp_ratio_const = 1.0f - (1.0f / comp_ratio);
thresh_pow_FS_wCR = powf(thresh_pow_FS, comp_ratio_const);
}
// settings
float32_t attack_sec = 0.002f, release_sec = 0.2f, level_lp_sec;
float32_t thresh_dBFS = 0.0f; // threshold for compression, relative to digital full scale
float32_t thresh_pow_FS = 1.0f; // same as above, but not in dB
void setThreshPow(float t_pow)
{
thresh_pow_FS = t_pow;
updateThresholdAndCompRatioConstants();
}
float32_t comp_ratio = 1.0f; // compression ratio
float32_t pre_gain = -1.0f; // gain to apply before the compression. negative value disables
float32_t post_gain = -1.0f;
boolean use_HP_prefilter;
// Accelerate the powf(10.0,x) function
static float32_t pow10f(float x)
{
// return powf(10.0f,x) //standard, but slower
return expf(2.302585092994f * x); // faster: exp(log(10.0f)*x)
}
// Accelerate the log10f(x) function?
static float32_t log10f_approx(float x)
{
// return log10f(x); //standard, but slower
return log2f_approx(x) * 0.3010299956639812f; // faster: log2(x)/log2(10)
}
/* ----------------------------------------------------------------------
** Fast approximation to the log2() function. It uses a two step
** process. First, it decomposes the floating-point number into
** a fractional component F and an exponent E. The fraction component
** is used in a polynomial approximation and then the exponent added
** to the result. A 3rd order polynomial is used and the result
** when computing db20() is accurate to 7.984884e-003 dB.
** ------------------------------------------------------------------- */
// https://community.arm.com/tools/f/discussions/4292/cmsis-dsp-new-functionality-proposal/22621#22621
// float log2f_approx_coeff[4] = {1.23149591368684f, -4.11852516267426f, 6.02197014179219f, -3.13396450166353f};
static float log2f_approx(float X)
{
// float *C = &log2f_approx_coeff[0];
float Y;
float F;
int E;
// This is the approximation to log2()
F = frexpf(fabsf(X), &E);
// Y = C[0]*F*F*F + C[1]*F*F + C[2]*F + C[3] + E;
// Y = *C++;
Y = 1.23149591368684f;
Y *= F;
// Y += (*C++);
Y += -4.11852516267426f;
Y *= F;
// Y += (*C++);
Y += 6.02197014179219f;
Y *= F;
// Y += (*C++);
Y += -3.13396450166353f;
Y += E;
return (Y);
}
};
#endif

@ -23,17 +23,28 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
*/ */
#include "effect_delaystereo.h" #include "effect_delaystereo_F32.h"
#define TREBLE_LOSS_FREQ (0.20f) #define TREBLE_LOSS_FREQ (0.20f)
#define BASS_LOSS_FREQ (0.05f) #define BASS_LOSS_FREQ (0.05f)
#define BASS_FREQ (0.15f) #define BASS_FREQ (0.15f)
extern uint8_t external_psram_size;
AudioEffectDelayStereo_F32::AudioEffectDelayStereo_F32(uint32_t dly_range_ms, bool use_psram) : AudioStream_F32(2, inputQueueArray) AudioEffectDelayStereo_F32::AudioEffectDelayStereo_F32(uint32_t dly_range_ms, bool use_psram) : AudioStream_F32(2, inputQueueArray)
{ {
psram_mode = use_psram; begin(dly_range_ms, use_psram);
}
void AudioEffectDelayStereo_F32::begin(uint32_t dly_range_ms, bool use_psram)
{
// failsafe if psram is required but not found
// limit the delay time to 500ms (88200 bytes at 44.1kHz)
if (psram_mode && external_psram_size == 0)
{
psram_mode = false;
if (dly_range_ms > 500) dly_range_ms = 500;
}
bool memOk = true; bool memOk = true;
dly_length = ((float32_t)(dly_range_ms+500)/1000.0f) * AUDIO_SAMPLE_RATE_EXACT; dly_length = ((float32_t)(dly_range_ms+500)/1000.0f) * AUDIO_SAMPLE_RATE_EXACT;
if (!dly0a.init(dly_length, use_psram)) memOk = false; if (!dly0a.init(dly_length, use_psram)) memOk = false;
@ -55,11 +66,7 @@ void AudioEffectDelayStereo_F32::update()
if (!initialized) return; if (!initialized) return;
if (!memsetup_done) if (!memsetup_done)
{ {
dly0a.reset(); memsetup_done = memCleanup();
dly0b.reset();
dly1a.reset();
dly1b.reset();
memsetup_done = true;
return; return;
} }
@ -71,41 +78,49 @@ void AudioEffectDelayStereo_F32::update()
if (bp) if (bp)
{ {
if (!cleanup_done) // mem cleanup not required in TRAILS mode
if (!cleanup_done && bp_mode != BYPASS_MODE_TRAILS)
{ {
dly0a.reset(); cleanup_done = memCleanup();
dly0b.reset();
dly1a.reset();
dly1b.reset();
cleanup_done = true;
tap_active = false; // reset tap tempo tap_active = false; // reset tap tempo
tap_counter = 0; tap_counter = 0;
} }
if (dry_gain > 0.0f) // if dry/wet mixer is used if (infinite) freeze(false);
switch(bp_mode)
{ {
blockL = AudioStream_F32::receiveReadOnly_f32(0); case BYPASS_MODE_PASS:
blockR = AudioStream_F32::receiveReadOnly_f32(1); blockL = AudioStream_F32::receiveReadOnly_f32(0);
if (!blockL || !blockR) blockR = AudioStream_F32::receiveReadOnly_f32(1);
{ if (!blockL || !blockR)
if (blockL) AudioStream_F32::release(blockL); {
if (blockR) AudioStream_F32::release(blockR); if (blockL) AudioStream_F32::release(blockL);
if (blockR) AudioStream_F32::release(blockR);
return;
}
AudioStream_F32::transmit(blockL, 0);
AudioStream_F32::transmit(blockR, 1);
AudioStream_F32::release(blockL);
AudioStream_F32::release(blockR);
return; return;
} break;
AudioStream_F32::transmit(blockL, 0); case BYPASS_MODE_OFF:
AudioStream_F32::transmit(blockR, 1); blockL = AudioStream_F32::allocate_f32();
AudioStream_F32::release(blockL); if (!blockL) return;
AudioStream_F32::release(blockR); memset(&blockL->data[0], 0, blockL->length*sizeof(float32_t));
return; AudioStream_F32::transmit(blockL, 0);
AudioStream_F32::transmit(blockL, 1);
AudioStream_F32::release(blockL);
return;
break;
case BYPASS_MODE_TRAILS:
inputGainSet = 0.0f;
tap_active = false; // reset tap tempo
tap_counter = 0;
break;
default:
break;
} }
blockL = AudioStream_F32::allocate_f32();
if (!blockL) return;
memset(&blockL->data[0], 0, blockL->length*sizeof(float32_t));
AudioStream_F32::transmit(blockL, 0);
AudioStream_F32::transmit(blockL, 1);
AudioStream_F32::release(blockL);
return;
} }
cleanup_done = false;
blockL = AudioStream_F32::receiveWritable_f32(0); blockL = AudioStream_F32::receiveWritable_f32(0);
blockR = AudioStream_F32::receiveWritable_f32(1); blockR = AudioStream_F32::receiveWritable_f32(1);
if (!blockL || !blockR) if (!blockL || !blockR)
@ -116,8 +131,11 @@ void AudioEffectDelayStereo_F32::update()
AudioStream_F32::release(blockR); AudioStream_F32::release(blockR);
return; return;
} }
cleanup_done = false;
for (i=0; i < blockL->length; i++) for (i=0; i < blockL->length; i++)
{ {
inputGain += (inputGainSet - inputGain) * 0.25f;
// tap tempo // tap tempo
if (tap_active) if (tap_active)
{ {
@ -138,7 +156,7 @@ void AudioEffectDelayStereo_F32::update()
dly_time -= dly_time_step; dly_time -= dly_time_step;
if (dly_time < dly_time_set) dly_time = dly_time_set; if (dly_time < dly_time_set) dly_time = dly_time_set;
} }
// lowpass the dely time // lowpass the delay time
acc1 = dly_time - dly_time_flt; acc1 = dly_time - dly_time_flt;
dly_time_flt += acc1 * 0.1f; dly_time_flt += acc1 * 0.1f;
dly_time = dly_time_flt; dly_time = dly_time_flt;
@ -165,12 +183,10 @@ void AudioEffectDelayStereo_F32::update()
acc2 = (float32_t)dly_length - 1.0f - (dly_time + mod_fr[3]); acc2 = (float32_t)dly_length - 1.0f - (dly_time + mod_fr[3]);
if (acc2 < 0.0f) mod_fr[3] += acc2; if (acc2 < 0.0f) mod_fr[3] += acc2;
float32_t idx = dly_time + mod_fr[0];
acc1 = dly0b.getTapHermite(dly_time+mod_fr[0]); acc1 = dly0b.getTapHermite(dly_time+mod_fr[0]);
outR = acc1 * 0.6f; outR = acc1 * 0.6f;
acc1 = flt0R.process(acc1) * feedb; acc1 = flt0R.process(acc1) * feedb;
acc1 += blockR->data[i] * input_attn; acc1 += blockR->data[i] * inputGain;
acc1 = flt1R.process(acc1); acc1 = flt1R.process(acc1);
acc2 = dly0a.getTapHermite(dly_time+mod_fr[1]); acc2 = dly0a.getTapHermite(dly_time+mod_fr[1]);
dly0b.write_toOffset(acc2, 0); dly0b.write_toOffset(acc2, 0);
@ -180,7 +196,7 @@ void AudioEffectDelayStereo_F32::update()
acc1 = dly1b.getTapHermite(dly_time+mod_fr[2]); acc1 = dly1b.getTapHermite(dly_time+mod_fr[2]);
outR += acc1 * 0.6f; outR += acc1 * 0.6f;
acc1 = flt0L.process(acc1) * feedb; acc1 = flt0L.process(acc1) * feedb;
acc1 += blockL->data[i] * input_attn; acc1 += blockL->data[i] * inputGain;
acc1 = flt1L.process(acc1); acc1 = flt1L.process(acc1);
acc2 = dly1a.getTapHermite(dly_time+mod_fr[3]); acc2 = dly1a.getTapHermite(dly_time+mod_fr[3]);
dly1b.write_toOffset(acc2, 0); dly1b.write_toOffset(acc2, 0);
@ -199,4 +215,116 @@ void AudioEffectDelayStereo_F32::update()
AudioStream_F32::transmit(blockR, 1); AudioStream_F32::transmit(blockR, 1);
AudioStream_F32::release(blockL); AudioStream_F32::release(blockL);
AudioStream_F32::release(blockR); AudioStream_F32::release(blockR);
}
void AudioEffectDelayStereo_F32::freeze(bool state)
{
if (infinite == state) return;
infinite = state;
if (state)
{
feedb_tmp = feedb; // store the settings
inputGain_tmp = inputGainSet;
bassCut_k_tmp = bassCut_k;
trebleCut_k_tmp = trebleCut_k;
__disable_irq();
feedb = 1.0f; // infinite echo
inputGainSet = freeze_ingain;
__enable_irq();
}
else
{
__disable_irq();
feedb = feedb_tmp;
inputGainSet = inputGain_tmp;
bassCut_k = bassCut_k_tmp;
trebleCut_k = trebleCut_k_tmp;
__enable_irq();
}
}
/**
* @brief Partial memory clear
* Clearing all the delay buffers at once, esp. if
* the PSRAM is used takes too long for the audio ISR.
* Hence the buffer clear is done in configurable portions
* spread over a few audio update routines.
*
* @return true Memory clean is complete
* @return false Memory clean still in progress
*/
bool AudioEffectDelayStereo_F32::memCleanup()
{
static uint8_t dlyIdx = 0;
bool result = false;
if (dlyIdx == 0) // value 0 is used to reset the addr
{
memCleanupStart = 0;
memCleanupEnd = memCleanupStep;
flt0L.reset();
flt0R.reset();
flt1L.reset();
flt1R.reset();
dlyIdx = 1;
}
if (memCleanupEnd > dly_length) // last segment
{
memCleanupEnd = dly_length;
result = true;
}
switch(dlyIdx)
{
case 1:
dly0a.reset(memCleanupStart, memCleanupEnd);
memCleanupStart = memCleanupEnd;
memCleanupEnd += memCleanupStep;
if (result) // if done, reset the mem addr
{
memCleanupStart = 0;
memCleanupEnd = memCleanupStep;
dlyIdx = 2;
result = false;
}
break;
case 2:
dly0b.reset(memCleanupStart, memCleanupEnd);
memCleanupStart = memCleanupEnd;
memCleanupEnd += memCleanupStep;
if (result) // if done, reset the mem addr
{
memCleanupStart = 0;
memCleanupEnd = memCleanupStep;
dlyIdx = 3;
result = false;
}
break;
case 3:
dly1a.reset(memCleanupStart, memCleanupEnd);
memCleanupStart = memCleanupEnd;
memCleanupEnd += memCleanupStep;
if (result) // if done, reset the mem addr
{
memCleanupStart = 0;
memCleanupEnd = memCleanupStep;
dlyIdx = 4;
result = false;
}
break;
case 4:
dly1b.reset(memCleanupStart, memCleanupEnd);
memCleanupStart = memCleanupEnd;
memCleanupEnd += memCleanupStep;
if (result) // if done, reset the mem addr
{
dlyIdx = 0;
result = true;
}
break;
default:
dlyIdx = 0; // cleanup done, reset the dly line idx
result = false;
break;
}
return result;
} }

@ -40,16 +40,18 @@ public:
~AudioEffectDelayStereo_F32(){}; ~AudioEffectDelayStereo_F32(){};
virtual void update(); virtual void update();
/** /**
* @brief delay time * @brief set the delay time
* *
* @param t scaled to 0.0f-1.0f range * @param t delay time scaled to range 0.0 to 1.0
* @param force bypass the smoothing, immediate change
*/ */
void time(float t) void time(float t, bool force = false)
{ {
t = constrain(t, 0.0f, 1.0f); t = constrain(t, 0.0f, 1.0f);
t = t * t; t = t * t;
t = map(t, 0.0f, 1.0f, (float32_t)(dly_length-dly_time_min), 0.0f); t = map(t, 0.0f, 1.0f, (float32_t)(dly_length-dly_time_min), 0.0f);
__disable_irq(); __disable_irq();
if (force) dly_time = t;
dly_time_set = t; dly_time_set = t;
__enable_irq(); __enable_irq();
} }
@ -73,13 +75,15 @@ public:
*/ */
void feedback(float n) void feedback(float n)
{ {
if (infinite) return;
float32_t fb, attn; float32_t fb, attn;
n = constrain(n, 0.0f, 1.0f); n = constrain(n, 0.0f, 1.0f);
fb = map(n, 0.0f, 1.0f, 0.0f, feedb_max) * hp_feedb_limit; fb = map(n, 0.0f, 1.0f, 0.0f, feedb_max) * hp_feedb_limit;
attn = map(n*n*n, 0.0f, 1.0f, 1.0f, 0.4f); attn = map(n*n*n, 0.0f, 1.0f, 1.0f, 0.4f);
inputGain_tmp = attn;
__disable_irq(); __disable_irq();
feedb = fb; feedb = fb;
input_attn = attn; inputGainSet = attn;
__enable_irq(); __enable_irq();
} }
/** /**
@ -116,7 +120,9 @@ public:
*/ */
void treble_cut(float n) void treble_cut(float n)
{ {
if (infinite) return;
n = 1.0f - constrain(n, 0.0f, 1.0f); n = 1.0f - constrain(n, 0.0f, 1.0f);
trebleCut_k_tmp = n;
__disable_irq(); __disable_irq();
trebleCut_k = n; trebleCut_k = n;
__enable_irq(); __enable_irq();
@ -141,8 +147,10 @@ public:
*/ */
void bass_cut(float n) void bass_cut(float n)
{ {
if (infinite) return;
n = constrain(n, 0.0f, 1.0f); n = constrain(n, 0.0f, 1.0f);
n = 2.0f * n - (n*n); n = 2.0f * n - (n*n);
bassCut_k_tmp = -n;
__disable_irq(); __disable_irq();
bassCut_k = -n; bassCut_k = -n;
__enable_irq(); __enable_irq();
@ -201,15 +209,45 @@ public:
lfo.setDepth(d); lfo.setDepth(d);
__enable_irq(); __enable_irq();
} }
typedef enum
{
BYPASS_MODE_PASS, // pass the input signal to the output
BYPASS_MODE_OFF, // mute the output
BYPASS_MODE_TRAILS // mutes the input only
}bypass_mode_t;
void bypass_setMode(bypass_mode_t m)
{
if (m <= BYPASS_MODE_TRAILS) bp_mode = m;
}
bypass_mode_t bypass_geMode() {return bp_mode;}
bool bypass_get(void) {return bp;} bool bypass_get(void) {return bp;}
void bypass_set(bool state) {bp = state;} void bypass_set(bool state)
{
if (bp == state) return;
bp = state;
if (bp)
{
__disable_irq();
memCleanupStart = 0;
memCleanupEnd = memCleanupStep;
__enable_irq();
freeze(false);
}
else
{
__disable_irq();
inputGainSet = inputGain_tmp;
__enable_irq();
}
}
bool bypass_tgl(void) bool bypass_tgl(void)
{ {
bp ^= 1; bypass_set(bp ^ 1);
return bp; return bp;
} }
void freeze(bool state);
bool freeze_tgl() {freeze(infinite^1); return infinite;}
bool freeze_get() {return infinite;}
uint32_t tap_tempo(bool avg=true) uint32_t tap_tempo(bool avg=true)
{ {
int32_t delta; int32_t delta;
@ -243,7 +281,6 @@ public:
} }
return tempo_ticks; return tempo_ticks;
} }
private: private:
audio_block_f32_t *inputQueueArray[2]; audio_block_f32_t *inputQueueArray[2];
@ -264,15 +301,19 @@ private:
AudioBasicLfo lfo = AudioBasicLfo(0.0f, lfo_ampl); AudioBasicLfo lfo = AudioBasicLfo(0.0f, lfo_ampl);
bool psram_mode; bool psram_mode;
bool memsetup_done = false; bool memsetup_done = false;
bool bp = false; bool bp = true;
bypass_mode_t bp_mode = BYPASS_MODE_TRAILS;
bool cleanup_done = false; bool cleanup_done = false;
bool infinite = false;
bool extInputMode = false; // external input via pointers passed to constructor
static constexpr float32_t feedb_max = 0.96f; static constexpr float32_t feedb_max = 0.96f;
float32_t feedb = 0; float32_t feedb = 0;
float32_t hp_feedb_limit = 1.0f; float32_t hp_feedb_limit = 1.0f;
float32_t wet_gain; float32_t wet_gain;
float32_t dry_gain; float32_t dry_gain;
float32_t input_attn = 1.0f; float32_t inputGainSet = 1.0f;
float32_t inputGain = 1.0f;
float32_t trebleCut_k = 1.0f; float32_t trebleCut_k = 1.0f;
float32_t bassCut_k = 0.0f; float32_t bassCut_k = 0.0f;
float32_t treble_k = 1.0f; float32_t treble_k = 1.0f;
@ -282,11 +323,24 @@ private:
static const uint32_t dly_time_min = 128; static const uint32_t dly_time_min = 128;
bool initialized = false; bool initialized = false;
// freeze variables
float32_t freeze_ingain = 0.00f;
float32_t inputGain_tmp = 1.0f;
float32_t bassCut_k_tmp = 0.0f;
float32_t trebleCut_k_tmp = 1.0f;
float32_t feedb_tmp = 0;
bool tap_active = false; bool tap_active = false;
uint32_t tap_counter = 0; uint32_t tap_counter = 0;
uint32_t tap_counter_last=0, tap_counter_new=0; uint32_t tap_counter_last=0, tap_counter_new=0;
static const uint32_t tap_counter_max = 3000*AUDIO_SAMPLE_RATE; // 3 sec static const uint32_t tap_counter_max = 3000*AUDIO_SAMPLE_RATE; // 3 sec
static const int32_t tap_counter_deltamax = 0.3f*AUDIO_SAMPLE_RATE_EXACT; static const int32_t tap_counter_deltamax = 0.3f*AUDIO_SAMPLE_RATE_EXACT;
bool memCleanup(void);
void begin(uint32_t dly_range_ms, bool use_psram);
const uint32_t memCleanupStep = 2048;
uint32_t memCleanupStart = 0;
uint32_t memCleanupEnd = memCleanupStep;
}; };
#endif // _EFFECT_DELAYSTEREO_H_ #endif // _EFFECT_DELAYSTEREO_H_

@ -1,12 +1,20 @@
/* /**
* AudioEffectGain_F32 * @file effect_gainStereo_F32.h
* * @author Piotr Zapart
* Created: Chip Audette, November 2016 * @brief Stereo volume + pan control
* Purpose; Apply digital gain to the audio data. Assumes floating-point data. * @version 0.1
* * @date 2024-03-20
* This processes a single stream fo audio data (ie, it is mono) *
* * @copyright Copyright (c) 2024 www.hexefx.com
* MIT License. use at your own risk. *
* 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 <https://www.gnu.org/licenses/>."
*/ */
#ifndef _AudioEffectGainStereo_F32_h #ifndef _AudioEffectGainStereo_F32_h
@ -14,14 +22,13 @@
#include <arm_math.h> //ARM DSP extensions. for speed! #include <arm_math.h> //ARM DSP extensions. for speed!
#include <AudioStream_F32.h> #include <AudioStream_F32.h>
#include <basic_components.h>
class AudioEffectGainStereo_F32 : public AudioStream_F32 class AudioEffectGainStereo_F32 : public AudioStream_F32
{ {
public: public:
// constructor AudioEffectGainStereo_F32(void) : AudioStream_F32(2, inputQueueArray_f32) { setPan(0.0f);};
AudioEffectGainStereo_F32(void) : AudioStream_F32(2, inputQueueArray_f32){}; AudioEffectGainStereo_F32(const AudioSettings_F32 &settings) : AudioStream_F32(2, inputQueueArray_f32){setPan(0.0f);};
AudioEffectGainStereo_F32(const AudioSettings_F32 &settings) : AudioStream_F32(2, inputQueueArray_f32){};
void update(void) void update(void)
{ {
audio_block_f32_t *blockL, *blockR; audio_block_f32_t *blockL, *blockR;
@ -35,29 +42,59 @@ public:
AudioStream_F32::release(blockR); AudioStream_F32::release(blockR);
return; return;
} }
arm_scale_f32(blockL->data, gain, blockL->data, blockL->length); // use ARM DSP for speed! gainL += (gainLset - gainL) * 0.25f;
arm_scale_f32(blockR->data, gain, blockR->data, blockR->length); gainR += (gainRset - gainR) * 0.25f;
arm_scale_f32(blockL->data, gainL, blockL->data, blockL->length); // use ARM DSP for speed!
arm_scale_f32(blockR->data, gainR, blockR->data, blockR->length);
AudioStream_F32::transmit(blockL, 0); AudioStream_F32::transmit(blockL, 0);
AudioStream_F32::transmit(blockR, 1); AudioStream_F32::transmit(blockR, 1);
AudioStream_F32::release(blockL); AudioStream_F32::release(blockL);
AudioStream_F32::release(blockR); AudioStream_F32::release(blockR);
} }
void setGain(float g)
{
float32_t gL, gR;
gain = g;
gL = panL * gain;
gR = panR * gain;
__disable_irq();
gainLset = gL;
gainRset = gR;
__enable_irq();
// methods to set parameters of this module }
void setGain(float g) { gain = g; }
void setGain_dB(float gain_dB) void setGain_dB(float gain_dB)
{ {
float gain = pow(10.0, gain_dB / 20.0); float gain = powf(10.0f, gain_dB / 20.0f);
setGain(gain); setGain(gain);
} }
// methods to return information about this module
float getGain(void) { return gain; } float getGain(void) { return gain; }
float getGain_dB(void) { return 20.0 * log10(gain); } float getGain_dB(void) { return 20.0 * log10(gain); }
void setPan(float32_t p)
{
float32_t tmp, gL, gR;
pan = constrain(p, -1.0f, 1.0f);
tmp = (pan + 1.0f) * 0.5f; // map to 0..1
mix_pwr(tmp, &panR, &panL);
gL = panL * gain;
gR = panR * gain;
__disable_irq();
gainLset = gL;
gainRset = gR;
__enable_irq();
}
float32_t getPan() { return pan;}
private: private:
audio_block_f32_t *inputQueueArray_f32[2]; // memory pointer for the input to this module audio_block_f32_t *inputQueueArray_f32[2]; // memory pointer for the input to this module
float gain = 1.0f; // default value float32_t gain = 1.0f; // default value
float32_t gainL, gainR, gainLset, gainRset;
float32_t pan, panL, panR;
}; };
#endif #endif

@ -0,0 +1,339 @@
/**
* @file effect_guitarBooster_F32.cpp
* @author Piotr Zapart
* @brief Oversampled Waveshaper based overdrive effect
* Stereo IO and bypass, the processing is mono
* @version 0.1
* @date 2024-03-20
*
* @copyright Copyright (c) 2024
*
*/
#include "effect_guitarBooster_F32.h"
void AudioEffectGuitarBooster_F32::update()
{
audio_block_f32_t *blockL, *blockR;
uint16_t i;
float32_t sampleWet, sampleDry;
float32_t *samplePtr;
float32_t _hpPre1_reg;// = hpPre1_reg;
float32_t _hpPre2_reg;// = hpPre2_reg;
float32_t _lp1_reg;
float32_t _lp2_reg;
float32_t _hpPost_reg;
float32_t _hpPre1_k = hpPre1_k;
float32_t _hpPre2_k = hpPre2_k;
float32_t _lp1_k = lp1_k;
float32_t _lp2_k = lp2_k;
float32_t _hpPost_k = hpPost_k;
float32_t _gainSet = gainSet;
float32_t _gain_hp = gain_hp;
float32_t _gain = gain;
float32_t _levelSet = levelSet;
float32_t _level = level;
const uint32_t blockLenInterpolated = upsample_k * AUDIO_BLOCK_SAMPLES;
if (bp) // handle bypass
{
blockL = AudioStream_F32::receiveReadOnly_f32(0);
blockR = AudioStream_F32::receiveReadOnly_f32(1);
if (!blockL || !blockR)
{
if (blockL)
AudioStream_F32::release(blockL);
if (blockR)
AudioStream_F32::release(blockR);
return;
}
AudioStream_F32::transmit(blockL, 0);
AudioStream_F32::transmit(blockR, 1);
AudioStream_F32::release(blockL);
AudioStream_F32::release(blockR);
return;
}
blockL = AudioStream_F32::receiveWritable_f32(0);
blockR = AudioStream_F32::receiveWritable_f32(1);
if (!blockL || !blockR)
{
if (blockL)
AudioStream_F32::release(blockL);
if (blockR)
AudioStream_F32::release(blockR);
return;
}
_hpPre1_reg = hpPre1_reg;
_hpPre2_reg = hpPre2_reg;
_lp1_reg = lp1_reg;
_lp2_reg = lp2_reg;
_hpPost_reg = hpPost_reg;
arm_add_f32(blockL->data, blockR->data, blockL->data, blockL->length); // add two channels
arm_fir_interpolate_f32(&interpolator, blockL->data, blockInterpolated, blockL->length);
samplePtr = blockInterpolated;
for (i = 0; i < blockLenInterpolated; i++)
{
sampleWet = *samplePtr;
sampleDry = sampleWet;
_gain += (_gainSet - _gain) * 0.25f;
// octave up
if (octave) sampleWet = 2.0f * fabsf(sampleWet) - 1.0f;
// input high pass
sampleWet -= (_hpPre1_reg += (sampleWet - _hpPre1_reg) * _hpPre1_k);
sampleWet -= (_hpPre2_reg += (sampleWet - _hpPre2_reg) * _hpPre2_k);
sampleWet *= _gain * _gain_hp;
// waveshaper
sampleWet = arm_linear_interp_f32(&waveshaper, sampleWet + DCbias) * -1.0f;
// lowpass
sampleWet = (_lp1_reg += (sampleWet - _lp1_reg) * _lp1_k);
sampleWet = (_lp2_reg += (sampleWet - _lp2_reg) * _lp2_k);
// output highpass
sampleWet -= (_hpPost_reg += (sampleWet - _hpPost_reg) * _hpPost_k);
_level += (_levelSet - _level) * 0.25f;
*samplePtr++ = (sampleWet * wetGain + sampleDry * dryGain) * level;
}
arm_fir_decimate_f32(&decimator, blockInterpolated, blockL->data, blockLenInterpolated);
hpPre1_reg = _hpPre1_reg;
hpPre2_reg = _hpPre2_reg;
lp1_reg = _lp1_reg;
lp2_reg = _lp2_reg;
hpPost_reg = _hpPost_reg;
gain = _gain;
level = _level;
AudioStream_F32::transmit(blockL, 0); // send blockL on both output channels
AudioStream_F32::transmit(blockL, 1);
AudioStream_F32::release(blockL);
AudioStream_F32::release(blockR);
}
void AudioEffectGuitarBooster_F32::bottom(float32_t b)
{
b = constrain(b, 0.0f, 1.0f);
gain_hp = 1.0f + b * 2.0f;
float32_t hp = map(b, 0.0f, 1.0f, GBOOST_BOTTOM_MAXF, GBOOST_BOTTOM_MINF);
hp = omega(hp);
__disable_irq();
hpPre1_k = hp;
hpPre2_k = hpPre1_k;
__enable_irq();
}
void AudioEffectGuitarBooster_F32::tone(float32_t t)
{
t = constrain(t, 0.0f, 1.0f);
t = t * t;
float32_t lp = map(t, 0.0f, 1.0f, GBOOST_TONE_MINF, GBOOST_TONE_MAXF);
lp = omega(lp);
__disable_irq();
lp1_k = lp;
__enable_irq();
}
float32_t AudioEffectGuitarBooster_F32::driveWaveform[2001]=
{
-0.34169694, -0.34162512, -0.34155323, -0.34148127, -0.34140924, -0.34133714, -0.34126496, -0.34119272, -0.34112041, -0.34104802,
-0.34097557, -0.34090304, -0.34083044, -0.34075777, -0.34068503, -0.34061222, -0.34053933, -0.34046637, -0.34039334, -0.34032024,
-0.34024707, -0.34017382, -0.34010050, -0.34002711, -0.33995364, -0.33988010, -0.33980649, -0.33973281, -0.33965905, -0.33958521,
-0.33951131, -0.33943733, -0.33936327, -0.33928914, -0.33921494, -0.33914066, -0.33906631, -0.33899188, -0.33891738, -0.33884280,
-0.33876814, -0.33869341, -0.33861861, -0.33854373, -0.33846877, -0.33839374, -0.33831863, -0.33824344, -0.33816818, -0.33809284,
-0.33801743, -0.33794193, -0.33786636, -0.33779072, -0.33771499, -0.33763919, -0.33756331, -0.33748735, -0.33741131, -0.33733520,
-0.33725901, -0.33718273, -0.33710638, -0.33702995, -0.33695345, -0.33687686, -0.33680019, -0.33672344, -0.33664662, -0.33656971,
-0.33649273, -0.33641566, -0.33633851, -0.33626128, -0.33618398, -0.33610659, -0.33602912, -0.33595157, -0.33587393, -0.33579622,
-0.33571842, -0.33564054, -0.33556258, -0.33548454, -0.33540642, -0.33532821, -0.33524992, -0.33517155, -0.33509309, -0.33501455,
-0.33493593, -0.33485722, -0.33477843, -0.33469956, -0.33462060, -0.33454156, -0.33446243, -0.33438322, -0.33430393, -0.33422454,
-0.33414508, -0.33406553, -0.33398589, -0.33390617, -0.33382636, -0.33374646, -0.33366648, -0.33358641, -0.33350626, -0.33342602,
-0.33334569, -0.33326527, -0.33318477, -0.33310418, -0.33302350, -0.33294274, -0.33286188, -0.33278094, -0.33269991, -0.33261879,
-0.33253758, -0.33245628, -0.33237490, -0.33229342, -0.33221185, -0.33213020, -0.33204845, -0.33196662, -0.33188469, -0.33180267,
-0.33172056, -0.33163836, -0.33155607, -0.33147369, -0.33139122, -0.33130865, -0.33122600, -0.33114325, -0.33106041, -0.33097747,
-0.33089444, -0.33081132, -0.33072811, -0.33064480, -0.33056140, -0.33047791, -0.33039432, -0.33031064, -0.33022686, -0.33014299,
-0.33005902, -0.32997496, -0.32989080, -0.32980655, -0.32972220, -0.32963776, -0.32955322, -0.32946858, -0.32938385, -0.32929901,
-0.32921409, -0.32912906, -0.32904394, -0.32895872, -0.32887340, -0.32878799, -0.32870247, -0.32861686, -0.32853115, -0.32844534,
-0.32835943, -0.32827342, -0.32818731, -0.32810110, -0.32801479, -0.32792838, -0.32784187, -0.32775526, -0.32766855, -0.32758174,
-0.32749482, -0.32740781, -0.32732069, -0.32723347, -0.32714614, -0.32705872, -0.32697119, -0.32688356, -0.32679582, -0.32670798,
-0.32662004, -0.32653199, -0.32644384, -0.32635558, -0.32626722, -0.32617876, -0.32609019, -0.32600151, -0.32591273, -0.32582384,
-0.32573484, -0.32564574, -0.32555653, -0.32546722, -0.32537779, -0.32528826, -0.32519862, -0.32510888, -0.32501902, -0.32492906,
-0.32483899, -0.32474880, -0.32465851, -0.32456811, -0.32447760, -0.32438698, -0.32429625, -0.32420541, -0.32411446, -0.32402339,
-0.32393222, -0.32384093, -0.32374953, -0.32365802, -0.32356639, -0.32347466, -0.32338281, -0.32329084, -0.32319877, -0.32310657,
-0.32301427, -0.32292185, -0.32282932, -0.32273667, -0.32264390, -0.32255102, -0.32245802, -0.32236491, -0.32227168, -0.32217834,
-0.32208487, -0.32199129, -0.32189760, -0.32180378, -0.32170985, -0.32161579, -0.32152162, -0.32142733, -0.32133292, -0.32123839,
-0.32114374, -0.32104898, -0.32095409, -0.32085907, -0.32076394, -0.32066869, -0.32057331, -0.32047782, -0.32038220, -0.32028646,
-0.32019059, -0.32009460, -0.31999849, -0.31990225, -0.31980589, -0.31970941, -0.31961280, -0.31951606, -0.31941920, -0.31932222,
-0.31922511, -0.31912787, -0.31903050, -0.31893301, -0.31883539, -0.31873764, -0.31863977, -0.31854176, -0.31844363, -0.31834537,
-0.31824698, -0.31814845, -0.31804980, -0.31795102, -0.31785211, -0.31775307, -0.31765389, -0.31755458, -0.31745515, -0.31735558,
-0.31725587, -0.31715603, -0.31705606, -0.31695596, -0.31685572, -0.31675535, -0.31665484, -0.31655420, -0.31645342, -0.31635250,
-0.31625145, -0.31615026, -0.31604894, -0.31594748, -0.31584588, -0.31574414, -0.31564226, -0.31554025, -0.31543809, -0.31533580,
-0.31523337, -0.31513079, -0.31502808, -0.31492522, -0.31482222, -0.31471908, -0.31461580, -0.31451237, -0.31440881, -0.31430510,
-0.31420124, -0.31409724, -0.31399310, -0.31388881, -0.31378438, -0.31367980, -0.31357507, -0.31347020, -0.31336518, -0.31326001,
-0.31315470, -0.31304923, -0.31294362, -0.31283786, -0.31273195, -0.31262589, -0.31251968, -0.31241332, -0.31230681, -0.31220015,
-0.31209333, -0.31198637, -0.31187925, -0.31177197, -0.31166455, -0.31155697, -0.31144923, -0.31134134, -0.31123330, -0.31112509,
-0.31101674, -0.31090822, -0.31079955, -0.31069072, -0.31058174, -0.31047259, -0.31036329, -0.31025382, -0.31014420, -0.31003442,
-0.30992447, -0.30981437, -0.30970410, -0.30959367, -0.30948308, -0.30937233, -0.30926141, -0.30915033, -0.30903908, -0.30892767,
-0.30881609, -0.30870435, -0.30859244, -0.30848036, -0.30836812, -0.30825571, -0.30814313, -0.30803038, -0.30791746, -0.30780438,
-0.30769112, -0.30757769, -0.30746409, -0.30735032, -0.30723638, -0.30712226, -0.30700797, -0.30689351, -0.30677887, -0.30666406,
-0.30654907, -0.30643391, -0.30631857, -0.30620305, -0.30608735, -0.30597148, -0.30585543, -0.30573920, -0.30562279, -0.30550620,
-0.30538943, -0.30527247, -0.30515534, -0.30503802, -0.30492052, -0.30480284, -0.30468497, -0.30456692, -0.30444869, -0.30433026,
-0.30421165, -0.30409286, -0.30397388, -0.30385470, -0.30373534, -0.30361580, -0.30349606, -0.30337613, -0.30325601, -0.30313570,
-0.30301519, -0.30289450, -0.30277361, -0.30265252, -0.30253124, -0.30240977, -0.30228810, -0.30216623, -0.30204417, -0.30192191,
-0.30179945, -0.30167680, -0.30155394, -0.30143088, -0.30130762, -0.30118416, -0.30106050, -0.30093664, -0.30081257, -0.30068830,
-0.30056382, -0.30043914, -0.30031426, -0.30018916, -0.30006386, -0.29993835, -0.29981263, -0.29968671, -0.29956057, -0.29943422,
-0.29930766, -0.29918089, -0.29905391, -0.29892671, -0.29879930, -0.29867167, -0.29854383, -0.29841577, -0.29828749, -0.29815900,
-0.29803029, -0.29790136, -0.29777221, -0.29764284, -0.29751325, -0.29738343, -0.29725339, -0.29712313, -0.29699265, -0.29686194,
-0.29673100, -0.29659984, -0.29646845, -0.29633683, -0.29620498, -0.29607291, -0.29594060, -0.29580806, -0.29567529, -0.29554229,
-0.29540905, -0.29527558, -0.29514188, -0.29500793, -0.29487376, -0.29473934, -0.29460468, -0.29446979, -0.29433466, -0.29419928,
-0.29406366, -0.29392780, -0.29379170, -0.29365535, -0.29351876, -0.29338192, -0.29324484, -0.29310751, -0.29296992, -0.29283209,
-0.29269401, -0.29255568, -0.29241709, -0.29227826, -0.29213917, -0.29199982, -0.29186022, -0.29172036, -0.29158024, -0.29143987,
-0.29129923, -0.29115834, -0.29101718, -0.29087577, -0.29073408, -0.29059214, -0.29044993, -0.29030745, -0.29016471, -0.29002170,
-0.28987842, -0.28973487, -0.28959105, -0.28944695, -0.28930259, -0.28915795, -0.28901303, -0.28886784, -0.28872237, -0.28857663,
-0.28843060, -0.28828430, -0.28813771, -0.28799085, -0.28784369, -0.28769626, -0.28754854, -0.28740053, -0.28725224, -0.28710365,
-0.28695478, -0.28680562, -0.28665616, -0.28650641, -0.28635637, -0.28620603, -0.28605540, -0.28590447, -0.28575324, -0.28560171,
-0.28544988, -0.28529774, -0.28514531, -0.28499257, -0.28483952, -0.28468617, -0.28453251, -0.28437854, -0.28422426, -0.28406966,
-0.28391476, -0.28375954, -0.28360401, -0.28344816, -0.28329199, -0.28313550, -0.28297869, -0.28282157, -0.28266411, -0.28250634,
-0.28234824, -0.28218981, -0.28203105, -0.28187197, -0.28171255, -0.28155280, -0.28139272, -0.28123231, -0.28107155, -0.28091046,
-0.28074904, -0.28058727, -0.28042516, -0.28026271, -0.28009991, -0.27993677, -0.27977328, -0.27960944, -0.27944525, -0.27928071,
-0.27911582, -0.27895057, -0.27878497, -0.27861901, -0.27845269, -0.27828601, -0.27811896, -0.27795156, -0.27778379, -0.27761565,
-0.27744715, -0.27727828, -0.27710903, -0.27693941, -0.27676942, -0.27659905, -0.27642831, -0.27625718, -0.27608568, -0.27591379,
-0.27574152, -0.27556886, -0.27539582, -0.27522238, -0.27504856, -0.27487434, -0.27469973, -0.27452472, -0.27434932, -0.27417351,
-0.27399731, -0.27382070, -0.27364369, -0.27346627, -0.27328844, -0.27311020, -0.27293156, -0.27275249, -0.27257301, -0.27239312,
-0.27221280, -0.27203207, -0.27185091, -0.27166932, -0.27148731, -0.27130487, -0.27112200, -0.27093870, -0.27075496, -0.27057078,
-0.27038617, -0.27020111, -0.27001562, -0.26982967, -0.26964328, -0.26945644, -0.26926916, -0.26908141, -0.26889321, -0.26870456,
-0.26851544, -0.26832587, -0.26813583, -0.26794532, -0.26775435, -0.26756290, -0.26737098, -0.26717859, -0.26698572, -0.26679237,
-0.26659854, -0.26640423, -0.26620942, -0.26601413, -0.26581835, -0.26562208, -0.26542530, -0.26522804, -0.26503027, -0.26483199,
-0.26463321, -0.26443393, -0.26423413, -0.26403382, -0.26383299, -0.26363165, -0.26342978, -0.26322739, -0.26302448, -0.26282103,
-0.26261706, -0.26241255, -0.26220750, -0.26200192, -0.26179579, -0.26158912, -0.26138190, -0.26117413, -0.26096581, -0.26075693,
-0.26054749, -0.26033749, -0.26012693, -0.25991579, -0.25970409, -0.25949182, -0.25927896, -0.25906553, -0.25885152, -0.25863692,
-0.25842173, -0.25820595, -0.25798958, -0.25777261, -0.25755503, -0.25733686, -0.25711807, -0.25689868, -0.25667867, -0.25645804,
-0.25623679, -0.25601492, -0.25579242, -0.25556929, -0.25534553, -0.25512113, -0.25489608, -0.25467040, -0.25444406, -0.25421708,
-0.25398944, -0.25376113, -0.25353217, -0.25330254, -0.25307224, -0.25284127, -0.25260962, -0.25237729, -0.25214427, -0.25191057,
-0.25167617, -0.25144107, -0.25120528, -0.25096878, -0.25073157, -0.25049365, -0.25025501, -0.25001565, -0.24977557, -0.24953476,
-0.24929321, -0.24905092, -0.24880790, -0.24856412, -0.24831960, -0.24807432, -0.24782828, -0.24758148, -0.24733391, -0.24708557,
-0.24683644, -0.24658654, -0.24633585, -0.24608436, -0.24583208, -0.24557900, -0.24532512, -0.24507042, -0.24481490, -0.24455857,
-0.24430141, -0.24404341, -0.24378458, -0.24352491, -0.24326440, -0.24300303, -0.24274080, -0.24247772, -0.24221376, -0.24194893,
-0.24168323, -0.24141663, -0.24114915, -0.24088078, -0.24061150, -0.24034131, -0.24007022, -0.23979820, -0.23952526, -0.23925139,
-0.23897658, -0.23870083, -0.23842414, -0.23814648, -0.23786787, -0.23758829, -0.23730773, -0.23702620, -0.23674368, -0.23646017,
-0.23617565, -0.23589013, -0.23560360, -0.23531605, -0.23502747, -0.23473786, -0.23444720, -0.23415550, -0.23386275, -0.23356893,
-0.23327404, -0.23297808, -0.23268103, -0.23238289, -0.23208366, -0.23178331, -0.23148186, -0.23117928, -0.23087557, -0.23057072,
-0.23026472, -0.22995758, -0.22964927, -0.22933978, -0.22902913, -0.22871728, -0.22840424, -0.22808999, -0.22777453, -0.22745785,
-0.22713994, -0.22682078, -0.22650038, -0.22617872, -0.22585580, -0.22553160, -0.22520611, -0.22487932, -0.22455123, -0.22422183,
-0.22389110, -0.22355904, -0.22322563, -0.22289087, -0.22255474, -0.22221723, -0.22187834, -0.22153805, -0.22119636, -0.22085325,
-0.22050870, -0.22016272, -0.21981528, -0.21946638, -0.21911601, -0.21876415, -0.21841080, -0.21805593, -0.21769954, -0.21734162,
-0.21698215, -0.21662112, -0.21625852, -0.21589434, -0.21552856, -0.21516117, -0.21479216, -0.21442152, -0.21404922, -0.21367526,
-0.21329962, -0.21292230, -0.21254327, -0.21216252, -0.21178003, -0.21139580, -0.21100981, -0.21062203, -0.21023247, -0.20984110,
-0.20944790, -0.20905286, -0.20865597, -0.20825721, -0.20785656, -0.20745401, -0.20704954, -0.20664313, -0.20623477, -0.20582443,
-0.20541211, -0.20499778, -0.20458142, -0.20416303, -0.20374257, -0.20332003, -0.20289540, -0.20246865, -0.20203976, -0.20160872,
-0.20117550, -0.20074008, -0.20030245, -0.19986258, -0.19942046, -0.19897606, -0.19852935, -0.19808033, -0.19762896, -0.19717523,
-0.19671911, -0.19626058, -0.19579962, -0.19533620, -0.19487030, -0.19440189, -0.19393096, -0.19345747, -0.19298140, -0.19250273,
-0.19202143, -0.19153748, -0.19105085, -0.19056150, -0.19006942, -0.18957458, -0.18907695, -0.18857650, -0.18807320, -0.18756703,
-0.18705795, -0.18654593, -0.18603095, -0.18551298, -0.18499198, -0.18446791, -0.18394076, -0.18341049, -0.18287706, -0.18234045,
-0.18180061, -0.18125751, -0.18071113, -0.18016141, -0.17960834, -0.17905187, -0.17849196, -0.17792858, -0.17736170, -0.17679126,
-0.17621724, -0.17563959, -0.17505828, -0.17447325, -0.17388449, -0.17329193, -0.17269554, -0.17209527, -0.17149109, -0.17088294,
-0.17027079, -0.16965458, -0.16903428, -0.16840982, -0.16778118, -0.16714829, -0.16651111, -0.16586959, -0.16522368, -0.16457333,
-0.16391848, -0.16325908, -0.16259509, -0.16192643, -0.16125307, -0.16057494, -0.15989199, -0.15920416, -0.15851139, -0.15781362,
-0.15711079, -0.15640284, -0.15568971, -0.15497133, -0.15424765, -0.15351859, -0.15278409, -0.15204408, -0.15129850, -0.15054728,
-0.14979034, -0.14902762, -0.14825905, -0.14748454, -0.14670404, -0.14591745, -0.14512471, -0.14432574, -0.14352046, -0.14270879,
-0.14189065, -0.14106596, -0.14023463, -0.13939659, -0.13855174, -0.13770000, -0.13684128, -0.13597550, -0.13510256, -0.13422237,
-0.13333483, -0.13243987, -0.13153737, -0.13062725, -0.12970940, -0.12878374, -0.12785014, -0.12690853, -0.12595879, -0.12500082,
-0.12403451, -0.12305977, -0.12207647, -0.12108452, -0.12008381, -0.11907421, -0.11805562, -0.11702794, -0.11599103, -0.11494479,
-0.11388910, -0.11282384, -0.11174890, -0.11066415, -0.10956947, -0.10846475, -0.10734985, -0.10622466, -0.10508905, -0.10394290,
-0.10278608, -0.10161846, -0.10043992, -0.09925033, -0.09804956, -0.09683749, -0.09561399, -0.09437892, -0.09313217, -0.09187360,
-0.09060308, -0.08932048, -0.08802568, -0.08671855, -0.08539897, -0.08406680, -0.08272193, -0.08136422, -0.07999357, -0.07860983,
-0.07721290, -0.07580265, -0.07437897, -0.07294175, -0.07149087, -0.07002622, -0.06854769, -0.06705518, -0.06554859, -0.06402781,
-0.06249275, -0.06094333, -0.05937944, -0.05780101, -0.05620796, -0.05460021, -0.05297768, -0.05134032, -0.04968806, -0.04802084,
-0.04633862, -0.04464134, -0.04292897, -0.04120148, -0.03945883, -0.03770101, -0.03592801, -0.03413981, -0.03233641, -0.03051782,
-0.02868406, -0.02683514, -0.02497110, -0.02309197, -0.02119779, -0.01928861, -0.01736449, -0.01542551, -0.01347172, -0.01150322,
-0.00952010, -0.00752244, -0.00551037, -0.00348398, -0.00144340, 0.00061124, 0.00267982, 0.00476218, 0.00685818, 0.00896766,
0.01109047, 0.01322643, 0.01537537, 0.01753711, 0.01971146, 0.02189824, 0.02409725, 0.02630828, 0.02853113, 0.03076559,
0.03301145, 0.03526850, 0.03753650, 0.03981524, 0.04210449, 0.04440403, 0.04671362, 0.04903302, 0.05136202, 0.05370037,
0.05604784, 0.05840419, 0.06076919, 0.06314260, 0.06552419, 0.06791371, 0.07031095, 0.07271566, 0.07512762, 0.07754659,
0.07997234, 0.08240466, 0.08484332, 0.08728809, 0.08973876, 0.09219511, 0.09465693, 0.09712400, 0.09959613, 0.10207310,
0.10455472, 0.10704079, 0.10953111, 0.11202550, 0.11452376, 0.11702571, 0.11953118, 0.12203998, 0.12455195, 0.12706691,
0.12958471, 0.13210517, 0.13462814, 0.13715347, 0.13968101, 0.14221060, 0.14474211, 0.14727539, 0.14981031, 0.15234674,
0.15488454, 0.15742360, 0.15996378, 0.16250497, 0.16504705, 0.16758990, 0.17013342, 0.17267751, 0.17522205, 0.17776694,
0.18031209, 0.18285740, 0.18540277, 0.18794812, 0.19049336, 0.19303840, 0.19558317, 0.19812757, 0.20067153, 0.20321498,
0.20575784, 0.20830003, 0.21084150, 0.21338217, 0.21592198, 0.21846086, 0.22099875, 0.22353559, 0.22607133, 0.22860590,
0.23113925, 0.23367132, 0.23620208, 0.23873145, 0.24125940, 0.24378588, 0.24631084, 0.24883424, 0.25135603, 0.25387617,
0.25639462, 0.25891133, 0.26142627, 0.26393941, 0.26645069, 0.26896010, 0.27146758, 0.27397311, 0.27647666, 0.27897818,
0.28147766, 0.28397505, 0.28647033, 0.28896347, 0.29145443, 0.29394320, 0.29642974, 0.29891403, 0.30139604, 0.30387575,
0.30635312, 0.30882814, 0.31130078, 0.31377102, 0.31623883, 0.31870419, 0.32116708, 0.32362748, 0.32608537, 0.32854072,
0.33099352, 0.33344374, 0.33589137, 0.33833639, 0.34077877, 0.34321850, 0.34565556, 0.34808993, 0.35052159, 0.35295053,
0.35537672, 0.35780016, 0.36022082, 0.36263869, 0.36505375, 0.36746599, 0.36987538, 0.37228192, 0.37468558, 0.37708636,
0.37948423, 0.38187919, 0.38427121, 0.38666028, 0.38904638, 0.39142951, 0.39380965, 0.39618678, 0.39856089, 0.40093196,
0.40329999, 0.40566495, 0.40802684, 0.41038563, 0.41274133, 0.41509390, 0.41744334, 0.41978964, 0.42213278, 0.42447275,
0.42680954, 0.42914313, 0.43147351, 0.43380067, 0.43612459, 0.43844526, 0.44076267, 0.44307681, 0.44538766, 0.44769521,
0.44999945, 0.45230037, 0.45459794, 0.45689217, 0.45918303, 0.46147052, 0.46375462, 0.46603532, 0.46831261, 0.47058648,
0.47285691, 0.47512388, 0.47738740, 0.47964744, 0.48190400, 0.48415705, 0.48640660, 0.48865262, 0.49089511, 0.49313404,
0.49536942, 0.49760123, 0.49982944, 0.50205407, 0.50427508, 0.50649247, 0.50870622, 0.51091633, 0.51312278, 0.51532556,
0.51752466, 0.51972006, 0.52191175, 0.52409972, 0.52628395, 0.52846444, 0.53064118, 0.53281414, 0.53498332, 0.53714870,
0.53931028, 0.54146803, 0.54362196, 0.54577203, 0.54791825, 0.55006060, 0.55219907, 0.55433364, 0.55646430, 0.55859104,
0.56071385, 0.56283271, 0.56494761, 0.56705854, 0.56916549, 0.57126844, 0.57336737, 0.57546229, 0.57755317, 0.57964000,
0.58172277, 0.58380147, 0.58587608, 0.58794659, 0.59001298, 0.59207525, 0.59413338, 0.59618736, 0.59823717, 0.60028281,
0.60232425, 0.60436149, 0.60639451, 0.60842330, 0.61044784, 0.61246813, 0.61448415, 0.61649588, 0.61850331, 0.62050644,
0.62250524, 0.62449970, 0.62648981, 0.62847556, 0.63045693, 0.63243390, 0.63440648, 0.63637463, 0.63833835, 0.64029763,
0.64225244, 0.64420279, 0.64614864, 0.64809000, 0.65002684, 0.65195915, 0.65388692, 0.65581014, 0.65772879, 0.65964285,
0.66155232, 0.66345717, 0.66535740, 0.66725300, 0.66914393, 0.67103021, 0.67291180, 0.67478869, 0.67666088, 0.67852834,
0.68039107, 0.68224905, 0.68410226, 0.68595069, 0.68779433, 0.68963316, 0.69146716, 0.69329633, 0.69512066, 0.69694011,
0.69875469, 0.70056437, 0.70236915, 0.70416901, 0.70596392, 0.70775389, 0.70953890, 0.71131892, 0.71309395, 0.71486398,
0.71662898, 0.71838894, 0.72014386, 0.72189371, 0.72363848, 0.72537815, 0.72711272, 0.72884217, 0.73056647, 0.73228563,
0.73399962, 0.73570843, 0.73741204, 0.73911045, 0.74080363, 0.74249158, 0.74417427, 0.74585169, 0.74752384, 0.74919069,
0.75085223, 0.75250845, 0.75415932, 0.75580485, 0.75744501, 0.75907979, 0.76070917, 0.76233315, 0.76395170, 0.76556481,
0.76717247, 0.76877467, 0.77037138, 0.77196261, 0.77354832, 0.77512851, 0.77670317, 0.77827227, 0.77983582, 0.78139378,
0.78294616, 0.78449293, 0.78603408, 0.78756960, 0.78909947, 0.79062368, 0.79214222, 0.79365508, 0.79516223, 0.79666367,
0.79815939, 0.79964936, 0.80113359, 0.80261204, 0.80408472, 0.80555161, 0.80701269, 0.80846796, 0.80991739, 0.81136098,
0.81279872, 0.81423059, 0.81565658, 0.81707668, 0.81849088, 0.81989915, 0.82130150, 0.82269791, 0.82408837, 0.82547286,
0.82685138, 0.82822391, 0.82959044, 0.83095096, 0.83230547, 0.83365394, 0.83499637, 0.83633274, 0.83766305, 0.83898729,
0.84030544, 0.84161750, 0.84292346, 0.84422329, 0.84551701, 0.84680459, 0.84808602, 0.84936131, 0.85063043, 0.85189338,
0.85315014, 0.85440072, 0.85564511, 0.85688329, 0.85811525, 0.85934099, 0.86056050, 0.86177378, 0.86298081, 0.86418159,
0.86537611, 0.86656437, 0.86774635, 0.86892205, 0.87009147, 0.87125460, 0.87241143, 0.87356196, 0.87470618, 0.87584409,
0.87697569, 0.87810096, 0.87921990, 0.88033251, 0.88143879, 0.88253873, 0.88363233, 0.88471958, 0.88580049, 0.88687504,
0.88794324, 0.88900509, 0.89006058, 0.89110971, 0.89215248, 0.89318889, 0.89421893, 0.89524262, 0.89625994, 0.89727090,
0.89827550, 0.89927374, 0.90026561, 0.90125113, 0.90223028, 0.90320309, 0.90416953, 0.90512963, 0.90608338, 0.90703078,
0.90797184, 0.90890656, 0.90983495, 0.91075701, 0.91167274, 0.91258216, 0.91348525, 0.91438204, 0.91527253, 0.91615672,
0.91703461, 0.91790623, 0.91877157, 0.91963064, 0.92048345, 0.92133001, 0.92217033, 0.92300441, 0.92383227, 0.92465392,
0.92546936, 0.92627861, 0.92708167, 0.92787857, 0.92866930, 0.92945388, 0.93023233, 0.93100466, 0.93177087, 0.93253099,
0.93328503, 0.93403299, 0.93477491, 0.93551078, 0.93624063, 0.93696446, 0.93768231, 0.93839417, 0.93910008, 0.93980004,
0.94049407, 0.94118220, 0.94186443, 0.94254079, 0.94321130, 0.94387597, 0.94453483, 0.94518788, 0.94583516, 0.94647669,
0.94711248, 0.94774255, 0.94836693, 0.94898564, 0.94959870, 0.95020613, 0.95080795, 0.95140420, 0.95199488, 0.95258003,
0.95315966, 0.95373381, 0.95430249, 0.95486573, 0.95542356, 0.95597600, 0.95652308, 0.95706482, 0.95760125, 0.95813239,
0.95865827, 0.95917892, 0.95969437, 0.96020464, 0.96070976, 0.96120975, 0.96170465, 0.96219449, 0.96267929, 0.96315908,
0.96363389, 0.96410374, 0.96456868, 0.96502872, 0.96548391, 0.96593425, 0.96637980, 0.96682058, 0.96725661, 0.96768793,
0.96811457, 0.96853656, 0.96895393, 0.96936672, 0.96977495, 0.97017865, 0.97057786, 0.97097261, 0.97136293, 0.97174885,
0.97213040, 0.97250762, 0.97288054, 0.97324919, 0.97361360, 0.97397381, 0.97432985, 0.97468174, 0.97502953, 0.97537324,
0.97571291, 0.97604857, 0.97638025, 0.97670799, 0.97703182, 0.97735177, 0.97766787, 0.97798016, 0.97828867, 0.97859343,
0.97889448, 0.97919184, 0.97948556, 0.97977565, 0.98006216, 0.98034512, 0.98062456, 0.98090052, 0.98117301, 0.98144209,
0.98170778, 0.98197010, 0.98222910, 0.98248481, 0.98273726, 0.98298647, 0.98323249, 0.98347534, 0.98371505, 0.98395166,
0.98418520, 0.98441569, 0.98464318, 0.98486768, 0.98508924, 0.98530788, 0.98552363, 0.98573652, 0.98594659, 0.98615386,
0.98635836, 0.98656013, 0.98675919, 0.98695557, 0.98714930, 0.98734042, 0.98752894, 0.98771490, 0.98789833, 0.98807925,
0.98825770, 0.98843370, 0.98860728, 0.98877847, 0.98894729, 0.98911378, 0.98927795, 0.98943985, 0.98959948, 0.98975689,
0.98991209, 0.99006512, 0.99021599, 0.99036474, 0.99051139, 0.99065597, 0.99079850, 0.99093900, 0.99107750, 0.99121403,
0.99134861, 0.99148127, 0.99161202, 0.99174089, 0.99186791, 0.99199310, 0.99211648, 0.99223808, 0.99235791, 0.99247600,
0.99259238, 0.99270706, 0.99282006, 0.99293142, 0.99304114, 0.99314926, 0.99325579, 0.99336075, 0.99346416, 0.99356605,
0.99366643, 0.99376533, 0.99386276, 0.99395874, 0.99405330, 0.99414644, 0.99423820, 0.99432859, 0.99441763, 0.99450533,
0.99459172, 0.99467682, 0.99476063, 0.99484318, 0.99492449, 0.99500457, 0.99508344, 0.99516112, 0.99523762, 0.99531296,
0.99538716, 0.99546022, 0.99553218, 0.99560304, 0.99567282, 0.99574153, 0.99580919, 0.99587582, 0.99594143, 0.99600603,
0.99606963, 0.99613226, 0.99619393, 0.99625465, 0.99631443, 0.99637328, 0.99643123, 0.99648828, 0.99654445, 0.99659975,
0.99665419, 0.99670779, 0.99676055, 0.99681249, 0.99686362, 0.99691396, 0.99696351, 0.99701229, 0.99706031, 0.99710758,
0.99715410, 0.99719990, 0.99724498, 0.99728936, 0.99733304, 0.99737603, 0.99741834, 0.99745999, 0.99750099, 0.99754134,
0.99758105, 0.99762014, 0.99765861, 0.99769647, 0.99773373, 0.99777041, 0.99780650, 0.99784202, 0.99787698, 0.99791138,
0.99794524, 0.99797856, 0.99801135, 0.99804362, 0.99807538, 0.99810663, 0.99813738, 0.99816764, 0.99819742, 0.99822673,
0.99825556, 0.99828394, 0.99831186, 0.99833934, 0.99836638, 0.99839298, 0.99841916, 0.99844492, 0.99847026, 0.99849520,
0.99851974, 0.99854388, 0.99856764, 0.99859102, 0.99861402, 0.99863665, 0.99865891, 0.99868082, 0.99870237, 0.99872358,
0.99874445, 0.99876497, 0.99878517, 0.99880504, 0.99882459, 0.99884383, 0.99886275, 0.99888137, 0.99889969, 0.99891771,
0.99893544, 0.99895288, 0.99897004, 0.99898692, 0.99900353, 0.99901987, 0.99903594, 0.99905176, 0.99906731, 0.99908262,
0.99909767, 0.99911249, 0.99912706, 0.99914139, 0.99915549, 0.99916936, 0.99918301, 0.99919643, 0.99920964, 0.99922263,
0.99923540, 0.99924797, 0.99926034, 0.99927250, 0.99928447, 0.99929624, 0.99930782, 0.99931921, 0.99933041, 0.99934143,
0.99935227, 0.99936294, 0.99937343, 0.99938375, 0.99939389, 0.99940388, 0.99941370, 0.99942336, 0.99943286, 0.99944221,
0.99945140, 0.99946044, 0.99946934, 0.99947809, 0.99948669, 0.99949516, 0.99950348, 0.99951167, 0.99951973, 0.99952765,
0.99953544, 0.99954311, 0.99955065, 0.99955807, 0.99956536, 0.99957253, 0.99957959, 0.99958653, 0.99959336, 0.99960007,
0.99960668, 0.99961317, 0.99961956, 0.99962585, 0.99963203, 0.99963811, 0.99964409, 0.99964997, 0.99965575, 0.99966144,
0.99966704, 0.99967254, 0.99967796, 0.99968328, 0.99968852, 0.99969367, 0.99969873, 0.99970371, 0.99970861, 0.99971343,
0.99971817, 0.99972283, 0.99972742, 0.99973193, 0.99973636, 0.99974072, 0.99974501, 0.99974923, 0.99975338, 0.99975746,
0.99976148, 0.99976543, 0.99976931, 0.99977313, 0.99977688, 0.99978058, 0.99978421, 0.99978778, 0.99979130, 0.99979475,
0.99979815, 0.99980149, 0.99980478, 0.99980801, 0.99981119, 0.99981432, 0.99981740, 0.99982042, 0.99982339, 0.99982632,
0.99982920, 0.99983203, 0.99983481, 0.99983755, 0.99984024, 0.99984289, 0.99984549, 0.99984805, 0.99985057, 0.99985304,
0.99985548, 0.99985788, 0.99986023, 0.99986255, 0.99986483, 0.99986707, 0.99986927, 0.99987144, 0.99987357, 0.99987567,
0.99987773, 0.99987975, 0.99988175, 0.99988371, 0.99988564, 0.99988753, 0.99988940, 0.99989123, 0.99989304, 0.99989481,
0.99989655
};

@ -0,0 +1,175 @@
#ifndef _EFFECT_GUITARBOOSTER_F32_H_
#define _EFFECT_GUITARBOOSTER_F32_H_
/**
* @file effect_guitarBooster_F32.h
* @author Piotr Zapart
* @brief Oversampled Waveshaper based overdrive effect
* Stereo IO and bypass, but the processing is mono
* @version 0.1
* @date 2024-03-20
*
* @copyright Copyright (c) 2024
*
* 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 <https://www.gnu.org/licenses/>."
*/
#include <AudioStream_F32.h>
#include "basic_DSPutils.h"
#include <arm_math.h>
#define GBOOST_TONE_MINF (800.0f)
#define GBOOST_TONE_MAXF (8000.0f)
#define GBOOST_LP2_F (10000.0f)
#define GBOOST_BOTTOM_MINF (50.0f)
#define GBOOST_BOTTOM_MAXF (350.0f)
class AudioEffectGuitarBooster_F32 : public AudioStream_F32
{
public:
AudioEffectGuitarBooster_F32(void) : AudioStream_F32(2, inputQueueArray)
{
fs_Hz = AUDIO_SAMPLE_RATE_EXACT;
blockSize = AUDIO_BLOCK_SAMPLES;
begin();
}
AudioEffectGuitarBooster_F32(const AudioSettings_F32 &settings) : AudioStream_F32(2, inputQueueArray)
{
fs_Hz = settings.sample_rate_Hz;
blockSize = settings.audio_block_samples;
begin();
}
virtual void update();
void begin()
{
arm_fir_interpolate_init_f32(&interpolator, upsample_k, FIR_taps, (float32_t*)FIR_coeffs, interpState, AUDIO_BLOCK_SAMPLES);
arm_fir_decimate_init_f32(&decimator, FIR_taps, upsample_k, (float32_t*)FIR_coeffs, decimState, upsample_k * AUDIO_BLOCK_SAMPLES);
bottom(1.0f);
tone(1.0f);
hpPost_k = omega(GBOOST_BOTTOM_MINF);
lp2_k = omega(GBOOST_LP2_F);
}
void drive(float32_t value)
{
value = fabs(value);
value = 1.0f + value * upsample_k;
__disable_irq()
gainSet = value;
__enable_irq();
}
void bottom(float32_t bottom);
void tone(float32_t t);
void bias(float32_t b)
{
b = constrain(b, -1.0f, 1.0f);
__disable_irq();
DCbias = b;
__enable_irq();
}
void mix(float32_t m)
{
float32_t d, w;
m = constrain(m, 0.0f, 1.0f);
mix_pwr(m, &w, &d);
__disable_irq();
wetGain = w;
dryGain = d;
__enable_irq();
}
void volume(float32_t l)
{
l = constrain(l, 0.0f, 1.0f);
__disable_irq();
levelSet = l;
__enable_irq();
}
// Bypass
bool bypass_get(void) {return bp;}
void bypass_set(bool state) {bp = state;}
bool bypass_tgl(void)
{
bp ^= 1;
return bp;
}
bool octave_get(void) {return octave;}
void octave_set(bool state) {octave = state;}
bool octave_tgl(void)
{
octave ^= 1;
return octave;
}
private:
audio_block_f32_t *inputQueueArray[2];
float fs_Hz;
uint16_t blockSize;
static const uint8_t upsample_k = 5;
static const uint8_t FIR_taps = 75;
static constexpr float32_t FIR_coeffs[FIR_taps] =
{
-0.000033, 0.000112,-0.000100,-0.000103, 0.000361,-0.000331,-0.000181, 0.000822,-0.000824,-0.000205,
0.001556,-0.001737,-0.000054, 0.002607,-0.003263, 0.000461, 0.003979,-0.005641, 0.001621, 0.005626,
-0.009170, 0.003841, 0.007448,-0.014287, 0.007776, 0.009301,-0.021812, 0.014667, 0.011009,-0.033775,
0.027639, 0.012392,-0.057262, 0.058949, 0.013293,-0.142816, 0.268001, 0.680272, 0.268001,-0.142816,
0.013293, 0.058949,-0.057262, 0.012392, 0.027639,-0.033775, 0.011009, 0.014667,-0.021812, 0.009301,
0.007776,-0.014287, 0.007448, 0.003841,-0.009170, 0.005626, 0.001621,-0.005641, 0.003979, 0.000461,
-0.003263, 0.002607,-0.000054,-0.001737, 0.001556,-0.000205,-0.000824, 0.000822,-0.000181,-0.000331,
0.000361,-0.000103,-0.000100, 0.000112,-0.000033
};
float32_t blockInterpolated[upsample_k * AUDIO_BLOCK_SAMPLES];
float32_t interpState[(FIR_taps / upsample_k) + AUDIO_BLOCK_SAMPLES - 1];
float32_t decimState[FIR_taps + (upsample_k * AUDIO_BLOCK_SAMPLES) - 1];
arm_fir_interpolate_instance_f32 interpolator;
arm_fir_decimate_instance_f32 decimator;
arm_linear_interp_instance_f32 waveshaper =
{
2000, -1.0f, 2.0f/2000.0f, &driveWaveform[0]
};
bool bp = true; // bypass flag
bool octave = true;
float32_t dryGain = 0.0f;
float32_t wetGain = 1.0f;
float32_t DCbias = 0.175f;
float32_t gainSet = 1.0f;
float32_t gain = 0.0f;
float32_t gain_hp = 1.0f;
float32_t levelSet = 1.0f;
float32_t level = 1.0f;
float32_t lp1_k = 0.0f;
float32_t lp1_reg = 0.0f;
float32_t lp2_k = 0.0f;
float32_t lp2_reg = 0.0f;
float32_t hpPre1_k = 0.0f;
float32_t hpPre1_reg = 0.0f;
float32_t hpPre2_k = 0.0f;
float32_t hpPre2_reg = 0.0f;
float32_t hpPost_k = 0.0f;
float32_t hpPost_reg = 0.0f;
static float32_t driveWaveform[2001];
inline float32_t omega(float f)
{
float32_t fs = fs_Hz * upsample_k;
return 1.0f - expf(-TWO_PI * f / fs);
}
};
#endif // _EFFECT_GUITARBOOSTER_F32_H_

@ -4,9 +4,10 @@
* Created: Max Huster, Feb 2021 * Created: Max Huster, Feb 2021
* Purpose: This module mutes the Audio completly, when it's below a given threshold. * Purpose: This module mutes the Audio completly, when it's below a given threshold.
* *
* This processes a single stream fo audio data (ie, it is mono)
*
* MIT License. use at your own risk. * MIT License. use at your own risk.
*
* 03.2024 - stereo version with optional side chain input via pointers
* by Piotr Zapart (www.hexefx.com)
*/ */
#ifndef _AudioEffectNoiseGateStereo_F32_h #ifndef _AudioEffectNoiseGateStereo_F32_h
@ -18,94 +19,150 @@
class AudioEffectNoiseGateStereo_F32 : public AudioStream_F32 class AudioEffectNoiseGateStereo_F32 : public AudioStream_F32
{ {
public: public:
// constructor AudioEffectNoiseGateStereo_F32(float32_t* sideChainSrcL, float32_t* sideChainSrcR) : AudioStream_F32(2, inputQueueArray_f32)
AudioEffectNoiseGateStereo_F32(void) : AudioStream_F32(4, inputQueueArray_f32){}; {
AudioEffectNoiseGateStereo_F32(const AudioSettings_F32 &settings) : AudioStream_F32(4, inputQueueArray_f32){}; p_sideChain_inL = sideChainSrcL;
p_sideChain_inR = sideChainSrcR;
setDefaults();
};
AudioEffectNoiseGateStereo_F32(const AudioSettings_F32 &settings) : AudioStream_F32(2, inputQueueArray_f32)
{
fs = settings.sample_rate_Hz;
setDefaults();
}
void update(void) void update(void)
{ {
audio_block_f32_t *blockL, *blockR, *blockSideChL, *blockSideChR, *blockSideCh, *blockGain; audio_block_f32_t *blockL, *blockR, *blockSideCh, *blockGain;
blockL = AudioStream_F32::receiveWritable_f32(0); audio_block_f32_t *blockOutL, *blockOutR;
blockR = AudioStream_F32::receiveWritable_f32(1);
blockSideChL = AudioStream_F32::receiveReadOnly_f32(2); // side chain inputL
blockSideChR = AudioStream_F32::receiveReadOnly_f32(3); // side chain inputR
blockSideCh = AudioStream_F32::allocate_f32(); // allocate new block for summed L+R
blockGain = AudioStream_F32::allocate_f32(); // create a new audio block for the gain
if (!blockL || !blockR || !blockSideChL || !blockSideChR || !blockSideCh || !blockGain) blockL = AudioStream_F32::receiveReadOnly_f32(0);
blockR = AudioStream_F32::receiveReadOnly_f32(1);
// no input signal
if (!blockL || !blockR)
{ {
if (blockSideChL) AudioStream_F32::release(blockSideChL);
if (blockSideChR) AudioStream_F32::release(blockSideChR);
if (blockSideCh) AudioStream_F32::release(blockSideCh);
if (blockGain) AudioStream_F32::release(blockGain);
if (blockL) AudioStream_F32::release(blockL); if (blockL) AudioStream_F32::release(blockL);
if (blockR) AudioStream_F32::release(blockR); if (blockR) AudioStream_F32::release(blockR);
return; return;
} }
// sum L + R if (bp)
arm_add_f32(blockSideChL->data, blockSideChR->data, blockSideCh->data, blockSideCh->length); {
AudioStream_F32::transmit(blockL, 0);
AudioStream_F32::transmit(blockR, 1);
AudioStream_F32::release(blockL);
AudioStream_F32::release(blockR);
return;
}
blockSideCh = AudioStream_F32::allocate_f32(); // allocate new block for summed L+R
blockGain = AudioStream_F32::allocate_f32(); // create a new audio block for the gain
if (!p_sideChain_inL || !p_sideChain_inR || !blockSideCh || !blockGain)
{
if (blockSideCh) AudioStream_F32::release(blockSideCh);
if (blockGain) AudioStream_F32::release(blockGain);
AudioStream_F32::transmit(blockL, 0);
AudioStream_F32::transmit(blockR, 1);
AudioStream_F32::release(blockL);
AudioStream_F32::release(blockR);
return;
}
blockOutL = AudioStream_F32::allocate_f32();
blockOutR = AudioStream_F32::allocate_f32();
if (!blockOutL || !blockOutR)
{
if (blockOutL) AudioStream_F32::release(blockOutL);
if (blockOutR) AudioStream_F32::release(blockOutR);
return;
}
//sum L + R
arm_add_f32(p_sideChain_inL, p_sideChain_inR, blockSideCh->data, blockSideCh->length);
arm_scale_f32(blockSideCh->data, 0.5f, blockSideCh->data, blockSideCh->length); // divide by 2 arm_scale_f32(blockSideCh->data, 0.5f, blockSideCh->data, blockSideCh->length); // divide by 2
// calculate the desired gain
calcGain(blockSideCh, blockGain); calcGain(blockSideCh, blockGain);
// smooth the "blocky" gain block
calcSmoothedGain(blockGain); calcSmoothedGain(blockGain);
// multiply it to the input singal arm_mult_f32(blockGain->data, blockL->data, blockOutL->data, blockOutL->length);
arm_mult_f32(blockGain->data, blockL->data, blockL->data, blockL->length); arm_mult_f32(blockGain->data, blockR->data, blockOutR->data, blockOutR->length);
arm_mult_f32(blockGain->data, blockR->data, blockR->data, blockR->length);
// release gainBlock
AudioStream_F32::release(blockGain); AudioStream_F32::release(blockGain);
AudioStream_F32::release(blockSideCh); AudioStream_F32::release(blockSideCh);
AudioStream_F32::release(blockSideChL);
AudioStream_F32::release(blockSideChR);
// transmit the block and be done AudioStream_F32::transmit(blockOutL, 0);
AudioStream_F32::transmit(blockL, 0); AudioStream_F32::transmit(blockOutR, 1);
AudioStream_F32::transmit(blockR, 1);
AudioStream_F32::release(blockL); AudioStream_F32::release(blockL);
AudioStream_F32::release(blockR); AudioStream_F32::release(blockR);
AudioStream_F32::release(blockOutL);
AudioStream_F32::release(blockOutR);
} }
/**
* @brief Set gfate threshold in decibels
* value less than -99dB turns the bypass on
*
* @param dbfs gate threshold in dB
*/
void setThreshold(float dbfs) void setThreshold(float dbfs)
{ {
if (dbfs < -99.0f) bp = true;
else bp = false;
// convert dbFS to linear value to comapre against later // convert dbFS to linear value to comapre against later
linearThreshold = pow10f(dbfs / 20.0f); linearThreshold = pow10f(dbfs / 20.0f);
} }
void setOpeningTime(float timeInSeconds) void setOpeningTime(float timeInSeconds)
{ {
openingTimeConst = expf(-1.0f / (timeInSeconds * AUDIO_SAMPLE_RATE)); openingTimeConst = expf(-1.0f / (timeInSeconds * fs));
} }
void setClosingTime(float timeInSeconds) void setClosingTime(float timeInSeconds)
{ {
closingTimeConst = expf(-1.0f / (timeInSeconds * AUDIO_SAMPLE_RATE)); closingTimeConst = expf(-1.0f / (timeInSeconds * fs));
} }
void setHoldTime(float timeInSeconds) void setHoldTime(float timeInSeconds)
{ {
holdTimeNumSamples = timeInSeconds * AUDIO_SAMPLE_RATE; holdTimeNumSamples = timeInSeconds * fs;
} }
bool infoIsOpen() bool infoIsOpen()
{ {
return _isOpenDisplay; return _isOpenDisplay;
} }
void bypass_set(bool state)
{
__disable_irq();
bp = state;
__enable_irq();
}
bool bypass_tgl(void)
{
bool bp_new = bp ^ 1;
__disable_irq();
bp = bp_new;
__enable_irq();
return bp;
}
private: private:
float32_t fs = AUDIO_SAMPLE_RATE_EXACT;
float32_t* p_sideChain_inL = NULL;
float32_t* p_sideChain_inR = NULL;
float32_t linearThreshold; float32_t linearThreshold;
float32_t prev_gain_dB = 0; float32_t prev_gain_dB = 0;
float32_t openingTimeConst, closingTimeConst; float32_t openingTimeConst, closingTimeConst;
float lastGainBlockValue = 0; float32_t lastGainBlockValue = 0;
int32_t counter, holdTimeNumSamples = 0; int32_t counter, holdTimeNumSamples = 0;
audio_block_f32_t *inputQueueArray_f32[4]; audio_block_f32_t *inputQueueArray_f32[4];
bool falling = false; bool falling = false;
bool bp = false;
bool _isOpen = false; bool _isOpen = false;
bool _isOpenDisplay = false; bool _isOpenDisplay = false;
bool _extSideChain = true;
void setDefaults()
{
setOpeningTime(0.01f);
setClosingTime(0.05f);
setHoldTime(0.01f);
}
void calcGain(audio_block_f32_t *input, audio_block_f32_t *gainBlock) void calcGain(audio_block_f32_t *input, audio_block_f32_t *gainBlock)
{ {
@ -153,7 +210,6 @@ private:
for (int i = 0; i < gain_block->length; i++) for (int i = 0; i < gain_block->length; i++)
{ {
gain = gain_block->data[i]; gain = gain_block->data[i];
// smooth the gain using the opening or closing constants // smooth the gain using the opening or closing constants
if (gain > prev_gain_dB) if (gain > prev_gain_dB)
{ // are we in the opening phase? { // are we in the opening phase?
@ -163,7 +219,6 @@ private:
{ // or, we're in the closing phase { // or, we're in the closing phase
gain_block->data[i] = closingTimeConst * prev_gain_dB + one_minus_closing_const * gain; gain_block->data[i] = closingTimeConst * prev_gain_dB + one_minus_closing_const * gain;
} }
// save value for the next time through this loop // save value for the next time through this loop
prev_gain_dB = gain_block->data[i]; prev_gain_dB = gain_block->data[i];
} }

@ -46,7 +46,9 @@ AudioEffectPlateReverb_F32::AudioEffectPlateReverb_F32() : AudioStream_F32(2, in
bool AudioEffectPlateReverb_F32::begin() bool AudioEffectPlateReverb_F32::begin()
{ {
input_attn = 0.5f; inputGainSet = 0.5f;
inputGain = 0.5f;
inputGain_tmp = 0.5f;
wet_gain = 1.0f; // default mode: wet signal only wet_gain = 1.0f; // default mode: wet signal only
dry_gain = 0.0f; dry_gain = 0.0f;
in_allp_k = INP_ALLP_COEFF; in_allp_k = INP_ALLP_COEFF;
@ -129,7 +131,7 @@ void AudioEffectPlateReverb_F32::update()
// handle bypass, 1st call will clean the buffers to avoid continuing the previous reverb tail // handle bypass, 1st call will clean the buffers to avoid continuing the previous reverb tail
if (flags.bypass) if (flags.bypass)
{ {
if (!flags.cleanup_done) if (!flags.cleanup_done && bp_mode != BYPASS_MODE_TRAILS)
{ {
in_allp_1L.reset(); in_allp_1L.reset();
in_allp_2L.reset(); in_allp_2L.reset();
@ -149,33 +151,37 @@ void AudioEffectPlateReverb_F32::update()
lp_dly4.reset(); lp_dly4.reset();
flags.cleanup_done = 1; flags.cleanup_done = 1;
} }
switch(bp_mode)
if (dry_gain > 0.0f) // if dry/wet mixer is used
{ {
blockL = AudioStream_F32::receiveReadOnly_f32(0); case BYPASS_MODE_PASS:
blockR = AudioStream_F32::receiveReadOnly_f32(1); blockL = AudioStream_F32::receiveReadOnly_f32(0);
if (!blockL || !blockR) blockR = AudioStream_F32::receiveReadOnly_f32(1);
{ if (!blockL || !blockR)
if (blockL) AudioStream_F32::release(blockL); {
if (blockR) AudioStream_F32::release(blockR); if (blockL) AudioStream_F32::release(blockL);
if (blockR) AudioStream_F32::release(blockR);
return;
}
AudioStream_F32::transmit(blockL, 0);
AudioStream_F32::transmit(blockR, 1);
AudioStream_F32::release(blockL);
AudioStream_F32::release(blockR);
return;
break;
case BYPASS_MODE_OFF:
blockL = AudioStream_F32::allocate_f32();
if (!blockL) return;
memset(&blockL->data[0], 0, blockL->length*sizeof(float32_t));
AudioStream_F32::transmit(blockL, 0);
AudioStream_F32::transmit(blockL, 1);
AudioStream_F32::release(blockL);
return; return;
} break;
AudioStream_F32::transmit(blockL, 0); case BYPASS_MODE_TRAILS:
AudioStream_F32::transmit(blockR, 1); default:
AudioStream_F32::release(blockL); break;
AudioStream_F32::release(blockR);
return;
} }
blockL = AudioStream_F32::allocate_f32();
if (!blockL) return;
memset(&blockL->data[0], 0, blockL->length*sizeof(float32_t));
AudioStream_F32::transmit(blockL, 0);
AudioStream_F32::transmit(blockL, 1);
AudioStream_F32::release(blockL);
return;
} }
flags.cleanup_done = 0;
blockL = AudioStream_F32::receiveWritable_f32(0); blockL = AudioStream_F32::receiveWritable_f32(0);
blockR = AudioStream_F32::receiveWritable_f32(1); blockR = AudioStream_F32::receiveWritable_f32(1);
@ -185,6 +191,7 @@ void AudioEffectPlateReverb_F32::update()
if (blockR) AudioStream_F32::release(blockR); if (blockR) AudioStream_F32::release(blockR);
return; return;
} }
flags.cleanup_done = 0;
rv_time = rv_time_k; rv_time = rv_time_k;
for (i=0; i < blockL->length; i++) for (i=0; i < blockL->length; i++)
@ -193,7 +200,9 @@ void AudioEffectPlateReverb_F32::update()
lfo1.update(); lfo1.update();
lfo2.update(); lfo2.update();
acc = blockL->data[i] * input_attn; inputGain += (inputGainSet - inputGain) * 0.25f;
acc = blockL->data[i] * inputGain;
// chained input allpasses, channel L // chained input allpasses, channel L
acc = in_allp_1L.process(acc); acc = in_allp_1L.process(acc);
@ -203,7 +212,7 @@ void AudioEffectPlateReverb_F32::update()
in_allp_out_L = pitchL.process(in_allp_out_L); in_allp_out_L = pitchL.process(in_allp_out_L);
// chained input allpasses, channel R // chained input allpasses, channel R
acc = blockR->data[i] * input_attn; acc = blockR->data[i] * inputGain;
acc = in_allp_1R.process(acc); acc = in_allp_1R.process(acc);
acc = in_allp_2R.process(acc); acc = in_allp_2R.process(acc);

@ -28,16 +28,6 @@
* Algorithm based on plate reverbs developed for SpinSemi FV-1 DSP chip * Algorithm based on plate reverbs developed for SpinSemi FV-1 DSP chip
* *
* Allpass + modulated delay line based lush plate reverb * Allpass + modulated delay line based lush plate reverb
*
* Input parameters are float in range 0.0 to 1.0:
*
* size - reverb time
* hidamp - hi frequency loss in the reverb tail
* lodamp - low frequency loss in the reverb tail
* lowpass - output/master lowpass filter, useful for darkening the reverb sound
* diffusion - lower settings will make the reverb tail more "echoey".
* freeze - infinite reverb tail effect
*
*/ */
#ifndef _EFFECT_PLATEREVERB_F32_H_ #ifndef _EFFECT_PLATEREVERB_F32_H_
@ -60,38 +50,63 @@ public:
bool begin(void); bool begin(void);
/**
* @brief sets the reverb time
*
* @param n range 0.0f - 1.0f
*/
void size(float n) void size(float n)
{ {
n = constrain(n, 0.0f, 1.0f); n = constrain(n, 0.0f, 1.0f);
n = 2*n - n*n; n = 2*n - n*n;
n = map(n, 0.0f, 1.0f, 0.2f, rv_time_k_max); n = map(n, 0.0f, 1.0f, 0.2f, rv_time_k_max);
//float attn = map(n, 0.2f, rv_time_k_max, 0.5f, 0.25f); rv_time_k_tmp = n;
inputGain_tmp = 0.5f;
__disable_irq(); __disable_irq();
rv_time_k = n; rv_time_k = n;
input_attn = 0.5f; inputGainSet = 0.5f;
__enable_irq(); __enable_irq();
} }
/**
* @brief returns the set reverb time
*
* @return float reverb time value
*/
float size_get(void) {return rv_time_k;} float size_get(void) {return rv_time_k;}
/**
* @brief Treble loss in reverb tail
*
* @param n 0.0f to 1.0f
*/
void hidamp(float n) void hidamp(float n)
{ {
n = 1.0f - constrain(n, 0.0f, 1.0f); n = 1.0f - constrain(n, 0.0f, 1.0f);
lp_hidamp_k_tmp = n;
__disable_irq(); __disable_irq();
lp_hidamp_k = n; lp_hidamp_k = n;
__enable_irq(); __enable_irq();
} }
/**
* @brief Bass loss in reverb tails
*
* @param n 0.0f to 1.0f
*/
void lodamp(float n) void lodamp(float n)
{ {
n = -constrain(n, 0.0f, 1.0f); n = -constrain(n, 0.0f, 1.0f);
float32_t tscal = 1.0f + n*0.12f; //n is negativbe here float32_t tscal = 1.0f + n*0.12f; //n is negativbe here
lp_lodamp_k_tmp = n;
__disable_irq(); __disable_irq();
lp_lodamp_k = n; lp_lodamp_k = n;
rv_time_scaler = tscal; // limit the max reverb time, otherwise it will clip rv_time_scaler = tscal; // limit the max reverb time, otherwise it will clip
__enable_irq(); __enable_irq();
} }
/**
* @brief Output lowpass filter
*
* @param n 0.0f to 1.0f
*/
void lowpass(float n) void lowpass(float n)
{ {
n = 1.0f - constrain(n, 0.0f, 1.0f); n = 1.0f - constrain(n, 0.0f, 1.0f);
@ -99,6 +114,11 @@ public:
master_lp_k = n; master_lp_k = n;
__enable_irq(); __enable_irq();
} }
/**
* @brief Output highpass filter
*
* @param n 0.0f 1.0f
*/
void hipass(float n) void hipass(float n)
{ {
n = -constrain(n, 0.0f, 1.0f); n = -constrain(n, 0.0f, 1.0f);
@ -106,6 +126,12 @@ public:
master_hp_k = n; master_hp_k = n;
__enable_irq(); __enable_irq();
} }
/**
* @brief reverb tail diffusion,
* lower values produce more single repeats, echos
*
* @param n 0.0f - 1.0f
*/
void diffusion(float n) void diffusion(float n)
{ {
n = constrain(n, 0.0f, 1.0f); n = constrain(n, 0.0f, 1.0f);
@ -115,9 +141,15 @@ public:
loop_allp_k = n; loop_allp_k = n;
__enable_irq(); __enable_irq();
} }
/**
* @brief Freeze option On/Off. Freeze sets the reverb
* time to infinity and mutes (almost) the input signal
*
* @param state
*/
void freeze(bool state) void freeze(bool state)
{ {
if (flags.freeze == state || flags.bypass) return;
flags.freeze = state; flags.freeze = state;
if (state) if (state)
{ {
@ -126,7 +158,7 @@ public:
lp_hidamp_k_tmp = lp_hidamp_k; lp_hidamp_k_tmp = lp_hidamp_k;
__disable_irq(); __disable_irq();
rv_time_k = freeze_rvtime_k; rv_time_k = freeze_rvtime_k;
input_attn = freeze_ingain; inputGainSet = freeze_ingain;
rv_time_scaler = 1.0f; rv_time_scaler = 1.0f;
lp_lodamp_k = freeze_lodamp_k; lp_lodamp_k = freeze_lodamp_k;
lp_hidamp_k = freeze_hidamp_k; lp_hidamp_k = freeze_hidamp_k;
@ -136,11 +168,14 @@ public:
} }
else else
{ {
//float attn = map(rv_time_k_tmp, 0.0f, rv_time_k_max, 0.5f, 0.25f); // recalc the in attenuation
float sc = 1.0f - lp_lodamp_k_tmp * 0.12f; // scale up the reverb time due to bass loss float sc = 1.0f - lp_lodamp_k_tmp * 0.12f; // scale up the reverb time due to bass loss
__disable_irq(); __disable_irq();
rv_time_k = rv_time_k_tmp; // restore the value rv_time_k = rv_time_k_tmp; // restore the value
input_attn = 0.5f; if (!flags.bypass)
{
inputGainSet = 0.5f;
inputGain_tmp = 0.5f;
}
rv_time_scaler = sc; rv_time_scaler = sc;
lp_hidamp_k = lp_hidamp_k_tmp; lp_hidamp_k = lp_hidamp_k_tmp;
lp_lodamp_k = lp_lodamp_k_tmp; lp_lodamp_k = lp_lodamp_k_tmp;
@ -160,9 +195,13 @@ public:
b = constrain(b, 0.0f, 1.0f); b = constrain(b, 0.0f, 1.0f);
b = map(b, 0.0f, 1.0f, 0.0f, 0.1f); b = map(b, 0.0f, 1.0f, 0.0f, 0.1f);
freeze_ingain = b; freeze_ingain = b;
if (flags.freeze) input_attn = b; // update input gain if freeze is enabled if (flags.freeze) inputGainSet = b; // update input gain if freeze is enabled
} }
/**
* @brief Internal Dry / Wet mixer
*
* @param m 0.0f (full dry) - 1.0f (full wet)
*/
void mix(float m) void mix(float m)
{ {
float32_t dry, wet; float32_t dry, wet;
@ -173,7 +212,11 @@ public:
dry_gain = dry; dry_gain = dry;
__enable_irq(); __enable_irq();
} }
/**
* @brief wet signal volume
*
* @param wet 0.0f - 1.0f
*/
void wet_level(float wet) void wet_level(float wet)
{ {
wet = constrain(wet, 0.0f, 6.0f); wet = constrain(wet, 0.0f, 6.0f);
@ -181,7 +224,11 @@ public:
wet_gain = wet; wet_gain = wet;
__enable_irq(); __enable_irq();
} }
/**
* @brief dry signal volume
*
* @param dry 0.0f - 1.0f
*/
void dry_level(float dry) void dry_level(float dry)
{ {
dry = constrain(dry, 0.0f, 1.0f); dry = constrain(dry, 0.0f, 1.0f);
@ -189,21 +236,51 @@ public:
dry_gain = dry; dry_gain = dry;
__enable_irq(); __enable_irq();
} }
/**
bool freeze_tgl() {flags.freeze ^= 1; freeze(flags.freeze); return flags.freeze;} * @brief toogle the Freeze mode
*
* @return true
* @return false
*/
bool freeze_tgl() {freeze(flags.freeze^1); return flags.freeze;}
/**
* @brief return the Freeze mode state
*
* @return true
* @return false
*/
bool freeze_get() {return flags.freeze;} bool freeze_get() {return flags.freeze;}
typedef enum
{
BYPASS_MODE_PASS, // pass the input signal to the output
BYPASS_MODE_OFF, // mute the output
BYPASS_MODE_TRAILS // mutes the input only
}bypass_mode_t;
/**
* @brief sets the bypass mode (see above)
*
* @param m
*/
void bypass_setMode(bypass_mode_t m)
{
if (m <= BYPASS_MODE_TRAILS) bp_mode = m;
}
bypass_mode_t bypass_geMode() {return bp_mode;}
bool bypass_get(void) {return flags.bypass;} bool bypass_get(void) {return flags.bypass;}
void bypass_set(bool state) void bypass_set(bool state)
{ {
flags.bypass = state; flags.bypass = state;
if (state) freeze(false); // disable freeze in bypass mode if (state)
{
if (bp_mode == BYPASS_MODE_TRAILS) inputGainSet = 0.0f;
freeze(false); // disable freeze in bypass mode
}
else inputGainSet = inputGain_tmp;
} }
bool bypass_tgl(void) bool bypass_tgl(void)
{ {
flags.bypass ^= 1; bypass_set(flags.bypass^1);
if (flags.bypass) freeze(false); // disable freeze in bypass mode
return flags.bypass; return flags.bypass;
} }
@ -219,9 +296,9 @@ public:
} }
/** /**
* @brief * @brief Contriols the amount of shimmer effect
* *
* @param s * @param s 0.0f - 1.0f
*/ */
void shimmer(float s) void shimmer(float s)
{ {
@ -232,11 +309,21 @@ public:
pitchShimR.setMix(s); pitchShimR.setMix(s);
shimmerRatio = s; shimmerRatio = s;
} }
/**
* @brief Sets the pitch of the shimmer effect
*
* @param ratio pitch up (>1.0f) or down (<1.0f) ratio
*/
void shimmerPitch(float ratio) void shimmerPitch(float ratio)
{ {
pitchShimL.setPitch(ratio); pitchShimL.setPitch(ratio);
pitchShimR.setPitch(ratio); pitchShimR.setPitch(ratio);
} }
/**
* @brief Sets the shimmer effect pitch in semitones
*
* @param semitones
*/
void shimmerPitchSemitones(int8_t semitones) void shimmerPitchSemitones(int8_t semitones)
{ {
pitchShimL.setPitchSemintone(semitones); pitchShimL.setPitchSemintone(semitones);
@ -252,6 +339,11 @@ public:
pitchL.setPitchSemintone(semitones); pitchL.setPitchSemintone(semitones);
pitchR.setPitchSemintone(semitones); pitchR.setPitchSemintone(semitones);
} }
/**
* @brief Reverb pitch shifter dry/wet mixer
*
* @param s 0.0f(dry reverb) - 1.0f (100% pitch shifter out)
*/
void pitchMix(float s) void pitchMix(float s)
{ {
s = constrain(s, 0.0f, 1.0f); s = constrain(s, 0.0f, 1.0f);
@ -268,6 +360,7 @@ private:
unsigned shimmer: 1; unsigned shimmer: 1;
unsigned cleanup_done: 1; unsigned cleanup_done: 1;
}flags; }flags;
bypass_mode_t bp_mode = BYPASS_MODE_PASS;
audio_block_f32_t *inputQueueArray_f32[2]; audio_block_f32_t *inputQueueArray_f32[2];
static const uint16_t IN_ALLP1_BUFL_LEN = 224u; static const uint16_t IN_ALLP1_BUFL_LEN = 224u;
@ -320,7 +413,9 @@ private:
AudioBasicLfo lfo1 = AudioBasicLfo(1.35f, LFO_AMPL); AudioBasicLfo lfo1 = AudioBasicLfo(1.35f, LFO_AMPL);
AudioBasicLfo lfo2 = AudioBasicLfo(1.57f, LFO_AMPL); AudioBasicLfo lfo2 = AudioBasicLfo(1.57f, LFO_AMPL);
float input_attn; float inputGain;
float inputGainSet;
float inputGain_tmp;
float wet_gain; float wet_gain;
float dry_gain; float dry_gain;

@ -1,7 +1,7 @@
#include "effect_reverbsc_F32.h" #include "effect_reverbsc_F32.h"
#define REVERBSC_DLYBUF_SIZE 98936
#define DELAYPOS_SHIFT 28 #define DELAYPOS_SHIFT 28
#define DELAYPOS_SCALE 0x10000000 #define DELAYPOS_SCALE 0x10000000
#define DELAYPOS_MASK 0x0FFFFFFF #define DELAYPOS_MASK 0x0FFFFFFF
@ -30,6 +30,8 @@ static int DelayLineBytesAlloc(float32_t sr, float32_t i_pitch_mod, int n);
static const float32_t kOutputGain = 0.35f; static const float32_t kOutputGain = 0.35f;
static const float32_t kJpScale = 0.25f; static const float32_t kJpScale = 0.25f;
extern uint8_t external_psram_size;
AudioEffectReverbSc_F32::AudioEffectReverbSc_F32(bool use_psram) : AudioStream_F32(2, inputQueueArray_f32) AudioEffectReverbSc_F32::AudioEffectReverbSc_F32(bool use_psram) : AudioStream_F32(2, inputQueueArray_f32)
{ {
sample_rate_ = AUDIO_SAMPLE_RATE_EXACT; sample_rate_ = AUDIO_SAMPLE_RATE_EXACT;
@ -37,13 +39,27 @@ AudioEffectReverbSc_F32::AudioEffectReverbSc_F32(bool use_psram) : AudioStream_F
lpfreq_ = 10000; lpfreq_ = 10000;
i_pitch_mod_ = 1; i_pitch_mod_ = 1;
damp_fact_ = 0.195847f; // ~16kHz damp_fact_ = 0.195847f; // ~16kHz
flags.mem_fail = 0;
flags.bypass = 0;
flags.freeze = 0;
flags.cleanup_done = 1;
flags.memsetup_done = 0;
int i, n_bytes = 0; int i, n_bytes = 0;
n_bytes = 0; n_bytes = 0;
if (use_psram) aux_ = (float32_t *) extmem_malloc(REVERBSC_DLYBUF_SIZE*sizeof(float32_t)); if (use_psram)
{
// no PSRAM detected - enter the memoery failsafe mode = fixed bypass
if (external_psram_size == 0)
{
flags.mem_fail = 1;
return;
}
aux_ = (float32_t *) extmem_malloc(aux_size_bytes);
}
else else
{ {
aux_ = (float32_t *) malloc(REVERBSC_DLYBUF_SIZE*sizeof(float32_t)); aux_ = (float32_t *) malloc(aux_size_bytes);
flags.memsetup_done = 1; flags.memsetup_done = 1;
} }
if (!aux_) return; if (!aux_) return;
@ -57,8 +73,7 @@ AudioEffectReverbSc_F32::AudioEffectReverbSc_F32(bool use_psram) : AudioStream_F
n_bytes += DelayLineBytesAlloc(AUDIO_SAMPLE_RATE_EXACT, 1, i); n_bytes += DelayLineBytesAlloc(AUDIO_SAMPLE_RATE_EXACT, 1, i);
} }
mix(0.5f); mix(0.5f);
flags.bypass = 0;
flags.freeze = 0;
initialised = true; initialised = true;
} }
@ -135,14 +150,6 @@ void AudioEffectReverbSc_F32::InitDelayLine(ReverbScDl_t *lp, int n)
void AudioEffectReverbSc_F32::update() void AudioEffectReverbSc_F32::update()
{ {
#if defined(__IMXRT1062__) #if defined(__IMXRT1062__)
if (!initialised) return;
if ( !flags.memsetup_done)
{
memset(aux_, 0, REVERBSC_DLYBUF_SIZE*sizeof(float32_t));
arm_dcache_flush_delete(aux_, REVERBSC_DLYBUF_SIZE*sizeof(float32_t));
flags.memsetup_done = 1;
return;
}
audio_block_f32_t *blockL, *blockR; audio_block_f32_t *blockL, *blockR;
int16_t i; int16_t i;
float32_t a_in_l, a_in_r, a_out_l, a_out_r, dryL, dryR; float32_t a_in_l, a_in_r, a_out_l, a_out_r, dryL, dryR;
@ -152,32 +159,68 @@ void AudioEffectReverbSc_F32::update()
uint32_t n; uint32_t n;
int buffer_size; /* Local copy */ int buffer_size; /* Local copy */
float32_t damp_fact = damp_fact_; float32_t damp_fact = damp_fact_;
if (!initialised) return;
// special case if memory allocation failed, pass the input signal directly to the output
if (flags.mem_fail)
{
blockL = AudioStream_F32::receiveReadOnly_f32(0);
blockR = AudioStream_F32::receiveReadOnly_f32(1);
if (!blockL || !blockR)
{
if (blockL) AudioStream_F32::release(blockL);
if (blockR) AudioStream_F32::release(blockR);
return;
}
AudioStream_F32::transmit(blockL, 0);
AudioStream_F32::transmit(blockR, 1);
AudioStream_F32::release(blockL);
AudioStream_F32::release(blockR);
return;
}
if ( !flags.memsetup_done)
{
flags.memsetup_done = memCleanup();
return;
}
if (flags.bypass) if (flags.bypass)
{ {
if (dry_gain > 0.0f) // if dry/wet mixer is used if (!flags.cleanup_done && bp_mode != BYPASS_MODE_TRAILS)
{ {
blockL = AudioStream_F32::receiveReadOnly_f32(0); flags.cleanup_done = memCleanup();
blockR = AudioStream_F32::receiveReadOnly_f32(1); }
if (!blockL || !blockR) switch(bp_mode)
{ {
if (blockL) AudioStream_F32::release(blockL); case BYPASS_MODE_PASS:
if (blockR) AudioStream_F32::release(blockR); blockL = AudioStream_F32::receiveReadOnly_f32(0);
blockR = AudioStream_F32::receiveReadOnly_f32(1);
if (!blockL || !blockR)
{
if (blockL) AudioStream_F32::release(blockL);
if (blockR) AudioStream_F32::release(blockR);
return;
}
AudioStream_F32::transmit(blockL, 0);
AudioStream_F32::transmit(blockR, 1);
AudioStream_F32::release(blockL);
AudioStream_F32::release(blockR);
return; return;
} break;
AudioStream_F32::transmit(blockL, 0); case BYPASS_MODE_OFF:
AudioStream_F32::transmit(blockR, 1); blockL = AudioStream_F32::allocate_f32();
AudioStream_F32::release(blockL); if (!blockL) return;
AudioStream_F32::release(blockR); memset(&blockL->data[0], 0, blockL->length*sizeof(float32_t));
return; AudioStream_F32::transmit(blockL, 0);
AudioStream_F32::transmit(blockL, 1);
AudioStream_F32::release(blockL);
return;
break;
case BYPASS_MODE_TRAILS:
break;
default:
break;
} }
blockL = AudioStream_F32::allocate_f32();
if (!blockL) return;
memset(&blockL->data[0], 0, blockL->length*sizeof(float32_t));
AudioStream_F32::transmit(blockL, 0);
AudioStream_F32::transmit(blockL, 1);
AudioStream_F32::release(blockL);
return;
} }
blockL = AudioStream_F32::receiveWritable_f32(0); blockL = AudioStream_F32::receiveWritable_f32(0);
blockR = AudioStream_F32::receiveWritable_f32(1); blockR = AudioStream_F32::receiveWritable_f32(1);
@ -189,8 +232,10 @@ void AudioEffectReverbSc_F32::update()
AudioStream_F32::release(blockR); AudioStream_F32::release(blockR);
return; return;
} }
flags.cleanup_done = 0;
for (i = 0; i < blockL->length; i++) for (i = 0; i < blockL->length; i++)
{ {
input_gain += (input_gain_set - input_gain) * 0.25f;
/* calculate "resultant junction pressure" and mix to input signals */ /* calculate "resultant junction pressure" and mix to input signals */
a_in_l = a_out_l = a_out_r = 0.0f; a_in_l = a_out_l = a_out_r = 0.0f;
dryL = blockL->data[i] * input_gain; dryL = blockL->data[i] * input_gain;
@ -290,15 +335,16 @@ void AudioEffectReverbSc_F32::update()
void AudioEffectReverbSc_F32::freeze(bool state) void AudioEffectReverbSc_F32::freeze(bool state)
{ {
if (flags.freeze == state) return;
flags.freeze = state; flags.freeze = state;
if (state) if (state)
{ {
feedback_tmp = feedback_; // store the settings feedback_tmp = feedback_; // store the settings
damp_fact_tmp = damp_fact_; damp_fact_tmp = damp_fact_;
input_gain_tmp = input_gain; input_gain_tmp = input_gain_set;
__disable_irq(); __disable_irq();
feedback_ = 1.0f; // infinite reverb feedback_ = 1.0f; // infinite reverb
input_gain = freeze_ingain; input_gain_set = freeze_ingain;
__enable_irq(); __enable_irq();
} }
else else
@ -306,7 +352,39 @@ void AudioEffectReverbSc_F32::freeze(bool state)
__disable_irq(); __disable_irq();
feedback_ = feedback_tmp; feedback_ = feedback_tmp;
damp_fact_ = damp_fact_tmp; damp_fact_ = damp_fact_tmp;
input_gain = input_gain_tmp; if (!flags.bypass)
{
input_gain_set = input_gain_tmp;
}
__enable_irq(); __enable_irq();
} }
} }
/**
* @brief Partial memory clear
* Clearing all the delay buffers at once, esp. if
* the PSRAM is used takes too long for the audio ISR.
* Hence the buffer clear is done in configurable portions
* spread over a few audio update routines.
*
* @return true Memory clean is complete
* @return false Memory clean still in progress
*/
bool AudioEffectReverbSc_F32::memCleanup()
{
bool result = false;
if (memCleanupEnd > REVERBSC_DLYBUF_SIZE) // last segment
{
memCleanupEnd = REVERBSC_DLYBUF_SIZE;
result = true;
}
uint32_t l = (memCleanupEnd - memCleanupStart) * sizeof(float32_t);
uint8_t* memPtr = (uint8_t *)&aux_[0]+(memCleanupStart*sizeof(float32_t));
memset(memPtr, 0, l);
arm_dcache_flush_delete(memPtr, l);
memCleanupStart = memCleanupEnd;
memCleanupEnd += memCleanupStep;
return result;
}

@ -8,13 +8,9 @@
* Year: 1999, 2005 * Year: 1999, 2005
* Ported to soundpipe by: Paul Batchelor * Ported to soundpipe by: Paul Batchelor
* *
* Ported to Teensy4 and OpenAudio_ArduinoLibrary: * Ported/upgraded to Teensy4 and OpenAudio_ArduinoLibrary:
* 01.2024 Piotr Zapart www.hexefx.com * 01.2024 Piotr Zapart www.hexefx.com
* *
* Fixes, changes:
* - In the original code the reverb level is affected by the feedback control, fixed
* - Optional
*
*/ */
#ifndef _EFFECT_REVERBSC_F32_H_ #ifndef _EFFECT_REVERBSC_F32_H_
@ -27,6 +23,8 @@
#include "arm_math.h" #include "arm_math.h"
#include "basic_DSPutils.h" #include "basic_DSPutils.h"
#define REVERBSC_DLYBUF_SIZE 98936
class AudioEffectReverbSc_F32 : public AudioStream_F32 class AudioEffectReverbSc_F32 : public AudioStream_F32
{ {
public: public:
@ -57,7 +55,7 @@ public:
feedback_tmp = feedb; feedback_tmp = feedb;
inGain = map(feedb, 0.1f, feedb_max, 0.5f, 0.2f); inGain = map(feedb, 0.1f, feedb_max, 0.5f, 0.2f);
__disable_irq(); __disable_irq();
input_gain = inGain; input_gain_set = inGain;
feedback_ = feedb; feedback_ = feedb;
__enable_irq(); __enable_irq();
} }
@ -89,44 +87,70 @@ public:
void wet_level(float32_t wet) void wet_level(float32_t wet)
{ {
wet_gain = constrain(wet, 0.0f, 1.0f); wet = constrain(wet, 0.0f, 1.0f);
__disable_irq();
wet_gain = wet;
__enable_irq();
} }
void dry_level(float32_t dry) void dry_level(float32_t dry)
{ {
dry_gain = constrain(dry, 0.0f, 1.0f); dry = constrain(dry, 0.0f, 1.0f);
__disable_irq();
dry_gain = dry;
__enable_irq();
} }
void freeze(bool state); void freeze(bool state);
bool freeze_tgl() {flags.freeze ^= 1; freeze(flags.freeze); return flags.freeze;} bool freeze_tgl() {freeze(flags.freeze^1); return flags.freeze;}
bool freeze_get() {return flags.freeze;} bool freeze_get() {return flags.freeze;}
typedef enum
{
BYPASS_MODE_PASS, // pass the input signal to the output
BYPASS_MODE_OFF, // mute the output
BYPASS_MODE_TRAILS // mutes the input only
}bypass_mode_t;
void bypass_setMode(bypass_mode_t m)
{
if (m <= BYPASS_MODE_TRAILS)
{
__disable_irq();
bp_mode = m;
__enable_irq();
}
}
bypass_mode_t bypass_geMode() {return bp_mode;}
bool bypass_get(void) {return flags.bypass;} bool bypass_get(void) {return flags.bypass;}
void bypass_set(bool state) void bypass_set(bool state)
{ {
if (flags.mem_fail) return;
flags.bypass = state; flags.bypass = state;
if (state) freeze(false); // disable freeze in bypass mode if (state)
{
if (bp_mode == BYPASS_MODE_TRAILS) input_gain_set = 0.0f;
freeze(false); // disable freeze in bypass mode
__disable_irq();
memCleanupStart = 0;
memCleanupEnd = memCleanupStep;
__enable_irq();
}
else input_gain_set = input_gain_tmp;
} }
bool bypass_tgl(void) bool bypass_tgl(void)
{ {
flags.bypass ^= 1; bypass_set(flags.bypass^1);
if (flags.bypass) freeze(false); // disable freeze in bypass mode
return flags.bypass; return flags.bypass;
} }
uint32_t getBfAddr()
{
float32_t *addr = aux_;
return (uint32_t)addr;
}
private: private:
struct flags_t struct flags_t
{ {
unsigned bypass: 1; unsigned bypass: 1;
unsigned freeze: 1; unsigned freeze: 1;
unsigned cleanup_done: 1;
unsigned memsetup_done: 1; unsigned memsetup_done: 1;
unsigned mem_fail: 1;
}flags = {0, 0, 0}; }flags = {0, 0, 0};
bypass_mode_t bp_mode;
audio_block_f32_t *inputQueueArray_f32[2]; audio_block_f32_t *inputQueueArray_f32[2];
void NextRandomLineseg(ReverbScDl_t *lp, int n); void NextRandomLineseg(ReverbScDl_t *lp, int n);
void InitDelayLine(ReverbScDl_t *lp, int n); void InitDelayLine(ReverbScDl_t *lp, int n);
@ -138,12 +162,21 @@ private:
bool initialised = false; bool initialised = false;
ReverbScDl_t delay_lines_[8]; ReverbScDl_t delay_lines_[8];
float32_t *aux_; // main delay line storage buffer, placed either in RAM2 or PSRAM float32_t *aux_; // main delay line storage buffer, placed either in RAM2 or PSRAM
const uint32_t aux_size_bytes = REVERBSC_DLYBUF_SIZE*sizeof(float32_t);
float32_t dry_gain = 0.5f; float32_t dry_gain = 0.5f;
float32_t wet_gain = 0.5f; float32_t wet_gain = 0.5f;
float32_t input_gain_set = 0.5f;
float32_t input_gain = 0.5f; float32_t input_gain = 0.5f;
float32_t input_gain_tmp = 0.5f; float32_t input_gain_tmp = 0.5f;
float32_t freeze_ingain = 0.05f; float32_t freeze_ingain = 0.05f;
static constexpr float32_t feedb_max = 0.99f; static constexpr float32_t feedb_max = 0.99f;
bool memCleanup(void);
const uint32_t memCleanupStep = 512;
uint32_t memCleanupStart = 0;
uint32_t memCleanupEnd = memCleanupStep;
}; };
#endif // _EFFECT_REVERBSC_H_ #endif // _EFFECT_REVERBSC_H_

@ -39,7 +39,7 @@
AudioEffectSpringReverb_F32::AudioEffectSpringReverb_F32() : AudioStream_F32(2, inputQueueArray) AudioEffectSpringReverb_F32::AudioEffectSpringReverb_F32() : AudioStream_F32(2, inputQueueArray)
{ {
input_attn = 0.5f; inputGain = 0.5f;
rv_time_k = 0.8f; rv_time_k = 0.8f;
in_allp_k = INP_ALLP_COEFF; in_allp_k = INP_ALLP_COEFF;
bool memOK = true; bool memOK = true;
@ -94,7 +94,7 @@ void AudioEffectSpringReverb_F32::update()
if (!initialized) return; if (!initialized) return;
if (bp) if (bp)
{ {
if (!cleanup_done) if (!cleanup_done && bp_mode != BYPASS_MODE_TRAILS)
{ {
sp_lp_allp1a.reset(); sp_lp_allp1a.reset();
sp_lp_allp1b.reset(); sp_lp_allp1b.reset();
@ -113,29 +113,36 @@ void AudioEffectSpringReverb_F32::update()
cleanup_done = true; cleanup_done = true;
} }
if (dry_gain > 0.0f) // if dry/wet mixer is used switch(bp_mode)
{ {
blockL = AudioStream_F32::receiveReadOnly_f32(0); case BYPASS_MODE_PASS:
blockR = AudioStream_F32::receiveReadOnly_f32(1); blockL = AudioStream_F32::receiveReadOnly_f32(0);
if (!blockL || !blockR) blockR = AudioStream_F32::receiveReadOnly_f32(1);
{ if (!blockL || !blockR)
if (blockL) AudioStream_F32::release(blockL); {
if (blockR) AudioStream_F32::release(blockR); if (blockL) AudioStream_F32::release(blockL);
if (blockR) AudioStream_F32::release(blockR);
return;
}
AudioStream_F32::transmit(blockL, 0);
AudioStream_F32::transmit(blockR, 1);
AudioStream_F32::release(blockL);
AudioStream_F32::release(blockR);
return; return;
} break;
AudioStream_F32::transmit(blockL, 0); case BYPASS_MODE_OFF:
AudioStream_F32::transmit(blockR, 1); blockL = AudioStream_F32::allocate_f32();
AudioStream_F32::release(blockL); if (!blockL) return;
AudioStream_F32::release(blockR); memset(&blockL->data[0], 0, blockL->length*sizeof(float32_t));
return; AudioStream_F32::transmit(blockL, 0);
AudioStream_F32::transmit(blockL, 1);
AudioStream_F32::release(blockL);
return;
break;
case BYPASS_MODE_TRAILS:
default:
break;
} }
blockL = AudioStream_F32::allocate_f32();
if (!blockL) return;
memset(&blockL->data[0], 0, blockL->length*sizeof(float32_t));
AudioStream_F32::transmit(blockL, 0);
AudioStream_F32::transmit(blockL, 1);
AudioStream_F32::release(blockL);
return;
} }
cleanup_done = false; cleanup_done = false;
blockL = AudioStream_F32::receiveWritable_f32(0); blockL = AudioStream_F32::receiveWritable_f32(0);
@ -152,13 +159,14 @@ void AudioEffectSpringReverb_F32::update()
for (i=0; i < blockL->length; i++) for (i=0; i < blockL->length; i++)
{ {
lfo.update(); lfo.update();
inputGain += (inputGainSet - inputGain) * 0.25f;
dryL = blockL->data[i]; dryL = blockL->data[i];
dryR = blockR->data[i]; dryR = blockR->data[i];
dry_in = (dryL + dryR) * input_attn; dry_in = (dryL + dryR) * inputGain;
mono_in = flt_in.process(dry_in)* (1.0f + in_BassCut_k*-2.5f); // add highpass gain compaensation? mono_in = flt_in.process(dry_in)* (1.0f + in_BassCut_k*-2.5f);
acc = lp_dly1.getTap(0) * rv_time; // get DLY1 output acc = lp_dly1.getTap(0) * rv_time;
lp_out1 = flt_lp1.process(acc); // filter it lp_out1 = flt_lp1.process(acc);
acc = sp_lp_allp1a.process(lp_out1); acc = sp_lp_allp1a.process(lp_out1);
acc = sp_lp_allp1b.process(acc); acc = sp_lp_allp1b.process(acc);

@ -69,10 +69,11 @@ public:
{ {
n = constrain(n, 0.0f, 1.0f); n = constrain(n, 0.0f, 1.0f);
n = map (n, 0.0f, 1.0f, 0.7f, rv_time_k_max); n = map (n, 0.0f, 1.0f, 0.7f, rv_time_k_max);
float32_t attn = map(n, 0.0f, rv_time_k_max, 0.5f, 0.2f); float32_t gain = map(n, 0.0f, rv_time_k_max, 0.5f, 0.2f);
inputGain_tmp = gain;
__disable_irq(); __disable_irq();
rv_time_k = n; rv_time_k = n;
input_attn = attn; inputGainSet = gain;
__enable_irq(); __enable_irq();
} }
@ -119,23 +120,46 @@ public:
__enable_irq(); __enable_irq();
} }
float32_t get_size(void) {return rv_time_k;} float32_t get_size(void) {return rv_time_k;}
typedef enum
{
BYPASS_MODE_PASS, // pass the input signal to the output
BYPASS_MODE_OFF, // mute the output
BYPASS_MODE_TRAILS // mutes the input only
}bypass_mode_t;
void bypass_setMode(bypass_mode_t m)
{
if (m <= BYPASS_MODE_TRAILS) bp_mode = m;
}
bypass_mode_t bypass_geMode() {return bp_mode;}
bool bypass_get(void) {return bp;} bool bypass_get(void) {return bp;}
void bypass_set(bool state) {bp = state;} void bypass_set(bool state)
{
bp = state;
if (bp && bp_mode==BYPASS_MODE_TRAILS)
{
inputGainSet = 0.0f;
}
if (bp == false) inputGainSet = inputGain_tmp;
}
bool bypass_tgl(void) bool bypass_tgl(void)
{ {
bp ^= 1; bypass_set(bp^1);
return bp; return bp;
} }
private: private:
audio_block_f32_t *inputQueueArray[2]; audio_block_f32_t *inputQueueArray[2];
float32_t input_attn; float32_t inputGainSet = 0.5f;
float32_t inputGain = 0.5f;
float32_t inputGain_tmp = 0.5f;
float32_t wet_gain; float32_t wet_gain;
float32_t dry_gain; float32_t dry_gain;
float32_t in_allp_k; // input allpass coeff (default 0.6) float32_t in_allp_k; // input allpass coeff (default 0.6)
float32_t chrp_allp_k[4] = {-0.7f, -0.65f, -0.6f, -0.5f}; float32_t chrp_allp_k[4] = {-0.7f, -0.65f, -0.6f, -0.5f};
bool bp = false; bool bp = false;
bypass_mode_t bp_mode = BYPASS_MODE_PASS;
bool cleanup_done = false; bool cleanup_done = false;
uint16_t chrp_alp1_idx[SPRVB_CHIRP_AMNT] = {0}; uint16_t chrp_alp1_idx[SPRVB_CHIRP_AMNT] = {0};
uint16_t chrp_alp2_idx[SPRVB_CHIRP_AMNT] = {0}; uint16_t chrp_alp2_idx[SPRVB_CHIRP_AMNT] = {0};
@ -167,6 +191,7 @@ private:
float32_t in_BassCut_k; float32_t in_BassCut_k;
float32_t lp_TrebleCut_k; float32_t lp_TrebleCut_k;
float32_t lp_BassCut_k; float32_t lp_BassCut_k;
AudioFilterShelvingLPHP flt_in; AudioFilterShelvingLPHP flt_in;
AudioFilterShelvingLPHP flt_lp1; AudioFilterShelvingLPHP flt_lp1;

@ -0,0 +1,114 @@
/**
* @file effect_xfaderStereo_F32.h
* @author Piotr Zapart
* @brief constant power crossfader for two stereo signals
* @version 0.1
* @date 2024-03-21
*
* @copyright Copyright (c) 2024
*
* 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 <https://www.gnu.org/licenses/>."
*/
#ifndef _EFFECT_XFADERSTEREO_F32_H_
#define _EFFECT_XFADERSTEREO_F32_H_
#include <arm_math.h>
#include <AudioStream_F32.h>
#include "basic_components.h"
class AudioEffectXfaderStereo_F32 : public AudioStream_F32
{
public:
AudioEffectXfaderStereo_F32(void) : AudioStream_F32(4, inputQueueArray_f32){};
AudioEffectXfaderStereo_F32(const AudioSettings_F32 &settings) : AudioStream_F32(2, inputQueueArray_f32){};
void mix(float32_t m)
{
float32_t gA, gB;
m = constrain(m, 0.0f, 1.0f);
mix_pwr(m, &gB, &gA);
__disable_irq()
gainA = gA;
gainB = gB;
__enable_irq();
}
void update()
{
audio_block_f32_t *blockLa, *blockRa, *blockLb, *blockRb;
audio_block_f32_t *blockOutLa, *blockOutRa,*blockOutLb, *blockOutRb;
blockLa = AudioStream_F32::receiveReadOnly_f32(0);
blockRa = AudioStream_F32::receiveReadOnly_f32(1);
blockLb = AudioStream_F32::receiveReadOnly_f32(2);
blockRb = AudioStream_F32::receiveReadOnly_f32(3);
if (!blockLa || !blockRa || !blockLb || !blockRb)
{
if (blockLa) AudioStream_F32::release(blockLa);
if (blockRa) AudioStream_F32::release(blockRa);
if (blockLb) AudioStream_F32::release(blockLb);
if (blockRb) AudioStream_F32::release(blockRb);
return;
}
// max A, B mited
if (gainA == 1.0f)
{
AudioStream_F32::transmit(blockLa, 0);
AudioStream_F32::transmit(blockRa, 1);
AudioStream_F32::release(blockLa);
AudioStream_F32::release(blockRa);
AudioStream_F32::release(blockLb);
AudioStream_F32::release(blockRb);
return;
}
if (gainB == 1.0f)
{
AudioStream_F32::transmit(blockLb, 0);
AudioStream_F32::transmit(blockRb, 1);
AudioStream_F32::release(blockLa);
AudioStream_F32::release(blockRa);
AudioStream_F32::release(blockLb);
AudioStream_F32::release(blockRb);
return;
}
blockOutLa = AudioStream_F32::allocate_f32();
blockOutRa = AudioStream_F32::allocate_f32();
blockOutLb = AudioStream_F32::allocate_f32();
blockOutRb = AudioStream_F32::allocate_f32();
if (!blockOutLa || !blockOutRa || !blockOutLa || !blockOutRa)
{
if (blockOutLa) AudioStream_F32::release(blockOutLa);
if (blockOutRa) AudioStream_F32::release(blockOutRa);
if (blockOutLb) AudioStream_F32::release(blockOutLb);
if (blockOutRb) AudioStream_F32::release(blockOutRb);
return;
}
arm_scale_f32(blockLa->data, gainA, blockOutLa->data, blockOutLa->length);
arm_scale_f32(blockRa->data, gainA, blockOutRa->data, blockOutRa->length);
arm_scale_f32(blockLb->data, gainB, blockOutLb->data, blockOutLb->length);
arm_scale_f32(blockRb->data, gainB, blockOutRb->data, blockOutRb->length);
arm_add_f32(blockOutLa->data, blockOutLb->data, blockOutLa->data, blockOutLa->length);
arm_add_f32(blockOutRa->data, blockOutRb->data, blockOutRa->data, blockOutRa->length);
AudioStream_F32::transmit(blockOutLa, 0);
AudioStream_F32::transmit(blockOutRa, 1);
AudioStream_F32::release(blockLa);
AudioStream_F32::release(blockRa);
AudioStream_F32::release(blockLb);
AudioStream_F32::release(blockRb);
AudioStream_F32::release(blockOutLa);
AudioStream_F32::release(blockOutRa);
AudioStream_F32::release(blockOutLb);
AudioStream_F32::release(blockOutRb);
}
private:
audio_block_f32_t *inputQueueArray_f32[4];
float32_t gainA = 1.0f;
float32_t gainB = 0.0f;
};
#endif // _EFFECT_XFADERSTEREO_F32_H_

@ -15,6 +15,7 @@
// - Uses 4 first order filters in series, should give 24dB per octave // - Uses 4 first order filters in series, should give 24dB per octave
// - Now with P4 Denormal fix :) // - Now with P4 Denormal fix :)
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
// Teensy OpenAudio_ArduinoLibrary_F32 port: 01.2024 Piotr Zapart www.hexefx.com
#ifndef _FILTER_3BANDEQ_H_ #ifndef _FILTER_3BANDEQ_H_
#define _FILTER_3BANDEQ_H_ #define _FILTER_3BANDEQ_H_
@ -23,7 +24,6 @@
#include "AudioStream_F32.h" #include "AudioStream_F32.h"
#include "arm_math.h" #include "arm_math.h"
#include "mathDSP_F32.h" #include "mathDSP_F32.h"
#include "basic_components.h"
class AudioFilterEqualizer3band_F32 : public AudioStream_F32 class AudioFilterEqualizer3band_F32 : public AudioStream_F32
{ {

@ -0,0 +1,59 @@
#include "filter_biquadStereo_F32.h"
void AudioFilterBiquadStereo_F32::update(void)
{
audio_block_f32_t *blockL, *blockR, *blockOutL, *blockOutR;
blockL = AudioStream_F32::receiveWritable_f32(0);
blockR = AudioStream_F32::receiveWritable_f32(1);
// no input signal
if (!blockL || !blockR)
{
if (blockL) AudioStream_F32::release(blockL);
if (blockR) AudioStream_F32::release(blockR);
return;
}
if (bp || gain_dry == 1.0f || !doBiquad)
{
AudioStream_F32::transmit(blockL, 0);
AudioStream_F32::transmit(blockR, 1);
AudioStream_F32::release(blockL);
AudioStream_F32::release(blockR);
return;
}
blockOutL = AudioStream_F32::allocate_f32();
blockOutR = AudioStream_F32::allocate_f32();
if (!blockOutL || !blockOutR)
{
if (blockOutL) AudioStream_F32::release(blockOutL);
if (blockOutR) AudioStream_F32::release(blockOutR);
AudioStream_F32::release(blockL);
AudioStream_F32::release(blockR);
return;
}
arm_biquad_cascade_df1_f32(&iirL_inst, blockL->data, blockOutL->data, blockOutL->length);
arm_biquad_cascade_df1_f32(&iirR_inst, blockR->data, blockOutR->data, blockOutR->length);
if (gain_wet != 1.0f) // transmit wet only
{
arm_scale_f32(blockL->data, gain_dry, blockL->data, blockL->length); // dryL * gain_dry
arm_scale_f32(blockR->data, gain_dry, blockR->data, blockR->length); // dryR * gain_dry
arm_scale_f32(blockOutL->data, gain_wet, blockOutL->data, blockOutL->length); // wetL * gain_wet
arm_scale_f32(blockOutR->data, gain_wet, blockOutR->data, blockOutR->length); // wetR * gain_wet
arm_add_f32(blockL->data, blockOutL->data, blockOutL->data, blockOutL->length); // dryL+wetL
arm_add_f32(blockR->data, blockOutR->data, blockOutR->data, blockOutR->length); // dryR+wetR
}
if (makeup_gain != 1.0f)
{
arm_scale_f32(blockOutL->data, makeup_gain, blockOutL->data, blockOutL->length); // wetL * makeup gain
arm_scale_f32(blockOutR->data, makeup_gain, blockOutR->data, blockOutR->length); // wetR * makeup gain
}
AudioStream_F32::transmit(blockOutL, 0);
AudioStream_F32::transmit(blockOutR, 1);
AudioStream_F32::release(blockOutL);
AudioStream_F32::release(blockOutR);
AudioStream_F32::release(blockL);
AudioStream_F32::release(blockR);
}

@ -0,0 +1,258 @@
#ifndef _FILTER_BIQUADSTEREO_F32_H_
#define _FILTER_BIQUADSTEREO_F32_H_
#include "Arduino.h"
#include "AudioStream_F32.h"
#include "arm_math.h"
#include "basic_DSPutils.h"
// Changed Feb 2021
#define IIR_STEREO_MAX_STAGES 4
class AudioFilterBiquadStereo_F32 : public AudioStream_F32
{
// GUI: inputs:1, outputs:1 //this line used for automatic generation of GUI node
// GUI: shortName:IIR2
public:
AudioFilterBiquadStereo_F32(uint8_t stages=IIR_STEREO_MAX_STAGES) : AudioStream_F32(2, inputQueueArray), numStagesUsed(stages)
{
setSampleRate_Hz(AUDIO_SAMPLE_RATE_EXACT);
doClassInit();
}
AudioFilterBiquadStereo_F32(const AudioSettings_F32 &settings, uint8_t stages=IIR_STEREO_MAX_STAGES) : AudioStream_F32(2, inputQueueArray), numStagesUsed(stages)
{
setSampleRate_Hz(settings.sample_rate_Hz);
doClassInit();
}
void doClassInit(void)
{
memset(&coeff32[0], 0, 5 * IIR_STEREO_MAX_STAGES * sizeof(coeff32[0]));
for (int ii = 0; ii < 4; ii++)
{
coeff32[5 * ii] = 1.0f; // b0 = 1 for pass through
}
arm_biquad_cascade_df1_init_f32(&iirL_inst, numStagesUsed, &coeff32[0], &stateL_F32[0]);
arm_biquad_cascade_df1_init_f32(&iirR_inst, numStagesUsed, &coeff32[0], &stateR_F32[0]);
doBiquad = false; // This is the way to jump over the biquad
}
// Up to 4 stages are allowed. Coefficients, either by design function
// or from direct setCoefficients() need to be added to the double array
// and also to the float
void setCoefficients(int iStage, double *cf)
{
if (iStage > numStagesUsed)
{
if (Serial)
{
Serial.print("AudioFilterBiquad_F32: setCoefficients:");
Serial.println(" *** MaxStages Error");
}
return;
}
for (int ii = 0; ii < 5; ii++)
{
coeff32[ii + 5 * iStage] = (float)cf[ii]; // and of floats
}
doBiquad = true;
}
void end(void)
{
doBiquad = false;
}
void setSampleRate_Hz(float _fs_Hz) { sampleRate_Hz = _fs_Hz; }
// Deprecated
void setBlockDC(void)
{
// https://www.keil.com/pack/doc/CMSIS/DSP/html/group__BiquadCascadeDF1.html#ga8e73b69a788e681a61bccc8959d823c5
// Use matlab to compute the coeff for HP at 40Hz: [b,a]=butter(2,40/(44100/2),'high'); %assumes fs_Hz = 44100
double b[] = {8.173653471988667e-01, -1.634730694397733e+00, 8.173653471988667e-01}; // from Matlab
double a[] = {1.000000000000000e+00, -1.601092394183619e+00, 6.683689946118476e-01}; // from Matlab
setFilterCoeff_Matlab(b, a);
}
void setFilterCoeff_Matlab(double b[], double a[])
{ // one stage of N=2 IIR
double coeff[5];
// https://www.keil.com/pack/doc/CMSIS/DSP/html/group__BiquadCascadeDF1.html#ga8e73b69a788e681a61bccc8959d823c5
// Use matlab to compute the coeff, such as: [b,a]=butter(2,20/(44100/2),'high'); %assumes fs_Hz = 44100
coeff[0] = b[0];
coeff[1] = b[1];
coeff[2] = b[2]; // here are the matlab "b" coefficients
coeff[3] = -a[1];
coeff[4] = -a[2]; // the DSP needs the "a" terms to have opposite sign vs Matlab
setCoefficients(0, coeff);
}
// Compute common filter functions
// http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
// void setLowpass(uint32_t stage, float frequency, float q = 0.7071) {
void setLowpass(int stage, float frequency, float q)
{
double coeff[5];
double w0 = frequency * (2 * 3.141592654 / sampleRate_Hz);
double sinW0 = sin(w0);
double alpha = sinW0 / ((double)q * 2.0);
double cosW0 = cos(w0);
double scale = 1.0 / (1.0 + alpha); // which is equal to 1.0 / a0
/* b0 */ coeff[0] = ((1.0 - cosW0) / 2.0) * scale;
/* b1 */ coeff[1] = (1.0 - cosW0) * scale;
/* b2 */ coeff[2] = coeff[0];
/* a1 */ coeff[3] = -(-2.0 * cosW0) * scale;
/* a2 */ coeff[4] = -(1.0 - alpha) * scale;
setCoefficients(stage, coeff);
}
void setHighpass(uint32_t stage, float frequency, float q)
{
double coeff[5];
double w0 = frequency * (2 * 3.141592654 / sampleRate_Hz);
double sinW0 = sin(w0);
double alpha = sinW0 / ((double)q * 2.0);
double cosW0 = cos(w0);
double scale = 1.0 / (1.0 + alpha);
/* b0 */ coeff[0] = ((1.0 + cosW0) / 2.0) * scale;
/* b1 */ coeff[1] = -(1.0 + cosW0) * scale;
/* b2 */ coeff[2] = coeff[0];
/* a1 */ coeff[3] = -(-2.0 * cosW0) * scale;
/* a2 */ coeff[4] = -(1.0 - alpha) * scale;
setCoefficients(stage, coeff);
}
void setBandpass(uint32_t stage, float frequency, float q)
{
double coeff[5];
double w0 = frequency * (2 * 3.141592654 / sampleRate_Hz);
double sinW0 = sin(w0);
double alpha = sinW0 / ((double)q * 2.0);
double cosW0 = cos(w0);
double scale = 1.0 / (1.0 + alpha);
/* b0 */ coeff[0] = alpha * scale;
/* b1 */ coeff[1] = 0;
/* b2 */ coeff[2] = (-alpha) * scale;
/* a1 */ coeff[3] = -(-2.0 * cosW0) * scale;
/* a2 */ coeff[4] = -(1.0 - alpha) * scale;
setCoefficients(stage, coeff);
}
// frequency in Hz. q makes the response stay close to 0.0dB until
// close to the notch frequency. q up to 100 or more seem stable.
void setNotch(uint32_t stage, float frequency, float q)
{
double coeff[5];
double w0 = frequency * (2 * 3.141592654 / sampleRate_Hz);
double sinW0 = sin(w0);
double alpha = sinW0 / ((double)q * 2.0);
double cosW0 = cos(w0);
double scale = 1.0 / (1.0 + alpha); // which is equal to 1.0 / a0
/* b0 */ coeff[0] = scale;
/* b1 */ coeff[1] = (-2.0 * cosW0) * scale;
/* b2 */ coeff[2] = coeff[0];
/* a1 */ coeff[3] = -(-2.0 * cosW0) * scale;
/* a2 */ coeff[4] = -(1.0 - alpha) * scale;
setCoefficients(stage, coeff);
}
void setLowShelf(uint32_t stage, float frequency, float gain, float slope)
{
double coeff[5];
double a = pow(10.0, gain / 40.0);
double w0 = frequency * (2 * 3.141592654 / sampleRate_Hz);
double sinW0 = sin(w0);
// double alpha = (sinW0 * sqrt((a+1/a)*(1/slope-1)+2) ) / 2.0;
double cosW0 = cos(w0);
// generate three helper-values (intermediate results):
double sinsq = sinW0 * sqrt((pow(a, 2.0) + 1.0) * (1.0 / slope - 1.0) + 2.0 * a);
double aMinus = (a - 1.0) * cosW0;
double aPlus = (a + 1.0) * cosW0;
double scale = 1.0 / ((a + 1.0) + aMinus + sinsq);
/* b0 */ coeff[0] = a * ((a + 1.0) - aMinus + sinsq) * scale;
/* b1 */ coeff[1] = 2.0 * a * ((a - 1.0) - aPlus) * scale;
/* b2 */ coeff[2] = a * ((a + 1.0) - aMinus - sinsq) * scale;
/* a1 */ coeff[3] = 2.0 * ((a - 1.0) + aPlus) * scale;
/* a2 */ coeff[4] = -((a + 1.0) + aMinus - sinsq) * scale;
setCoefficients(stage, coeff);
}
void setHighShelf(uint32_t stage, float frequency, float gain, float slope)
{
double coeff[5];
double a = pow(10.0, gain / 40.0);
double w0 = frequency * (2 * 3.141592654 / sampleRate_Hz);
double sinW0 = sin(w0);
// double alpha = (sinW0 * sqrt((a+1/a)*(1/slope-1)+2) ) / 2.0;
double cosW0 = cos(w0);
// generate three helper-values (intermediate results):
double sinsq = sinW0 * sqrt((pow(a, 2.0) + 1.0) * (1.0 / slope - 1.0) + 2.0 * a);
double aMinus = (a - 1.0) * cosW0;
double aPlus = (a + 1.0) * cosW0;
double scale = 1.0 / ((a + 1.0) - aMinus + sinsq);
/* b0 */ coeff[0] = a * ((a + 1.0) + aMinus + sinsq) * scale;
/* b1 */ coeff[1] = -2.0 * a * ((a - 1.0) + aPlus) * scale;
/* b2 */ coeff[2] = a * ((a + 1.0) + aMinus - sinsq) * scale;
/* a1 */ coeff[3] = -2.0 * ((a - 1.0) - aPlus) * scale;
/* a2 */ coeff[4] = -((a + 1.0) - aMinus - sinsq) * scale;
setCoefficients(stage, coeff);
}
void update(void);
void mix(float m)
{
float g_wet, g_dry;
m = constrain(m, 0.0f, 1.0f);
mix_pwr(m, &g_wet, &g_dry);
__disable_irq();
gain_wet = g_wet;
gain_dry = g_dry;
__enable_irq();
}
void makeupGain(float g)
{
__disable_irq();
makeup_gain = g;
__enable_irq();
}
void bypass_set(bool state)
{
__disable_irq();
bp = state;
__enable_irq();
}
bool bypass_tgl(void)
{
bool bp_new = bp ^ 1;
__disable_irq();
bp = bp_new;
__enable_irq();
return bp;
}
private:
audio_block_f32_t *inputQueueArray[2];
bool bp = false;
float coeff32[5 * IIR_STEREO_MAX_STAGES]; // Local copies to be transferred with begin()
float stateL_F32[4 * IIR_STEREO_MAX_STAGES];
float stateR_F32[4 * IIR_STEREO_MAX_STAGES];
float sampleRate_Hz = AUDIO_SAMPLE_RATE_EXACT; // default. from AudioStream.h??
const uint8_t numStagesUsed;
bool doBiquad = false;
float gain_dry = 0.0f;
float gain_wet = 1.0f;
float makeup_gain = 1.0f;
/* Info - The structure from arm_biquad_casd_df1_inst_f32 consists of
* uint32_t numStages;
* const float32_t *pCoeffs; //Points to the array of coefficients, length 5*numStages.
* float32_t *pState; //Points to the array of state variables, length 4*numStages.
*/
// ARM DSP Math library filter instance.
arm_biquad_casd_df1_inst_f32 iirL_inst;
arm_biquad_casd_df1_inst_f32 iirR_inst;
};
#endif // _FILTER_BIQUADSTEREO_F32_H_

@ -113,9 +113,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
* *
* 01.2024 - added * 01.2024 - added bypass system, Piot rZapart www.hexefx.com
*
*
*/ */
#ifndef _FILTEREQUALIZER_F32_H_ #ifndef _FILTEREQUALIZER_F32_H_

@ -56,7 +56,30 @@ void AudioFilterIRCabsim_F32::update()
if (blockR) AudioStream_F32::release(blockR); if (blockR) AudioStream_F32::release(blockR);
return; return;
} }
#ifdef USE_IR_ISR_LOAD
switch(ir_loadState)
{
case IR_LOAD_START:
ptr_fmask = &fmask[0][0];
ptr_fftout = &fftout[0];
memset(ptr_fftout, 0, nfor*512*4); // clear fftout array
memset(fftin, 0, 512 * 4); // clear fftin array
ir_loadState = IR_LOAD_STEP1;
break;
case IR_LOAD_STEP1:
init_partitioned_filter_masks(irPtrTable[ir_idx]);
ir_loadState = IR_LOAD_STEP2;
break;
case IR_LOAD_STEP2:
delay.reset();
ir_loaded = 1;
ir_loadState = IR_LOAD_FINISHED;
break;
case IR_LOAD_FINISHED:
default: break;
}
#endif
if (!ir_loaded) // ir not loaded yet or bypass mode if (!ir_loaded) // ir not loaded yet or bypass mode
{ {
// bypass clean signal // bypass clean signal
@ -66,6 +89,7 @@ void AudioFilterIRCabsim_F32::update()
AudioStream_F32::release(blockR); AudioStream_F32::release(blockR);
return; return;
} }
if (first_block) // fill real & imaginaries with zeros for the first BLOCKSIZE samples if (first_block) // fill real & imaginaries with zeros for the first BLOCKSIZE samples
{ {
memset(&fftin[0], 0, blockL->length*sizeof(float32_t)*4); memset(&fftin[0], 0, blockL->length*sizeof(float32_t)*4);
@ -136,7 +160,6 @@ void AudioFilterIRCabsim_F32::update()
// apply post EQ, restore the channel R phase, reduce the gain a bit // apply post EQ, restore the channel R phase, reduce the gain a bit
if (doubleTrack) if (doubleTrack)
{ {
arm_fir_f32(&FIR_postL, blockL->data, blockL->data, blockL->length); arm_fir_f32(&FIR_postL, blockL->data, blockL->data, blockL->length);
arm_fir_f32(&FIR_postR, blockR->data, blockR->data, blockR->length); arm_fir_f32(&FIR_postR, blockR->data, blockR->data, blockR->length);
arm_scale_f32(blockR->data, -doubler_gainR, blockR->data, blockR->length); arm_scale_f32(blockR->data, -doubler_gainR, blockR->data, blockR->length);
@ -156,6 +179,7 @@ void AudioFilterIRCabsim_F32::ir_register(const float32_t *irPtr, uint8_t positi
irPtrTable[position] = irPtr; irPtrTable[position] = irPtr;
} }
void AudioFilterIRCabsim_F32::ir_load(uint8_t idx) void AudioFilterIRCabsim_F32::ir_load(uint8_t idx)
{ {
const float32_t *newIrPtr = NULL; const float32_t *newIrPtr = NULL;
@ -173,6 +197,17 @@ void AudioFilterIRCabsim_F32::ir_load(uint8_t idx)
{ {
return; return;
} }
#ifdef USE_IR_ISR_LOAD
nc = newIrPtr[0];
uint32_t _nfor = nc / IR_BUFFER_SIZE;
if (_nfor > nforMax) _nfor = nforMax;
__disable_irq()
nfor = _nfor;
ir_loadState = IR_LOAD_START;
__enable_irq();
ir_length_ms = (1000.0f * nfor * (float32_t)AUDIO_BLOCK_SAMPLES) / AUDIO_SAMPLE_RATE_EXACT;
#else
AudioNoInterrupts(); AudioNoInterrupts();
nc = newIrPtr[0]; nc = newIrPtr[0];
nfor = nc / IR_BUFFER_SIZE; nfor = nc / IR_BUFFER_SIZE;
@ -188,8 +223,11 @@ void AudioFilterIRCabsim_F32::ir_load(uint8_t idx)
delay.reset(); delay.reset();
ir_loaded = 1; ir_loaded = 1;
AudioInterrupts(); AudioInterrupts();
#endif
} }
void AudioFilterIRCabsim_F32::init_partitioned_filter_masks(const float32_t *irPtr) void AudioFilterIRCabsim_F32::init_partitioned_filter_masks(const float32_t *irPtr)
{ {
const static arm_cfft_instance_f32 *maskS; const static arm_cfft_instance_f32 *maskS;

@ -41,6 +41,9 @@
#define IR_N_B (1) #define IR_N_B (1)
#define IR_MAX_REG_NUM 11 // max number of registered IRs #define IR_MAX_REG_NUM 11 // max number of registered IRs
#define USE_IR_ISR_LOAD
class AudioFilterIRCabsim_F32 : public AudioStream_F32 class AudioFilterIRCabsim_F32 : public AudioStream_F32
{ {
public: public:
@ -155,6 +158,17 @@ private:
0.154734746f, 0.35352844f, 0.441179603f, 0.35352844f, 0.154734746f, -0.0208595414f, 0.154734746f, 0.35352844f, 0.441179603f, 0.35352844f, 0.154734746f, -0.0208595414f,
-0.0834295526f, -0.0470990688f, 0.0108086104f, 0.0319586769f, 0.0158470348f, -0.00560652791f, -0.0834295526f, -0.0470990688f, 0.0108086104f, 0.0319586769f, 0.0158470348f, -0.00560652791f,
-0.0112718018f, -0.00476867426f, 0.0013392308f, 0.00207542209f, 0.000503875781f, 0.0f }; -0.0112718018f, -0.00476867426f, 0.0013392308f, 0.00207542209f, 0.000503875781f, 0.0f };
typedef enum
{
IR_LOAD_START,
IR_LOAD_STEP1,
IR_LOAD_STEP2,
IR_LOAD_FINISHED
}ir_loadState_t;
ir_loadState_t ir_loadState = IR_LOAD_FINISHED;
}; };

@ -1,13 +1,19 @@
#ifndef _HEXEFX_AUDIO_H #ifndef _HEXEFX_AUDIO_H
#define _HEXEFX_AUDIO_H #define _HEXEFX_AUDIO_H
#include "control_WM8731_F32.h"
#include "control_SGTL5000_F32.h"
#include "input_i2s2_F32.h" #include "input_i2s2_F32.h"
#include "output_i2s2_F32.h" #include "output_i2s2_F32.h"
#include "switch_selectorStereo_F32.h"
#include "filter_ir_cabsim_F32.h" #include "filter_ir_cabsim_F32.h"
#include "filter_tonestackStereo_F32.h" #include "filter_tonestackStereo_F32.h"
#include "filter_equalizer_F32.h" #include "filter_equalizer_F32.h"
#include "filter_3bandeq.h" #include "filter_3bandeq.h"
#include "filter_biquadStereo_F32.h"
#include "effect_gainStereo_F32.h" #include "effect_gainStereo_F32.h"
#include "effect_platereverb_F32.h" #include "effect_platereverb_F32.h"
@ -17,6 +23,9 @@
#include "effect_infphaser_F32.h" #include "effect_infphaser_F32.h"
#include "effect_phaserStereo_F32.h" #include "effect_phaserStereo_F32.h"
#include "effect_noiseGateStereo_F32.h" #include "effect_noiseGateStereo_F32.h"
#include "effect_delaystereo.h" #include "effect_delaystereo_F32.h"
#include "effect_compressorStereo_F32.h"
#include "effect_guitarBooster_F32.h"
#include "effect_xfaderStereo_F32.h"
#endif // _HEXEFX_AUDIO_H #endif // _HEXEFX_AUDIO_H

@ -1,6 +1,6 @@
/* /*
* input_i2s2_f32.cpp * input_i2s2_f32.cpp
* *
* Audio Library for Teensy 3.X * Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
* *
@ -26,23 +26,23 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
/* /*
* Extended by Chip Audette, OpenAudio, May 2019 * Extended by Chip Audette, OpenAudio, May 2019
* Converted to F32 and to variable audio block length * Converted to F32 and to variable audio block length
* The F32 conversion is under the MIT License. Use at your own risk. * The F32 conversion is under the MIT License. Use at your own risk.
*/ */
// Updated OpenAudio F32 with this version from Chip Audette's Tympan Library Jan 2021 RSL // Updated OpenAudio F32 with this version from Chip Audette's Tympan Library Jan 2021 RSL
#include <Arduino.h> //do we really need this? (Chip: 2020-10-31) #include <Arduino.h> //do we really need this? (Chip: 2020-10-31)
#include "input_i2s2_F32.h" #include "input_i2s2_F32.h"
#include "output_i2s2_F32.h" #include "output_i2s2_F32.h"
#include "basic_DSPutils.h"
#include <arm_math.h> #include <arm_math.h>
//DMAMEM __attribute__((aligned(32))) // DMAMEM __attribute__((aligned(32)))
static uint32_t i2s2_rx_buffer[AUDIO_BLOCK_SAMPLES]; //good for 16-bit audio samples coming in from teh AIC. 32-bit transfers will need this to be bigger. static uint64_t i2s2_rx_buffer[AUDIO_BLOCK_SAMPLES] __attribute__((aligned(32))); // good for 16-bit audio samples coming in from teh AIC. 32-bit transfers will need this to be bigger.
audio_block_f32_t * AudioInputI2S2_F32::block_left_f32 = NULL; audio_block_f32_t *AudioInputI2S2_F32::block_left_f32 = NULL;
audio_block_f32_t * AudioInputI2S2_F32::block_right_f32 = NULL; audio_block_f32_t *AudioInputI2S2_F32::block_right_f32 = NULL;
uint16_t AudioInputI2S2_F32::block_offset = 0; uint16_t AudioInputI2S2_F32::block_offset = 0;
bool AudioInputI2S2_F32::update_responsibility = false; bool AudioInputI2S2_F32::update_responsibility = false;
DMAChannel AudioInputI2S2_F32::dma(false); DMAChannel AudioInputI2S2_F32::dma(false);
@ -53,164 +53,123 @@ unsigned long AudioInputI2S2_F32::update_counter = 0;
float AudioInputI2S2_F32::sample_rate_Hz = AUDIO_SAMPLE_RATE; float AudioInputI2S2_F32::sample_rate_Hz = AUDIO_SAMPLE_RATE;
int AudioInputI2S2_F32::audio_block_samples = AUDIO_BLOCK_SAMPLES; int AudioInputI2S2_F32::audio_block_samples = AUDIO_BLOCK_SAMPLES;
//#for 16-bit transfers #define I2S2_BUFFER_TO_USE_BYTES (AudioOutputI2S2_F32::audio_block_samples * sizeof(i2s2_rx_buffer[0]))
#define I2S2_BUFFER_TO_USE_BYTES (AudioOutputI2S2_F32::audio_block_samples*sizeof(i2s2_rx_buffer[0]))
//#for 32-bit transfers
//#define I2S2_BUFFER_TO_USE_BYTES (AudioOutputI2S_F32::audio_block_samples*2*sizeof(i2s_rx_buffer[0]))
void AudioInputI2S2_F32::begin(void) { // --------------------------------------------------------------------------------
bool transferUsing32bit = false; void AudioInputI2S2_F32::begin()
begin(transferUsing32bit); {
}
void AudioInputI2S2_F32::begin(bool transferUsing32bit) {
dma.begin(true); // Allocate the DMA channel first dma.begin(true); // Allocate the DMA channel first
AudioOutputI2S2_F32::sample_rate_Hz = sample_rate_Hz; //these were given in the AudioSettings in the contructor AudioOutputI2S2_F32::sample_rate_Hz = sample_rate_Hz; // these were given in the AudioSettings in the contructor
AudioOutputI2S2_F32::audio_block_samples = audio_block_samples;//these were given in the AudioSettings in the contructor AudioOutputI2S2_F32::audio_block_samples = audio_block_samples; // these were given in the AudioSettings in the contructor
//block_left_1st = NULL;
//block_right_1st = NULL;
// TODO: should we set & clear the I2S_RCSR_SR bit here? // TODO: should we set & clear the I2S_RCSR_SR bit here?
AudioOutputI2S2_F32::config_i2s(transferUsing32bit); AudioOutputI2S2_F32::config_i2s();
#if defined(__IMXRT1062__) CORE_PIN5_CONFIG = 2; // EMC_08, 2=SAI2_RX_DATA, page 434
CORE_PIN5_CONFIG = 2; //EMC_08, 2=SAI2_RX_DATA, page 434
IOMUXC_SAI2_RX_DATA0_SELECT_INPUT = 0; // 0=GPIO_EMC_08_ALT2, page 876 IOMUXC_SAI2_RX_DATA0_SELECT_INPUT = 0; // 0=GPIO_EMC_08_ALT2, page 876
dma.TCD->SADDR = (void *)((uint32_t)&I2S2_RDR0 + 2); dma.TCD->SADDR = (void *)((uint32_t)&I2S2_RDR0);
dma.TCD->SOFF = 0; dma.TCD->SOFF = 0;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
dma.TCD->NBYTES_MLNO = 2; dma.TCD->NBYTES_MLNO = 4;
dma.TCD->SLAST = 0; dma.TCD->SLAST = 0;
dma.TCD->DADDR = i2s2_rx_buffer; dma.TCD->DADDR = i2s2_rx_buffer;
dma.TCD->DOFF = 2; dma.TCD->DOFF = 4;
//dma.TCD->CITER_ELINKNO = sizeof(i2s_rx_buffer) / 2; //original from Teensy Audio Library dma.TCD->CITER_ELINKNO = I2S2_BUFFER_TO_USE_BYTES / 4;
dma.TCD->CITER_ELINKNO = I2S2_BUFFER_TO_USE_BYTES / 2;
//dma.TCD->DLASTSGA = -sizeof(i2s_rx_buffer); //original from Teensy Audio Library
dma.TCD->DLASTSGA = -I2S2_BUFFER_TO_USE_BYTES; dma.TCD->DLASTSGA = -I2S2_BUFFER_TO_USE_BYTES;
//dma.TCD->BITER_ELINKNO = sizeof(i2s_rx_buffer) / 2; //original from Teensy Audio Library dma.TCD->BITER_ELINKNO = I2S2_BUFFER_TO_USE_BYTES / 4;
dma.TCD->BITER_ELINKNO = I2S2_BUFFER_TO_USE_BYTES / 2;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_RX); dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_RX);
I2S2_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; // page 2099 I2S2_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; // page 2099
I2S2_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // page 2087
#endif
update_responsibility = update_setup(); update_responsibility = update_setup();
dma.enable(); dma.enable();
dma.attachInterrupt(isr); dma.attachInterrupt(isr);
update_counter = 0; update_counter = 0;
} }
// ------------------------------ RX DMA ISR ------------------------------------------
void AudioInputI2S2_F32::isr(void) void AudioInputI2S2_F32::isr(void)
{ {
uint32_t daddr, offset; uint32_t daddr, offset;
const int16_t *src, *end; const int32_t *src, *end;
//int16_t *dest_left, *dest_right;
//audio_block_t *left, *right;
float32_t *dest_left_f32, *dest_right_f32; float32_t *dest_left_f32, *dest_right_f32;
audio_block_f32_t *left_f32, *right_f32; audio_block_f32_t *left_f32, *right_f32;
#if defined(KINETISK) || defined(__IMXRT1062__)
daddr = (uint32_t)(dma.TCD->DADDR); daddr = (uint32_t)(dma.TCD->DADDR);
#endif
dma.clearInterrupt(); dma.clearInterrupt();
//Serial.println("isr"); if (daddr < (uint32_t)i2s2_rx_buffer + I2S2_BUFFER_TO_USE_BYTES / 2)
{
//if (daddr < (uint32_t)i2s_rx_buffer + sizeof(i2s_rx_buffer) / 2) { //original Teensy Audio Library
if (daddr < (uint32_t)i2s2_rx_buffer + I2S2_BUFFER_TO_USE_BYTES / 2) {
// DMA is receiving to the first half of the buffer // DMA is receiving to the first half of the buffer
// need to remove data from the second half // need to remove data from the second half
//src = (int16_t *)&i2s_rx_buffer[AUDIO_BLOCK_SAMPLES/2]; //original Teensy Audio Library src = (int32_t *)&i2s2_rx_buffer[audio_block_samples / 2];
//end = (int16_t *)&i2s_rx_buffer[AUDIO_BLOCK_SAMPLES]; //original Teensy Audio Library end = (int32_t *)&i2s2_rx_buffer[audio_block_samples];
src = (int16_t *)&i2s2_rx_buffer[audio_block_samples/2]; update_counter++; // let's increment the counter here to ensure that we get every ISR resulting in audio
end = (int16_t *)&i2s2_rx_buffer[audio_block_samples];
update_counter++; //let's increment the counter here to ensure that we get every ISR resulting in audio
if (AudioInputI2S2_F32::update_responsibility) AudioStream_F32::update_all(); if (AudioInputI2S2_F32::update_responsibility) AudioStream_F32::update_all();
} else { }
else
{
// DMA is receiving to the second half of the buffer // DMA is receiving to the second half of the buffer
// need to remove data from the first half // need to remove data from the first half
src = (int16_t *)&i2s2_rx_buffer[0]; src = (int32_t *)&i2s2_rx_buffer[0];
//end = (int16_t *)&i2s_rx_buffer[AUDIO_BLOCK_SAMPLES/2]; //original Teensy Audio Library end = (int32_t *)&i2s2_rx_buffer[audio_block_samples / 2];
end = (int16_t *)&i2s2_rx_buffer[audio_block_samples/2];
} }
left_f32 = AudioInputI2S2_F32::block_left_f32; left_f32 = AudioInputI2S2_F32::block_left_f32;
right_f32 = AudioInputI2S2_F32::block_right_f32; right_f32 = AudioInputI2S2_F32::block_right_f32;
if (left_f32 != NULL && right_f32 != NULL) { if (left_f32 != NULL && right_f32 != NULL)
{
offset = AudioInputI2S2_F32::block_offset; offset = AudioInputI2S2_F32::block_offset;
//if (offset <= (uint32_t)(AUDIO_BLOCK_SAMPLES/2)) { //original Teensy Audio Library if (offset <= ((uint32_t)audio_block_samples / 2))
if (offset <= ((uint32_t) audio_block_samples/2)) { {
dest_left_f32 = &(left_f32->data[offset]); dest_left_f32 = &(left_f32->data[offset]);
dest_right_f32 = &(right_f32->data[offset]); dest_right_f32 = &(right_f32->data[offset]);
//AudioInputI2S2_F32::block_offset = offset + AUDIO_BLOCK_SAMPLES/2; //original Teensy Audio Library AudioInputI2S2_F32::block_offset = offset + audio_block_samples / 2;
AudioInputI2S2_F32::block_offset = offset + audio_block_samples/2; do
do { {
//Serial.println(*src); *dest_left_f32++ = (float32_t)*src++;
//n = *src++; *dest_right_f32++ = (float32_t)*src++;
//*dest_left++ = (int16_t)n;
//*dest_right++ = (int16_t)(n >> 16);
*dest_left_f32++ = (float32_t) *src++;
*dest_right_f32++ = (float32_t) *src++;
} while (src < end); } while (src < end);
} }
} }
} }
// --------------------------------------------------------------------------------
#define I16_TO_F32_NORM_FACTOR (3.051850947599719e-05) //which is 1/32767 void AudioInputI2S2_F32::update_1chan(int chan, audio_block_f32_t *&out_f32)
void AudioInputI2S2_F32::scale_i16_to_f32( float32_t *p_i16, float32_t *p_f32, int len) { {
for (int i=0; i<len; i++) { *p_f32++ = ((*p_i16++) * I16_TO_F32_NORM_FACTOR); } if (!out_f32)
} return;
#define I24_TO_F32_NORM_FACTOR (1.192093037616377e-07) //which is 1/(2^23 - 1) // scale the float values so that the maximum possible audio values span -1.0 to + 1.0
void AudioInputI2S2_F32::scale_i24_to_f32( float32_t *p_i24, float32_t *p_f32, int len) { arm_scale_f32(out_f32->data, I32_TO_F32_NORM_FACTOR, out_f32->data, audio_block_samples);
for (int i=0; i<len; i++) { *p_f32++ = ((*p_i24++) * I24_TO_F32_NORM_FACTOR); } // prepare to transmit by setting the update_counter (which helps tell if data is skipped or out-of-order)
}
#define I32_TO_F32_NORM_FACTOR (4.656612875245797e-10) //which is 1/(2^31 - 1)
void AudioInputI2S2_F32::scale_i32_to_f32( float32_t *p_i32, float32_t *p_f32, int len) {
for (int i=0; i<len; i++) { *p_f32++ = ((*p_i32++) * I32_TO_F32_NORM_FACTOR); }
}
void AudioInputI2S2_F32::update_1chan(int chan, audio_block_f32_t *&out_f32) {
if (!out_f32) return;
//scale the float values so that the maximum possible audio values span -1.0 to + 1.0
//scale_i32_to_f32(out_f32->data, out_f32->data, audio_block_samples);
scale_i16_to_f32(out_f32->data, out_f32->data, audio_block_samples);
//prepare to transmit by setting the update_counter (which helps tell if data is skipped or out-of-order)
out_f32->id = update_counter; out_f32->id = update_counter;
AudioStream_F32::transmit(out_f32, chan); // transmit the f32 data!
//transmit the f32 data! AudioStream_F32::release(out_f32); // release the memory blocks
AudioStream_F32::transmit(out_f32,chan);
//release the memory blocks
AudioStream_F32::release(out_f32);
} }
// --------------------------------------------------------------------------------
void AudioInputI2S2_F32::update(void) void AudioInputI2S2_F32::update(void)
{ {
static bool flag_beenSuccessfullOnce = false; static bool flag_beenSuccessfullOnce = false;
audio_block_f32_t *new_left=NULL, *new_right=NULL, *out_left=NULL, *out_right=NULL; audio_block_f32_t *new_left = NULL, *new_right = NULL, *out_left = NULL, *out_right = NULL;
new_left = AudioStream_F32::allocate_f32(); new_left = AudioStream_F32::allocate_f32();
new_right = AudioStream_F32::allocate_f32(); new_right = AudioStream_F32::allocate_f32();
if ((!new_left) || (!new_right)) { if ((!new_left) || (!new_right))
//ran out of memory. Clear and return! {
if (new_left) AudioStream_F32::release(new_left); // ran out of memory. Clear and return!
if (new_right) AudioStream_F32::release(new_right); if (new_left) AudioStream_F32::release(new_left);
new_left = NULL; new_right = NULL; if (new_right) AudioStream_F32::release(new_right);
new_left = NULL;
new_right = NULL;
flag_out_of_memory = 1; flag_out_of_memory = 1;
if (flag_beenSuccessfullOnce) Serial.println("Input_I2S_F32: update(): WARNING!!! Out of Memory."); if (flag_beenSuccessfullOnce)
} else { Serial.println("Input_I2S_F32: update(): WARNING!!! Out of Memory.");
flag_beenSuccessfullOnce = true;
} }
else {flag_beenSuccessfullOnce = true; }
__disable_irq(); __disable_irq();
if (block_offset >= audio_block_samples) { if (block_offset >= audio_block_samples)
{
// the DMA filled 2 blocks, so grab them and get the // the DMA filled 2 blocks, so grab them and get the
// 2 new blocks to the DMA, as quickly as possible // 2 new blocks to the DMA, as quickly as possible
out_left = block_left_f32; out_left = block_left_f32;
@ -219,28 +178,31 @@ void AudioInputI2S2_F32::update(void)
block_right_f32 = new_right; block_right_f32 = new_right;
block_offset = 0; block_offset = 0;
__enable_irq(); __enable_irq();
update_1chan(0, out_left); // uses audio_block_samples and update_counter
//update_counter++; //I chose to update it in the ISR instead. update_1chan(1, out_right); // uses audio_block_samples and update_counter
update_1chan(0,out_left); //uses audio_block_samples and update_counter }
update_1chan(1,out_right); //uses audio_block_samples and update_counter else if (new_left != NULL)
{
} else if (new_left != NULL) {
// the DMA didn't fill blocks, but we allocated blocks // the DMA didn't fill blocks, but we allocated blocks
if (block_left_f32 == NULL) { if (block_left_f32 == NULL)
{
// the DMA doesn't have any blocks to fill, so // the DMA doesn't have any blocks to fill, so
// give it the ones we just allocated // give it the ones we just allocated
block_left_f32 = new_left; block_left_f32 = new_left;
block_right_f32 = new_right; block_right_f32 = new_right;
block_offset = 0; block_offset = 0;
__enable_irq(); __enable_irq();
} else { }
else
{
// the DMA already has blocks, doesn't need these // the DMA already has blocks, doesn't need these
__enable_irq(); __enable_irq();
AudioStream_F32::release(new_left); AudioStream_F32::release(new_left);
AudioStream_F32::release(new_right); AudioStream_F32::release(new_right);
} }
} else { }
else
{
// The DMA didn't fill blocks, and we could not allocate // The DMA didn't fill blocks, and we could not allocate
// memory... the system is likely starving for memory! // memory... the system is likely starving for memory!
// Sadly, there's nothing we can do. // Sadly, there's nothing we can do.
@ -248,13 +210,11 @@ void AudioInputI2S2_F32::update(void)
} }
} }
/******************************************************************/ // --------------------------------------------------------------------------------
void AudioInputI2S2slave_F32::begin(void) void AudioInputI2S2slave_F32::begin(void)
{ {
dma.begin(true); // Allocate the DMA channel first dma.begin(true); // Allocate the DMA channel first
AudioOutputI2S2slave_F32::config_i2s(); AudioOutputI2S2slave_F32::config_i2s();
} }

@ -39,9 +39,10 @@
#include <Arduino.h> #include <Arduino.h>
#include <arm_math.h> #include <arm_math.h>
#include "AudioStream_F32.h" #include "AudioStream_F32.h"
#include "AudioStream.h" //Do we really need this?? (Chip, 2020-10-31)
#include "DMAChannel.h" #include "DMAChannel.h"
class AudioInputI2S2_F32 : public AudioStream_F32 class AudioInputI2S2_F32 : public AudioStream_F32
{ {
//GUI: inputs:0, outputs:2 //this line used for automatic generation of GUI nodes //GUI: inputs:0, outputs:2 //this line used for automatic generation of GUI nodes
@ -54,21 +55,15 @@ public:
} }
virtual void update(void); virtual void update(void);
static void scale_i16_to_f32( float32_t *p_i16, float32_t *p_f32, int len) ;
static void scale_i24_to_f32( float32_t *p_i24, float32_t *p_f32, int len) ;
static void scale_i32_to_f32( float32_t *p_i32, float32_t *p_f32, int len);
void begin(void); void begin(void);
void begin(bool);
void sub_begin_i32(void);
//void sub_begin_i16(void);
int get_isOutOfMemory(void) { return flag_out_of_memory; } int get_isOutOfMemory(void) { return flag_out_of_memory; }
void clear_isOutOfMemory(void) { flag_out_of_memory = 0; } void clear_isOutOfMemory(void) { flag_out_of_memory = 0; }
//friend class AudioOutputI2S_F32; bool get_update_responsibility() { return update_responsibility;}
protected: protected:
AudioInputI2S2_F32(int dummy): AudioStream_F32(0, NULL) {} // to be used only inside AudioInputI2Sslave !! AudioInputI2S2_F32(int dummy): AudioStream_F32(0, NULL) {} // to be used only inside AudioInputI2Sslave !!
static bool update_responsibility; static bool update_responsibility;
static DMAChannel dma; static DMAChannel dma;
static void isr_32(void);
static void isr(void); static void isr(void);
virtual void update_1chan(int, audio_block_f32_t *&); virtual void update_1chan(int, audio_block_f32_t *&);
private: private:
@ -86,7 +81,6 @@ class AudioInputI2S2slave_F32 : public AudioInputI2S2_F32
public: public:
AudioInputI2S2slave_F32(void) : AudioInputI2S2_F32(0) { begin(); } AudioInputI2S2slave_F32(void) : AudioInputI2S2_F32(0) { begin(); }
void begin(void); void begin(void);
friend void dma_ch1_isr(void);
}; };
#endif // _INPUT_I2S_F32_H_ #endif // _INPUT_I2S_F32_H_

@ -35,13 +35,8 @@
// Ported to I2S2, 12.2023 by Piotr Zapart www.hexefx.com - for teensy4.x only! // Ported to I2S2, 12.2023 by Piotr Zapart www.hexefx.com - for teensy4.x only!
#include "output_i2s2_F32.h" #include "output_i2s2_F32.h"
#include <arm_math.h> #include "basic_DSPutils.h"
#include <Audio.h> //to get access to Audio/utlity/imxrt_hw.h...do we really need this??? WEA 2020-10-31
float AudioOutputI2S2_F32::setI2SFreq_T3(const float freq_Hz)
{
return 0.0f;
}
audio_block_f32_t *AudioOutputI2S2_F32::block_left_1st = NULL; audio_block_f32_t *AudioOutputI2S2_F32::block_left_1st = NULL;
audio_block_f32_t *AudioOutputI2S2_F32::block_right_1st = NULL; audio_block_f32_t *AudioOutputI2S2_F32::block_right_1st = NULL;
@ -51,7 +46,7 @@ uint16_t AudioOutputI2S2_F32::block_left_offset = 0;
uint16_t AudioOutputI2S2_F32::block_right_offset = 0; uint16_t AudioOutputI2S2_F32::block_right_offset = 0;
bool AudioOutputI2S2_F32::update_responsibility = false; bool AudioOutputI2S2_F32::update_responsibility = false;
DMAChannel AudioOutputI2S2_F32::dma(false); DMAChannel AudioOutputI2S2_F32::dma(false);
DMAMEM __attribute__((aligned(32))) static uint32_t i2s2_tx_buffer[AUDIO_BLOCK_SAMPLES]; DMAMEM __attribute__((aligned(32))) static uint64_t i2s2_tx_buffer[AUDIO_BLOCK_SAMPLES];
float AudioOutputI2S2_F32::sample_rate_Hz = AUDIO_SAMPLE_RATE; float AudioOutputI2S2_F32::sample_rate_Hz = AUDIO_SAMPLE_RATE;
int AudioOutputI2S2_F32::audio_block_samples = AUDIO_BLOCK_SAMPLES; int AudioOutputI2S2_F32::audio_block_samples = AUDIO_BLOCK_SAMPLES;
@ -60,80 +55,66 @@ int AudioOutputI2S2_F32::audio_block_samples = AUDIO_BLOCK_SAMPLES;
#include <utility/imxrt_hw.h> //from Teensy Audio library. For set_audioClock() #include <utility/imxrt_hw.h> //from Teensy Audio library. For set_audioClock()
#endif #endif
// #for 16-bit transfers
#define I2S2_BUFFER_TO_USE_BYTES (AudioOutputI2S2_F32::audio_block_samples * sizeof(i2s2_tx_buffer[0])) #define I2S2_BUFFER_TO_USE_BYTES (AudioOutputI2S2_F32::audio_block_samples * sizeof(i2s2_tx_buffer[0]))
// --------------------------------------------------------------------------------
// #for 32-bit transfers
// #define I2S_BUFFER_TO_USE_BYTES (AudioOutputI2S2_F32::audio_block_samples*2*sizeof(i2s_tx_buffer[0]))
void AudioOutputI2S2_F32::begin(void) void AudioOutputI2S2_F32::begin(void)
{ {
bool transferUsing32bit = false; bool transferUsing32bit = true;
begin(transferUsing32bit); begin(transferUsing32bit);
} }
// --------------------------------------------------------------------------------
void AudioOutputI2S2_F32::begin(bool transferUsing32bit) void AudioOutputI2S2_F32::begin(bool transferUsing32bit)
{ {
dma.begin(true); // Allocate the DMA channel first dma.begin(true); // Allocate the DMA channel first
block_left_1st = NULL; block_left_1st = NULL;
block_right_1st = NULL; block_right_1st = NULL;
AudioOutputI2S2_F32::config_i2s(transferUsing32bit, sample_rate_Hz); AudioOutputI2S2_F32::config_i2s(transferUsing32bit, sample_rate_Hz);
#if defined(__IMXRT1062__) #if defined(__IMXRT1062__)
CORE_PIN2_CONFIG = 2; // EMC_04, 2=SAI2_TX_DATA, page 428 CORE_PIN2_CONFIG = 2; // EMC_04, 2=SAI2_TX_DATA, page 428
dma.TCD->SADDR = i2s2_tx_buffer; dma.TCD->SADDR = i2s2_tx_buffer;
dma.TCD->SOFF = 2; dma.TCD->SOFF = 4;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
dma.TCD->NBYTES_MLNO = 2; dma.TCD->NBYTES_MLNO = 4;
// dma.TCD->SLAST = -sizeof(i2s_tx_buffer);//orig from Teensy Audio Library 2020-10-31
dma.TCD->SLAST = -I2S2_BUFFER_TO_USE_BYTES; dma.TCD->SLAST = -I2S2_BUFFER_TO_USE_BYTES;
dma.TCD->DOFF = 0; dma.TCD->DOFF = 0;
// dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; //orig from Teensy Audio Library 2020-10-31 dma.TCD->CITER_ELINKNO = I2S2_BUFFER_TO_USE_BYTES / 4;
dma.TCD->CITER_ELINKNO = I2S2_BUFFER_TO_USE_BYTES / 2;
dma.TCD->DLASTSGA = 0; dma.TCD->DLASTSGA = 0;
// dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;//orig from Teensy Audio Library 2020-10-31 dma.TCD->BITER_ELINKNO = I2S2_BUFFER_TO_USE_BYTES / 4;
dma.TCD->BITER_ELINKNO = I2S2_BUFFER_TO_USE_BYTES / 2;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.TCD->DADDR = (void *)((uint32_t)&I2S2_TDR0 + 2); dma.TCD->DADDR = (void *)((uint32_t)&I2S2_TDR0);
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_TX); dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_TX);
dma.enable(); // newer location of this line in Teensy Audio library dma.enable(); // newer location of this line in Teensy Audio library
// I2S2_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE; I2S2_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE;
I2S2_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE | I2S_TCSR_FR; I2S2_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
#endif #endif
update_responsibility = update_setup(); update_responsibility = update_setup();
dma.attachInterrupt(AudioOutputI2S2_F32::isr); dma.attachInterrupt(AudioOutputI2S2_F32::isr);
enabled = 1; enabled = 1;
} }
// --------------------------------------------------------------------------------
void AudioOutputI2S2_F32::isr(void) void AudioOutputI2S2_F32::isr(void)
{ {
#if defined(KINETISK) || defined(__IMXRT1062__) int32_t *dest;
int16_t *dest;
audio_block_f32_t *blockL, *blockR; audio_block_f32_t *blockL, *blockR;
uint32_t saddr, offsetL, offsetR; uint32_t saddr, offsetL, offsetR;
saddr = (uint32_t)(dma.TCD->SADDR); saddr = (uint32_t)(dma.TCD->SADDR);
dma.clearInterrupt(); dma.clearInterrupt();
// if (saddr < (uint32_t)i2s_tx_buffer + sizeof(i2s_tx_buffer) / 2) { //original 16-bit
if (saddr < (uint32_t)i2s2_tx_buffer + I2S2_BUFFER_TO_USE_BYTES / 2) if (saddr < (uint32_t)i2s2_tx_buffer + I2S2_BUFFER_TO_USE_BYTES / 2)
{ // are we transmitting the first half or second half of the buffer? { // are we transmitting the first half or second half of the buffer?
// DMA is transmitting the first half of the buffer // DMA is transmitting the first half of the buffer
// so we must fill the second half // so we must fill the second half
// dest = (int16_t *)&i2s_tx_buffer[AUDIO_BLOCK_SAMPLES/2]; //original Teensy Audio dest = (int32_t *)&i2s2_tx_buffer[audio_block_samples / 2];
dest = (int16_t *)&i2s2_tx_buffer[audio_block_samples / 2]; // this will be diff if we were to do 32-bit samples if (AudioOutputI2S2_F32::update_responsibility) AudioStream_F32::update_all();
if (AudioOutputI2S2_F32::update_responsibility)
AudioStream_F32::update_all();
} }
else else
{ {
// DMA is transmitting the second half of the buffer // DMA is transmitting the second half of the buffer
// so we must fill the first half // so we must fill the first half
dest = (int16_t *)i2s2_tx_buffer; dest = (int32_t *)&i2s2_tx_buffer[0];
} }
blockL = AudioOutputI2S2_F32::block_left_1st; blockL = AudioOutputI2S2_F32::block_left_1st;
@ -141,52 +122,45 @@ void AudioOutputI2S2_F32::isr(void)
offsetL = AudioOutputI2S2_F32::block_left_offset; offsetL = AudioOutputI2S2_F32::block_left_offset;
offsetR = AudioOutputI2S2_F32::block_right_offset; offsetR = AudioOutputI2S2_F32::block_right_offset;
int16_t *d = dest; int32_t *d = dest;
if (blockL && blockR) if (blockL && blockR)
{ {
// memcpy_tointerleaveLR(dest, blockL->data + offsetL, blockR->data + offsetR);
// memcpy_tointerleaveLRwLen(dest, blockL->data + offsetL, blockR->data + offsetR, audio_block_samples/2);
float32_t *pL = blockL->data + offsetL; float32_t *pL = blockL->data + offsetL;
float32_t *pR = blockR->data + offsetR; float32_t *pR = blockR->data + offsetR;
for (int i = 0; i < audio_block_samples / 2; i++) for (int i = 0; i < audio_block_samples / 2; i++)
{ {
*d++ = (int16_t)*pL++; *d++ = (int32_t)*pL++;
*d++ = (int16_t)*pR++; // interleave *d++ = (int32_t)*pR++; // interleave
//*d++ = 0;
//*d++ = 0;
} }
offsetL += audio_block_samples / 2; offsetL += audio_block_samples / 2;
offsetR += audio_block_samples / 2; offsetR += audio_block_samples / 2;
} }
else if (blockL) else if (blockL)
{ {
// memcpy_tointerleaveLR(dest, blockL->data + offsetL, blockR->data + offsetR);
float32_t *pL = blockL->data + offsetL; float32_t *pL = blockL->data + offsetL;
for (int i = 0; i < audio_block_samples / 2 * 2; i += 2) for (int i = 0; i < audio_block_samples; i += 2)
{ {
*(d + i) = (int16_t)*pL++; *(d + i) = (int32_t)*pL++;
} // interleave }
offsetL += audio_block_samples / 2; offsetL += audio_block_samples / 2;
} }
else if (blockR) else if (blockR)
{ {
float32_t *pR = blockR->data + offsetR; float32_t *pR = blockR->data + offsetR;
for (int i = 0; i < audio_block_samples / 2 * 2; i += 2) for (int i = 0; i < audio_block_samples; i += 2)
{ {
*(d + i) = (int16_t)*pR++; *(d + i) = (int32_t)*pR++;
} // interleave }
offsetR += audio_block_samples / 2; offsetR += audio_block_samples / 2;
} }
else else
{ {
// memset(dest,0,AUDIO_BLOCK_SAMPLES * 2); memset(dest, 0, audio_block_samples * 4);
memset(dest, 0, audio_block_samples * 2);
return; return;
} }
arm_dcache_flush_delete(dest, sizeof(i2s2_tx_buffer) / 2); arm_dcache_flush_delete(dest, sizeof(i2s2_tx_buffer) / 2);
// if (offsetL < AUDIO_BLOCK_SAMPLES) { //orig Teensy Audio
if (offsetL < (uint16_t)audio_block_samples) if (offsetL < (uint16_t)audio_block_samples)
{ {
AudioOutputI2S2_F32::block_left_offset = offsetL; AudioOutputI2S2_F32::block_left_offset = offsetL;
@ -198,7 +172,6 @@ void AudioOutputI2S2_F32::isr(void)
AudioOutputI2S2_F32::block_left_1st = AudioOutputI2S2_F32::block_left_2nd; AudioOutputI2S2_F32::block_left_1st = AudioOutputI2S2_F32::block_left_2nd;
AudioOutputI2S2_F32::block_left_2nd = NULL; AudioOutputI2S2_F32::block_left_2nd = NULL;
} }
// if (offsetR < AUDIO_BLOCK_SAMPLES) { //orig Teensy Audio
if (offsetR < (uint16_t)audio_block_samples) if (offsetR < (uint16_t)audio_block_samples)
{ {
AudioOutputI2S2_F32::block_right_offset = offsetR; AudioOutputI2S2_F32::block_right_offset = offsetR;
@ -210,59 +183,24 @@ void AudioOutputI2S2_F32::isr(void)
AudioOutputI2S2_F32::block_right_1st = AudioOutputI2S2_F32::block_right_2nd; AudioOutputI2S2_F32::block_right_1st = AudioOutputI2S2_F32::block_right_2nd;
AudioOutputI2S2_F32::block_right_2nd = NULL; AudioOutputI2S2_F32::block_right_2nd = NULL;
} }
#endif
}
#define F32_TO_I16_NORM_FACTOR (32767) // which is 2^15-1
void AudioOutputI2S2_F32::scale_f32_to_i16(float32_t *p_f32, float32_t *p_i16, int len)
{
for (int i = 0; i < len; i++)
{
*p_i16++ = max(-F32_TO_I16_NORM_FACTOR, min(F32_TO_I16_NORM_FACTOR, (*p_f32++) * F32_TO_I16_NORM_FACTOR));
}
}
#define F32_TO_I24_NORM_FACTOR (8388607) // which is 2^23-1
void AudioOutputI2S2_F32::scale_f32_to_i24(float32_t *p_f32, float32_t *p_i24, int len)
{
for (int i = 0; i < len; i++)
{
*p_i24++ = max(-F32_TO_I24_NORM_FACTOR, min(F32_TO_I24_NORM_FACTOR, (*p_f32++) * F32_TO_I24_NORM_FACTOR));
}
}
#define F32_TO_I32_NORM_FACTOR (2147483647) // which is 2^31-1
// define F32_TO_I32_NORM_FACTOR (8388607) //which is 2^23-1
void AudioOutputI2S2_F32::scale_f32_to_i32(float32_t *p_f32, float32_t *p_i32, int len)
{
for (int i = 0; i < len; i++)
{
*p_i32++ = max(-F32_TO_I32_NORM_FACTOR, min(F32_TO_I32_NORM_FACTOR, (*p_f32++) * F32_TO_I32_NORM_FACTOR));
}
// for (int i=0; i<len; i++) { *p_i32++ = (*p_f32++) * F32_TO_I32_NORM_FACTOR + 512.f*8388607.f; }
} }
// --------------------------------------------------------------------------------
// update has to be carefully coded so that, if audio_blocks are not available, the code exits // update has to be carefully coded so that, if audio_blocks are not available, the code exits
// gracefully and won't hang. That'll cause the whole system to hang, which would be very bad. // gracefully and won't hang. That'll cause the whole system to hang, which would be very bad.
// static int count = 0; // static int count = 0;
void AudioOutputI2S2_F32::update(void) void AudioOutputI2S2_F32::update(void)
{ {
// null audio device: discard all incoming data
// if (!active) return;
// audio_block_t *block = receiveReadOnly();
// if (block) release(block);
audio_block_f32_t *block_f32; audio_block_f32_t *block_f32;
audio_block_f32_t *block_f32_scaled = AudioStream_F32::allocate_f32(); audio_block_f32_t *block_f32_scaled = AudioStream_F32::allocate_f32();
audio_block_f32_t *block2_f32_scaled = AudioStream_F32::allocate_f32(); audio_block_f32_t *block2_f32_scaled = AudioStream_F32::allocate_f32();
if ((!block_f32_scaled) || (!block2_f32_scaled)) if ((!block_f32_scaled) || (!block2_f32_scaled))
{ {
// couldn't get some working memory. Return. // couldn't get some working memory. Return.
if (block_f32_scaled) if (block_f32_scaled) AudioStream_F32::release(block_f32_scaled);
AudioStream_F32::release(block_f32_scaled); if (block2_f32_scaled) AudioStream_F32::release(block2_f32_scaled);
if (block2_f32_scaled)
AudioStream_F32::release(block2_f32_scaled);
return; return;
} }
// now that we have our working memory, proceed with getting the audio data and processing // now that we have our working memory, proceed with getting the audio data and processing
block_f32 = receiveReadOnly_f32(0); // input 0 = left channel block_f32 = receiveReadOnly_f32(0); // input 0 = left channel
if (block_f32) if (block_f32)
@ -274,22 +212,8 @@ void AudioOutputI2S2_F32::update(void)
Serial.print(", but I2S settings want it to be = "); Serial.print(", but I2S settings want it to be = ");
Serial.println(audio_block_samples); Serial.println(audio_block_samples);
} }
// Serial.print("AudioOutputI2S2_F32: audio_block_samples = ");
// Serial.println(audio_block_samples); scale_float_to_int32range(block_f32->data, block_f32_scaled->data, audio_block_samples);
// scale F32 to Int32
// block_f32_scaled = AudioStream_F32::allocate_f32();
// scale_f32_to_i32(block_f32->data, block_f32_scaled->data, audio_block_samples);
scale_f32_to_i16(block_f32->data, block_f32_scaled->data, audio_block_samples);
// count++;
// if (count > 100) {
// Serial.print("AudioOutputI2S2_F32::update() orig, scaled = ");
// Serial.print(block_f32->data[30]);
// Serial.print(", ");
// Serial.println(block_f32_scaled->data[30]);
// count=0;
// }
// now process the data blocks // now process the data blocks
__disable_irq(); __disable_irq();
@ -326,10 +250,8 @@ void AudioOutputI2S2_F32::update(void)
block_f32 = receiveReadOnly_f32(1); // input 1 = right channel block_f32 = receiveReadOnly_f32(1); // input 1 = right channel
if (block_f32) if (block_f32)
{ {
// scale F32 to Int32 arm_scale_f32(block_f32->data, (float32_t)F32_TO_I32_NORM_FACTOR,
// block_f32_scaled = AudioStream_F32::allocate_f32(); block_f32_scaled->data,audio_block_samples);
// scale_f32_to_i32(block_f32->data, block_f32_scaled->data, audio_block_samples);
scale_f32_to_i16(block_f32->data, block_f32_scaled->data, audio_block_samples);
__disable_irq(); __disable_irq();
if (block_right_1st == NULL) if (block_right_1st == NULL)
@ -361,8 +283,6 @@ void AudioOutputI2S2_F32::update(void)
AudioStream_F32::release(block_f32_scaled); AudioStream_F32::release(block_f32_scaled);
} }
} }
void AudioOutputI2S2_F32::config_i2s(void) { config_i2s(false, AudioOutputI2S2_F32::sample_rate_Hz); } void AudioOutputI2S2_F32::config_i2s(void) { config_i2s(false, AudioOutputI2S2_F32::sample_rate_Hz); }
void AudioOutputI2S2_F32::config_i2s(bool transferUsing32bit) { config_i2s(transferUsing32bit, AudioOutputI2S2_F32::sample_rate_Hz); } void AudioOutputI2S2_F32::config_i2s(bool transferUsing32bit) { config_i2s(transferUsing32bit, AudioOutputI2S2_F32::sample_rate_Hz); }
void AudioOutputI2S2_F32::config_i2s(float fs_Hz) { config_i2s(false, fs_Hz); } void AudioOutputI2S2_F32::config_i2s(float fs_Hz) { config_i2s(false, fs_Hz); }
@ -373,12 +293,9 @@ void AudioOutputI2S2_F32::config_i2s(bool transferUsing32bit, float fs_Hz)
CCM_CCGR5 |= CCM_CCGR5_SAI2(CCM_CCGR_ON); CCM_CCGR5 |= CCM_CCGR5_SAI2(CCM_CCGR_ON);
// if either transmitter or receiver is enabled, do nothing // if either transmitter or receiver is enabled, do nothing
if (I2S2_TCSR & I2S_TCSR_TE) if (I2S2_TCSR & I2S_TCSR_TE) return;
return; if (I2S2_RCSR & I2S_RCSR_RE) return;
if (I2S2_RCSR & I2S_RCSR_RE)
return;
// PLL: // PLL:
// int fs = AUDIO_SAMPLE_RATE_EXACT; //original from Teensy Audio Library
int fs = fs_Hz; int fs = fs_Hz;
// PLL between 27*24 = 648MHz und 54*24=1296MHz // PLL between 27*24 = 648MHz und 54*24=1296MHz
@ -404,7 +321,6 @@ void AudioOutputI2S2_F32::config_i2s(bool transferUsing32bit, float fs_Hz)
int tsync = 1; int tsync = 1;
I2S2_TMR = 0; I2S2_TMR = 0;
// I2S1_TCSR = (1<<25); //Reset
I2S2_TCR1 = I2S_TCR1_RFW(1); I2S2_TCR1 = I2S_TCR1_RFW(1);
I2S2_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP // sync=0; tx is async; I2S2_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP // sync=0; tx is async;
| (I2S_TCR2_BCD | I2S_TCR2_DIV((1)) | I2S_TCR2_MSEL(1)); | (I2S_TCR2_BCD | I2S_TCR2_DIV((1)) | I2S_TCR2_MSEL(1));
@ -413,7 +329,6 @@ void AudioOutputI2S2_F32::config_i2s(bool transferUsing32bit, float fs_Hz)
I2S2_TCR5 = I2S_TCR5_WNW((32 - 1)) | I2S_TCR5_W0W((32 - 1)) | I2S_TCR5_FBT((32 - 1)); I2S2_TCR5 = I2S_TCR5_WNW((32 - 1)) | I2S_TCR5_W0W((32 - 1)) | I2S_TCR5_FBT((32 - 1));
I2S2_RMR = 0; I2S2_RMR = 0;
// I2S1_RCSR = (1<<25); //Reset
I2S2_RCR1 = I2S_RCR1_RFW(1); I2S2_RCR1 = I2S_RCR1_RFW(1);
I2S2_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_RCR2_BCP // sync=0; rx is async; I2S2_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_RCR2_BCP // sync=0; rx is async;
| (I2S_RCR2_BCD | I2S_RCR2_DIV((1)) | I2S_RCR2_MSEL(1)); | (I2S_RCR2_BCD | I2S_RCR2_DIV((1)) | I2S_RCR2_MSEL(1));
@ -442,17 +357,17 @@ void AudioOutputI2S2slave_F32::begin(void)
#if defined(__IMXRT1062__) #if defined(__IMXRT1062__)
CORE_PIN7_CONFIG = 3; // 1:TX_DATA0 CORE_PIN7_CONFIG = 3; // 1:TX_DATA0
dma.TCD->SADDR = i2s2_tx_buffer; dma.TCD->SADDR = i2s2_tx_buffer;
dma.TCD->SOFF = 2; dma.TCD->SOFF = 4;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
dma.TCD->NBYTES_MLNO = 2; dma.TCD->NBYTES_MLNO = 4;
dma.TCD->SLAST = -sizeof(i2s2_tx_buffer); dma.TCD->SLAST = -sizeof(i2s2_tx_buffer);
// dma.TCD->DADDR = (void *)((uint32_t)&I2S1_TDR1 + 2); // dma.TCD->DADDR = (void *)((uint32_t)&I2S1_TDR1 + 2);
dma.TCD->DOFF = 0; dma.TCD->DOFF = 0;
dma.TCD->CITER_ELINKNO = sizeof(i2s2_tx_buffer) / 2; dma.TCD->CITER_ELINKNO = sizeof(i2s2_tx_buffer) / 4;
dma.TCD->DLASTSGA = 0; dma.TCD->DLASTSGA = 0;
dma.TCD->BITER_ELINKNO = sizeof(i2s2_tx_buffer) / 2; dma.TCD->BITER_ELINKNO = sizeof(i2s2_tx_buffer) / 4;
// dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_TX); // dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_TX);
dma.TCD->DADDR = (void *)((uint32_t)&I2S2_TDR0 + 2); dma.TCD->DADDR = (void *)((uint32_t)&I2S2_TDR0);
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_TX); dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_TX);
dma.enable(); dma.enable();

@ -58,22 +58,11 @@ public:
virtual void update(void); virtual void update(void);
void begin(void); void begin(void);
void begin(bool); void begin(bool);
void sub_begin_i32(void);
void sub_begin_i16(void);
friend class AudioInputI2S2_F32; friend class AudioInputI2S2_F32;
//friend class AudioInputI2S_F32;
#if defined(__IMXRT1062__)
friend class AudioOutputI2SQuad_F32; friend class AudioOutputI2SQuad_F32;
friend class AudioInputI2SQuad_F32; friend class AudioInputI2SQuad_F32;
bool get_update_responsibility() { return update_responsibility;}
#endif
static void scale_f32_to_i16( float32_t *p_f32, float32_t *p_i16, int len) ;
static void scale_f32_to_i24( float32_t *p_f32, float32_t *p_i16, int len) ;
static void scale_f32_to_i32( float32_t *p_f32, float32_t *p_i32, int len) ;
static float setI2SFreq_T3(const float); // I2S clock for T3,x
protected: protected:
AudioOutputI2S2_F32(int dummy): AudioStream_F32(2, inputQueueArray) {} // to be used only inside AudioOutputI2Sslave !! AudioOutputI2S2_F32(int dummy): AudioStream_F32(2, inputQueueArray) {} // to be used only inside AudioOutputI2Sslave !!
static void config_i2s(void); static void config_i2s(void);
@ -85,8 +74,6 @@ protected:
static audio_block_f32_t *block_right_1st; static audio_block_f32_t *block_right_1st;
static bool update_responsibility; static bool update_responsibility;
static DMAChannel dma; static DMAChannel dma;
static void isr_16(void);
static void isr_32(void);
static void isr(void); static void isr(void);
private: private:
static audio_block_f32_t *block_left_2nd; static audio_block_f32_t *block_left_2nd;
@ -98,14 +85,13 @@ private:
static int audio_block_samples; static int audio_block_samples;
volatile uint8_t enabled = 1; volatile uint8_t enabled = 1;
}; };
class AudioOutputI2S2slave_F32 : public AudioOutputI2S2_F32 class AudioOutputI2S2slave_F32 : public AudioOutputI2S2_F32
{ {
public: public:
AudioOutputI2S2slave_F32(void) : AudioOutputI2S2_F32(0) { begin(); } ; AudioOutputI2S2slave_F32(void) : AudioOutputI2S2_F32(0) { begin(); } ;
void begin(void); void begin(void);
friend class AudioInputI2S2slave_F32; friend class AudioInputI2S2slave_F32;
friend void dma_ch0_isr(void);
protected: protected:
static void config_i2s(void); static void config_i2s(void);
}; };

@ -0,0 +1,93 @@
/**
* @file switch_selectorStereo_F32.h
* @author Piotr Zapart
* @brief Signal selector for routing mono to stereo
* @version 0.1
* @date 2024-03-21
*
* @copyright Copyright (c) 2024 www.hexefx.com
*
* 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 <https://www.gnu.org/licenses/>."
*/
#ifndef _SWITCH_SELECTORSTEREO_F32_H_
#define _SWITCH_SELECTORSTEREO_F32_H_
#include <AudioStream_F32.h>
#include <arm_math.h>
class AudioSwitchSelectorStereo : public AudioStream_F32
{
public:
AudioSwitchSelectorStereo(void) : AudioStream_F32(2, inputQueueArray){};
typedef enum
{
SIGNAL_SELECT_LR, // default stereo operation
SIGNAL_SELECT_L, // left input as mono input
SIGNAL_SELECT_R // right input as mono input
}selector_mode_t;
selector_mode_t setMode(selector_mode_t m)
{
if (m <= 2)
{
__disable_irq();
mode = m;
__enable_irq();
}
return mode;
}
selector_mode_t getMode() {return mode;};
void update()
{
audio_block_f32_t *blockL, *blockR, *outL, *outR;
blockL = AudioStream_F32::receiveWritable_f32(0);
blockR = AudioStream_F32::receiveWritable_f32(1);
if (!blockL || !blockR)
{
if (blockL) AudioStream_F32::release(blockL);
if (blockR) AudioStream_F32::release(blockR);
return;
}
switch(mode)
{
case SIGNAL_SELECT_LR:
outL = blockL;
outR = blockR;
break;
case SIGNAL_SELECT_L:
outL = blockL;
outR = blockL;
break;
case SIGNAL_SELECT_R:
outL = blockR;
outR = blockR;
break;
default:
outL = blockL;
outR = blockR;
break;
}
AudioStream_F32::transmit(outL, 0);
AudioStream_F32::transmit(outR, 1);
AudioStream_F32::release(blockL);
AudioStream_F32::release(blockR);
}
private:
audio_block_f32_t *inputQueueArray[2];
selector_mode_t mode = SIGNAL_SELECT_LR;
};
#endif // _SWITCH_SELECTORSTEREO_F32_H_

@ -81,4 +81,3 @@ const float music_intevals[37] =
3.174802f, 3.363586f, 3.563595f, 3.775497f, 4.000000f 3.174802f, 3.363586f, 3.563595f, 3.775497f, 4.000000f
}; };

Loading…
Cancel
Save