normalized params, es8388, custom i2s

pull/2/head
pio 8 months ago
parent 07d5e7e9dc
commit eaa382cbfb
  1. 13
      src/basic_DSPutils.h
  2. 625
      src/control_ES8388_F32.cpp
  3. 55
      src/control_ES8388_F32.h
  4. 1016
      src/control_SGTL5000_F32.cpp
  5. 121
      src/control_SGTL5000_F32.h
  6. 151
      src/control_WM8731_F32.cpp
  7. 48
      src/control_WM8731_F32.h
  8. 45
      src/effect_compressorStereo_F32.h
  9. 21
      src/effect_delaystereo_F32.cpp
  10. 1
      src/effect_delaystereo_F32.h
  11. 16
      src/effect_gainStereo_F32.h
  12. 26
      src/effect_guitarBooster_F32.h
  13. 30
      src/effect_noiseGateStereo_F32.h
  14. 7
      src/effect_reverbsc_F32.cpp
  15. 49
      src/effect_xfaderStereo_F32.h
  16. 2
      src/filter_ir_cabsim_F32.cpp
  17. 1
      src/filter_ir_cabsim_F32.h
  18. 3
      src/hexefx_audio_F32.h
  19. 240
      src/input_i2s_ext_F32.cpp
  20. 87
      src/input_i2s_ext_F32.h
  21. 4
      src/output_i2s2_F32.cpp
  22. 432
      src/output_i2s_ext_F32.cpp
  23. 99
      src/output_i2s_ext_F32.h

@ -61,4 +61,17 @@ inline void memcpyDeinterleave_f32(float32_t *src, float32_t *dstA, float32_t *d
} }
} }
inline void memcpyDeinterleave_f32(float32_t *src, float32_t *dstA, float32_t *dstB, int16_t sz); inline void memcpyDeinterleave_f32(float32_t *src, float32_t *dstA, float32_t *dstB, int16_t sz);
// added input saturation
template <class T, class A, class B, class C, class D>
T map_sat(T x, A in_min, B in_max, C out_min, D out_max, typename std::enable_if<std::is_floating_point<T>::value >::type* = 0)
{
if (x <= in_min) return out_min;
else if (x >= in_max) return out_max;
// when the input is a float or double, do all math using the input's type
return (x - (T)in_min) * ((T)out_max - (T)out_min) / ((T)in_max - (T)in_min) + (T)out_min;
}
#endif // _BASIC_DSPUTILS_H_ #endif // _BASIC_DSPUTILS_H_

