当前位置: 首页 > news >正文

apollo3录音到wav播放解决方法

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

相关文章:

  • Pixelmator Pro for Mac 专业图像处理软件【媲美PS的修图】
  • perl、python、tcl语法中读写Excel的模块
  • 【Qt】带参数的信号和槽函数
  • HTML嵌入CSS样式超详解(尊享)
  • 《2025年软件测试工程师面试》MySQL面试题
  • 【性能工具】Perfetto中如何分析主线程为何进入sleep状态
  • Week4_250310~250316_OI日志(待完善)
  • PySide(PyQT),QGraphicsRectItem的setPos()和setRect()的坐标位置的区别
  • Dynamics 365 新版的Power apps开发界面中如何找到开发者资源
  • WM_CREATE
  • nndl: chap1_warmup_numpy_tutorial
  • Java高频面试之集合-09
  • 【Python】dash-fastapi前后端搭建
  • QT多线程
  • 初级网工之路: 如何通过服务器进行交换机的配置
  • Faster R-CNN原理详解以及Pytorch实现模型训练与推理
  • 【NexLM 开源系列】让 AI 聊天更丝滑:SSE 实现流式对话!
  • 存储过程和自定义函数在银行信贷业务中的应用(oracle)
  • pop_dialog_state(state: State)弹出对话栈并返回到主助手,让整个对话流程图可以明确追踪对话流,并将控制权委派给特定的子对话图。
  • CHI协议中的read transaction flow
  • 泽连斯基表示将在土耳其“等候”普京
  • 经济日报金观平:充分发挥超大规模市场优势
  • 深圳市政协原副主席王幼鹏被“双开”
  • 2025中国南昌国际龙舟赛5月23日启幕,是历年来南昌举办的最高规格龙舟赛事
  • 山东14家城商行中,仅剩枣庄银行年营业收入不足10亿
  • 习近平向中国人民解放军仪仗队致意