1. 程式人生 > >C++根據頻率生成wav音訊檔案

C++根據頻率生成wav音訊檔案

當建立並讀寫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;
}