@ -0,0 +1,625 @@
#include "control_ES8388_F32.h"
#define ES8388_REG_CHIP_CTRL1 (0x00) // Default 0000 0110
#define ES8388_REG_CHIP_CTRL1_DFLT (0x06)
#define ES8388_BIT_SCPRESET (1<<7) // 1=reset registers to default
#define ES8388_BIT_LRCM (1<<6)
#define ES8388_BIT_DACMCLK (1<<5)
#define ES8388_BIT_SAMEFS (1<<4)
#define ES8388_BIT_SEQEN (1<<3)
#define ES8388_BIT_ENREF (1<<2)
#define ES8388_VMIDSEL_DIS (0x00)
#define ES8388_VMIDSEL_50K (0x01)
#define ES8388_VMIDSEL_500K (0x02)
#define ES8388_VMIDSEL_5K (0x03)
#define ES8388_REG_CHIP_CTRL2 (0x01) // Default 0101 1100
#define ES8388_REG_CHIP_CTRL2_DFLT (0x5C)
#define ES8388_BIT_LPVCMMOD (1<<5)
#define ES8388_BIT_LPVREFBUF (1<<4)
#define ES8388_BIT_PDNANA (1<<3)
#define ES8388_BIT_PDBIBIASGEN (1<<2)
#define ES8388_BIT_VREFLO (1<<1)
#define ES8388_BIT_PDBVREFBUF (1<<0)
#define ES8388_REG_CHIP_PWR_MAN (0x02) // Default 1100 0011
#define ES8388_REG_CHIP_PWR_MAN_DFLT (0xC3)
#define ES8388_BIT_ADC_DIGPDN (1<<7)
#define ES8388_BIT_DAC_DIGPDN (1<<6)
#define ES8388_BIT_ADCSTMRST (1<<5)
#define ES8388_BIT_DACSTMRST (1<<4)
#define ES8388_BIT_ADCDLL_PDN (1<<3)
#define ES8388_BIT_DACDLL_PDN (1<<2)
#define ES8388_BIT_ADCVREFPDN (1<<1)
#define ES8388_BIT_DACVREFPDN (1<<0)
#define ES8388_REG_ADC_PWR_MAN (0x03) // Default 1111 1100
#define ES8388_REG_ADC_PWR_MAN_DFLT (0xFC)
#define ES8388_BIT_PDNAINL (1<<7)
#define ES8388_BIT_PDNAINR (1<<6)
#define ES8388_BIT_PDNADCL (1<<5)
#define ES8388_BIT_PDNADCR (1<<4)
#define ES8388_BIT_PDNMICB (1<<3)
#define ES8388_BIT_PDNADCBIASG (1<<2)
#define ES8388_BIT_FLASHLP (1<<1)
#define ES8388_BIT_INT1LP (1<<0)
#define ES8388_REG_DAC_PWR_MAN (0x04) // Default 1100 0000
#define ES8388_REG_DAC_PWR_MAN_DFLT (0xC0)
#define ES8388_BIT_PDNDACL (1<<7)
#define ES8388_BIT_PDNDACR (1<<6)
#define ES8388_BIT_LOUT1_EN (1<<5)
#define ES8388_BIT_ROUT1_EN (1<<4)
#define ES8388_BIT_LOUT2_EN (1<<3)
#define ES8388_BIT_ROUT2_EN (1<<2)
#define ES8388_REG_CHIP_LOWPWR1 (0x05) // Default 0000 0000
#define ES8388_REG_CHIP_LOWPWR1_DFLT (0x00)
#define ES8388_BIT_LPDACL (1<<7)
#define ES8388_BIT_LPDACR (1<<6)
#define ES8388_BIT_LPOUT1 (1<<5)
#define ES8388_BIT_LPOUT2 (1<<3)
#define ES8388_REG_CHIP_LOWPWR2 (0x06) // Default 0000 0000
#define ES8388_REG_CHIP_LOWPWR2_DFLT (0x00)
#define ES8388_BIT_LPPGA (1<<7)
#define ES8388_BIT_LPMIX (1<<6)
#define ES8388_BIT_LPADCVRP (1<<1)
#define ES8388_BIT_LPDACVRP (1<<0)
#define ES8388_REG_ANALOG_VOLT_MAN (0x07) // Default 0111 1100
#define ES8388_REG_ANALOG_VOLT_MAN_DFLT (0x7C)
#define ES8388_BIT_VSEL_MASK (0x7C)
#define ES8388_REG_MASTER_MODE_CTRL (0x08) // Default 1000 0000
#define ES8388_REG_MASTER_MODE_CTRL_DFLT (0x80)
#define ES8388_BIT_MSC (1<<7)
#define ES8388_BIT_MCLKDIV2 (1<<6)
#define ES8388_BIT_BCLKINV (1<<5)
#define ES8388_BCLKDIVAUTO (0x00)
#define ES8388_BCLKDIV1 (0x01)
#define ES8388_BCLKDIV2 (0x02)
#define ES8388_BCLKDIV3 (0x03)
#define ES8388_BCLKDIV4 (0x04)
#define ES8388_BCLKDIV6 (0x05)
#define ES8388_BCLKDIV8 (0x06)
#define ES8388_BCLKDIV9 (0x07)
#define ES8388_BCLKDIV11 (0x08)
#define ES8388_BCLKDIV12 (0x09)
#define ES8388_BCLKDIV16 (0x0A)
#define ES8388_BCLKDIV18 (0x0B)
#define ES8388_BCLKDIV22 (0x0C)
#define ES8388_BCLKDIV24 (0x0D)
#define ES8388_BCLKDIV33 (0x0E)
#define ES8388_BCLKDIV36 (0x0F)
#define ES8388_BCLKDIV44 (0x10)
#define ES8388_BCLKDIV48 (0x11)
#define ES8388_BCLKDIV66 (0x12)
#define ES8388_BCLKDIV72 (0x13)
#define ES8388_BCLKDIV5 (0x14)
#define ES8388_BCLKDIV10 (0x15)
#define ES8388_BCLKDIV15 (0x16)
#define ES8388_BCLKDIV17 (0x17)
#define ES8388_BCLKDIV20 (0x18)
#define ES8388_BCLKDIV25 (0x19)
#define ES8388_BCLKDIV30 (0x1A)
#define ES8388_BCLKDIV32 (0x1B)
#define ES8388_BCLKDIV34 (0x1C)
#define ES8388_BCLKDIV(x) ((x)&0x1F)
#define ES8388_REG_ADC_CTRL1 (0x09) // Default 0000 0000
#define ES8388_REG_ADC_CTRL1_DFLT (0x00)
#define ES8388_MICAMPL_MASK (0xF0)
#define ES8388_MICAMPL_SHIFT (0x04)
#define ES8388_MICAMPR_MASK (0x0F)
#define ES8388_MICAMPR_SHIFT (0x00)
#define ES8388_REG_ADC_CTRL2 (0x0A) // Default 0000 0000
#define ES8388_REG_ADC_CTRL2_DFLT (0x00)
#define ES8388_LINSEL_MASK (0xC0)
#define ES8388_LINSEL_SHIFT (0x06)
#define ES8388_INPUT1 (0x00)
#define ES8388_INPUT2 (0x01)
#define ES8388_INPUTDIFF (0x03)
#define ES8388_LINSEL(x) (((x)<<ES8388_LINSEL_SHIFT)&ES8388_LINSEL_MASK)
#define ES8388_RINSEL_MASK (0x30)
#define ES8388_RINSEL_SHIFT (0x04)
#define ES8388_RINSEL(x) (((x)<<ES8388_RINSEL_SHIFT)&ES8388_RINSEL_MASK)
#define ES8388_BIT_DSSEL (1<<3)
#define ES8388_BIT_DSR (1<<2)
#define ES8388_REG_ADC_CTRL3 (0x0B) // Default 0000 0010
#define ES8388_REG_ADC_CTRL3_DFLT (0x02)
#define ES8388_BIT_DS (1<<7)
#define ES8388_MONOMIX_MASK (0x18)
#define ES8388_MONOMIX_SHIFT (0x03)
#define ES8388_MONOMIX_STEREO (0x00)
#define ES8388_MONOMIX_ADCL (0x01)
#define ES8388_MONOMIX_ADCR (0x02)
#define ED8388_MONOMIX(x) (((x)<<ES8388_MONOMIX_SHIFT)&ES8388_MONOMIX_MASK)
#define ES8388_REG_ADC_CTRL4 (0x0C) // Default 0000 0000
#define ES8388_REG_ADC_CTRL4_DFLT (0x00)
#define ES8388_DATSEL_MASK (0xC0)
#define ES8388_DATSEL_SHIFT (0x06)
#define ES8388_DATSEL_LL_RR (0x00)
#define ES8388_DATSEL_LL_LR (0x01)
#define ES8388_DATSEL_RL_RR (0x02)
#define ES8388_DATSEL_LR_RL (0x03)
#define ES8388_BIT_ADCLRP (1<<5)
#define ES8388_ADCWL_MASK (0x1C)
#define ES8388_ADCWL_SHIFT (0x1C)
#define ES8388_ADCWL_24BIT (0x00)
#define ES8388_ADCWL_20BIT (0x01)
#define ES8388_ADCWL_18BIT (0x02)
#define ES8388_ADCWL_16BIT (0x03)
#define ES8388_ADCWL_32BIT (0x04)
#define ES8388_ADCWL(x) (((x)<<ES8388_ADCWL_SHIFT)&ES8388_ADCWL_MASK)
#define ES8388_ADCFORMAT_MASK (0x03)
#define ES8388_ADCFORMAT_SHIFT (0x00)
#define ES8388_ADCFORMAT_I2S (0x00)
#define ES8388_ADCFORMAT_LJUST (0x01)
#define ES8388_ADCFORMAT_RJUST (0x02)
#define ES8388_ADCFORMAT_DSPPCM (0x03)
#define ES8388_ADCFORMAT(x) (((x)<<ES8388_ADCFORMAT_SHIFT)&ES8388_ADCFORMAT_MASK)
#define ES8388_REG_ADC_CTRL5 (0x0D) // Default 0000 0110
#define ES8388_REG_ADC_CTRL5_DFLT (0x06)
#define ES8388_BIT_ADCFSMODE (1<<5)
#define ES8388_ADCFSRATIO_MASK (0x1F)
#define ES8388_ADCFSRATIO_SHIFT (0x00)
#define ES8388_FSRATIO_128 (0x00)
#define ES8388_FSRATIO_192 (0x01)
#define ES8388_FSRATIO_256 (0x02)
#define ES8388_FSRATIO_384 (0x03)
#define ES8388_FSRATIO_512 (0x04)
#define ES8388_FSRATIO_576 (0x05)
#define ES8388_FSRATIO_768 (0x06) // default
#define ES8388_FSRATIO_1024 (0x07)
#define ES8388_FSRATIO_1152 (0x08)
#define ES8388_FSRATIO_1408 (0x09)
#define ES8388_FSRATIO_1536 (0x0A)
#define ES8388_FSRATIO_2112 (0x0B)
#define ES8388_FSRATIO_2304 (0x0C)
#define ES8388_FSRATIO_125 (0x10)
#define ES8388_FSRATIO_136 (0x11)
#define ES8388_FSRATIO_250 (0x12)
#define ES8388_FSRATIO_272 (0x13)
#define ES8388_FSRATIO_375 (0x14)
#define ES8388_FSRATIO_500 (0x15)
#define ES8388_FSRATIO_544 (0x16)
#define ES8388_FSRATIO_750 (0x17)
#define ES8388_FSRATIO_1000 (0x18)
#define ES8388_FSRATIO_1088 (0x19)
#define ES8388_FSRATIO_1496 (0x1A)
#define ES8388_FSRATIO_1500 (0x1B)
#define ES8388_ADCFSRATIO(x) (((x)<<ES8388_ADCFSRATIO_SHIFT)&ES8388_ADCFSRATIO_MASK)
#define ES8388_REG_ADC_CTRL6 (0x0E) // Default 0011 0000
#define ES8388_REG_ADC_CTRL6_DFLT (0x30)
#define ES8388_BIT_ADCINVL (1<<7)
#define ES8388_BIT_ADCINVR (1<<6)
#define ES8388_BIT_ADCHPFL (1<<5)
#define ES8388_BIT_ADCHPFR (1<<4)
#define ES8388_REG_ADC_CTRL7 (0x0F) // Default 0010 0000
#define ES8388_REG_ADC_CTRL7_DFLT (0x20)
#define ES8388_ADCRAMPRATE_MASK (0xC0)
#define ES8388_ADCRAMPRATE_SHIFT (0x06)
#define ES8388_ADCRAMPRATE_05DB_4LRCLK (0x00)
#define ES8388_ADCRAMPRATE_05DB_8LRCLK (0x01)
#define ES8388_ADCRAMPRATE_05DB_16LRCLK (0x02)
#define ES8388_ADCRAMPRATE_05DB_32LRCLK (0x03)
#define ES8388_ADCRAMPRATE(x) (((x)<<ES8388_ADCRAMPRATE_SHIFT)&ES8388_ADCRAMPRATE_MASK)
#define ES8388_BIT_ADCSOFTRAMP (1<<5)
#define ES8388_BIT_ADCLER (1<<3)
#define ES8388_BIT_ADCMUTE (1<<2)
#define ES8388_REG_ADC_CTRL8 (0x10) // Default 1100 0000
#define ES8388_REG_ADC_CTRL8_DFLT (0xC0)
#define ESP8388_LADCVOL (ES8388_REG_ADC_CTRL8)
#define ES8388_REG_ADC_CTRL9 (0x11) // Default 1100 0000
#define ES8388_REG_ADC_CTRL9_DFLT (0xC0)
#define ESP8388_RADCVOL (ES8388_REG_ADC_CTRL9)
#define ES8388_REG_ADC_CTRL10 (0x12) // Default 0011 1000
#define ES8388_REG_ADC_CTRL10_DFLT (0x38)
#define ES8388_ALCSEL_MASK (0xC0)
#define ES8388_ALCSEL_SHIFT (0x06)
#define ES8388_ALCSEL_OFF (0x00)
#define ES8388_ALCSEL_R (0x01)
#define ES8388_ALCSEL_L (0x02)
#define ES8388_ALCSEL_LR (0x03)
#define ES8388_ALCSEL(x) (((x)<<ES8388_ALCSEL_SHIFT)&ES8388_ALCSEL_MASK)
#define ES8388_MAXGAIN_MASK (0x38)
#define ES8388_MAXGAIN_SHIFT (0x03)
#define ES8388_MAXGAIN_M6_5DB (0x00)
#define ES8388_MAXGAIN_M0_5DB (0x01)
#define ES8388_MAXGAIN_5_5DB (0x02)
#define ES8388_MAXGAIN_11_5DB (0x03)
#define ES8388_MAXGAIN_17_5DB (0x04)
#define ES8388_MAXGAIN_23_5DB (0x05)
#define ES8388_MAXGAIN_29_5DB (0x06)
#define ES8388_MAXGAIN_35_5DB (0x07)
#define ES8388_MAXGAIN(x) (((x)<<ES8388_MAXGAIN_SHIFT)&ES8388_MAXGAIN_MASK)
#define ES8388_MINGAIN_MASK (0x07)
#define ES8388_MINGAIN_SHIFT (0x00)
#define ES8388_MINGAIN_M12DB (0x00)
#define ES8388_MINGAIN_M6DB (0x01)
#define ES8388_MINGAIN_0DB (0x02)
#define ES8388_MINGAIN_6DB (0x03)
#define ES8388_MINGAIN_12DB (0x04)
#define ES8388_MINGAIN_18DB (0x05)
#define ES8388_MINGAIN_24DB (0x06)
#define ES8388_MINGAIN_30DB (0x07)
#define ES8388_MINGAIN(x) (((x)<<ES8388_MINGAIN_SHIFT)&ES8388_MINGAIN_MASK)
#define ES8388_REG_ADC_CTRL11 (0x13) // Default 1011 0000
#define ES8388_REG_ADC_CTRL11_DFLT (0xB0)
#define ES8388_ALCLVL_MASK (0xF0)
#define ES8388_ALCLVL_SHIFT (0x04)
#define ES8388_ALCLVL(x) (((x)<<ES8388_ALCLVL_SHIFT)&ES8388_ALCLVL_MASK)
#define ES8388_ALCHLD_MASK (0x0F)
#define ES8388_ALCHLD_SHIFT (0x00)
#define ES8388_ALCHLD(x) (((x)<<ES8388_ALCHLD_SHIFT)&ES8388_ALCHLD_MASK)
#define ES8388_REG_ADC_CTRL12 (0x14) // Default 0011 0010
#define ES8388_REG_ADC_CTRL12_DFLT (0x32)
#define ES8388_ALCDCY_MASK (0xF0)
#define ES8388_ALCDCY_SHIFT (0x04)
#define ES8388_ALCDCY(x) (((x)<<ES8388_ALCDCY_SHIFT)&ES8388_ALCDCY_MASK)
#define ES8388_ALCATK_MASK (0x0F)
#define ES8388_ALCATK_SHIFT (0x00)
#define ES8388_ALCATK(x) (((x)<<ES8388_ALCATK_SHIFT)&ES8388_ALCATK_MASK)
#define ES8388_REG_ADC_CTRL13 (0x15) // Default 0000 0110
#define ES8388_REG_ADC_CTRL13_DFLT (0x06)
#define ES8388_BIT_ALCMODE (1<<7)
#define ES8388_BIT_ALCZC (1<<6)
#define ES8388_BIT_TIMEOUT (1<<5)
#define ES8388_WINSIZE_MASK (0x1F)
#define ES8388_WINSIZE_SHIFT (0x00)
#define ES8388_WINSIZE(x) (((x)<<ES8388_WINSIZE_SHIFT)&ES8388_WINSIZE_MASK)
#define ES8388_REG_ADC_CTRL14 (0x16) // Default 0000 0000
#define ES8388_REG_ADC_CTRL14_DFLT (0x00)
#define ES8388_NGTH_MASK (0xF8)
#define ES8388_NGTH_SHIFT (0x03)
#define ES8388_NGTH(x) (((x)<<ES8388_NGTH_SHIFT)&ES8388_NGTH_MASK)
#define ES8388_NGG_MASK (0x06)
#define ES8388_NGG_SHIFT (0x01)
#define ES8388_NGG_PGA_CONST (0x00)
#define ES8388_NGG_ADCMUTE (0x01)
#define ES8388_NGG(x) (((x)<<ES8388_NGG_SHIFT)&ES8388_NGG_MASK)
#define ES8388_BIT_NGAT_EN (1<<0)
#define ES8388_REG_DAC_CTRL1 (0x17) // Default 0000 0000
#define ES8388_REG_DAC_CTRL1_DFLT (0x00)
#define ES8388_BIT_DACLRSWAP (1<<7)
#define ES8388_BIT_DACLRP (1<<6)
#define ES8388_DACWL_MASK (0x38)
#define ES8388_DACWL_SHIFT (0x03)
#define ES8388_DACWL_24BIT (0x00)
#define ES8388_DACWL_20BIT (0x01)
#define ES8388_DACWL_18BIT (0x02)
#define ES8388_DACWL_16BIT (0x03)
#define ES8388_DACWL_32BIT (0x04)
#define ES8388_DACWL(x) (((x)<<ES8388_DACWL_SHIFT)&ES8388_DACWL_MASK)
#define ES8388_DACFORMAT_MASK (0x06)
#define ES8388_DACFORMAT_SHIFT (0x01)
#define ES8388_DACFORMAT_I2S (0x00)
#define ES8388_DACFORMAT_LJUST (0x01)
#define ES8388_DACFORMAT_RJUST (0x02)
#define ES8388_DACFORMAT_DSPPCM (0x03)
#define ES8388_DACFORMAT(x) (((x)<<ES8388_DACFORMAT_SHIFT)&ES8388_DACFORMAT_MASK)
#define ES8388_REG_DAC_CTRL2 (0x18) // Default 0000 0110
#define ES8388_REG_DAC_CTRL2_DFLT (0x06)
#define ES8388_BIT_DACFSMODE (1<<5)
#define ES8388_DACFSRATIO_MASK (0x1F)
#define ES8388_DACFSRATIO_SHIFT (0x00) // values define in ADCFSRATIO
#define ES8388_DACFSRATIO(x) (((x)<<ES8388_DACFSRATIO_SHIFT)&ES8388_DACFSRATIO_MASK)
#define ES8388_REG_DAC_CTRL3 (0x19) // Default 0010 0010
#define ES8388_REG_DAC_CTRL3_DFLT (0x22)
#define ES8388_DACRAMPRATE_MASK (0xC0)
#define ES8388_DACRAMPRATE_SHIFT (0x06)
#define ES8388_DACRAMPRATE_05DB_4LRCLK (0x00)
#define ES8388_DACRAMPRATE_05DB_32LRCLK (0x01)
#define ES8388_DACRAMPRATE_05DB_64LRCLK (0x02)
#define ES8388_DACRAMPRATE_05DB_128LRCLK (0x03)
#define ES8388_DACRAMPRATE(x) (((x)<<ES8388_DACRAMPRATE_SHIFT)&ES8388_DACRAMPRATE_MASK)
#define ES8388_BIT_DACSOFTRAMP (1<<5)
#define ES8388_BIT_DACLER (1<<3)
#define ES8388_BIT_DACMUTE (1<<2)
#define ES8388_REG_DAC_CTRL4 (0x1A) // Default 1100 0000
#define ES8388_REG_DAC_CTRL4_DFLT (0xC0)
#define ES8388_LDACVOL (ES8388_REG_DAC_CTRL4)
#define ES8388_REG_DAC_CTRL5 (0x1B) // Default 1100 0000
#define ES8388_REG_DAC_CTRL5_DFLT (0xC0)
#define ES8388_RDACVOL (ES8388_REG_DAC_CTRL5)
#define ES8388_REG_DAC_CTRL6 (0x1C) // Default 0000 1000
#define ES8388_REG_DAC_CTRL6_DFLT (0x08)
#define ES8388_DACDEEMP_MASK (0xC0)
#define ES8388_DACDEEMP_SHIFT (0x06)
#define ES8388_DACDEEMP_OFF (0x00)
#define ES8388_DACDEEMP_32KHZ (0x01)
#define ES8388_DACDEEMP_44_1KHZ (0x02)
#define ES8388_DACDEEMP_48KHZ (0x03)
#define ES8388_DACDEEMP(x) (((x)<<ES8388_DACDEEMP_SHIFT)&ES8388_DACDEEMP_MASK)
#define ES8388_BIT_DACINVL (1<<5)
#define ES8388_BIT_DACINVR (1<<4)
#define ES8388_BIT_CLICKFREE (1<<3)
#define ES8388_REG_DAC_CTRL7 (0x1D) // Default 0000 0000
#define ES8388_BIT_DACZEROL (1<<7)
#define ES8388_BIT_DACZEROR (1<<6)
#define ES8388_BIT_DACMONO (1<<5)
#define ES8388_DACSE_MASK (0x1C)
#define ES8388_DACSE_SHIFT (0x02)
#define ES8388_DACSE(x) (((x)<<ES8388_DACSE_SHIFT)&ES8388_DACSE_MASK)
#define ES8388_VPP_MASK (0x03)
#define ES8388_VPP_SHIFT (0x00)
#define ES8388_VPP_3_5V (0x00)
#define ES8388_VPP_4V (0x01)
#define ES8388_VPP_3V (0x02)
#define ES8388_VPP_2_5V (0x03)
#define ES8388_VPP(x) (((x)<<ES8388_VPP_SHIFT)&ES8388_VPP_MASK)
#define ES8388_REG_DAC_CTRL8 (0x1E) // DAC shelving filter coeff a [29:24]
#define ES8388_REG_DAC_CTRL9 (0x1F) // DAC shelving filter coeff a [23:16]
#define ES8388_REG_DAC_CTRL10 (0x20) // DAC shelving filter coeff a [15:08]
#define ES8388_REG_DAC_CTRL11 (0x21) // DAC shelving filter coeff a [07:00]
#define ES8388_REG_DAC_CTRL12 (0x22) // DAC shelving filter coeff b [29:24]
#define ES8388_REG_DAC_CTRL13 (0x23) // DAC shelving filter coeff b [23:16]
#define ES8388_REG_DAC_CTRL14 (0x24) // DAC shelving filter coeff b [15:08]
#define ES8388_REG_DAC_CTRL15 (0x25) // DAC shelving filter coeff b [07:00]
#define ES8388_REG_DAC_CTRL16 (0x26) // Default 0000 0000
#define ES8388_REG_DAC_CTRL16_DFLT (0x00)
#define ES8388_LMIXSEL_MASK (0x38)
#define ES8388_LMIXSEL_SHIFT (0x03)
#define ES8388_LMIXSEL_LIN1 (0x00)
#define ES8388_LMIXSEL_LIN2 (0x01)
#define ES8388_LMIXSEL_ADCL_P (0x03)
#define ES8388_LMIXSEL_ADCL_N (0x04)
#define ES8388_LMIXSEL(x) (((x)<<ES8388_LMIXSEL_SHIFT)&ES8388_LMIXSEL_MASK)
#define ES8388_RMIXSEL_MASK (0x03)
#define ES8388_RMIXSEL_SHIFT (0x00)
#define ES8388_RMIXSEL_RIN1 (0x00)
#define ES8388_RMIXSEL_RIN2 (0x01)
#define ES8388_RMIXSEL_ADCR_P (0x03)
#define ES8388_RMIXSEL_ADCR_N (0x04)
#define ES8388_RMIXSEL(x) (((x)<<ES8388_RMIXSEL_SHIFT)&ES8388_RMIXSEL_MASK)
#define ES8388_REG_DAC_CTRL17 (0x27) // Default 0011 1000
#define ES8388_REG_DAC_CTRL17_DFLT (0x38)
#define ES8388_BIT_LD2LO (1<<7)
#define ES8388_BIT_LI2LO (1<<3)
#define ES8388_LI2LOVOL_MASK (0x38)
#define ES8388_LI2LOVOL_SHIFT (0x03)
#define ES8388_VOL_6DB (0x00)
#define ES8388_VOL_3DB (0x01)
#define ES8388_VOL_0DB (0x02)
#define ES8388_VOL_M3DB (0x03)
#define ES8388_VOL_M6DB (0x04)
#define ES8388_VOL_M9DB (0x05)
#define ES8388_VOL_M12DB (0x06)
#define ES8388_VOL_M15DB (0x07)
#define ES8388_LI2LOVOL(x) (((x)<<ES8388_LI2LOVOL_SHIFT)&ES8388_LI2LOVOL_MASK)
#define ES8388_REG_DAC_CTRL18 (0x28) // No data? Default 0010 1000
#define ES8388_REG_DAC_CTRL19 (0x29) // No data? Default 0010 1000
#define ES8388_REG_DAC_CTRL20 (0x2A) // Default 0011 1000
#define ES8388_REG_DAC_CTRL20_DFLT (0x38)
#define ES8388_BIT_RD2RO (1<<7) // DACR to outmixer R
#define ES8388_BIT_RI2RO (1<<6) // RIN to outmixer R
#define ES8388_RI2ROVOL_MASK (0x38)
#define ES8388_RI2ROVOL_SHIFT (0x03)
#define ES8388_RI2ROVOL(x) (((x)<<ES8388_RI2ROVOL_SHIFT)&ES8388_RI2ROVOL_MASK)
#define ES8388_REG_DAC_CTRL21 (0x2B) // Default 0000 0000
#define ES8388_REG_DAC_CTRL21_DFLT (0x00)
#define ES8388_BIT_SLRCK (1<<7)
#define ES8388_BIT_LRCK_SEL (1<<6)
#define ES8388_BIT_OFFSET_DIS (1<<5)
#define ES8388_BIT_MCLK_DIS (1<<4)
#define ES8388_BIT_ADC_DLL_PWD (1<<3)
#define ES8388_BIT_DAC_DLL_PWD (1<<2)
#define ES8388_REG_DAC_CTRL22 (0x2C) // Default 0000 0000
#define ES8388_REG_DAC_OFFSET (ES8388_REG_DAC_CTRL21)
#define ES8388_REG_DAC_CTRL23 (0x2D) // Default 0000 0000
#define ES8388_BIT_VROI (1<<4)
#define ES8388_REG_DAC_CTRL24 (0x2E) // Default 0000 0000
#define ES8388_LOUT1VOL (ES8388_REG_DAC_CTRL24)
#define ES8388_REG_DAC_CTRL25 (0x2F) // Default 0000 0000
#define ES8388_ROUT1VOL (ES8388_REG_DAC_CTRL25)
#define ES8388_REG_DAC_CTRL26 (0x30) // Default 0000 0000
#define ES8388_LOUT2VOL (ES8388_REG_DAC_CTRL26)
#define ES8388_REG_DAC_CTRL27 (0x31) // Default 0000 0000
#define ES8388_ROUT2VOL (ES8388_REG_DAC_CTRL27)
#define ES8388_REG_DAC_CTRL28 (0x32) // No data? Default 0000 1000
bool AudioControlES8388_F32::configured = false;
bool AudioControlES8388_F32::enable(TwoWire *i2cBus, uint8_t addr, config_t cfg)
{
configured = false;
ctrlBus = i2cBus;
i2cAddr = addr;
ctrlBus->begin();
ctrlBus->setClock(100000);
bool reply = true;
reply = writeReg(ES8388_REG_MASTER_MODE_CTRL, 0x00); // set to slave mode
reply &= writeReg(ES8388_REG_CHIP_PWR_MAN, 0xF3); // power down
reply &=writeReg(ES8388_REG_DAC_CTRL21, ES8388_BIT_SLRCK); // DACLRC = ADCLRC
reply &=writeReg(ES8388_REG_CHIP_CTRL1, ES8388_VMIDSEL_5K | ES8388_BIT_ENREF); // 50k divider,
reply &=writeReg(ES8388_REG_CHIP_CTRL2, 0x40); // low power modes off, bit6 not defined? based on default value
reply &=writeReg(ES8388_REG_ADC_PWR_MAN, 0x00); // power up ADC, turn off the PDNMICB?
reply &=writeReg(ES8388_REG_DAC_PWR_MAN, ES8388_BIT_LOUT1_EN | ES8388_BIT_ROUT1_EN); // enable LR1
if (reply == false)
{
DBG_SERIAL.println("Codec i2c error");
return false;
}
switch (cfg)
{
case ES8388_CFG_LINEIN_DIFF:
writeReg(ES8388_REG_ADC_CTRL2, ES8388_LINSEL(ES8388_INPUTDIFF) | // LIN=LIN1-RIN1 (ADCCTRL3[7] DS = 0)
ES8388_RINSEL(ES8388_INPUTDIFF) | // RIN=LIN2-RIN2 (DSR = 1)
ES8388_BIT_DSSEL | // use different DSR settings for L and R
ES8388_BIT_DSR); // DS setting for channel R
break;
case ES8388_CFG_LINEIN_SINGLE_ENDED:
writeReg(ES8388_REG_ADC_CTRL2, ES8388_LINSEL(ES8388_INPUT1) | // LIN=LIN1-RIN1 (ADCCTRL3[7] DS = 0)
ES8388_RINSEL(ES8388_INPUT1)); // RIN=LIN2-RIN2 (DSR = 1)
break;
default:
writeReg(ES8388_REG_ADC_CTRL2, ES8388_LINSEL(ES8388_INPUT1) | // LIN=LIN1-RIN1 (ADCCTRL3[7] DS = 0)
ES8388_RINSEL(ES8388_INPUT1)); // RIN=LIN2-RIN2 (DSR = 1)
break;
}
writeReg(ES8388_REG_ADC_CTRL6, 0x00); // disable HPF
// 0dB
writeReg(ES8388_REG_ADC_CTRL4, ES8388_ADCWL(ES8388_ADCWL_32BIT)); // 24bit
writeReg(ES8388_REG_ADC_CTRL5, ES8388_ADCFSRATIO(ES8388_FSRATIO_256)); // 256*Fs, single speed
// ADC digital volume
writeReg(ESP8388_LADCVOL, 0x00); // 0dB
writeReg(ESP8388_RADCVOL, 0x00); // 0dB
// DAC setup
writeReg(ES8388_REG_DAC_CTRL1, ES8388_DACWL(ES8388_DACWL_32BIT)); // 24bit
writeReg(ES8388_REG_DAC_CTRL2, ES8388_DACFSRATIO(ES8388_FSRATIO_256)); // 256*Fs single speed
// DAC digital volume
writeReg(ES8388_REG_DAC_CTRL4, 0x00); // 0dB
writeReg(ES8388_REG_DAC_CTRL5, 0x00); // 0dB
// Mixer Setup
writeReg(ES8388_REG_DAC_CTRL16, ES8388_LMIXSEL(ES8388_LMIXSEL_ADCL_P) |
ES8388_RMIXSEL(ES8388_RMIXSEL_ADCR_P));
writeReg(ES8388_REG_DAC_CTRL17, ES8388_BIT_LD2LO); // LDAC to left mixer enable, gain 0dB
writeReg(ES8388_REG_DAC_CTRL20, ES8388_BIT_RD2RO); // RDAC to right mixer enable, gain 0dB
// R L OUT volume
dacGain = 0x1E;
writeReg(ES8388_LOUT1VOL, dacGain); // L1 0dB
writeReg(ES8388_ROUT1VOL, dacGain); // R1 0dB
// optimize A/D conversion for 1/4 Vrms range
optimizeConversion(0);
writeReg(ES8388_REG_CHIP_PWR_MAN, 0x00); // Power up DEM and STM
// ALC config
// writeReg(ES8388_REG_ADC_CTRL10, ES8388_ALCSEL(ES8388_ALCSEL_LR) | // ALC OFF
// ES8388_MAXGAIN(ES8388_MAXGAIN_M0_5DB) | // max gain -0.5dB
// ES8388_MINGAIN(ES8388_MINGAIN_M12DB)); // min gain -12dB
// writeReg(ES8388_REG_ADC_CTRL11, ES8388_ALCLVL(0x0A)); // target gain -1.5dB, hold time=0
// writeReg(ES8388_REG_ADC_CTRL12, ES8388_ALCATK(0x02) | // ALC limiter attack time 90.8us
// ES8388_ALCDCY(0x01)); // ALC limiter decay time 182us
// writeReg(ES8388_REG_ADC_CTRL13, ES8388_BIT_ALCMODE | ES8388_WINSIZE(0x06)); // Limiter mode, no ZC, 96*16 samples peak window
// writeReg(ES8388_REG_ADC_CTRL14, 0x00); // disable noise gate
//writeReg(ES8388_REG_ADC_CTRL14, ES8388_NGTH(0x1F) | ES8388_NGG(ES8388_NGG_ADCMUTE)| ES8388_BIT_NGAT_EN);
// ADC PGA gain
//writeReg(ES8388_REG_ADC_CTRL1, 0x00);
configured = true;
return true;
}
void AudioControlES8388_F32::optimizeConversion(uint8_t range)
{
uint8_t ingain[] = {0, 2, 4, 6, 8}; // 0db, 6dB, 12dB, 18dB, 24dB
uint8_t outvol[] = {30, 26, 22, 18, 14}; // 0db, -6dB, -12dB, -18dB, -24dB
if (range < 0) range = 0;
if (range > 4) range = 4;
volume(outvol[range]);
setInGain(ingain[range]);
}
// get and set the output level (analog gain)
// vol = 0-31
void AudioControlES8388_F32::volume(uint8_t vol)
{
if (vol > 30)
vol = 30;
writeReg(ES8388_REG_DAC_CTRL24, vol); // LOUT1VOL
writeReg(ES8388_REG_DAC_CTRL25, vol); // ROUT1VOL
}
bool AudioControlES8388_F32::volume(float n)
{
n = constrain(n, 0.0f, 1.0f);
uint8_t vol = n * 30.99f;
if (vol > 30)
vol = 30;
writeReg(ES8388_REG_DAC_CTRL24, vol); // LOUT1VOL
writeReg(ES8388_REG_DAC_CTRL25, vol); // ROUT1VOL
return true;
}
uint8_t AudioControlES8388_F32::getOutVol()
{
uint8_t vol;
readReg(ES8388_REG_DAC_CTRL24, &vol);
return vol;
}
bool AudioControlES8388_F32::setInGain(uint8_t gain)
{
if (gain > 8)
gain = 8;
uint8_t temp;
temp = gain << 4;
temp = temp | gain;
return writeReg(ES8388_REG_ADC_CTRL1, temp);
}
uint8_t AudioControlES8388_F32::getInGain()
{
uint8_t temp;
readReg(ES8388_REG_ADC_CTRL1, &temp);
temp = (temp & 0xF0) >> 4;
return temp;
}
void AudioControlES8388_F32::set_noiseGate(float thres)
{
uint8_t thres_val = constrain(thres, 0.0f, 1.0f) * 31.99f;
DBG_SERIAL.printf("Gate: %d\r\n", thres_val);
writeReg(ES8388_REG_ADC_CTRL14, ES8388_NGTH(thres_val) | ES8388_NGG(ES8388_NGG_ADCMUTE)| ES8388_BIT_NGAT_EN);
}
// bypassed the analog input to the output, disconnect the digital i / o
bool AudioControlES8388_F32::analogBypass(bool bypass)
{
bool res = true;
if (bypass)
{
res = writeReg(ES8388_REG_DAC_CTRL17, ES8388_BIT_LI2LO | ES8388_LI2LOVOL(ES8388_VOL_0DB));
res &= writeReg(ES8388_REG_DAC_CTRL20, ES8388_BIT_RI2RO | ES8388_RI2ROVOL(ES8388_VOL_0DB));
}
else
{
res = writeReg(ES8388_REG_DAC_CTRL17, ES8388_BIT_LD2LO | ES8388_LI2LOVOL(ES8388_VOL_0DB));
res &= writeReg(ES8388_REG_DAC_CTRL20, ES8388_BIT_RD2RO | ES8388_RI2ROVOL(ES8388_VOL_0DB));
}
return res;
}
// bypassed the analog input to the output, disconnect the digital input, preserve the digital output connection
bool AudioControlES8388_F32::analogSoftBypass(bool bypass)
{
bool res = true;
if (bypass)
{
res &= writeReg(ES8388_REG_DAC_CTRL17, ES8388_BIT_LI2LO | // Lin in on
ES8388_BIT_LD2LO | // L Dac on
ES8388_LI2LOVOL(ES8388_VOL_0DB)); // Lin gain 0dB
res &= writeReg(ES8388_REG_DAC_CTRL20, ES8388_BIT_RI2RO | // Rin in on
ES8388_BIT_RD2RO | // R Dac on
ES8388_RI2ROVOL(ES8388_VOL_0DB)); // Rin gain 0dB
}
else
{
res = writeReg(ES8388_REG_DAC_CTRL17, ES8388_BIT_LD2LO | ES8388_LI2LOVOL(ES8388_VOL_0DB));
res &= writeReg(ES8388_REG_DAC_CTRL20, ES8388_BIT_RD2RO | ES8388_RI2ROVOL(ES8388_VOL_0DB));
}
return res;
}
bool AudioControlES8388_F32::writeReg(uint8_t addr, uint8_t val)
{
ctrlBus->beginTransmission(i2cAddr);
ctrlBus->write(addr);
ctrlBus->write(val);
return ctrlBus->endTransmission() == 0;
}
bool AudioControlES8388_F32::readReg(uint8_t addr, uint8_t *valPtr)
{
ctrlBus->beginTransmission(i2cAddr);
ctrlBus->write(addr);
if (ctrlBus->endTransmission(false) != 0)
return false;
if (ctrlBus->requestFrom((int)i2cAddr, 1) < 1) return false;
*valPtr = ctrlBus->read();
return true;
}
uint8_t AudioControlES8388_F32::modifyReg(uint8_t reg, uint8_t val, uint8_t iMask)
{
uint8_t val1;
val1 = (readReg(reg, &val1) & (~iMask)) | val;
if (!writeReg(reg, val1))
return 0;
return val1;
}

