trunk roundhouse kick

This commit is contained in:
Thomas Göttgens
2023-01-21 14:34:29 +01:00
parent 6cf18b7d07
commit 51b2c431d9
234 changed files with 4989 additions and 5101 deletions

View File

@@ -2,11 +2,11 @@
#include "configuration.h"
#if defined(ARCH_ESP32)
#include "AudioModule.h"
#include "FSCommon.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "RTC.h"
#include "Router.h"
#include "FSCommon.h"
#ifdef OLED_RU
#include "graphics/fonts/OLEDDisplayFontsRU.h"
@@ -17,14 +17,15 @@
A interface to send raw codec2 audio data over the mesh network. Based on the example code from the ESP32_codec2 project.
https://github.com/deulis/ESP32_Codec2
Codec 2 is a low-bitrate speech audio codec (speech coding)
Codec 2 is a low-bitrate speech audio codec (speech coding)
that is patent free and open source develop by David Grant Rowe.
http://www.rowetel.com/ and https://github.com/drowe67/codec2
Basic Usage:
1) Enable the module by setting audio.codec2_enabled to 1.
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)
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
* Half Duplex
@@ -64,10 +65,10 @@ AudioModule *audioModule;
#define FONT_HEIGHT_MEDIUM fontHeight(FONT_MEDIUM)
#define FONT_HEIGHT_LARGE fontHeight(FONT_LARGE)
void run_codec2(void* parameter)
void run_codec2(void *parameter)
{
// 4 bytes of header in each frame hex c0 de c2 plus the bitrate
memcpy(audioModule->tx_encode_frame,&audioModule->tx_header,sizeof(audioModule->tx_header));
memcpy(audioModule->tx_encode_frame, &audioModule->tx_header, sizeof(audioModule->tx_header));
LOG_INFO("Starting codec2 task\n");
@@ -79,11 +80,11 @@ void run_codec2(void* parameter)
for (int i = 0; i < audioModule->adc_buffer_size; i++)
audioModule->speech[i] = (int16_t)hp_filter.Update((float)audioModule->speech[i]);
codec2_encode(audioModule->codec2, audioModule->tx_encode_frame + audioModule->tx_encode_frame_index, audioModule->speech);
audioModule->tx_encode_frame_index += audioModule->encode_codec_size;
codec2_encode(audioModule->codec2, audioModule->tx_encode_frame + audioModule->tx_encode_frame_index,
audioModule->speech);
audioModule->tx_encode_frame_index += audioModule->encode_codec_size;
if (audioModule->tx_encode_frame_index == (audioModule->encode_frame_size + sizeof(audioModule->tx_header)))
{
if (audioModule->tx_encode_frame_index == (audioModule->encode_frame_size + sizeof(audioModule->tx_header))) {
LOG_INFO("Sending %d codec2 bytes\n", audioModule->encode_frame_size);
audioModule->sendPayload();
audioModule->tx_encode_frame_index = sizeof(audioModule->tx_header);
@@ -92,19 +93,18 @@ void run_codec2(void* parameter)
if (audioModule->radio_state == RadioState::rx) {
size_t bytesOut = 0;
if (memcmp(audioModule->rx_encode_frame, &audioModule->tx_header, sizeof(audioModule->tx_header)) == 0) {
for (int i = 4; i < audioModule->rx_encode_frame_index; i += audioModule->encode_codec_size)
{
for (int i = 4; i < audioModule->rx_encode_frame_index; i += audioModule->encode_codec_size) {
codec2_decode(audioModule->codec2, audioModule->output_buffer, audioModule->rx_encode_frame + i);
i2s_write(I2S_PORT, &audioModule->output_buffer, audioModule->adc_buffer_size, &bytesOut, pdMS_TO_TICKS(500));
i2s_write(I2S_PORT, &audioModule->output_buffer, audioModule->adc_buffer_size, &bytesOut,
pdMS_TO_TICKS(500));
}
} else {
// if the buffer header does not match our own codec, make a temp decoding setup.
CODEC2* tmp_codec2 = codec2_create(audioModule->rx_encode_frame[3]);
CODEC2 *tmp_codec2 = codec2_create(audioModule->rx_encode_frame[3]);
codec2_set_lpc_post_filter(tmp_codec2, 1, 0, 0.8, 0.2);
int tmp_encode_codec_size = (codec2_bits_per_frame(tmp_codec2) + 7) / 8;
int tmp_adc_buffer_size = codec2_samples_per_frame(tmp_codec2);
for (int i = 4; i < audioModule->rx_encode_frame_index; i += tmp_encode_codec_size)
{
for (int i = 4; i < audioModule->rx_encode_frame_index; i += tmp_encode_codec_size) {
codec2_decode(tmp_codec2, audioModule->output_buffer, audioModule->rx_encode_frame + i);
i2s_write(I2S_PORT, &audioModule->output_buffer, tmp_adc_buffer_size, &bytesOut, pdMS_TO_TICKS(500));
}
@@ -125,16 +125,18 @@ AudioModule::AudioModule() : SinglePortModule("AudioModule", PortNum_AUDIO_APP),
// moduleConfig.audio.ptt_pin = 39;
if ((moduleConfig.audio.codec2_enabled) && (myRegion->audioPermitted)) {
LOG_INFO("Setting up codec2 in mode %u", (moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1);
LOG_INFO("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);
memcpy(tx_header.magic,c2_magic,sizeof(c2_magic));
memcpy(tx_header.magic, c2_magic, sizeof(c2_magic));
tx_header.mode = (moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1;
codec2_set_lpc_post_filter(codec2, 1, 0, 0.8, 0.2);
encode_codec_size = (codec2_bits_per_frame(codec2) + 7) / 8;
encode_frame_num = (Constants_DATA_PAYLOAD_LEN - sizeof(tx_header)) / encode_codec_size;
encode_frame_size = encode_frame_num * encode_codec_size; // max 233 bytes + 4 header bytes
adc_buffer_size = codec2_samples_per_frame(codec2);
LOG_INFO(" using %d frames of %d bytes for a total payload length of %d bytes\n", encode_frame_num, encode_codec_size, encode_frame_size);
LOG_INFO(" using %d frames of %d bytes for a total payload length of %d bytes\n", encode_frame_num, encode_codec_size,
encode_frame_size);
xTaskCreate(&run_codec2, "codec2_task", 30000, NULL, 5, &codec2HandlerTask);
} else {
disable();
@@ -151,17 +153,18 @@ void AudioModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int
display->setFont(FONT_SMALL);
display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
display->setColor(BLACK);
display->drawStringf(0 + x, 0 + y, buffer, "Codec2 Mode %d Audio", (moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1);
display->drawStringf(0 + x, 0 + y, buffer, "Codec2 Mode %d Audio",
(moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1);
display->setColor(WHITE);
display->setFont(FONT_LARGE);
display->setTextAlignment(TEXT_ALIGN_CENTER);
switch (radio_state) {
case RadioState::tx:
display->drawString(display->getWidth() / 2 + x, (display->getHeight() - FONT_HEIGHT_SMALL) / 2 + y, "PTT");
break;
default:
display->drawString(display->getWidth() / 2 + x, (display->getHeight() - FONT_HEIGHT_SMALL) / 2 + y, "Receive");
break;
case RadioState::tx:
display->drawString(display->getWidth() / 2 + x, (display->getHeight() - FONT_HEIGHT_SMALL) / 2 + y, "PTT");
break;
default:
display->drawString(display->getWidth() / 2 + x, (display->getHeight() - FONT_HEIGHT_SMALL) / 2 + y, "Receive");
break;
}
}
@@ -171,38 +174,37 @@ int32_t AudioModule::runOnce()
esp_err_t res;
if (firstTime) {
// Set up I2S Processor configuration. This will produce 16bit samples at 8 kHz instead of 12 from the ADC
LOG_INFO("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
};
LOG_INFO("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)
if (res != ESP_OK)
LOG_ERROR("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
};
.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)
if (res != ESP_OK)
LOG_ERROR("Failed to set I2S pin config: %d\n", res);
res = i2s_start(I2S_PORT);
if(res != ESP_OK)
if (res != ESP_OK)
LOG_ERROR("Failed to start I2S: %d\n", res);
radio_state = RadioState::rx;
// Configure PTT input
@@ -237,13 +239,14 @@ int32_t AudioModule::runOnce()
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.
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.
if (res == ESP_OK) {
adc_buffer_index += bytesIn;
if (adc_buffer_index == adc_buffer_size) {
adc_buffer_index = 0;
memcpy((void*)speech, (void*)adc_buffer, 2 * adc_buffer_size);
memcpy((void *)speech, (void *)adc_buffer, 2 * adc_buffer_size);
// Notify run_codec2 task that the buffer is ready.
radio_state = RadioState::tx;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
@@ -258,7 +261,6 @@ int32_t AudioModule::runOnce()
} else {
return disable();
}
}
MeshPacket *AudioModule::allocReply()
@@ -281,7 +283,7 @@ void AudioModule::sendPayload(NodeNum dest, bool wantReplies)
p->to = dest;
p->decoded.want_response = wantReplies;
p->want_ack = false; // Audio is shoot&forget. No need to wait for ACKs.
p->want_ack = false; // Audio is shoot&forget. No need to wait for ACKs.
p->priority = MeshPacket_Priority_MAX; // Audio is important, because realtime
p->decoded.payload.size = tx_encode_frame_index;