【ESP32 C++教程】Unit10-2:音频录制

本小节使用音频开发框架实现一个音频录制到文件的示例。

通过前一小节了解音频开发框架后,就可设计一个音频录制的处理流程,如下:
麦克风输入->音频编码->输出到文件
下面我们来实现这个流程。

本例使用一个支持I2S协议的麦克风模块,芯片为SPH0645LM4H
【ESP32 C++教程】Unit10-2:音频录制
I2S MEMS麦克风详情

示例:音频录制

本示例使用I2S MEMS麦克风模块录制音频并存储到SD存储卡的文件中。

从 https://gitee.com/billyzh/esp32-cpp-lesson 下载本教程的源码到本地硬盘文件夹,如d:\esp32-cpp-lesson
在VSCode中,选择【文件】->【打开文件夹...】选择上一步保存的文件夹打开

打开项目后,选择config.h文件,修改第10行为
#define APP_LESSON102_A 1

打开unit10-lesson101a/app_config.h文件,设置应用的相关配置
#define CONFIG_USE_FS 1 //启用文件系统模块
#define CONFIG_USE_AUDIO 1 //启用音频模块
#define CONFIG_USE_DISPLAY 1 //启用显示模块
#define CONFIG_USE_TFT_ESPI 1 //使用TFT_eSPI显示驱动


打开unit10-lesson101a/board_config.h文件,设置显示硬件的相关配置
#define SD_MOSI_PIN GPIO_NUM_17 //SD存储模块引脚
#define SD_MISO_PIN GPIO_NUM_16
#define SD_CLK_PIN GPIO_NUM_15
#define SD_CS_PIN GPIO_NUM_14
#define MIC_BCLK_PIN GPIO_NUM_25 //麦克风模块引脚
#define MIC_WS_PIN GPIO_NUM_26
#define MIC_DIN_PIN GPIO_NUM_27
#define DISPLAY_WIDTH 240 //屏幕宽度像素
#define DISPLAY_HEIGHT 320 //屏幕高度像素
#define DISPLAY_INVERT_COLOR true //是否反转颜色

创建SPH0645驱动类

虽然音频开发框架提供了一些常用的音频硬件驱动类,但有些音频硬件需要做一些特殊处理,可以通过扩展相关类来实现。
本例使用的SPH0645LM4H芯片为单声道麦克风,数据采用32bit传输,需要做扩展处理。

AudioI2sSph0645类继承自AudioI2sSimplex类,代码如下(unit10-lesson102a/audio_i2s_sph0645.cpp)

AudioI2sSph0645::AudioI2sSph0645(gpio_num_t mic_sck, gpio_num_t mic_ws, gpio_num_t mic_din, gpio_num_t mclk) 
{  
    i2s_driver_ = new I2sDriver();
    i2s_driver_->setPins(mic_sck, mic_ws, -1, mic_din, mclk);
}

bool AudioI2sSph0645::Init(const audio_config_t &config)
{
    Log::Info(TAG, "Init...");
    AudioI2sCodec::Init(config);
    
    uint32_t sample_rate = 16000;

    if (!i2s_driver_->begin(I2S_MODE_STD, sample_rate, I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_MONO, I2S_STD_SLOT_LEFT)) {
        Log::Error(TAG, "Failed to initialize I2S!");
        return false;
    }

    // 对于SPH0645LM4H型号的麦克风,配置一个32bit->16bit的转换程序
    i2s_driver_->configureRX(sample_rate, I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_MONO, I2S_RX_TRANSFORM_32_TO_16);

    Log::Info(TAG, "i2s initialize success.");
    return true;
}

audio_config_t AudioI2sSph0645::audio_config() 
{
    audio_config_t cfg = {
        .input_rate = (sample_rate_t)16000,
        .input_bits = (sample_bits_t)32,
        .input_channels = (channels_t)1,
        .output_rate = (sample_rate_t)i2s_driver_->rxSampleRate(),
        .output_bits = (sample_bits_t)i2s_driver_->rxDataWidth(),
        .output_channels = (channels_t)i2s_driver_->rxSlotMode()
    };
    return cfg;
}

代码解读
1.在构造函数内,创建I2sDriver类实例,然后设置引脚;
2.在Init函数内,调用begin方法启动i2s、调用configureRX配置32bit->16bit的音频数据转换程序;
3.重载audio_config()函数,返回一个包含音频采样率、位宽和通道数的配置用于后面的处理,当输入与输出的音频配置不一致时,必须重载此函数;

创建FileSystem实例和Display实例,同前一小节,代码见(unit10-lesson102a/my_board.cpp)

创建AudioCodec实例,代码如下(unit10-lesson102a/my_board.cpp)

void MyBoard::InitAudioCodec() {
    Log::Info(TAG, "initial audio codec.");
    audio_codec_ = new AudioI2sSph0645(MIC_BCLK_PIN, MIC_WS_PIN, MIC_DIN_PIN);
}

这里使用Sph0645驱动类来构造AudioCodec实例。

音频录制代码(unit10-lesson102a/my_application.cpp)

