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

来源:互联网 发布:张家港美工设计培训 编辑:程序博客网 时间:2024/06/06 13:18

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

一.WAV文件格式

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



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、单通道:




8bit、双通道:



16bit、单通道:



16bit、双通道:


二.实现代码

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

#include <windows.h> #include <mmsystem.h>#include <iostream>#include<fstream> #include<math.h>#define WAVE_HEAD_LENGTH 44//wav头文件长度#define m_samplefreq 44100#define m_channels 2#define m_channelbits 8#define MATH_PI 3.1415using 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音频文件频谱:




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

1 0