C++根據頻率生成wav音訊檔案
阿新 • • 發佈:2018-12-20
當建立並讀寫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; }