目录

apollo3录音到wav播放解决方法

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

}

}

项目编译,在开发板上运行,打开官方配备的工具,抓取录音数据,如下图: https://i-blog.csdnimg.cn/direct/baf0a89eaa514aaab3b0355713a8ccf1.png

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

https://i-blog.csdnimg.cn/direct/744357fcae334bb99984b2f11ffcf813.png

使用提供的 Python 脚本

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

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

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

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

https://i-blog.csdnimg.cn/direct/9f906bacbfd64c3f8046090d737a73ac.png

如果出现以下错误提示

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文件头,

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

https://i-blog.csdnimg.cn/direct/fbe9dd1f1a1a4feab061ef7a14c5d7ca.png

如果出现以下错误提示:

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