1 /************************** BEGIN esp32audio.h **************************/ 2 /************************************************************************ 3 FAUST Architecture File 4 Copyright (C) 2020 GRAME, Centre National de Creation Musicale 5 --------------------------------------------------------------------- 6 This Architecture section is free software; you can redistribute it 7 and/or modify it under the terms of the GNU General Public License 8 as published by the Free Software Foundation; either version 3 of 9 the License, or (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program; If not, see <http://www.gnu.org/licenses/>. 18 19 EXCEPTION : As a special exception, you may create a larger work 20 that contains this FAUST architecture section and distribute 21 that work under terms of your choice, so long as this FAUST 22 architecture section is not modified. 23 ************************************************************************/ 24 25 #ifndef __esp32audio__ 26 #define __esp32audio__ 27 28 #include <utility> 29 30 #include "freertos/FreeRTOS.h" 31 #include "freertos/task.h" 32 #include "driver/i2s.h" 33 34 #include "faust/audio/audio.h" 35 #include "faust/dsp/dsp.h" 36 37 #define MULT_S32 2147483647 38 #define DIV_S32 4.6566129e-10 39 #define clip(sample) std::max(-MULT_S32, std::min(MULT_S32, ((int32_t)(sample * MULT_S32)))); 40 41 #define AUDIO_MAX_CHAN 2 42 43 class esp32audio : public audio { 44 45 private: 46 47 int fSampleRate; 48 int fBufferSize; 49 int fNumInputs; 50 int fNumOutputs; 51 float** fInChannel; 52 float** fOutChannel; 53 TaskHandle_t fHandle; 54 dsp* fDSP; 55 bool fRunning; 56 57 template <int INPUTS, int OUTPUTS> audioTask()58 void audioTask() 59 { 60 while (fRunning) { 61 if (INPUTS > 0) { 62 // Read from the card 63 int32_t samples_data_in[AUDIO_MAX_CHAN*fBufferSize]; 64 size_t bytes_read = 0; 65 i2s_read((i2s_port_t)0, &samples_data_in, AUDIO_MAX_CHAN*sizeof(float)*fBufferSize, &bytes_read, portMAX_DELAY); 66 67 // Convert and copy inputs 68 if (INPUTS == AUDIO_MAX_CHAN) { 69 // if stereo 70 for (int i = 0; i < fBufferSize; i++) { 71 fInChannel[0][i] = (float)samples_data_in[i*AUDIO_MAX_CHAN]*DIV_S32; 72 fInChannel[1][i] = (float)samples_data_in[i*AUDIO_MAX_CHAN+1]*DIV_S32; 73 } 74 } else { 75 // otherwise only first channel 76 for (int i = 0; i < fBufferSize; i++) { 77 fInChannel[0][i] = (float)samples_data_in[i*AUDIO_MAX_CHAN]*DIV_S32; 78 } 79 } 80 } 81 82 // Call DSP 83 fDSP->compute(fBufferSize, fInChannel, fOutChannel); 84 85 // Convert and copy outputs 86 int32_t samples_data_out[AUDIO_MAX_CHAN*fBufferSize]; 87 if (OUTPUTS == AUDIO_MAX_CHAN) { 88 // if stereo 89 for (int i = 0; i < fBufferSize; i++) { 90 samples_data_out[i*AUDIO_MAX_CHAN] = clip(fOutChannel[0][i]); 91 samples_data_out[i*AUDIO_MAX_CHAN+1] = clip(fOutChannel[1][i]); 92 } 93 } else { 94 // otherwise only first channel 95 for (int i = 0; i < fBufferSize; i++) { 96 samples_data_out[i*AUDIO_MAX_CHAN] = clip(fOutChannel[0][i]); 97 samples_data_out[i*AUDIO_MAX_CHAN+1] = samples_data_out[i*AUDIO_MAX_CHAN]; 98 } 99 } 100 101 // Write to the card 102 size_t bytes_written = 0; 103 i2s_write((i2s_port_t)0, &samples_data_out, AUDIO_MAX_CHAN*sizeof(float)*fBufferSize, &bytes_written, portMAX_DELAY); 104 } 105 106 // Task has to deleted itself beforee returning 107 vTaskDelete(nullptr); 108 } 109 destroy()110 void destroy() 111 { 112 for (int i = 0; i < fNumInputs; i++) { 113 delete[] fInChannel[i]; 114 } 115 delete [] fInChannel; 116 117 for (int i = 0; i < fNumOutputs; i++) { 118 delete[] fOutChannel[i]; 119 } 120 delete [] fOutChannel; 121 } 122 audioTaskHandler(void * arg)123 static void audioTaskHandler(void* arg) 124 { 125 esp32audio* audio = static_cast<esp32audio*>(arg); 126 127 if (audio->fNumInputs == 0 && audio->fNumOutputs == 1) { 128 audio->audioTask<0,1>(); 129 } else if (audio->fNumInputs == 0 && audio->fNumOutputs == 2) { 130 audio->audioTask<0,2>(); 131 } else if (audio->fNumInputs == 1 && audio->fNumOutputs == 1) { 132 audio->audioTask<1,1>(); 133 } else if (audio->fNumInputs == 1 && audio->fNumOutputs == 2) { 134 audio->audioTask<1,2>(); 135 } else if (audio->fNumInputs == 2 && audio->fNumOutputs == 1) { 136 audio->audioTask<2,1>(); 137 } else if (audio->fNumInputs == 2 && audio->fNumOutputs == 2) { 138 audio->audioTask<2,2>(); 139 } 140 } 141 142 public: 143 esp32audio(int srate,int bsize)144 esp32audio(int srate, int bsize): 145 fSampleRate(srate), 146 fBufferSize(bsize), 147 fNumInputs(0), 148 fNumOutputs(0), 149 fInChannel(nullptr), 150 fOutChannel(nullptr), 151 fHandle(nullptr), 152 fDSP(nullptr), 153 fRunning(false) 154 { 155 i2s_pin_config_t pin_config; 156 #if TTGO_TAUDIO 157 pin_config = { 158 .bck_io_num = 33, 159 .ws_io_num = 25, 160 .data_out_num = 26, 161 .data_in_num = 27 162 }; 163 #elif A1S_BOARD 164 pin_config = { 165 .bck_io_num = 27, 166 .ws_io_num = 26, 167 .data_out_num = 25, 168 .data_in_num = 35 169 }; 170 #elif LYRA_T 171 pin_config = { 172 .bck_io_num = 5, 173 .ws_io_num = 25, 174 .data_out_num = 26, 175 .data_in_num = 35 176 }; 177 #else // Default 178 pin_config = { 179 .bck_io_num = 33, 180 .ws_io_num = 25, 181 .data_out_num = 26, 182 .data_in_num = 27 183 }; 184 #endif 185 186 #if A1S_BOARD 187 i2s_config_t i2s_config = { 188 .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_RX), 189 .sample_rate = fSampleRate, 190 .bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT, 191 .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, 192 .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB), 193 .intr_alloc_flags = ESP_INTR_FLAG_LEVEL3, // high interrupt priority 194 .dma_buf_count = 3, 195 .dma_buf_len = fBufferSize, 196 .use_apll = true 197 }; 198 #else // default 199 i2s_config_t i2s_config = { 200 .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_RX), 201 .sample_rate = fSampleRate, 202 .bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT, 203 .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, 204 .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB), 205 .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // high interrupt priority 206 .dma_buf_count = 3, 207 .dma_buf_len = fBufferSize, 208 .use_apll = false 209 }; 210 #endif 211 i2s_driver_install((i2s_port_t)0, &i2s_config, 0, nullptr); 212 i2s_set_pin((i2s_port_t)0, &pin_config); 213 PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1); 214 REG_WRITE(PIN_CTRL, 0xFFFFFFF0); 215 } 216 ~esp32audio()217 virtual ~esp32audio() 218 { 219 destroy(); 220 } 221 init(const char * name,dsp * dsp)222 virtual bool init(const char* name, dsp* dsp) 223 { 224 destroy(); 225 226 fDSP = dsp; 227 fNumInputs = fDSP->getNumInputs(); 228 fNumOutputs = fDSP->getNumOutputs(); 229 230 fDSP->init(fSampleRate); 231 232 if (fNumInputs > 0) { 233 fInChannel = new FAUSTFLOAT*[fNumInputs]; 234 for (int i = 0; i < fNumInputs; i++) { 235 fInChannel[i] = new FAUSTFLOAT[fBufferSize]; 236 } 237 } else { 238 fInChannel = nullptr; 239 } 240 241 if (fNumOutputs > 0) { 242 fOutChannel = new FAUSTFLOAT*[fNumOutputs]; 243 for (int i = 0; i < fNumOutputs; i++) { 244 fOutChannel[i] = new FAUSTFLOAT[fBufferSize]; 245 } 246 } else { 247 fOutChannel = nullptr; 248 } 249 250 return true; 251 } 252 start()253 virtual bool start() 254 { 255 if (!fRunning) { 256 fRunning = true; 257 return (xTaskCreatePinnedToCore(audioTaskHandler, "Faust DSP Task", 4096, (void*)this, 24, &fHandle, 0) == pdPASS); 258 } else { 259 return true; 260 } 261 } 262 stop()263 virtual void stop() 264 { 265 if (fRunning) { 266 fRunning = false; 267 vTaskDelay(1/portTICK_PERIOD_MS); 268 fHandle = nullptr; 269 } 270 } 271 getBufferSize()272 virtual int getBufferSize() { return fBufferSize; } getSampleRate()273 virtual int getSampleRate() { return fSampleRate; } 274 getNumInputs()275 virtual int getNumInputs() { return AUDIO_MAX_CHAN; } getNumOutputs()276 virtual int getNumOutputs() { return AUDIO_MAX_CHAN; } 277 278 // Returns the average proportion of available CPU being spent inside the audio callbacks (between 0 and 1.0). getCPULoad()279 virtual float getCPULoad() { return 0.f; } 280 281 }; 282 283 #endif 284 /************************** END esp32audio.h **************************/ 285