esp32开发与应用(边采样边播放音频)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】

前面说到了音频的播放,一般采用i2s的格式,也就是bclk、lrclk、sda。今天来学一下麦克风。如果是播放音乐,没有麦克风也是可以的。但很多时候,我们还是有麦克风采样、录制的需求。所以这个时候,就要学一下麦克风采样。

1、麦克风接口

一般mic有两种接口,一种是pdm接口,也就是只有clk和data。还有一种就是i2s接口,这种就和之前的音乐播放一样的接口,即3个pin,lrclk、bclk和sda。

2、管脚分配

如果是mic,一般走4和5;如果是i2s,一般走26、25、22。

3、电压分配

3.3v即可。

4、芯片选型

这次采用的麦克风mic采样芯片,和前面一篇文章其实是一块板子,使用的芯片是lm2718。

5、示例代码

考虑到之前学习过音频播放,这里采用的方式是一边采样、一边播放的方式。这样既可以巩固原来的知识点,又可以学习到新的mic采样知识。整个就是一个扩音器的效果。

复制代码
#include <stdint.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/i2s_std.h"
#include "driver/i2s_pdm.h"

static const char *TAG = "MIC_LOOPBACK";

#define SAMPLE_RATE 16000

// ---- Mic (PDM RX) pins ----
#define PDM_CLK_GPIO   GPIO_NUM_4
#define PDM_DATA_GPIO  GPIO_NUM_5

// ---- Speaker (I2S STD TX, NS4168) pins ----
#define I2S_BCLK       GPIO_NUM_26
#define I2S_WS         GPIO_NUM_25
#define I2S_DOUT       GPIO_NUM_22

#define READ_CHUNK_SAMPLES 512      // ~32ms per chunk @16kHz, controls latency

static i2s_chan_handle_t rx_chan = NULL; // mic
static i2s_chan_handle_t tx_chan = NULL; // speaker

// ---------- Mic: PDM RX on I2S_NUM_0 ----------
static void mic_init(void)
{
    i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
    ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, NULL, &rx_chan));

    i2s_pdm_rx_config_t pdm_rx_cfg = {
        .clk_cfg = I2S_PDM_RX_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
        .slot_cfg = I2S_PDM_RX_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT,
                                                    I2S_SLOT_MODE_MONO),
        .gpio_cfg = {
            .clk = PDM_CLK_GPIO,
            .din = PDM_DATA_GPIO,
            .invert_flags = { .clk_inv = false },
        },
    };
    pdm_rx_cfg.slot_cfg.slot_mask = I2S_PDM_SLOT_LEFT; // try _RIGHT if silent/noisy

    ESP_ERROR_CHECK(i2s_channel_init_pdm_rx_mode(rx_chan, &pdm_rx_cfg));
    ESP_ERROR_CHECK(i2s_channel_enable(rx_chan));

    ESP_LOGI(TAG, "Mic (PDM RX) ready on I2S_NUM_0, CLK=%d DATA=%d",
             PDM_CLK_GPIO, PDM_DATA_GPIO);
}

// ---------- Speaker: standard I2S TX on I2S_NUM_1 (same as your NS4168 code) ----------
static void speaker_init(void)
{
    i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_1, I2S_ROLE_MASTER);
    ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_chan, NULL));

    i2s_std_config_t std_cfg = {
        .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
        .slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT,
                                                         I2S_SLOT_MODE_MONO),
        .gpio_cfg = {
            .mclk = I2S_GPIO_UNUSED,
            .bclk = I2S_BCLK,
            .ws   = I2S_WS,
            .dout = I2S_DOUT,
            .din  = I2S_GPIO_UNUSED,
        },
    };

    ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_chan, &std_cfg));
    ESP_ERROR_CHECK(i2s_channel_enable(tx_chan));

    ESP_LOGI(TAG, "Speaker (I2S STD TX) ready on I2S_NUM_1, BCLK=%d WS=%d DOUT=%d",
             I2S_BCLK, I2S_WS, I2S_DOUT);
}

// ---------- Loop: read from mic, write straight to speaker ----------
static void loopback_task(void *arg)
{
    int16_t *buf = malloc(READ_CHUNK_SAMPLES * sizeof(int16_t));
    if (!buf) {
        ESP_LOGE(TAG, "malloc failed");
        vTaskDelete(NULL);
        return;
    }

    // Optional software gain: raw PDM mic level is often quiet.
    // Adjust or set to 1.0f if it clips/distorts.
    const float GAIN = 4.0f;

    while (1) {
        size_t bytes_read = 0;
        esp_err_t ret = i2s_channel_read(rx_chan, buf,
                                          READ_CHUNK_SAMPLES * sizeof(int16_t),
                                          &bytes_read, portMAX_DELAY);
        if (ret != ESP_OK || bytes_read == 0) {
            continue;
        }

        int samples = bytes_read / sizeof(int16_t);
        for (int i = 0; i < samples; i++) {
            int32_t v = (int32_t)(buf[i] * GAIN);
            if (v > INT16_MAX) v = INT16_MAX;
            if (v < INT16_MIN) v = INT16_MIN;
            buf[i] = (int16_t)v;
        }

        size_t bytes_written = 0;
        ESP_ERROR_CHECK(
            i2s_channel_write(tx_chan, buf, bytes_read, &bytes_written, portMAX_DELAY)
        );
    }

    free(buf);
    vTaskDelete(NULL);
}

void app_main(void)
{
    ESP_LOGI(TAG, "Mic -> Speaker realtime loopback starting");

    mic_init();
    speaker_init();

    xTaskCreate(loopback_task, "loopback_task", 4096, NULL, 5, NULL);
}

6、测试和验证

测试的话,就是等程序烧入之后,看看对着mic说话的时候,听听喇叭里面有没有声音就好。

相关推荐
DogDaoDao4 小时前
【H266/VVC提案解读】ITU-T H.274 (V4) 规范深度解读 — 视频编码 SEI 消息的全面演进
音视频·实时音视频·视频编解码·流媒体·h266·vvc·视频编解码标准
hhzz6 小时前
Tiger AI 平台「手势识别」功能全解析:从数字手势 0–9,到中国手语字母,再到本地视频批量识别——一条链路,三种输入,双模型可同开;双手比划,机器秒懂
人工智能·python·深度学习·aigc·音视频
martindelophy6 小时前
DeepSeek Harness 插件开发实战:用自然语言检查、修改并渲染 Timeline Studio 视频工程
音视频
平原20187 小时前
LTX-2.5 22B 本地部署:ModelScope 下载、8 步推理与图生视频命令
音视频
淡淡的香烟7 小时前
Android视频直播播放器简单封装
android·物联网·音视频
show43318 小时前
2026视频处理小程序技术选型指南:链接解析+OCR+ASR+AI配音多引擎对比
小程序·ocr·音视频
昨日之日200618 小时前
LTX-2.5:更清晰、更可控、同步音画、多镜头连贯的AI视频神器
人工智能·音视频
点云-激光雷达-Slam-三维牙齿1 天前
批量音频转文本 ASR 目前中文识别效果最好的qwen3 模型
音视频
小柯南敲键盘1 天前
跨马翻译:跨境电商批量图片翻译与视频字幕一站式工具
人工智能·python·音视频
weixin_446260851 天前
AVA-Encoder:面向智能体原生视频表征学习框架
学习·音视频