Implemented preceding buffer

Implemented noise detection from preceding buffer

Bug with ready request (core is panicked)

Bug with noise detection (works sometimes)
This commit is contained in:
Sviatoslav Tsariov Yurievich 2024-05-16 12:58:51 +03:00
parent 44bea612ff
commit 6cc1cb34f1
8 changed files with 152 additions and 72 deletions

View File

@ -26,5 +26,5 @@
// make it configurable with some panel // make it configurable with some panel
//#define NOISE_THRESHOLD 3000000 //#define NOISE_THRESHOLD 3000000
//#define NOISE_THRESHOLD 10000000 #define NOISE_THRESHOLD 10000000
#define NOISE_THRESHOLD 20000000 //#define NOISE_THRESHOLD 20000000

View File

@ -78,6 +78,10 @@ String Recorder::_formPayloadEnd() {
return "\"}}\r\n\r\n"; return "\"}}\r\n\r\n";
} }
bool Recorder::isNoiseDetected(int threshold) { void Recorder::writeToPrecedingBuffer() {
return _audio->isNoiseDetected(threshold); _audio->writeToPrecedingBuffer();
}
bool Recorder::isNoiseDetected() {
return _audio->isNoiseDetected();
} }

View File

@ -21,5 +21,6 @@ public:
Recorder(MicType micType, Http* http); Recorder(MicType micType, Http* http);
~Recorder(); ~Recorder();
void recordAudio(); void recordAudio();
bool isNoiseDetected(int threshold); void writeToPrecedingBuffer();
bool isNoiseDetected();
}; };

View File