@ -0,0 +1,55 @@
#ifndef _CONTROL_ES8388_F32_H_
#define _CONTROL_ES8388_F32_H_
#include <Arduino.h>
#include <Wire.h>
#include "AudioControl.h"
#define ES8388_I2C_ADDR_L (0x10) // CS/ADD pin low
#define ES8388_I2C_ADDR_H (0x11) // CS/ADD pin high
class AudioControlES8388_F32 //: public AudioControl
{
public:
AudioControlES8388_F32(void){};
~AudioControlES8388_F32(void){};
typedef enum
{
ES8388_CFG_LINEIN_SINGLE_ENDED = 0,
ES8388_CFG_LINEIN_DIFF,
}config_t;
bool enable()
{
return enable(&Wire, ES8388_I2C_ADDR_L, ES8388_CFG_LINEIN_SINGLE_ENDED);
}
bool enable(TwoWire *i2cBus, uint8_t addr, config_t cfg);
bool disable(void) { return false; }
bool volume(float n);
bool inputLevel(float n); // range: 0.0f to 1.0f
void set_noiseGate(float thres);
void volume(uint8_t vol);
uint8_t getOutVol();
bool setInGain(uint8_t gain);
uint8_t getInGain();
bool analogBypass(bool bypass);
bool analogSoftBypass(bool bypass);
private:
static bool configured;
TwoWire *ctrlBus;
uint8_t i2cAddr;
uint8_t dacGain;
bool writeReg(uint8_t addr, uint8_t val);
bool readReg(uint8_t addr, uint8_t* valPtr);
uint8_t modifyReg(uint8_t reg, uint8_t val, uint8_t iMask);
void optimizeConversion(uint8_t range);
};
#endif // _CONTROL_ES8388_F32_H_

