apollo3录音到wav播放解决方法

ops/2025/3/16 3:28:41/

SDK DEMO项目:ap3bp_evb_vos_pcm_recorder_20210901

pcm_recorder.c

//*****************************************************************************
//
// Options
//
//*****************************************************************************
#define PRINT_UART          0
#define USE_OPUS            1

//*****************************************************************************
//
// Cache configuration
//
//*****************************************************************************
const am_hal_cachectrl_config_t am_hal_cachectrl_benchmark =
{
    .bLRU                       = 0,
    .eDescript                  = AM_HAL_CACHECTRL_DESCR_1WAY_128B_512E,
    .eMode                      = AM_HAL_CACHECTRL_CONFIG_MODE_INSTR,
};

#define RTT_LOGGER_BUFFER_LENGTH_BYTE   (128*1024)
uint8_t ui8RttLoggerBuffer[RTT_LOGGER_BUFFER_LENGTH_BYTE];

#define AUDIO_FRAME_MS                  20

#define AUDIO_FRAME_SIZE_SAMPLES        ((16000)/(1000/AUDIO_FRAME_MS))
#define AUDIO_FRAME_SIZE_MONO_BYTES     (AUDIO_FRAME_SIZE_SAMPLES*2)
#define AUDIO_FRAME_SIZE_STEREO_BYTES   (AUDIO_FRAME_SIZE_SAMPLES*4)

#define ENCODED_HEADER_SIZE_BYITES      8
#define ENCODED_COMPRESS_RATE           8
#define ENCODED_FRAME_SIZE_BYTES        ((AUDIO_FRAME_SIZE_SAMPLES*2/ENCODED_COMPRESS_RATE) + ENCODED_HEADER_SIZE_BYITES)


uint8_t g_opusAudioInputBuffer[AUDIO_FRAME_SIZE_MONO_BYTES]; // 20ms mono raw audio frame
bool g_opusInputReadyFlag = false;

#if USE_OPUS
uint8_t g_opusAudioOutputBuffer[ENCODED_FRAME_SIZE_BYTES]; // encoded audio frame, with header
uint16_t g_opusInputIndex = 0;
#endif // #if USE_OPUS

//*****************************************************************************
//
// PDM configuration information.
//
//*****************************************************************************
void *PDMHandle = NULL;

#define PDM_CLK                     12
#define PDM_CLK_PIN_CFG             AM_HAL_PIN_12_PDMCLK
#define PDM_DATA                    11
#define PDM_DATA_PIN_CFG            AM_HAL_PIN_11_PDMDATA

am_hal_pdm_config_t g_sPdmConfig =
{
    .eClkDivider = AM_HAL_PDM_MCLKDIV_1,    //主时钟分频设置为1,不分频
    .eLeftGain = AM_HAL_PDM_GAIN_P105DB,    //左声道增益延迟设置为105dB
    .eRightGain = AM_HAL_PDM_GAIN_P105DB,
    .ui32DecimationRate = 48,     // CLK 1.5 Mhz  //降频采样倍数
    .bHighPassEnable = 0, //禁用高通滤波器
    .ui32HighPassCutoff = 0xB,
    .ePDMClkSpeed = AM_HAL_PDM_CLK_1_5MHZ,    //PDM采样频率,1.5M转换后是16K音频频率
    .bInvertI2SBCLK = 0,
    .ePDMClkSource = AM_HAL_PDM_INTERNAL_CLK,    //使用内部时钟源
    .bPDMSampleDelay = 0,    //PDM采样延迟
    .bDataPacking = 1,    //启用数据换包
    .ePCMChannels = AM_HAL_PDM_CHANNEL_LEFT,    //录音声道选择
    .bLRSwap = 0,    //不交换左右声道
    .bSoftMute = 0,    //禁用软静音
};

//*****************************************************************************
//
// Start a transaction to get some number of bytes from the PDM interface.
//
//*****************************************************************************
uint32_t g_ui32PDMDataBuffer[AUDIO_FRAME_SIZE_MONO_BYTES/4];
void
pdm_trigger_dma(void)
{
    //
    // Configure DMA and target address.
    //
    am_hal_pdm_transfer_t sTransfer;
    sTransfer.ui32TargetAddr = (uint32_t ) g_ui32PDMDataBuffer;
    sTransfer.ui32TotalCount = (AUDIO_FRAME_SIZE_MONO_BYTES);

    //
    // Start the data transfer.
    //
    am_hal_pdm_dma_start(PDMHandle, &sTransfer);
}

