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数据存储格式

PCM数据存储格式

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

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

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


PCM文件的16进制数据

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


左右声道对比

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

PCM Examples

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

#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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。