void MyApplication::OnInit() {
    Display *display = Board::GetInstance().GetDisplay();
    display->Rotate(1);

    FileSystem *fsys = Board::GetInstance().GetFileSystem();
    std::string filepath = "/test001.wav";

    display->GetWindow()->SetText(1, "Recording to file: " + filepath);

    // 音频处理流
    // I2sInput(麦克风输入) -> Encoder(编码) -> FileOutput(输出到文件)

    uint16_t samples_msec = 500; // 0.5s

    // I2S输入
    AudioCodec *audio_codec = Board::GetInstance().GetAudioCodec();
    AudioI2sInput *input = new AudioI2sInput(audio_codec, samples_msec);

    // 编码输出
    AudioEncodeOutput *output = new AudioEncodeOutput(fsys, filepath, "wav");
    audio_config_t output_config = {
        .output_rate = SAMPLE_RATE_16K,
        .output_bits = SAMPLE_BITS_16,   // 32bit -> 16bit
        .output_channels = CHANNELS_1
    };
    output->SetAudioConfig(output_config);

    // 音频管道
    pipe_ = new AudioPipe();

    // 事件监听
    pipe_->SetPipeListener([this,input](PipeAction action){
        AudioCodec *codec = Board::GetInstance().GetAudioCodec();
        if (action==PipeAction::Inited)
        {
            codec->Start();
        }
        else if (action==PipeAction::Ended)
        {
            codec->EnableInput(false);
            Display *display = Board::GetInstance().GetDisplay();
            display->GetWindow()->SetText(3, "Record msec: " + std::to_string(input->duration_ms()));
            display->GetWindow()->SetText(4, "Audio pipe ended.");
        }
        else if (action==PipeAction::Processing) 
        {
            Log::Info(TAG, "audio processing...");

            info_count_++;
            if (info_count_ % 10 == 0) 
            {  // 取整
                int n = (info_count_ / 10) % 2;
                Display *display = Board::GetInstance().GetDisplay();
                display->GetWindow()->SetText(3, info_text_[n]);
            }
        }
        else if (action==PipeAction::Error)
        {
            Display *display = Board::GetInstance().GetDisplay();
            display->GetWindow()->SetText(4, pipe_->last_error());
        }
    });

    // 启动管道
    pipe_->Start(input, output);
    Log::Info(TAG, "Audio pipe started.");
}

程序解读
1.创建音频输入AudioInput的实例,本例为麦克风输入,使用AudioI2sInput类,并关联AudioCodec实例;
2.创建音频输出AudioOutput的实例,本例为文件输出,使用Wav编码,故使用AudioEncodeOuput类;
3.创建音频管道实例,设置音频管道监听程序,音频管道处理中会在各处理节点进行回调;
4.启动音频管道;
5.双击按键可停止录制。

编译项目并上传开发板检验

【ESP32 C++教程】Unit10-2:音频录制


通过音频开发框架,只需要设计音频处理流程,就可以简单快捷的开发音频应用,
更多音频开发的高级内容,请查看后续的【ESP32音频开发教程】

- 本文为本站原创文章,转载请保留出处。
- 文章链接:https://www.xpstem.com/article/2000537

04-03   阅读(20)   评论(0)
 标签: 编程 ESP32 ESP32-ArduinoFx I2S

涨知识
导轨

导轨又称滑轨、线性导轨、线性滑轨,用于直线往复运动场合,拥有比直线轴承更高的额定负载, 同时可以承担一定的扭矩,可在高负载的情况下实现高精度的直线运动。

评论:
相关文章
MimiClaw应用与开发教程1:部署和测试

MimiClaw‌ 是一款基于 ‌ESP32-S3‌ 芯片的超轻量级AI助手,适合嵌入式AI与物联网开发者快速部署本地化AI代理。本系列教程基于MimiClaw的Arduino移植版本进行讲解,小节主要讲解部署和测试。


ESP32扫描wifi 热点列表

就像我们用手机打开WiFi功能后可以浏览附近的可用WiFi。要将手机连接到热点,通常需要打开Wi-Fi设置应用程序,列出可用的网络,然后选择所需的热点。然后输入密码(或不输入密码),可以使用ESP32进行相同的操作。


MimiClaw 配置飞书机器人和添加硬件控制技能

本文本介绍配置飞书机器人为MimiClaw的一个输入/输出端,和添加一个控制WS2812与LED的控制技能。


ESP32-S3 部署 MimicLaw 完整教程:从零到成功调用 DeepSeek

一块 30 块钱的开发板 + 一个大模型 API,就能做出可以听懂人话的智能硬件。 本文记录完整安装过程和踩坑经验,确保你跟着做就能跑通。


MimiClaw 架构全解析,把 “智能龙虾” 跑在 ESP32 上

本文将从手绘架构图入手,逐层拆解 MimiClaw 的分层设计、核心模块、数据流转与底层实现,带你解剖这只“智能虾”的技术骨架,看懂在 C 语言加持下,AI 智能体如何以可穿戴设备的形态,在你身边稳稳运行、离线服务、主动响应。


如何用 platform.local.txt 深度定制 ESP32 编译流程?

本文介绍如何在不脱离 ArduinoIDE 可视化开发的前提下,通过一个名为 platform.local.txt 的小文件,实现对 ESP32 编译流程的精准控制。


优化Arduino-ESP32程序体积

本文将系统分析程序体积增长的五大根源,并提供经过验证的优化方案,帮助减小固件大小。


开发ESP32大模型AI语音助手-从软件到硬件

本文所DIY的语音助手设备端使用的是MicroPython、服务端是Python,对于很多开发者来说MicroPython入门没难度。


ESP32 I2S 接口深度解析:从时序、格式到 ESP-IDF 驱动实战

I2S协议通过BCLK、LRCLK和DATA三线精准传输音频数据,但时序边沿、帧格式、时钟源等细节常引发噪声或断连。本文详解ESP32的I2S实现,从协议原理到ESP-IDF v5.x代码配置,助你避开常见陷阱,确保音频稳定传输。


【ESP32 C++教程】Unit10-1:音频播放

本小节介绍音频的基础知识、音频开发框架和AudioCodec的简介,用一个音频播放示例来说明音频管道的使用。