diff --git a/protobufs b/protobufs index 2bb457de4..0a5959958 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 2bb457de42cd6d5c2a6e7d8a4992fc6c90e37de5 +Subproject commit 0a59599589bc77763e76302e533a4c4bfa5ec80e diff --git a/src/mesh/generated/localonly.pb.h b/src/mesh/generated/localonly.pb.h index b8c8b27ef..596bb7b9a 100644 --- a/src/mesh/generated/localonly.pb.h +++ b/src/mesh/generated/localonly.pb.h @@ -151,7 +151,7 @@ extern const pb_msgdesc_t LocalModuleConfig_msg; /* Maximum encoded size of messages (where known) */ #define LocalConfig_size 387 -#define LocalModuleConfig_size 364 +#define LocalModuleConfig_size 358 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/mesh/generated/module_config.pb.h b/src/mesh/generated/module_config.pb.h index 189eb2ca5..4b2e29f17 100644 --- a/src/mesh/generated/module_config.pb.h +++ b/src/mesh/generated/module_config.pb.h @@ -63,8 +63,6 @@ typedef enum _ModuleConfig_CannedMessageConfig_InputEventChar { /* Struct definitions */ typedef struct _ModuleConfig_AudioConfig { bool codec2_enabled; - uint8_t mic_chan; - uint8_t amp_pin; uint8_t ptt_pin; ModuleConfig_AudioConfig_Audio_Baud bitrate; uint8_t i2s_ws; @@ -187,7 +185,7 @@ extern "C" { /* Initializer values for message structs */ #define ModuleConfig_init_default {0, {ModuleConfig_MQTTConfig_init_default}} #define ModuleConfig_MQTTConfig_init_default {0, "", "", "", 0, 0} -#define ModuleConfig_AudioConfig_init_default {0, 0, 0, 0, _ModuleConfig_AudioConfig_Audio_Baud_MIN, 0, 0, 0, 0} +#define ModuleConfig_AudioConfig_init_default {0, 0, _ModuleConfig_AudioConfig_Audio_Baud_MIN, 0, 0, 0, 0} #define ModuleConfig_SerialConfig_init_default {0, 0, 0, 0, _ModuleConfig_SerialConfig_Serial_Baud_MIN, 0, _ModuleConfig_SerialConfig_Serial_Mode_MIN} #define ModuleConfig_ExternalNotificationConfig_init_default {0, 0, 0, 0, 0, 0, 0} #define ModuleConfig_StoreForwardConfig_init_default {0, 0, 0, 0, 0} @@ -196,7 +194,7 @@ extern "C" { #define ModuleConfig_CannedMessageConfig_init_default {0, 0, 0, 0, _ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _ModuleConfig_CannedMessageConfig_InputEventChar_MIN, 0, 0, "", 0} #define ModuleConfig_init_zero {0, {ModuleConfig_MQTTConfig_init_zero}} #define ModuleConfig_MQTTConfig_init_zero {0, "", "", "", 0, 0} -#define ModuleConfig_AudioConfig_init_zero {0, 0, 0, 0, _ModuleConfig_AudioConfig_Audio_Baud_MIN, 0, 0, 0, 0} +#define ModuleConfig_AudioConfig_init_zero {0, 0, _ModuleConfig_AudioConfig_Audio_Baud_MIN, 0, 0, 0, 0} #define ModuleConfig_SerialConfig_init_zero {0, 0, 0, 0, _ModuleConfig_SerialConfig_Serial_Baud_MIN, 0, _ModuleConfig_SerialConfig_Serial_Mode_MIN} #define ModuleConfig_ExternalNotificationConfig_init_zero {0, 0, 0, 0, 0, 0, 0} #define ModuleConfig_StoreForwardConfig_init_zero {0, 0, 0, 0, 0} @@ -206,14 +204,12 @@ extern "C" { /* Field tags (for use in manual encoding/decoding) */ #define ModuleConfig_AudioConfig_codec2_enabled_tag 1 -#define ModuleConfig_AudioConfig_mic_chan_tag 2 -#define ModuleConfig_AudioConfig_amp_pin_tag 3 -#define ModuleConfig_AudioConfig_ptt_pin_tag 4 -#define ModuleConfig_AudioConfig_bitrate_tag 5 -#define ModuleConfig_AudioConfig_i2s_ws_tag 6 -#define ModuleConfig_AudioConfig_i2s_sd_tag 7 -#define ModuleConfig_AudioConfig_i2s_din_tag 8 -#define ModuleConfig_AudioConfig_i2s_sck_tag 9 +#define ModuleConfig_AudioConfig_ptt_pin_tag 2 +#define ModuleConfig_AudioConfig_bitrate_tag 3 +#define ModuleConfig_AudioConfig_i2s_ws_tag 4 +#define ModuleConfig_AudioConfig_i2s_sd_tag 5 +#define ModuleConfig_AudioConfig_i2s_din_tag 6 +#define ModuleConfig_AudioConfig_i2s_sck_tag 7 #define ModuleConfig_CannedMessageConfig_rotary1_enabled_tag 1 #define ModuleConfig_CannedMessageConfig_inputbroker_pin_a_tag 2 #define ModuleConfig_CannedMessageConfig_inputbroker_pin_b_tag 3 @@ -300,14 +296,12 @@ X(a, STATIC, SINGULAR, BOOL, json_enabled, 6) #define ModuleConfig_AudioConfig_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, BOOL, codec2_enabled, 1) \ -X(a, STATIC, SINGULAR, UINT32, mic_chan, 2) \ -X(a, STATIC, SINGULAR, UINT32, amp_pin, 3) \ -X(a, STATIC, SINGULAR, UINT32, ptt_pin, 4) \ -X(a, STATIC, SINGULAR, UENUM, bitrate, 5) \ -X(a, STATIC, SINGULAR, UINT32, i2s_ws, 6) \ -X(a, STATIC, SINGULAR, UINT32, i2s_sd, 7) \ -X(a, STATIC, SINGULAR, UINT32, i2s_din, 8) \ -X(a, STATIC, SINGULAR, UINT32, i2s_sck, 9) +X(a, STATIC, SINGULAR, UINT32, ptt_pin, 2) \ +X(a, STATIC, SINGULAR, UENUM, bitrate, 3) \ +X(a, STATIC, SINGULAR, UINT32, i2s_ws, 4) \ +X(a, STATIC, SINGULAR, UINT32, i2s_sd, 5) \ +X(a, STATIC, SINGULAR, UINT32, i2s_din, 6) \ +X(a, STATIC, SINGULAR, UINT32, i2s_sck, 7) #define ModuleConfig_AudioConfig_CALLBACK NULL #define ModuleConfig_AudioConfig_DEFAULT NULL @@ -395,7 +389,7 @@ extern const pb_msgdesc_t ModuleConfig_CannedMessageConfig_msg; #define ModuleConfig_CannedMessageConfig_fields &ModuleConfig_CannedMessageConfig_msg /* Maximum encoded size of messages (where known) */ -#define ModuleConfig_AudioConfig_size 25 +#define ModuleConfig_AudioConfig_size 19 #define ModuleConfig_CannedMessageConfig_size 49 #define ModuleConfig_ExternalNotificationConfig_size 22 #define ModuleConfig_MQTTConfig_size 169 diff --git a/src/modules/esp32/AudioModule.cpp b/src/modules/esp32/AudioModule.cpp index a47f47bd8..b1433a0ae 100644 --- a/src/modules/esp32/AudioModule.cpp +++ b/src/modules/esp32/AudioModule.cpp @@ -8,8 +8,6 @@ #include "Router.h" #include "FSCommon.h" -#include -#include #include /* @@ -23,23 +21,14 @@ Basic Usage: 1) Enable the module by setting audio.codec2_enabled to 1. - 2a) Set the pins for the I2S interface if you want to use digital audio. Recommended on TLora is I2S_WS 13/I2S_SD 15/I2S_SIN 2/I2S_SCK 14 - 2b) Set the pins (audio.mic_pin / audio.amp_pin) for your preferred microphone and amplifier GPIO pins if you want to use analog audio. - This is rather heavy on the CPU and not recommended. - On tbeam, best use: - audio.mic_chan 6 (GPIO 34) - audio.amp_pin 14 - audio.ptt_pin 39 + 2) Set the pins for the I2S interface. Recommended on TLora is I2S_WS 13/I2S_SD 15/I2S_SIN 2/I2S_SCK 14 3) Set audio.bitrate to the desired codec2 rate (CODEC2_3200, CODEC2_2400, CODEC2_1600, CODEC2_1400, CODEC2_1300, CODEC2_1200, CODEC2_700, CODEC2_700B) KNOWN PROBLEMS - * Until the module is initilized by the startup sequence, the amp_pin is in a floating - state. This may produce a bit of "noise". + * Half Duplex * Will not work on NRF and the Linux device targets (yet?). */ -#define AMIC 6 -#define AAMP 14 #define PTT_PIN 39 ButterworthFilter hp_filter(240, 8000, ButterworthFilter::ButterworthFilter::Highpass, 1); @@ -60,67 +49,10 @@ AudioModule *audioModule; #define YIELD_FROM_ISR(x) portYIELD_FROM_ISR(x) #endif -portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; -hw_timer_t* adcTimer = NULL; - //int16_t 1KHz sine test tone int16_t Sine1KHz[8] = { -21210 , -30000, -21210, 0 , 21210 , 30000 , 21210, 0 }; int Sine1KHz_index = 0; -int IRAM_ATTR local_adc1_read(int channel) { - uint16_t adc_value; -#if CONFIG_IDF_TARGET_ESP32S3 - SENS.sar_meas1_ctrl2.sar1_en_pad = (1 << channel); // only one channel is selected - while (SENS.sar_slave_addr1.meas_status != 0); - SENS.sar_meas1_ctrl2.meas1_start_sar = 0; - SENS.sar_meas1_ctrl2.meas1_start_sar = 1; - while (SENS.sar_meas1_ctrl2.meas1_done_sar == 0); - adc_value = SENS.sar_meas1_ctrl2.meas1_data_sar; -#else - SENS.sar_meas_start1.sar1_en_pad = (1 << channel); // only one channel is selected - while (SENS.sar_slave_addr1.meas_status != 0); - SENS.sar_meas_start1.meas1_start_sar = 0; - SENS.sar_meas_start1.meas1_start_sar = 1; - while (SENS.sar_meas_start1.meas1_done_sar == 0); - adc_value = SENS.sar_meas_start1.meas1_data_sar; -#endif - return adc_value; -} - -IRAM_ATTR void am_onTimer() -{ - portENTER_CRITICAL_ISR(&timerMux); //Enter crital code without interruptions - if ((audioModule->radio_state == RadioState::tx) && (!moduleConfig.audio.i2s_sd)) { - audioModule->adc_buffer[audioModule->adc_buffer_index++] = (16 * local_adc1_read(audioModule->mic_chan)) - 32768; - - //If you want to test with a 1KHz tone, comment the line above and descomment the three lines below - - // adc_buffer[adc_buffer_index++] = Sine1KHz[Sine1KHz_index++]; - // if (Sine1KHz_index >= 8) - // Sine1KHz_index = 0; - - if (audioModule->adc_buffer_index == audioModule->adc_buffer_size) { - audioModule->adc_buffer_index = 0; - memcpy((void*)audioModule->speech, (void*)audioModule->adc_buffer, 2 * audioModule->adc_buffer_size); - BaseType_t xHigherPriorityTaskWoken = pdFALSE; - vTaskNotifyGiveFromISR(codec2HandlerTask, &xHigherPriorityTaskWoken); - if (xHigherPriorityTaskWoken == pdTRUE) - YIELD_FROM_ISR(xHigherPriorityTaskWoken); - } - } else if ((audioModule->radio_state == RadioState::rx) && (!moduleConfig.audio.i2s_din)) { - // ESP32-S3 does not have DAC support -#if !defined(CONFIG_IDF_TARGET_ESP32S3) - - //Get a value from audio_fifo and convert it to 0 - 255 to play it in the ADC - if (audioModule->fifo.get(&audioModule->sample)) - audioModule->rx_raw_audio_value = (uint8_t)((audioModule->sample + 32768) / 256); - - dacWrite(moduleConfig.audio.amp_pin ? moduleConfig.audio.amp_pin : AAMP, audioModule->rx_raw_audio_value); -#endif - } - portEXIT_CRITICAL_ISR(&timerMux); // exit critical code -} - void run_codec2(void* parameter) { while (true) { @@ -128,10 +60,12 @@ void run_codec2(void* parameter) if (tcount != 0) { if (audioModule->radio_state == RadioState::tx) { - + + // Apply the TX filter for (int i = 0; i < audioModule->adc_buffer_size; i++) audioModule->speech[i] = (int16_t)hp_filter.Update((float)audioModule->speech[i]); + // Encode the audio codec2_encode(audioModule->codec2, audioModule->tx_encode_frame + audioModule->tx_encode_frame_index, audioModule->speech); //increment the pointer where the encoded frame must be saved @@ -141,7 +75,7 @@ void run_codec2(void* parameter) if (audioModule->tx_encode_frame_index == audioModule->encode_frame_size) { //Transmit it - DEBUG_MSG("♪♫♪ Sending %d bytes\n", audioModule->encode_frame_size); + DEBUG_MSG("♪♫♪ Sending %d codec2 bytes\n", audioModule->encode_frame_size); audioModule->sendPayload(); audioModule->tx_encode_frame_index = 0; } @@ -152,16 +86,8 @@ void run_codec2(void* parameter) { //Decode the codec2 frame codec2_decode(audioModule->codec2, audioModule->output_buffer, audioModule->rx_encode_frame + i); - - if (moduleConfig.audio.i2s_din) { - size_t bytesOut = 0; - - i2s_write(I2S_PORT, &audioModule->output_buffer, audioModule->adc_buffer_size, &bytesOut, pdMS_TO_TICKS(40)); - } else { - //Put the decoded audio in the fifo - for (int j = 0; j < audioModule->adc_buffer_size; j++) - audioModule->fifo.put(audioModule->output_buffer[j]); - } + size_t bytesOut = 0; + i2s_write(I2S_PORT, &audioModule->output_buffer, audioModule->adc_buffer_size, &bytesOut, pdMS_TO_TICKS(500)); } } } @@ -171,7 +97,6 @@ void run_codec2(void* parameter) AudioModule::AudioModule() : SinglePortModule("AudioModule", PortNum_AUDIO_APP), concurrency::OSThread("AudioModule") { if ((moduleConfig.audio.codec2_enabled) && (myRegion->audioPermitted)) { - fifo.init(); DEBUG_MSG("♪♫♪ Setting up codec2 in mode %u", (moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1); codec2 = codec2_create((moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1); codec2_set_lpc_post_filter(codec2, 1, 0, 0.8, 0.2); @@ -191,86 +116,39 @@ int32_t AudioModule::runOnce() if ((moduleConfig.audio.codec2_enabled) && (myRegion->audioPermitted)) { esp_err_t res; if (firstTime) { - // if we have I2S_SD defined, take samples from digital mic. I2S_DIN means digital output to amp. - if (moduleConfig.audio.i2s_sd || moduleConfig.audio.i2s_din) { - // Set up I2S Processor configuration. This will produce 16bit samples at 8 kHz instead of 12 from the ADC - DEBUG_MSG("♪♫♪ Initializing I2S SD: %d DIN: %d WS: %d SCK:%d\n", moduleConfig.audio.i2s_sd, moduleConfig.audio.i2s_din, moduleConfig.audio.i2s_ws, moduleConfig.audio.i2s_sck); - i2s_config_t i2s_config = { - .mode = (i2s_mode_t)(I2S_MODE_MASTER | (moduleConfig.audio.i2s_sd ? I2S_MODE_RX : 0) | (moduleConfig.audio.i2s_din ? I2S_MODE_TX : 0)), - .sample_rate = 8000, - .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, - .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT, - .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_STAND_I2S), - .intr_alloc_flags = 0, - .dma_buf_count = 8, - .dma_buf_len = adc_buffer_size, // 320 * 2 bytes - .use_apll = false, - .tx_desc_auto_clear = true, - .fixed_mclk = 0 - }; - res = i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL); - if(res != ESP_OK) - DEBUG_MSG("♪♫♪ Failed to install I2S driver: %d\n", res); + // Set up I2S Processor configuration. This will produce 16bit samples at 8 kHz instead of 12 from the ADC + DEBUG_MSG("♪♫♪ Initializing I2S SD: %d DIN: %d WS: %d SCK:%d\n", moduleConfig.audio.i2s_sd, moduleConfig.audio.i2s_din, moduleConfig.audio.i2s_ws, moduleConfig.audio.i2s_sck); + i2s_config_t i2s_config = { + .mode = (i2s_mode_t)(I2S_MODE_MASTER | (moduleConfig.audio.i2s_sd ? I2S_MODE_RX : 0) | (moduleConfig.audio.i2s_din ? I2S_MODE_TX : 0)), + .sample_rate = 8000, + .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, + .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT, + .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_STAND_I2S), + .intr_alloc_flags = 0, + .dma_buf_count = 8, + .dma_buf_len = adc_buffer_size, // 320 * 2 bytes + .use_apll = false, + .tx_desc_auto_clear = true, + .fixed_mclk = 0 + }; + res = i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL); + if(res != ESP_OK) + DEBUG_MSG("♪♫♪ Failed to install I2S driver: %d\n", res); - const i2s_pin_config_t pin_config = { - .bck_io_num = moduleConfig.audio.i2s_sck, - .ws_io_num = moduleConfig.audio.i2s_ws, - .data_out_num = moduleConfig.audio.i2s_din ? moduleConfig.audio.i2s_din : I2S_PIN_NO_CHANGE, - .data_in_num = moduleConfig.audio.i2s_sd ? moduleConfig.audio.i2s_sd : I2S_PIN_NO_CHANGE - }; - res = i2s_set_pin(I2S_PORT, &pin_config); - if(res != ESP_OK) - DEBUG_MSG("♪♫♪ Failed to set I2S pin config: %d\n", res); + const i2s_pin_config_t pin_config = { + .bck_io_num = moduleConfig.audio.i2s_sck, + .ws_io_num = moduleConfig.audio.i2s_ws, + .data_out_num = moduleConfig.audio.i2s_din ? moduleConfig.audio.i2s_din : I2S_PIN_NO_CHANGE, + .data_in_num = moduleConfig.audio.i2s_sd ? moduleConfig.audio.i2s_sd : I2S_PIN_NO_CHANGE + }; + res = i2s_set_pin(I2S_PORT, &pin_config); + if(res != ESP_OK) + DEBUG_MSG("♪♫♪ Failed to set I2S pin config: %d\n", res); - res = i2s_start(I2S_PORT); - if(res != ESP_OK) - DEBUG_MSG("♪♫♪ Failed to start I2S: %d\n", res); - } - - // setup analogue DAC only if we don't use I2S for output. This is not available on ESP32-S3 -#if !defined(CONFIG_IDF_TARGET_ESP32S3) - if (!moduleConfig.audio.i2s_din) - DEBUG_MSG("♪♫♪ Initializing DAC on Pin %u\n", moduleConfig.audio.amp_pin ? moduleConfig.audio.amp_pin : AAMP); -#else - if (!moduleConfig.audio.i2s_din) { - DEBUG_MSG("♪♫♪ ESP32-S3 does not support DAC. Audio Module Disabled.\n"); - return INT32_MAX; - } -#endif + res = i2s_start(I2S_PORT); + if(res != ESP_OK) + DEBUG_MSG("♪♫♪ Failed to start I2S: %d\n", res); - if (!moduleConfig.audio.i2s_sd) { - // Set up ADC if we don't have a digital microphone. - DEBUG_MSG("♪♫♪ Initializing ADC on Channel %u\n", moduleConfig.audio.mic_chan ? moduleConfig.audio.mic_chan : AMIC); - mic_chan = moduleConfig.audio.mic_chan ? (adc1_channel_t)(int)moduleConfig.audio.mic_chan : (adc1_channel_t)AMIC; - adc1_config_width(ADC_WIDTH_12Bit); - adc1_config_channel_atten(mic_chan, ADC_ATTEN_DB_6); - adc1_get_raw(mic_chan); - } - - if ((!moduleConfig.audio.i2s_sd) || (!moduleConfig.audio.i2s_din)) { - // Start a timer at 8kHz to sample the ADC and play the audio on the DAC, but only if we have analogue audio to process - uint32_t cpufreq = getCpuFrequencyMhz(); - switch (cpufreq){ - case 160: - adcTimer = timerBegin(3, 1000, true); // 160 MHz / 1000 = 160KHz - break; - case 240: - adcTimer = timerBegin(3, 1500, true); // 240 MHz / 1500 = 160KHz - break; - case 320: - adcTimer = timerBegin(3, 2000, true); // 320 MHz / 2000 = 160KHz - break; - case 80: - default: - adcTimer = timerBegin(3, 500, true); // 80 MHz / 500 = 160KHz - break; - } - DEBUG_MSG("♪♫♪ Timer CPU Frequency: %u MHz\n", cpufreq); - timerAttachInterrupt(adcTimer, &am_onTimer, false); - timerAlarmWrite(adcTimer, 20, true); // Interrupts when counter == 20, 8.000 times a second - timerAlarmEnable(adcTimer); - } - radio_state = RadioState::rx; // Configure PTT input @@ -289,7 +167,7 @@ int32_t AudioModule::runOnce() if (radio_state == RadioState::tx) { if (tx_encode_frame_index > 0) { // Send the incomplete frame - DEBUG_MSG("♪♫♪ Sending %d bytes (incomplete)\n", tx_encode_frame_index); + DEBUG_MSG("♪♫♪ Sending %d codec2 bytes (incomplete)\n", tx_encode_frame_index); sendPayload(); } DEBUG_MSG("♪♫♪ PTT released, switching to RX\n"); @@ -297,7 +175,7 @@ int32_t AudioModule::runOnce() radio_state = RadioState::rx; } } - if ((radio_state == RadioState::tx) && moduleConfig.audio.i2s_sd) { + if (radio_state == RadioState::tx) { // Get I2S data from the microphone and place in data buffer size_t bytesIn = 0; res = i2s_read(I2S_PORT, adc_buffer + adc_buffer_index, adc_buffer_size - adc_buffer_index, &bytesIn, pdMS_TO_TICKS(40)); // wait 40ms for audio to arrive. diff --git a/src/modules/esp32/AudioModule.h b/src/modules/esp32/AudioModule.h index c81c5c310..aee09a0b0 100644 --- a/src/modules/esp32/AudioModule.h +++ b/src/modules/esp32/AudioModule.h @@ -7,11 +7,9 @@ #include "NodeDB.h" #include #include -// #include #include #include #include -#include #define ADC_BUFFER_SIZE_MAX 320 @@ -32,11 +30,9 @@ class AudioModule : public SinglePortModule, private concurrency::OSThread int encode_codec_size = 0; int encode_frame_size = 0; volatile RadioState radio_state = RadioState::rx; - FastAudioFIFO fifo; + struct CODEC2* codec2 = NULL; - int16_t sample; - adc1_channel_t mic_chan = (adc1_channel_t)0; - uint8_t rx_raw_audio_value = 127; + // int16_t sample; AudioModule(); @@ -49,7 +45,6 @@ class AudioModule : public SinglePortModule, private concurrency::OSThread int encode_frame_num = 0; bool firstTime = true; - virtual int32_t runOnce() override; virtual MeshPacket *allocReply() override;