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中查看,这里只作简要介绍)



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.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音频文件频谱:




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

  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在C语言中生成wav文件可以通过使用文件操作函数和wav文件格式的结构体来实现。首先,需要使用C语言中的文件操作函数如fopen()打开一个文件,然后使用fwrite()函数将音频数据写入到文件中。同时,需要创建一个wav格式的头部信息,并将其写入到文件开头。 具体步骤如下: 1. 定义wav文件头部结构体,包括RIFF标识、文件大小、WAVE标识等信息。 2. 创建一个音频数据数组,用于存储音频数据。 3. 使用文件操作函数fopen()创建一个.wav文件。 4. 将wav文件头部信息写入到文件中。 5. 将音频数据数组中的数据写入到.wav文件中。 6. 关闭文件。 以下是一个简单的C语言代码示例: ```c #include <stdio.h> #include <stdint.h> // 定义wav文件头部结构体 typedef struct { uint8_t riff[4]; // "RIFF"标识 uint32_t fileSize; // 文件大小 uint8_t wave[4]; // "WAVE"标识 // ...其他头部信息(省略) } WavHeader; int main() { FILE *file; WavHeader header; // 打开文件 file = fopen("output.wav", "wb"); if (file == NULL) { printf("文件打开失败\n"); return -1; } // 写入wav文件头部信息 // ...初始化头部信息(省略) fwrite(&header, sizeof(WavHeader), 1, file); // 写入音频数据 // ...将音频数据写入文件(省略) // 关闭文件 fclose(file); return 0; } ``` 以上是一个简单的C语言示例,通过以上步骤就可以在C语言中生成一个.wav文件。需要注意的是,以上示例并未完整包含所有的wav文件头部信息和音频数据写入操作,具体实现时需要根据实际需求进行完善。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值