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

时间:2021-10-20 15:14:18

当创建并读写wav文件时,要以二进制打开

#include <windows.h> 
#include <mmsystem.h>
#include <iostream>
#include<fstream> 
#include<math.h>
#include <vector>

#define WAVE_HEAD_LENGTH 44//wav头文件长度
#define m_samplefreq 22050 //每秒采样数-采样速率(22050Hz)
#define m_channels 1 //通道数量,1为单通道,2为双通道
#define m_channelbits 8
#define MATH_PI 3.1415

using namespace std;
//.wav文件的文件头结构 
typedef struct
{
	char chRIFF[4]; //头部那个RIFF;
	DWORD dwRIFFLen; //存的是后面所有文件的大小;
	char chWAVE[4];
	char chFMT[4];
	DWORD dwFMTLen; //存的是fmt保存的大小,包含这之后,data前面几个,共16个;
	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 = INT16(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 = INT16(amp / 100 * 32768 * sin(2 * MATH_PI * freq * i / rate)) ;
				INT16 vr = INT16(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) = char(sin(i * (MATH_PI * 2) / rate * freq) * amp * 128 / 100 + 128);
			}
		}
		else
		{
			for (int i = 0; i < len; i++)
			{
				*(p + flag)= char(sin(i * (MATH_PI * 2) / rate * freq) * amp * 128 / 100+128);
				*(p + flag + 1)= char(sin((i+5) * (MATH_PI * 2) / rate * freq) * amp * 128 / 100+128);
				flag += 2;
			}
		}
	}
}

//频率、音量、持续时间、wav文件保存路径(包含文件名+后缀
int Create(vector<int> freqVec, int volume, int durations, std::string wavPath)
{
	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);

	int freNum = freqVec.size();
	int singleLen = m_samplefreq*durations/freNum;
	char* tempBuffer = pWaveBuffer + WAVE_HEAD_LENGTH;

	for ( int iFre = 0; iFre < freNum; ++iFre )
	{
		if ( iFre > 0 )
		{
			tempBuffer += singleLen;
		}
		
		//TODO:最后一个参数
		MakeWaveData(pHeader->pwf.wf.nSamplesPerSec, freqVec[iFre], volume, tempBuffer, singleLen);//采样点数
	}

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

	delete(pHeader);
	return 1;
}

int main()
{
	vector<int> freqVec;
	freqVec.push_back(262);
	freqVec.push_back(294);
	freqVec.push_back(330);
	freqVec.push_back(349);
	freqVec.push_back(392);

	freqVec.push_back(440);
	freqVec.push_back(494);
	freqVec.push_back(524);

	std::string wavPath = "D:\\lhcWave.wav";

	if (Create(freqVec, 20, freqVec.size()/2, wavPath))
		cout << "创建成功!" << endl;
	else
		cout << "创建失败!" << endl;

	//system( "pause" );
	return 0;
}