1. 引言

音视频开发是移动应用、嵌入式系统和多媒体平台的核心技术方向。无论是语音通话、音乐播放、语音识别还是智能音箱,都离不开音频数据的采集、处理与播放。而这一切的起点,就是音频原始数据 PCM(脉冲编码调制)。本文将从 PCM 原理出发,系统讲解音频数据的生成过程、核心参数与存储格式,并结合 AI 模块(语音识别、AI 降噪等)的实战应用,帮助读者建立从底层数据到上层智能处理的完整认知。

2. PCM 音频原始数据

2.1 PCM 数据的生成过程

PCM 全称是脉冲编码调制(Pulse Code Modulation),是未经过压缩的音频数据。它由模拟信号经过 采样、量化、编码 三大步骤转换而来:

模拟音频信号

采样

量化

编码

PCM 数字信号

采样:把一段连续的模拟信号转换成离散的数字信号。采样率指每秒钟采样的个数。根据奈奎斯特采样定理:当采样率大于等于连续信号最高频率的 2 倍时,采样信号就能无差别还原出原始信号。人类听觉频率范围是 20Hz~20kHz,因此采样率需达到 40kHz 以上才能保证数据完整性。

量化:在坐标轴上把每一个离散采样点进行数字化操作,即把每个采样点用竖线表示出来,方便后续数字化存储。

编码:把每一个量化后的采样点以二进制形式存储起来。把所有采样点的编码数据连续拼接,就是一段完整的 PCM 数据,例如:

011011110111101……

因此,音频原始数字信号的生成链路可概括为:

采样 -> 量化 -> 编码 = PCM 数据

2.2 PCM 核心参数

2.2.1 采样率

采样率指每秒钟采样的个数,即 1 秒内采集声音的频率。例如 48000Hz 表示一秒钟采集 48000 个采样点。采样率越高,音频质量越好。PCM 常用采样率如下:

  • 192000Hz(192kHz):蓝光、高清电影 DVD。
  • 96000Hz(96kHz):蓝光、高清电影 DVD。
  • 48000Hz(48kHz):数字电视、DVD,最常用。
  • 44100Hz(44.1kHz):CD 音质。
  • 22000Hz(22kHz):无线广播。
2.2.2 采样深度(位深)

采样深度表示每次采样的大小。例如采样深度为 16bit,则声音有 2 的 16 次方个振幅等级;32bit 则有 2 的 32 次方个振幅等级。振幅等级越多,声音质量越高。PCM 常见采样深度有 8bit、16bit、32bit 三种。

2.2.3 通道数

PCM 常见通道数有四种:

  • 单声道:只有一个声音通道,如电话、喇叭。
  • 双声道(立体声):两个独立声道,录制时声音分配到左右两个通道,听感更立体。
  • 四声道:前左、前右、后左、后右四个发声通道,听感如被声音包围。
  • 5.1 声道:广泛用于家庭影院,开发中较少使用。
2.2.4 PCM 比特率

比特率指每秒传输的比特数(bit),PCM 比特率计算公式为:

比特率 = 采样率 × 采样深度 × 通道数

例如采样率 48000、采样深度 16bit、通道数 2,则比特率为:

48000 × 16 × 2 = 1536000 bit/s
2.2.5 PCM 文件大小计算

假设一个 PCM 音频设备采样率 48000、采样精度 16bit、2 通道,采集 10 分钟数据,其大小为:

48000 × 16 × 2 × 10 × 60 = 921600000 bit

将 bit 转换为字节:

921600000 / 8 / 1024 / 1024 ≈ 109 MB

可见 PCM 数据体积较大,这也是实际工程中常需压缩编码(如 AAC、Opus)的原因。

2.3 PCM 存储格式

PCM 单双声道的存储布局不同,这里重点讲解双声道。双声道有两种存储方式:

  • 交错模式(Interleaved):先记录第一帧的左声道样本和右声道样本,再记录第二帧,依次交替。
  • 非交错模式(Non-Interleaved):先记录一个周期内所有帧的左声道样本,再记录所有右声道样本。
交错模式:L R L R L R L R
非交错模式:L L L L L R R R R R

3. RV1126 AI 音频模块实战

#include <assert.h>
#include <fcntl.h>
#include <getopt.h>
#include <pthread.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

#include "rkmedia_api.h"
#define AUDIO_PATH "default"
#define AI_CHN 0

//获取PCM数据的线程
void * get_ai_pcm_thread(void * args)
{
    pthread_detach(pthread_self());
    FILE * pcm_file = fopen("test_ai.pcm", "w+");
    MEDIA_BUFFER mb ;

    while (1)
    {
        //获取PCM数据的线程
        mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_AI, AI_CHN, -1);
        if(!mb)
        {
            printf("RK_MPI_SYS_GetMediaBuffer Failed....\n");
            break;
        }
        
        printf("get_pcm_data success...\n");
        fwrite(RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb), 1, pcm_file);
        RK_MPI_MB_ReleaseBuffer(mb);
    }
}


int main(int argc, char *argv[])
{
    int ret;
    AI_CHN_ATTR_S ai_chn_s;
    ai_chn_s.pcAudioNode = AUDIO_PATH; //音频路径,默认default
    ai_chn_s.u32SampleRate = 48000; //音频采样率
    ai_chn_s.u32Channels = 2;   //音频通道数
    ai_chn_s.u32NbSamples = 1024; //音频采样个数,这里固定1024
    ai_chn_s.enSampleFormat = RK_SAMPLE_FMT_S16;//将音频通道的采样格式设置为 16位有符号PCM
    ai_chn_s.enAiLayout = AI_LAYOUT_NORMAL;  //NORMAL
    ret = RK_MPI_AI_SetChnAttr(AI_CHN, &ai_chn_s);  //设置AI模块
    if(ret)
    {
        printf("Set Ai_Chn_Attr Failed....\n");
    }
    else
    {
        printf("Set Ai_Chn_Attr Success....\n");
    }

    ret = RK_MPI_AI_EnableChn(AI_CHN);  //使能AI模块
    if(ret)
    {
        printf("Enable Ai_Chn_Attr Failed....\n");
    }

    ret = RK_MPI_AI_StartStream(AI_CHN); //启动音频AI模块
    if(ret)
    {
        printf("RK_MPI_AI_StartStream Failed....\n");
    }

    pthread_t pid;
    pthread_create(&pid, NULL, get_ai_pcm_thread, NULL); 

    while (1)
    {
        sleep(2);
    }

    RK_MPI_AI_DisableChn(AI_CHN);


    return 0;
}

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