pschatzmann / arduino-audio-tools

Arduino Audio Tools (a powerful Audio library not only for Arduino)
GNU General Public License v3.0
1.55k stars 237 forks source link

TFLiteAudioStream result_queue is sometimes empty; exceptions are unhandled. #1584

Closed neilshevlin closed 5 months ago

neilshevlin commented 6 months ago

Problem Description

When using any of the converted tensorflow lite binaries with the TFLiteAudioStream I will get the following assert failure:

assert failed: T& audio_tools::Vector<T>::operator[](int) [with T = float] Vector.h:246 (p_data != nullptr)

Device Description

I am using and AI thinker / AudioKitEs8388V1 as my development board.

Sketch

#include "AudioTools.h"
#include "AudioLibs/AudioBoardStream.h"
#include "AudioLibs/TfLiteAudioStream.h"
#include "model.h"  // tensorflow model

AudioBoardStream kit(AudioKitEs8388V1);  // Audio source
TfLiteAudioStream tfl;  // Audio sink
const char* kCategoryLabels[4] = {
    "silence",
    "unknown",
    "yes",
    "no",
};
StreamCopy copier(tfl, kit);  // copy mic to tfl
int channels = 1;
int samples_per_second = 16000;

void respondToCommand(const char* found_command, uint8_t score,
                      bool is_new_command) {
  if (is_new_command) {
    char buffer[80];
    sprintf(buffer, "Result: %s, score: %d, is_new: %s", found_command, score,
            is_new_command ? "true" : "false");
    Serial.println(buffer);
  }
}

void setup() {
  Serial.begin(115200);
  AudioLogger::instance().begin(Serial, AudioLogger::Warning);

  // setup Audiokit
  auto cfg = kit.defaultConfig(RX_MODE);
  cfg.input_device = ADC_INPUT_LINE2;
  cfg.channels = channels;
  cfg.sample_rate = samples_per_second;
  cfg.use_apll = false;
  //cfg.auto_clear = true;
  cfg.buffer_size = 512;
  cfg.buffer_count = 16;
  kit.begin(cfg);

  // Setup tensorflow
  auto tcfg = tfl.defaultConfig();
  tcfg.setCategories(kCategoryLabels);
  tcfg.channels = channels;
  tcfg.sample_rate = samples_per_second;
  tcfg.kTensorArenaSize = 10 * 1024;
  tcfg.respondToCommand = respondToCommand;
  tcfg.model = g_model;
  tfl.begin(tcfg);
}

void loop() { copier.copy(); }

This is just the example sketch. In addition however I have also made use of the following: tcfg.useAllOpsResolver = true; To allow for the CONV_2D operator from more modern tf lite models.


### Other Steps to Reproduce

I can get around the problem by modifying `TFLiteAudioStream.h` and the `TfLiteMicroSpeechRecognizeCommands` class. By specifically modifying: 
```void deleteOldRecords(int32_t limit) {
        // I added a !result_queue.empty() 
        while (!result_queue.empty() && result_queue[0].time_ms < limit) {
          result_queue.pop_front();
        }
      }

The result queue is sometimes empty, which leads to the assert fail in Vector.h. However I am not yet familiar enough with this library to know if this is the right implementation for this fix.

What is your development environment

Using platformio

I have checked existing issues, discussions and online documentation

neilshevlin commented 6 months ago

If the fix for this looks right I can make a PR.

pschatzmann commented 6 months ago

Thanks for your feed-back. I agree that it makes sense to add a check before deleting values. However I prefer to make this check first before going into the loop: I committed the corresponding correction