//*****************************************************************************
//
// Structure for handling PDM register state information for power up/down
//
//*****************************************************************************
typedef struct
{
    bool bValid;
}
am_hal_pdm_register_state_t;

//*****************************************************************************
//
// Structure for handling PDM HAL state information.
//
//*****************************************************************************
typedef struct
{
    am_hal_handle_prefix_t prefix;
    am_hal_pdm_register_state_t sRegState;
    uint32_t ui32Module;
}
am_hal_pdm_state_t;

void am_app_KWD_pdm_dma_disable(void *pHandle)
{
    am_hal_pdm_state_t *pState = (am_hal_pdm_state_t *) pHandle;
    uint32_t ui32Module = pState->ui32Module;

    //
    // Disable DMA
    //
    PDMn(ui32Module)->DMACFG_b.DMAEN = PDM_DMACFG_DMAEN_DIS;

}

//-----------------------------------------------------------------------------
// METHOD:  PDM_Init
// PURPOSE: PDM module configuration
//-----------------------------------------------------------------------------
void PDMInit(void)
{

    if (PDMHandle != NULL)
        return;
    //
    // Initialize, power-up, and configure the PDM.
    //
    am_hal_pdm_initialize(0, &PDMHandle);
    //
    // Configure the necessary pins.
    //
    am_hal_gpio_pincfg_t sPinCfg = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

    sPinCfg.uFuncSel = PDM_CLK_PIN_CFG;
    am_hal_gpio_pinconfig(PDM_CLK, sPinCfg);

    sPinCfg.uFuncSel = PDM_DATA_PIN_CFG;
    am_hal_gpio_pinconfig(PDM_DATA, sPinCfg);

    am_hal_pdm_power_control(PDMHandle, AM_HAL_PDM_POWER_ON, false);
    am_hal_pdm_configure(PDMHandle, &g_sPdmConfig);
    am_hal_pdm_fifo_flush(PDMHandle);

    //
    // Configure and enable PDM interrupts (set up to trigger on DMA
    // completion).
    //
    am_hal_pdm_interrupt_enable(PDMHandle, (AM_HAL_PDM_INT_DERR
                                            | AM_HAL_PDM_INT_DCMP
                                            | AM_HAL_PDM_INT_UNDFL
                                            | AM_HAL_PDM_INT_OVF));
    NVIC_EnableIRQ(PDM_IRQn);
}

void PDMDeInit()
{
    if(PDMHandle == NULL)
      return;

    am_hal_pdm_interrupt_clear(PDMHandle, (AM_HAL_PDM_INT_DERR
                                            | AM_HAL_PDM_INT_DCMP
                                            | AM_HAL_PDM_INT_UNDFL
                                            | AM_HAL_PDM_INT_OVF));

    am_hal_pdm_interrupt_disable(PDMHandle, (AM_HAL_PDM_INT_DERR
                                            | AM_HAL_PDM_INT_DCMP
                                            | AM_HAL_PDM_INT_UNDFL
                                            | AM_HAL_PDM_INT_OVF));

    NVIC_DisableIRQ(PDM_IRQn);

    am_hal_gpio_pinconfig(PDM_CLK, g_AM_HAL_GPIO_DISABLE);
    am_hal_gpio_pinconfig(PDM_DATA, g_AM_HAL_GPIO_DISABLE);

    // This was a workaround code to measure power consumption. Need to review!!
    am_app_KWD_pdm_dma_disable(PDMHandle);

    am_hal_pdm_disable(PDMHandle);
    am_hal_pdm_power_control(PDMHandle, AM_HAL_PDM_POWER_OFF, false);

    am_hal_pdm_deinitialize(PDMHandle);
    PDMHandle = NULL;

}

