一认识PCM

Posted future_sky_word

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一认识PCM相关的知识,希望对你有一定的参考价值。

文章目录

1、概述

1.1 什么是pcm

pcm:脉冲编码调制,是将模拟信号转化为数字信号的一种方法。
声音的转化的过程为:

  1. 先对连续的模拟信号按照固定频率周期性采样
  2. 将采样到的数据按照一定的精度进行量化,量化后的信号和采样后的信号差值叫做量化误差
  3. 将量化后的数据进行最后的编码存储,最终模拟信号变化为数字信号

1.2 pcm属性、量化指标

  1. 采样率: 单位时间内采样的次数,以Hz为单位,采样频率越高,对信号的还原度越高越高 采样位数: 一个采样信号的位数,也是对采样精度的变现
  2. 位深度:表示用多少个二进制位来描述采样数据,一般为16bit
  3. 字节序:表示PCM数据存储的字节序是大端存储(big-endian)还是小端存储(little-endian),通常为小端存储
  4. 声道数:表示PCM文件中包含的声道数,是单声道还是双声道,此外还有5.1声道等 比特率:没有压缩的音频数据的比特率 = 采样频率 * 采样精度 * 通道数
  5. 码率:压缩后的音频数据的比特率,等于 音频文件大小 / 时长。码率越大,压缩效率越低,音质越好,压缩后数据越大。常见的码率如下:
码率音质
96kbpsFM质量
128-160kbps一般质量音频
192kbpsCD质量
256-320Kbps高质量音频

2、PCM数据格式

1. 单声道音频,采样数据按照时间的先后顺序依次存储
2. 双声道音频,按照LRLRLR方式存储,具体每个采样点的存储方式与机器大小端有关

大端存储

小端存储

PCM编码格式

目录

一、PCM音频编码格式

1. 相关英文单词

2. PCM数据结构

2.1 单声道数据

2.2 多声道数据

2.3 小结

二、WAV音频文件格式


一、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原始编码格式进行),需要注意下面几点即可:

  1. 构成:文件头+数据部分。
  2. 文件头占44个字节,后面就是数据部分,读取音频数据时,要注意偏移。
  3. 音频部分的编码可以是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并保存

Android音视频系列(七):PCM音频单声道与双声道的相互转换

PCM格式的视频文件怎么播放,或者转换格式