@ -1,10 +1,17 @@
#include "infra/Audio.h" #include "infra/Audio.h"
#include <base64.h> #include <base64.h>
#include "config/config.h"
#define PRECEDING_BUFFER_SIZE 4
Audio::Audio(MicType micType) { Audio::Audio(MicType micType) {
_wavData = new char*[_wavDataSize/_dividedWavDataSize]; _wavData = new char*[_wavDataSize/_dividedWavDataSize];
for (int i = 0; i < _wavDataSize/_dividedWavDataSize; ++i) _wavData[i] = new char[_dividedWavDataSize]; for (int i = 0; i < _wavDataSize/_dividedWavDataSize; ++i)
_wavData[i] = new char[_dividedWavDataSize];
_precedingWavBuffer = new CircularBuffer<char*>(PRECEDING_BUFFER_SIZE); // _precedingWavDataSize/_dividedWavDataSize
i2s = new I2S(micType); i2s = new I2S(micType);
} }
@ -12,6 +19,8 @@ Audio::~Audio() {
for (int i = 0; i < _wavDataSize/_dividedWavDataSize; ++i) delete[] _wavData[i]; for (int i = 0; i < _wavDataSize/_dividedWavDataSize; ++i) delete[] _wavData[i];
delete[] _wavData; delete[] _wavData;
delete i2s; delete i2s;
emptyPrecedingBuffer();
} }
void Audio::_createWavHeader(byte* header, int waveDataSize){ void Audio::_createWavHeader(byte* header, int waveDataSize){
@ -63,37 +72,80 @@ void Audio::_createWavHeader(byte* header, int waveDataSize){
} }
char** Audio::record() { char** Audio::record() {
_noiseDetectedInPrecedingBuffer = false;
_createWavHeader(_paddedHeader, _wavDataSize); _createWavHeader(_paddedHeader, _wavDataSize);
_startMicros = micros(); _startMicros = micros();
_writeToBuffer(_wavData, _wavDataSize - _prerecordedWavDataSize); //_readFromPrecedingBuffer(_wavData, _precedingWavDataSize);
//_recordToBuffer(_wavData + _precedingWavDataSize, _wavDataSize - _precedingWavDataSize);
_recordToBuffer(_wavData, _wavDataSize);
return _wavData; return _wavData;
} }
void Audio::_writeToBuffer(char** wavBuffer, int waveDataSize) { void Audio::_recordToBuffer(char** wavBuffer, int waveDataSize) {
int bitBitPerSample = i2s->GetBitPerSample(); int bitBitPerSample = i2s->GetBitPerSample();
if (bitBitPerSample == 16) { if (bitBitPerSample == 16) {
for (int j = 0; j < waveDataSize / _dividedWavDataSize; ++j) { for (int j = 0; j < waveDataSize / _dividedWavDataSize; ++j) {
i2s->Read(_i2sBuffer, _i2sBufferSize/2); i2s->Read(_i2sBuffer, _i2sBufferSize/2);
for (int i = 0; i < _i2sBufferSize/8; ++i) { _convertBufferToWav(wavBuffer[j]);
wavBuffer[j][2*i] = _i2sBuffer[4*i + 2];
wavBuffer[j][2*i + 1] = _i2sBuffer[4*i + 3];
}
} }
} }
else if (bitBitPerSample == 32) { else if (bitBitPerSample == 32) {
for (int j = 0; j < waveDataSize / _dividedWavDataSize; ++j) { for (int j = 0; j < waveDataSize / _dividedWavDataSize; ++j) {
i2s->Read(_i2sBuffer, _i2sBufferSize); i2s->Read(_i2sBuffer, _i2sBufferSize);
for (int i = 0; i < _i2sBufferSize/8; ++i) { for (int i = 0; i < _i2sBufferSize/8; ++i) {
wavBuffer[j][2*i] = _i2sBuffer[8*i + 2] << 4; wavBuffer[j][2*i] = _i2sBuffer[8*i + 2] << 4; // TODO: check << 4 in 16bit
wavBuffer[j][2*i + 1] = _i2sBuffer[8*i + 3] << 4; wavBuffer[j][2*i + 1] = _i2sBuffer[8*i + 3] << 4;
} }
} }
} }
} }
void Audio::emptyPrecedingBuffer() {
while (!_precedingWavBuffer->isEmpty()) {
char* tmp;
_precedingWavBuffer->popFront(tmp);
delete tmp;
}
}
void Audio::writeToPrecedingBuffer() {
if (_precedingWavBuffer->isFull()) {
char* tmp;
_precedingWavBuffer->popFront(tmp);
delete tmp;
}
i2s->Read(_i2sBuffer, _i2sBufferSize/2);
_noiseDetectedInPrecedingBuffer = _isNoiseDetectedInPrecedingBuffer();
char* _partialWavBuffer = new char[_precedingWavDataSize];
_convertBufferToWav(_partialWavBuffer);
CircularBufferErrorCode err = _precedingWavBuffer->pushBack(_partialWavBuffer);
}
void Audio::_readFromPrecedingBuffer(char** wavBuffer, int waveDataSize) {
char* tmp;
for (int i = 0; i < PRECEDING_BUFFER_SIZE; i++) {
_precedingWavBuffer->popFront(tmp);
wavBuffer[i] = tmp;
}
emptyPrecedingBuffer();
}
void Audio::_convertBufferToWav(char* partialWavBuffer) {
for (int i = 0; i < _i2sBufferSize/8; ++i) {
partialWavBuffer[2*i] = _i2sBuffer[4*i + 2];
partialWavBuffer[2*i + 1] = _i2sBuffer[4*i + 3];
}
}
int Audio::getSize() { int Audio::getSize() {
return (_wavDataSize + sizeof(_paddedHeader)) * 4 / 3; // 4/3 is from base64 encoding return (_wavDataSize + sizeof(_paddedHeader)) * 4 / 3; // 4/3 is from base64 encoding
} }
@ -116,11 +168,15 @@ unsigned long Audio::getStartMicros() {
return _startMicros; return _startMicros;
} }
bool Audio::isNoiseDetected(int threshold) { bool Audio::_isNoiseDetectedInPrecedingBuffer() {
i2s->Read(_i2sBuffer2, _i2sBufferSize/2); if (*(int*)_i2sBuffer > NOISE_THRESHOLD || *(int*)_i2sBuffer < -NOISE_THRESHOLD) {
if (*(int*)_i2sBuffer2 > threshold || *(int*)_i2sBuffer2 < -threshold) { Serial.println("Noise detected");
return true; return true;
} else { } else {
return false; return false;
} }
} }
bool Audio::isNoiseDetected() {
return _noiseDetectedInPrecedingBuffer;
}

