deulis / ESP32_Codec2

Codec2 library for ESP32 (Arduino)
GNU General Public License v3.0
70 stars 23 forks source link

codec2_encode hangs MCU and trips watchdog #4

Open Bartvelp opened 2 years ago

Bartvelp commented 2 years ago

Hi, Thanks for your work porting codec2, I'm trying to use your library, and it succesfully compiles. The following code produces hangs on the line executing codec2_encode maybe I am overlooking something but what exactly?

#include <codec2.h>
#include "soc/rtc_wdt.h"

struct CODEC2* codec2_state;

int16_t sine1KHz[8] = { -21210 , -30000, -21210, 0 , 21210 , 30000 , 21210, 0 };

void setup() {
  Serial.begin(115200);
  Serial.println();
  Serial.println("Started codec2 sample");
  rtc_wdt_protect_off();
  rtc_wdt_disable();
  // Set bitrate
  codec2_state = codec2_create(CODEC2_MODE_1600);
  // Set some tuning parameters
  // codec2_set_lpc_post_filter(codec2_state, 1, 0, 0.8, 0.2);

  // Encode a (fixed size) input buffer of type int16_t to a (fixed size) output buffer
  // CODEC2_MODE_1600 encodes 320 speech samples (320 * 2 = 640 bytes / 40ms of speech) into 8 bytes (64 bits)
  int16_t audioBuf[320];
  uint8_t c2Buf[8];
  // Fill buffer with a sine wave
  for (int i = 0; i < 320; i++) audioBuf[i] = sine1KHz[i % 8];

  Serial.println("Encoding 1kHz sine wave");
  long startTime = millis();
  codec2_encode(codec2_state, c2Buf, audioBuf);

  Serial.println("Done encoding, took ms: " + String(millis() - startTime));
  startTime = millis();
  codec2_decode(codec2_state, audioBuf, c2Buf); 
  Serial.println("Done decoding, took ms: " + String(millis() - startTime));
}

void loop() {

}

EDIT: it can also throw:

Guru Meditation Error: Core  1 panic'ed (Unknown reason)
Core 1 register dump:
[nothing is actually dumped]
Bartvelp commented 2 years ago

By the magic of adding Serial prints to the source I've further pinpointed the issue to analyse_one_frame But now it triggers a stackoverflow detection?

14:17:23.580 -> analyse_one_frame -1
14:17:23.580 -> analyse_one_frame 0
14:17:23.580 -> analyse_one_frame 0.5
14:17:23.580 -> analyse_one_f***ERROR*** A stack overflow in task loopTask has been detected.

By the code:

void analyse_one_frame(struct CODEC2 *c2, MODEL *model, short speech[])
{
    serialLog("analyse_one_frame -1");

    COMP    Sw[FFT_ENC];
    COMP    Sw_[FFT_ENC];
    COMP    Ew[FFT_ENC];
    float   pitch;
    int     i;
    PROFILE_VAR(dft_start, nlp_start, model_start, two_stage, estamps);

    serialLog("analyse_one_frame 0");

    /* Read input speech */

    for(i=0; i<M-N; i++)
      c2->Sn[i] = c2->Sn[i+N];
    serialLog("analyse_one_frame 0.5");
    for(i=0; i<N; i++)
      c2->Sn[i+M-N] = speech[i];    
    serialLog("analyse_one_frame 1");

    PROFILE_SAMPLE(dft_start);  

    dft_speech(c2->fft_fwd_cfg, Sw, c2->Sn, c2->w);
    PROFILE_SAMPLE_AND_LOG(nlp_start, dft_start, "    dft_speech");
    serialLog("analyse_one_frame 2");

I'm no C guru and have no idea what is happening.

EDIT: I tried running a reduced version of your sketch and that seems to be working, probably something in my code then. What is wrong with my initial code snippet?

Bartvelp commented 2 years ago

I am fairly sure there is some bug in the codec2 causing stack overflow warnings and the like, apperently when using a FreeRTOS task, the code doesn't crash. This is a suboptimal solution I've come up with in the mean time

#include <codec2.h>

struct CODEC2* codec2_state;

int16_t sine1KHz[8] = { -21210 , -30000, -21210, 0 , 21210 , 30000 , 21210, 0 };

int16_t audioBuf[320];
uint8_t c2Buf[8];
boolean flagEncodeAudioBuf = false;
boolean flagDecodeC2Buf = false;

void setup() {
  Serial.begin(115200);
  Serial.println();
  Serial.println("Started codec2 sample");

  // Set bitrate
  codec2_state = codec2_create(CODEC2_MODE_1600);
  // Set some tuning parameters
  codec2_set_lpc_post_filter(codec2_state, 1, 0, 0.8, 0.2);

  xTaskCreate(&codec2_watcher, "codec2_watcher_task", 30000, NULL, 5, NULL);

  // Encode a (fixed size) input buffer of type int16_t to a (fixed size) output buffer
  // CODEC2_MODE_1600 encodes 320 speech samples (320 * 2 = 640 bytes / 40ms of speech) into 8 bytes (64 bits)
  // Fill buffer with a sine wave
  for (int i = 0; i < 320; i++) audioBuf[i] = sine1KHz[i % 8];

  for (int i = 0; i < 320; i++) Serial.print(String(audioBuf[i], DEC) + " ");
  Serial.println();

  int startTimeEncode = millis();
  c2_encode();
  Serial.println("Done encoding, took ms: " + String(millis() - startTimeEncode));

  for (int i = 0; i < 8; i++) Serial.print(String(c2Buf[i], DEC) + " ");
  Serial.println();

  int startTimeDecode = millis();
  c2_decode();
  Serial.println("Done decoding, took ms: " + String(millis() - startTimeDecode));

  for (int i = 0; i < 320; i++) Serial.print(String(audioBuf[i], DEC) + " ");
  Serial.println();
}

void loop() {
}

void c2_encode() {
  flagEncodeAudioBuf = true;
  while (flagEncodeAudioBuf) delay(1); // Wait for the codec2_watch
}

void c2_decode() {
  flagDecodeC2Buf = true;
  while (flagDecodeC2Buf) delay(1); // Wait for the codec2_watch
}

void codec2_watcher(void* parameter) {
  while (true) {
    // yield() DOES NOT work, that trips the WDT every 5 secs
    // delay(1) is VITAL
    delay(1); 
    if (flagEncodeAudioBuf) { // We have some work to do
      codec2_encode(codec2_state, c2Buf, audioBuf);
      flagEncodeAudioBuf = false; // Notify encode()
    }
    if (flagDecodeC2Buf) { // We have some work to do
      codec2_decode(codec2_state, audioBuf, c2Buf);
      flagDecodeC2Buf = false; // Notify decode()
    }
  }
}

It currently takes 14 ms to encode 40 ms of data, and 24 ms to decode, definitely not insignificant.

deulis commented 2 years ago

I did never test it without a FreeRTOS task. The codec and decode tasks are very heavy, but as long as you can encode or decode in less than 40ms everything should be OK. For encoding the idea is to take a 40ms audio sample and encode it before arrive the next 40ms sample of audio. For decoding you need to decoding in less than the 40ms of the audio your are currently playing from the previous decoded. You did a great work!. Thanks!

Bartvelp commented 2 years ago

Thanks for your reply, I agree, as long as decoding and encoding takes less than realtime, it is fine. Still I think there is some bug in this library causing the errors, it should be callable from outside a dedicated Task.