一认识PCM
Posted future_sky_word
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一认识PCM相关的知识,希望对你有一定的参考价值。
文章目录
1、概述
1.1 什么是pcm
pcm:脉冲编码调制,是将模拟信号转化为数字信号的一种方法。
声音的转化的过程为:
- 先对连续的模拟信号按照固定频率周期性采样
- 将采样到的数据按照一定的精度进行量化,量化后的信号和采样后的信号差值叫做量化误差
- 将量化后的数据进行最后的编码存储,最终模拟信号变化为数字信号
1.2 pcm属性、量化指标
- 采样率: 单位时间内采样的次数,以Hz为单位,采样频率越高,对信号的还原度越高越高 采样位数: 一个采样信号的位数,也是对采样精度的变现
- 位深度:表示用多少个二进制位来描述采样数据,一般为16bit
- 字节序:表示PCM数据存储的字节序是大端存储(big-endian)还是小端存储(little-endian),通常为小端存储
- 声道数:表示PCM文件中包含的声道数,是单声道还是双声道,此外还有5.1声道等 比特率:没有压缩的音频数据的比特率 = 采样频率 * 采样精度 * 通道数
- 码率:压缩后的音频数据的比特率,等于 音频文件大小 / 时长。码率越大,压缩效率越低,音质越好,压缩后数据越大。常见的码率如下:
码率 | 音质 |
---|---|
96kbps | FM质量 |
128-160kbps | 一般质量音频 |
192kbps | CD质量 |
256-320Kbps | 高质量音频 |
2、PCM数据格式
1. 单声道音频,采样数据按照时间的先后顺序依次存储
2. 双声道音频,按照LRLRLR方式存储,具体每个采样点的存储方式与机器大小端有关
大端存储
小端存储
PCM编码格式
目录
一、PCM音频编码格式
1. 相关英文单词
Amplitude:幅度 PAM:Pulse Amplitude Modulation 脉冲幅度调制(数字信号过程采样) Stereo:立体声(双声道),Mono:单声道 PCM:Pulse Code Modulation Sampling:采样 Quantization:量化 Code:编码 Nyquist Sampling theorem:奈奎斯特采样定理 Analog Signal:模拟信号 Digital Signal:数字信号 Sample rate:采样率(每秒钟的样本点数)sampleing bit depth:采样位深度 Channel:通道数/声道数 |
(1)PCM是最原始的音频编码格式,并不是文件格式,WAV/MP3等才是文件格式。
(2)采样率:每秒钟的采样次数/每秒钟的样本点数,即一秒钟能采集到多少个离散的数值点,比如:16KHz=16000Hz,意思就是16000个采样点/s,采样率是保证音质的一个重要参数。
(3)采样位深度是每一个采样点的值的bit数,很容易理解,bit越多,能表示的数范围就越广,更加能表示模拟信号,可以直观理解,如果采样率和位深都很大,采样的离散序列就可以近似等于模拟信号。
8bit:256个值,从0~255 16bit:65536个值,从0~65535 24bit:……………………………… 32bit:…………………………………. |
2. PCM数据结构
(1)关于音频数据流
编码之后的PCM数据流是一串0和1的组合:0011000010101100……
所以,就要根据一些相关信息来解析数据。假如采样位深是8bit,且是有符号的数据。那么数据解析如下:
0011 0000 1010 1100…… |
第一个字节,除掉最高位符号位后,值就是+48
第二个字节,除掉最高位符号位后,值就是-44
在范围-128~127之内。
一般来说,常用的是16bit的位深,在C语言中用short类型(两字节)的数据来表示即可。
2.1 单声道数据
实例:读取一个32KHz,单声道,16bit的PCM音频文件数据到内存,并再次写入新文件。
int main(int argc, char** argv)
int i, j, count;
FILE *fpin = NULL;
FILE *fpout = NULL;
fpin = fopen("input.pcm", "rb"); //单声道
fpout = fopen("output.pcm", "wb");
fseek(fpin, 0, SEEK_END);
long inputdata_length = ftell(fpin);
inputdata_length /= 2;
printf("input_file_len:% ld\\n", inputdata_length);
short *stream_in = (short *)malloc(inputdata_length * sizeof(short));
//读出数据流(0/1数据流--->字节数据)
rewind(fpin);
count = fread(stream_in, sizeof(short), inputdata_length, fpin);
printf("count=%d\\n", count);
//将数据写入文件
fwrite(stream_in, sizeof(short), inputdata_length, fpout);
fclose(fpin);
fclose(fpout);
free(stream_in);
printf("process finished.\\r\\n");
return 0;
注:
(1)音频数据一般都有符号,可以直接打印出每一个采样点的音频数据:printf(“stream_in[%d]=%hd\\n”, 100, stream_in[100]);
(2) 16bit的深度,所以缓存空间的数据类型定义成short,刚好合适
(3) 代码运行结束,生成的文件大小没变,可使用cooledit pro打开对比查看波形图
2.2 多声道数据
(1)数据排布
对于多声道的数据流,声道数据会交叉排列。当然,这跟底层驱动程序相关,比如一些ADC采集模块,stereo数据排布就是这样的。
第1个byte(左声道)/第2个byte(右)/第3个byte(左)/第4个byte(左)…… |
(2)关于字节序
数据是大端存储结构还是小端存储结构,一般使用的是小端存储结构(Little Endian)。如下图:
实例:分离双声道PCM文件数据(LE/44100Hz/16bi/立体声)
int main(int argc, char** argv)
int i, j, count;
FILE *fpin = NULL;
FILE *fp_l = NULL;
FILE *fp_r = NULL;
fpin = fopen("44100_2ch_stereo.pcm", "rb");
fp_l = fopen("left.pcm", "wb");
fp_r = fopen("right.pcm", "wb");
fseek(fpin, 0, SEEK_END);
int inputdata_length = ftell(fpin);
inputdata_length = inputdata_length/2; //16bit
printf("input_file_len:% d\\n", inputdata_length);
short *stream_in = (short *)malloc(inputdata_length * sizeof(short));
short *stream_l = (short *)malloc((inputdata_length/2) * sizeof(short));
short *stream_r = (short *)malloc((inputdata_length/2) * sizeof(short));
//读出数据流(数据流--->字节数据)
rewind(fpin);
count = fread(stream_in, sizeof(short), inputdata_length, fpin);
printf("count=%d\\n", count);
//分离左右声道数据
int loop=inputdata_length/2;
short *dat_ptr=stream_in;
for(int i=0; i<loop; i=i+2)
stream_l[i]=*dat_ptr;
stream_l[i+1]=*(dat_ptr+1);
dat_ptr=dat_ptr+2;
stream_r[i]=*dat_ptr;
stream_r[i+1]=*(dat_ptr+1);
dat_ptr=dat_ptr+2;
//将左右声道数据写入文件
fwrite(stream_l, sizeof(short), inputdata_length/2, fp_l);
fwrite(stream_r, sizeof(short), inputdata_length/2, fp_r);
fclose(fpin);
fclose(fp_l);
fclose(fp_r);
free(stream_in);
free(stream_l);
free(stream_r);
printf("process finished.\\r\\n");
return 0;
文件大小和数据量减小一半,左右声道均选择44100Hz的采样率进行播放,声音正常。
2.3 小结
(1)s16le
s-----signed,就是有符号数据,根据采样位深度,可以知道数值范围,所以在音频处理的时候,需要注意数据是否溢出
16----采样位深16bit
le----小端存储
(2)PC的声卡一般是16bit或者24bit
(3)PCM不是音频文件格式,因此一般的音乐播放软件无法打开,需要保存为wav或者其他格式(加个文件头)才行。
二、WAV音频文件格式
关于wav音频文件格式不放在这里解析,算法处理过程中(实际上算法一般都是针对PCM原始编码格式进行),需要注意下面几点即可:
- 构成:文件头+数据部分。
- 文件头占44个字节,后面就是数据部分,读取音频数据时,要注意偏移。
- 音频部分的编码可以是PCM,也可以是其他格式。
实例:读取wav音频文件数据,伪代码如下
/* 01. 读取音频数据 */ fp_in = fopen("clean.wav", "rb"); fseek(fp_in, 0, SEEK_END); long clean_len = ftell(fp_in); clean_len = clean_len -44; //减掉文件头 clean_len /= 2; printf("clean voice len:% ld\\n", clean_len); short *clean_buf = (short *)malloc(clean_len * sizeof(short)); //申请音频数据存储空间 rewind(fp_in); //先偏移,跳过文件头再读数据 fseek(fp_in, 44, SEEK_SET); count = fread(clean_buf, sizeof(short), clean_len, fp_in); /* 02. 分帧,加窗,傅里叶变换到频域,计算 */ /* 03. 还原到时域 */ /* 04. 写入新文件(注意文件头部信息的写入) */ |
以上是关于一认识PCM的主要内容,如果未能解决你的问题,请参考以下文章
音视频开发8. 使用ffmpeg 将pcm转码aac实践(C++)
音视频开发9. 使用ffmpeg 将pcm转码mp3实践(C++)
音视频开发9. 使用ffmpeg 将pcm转码mp3实践(C++)
Android 音视频开发:使用AudioRecord采集音频PCM并保存