.ino file
#define I2S_BCK_PIN 17 // Bit clock
#define I2S_WS_PIN 18 // Word select
#define I2S_SD_PIN 16 // Serial data
#define I2S_PORT I2S_NUM_0
this is my hardware config
.cpp file
AudioProcessor::AudioProcessor(int bckPin, int wsPin, int sdPin,
float input_scale, int input_zero_point, i2s_port_t port)
: _audio_buffer(nullptr), _i2sPort(port), _sampleRate(0),
_write_index(0), _samples_collected(0),
_frame_position(0), _hop_counter(0),
_input_scale(input_scale), _input_zero_point(input_zero_point)
{
_pinConfig.bck_io_num = bckPin;
_pinConfig.ws_io_num = wsPin;
_pinConfig.data_out_num = I2S_PIN_NO_CHANGE;
_pinConfig.data_in_num = sdPin;
memset(_frame_buffer, 0, sizeof(_frame_buffer));
}
AudioProcessor::~AudioProcessor()
{
i2s_driver_uninstall(_i2sPort);
}
bool AudioProcessor::begin(int sampleRate)
{
_sampleRate = sampleRate;
Serial.println("Initializing Audio Processor...");
Serial.printf("Sample rate: %d Hz\n", _sampleRate);
Serial.printf("Analysis window: %d seconds\n", ANALYSIS_SECONDS);
Serial.printf("Buffer size: %d samples\n", BUFFER_SAMPLES);
// Initialize MFCC extractor
if (!_mfcc_extractor.begin())
{
Serial.println("Failed to initialize MFCC extractor");
return false;
}
// Configure I2S
i2s_config_t i2sConfig = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
.sample_rate = static_cast<uint32_t>(_sampleRate),
.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 = 8,
.dma_buf_len = 256,
.use_apll = false,
.tx_desc_auto_clear = false,
.fixed_mclk = 0};
// Install I2S driver
esp_err_t err = i2s_driver_install(_i2sPort, &i2sConfig, 0, NULL);
if (err != ESP_OK)
{
Serial.printf("Failed installing I2S driver: %d\n", err);
return false;
}
// Set I2S pins
err = i2s_set_pin(_i2sPort, &_pinConfig);
if (err != ESP_OK)
{
Serial.printf("Failed setting I2S pins: %d\n", err);
i2s_driver_uninstall(_i2sPort);
return false;
}
// Explicitly set clock for mono 32-bit samples
err = i2s_set_clk(_i2sPort, _sampleRate, I2S_BITS_PER_SAMPLE_32BIT, I2S_CHANNEL_MONO);
if (err != ESP_OK)
{
Serial.printf("Failed setting I2S clock: %d\n", err);
i2s_driver_uninstall(_i2sPort);
return false;
}
// Clear DMA buffer
i2s_zero_dma_buffer(_i2sPort);
Serial.println("Audio Processor initialized successfully");
return true;
}
float AudioProcessor::convertI2SToFloat(int32_t i2s_sample)
{
// INMP441: 24-bit audio in 32-bit container
// Extract 24-bit value (right-shift 8 bits)
int32_t audio_24bit = i2s_sample >> 8;
// Sign extend if negative (24-bit signed to 32-bit signed)
if (audio_24bit & 0x00800000)
{
audio_24bit |= 0xFF000000;
}
// Convert to float in range [-1.0, 1.0]
// 24-bit signed range: -8388608 to 8388607
return (float)audio_24bit / 8388608.0f;
}
void AudioProcessor::quantizeMFCC(const float *mfcc_float, int8_t *mfcc_int8)
{
// Quantize using model's exact parameters
for (int i = 0; i < NUM_INPUTS; i++)
{
// Formula: quantized = round(value / scale + zero_point)
float scaled = mfcc_float[i] / _input_scale;
int32_t quantized = (int32_t)roundf(scaled + _input_zero_point);
// Clamp to int8 range (safety)
if (quantized > 127)
quantized = 127;
if (quantized < -128)
quantized = -128;
mfcc_int8[i] = (int8_t)quantized;
}
}
int AudioProcessor::read(int32_t *buffer, int bufferLength)
{
size_t bytesRead = 0;
// Read I2S data (blocking)
esp_err_t err = i2s_read(_i2sPort,
(void *)buffer,
bufferLength * sizeof(int32_t),
&bytesRead,
portMAX_DELAY);
if (err != ESP_OK)
{
Serial.printf("I2S read error: %d\n", err);
return 0;
}
int samplesRead = (int)(bytesRead / sizeof(int32_t));
// Convert and store in circular buffer
for (int i = 0; i < samplesRead; i++)
{
float sample = convertI2SToFloat(buffer[i]);
// Store in circular buffer
_audio_buffer[_write_index] = sample;
_write_index = (_write_index + 1) % BUFFER_SAMPLES;
_samples_collected++;
// Keep track of the most recent samples
if (_samples_collected > BUFFER_SAMPLES)
{
_samples_collected = BUFFER_SAMPLES;
}
}
return samplesRead;
}