引言
我是C++的初學者,入門都要靠VCKBASE,好在裡面有很多適合於初學者的例子,讓我少走了很多彎路,為了回饋大家,我也把我最近剛完成的一個簡單的小程序提供給大家,讓那些曾經和我一樣徘徊在C++大門之外的人能快些掌握要領,大家不妨看一看。
本文以VC知識庫第26期 栾義明 先生的《基於API的錄音機程序》為基礎的,在此深表感謝!相同之處不再重復,我在此基礎上增加了將錄音保存為wav文件的格式,便於大家參考。
基本步驟及思想:設定音頻采集參數(采樣率、聲道等),打開音頻設備、准備wave數據頭和開辟緩存,操作采集的數據並保存為wav文件。設定音頻回放參數,打開回放設備、准備wave數據頭和寫wave數據。另外樣本程序需包含#include <mmsystem.h>和#pragma comment(lib,"WINMM.LIB")多媒體支持。
在介紹程序前,需要你對wave文件的格式和相關一些基礎概念有所了解,這些均可以在msdn中查找,為方便理解,我們將其整理,如果對這些基礎知識已有所了解,可以跳過。
概念1、定義波形數據格式
typedef struct{WORD wFormatTag;
WORD nChannels;
DWORD nSamplesPerSec;
DWORD nAvgBytesPerSec;
WORD nBlockAlign;
WORD wBitsPerSample;
WORD cbSize; } WAVEFORMATEX;
具體參數解釋如下:
wFormatTag:波形數據的格式,定義在MMREG.H文件中
nChannels:波形數據的通道數:單聲道或立體聲
nSamplesPerSec:采樣率,對於PCM格式的波形數據,采樣率有8.0 kHz,11.025kHz,22.05 kHz,44.1 kHz等
nAvgBytesPerSec:數據率,對於PCM格式的波形數據,數據率等於采樣率乘以每樣點字節數
nBlockAlign:每個樣點字節數
wBitsPerSample:采樣精度,對於PCM格式的波形數據,采樣精度為8或16
cbSize:附加格式信息的數據塊大小
概念2、定義設備頭結構
WAVEHDR定義了指向波形數據緩沖區的設備頭。
typedef struct { LPSTR lpData;
DWORD dwBufferLength;
DWORD dwBytesRecorded;
DWORD dwUser;
DWORD dwFlags;
DWORD dwLoops;
struct wavehdr_tag * lpNext;
DWORD reserved; } WAVEHDR;
lpData:波形數據的緩沖區地址
dwBufferLength:波形數據的緩沖區地址的長度
dwBytesRecorded:當設備用於錄音時,標志已經錄入的數據長度
dwUser:用戶數據
dwFlags:波形數據的緩沖區的屬性
dwLoops:播放循環的次數,僅用於播放控制中
lpNext和reserved均為保留值
注意:上述結構體以及我們在程序中所使用到的“HWAVEIN””HWAVEOUT”結構體均是系統已經存在的,我們只需要對其進行賦值即可。
概念3、消息處理函數
MM_WIM_OPEN //設備的打開
MM_WIM_DATA //設備數據的采集及操作
MM_WIM_CLOSE //設備的關閉
相應回放設備的消息分別為MM_WOM_OPEN,MM_WOM_DATA,MM_WOM_CLOSE.
注意:消息處理函數是消息自我驅動的,不需要我們的人為干預。比如:當我們打開設備時,系統會自動調用MM_WIM_OPEN,當我們將數據添加到緩沖區,而緩沖區滿時,系統會自動調用MM_WIM_DATA,我們所需要做的,就是對該函數編好相應的源代碼。
現在,我們進入正題:如何實現一個錄音機。
⑴先對WAVEFORMATEX結構體進行賦值,然後為緩沖區分配內存
pBuffer1=(PBYTE)malloc(INP_BUFFER_SIZE);
pBuffer2=(PBYTE)malloc(INP_BUFFER_SIZE);
對設備頭結構體分配內存
pWaveHdr1=reinterpret_cast<PWAVEHDR>(malloc(sizeof(WAVEHDR)));
pWaveHdr2=reinterpret_cast<PWAVEHDR>(malloc(sizeof(WAVEHDR)));
然後使用wave音頻相關函數對輸入數據進行操作:
①為波形輸入設備准備緩沖區
waveInPrepareHeader(hWaveIn,pWaveHdr1,sizeof(WAVEHDR));
waveInPrepareHeader(hWaveIn,pWaveHdr2,sizeof(WAVEHDR));
②為波形輸入設備添加緩沖區
waveInAddBuffer (hWaveIn, pWaveHdr1, sizeof (WAVEHDR)) ;
waveInAddBuffer (hWaveIn, pWaveHdr2, sizeof (WAVEHDR)) ;
③啟動聲音輸入設備,將輸入數據寫入內存
waveInStart (hWaveIn) ;
⑵編寫消息處理函數,其中,MM_WIM_DATA 函數是本程序的核心。其主要作用是將輸入數據另行保存在一緩沖區內(pSaveBuffer),該緩沖區的長度將隨著已錄入數據的大小而增加,從而實現保存輸入話音數據的功能。同時,可將緩沖區內數據保存為wav文件。其具體實現如下:
CFile m_file;
CFileException fileException;
CString m_csFileName= "F:\\audio.wav";//保存路徑
m_file.Open(m_csFileName,CFile::modeCreate|CFile::modeReadWrite, &fileException);
DWORD m_WaveHeaderSize = 38;
DWORD m_WaveFormatSize = 18;
m_file.SeekToBegin();
m_file.Write("RIFF",4);
unsigned int Sec=(sizeof pSaveBuffer + m_WaveHeaderSize);
m_file.Write(&Sec,sizeof(Sec));
m_file.Write("WAVE",4);
m_file.Write("fmt ",4);
m_file.Write(&m_WaveFormatSize,sizeof(m_WaveFormatSize));
m_file.Write(&waveform.wFormatTag,sizeof(waveform.wFormatTag));
m_file.Write(&waveform.nChannels,sizeof(waveform.nChannels));
m_file.Write(&waveform.nSamplesPerSec,sizeof(waveform.nSamplesPerSec));
m_file.Write(&waveform.nAvgBytesPerSec,sizeof(waveform.nAvgBytesPerSec));
m_file.Write(&waveform.nBlockAlign,sizeof(waveform.nBlockAlign));
m_file.Write(&waveform.wBitsPerSample,sizeof(waveform.wBitsPerSample));
m_file.Write(&waveform.cbSize,sizeof(waveform.cbSize));
m_file.Write("data",4);
m_file.Write(&dwDataLength,sizeof(dwDataLength));
m_file.Write(pSaveBuffer,dwDataLength);
m_file.Seek(dwDataLength,CFile::begin);
m_file.Close();
存在的問題:
我們也才開始vc學習,只是將自己了解的知識與和我們水平相當的新手們分享,希望一起提高。本程序有時不穩定,但音質很好,可能還有尚未暴露的錯誤,懇請廣大高手們不吝賜教。E-mail: [email protected],[email protected]
本文配套源碼