From 178bfc630a58c46007fab840058a95ac1a6d65d3 Mon Sep 17 00:00:00 2001 From: Jose Date: Fri, 25 Apr 2025 10:34:20 +0200 Subject: [PATCH] =?UTF-8?q?feat=20=E2=9C=A8:=20Added=20new=20header=20file?= =?UTF-8?q?s=20AudioLevelTracker.h,=20Config.h,=20I2SConfig.h,=20and=20upd?= =?UTF-8?q?ated=20Arduino=20code=20for=20improved=20I2S=20communication=20?= =?UTF-8?q?and=20dynamic=20range=20limiting.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added a new header file `AudioLevelTracker.h` to track audio levels with a history of up to 3 seconds and a maximum range limit. - `Config.h` has been added with new serial and I2S configurations. The aim is to configure these peripherals for audio processing. - Added new header file `I2SConfig.h` with setup functions for initializing and reading from I2S interface. - The `AudioLevelTracker.cpp` file has been updated to include functionality for tracking the maximum audio level over a specified duration. - Initial setup for I2S communication with a microphone. - Updated `Arduino` code with improved I2S configuration and dynamic range limiting. --- include/AudioLevelTracker.h | 23 ++++++ include/Config.h | 41 ++++++++++ include/I2SConfig.h | 9 +++ src/AudioLevelTracker.cpp | 38 +++++++++ src/I2SConfig.cpp | 34 +++++++++ src/main.cpp | 148 +++++++----------------------------- 6 files changed, 173 insertions(+), 120 deletions(-) create mode 100644 include/AudioLevelTracker.h create mode 100644 include/Config.h create mode 100644 include/I2SConfig.h create mode 100644 src/AudioLevelTracker.cpp create mode 100644 src/I2SConfig.cpp diff --git a/include/AudioLevelTracker.h b/include/AudioLevelTracker.h new file mode 100644 index 0000000..2fa5eb8 --- /dev/null +++ b/include/AudioLevelTracker.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +class AudioLevelTracker { +public: + AudioLevelTracker(); + void updateMaxLevel(int32_t sample); + int32_t getMaxLevel() const; + void resetMaxLevel(); + +private: + struct SamplePoint { + uint32_t timestamp; + int32_t value; + }; + std::deque sampleHistory; + int32_t maxLevel; + + static const uint32_t HISTORY_DURATION_MS = 3000; // 3 seconds history + static const int32_t MAX_RANGE_LIMIT = 200000000; // Maximum allowed range limit +}; \ No newline at end of file diff --git a/include/Config.h b/include/Config.h new file mode 100644 index 0000000..e99cfb4 --- /dev/null +++ b/include/Config.h @@ -0,0 +1,41 @@ +#pragma once +#include + +namespace Config { + + // Serial Configuration + constexpr int SERIAL_BAUD_RATE = 115200; // Serial communication baud rate + + // I2S Pin Configuration + constexpr int I2S_MIC_SERIAL_CLOCK = 8; // SCK + constexpr int I2S_MIC_LEFT_RIGHT_CLOCK = 9; // WS/LRC + constexpr int I2S_MIC_SERIAL_DATA = 10; // SD + + // I2S Configuration + constexpr int SAMPLE_RATE = 8000; // Hz + constexpr int BITS_PER_SAMPLE = 32; // Bits + constexpr int CHANNELS = 1; // Mono input + constexpr int SAMPLE_BUFFER_SIZE = 512; // Samples per buffer + + // DMA Configuration + constexpr int DMA_BUFFER_COUNT = 8; // Number of DMA buffers + constexpr int DMA_BUFFER_LEN = 1024; // Length of each DMA buffer + + // Audio Processing + constexpr float DC_OFFSET = 0.0f; // DC offset correction + constexpr float GAIN = 1.0f; // Audio gain multiplier + constexpr int32_t NOISE_THRESHOLD = 1000; // Ignore audio below this level + constexpr int32_t DEFAULT_RANGE_LIMIT = 200000000; // Default range limit for plotting + constexpr float DECAY_FACTOR = 0.95f; // Level decay rate + + // Timing and Debug + constexpr uint32_t LEVEL_UPDATE_INTERVAL_MS = 100; // Level update interval + constexpr bool ENABLE_DEBUG = true; // Enable debug output + constexpr int DEBUG_INTERVAL_MS = 1000; // Debug print interval + + // System Configuration + constexpr uint32_t TASK_STACK_SIZE = 4096; // Audio task stack size + constexpr uint8_t TASK_PRIORITY = 1; // Audio task priority (0-24) + constexpr uint8_t TASK_CORE = 0; // Core to run audio task (0 or 1) + +} \ No newline at end of file diff --git a/include/I2SConfig.h b/include/I2SConfig.h new file mode 100644 index 0000000..202d548 --- /dev/null +++ b/include/I2SConfig.h @@ -0,0 +1,9 @@ +#pragma once + +#include +#include +#include "Config.h" + +// I2S setup functions +void initI2S(); +void readI2SSamples(int32_t* samples, size_t* bytesRead); \ No newline at end of file diff --git a/src/AudioLevelTracker.cpp b/src/AudioLevelTracker.cpp new file mode 100644 index 0000000..1814b65 --- /dev/null +++ b/src/AudioLevelTracker.cpp @@ -0,0 +1,38 @@ +#include "AudioLevelTracker.h" + +AudioLevelTracker::AudioLevelTracker() { + resetMaxLevel(); +} + +void AudioLevelTracker::updateMaxLevel(int32_t sample) { + uint32_t currentTime = millis(); + + // Remove old samples (older than specified duration) + while (!sampleHistory.empty() && + (currentTime - sampleHistory.front().timestamp) > HISTORY_DURATION_MS) { + sampleHistory.pop_front(); + } + + // Add new sample, but cap it at MAX_RANGE_LIMIT + int32_t absValue = abs(sample); + absValue = min(absValue, MAX_RANGE_LIMIT); // Cap the value + SamplePoint newPoint = {currentTime, absValue}; + sampleHistory.push_back(newPoint); + + // Update maximum + maxLevel = 0; + for (const auto& point : sampleHistory) { + if (point.value > maxLevel) { + maxLevel = point.value; + } + } +} + +int32_t AudioLevelTracker::getMaxLevel() const { + return maxLevel; +} + +void AudioLevelTracker::resetMaxLevel() { + maxLevel = 0; + sampleHistory.clear(); +} \ No newline at end of file diff --git a/src/I2SConfig.cpp b/src/I2SConfig.cpp new file mode 100644 index 0000000..c80d03b --- /dev/null +++ b/src/I2SConfig.cpp @@ -0,0 +1,34 @@ +#include "I2SConfig.h" + +void initI2S() { + // I2S configuration + i2s_config_t i2s_config = { + .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX), + .sample_rate = Config::SAMPLE_RATE, + .bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT, + .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT, + .communication_format = I2S_COMM_FORMAT_STAND_I2S, + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, + .dma_buf_count = 4, + .dma_buf_len = 1024, + .use_apll = false, + .tx_desc_auto_clear = false, + .fixed_mclk = 0 + }; + + // I2S pin configuration + i2s_pin_config_t i2s_mic_pins = { + .bck_io_num = Config::I2S_MIC_SERIAL_CLOCK, + .ws_io_num = Config::I2S_MIC_LEFT_RIGHT_CLOCK, + .data_out_num = I2S_PIN_NO_CHANGE, + .data_in_num = Config::I2S_MIC_SERIAL_DATA + }; + + // Install and configure I2S driver + i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL); + i2s_set_pin(I2S_NUM_0, &i2s_mic_pins); +} + +void readI2SSamples(int32_t* samples, size_t* bytesRead) { + i2s_read(I2S_NUM_0, samples, Config::SAMPLE_BUFFER_SIZE * sizeof(int32_t), bytesRead, portMAX_DELAY); +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index f1397a4..64420c5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,130 +1,38 @@ #include -#include -#include - -// you shouldn't need to change these settings -#define SAMPLE_BUFFER_SIZE 512 -#define SAMPLE_RATE 8000 -// most microphones will probably default to left channel but you may need to tie the L/R pin low -#define I2S_MIC_CHANNEL I2S_CHANNEL_FMT_ONLY_LEFT -// either wire your microphone to the same pins or change these to match your wiring -#define I2S_MIC_SERIAL_CLOCK 8 -#define I2S_MIC_LEFT_RIGHT_CLOCK 9 -#define I2S_MIC_SERIAL_DATA 10 - -// Add sample history tracking for range limiting -#define HISTORY_DURATION_MS 1000 // 1 seconds history -#define SAMPLES_PER_MS (SAMPLE_RATE / 1000) -#define HISTORY_SIZE (HISTORY_DURATION_MS * SAMPLES_PER_MS) - -#define MAX_RANGE_LIMIT 150000000 // Maximum allowed range limit -#define DEFAULT_RANGE_LIMIT 20000 // Default range limit when no history - -class AudioLevelTracker { -public: - AudioLevelTracker() { - resetMaxLevel(); - } - - void updateMaxLevel(int32_t sample) { - uint32_t currentTime = millis(); - - // Remove old samples (older than 10 seconds) - while (!sampleHistory.empty() && - (currentTime - sampleHistory.front().timestamp) > HISTORY_DURATION_MS) { - sampleHistory.pop_front(); - } - - // Add new sample, but cap it at MAX_RANGE_LIMIT - int32_t absValue = abs(sample); - absValue = min(absValue, MAX_RANGE_LIMIT); // Cap the value - SamplePoint newPoint = {currentTime, absValue}; - sampleHistory.push_back(newPoint); - - // Update maximum - maxLevel = 0; - for (const auto& point : sampleHistory) { - if (point.value > maxLevel) { - maxLevel = point.value; - } - } - } - - int32_t getMaxLevel() const { - return maxLevel; - } - - void resetMaxLevel() { - maxLevel = 0; - sampleHistory.clear(); - } - -private: - struct SamplePoint { - uint32_t timestamp; - int32_t value; - }; - std::deque sampleHistory; - int32_t maxLevel; -}; +#include "Config.h" +#include "I2SConfig.h" +#include "AudioLevelTracker.h" +// Declare array with explicit namespace reference +static int32_t raw_samples[Config::SAMPLE_BUFFER_SIZE]; AudioLevelTracker audioLevelTracker; -// don't mess around with this -i2s_config_t i2s_config = { - .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX), - .sample_rate = SAMPLE_RATE, - .bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT, - .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT, - .communication_format = I2S_COMM_FORMAT_STAND_I2S, // Updated from I2S_COMM_FORMAT_I2S - .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, - .dma_buf_count = 4, - .dma_buf_len = 1024, - .use_apll = false, - .tx_desc_auto_clear = false, - .fixed_mclk = 0}; - -// and don't mess around with this -i2s_pin_config_t i2s_mic_pins = { - .bck_io_num = I2S_MIC_SERIAL_CLOCK, - .ws_io_num = I2S_MIC_LEFT_RIGHT_CLOCK, - .data_out_num = I2S_PIN_NO_CHANGE, - .data_in_num = I2S_MIC_SERIAL_DATA}; - -void setup() -{ - // we need serial output for the plotter - Serial.begin(115200); - // start up the I2S peripheral - i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL); - i2s_set_pin(I2S_NUM_0, &i2s_mic_pins); +void setup() { + Serial.begin(Config::SERIAL_BAUD_RATE); + initI2S(); } -int32_t raw_samples[SAMPLE_BUFFER_SIZE]; -void loop() -{ - // read from the I2S device - size_t bytes_read = 0; - i2s_read(I2S_NUM_0, raw_samples, sizeof(int32_t) * SAMPLE_BUFFER_SIZE, &bytes_read, portMAX_DELAY); - int samples_read = bytes_read / sizeof(int32_t); - - // Calculate dynamic range limit based on max level from last 10 seconds - int32_t currentMaxLevel = audioLevelTracker.getMaxLevel(); - int32_t rangelimit = currentMaxLevel > 0 ? currentMaxLevel : DEFAULT_RANGE_LIMIT; // fallback to default if no history +void loop() { + size_t bytes_read = 0; + readI2SSamples(raw_samples, &bytes_read); + int samples_read = bytes_read / sizeof(int32_t); + + // Calculate dynamic range limit based on max level + int32_t currentMaxLevel = audioLevelTracker.getMaxLevel(); + int32_t rangelimit = currentMaxLevel > 0 ? currentMaxLevel : Config::DEFAULT_RANGE_LIMIT; - // dump the samples out to the serial channel - for (int i = 0; i < samples_read; i++) - { - // Update the max level tracker with current sample - audioLevelTracker.updateMaxLevel(raw_samples[i]); + // Process and output samples + for (int i = 0; i < samples_read; i++) { + // Update the max level tracker with current sample + audioLevelTracker.updateMaxLevel(raw_samples[i]); - // Print range limits for plotter - Serial.print(rangelimit * -1); - Serial.print(" "); - Serial.print(rangelimit); - Serial.print(" "); + // Print range limits for plotter + Serial.print(rangelimit * -1); + Serial.print(" "); + Serial.print(rangelimit); + Serial.print(" "); - // Print the actual sample - Serial.printf("%ld\n", raw_samples[i]); - } + // Print the actual sample + Serial.printf("%ld\n", raw_samples[i]); + } } \ No newline at end of file