PCM数据格式

音频参数

经常见到这样的描述: 44100HZ 16bit stereo 或者 22050HZ 8bit mono 等等.

  • 44100HZ 16bit stereo:
    每秒钟有 44100 次采样, 采样数据用 16 位(2字节)记录, 双声道(立体声);
  • 22050HZ 8bit mono:
    每秒钟有 22050 次采样, 采样数据用 8 位(1字节)记录, 单声道;

什么是PCM音频数据

PCM(Pulse Code Modulation)也被称为脉冲编码调制。
PCM音频数据是未经压缩的音频采样数据裸流,它是由模拟信号经过采样、量化、编码转换成的标准的数字音频数据。

PCM数据存储格式

IMAGE

PCM数据存储格式

本文中声音样值的采样频率一律是44100Hz,采样格式一律为16LE。
“16”代表采样位数是16bit。由于1Byte=8bit,所以一个声道的一个采样值占用2Byte。
“LE”代表Little Endian,代表2 Byte采样值的存储方式为高位存在高地址中。

立体声的左右声道的数据是一样的吗?

原来一直理解立体声的左右声道是一样的,但是通过查看实际的数据,发现并不一样。比如:

IMAGE

PCM文件的16进制数据

从以上可以看出,刚开始的4个字节是同一个采样点,其中0x04e8是左声道,0x01c9是右声道,发现是不一样的。
通过cool edit pro查看波形:
IMAGE

左右声道对比

可以看出左右声道,虽然波形相似,但是的确存在不同。

PCM Examples

以下代码可读取pcm文件,并演示了打印采样点的值和将立体声分离成左声道和右声道。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include "stdafx.h"
#include <fstream>
#include <iostream>
using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
// Read file to buffer
char szFileName[256] = {0};
sprintf_s(szFileName, ".\\44100_stereo_s16le.pcm");

ifstream inFile(szFileName, ios::in | ios::binary | ios::ate);
long nFileSizeInBytes = inFile.tellg();

char *pszPCMBuffer = new char[nFileSizeInBytes];

inFile.seekg(0, ios::beg);
inFile.read(pszPCMBuffer, nFileSizeInBytes);
inFile.close();

cout<<"FileName: " << szFileName <<", nFileSizeInBytes: " << nFileSizeInBytes <<endl;
cout << "Read file to a buffer Done!!!\n\n";

// 从文件中读取采样点并打印
short szItem[1024] = {0};
memcpy(szItem, pszPCMBuffer, 256);

for (int i = 0; i < 128; i++)
{
printf("%6d ", szItem[i]);

if ((i + 1) % 8 == 0)
{
cout<<endl;
}
}

// 将双声道分离成左声道和右声道
FILE *fpLeft = fopen("44100_mono_s16le_l.pcm","wb+");
FILE *fpRight = fopen("44100_mono_s16le_r.pcm","wb+");

printf("\n Split stereo to left and right ...\n");

for (int nPos = 0; nPos < nFileSizeInBytes; )
{
if ((nPos / 2) % 2 == 0) // 偶数
{
if (fpLeft)
{
fwrite(pszPCMBuffer + nPos, 1, 2, fpLeft);
}
}
else
{
if (fpRight)
{
fwrite(pszPCMBuffer + nPos, 1, 2, fpRight);
}
}

nPos += 2;
}

if (fpLeft)
{
fclose(fpLeft);
fpLeft = NULL;
}

if (fpRight)
{
fclose(fpRight);
fpRight = NULL;
}

printf("\n Split stereo to left and right Done. \n");

if (pszPCMBuffer)
{
delete [] pszPCMBuffer;
pszPCMBuffer = NULL;
}

getchar();

return 0;
}

References:

http://blog.csdn.net/ownwell/article/details/8114121
http://blog.csdn.net/leixiaohua1020/article/details/50534316
https://www.cnblogs.com/CoderTian/p/6657844.html

作者:FlyingPenguin
链接:https://www.jianshu.com/p/e568f94cdf6a
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。