void am_pdm0_isr(void)
{
    uint32_t ui32Status;

    //
    // Read the interrupt status.
    //
    am_hal_pdm_interrupt_status_get(PDMHandle, &ui32Status, true);
    am_hal_pdm_interrupt_clear(PDMHandle, ui32Status);

    //
    // Once our DMA transaction completes, we will disable the PDM and send a
    // flag back down to the main routine. Disabling the PDM is only necessary
    // because this example only implemented a single buffer for storing FFT
    // data. More complex programs could use a system of multiple buffers to
    // allow the CPU to run the FFT in one buffer while the DMA pulls PCM data
    // into another buffer.
    //
    if (ui32Status & AM_HAL_PDM_INT_DCMP)
    {
        // trigger next traction
        PDMn(0)->DMATOTCOUNT = AUDIO_FRAME_SIZE_MONO_BYTES;  // FIFO unit in bytes

        amu2s_send(Amu2s_pcm, g_ui32PDMDataBuffer, AMU2S_PCM_BYTES);

#if USE_OPUS
        //
        // move data to opus buffer
        //
        memmove(g_opusAudioInputBuffer, g_ui32PDMDataBuffer,AUDIO_FRAME_SIZE_MONO_BYTES);
        g_opusInputReadyFlag = true;
#endif // #if USE_OPUS
    }
    else if(ui32Status & (AM_HAL_PDM_INT_UNDFL | AM_HAL_PDM_INT_OVF))
    {
        am_hal_pdm_fifo_flush(PDMHandle);
    }

}

#define AM_CRITICAL_BEGIN_VOS                                               \
    if ( 1 )                                                                \
    {                                                                       \
        volatile uint32_t ui32Primask_04172010;                             \
        ui32Primask_04172010 = am_hal_interrupt_master_disable();

#define AM_CRITICAL_END_VOS                                                 \
        am_hal_interrupt_master_set(ui32Primask_04172010);                  \
    }

//*****************************************************************************
//
// Main Function.
//
//*****************************************************************************
int
main(void)
{
    //
    // Set the clock frequency.
    //
    am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_SYSCLK_MAX, 0);

    //
    // Set the default cache configuration
    //
    am_hal_cachectrl_config(&am_hal_cachectrl_benchmark);
    am_hal_cachectrl_enable();

    //
    // Configure the board for low power operation.
    //
    am_bsp_low_power_init();

#if (PRINT_UART == 1)
    am_bsp_uart_printf_enable();

    //
    // Clear the terminal and print the banner.
    //
    am_util_stdio_terminal_clear();
    am_util_stdio_printf("Ambiq Micro 'while' example.\n\n");

    //
    // Brief description
    //
    am_util_stdio_printf("Used for measuring power in an infinite while loop.\n");

    //
    // Print the compiler version.
    //
    am_util_stdio_printf("App Compiler:    %s\n", COMPILER_VERSION);
    am_util_stdio_printf("HAL Compiler:    %s\n", g_ui8HALcompiler);
    am_util_stdio_printf("HAL SDK version: %d.%d.%d\n",
                         g_ui32HALversion.s.Major,
                         g_ui32HALversion.s.Minor,
                         g_ui32HALversion.s.Revision);
    am_util_stdio_printf("HAL compiled with %s-style registers\n",
                         g_ui32HALversion.s.bAMREGS ? "AM_REG" : "CMSIS");


#endif // PRINT_UART

    amu2s_init();
    PDMInit();
    am_hal_pdm_enable(PDMHandle);
    pdm_trigger_dma();

    SEGGER_RTT_ConfigUpBuffer(1, "TestDataLogger", ui8RttLoggerBuffer, RTT_LOGGER_BUFFER_LENGTH_BYTE, SEGGER_RTT_MODE_NO_BLOCK_SKIP);

#if USE_OPUS
    /* initialize the audio encoder */
    audio_enc_init(true);       // true for with header
#endif // #if USE_OPUS

    // master interrupt enable
    am_hal_interrupt_master_enable();

    while(1)
    {

#if USE_OPUS
        if(g_opusInputReadyFlag == true)
        {
            g_opusInputReadyFlag = false;
            // data ready to encode, run encoder

            uint32_t encoded_bytes = audio_enc_encode_frame((short*)g_opusAudioInputBuffer, AUDIO_FRAME_SIZE_SAMPLES, (unsigned char*)g_opusAudioOutputBuffer);

            // send to RTT
            SEGGER_RTT_Write(1, (uint8_t*)g_opusAudioOutputBuffer, encoded_bytes);

            // send to AmU2S
            amu2s_send(Amu2s_opus, g_opusAudioOutputBuffer, AMU2S_OPUS_BYTES);

#endif // #if USE_OPUS
        }
    }

项目编译,在开发板上运行,打开官方配备的工具,抓取录音数据,如下图:

按任意键停止抓取,即可抓取到非标准的PCM音频,如下图:

