1. 引言
音视频开发是移动应用、嵌入式系统和多媒体平台的核心技术方向。无论是语音通话、音乐播放、语音识别还是智能音箱,都离不开音频数据的采集、处理与播放。而这一切的起点,就是音频原始数据 PCM(脉冲编码调制)。本文将从 PCM 原理出发,系统讲解音频数据的生成过程、核心参数与存储格式,并结合 AI 模块(语音识别、AI 降噪等)的实战应用,帮助读者建立从底层数据到上层智能处理的完整认知。
2. PCM 音频原始数据
2.1 PCM 数据的生成过程
PCM 全称是脉冲编码调制(Pulse Code Modulation),是未经过压缩的音频数据。它由模拟信号经过 采样、量化、编码 三大步骤转换而来:
#mermaid-svg-r2RRNApwZbZuF4Cc{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-r2RRNApwZbZuF4Cc .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-r2RRNApwZbZuF4Cc .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-r2RRNApwZbZuF4Cc .error-icon{fill:#552222;}#mermaid-svg-r2RRNApwZbZuF4Cc .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-r2RRNApwZbZuF4Cc .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-r2RRNApwZbZuF4Cc .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-r2RRNApwZbZuF4Cc .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-r2RRNApwZbZuF4Cc .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-r2RRNApwZbZuF4Cc .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-r2RRNApwZbZuF4Cc .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-r2RRNApwZbZuF4Cc .marker{fill:#333333;stroke:#333333;}#mermaid-svg-r2RRNApwZbZuF4Cc .marker.cross{stroke:#333333;}#mermaid-svg-r2RRNApwZbZuF4Cc svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-r2RRNApwZbZuF4Cc p{margin:0;}#mermaid-svg-r2RRNApwZbZuF4Cc .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-r2RRNApwZbZuF4Cc .cluster-label text{fill:#333;}#mermaid-svg-r2RRNApwZbZuF4Cc .cluster-label span{color:#333;}#mermaid-svg-r2RRNApwZbZuF4Cc .cluster-label span p{background-color:transparent;}#mermaid-svg-r2RRNApwZbZuF4Cc .label text,#mermaid-svg-r2RRNApwZbZuF4Cc span{fill:#333;color:#333;}#mermaid-svg-r2RRNApwZbZuF4Cc .node rect,#mermaid-svg-r2RRNApwZbZuF4Cc .node circle,#mermaid-svg-r2RRNApwZbZuF4Cc .node ellipse,#mermaid-svg-r2RRNApwZbZuF4Cc .node polygon,#mermaid-svg-r2RRNApwZbZuF4Cc .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-r2RRNApwZbZuF4Cc .rough-node .label text,#mermaid-svg-r2RRNApwZbZuF4Cc .node .label text,#mermaid-svg-r2RRNApwZbZuF4Cc .image-shape .label,#mermaid-svg-r2RRNApwZbZuF4Cc .icon-shape .label{text-anchor:middle;}#mermaid-svg-r2RRNApwZbZuF4Cc .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-r2RRNApwZbZuF4Cc .rough-node .label,#mermaid-svg-r2RRNApwZbZuF4Cc .node .label,#mermaid-svg-r2RRNApwZbZuF4Cc .image-shape .label,#mermaid-svg-r2RRNApwZbZuF4Cc .icon-shape .label{text-align:center;}#mermaid-svg-r2RRNApwZbZuF4Cc .node.clickable{cursor:pointer;}#mermaid-svg-r2RRNApwZbZuF4Cc .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-r2RRNApwZbZuF4Cc .arrowheadPath{fill:#333333;}#mermaid-svg-r2RRNApwZbZuF4Cc .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-r2RRNApwZbZuF4Cc .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-r2RRNApwZbZuF4Cc .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-r2RRNApwZbZuF4Cc .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-r2RRNApwZbZuF4Cc .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-r2RRNApwZbZuF4Cc .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-r2RRNApwZbZuF4Cc .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-r2RRNApwZbZuF4Cc .cluster text{fill:#333;}#mermaid-svg-r2RRNApwZbZuF4Cc .cluster span{color:#333;}#mermaid-svg-r2RRNApwZbZuF4Cc div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-r2RRNApwZbZuF4Cc .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-r2RRNApwZbZuF4Cc rect.text{fill:none;stroke-width:0;}#mermaid-svg-r2RRNApwZbZuF4Cc .icon-shape,#mermaid-svg-r2RRNApwZbZuF4Cc .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-r2RRNApwZbZuF4Cc .icon-shape p,#mermaid-svg-r2RRNApwZbZuF4Cc .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-r2RRNApwZbZuF4Cc .icon-shape .label rect,#mermaid-svg-r2RRNApwZbZuF4Cc .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-r2RRNApwZbZuF4Cc .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-r2RRNApwZbZuF4Cc .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-r2RRNApwZbZuF4Cc :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 模拟音频信号
采样
量化
编码
PCM 数字信号
采样:把一段连续的模拟信号转换成离散的数字信号。采样率指每秒钟采样的个数。根据奈奎斯特采样定理:当采样率大于等于连续信号最高频率的 2 倍时,采样信号就能无差别还原出原始信号。人类听觉频率范围是 20Hz~20kHz,因此采样率需达到 40kHz 以上才能保证数据完整性。
量化:在坐标轴上把每一个离散采样点进行数字化操作,即把每个采样点用竖线表示出来,方便后续数字化存储。
编码:把每一个量化后的采样点以二进制形式存储起来。把所有采样点的编码数据连续拼接,就是一段完整的 PCM 数据,例如:
text
011011110111101......
因此,音频原始数字信号的生成链路可概括为:
text
采样 -> 量化 -> 编码 = 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 比特率计算公式为:
text
比特率 = 采样率 × 采样深度 × 通道数
例如采样率 48000、采样深度 16bit、通道数 2,则比特率为:
text
48000 × 16 × 2 = 1536000 bit/s
2.2.5 PCM 文件大小计算
假设一个 PCM 音频设备采样率 48000、采样精度 16bit、2 通道,采集 10 分钟数据,其大小为:
text
48000 × 16 × 2 × 10 × 60 = 921600000 bit
将 bit 转换为字节:
text
921600000 / 8 / 1024 / 1024 ≈ 109 MB
可见 PCM 数据体积较大,这也是实际工程中常需压缩编码(如 AAC、Opus)的原因。
2.3 PCM 存储格式
PCM 单双声道的存储布局不同,这里重点讲解双声道。双声道有两种存储方式:
- 交错模式(Interleaved):先记录第一帧的左声道样本和右声道样本,再记录第二帧,依次交替。
- 非交错模式(Non-Interleaved):先记录一个周期内所有帧的左声道样本,再记录所有右声道样本。
text
交错模式:L R L R L R L R
非交错模式:L L L L L R R R R R
3. RV1126 AI 音频模块实战
cpp
#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;
}