View File

@ -3,6 +3,7 @@
#include <Arduino.h> #include <Arduino.h>
#include "libs/CircularBuffer.h"
#include "infra/I2S.h" #include "infra/I2S.h"
// 16bit, monoral, 16000Hz, linear PCM // 16bit, monoral, 16000Hz, linear PCM
@ -18,15 +19,21 @@ private:
static const int _dividedWavDataSize = _i2sBufferSize/4; static const int _dividedWavDataSize = _i2sBufferSize/4;
char** _wavData; // It's divided. Because large continuous memory area can't be allocated in esp32. char** _wavData; // It's divided. Because large continuous memory area can't be allocated in esp32.
static const int _prerecordedWavDataSize = _wavDataSize/4; static const int _precedingWavDataSize = _wavDataSize/4;
char** _prerecordedWavData; char** _precedingWavData;
CircularBuffer<char*>* _precedingWavBuffer;
bool _precedingBufferFull = false;
bool _noiseDetectedInPrecedingBuffer = false;
byte _paddedHeader[_headerSize + 4] = {0}; // The size must be multiple of 3 for Base64 encoding. Additional byte size must be even because wave data is 16bit. byte _paddedHeader[_headerSize + 4] = {0}; // The size must be multiple of 3 for Base64 encoding. Additional byte size must be even because wave data is 16bit.
unsigned long _startMicros = 0; unsigned long _startMicros = 0;
void _writeToBuffer(char** wavBuffer, int waveDataSize); void _recordToBuffer(char** wavBuffer, int waveDataSize);
void _convertBufferToWav(char* partialWavBuffer);
void _readFromPrecedingBuffer(char** wavBuffer, int waveDataSize);
void _createWavHeader(byte* header, int waveDataSize); void _createWavHeader(byte* header, int waveDataSize);
bool _isNoiseDetectedInPrecedingBuffer();
public: public:
Audio(MicType micType); Audio(MicType micType);
@ -35,7 +42,9 @@ public:
int getSize(); int getSize();
String print(Stream* stream); String print(Stream* stream);
unsigned long getStartMicros(); unsigned long getStartMicros();
bool isNoiseDetected(int threshold); void emptyPrecedingBuffer();
void writeToPrecedingBuffer();
bool isNoiseDetected();
}; };
#endif // _AUDIO_H #endif // _AUDIO_H

View File

