C++根据频率生成wav音频文件

最近在研究如何根据频率来产生wav音频文件。经过一番查阅资料发现了挺不错的解决方案,整理了一下写出来与大家分享。(ps:第一次写博客还不是很熟悉,诸如排版之类的问题还请大家见谅)

一.WAV文件格式

WAVE文件是非常简单的一种RIFF文件,它的格式类型为”WAVE”。RIFF块包含两个子块,这两个子块的ID分别是”fmt”和”data”,其中”fmt”子块由结构PCMWAVEFORMAT所组成,其子块的大小就是sizeofof(PCMWAVEFORMAT),数据组成就是PCMWAVEFORMAT结构中的数据。(详细介绍于http://blog.csdn.net/zhihu008/article/details/7854529中查看,这里只作简要介绍)


C++根据频率生成wav音频文件_第1张图片
wav文件结构

PCMWAVEFORMAT结构定义如下:

typedef struct
{
    WAVEFORMAT wf; // 波形格式;
    WORD wBitsPerSample;//WAVE文件的采样大小;
} PCMWAVEFORMAT;


WAVEFORMAT结构定义如下:

typedef struct
{
    WORD wFormatag;//编码格式,包括WAVE_FORMAT_PCM,WAVEFORMAT_ADPCM等
    WORD nChannls;//声道数,单声道为1,双声道为2
    DWORD nSamplesPerSec;//采样频率
    DWORD nAvgBytesperSec;//每秒的数据量
    WORD nBlockAlign;//块对齐(每个采样的字节长度)
} WAVEFORMAT;


WAV文件头结构:

typedef struct
{
    char chRIFF[4];//"RIFF"
    DWORD dwRIFFLen;//总长度-8
    char chWAVE[4];//"WAVE"
    char chFMT[4];//"fmt "
    DWORD dwFMTLen;//sizeof(PCMWAVEFORMAT )
    PCMWAVEFORMAT pwf;
    char chDATA[4];//"data"
    DWORD dwDATALen;//音频数据长度
}WaveHeader;


其中的字符数组都是固定格式。以下分别对应8、16比特单双通道的数据存储格式图:
8bit、单通道:


C++根据频率生成wav音频文件_第2张图片

8bit、双通道:

C++根据频率生成wav音频文件_第3张图片

16bit、单通道:

C++根据频率生成wav音频文件_第4张图片

16bit、双通道:

C++根据频率生成wav音频文件_第5张图片

二.实现代码

代码有个很玄学的地方,我也不懂该怎么描述。的确能创建出指定频率的wav文件,但是生成8bit和16bit波形数据有一些不懂,我找了很久也没能发现原因。其中值得注意的是,当创建并读写wav文件时,要以二进制打开。

#include  
#include 
#include 
#include 
#include

#define WAVE_HEAD_LENGTH 44//wav头文件长度
#define m_samplefreq 44100
#define m_channels 2
#define m_channelbits 8
#define MATH_PI 3.1415

using namespace std;
//.wav文件的文件头结构 
typedef struct
{
    char chRIFF[4];
    DWORD dwRIFFLen;
    char chWAVE[4];
    char chFMT[4];
    DWORD dwFMTLen;
    PCMWAVEFORMAT pwf;
    char chDATA[4];
    DWORD dwDATALen;
    //UINT8* pBufer;
}WaveHeader;
void MakeWaveData(int rate, int freq, int amp, char* p, int len)//采样率、频率、音量、采样点数
{
    int flag = 0;
    if (m_channelbits == 16)        //16位
    {
        if (m_channels == 1)
        {
            for (int i = 0; i < len; i++)
            {
                INT16 v = amp/100*32768 * sin(2 * MATH_PI * freq * i / rate);
                *(p + flag) = v & 0xFF;//低8位
                *(p + flag + 1) = (v >> 8) & 0xFF;//16bit量化 高8位
                flag += 2;
            }
        }
        else
        {
            for (int i = 0; i < len; i++)
            {
                INT16 vl = amp / 100 * 32768 * sin(2 * MATH_PI * freq * i / rate) ;
                INT16 vr = amp / 100 * 32768 * sin((2 * MATH_PI * freq * (i+5) )/ rate) ;
                *(p + flag) = (vl & 0xFF);      
                *(p + flag + 1) = ((vl >> 8) & 0xFF);
                *(p + flag + 2) = (vr & 0xFF);
                *(p + flag + 3) = ((vr >> 8) & 0xFF);
                flag += 4;
            }
        }
    }
    else
    {   
        if (m_channels == 1)
        {
            for (int i = 0; i < len; i++)
            {
                *(p + i) = sin(i * (MATH_PI * 2) / rate * freq) * amp * 128 / 100 + 128;
            }
        }
        else
        {
            for (int i = 0; i < len; i++)
            {
                *(p + flag)= sin(i * (MATH_PI * 2) / rate * freq) * amp * 128 / 100+128;
                *(p + flag + 1)= sin((i+5) * (MATH_PI * 2) / rate * freq) * amp * 128 / 100+128;
                flag += 2;
            }
        }
    }
}
int Create(int freq, int volume, int durations)//频率、音量、持续时间
{
    WaveHeader *pHeader = new WaveHeader;
    DWORD totalLen = (m_samplefreq * m_channels * m_channelbits / 8) * durations + 44;//文件总长度=(采样率 * 通道数 * 比特数 / 8) * 持续时间(s)
    pHeader->chRIFF[0] = 'R';
    pHeader->chRIFF[1] = 'I';
    pHeader->chRIFF[2] = 'F';
    pHeader->chRIFF[3] = 'F';
    pHeader->dwRIFFLen = totalLen - 8;//文件的总长度-8bits

    pHeader->chWAVE[0] = 'W';
    pHeader->chWAVE[1] = 'A';
    pHeader->chWAVE[2] = 'V';
    pHeader->chWAVE[3] = 'E';

    pHeader->chFMT[0] = 'f';
    pHeader->chFMT[1] = 'm';
    pHeader->chFMT[2] = 't';
    pHeader->chFMT[3] = ' ';

    pHeader->dwFMTLen = 0x0010;//一般情况下Size为16,如果为18则最后多了2个字节的附加信息
    pHeader->pwf.wf.wFormatTag = 0x0001;//编码方式
    pHeader->pwf.wf.nChannels = m_channels; //1为单通道,2为双通道
    pHeader->pwf.wf.nSamplesPerSec = m_samplefreq;  //=44.1KHz
    pHeader->pwf.wf.nAvgBytesPerSec = m_samplefreq * m_channels * m_channelbits / 8;//每秒所需字节数
    pHeader->pwf.wf.nBlockAlign = m_channels * m_channelbits / 8;//一个采样的字节数
    pHeader->pwf.wBitsPerSample = m_channelbits;//16位,即设置PCM的方式为16位立体声(双通道)

    pHeader->chDATA[0] = 'd';
    pHeader->chDATA[1] = 'a';
    pHeader->chDATA[2] = 't';
    pHeader->chDATA[3] = 'a';
    pHeader->dwDATALen = totalLen - WAVE_HEAD_LENGTH;//数据的长度,=文件总长度-头长度(44bit)

    char *pWaveBuffer = new char[totalLen]; //音频数据
    memcpy(pWaveBuffer, pHeader, WAVE_HEAD_LENGTH);

    MakeWaveData(pHeader->pwf.wf.nSamplesPerSec, freq, volume, pWaveBuffer+ WAVE_HEAD_LENGTH, m_samplefreq*durations);//采样点数

    ofstream ocout;
    ocout.open("D:\\newWave.wav", ios::out | ios::binary);//以二进制形式打开文件
    if (ocout)
        ocout.write(pWaveBuffer, totalLen);
    else
        return 0;
    ocout.close();

    delete(pHeader);
    return 1;
}
int main()
{
    if (Create(10000, 100, 5))
        cout << "创建成功!" << endl;
    else
        cout << "创建失败!" << endl;
    return 0;
}

生成的8bit音频文件频谱:


C++根据频率生成wav音频文件_第6张图片

参考资料:
http://blog.csdn.net/zhihu008/article/details/7854529#comments
http://bbs.csdn.net/topics/390026541/

你可能感兴趣的:(音频)