File diff suppressed because it is too large Load Diff

@ -1,31 +1,98 @@
#ifndef _CONTROL_SGTL5000_F32_H_ #ifndef _CONTROL_SGTL5000_F32_H_
#define _CONTROL_SGTL5000_F32_H_ #define _CONTROL_SGTL5000_F32_H_
/* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/** /**
* @file control_SGTL5000_ext.h * @file control_SGTL5000_F32.h
* @author Piotr Zapart * @author Piotr Zapart
* @brief enables the bit depth setting for the SGTL5000 codec chip * @brief enables the bit depth setting for the SGTL5000 codec chip + configurable Wire interface
* @version 0.1 * @version 0.1
* @date 2024-03-20 * @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 <Arduino.h>
#include <control_sgtl5000.h> #include <Wire.h>
#include <AudioStream_F32.h>
#include "AudioControl.h"
class AudioControlSGTL5000_F32 : public AudioControlSGTL5000 class AudioControlSGTL5000_F32 : public AudioControl
{ {
//GUI: inputs:0, outputs:0 //this line used for automatic generation of GUI node public:
public: AudioControlSGTL5000_F32(){};
AudioControlSGTL5000_F32(void) {}; void setAddress(uint8_t gpioLevel);
bool enable();
bool enable(TwoWire *i2cBus, uint8_t addr=0x0A, const uint32_t extMCLK=0, const uint32_t pllFreq = (4096.0l * AUDIO_SAMPLE_RATE_EXACT));
bool disable(void) { return false; }
bool volume(float n) { return volumeInteger(n * 129 + 0.499f); }
bool inputLevel(float volume) {return false;}
bool muteHeadphone(void) { return write(0x0024, ana_ctrl | (1 << 4)); }
bool unmuteHeadphone(void) { return write(0x0024, ana_ctrl & ~(1 << 4)); }
bool muteLineout(void) { return write(0x0024, ana_ctrl | (1 << 8)); }
bool unmuteLineout(void) { return write(0x0024, ana_ctrl & ~(1 << 8)); }
bool inputSelect(int n);
bool headphoneSelect(int n);
bool volume(float left, float right);
bool micGain(uint16_t dB);
bool lineInLevel(uint8_t n) { return lineInLevel(n, n); }
bool lineInLevel(uint8_t left, uint8_t right);
uint16_t lineOutLevel(uint8_t n);
uint16_t lineOutLevel(uint8_t left, uint8_t right);
uint16_t dacVolume(float n);
uint16_t dacVolume(float left, float right);
bool dacVolumeRamp();
bool dacVolumeRampLinear();
bool dacVolumeRampDisable();
uint16_t adcHighPassFilterEnable(void);
uint16_t adcHighPassFilterFreeze(void);
uint16_t adcHighPassFilterDisable(void);
uint16_t audioPreProcessorEnable(void);
uint16_t audioPostProcessorEnable(void);
uint16_t audioProcessorDisable(void);
uint16_t eqFilterCount(uint8_t n);
uint16_t eqSelect(uint8_t n);
uint16_t eqBand(uint8_t bandNum, float n);
void eqBands(float bass, float mid_bass, float midrange, float mid_treble, float treble);
void eqBands(float bass, float treble);
void eqFilter(uint8_t filterNum, int *filterParameters);
uint16_t autoVolumeControl(uint8_t maxGain, uint8_t lbiResponse, uint8_t hardLimit, float threshold, float attack, float decay);
uint16_t autoVolumeEnable(void);
uint16_t autoVolumeDisable(void);
uint16_t enhanceBass(float lr_lev, float bass_lev);
uint16_t enhanceBass(float lr_lev, float bass_lev, uint8_t hpf_bypass, uint8_t cutoff);
uint16_t enhanceBassEnable(void);
uint16_t enhanceBassDisable(void);
uint16_t surroundSound(uint8_t width);
uint16_t surroundSound(uint8_t width, uint8_t select);
uint16_t surroundSoundEnable(void);
uint16_t surroundSoundDisable(void);
void killAutomation(void) { semi_automated = false; }
void setMasterMode(uint32_t freqMCLK_in);
typedef enum typedef enum
{ {
I2S_BITS_32 = 0, I2S_BITS_32 = 0,
@ -33,8 +100,24 @@ class AudioControlSGTL5000_F32 : public AudioControlSGTL5000
I2S_BITS_20, I2S_BITS_20,
I2S_BITS_16 I2S_BITS_16
}bit_depth_t; }bit_depth_t;
void set_bitDepth(bit_depth_t bits); void set_bitDepth(bit_depth_t bits);
protected:
bool muted;
bool volumeInteger(uint16_t n); // range: 0x00 to 0x80
uint16_t ana_ctrl;
uint8_t i2c_addr;
unsigned char calcVol(float n, unsigned char range);
uint16_t read(uint16_t reg);
bool write(uint16_t reg, uint16_t val);
uint16_t modify(uint16_t reg, uint16_t val, uint16_t iMask);
uint16_t dap_audio_eq_band(uint8_t bandNum, float n);
private:
bool semi_automated;
void automate(uint8_t dap, uint8_t eq);
void automate(uint8_t dap, uint8_t eq, uint8_t filterCount);
TwoWire *_wire;
}; };
#endif // _CONTROL_SGTL5000_EXT_H_ #endif // _CONTROL_SGTL5000_F32_H_

@ -198,116 +198,179 @@
#define WM8731_REG_ACTIVE (9) #define WM8731_REG_ACTIVE (9)
#define WM8731_REG_RESET (15) #define WM8731_REG_RESET (15)
// ----------------------------------------------------------------------------------
bool AudioControlWM8731_F32::enable(bit_depth_t bits, uint8_t addr) bool AudioControlWM8731_F32::enable(bit_depth_t bits, TwoWire *i2cBus, uint8_t addr)
{ {
_wire = i2cBus;
i2c_addr = addr; i2c_addr = addr;
Wire.begin();
_wire->begin();
delay(5); delay(5);
if (!write(WM8731_REG_RESET, 0)) if (!write(WM8731_REG_RESET, 0))
{ {
return false; // no WM8731 chip responding return false; // no WM8731 chip responding
} }
write(WM8731_REG_INTERFACE, WM8731_BITS_FORMAT(WM8731_FORMAT_I2S_MSB_LEFT) | write(WM8731_REG_INTERFACE, WM8731_BITS_FORMAT(WM8731_FORMAT_I2S_MSB_LEFT) |
WM8731_BITS_IWL(bits)); // I2S, x bit, MCLK slave 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), write(WM8731_REG_SAMPLING, WM8731_BITS_USB_NORMAL(0) | // normal mode
// the output should then be de-selected from the line and headphone output WM8731_BITS_BOSR(0) | // 256*fs
// (DACSEL), then the DAC powered down (DACPD). WM8731_BITS_SR(8) | // 44.1kHz
WM8731_BITS_CLKIDIV2(0) | // MCLK/1
WM8731_BITS_CLKODIV2(0));
write(WM8731_REG_DIGITAL, 0x08); // DAC soft mute write(WM8731_REG_DIGITAL, WM8731_BITS_DACMU(1)); // Soft mute DAC
write(WM8731_REG_ANALOG, 0x00); // disable all write(WM8731_REG_ANALOG, 0x00); // disable all
write(WM8731_REG_POWERDOWN, 0x00); // codec powerdown write(WM8731_REG_POWERDOWN, 0x00); // codec powerdown
write(WM8731_REG_LHEADOUT, 0x80); // volume off write(WM8731_REG_LHEADOUT, 0x80); // volume off
write(WM8731_REG_RHEADOUT, 0x80); write(WM8731_REG_RHEADOUT, 0x80);
delay(300);
delay(100); // how long to power up?
write(WM8731_REG_ACTIVE, 1); write(WM8731_REG_ACTIVE, 1);
delay(5); delay(5);
write(WM8731_REG_DIGITAL, 0x00); // DAC unmuted write(WM8731_REG_DIGITAL, WM8731_BITS_DACMU(0)); // DAC unmuted
write(WM8731_REG_ANALOG, 0x10); // DAC selected write(WM8731_REG_ANALOG, WM8731_BITS_DACSEL(1)); // DAC selected
return true; return true;
} }
// ----------------------------------------------------------------------------------
void AudioControlWM8731_F32::dac_mute(bool m) void AudioControlWM8731_F32::dac_mute(bool m)
{ {
write(WM8731_REG_DIGITAL, m ? WM8731_BITS_DACMU(1) : WM8731_BITS_DACMU(0)); // DAC soft mute modify(WM8731_REG_DIGITAL, m ? WM8731_BITS_DACMU(1) : WM8731_BITS_DACMU(0), WM8731_BITS_DACMU_MASK);
//write(WM8731_REG_DIGITAL, ); // DAC soft mute
DACmute = m; DACmute = m;
} }
// ----------------------------------------------------------------------------------
void AudioControlWM8731_F32::HPfilter(bool state) void AudioControlWM8731_F32::hp_filter(bool state)
{ {
write(WM8731_REG_DIGITAL, WM8731_BITS_DACMU(DACmute) | WM8731_BITS_ADCHPD(state)); modify(WM8731_REG_DIGITAL, WM8731_BITS_ADCHPD(state), WM8731_BITS_ADCHPD_MASK);
//write(WM8731_REG_DIGITAL, WM8731_BITS_DACMU(DACmute) | WM8731_BITS_ADCHPD(state));
} }
// ----------------------------------------------------------------------------------
bool AudioControlWM8731_F32::write(unsigned int reg, unsigned int val) // Freeze the HP filter
void AudioControlWM8731_F32::dcbias_store(bool state)
{ {
modify(WM8731_REG_DIGITAL, WM8731_BITS_HPOR(state), WM8731_BITS_HPOR_MASK);
}
// ----------------------------------------------------------------------------------
// Mute both Line inputs
void AudioControlWM8731_F32::lineIn_mute(bool m)
{
modify(WM8731_REG_LLINEIN, WM8731_BITS_LINMUTE(m), WM8731_BITS_LINMUTE_MASK);
modify(WM8731_REG_RLINEIN, WM8731_BITS_RINMUTE(m), WM8731_BITS_RINMUTE_MASK);
}
// ----------------------------------------------------------------------------------
// Enable/Disable DAC output mixer switch
void AudioControlWM8731_F32::dac_enable(bool en)
{
modify(WM8731_REG_ANALOG, WM8731_BITS_DACSEL(en), WM8731_BITS_DACSEL_MASK);
}
// ----------------------------------------------------------------------------------
// Enable Dry (Bypass) output mixer switch
void AudioControlWM8731_F32::dry_enable(bool en)
{
modify(WM8731_REG_ANALOG, WM8731_BITS_BYPASS(en), WM8731_BITS_BYPASS_MASK);
dry_sig = en;
}
// ----------------------------------------------------------------------------------
// analog bypass switch
void AudioControlWM8731_F32::bypass_set(bool b)
{
uint8_t bp_state = ((((uint8_t)dry_sig)<<1) & 0x01) | b;
switch(bp_state)
{
case 0b00: // Dry Off, Wet on -> pass Wet only
case 0b11:
dry_enable(false);
dac_enable(true);
break;
case 0b01: // dry OFF, bypass ON -> pass Dry only
dry_enable(false);
dac_enable(true);
break;
case 0b10: // Dry on, Wet on
dry_enable(true);
dac_enable(true);
break;
default: break;
}
}
// ----------------------------------------------------------------------------------
bool AudioControlWM8731_F32::write(uint16_t regAddr, uint16_t val)
{
reg[regAddr] = val;
int attempt = 0; int attempt = 0;
while (1) while (1)
{ {
attempt++; attempt++;
Wire.beginTransmission(i2c_addr); _wire->beginTransmission(i2c_addr);
Wire.write((reg << 1) | ((val >> 8) & 1)); _wire->write((regAddr << 1) | ((val >> 8) & 1));
Wire.write(val & 0xFF); _wire->write(val & 0xFF);
int status = Wire.endTransmission(); int status = _wire->endTransmission();
if (status == 0) return true; if (status == 0) return true;
if (attempt >= 12) return false; if (attempt >= 12) return false;
delayMicroseconds(80); delayMicroseconds(80);
} }
} }
// ----------------------------------------------------------------------------------
uint16_t AudioControlWM8731_F32::modify(uint16_t regAddr, uint16_t val, uint16_t iMask)
bool AudioControlWM8731_F32::volumeInteger(unsigned int n) {
reg[regAddr] = (reg[regAddr] & (~iMask)) | val;
if (!write(regAddr, reg[regAddr])) return 0;
return reg[regAddr];
}
// ----------------------------------------------------------------------------------
// Set the headphone volume
bool AudioControlWM8731_F32::hp_volumeInteger(uint16_t n)
{ {
// n = 127 for max volume (+6 dB) // n = 127 for max volume (+6 dB)
// n = 48 for min volume (-73 dB) // n = 48 for min volume (-73 dB)
// n = 0 to 47 for mute // n = 0 to 47 for mute
if (n > 127) if (n > 127) n = 127;
n = 127;
// Serial.print("volumeInteger, n = ");
// Serial.println(n);
write(WM8731_REG_LHEADOUT, n | 0x180); write(WM8731_REG_LHEADOUT, n | 0x180);
write(WM8731_REG_RHEADOUT, n | 0x80); write(WM8731_REG_RHEADOUT, n | 0x80);
return true; return true;
} }
// ----------------------------------------------------------------------------------
bool AudioControlWM8731_F32::inputLevel(float n) bool AudioControlWM8731_F32::inputLevel(float n)
{ {
// range is 0x00 (min) - 0x1F (max) // range is 0x00 (min) - 0x1F (max)
int _level = int(n * 31.f); int _level = int(n * 31.f);
_level = _level > 0x1F ? 0x1F : _level; _level = _level > 0x1F ? 0x1F : _level;
write(WM8731_REG_LLINEIN, _level); write(WM8731_REG_LLINEIN, _level);
write(WM8731_REG_RLINEIN, _level); write(WM8731_REG_RLINEIN, _level);
return true; return true;
} }
// ----------------------------------------------------------------------------------
bool AudioControlWM8731_F32::inputSelect(input_select_t n) bool AudioControlWM8731_F32::inputLevelraw(uint8_t n)
{
// range is 0x00 (min) - 0x1F (max)
n = n > 0x1F ? 0x1F : n;
write(WM8731_REG_LLINEIN, n);
write(WM8731_REG_RLINEIN, n);
return true;
}
// ----------------------------------------------------------------------------------
bool AudioControlWM8731_F32::inputSelect(int n)
{ {
if (n == INPUT_SELECT_LINEIN) write(WM8731_REG_ANALOG, 0x12); if (n == AUDIO_INPUT_LINEIN) modify(WM8731_REG_ANALOG, WM8731_BITS_INSEL(0), WM8731_BITS_INSEL_MASK);
else if (n == INPUT_SELECT_MIC) write(WM8731_REG_ANALOG, 0x15); else if (n == AUDIO_INPUT_MIC) modify(WM8731_REG_ANALOG, WM8731_BITS_INSEL(1), WM8731_BITS_INSEL_MASK);
else return false; else return false;
return true; return true;
} }
/******************************************************************/ /******************************************************************/
bool AudioControlWM8731_F32_master::enable(bit_depth_t bits, uint8_t addr) bool AudioControlWM8731_F32_master::enable(bit_depth_t bits, TwoWire *i2cBus, uint8_t addr)
{ {
_wire = i2cBus;
i2c_addr = addr; i2c_addr = addr;
Wire.begin(); _wire->begin();
delay(5); delay(5);
// write(WM8731_REG_RESET, 0); // write(WM8731_REG_RESET, 0);
write(WM8731_REG_INTERFACE, write(WM8731_REG_INTERFACE,
WM8731_BITS_FORMAT(WM8731_FORMAT_I2S_MSB_LEFT) | WM8731_BITS_FORMAT(WM8731_FORMAT_I2S_MSB_LEFT) |
WM8731_BITS_IWL(bits)| WM8731_BITS_IWL(bits)|
WM8731_BITS_MS(1)); // I2S, x bit, MCLK slave WM8731_BITS_MS(1)); // I2S, x bit, MCLK master
write(WM8731_REG_SAMPLING, 0x20); // 256*Fs, 44.1 kHz, MCLK/1 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), // In order to prevent pops, the DAC should first be soft-muted (DACMU),

@ -19,14 +19,16 @@
#define _CONTROL_WM8731_F32_H_ #define _CONTROL_WM8731_F32_H_
#include <Arduino.h> #include <Arduino.h>
#include <Wire.h>
#include "AudioControl.h"
#define WM8731_I2C_ADDR_CSB0 0x1A #define WM8731_I2C_ADDR_CSB0 0x1A
#define WM8731_I2C_ADDR_CSB1 0x1B #define WM8731_I2C_ADDR_CSB1 0x1B
class AudioControlWM8731_F32 class AudioControlWM8731_F32 : public AudioControl
{ {
public: public:
AudioControlWM8731_F32(){}; AudioControlWM8731_F32() {}
typedef enum typedef enum
{ {
@ -36,34 +38,50 @@ public:
I2S_BITS_32 I2S_BITS_32
}bit_depth_t; }bit_depth_t;
typedef enum bool enable()
{ {
INPUT_SELECT_LINEIN = 0, return enable(I2S_BITS_32, &Wire, WM8731_I2C_ADDR_CSB0);
INPUT_SELECT_MIC }
}input_select_t; bool enable(bit_depth_t bits, TwoWire *i2cBus, uint8_t addr);
bool enable(bit_depth_t bits = I2S_BITS_16, uint8_t addr=WM8731_I2C_ADDR_CSB0);
bool disable(void) { return false; } bool disable(void) { return false; }
bool volume(float n) { return volumeInteger(n * 80.0f + 47.499f); } bool volume(float n) { return hp_volumeInteger(n * 80.0f + 47.499f); }
bool inputLevel(float n); // range: 0.0f to 1.0f bool inputLevel(float n); // range: 0.0f to 1.0f
bool inputSelect(input_select_t n=INPUT_SELECT_LINEIN); bool inputLevelraw(uint8_t n); // direc value 0-31
bool inputSelect(int n=AUDIO_INPUT_LINEIN);
void lineIn_mute(bool m);
void dry_enable(bool en); // bypass without muting the DAC
void hp_filter(bool state);
void dcbias_store(bool state);
void dac_mute(bool m); void dac_mute(bool m);
void HPfilter(bool state); void dac_enable(bool en);
void bypass_set(bool b);
bool bypass_get() {return bp;}
bool bypass_tgl() {bp ^= 1; bypass_set(bp); return bp;}
protected: protected:
bool write(unsigned int reg, unsigned int val); bool write(uint16_t regAddr, uint16_t val);
bool volumeInteger(unsigned int n); // range: 0x2F to 0x7F uint16_t read(uint16_t reg);
uint16_t modify(uint16_t reg, uint16_t val, uint16_t iMask);
bool hp_volumeInteger(uint16_t n); // range: 0x2F to 0x7F
private: private:
uint8_t bit_depth = I2S_BITS_16; uint16_t reg[16];
uint8_t bit_depth = I2S_BITS_32;
uint8_t i2c_addr; uint8_t i2c_addr;
bool DACmute = false; bool DACmute = false;
TwoWire *_wire;
bool dry_sig = false;
bool bp = false; // used for analog bypass
}; };
class AudioControlWM8731_F32_master : public AudioControlWM8731_F32 class AudioControlWM8731_F32_master : public AudioControlWM8731_F32
{ {
public: public:
bool enable(bit_depth_t bits = I2S_BITS_16, uint8_t addr=WM8731_I2C_ADDR_CSB0); bool enable(bit_depth_t bits = I2S_BITS_32, TwoWire *i2cBus = &Wire, uint8_t addr=WM8731_I2C_ADDR_CSB0);
private: private:
uint8_t i2c_addr; uint8_t i2c_addr;
TwoWire *_wire;
bool dry_sig = false;
}; };
#endif // _CONTROL_WM8731_EXTENDED_H_ #endif // _CONTROL_WM8731_EXTENDED_H_

@ -18,6 +18,23 @@
#include <arm_math.h> //ARM DSP extensions. https://www.keil.com/pack/doc/CMSIS/DSP/html/index.html #include <arm_math.h> //ARM DSP extensions. https://www.keil.com/pack/doc/CMSIS/DSP/html/index.html
#include <AudioStream_F32.h> #include <AudioStream_F32.h>
#include "basic_DSPutils.h"
// ranges used for normalized parameters.
// input is 0.0f to 1.0f, output RANGE_MIN to RANGE_MAX
#define COMPRESSOR_PREGAIN_RANGE_MIN (0.0f)
#define COMPRESSOR_PREGAIN_RANGE_MAX (4.0f)
#define COMPRESSOR_POSTGAIN_RANGE_MIN (0.0f)
#define COMPRESSOR_POSTGAIN_RANGE_MAX (4.0f)
#define COMPRESSOR_ATTACK_RANGE_MIN (0.001f)
#define COMPRESSOR_ATTACK_RANGE_MAX (0.1f)
#define COMPRESSOR_RELEASE_RANGE_MIN (0.1f)
#define COMPRESSOR_RELEASE_RANGE_MAX (1.0f)
#define COMPRESSOR_THRES_RANGE_MIN (0.0f)
#define COMPRESSOR_THRES_RANGE_MAX (-40.0f)
#define COMPRESSOR_RATIO_RANGE_MIN (0.0f)
#define COMPRESSOR_RATIO_RANGE_MAX (10.0f)
class AudioEffectCompressorStereo_F32 : public AudioStream_F32 class AudioEffectCompressorStereo_F32 : public AudioStream_F32
{ {
@ -281,30 +298,48 @@ public:
arm_biquad_cascade_df1_init_f32(&hp_filt_structR, hp_nstages, hp_coeff, hp_stateR); arm_biquad_cascade_df1_init_f32(&hp_filt_structR, hp_nstages, hp_coeff, hp_stateR);
} }
void setPreGain(float g) { pre_gain = g; } void setPreGain(float g) { pre_gain = g; }
void setPreGain_normalized(float g) { pre_gain = map_sat(g, 0.0f, 1.0f, COMPRESSOR_PREGAIN_RANGE_MIN, COMPRESSOR_PREGAIN_RANGE_MAX); }
void setPreGain_dB(float gain_dB) { setPreGain(pow(10.0f, gain_dB / 20.0f)); } void setPreGain_dB(float gain_dB) { setPreGain(pow(10.0f, gain_dB / 20.0f)); }
void setPostGain(float g) { post_gain = g; } void setPostGain(float g) { post_gain = g; }
void setPostGain_normalized(float g) { post_gain = map_sat(g, 0.0f, 1.0f, COMPRESSOR_POSTGAIN_RANGE_MIN, COMPRESSOR_POSTGAIN_RANGE_MAX); }
void setPostGain_dB(float gain_dB) { setPostGain(pow(10.0f, gain_dB / 20.0f)); } void setPostGain_dB(float gain_dB) { setPostGain(pow(10.0f, gain_dB / 20.0f)); }
void setCompressionRatio(float cr) void setCompressionRatio(float cr)
{ {
comp_ratio = max(0.001f, cr); // limit to positive values comp_ratio = max(0.001f, cr); // limit to positive values
updateThresholdAndCompRatioConstants(); updateThresholdAndCompRatioConstants();
} }
void setCompressionRatio_normalized(float cr)
{
cr = map_sat(cr, 0.0f, 1.0f, COMPRESSOR_RATIO_RANGE_MIN, COMPRESSOR_RATIO_RANGE_MAX);
setCompressionRatio(cr);
}
void setAttack_sec(float a) void setAttack_sec(float a)
{ {
attack_sec = a; attack_sec = a;
attack_const = expf(-1.0f / (attack_sec * fs_Hz)); // expf() is much faster than exp() attack_const = expf(-1.0f / (attack_sec * fs_Hz)); // expf() is much faster than exp()
// also update the time constant for the envelope extraction // 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 setLevelTimeConst_sec(min(attack_sec, release_sec) / 5.0f); // make the level time-constant one-fifth the gain time constants
} }
void setAttack_normalized(float a)
{
a = map_sat(a, 0.0f, 1.0f, COMPRESSOR_ATTACK_RANGE_MIN, COMPRESSOR_ATTACK_RANGE_MAX);
setAttack_sec(a);
}
void setRelease_sec(float r) void setRelease_sec(float r)
{ {
release_sec = r; release_sec = r;
release_const = expf(-1.0f / (release_sec * fs_Hz)); // expf() is much faster than exp() release_const = expf(-1.0f / (release_sec * fs_Hz)); // expf() is much faster than exp()
// also update the time constant for the envelope extraction // 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 setLevelTimeConst_sec(min(attack_sec, release_sec) / 5.0f); // make the level time-constant one-fifth the gain time constants
} }
void setRelease_normalized(float r)
{
r = map_sat(r, 0.0f, 1.0f, COMPRESSOR_RELEASE_RANGE_MIN, COMPRESSOR_RELEASE_RANGE_MAX);
setRelease_sec(r);
}
void setLevelTimeConst_sec(float t_sec) void setLevelTimeConst_sec(float t_sec)
{ {
const float min_t_sec = 0.002f; // this is the minimum allowed value const float min_t_sec = 0.002f; // this is the minimum allowed value
@ -316,6 +351,12 @@ public:
thresh_dBFS = val; thresh_dBFS = val;
setThreshPow(pow(10.0f, thresh_dBFS / 10.0f)); setThreshPow(pow(10.0f, thresh_dBFS / 10.0f));
} }
void setThresh_normalized(float val)
{
val = map_sat(val, 0.0f, 1.0f, COMPRESSOR_THRES_RANGE_MIN, COMPRESSOR_THRES_RANGE_MAX);
setThresh_dBFS(val);
}
void enableHPFilter(boolean flag) { use_HP_prefilter = flag; }; void enableHPFilter(boolean flag) { use_HP_prefilter = flag; };
// methods to return information about this module // methods to return information about this module

@ -38,19 +38,27 @@ AudioEffectDelayStereo_F32::AudioEffectDelayStereo_F32(uint32_t dly_range_ms, bo
void AudioEffectDelayStereo_F32::begin(uint32_t dly_range_ms, bool use_psram) void AudioEffectDelayStereo_F32::begin(uint32_t dly_range_ms, bool use_psram)
{ {
initialized = false;
// failsafe if psram is required but not found // failsafe if psram is required but not found
// limit the delay time to 500ms (88200 bytes at 44.1kHz) // limit the delay time to 500ms (88200 bytes at 44.1kHz)
psram_mode = use_psram;
#if ARDUINO_TEENSY41
if (psram_mode && external_psram_size == 0) if (psram_mode && external_psram_size == 0)
{ {
psram_mode = false; psram_mode = false;
if (dly_range_ms > 500) dly_range_ms = 500; if (dly_range_ms > 200) dly_range_ms = 200;
} }
#else
psram_mode = false;
if (dly_range_ms > 200) dly_range_ms = 200;
#endif
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)/1000.0f) * AUDIO_SAMPLE_RATE_EXACT;
if (!dly0a.init(dly_length, use_psram)) memOk = false; if (!dly0a.init(dly_length, psram_mode)) memOk = false;
if (!dly0b.init(dly_length, use_psram)) memOk = false; if (!dly0b.init(dly_length, psram_mode)) memOk = false;
if (!dly1a.init(dly_length, use_psram)) memOk = false; if (!dly1a.init(dly_length, psram_mode)) memOk = false;
if (!dly1b.init(dly_length, use_psram)) memOk = false; if (!dly1b.init(dly_length, psram_mode)) memOk = false;
flt0L.init(BASS_LOSS_FREQ, &bassCut_k, TREBLE_LOSS_FREQ, &trebleCut_k); flt0L.init(BASS_LOSS_FREQ, &bassCut_k, TREBLE_LOSS_FREQ, &trebleCut_k);
flt1L.init(BASS_LOSS_FREQ, &bass_k, TREBLE_LOSS_FREQ, &treble_k); flt1L.init(BASS_LOSS_FREQ, &bass_k, TREBLE_LOSS_FREQ, &treble_k);
flt0R.init(BASS_LOSS_FREQ, &bassCut_k, TREBLE_LOSS_FREQ, &trebleCut_k); flt0R.init(BASS_LOSS_FREQ, &bassCut_k, TREBLE_LOSS_FREQ, &trebleCut_k);
@ -254,6 +262,7 @@ void AudioEffectDelayStereo_F32::freeze(bool state)
*/ */
bool AudioEffectDelayStereo_F32::memCleanup() bool AudioEffectDelayStereo_F32::memCleanup()
{ {
static uint8_t dlyIdx = 0; static uint8_t dlyIdx = 0;
bool result = false; bool result = false;
if (dlyIdx == 0) // value 0 is used to reset the addr if (dlyIdx == 0) // value 0 is used to reset the addr

@ -281,6 +281,7 @@ public:
} }
return tempo_ticks; return tempo_ticks;
} }
bool is_initialized() {return initialized;}
private: private:
audio_block_f32_t *inputQueueArray[2]; audio_block_f32_t *inputQueueArray[2];

@ -34,6 +34,7 @@ public:
audio_block_f32_t *blockL, *blockR; audio_block_f32_t *blockL, *blockR;
blockL = AudioStream_F32::receiveWritable_f32(0); blockL = AudioStream_F32::receiveWritable_f32(0);
blockR = AudioStream_F32::receiveWritable_f32(1); blockR = AudioStream_F32::receiveWritable_f32(1);
float gL, gR;
if (!blockL || !blockR) if (!blockL || !blockR)
{ {
if (blockL) if (blockL)
@ -44,9 +45,10 @@ public:
} }
gainL += (gainLset - gainL) * 0.25f; gainL += (gainLset - gainL) * 0.25f;
gainR += (gainRset - gainR) * 0.25f; gainR += (gainRset - gainR) * 0.25f;
if (phase_flip) { gL = -gainL; gR = -gainR; }
arm_scale_f32(blockL->data, gainL, blockL->data, blockL->length); // use ARM DSP for speed! else { gL = gainL; gR = gainR; }
arm_scale_f32(blockR->data, gainR, blockR->data, blockR->length); arm_scale_f32(blockL->data, gL, blockL->data, blockL->length); // use ARM DSP for speed!
arm_scale_f32(blockR->data, gR, 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);
@ -90,11 +92,19 @@ public:
} }
float32_t getPan() { return pan;} float32_t getPan() { return pan;}
void phase_inv(bool inv)
{
__disable_irq();
phase_flip = inv;
__enable_irq();
}
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
float32_t gain = 1.0f; // default value float32_t gain = 1.0f; // default value
float32_t gainL, gainR, gainLset, gainRset; float32_t gainL, gainR, gainLset, gainRset;
float32_t pan, panL, panR; float32_t pan, panL, panR;
bool phase_flip = false;
}; };
#endif #endif

