本文为记录自己学习雷霄骅的pcm入门的demo后,从中学到的pcm知识。
先介绍必要的入门知识:
pcm 16LE
1. pcm 16------代表2 Byte采样值,
2. 存储方式(LE:LITTLE-ENDIAN)--------即低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。 与之对应的 是:BIG-ENDIAN(大字节序、高字节序) 详细讲解
从左到右:高位字节 -----------------> 低位字节
从左到右:低地址 -----------------> 高地址
eg: int a = 0x12345678
在BIG-ENDIAN的情况下存放为:数据 0x12 | 0x34 | 0x56 | 0x78(正常)
在LITTLE-ENDIAN的情况下存放为:数据 0x78 | 0x56 | 0x34 | 0x12(倒序)
3. 采样数据的取值范围是-32768到32767(即 2的16次幂,2Byte)
pcm 8
1. 采样值1Byte;
2.采样数据的取值范围是0到255(即 2的8次幂,1Byte)
数据处理:
对pcm数据的处理从业务层考虑有下面几种情况:
a. 如果是多声道,可能需要分离声道
b. 改变音量的大小
c. 改变播放速度的快慢
d. pcm格式的转换
在处理pcm数据之前,我们需要明白pcm 的格式,才能知道其采样大小和存储结构。如下将以pcm16le格式为例子,data[12]为pcm数据存储数据 。单位为 1Byte的二进制数据 。叙述如何实现上面的情况:
情况a:
由于pcm16le 是双声道,存储的数据结构为 一个2Byte左声道数据然后一个2Byte右声道数据这样顺序存储。所以分离就简单了。 eg,data[12] ------取data[0]data[1](左声道)、 data[2]data[3](右声道)、data[4]data[5] (左声道)、data[6]data[7](右声道)、data[8]data[9](左声道)、data[10]data[11](右声道)
情况b:
将每次的采样数据变大或变小,就是改变其音量。eg,将 data[0]data[1]和data[4]data[5]和data[8]data[9](左声道) 二进制数据转换为short类型数据 然后除以2,表示左声道音量减半。
情况c:
间隔读取数据,就可以改变播放速度。 eg,读取 data[0]data[1](左声道)、 data[2]data[3](右声道)但不读取data[4]data[5] (左声道)、data[6]data[7] 然后再读取data[8]data[9](左声道)、data[10]data[11](右声道)播放速度就变快了
情况 d:
假如我们需要将pcm16le转为pcm8 格式。我们需要将音频采集数值范围从2Byte 降为1Byte。
需要经过两个步骤:
第一步是将-32768到32767的16bit(2Byte)有符号数值转换为-128到127的8bit(1Byte)有符号数值,可以通过位运算 (>>8) eg, -32768>>8 =-128
第二步是将-128到127的8bit有符号数值转换为0到255的8bit无符号数值。(所有数据+128)
方法二: 我们知道pcm16le的存储格式是LITTLE-ENDIAN,我们可以直接取高位的1Byte,这样也可以和第一步的效果一 样。
int simplest_pcm16le_to_pcm8_method2(char *url){
FILE *fp = fopen(url, "rb+");
FILE *fp1 = fopen("output_82.pcm", "wb+");
int cnt = 0;
unsigned char *sample = (unsigned char *)malloc(2);
short samplenum8;
unsigned char samplenum8_u;
while (!feof(fp)){
//sample-----缓存区
//2 ----代表一次读取2个字节
//1 ----读取长度为1
//fp -----数据源
fread(sample, 2, 1, fp);
//取值范围 -128-----127
samplenum8 = sample[1];
//PCM8格式的采样数据的取值范围是0到255
samplenum8_u = samplenum8+128;
//写入声道
fwrite(&samplenum8_u, 1, 1, fp1);
cnt++;
}
printf("Sample Cnt:%d\n", cnt);
free(sample);
fclose(fp);
fclose(fp1);
system("pause");
return 0;
}