@ -1,7 +1,6 @@
#include <Arduino.h> #include <Arduino.h>
#include <Serial.h>
enum class ErrorCode { enum class CircularBufferErrorCode {
SUCCESS, SUCCESS,
BUFFER_FULL, BUFFER_FULL,
BUFFER_EMPTY, BUFFER_EMPTY,
@ -15,13 +14,13 @@ public:
_buffer = static_cast<T*>(malloc(_capacity * sizeof(T))); _buffer = static_cast<T*>(malloc(_capacity * sizeof(T)));
if (_buffer == nullptr) { if (_buffer == nullptr) {
// Handle memory allocation error // Handle memory allocation error
_errorCode = ErrorCode::MEMORY_ERROR; _errorCode = CircularBufferErrorCode::MEMORY_ERROR;
} else { } else {
_bufferEnd = _buffer + _capacity; _bufferEnd = _buffer + _capacity;
_count = 0; _count = 0;
_head = _buffer; _head = _buffer;
_tail = _buffer; _tail = _buffer;
_errorCode = ErrorCode::SUCCESS; _errorCode = CircularBufferErrorCode::SUCCESS;
} }
} }
@ -29,9 +28,9 @@ public:
free(_buffer); free(_buffer);
} }
ErrorCode pushBack(const T& item) { CircularBufferErrorCode pushBack(const T& item) {
if (_count == _capacity) { if (isFull()) {
return ErrorCode::BUFFER_FULL; return CircularBufferErrorCode::BUFFER_FULL;
} }
*_head = item; *_head = item;
_head++; _head++;
@ -39,12 +38,12 @@ public:
_head = _buffer; _head = _buffer;
} }
_count++; _count++;
return ErrorCode::SUCCESS; return CircularBufferErrorCode::SUCCESS;
} }
ErrorCode popFront(T& item) { CircularBufferErrorCode popFront(T& item) {
if (_count == 0) { if (isEmpty()) {
return ErrorCode::BUFFER_EMPTY; return CircularBufferErrorCode::BUFFER_EMPTY;
} }
item = *_tail; item = *_tail;
_tail++; _tail++;
@ -52,7 +51,15 @@ public:
_tail = _buffer; _tail = _buffer;
} }
_count--; _count--;
return ErrorCode::SUCCESS; return CircularBufferErrorCode::SUCCESS;
}
bool isFull() {
return _count == _capacity;
}
bool isEmpty() {
return _count == 0;
} }
private: private:
@ -62,5 +69,5 @@ private:
size_t _count; // Number of items in the buffer size_t _count; // Number of items in the buffer
T *_head; // Pointer to head T *_head; // Pointer to head
T *_tail; // Pointer to tail T *_tail; // Pointer to tail
ErrorCode _errorCode; // Error code CircularBufferErrorCode _errorCode; // Error code
}; };

View File

