Files
Dorm-Air-Conditioner-Smart-…/microphone.h
2025-09-20 16:57:16 +08:00

241 lines
7.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#ifndef MICROPHONE_H
#define MICROPHONE_H
#include <Arduino.h>
#include <driver/i2s.h>
#include <math.h>
// INMP441 麦克风模块类 - 所有功能集成在头文件中
class INMP441Microphone {
private:
// I2S 端口配置
static const i2s_port_t I2S_PORT = I2S_NUM_0;
// GPIO 引脚定义
int sck_pin; // 串行时钟引脚
int ws_pin; // 字选择引脚
int sd_pin; // 串行数据引脚
// 采样配置
uint32_t sample_rate;
uint16_t buffer_size;
bool initialized;
public:
// 构造函数
INMP441Microphone(int sck = 14, int ws = 15, int sd = 32, uint32_t rate = 16000, uint16_t buf_size = 1024) {
sck_pin = sck;
ws_pin = ws;
sd_pin = sd;
sample_rate = rate;
buffer_size = buf_size;
initialized = false;
}
// 析构函数
~INMP441Microphone() {
if (initialized) {
end();
}
}
// 初始化麦克风
bool begin() {
if (initialized) {
return true;
}
// I2S 配置
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,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.dma_buf_count = 4,
.dma_buf_len = buffer_size,
.use_apll = false,
.tx_desc_auto_clear = false,
.fixed_mclk = 0
};
// I2S 引脚配置
i2s_pin_config_t pin_config = {
.bck_io_num = sck_pin, // 串行时钟 (SCK)
.ws_io_num = ws_pin, // 字选择 (WS)
.data_out_num = I2S_PIN_NO_CHANGE, // 不使用输出
.data_in_num = sd_pin // 串行数据输入 (SD)
};
// 安装和启动 I2S 驱动
esp_err_t err = i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL);
if (err != ESP_OK) {
Serial.printf("Failed to install I2S driver: %s\n", esp_err_to_name(err));
return false;
}
err = i2s_set_pin(I2S_PORT, &pin_config);
if (err != ESP_OK) {
Serial.printf("Failed to set I2S pins: %s\n", esp_err_to_name(err));
i2s_driver_uninstall(I2S_PORT);
return false;
}
// 清空 DMA 缓冲区
i2s_zero_dma_buffer(I2S_PORT);
initialized = true;
Serial.println("INMP441 microphone initialized successfully");
return true;
}
// 停止麦克风
void end() {
if (initialized) {
i2s_driver_uninstall(I2S_PORT);
initialized = false;
Serial.println("INMP441 microphone stopped");
}
}
// 读取音频数据 (返回16位PCM数据)
size_t readAudioData(int16_t* buffer, size_t buffer_len) {
if (!initialized || buffer == nullptr || buffer_len == 0) {
return 0;
}
// 创建临时缓冲区用于存储32位原始数据
int32_t* raw_buffer = (int32_t*)malloc(buffer_len * sizeof(int32_t));
if (raw_buffer == nullptr) {
return 0;
}
size_t bytes_read = 0;
esp_err_t err = i2s_read(I2S_PORT, raw_buffer, buffer_len * sizeof(int32_t), &bytes_read, portMAX_DELAY);
if (err != ESP_OK) {
free(raw_buffer);
return 0;
}
// 转换32位数据到16位PCM
size_t samples_read = bytes_read / sizeof(int32_t);
for (size_t i = 0; i < samples_read && i < buffer_len; i++) {
// INMP441 输出24位数据在32位容器的高24位
// 右移8位得到24位数据再右移8位得到16位数据
buffer[i] = (int16_t)(raw_buffer[i] >> 16);
}
free(raw_buffer);
return samples_read;
}
// 读取音频数据 (返回32位原始I2S数据)
size_t readRawData(int32_t* buffer, size_t buffer_len) {
if (!initialized || buffer == nullptr || buffer_len == 0) {
return 0;
}
size_t bytes_read = 0;
esp_err_t err = i2s_read(I2S_PORT, buffer, buffer_len * sizeof(int32_t), &bytes_read, portMAX_DELAY);
if (err != ESP_OK) {
return 0;
}
return bytes_read / sizeof(int32_t);
}
// 获取采样率
uint32_t getSampleRate() const { return sample_rate; }
// 获取缓冲区大小
uint16_t getBufferSize() const { return buffer_size; }
// 检查是否已初始化
bool isInitialized() const { return initialized; }
// 设置采样率 (需要重新初始化)
void setSampleRate(uint32_t rate) {
bool was_initialized = initialized;
if (was_initialized) {
end();
}
sample_rate = rate;
if (was_initialized) {
begin();
}
}
// 获取音频电平 (RMS值)
float getAudioLevel(int16_t* buffer, size_t buffer_len) {
if (buffer == nullptr || buffer_len == 0) {
return 0.0f;
}
float sum = 0.0f;
for (size_t i = 0; i < buffer_len; i++) {
float sample = (float)buffer[i];
sum += sample * sample;
}
return sqrt(sum / buffer_len);
}
// 检测音频活动 (简单的音量阈值检测)
bool detectAudioActivity(int16_t* buffer, size_t buffer_len, int16_t threshold = 1000) {
if (buffer == nullptr || buffer_len == 0) {
return false;
}
// 计算RMS值
float rms = getAudioLevel(buffer, buffer_len);
// 检查是否超过阈值
return rms > threshold;
}
// 打印音频统计信息
void printAudioStats(int16_t* buffer, size_t buffer_len, unsigned long timestamp) {
if (buffer == nullptr || buffer_len == 0) {
return;
}
// 计算音频统计信息
float audioLevel = getAudioLevel(buffer, buffer_len);
bool hasActivity = detectAudioActivity(buffer, buffer_len, 1000);
// 找到最大值和最小值
int16_t maxVal = buffer[0];
int16_t minVal = buffer[0];
for (size_t i = 1; i < buffer_len; i++) {
if (buffer[i] > maxVal) maxVal = buffer[i];
if (buffer[i] < minVal) minVal = buffer[i];
}
// 打印统计信息
Serial.printf("[%6lu] RMS: %8.1f | 活动: %s | 最大: %6d | 最小: %6d | 样本: %d\n",
timestamp / 1000,
audioLevel,
hasActivity ? "" : "",
maxVal,
minVal,
buffer_len);
}
// 打印引脚连接信息
void printPinInfo() {
Serial.println("INMP441 引脚连接:");
Serial.printf(" SCK -> GPIO%d\n", sck_pin);
Serial.printf(" WS -> GPIO%d\n", ws_pin);
Serial.printf(" SD -> GPIO%d\n", sd_pin);
Serial.println(" VDD -> 3.3V");
Serial.println(" GND -> GND");
}
};
#endif // MICROPHONE_H