使用提供的 Python 脚本

运行以下命令解码为 PCM 文件:

python ftdi_bin_decoder.py decoder input.bin --output=audio --format=raw

生成文件:audio_pcm(原始 PCM 数据)

执行成功,同时生成标准的PCM数据BIN文件和OPUS压缩的BIN文件,如下图:

如果出现以下错误提示
import pandas as pd
ModuleNotFoundError: No module named 'pandas'
遇到 ModuleNotFoundError: No module named 'pandas' 错误,说明你的 Python 环境中缺少 pandas 库

通过 pip 直接安装:pip install pandas

转换为 WAV 播放命令 :
python bin_to_wav.py mono -i audio_pcm
在PCM音频(BIN)文件加上WAV文件头,

执行成功后,生成可播放音频文件,如下图:

如果出现以下错误提示:
import soundfile as sf
ModuleNotFoundError: No module named 'soundfile'

错误 ModuleNotFoundError: No module named 'soundfile' 表明 Python 环境中缺少 soundfile 库。该库用于读写音频文件(如 WAV 格式),是 bin_to_wav.py 脚本的依赖项。
解决方法
安装 soundfile 库
通过 pip 直接安装:pip install soundfile

bin_to_wav.py这个python脚本重要补充说明

  1. 硬件适配特性

    • 声道交换:通过 --swap 1 参数修正 Apollo PDM 模块的左右声道反接问题

    • 位深度处理:假设输入为 16-bit PCM,通过 /32768 实现标准化

  2. 音频处理流程: 原始PCM --> 解析为整数 --> 声道分离 --> 浮点归一化 --> 峰值归一化 --> 输出WAV


http://www.ppmy.cn/ops/166109.html

相关文章

树莓科技(成都)集团:如何铸就第五代产业园标杆

树莓科技(成都)集团铸就第五代产业园标杆,主要体现在以下几个方面: 精准定位与前瞻布局 树莓科技并非盲目扩张,而是精准锚定数字经济发展方向。以成都为起点,迅速构建起全国性的园区版图,体现…

多模态大模型Ovis核心技术点、训练细节、训练数据

文章提出:传统的 MLLMs 中,文本嵌入是从 LLM 的嵌入查找表中索引得到的,而视觉嵌入是由视觉编码器(如:ViT)直接生成的连续向量。这种差异导致在视觉和文本信息融合时存在挑战。 与传统的MLLM不同&#xff…

Unity大型游戏开发全流程指南

一、开发流程与核心步骤 1. 项目规划与设计阶段 需求分析 明确游戏类型(MMORPG/开放世界/竞技等)、核心玩法(战斗/建造/社交)、目标平台(PC/移动/主机)示例:MMORPG需规划角色成长树、副本Boss…

【go】函数类型的作用

Go 语言函数类型的巧妙应用 函数类型在 Go 语言中非常强大,允许将函数作为值进行传递和操作。下面详细介绍函数类型的各种妙用: 1. 回调函数 // 定义一个函数类型 type Callback func(int) int// 接受回调函数的函数 func processData(data []int, ca…

conda install 和 pip install 的区别

conda install 和 pip install 是两个常用的包安装命令,但它们在很多方面存在差异。 1. 所属管理系统不同 1.1 conda install conda install 是Anaconda和Miniconda发行版自带的包管理工具 conda 的安装命令。conda 是一个跨平台的开源包管理系统和环境管理系统&…

蓝桥杯 阶乘求值

问题描述 给定 nn,求 n!n! 除以 10000000071000000007 的余数。 其中 n!n! 表示 nn 的阶乘,值为从 11 连乘到 nn 的积,即 n!123…nn!123…n。 输入格式 输入一行包含一个整数 nn。 输出格式 输出一行,包含一个整数,表示…

LearnOpenGL-笔记-其四

上一篇笔记中结束了材质的内容,现在让我们进入新的部分: 光照贴图 光照贴图的概念其实非常简单,简单地说,我们希望不同的材质拥有不同的光照属性,所以不同的材质要应用不用的光照贴图,最常见的有我们的漫…

(七)Spring Boot学习——Redis使用

有部分内容是常用的,为了避免每次都查询数据库,将部分数据存入Redis。 一、 下载并安装 Redis Windows 版的 Redis 官方已不再维护,你可以使用 微软提供的 Redis for Windows 版本 或者 使用 WSL(Windows Subsystem for Linux&a…