diff --git a/AudioConfigESP32.h b/AudioConfigESP32.h index fd3fcba76..26d19476d 100644 --- a/AudioConfigESP32.h +++ b/AudioConfigESP32.h @@ -1,6 +1,9 @@ #ifndef AUDIOCONFIGESP32_H #define AUDIOCONFIGESP32_H +#include +#include + #if not IS_ESP32() #error This header should be included for ESP32 architecture, only #endif @@ -9,37 +12,69 @@ #error HIFI mode is not available for this CPU architecture (but check ESP32_AUDIO_OUT_MODE, and PDM_RESOLUTION) #endif -// Audio output options +// Audio IO options #define INTERNAL_DAC 1 // output using internal DAC via I2S, output on pin 26 #define PT8211_DAC 2 // output using an external PT8211 DAC via I2S -#define PDM_VIA_I2S 3 // output PDM coded sample on the I2S data pin (pin 33, by default, configurable, below) +#define PDM_VIA_I2S 3 // output PDM coded sample on the I2S data pin (pin 33, by default, configurable, below) +#define I2S_DAC_AND_I2S_ADC 4 // output using an external DAC , input with exteran ADC - both via I2S -// Set output mode +// Set output & input mode #define ESP32_AUDIO_OUT_MODE INTERNAL_DAC // For external I2S output, only: I2S_PINS #define ESP32_I2S_BCK_PIN 26 #define ESP32_I2S_WS_PIN 25 #define ESP32_I2S_DATA_PIN 33 +#define ESP32_I2S_DATA_PIN_IN 32 -#include + +// Preferred I2S port: note that this might be taken into consideration if there is a choice +// The Internal ADC and DAC only work on port 0 and can not be used at the same time! const i2s_port_t i2s_num = I2S_NUM_0; + + +// Select the data range of the ADC +//#define ADC_VALUE(in) (in) // no scaling +#define ADC_VALUE(in) (0.015625*(in + 32768)) // scale to 0 to 1023 + +// Optionally select a higher Sample Rate +//#define ESP32_AUDIO_RATE 8000 + + +///-------------------------------------------------------------------------------------------------------- /// User config end. Do not modify below this line +///-------------------------------------------------------------------------------------------------------- +#if !EXTERNAL_AUDIO_OUTPUT +# if (ESP32_AUDIO_OUT_MODE == INTERNAL_DAC) +# define AUDIO_BITS 8 +# define PDM_RESOLUTION 1 +# define IS_INTERNAL_DAC +# elif (ESP32_AUDIO_OUT_MODE == PT8211_DAC || ESP32_AUDIO_OUT_MODE == I2S_DAC_AND_I2S_ADC) +# define AUDIO_BITS 16 +# define PDM_RESOLUTION 1 +# define IS_I2S_DAC +# elif (ESP32_AUDIO_OUT_MODE == PDM_VIA_I2S) +# define AUDIO_BITS 16 +# define PDM_RESOLUTION 4 +# define IS_PDM +# else +# error Invalid output mode configured in AudioConfigESP32.h +# endif -#if (ESP32_AUDIO_OUT_MODE == INTERNAL_DAC) -#define AUDIO_BITS 8 -#define PDM_RESOLUTION 1 -#elif (ESP32_AUDIO_OUT_MODE == PT8211_DAC) -#define AUDIO_BITS 16 -#define PDM_RESOLUTION 1 -#elif (ESP32_AUDIO_OUT_MODE == PDM_VIA_I2S) -#define AUDIO_BITS 16 -#define PDM_RESOLUTION 4 -#else -#error Invalid output mode configured in AudioConfigESP32.h -#endif +# define AUDIO_BIAS ((uint16_t) 1<<(AUDIO_BITS-1)) +# define BYPASS_MOZZI_OUTPUT_BUFFER true + +// Use defined rate as new AUDIO_RATE_PLATFORM_DEFAULT +# ifdef ESP32_AUDIO_RATE +# undef AUDIO_RATE_PLATFORM_DEFAULT +# define AUDIO_RATE_PLATFORM_DEFAULT ESP32_AUDIO_RATE +# endif -#define AUDIO_BIAS ((uint16_t) 1<<(AUDIO_BITS-1)) -#define BYPASS_MOZZI_OUTPUT_BUFFER true +// If ADC is available we support getAudioInput() +# if ESP32_AUDIO_OUT_MODE == I2S_DAC_AND_I2S_ADC +# define USE_CUSTOM_AUDIO_INPUT +int getAudioInput(); +# endif +#endif #endif // #ifndef AUDIOCONFIGESP_H diff --git a/MozziGuts.cpp b/MozziGuts.cpp index b6aad9dda..b9f65f31b 100644 --- a/MozziGuts.cpp +++ b/MozziGuts.cpp @@ -140,7 +140,7 @@ int mozziAnalogRead(uint8_t pin) { #endif } -#if (USE_AUDIO_INPUT == true) +#if (USE_AUDIO_INPUT == true && !defined(USE_CUSTOM_AUDIO_INPUT)) static AudioOutputStorage_t audio_input; // holds the latest audio from input_buffer AudioOutputStorage_t getAudioInput() { return audio_input; } #endif diff --git a/MozziGuts_impl_ESP32.hpp b/MozziGuts_impl_ESP32.hpp index 7657eb62c..a5cb72efd 100644 --- a/MozziGuts_impl_ESP32.hpp +++ b/MozziGuts_impl_ESP32.hpp @@ -9,14 +9,19 @@ * Attribution-NonCommercial-ShareAlike 4.0 International License. * */ - #if !(IS_ESP32()) # error "Wrong implementation included for this platform" #endif +#include "AudioConfigESP32.h" +#include // for I2S-based output modes +#include // for EXTERNAL_AUDIO_OUTPUT + +static const char module[]="Mozzi-ESP32"; + + +// ////// BEGIN analog input code //////// +#define getADCReading() 0; -////// BEGIN analog input code //////// -//#define MOZZI_FAST_ANALOG_IMPLEMENTED // not yet -#define getADCReading() 0 #define channelNumToIndex(channel) channel uint8_t adcPinToChannelNum(uint8_t pin) { return pin; @@ -36,32 +41,110 @@ void setupMozziADC(int8_t speed) { ////// END analog input code //////// +#if (EXTERNAL_AUDIO_OUTPUT) // for external audio output, set up a timer running a audio rate + +void CACHED_FUNCTION_ATTR timer0_audio_output_isr(void *) { + TIMERG0.int_clr_timers.t0 = 1; + TIMERG0.hw_timer[0].config.alarm_en = 1; + defaultAudioOutput(); +} -//// BEGIN AUDIO OUTPUT code /////// -#include // for I2S-based output modes -#include // for EXTERNAL_AUDIO_OUTPUT +static void startAudio() { + static intr_handle_t s_timer_handle; + const int div = 2; + timer_config_t config = { + .alarm_en = (timer_alarm_t)true, + .counter_en = (timer_start_t)false, + .intr_type = (timer_intr_mode_t) TIMER_INTR_LEVEL, + .counter_dir = TIMER_COUNT_UP, + .auto_reload = (timer_autoreload_t) true, + .divider = div // For max available precision: The APB_CLK clock signal is running at 80 MHz, i.e. 2/80 uS per tick + // Min acceptable value is 2 + }; + timer_init(TIMER_GROUP_0, TIMER_0, &config); + timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0); + timer_set_alarm_value(TIMER_GROUP_0, TIMER_0, 80000000UL / AUDIO_RATE / div); + timer_enable_intr(TIMER_GROUP_0, TIMER_0); + timer_isr_register(TIMER_GROUP_0, TIMER_0, &timer0_audio_output_isr, nullptr, 0, &s_timer_handle); + timer_start(TIMER_GROUP_0, TIMER_0); +} + +void stopMozzi() { + timer_pause(TIMER_GROUP_0, TIMER_0); + timer_disable_intr(TIMER_GROUP_0, TIMER_0); +} + +#else // I2S Output -#if (EXTERNAL_AUDIO_OUTPUT != true) -# include "AudioConfigESP32.h" // On ESP32 we cannot test wether the DMA buffer has room. Instead, we have to use a one-sample mini buffer. In each iteration we // _try_ to write that sample to the DMA buffer, and if successful, we can buffer the next sample. Somewhat cumbersome, but works. // TODO: Should ESP32 gain an implemenation of i2s_available(), we should switch to using that, instead. static bool _esp32_can_buffer_next = true; -# if (ESP32_AUDIO_OUT_MODE == INTERNAL_DAC) +# if defined(IS_INTERNAL_DAC) static uint16_t _esp32_prev_sample[2]; -# define ESP_SAMPLE_SIZE (2*sizeof(uint16_t)) -# elif (ESP32_AUDIO_OUT_MODE == PT8211_DAC) +# elif defined(IS_I2S_DAC) static int16_t _esp32_prev_sample[2]; -# define ESP_SAMPLE_SIZE (2*sizeof(int16_t)) -# elif (ESP32_AUDIO_OUT_MODE == PDM_VIA_I2S) +# elif defined(IS_PDM) static uint32_t _esp32_prev_sample[PDM_RESOLUTION]; -# define ESP_SAMPLE_SIZE (PDM_RESOLUTION*sizeof(uint32_t)) # endif + +/// Make sure that we provide a supported port +int getI2SPort(){ + switch (ESP32_AUDIO_OUT_MODE){ + case INTERNAL_DAC: + return 0; + case PDM_VIA_I2S: + return 0; + case PT8211_DAC: + return i2s_num; + case I2S_DAC_AND_I2S_ADC: + return i2s_num; + } + ESP_LOGE(module, "%s - %s", __func__, "ESP32_AUDIO_OUT_MODE invalid"); + return -1; +} + + +/// Determine the I2S Output Mode (and input mode if on same port) +int getI2SMode(){ + switch (ESP32_AUDIO_OUT_MODE){ + case INTERNAL_DAC: + ESP_LOGD(module, "%s: %s", __func__, "I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN"); + return I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN; + case PDM_VIA_I2S: + ESP_LOGD(module, "%s: %s", __func__, "I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_PDM"); + return I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_PDM; + case PT8211_DAC: + ESP_LOGD(module, "%s: %s", __func__, "I2S_MODE_MASTER | I2S_MODE_TX"); + return I2S_MODE_MASTER | I2S_MODE_TX; + case I2S_DAC_AND_I2S_ADC: + ESP_LOGD(module, "%s: %s", __func__, "I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_RX"); + return I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_RX; + } + ESP_LOGE(module, "%s: %s", __func__, "ESP32_AUDIO_OUT_MODE invalid"); + return -1; +} + +/// @brief Reads a sample from the I2S buffer. I2S is stereo, so we combine the result to one single sample +int getAudioInput() { + static const int i2s_port = getI2SPort(); + if (i2s_port==-1) return 0; + + int16_t tmp[2]; + size_t result; + esp_err_t rc = i2s_read((i2s_port_t)i2s_port, tmp, sizeof(tmp), &result, portMAX_DELAY); + return ADC_VALUE((tmp[0]+tmp[1]) / 2); +} + + inline bool esp32_tryWriteSample() { + static i2s_port_t port = (i2s_port_t) getI2SPort(); size_t bytes_written; - i2s_write(i2s_num, &_esp32_prev_sample, ESP_SAMPLE_SIZE, &bytes_written, 0); + int write_len = sizeof(_esp32_prev_sample); + i2s_write(port, &_esp32_prev_sample, write_len, &bytes_written, 0); + //ESP_LOGD(module, "%s port:%d, len: %d, written: %d, value: %d", __func__, port, write_len, bytes_written, _esp32_prev_sample[0]); return (bytes_written != 0); } @@ -72,7 +155,7 @@ inline bool canBufferAudioOutput() { } inline void audioOutput(const AudioOutput f) { -# if (ESP32_AUDIO_OUT_MODE == INTERNAL_DAC) +# if defined(IS_INTERNAL_DAC) _esp32_prev_sample[0] = (f.l() + AUDIO_BIAS) << 8; # if (AUDIO_CHANNELS > 1) _esp32_prev_sample[1] = (f.r() + AUDIO_BIAS) << 8; @@ -80,7 +163,7 @@ inline void audioOutput(const AudioOutput f) { // For simplicity of code, even in mono, we're writing stereo samples _esp32_prev_sample[1] = _esp32_prev_sample[0]; # endif -# elif (ESP32_AUDIO_OUT_MODE == PDM_VIA_I2S) +# elif defined(IS_PDM) for (uint8_t i=0; i