WAVE绘制频谱图(二)——WAVE文件解析提取PCM数据

{

https://blog.csdn.net/qq_36568418/article/details/91530563?spm=1001.2014.3001.5502

}

{

为了不太多依赖于平台环境,音频数据提取部分尽量都用c++实现。

话不多说,直接还是上代码,代码比较详细不再赘述。
WAVE格式头结构体:

    //RIFF头的宏定义缩写,便于后面的比较 低字节-》高字节
    #define ID_RIFF mmioFOURCC('R', 'I', 'F', 'F')
    #define ID_WAVE mmioFOURCC('W', 'A', 'V', 'E')
    #define ID_data mmioFOURCC('d', 'a', 't', 'a')
    #define ID_fmt  mmioFOURCC('f', 'm', 't', '\x20')
    #define ID_fact mmioFOURCC('f', 'a', 'c', 't')
     
    //WAVE文件一般有四种块,它们依次是:RIFF块、格式块、附加块(可选),数据块
    struct RIFFFORMAT{//长度12
        unsigned long Ckid;    //RIFF标识
        unsigned long FileSize; //文件大小
        unsigned long FccType; //WAVE标志
    };
     
    //WAV音频头
    struct WAVE_FORMAT
    {
        unsigned long Ckid; //fmt
        unsigned long CkSize; //块大小
        unsigned short wFormatTag;//音频格式一般为WAVE_FORMAT_PCM
        unsigned short nChannels;//采样声道数
        unsigned long nSamplesPerSec;//采样率
        unsigned long nAvgBytesPerSec;//每秒字节数  采样率*采样精度/8(字节位数)
        unsigned short nBlockAlign;//块大小 采样字节*声道数
        unsigned short wBitsPerSample;//采样精度 采样精度/8 = 采样字节
        unsigned short cbSize;        //预留字节 一般为0扩展域有的没有
     
    };
    //wave数据块
    struct WAVE_DATA{
        unsigned long Wdid; //data 标志
        unsigned long WdSize; //块大小
        unsigned char* Wdbuf; //数据指针 有符号
    };

读取PCM文件:

     
        char* m_path = "../test.wav";
        //音频头文件信息
        RIFFFORMAT head;//RIFF头
        WAVE_FORMAT fmt;//格式块
        WAVE_DATA data;//数据块
        
            fstream fs;    
        fs.open(m_path, std::ios::binary | std::ios::in);
        fs.read((char*)&head, sizeof(RIFFFORMAT));
        
        if(head.Ckid != ID_RIFF||head.FccType!=ID_WAVE) //不是wave格式
        {
            fs.close();
            return;
        }
     
        fs.read((char*)&fmt, 24);    //读取格式块
        if(fmt.Ckid != ID_fmt||fmt.wFormatTag != WAVE_FORMAT_PCM)//只处理pcm 数据
        {
            fs.close();
            return;
        }
        if(fmt.CkSize == 18) //说明含有保留字节
        {
            fs.read((char*)&(fmt.cbSize), 2);
        }else
        {
            fmt.cbSize = 0;     //不含有则置位0
        }
     
        //以下标志位有的wave包含fact 有的不包含 需要区分
        unsigned long Sign;      
            fs.read((char*)&Sign, 4);
     
        if(Sign == ID_fact)     //如果有fact块则跳过
        {
            fs.seekp(4,ios::cur);//从当前位置跳过4个字节
            fs.read((char*)&Sign, 4);//再读取
        }
     
        if(Sign == ID_data)//读取到data数据块
        {
            data.Wdid = Sign;
            fs.read((char*)&(data.WdSize), 4);//数据块大小
            data.Wdbuf = new unsigned char[data.WdSize];
            fs.read((char*)data.Wdbuf, data.WdSize);//数据读取
            fs.close();      //读取完成关闭文件
        }

上述代码部分设计RIFF头比较,才用了Windows中的 mmioFOURCC()包含于  <MMSystem.h>文件中,当然也可以自己实现

实现过程如下:

    #define MAKEFOURCC(ch0, ch1, ch2, ch3)                              \
                    ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) |   \
                    ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24 ))

一个简单的位操作,过程不复杂。

如上操作就已经把PCM数据取出来存放在data.wdbuf中了。

}

posted @ 2022-12-21 16:24  YZFHKMS-X  阅读(345)  评论(0编辑  收藏  举报