@ -59,6 +59,7 @@ public:
tone(1.0f); tone(1.0f);
hpPost_k = omega(GBOOST_BOTTOM_MINF); hpPost_k = omega(GBOOST_BOTTOM_MINF);
lp2_k = omega(GBOOST_LP2_F); lp2_k = omega(GBOOST_LP2_F);
gainRange = 4.0f;
} }
void drive(float32_t value) void drive(float32_t value)
{ {
@ -68,6 +69,27 @@ public:
gainSet = value; gainSet = value;
__enable_irq(); __enable_irq();
} }
/**
* @brief Normalized drive, scaled to 1.0 ... gainRange value
*
* @param value 0.0f - 1.0f
*/
void drive_normalized(float32_t value)
{
value = fabs(value);
value = constrain(value, 0.0f, 1.0f);
value = 1.0f + value * upsample_k * gainRange;
__disable_irq()
gainSet = value;
__enable_irq();
}
void driveRange(float32_t value)
{
__disable_irq()
gainRange = value;
__enable_irq();
}
void bottom(float32_t bottom); void bottom(float32_t bottom);
void tone(float32_t t); void tone(float32_t t);
void bias(float32_t b) void bias(float32_t b)
@ -146,8 +168,8 @@ private:
float32_t dryGain = 0.0f; float32_t dryGain = 0.0f;
float32_t wetGain = 1.0f; float32_t wetGain = 1.0f;
float32_t DCbias = 0.175f; float32_t DCbias = 0.175f;
float32_t gainRange = 4.0f;
float32_t gainSet = 1.0f; float32_t gainSet = 1.0f; // gain is in range 0.0 to 1.0, scaled to 0.0 to gainRange
float32_t gain = 0.0f; float32_t gain = 0.0f;
float32_t gain_hp = 1.0f; float32_t gain_hp = 1.0f;
float32_t levelSet = 1.0f; float32_t levelSet = 1.0f;

