diff --git a/README.md b/README.md index d678c83..92b0297 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,12 @@ Versatile stereo ping-pong delay with modulation. **AudioEffectNoiseGateStereo_F32** Stereo noise gate with external SideChain input. +**AudioEffectGuitarBooster_F32** +Overdrive emulation using oversampled wave shaper, switchable octave up. + +**AudioEffectWahMono_F32** +WAH pedal emulation including 8 models and versatile range handling. + **AudioFilterToneStackStereo_F32** Stereo guitar tone stack (EQ) emulator. @@ -43,12 +49,20 @@ Simple 3 band (Treble, Mid, Bass) equalizer. **AudioEffectGainStereo_F32** Stereo gain control (volume etc.) +**AudioSwitchSelectorStereo** +Stereo/mono signal selector. + ## I/O **AudioInputI2S2_F32** **AudioOutputI2S2_F32** Input and output for the I2S2 interface, Teensy 4.1 only. +**AudioInputI2S_ext_F32** +**AudioOutputI2S_ext_F32** +Custom input and output for the I2S interface, including a few extra options (ie. channel swap) + + ## Basic Single header basic building blocks for various DSP components: - allpass filter @@ -60,8 +74,9 @@ Single header basic building blocks for various DSP components: ## Example projects https://github.com/hexeguitar/hexefx_audiolib_F32_examples +https://github.com/hexeguitar/tgx4 --- -Copyright 01.2024 by Piotr Zapart +Copyright 07.2024 by Piotr Zapart www.hexefx.com diff --git a/library.properties b/library.properties index 7bccef3..a7398bf 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=hexefx_audiolib_F32 -version=1.0.0 +version=1.1.0 author=Piotr Zapart maintainer=Piotr Zapart sentence=Audio effect extension for the OpenAudio_ArduinoLibrary diff --git a/src/basic_shelvFilter.h b/src/basic_shelvFilter.h index 5b75e89..59f9a01 100644 --- a/src/basic_shelvFilter.h +++ b/src/basic_shelvFilter.h @@ -29,6 +29,7 @@ public: } inline float process(float input) { + if (bp) return input; float tmp1, tmp2; // smoothly update params if (hidamp < (*hidampPtr)) @@ -64,6 +65,7 @@ public: lpreg = 0.0f; hpreg = 0.0f; } + void bypass_set(bool state) { bp = state; reset();} private: float lpreg; float hpreg; @@ -74,6 +76,7 @@ private: float hp_f; float lp_f; static constexpr float upd_step = 0.02f; + bool bp = false; }; diff --git a/src/control_AK4452_F32.cpp b/src/control_AK4452_F32.cpp new file mode 100644 index 0000000..9394e3b --- /dev/null +++ b/src/control_AK4452_F32.cpp @@ -0,0 +1,204 @@ +/** + * @file control_AK4452_F32.cpp + * @author Piotr Zapart + * @brief driver for the AK4452 DAC + * @version 0.1 + * @date 2024-06-14 + * + * @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 ." + */ +#include "control_AK4452_F32.h" + +#define AK4452_REG_CTRL1 (0x00) +#define AK4452_REG_CTRL1_DFLT (0x0C) + #define AK4452_BIT_RSTN (1<<0) + #define AK4452_DIF_MASK (0x0E) + #define AK4452_DIF_SHIFT (0x01) + #define AK4452_DIF(x) (((x)<begin(); + ctrlBus->setClock(400000); + + if (!writeReg(AK4452_REG_CTRL1, 0x00)) // put the registers in reset mode + { + return false; // codec not found + } + // DIF[2:0] = 0b111 - 32bit I2S, Normal mode + // DFS[2:0] = 0b000 (default) Normal speed mode + // DSDSEL[1:0] = 0b10 256fs + writeReg(AK4452_REG_DSD2, AK4452_BIT_DSDSEL1); + writeReg(AK4452_REG_CTRL1, AK4452_DIF(AK4452_DIF_32B_I2S) | AK4452_BIT_RSTN); + + configured = true; + return true; +} + +bool AudioControlAK4452_F32::writeReg(uint8_t addr, uint8_t val) +{ + ctrlBus->beginTransmission(i2cAddr); + ctrlBus->write(addr); + ctrlBus->write(val); + return ctrlBus->endTransmission() == 0; +} +bool AudioControlAK4452_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 AudioControlAK4452_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; +} diff --git a/src/control_AK4452_F32.h b/src/control_AK4452_F32.h new file mode 100644 index 0000000..683cc10 --- /dev/null +++ b/src/control_AK4452_F32.h @@ -0,0 +1,56 @@ +/** + * @file control_AK4452_F32.h + * @author Piotr Zapart + * @brief driver for the AK4452 DAC + * @version 0.1 + * @date 2024-06-14 + * + * @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 ." + */ +#ifndef _CONTROL_AK4452_H_ +#define _CONTROL_AK4452_H_ +#include +#include +#include "AudioControl.h" + +#define AK4452_ADDR00 (0x10) +#define AK4452_ADDR01 (0x11) +#define AK4452_ADDR10 (0x12) +#define AK4452_ADDR11 (0x13) + +class AudioControlAK4452_F32 : public AudioControl +{ +public: + AudioControlAK4452_F32(){}; + ~AudioControlAK4452_F32(){}; + bool enable() + { + return enable(&Wire, AK4452_ADDR10); + } + bool enable(TwoWire *i2cBus, uint8_t addr); + + // not used but required by AudioControl + bool disable() {return true;} + bool volume(float volume) {return true;}; + bool inputLevel(float volume) {return true;} + bool inputSelect(int n) {return true;} + +private: + static bool configured; + TwoWire *ctrlBus; + uint8_t i2cAddr; + + 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); +}; + +#endif // _CONTROL_AK4452_H_ diff --git a/src/control_AK5552_F32.cpp b/src/control_AK5552_F32.cpp new file mode 100644 index 0000000..2fed00b --- /dev/null +++ b/src/control_AK5552_F32.cpp @@ -0,0 +1,114 @@ +/** + * @file control_AK5552_F32.cpp + * @author Piotr Zapart + * @brief driver for the AK5552 ADC + * @version 0.1 + * @date 2024-06-14 + * + * @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 ." + */ +#include "control_AK5552_F32.h" + +#define AK5552_REG_PWR_MAN1 (0x00) +#define AK5552_REG_PWR_MAN1_DFLT (0xFF) + #define AK5552_BIT_PW1 (1<<0) + #define AK5552_BIT_PW2 (1<<1) + +#define AK5552_REG_PWR_MAN2 (0x01) +#define AK5552_REG_PWR_MAN2_DFLT (0x01) + #define AK5552_BIT_RSTN (1<<0) + #define AK5552_BIT_MONO1 (1<<1) + #define AK5552_BIT_MONO2 (1<<2) + +#define AK5552_REG_PWR_CTRL1 (0x02) +#define AK5552_REG_PWR_CTRL1_DFLT (0x01) + #define AK5552_BIT_HPFE (1<<0) + #define AK5552_BIT_DIF0 (1<<1) + #define AK5552_BIT_DIF1 (1<<2) + #define AK5552_BIT_CKS0 (1<<3) + #define AK5552_BIT_CKS1 (1<<4) + #define AK5552_BIT_CKS2 (1<<5) + #define AK5552_BIT_CKS3 (1<<6) + +#define AK5552_REG_PWR_CTRL2 (0x03) +#define AK5552_REG_PWR_CTRL2_DFLT (0x00) + #define AK5552_BIT_TDM0 (1<<5) + #define AK5552_BIT_TDM1 (1<<6) + +#define AK5552_REG_PWR_CTRL3 (0x04) +#define AK5552_REG_PWR_CTRL3_DFLT (0x00) + #define AK5552_BIT_SLOW (1<<0) + #define AK5552_BIT_SD (1<<1) + +#define AK5552_REG_PWR_DSD (0x04) +#define AK5552_REG_PWR_DSD_DFLT (0x00) + #define AK5552_DSDSEL_64FS (0x00) + #define AK5552_DSDSEL_128FS (0x01) + #define AK5552_DSDSEL_256FS (0x10) + #define AK5552_DSDSEL(x) ((x)&0x03) + #define AK5552_BIT_DCKB (1<<2) + #define AK5552_BIT_PMOD (1<<3) + #define AK5552_BIT_DCKS (1<<5) + +bool AudioControlAK5552_F32::configured = false; + + +bool AudioControlAK5552_F32::enable(TwoWire *i2cBus, uint8_t addr) +{ + ctrlBus = i2cBus; + i2cAddr = addr; + ctrlBus->begin(); + ctrlBus->setClock(400000); + + if (!writeReg(AK5552_REG_PWR_MAN2, 0x00)) // put the registers in reset mode + { + return false; // codec not found + } + // Normal Speed 256fs, table 5, page 34 + // CKS3=0, CKS2=0, CKS1=1, CKS0=0 + // 32bit I2S, Slave mode, table 8, page 62 + // TDM1=0, TDM0=0, MSN=0, DIF1=1, DIF0=1 + writeReg( AK5552_REG_PWR_CTRL1, AK5552_BIT_CKS1 | + AK5552_BIT_DIF1 | + AK5552_BIT_DIF0); + // enable short delay + writeReg(AK5552_REG_PWR_CTRL3, AK5552_BIT_SD); + writeReg(AK5552_REG_PWR_MAN2, AK5552_BIT_RSTN); // register in normal operation + configured = true; + return true; +} + +bool AudioControlAK5552_F32::writeReg(uint8_t addr, uint8_t val) +{ + ctrlBus->beginTransmission(i2cAddr); + ctrlBus->write(addr); + ctrlBus->write(val); + return ctrlBus->endTransmission() == 0; +} +bool AudioControlAK5552_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 AudioControlAK5552_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; +} diff --git a/src/control_AK5552_F32.h b/src/control_AK5552_F32.h new file mode 100644 index 0000000..b94db99 --- /dev/null +++ b/src/control_AK5552_F32.h @@ -0,0 +1,58 @@ +/** + * @file control_AK5552_F32.h + * @author Piotr Zapart + * @brief driver for the AK5552 ADC + * @version 0.1 + * @date 2024-06-14 + * + * @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 ." + */ +#ifndef _CONTROL_AK5552_F32_H_ +#define _CONTROL_AK5552_F32_H_ + +#include +#include +#include "AudioControl.h" + +#define AK5552_ADDR00 (0x10) +#define AK5552_ADDR01 (0x11) +#define AK5552_ADDR10 (0x12) +#define AK5552_ADDR11 (0x13) + +class AudioControlAK5552_F32 : public AudioControl +{ +public: + AudioControlAK5552_F32(){}; + ~AudioControlAK5552_F32(){}; + bool enable() + { + return enable(&Wire, AK5552_ADDR00); + } + bool enable(TwoWire *i2cBus, uint8_t addr); + + // not used but required by AudioControl + bool disable() {return true;} + bool volume(float volume) {return true;}; + bool inputLevel(float volume) {return true;} + bool inputSelect(int n) {return true;} + +private: + static bool configured; + TwoWire *ctrlBus; + uint8_t i2cAddr; + + 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); +}; + + +#endif // _CONTROL_AK5552_H_ diff --git a/src/control_ES8388_F32.cpp b/src/control_ES8388_F32.cpp index d219764..9db1077 100644 --- a/src/control_ES8388_F32.cpp +++ b/src/control_ES8388_F32.cpp @@ -1,7 +1,5 @@ #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 @@ -475,19 +473,6 @@ bool AudioControlES8388_F32::enable(TwoWire *i2cBus, uint8_t addr, config_t cfg) // 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 - disabled for now, will be tested someday.. - // 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; @@ -555,7 +540,7 @@ void AudioControlES8388_F32::set_noiseGate(float thres) 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; @@ -572,16 +557,15 @@ bool AudioControlES8388_F32::analogBypass(bool bypass) 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 + res &= writeReg(ES8388_REG_DAC_CTRL17, ES8388_BIT_LI2LO | // Lin 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 + res &= writeReg(ES8388_REG_DAC_CTRL20, ES8388_BIT_RI2RO | // Rin on ES8388_BIT_RD2RO | // R Dac on ES8388_RI2ROVOL(ES8388_VOL_0DB)); // Rin gain 0dB } diff --git a/src/control_ES8388_F32.h b/src/control_ES8388_F32.h index 9814aaf..f0d9c6d 100644 --- a/src/control_ES8388_F32.h +++ b/src/control_ES8388_F32.h @@ -1,3 +1,20 @@ +/** + * @file control_ES8388_F32.h + * @author your name (you@domain.com) + * @brief driver for the ES8388 codec chip + * @version 0.1 + * @date 2024-06-14 + * + * @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 ." + */ #ifndef _CONTROL_ES8388_F32_H_ #define _CONTROL_ES8388_F32_H_ @@ -8,7 +25,7 @@ #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 +class AudioControlES8388_F32 : public AudioControl { public: AudioControlES8388_F32(void){}; @@ -27,8 +44,8 @@ public: 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 - + bool inputLevel(float n) {return true;} // range: 0.0f to 1.0f + bool inputSelect(int n) {return true;} void set_noiseGate(float thres); diff --git a/src/control_WM8731_F32.cpp b/src/control_WM8731_F32.cpp index e95ebd9..3743039 100644 --- a/src/control_WM8731_F32.cpp +++ b/src/control_WM8731_F32.cpp @@ -236,13 +236,12 @@ bool AudioControlWM8731_F32::enable(bit_depth_t bits, TwoWire *i2cBus, uint8_t a void AudioControlWM8731_F32::dac_mute(bool m) { 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; } // ---------------------------------------------------------------------------------- void AudioControlWM8731_F32::hp_filter(bool state) { - modify(WM8731_REG_DIGITAL, WM8731_BITS_ADCHPD(state), WM8731_BITS_ADCHPD_MASK); + modify(WM8731_REG_DIGITAL, WM8731_BITS_ADCHPD(state^1), WM8731_BITS_ADCHPD_MASK); //write(WM8731_REG_DIGITAL, WM8731_BITS_DACMU(DACmute) | WM8731_BITS_ADCHPD(state)); } // ---------------------------------------------------------------------------------- diff --git a/src/effect_delaystereo_F32.cpp b/src/effect_delaystereo_F32.cpp index a13413a..8517b71 100644 --- a/src/effect_delaystereo_F32.cpp +++ b/src/effect_delaystereo_F32.cpp @@ -232,9 +232,13 @@ void AudioEffectDelayStereo_F32::freeze(bool state) { feedb_tmp = feedb; // store the settings inputGain_tmp = inputGainSet; - bassCut_k_tmp = bassCut_k; - trebleCut_k_tmp = trebleCut_k; + // bassCut_k_tmp = bassCut_k; + // trebleCut_k_tmp = trebleCut_k; __disable_irq(); + flt0R.bypass_set(true); + flt1R.bypass_set(true); + flt0L.bypass_set(true); + flt1L.bypass_set(true); feedb = 1.0f; // infinite echo inputGainSet = freeze_ingain; __enable_irq(); @@ -244,8 +248,12 @@ void AudioEffectDelayStereo_F32::freeze(bool state) __disable_irq(); feedb = feedb_tmp; inputGainSet = inputGain_tmp; - bassCut_k = bassCut_k_tmp; - trebleCut_k = trebleCut_k_tmp; + flt0R.bypass_set(false); + flt1R.bypass_set(false); + flt0L.bypass_set(false); + flt1L.bypass_set(false); + // bassCut_k = bassCut_k_tmp; + // trebleCut_k = trebleCut_k_tmp; __enable_irq(); } } diff --git a/src/effect_gainStereo_F32.h b/src/effect_gainStereo_F32.h index 426db12..99329a4 100644 --- a/src/effect_gainStereo_F32.h +++ b/src/effect_gainStereo_F32.h @@ -76,12 +76,9 @@ public: void setPan(float32_t p) { - float32_t tmp, gL, gR; - pan = constrain(p, -1.0f, 1.0f); - tmp = (pan + 1.0f) * 0.5f; // map to 0..1 - - mix_pwr(tmp, &panR, &panL); - + float32_t gL, gR; + pan = constrain(p, 0.0f, 1.0f); + mix_pwr(pan, &panR, &panL); gL = panL * gain; gR = panR * gain; @@ -89,7 +86,6 @@ public: gainLset = gL; gainRset = gR; __enable_irq(); - } float32_t getPan() { return pan;} void phase_inv(bool inv) diff --git a/src/effect_guitarBooster_F32.h b/src/effect_guitarBooster_F32.h index 6a1ac74..58319ca 100644 --- a/src/effect_guitarBooster_F32.h +++ b/src/effect_guitarBooster_F32.h @@ -1,6 +1,3 @@ -#ifndef _EFFECT_GUITARBOOSTER_F32_H_ -#define _EFFECT_GUITARBOOSTER_F32_H_ - /** * @file effect_guitarBooster_F32.h * @author Piotr Zapart @@ -20,6 +17,8 @@ * You should have received a copy of the GNU General Public License along with this program. * If not, see ." */ +#ifndef _EFFECT_GUITARBOOSTER_F32_H_ +#define _EFFECT_GUITARBOOSTER_F32_H_ #include #include "basic_DSPutils.h" @@ -78,7 +77,8 @@ public: { value = fabs(value); value = constrain(value, 0.0f, 1.0f); - value = 1.0f + value * upsample_k * gainRange; + // start with 0.5 - L+R are summed giving x2 gain + value = 0.5f + value * upsample_k * gainRange; __disable_irq() gainSet = value; __enable_irq(); @@ -168,7 +168,7 @@ private: float32_t dryGain = 0.0f; float32_t wetGain = 1.0f; float32_t DCbias = 0.175f; - float32_t gainRange = 4.0f; + float32_t gainRange = 4.5f; 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_hp = 1.0f; diff --git a/src/effect_noiseGateStereo_F32.h b/src/effect_noiseGateStereo_F32.h index 6de32ef..2adea54 100644 --- a/src/effect_noiseGateStereo_F32.h +++ b/src/effect_noiseGateStereo_F32.h @@ -86,7 +86,7 @@ public: } //sum L + R arm_add_f32(p_sideChain_inL, p_sideChain_inR, blockSideCh->data, blockSideCh->length); - arm_scale_f32(blockSideCh->data, 0.5f, blockSideCh->data, blockSideCh->length); // divide by 2 + arm_scale_f32(blockSideCh->data, sideChain_gain * 0.5f, blockSideCh->data, blockSideCh->length); // divide by 2 calcGain(blockSideCh, blockGain); calcSmoothedGain(blockGain); @@ -153,6 +153,10 @@ public: time = map_sat(time, 0.0f, 1.0f, NOISEGATE_HOLDT_MIN, NOISEGATE_HOLDT_MAX); setHoldTime(time); } + void setSideChainGain(float g) + { + sideChain_gain = g; + } bool infoIsOpen() { @@ -176,6 +180,7 @@ private: float32_t fs = AUDIO_SAMPLE_RATE_EXACT; float32_t* p_sideChain_inL = NULL; float32_t* p_sideChain_inR = NULL; + float32_t sideChain_gain = 1.0f;; float32_t linearThreshold; float32_t prev_gain_dB = 0; float32_t openingTimeConst, closingTimeConst; diff --git a/src/effect_platereverb_F32.cpp b/src/effect_platereverb_F32.cpp index 659d2a3..68a8df9 100644 --- a/src/effect_platereverb_F32.cpp +++ b/src/effect_platereverb_F32.cpp @@ -55,6 +55,8 @@ bool AudioEffectPlateReverb_F32::begin() loop_allp_k = LOOP_ALLOP_COEFF; rv_time_scaler = 1.0f; rv_time_k = 0.2f; + pitch_semit = 0; + pitchShim_semit = 0; if(!in_allp_1L.init(&in_allp_k)) return false; if(!in_allp_2L.init(&in_allp_k)) return false; diff --git a/src/effect_platereverb_F32.h b/src/effect_platereverb_F32.h index 311eef0..91edf28 100644 --- a/src/effect_platereverb_F32.h +++ b/src/effect_platereverb_F32.h @@ -292,7 +292,9 @@ public: void chorus(float c) { c = map(c, 0.0f, 1.0f, 1.0f, 100.0f); + __disable_irq(); LFO_AMPLset = (uint32_t)c; + __enable_irq(); } /** @@ -304,10 +306,12 @@ public: { if (flags.freeze) return; // do not update the shimmer if in freeze mode s = constrain(s, 0.0f, 1.0f); - s = 2*s - s*s; + s = 2*s - s*s; + __disable_irq(); pitchShimL.setMix(s); pitchShimR.setMix(s); shimmerRatio = s; + __enable_irq(); } /** * @brief Sets the pitch of the shimmer effect @@ -316,8 +320,10 @@ public: */ void shimmerPitch(float ratio) { + __disable_irq(); pitchShimL.setPitch(ratio); pitchShimR.setPitch(ratio); + __enable_irq(); } /** * @brief Sets the shimmer effect pitch in semitones @@ -326,9 +332,28 @@ public: */ void shimmerPitchSemitones(int8_t semitones) { + __disable_irq(); pitchShimL.setPitchSemintone(semitones); pitchShimR.setPitchSemintone(semitones); + __enable_irq(); } + /** + * @brief shimemr pitch set using built in semitzone table + * + * + * @param value float 0.0f to 1.0f + */ + void shimmerPitchNormalized(float32_t value) + { + value = constrain(value, 0.0f, 1.0f); + float32_t idx = map(value, 0.0f, 1.0f, 0.0f, (float32_t)sizeof(semitoneTable)+0.499f); + pitchShim_semit = semitoneTable[(uint8_t)idx]; + __disable_irq(); + pitchShimL.setPitchSemintone(pitchShim_semit); + pitchShimR.setPitchSemintone(pitchShim_semit); + __enable_irq(); + } + int8_t shimmerPitch_get() {return pitchShim_semit;} /** * @brief set the reverb pitch. Range -12 to +24 * @@ -336,9 +361,28 @@ public: */ void pitchSemitones(int8_t semitones) { + __disable_irq(); pitchL.setPitchSemintone(semitones); pitchR.setPitchSemintone(semitones); + __enable_irq(); } + /** + * @brief sets the reverb pitch using the built in table + * input range is float 0.0 to 1.0 + * + * @param value + */ + void pitchNormalized(float32_t value) + { + value = constrain(value, 0.0f, 1.0f); + float32_t idx = map(value, 0.0f, 1.0f, 0.0f, (float32_t)sizeof(semitoneTable)+0.499f); + pitch_semit = semitoneTable[(uint8_t)idx]; + __disable_irq(); + pitchL.setPitchSemintone(pitch_semit); + pitchR.setPitchSemintone(pitch_semit); + __enable_irq(); + } + int8_t pitch_get() {return pitch_semit;} /** * @brief Reverb pitch shifter dry/wet mixer * @@ -347,9 +391,11 @@ public: void pitchMix(float s) { s = constrain(s, 0.0f, 1.0f); + __disable_irq(); pitchL.setMix(s); pitchR.setMix(s); pitchRatio = s; + __enable_irq(); } private: @@ -451,6 +497,9 @@ private: AudioBasicPitch pitchShimL; AudioBasicPitch pitchShimR; + const int8_t semitoneTable[9] = {-12, -7, -5, -3, 0, 3, 5, 7, 12}; + int8_t pitch_semit; + int8_t pitchShim_semit; const float rv_time_k_max = 0.97f; float rv_time_k, rv_time_k_tmp; // reverb time coeff float rv_time_scaler; // with high lodamp settings lower the max reverb time to avoid clipping diff --git a/src/effect_wahMono_F32.cpp b/src/effect_wahMono_F32.cpp new file mode 100644 index 0000000..baf7cdc --- /dev/null +++ b/src/effect_wahMono_F32.cpp @@ -0,0 +1,254 @@ +/** + * @file effect_wahMono_F32.cpp + * @author Piotr Zapart + * @brief Mono WAH effect + * @version 0.1 + * @date 2024-07-09 + * + * @copyright Copyright (c) 2024 www.hexefx.com + * + * Implementation is based on the work of Transmogrifox + * https://cackleberrypines.net/transmogrifox/src/bela/inductor_wah_C_src/ + * + * 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 ." + */ + + +#include "effect_wahMono_F32.h" + +#define k *1e3 +#define nF *1e-9 +const AudioEffectWahMono_F32::wah_componentValues_t AudioEffectWahMono_F32::compValues[WAH_MODEL_LAST] = +{// Lp Cf Ci Rpot Ri Rs Rp, Rc Rbias Re beta name + {0.50f, 10.0f nF, 10.0f nF, 100.0f k, 68.0f k, 1.5f k, 33.0f k, 22.0f k, 470.0f k, 390.0f, 250.0f}, // G1 + {0.50f, 10.0f nF, 10.0f nF, 100.0f k, 68.0f k, 1.5f k, 33.0f k, 22.0f k, 470.0f k, 510.0f, 650.0f}, // G2 + {0.66f, 10.0f nF, 10.0f nF, 100.0f k, 68.0f k, 1.5f k, 33.0f k, 22.0f k, 470.0f k, 390.0f, 250.0f}, // G3 + {0.50f, 10.0f nF, 10.0f nF, 100.0f k, 68.0f k, 1.5f k, 100.0f k,22.0f k, 470.0f k, 470.0f, 250.0f}, // G4 + {0.50f, 15.0f nF, 8.0f nF, 100.0f k, 220.0f k, 0.1f k, 47.0f k, 22.0f k, 470.0f k, 510.0f, 250.0f}, // VOCAL + {0.50f, 10.0f nF, 47.0f nF, 100.0f k, 68.0f k, 1.5f k,150.0f k, 22.0f k, 470.0f k, 150.0f, 250.0f}, // EXTREME + {0.66f, 10.0f nF, 10.0f nF, 100.0f k, 68.0f k, 1.5f k,33.0f k, 22.0f k, 470.0f k, 470.0f, 250.0f}, // CUSTOM + {0.50f, 100.0f nF, 10.0f nF, 100.0f k, 100.0f k, 1.5f k,100.0f k, 22.0f k, 470.0f k, 470.0f, 200.0f} // BASS +}; +#undef k +#undef nF + +void AudioEffectWahMono_F32::update() +{ + audio_block_f32_t *blockL, *blockR, *blockMod; + float32_t a0, a1, a2, ax, drySig; + uint16_t i; + if (bp) // handle bypass + { + blockL = AudioStream_F32::receiveReadOnly_f32(0); + blockR = AudioStream_F32::receiveReadOnly_f32(1); + if (!blockL || !blockR) + { + if (blockL) + AudioStream_F32::release(blockL); + if (blockR) + AudioStream_F32::release(blockR); + return; + } + AudioStream_F32::transmit(blockL, 0); + AudioStream_F32::transmit(blockR, 1); + AudioStream_F32::release(blockL); + AudioStream_F32::release(blockR); + return; + } + blockL = AudioStream_F32::receiveWritable_f32(0); + blockR = AudioStream_F32::receiveWritable_f32(1); + if (!blockL || !blockR) + { + if (blockL) + AudioStream_F32::release(blockL); + if (blockR) + AudioStream_F32::release(blockR); + return; + } + blockMod = AudioStream_F32::receiveReadOnly_f32(2); + + arm_add_f32(blockL->data, blockR->data, blockL->data, blockL->length); // add two channels + arm_scale_f32(blockL->data, input_gain, blockL->data, blockL->length); + for (i=0; ilength; i++) + { + //variable gp is the pot gain, nominal range 0.0 to 1.0 + //although this can be abused for extended range. + //A value less than zero would make the filter go unstable + //and put a lot of NaNs in your audio output + if (blockMod) + { + // TODO: scale the range to 0.0-1.0 + gp = blockMod->data[i]; + } + if(gp < 0.0f) gp = 0.0f; + float32_t gp_scaled = map_sat(gp, 0.0f, 1.0f, gp_top, gp_btm); + + //The magic numbers below approximate frequency warping characteristic + float32_t gw = 4.6f-18.4f/(4.0f + gp_scaled); + + //Update Biquad coefficients + a0 = a0b + gw*a0c; + a1 = -(a1b + gw*a1c); + a2 = -(a2b + gw*a2c); + ax = 1.0f/a0; + + drySig = blockL->data[i]; + + //run it through the 1-pole HPF and gain first + float32_t hpf = ghpf * (drySig - xh1) - a1p*yh1; + xh1 = drySig; + yh1 = hpf; + + //Apply modulated biquad + float32_t y0 = b0*hpf + b1*x1 + b2*x2 + a1*y1 + a2*y2; + y0 *= ax; + float32_t out = clip(y0); + y0 = 0.95f*y0 + 0.05f*out; //Let a little harmonic distortion feed back into the filter loop + x2 = x1; + x1 = hpf; + y2 = y1; + y1 = y0; + + out = out * wet_gain + drySig * dry_gain; + + blockL->data[i] = out; + blockR->data[i] = out; + } + AudioStream_F32::transmit(blockL, 0); // send blockL on both output channels + AudioStream_F32::transmit(blockL, 1); + AudioStream_F32::release(blockL); + AudioStream_F32::release(blockR); + if (blockMod) AudioStream_F32::release(blockMod); +} + +FLASHMEM void AudioEffectWahMono_F32::setModel(wahModel_t model) +{ + if (model > WAH_MODEL_LAST) model = WAH_MODEL_G1; + + float32_t _b0, _b1, _b2; + float32_t _a0c, _a1c, _a2c; + float32_t _b0h, _b1h, _b2h; + float32_t _b0b, _b2b; + float32_t _a0b, _a1b, _a2b; + float32_t _a1p, _ghpf; + + //helper variables + float32_t ro = 0.0f; + float32_t re = 0.0f; + float32_t Req = 0.0f; + float32_t ic = 0.0f; + + float32_t RpRi, f0, w0, Q, c, s, alpha; + + //Equivalent output resistance seen from BJT collector + ro = compValues[model].Rc * compValues[model].Rpot / (compValues[model].Rc + compValues[model].Rpot) ; + ro = ro * compValues[model].Rbias / (ro + compValues[model].Rbias); + ic = 3.7f/compValues[model].Rc; //Typical bias current + re = 0.025f/ic; //BJT gain stage equivalent internal emitter resistance + // gm = Ic/Vt, re = 1/gm + Req = compValues[model].Re + re; + gf = -ro/Req; //forward gain of transistor stage + re = compValues[model].beta*Req; //Resistance looking into BJT emitter + Rp = compValues[model].Rp*re/(re + compValues[model].Rp); + + RpRi = Rp*compValues[model].Ri/(Rp + compValues[model].Ri); + f0 = 1.0f/(2.0f*M_PI*sqrtf(compValues[model].Lp*compValues[model].Cf)); + w0 = 2.0f*M_PI*f0/fs; + Q = RpRi*sqrtf(compValues[model].Cf/compValues[model].Lp); + c = cosf(w0); + s = sinf(w0); + alpha = s/(2.0f*Q); + + //High Pass Biquad Coefficients + _b0h = (1.0f + c)/2.0f; + _b1h = -(1.0f + c); + _b2h = (1.0f + c)/2.0f; + + //Band-pass biquad coefficients + _b0b = Q*alpha; + _b2b = -Q*alpha; + _a0b = 1.0f + alpha; + _a1b = -2.0f*c; + _a2b = 1.0f - alpha; + + //1-pole high pass filter coefficients + // H(z) = g * (1 - z^-1)/(1 - a1*z^-1) + // Direct Form 1: + // y[n] = ghpf * ( x[n] - x[n-1] ) - a1p*y[n-1] + + _a1p = -expf(-1.0f/(compValues[model].Ri*compValues[model].Ci*fs)); + _ghpf = gf*(1.0f - a1p)*0.5f; //BJT forward gain worked in here to + // save extra multiplications in + // updating biquad coefficients + + //Distill all down to final biquad coefficients + float32_t Gi = compValues[model].Rs/(compValues[model].Ri + compValues[model].Rs); + float32_t gbpf = 1.0f/(2.0f*M_PI*f0*compValues[model].Ri*compValues[model].Cf); //band-pass component equivalent gain + + //Final Biquad numerator coefficients + _b0 = gbpf*b0b + Gi*a0b; + _b1 = Gi*a1b; + _b2 = gbpf*b2b + Gi*a2b; + + //Constants to make denominator coefficients computation more efficient + //in real-time + _a0c = -gf*b0h; + _a1c = -gf*b1h; + _a2c = -gf*b2h; + + __disable_irq(); + b0h = _b0h; + b1h = _b1h; + b2h = _b2h; + b0b = _b0b; + b2b = _b2b; + a0b = _a0b; + a1b = _a1b; + a2b = _a2b; + a1p = _a1p; + ghpf = _ghpf; + b0 = _b0; + b1 = _b1; + b2 = _b2; + a0c = _a0c; + a1c = _a1c; + a2c = _a2c; + y1 = 0.0f; //biquad state variables + y2 = 0.0f; + x1 = 0.0f; + x2 = 0.0f; + //First order high-pass filter state variables + yh1 = 0.0f; + xh1 = 0.0f; + __enable_irq(); +} + + +float32_t AudioEffectWahMono_F32::clip(float32_t x) +{ + + float32_t thrs = 0.8f; + float32_t nthrs = -0.72f; + float32_t f=1.25f; + + //Hard limiting + if(x >= 1.2f) x = 1.2f; + if(x <= -1.12f) x = -1.12f; + + //Soft clipping + if(x > thrs) + { + x -= f*sqr(x - thrs); + } + if(x < nthrs){ + x += f*sqr(x - nthrs); + } + return x; +} \ No newline at end of file diff --git a/src/effect_wahMono_F32.h b/src/effect_wahMono_F32.h new file mode 100644 index 0000000..7e345d4 --- /dev/null +++ b/src/effect_wahMono_F32.h @@ -0,0 +1,167 @@ +/** + * @file effect_wahMono_F32.h + * @author Piotr Zapart + * @brief Mono WAH effect + * @version 0.1 + * @date 2024-07-09 + * + * @copyright Copyright (c) 2024 www.hexefx.com + * + * Implementation is based on the work of Transmogrifox + * https://cackleberrypines.net/transmogrifox/src/bela/inductor_wah_C_src/ + * + * 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 ." + */ +#ifndef _EFFECT_WAHMONO_F32_H_ +#define _EFFECT_WAHMONO_F32_H_ + +#include +#include "AudioStream_F32.h" +#include "basic_DSPutils.h" + +typedef enum +{ + WAH_MODEL_G1 = 0, + WAH_MODEL_G2, + WAH_MODEL_G3, + WAH_MODEL_G4, + WAH_MODEL_VOCAL, + WAH_MODEL_EXTREME, + WAH_MODEL_CUSTOM, + WAH_MODEL_BASS, + WAH_MODEL_LAST +}wahModel_t; + +class AudioEffectWahMono_F32 : public AudioStream_F32 +{ +public: + AudioEffectWahMono_F32(void) : AudioStream_F32(3, inputQueueArray_f32) + { + setModel(WAH_MODEL_G3); + } + // Alternate specification of block size. Sample rate does not apply for analyze_rms + AudioEffectWahMono_F32(const AudioSettings_F32 &settings) : AudioStream_F32(3, inputQueueArray_f32) + { + block_size = settings.audio_block_samples; + fs = settings.sample_rate_Hz; + setModel(WAH_MODEL_G3); + } + virtual void update(); + + void setModel(wahModel_t model); + void setFreq(float32_t val) + { + val = constrain(val, 0.0f, 1.0f); + val = 1.0f - val; + __disable_irq(); + gp = val; + __enable_irq(); + } + void setRange(float32_t heel, float32_t toe) + { + gp_top = 1.0f - constrain(heel, 0.0f, 1.0f); + gp_btm = 1.0f - constrain(toe, 0.0f, 1.0f); + } + + void setMix(float32_t mix) + { + mix = constrain(mix, 0.0f, 1.0f); + float32_t dry, wet; + mix_pwr(mix, &wet, &dry); + __disable_irq(); + dry_gain = dry; + wet_gain = wet; + __enable_irq(); + } + bool bypass_get(void) {return bp;} + void bypass_set(bool state) {bp = state;} + bool bypass_tgl(void) + { + bp ^= 1; + return bp; + } +private: + bool bp = true; // bypass flag + audio_block_f32_t *inputQueueArray_f32[3]; + uint16_t block_size = AUDIO_BLOCK_SAMPLES; + float32_t fs = AUDIO_SAMPLE_RATE_EXACT; + typedef struct + { + //Circuit parameters + //Using these makes it straight-forward to model other + //variants of the circuit + float32_t Lp; //RLC tank inductor + float32_t Cf; //feedback capacitor + float32_t Ci; //input capacitor + + float32_t Rpot; //Pot resistance value + float32_t Ri; //input feed resistor + float32_t Rs; //RLC tank to BJT base resistor (dry mix) + float32_t Rp; //resistor placed parallel with the inductor + + //Gain-setting components + float32_t Rc; //BJT gain stage collector resistor + + float32_t Rbias; //Typically 470k bias resistor shows up in parallel with output + float32_t Re; //BJT gain stage emitter resistor + + float32_t beta; //BJT forward gain + }wah_componentValues_t; + + static const wah_componentValues_t compValues[WAH_MODEL_LAST]; + + float32_t gp = 0.0f; // pot gain + float32_t input_gain = 0.5f; + float32_t dry_gain = 0.0f; + float32_t wet_gain = 1.0f; + float32_t gp_top = 0.0f; + float32_t gp_btm = 1.0f; + + float32_t re; //equivalent resistance looking into input BJT base + float32_t Rp; //resistor placed parallel with the inductor + float32_t gf; //forward gain of BJT amplifier + //High-Pass biquad coefficients + float32_t b0h, b1h, b2h; + + //Band-Pass biquad coefficients + float32_t b0b, b2b; + float32_t a0b, a1b, a2b; + + //Final combined biquad coefficients used by run_filter() + float32_t b0; + float32_t b1; + float32_t b2; + + float32_t a0c; + float32_t a1c; + float32_t a2c; + //First order high-pass filter coefficients + //y[n] = ghpf * ( x[n] - x[n-1] ) - a1p*y[n-1] + float32_t a1p; + float32_t ghpf; + + //biquad state variables + float32_t y1; + float32_t y2; + float32_t x1; + float32_t x2; + + //First order high-pass filter state variables + float32_t yh1; + float32_t xh1; + float32_t clip(float32_t x); + float32_t sqr(float32_t x) + { + return x*x; + } +}; + + +#endif // _EFFECT_WAHMONO_F32_H_ diff --git a/src/filter_DCblockerStereo_F32.h b/src/filter_DCblockerStereo_F32.h new file mode 100644 index 0000000..42d7e72 --- /dev/null +++ b/src/filter_DCblockerStereo_F32.h @@ -0,0 +1,153 @@ +/** + * @file filter_DCblockerStereo_F32.h + * @author Piotr Zapart + * @brief simple IIR based stereo DB blocking filter + * @version 0.1 + * @date 2024-06-01 + * + * @copyright Copyright (c) 2024 + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * You should have received a copy of the GNU General Public License along with this program. + * If not, see ." + */ + +#ifndef _FILTER_DCBLOCKERSTEREO_F32_H_ +#define _FILTER_DCBLOCKERSTEREO_F32_H_ + +#include +#include "basic_DSPutils.h" +#include + +// y = x - xm1 + 0.995 * ym1; +// xm1 = x; +// ym1 = y; + +class AudioFilterDCblockerStereo_F32 : public AudioStream_F32 +{ +public: + AudioFilterDCblockerStereo_F32(void) : AudioStream_F32(2, inputQueueArray) + { + fs_Hz = AUDIO_SAMPLE_RATE_EXACT; + blockSize = AUDIO_BLOCK_SAMPLES; + } + + AudioFilterDCblockerStereo_F32(const AudioSettings_F32 &settings) : AudioStream_F32(2, inputQueueArray) + { + fs_Hz = settings.sample_rate_Hz; + blockSize = settings.audio_block_samples; + } + + void update() + { + audio_block_f32_t *blockL, *blockR; + uint16_t i; + float32_t tmpf32; + + + if (bp) // handle bypass + { + blockL = AudioStream_F32::receiveReadOnly_f32(0); + blockR = AudioStream_F32::receiveReadOnly_f32(1); + if (!blockL || !blockR) + { + if (blockL) + AudioStream_F32::release(blockL); + if (blockR) + AudioStream_F32::release(blockR); + return; + } + AudioStream_F32::transmit(blockL, 0); + AudioStream_F32::transmit(blockR, 1); + AudioStream_F32::release(blockL); + AudioStream_F32::release(blockR); + return; + } + blockL = AudioStream_F32::receiveWritable_f32(0); + blockR = AudioStream_F32::receiveWritable_f32(1); + if (!blockL || !blockR) + { + if (blockL) + AudioStream_F32::release(blockL); + if (blockR) + AudioStream_F32::release(blockR); + return; + } + + float32_t _xRegL = xRegL; + float32_t _xRegR = xRegR; + float32_t _yRegL = yRegL; + float32_t _yRegR = yRegR; + + for (i=0; idata[i] - _xRegL + k * _yRegL; + _yRegL = tmpf32; + _xRegL = blockL->data[i]; + blockL->data[i] = _yRegL; + + tmpf32 = blockL->data[i+1] - _xRegL + k * _yRegL; + _yRegL = tmpf32; + _xRegL = blockL->data[i+1]; + blockL->data[i+1] = _yRegL; + + tmpf32 = blockL->data[i+2] - _xRegL + k * _yRegL; + _yRegL = tmpf32; + _xRegL = blockL->data[i+2]; + blockL->data[i+2] = _yRegL; + + tmpf32 = blockL->data[i+3] - _xRegL + k * _yRegL; + _yRegL = tmpf32; + _xRegL = blockL->data[i+3]; + blockL->data[i+3] = _yRegL; + + tmpf32 = blockR->data[i] - _xRegR + k * _yRegR; + _yRegR = tmpf32; + _xRegR = blockR->data[i]; + blockR->data[i] = _yRegR; + + tmpf32 = blockR->data[i+1] - _xRegR + k * _yRegR; + _yRegR = tmpf32; + _xRegR = blockR->data[i+1]; + blockR->data[i+1] = _yRegR; + + tmpf32 = blockR->data[i+2] - _xRegR + k * _yRegR; + _yRegR = tmpf32; + _xRegR = blockR->data[i+2]; + blockR->data[i+2] = _yRegR; + + tmpf32 = blockR->data[i+3] - _xRegR + k * _yRegR; + _yRegR = tmpf32; + _xRegR = blockR->data[i+3]; + blockR->data[i+3] = _yRegR; + } + xRegL = _xRegL; + yRegL = _yRegL; + xRegR = _xRegR; + yRegR = _yRegR; + + AudioStream_F32::transmit(blockL, 0); // send blockL on both output channels + AudioStream_F32::transmit(blockL, 1); + AudioStream_F32::release(blockL); + AudioStream_F32::release(blockR); + + } + +private: + audio_block_f32_t *inputQueueArray[2]; + float32_t fs_Hz; + uint16_t blockSize; + float k = 0.995f; + bool bp = false; // bypass flag + float32_t xRegL = 0.0f; + float32_t xRegR = 0.0f; + float32_t yRegL = 0.0f; + float32_t yRegR = 0.0f; +}; + + +#endif // _FILTER_DCBLOCKERSTEREO_F32_H_ diff --git a/src/hexefx_audio_F32.h b/src/hexefx_audio_F32.h index 6d51170..f95350a 100644 --- a/src/hexefx_audio_F32.h +++ b/src/hexefx_audio_F32.h @@ -4,6 +4,8 @@ #include "control_WM8731_F32.h" #include "control_SGTL5000_F32.h" #include "control_ES8388_F32.h" +#include "control_AK4452_F32.h" +#include "control_AK5552_F32.h" #include "input_i2s_ext_F32.h" #include "output_i2s_ext_F32.h" // extended version @@ -17,6 +19,7 @@ #include "filter_equalizer_F32.h" #include "filter_3bandeq.h" #include "filter_biquadStereo_F32.h" +#include "filter_DCblockerStereo_F32.h" #include "effect_gainStereo_F32.h" #include "effect_platereverb_F32.h" @@ -30,5 +33,6 @@ #include "effect_compressorStereo_F32.h" #include "effect_guitarBooster_F32.h" #include "effect_xfaderStereo_F32.h" +#include "effect_wahMono_F32.h" #endif // _HEXEFX_AUDIO_H diff --git a/src/input_i2s2_F32.cpp b/src/input_i2s2_F32.cpp index b965ee1..b2d8ea7 100644 --- a/src/input_i2s2_F32.cpp +++ b/src/input_i2s2_F32.cpp @@ -178,8 +178,8 @@ void AudioInputI2S2_F32::update(void) block_right_f32 = new_right; block_offset = 0; __enable_irq(); - update_1chan(0, out_left); // uses audio_block_samples and update_counter - update_1chan(1, out_right); // uses audio_block_samples and update_counter + update_1chan(0^channel_swap, out_left); // uses audio_block_samples and update_counter + update_1chan(1^channel_swap, out_right); // uses audio_block_samples and update_counter } else if (new_left != NULL) { diff --git a/src/input_i2s2_F32.h b/src/input_i2s2_F32.h index ec3b0cb..46aca95 100644 --- a/src/input_i2s2_F32.h +++ b/src/input_i2s2_F32.h @@ -60,6 +60,8 @@ public: int get_isOutOfMemory(void) { return flag_out_of_memory; } void clear_isOutOfMemory(void) { flag_out_of_memory = 0; } bool get_update_responsibility() { return update_responsibility;} + void set_channel_swap(bool sw) { channel_swap = sw ? 1 : 0;} + bool get_channel_swap() {return (bool)channel_swap;} protected: AudioInputI2S2_F32(int dummy): AudioStream_F32(0, NULL) {} // to be used only inside AudioInputI2Sslave !! static bool update_responsibility; @@ -74,6 +76,7 @@ private: static uint16_t block_offset; static int flag_out_of_memory; static unsigned long update_counter; + uint8_t channel_swap = 0; }; class AudioInputI2S2slave_F32 : public AudioInputI2S2_F32 diff --git a/src/input_i2s_ext_F32.cpp b/src/input_i2s_ext_F32.cpp index d61895b..487838b 100644 --- a/src/input_i2s_ext_F32.cpp +++ b/src/input_i2s_ext_F32.cpp @@ -199,8 +199,8 @@ void AudioInputI2S_ext_F32::update(void) __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 + update_1chan(0^channel_swap, out_left); // uses audio_block_samples and update_counter + update_1chan(1^channel_swap, out_right); // uses audio_block_samples and update_counter } else if (new_left != NULL) { diff --git a/src/input_i2s_ext_F32.h b/src/input_i2s_ext_F32.h index 5f3983d..4169133 100644 --- a/src/input_i2s_ext_F32.h +++ b/src/input_i2s_ext_F32.h @@ -58,7 +58,8 @@ public: void begin(void); int get_isOutOfMemory(void) { return flag_out_of_memory; } void clear_isOutOfMemory(void) { flag_out_of_memory = 0; } - + void set_channel_swap(bool sw) { channel_swap = sw ? 1 : 0;} + bool get_channel_swap() {return (bool)channel_swap;} protected: AudioInputI2S_ext_F32(int dummy) : AudioStream_F32(0, NULL) {} // to be used only inside AudioInputI2Sslave !! static bool update_responsibility; @@ -74,7 +75,7 @@ private: 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 + uint8_t channel_swap = 0; }; class AudioInputI2Sslave_ext_F32 : public AudioInputI2S_ext_F32 diff --git a/src/output_i2s2_F32.cpp b/src/output_i2s2_F32.cpp index dd3ce88..1d16efd 100644 --- a/src/output_i2s2_F32.cpp +++ b/src/output_i2s2_F32.cpp @@ -202,7 +202,7 @@ void AudioOutputI2S2_F32::update(void) 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 + block_f32 = receiveReadOnly_f32(0^channel_swap); // input 0 = left channel if (block_f32) { if (block_f32->length != audio_block_samples) @@ -247,7 +247,7 @@ void AudioOutputI2S2_F32::update(void) } block_f32_scaled = block2_f32_scaled; // this is simply renaming the pre-allocated buffer - block_f32 = receiveReadOnly_f32(1); // input 1 = right channel + block_f32 = receiveReadOnly_f32(1^channel_swap); // input 1 = right channel if (block_f32) { scale_float_to_int32range(block_f32->data, block_f32_scaled->data, audio_block_samples); diff --git a/src/output_i2s2_F32.h b/src/output_i2s2_F32.h index e655f28..124224c 100644 --- a/src/output_i2s2_F32.h +++ b/src/output_i2s2_F32.h @@ -63,6 +63,8 @@ public: friend class AudioOutputI2SQuad_F32; friend class AudioInputI2SQuad_F32; bool get_update_responsibility() { return update_responsibility;} + void set_channel_swap(bool sw) { channel_swap = sw ? 1 : 0;} + bool get_channel_swap() {return (bool)channel_swap;} protected: AudioOutputI2S2_F32(int dummy): AudioStream_F32(2, inputQueueArray) {} // to be used only inside AudioOutputI2Sslave !! static void config_i2s(void); @@ -84,6 +86,7 @@ private: static float sample_rate_Hz; static int audio_block_samples; volatile uint8_t enabled = 1; + uint8_t channel_swap = 0; }; class AudioOutputI2S2slave_F32 : public AudioOutputI2S2_F32 diff --git a/src/output_i2s_ext_F32.cpp b/src/output_i2s_ext_F32.cpp index d6cd1ce..e5e7cda 100644 --- a/src/output_i2s_ext_F32.cpp +++ b/src/output_i2s_ext_F32.cpp @@ -203,7 +203,7 @@ void AudioOutputI2S_ext_F32::update(void) 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 + block_f32 = receiveReadOnly_f32(0^channel_swap); // input 0 = left channel if (block_f32) { if (block_f32->length != audio_block_samples) @@ -251,7 +251,7 @@ void AudioOutputI2S_ext_F32::update(void) block_f32_scaled = block2_f32_scaled; // this is simply renaming the pre-allocated buffer - block_f32 = receiveReadOnly_f32(1); // input 1 = right channel + block_f32 = receiveReadOnly_f32(1^channel_swap); // input 1 = right channel if (block_f32) { // Optional scaling for easy volume control. Leave outputScale==1.0f for default diff --git a/src/output_i2s_ext_F32.h b/src/output_i2s_ext_F32.h index 2118b71..81f206a 100644 --- a/src/output_i2s_ext_F32.h +++ b/src/output_i2s_ext_F32.h @@ -60,6 +60,8 @@ public: void setGain(float _oscale) {outputScale = _oscale; } virtual void update(void); void begin(void); + void set_channel_swap(bool sw) { channel_swap = sw ? 1 : 0;} + bool get_channel_swap() {return (bool)channel_swap;} friend class AudioInputI2S_ext_F32; #if defined(__IMXRT1062__) friend class AudioOutputI2SQuad_F32; @@ -84,6 +86,7 @@ private: static int audio_block_samples; volatile uint8_t enabled = 1; float outputScale = 1.0f; // Quick volume control + uint8_t channel_swap = 0; }; class AudioOutputI2Sslave_ext_F32 : public AudioOutputI2S_ext_F32