PCM
PCM(Pulse Code Modulation,脉冲编码调制)是一种将模拟信号转换为数字信号的编码技术,也是现代数字通信和音频存储的核心基础。它的核心原理是通过采样、量化和编码三个步骤,将连续的模拟信号(如声音、图像)转变为离散的数字二进制码流,从而实现高效传输、存储和处理。
PCM的基本工作原理
-
采样(Sampling)
以固定时间间隔(采样率,如44.1kHz)测量模拟信号的瞬时振幅值。根据奈奎斯特采样定理,采样率至少需达到信号最高频率的2倍,才能完整还原原始信号(例如人耳可听范围20Hz-20kHz,故CD音质采用44.1kHz采样率)。 -
量化(Quantization)
将采样得到的连续振幅值近似为有限个离散电平(量化精度,如16bit、24bit)。量化位数越高,动态范围越广(16bit可表示65,536个电平),信号保真度越好,但数据量也越大。 -
编码(Encoding)
将量化后的数值转换为二进制码(如16bit线性PCM),形成数字信号序列,便于存储或传输。
PCM的关键参数
- 采样率(Sample Rate):每秒采样次数(如8kHz电话语音、48kHz高清音频)。
- 位深度(Bit Depth):单个采样的量化精度(如16bit CD音质、24bit专业录音)。
- 声道数(Channels):单声道(Mono)、立体声(Stereo)或多声道(如5.1环绕声)。
PCM的应用场景
-
数字音频
- CD音质(44.1kHz/16bit立体声PCM)
- WAV、AIFF等无损音频格式直接存储PCM数据。
- 蓝牙音频传输(如SBC编解码器先解码为PCM再播放)。
-
通信系统
- 传统电话语音(G.711标准采用8kHz/8bit PCM)。
- VoIP、视频会议中的音频底层编码。
-
专业影音制作
- 影视录音、音乐制作中常用高精度PCM(如96kHz/24bit)保证后期处理空间。
-
存储与传输
- 多数有损压缩格式(如MP3、AAC)需先将PCM数据通过心理声学模型压缩。
- HDMI、USB音频接口直接传输PCM数字信号。
PCM的优缺点
- 优点:
- 保真度高,无损编码时可完全还原原始信号。
- 处理简单,兼容性强,几乎所有数字系统均支持。
- 缺点:
- 数据量大(1分钟CD音质PCM约10MB),需依赖压缩技术降低存储/传输负担。
- 直接传输效率低,通常需结合有损或无损编码(如FLAC)。
PCM与其他编码技术的对比
- 与DSD(Direct Stream Digital):
PCM采用多比特量化,DSD使用1bit超高采样率(如2.8MHz),后者更接近模拟波形,但编辑灵活性较差(常用于SACD)。 - 与压缩编码(MP3/AAC):
PCM保留完整信息,压缩编码通过舍弃人耳不敏感的频段减少数据量。
总结
PCM是数字信号处理的基石,其标准化和通用性使其成为音频、通信等领域的核心编码方式。尽管数据量大,但通过高采样率和高位深配置(如192kHz/24bit),它能满足从消费级到专业级的高保真需求。现代技术(如MQA编码)仍在PCM基础上优化效率,确保其在未来持续发挥关键作用。
代码
// 测试的PCM数据采用采样率44.1k, 采用精度S16SYS, 通道数2
#include <stdio.h>
#include <SDL2/SDL.h>
#define PCM_BUFFER_SIZE (1024*2*2*2)
// 已知通道数为2 采样深度为16bit(2字节) 每次读取两帧数据 每次采样1024个采样点
static Uint8* s_audio_pos = NULL; // 起始位置
static Uint8* s_audio_end = NULL; // 结束位置
static Uint8* s_audio_buf = NULL; // 数据
void fill_pcm_callback (void *userdata, Uint8 * stream, int len) {
SDL_memset(stream, 0, len);
if (s_audio_pos >= s_audio_end) {
return;
}
int remain_buffer_len = s_audio_end - s_audio_buf; // 计算读取数据的大小
len = len < remain_buffer_len ? len : remain_buffer_len;
SDL_MixAudio(stream, s_audio_pos, len, SDL_MIX_MAXVOLUME/6); // 将内存数据拷贝到音频数据流
printf("len=%d\n", len);
s_audio_pos += len; // 偏移
}
#undef main
int main(int argc, char* argv[]) {
int ret = -1;
FILE* audio_fp = NULL;
SDL_AudioSpec spec;
const char* audio_path = "../44100_16bit_2ch.pcm";
size_t read_buf_len = 0;
if(SDL_Init(SDL_INIT_AUDIO)) {
fprintf(stderr, "Could not init sdl\n");
return ret;
}
audio_fp = fopen(audio_path, "rb");
if (!audio_fp) {
fprintf(stderr, "Could not open audio file\n");
return -1;
}
s_audio_buf = (Uint8*)malloc(PCM_BUFFER_SIZE);
spec.channels = 2; // 双通道
spec.callback = fill_pcm_callback; // 回调函数
spec.format = AUDIO_S16SYS; // 精度
spec.userdata = NULL;
spec.silence = 0;
spec.samples = 1024; // 采样点
spec.freq = 44100; // 频率
if(SDL_OpenAudio(&spec, NULL)) {
fprintf(stderr, "Failed to open audio\n");
goto _FAIL;
}
SDL_PauseAudio(0);
int data_count = 0;
for(;;) {
read_buf_len = fread(s_audio_buf, 1, PCM_BUFFER_SIZE, audio_fp);
if (read_buf_len == 0) {
break;
}
data_count += read_buf_len;
s_audio_pos = s_audio_buf;
s_audio_end = s_audio_buf + read_buf_len;
while(s_audio_pos < s_audio_end) {
SDL_Delay(10);
}
}
SDL_CloseAudio();
_FAIL:
if (audio_fp) {
fclose(audio_fp);
}
if (s_audio_buf) {
free(s_audio_buf);
}
SDL_Quit();
return 0;
}
代码解释写在注释里啦!
评论