@ -16,6 +16,16 @@
#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>
// ranges used for normalized param settings
#define NOISEGATE_THRES_MIN (0.0f)
#define NOISEGATE_THRES_MAX (-100.0f)
#define NOISEGATE_OPENT_MIN (0.001f)
#define NOISEGATE_OPENT_MAX (0.1f)
#define NOISEGATE_HOLDT_MIN (0.001f)
#define NOISEGATE_HOLDT_MAX (0.1f)
#define NOISEGATE_CLOSET_MIN (0.001f)
#define NOISEGATE_CLOSET_MAX (0.1f)
class AudioEffectNoiseGateStereo_F32 : public AudioStream_F32 class AudioEffectNoiseGateStereo_F32 : public AudioStream_F32
{ {
public: public:
@ -108,21 +118,41 @@ public:
// 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 setThreshold_normalized(float dbfs)
{
dbfs = map_sat(dbfs, 0.0f, 1.0f, NOISEGATE_THRES_MIN, NOISEGATE_THRES_MAX);
setThreshold(dbfs);
}
void setOpeningTime(float timeInSeconds) void setOpeningTime(float timeInSeconds)
{ {
openingTimeConst = expf(-1.0f / (timeInSeconds * fs)); openingTimeConst = expf(-1.0f / (timeInSeconds * fs));
} }
void setOpeningTime_normalized(float time)
{
time = map_sat(time, 0.0f, 1.0f, NOISEGATE_OPENT_MIN, NOISEGATE_OPENT_MAX);
setOpeningTime(time);
}
void setClosingTime(float timeInSeconds) void setClosingTime(float timeInSeconds)
{ {
closingTimeConst = expf(-1.0f / (timeInSeconds * fs)); closingTimeConst = expf(-1.0f / (timeInSeconds * fs));
} }
void setClosingTime_normalized(float time)
{
time = map_sat(time, 0.0f, 1.0f, NOISEGATE_CLOSET_MIN, NOISEGATE_CLOSET_MAX);
setClosingTime(time);
}
void setHoldTime(float timeInSeconds) void setHoldTime(float timeInSeconds)
{ {
holdTimeNumSamples = timeInSeconds * fs; holdTimeNumSamples = timeInSeconds * fs;
} }
void setHoldTime_normalized(float time)
{
time = map_sat(time, 0.0f, 1.0f, NOISEGATE_HOLDT_MIN, NOISEGATE_HOLDT_MAX);
setHoldTime(time);
}
bool infoIsOpen() bool infoIsOpen()
{ {

@ -49,13 +49,20 @@ AudioEffectReverbSc_F32::AudioEffectReverbSc_F32(bool use_psram) : AudioStream_F
n_bytes = 0; n_bytes = 0;
if (use_psram) if (use_psram)
{ {
#if ARDUINO_TEENSY41
// no PSRAM detected - enter the memoery failsafe mode = fixed bypass // no PSRAM detected - enter the memoery failsafe mode = fixed bypass
if (external_psram_size == 0) if (external_psram_size == 0)
{ {
flags.mem_fail = 1; flags.mem_fail = 1;
initialised = true;
return; return;
} }
aux_ = (float32_t *) extmem_malloc(aux_size_bytes); aux_ = (float32_t *) extmem_malloc(aux_size_bytes);
#else
flags.mem_fail = 1;
initialised = true;
return;
#endif
} }
else else
{ {

@ -41,39 +41,44 @@ public:
void update() void update()
{ {
audio_block_f32_t *blockLa, *blockRa, *blockLb, *blockRb; audio_block_f32_t *blockLa, *blockRa, *blockLb, *blockRb;
audio_block_f32_t *blockOutLa, *blockOutRa,*blockOutLb, *blockOutRb; audio_block_f32_t *blockOutLa, *blockOutRa,*blockOutLb, *blockOutRb, *blockZero;
blockZero = AudioStream_F32::allocate_f32();
if(!blockZero) return;
memset(&blockZero->data[0], 0, blockZero->length*sizeof(float32_t));
blockLa = AudioStream_F32::receiveReadOnly_f32(0); blockLa = AudioStream_F32::receiveReadOnly_f32(0);
blockRa = AudioStream_F32::receiveReadOnly_f32(1); blockRa = AudioStream_F32::receiveReadOnly_f32(1);
blockLb = AudioStream_F32::receiveReadOnly_f32(2); blockLb = AudioStream_F32::receiveReadOnly_f32(2);
blockRb = AudioStream_F32::receiveReadOnly_f32(3); blockRb = AudioStream_F32::receiveReadOnly_f32(3);
if (!blockLa || !blockRa || !blockLb || !blockRb) if (!blockLa) blockLa = blockZero;
{ if (!blockLb) blockLb = blockZero;
if (blockLa) AudioStream_F32::release(blockLa); if (!blockRa) blockRa = blockZero;
if (blockRa) AudioStream_F32::release(blockRa); if (!blockRb) blockRb = blockZero;
if (blockLb) AudioStream_F32::release(blockLb);
if (blockRb) AudioStream_F32::release(blockRb);
return;
}
// max A, B mited // max A, B mited
if (gainA == 1.0f) if (gainA == 1.0f)
{ {
AudioStream_F32::transmit(blockLa, 0); AudioStream_F32::transmit(blockLa, 0);
AudioStream_F32::transmit(blockRa, 1); AudioStream_F32::transmit(blockRa, 1);
AudioStream_F32::release(blockLa); if (blockLa != blockZero) AudioStream_F32::release(blockLa);
AudioStream_F32::release(blockRa); if (blockRa != blockZero) AudioStream_F32::release(blockRa);
AudioStream_F32::release(blockLb); if (blockLb != blockZero) AudioStream_F32::release(blockLb);
AudioStream_F32::release(blockRb); if (blockRb != blockZero) AudioStream_F32::release(blockRb);
AudioStream_F32::release(blockZero);
return; return;
} }
if (gainB == 1.0f) if (gainB == 1.0f)
{ {
AudioStream_F32::transmit(blockLb, 0); AudioStream_F32::transmit(blockLb, 0);
AudioStream_F32::transmit(blockRb, 1); AudioStream_F32::transmit(blockRb, 1);
AudioStream_F32::release(blockLa); if (blockLa != blockZero) AudioStream_F32::release(blockLa);
AudioStream_F32::release(blockRa); if (blockRa != blockZero) AudioStream_F32::release(blockRa);
AudioStream_F32::release(blockLb); if (blockLb != blockZero) AudioStream_F32::release(blockLb);
AudioStream_F32::release(blockRb); if (blockRb != blockZero) AudioStream_F32::release(blockRb);
AudioStream_F32::release(blockZero);
return; return;
} }
blockOutLa = AudioStream_F32::allocate_f32(); blockOutLa = AudioStream_F32::allocate_f32();
@ -97,14 +102,16 @@ public:
arm_add_f32(blockOutRa->data, blockOutRb->data, blockOutRa->data, blockOutRa->length); arm_add_f32(blockOutRa->data, blockOutRb->data, blockOutRa->data, blockOutRa->length);
AudioStream_F32::transmit(blockOutLa, 0); AudioStream_F32::transmit(blockOutLa, 0);
AudioStream_F32::transmit(blockOutRa, 1); AudioStream_F32::transmit(blockOutRa, 1);
AudioStream_F32::release(blockLa); if (blockLa != blockZero) AudioStream_F32::release(blockLa);
AudioStream_F32::release(blockRa); if (blockRa != blockZero) AudioStream_F32::release(blockRa);
AudioStream_F32::release(blockLb); if (blockLb != blockZero) AudioStream_F32::release(blockLb);
AudioStream_F32::release(blockRb); if (blockRb != blockZero) AudioStream_F32::release(blockRb);
AudioStream_F32::release(blockZero);
AudioStream_F32::release(blockOutLa); AudioStream_F32::release(blockOutLa);
AudioStream_F32::release(blockOutRa); AudioStream_F32::release(blockOutRa);
AudioStream_F32::release(blockOutLb); AudioStream_F32::release(blockOutLb);
AudioStream_F32::release(blockOutRb); AudioStream_F32::release(blockOutRb);
} }
private: private:
audio_block_f32_t *inputQueueArray_f32[4]; audio_block_f32_t *inputQueueArray_f32[4];

@ -222,6 +222,8 @@ void AudioFilterIRCabsim_F32::ir_load(uint8_t idx)
delay.reset(); delay.reset();
ir_loaded = 1; ir_loaded = 1;
ir_loadState = IR_LOAD_FINISHED;
AudioInterrupts(); AudioInterrupts();
#endif #endif

@ -78,6 +78,7 @@ public:
return doubleTrack; return doubleTrack;
} }
bool doubler_get() {return doubleTrack;} bool doubler_get() {return doubleTrack;}
bool init_done() {return initialized;}
private: private:
audio_block_f32_t *inputQueueArray_f32[2]; audio_block_f32_t *inputQueueArray_f32[2];
uint16_t block_size = AUDIO_BLOCK_SAMPLES; uint16_t block_size = AUDIO_BLOCK_SAMPLES;

@ -3,7 +3,10 @@
#include "control_WM8731_F32.h" #include "control_WM8731_F32.h"
#include "control_SGTL5000_F32.h" #include "control_SGTL5000_F32.h"
#include "control_ES8388_F32.h"
#include "input_i2s_ext_F32.h"
#include "output_i2s_ext_F32.h" // extended version
#include "input_i2s2_F32.h" #include "input_i2s2_F32.h"
#include "output_i2s2_F32.h" #include "output_i2s2_F32.h"

@ -0,0 +1,240 @@
/*
* input_i2s_f32.cpp
*
* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/*
* Extended by Chip Audette, OpenAudio, May 2019
* Converted to F32 and to variable audio block length
* 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
// Removed unused pieces. RSL 30 May 2022
/*
* Rewritten ny Piotr Zapart 04.2024:
* - made the 32bit mode default
* - new scale methods based on arm dsp library
* - For Teensy4.x only as Teensy3 is obsolete
*/
#include <Arduino.h> //do we really need this? (Chip: 2020-10-31)
#include "input_i2s_ext_F32.h"
#include "output_i2s_ext_F32.h"
#include "basic_DSPutils.h"
#include <arm_math.h>
// DMAMEM __attribute__((aligned(32)))
static uint64_t i2s_rx_buffer[AUDIO_BLOCK_SAMPLES]; // Two 32-bit transfers per sample.
audio_block_f32_t *AudioInputI2S_ext_F32::block_left_f32 = NULL;
audio_block_f32_t *AudioInputI2S_ext_F32::block_right_f32 = NULL;
uint16_t AudioInputI2S_ext_F32::block_offset = 0;
bool AudioInputI2S_ext_F32::update_responsibility = false;
DMAChannel AudioInputI2S_ext_F32::dma(false);
int AudioInputI2S_ext_F32::flag_out_of_memory = 0;
unsigned long AudioInputI2S_ext_F32::update_counter = 0;
float AudioInputI2S_ext_F32::sample_rate_Hz = AUDIO_SAMPLE_RATE;
int AudioInputI2S_ext_F32::audio_block_samples = AUDIO_BLOCK_SAMPLES;
#define I2S_BUFFER_TO_USE_BYTES (AudioOutputI2S_ext_F32::audio_block_samples * sizeof(i2s_rx_buffer[0]))
void AudioInputI2S_ext_F32::begin()
{
dma.begin(true); // Allocate the DMA channel first
AudioOutputI2S_ext_F32::sample_rate_Hz = sample_rate_Hz; // these were given in the AudioSettings in the contructor
AudioOutputI2S_ext_F32::audio_block_samples = audio_block_samples; // these were given in the AudioSettings in the contructor
// TODO: should we set & clear the I2S_RCSR_SR bit here?
AudioOutputI2S_ext_F32::config_i2s();
#if defined(__IMXRT1062__)
CORE_PIN8_CONFIG = 3; // 1:RX_DATA0
IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2;
dma.TCD->SADDR = (void *)((uint32_t)&I2S1_RDR0 + 0);
dma.TCD->SOFF = 0;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
dma.TCD->NBYTES_MLNO = 4;
dma.TCD->SLAST = 0;
dma.TCD->DADDR = i2s_rx_buffer;
dma.TCD->DOFF = 4;
dma.TCD->CITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 4;
dma.TCD->DLASTSGA = -I2S_BUFFER_TO_USE_BYTES;
dma.TCD->BITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 4;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_RX);
I2S1_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR;
#endif
update_responsibility = update_setup();
dma.enable();
dma.attachInterrupt(isr);
update_counter = 0;
}
void AudioInputI2S_ext_F32::isr(void)
{
uint32_t daddr, offset;
const int32_t *src, *end;
float32_t *dest_left_f32, *dest_right_f32;
audio_block_f32_t *left_f32, *right_f32;
#if defined(__IMXRT1062__)
daddr = (uint32_t)(dma.TCD->DADDR);
#endif
dma.clearInterrupt();
if (daddr < (uint32_t)i2s_rx_buffer + I2S_BUFFER_TO_USE_BYTES / 2)
{
// DMA is receiving to the first half of the buffer
// need to remove data from the second half
src = (int32_t *)&i2s_rx_buffer[audio_block_samples / 2];
end = (int32_t *)&i2s_rx_buffer[audio_block_samples];
update_counter++; // let's increment the counter here to ensure that we get every ISR resulting in audio
if (AudioInputI2S_ext_F32::update_responsibility)
AudioStream_F32::update_all();
}
else
{
// DMA is receiving to the second half of the buffer
// need to remove data from the first half
src = (int32_t *)&i2s_rx_buffer[0];
end = (int32_t *)&i2s_rx_buffer[audio_block_samples / 2];
}
left_f32 = AudioInputI2S_ext_F32::block_left_f32;
right_f32 = AudioInputI2S_ext_F32::block_right_f32;
if (left_f32 != NULL && right_f32 != NULL)
{
offset = AudioInputI2S_ext_F32::block_offset;
if (offset <= ((uint32_t)audio_block_samples / 2))
{
dest_left_f32 = &(left_f32->data[offset]);
dest_right_f32 = &(right_f32->data[offset]);
AudioInputI2S_ext_F32::block_offset = offset + audio_block_samples / 2;
do
{
*dest_left_f32++ = (float32_t)*src++;
*dest_right_f32++ = (float32_t)*src++;
} while (src < end);
}
}
}
void AudioInputI2S_ext_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
arm_scale_f32(out_f32->data, I32_TO_F32_NORM_FACTOR, 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;
// transmit the f32 data!
AudioStream_F32::transmit(out_f32, chan);
// release the memory blocks
AudioStream_F32::release(out_f32);
}
void AudioInputI2S_ext_F32::update(void)
{
static bool flag_beenSuccessfullOnce = false;
audio_block_f32_t *new_left = NULL, *new_right = NULL, *out_left = NULL, *out_right = NULL;
new_left = AudioStream_F32::allocate_f32();
new_right = AudioStream_F32::allocate_f32();
if ((!new_left) || (!new_right))
{
// ran out of memory. Clear and return!
if (new_left)
AudioStream_F32::release(new_left);
if (new_right)
AudioStream_F32::release(new_right);
new_left = NULL;
new_right = NULL;
flag_out_of_memory = 1;
if (flag_beenSuccessfullOnce)
Serial.println("Input_I2S_F32: update(): WARNING!!! Out of Memory.");
}
else
{
flag_beenSuccessfullOnce = true;
}
__disable_irq();
if (block_offset >= audio_block_samples)
{
// the DMA filled 2 blocks, so grab them and get the
// 2 new blocks to the DMA, as quickly as possible
out_left = block_left_f32;
block_left_f32 = new_left;
out_right = block_right_f32;
block_right_f32 = new_right;
block_offset = 0;
__enable_irq();
// update_counter++; //I chose to update it in the ISR instead.
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)
{
// the DMA didn't fill blocks, but we allocated blocks
if (block_left_f32 == NULL)
{
// the DMA doesn't have any blocks to fill, so
// give it the ones we just allocated
block_left_f32 = new_left;
block_right_f32 = new_right;
block_offset = 0;
__enable_irq();
}
else
{
// the DMA already has blocks, doesn't need these
__enable_irq();
AudioStream_F32::release(new_left);
AudioStream_F32::release(new_right);
}
}
else
{
// The DMA didn't fill blocks, and we could not allocate
// memory... the system is likely starving for memory!
// Sadly, there's nothing we can do.
__enable_irq();
}
}
/******************************************************************/
void AudioInputI2Sslave_ext_F32::begin(void)
{
dma.begin(true); // Allocate the DMA channel first
AudioOutputI2Sslave_ext_F32::config_i2s();
}

@ -0,0 +1,87 @@
/*
* ***** input_i2s_f32.h ******
*
* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/*
* Extended by Chip Audette, OpenAudio, May 2019
* Converted to F32 and to variable audio block length
* 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
// Removed unused pieces. RSL 30 May 2022
#ifndef _input_i2s_ext_f32_h_
#define _input_i2s_ext_f32_h_
#include <Arduino.h>
#include <arm_math.h>
#include "AudioStream_F32.h"
#include "DMAChannel.h"
class AudioInputI2S_ext_F32 : public AudioStream_F32
{
// GUI: inputs:0, outputs:2 //this line used for automatic generation of GUI nodes
public:
AudioInputI2S_ext_F32(void) : AudioStream_F32(0, NULL) { begin(); } // uses default AUDIO_SAMPLE_RATE and BLOCK_SIZE_SAMPLES from AudioStream.h
AudioInputI2S_ext_F32(const AudioSettings_F32 &settings) : AudioStream_F32(0, NULL)
{
sample_rate_Hz = settings.sample_rate_Hz;
audio_block_samples = settings.audio_block_samples;
begin();
}
virtual void update(void);
void begin(void);
int get_isOutOfMemory(void) { return flag_out_of_memory; }
void clear_isOutOfMemory(void) { flag_out_of_memory = 0; }
protected:
AudioInputI2S_ext_F32(int dummy) : AudioStream_F32(0, NULL) {} // to be used only inside AudioInputI2Sslave !!
static bool update_responsibility;
static DMAChannel dma;
static void isr(void);
virtual void update_1chan(int, audio_block_f32_t *&);
private:
static audio_block_f32_t *block_left_f32;
static audio_block_f32_t *block_right_f32;
static float sample_rate_Hz;
static int audio_block_samples;
static uint16_t block_offset;
static int flag_out_of_memory;
static unsigned long update_counter;
static bool msbFirstMode; // some codecs like the new AKM series (AK4558) use MSB exclusively
};
class AudioInputI2Sslave_ext_F32 : public AudioInputI2S_ext_F32
{
public:
AudioInputI2Sslave_ext_F32(void) : AudioInputI2S_ext_F32(0) { begin(); }
void begin(void);
friend void dma_ch1_isr(void);
};
#endif

@ -250,9 +250,7 @@ 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)
{ {
arm_scale_f32(block_f32->data, (float32_t)F32_TO_I32_NORM_FACTOR, scale_float_to_int32range(block_f32->data, block_f32_scaled->data, audio_block_samples);
block_f32_scaled->data,audio_block_samples);
__disable_irq(); __disable_irq();
if (block_right_1st == NULL) if (block_right_1st == NULL)
{ {

@ -0,0 +1,432 @@
/*
* ***** output_i2s_f32.cpp *****
*
* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/*
* Extended by Chip Audette, OpenAudio, May 2019
* Converted to F32 and to variable audio block length
* 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
// Removed old commented out code. RSL 30 May 2022
#include "output_i2s_ext_F32.h"
#include "basic_DSPutils.h"
#include <arm_math.h>
#include <Audio.h> //to get access to Audio/utlity/imxrt_hw.h...do we really need this??? WEA 2020-10-31
audio_block_f32_t *AudioOutputI2S_ext_F32::block_left_1st = NULL;
audio_block_f32_t *AudioOutputI2S_ext_F32::block_right_1st = NULL;
audio_block_f32_t *AudioOutputI2S_ext_F32::block_left_2nd = NULL;
audio_block_f32_t *AudioOutputI2S_ext_F32::block_right_2nd = NULL;
uint16_t AudioOutputI2S_ext_F32::block_left_offset = 0;
uint16_t AudioOutputI2S_ext_F32::block_right_offset = 0;
bool AudioOutputI2S_ext_F32::update_responsibility = false;
DMAChannel AudioOutputI2S_ext_F32::dma(false);
DMAMEM __attribute__((aligned(32))) static uint64_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES];
float AudioOutputI2S_ext_F32::sample_rate_Hz = AUDIO_SAMPLE_RATE;
int AudioOutputI2S_ext_F32::audio_block_samples = AUDIO_BLOCK_SAMPLES;
#if defined(__IMXRT1062__)
#include <utility/imxrt_hw.h> //from Teensy Audio library. For set_audioClock()
#endif
#define I2S_BUFFER_TO_USE_BYTES (AudioOutputI2S_ext_F32::audio_block_samples * sizeof(i2s_tx_buffer[0]))
void AudioOutputI2S_ext_F32::begin()
{
dma.begin(true); // Allocate the DMA channel first
block_left_1st = NULL;
block_right_1st = NULL;
AudioOutputI2S_ext_F32::config_i2s(sample_rate_Hz);
#if defined(__IMXRT1062__)
CORE_PIN7_CONFIG = 3; // 1:TX_DATA0
dma.TCD->SADDR = i2s_tx_buffer;
dma.TCD->SOFF = 4;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
dma.TCD->NBYTES_MLNO = 4;
dma.TCD->SLAST = -I2S_BUFFER_TO_USE_BYTES;
dma.TCD->DOFF = 0;
dma.TCD->CITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 4;
dma.TCD->DLASTSGA = 0;
dma.TCD->BITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 4;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.TCD->DADDR = (void *)((uint32_t)&I2S1_TDR0 + 0);
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_TX);
dma.enable(); // newer location of this line in Teensy Audio library
I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE;
I2S1_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
#endif
update_responsibility = update_setup();
dma.attachInterrupt(AudioOutputI2S_ext_F32::isr);
enabled = 1;
}
void AudioOutputI2S_ext_F32::isr(void)
{
#if defined(__IMXRT1062__)
int32_t *dest;
audio_block_f32_t *blockL, *blockR;
uint32_t saddr, offsetL, offsetR;
saddr = (uint32_t)(dma.TCD->SADDR);
dma.clearInterrupt();
if (saddr < (uint32_t)i2s_tx_buffer + I2S_BUFFER_TO_USE_BYTES / 2)
{ // are we transmitting the first half or second half of the buffer?
// DMA is transmitting the first half of the buffer
// so we must fill the second half
dest = (int32_t *)&i2s_tx_buffer[audio_block_samples / 2];
if (AudioOutputI2S_ext_F32::update_responsibility)
AudioStream_F32::update_all();
}
else
{
// DMA is transmitting the second half of the buffer
// so we must fill the first half
dest = (int32_t *)i2s_tx_buffer;
}
blockL = AudioOutputI2S_ext_F32::block_left_1st;
blockR = AudioOutputI2S_ext_F32::block_right_1st;
offsetL = AudioOutputI2S_ext_F32::block_left_offset;
offsetR = AudioOutputI2S_ext_F32::block_right_offset;
int32_t *d = dest;
if (blockL && blockR)
{
float32_t *pL = blockL->data + offsetL;
float32_t *pR = blockR->data + offsetR;
for (int i = 0; i < audio_block_samples / 2; i++)
{
*d++ = (int32_t)*pL++;
*d++ = (int32_t)*pR++; // interleave
}
offsetL += audio_block_samples / 2;
offsetR += audio_block_samples / 2;
}
else if (blockL)
{
float32_t *pL = blockL->data + offsetL;
for (int i = 0; i < audio_block_samples / 2 * 2; i += 2)
{
*(d + i) = (int32_t)*pL++;
} // interleave
offsetL += audio_block_samples / 2;
}
else if (blockR)
{
float32_t *pR = blockR->data + offsetR;
for (int i = 0; i < audio_block_samples / 2 * 2; i += 2)
{
*(d + i) = (int32_t)*pR++;
} // interleave
offsetR += audio_block_samples / 2;
}
else
{
memset(dest, 0, audio_block_samples * 4);
return;
}
arm_dcache_flush_delete(dest, sizeof(i2s_tx_buffer) / 2);
if (offsetL < (uint16_t)audio_block_samples)
{
AudioOutputI2S_ext_F32::block_left_offset = offsetL;
}
else
{
AudioOutputI2S_ext_F32::block_left_offset = 0;
AudioStream_F32::release(blockL);
AudioOutputI2S_ext_F32::block_left_1st = AudioOutputI2S_ext_F32::block_left_2nd;
AudioOutputI2S_ext_F32::block_left_2nd = NULL;
}
if (offsetR < (uint16_t)audio_block_samples)
{
AudioOutputI2S_ext_F32::block_right_offset = offsetR;
}
else
{
AudioOutputI2S_ext_F32::block_right_offset = 0;
AudioStream_F32::release(blockR);
AudioOutputI2S_ext_F32::block_right_1st = AudioOutputI2S_ext_F32::block_right_2nd;
AudioOutputI2S_ext_F32::block_right_2nd = NULL;
}
#endif
}
// 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.
// static int count = 0;
void AudioOutputI2S_ext_F32::update(void)
{
audio_block_f32_t *block_f32;
audio_block_f32_t *block_f32_scaled = AudioStream_F32::allocate_f32();
audio_block_f32_t *block2_f32_scaled = AudioStream_F32::allocate_f32();
if ((!block_f32_scaled) || (!block2_f32_scaled))
{
// couldn't get some working memory. Return.
if (block_f32_scaled)
AudioStream_F32::release(block_f32_scaled);
if (block2_f32_scaled)
AudioStream_F32::release(block2_f32_scaled);
return;
}
// now that we have our working memory, proceed with getting the audio data and processing
block_f32 = receiveReadOnly_f32(0); // input 0 = left channel
if (block_f32)
{
if (block_f32->length != audio_block_samples)
{
Serial.print("AudioOutputI2S_ext_F32: *** WARNING ***: audio_block says len = ");
Serial.print(block_f32->length);
Serial.print(", but I2S settings want it to be = ");
Serial.println(audio_block_samples);
}
// Optional scaling for easy volume control. Leave outputScale==1.0f for default
if (outputScale < 1.0f || outputScale > 1.0f)
arm_scale_f32(block_f32->data, outputScale, block_f32->data, block_f32->length);
scale_float_to_int32range(block_f32->data, block_f32_scaled->data, audio_block_samples);
// now process the data blocks
__disable_irq();
if (block_left_1st == NULL)
{
block_left_1st = block_f32_scaled;
block_left_offset = 0;
__enable_irq();
}
else if (block_left_2nd == NULL)
{
block_left_2nd = block_f32_scaled;
__enable_irq();
}
else
{
audio_block_f32_t *tmp = block_left_1st;
block_left_1st = block_left_2nd;
block_left_2nd = block_f32_scaled;
block_left_offset = 0;
__enable_irq();
AudioStream_F32::release(tmp);
}
AudioStream_F32::transmit(block_f32, 0);
AudioStream_F32::release(block_f32); // echo the incoming audio out the outputs
}
else
{
// this branch should never get called, but if it does, let's release the buffer that was never used
AudioStream_F32::release(block_f32_scaled);
}
block_f32_scaled = block2_f32_scaled; // this is simply renaming the pre-allocated buffer
block_f32 = receiveReadOnly_f32(1); // input 1 = right channel
if (block_f32)
{
// Optional scaling for easy volume control. Leave outputScale==1.0f for default
if (outputScale < 1.0f || outputScale > 1.0f)
arm_scale_f32(block_f32->data, outputScale, block_f32->data, block_f32->length);
// scale F32 to Int32
scale_float_to_int32range(block_f32->data, block_f32_scaled->data, audio_block_samples);
__disable_irq();
if (block_right_1st == NULL)
{
block_right_1st = block_f32_scaled;
block_right_offset = 0;
__enable_irq();
}
else if (block_right_2nd == NULL)
{
block_right_2nd = block_f32_scaled;
__enable_irq();
}
else
{
audio_block_f32_t *tmp = block_right_1st;
block_right_1st = block_right_2nd;
block_right_2nd = block_f32_scaled;
block_right_offset = 0;
__enable_irq();
AudioStream_F32::release(tmp);
}
AudioStream_F32::transmit(block_f32, 1);
AudioStream_F32::release(block_f32); // echo the incoming audio out the outputs
}
else
{
// this branch should never get called, but if it does, let's release the buffer that was never used
AudioStream_F32::release(block_f32_scaled);
}
}
void AudioOutputI2S_ext_F32::config_i2s(void) { config_i2s(AudioOutputI2S_ext_F32::sample_rate_Hz); }
void AudioOutputI2S_ext_F32::config_i2s(float fs_Hz)
{
#if defined(__IMXRT1062__)
CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON);
// if either transmitter or receiver is enabled, do nothing
if (I2S1_TCSR & I2S_TCSR_TE)
return;
if (I2S1_RCSR & I2S_RCSR_RE)
return;
// PLL:
int fs = fs_Hz;
// PLL between 27*24 = 648MHz und 54*24=1296MHz
int n1 = 4; // SAI prescaler 4 => (n1*n2) = multiple of 4
int n2 = 1 + (24000000 * 27) / (fs * 256 * n1);
double C = ((double)fs * 256 * n1 * n2) / 24000000;
int c0 = C;
int c2 = 10000;
int c1 = C * c2 - (c0 * c2);
set_audioClock(c0, c1, c2);
// clear SAI1_CLK register locations
CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI1_CLK_SEL_MASK)) | CCM_CSCMR1_SAI1_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4
CCM_CS1CDR = (CCM_CS1CDR & ~(CCM_CS1CDR_SAI1_CLK_PRED_MASK | CCM_CS1CDR_SAI1_CLK_PODF_MASK)) | CCM_CS1CDR_SAI1_CLK_PRED(n1 - 1) // &0x07
| CCM_CS1CDR_SAI1_CLK_PODF(n2 - 1); // &0x3f
// Select MCLK
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL_MASK)) | (IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0));
CORE_PIN23_CONFIG = 3; // 1:MCLK
CORE_PIN21_CONFIG = 3; // 1:RX_BCLK
CORE_PIN20_CONFIG = 3; // 1:RX_SYNC
int rsync = 0;
int tsync = 1;
I2S1_TMR = 0;
I2S1_TCR1 = I2S_TCR1_RFW(1);
I2S1_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP // sync=0; tx is async;
| (I2S_TCR2_BCD | I2S_TCR2_DIV((1)) | I2S_TCR2_MSEL(1));
I2S1_TCR3 = I2S_TCR3_TCE;
I2S1_TCR4 = I2S_TCR4_FRSZ((2 - 1)) | I2S_TCR4_SYWD((32 - 1)) | I2S_TCR4_MF | I2S_TCR4_FSD | I2S_TCR4_FSE | I2S_TCR4_FSP;
I2S1_TCR5 = I2S_TCR5_WNW((32 - 1)) | I2S_TCR5_W0W((32 - 1)) | I2S_TCR5_FBT((32 - 1));
I2S1_RMR = 0;
I2S1_RCR1 = I2S_RCR1_RFW(1);
I2S1_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_RCR2_BCP // sync=0; rx is async;
| (I2S_RCR2_BCD | I2S_RCR2_DIV((1)) | I2S_RCR2_MSEL(1));
I2S1_RCR3 = I2S_RCR3_RCE;
I2S1_RCR4 = I2S_RCR4_FRSZ((2 - 1)) | I2S_RCR4_SYWD((32 - 1)) | I2S_RCR4_MF | I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD;
I2S1_RCR5 = I2S_RCR5_WNW((32 - 1)) | I2S_RCR5_W0W((32 - 1)) | I2S_RCR5_FBT((32 - 1));
#endif
}
/******************************************************************/
// From Chip: The I2SSlave functionality has NOT been extended to
// allow for different block sizes or sample rates (2020-10-31)
void AudioOutputI2Sslave_ext_F32::begin(void)
{
dma.begin(true); // Allocate the DMA channel first
// pinMode(2, OUTPUT);
block_left_1st = NULL;
block_right_1st = NULL;
AudioOutputI2Sslave_ext_F32::config_i2s();
#if defined(__IMXRT1062__)
CORE_PIN7_CONFIG = 3; // 1:TX_DATA0
dma.TCD->SADDR = i2s_tx_buffer;
dma.TCD->SOFF = 2;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
dma.TCD->NBYTES_MLNO = 2;
dma.TCD->SLAST = -sizeof(i2s_tx_buffer);
// dma.TCD->DADDR = (void *)((uint32_t)&I2S1_TDR1 + 2);
dma.TCD->DOFF = 0;
dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
dma.TCD->DLASTSGA = 0;
dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
// dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_TX);
dma.TCD->DADDR = (void *)((uint32_t)&I2S1_TDR0 + 2);
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_TX);
dma.enable();
I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE;
I2S1_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
#endif
update_responsibility = update_setup();
// dma.enable();
dma.attachInterrupt(AudioOutputI2S_ext_F32::isr);
}
void AudioOutputI2Sslave_ext_F32::config_i2s(void)
{
#if defined(__IMXRT1062__)
CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON);
// if either transmitter or receiver is enabled, do nothing
if (I2S1_TCSR & I2S_TCSR_TE)
return;
if (I2S1_RCSR & I2S_RCSR_RE)
return;
// not using MCLK in slave mode - hope that's ok?
// CORE_PIN23_CONFIG = 3; // AD_B1_09 ALT3=SAI1_MCLK
CORE_PIN21_CONFIG = 3; // AD_B1_11 ALT3=SAI1_RX_BCLK
CORE_PIN20_CONFIG = 3; // AD_B1_10 ALT3=SAI1_RX_SYNC
IOMUXC_SAI1_RX_BCLK_SELECT_INPUT = 1; // 1=GPIO_AD_B1_11_ALT3, page 868
IOMUXC_SAI1_RX_SYNC_SELECT_INPUT = 1; // 1=GPIO_AD_B1_10_ALT3, page 872
// configure transmitter
I2S1_TMR = 0;
I2S1_TCR1 = I2S_TCR1_RFW(1); // watermark at half fifo size
I2S1_TCR2 = I2S_TCR2_SYNC(1) | I2S_TCR2_BCP;
I2S1_TCR3 = I2S_TCR3_TCE;
I2S1_TCR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(31) | I2S_TCR4_MF | I2S_TCR4_FSE | I2S_TCR4_FSP | I2S_RCR4_FSD;
I2S1_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);
// configure receiver
I2S1_RMR = 0;
I2S1_RCR1 = I2S_RCR1_RFW(1);
I2S1_RCR2 = I2S_RCR2_SYNC(0) | I2S_TCR2_BCP;
I2S1_RCR3 = I2S_RCR3_RCE;
I2S1_RCR4 = I2S_RCR4_FRSZ(1) | I2S_RCR4_SYWD(31) | I2S_RCR4_MF | I2S_RCR4_FSE | I2S_RCR4_FSP;
I2S1_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31);
#endif
}