@ -37,19 +37,22 @@ void setInitialTime() {
timeService.setInitialTime(currentTime); timeService.setInitialTime(currentTime);
} }
void micTask(void* parameter) { void writeToPrecedingBufferTask(void* parameter) {
while (true) { while (true) {
if (recorder->isNoiseDetected(NOISE_THRESHOLD)) { recorder->writeToPrecedingBuffer();
if (recorder->isNoiseDetected()) {
recorder->recordAudio(); recorder->recordAudio();
} }
vTaskDelay(1);
vTaskDelay(500 / portTICK_PERIOD_MS);
} }
} }
void createMicTask() { void createWriteToPrecedingBufferTask() {
TaskHandle_t xHandle = NULL; TaskHandle_t xHandle = NULL;
xTaskCreatePinnedToCore(micTask, "micTask", 4096, NULL, 1, &xHandle, 1); xTaskCreatePinnedToCore(writeToPrecedingBufferTask, "writeToPrecedingBufferTask", 4096, NULL, 1, &xHandle, 0);
if (xHandle == NULL) { if (xHandle == NULL) {
ESP_LOGE("TASK1", "Failed to task create"); ESP_LOGE("TASK1", "Failed to task create");
@ -65,7 +68,7 @@ void setup() {
setInitialTime(); setInitialTime();
recorder = new Recorder(ADMP441, &http); recorder = new Recorder(ADMP441, &http);
createMicTask(); createWriteToPrecedingBufferTask();
beginMicros = micros(); beginMicros = micros();
} }

View File

@ -21,17 +21,17 @@ protected:
// Test CircularBuffer push and pop operations // Test CircularBuffer push and pop operations
TEST_F(CircularBufferTest, PushAndPop) { TEST_F(CircularBufferTest, PushAndPop) {
// Push some integers into the circular buffer // Push some integers into the circular buffer
EXPECT_EQ(buffer->pushBack(10), ErrorCode::SUCCESS); EXPECT_EQ(buffer->pushBack(10), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(buffer->pushBack(20), ErrorCode::SUCCESS); EXPECT_EQ(buffer->pushBack(20), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(buffer->pushBack(30), ErrorCode::SUCCESS); EXPECT_EQ(buffer->pushBack(30), CircularBufferErrorCode::SUCCESS);
// Pop integers from the circular buffer and check values // Pop integers from the circular buffer and check values
int item; int item;
EXPECT_EQ(buffer->popFront(item), ErrorCode::SUCCESS); EXPECT_EQ(buffer->popFront(item), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(item, 10); EXPECT_EQ(item, 10);
EXPECT_EQ(buffer->popFront(item), ErrorCode::SUCCESS); EXPECT_EQ(buffer->popFront(item), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(item, 20); EXPECT_EQ(item, 20);
EXPECT_EQ(buffer->popFront(item), ErrorCode::SUCCESS); EXPECT_EQ(buffer->popFront(item), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(item, 30); EXPECT_EQ(item, 30);
} }
@ -41,17 +41,17 @@ TEST_F(CircularBufferTest, DifferentTypes) {
CircularBuffer<char> charBuffer(3); CircularBuffer<char> charBuffer(3);
// Push some characters into the circular buffer // Push some characters into the circular buffer
EXPECT_EQ(charBuffer.pushBack('a'), ErrorCode::SUCCESS); EXPECT_EQ(charBuffer.pushBack('a'), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(charBuffer.pushBack('b'), ErrorCode::SUCCESS); EXPECT_EQ(charBuffer.pushBack('b'), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(charBuffer.pushBack('c'), ErrorCode::SUCCESS); EXPECT_EQ(charBuffer.pushBack('c'), CircularBufferErrorCode::SUCCESS);
// Pop characters from the circular buffer and check values // Pop characters from the circular buffer and check values
char c; char c;
EXPECT_EQ(charBuffer.popFront(c), ErrorCode::SUCCESS); EXPECT_EQ(charBuffer.popFront(c), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(c, 'a'); EXPECT_EQ(c, 'a');
EXPECT_EQ(charBuffer.popFront(c), ErrorCode::SUCCESS); EXPECT_EQ(charBuffer.popFront(c), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(c, 'b'); EXPECT_EQ(c, 'b');
EXPECT_EQ(charBuffer.popFront(c), ErrorCode::SUCCESS); EXPECT_EQ(charBuffer.popFront(c), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(c, 'c'); EXPECT_EQ(c, 'c');
} }
@ -63,16 +63,16 @@ TEST_F(CircularBufferTest, FullAndEmpty) {
buffer->pushBack(30); buffer->pushBack(30);
// Try pushing into a full buffer // Try pushing into a full buffer
EXPECT_EQ(buffer->pushBack(40), ErrorCode::BUFFER_FULL); EXPECT_EQ(buffer->pushBack(40), CircularBufferErrorCode::BUFFER_FULL);
// Pop all elements from the buffer // Pop all elements from the buffer
int item; int item;
EXPECT_EQ(buffer->popFront(item), ErrorCode::SUCCESS); EXPECT_EQ(buffer->popFront(item), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(buffer->popFront(item), ErrorCode::SUCCESS); EXPECT_EQ(buffer->popFront(item), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(buffer->popFront(item), ErrorCode::SUCCESS); EXPECT_EQ(buffer->popFront(item), CircularBufferErrorCode::SUCCESS);
// Try popping from an empty buffer // Try popping from an empty buffer
EXPECT_EQ(buffer->popFront(item), ErrorCode::BUFFER_EMPTY); EXPECT_EQ(buffer->popFront(item), CircularBufferErrorCode::BUFFER_EMPTY);
} }
// Test CircularBuffer with nested CircularBuffer // Test CircularBuffer with nested CircularBuffer
@ -82,36 +82,36 @@ TEST_F(CircularBufferTest, CircularBufferWithNestedCircularBuffer) {
CircularBuffer<char> charSubBuffer1(3); CircularBuffer<char> charSubBuffer1(3);
CircularBuffer<char> charSubBuffer2(3); CircularBuffer<char> charSubBuffer2(3);
EXPECT_EQ(charSubBuffer1.pushBack('a'), ErrorCode::SUCCESS); EXPECT_EQ(charSubBuffer1.pushBack('a'), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(charSubBuffer1.pushBack('b'), ErrorCode::SUCCESS); EXPECT_EQ(charSubBuffer1.pushBack('b'), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(charSubBuffer1.pushBack('c'), ErrorCode::SUCCESS); EXPECT_EQ(charSubBuffer1.pushBack('c'), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(charSubBuffer2.pushBack('d'), ErrorCode::SUCCESS); EXPECT_EQ(charSubBuffer2.pushBack('d'), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(charSubBuffer2.pushBack('e'), ErrorCode::SUCCESS); EXPECT_EQ(charSubBuffer2.pushBack('e'), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(charSubBuffer2.pushBack('f'), ErrorCode::SUCCESS); EXPECT_EQ(charSubBuffer2.pushBack('f'), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(charBuffer.pushBack(&charSubBuffer1), ErrorCode::SUCCESS); EXPECT_EQ(charBuffer.pushBack(&charSubBuffer1), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(charBuffer.pushBack(&charSubBuffer2), ErrorCode::SUCCESS); EXPECT_EQ(charBuffer.pushBack(&charSubBuffer2), CircularBufferErrorCode::SUCCESS);
CircularBuffer<char>* poppedCharSubBuffer; CircularBuffer<char>* poppedCharSubBuffer;
EXPECT_EQ(charBuffer.popFront(poppedCharSubBuffer), ErrorCode::SUCCESS); EXPECT_EQ(charBuffer.popFront(poppedCharSubBuffer), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(poppedCharSubBuffer, &charSubBuffer1); EXPECT_EQ(poppedCharSubBuffer, &charSubBuffer1);
char c; char c;
EXPECT_EQ(poppedCharSubBuffer->popFront(c), ErrorCode::SUCCESS); EXPECT_EQ(poppedCharSubBuffer->popFront(c), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(c, 'a'); EXPECT_EQ(c, 'a');
EXPECT_EQ(poppedCharSubBuffer->popFront(c), ErrorCode::SUCCESS); EXPECT_EQ(poppedCharSubBuffer->popFront(c), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(c, 'b'); EXPECT_EQ(c, 'b');
EXPECT_EQ(poppedCharSubBuffer->popFront(c), ErrorCode::SUCCESS); EXPECT_EQ(poppedCharSubBuffer->popFront(c), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(c, 'c'); EXPECT_EQ(c, 'c');
EXPECT_EQ(charBuffer.popFront(poppedCharSubBuffer), ErrorCode::SUCCESS); EXPECT_EQ(charBuffer.popFront(poppedCharSubBuffer), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(poppedCharSubBuffer, &charSubBuffer2); EXPECT_EQ(poppedCharSubBuffer, &charSubBuffer2);
EXPECT_EQ(poppedCharSubBuffer->popFront(c), ErrorCode::SUCCESS); EXPECT_EQ(poppedCharSubBuffer->popFront(c), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(c, 'd'); EXPECT_EQ(c, 'd');
EXPECT_EQ(poppedCharSubBuffer->popFront(c), ErrorCode::SUCCESS); EXPECT_EQ(poppedCharSubBuffer->popFront(c), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(c, 'e'); EXPECT_EQ(c, 'e');
EXPECT_EQ(poppedCharSubBuffer->popFront(c), ErrorCode::SUCCESS); EXPECT_EQ(poppedCharSubBuffer->popFront(c), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(c, 'f'); EXPECT_EQ(c, 'f');
} }