From 6cc1cb34f15781dfb10fe7c200b92be64e83694d Mon Sep 17 00:00:00 2001 From: DarkSlein Date: Thu, 16 May 2024 12:58:51 +0300 Subject: [PATCH] Implemented preceding buffer Implemented noise detection from preceding buffer Bug with ready request (core is panicked) Bug with noise detection (works sometimes) --- src/config/config.h | 4 +- src/domain/Recorder.cpp | 8 ++- src/domain/Recorder.h | 3 +- src/infra/Audio.cpp | 78 +++++++++++++++++++++++---- src/infra/Audio.h | 17 ++++-- src/libs/CircularBuffer.h | 33 +++++++----- src/main.cpp | 15 +++--- test/libs/CircularBuffer_unittest.cpp | 66 +++++++++++------------ 8 files changed, 152 insertions(+), 72 deletions(-) diff --git a/src/config/config.h b/src/config/config.h index a159ed9..99f0494 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -26,5 +26,5 @@ // make it configurable with some panel //#define NOISE_THRESHOLD 3000000 -//#define NOISE_THRESHOLD 10000000 -#define NOISE_THRESHOLD 20000000 \ No newline at end of file +#define NOISE_THRESHOLD 10000000 +//#define NOISE_THRESHOLD 20000000 \ No newline at end of file diff --git a/src/domain/Recorder.cpp b/src/domain/Recorder.cpp index d7877d1..938a87c 100644 --- a/src/domain/Recorder.cpp +++ b/src/domain/Recorder.cpp @@ -78,6 +78,10 @@ String Recorder::_formPayloadEnd() { return "\"}}\r\n\r\n"; } -bool Recorder::isNoiseDetected(int threshold) { - return _audio->isNoiseDetected(threshold); +void Recorder::writeToPrecedingBuffer() { + _audio->writeToPrecedingBuffer(); +} + +bool Recorder::isNoiseDetected() { + return _audio->isNoiseDetected(); } \ No newline at end of file diff --git a/src/domain/Recorder.h b/src/domain/Recorder.h index c0da325..58d72e1 100644 --- a/src/domain/Recorder.h +++ b/src/domain/Recorder.h @@ -21,5 +21,6 @@ public: Recorder(MicType micType, Http* http); ~Recorder(); void recordAudio(); - bool isNoiseDetected(int threshold); + void writeToPrecedingBuffer(); + bool isNoiseDetected(); }; diff --git a/src/infra/Audio.cpp b/src/infra/Audio.cpp index dc2fa3c..b78426a 100644 --- a/src/infra/Audio.cpp +++ b/src/infra/Audio.cpp @@ -1,10 +1,17 @@ #include "infra/Audio.h" #include +#include "config/config.h" + +#define PRECEDING_BUFFER_SIZE 4 Audio::Audio(MicType micType) { _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(PRECEDING_BUFFER_SIZE); // _precedingWavDataSize/_dividedWavDataSize + i2s = new I2S(micType); } @@ -12,6 +19,8 @@ Audio::~Audio() { for (int i = 0; i < _wavDataSize/_dividedWavDataSize; ++i) delete[] _wavData[i]; delete[] _wavData; delete i2s; + + emptyPrecedingBuffer(); } void Audio::_createWavHeader(byte* header, int waveDataSize){ @@ -63,37 +72,80 @@ void Audio::_createWavHeader(byte* header, int waveDataSize){ } char** Audio::record() { + _noiseDetectedInPrecedingBuffer = false; + _createWavHeader(_paddedHeader, _wavDataSize); _startMicros = micros(); - _writeToBuffer(_wavData, _wavDataSize - _prerecordedWavDataSize); + //_readFromPrecedingBuffer(_wavData, _precedingWavDataSize); + //_recordToBuffer(_wavData + _precedingWavDataSize, _wavDataSize - _precedingWavDataSize); + _recordToBuffer(_wavData, _wavDataSize); return _wavData; } -void Audio::_writeToBuffer(char** wavBuffer, int waveDataSize) { +void Audio::_recordToBuffer(char** wavBuffer, int waveDataSize) { int bitBitPerSample = i2s->GetBitPerSample(); if (bitBitPerSample == 16) { for (int j = 0; j < waveDataSize / _dividedWavDataSize; ++j) { i2s->Read(_i2sBuffer, _i2sBufferSize/2); - for (int i = 0; i < _i2sBufferSize/8; ++i) { - wavBuffer[j][2*i] = _i2sBuffer[4*i + 2]; - wavBuffer[j][2*i + 1] = _i2sBuffer[4*i + 3]; - } + _convertBufferToWav(wavBuffer[j]); } } else if (bitBitPerSample == 32) { for (int j = 0; j < waveDataSize / _dividedWavDataSize; ++j) { i2s->Read(_i2sBuffer, _i2sBufferSize); 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; } } } } +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() { return (_wavDataSize + sizeof(_paddedHeader)) * 4 / 3; // 4/3 is from base64 encoding } @@ -116,11 +168,15 @@ unsigned long Audio::getStartMicros() { return _startMicros; } -bool Audio::isNoiseDetected(int threshold) { - i2s->Read(_i2sBuffer2, _i2sBufferSize/2); - if (*(int*)_i2sBuffer2 > threshold || *(int*)_i2sBuffer2 < -threshold) { +bool Audio::_isNoiseDetectedInPrecedingBuffer() { + if (*(int*)_i2sBuffer > NOISE_THRESHOLD || *(int*)_i2sBuffer < -NOISE_THRESHOLD) { + Serial.println("Noise detected"); return true; } else { return false; } +} + +bool Audio::isNoiseDetected() { + return _noiseDetectedInPrecedingBuffer; } \ No newline at end of file diff --git a/src/infra/Audio.h b/src/infra/Audio.h index 67ee080..0f5c27a 100644 --- a/src/infra/Audio.h +++ b/src/infra/Audio.h @@ -3,6 +3,7 @@ #include +#include "libs/CircularBuffer.h" #include "infra/I2S.h" // 16bit, monoral, 16000Hz, linear PCM @@ -18,15 +19,21 @@ private: static const int _dividedWavDataSize = _i2sBufferSize/4; char** _wavData; // It's divided. Because large continuous memory area can't be allocated in esp32. - static const int _prerecordedWavDataSize = _wavDataSize/4; - char** _prerecordedWavData; + static const int _precedingWavDataSize = _wavDataSize/4; + char** _precedingWavData; + CircularBuffer* _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. 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); + bool _isNoiseDetectedInPrecedingBuffer(); public: Audio(MicType micType); @@ -35,7 +42,9 @@ public: int getSize(); String print(Stream* stream); unsigned long getStartMicros(); - bool isNoiseDetected(int threshold); + void emptyPrecedingBuffer(); + void writeToPrecedingBuffer(); + bool isNoiseDetected(); }; #endif // _AUDIO_H diff --git a/src/libs/CircularBuffer.h b/src/libs/CircularBuffer.h index b1ac929..6eb00ee 100644 --- a/src/libs/CircularBuffer.h +++ b/src/libs/CircularBuffer.h @@ -1,7 +1,6 @@ #include -#include -enum class ErrorCode { +enum class CircularBufferErrorCode { SUCCESS, BUFFER_FULL, BUFFER_EMPTY, @@ -15,13 +14,13 @@ public: _buffer = static_cast(malloc(_capacity * sizeof(T))); if (_buffer == nullptr) { // Handle memory allocation error - _errorCode = ErrorCode::MEMORY_ERROR; + _errorCode = CircularBufferErrorCode::MEMORY_ERROR; } else { _bufferEnd = _buffer + _capacity; _count = 0; _head = _buffer; _tail = _buffer; - _errorCode = ErrorCode::SUCCESS; + _errorCode = CircularBufferErrorCode::SUCCESS; } } @@ -29,9 +28,9 @@ public: free(_buffer); } - ErrorCode pushBack(const T& item) { - if (_count == _capacity) { - return ErrorCode::BUFFER_FULL; + CircularBufferErrorCode pushBack(const T& item) { + if (isFull()) { + return CircularBufferErrorCode::BUFFER_FULL; } *_head = item; _head++; @@ -39,12 +38,12 @@ public: _head = _buffer; } _count++; - return ErrorCode::SUCCESS; + return CircularBufferErrorCode::SUCCESS; } - ErrorCode popFront(T& item) { - if (_count == 0) { - return ErrorCode::BUFFER_EMPTY; + CircularBufferErrorCode popFront(T& item) { + if (isEmpty()) { + return CircularBufferErrorCode::BUFFER_EMPTY; } item = *_tail; _tail++; @@ -52,9 +51,17 @@ public: _tail = _buffer; } _count--; - return ErrorCode::SUCCESS; + return CircularBufferErrorCode::SUCCESS; } +bool isFull() { + return _count == _capacity; +} + +bool isEmpty() { + return _count == 0; +} + private: T *_buffer; // Data _buffer T *_bufferEnd; // End of data buffer @@ -62,5 +69,5 @@ private: size_t _count; // Number of items in the buffer T *_head; // Pointer to head T *_tail; // Pointer to tail - ErrorCode _errorCode; // Error code + CircularBufferErrorCode _errorCode; // Error code }; diff --git a/src/main.cpp b/src/main.cpp index 2e0947b..0bdc5a2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -37,19 +37,22 @@ void setInitialTime() { timeService.setInitialTime(currentTime); } -void micTask(void* parameter) { +void writeToPrecedingBufferTask(void* parameter) { while (true) { - if (recorder->isNoiseDetected(NOISE_THRESHOLD)) { + recorder->writeToPrecedingBuffer(); + + if (recorder->isNoiseDetected()) { recorder->recordAudio(); } - vTaskDelay(1); + + vTaskDelay(500 / portTICK_PERIOD_MS); } } -void createMicTask() { +void createWriteToPrecedingBufferTask() { TaskHandle_t xHandle = NULL; - xTaskCreatePinnedToCore(micTask, "micTask", 4096, NULL, 1, &xHandle, 1); + xTaskCreatePinnedToCore(writeToPrecedingBufferTask, "writeToPrecedingBufferTask", 4096, NULL, 1, &xHandle, 0); if (xHandle == NULL) { ESP_LOGE("TASK1", "Failed to task create"); @@ -65,7 +68,7 @@ void setup() { setInitialTime(); recorder = new Recorder(ADMP441, &http); - createMicTask(); + createWriteToPrecedingBufferTask(); beginMicros = micros(); } diff --git a/test/libs/CircularBuffer_unittest.cpp b/test/libs/CircularBuffer_unittest.cpp index 99ddd4f..6c6c8d1 100644 --- a/test/libs/CircularBuffer_unittest.cpp +++ b/test/libs/CircularBuffer_unittest.cpp @@ -21,17 +21,17 @@ protected: // Test CircularBuffer push and pop operations TEST_F(CircularBufferTest, PushAndPop) { // Push some integers into the circular buffer - EXPECT_EQ(buffer->pushBack(10), ErrorCode::SUCCESS); - EXPECT_EQ(buffer->pushBack(20), ErrorCode::SUCCESS); - EXPECT_EQ(buffer->pushBack(30), ErrorCode::SUCCESS); + EXPECT_EQ(buffer->pushBack(10), CircularBufferErrorCode::SUCCESS); + EXPECT_EQ(buffer->pushBack(20), CircularBufferErrorCode::SUCCESS); + EXPECT_EQ(buffer->pushBack(30), CircularBufferErrorCode::SUCCESS); // Pop integers from the circular buffer and check values int item; - EXPECT_EQ(buffer->popFront(item), ErrorCode::SUCCESS); + EXPECT_EQ(buffer->popFront(item), CircularBufferErrorCode::SUCCESS); EXPECT_EQ(item, 10); - EXPECT_EQ(buffer->popFront(item), ErrorCode::SUCCESS); + EXPECT_EQ(buffer->popFront(item), CircularBufferErrorCode::SUCCESS); EXPECT_EQ(item, 20); - EXPECT_EQ(buffer->popFront(item), ErrorCode::SUCCESS); + EXPECT_EQ(buffer->popFront(item), CircularBufferErrorCode::SUCCESS); EXPECT_EQ(item, 30); } @@ -41,17 +41,17 @@ TEST_F(CircularBufferTest, DifferentTypes) { CircularBuffer charBuffer(3); // Push some characters into the circular buffer - EXPECT_EQ(charBuffer.pushBack('a'), ErrorCode::SUCCESS); - EXPECT_EQ(charBuffer.pushBack('b'), ErrorCode::SUCCESS); - EXPECT_EQ(charBuffer.pushBack('c'), ErrorCode::SUCCESS); + EXPECT_EQ(charBuffer.pushBack('a'), CircularBufferErrorCode::SUCCESS); + EXPECT_EQ(charBuffer.pushBack('b'), CircularBufferErrorCode::SUCCESS); + EXPECT_EQ(charBuffer.pushBack('c'), CircularBufferErrorCode::SUCCESS); // Pop characters from the circular buffer and check values char c; - EXPECT_EQ(charBuffer.popFront(c), ErrorCode::SUCCESS); + EXPECT_EQ(charBuffer.popFront(c), CircularBufferErrorCode::SUCCESS); EXPECT_EQ(c, 'a'); - EXPECT_EQ(charBuffer.popFront(c), ErrorCode::SUCCESS); + EXPECT_EQ(charBuffer.popFront(c), CircularBufferErrorCode::SUCCESS); EXPECT_EQ(c, 'b'); - EXPECT_EQ(charBuffer.popFront(c), ErrorCode::SUCCESS); + EXPECT_EQ(charBuffer.popFront(c), CircularBufferErrorCode::SUCCESS); EXPECT_EQ(c, 'c'); } @@ -63,16 +63,16 @@ TEST_F(CircularBufferTest, FullAndEmpty) { buffer->pushBack(30); // 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 int item; - EXPECT_EQ(buffer->popFront(item), ErrorCode::SUCCESS); - EXPECT_EQ(buffer->popFront(item), ErrorCode::SUCCESS); - EXPECT_EQ(buffer->popFront(item), ErrorCode::SUCCESS); + EXPECT_EQ(buffer->popFront(item), CircularBufferErrorCode::SUCCESS); + EXPECT_EQ(buffer->popFront(item), CircularBufferErrorCode::SUCCESS); + EXPECT_EQ(buffer->popFront(item), CircularBufferErrorCode::SUCCESS); // 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 @@ -82,36 +82,36 @@ TEST_F(CircularBufferTest, CircularBufferWithNestedCircularBuffer) { CircularBuffer charSubBuffer1(3); CircularBuffer charSubBuffer2(3); - EXPECT_EQ(charSubBuffer1.pushBack('a'), ErrorCode::SUCCESS); - EXPECT_EQ(charSubBuffer1.pushBack('b'), ErrorCode::SUCCESS); - EXPECT_EQ(charSubBuffer1.pushBack('c'), ErrorCode::SUCCESS); + EXPECT_EQ(charSubBuffer1.pushBack('a'), CircularBufferErrorCode::SUCCESS); + EXPECT_EQ(charSubBuffer1.pushBack('b'), CircularBufferErrorCode::SUCCESS); + EXPECT_EQ(charSubBuffer1.pushBack('c'), CircularBufferErrorCode::SUCCESS); - EXPECT_EQ(charSubBuffer2.pushBack('d'), ErrorCode::SUCCESS); - EXPECT_EQ(charSubBuffer2.pushBack('e'), ErrorCode::SUCCESS); - EXPECT_EQ(charSubBuffer2.pushBack('f'), ErrorCode::SUCCESS); + EXPECT_EQ(charSubBuffer2.pushBack('d'), CircularBufferErrorCode::SUCCESS); + EXPECT_EQ(charSubBuffer2.pushBack('e'), CircularBufferErrorCode::SUCCESS); + EXPECT_EQ(charSubBuffer2.pushBack('f'), CircularBufferErrorCode::SUCCESS); - EXPECT_EQ(charBuffer.pushBack(&charSubBuffer1), ErrorCode::SUCCESS); - EXPECT_EQ(charBuffer.pushBack(&charSubBuffer2), ErrorCode::SUCCESS); + EXPECT_EQ(charBuffer.pushBack(&charSubBuffer1), CircularBufferErrorCode::SUCCESS); + EXPECT_EQ(charBuffer.pushBack(&charSubBuffer2), CircularBufferErrorCode::SUCCESS); CircularBuffer* poppedCharSubBuffer; - EXPECT_EQ(charBuffer.popFront(poppedCharSubBuffer), ErrorCode::SUCCESS); + EXPECT_EQ(charBuffer.popFront(poppedCharSubBuffer), CircularBufferErrorCode::SUCCESS); EXPECT_EQ(poppedCharSubBuffer, &charSubBuffer1); char c; - EXPECT_EQ(poppedCharSubBuffer->popFront(c), ErrorCode::SUCCESS); + EXPECT_EQ(poppedCharSubBuffer->popFront(c), CircularBufferErrorCode::SUCCESS); EXPECT_EQ(c, 'a'); - EXPECT_EQ(poppedCharSubBuffer->popFront(c), ErrorCode::SUCCESS); + EXPECT_EQ(poppedCharSubBuffer->popFront(c), CircularBufferErrorCode::SUCCESS); EXPECT_EQ(c, 'b'); - EXPECT_EQ(poppedCharSubBuffer->popFront(c), ErrorCode::SUCCESS); + EXPECT_EQ(poppedCharSubBuffer->popFront(c), CircularBufferErrorCode::SUCCESS); 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->popFront(c), ErrorCode::SUCCESS); + EXPECT_EQ(poppedCharSubBuffer->popFront(c), CircularBufferErrorCode::SUCCESS); EXPECT_EQ(c, 'd'); - EXPECT_EQ(poppedCharSubBuffer->popFront(c), ErrorCode::SUCCESS); + EXPECT_EQ(poppedCharSubBuffer->popFront(c), CircularBufferErrorCode::SUCCESS); EXPECT_EQ(c, 'e'); - EXPECT_EQ(poppedCharSubBuffer->popFront(c), ErrorCode::SUCCESS); + EXPECT_EQ(poppedCharSubBuffer->popFront(c), CircularBufferErrorCode::SUCCESS); EXPECT_EQ(c, 'f'); }