@ -0,0 +1,99 @@
/*
* ***** output_i2s_f32.h *****
*
* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/*
* Extended by Chip Audette, OpenAudio, May 2019
* Converted to F32 and to variable audio block length
* 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
// Removed old commented out code. RSL 30 May 2022
#ifndef output_i2s_ext_f32_h_
#define output_i2s_ext_f32_h_
#include <Arduino.h>
#include <arm_math.h>
#include "AudioStream_F32.h"
#include "DMAChannel.h"
class AudioOutputI2S_ext_F32 : public AudioStream_F32
{
//GUI: inputs:2, outputs:0 //this line used for automatic generation of GUI node
public:
//uses default AUDIO_SAMPLE_RATE and BLOCK_SIZE_SAMPLES from AudioStream.h:
AudioOutputI2S_ext_F32(void) : AudioStream_F32(2, inputQueueArray) { begin();}
// Allow variable sample rate and block size:
AudioOutputI2S_ext_F32(const AudioSettings_F32 &settings) : AudioStream_F32(2, inputQueueArray)
{
sample_rate_Hz = settings.sample_rate_Hz;
audio_block_samples = settings.audio_block_samples;
begin();
}
// outputScale is a gain control for both left and right. If set exactly
// to 1.0f it is left as a pass-through.
void setGain(float _oscale) {outputScale = _oscale; }
virtual void update(void);
void begin(void);
friend class AudioInputI2S_ext_F32;
#if defined(__IMXRT1062__)
friend class AudioOutputI2SQuad_F32;
friend class AudioInputI2SQuad_F32;
#endif
protected:
AudioOutputI2S_ext_F32(int dummy): AudioStream_F32(2, inputQueueArray) {} // to be used only inside AudioOutputI2Sslave !!
static void config_i2s(void);
static void config_i2s(float);
static audio_block_f32_t *block_left_1st;
static audio_block_f32_t *block_right_1st;
static bool update_responsibility;
static DMAChannel dma;
static void isr(void);
private:
static audio_block_f32_t *block_left_2nd;
static audio_block_f32_t *block_right_2nd;
static uint16_t block_left_offset;
static uint16_t block_right_offset;
audio_block_f32_t *inputQueueArray[2];
static float sample_rate_Hz;
static int audio_block_samples;
volatile uint8_t enabled = 1;
float outputScale = 1.0f; // Quick volume control
};
class AudioOutputI2Sslave_ext_F32 : public AudioOutputI2S_ext_F32
{
public:
AudioOutputI2Sslave_ext_F32(void) : AudioOutputI2S_ext_F32(0) { begin(); } ;
void begin(void);
friend class AudioInputI2Sslave_ext_F32;
friend void dma_ch0_isr(void);
protected:
static void config_i2s(void);
};
#endif
Loading…
Cancel
Save