├── .gitignore ├── HX_ToneStack_F32 ├── README.md ├── ToneStack_F32.png ├── filter_tdf2.h ├── filter_tonestackStereo_F32.cpp └── filter_tonestackStereo_F32.h ├── Hx_InfinitePhaser_F32 ├── InfinitePhaser_patch.png ├── README.md ├── effect_infphaser_F32.cpp └── effect_infphaser_F32.h ├── Hx_MonoToStereo_F32 ├── README.md ├── effect_monoToStereo_F32.cpp ├── effect_monoToStereo_F32.h └── monoToStereo_F32_testPatch.png ├── Hx_Phaser ├── README.md ├── effect_phaser.cpp ├── effect_phaser.h └── phaser_internal.png ├── Hx_PlateReverb ├── Hx_PlateReverb.ino ├── README.md ├── StereoPlateReverb.png ├── effect_platervbstereo.cpp └── effect_platervbstereo.h ├── Hx_PlateReverb_F32 ├── README.md ├── effect_platervbstereo_F32.cpp ├── effect_platervbstereo_F32.h └── plateReverb_schm.png ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /HX_ToneStack_F32/README.md: -------------------------------------------------------------------------------- 1 | ## Stereo Tone Stack 2 | Fully stereo in/out guitar tone stack component for the [OpenAudio_ArduinoLibrary](https://github.com/chipaudette/OpenAudio_ArduinoLibrary "OpenAudio_ArduinoLibrary"). 3 | Emulates 9 different EQ models from various guitar amplifiers. 4 | Code is based on implementation created by David Yeh and Tim Goetze. 5 | 6 | ### Connections: 7 | ToneStack requires stereo in and out connenctions. 8 | ### API: 9 | 10 | ```void setModel(toneStack_presets_e m);``` 11 | set one of the 9 available EQ models, use `TONESTACK_OFF` to bypass the module. 12 | Available models: 13 | * TONESTACK_OFF, 14 | * TONESTACK_BASSMAN, 15 | * TONESTACK_PRINCE, 16 | * TONESTACK_MESA, 17 | * TONESTACK_VOX, 18 | * TONESTACK_JCM800, 19 | * TONESTACK_TWIN, 20 | * TONESTACK_HK, 21 | * TONESTACK_JAZZ, 22 | * TONESTACK_PIGNOSE 23 | 24 | Example: 25 | ```tonestack.model(TONESTACK_BASSMAN); // set EQ model to Fender Bassman``` 26 | 27 | ```const char *getName()``` 28 | returns a pointer to the currently used EQ model name as char array. 29 | Example: 30 | ```Serial.println(tonestack.getName()); // print the preset name``` 31 | 32 | ```void setTone(float32_t b, float32_t m, float32_t t);``` 33 | set Bass (b), Mid (m) and Treble (t) at once. Range 0.0f to 1.0f 34 | Example: 35 | ```tonestack.setTone(0.5f, 0.5f, 0.5f); // set all controls to 50% ``` 36 | 37 | ```void setBass(float32_t b);``` 38 | set the bass control. Parameter range: 0.0 to 1.0. 39 | Example: 40 | ```tonestack.setBass(0.7f);``` 41 | 42 | ```void setMid(float32_t m);``` 43 | set the middle control. Parameter range: 0.0 to 1.0. 44 | Example: 45 | ```tonestack.setMid(0.2f);``` 46 | 47 | ```void setTreble(float32_t t);``` 48 | set the treble control. Parameter range: 0.0 to 1.0. 49 | Example: 50 | ```tonestack.setTreble(0.3f);``` 51 | 52 | ```void setGain(float32_t g);``` 53 | set the gain/volume. 54 | Example: 55 | ```tonestack.setGain(1.5f);``` 56 | 57 | Typical application using OpenAudio_ArduinoLibrary: 58 | ![alt text][pic1] 59 | 60 | ### Sound Sample 61 | 62 | [![Teensy4 toneStackStereo_F32](https://img.youtube.com/vi/CsFPGtq5eNM/0.jpg)](https://www.youtube.com/watch?v=CsFPGtq5eNM) 63 | 64 | [pic1]: ToneStack_F32.png "Stereo Tone Stack connections" -------------------------------------------------------------------------------- /HX_ToneStack_F32/ToneStack_F32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hexeguitar/t40fx/115f81f2ddf35346e38e9b6b0a6d341aad9b2e76/HX_ToneStack_F32/ToneStack_F32.png -------------------------------------------------------------------------------- /HX_ToneStack_F32/filter_tdf2.h: -------------------------------------------------------------------------------- 1 | /* 2 | TDFII.h 3 | 4 | Copyright 2006-7 5 | David Yeh (implementation) 6 | 2006-14 7 | Tim Goetze (cosmetics) 8 | 9 | transposed Direct Form II digital filter. 10 | Assumes order of b = order of a. 11 | Assumes a0 = 1. 12 | 13 | Ported for 32bit float version for OpenAudio_ArduinoLibrary: 14 | https://github.com/chipaudette/OpenAudio_ArduinoLibrary 15 | 16 | 12.2023 Piotr Zapart www.hexefx.com 17 | 18 | */ 19 | /* 20 | This program is free software; you can redistribute it and/or 21 | modify it under the terms of the GNU General Public License 22 | as published by the Free Software Foundation; either version 3 23 | of the License, or (at your option) any later version. 24 | 25 | This program is distributed in the hope that it will be useful, 26 | but WITHOUT ANY WARRANTY; without even the implied warranty of 27 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 28 | GNU General Public License for more details. 29 | 30 | You should have received a copy of the GNU General Public License 31 | along with this program; if not, write to the Free Software 32 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 33 | 02111-1307, USA or point your web browser to http://www.gnu.org. 34 | */ 35 | #ifndef _FILTER_TDF2_H_ 36 | #define _FILTER_TDF2_H_ 37 | 38 | #include "arm_math.h" 39 | 40 | template 41 | class AudioFilterTDF2 42 | { 43 | public: 44 | float32_t a[N + 1]; 45 | float32_t b[N + 1]; 46 | float32_t h[N + 1]; 47 | 48 | void reset() 49 | { 50 | for (int i = 0; i <= N; ++i) 51 | h[i] = 0; // zero state 52 | } 53 | 54 | void init() 55 | { 56 | reset(); 57 | clear(); 58 | } 59 | 60 | void clear() 61 | { 62 | for (int i = 0; i <= N; i++) 63 | a[i] = b[i] = 0; 64 | b[0] = 1; 65 | } 66 | 67 | void process(float32_t *src, float32_t *dst, uint32_t blockSize) 68 | { 69 | for (uint16_t i = 0; i 6 | 2006-14 7 | Tim Goetze (cosmetics) 8 | 9 | Tone Stack emulation for Teensy 4.x 10 | Ported for 32bit float version for OpenAudio_ArduinoLibrary: 11 | https://github.com/chipaudette/OpenAudio_ArduinoLibrary 12 | 13 | 12.2023 Piotr Zapart www.hexefx.com 14 | 15 | */ 16 | /* 17 | This program is free software; you can redistribute it and/or 18 | modify it under the terms of the GNU General Public License 19 | as published by the Free Software Foundation; either version 3 20 | of the License, or (at your option) any later version. 21 | 22 | This program is distributed in the hope that it will be useful, 23 | but WITHOUT ANY WARRANTY; without even the implied warranty of 24 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 | GNU General Public License for more details. 26 | 27 | You should have received a copy of the GNU General Public License 28 | along with this program; if not, write to the Free Software 29 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 30 | 02111-1307, USA or point your web browser to http://www.gnu.org. 31 | */ 32 | #include "filter_tonestackStereo_F32.h" 33 | 34 | /** 35 | * @brief EQ models based on various guitar amplifiers 36 | */ 37 | AudioFilterToneStackStereo_F32::toneStackParams_t AudioFilterToneStackStereo_F32::presets[] = { 38 | /* for convenience, */ 39 | #define k *1e3 40 | #define M *1e6 41 | #define nF *1e-9 42 | #define pF *1e-12 43 | /* parameter order is R1 - R4, C1 - C3 */ 44 | /* R1=treble R2=Bass R3=Mid, C1-3 related caps, R4 = parallel resistor */ 45 | /* { 250000, 1000000, 25000, 56000, 0.25e-9, 20e-9, 20e-9 }, DY */ 46 | {250 k, 1 M, 25 k, 56 k, 250 pF, 20 nF, 20 nF, "Bassman"}, /* 59 Bassman 5F6-A */ 47 | {250 k, 250 k, 4.8 k, 100 k, 250 pF, 100 nF, 47 nF, "Prince"}, /* 64 Princeton AA1164 */ 48 | {250 k, 1 M, 25 k, 47 k, 600 pF, 20 nF, 20 nF, "Mesa"}, /* Mesa Dual Rect. 'Orange' */ 49 | /* Vox -- R3 is fixed (circuit differs anyway) */ 50 | {1 M, 1 M, 10 k, 100 k, 50 pF, 22 nF, 22 nF, "Vox"}, /* Vox "top boost" */ 51 | 52 | {220 k, 1 M, 22 k, 33 k, 470 pF, 22 nF, 22 nF, "JCM800"}, /* 59/81 JCM-800 Lead 100 2203 */ 53 | {250 k, 250 k, 10 k, 100 k, 120 pF, 100 nF, 47 nF, "Twin"}, /* 69 Twin Reverb AA270 */ 54 | 55 | {500 k, 1 M, 25 k, 47 k, 150 pF, 22 nF, 22 nF, "HK"}, /* Hughes & Kettner Tube 20 */ 56 | {250 k, 250 k, 10 k, 100 k, 150 pF, 82 nF, 47 nF, "Jazz"}, /* Roland Jazz Chorus */ 57 | {250 k, 1 M, 50 k, 33 k, 100 pF, 22 nF, 22 nF, "Pignose"}, /* Pignose G40V */ 58 | #undef k 59 | #undef M 60 | #undef nF 61 | #undef pF 62 | }; 63 | 64 | AudioFilterToneStackStereo_F32 :: AudioFilterToneStackStereo_F32() : AudioStream_F32(2, inputQueueArray_f32) 65 | { 66 | gain = 1.0f; 67 | setModel(TONESTACK_OFF); 68 | } 69 | 70 | void AudioFilterToneStackStereo_F32::setModel(toneStack_presets_e m) 71 | { 72 | if (m >= TONE_STACK_MAX_MODELS) return; 73 | if (m == TONESTACK_OFF) 74 | { 75 | bp = true; 76 | filterL.reset(); 77 | filterR.reset(); 78 | return; 79 | } 80 | bp = false; 81 | currentModel = m - 1; 82 | 83 | float32_t R1 = presets[currentModel].R1, \ 84 | R2 = presets[currentModel].R2, \ 85 | R3 = presets[currentModel].R3, \ 86 | R4 = presets[currentModel].R4; 87 | float32_t C1 = presets[currentModel].C1, \ 88 | C2 = presets[currentModel].C2, \ 89 | C3 = presets[currentModel].C3; 90 | 91 | b1t = C1 * R1; 92 | b1m = C3 * R3; 93 | b1l = C1 * R2 + C2 * R2; 94 | b1d = C1 * R3 + C2 * R3; 95 | b2t = C1 * C2 * R1 * R4 + C1 * C3 * R1 * R4; 96 | b2m2 = -(C1 * C3 * R3 * R3 + C2 * C3 * R3 * R3); 97 | b2m = C1 * C3 * R1 * R3 + C1 * C3 * R3 * R3 + C2 * C3 * R3 * R3; 98 | b2l = C1 * C2 * R1 * R2 + C1 * C2 * R2 * R4 + C1 * C3 * R2 * R4; 99 | b2lm = C1 * C3 * R2 * R3 + C2 * C3 * R2 * R3; 100 | b2d = C1 * C2 * R1 * R3 + C1 * C2 * R3 * R4 + C1 * C3 * R3 * R4; 101 | b3lm = C1 * C2 * C3 * R1 * R2 * R3 + C1 * C2 * C3 * R2 * R3 * R4; 102 | b3m2 = -(C1 * C2 * C3 * R1 * R3 * R3 + C1 * C2 * C3 * R3 * R3 * R4); 103 | b3m = C1 * C2 * C3 * R1 * R3 * R3 + C1 * C2 * C3 * R3 * R3 * R4; 104 | b3t = C1 * C2 * C3 * R1 * R3 * R4; 105 | b3tm = -b3t; 106 | b3tl = C1 * C2 * C3 * R1 * R2 * R4; 107 | a0 = 1.0f; 108 | a1d = C1 * R1 + C1 * R3 + C2 * R3 + C2 * R4 + C3 * R4; 109 | a1m = C3 * R3; 110 | a1l = C1 * R2 + C2 * R2; 111 | a2m = C1 * C3 * R1 * R3 - C2 * C3 * R3 * R4 + C1 * C3 * R3 * R3 + C2 * C3 * R3 * R3; 112 | a2lm = C1 * C3 * R2 * R3 + C2 * C3 * R2 * R3; 113 | a2m2 = -(C1 * C3 * R3 * R3 + C2 * C3 * R3 * R3); 114 | a2l = C1 * C2 * R2 * R4 + C1 * C2 * R1 * R2 + C1 * C3 * R2 * R4 + C2 * C3 * R2 * R4; 115 | a2d = C1 * C2 * R1 * R4 + C1 * C3 * R1 * R4 + C1 * C2 * R3 * R4 + C1 * C2 * R1 * R3 + C1 * C3 * R3 * R4 + C2 * C3 * R3 * R4; 116 | a3lm = C1 * C2 * C3 * R1 * R2 * R3 + C1 * C2 * C3 * R2 * R3 * R4; 117 | a3m2 = -(C1 * C2 * C3 * R1 * R3 * R3 + C1 * C2 * C3 * R3 * R3 * R4); 118 | a3m = C1 * C2 * C3 * R3 * R3 * R4 + C1 * C2 * C3 * R1 * R3 * R3 - C1 * C2 * C3 * R1 * R3 * R4; 119 | a3l = C1 * C2 * C3 * R1 * R2 * R4; 120 | a3d = C1 * C2 * C3 * R1 * R3 * R4; 121 | 122 | filterL.reset(); 123 | filterR.reset(); 124 | } 125 | 126 | void AudioFilterToneStackStereo_F32::update() 127 | { 128 | #if defined(__ARM_ARCH_7EM__) 129 | audio_block_f32_t *blockL, *blockR; 130 | blockL = AudioStream_F32::receiveWritable_f32(0); // audio data 131 | blockR = AudioStream_F32::receiveWritable_f32(1); // audio data 132 | if (!blockL || !blockR) 133 | { 134 | if (blockL) release((audio_block_f32_t *)blockL); 135 | if (blockR) release((audio_block_f32_t *)blockR); 136 | return; 137 | } 138 | if (bp) // bypass mode 139 | { 140 | AudioStream_F32::transmit((audio_block_f32_t *)blockL,0); 141 | AudioStream_F32::transmit((audio_block_f32_t *)blockR,1); 142 | AudioStream_F32::release((audio_block_f32_t *)blockL); 143 | AudioStream_F32::release((audio_block_f32_t *)blockR); 144 | return; 145 | } 146 | filterL.process(blockL->data, blockL->data, blockL->length); 147 | filterR.process(blockR->data, blockR->data, blockR->length); 148 | if (gain != 1.0f) 149 | { 150 | arm_scale_f32(blockL->data, gain, blockL->data, blockL->length); 151 | arm_scale_f32(blockR->data, gain, blockR->data, blockR->length); 152 | } 153 | AudioStream_F32::transmit(blockL, 0); 154 | AudioStream_F32::transmit(blockR, 1); 155 | AudioStream_F32::release(blockL); 156 | AudioStream_F32::release(blockR); 157 | #endif 158 | } 159 | -------------------------------------------------------------------------------- /HX_ToneStack_F32/filter_tonestackStereo_F32.h: -------------------------------------------------------------------------------- 1 | /* 2 | ToneStack.h 3 | 4 | Copyright 2006-7 5 | David Yeh 6 | 2006-14 7 | Tim Goetze (cosmetics) 8 | 9 | Tone Stack emulation for Teensy 4.x 10 | 11 | Ported for 32bit float version for OpenAudio_ArduinoLibrary: 12 | https://github.com/chipaudette/OpenAudio_ArduinoLibrary 13 | 14 | 12.2023 Piotr Zapart www.hexefx.com 15 | */ 16 | /* 17 | This program is free software; you can redistribute it and/or 18 | modify it under the terms of the GNU General Public License 19 | as published by the Free Software Foundation; either version 3 20 | of the License, or (at your option) any later version. 21 | 22 | This program is distributed in the hope that it will be useful, 23 | but WITHOUT ANY WARRANTY; without even the implied warranty of 24 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 | GNU General Public License for more details. 26 | 27 | You should have received a copy of the GNU General Public License 28 | along with this program; if not, write to the Free Software 29 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 30 | 02111-1307, USA or point your web browser to http://www.gnu.org. 31 | */ 32 | 33 | #ifndef _FILTER_TONESTACK_STEREO_F32_H_ 34 | #define _FILTER_TONESTACK_STEREO_F32_H_ 35 | 36 | #include 37 | #include 38 | #include "AudioStream_F32.h" 39 | #include "filter_tdf2.h" 40 | #include "arm_math.h" 41 | 42 | #define TONE_STACK_MAX_MODELS (10) 43 | 44 | typedef enum 45 | { 46 | TONESTACK_OFF, 47 | TONESTACK_BASSMAN, 48 | TONESTACK_PRINCE, 49 | TONESTACK_MESA, 50 | TONESTACK_VOX, 51 | TONESTACK_JCM800, 52 | TONESTACK_TWIN, 53 | TONESTACK_HK, 54 | TONESTACK_JAZZ, 55 | TONESTACK_PIGNOSE 56 | }toneStack_presets_e; 57 | 58 | class AudioFilterToneStackStereo_F32 : public AudioStream_F32 59 | { 60 | public: 61 | AudioFilterToneStackStereo_F32(); 62 | ~AudioFilterToneStackStereo_F32(){}; 63 | virtual void update(void); 64 | 65 | typedef struct 66 | { 67 | float32_t R1, R2, R3, R4; 68 | float32_t C1, C2, C3; 69 | const char *name; 70 | } toneStackParams_t; 71 | /** 72 | * @brief preset table 73 | */ 74 | static toneStackParams_t presets[]; 75 | /** 76 | * @brief Set the EQ type from the available pool of models 77 | * Use TONESTACK_OFF to bypass the module 78 | * 79 | * @param m model defined in toneStack_presets_e 80 | */ 81 | void setModel(toneStack_presets_e m); 82 | 83 | /** 84 | * @brief return the name of the model defined in presets[] 85 | * 86 | * @return const char* pointer to the name char array 87 | */ 88 | const char *getName(){ return presets[currentModel].name;} 89 | 90 | /** 91 | * @brief set all 3 parameters at once 92 | * 93 | * @param b bass setting 94 | * @param m middle setting 95 | * @param t treble setting 96 | */ 97 | void setTone(float32_t b, float32_t m, float32_t t) 98 | { 99 | b = constrain(b, 0.0f, 1.0f); bass = b; 100 | m = constrain(m, 0.0f, 1.0f); mid = m; 101 | t = constrain(t, 0.0f, 1.0f); treble = t; 102 | struct 103 | { 104 | float32_t a1, a2, a3; 105 | float32_t b1, b2, b3; 106 | } acoef; // analog coefficients 107 | 108 | // digital coefficients 109 | float32_t dcoef_a[order + 1]; 110 | float32_t dcoef_b[order + 1]; 111 | 112 | m = (m - 1.0f) * 3.5f; 113 | m = pow10f(m); 114 | acoef.a1 = a1d + m * a1m + b * a1l; 115 | acoef.a2 = m * a2m + b * m * a2lm + m * m * a2m2 + b * a2l + a2d; 116 | acoef.a3 = b * m * a3lm + m * m * a3m2 + m * a3m + b * a3l + a3d; 117 | dcoef_a[0] = -1.0f - acoef.a1 * c - acoef.a2 * c * c - acoef.a3 * c * c * c; // sets scale 118 | dcoef_a[1] = -3.0f - acoef.a1 * c + acoef.a2 * c * c + 3.0f * acoef.a3 * c * c * c; 119 | dcoef_a[2] = -3.0f + acoef.a1 * c + acoef.a2 * c * c - 3.0f * acoef.a3 * c * c * c; 120 | dcoef_a[3] = -1.0f + acoef.a1 * c - acoef.a2 * c * c + acoef.a3 * c * c * c; 121 | 122 | acoef.b1 = t * b1t + m * b1m + b * b1l + b1d; 123 | acoef.b2 = t * b2t + m * m * b2m2 + m * b2m + b * b2l + b * m * b2lm + b2d; 124 | acoef.b3 = b * m * b3lm + m * m * b3m2 + m * b3m + t * b3t + t * m * b3tm + t * b * b3tl; 125 | dcoef_b[0] = -acoef.b1 * c - acoef.b2 * c * c - acoef.b3 * c * c * c; 126 | dcoef_b[1] = -acoef.b1 * c + acoef.b2 * c * c + 3.0f * acoef.b3 * c * c * c; 127 | dcoef_b[2] = acoef.b1 * c + acoef.b2 * c * c - 3.0f * acoef.b3 * c * c * c; 128 | dcoef_b[3] = acoef.b1 * c - acoef.b2 * c * c + acoef.b3 * c * c * c; 129 | 130 | __disable_irq(); 131 | for (int i = 1; i <= order; ++i) 132 | { 133 | filterL.a[i] = dcoef_a[i] / dcoef_a[0]; 134 | filterR.a[i] = filterL.a[i]; 135 | } 136 | for (int i = 0; i <= order; ++i) 137 | { 138 | filterL.b[i] = dcoef_b[i] / dcoef_a[0]; 139 | filterR.b[i] = filterL.b[i]; 140 | } 141 | __enable_irq(); 142 | } 143 | /** 144 | * @brief set the bass range EQ 145 | * 146 | * @param b bass setting 147 | */ 148 | void setBass(float32_t b) { setTone(b, mid, treble);} 149 | 150 | /** 151 | * @brief set the mid range EQ 152 | * 153 | * @param m middle setting 154 | */ 155 | void setMid(float32_t m) { setTone(bass, m, treble);} 156 | 157 | /** 158 | * @brief set the treble range EQ 159 | * 160 | * @param t treble setting 161 | */ 162 | void setTreble(float32_t t) {setTone(bass, mid, t);} 163 | 164 | /** 165 | * @brief Master volume setting 166 | * 167 | * @param g gain value 168 | */ 169 | void setGain(float32_t g) { gain = g;} 170 | 171 | private: 172 | static const uint8_t order = 3; 173 | AudioFilterTDF2 filterL; 174 | AudioFilterTDF2 filterR; 175 | audio_block_f32_t *inputQueueArray_f32[2]; 176 | bool bp = false; // bypass 177 | uint8_t currentModel; 178 | float32_t c = 2.0f * AUDIO_SAMPLE_RATE; 179 | float32_t b1t, b1m, b1l, b1d, 180 | b2t, b2m2, b2m, b2l, b2lm, b2d, 181 | b3lm, b3m2, b3m, b3t, b3tm, b3tl, 182 | a0, a1d, a1m, a1l, a2m, a2lm, a2m2, a2l, a2d, 183 | a3lm, a3m2, a3m, a3l, a3d; // intermediate calculations 184 | float32_t bass, mid, treble, gain; 185 | }; 186 | 187 | #endif // _FILTER_TONESTACK_F32_H_ 188 | -------------------------------------------------------------------------------- /Hx_InfinitePhaser_F32/InfinitePhaser_patch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hexeguitar/t40fx/115f81f2ddf35346e38e9b6b0a6d341aad9b2e76/Hx_InfinitePhaser_F32/InfinitePhaser_patch.png -------------------------------------------------------------------------------- /Hx_InfinitePhaser_F32/README.md: -------------------------------------------------------------------------------- 1 | ## Shepard/Barberpole Infinite phaser, F32 mono version for OpenAudio_ArduinoLibrary 2 | 3 | ![alt text][pic1] 4 | 5 | An interesting effect creating an illusion of infinite phasing up or down by running 6 parallel/interleaved 6 stage phaser units. 6 | Float32 mono version for use with [OpenAudio_ArduinoLibrary](https://github.com/chipaudette/OpenAudio_ArduinoLibrary "OpenAudio_ArduinoLibrary"). 7 | 8 | 9 | ### Modulation scalling: 10 | Instead of a common approach of using two LFO cotrols: Rate and Depth, where the modulation waveform oscillates around the middle of the scale, this phaser uses two parameters to control the depth and range of the modulation: **top** and **bottom** values. Input modulation signal will be scaled and shifted to operate in range between these two values. 11 | Experiments show the most useful LFO rate range is well below 1Hz, hence i've scaled the _rate_ parameter to range from -1.0f (phasing down) to 1.0f (phasing up). Internally it's limited to 0.25Hz and can be changeg in teh header file if desired: 12 | 13 | ```#define INFINITE_PHASER_MAX_LFO_HZ (0.25f) // maximum LFO rate range``` 14 | 15 | 16 | ### API: 17 | 18 | ```void lfo(float32_t rate, float32_t top, float32_t bottom);``` 19 | Controls the internal LFO frequency in scaled range from -1.0 to 1.0, top level (0.0 ... 1.0) and bottom level (0.0 ... 1.0) 20 | Example: 21 | ```phaser.lfo(0.5f, 0.0f, 1.0f); // forward 50% speed, full scale``` 22 | 23 | ```void lfo_rate(float32_t rate);``` 24 | Controls the internal LFO modulation rate. Use if only the speed update is required. 25 | Example: 26 | ```phaser.lfo_rate(-1.0f); // full reverse throttle``` 27 | 28 | ```void depth(float32_t top, float32_t bottom);``` 29 | Scales and offsets the modulation waveform (internal or external). 30 | Example: 31 | ```phaser.depth(0.4f, 0.8f); // modulation waveform between 0.4 and 0.8``` 32 | 33 | ```void depth_top(float32_t top);``` 34 | Individually set the dept top parameter, range 0.0f to 1.0f. 35 | 36 | ```void depth_btm(float32_t btm);``` 37 | Individually set the dept bottom parameter, range 0.0f to 1.0f. 38 | 39 | ```void feedback(float32_t value);``` 40 | Controls the amount of feedback, range 0.0f to 1.0f. 41 | Example: 42 | ```phaser.feedback(0.5f); // set the feedback to 0.5``` 43 | 44 | ```void mix(float32_t value);``` 45 | Dry / Wet mix ratio. Set to 0.5f for classic phaser sounds. 46 | Example: 47 | ```phaser.mix(0.3f); // dry = 0.7, wet = 0.3``` 48 | 49 | ```void stages(uint8_t st);``` 50 | Controls the number of phase shifter stagtes. Accepted values are: 2, 4, 6. The more stages the more resonant notches are produced. 51 | Example: 52 | ```phaser.stages(6); // 6 stage phaser``` 53 | 54 | ```void set_bypass(bool state);``` 55 | Disables (true) or enables (false) the phaser. 56 | Example: 57 | ```phaser.set_bypass(true); // disable the phaser (saves CPU load) ``` 58 | 59 | ```void tgl_bypass(void);``` 60 | Toggles the current bypass status. 61 | 62 | ```bool get_bypass(void);``` 63 | Returns the current bypass status. 64 | 65 | ### Sound example: 66 | 67 | [![Teensy4 InfinitePhaser_F32](https://img.youtube.com/vi/IxoAJXqS40E/0.jpg)](https://www.youtube.com/watch?v=IxoAJXqS40E) 68 | 69 | [pic1]: InfinitePhaser_patch.png "Internal structure" 70 | -------------------------------------------------------------------------------- /Hx_InfinitePhaser_F32/effect_infphaser_F32.cpp: -------------------------------------------------------------------------------- 1 | /* Mono Shephard/Barberpole Phaser/Vibrato effect for Teensy Audio library 2 | * 3 | * Author: Piotr Zapart 4 | * www.hexefx.com 5 | * 6 | * Copyright (c) 2021 by Piotr Zapart 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | #include "effect_infphaser_F32.h" 27 | 28 | // ---------------------------- INFINITE PHASER MODULATION ----------------------- 29 | #define INF_PHASER_STEP (0x100000000u / INFINITE_PHASER_PATHS) 30 | 31 | #define USE_TABLE 32 | 33 | 34 | AudioEffectInfinitePhaser_F32::AudioEffectInfinitePhaser_F32() : AudioStream_F32(1, inputQueueArray_f32) 35 | { 36 | memset(allpass_x, 0, INFINITE_PHASER_STAGES * INFINITE_PHASER_PATHS * sizeof(float32_t)); 37 | memset(allpass_y, 0, INFINITE_PHASER_STAGES * INFINITE_PHASER_PATHS * sizeof(float32_t)); 38 | bps = false; 39 | lfo_top = 1.0f; 40 | lfo_btm = 0.0f; 41 | lfo_phase_acc = 0; 42 | lfo_add = 0; 43 | feedb = 0.5f; // effect is hard noticable with low feedback settings, hence the range is limited to 0.5-0.999 44 | mix_ratio = 0.5f; // start with classic phaser sound 45 | stg = INFINITE_PHASER_STAGES; 46 | } 47 | AudioEffectInfinitePhaser_F32::~AudioEffectInfinitePhaser_F32() 48 | { 49 | } 50 | 51 | void AudioEffectInfinitePhaser_F32::update() 52 | { 53 | 54 | #if defined(__ARM_ARCH_7EM__) 55 | audio_block_f32_t *blockIn; 56 | uint16_t i = 0; 57 | float32_t modSig; 58 | uint32_t phaseAcc = lfo_phase_acc; 59 | int32_t phaseAdd = lfo_add; 60 | float32_t top = lfo_top; 61 | float32_t btm = lfo_btm; 62 | uint32_t phase_acc_local; 63 | uint32_t y0, y1; 64 | float32_t inSig, drySig, wetSig; 65 | float32_t fdb = feedb; 66 | float32_t ampl; 67 | 68 | blockIn = AudioStream_F32::receiveWritable_f32(0); // audio data 69 | 70 | if (!blockIn) 71 | { 72 | return; 73 | } 74 | if (bps) 75 | { 76 | AudioStream_F32::transmit(blockIn); 77 | AudioStream_F32::release(blockIn); 78 | return; 79 | } 80 | 81 | for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) 82 | { 83 | wetSig = 0.0f; 84 | drySig = blockIn->data[i] * (1.0f - fdb*0.25f); // attenuate the input if using feedback 85 | 86 | y1 = INFINITE_PHASER_PATHS; 87 | while (y1) 88 | { 89 | y1--; 90 | phase_acc_local = phaseAcc + y1*INF_PHASER_STEP; 91 | modSig = 1.0f - ((float32_t)phase_acc_local / 4294967295.0f); 92 | ampl = modSig * 2.0f; 93 | 94 | if (ampl > 1.0f) ampl = -2.0f * modSig + 2.0f; 95 | modSig = modSig*modSig * abs(top - btm) + min(top, btm); 96 | 97 | inSig = drySig + last_sample[y1] * fdb; 98 | y0 = stg; 99 | while (y0) // process allpass filters in pairs 100 | { 101 | y0--; 102 | allpass_y[y1][y0] = modSig * (allpass_y[y1][y0] + inSig) - allpass_x[y1][y0]; 103 | allpass_x[y1][y0] = inSig; 104 | y0--; 105 | allpass_y[y1][y0] = modSig * (allpass_y[y1][y0] + allpass_y[y1][y0+1]) - allpass_x[y1][y0]; 106 | allpass_x[y1][y0] = allpass_y[y1][y0+1]; 107 | inSig = allpass_y[y1][y0]; 108 | } 109 | last_sample[y1] = inSig; 110 | wetSig += ((drySig * (1.0f - mix_ratio) + inSig * mix_ratio)* ampl)/2.0f; 111 | } 112 | blockIn->data[i] = wetSig; 113 | 114 | phaseAcc += phaseAdd; 115 | } 116 | lfo_phase_acc = phaseAcc; 117 | AudioStream_F32::transmit(blockIn); 118 | AudioStream_F32::release(blockIn); 119 | #endif 120 | } 121 | 122 | -------------------------------------------------------------------------------- /Hx_InfinitePhaser_F32/effect_infphaser_F32.h: -------------------------------------------------------------------------------- 1 | /* Mono Shephard/Barberpole Phaser/Vibrato effect for Teensy Audio library 2 | * 3 | * Author: Piotr Zapart 4 | * www.hexefx.com 5 | * 6 | * Copyright (c) 2021 by Piotr Zapart 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | #ifndef _EFFECT_INFPHASER_F32_H 28 | #define _EFFECT_INFPHASER_F32_H 29 | 30 | #include 31 | #include "Audio.h" 32 | #include "AudioStream_F32.h" 33 | #include "arm_math.h" 34 | 35 | // ################ SHEPARD/BARBERPOLE INFINITE PHASER ################ 36 | #define INFINITE_PHASER_STAGES 6 37 | #define INFINITE_PHASER_PATHS 6 // 6 parallel paths 38 | 39 | #define INFINITE_PHASER_MAX_LFO_HZ (0.25f) // maximum LFO rate range 40 | 41 | class AudioEffectInfinitePhaser_F32 : public AudioStream_F32 42 | { 43 | public: 44 | AudioEffectInfinitePhaser_F32(); 45 | ~AudioEffectInfinitePhaser_F32(); 46 | virtual void update(); 47 | /** 48 | * @brief Scale and offset the modulation signal. 49 | * LFO will oscillate between these two max and min values. 50 | * 51 | * @param top top level of the LFO 52 | * @param bottom bottom level of the LFO 53 | */ 54 | void depth(float32_t top, float32_t bottom) 55 | { 56 | float32_t a, b; 57 | a = constrain(top, 0.0f, 1.0f); 58 | b = constrain(bottom, 0.0f, 1.0f); 59 | __disable_irq(); 60 | lfo_top = a; 61 | lfo_btm = b; 62 | __enable_irq(); 63 | } 64 | void depth_top(float32_t top) 65 | { 66 | float32_t a = constrain(top, 0.0f, 1.0f); 67 | __disable_irq(); 68 | lfo_top = a; 69 | __enable_irq(); 70 | } 71 | void depth_btm(float32_t btm) 72 | { 73 | float32_t a = constrain(btm, 0.0f, 1.0f); 74 | __disable_irq(); 75 | lfo_btm = a; 76 | __enable_irq(); 77 | } 78 | /** 79 | * @brief Controls the internal LFO. 80 | * Use this function to update all lfo parameteres at once 81 | * 82 | * @param rate scaled lfo frequency -1.0f to 1.0f, use 0.0f for manual phaser control 83 | * @param top lfo top level, range 0.0f to 1.0f 84 | * @param btm lfo bottm level, range 0.0f to 1.0f 85 | */ 86 | void lfo(float32_t rate, float32_t top, float32_t btm) 87 | { 88 | int32_t add; 89 | if (rate < 0.0f) rate = rate*rate*(-1.0f); 90 | else rate = rate*rate; 91 | top = constrain(top, 0.0f, 1.0f); 92 | btm = constrain(btm, 0.0f, 1.0f); 93 | add = rate * (4294967296.0 / AUDIO_SAMPLE_RATE_EXACT); 94 | __disable_irq(); 95 | lfo_top = top; 96 | lfo_btm = btm; 97 | lfo_add = add; 98 | __enable_irq(); 99 | } 100 | /** 101 | * @brief Set the rate of the internal LFO 102 | * 103 | * @param rate lfo frequency, use 0.0f for manual phaser control 104 | * Range -1.0f to 1.0f for reverse and forward modulation 105 | */ 106 | void lfo_rate(float32_t rate) 107 | { 108 | if (rate < 0.0f) rate = rate*rate*(-1.0f); 109 | else rate = rate*rate; 110 | int32_t add; 111 | rate = map(rate, -1.0f, 1.0f, -INFINITE_PHASER_MAX_LFO_HZ, INFINITE_PHASER_MAX_LFO_HZ); 112 | add = rate * (4294967296.0f / AUDIO_SAMPLE_RATE_EXACT); 113 | __disable_irq(); 114 | lfo_add = add; 115 | __enable_irq(); 116 | } 117 | /** 118 | * @brief Controls the feedback parameter 119 | * 120 | * @param fdb feedback value in range 0.0f to 1.0f 121 | */ 122 | void feedback(float32_t fdb) 123 | { 124 | fdb = map(fdb, 0.0f, 1.0f, 0.5f, 0.999f); 125 | __disable_irq(); 126 | feedb = fdb; 127 | __enable_irq(); 128 | } 129 | /** 130 | * @brief Dry / Wet mixer ratio. Classic Phaser sound uses 0.5f for 50% dry and 50%Wet 131 | * 1.0f will produce 100% wet signal craeting a vibrato effect 132 | * 133 | * @param ratio mixing ratio, range 0.0f (full dry) to 1.0f (full wet) 134 | */ 135 | void mix(float32_t ratio) 136 | { 137 | ratio = constrain(ratio, 0.0f, 1.0f); 138 | __disable_irq(); 139 | mix_ratio = ratio; 140 | __enable_irq(); 141 | } 142 | /** 143 | * @brief Sets the number of stages used in the phaser 144 | * Allowed values are: 2, 4, 6 145 | * 146 | * @param st number of stages, even value <= 6 147 | */ 148 | void stages(uint8_t st) 149 | { 150 | if (st && st == ((st >> 1) << 1) && st <= INFINITE_PHASER_STAGES) // only 2, 4, 6, 8, 12 allowed 151 | { 152 | __disable_irq(); 153 | stg = st; 154 | __enable_irq(); 155 | } 156 | } 157 | /** 158 | * @brief Use to bypass the effect (true) 159 | * 160 | * @param state true = bypass on, false = phaser on 161 | */ 162 | void set_bypass(bool state) {bps = state;} 163 | bool get_bypass(void) {return bps;} 164 | bool tgl_bypass(void) {bps ^= 1; return bps;} 165 | private: 166 | uint8_t stg; // number of stages 167 | bool bps; // bypass 168 | audio_block_f32_t *inputQueueArray_f32[1]; 169 | float32_t allpass_x[INFINITE_PHASER_PATHS][INFINITE_PHASER_STAGES]; // allpass inputs 170 | float32_t allpass_y[INFINITE_PHASER_PATHS][INFINITE_PHASER_STAGES]; // allpass outputs 171 | float32_t mix_ratio; // 0 = dry. 1.0 = wet 172 | float32_t feedb; // feedback 173 | float32_t last_sample[INFINITE_PHASER_PATHS]; 174 | uint32_t lfo_phase_acc; // interfnal lfo 175 | int32_t lfo_add; 176 | float32_t lfo_top; 177 | float32_t lfo_btm; 178 | }; 179 | 180 | #endif // _EFFECT_INFPHASER_H 181 | -------------------------------------------------------------------------------- /Hx_MonoToStereo_F32/README.md: -------------------------------------------------------------------------------- 1 | ## Mono to Stereo expander 2 | ### 32bit float version for [OpenAudio_ArduinoLibrary](https://github.com/chipaudette/OpenAudio_ArduinoLibrary "OpenAudio_ArduinoLibrary") 3 | 4 | ### Inputs: 5 | * mono in, _AudioConnection_F32_ type 6 | 7 | ### Outputs: 8 | * left out, _AudioConnection_F32_ type 9 | * right out, _AudioConnection_F32_ type 10 | 11 | ### Test patch: 12 | ![alt text][pic1] 13 | 14 | ### API: 15 | 16 | ```void setSpread(float32_t val);``` 17 | amount of stereo spread. Parameter range: 0.0f to 1.0f. 18 | Example: 19 | ```monoToStereo.setSpread(1.0f); // apply max stereo spread ``` 20 | 21 | ```void setPan(float32_t val);``` 22 | panorama setting. Parameter range: -1.0f (max left) to 1.0f (max right), 0.0f = centre. 23 | Example: 24 | ```monoToStereo.setPan(-0.5f); // set the panorama to 25% left ``` 25 | 26 | ```void setBypass(bool state);``` 27 | bypass setting: _false_ = effect **ON**, _true_ = effect **OFF** 28 | 29 | ```void tglBypass(void);``` 30 | toggles the bypass setting. 31 | 32 | ```bool getBypass(void);``` 33 | returns the current bypass setting. 34 | 35 | ### Sound example: 36 | 37 | [![Teensy4 monoToStereo_F32](https://img.youtube.com/vi/y2SUNxpsVs0/0.jpg)](https://www.youtube.com/watch?v=y2SUNxpsVs0) 38 | 39 | 40 | 41 | [pic1]: monoToStereo_F32_testPatch.png "Test patch" 42 | -------------------------------------------------------------------------------- /Hx_MonoToStereo_F32/effect_monoToStereo_F32.cpp: -------------------------------------------------------------------------------- 1 | /* mono to stereo expander for Teensy 4 2 | * 32bit float version for OpenAudio_ArduinoLibrary: 3 | * https://github.com/chipaudette/OpenAudio_ArduinoLibrary 4 | * 5 | * Author: Piotr Zapart 6 | * www.hexefx.com 7 | * 8 | * Copyright (c) 2021 by Piotr Zapart 9 | * 10 | * Development of this audio library was funded by PJRC.COM, LLC by sales of 11 | * Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop 12 | * open source software by purchasing Teensy or other PJRC products. 13 | * 14 | * Permission is hereby granted, free of charge, to any person obtaining a copy 15 | * of this software and associated documentation files (the "Software"), to deal 16 | * in the Software without restriction, including without limitation the rights 17 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | * copies of the Software, and to permit persons to whom the Software is 19 | * furnished to do so, subject to the following conditions: 20 | * 21 | * The above copyright notice, development funding notice, and this permission 22 | * notice shall be included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 30 | * THE SOFTWARE. 31 | */ 32 | #include "effect_monoToStereo_F32.h" 33 | 34 | const float32_t allpass_k_table[ALLP_NETWORK_LEN] = 35 | { 36 | -0.9823311567306519f, -0.9838343858718872f, -0.9838343858718872f, 37 | -0.9843953251838684f, -0.9843953251838684f, -0.9850407838821411f, 38 | -0.9850407838821411f, -0.9856590032577515f, -0.9856590032577515f, 39 | -0.9868955612182617f, -0.9868955612182617f, -0.9894036054611206f, 40 | -0.9894036054611206f, -0.9902338981628418f, -0.9902338981628418f, 41 | -0.9911531209945679f, -0.9911531209945679f, -0.9911531209945679f, 42 | -0.9911531209945679f, -0.9928167462348938f, -0.9928167462348938f 43 | }; 44 | 45 | AudioEffectMonoToStereo_F32::AudioEffectMonoToStereo_F32() : AudioStream_F32(1, inputQueueArray_f32) 46 | { 47 | pancos = 1.0f; 48 | pansin= 0.0f; 49 | width = 0.0f; 50 | bypass = false; 51 | } 52 | AudioEffectMonoToStereo_F32::~AudioEffectMonoToStereo_F32() 53 | { 54 | } 55 | 56 | void AudioEffectMonoToStereo_F32::update() 57 | { 58 | #if defined(__ARM_ARCH_7EM__) 59 | 60 | audio_block_f32_t *blockIn; 61 | uint16_t i; 62 | float32_t _width = width; 63 | float32_t _pancos = pancos; 64 | float32_t _pansin = pansin; 65 | float32_t allp1Out, allp2Out, stereoL, stereoR; 66 | 67 | blockIn = AudioStream_F32::receiveReadOnly_f32(0); 68 | if (!blockIn) return; 69 | 70 | audio_block_f32_t *blockOutL = AudioStream_F32::allocate_f32(); 71 | audio_block_f32_t *blockOutR = AudioStream_F32::allocate_f32(); 72 | if (!blockOutL || !blockOutR) 73 | { 74 | 75 | if (blockOutL) 76 | AudioStream_F32::release(blockOutL); 77 | if (blockOutR) 78 | AudioStream_F32::release(blockOutR); 79 | return; 80 | } 81 | if (bypass) 82 | { 83 | AudioStream_F32::transmit(blockIn, 0); // transmit input on both 84 | AudioStream_F32::transmit(blockIn, 1); // out channels 85 | AudioStream_F32::release(blockIn); 86 | AudioStream_F32::release(blockOutL); 87 | AudioStream_F32::release(blockOutR); 88 | return; 89 | } 90 | for (i = 0; i < blockIn->length; i++) 91 | { 92 | allp1Out = do_allp_netw(blockIn->data[i], allpass_netw_1x, allpass_netw_1y); 93 | allp2Out = do_allp_netw(allp1Out, allpass_netw_2x, allpass_netw_2y); 94 | stereoL = blockIn->data[i] * _width + allp1Out; 95 | stereoR = allp1Out - (allp2Out * _width); 96 | blockOutL->data[i] = (stereoL * _pancos) + (stereoR * _pansin); 97 | blockOutR->data[i] = (stereoR * _pancos) - (stereoL * _pansin); 98 | } 99 | AudioStream_F32::transmit(blockOutL, 0); 100 | AudioStream_F32::transmit(blockOutR, 1); 101 | AudioStream_F32::release(blockOutL); 102 | AudioStream_F32::release(blockOutR); 103 | AudioStream_F32::release(blockIn); 104 | 105 | #endif 106 | } 107 | 108 | 109 | // y[n] = c*x[n] + x[n-1] - c*y[n-1] 110 | // y[n] = c*(x[n] - y[n-1]) + x[n-1] 111 | // c = (tan(pi*fc/fs)-1) / (tan(pi*fc/fs)+1) 112 | float32_t AudioEffectMonoToStereo_F32::do_allp_netw(float32_t inSig, float32_t *x, float32_t *y) 113 | { 114 | uint32_t stg = ALLP_NETWORK_LEN; 115 | while (stg) 116 | { 117 | stg--; 118 | y[stg] = allpass_k_table[stg] * (inSig - y[stg]) + x[stg]; 119 | x[stg] = inSig; 120 | inSig = y[stg]; 121 | stg--; 122 | y[stg] = allpass_k_table[stg] * (inSig - y[stg]) + x[stg]; 123 | x[stg] = inSig; 124 | inSig = y[stg]; 125 | stg--; 126 | y[stg] = allpass_k_table[stg] * (inSig - y[stg]) + x[stg]; 127 | x[stg] = inSig; 128 | inSig = y[stg]; 129 | } 130 | return y[0]; 131 | } -------------------------------------------------------------------------------- /Hx_MonoToStereo_F32/effect_monoToStereo_F32.h: -------------------------------------------------------------------------------- 1 | /* mono to stereo expander for Teensy 4 2 | * 32bit float version for OpenAudio_ArduinoLibrary: 3 | * https://github.com/chipaudette/OpenAudio_ArduinoLibrary 4 | * 5 | * Author: Piotr Zapart 6 | * www.hexefx.com 7 | * 8 | * Copyright (c) 2021 by Piotr Zapart 9 | * 10 | * Development of this audio library was funded by PJRC.COM, LLC by sales of 11 | * Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop 12 | * open source software by purchasing Teensy or other PJRC products. 13 | * 14 | * Permission is hereby granted, free of charge, to any person obtaining a copy 15 | * of this software and associated documentation files (the "Software"), to deal 16 | * in the Software without restriction, including without limitation the rights 17 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | * copies of the Software, and to permit persons to whom the Software is 19 | * furnished to do so, subject to the following conditions: 20 | * 21 | * The above copyright notice, development funding notice, and this permission 22 | * notice shall be included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 30 | * THE SOFTWARE. 31 | */ 32 | #ifndef _EFFECT_MONOTOSTEREO_F32_H 33 | #define _EFFECT_MONOTOSTEREO_F32_H 34 | 35 | #include 36 | #include "AudioStream_F32.h" 37 | #include "arm_math.h" 38 | 39 | #define ALLP_NETWORK_LEN 21 40 | 41 | class AudioEffectMonoToStereo_F32 : public AudioStream_F32 42 | { 43 | public: 44 | AudioEffectMonoToStereo_F32(); 45 | ~AudioEffectMonoToStereo_F32(); 46 | virtual void update(); 47 | void setSpread(float32_t val) 48 | { 49 | val = constrain(val, 0.0f, 1.0f); 50 | __disable_irq(); 51 | width = val; 52 | __enable_irq(); 53 | } 54 | void setPan(float32_t val) 55 | { 56 | float32_t a, b; 57 | val = constrain(val, -1.0f, 1.0f); 58 | a = map(val, -1.0f, 1.0f, -0.707f, 0.707f); 59 | b = 1.0f - abs(val*0.293f); 60 | __disable_irq(); 61 | pansin = a; 62 | pancos = b; 63 | __enable_irq(); 64 | } 65 | void setBypass(bool state) {bypass = state;} 66 | void tglBypass(void) {bypass ^= 1;} 67 | bool getBypass(void) { return bypass;} 68 | private: 69 | bool bypass; 70 | float32_t width; 71 | float32_t pancos, pansin; 72 | float32_t do_allp_netw(float32_t inSig, float32_t *x, float32_t *y); 73 | float32_t allpass_netw_1x[ALLP_NETWORK_LEN]; 74 | float32_t allpass_netw_1y[ALLP_NETWORK_LEN]; 75 | float32_t allpass_netw_2x[ALLP_NETWORK_LEN]; 76 | float32_t allpass_netw_2y[ALLP_NETWORK_LEN]; 77 | 78 | audio_block_f32_t *inputQueueArray_f32[1]; 79 | }; 80 | 81 | #endif // _EFFECT_MONOTOSTEREO_F32_H 82 | -------------------------------------------------------------------------------- /Hx_MonoToStereo_F32/monoToStereo_F32_testPatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hexeguitar/t40fx/115f81f2ddf35346e38e9b6b0a6d341aad9b2e76/Hx_MonoToStereo_F32/monoToStereo_F32_testPatch.png -------------------------------------------------------------------------------- /Hx_Phaser/README.md: -------------------------------------------------------------------------------- 1 | ## Mono 12 stage Phaser effect 2 | 3 | ![alt text][pic1] 4 | 5 | ### Modulation sources: 6 | * internal LFO, switched to if there is no modulation input signal provided (input index [1]). Internal LFO uses a hyperbolic waveform to produce smooth transistion over the whole frequency spectrum. 7 | * external modulation signal fed into input [1]. Range has to be of int16_t (-32768 ... 32767) to cover the whole range. 8 | 9 | ### Modulation scalling: 10 | Instead of a common approach of using two LFO cotrols: Rate and Depth, where the modulation waveform oscillates around the middle of the scale, this phaser uses two parameters to control the depth and range of the modulation: **top** and **bottom** values. Input modulation signal will be scaled and shifted to operate in range between these two values. 11 | 12 | ### API: 13 | 14 | ```void lfo(float32_t f_Hz, float32_t top, float32_t bottom);``` 15 | Controls the internal LFO: frequency in Hz, top level (0.0 ... 1.0) and bottom level (0.0 ... 1.0) 16 | Example: 17 | ```phaser.lfo(0.5f, 0.0f, 1.0f); // 0.5Hz, full scale``` 18 | 19 | ```void lfo_rate(float32_t f_Hz);``` 20 | Controls the internal LFO frequency. Use if only the frequency update is required. 21 | Example: 22 | ```phaser.lfo_rate(1.6f); // internal LFO: 1.6Hz``` 23 | 24 | ```void depth(float32_t top, float32_t bottom);``` 25 | Scales and offsets the modulation waveform (internal or external). 26 | Example: 27 | ```phaser.depth(0.4f, 0.8f); // modulation waveform between 0.4 and 0.8``` 28 | 29 | ```void feedback(float32_t value);``` 30 | Controls the amount of feedback, range 0.0f to 1.0f. 31 | Example: 32 | ```phaser.feedback(0.5f); // set the feedback to 0.5``` 33 | 34 | ```void mix(float32_t value);``` 35 | Dry / Wet mix ratio. Set to 0.5f for classic phaser sounds. Setting the feedback to 0.0 and the mix to 1.0 (full wet) will produce a vibrato effect. 36 | Example: 37 | ```phaser.mix(0.3f); // dry = 0.7, wet = 0.3``` 38 | 39 | ```void stages(uint8_t st);``` 40 | Controls the number of phase shifter stagtes. Accepted values are: 2, 4, 6, 8, 10, 12. The more stages the more resonant notches are produced. 41 | Example: 42 | ```phaser.stages(6); // 6 stage phaser``` 43 | 44 | ```void set_bypass(bool state);``` 45 | Disables (true) or enables (false) the phaser. 46 | Example: 47 | ```phaser.set_bypass(true); // disable the phaser (saves CPU load) ``` 48 | 49 | ```void tgl_bypass(void);``` 50 | Toggles the current bypass status. 51 | 52 | ```bool get_bypass(void);``` 53 | Returns the current bypass status. 54 | 55 | ### Sound sample: 56 | https://soundcloud.com/hexeguitar/teensy-audio-12-stage-phaser 57 | 58 | [pic1]: phaser_internal.png "Internal structure" -------------------------------------------------------------------------------- /Hx_Phaser/effect_phaser.cpp: -------------------------------------------------------------------------------- 1 | /* Mono Phaser/Vibrato effect for Teensy Audio library 2 | * 3 | * Author: Piotr Zapart 4 | * www.hexefx.com 5 | * 6 | * Copyright (c) 2021 by Piotr Zapart 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | #include 28 | #include "effect_phaser.h" 29 | 30 | // ---------------------------- INTERNAL LFO ------------------------------------- 31 | #define LFO_LUT_BITS 8 32 | #define LFO_MAX_F (AUDIO_SAMPLE_RATE_EXACT / 2.0f) 33 | #define LFO_INTERP_INT_SHIFT (32-LFO_LUT_BITS) 34 | #define LFO_INTERP_FRACT_MASK ((1< use internal LFO 113 | if (bps) 114 | { 115 | transmit((audio_block_t *)blockIn); 116 | release((audio_block_t *)blockIn); 117 | if (blockMod) release((audio_block_t *)blockMod); 118 | return; 119 | } 120 | arm_q15_to_float((q15_t *)blockIn->data, bf_f32, AUDIO_BLOCK_SAMPLES); 121 | 122 | for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) 123 | { 124 | if(internalLFO) 125 | { 126 | uint32_t LUTaddr = phaseAcc >> LFO_INTERP_INT_SHIFT; //8 bit address 127 | fract = phaseAcc & LFO_INTERP_FRACT_MASK; // fractional part mask 128 | y0 = AudioWaveformHyperTri[LUTaddr]; 129 | y1 = AudioWaveformHyperTri[LUTaddr+1]; 130 | y = ((int64_t) y0 * (LFO_INTERP_FRACT_MASK - fract)); 131 | y += ((int64_t) y1 * (fract)); 132 | modSig = (float32_t)(y>>LFO_INTERP_INT_SHIFT) / 65535.0f; 133 | phaseAcc += phaseAdd; 134 | } 135 | else 136 | { 137 | modSig = ((float32_t)blockMod->data[i] + 32768.0f) / 65535.0f; // mod signal is 0.0 to 1.0 138 | } 139 | // apply scale/offset to the modulation wave 140 | modSig = modSig * abs(top - btm) + min(top, btm); 141 | drySig = bf_f32[i] * (1.0f - fdb*0.25f); // attenuate the input is using feedback 142 | inSig = drySig + last_sample * fdb; 143 | y0 = stg; 144 | while (y0) // process allpass filters in pairs 145 | { 146 | y0--; 147 | allpass_y[y0] = modSig * (allpass_y[y0] + inSig) - allpass_x[y0]; 148 | allpass_x[y0] = inSig; 149 | y0--; 150 | allpass_y[y0] = modSig * (allpass_y[y0] + allpass_y[y0+1]) - allpass_x[y0]; 151 | allpass_x[y0] = allpass_y[y0+1]; 152 | inSig = allpass_y[y0]; 153 | } 154 | last_sample = inSig; 155 | drySig = drySig * (1.0f - mix_ratio) + last_sample * mix_ratio; // dry/wet mixer 156 | blockIn->data[i] = (int16_t)(drySig * 32767.0f); 157 | } 158 | lfo_phase_acc = phaseAcc; 159 | transmit(blockIn); 160 | release(blockIn); 161 | if (blockMod) release((audio_block_t *)blockMod); 162 | #endif 163 | 164 | 165 | } 166 | -------------------------------------------------------------------------------- /Hx_Phaser/effect_phaser.h: -------------------------------------------------------------------------------- 1 | /* Mono Phaser/Vibrato effect for Teensy Audio library 2 | * 3 | * Author: Piotr Zapart 4 | * www.hexefx.com 5 | * 6 | * Copyright (c) 2021 by Piotr Zapart 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | #ifndef _EFFECT_PHASER_H 28 | #define _EFFECT_PHASER_H 29 | 30 | #include 31 | #include "Audio.h" 32 | #include "AudioStream.h" 33 | #include "arm_math.h" 34 | 35 | #define PHASER_STEREO_STAGES 12 36 | 37 | class AudioEffectPhaser : public AudioStream 38 | { 39 | public: 40 | AudioEffectPhaser(); 41 | ~AudioEffectPhaser(); 42 | virtual void update(); 43 | 44 | /** 45 | * @brief Scale and offset the modulation signal. It can be the internal LFO 46 | * or the incomig routed modulation AudioSignal. 47 | * LFO will oscillate between these two max and min values. 48 | * 49 | * @param top top level of the LFO 50 | * @param bottom bottom level of the LFO 51 | */ 52 | void depth(float32_t top, float32_t bottom) 53 | { 54 | float32_t a, b; 55 | a = constrain(top, 0.0f, 1.0f); 56 | b = constrain(bottom, 0.0f, 1.0f); 57 | __disable_irq(); 58 | lfo_top = a; 59 | lfo_btm = b; 60 | __enable_irq(); 61 | } 62 | /** 63 | * @brief Controls the internal LFO, or if a control signal is used, scales it 64 | * Use this function to update all lfo parameteres at once 65 | * 66 | * @param f_Hz lfo frequency, use 0.0f for manual phaser control 67 | * @param top lfo top level 68 | * @param btm lfo bottm level 69 | */ 70 | void lfo(float32_t f_Hz, float32_t top, float32_t btm) 71 | { 72 | float32_t a, b, c; 73 | uint32_t add; 74 | a = constrain(top, 0.0f, 1.0f); 75 | b = constrain(btm, 0.0f, 1.0f); 76 | c = constrain(f_Hz, 0.0f, AUDIO_SAMPLE_RATE_EXACT/2); 77 | add = c * (4294967296.0 / AUDIO_SAMPLE_RATE_EXACT); 78 | __disable_irq(); 79 | lfo_top = a; 80 | lfo_btm = b; 81 | lfo_add = add; 82 | __enable_irq(); 83 | } 84 | /** 85 | * @brief Set the rate of the internal LFO 86 | * 87 | * @param f_Hz lfo frequency, use 0.0f for manual phaser control 88 | */ 89 | void lfo_rate(float32_t f_Hz) 90 | { 91 | float32_t c; 92 | uint32_t add; 93 | c = constrain(f_Hz, 0.0f, AUDIO_SAMPLE_RATE_EXACT/2); 94 | add = c * (4294967296.0 / AUDIO_SAMPLE_RATE_EXACT); 95 | __disable_irq(); 96 | lfo_add = add; 97 | __enable_irq(); 98 | } 99 | /** 100 | * @brief Controls the feedback parameter 101 | * 102 | * @param fdb ffedback value in range 0.0f to 1.0f 103 | */ 104 | void feedback(float32_t fdb) 105 | { 106 | feedb = constrain(fdb, 0.0f, 1.0f); 107 | } 108 | /** 109 | * @brief Dry / Wet mixer ratio. Classic Phaser sound uses 0.5f for 50% dry and 50%Wet 110 | * 1.0f will produce 100% wet signal craeting a vibrato effect 111 | * 112 | * @param ratio mixing ratio, range 0.0f (full dry) to 1.0f (full wet) 113 | */ 114 | void mix(float32_t ratio) 115 | { 116 | mix_ratio = constrain(ratio, 0.0f, 1.0f); 117 | } 118 | /** 119 | * @brief Sets the number of stages used in the phaser 120 | * Allowed values are: 2, 4, 6, 8, 10, 12 121 | * 122 | * @param st number of stages, even value <= 12 123 | */ 124 | void stages(uint8_t st) 125 | { 126 | if (st && st == ((st >> 1) << 1) && st <= PHASER_STEREO_STAGES) // only 2, 4, 6, 8, 12 allowed 127 | { 128 | stg = st; 129 | } 130 | } 131 | /** 132 | * @brief Use to bypass the effect (true) 133 | * 134 | * @param state true = bypass on, false = phaser on 135 | */ 136 | void bypass(bool state) {bps = state;} 137 | void tgl_bypass(void) {bps ^= 1;} 138 | 139 | private: 140 | uint8_t stg; // number of stages 141 | bool bps; // bypass 142 | audio_block_t *inputQueueArray[2]; 143 | float32_t allpass_x[PHASER_STEREO_STAGES]; // allpass inputs 144 | float32_t allpass_y[PHASER_STEREO_STAGES]; // allpass outputs 145 | float32_t mix_ratio; // 0 = dry. 1.0 = wet 146 | float32_t feedb; // feedback 147 | float32_t last_sample; 148 | uint32_t lfo_phase_acc; // interfnal lfo 149 | uint32_t lfo_add; 150 | float32_t lfo_top; 151 | float32_t lfo_btm; 152 | }; 153 | 154 | #endif // _EFFECT_PHASER_H 155 | -------------------------------------------------------------------------------- /Hx_Phaser/phaser_internal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hexeguitar/t40fx/115f81f2ddf35346e38e9b6b0a6d341aad9b2e76/Hx_Phaser/phaser_internal.png -------------------------------------------------------------------------------- /Hx_PlateReverb/Hx_PlateReverb.ino: -------------------------------------------------------------------------------- 1 | /** 2 | Example project for the Stereo Plate reverb audio component 3 | (c) 31.12.2020 by Piotr Zapart www-hexefx.com 4 | 5 | Attention!!! Works with Teensy 4.x only! 6 | 7 | The audio path follows a typical scheme used in mixing consoles 8 | where the reverb is put into aux path. 9 | Each source (like i2s or PlaySDWav etc) has a Reverb Send Level control 10 | The Stereo reverb output is mixed then with the dry signals using the output mixers. 11 | 12 | 13 | */ 14 | 15 | #include 16 | #include "Audio.h" 17 | #include "effect_platervbstereo.h" 18 | 19 | #define I2S_REVERB_SEND_CH 0 20 | #define SDWAV_REVERB_SEND_CH 1 21 | #define REVERB_MIX_CH 1 22 | #define I2S_MIX_CH 0 23 | 24 | AudioPlaySdWav playSdWav; 25 | AudioInputI2S i2s_in; 26 | AudioMixer4 reverb_send_L; 27 | AudioMixer4 reverb_send_R; 28 | AudioEffectPlateReverb reverb; 29 | AudioMixer4 mixer_out_L; 30 | AudioMixer4 mixer_out_R; 31 | AudioOutputI2S i2s_out; 32 | 33 | AudioConnection patchCord1(playSdWav, 0, reverb_send_L, 1); // wav player L reverb send 34 | AudioConnection patchCord2(playSdWav, 0, mixer_out_L, 2); // wav player L into output mixer 35 | AudioConnection patchCord3(playSdWav, 1, reverb_send_R, 1); // wav player R reverb send 36 | AudioConnection patchCord4(playSdWav, 1, mixer_out_R, 2); // wav player R into output mixer 37 | 38 | AudioConnection patchCord5(i2s_in, 0, mixer_out_L, 0); // i2s out L into output mixer 39 | AudioConnection patchCord6(i2s_in, 1, mixer_out_R, 0); // i2s out R into output mixer 40 | 41 | AudioConnection patchCord7(i2s_in, 0, reverb_send_L, 0); // i2s out reverb send L 42 | AudioConnection patchCord8(i2s_in, 1, reverb_send_R, 0); // i2s out reverb send R 43 | 44 | AudioConnection patchCord9(reverb_send_L, 0, reverb, 0); // reverb inputs 45 | AudioConnection patchCord10(reverb_send_R, 0, reverb, 1); 46 | 47 | AudioConnection patchCord11(reverb, 0, mixer_out_L, 1); // reverb out into output mixer 48 | AudioConnection patchCord12(reverb, 1, mixer_out_R, 1); 49 | 50 | AudioConnection patchCord13(mixer_out_L, 0, i2s_out, 0); // output mixers -> codec DAC 51 | AudioConnection patchCord14(mixer_out_R, 0, i2s_out, 1); 52 | 53 | AudioControlSGTL5000 codec; 54 | 55 | 56 | uint32_t timeLast = 0, timeNow = 0; 57 | 58 | void flexRamInfo(void); 59 | void i2s_set_rev_send(float32_t lvl); 60 | void reverb_set_volume(float32_t lvl); 61 | void wav_set_rev_send(float32_t lvl); 62 | 63 | void setup() 64 | { 65 | Serial.begin(115200); 66 | //while(!Serial); 67 | delay(1000); 68 | Serial.println("--------------------------"); 69 | Serial.println("T40_GFX - stereo plate reverb"); 70 | #ifdef REVERB_USE_DMAMEM 71 | Serial.println("DMAMEM is used for reverb buffers"); 72 | #endif 73 | AudioMemory(12); 74 | codec.enable(); 75 | codec.volume(0.0); // headphones not used 76 | codec.inputSelect(AUDIO_INPUT_LINEIN); 77 | codec.lineInLevel(2); 78 | codec.lineOutLevel(31); 79 | flexRamInfo(); 80 | 81 | i2s_set_rev_send(0.7); 82 | reverb_set_volume(0.6); 83 | 84 | reverb.size(1.0); // max reverb length 85 | reverb.lowpass(0.3); // sets the reverb master lowpass filter 86 | reverb.lodamp(0.1); // amount of low end loss in the reverb tail 87 | reverb.hidamp(0.2); // amount of treble loss in the reverb tail 88 | reverb.diffusion(1.0); // 1.0 is the detault setting, lower it to create more "echoey" reverb 89 | 90 | } 91 | 92 | void loop() 93 | { 94 | timeNow = millis(); 95 | if (timeNow - timeLast > 1000) 96 | { 97 | Serial.print("Reverb CPU load = "); 98 | Serial.println(reverb.processorUsageMax()); 99 | timeLast = timeNow; 100 | } 101 | 102 | } 103 | 104 | 105 | void flexRamInfo(void) 106 | { // credit to FrankB, KurtE and defragster ! 107 | #if defined(__IMXRT1052__) || defined(__IMXRT1062__) 108 | int itcm = 0; 109 | int dtcm = 0; 110 | int ocram = 0; 111 | Serial.print("FlexRAM-Banks: ["); 112 | for (int i = 15; i >= 0; i--) 113 | { 114 | switch ((IOMUXC_GPR_GPR17 >> (i * 2)) & 0b11) 115 | { 116 | case 0b00: 117 | Serial.print("."); 118 | break; 119 | case 0b01: 120 | Serial.print("O"); 121 | ocram++; 122 | break; 123 | case 0b10: 124 | Serial.print("D"); 125 | dtcm++; 126 | break; 127 | case 0b11: 128 | Serial.print("I"); 129 | itcm++; 130 | break; 131 | } 132 | } 133 | Serial.print("] ITCM: "); 134 | Serial.print(itcm * 32); 135 | Serial.print(" KB, DTCM: "); 136 | Serial.print(dtcm * 32); 137 | Serial.print(" KB, OCRAM: "); 138 | Serial.print(ocram * 32); 139 | #if defined(__IMXRT1062__) 140 | Serial.print("(+512)"); 141 | #endif 142 | Serial.println(" KB"); 143 | extern unsigned long _stext; 144 | extern unsigned long _etext; 145 | extern unsigned long _sdata; 146 | extern unsigned long _ebss; 147 | extern unsigned long _flashimagelen; 148 | extern unsigned long _heap_start; 149 | 150 | Serial.println("MEM (static usage):"); 151 | Serial.println("RAM1:"); 152 | 153 | Serial.print("ITCM = FASTRUN: "); 154 | Serial.print((unsigned)&_etext - (unsigned)&_stext); 155 | Serial.print(" "); 156 | Serial.print((float)((unsigned)&_etext - (unsigned)&_stext) / ((float)itcm * 32768.0) * 100.0); 157 | Serial.print("% of "); 158 | Serial.print(itcm * 32); 159 | Serial.print("kb "); 160 | Serial.print(" ("); 161 | Serial.print(itcm * 32768 - ((unsigned)&_etext - (unsigned)&_stext)); 162 | Serial.println(" Bytes free)"); 163 | 164 | Serial.print("DTCM = Variables: "); 165 | Serial.print((unsigned)&_ebss - (unsigned)&_sdata); 166 | Serial.print(" "); 167 | Serial.print((float)((unsigned)&_ebss - (unsigned)&_sdata) / ((float)dtcm * 32768.0) * 100.0); 168 | Serial.print("% of "); 169 | Serial.print(dtcm * 32); 170 | Serial.print("kb "); 171 | Serial.print(" ("); 172 | Serial.print(dtcm * 32768 - ((unsigned)&_ebss - (unsigned)&_sdata)); 173 | Serial.println(" Bytes free)"); 174 | 175 | Serial.println("RAM2:"); 176 | Serial.print("OCRAM = DMAMEM: "); 177 | Serial.print((unsigned)&_heap_start - 0x20200000); 178 | Serial.print(" "); 179 | Serial.print((float)((unsigned)&_heap_start - 0x20200000) / ((float)512 * 1024.0) * 100.0); 180 | Serial.print("% of "); 181 | Serial.print(512); 182 | Serial.print("kb"); 183 | Serial.print(" ("); 184 | Serial.print(512 * 1024 - ((unsigned)&_heap_start - 0x20200000)); 185 | Serial.println(" Bytes free)"); 186 | 187 | Serial.print("FLASH: "); 188 | Serial.print((unsigned)&_flashimagelen); 189 | Serial.print(" "); 190 | Serial.print(((unsigned)&_flashimagelen) / (2048.0 * 1024.0) * 100.0); 191 | Serial.print("% of "); 192 | Serial.print(2048); 193 | Serial.print("kb"); 194 | Serial.print(" ("); 195 | Serial.print(2048 * 1024 - ((unsigned)&_flashimagelen)); 196 | Serial.println(" Bytes free)"); 197 | 198 | #endif 199 | } 200 | 201 | void i2s_set_rev_send(float32_t lvl) 202 | { 203 | lvl = constrain(lvl, 0.0, 1.0); 204 | reverb_send_L.gain(I2S_REVERB_SEND_CH, lvl); 205 | reverb_send_R.gain(I2S_REVERB_SEND_CH, lvl); 206 | } 207 | 208 | 209 | void reverb_set_volume(float32_t lvl) 210 | { 211 | lvl = constrain(lvl, 0.0, 1.0); 212 | mixer_out_L.gain(REVERB_MIX_CH, lvl); 213 | mixer_out_R.gain(REVERB_MIX_CH, lvl); 214 | } 215 | 216 | void wav_set_rev_send(float32_t lvl) 217 | { 218 | lvl = constrain(lvl, 0.0, 1.0); 219 | reverb_send_L.gain(SDWAV_REVERB_SEND_CH, lvl); 220 | reverb_send_R.gain(SDWAV_REVERB_SEND_CH, lvl); 221 | } 222 | -------------------------------------------------------------------------------- /Hx_PlateReverb/README.md: -------------------------------------------------------------------------------- 1 | ## Stereo Plate Reverb 2 | Fully stereo in/out reverb component for the standard 16bit Audio library. 3 | 4 | ### Connections: 5 | Reverb requires stereo in and out connenctions. 6 | ### API: 7 | 8 | ```void size(float32_t n);``` 9 | sets the reverb time. Parameter range: 0.0 to 1.0. 10 | Example: 11 | ```reverb.size(1.0f); // set the reverb time to maximum``` 12 | 13 | 14 | ```void lowpass(float32_t n);``` 15 | sets the reverb master lowpass filter. Parameter range: 0.0 to 1.0. 16 | Example: 17 | ```reverb.lowpass(0.7f); // darken the reverb sound``` 18 | 19 | 20 | ```void hidamp(float32_t n);``` 21 | sets the treble loss. Parameter range: 0.0 to 1.0. 22 | Example: 23 | ```reverb.hidamp(1.0f); // max hi band dampening results in darker sound ``` 24 | 25 | 26 | ```void lodamp(float32_t n);``` 27 | sets the bass cut. Parameter range: 0.0 to 1.0. 28 | Example: 29 | ```reverb.lodamp(0.5f); // cut more bass in the reverb tail to make the sound brighter ``` 30 | 31 | ```void set_bypass(bool state);``` 32 | Disables (true) or enables (false) the reverb engine. 33 | Example: 34 | ```reverb.set_bypass(true); // disable the reverb (saves CPU load) ``` 35 | 36 | ```bool get_bypass(void);``` 37 | Returns the current reverb bypass status. 38 | 39 | ```void tgl_bypass(void);``` 40 | Toggles the current reverb bypass status. 41 | 42 | Audio connections used in the example project: 43 | ![alt text][pic1] 44 | 45 | ### Additional config: 46 | 47 | by default the reverb places it's buffers into OCRAM/DMAMEM region. 48 | Comment out the 49 | ```#define REVERB_USE_DMAMEM``` 50 | line in the ```effect_platervbstereo.h``` file to place the variables into the DCTM ram region. 51 | 52 | [pic1]: StereoPlateReverb.png "Stereo plate reverb connections" -------------------------------------------------------------------------------- /Hx_PlateReverb/StereoPlateReverb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hexeguitar/t40fx/115f81f2ddf35346e38e9b6b0a6d341aad9b2e76/Hx_PlateReverb/StereoPlateReverb.png -------------------------------------------------------------------------------- /Hx_PlateReverb/effect_platervbstereo.cpp: -------------------------------------------------------------------------------- 1 | /* Stereo plate reverb for Teensy 4 2 | * 3 | * Author: Piotr Zapart 4 | * www.hexefx.com 5 | * 6 | * Copyright (c) 2020 by Piotr Zapart 7 | * 8 | * Development of this audio library was funded by PJRC.COM, LLC by sales of 9 | * Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop 10 | * open source software by purchasing Teensy or other PJRC products. 11 | * 12 | * Permission is hereby granted, free of charge, to any person obtaining a copy 13 | * of this software and associated documentation files (the "Software"), to deal 14 | * in the Software without restriction, including without limitation the rights 15 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | * copies of the Software, and to permit persons to whom the Software is 17 | * furnished to do so, subject to the following conditions: 18 | * 19 | * The above copyright notice, development funding notice, and this permission 20 | * notice shall be included in all copies or substantial portions of the Software. 21 | * 22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 28 | * THE SOFTWARE. 29 | */ 30 | 31 | 32 | #include 33 | #include "effect_platervbstereo.h" 34 | 35 | #define INP_ALLP_COEFF (0.65f) // default input allpass coeff 36 | #define LOOP_ALLOP_COEFF (0.65f) // default loop allpass coeff 37 | 38 | #define HI_LOSS_FREQ (0.3f) // scaled center freq for the treble loss filter 39 | // #define HI_LOSS_FREQ_MAX (0.08f) 40 | #define LO_LOSS_FREQ (0.06f) // scaled center freq for the bass loss filter 41 | 42 | #define LFO_AMPL_BITS (5) // 2^LFO_AMPL_BITS will be the LFO amplitude 43 | #define LFO_AMPL ((1<>1) // read offset = half the amplitude 45 | #define LFO_FRAC_BITS (16 - LFO_AMPL_BITS) // fractional part used for linear interpolation 46 | #define LFO_FRAC_MASK ((1< 16 161 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 162 | #endif 163 | #if AUDIO_BLOCK_SAMPLES > 32 164 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 165 | #endif 166 | #if AUDIO_BLOCK_SAMPLES > 48 167 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 168 | #endif 169 | #if AUDIO_BLOCK_SAMPLES > 64 170 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 171 | #endif 172 | #if AUDIO_BLOCK_SAMPLES > 80 173 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174 | #endif 175 | #if AUDIO_BLOCK_SAMPLES > 96 176 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 177 | #endif 178 | #if AUDIO_BLOCK_SAMPLES > 112 179 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 180 | #endif 181 | } }; 182 | 183 | void AudioEffectPlateReverb::update() 184 | { 185 | const audio_block_t *blockL, *blockR; 186 | 187 | #if defined(__ARM_ARCH_7EM__) 188 | audio_block_t *outblockL; 189 | audio_block_t *outblockR; 190 | int i; 191 | float32_t input, acc, temp1, temp2; 192 | uint16_t temp16; 193 | float32_t rv_time; 194 | 195 | // for LFOs: 196 | int16_t lfo1_out_sin, lfo1_out_cos, lfo2_out_sin, lfo2_out_cos; 197 | int32_t y0, y1; 198 | int64_t y; 199 | uint32_t idx; 200 | static bool cleanup_done = false; 201 | // handle bypass, 1st call will clean the buffers to avoid continuing the previous reverb tail 202 | if (bypass) 203 | { 204 | if (!cleanup_done) 205 | { 206 | memset(in_allp1_bufL, 0, sizeof(in_allp1_bufL)); 207 | memset(in_allp2_bufL, 0, sizeof(in_allp2_bufL)); 208 | memset(in_allp3_bufL, 0, sizeof(in_allp3_bufL)); 209 | memset(in_allp4_bufL, 0, sizeof(in_allp4_bufL)); 210 | memset(in_allp1_bufR, 0, sizeof(in_allp1_bufR)); 211 | memset(in_allp2_bufR, 0, sizeof(in_allp2_bufR)); 212 | memset(in_allp3_bufR, 0, sizeof(in_allp3_bufR)); 213 | memset(in_allp4_bufR, 0, sizeof(in_allp4_bufR)); 214 | memset(lp_allp1_buf, 0, sizeof(lp_allp1_buf)); 215 | memset(lp_allp2_buf, 0, sizeof(lp_allp2_buf)); 216 | memset(lp_allp3_buf, 0, sizeof(lp_allp3_buf)); 217 | memset(lp_allp4_buf, 0, sizeof(lp_allp4_buf)); 218 | memset(lp_dly1_buf, 0, sizeof(lp_dly1_buf)); 219 | memset(lp_dly2_buf, 0, sizeof(lp_dly2_buf)); 220 | memset(lp_dly3_buf, 0, sizeof(lp_dly3_buf)); 221 | memset(lp_dly4_buf, 0, sizeof(lp_dly4_buf)); 222 | 223 | cleanup_done = true; 224 | } 225 | blockL = receiveReadOnly(0); 226 | blockR = receiveReadOnly(1); 227 | if (!blockL) blockL = &zeroblock; 228 | if (!blockR) blockR = &zeroblock; 229 | transmit((audio_block_t *)blockL,0); 230 | transmit((audio_block_t *)blockR,1); 231 | if (blockL != &zeroblock) release((audio_block_t *)blockL); 232 | if (blockR != &zeroblock) release((audio_block_t *)blockR); 233 | 234 | return; 235 | } 236 | cleanup_done = false; 237 | 238 | 239 | blockL = receiveReadOnly(0); 240 | blockR = receiveReadOnly(1); 241 | outblockL = allocate(); 242 | outblockR = allocate(); 243 | if (!outblockL || !outblockR) { 244 | if (outblockL) release(outblockL); 245 | if (outblockR) release(outblockR); 246 | if (blockL) release((audio_block_t *)blockL); 247 | if (blockR) release((audio_block_t *)blockR); 248 | return; 249 | } 250 | 251 | if (!blockL) blockL = &zeroblock; 252 | if (!blockR) blockR = &zeroblock; 253 | 254 | // convert data to float32 255 | arm_q15_to_float((q15_t *)blockL->data, input_blockL, AUDIO_BLOCK_SAMPLES); 256 | arm_q15_to_float((q15_t *)blockR->data, input_blockR, AUDIO_BLOCK_SAMPLES); 257 | 258 | rv_time = rv_time_k; 259 | 260 | for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) 261 | { 262 | // do the LFOs 263 | lfo1_phase_acc += lfo1_adder; 264 | idx = lfo1_phase_acc >> 24; // 8bit lookup table address 265 | y0 = AudioWaveformSine[idx]; 266 | y1 = AudioWaveformSine[idx+1]; 267 | idx = lfo1_phase_acc & 0x00FFFFFF; // lower 24 bit = fractional part 268 | y = (int64_t)y0 * (0x00FFFFFF - idx); 269 | y += (int64_t)y1 * idx; 270 | lfo1_out_sin = (int32_t) (y >> (32-8)); // 16bit output 271 | idx = ((lfo1_phase_acc >> 24)+64) & 0xFF; 272 | y0 = AudioWaveformSine[idx]; 273 | y1 = AudioWaveformSine[idx + 1]; 274 | y = (int64_t)y0 * (0x00FFFFFF - idx); 275 | y += (int64_t)y1 * idx; 276 | lfo1_out_cos = (int32_t) (y >> (32-8)); // 16bit output 277 | 278 | lfo2_phase_acc += lfo2_adder; 279 | idx = lfo2_phase_acc >> 24; // 8bit lookup table address 280 | y0 = AudioWaveformSine[idx]; 281 | y1 = AudioWaveformSine[idx+1]; 282 | idx = lfo2_phase_acc & 0x00FFFFFF; // lower 24 bit = fractional part 283 | y = (int64_t)y0 * (0x00FFFFFF - idx); 284 | y += (int64_t)y1 * idx; 285 | lfo2_out_sin = (int32_t) (y >> (32-8)); //32-8->output 16bit, 286 | idx = ((lfo2_phase_acc >> 24)+64) & 0xFF; 287 | y0 = AudioWaveformSine[idx]; 288 | y1 = AudioWaveformSine[idx + 1]; 289 | y = (int64_t)y0 * (0x00FFFFFF - idx); 290 | y += (int64_t)y1 * idx; 291 | lfo2_out_cos = (int32_t) (y >> (32-8)); // 16bit output 292 | 293 | input = input_blockL[i] * input_attn; 294 | // chained input allpasses, channel L 295 | acc = in_allp1_bufL[in_allp1_idxL] + input * in_allp_k; 296 | in_allp1_bufL[in_allp1_idxL] = input - in_allp_k * acc; 297 | input = acc; 298 | if (++in_allp1_idxL >= sizeof(in_allp1_bufL)/sizeof(float32_t)) in_allp1_idxL = 0; 299 | 300 | acc = in_allp2_bufL[in_allp2_idxL] + input * in_allp_k; 301 | in_allp2_bufL[in_allp2_idxL] = input - in_allp_k * acc; 302 | input = acc; 303 | if (++in_allp2_idxL >= sizeof(in_allp2_bufL)/sizeof(float32_t)) in_allp2_idxL = 0; 304 | 305 | acc = in_allp3_bufL[in_allp3_idxL] + input * in_allp_k; 306 | in_allp3_bufL[in_allp3_idxL] = input - in_allp_k * acc; 307 | input = acc; 308 | if (++in_allp3_idxL >= sizeof(in_allp3_bufL)/sizeof(float32_t)) in_allp3_idxL = 0; 309 | 310 | acc = in_allp4_bufL[in_allp4_idxL] + input * in_allp_k; 311 | in_allp4_bufL[in_allp4_idxL] = input - in_allp_k * acc; 312 | in_allp_out_L = acc; 313 | if (++in_allp4_idxL >= sizeof(in_allp4_bufL)/sizeof(float32_t)) in_allp4_idxL = 0; 314 | 315 | input = input_blockR[i] * input_attn; 316 | 317 | // chained input allpasses, channel R 318 | acc = in_allp1_bufR[in_allp1_idxR] + input * in_allp_k; 319 | in_allp1_bufR[in_allp1_idxR] = input - in_allp_k * acc; 320 | input = acc; 321 | if (++in_allp1_idxR >= sizeof(in_allp1_bufR)/sizeof(float32_t)) in_allp1_idxR = 0; 322 | 323 | acc = in_allp2_bufR[in_allp2_idxR] + input * in_allp_k; 324 | in_allp2_bufR[in_allp2_idxR] = input - in_allp_k * acc; 325 | input = acc; 326 | if (++in_allp2_idxR >= sizeof(in_allp2_bufR)/sizeof(float32_t)) in_allp2_idxR = 0; 327 | 328 | acc = in_allp3_bufR[in_allp3_idxR] + input * in_allp_k; 329 | in_allp3_bufR[in_allp3_idxR] = input - in_allp_k * acc; 330 | input = acc; 331 | if (++in_allp3_idxR >= sizeof(in_allp3_bufR)/sizeof(float32_t)) in_allp3_idxR = 0; 332 | 333 | acc = in_allp4_bufR[in_allp4_idxR] + input * in_allp_k; 334 | in_allp4_bufR[in_allp4_idxR] = input - in_allp_k * acc; 335 | in_allp_out_R = acc; 336 | if (++in_allp4_idxR >= sizeof(in_allp4_bufR)/sizeof(float32_t)) in_allp4_idxR = 0; 337 | 338 | // input allpases done, start loop allpases 339 | input = lp_allp_out + in_allp_out_R; 340 | acc = lp_allp1_buf[lp_allp1_idx] + input * loop_allp_k; // input is the lp allpass chain output 341 | lp_allp1_buf[lp_allp1_idx] = input - loop_allp_k * acc; 342 | input = acc; 343 | if (++lp_allp1_idx >= sizeof(lp_allp1_buf)/sizeof(float32_t)) lp_allp1_idx = 0; 344 | 345 | acc = lp_dly1_buf[lp_dly1_idx]; // read the end of the delay 346 | lp_dly1_buf[lp_dly1_idx] = input; // write new sample 347 | input = acc; 348 | if (++lp_dly1_idx >= sizeof(lp_dly1_buf)/sizeof(float32_t)) lp_dly1_idx = 0; // update index 349 | 350 | // hi/lo shelving filter 351 | temp1 = input - lpf1; 352 | lpf1 += temp1 * lp_lowpass_f; 353 | temp2 = input - lpf1; 354 | temp1 = lpf1 - hpf1; 355 | hpf1 += temp1 * lp_hipass_f; 356 | acc = lpf1 + temp2*lp_hidamp_k + hpf1*lp_lodamp_k; 357 | acc = acc * rv_time * rv_time_scaler; // scale by the reveb time 358 | 359 | input = acc + in_allp_out_L; 360 | 361 | acc = lp_allp2_buf[lp_allp2_idx] + input * loop_allp_k; 362 | lp_allp2_buf[lp_allp2_idx] = input - loop_allp_k * acc; 363 | input = acc; 364 | if (++lp_allp2_idx >= sizeof(lp_allp2_buf)/sizeof(float32_t)) lp_allp2_idx = 0; 365 | acc = lp_dly2_buf[lp_dly2_idx]; // read the end of the delay 366 | lp_dly2_buf[lp_dly2_idx] = input; // write new sample 367 | input = acc; 368 | if (++lp_dly2_idx >= sizeof(lp_dly2_buf)/sizeof(float32_t)) lp_dly2_idx = 0; // update index 369 | // hi/lo shelving filter 370 | temp1 = input - lpf2; 371 | lpf2 += temp1 * lp_lowpass_f; 372 | temp2 = input - lpf2; 373 | temp1 = lpf2 - hpf2; 374 | hpf2 += temp1 * lp_hipass_f; 375 | acc = lpf2 + temp2*lp_hidamp_k + hpf2*lp_lodamp_k; 376 | acc = acc * rv_time * rv_time_scaler; 377 | 378 | input = acc + in_allp_out_R; 379 | 380 | acc = lp_allp3_buf[lp_allp3_idx] + input * loop_allp_k; 381 | lp_allp3_buf[lp_allp3_idx] = input - loop_allp_k * acc; 382 | input = acc; 383 | if (++lp_allp3_idx >= sizeof(lp_allp3_buf)/sizeof(float32_t)) lp_allp3_idx = 0; 384 | acc = lp_dly3_buf[lp_dly3_idx]; // read the end of the delay 385 | lp_dly3_buf[lp_dly3_idx] = input; // write new sample 386 | input = acc; 387 | if (++lp_dly3_idx >= sizeof(lp_dly3_buf)/sizeof(float32_t)) lp_dly3_idx = 0; // update index 388 | // hi/lo shelving filter 389 | temp1 = input - lpf3; 390 | lpf3 += temp1 * lp_lowpass_f; 391 | temp2 = input - lpf3; 392 | temp1 = lpf3 - hpf3; 393 | hpf3 += temp1 * lp_hipass_f; 394 | acc = lpf3 + temp2*lp_hidamp_k + hpf3*lp_lodamp_k; 395 | acc = acc * rv_time * rv_time_scaler; 396 | 397 | input = acc + in_allp_out_L; 398 | 399 | acc = lp_allp4_buf[lp_allp4_idx] + input * loop_allp_k; 400 | lp_allp4_buf[lp_allp4_idx] = input - loop_allp_k * acc; 401 | input = acc; 402 | if (++lp_allp4_idx >= sizeof(lp_allp4_buf)/sizeof(float32_t)) lp_allp4_idx = 0; 403 | acc = lp_dly4_buf[lp_dly4_idx]; // read the end of the delay 404 | lp_dly4_buf[lp_dly4_idx] = input; // write new sample 405 | input = acc; 406 | if (++lp_dly4_idx >= sizeof(lp_dly4_buf)/sizeof(float32_t)) lp_dly4_idx= 0; // update index 407 | // hi/lo shelving filter 408 | temp1 = input - lpf4; 409 | lpf4 += temp1 * lp_lowpass_f; 410 | temp2 = input - lpf4; 411 | temp1 = lpf4 - hpf4; 412 | hpf4 += temp1 * lp_hipass_f; 413 | acc = lpf4 + temp2*lp_hidamp_k + hpf4*lp_lodamp_k; 414 | acc = acc * rv_time * rv_time_scaler; 415 | 416 | lp_allp_out = acc; 417 | 418 | // channel L: 419 | #ifdef TAP1_MODULATED 420 | temp16 = (lp_dly1_idx + lp_dly1_offset_L + (lfo1_out_cos>>LFO_FRAC_BITS)) % (sizeof(lp_dly1_buf)/sizeof(float32_t)); 421 | temp1 = lp_dly1_buf[temp16++]; // sample now 422 | if (temp16 >= sizeof(lp_dly1_buf)/sizeof(float32_t)) temp16 = 0; 423 | temp2 = lp_dly1_buf[temp16]; // sample next 424 | input = (float32_t)(lfo1_out_cos & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k 425 | acc = (temp1*(1.0f-input) + temp2*input)* 0.8f; 426 | #else 427 | temp16 = (lp_dly1_idx + lp_dly1_offset_L) % (sizeof(lp_dly1_buf)/sizeof(float32_t)); 428 | acc = lp_dly1_buf[temp16]* 0.8f; 429 | #endif 430 | 431 | 432 | #ifdef TAP2_MODULATED 433 | temp16 = (lp_dly2_idx + lp_dly2_offset_L + (lfo1_out_sin>>LFO_FRAC_BITS)) % (sizeof(lp_dly2_buf)/sizeof(float32_t)); 434 | temp1 = lp_dly2_buf[temp16++]; 435 | if (temp16 >= sizeof(lp_dly2_buf)/sizeof(float32_t)) temp16 = 0; 436 | temp2 = lp_dly2_buf[temp16]; 437 | input = (float32_t)(lfo1_out_sin & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k 438 | acc += (temp1*(1.0f-input) + temp2*input)* 0.7f; 439 | #else 440 | temp16 = (lp_dly2_idx + lp_dly2_offset_L) % (sizeof(lp_dly2_buf)/sizeof(float32_t)); 441 | acc += (temp1*(1.0f-input) + temp2*input)* 0.6f; 442 | #endif 443 | 444 | temp16 = (lp_dly3_idx + lp_dly3_offset_L + (lfo2_out_cos>>LFO_FRAC_BITS)) % (sizeof(lp_dly3_buf)/sizeof(float32_t)); 445 | temp1 = lp_dly3_buf[temp16++]; 446 | if (temp16 >= sizeof(lp_dly3_buf)/sizeof(float32_t)) temp16 = 0; 447 | temp2 = lp_dly3_buf[temp16]; 448 | input = (float32_t)(lfo2_out_cos & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k 449 | acc += (temp1*(1.0f-input) + temp2*input)* 0.6f; 450 | 451 | temp16 = (lp_dly4_idx + lp_dly4_offset_L + (lfo2_out_sin>>LFO_FRAC_BITS)) % (sizeof(lp_dly4_buf)/sizeof(float32_t)); 452 | temp1 = lp_dly4_buf[temp16++]; 453 | if (temp16 >= sizeof(lp_dly4_buf)/sizeof(float32_t)) temp16 = 0; 454 | temp2 = lp_dly4_buf[temp16]; 455 | input = (float32_t)(lfo2_out_sin & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k 456 | acc += (temp1*(1.0f-input) + temp2*input)* 0.5f; 457 | 458 | // Master lowpass filter 459 | temp1 = acc - master_lowpass_l; 460 | master_lowpass_l += temp1 * master_lowpass_f; 461 | 462 | outblockL->data[i] =(int16_t)(master_lowpass_l * 32767.0f); //sat16(output * 30, 0); 463 | 464 | // Channel R 465 | #ifdef TAP1_MODULATED 466 | temp16 = (lp_dly1_idx + lp_dly1_offset_R + (lfo1_out_sin>>LFO_FRAC_BITS)) % (sizeof(lp_dly1_buf)/sizeof(float32_t)); 467 | temp1 = lp_dly1_buf[temp16++]; // sample now 468 | if (temp16 >= sizeof(lp_dly1_buf)/sizeof(float32_t)) temp16 = 0; 469 | temp2 = lp_dly1_buf[temp16]; // sample next 470 | input = (float32_t)(lfo1_out_sin & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k 471 | 472 | acc = (temp1*(1.0f-input) + temp2*input)* 0.8f; 473 | #else 474 | temp16 = (lp_dly1_idx + lp_dly1_offset_R) % (sizeof(lp_dly1_buf)/sizeof(float32_t)); 475 | acc = lp_dly1_buf[temp16] * 0.8f; 476 | #endif 477 | #ifdef TAP2_MODULATED 478 | temp16 = (lp_dly2_idx + lp_dly2_offset_R + (lfo1_out_cos>>LFO_FRAC_BITS)) % (sizeof(lp_dly2_buf)/sizeof(float32_t)); 479 | temp1 = lp_dly2_buf[temp16++]; 480 | if (temp16 >= sizeof(lp_dly2_buf)/sizeof(float32_t)) temp16 = 0; 481 | temp2 = lp_dly2_buf[temp16]; 482 | input = (float32_t)(lfo1_out_cos & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k 483 | acc += (temp1*(1.0f-input) + temp2*input)* 0.7f; 484 | #else 485 | temp16 = (lp_dly2_idx + lp_dly2_offset_R) % (sizeof(lp_dly2_buf)/sizeof(float32_t)); 486 | acc += (temp1*(1.0f-input) + temp2*input)* 0.7f; 487 | #endif 488 | temp16 = (lp_dly3_idx + lp_dly3_offset_R + (lfo2_out_sin>>LFO_FRAC_BITS)) % (sizeof(lp_dly3_buf)/sizeof(float32_t)); 489 | temp1 = lp_dly3_buf[temp16++]; 490 | if (temp16 >= sizeof(lp_dly3_buf)/sizeof(float32_t)) temp16 = 0; 491 | temp2 = lp_dly3_buf[temp16]; 492 | input = (float32_t)(lfo2_out_sin & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k 493 | acc += (temp1*(1.0f-input) + temp2*input)* 0.6f; 494 | 495 | temp16 = (lp_dly4_idx + lp_dly4_offset_R + (lfo2_out_cos>>LFO_FRAC_BITS)) % (sizeof(lp_dly4_buf)/sizeof(float32_t)); 496 | temp1 = lp_dly4_buf[temp16++]; 497 | if (temp16 >= sizeof(lp_dly4_buf)/sizeof(float32_t)) temp16 = 0; 498 | temp2 = lp_dly4_buf[temp16]; 499 | input = (float32_t)(lfo2_out_cos & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k 500 | acc += (temp1*(1.0f-input) + temp2*input)* 0.5f; 501 | 502 | // Master lowpass filter 503 | temp1 = acc - master_lowpass_r; 504 | master_lowpass_r += temp1 * master_lowpass_f; 505 | outblockR->data[i] =(int16_t)(master_lowpass_r * 32767.0f); 506 | 507 | } 508 | transmit(outblockL, 0); 509 | transmit(outblockR, 1); 510 | release(outblockL); 511 | release(outblockR); 512 | if (blockL != &zeroblock) release((audio_block_t *)blockL); 513 | if (blockR != &zeroblock) release((audio_block_t *)blockR); 514 | 515 | #elif defined(KINETISL) 516 | blockL = receiveReadOnly(0); 517 | if (blockL) release(blockL); 518 | blockR = receiveReadOnly(1); 519 | if (blockR) release(blockR); 520 | #endif 521 | } 522 | -------------------------------------------------------------------------------- /Hx_PlateReverb/effect_platervbstereo.h: -------------------------------------------------------------------------------- 1 | /* Stereo plate reverb for Teensy 4 2 | * 3 | * Author: Piotr Zapart 4 | * www.hexefx.com 5 | * 6 | * Copyright (c) 2020 by Piotr Zapart 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | /*** 28 | * Algorithm based on plate reverbs developed for SpinSemi FV-1 DSP chip 29 | * 30 | * Allpass + modulated delay line based lush plate reverb 31 | * 32 | * Input parameters are float in range 0.0 to 1.0: 33 | * 34 | * size - reverb time 35 | * hidamp - hi frequency loss in the reverb tail 36 | * lodamp - low frequency loss in the reverb tail 37 | * lowpass - output/master lowpass filter, useful for darkening the reverb sound 38 | * diffusion - lower settings will make the reverb tail more "echoey", optimal value 0.65 39 | * 40 | */ 41 | 42 | 43 | #ifndef _EFFECT_PLATERVBSTEREO_H 44 | #define _EFFECT_PLATERVBSTEREO_H 45 | 46 | #include 47 | #include "Audio.h" 48 | #include "AudioStream.h" 49 | #include "arm_math.h" 50 | 51 | 52 | // if uncommented will place all the buffers in the DMAMEM section ofd the memory 53 | // works with single instance of the reverb only 54 | #define REVERB_USE_DMAMEM 55 | 56 | /*** 57 | * Loop delay modulation: comment/uncomment to switch sin/cos 58 | * modulation for the 1st or 2nd tap, 3rd tap is always modulated 59 | * more modulation means more chorus type sounding reverb tail 60 | */ 61 | //#define TAP1_MODULATED 62 | #define TAP2_MODULATED 63 | 64 | class AudioEffectPlateReverb : public AudioStream 65 | { 66 | public: 67 | AudioEffectPlateReverb(); 68 | virtual void update(); 69 | 70 | void size(float n) 71 | { 72 | n = constrain(n, 0.0f, 1.0f); 73 | n = map (n, 0.0f, 1.0f, 0.2f, rv_time_k_max); 74 | float32_t attn = map(n, 0.0f, rv_time_k_max, 0.5f, 0.25f); 75 | __disable_irq(); 76 | rv_time_k = n; 77 | input_attn = attn; 78 | __enable_irq(); 79 | } 80 | 81 | void hidamp(float n) 82 | { 83 | n = constrain(n, 0.0f, 1.0f); 84 | __disable_irq(); 85 | lp_hidamp_k = 1.0f - n; 86 | __enable_irq(); 87 | } 88 | 89 | void lodamp(float n) 90 | { 91 | n = constrain(n, 0.0f, 1.0f); 92 | __disable_irq(); 93 | lp_lodamp_k = -n; 94 | rv_time_scaler = 1.0f - n * 0.12f; // limit the max reverb time, otherwise it will clip 95 | __enable_irq(); 96 | } 97 | 98 | void lowpass(float n) 99 | { 100 | n = constrain(n, 0.0f, 1.0f); 101 | n = map(n*n*n, 0.0f, 1.0f, 0.05f, 1.0f); 102 | master_lowpass_f = n; 103 | } 104 | 105 | void diffusion(float n) 106 | { 107 | n = constrain(n, 0.0f, 1.0f); 108 | n = map(n, 0.0f, 1.0f, 0.005f, 0.65f); 109 | __disable_irq(); 110 | in_allp_k = n; 111 | loop_allp_k = n; 112 | __enable_irq(); 113 | } 114 | 115 | float32_t get_size(void) {return rv_time_k;} 116 | bool get_bypass(void) {return bypass;} 117 | void set_bypass(bool state) {bypass = state;}; 118 | void tgl_bypass(void) {bypass ^=1;} 119 | private: 120 | bool bypass = false; 121 | audio_block_t *inputQueueArray[2]; 122 | #ifndef REVERB_USE_DMAMEM 123 | float32_t input_blockL[AUDIO_BLOCK_SAMPLES]; 124 | float32_t input_blockR[AUDIO_BLOCK_SAMPLES]; 125 | #endif 126 | float32_t input_attn; 127 | 128 | float32_t in_allp_k; // input allpass coeff 129 | #ifndef REVERB_USE_DMAMEM 130 | float32_t in_allp1_bufL[224]; // input allpass buffers 131 | float32_t in_allp2_bufL[420]; 132 | float32_t in_allp3_bufL[856]; 133 | float32_t in_allp4_bufL[1089]; 134 | #endif 135 | uint16_t in_allp1_idxL; 136 | uint16_t in_allp2_idxL; 137 | uint16_t in_allp3_idxL; 138 | uint16_t in_allp4_idxL; 139 | float32_t in_allp_out_L; // L allpass chain output 140 | #ifndef REVERB_USE_DMAMEM 141 | float32_t in_allp1_bufR[156]; // input allpass buffers 142 | float32_t in_allp2_bufR[520]; 143 | float32_t in_allp3_bufR[956]; 144 | float32_t in_allp4_bufR[1289]; 145 | #endif 146 | uint16_t in_allp1_idxR; 147 | uint16_t in_allp2_idxR; 148 | uint16_t in_allp3_idxR; 149 | uint16_t in_allp4_idxR; 150 | float32_t in_allp_out_R; // R allpass chain output 151 | #ifndef REVERB_USE_DMAMEM 152 | float32_t lp_allp1_buf[2303]; // loop allpass buffers 153 | float32_t lp_allp2_buf[2905]; 154 | float32_t lp_allp3_buf[3175]; 155 | float32_t lp_allp4_buf[2398]; 156 | #endif 157 | uint16_t lp_allp1_idx; 158 | uint16_t lp_allp2_idx; 159 | uint16_t lp_allp3_idx; 160 | uint16_t lp_allp4_idx; 161 | float32_t loop_allp_k; // loop allpass coeff 162 | float32_t lp_allp_out; 163 | #ifndef REVERB_USE_DMAMEM 164 | float32_t lp_dly1_buf[3423]; 165 | float32_t lp_dly2_buf[4589]; 166 | float32_t lp_dly3_buf[4365]; 167 | float32_t lp_dly4_buf[3698]; 168 | #endif 169 | uint16_t lp_dly1_idx; 170 | uint16_t lp_dly2_idx; 171 | uint16_t lp_dly3_idx; 172 | uint16_t lp_dly4_idx; 173 | 174 | const uint16_t lp_dly1_offset_L = 201; // delay line tap offets 175 | const uint16_t lp_dly2_offset_L = 145; 176 | const uint16_t lp_dly3_offset_L = 1897; 177 | const uint16_t lp_dly4_offset_L = 280; 178 | 179 | const uint16_t lp_dly1_offset_R = 1897; 180 | const uint16_t lp_dly2_offset_R = 1245; 181 | const uint16_t lp_dly3_offset_R = 487; 182 | const uint16_t lp_dly4_offset_R = 780; 183 | 184 | float32_t lp_hidamp_k; // loop high band damping coeff 185 | float32_t lp_lodamp_k; // loop low baand damping coeff 186 | 187 | float32_t lpf1; // lowpass filters 188 | float32_t lpf2; 189 | float32_t lpf3; 190 | float32_t lpf4; 191 | 192 | float32_t hpf1; // highpass filters 193 | float32_t hpf2; 194 | float32_t hpf3; 195 | float32_t hpf4; 196 | 197 | float32_t lp_lowpass_f; // loop lowpass scaled frequency 198 | float32_t lp_hipass_f; // loop highpass scaled frequency 199 | 200 | float32_t master_lowpass_f; 201 | float32_t master_lowpass_l; 202 | float32_t master_lowpass_r; 203 | 204 | const float32_t rv_time_k_max = 0.95f; 205 | float32_t rv_time_k; // reverb time coeff 206 | float32_t rv_time_scaler; // with high lodamp settings lower the max reverb time to avoid clipping 207 | 208 | uint32_t lfo1_phase_acc; // LFO 1 209 | uint32_t lfo1_adder; 210 | 211 | uint32_t lfo2_phase_acc; // LFO 2 212 | uint32_t lfo2_adder; 213 | }; 214 | 215 | #endif // _EFFECT_PLATEREV_H 216 | -------------------------------------------------------------------------------- /Hx_PlateReverb_F32/README.md: -------------------------------------------------------------------------------- 1 | ## Stereo Plate Reverb 2 | Fully stereo in/out reverb component for the [OpenAudio_ArduinoLibrary](https://github.com/chipaudette/OpenAudio_ArduinoLibrary "OpenAudio_ArduinoLibrary"). 3 | 4 | ### Connections: 5 | Reverb requires stereo in and out connenctions. 6 | ### API: 7 | 8 | ```void size(float32_t n);``` 9 | sets the reverb time. Parameter range: 0.0 to 1.0. 10 | Example: 11 | ```reverb.size(1.0f); // set the reverb time to maximum``` 12 | 13 | ```void lowpass(float32_t n);``` 14 | sets the reverb master lowpass filter. Parameter range: 0.0 to 1.0. 15 | Example: 16 | ```reverb.lowpass(0.7f); // darken the reverb sound``` 17 | 18 | ```void hidamp(float32_t n);``` 19 | sets the treble loss. Parameter range: 0.0 to 1.0. 20 | Example: 21 | ```reverb.hidamp(1.0f); // max hi band dampening results in darker sound ``` 22 | 23 | ```void lodamp(float32_t n);``` 24 | sets the bass cut. Parameter range: 0.0 to 1.0. 25 | Example: 26 | ```reverb.lodamp(0.5f); // cut more bass in the reverb tail to make the sound brighter ``` 27 | 28 | ```void diffusion(float32_t n);``` 29 | diffision controls the density of the reverb tail, lower values add more echo type reflections, higher values produce a lush rich reverb tail. Useful for creating a different type of reverbs: ie. with size set to 0 and diffusion to something in range 0.0f-0.3f the result will be like a ping-pong delay with room reverb. 30 | Example: 31 | ```reverb.density(0.5f); // alter the allpass coefficients to change the reverb sound ``` 32 | 33 | ```void freeze(bool state);``` 34 | Cuts off the input signal and increases the reverb time coeff to 1.0, creating an infinite reverb. Combined with low diffusion settings might produce clicking, so use with caution. 35 | Example: 36 | ```reverb.freeze(true); // turn freeze on ``` 37 | 38 | ```bool freeze_tgl(void);``` 39 | Toggles the freze state and retunrs the current state. 40 | Example: 41 | ```Serial.printf("Freeze = %d", reverb.freeze_tgl()); // toggle the freeze and print the current state ``` 42 | 43 | ```bool freeze_get(void);``` 44 | Returns the current freeze state. 45 | 46 | ```void set_bypass(bool state);``` 47 | Disables (true) or enables (false) the reverb engine. 48 | Example: 49 | ```reverb.set_bypass(true); // disable the reverb (saves CPU load) ``` 50 | 51 | ```bool get_bypass(void);``` 52 | Returns the current reverb bypass status. 53 | 54 | ```void tgl_bypass(void);``` 55 | Toggles the current reverb bypass status. 56 | 57 | Typical application using OpenAudio_ArduinoLibrary: 58 | ![alt text][pic1] 59 | 60 | ### Additional config: 61 | 62 | by default the reverb places it's buffers into OCRAM/DMAMEM region. 63 | Comment out the 64 | ```#define REVERB_F32_USE_DMAMEM``` 65 | line in the ```effect_platervbstereo_F32.h``` file to place the variables into the DCTM ram region. 66 | 67 | [pic1]: plateReverb_schm.png "Stereo plate reverb connections" -------------------------------------------------------------------------------- /Hx_PlateReverb_F32/effect_platervbstereo_F32.cpp: -------------------------------------------------------------------------------- 1 | /* Stereo plate reverb for Teensy 4 2 | * 32bit float version for OpenAudio_ArduinoLibrary: 3 | * https://github.com/chipaudette/OpenAudio_ArduinoLibrary 4 | * 5 | * Author: Piotr Zapart 6 | * www.hexefx.com 7 | * 8 | * Copyright (c) 2023 by Piotr Zapart 9 | * 10 | * Development of this audio library was funded by PJRC.COM, LLC by sales of 11 | * Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop 12 | * open source software by purchasing Teensy or other PJRC products. 13 | * 14 | * Permission is hereby granted, free of charge, to any person obtaining a copy 15 | * of this software and associated documentation files (the "Software"), to deal 16 | * in the Software without restriction, including without limitation the rights 17 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | * copies of the Software, and to permit persons to whom the Software is 19 | * furnished to do so, subject to the following conditions: 20 | * 21 | * The above copyright notice, development funding notice, and this permission 22 | * notice shall be included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 30 | * THE SOFTWARE. 31 | */ 32 | 33 | 34 | #include 35 | #include "effect_platervbstereo_F32.h" 36 | 37 | #define INP_ALLP_COEFF (0.65f) // default input allpass coeff 38 | #define LOOP_ALLOP_COEFF (0.65f) // default loop allpass coeff 39 | 40 | #define HI_LOSS_FREQ (0.3f) // scaled center freq for the treble loss filter 41 | #define LO_LOSS_FREQ (0.06f) // scaled center freq for the bass loss filter 42 | 43 | #define LFO_AMPL_BITS (5) // 2^LFO_AMPL_BITS will be the LFO amplitude 44 | #define LFO_AMPL ((1<>1) // read offset = half the amplitude 46 | #define LFO_FRAC_BITS (16 - LFO_AMPL_BITS) // fractional part used for linear interpolation 47 | #define LFO_FRAC_MASK ((1<> 24; // 8bit lookup table address 228 | y0 = AudioWaveformSine[idx]; 229 | y1 = AudioWaveformSine[idx+1]; 230 | idx = lfo1_phase_acc & 0x00FFFFFF; // lower 24 bit = fractional part 231 | y = (int64_t)y0 * (0x00FFFFFF - idx); 232 | y += (int64_t)y1 * idx; 233 | lfo1_out_sin = (int32_t) (y >> (32-8)); // 16bit output 234 | idx = ((lfo1_phase_acc >> 24)+64) & 0xFF; 235 | y0 = AudioWaveformSine[idx]; 236 | y1 = AudioWaveformSine[idx + 1]; 237 | y = (int64_t)y0 * (0x00FFFFFF - idx); 238 | y += (int64_t)y1 * idx; 239 | lfo1_out_cos = (int32_t) (y >> (32-8)); // 16bit output 240 | 241 | lfo2_phase_acc += lfo2_adder; 242 | idx = lfo2_phase_acc >> 24; // 8bit lookup table address 243 | y0 = AudioWaveformSine[idx]; 244 | y1 = AudioWaveformSine[idx+1]; 245 | idx = lfo2_phase_acc & 0x00FFFFFF; // lower 24 bit = fractional part 246 | y = (int64_t)y0 * (0x00FFFFFF - idx); 247 | y += (int64_t)y1 * idx; 248 | lfo2_out_sin = (int32_t) (y >> (32-8)); //32-8->output 16bit, 249 | idx = ((lfo2_phase_acc >> 24)+64) & 0xFF; 250 | y0 = AudioWaveformSine[idx]; 251 | y1 = AudioWaveformSine[idx + 1]; 252 | y = (int64_t)y0 * (0x00FFFFFF - idx); 253 | y += (int64_t)y1 * idx; 254 | lfo2_out_cos = (int32_t) (y >> (32-8)); // 16bit output 255 | 256 | input = blockL->data[i] * input_attn; 257 | // chained input allpasses, channel L 258 | acc = in_allp1_bufL[in_allp1_idxL] + input * in_allp_k; 259 | in_allp1_bufL[in_allp1_idxL] = input - in_allp_k * acc; 260 | input = acc; 261 | if (++in_allp1_idxL >= sizeof(in_allp1_bufL)/sizeof(float32_t)) in_allp1_idxL = 0; 262 | 263 | acc = in_allp2_bufL[in_allp2_idxL] + input * in_allp_k; 264 | in_allp2_bufL[in_allp2_idxL] = input - in_allp_k * acc; 265 | input = acc; 266 | if (++in_allp2_idxL >= sizeof(in_allp2_bufL)/sizeof(float32_t)) in_allp2_idxL = 0; 267 | 268 | acc = in_allp3_bufL[in_allp3_idxL] + input * in_allp_k; 269 | in_allp3_bufL[in_allp3_idxL] = input - in_allp_k * acc; 270 | input = acc; 271 | if (++in_allp3_idxL >= sizeof(in_allp3_bufL)/sizeof(float32_t)) in_allp3_idxL = 0; 272 | 273 | acc = in_allp4_bufL[in_allp4_idxL] + input * in_allp_k; 274 | in_allp4_bufL[in_allp4_idxL] = input - in_allp_k * acc; 275 | in_allp_out_L = acc; 276 | if (++in_allp4_idxL >= sizeof(in_allp4_bufL)/sizeof(float32_t)) in_allp4_idxL = 0; 277 | 278 | input = blockR->data[i] * input_attn; 279 | 280 | // chained input allpasses, channel R 281 | acc = in_allp1_bufR[in_allp1_idxR] + input * in_allp_k; 282 | in_allp1_bufR[in_allp1_idxR] = input - in_allp_k * acc; 283 | input = acc; 284 | if (++in_allp1_idxR >= sizeof(in_allp1_bufR)/sizeof(float32_t)) in_allp1_idxR = 0; 285 | 286 | acc = in_allp2_bufR[in_allp2_idxR] + input * in_allp_k; 287 | in_allp2_bufR[in_allp2_idxR] = input - in_allp_k * acc; 288 | input = acc; 289 | if (++in_allp2_idxR >= sizeof(in_allp2_bufR)/sizeof(float32_t)) in_allp2_idxR = 0; 290 | 291 | acc = in_allp3_bufR[in_allp3_idxR] + input * in_allp_k; 292 | in_allp3_bufR[in_allp3_idxR] = input - in_allp_k * acc; 293 | input = acc; 294 | if (++in_allp3_idxR >= sizeof(in_allp3_bufR)/sizeof(float32_t)) in_allp3_idxR = 0; 295 | 296 | acc = in_allp4_bufR[in_allp4_idxR] + input * in_allp_k; 297 | in_allp4_bufR[in_allp4_idxR] = input - in_allp_k * acc; 298 | in_allp_out_R = acc; 299 | if (++in_allp4_idxR >= sizeof(in_allp4_bufR)/sizeof(float32_t)) in_allp4_idxR = 0; 300 | 301 | // shimmer: add octave up for lp_allp_out - TODO someday 302 | 303 | // input allpases done, start loop allpases 304 | input = lp_allp_out + in_allp_out_R; 305 | acc = lp_allp1_buf[lp_allp1_idx] + input * loop_allp_k; // input is the lp allpass chain output 306 | lp_allp1_buf[lp_allp1_idx] = input - loop_allp_k * acc; 307 | input = acc; 308 | if (++lp_allp1_idx >= sizeof(lp_allp1_buf)/sizeof(float32_t)) lp_allp1_idx = 0; 309 | 310 | acc = lp_dly1_buf[lp_dly1_idx]; // read the end of the delay 311 | lp_dly1_buf[lp_dly1_idx] = input; // write new sample 312 | input = acc; 313 | if (++lp_dly1_idx >= sizeof(lp_dly1_buf)/sizeof(float32_t)) lp_dly1_idx = 0; // update index 314 | 315 | // hi/lo shelving filter 316 | temp1 = input - lpf1; 317 | lpf1 += temp1 * lp_lowpass_f; 318 | temp2 = input - lpf1; 319 | temp1 = lpf1 - hpf1; 320 | hpf1 += temp1 * lp_hipass_f; 321 | acc = lpf1 + temp2*lp_hidamp_k + hpf1*lp_lodamp_k; 322 | acc = acc * rv_time * rv_time_scaler; // scale by the reveb time 323 | 324 | input = acc + in_allp_out_L; 325 | 326 | acc = lp_allp2_buf[lp_allp2_idx] + input * loop_allp_k; 327 | lp_allp2_buf[lp_allp2_idx] = input - loop_allp_k * acc; 328 | input = acc; 329 | if (++lp_allp2_idx >= sizeof(lp_allp2_buf)/sizeof(float32_t)) lp_allp2_idx = 0; 330 | acc = lp_dly2_buf[lp_dly2_idx]; // read the end of the delay 331 | lp_dly2_buf[lp_dly2_idx] = input; // write new sample 332 | input = acc; 333 | if (++lp_dly2_idx >= sizeof(lp_dly2_buf)/sizeof(float32_t)) lp_dly2_idx = 0; // update index 334 | // hi/lo shelving filter 335 | temp1 = input - lpf2; 336 | lpf2 += temp1 * lp_lowpass_f; 337 | temp2 = input - lpf2; 338 | temp1 = lpf2 - hpf2; 339 | hpf2 += temp1 * lp_hipass_f; 340 | acc = lpf2 + temp2*lp_hidamp_k + hpf2*lp_lodamp_k; 341 | acc = acc * rv_time * rv_time_scaler; 342 | 343 | input = acc + in_allp_out_R; 344 | 345 | acc = lp_allp3_buf[lp_allp3_idx] + input * loop_allp_k; 346 | lp_allp3_buf[lp_allp3_idx] = input - loop_allp_k * acc; 347 | input = acc; 348 | if (++lp_allp3_idx >= sizeof(lp_allp3_buf)/sizeof(float32_t)) lp_allp3_idx = 0; 349 | acc = lp_dly3_buf[lp_dly3_idx]; // read the end of the delay 350 | lp_dly3_buf[lp_dly3_idx] = input; // write new sample 351 | input = acc; 352 | if (++lp_dly3_idx >= sizeof(lp_dly3_buf)/sizeof(float32_t)) lp_dly3_idx = 0; // update index 353 | // hi/lo shelving filter 354 | temp1 = input - lpf3; 355 | lpf3 += temp1 * lp_lowpass_f; 356 | temp2 = input - lpf3; 357 | temp1 = lpf3 - hpf3; 358 | hpf3 += temp1 * lp_hipass_f; 359 | acc = lpf3 + temp2*lp_hidamp_k + hpf3*lp_lodamp_k; 360 | acc = acc * rv_time * rv_time_scaler; 361 | 362 | input = acc + in_allp_out_L; 363 | 364 | acc = lp_allp4_buf[lp_allp4_idx] + input * loop_allp_k; 365 | lp_allp4_buf[lp_allp4_idx] = input - loop_allp_k * acc; 366 | input = acc; 367 | if (++lp_allp4_idx >= sizeof(lp_allp4_buf)/sizeof(float32_t)) lp_allp4_idx = 0; 368 | acc = lp_dly4_buf[lp_dly4_idx]; // read the end of the delay 369 | lp_dly4_buf[lp_dly4_idx] = input; // write new sample 370 | input = acc; 371 | if (++lp_dly4_idx >= sizeof(lp_dly4_buf)/sizeof(float32_t)) lp_dly4_idx= 0; // update index 372 | // hi/lo shelving filter 373 | temp1 = input - lpf4; 374 | lpf4 += temp1 * lp_lowpass_f; 375 | temp2 = input - lpf4; 376 | temp1 = lpf4 - hpf4; 377 | hpf4 += temp1 * lp_hipass_f; 378 | acc = lpf4 + temp2*lp_hidamp_k + hpf4*lp_lodamp_k; 379 | acc = acc * rv_time * rv_time_scaler; 380 | 381 | lp_allp_out = acc; 382 | 383 | // channel L: 384 | #ifdef TAP1_MODULATED 385 | temp16 = (lp_dly1_idx + lp_dly1_offset_L + (lfo1_out_cos>>LFO_FRAC_BITS)) % (sizeof(lp_dly1_buf)/sizeof(float32_t)); 386 | temp1 = lp_dly1_buf[temp16++]; // sample now 387 | if (temp16 >= sizeof(lp_dly1_buf)/sizeof(float32_t)) temp16 = 0; 388 | temp2 = lp_dly1_buf[temp16]; // sample next 389 | input = (float32_t)(lfo1_out_cos & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k 390 | acc = (temp1*(1.0f-input) + temp2*input)* 0.8f; 391 | #else 392 | temp16 = (lp_dly1_idx + lp_dly1_offset_L) % (sizeof(lp_dly1_buf)/sizeof(float32_t)); 393 | acc = lp_dly1_buf[temp16]* 0.8f; 394 | #endif 395 | 396 | 397 | #ifdef TAP2_MODULATED 398 | temp16 = (lp_dly2_idx + lp_dly2_offset_L + (lfo1_out_sin>>LFO_FRAC_BITS)) % (sizeof(lp_dly2_buf)/sizeof(float32_t)); 399 | temp1 = lp_dly2_buf[temp16++]; 400 | if (temp16 >= sizeof(lp_dly2_buf)/sizeof(float32_t)) temp16 = 0; 401 | temp2 = lp_dly2_buf[temp16]; 402 | input = (float32_t)(lfo1_out_sin & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k 403 | acc += (temp1*(1.0f-input) + temp2*input)* 0.7f; 404 | #else 405 | temp16 = (lp_dly2_idx + lp_dly2_offset_L) % (sizeof(lp_dly2_buf)/sizeof(float32_t)); 406 | acc += (temp1*(1.0f-input) + temp2*input)* 0.6f; 407 | #endif 408 | 409 | temp16 = (lp_dly3_idx + lp_dly3_offset_L + (lfo2_out_cos>>LFO_FRAC_BITS)) % (sizeof(lp_dly3_buf)/sizeof(float32_t)); 410 | temp1 = lp_dly3_buf[temp16++]; 411 | if (temp16 >= sizeof(lp_dly3_buf)/sizeof(float32_t)) temp16 = 0; 412 | temp2 = lp_dly3_buf[temp16]; 413 | input = (float32_t)(lfo2_out_cos & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k 414 | acc += (temp1*(1.0f-input) + temp2*input)* 0.6f; 415 | 416 | temp16 = (lp_dly4_idx + lp_dly4_offset_L + (lfo2_out_sin>>LFO_FRAC_BITS)) % (sizeof(lp_dly4_buf)/sizeof(float32_t)); 417 | temp1 = lp_dly4_buf[temp16++]; 418 | if (temp16 >= sizeof(lp_dly4_buf)/sizeof(float32_t)) temp16 = 0; 419 | temp2 = lp_dly4_buf[temp16]; 420 | input = (float32_t)(lfo2_out_sin & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k 421 | acc += (temp1*(1.0f-input) + temp2*input)* 0.5f; 422 | 423 | // Master lowpass filter 424 | temp1 = acc - master_lowpass_l; 425 | master_lowpass_l += temp1 * master_lowpass_f; 426 | 427 | outblockL->data[i] = master_lowpass_l; //sat16(output * 30, 0); 428 | 429 | // Channel R 430 | #ifdef TAP1_MODULATED 431 | temp16 = (lp_dly1_idx + lp_dly1_offset_R + (lfo2_out_cos>>LFO_FRAC_BITS)) % (sizeof(lp_dly1_buf)/sizeof(float32_t)); 432 | temp1 = lp_dly1_buf[temp16++]; // sample now 433 | if (temp16 >= sizeof(lp_dly1_buf)/sizeof(float32_t)) temp16 = 0; 434 | temp2 = lp_dly1_buf[temp16]; // sample next 435 | input = (float32_t)(lfo2_out_cos & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k 436 | 437 | acc = (temp1*(1.0f-input) + temp2*input)* 0.8f; 438 | #else 439 | temp16 = (lp_dly1_idx + lp_dly1_offset_R) % (sizeof(lp_dly1_buf)/sizeof(float32_t)); 440 | acc = lp_dly1_buf[temp16] * 0.8f; 441 | #endif 442 | #ifdef TAP2_MODULATED 443 | temp16 = (lp_dly2_idx + lp_dly2_offset_R + (lfo1_out_cos>>LFO_FRAC_BITS)) % (sizeof(lp_dly2_buf)/sizeof(float32_t)); 444 | temp1 = lp_dly2_buf[temp16++]; 445 | if (temp16 >= sizeof(lp_dly2_buf)/sizeof(float32_t)) temp16 = 0; 446 | temp2 = lp_dly2_buf[temp16]; 447 | input = (float32_t)(lfo1_out_cos & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k 448 | acc += (temp1*(1.0f-input) + temp2*input)* 0.7f; 449 | #else 450 | temp16 = (lp_dly2_idx + lp_dly2_offset_R) % (sizeof(lp_dly2_buf)/sizeof(float32_t)); 451 | acc += (temp1*(1.0f-input) + temp2*input)* 0.7f; 452 | #endif 453 | temp16 = (lp_dly3_idx + lp_dly3_offset_R + (lfo2_out_sin>>LFO_FRAC_BITS)) % (sizeof(lp_dly3_buf)/sizeof(float32_t)); 454 | temp1 = lp_dly3_buf[temp16++]; 455 | if (temp16 >= sizeof(lp_dly3_buf)/sizeof(float32_t)) temp16 = 0; 456 | temp2 = lp_dly3_buf[temp16]; 457 | input = (float32_t)(lfo2_out_sin & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k 458 | acc += (temp1*(1.0f-input) + temp2*input)* 0.6f; 459 | 460 | temp16 = (lp_dly4_idx + lp_dly4_offset_R + (lfo2_out_cos>>LFO_FRAC_BITS)) % (sizeof(lp_dly4_buf)/sizeof(float32_t)); 461 | temp1 = lp_dly4_buf[temp16++]; 462 | if (temp16 >= sizeof(lp_dly4_buf)/sizeof(float32_t)) temp16 = 0; 463 | temp2 = lp_dly4_buf[temp16]; 464 | input = (float32_t)(lfo2_out_cos & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k 465 | acc += (temp1*(1.0f-input) + temp2*input)* 0.5f; 466 | 467 | // Master lowpass filter 468 | temp1 = acc - master_lowpass_r; 469 | master_lowpass_r += temp1 * master_lowpass_f; 470 | outblockR->data[i] = master_lowpass_r; 471 | 472 | } 473 | AudioStream_F32::transmit(outblockL, 0); 474 | AudioStream_F32::transmit(outblockR, 1); 475 | AudioStream_F32::release(outblockL); 476 | AudioStream_F32::release(outblockR); 477 | AudioStream_F32::release((audio_block_f32_t *)blockL); 478 | AudioStream_F32::release((audio_block_f32_t *)blockR); 479 | #endif 480 | } 481 | -------------------------------------------------------------------------------- /Hx_PlateReverb_F32/effect_platervbstereo_F32.h: -------------------------------------------------------------------------------- 1 | /* Stereo plate reverb for Teensy 4 2 | * 32bit float version for OpenAudio_ArduinoLibrary: 3 | * https://github.com/chipaudette/OpenAudio_ArduinoLibrary 4 | * Author: Piotr Zapart 5 | * www.hexefx.com 6 | * 7 | * Copyright (c) 2023 by Piotr Zapart 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in all 17 | * copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | * SOFTWARE. 26 | */ 27 | 28 | /*** 29 | * Algorithm based on plate reverbs developed for SpinSemi FV-1 DSP chip 30 | * 31 | * Allpass + modulated delay line based lush plate reverb 32 | * 33 | * Input parameters are float in range 0.0 to 1.0: 34 | * 35 | * size - reverb time 36 | * hidamp - hi frequency loss in the reverb tail 37 | * lodamp - low frequency loss in the reverb tail 38 | * lowpass - output/master lowpass filter, useful for darkening the reverb sound 39 | * diffusion - lower settings will make the reverb tail more "echoey". 40 | * freeze - infinite reverb tail effect 41 | * 42 | */ 43 | #ifndef _EFFECT_PLATERVBSTEREO_F32_H 44 | #define _EFFECT_PLATERVBSTEREO_F32_H 45 | 46 | #include 47 | #include "Audio.h" 48 | #include "AudioStream_F32.h" 49 | #include "arm_math.h" 50 | 51 | // if uncommented will place all the buffers in the DMAMEM section ofd the memory 52 | // works with single instance of the reverb only 53 | #define REVERB_F32_USE_DMAMEM 54 | 55 | /*** 56 | * Loop delay modulation: comment/uncomment to switch sin/cos 57 | * modulation for the 1st or 2nd tap, 3rd tap is always modulated 58 | * more modulation means more chorus type sounding reverb tail 59 | */ 60 | //#define TAP1_MODULATED 61 | #define TAP2_MODULATED 62 | 63 | class AudioEffectPlateReverb_F32 : public AudioStream_F32 64 | { 65 | public: 66 | AudioEffectPlateReverb_F32(); 67 | virtual void update(); 68 | 69 | /** 70 | * @brief reverb time. Please not the hidamp/lodamp params also control how 71 | * fast the reverb tail dacays. 72 | * 73 | * @param n time, range 0.0f to 1.0f 74 | */ 75 | void size(float n) 76 | { 77 | n = constrain(n, 0.0f, 1.0f); 78 | n = map (n, 0.0f, 1.0f, 0.2f, rv_time_k_max); 79 | float32_t attn = map(n, 0.0f, rv_time_k_max, 0.5f, 0.25f); 80 | __disable_irq(); 81 | rv_time_k = n; 82 | input_attn = attn; 83 | __enable_irq(); 84 | } 85 | /** 86 | * @brief amount treble loss in the reverb tail 87 | * 88 | * @param n range 0.0f to 1.0f 89 | */ 90 | void hidamp(float n) 91 | { 92 | n = constrain(n, 0.0f, 1.0f); 93 | __disable_irq(); 94 | lp_hidamp_k = 1.0f - n; 95 | __enable_irq(); 96 | } 97 | /** 98 | * @brief amount og bass lost in the reverb tail 99 | * 100 | * @param n range 0.0f to 1.0f 101 | */ 102 | void lodamp(float n) 103 | { 104 | n = constrain(n, 0.0f, 1.0f); 105 | __disable_irq(); 106 | lp_lodamp_k = -n; 107 | rv_time_scaler = 1.0f - n * 0.12f; // limit the max reverb time, otherwise it will clip 108 | __enable_irq(); 109 | } 110 | /** 111 | * @brief lowpass filter applied to the reverb output 112 | * use it to darken the reverb tail 113 | * 114 | * @param n range 0.0f to 1.0f 115 | */ 116 | void lowpass(float n) 117 | { 118 | n = constrain(n, 0.0f, 1.0f); 119 | n = map(n*n*n, 0.0f, 1.0f, 1.0f, 0.05f); 120 | master_lowpass_f = n; 121 | } 122 | /** 123 | * @brief "echoiness" of the reverb sound. Lower values produce more 124 | * echo like reflections. 1.0 produces a lush plate reverb. 125 | * Use this parameter together with the "size" to produce different type 126 | * of reverb sounds. Ie, small size (0) + low diffusion create a room type 127 | * reverb sound. 128 | * 129 | * @param n 130 | */ 131 | void diffusion(float n) 132 | { 133 | n = constrain(n, 0.0f, 1.0f); 134 | n = map(n, 0.0f, 1.0f, 0.005f, 0.65f); 135 | __disable_irq(); 136 | in_allp_k = n; 137 | loop_allp_k = n; 138 | __enable_irq(); 139 | } 140 | /** 141 | * @brief Freezes the reverb tank by cutting off the input signal 142 | * and increasing the reverb time coeff to 1.0 giving an infinite 143 | * tail. 144 | * 145 | * @param state freeze on (true) or off (false) 146 | */ 147 | void freeze(bool state) 148 | { 149 | flags.freeze = state; 150 | if (state) 151 | { 152 | rv_time_k_tmp = rv_time_k; // store the settings 153 | lp_lodamp_k_tmp = lp_lodamp_k; 154 | lp_hidamp_k_tmp = lp_hidamp_k; 155 | 156 | __disable_irq(); 157 | rv_time_k = freeze_rvtime_k; 158 | input_attn = freeze_ingain; 159 | rv_time_scaler = 1.0f; 160 | lp_lodamp_k = freeze_lodamp_k; 161 | lp_hidamp_k = freeze_hidamp_k; 162 | 163 | __enable_irq(); 164 | } 165 | else 166 | { 167 | float32_t attn = map(rv_time_k_tmp, 0.0f, rv_time_k_max, 0.5f, 0.25f); // recalc the in attenuation 168 | float32_t sc = 1.0f + lp_lodamp_k_tmp * 0.12f; 169 | __disable_irq(); 170 | rv_time_k = rv_time_k_tmp; // restore the value 171 | input_attn = attn; 172 | rv_time_scaler = sc; 173 | lp_hidamp_k = lp_hidamp_k_tmp; 174 | lp_lodamp_k = lp_lodamp_k_tmp; 175 | __enable_irq(); 176 | } 177 | } 178 | /** 179 | * @brief Toggles the freeze function and returns the current state 180 | * 181 | * @return true toggle resulted in freeze on 182 | * @return false toggle resulted in freeze off 183 | */ 184 | bool freeze_tgl() {flags.freeze ^= 1; freeze(flags.freeze); return flags.freeze;} 185 | 186 | bool freeze_get() {return flags.freeze;} 187 | 188 | float32_t size_get(void) {return rv_time_k;} 189 | 190 | bool bypass_get(void) {return flags.bypass;} 191 | void bypass_set(bool state) 192 | { 193 | flags.bypass = state; 194 | if (state) freeze(false); // disable freeze in bypass mode 195 | } 196 | bool bypass_tgl(void) 197 | { 198 | flags.bypass ^= 1; 199 | if (flags.bypass) freeze(false); // disable freeze in bypass mode 200 | return flags.bypass; 201 | } 202 | 203 | private: 204 | struct flags_t 205 | { 206 | unsigned bypass: 1; 207 | unsigned freeze: 1; 208 | unsigned shimmer: 1; // maybe will be added at some point 209 | unsigned cleanup_done: 1; 210 | }flags; 211 | 212 | audio_block_f32_t *inputQueueArray_f32[2]; 213 | float32_t input_attn; 214 | float32_t in_allp_k; // input allpass coeff 215 | #ifndef REVERB_F32_USE_DMAMEM 216 | float32_t in_allp1_bufL[224]; // input allpass buffers 217 | float32_t in_allp2_bufL[420]; 218 | float32_t in_allp3_bufL[856]; 219 | float32_t in_allp4_bufL[1089]; 220 | #endif 221 | uint16_t in_allp1_idxL; 222 | uint16_t in_allp2_idxL; 223 | uint16_t in_allp3_idxL; 224 | uint16_t in_allp4_idxL; 225 | float32_t in_allp_out_L; // L allpass chain output 226 | #ifndef REVERB_F32_USE_DMAMEM 227 | float32_t in_allp1_bufR[156]; // input allpass buffers 228 | float32_t in_allp2_bufR[520]; 229 | float32_t in_allp3_bufR[956]; 230 | float32_t in_allp4_bufR[1289]; 231 | #endif 232 | uint16_t in_allp1_idxR; 233 | uint16_t in_allp2_idxR; 234 | uint16_t in_allp3_idxR; 235 | uint16_t in_allp4_idxR; 236 | float32_t in_allp_out_R; // R allpass chain output 237 | #ifndef REVERB_F32_USE_DMAMEM 238 | float32_t lp_allp1_buf[2303]; // loop allpass buffers 239 | float32_t lp_allp2_buf[2905]; 240 | float32_t lp_allp3_buf[3175]; 241 | float32_t lp_allp4_buf[2398]; 242 | #endif 243 | uint16_t lp_allp1_idx; 244 | uint16_t lp_allp2_idx; 245 | uint16_t lp_allp3_idx; 246 | uint16_t lp_allp4_idx; 247 | float32_t loop_allp_k; // loop allpass coeff 248 | float32_t lp_allp_out; 249 | #ifndef REVERB_F32_USE_DMAMEM 250 | float32_t lp_dly1_buf[3423]; 251 | float32_t lp_dly2_buf[4589]; 252 | float32_t lp_dly3_buf[4365]; 253 | float32_t lp_dly4_buf[3698]; 254 | #endif 255 | uint16_t lp_dly1_idx; 256 | uint16_t lp_dly2_idx; 257 | uint16_t lp_dly3_idx; 258 | uint16_t lp_dly4_idx; 259 | 260 | const uint16_t lp_dly1_offset_L = 201; // delay line tap offets 261 | const uint16_t lp_dly2_offset_L = 145; 262 | const uint16_t lp_dly3_offset_L = 1897; 263 | const uint16_t lp_dly4_offset_L = 280; 264 | 265 | const uint16_t lp_dly1_offset_R = 1897; 266 | const uint16_t lp_dly2_offset_R = 1245; 267 | const uint16_t lp_dly3_offset_R = 487; 268 | const uint16_t lp_dly4_offset_R = 780; 269 | 270 | float32_t lp_hidamp_k; // loop high band damping coeff 271 | float32_t lp_hidamp_k_tmp; 272 | float32_t lp_lodamp_k; // loop low baand damping coeff 273 | float32_t lp_lodamp_k_tmp; 274 | 275 | float32_t lpf1; // lowpass filters 276 | float32_t lpf2; 277 | float32_t lpf3; 278 | float32_t lpf4; 279 | 280 | float32_t hpf1; // highpass filters 281 | float32_t hpf2; 282 | float32_t hpf3; 283 | float32_t hpf4; 284 | 285 | float32_t lp_lowpass_f; // loop lowpass scaled frequency 286 | float32_t lp_hipass_f; // loop highpass scaled frequency 287 | 288 | float32_t master_lowpass_f; 289 | float32_t master_lowpass_l; 290 | float32_t master_lowpass_r; 291 | 292 | const float32_t rv_time_k_max = 0.95f; 293 | float32_t rv_time_k; // reverb time coeff 294 | float32_t rv_time_k_tmp; // temp for restoring original value after freeze_off 295 | float32_t rv_time_scaler; // with high lodamp settings lower the max reverb time to avoid clipping 296 | 297 | uint32_t lfo1_phase_acc; // LFO 1 298 | uint32_t lfo1_adder; 299 | 300 | uint32_t lfo2_phase_acc; // LFO 2 301 | uint32_t lfo2_adder; 302 | 303 | const float32_t freeze_rvtime_k = 1.0f; 304 | const float32_t freeze_ingain = 0.0f; 305 | const float32_t freeze_lodamp_k = 0.0f; 306 | const float32_t freeze_hidamp_k = 1.0f; 307 | }; 308 | 309 | #endif // _EFFECT_PLATEREV_H 310 | -------------------------------------------------------------------------------- /Hx_PlateReverb_F32/plateReverb_schm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hexeguitar/t40fx/115f81f2ddf35346e38e9b6b0a6d341aad9b2e76/Hx_PlateReverb_F32/plateReverb_schm.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Piotr Zapart 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # t40fx 2 | ## OBSOLETE 3 | Further work on Teensy audio components is available as new extention for the OpenAudio_ArduinoLibrary: 4 | library: https://github.com/hexeguitar/hexefx_audiolib_F32 5 | examples: https://github.com/hexeguitar/hexefx_audiolib_F32_examples 6 | 7 | Teensy4.x Audio Lib Components: 8 | ## [Stereo Guitar Tone Stack F32](https://github.com/hexeguitar/t40fx/tree/main/HX_ToneStack_F32 "Stereo Guitar Tone Stack F32") - version for [OpenAudio_ArduinoLibrary](https://github.com/chipaudette/OpenAudio_ArduinoLibrary "OpenAudio_ArduinoLibrary") 9 | ## [Mono to Stereo F32](https://github.com/hexeguitar/t40fx/tree/main/Hx_MonoToStereo_F32 "Mono to Stereo F32") - version for [OpenAudio_ArduinoLibrary](https://github.com/chipaudette/OpenAudio_ArduinoLibrary "OpenAudio_ArduinoLibrary") 10 | ## [Stereo Plate Reverb F32](https://github.com/hexeguitar/t40fx/tree/main/Hx_PlateReverb_F32 "Stereo Plate reverb") - version for [OpenAudio_ArduinoLibrary](https://github.com/chipaudette/OpenAudio_ArduinoLibrary "OpenAudio_ArduinoLibrary") 11 | ## [Stereo Plate Reverb](https://github.com/hexeguitar/t40fx/tree/main/Hx_PlateReverb "Stereo Plate reverb") 12 | ## [Mono InfinitePhaser F32](https://github.com/hexeguitar/t40fx/tree/main/Hx_InfinitePhaser_F32 "Mono InfinitePhaser F32") 13 | ## [Mono 12 stage Phaser](https://github.com/hexeguitar/t40fx/tree/main/Hx_Phaser "Mono 12 stage phaser") 14 | 15 | ___ 16 | 17 | Copyright 12.2023 by Piotr Zapart 18 | www.hexefx.com 19 | --------------------------------------------------------------------------------