【VS开发】【智能语音处理】Windows下麦克风语音采集
簡介
?? 這是我很早以前的大學畢業設計,忽然間找到貼出來以紀念自己的純真年代...但是因為CSDN不給面子所以導致短短的一篇文章貼了足足7次..他老提時說文章超過了64K,老大,拜托,那是算上了里面的圖片大小吧...:-(
?? 本文簡單介紹了聲卡的工作原理?,?錄音的原理以及數字音頻的基本知識并且利用?Windows?提供的?Waveform Aduio APIs?以及?Multimedia File I/O APIs?實現一個?Windows?環境下的麥克風錄音以及將錄音文件保存成?.wav?文件的簡單系統?.
?
關鍵字
??Waveform Aduio APIs, Multimedia File I/O APIs,waveInXXX,
mmioXXX,?麥克風?,?錄音?,?波形文件?,VC6++
?
??要深入的了解麥克風錄音的實現我們必須了解聲卡的工作原理?,麥克風錄音的原理以及了解相關的編程接口,下面我們將慢慢道來…
?
1.?聲卡的工作原理
?
聲卡的工作原理其實很簡單?,我們知道,麥克風和喇叭所用的都是模擬信號,而電腦所能處理的都是數字信號,兩者不能混用,聲卡的作用就是實現兩者的轉換。從結構上分,聲卡可分為模數轉換電路和數模轉換電路兩部分,模數轉換電路負責將麥克風等聲音輸入設備采到的模擬聲音信號轉換為電腦能處理的數字信號,而數模轉換電路負責將電腦使用的數字聲音信號轉換為喇叭等設備能使用的模擬信號,就這么簡單?。
?
 上圖就是一塊典型的聲卡?,?Mic?插口?用于連接麥克風?,?通過它可以錄制外界的聲音
2.?數字音頻基礎知識
??麥克風錄音的過程其實就是將模擬信號轉化成數字信號的過程?,?其中涉及的一些概念如下?:
?1.?采樣率?(Sampling Rate)
????采樣率指聲卡在一秒之中對聲音?(?波形?)?作記錄的次數?,?根據研究聲音播出時的質量常常只能達到采樣率的一半?,?因此必須采取雙倍的采樣率才能將聲音標準重現?.?也就是只要采樣率大于原始信號頻率的兩倍以上即可減低錯誤?,?達到和原始聲音差不多的質量?.?人的聽力大概是?20KHZ,?所以高品質的采樣率應為其兩倍以上?.
????當聲音來源為音樂時?,?因為它所橫跨的頻率變化極為寬廣?,?通常以?44.1KHZ?的頻率為?CD?音樂采樣率的標準?.?但是若以語言為主由于人說話的語音大概是?10KHZ,?因此加倍采樣?,?只取?22KHZ?即可?,?采樣率越高所記錄下來的音質就越清晰?,?當然?,?越高的采樣所記錄下的文件就越大?.
??2.?采樣位
????解析度決定了采樣的音波是否能保持原來的形狀?,?越接近原型則需解析度越高?,?若以?8?位來采樣的話其能表達的組合種類是?2?的?8?次方?,?即?256,?表示用?8?位的采樣大小能分辨出?256?個層次的聲音?,?若用?16?位來采樣?,?則能分辨的差異將高達?2?的?16?次方?,?為?65536,?其精度自然大為提高?.16?位?,8?位采樣的差別在于動態范圍的寬窄?,?動態范圍寬廣?,?音量起伏的大小變化就能夠更精細的被記錄下來?,?如此一來不論是細微的聲音或是強烈的動感震撼?,?都可以表現的淋漓盡致?,?而?CD?音質的采樣規格正式?16?位采樣的規格?.
??3.?量化誤差?(Quantization error)
???????在采樣的過程中?,?不斷連續變化的模擬信號要用數字化的數值來表示?,?這樣的過程就會發生所謂的量化誤差?(Quantization error).?所謂的量化誤差指的是實際的信號的振幅?(smplitude)?和數字化之后所的數字之間的差異?.?如果用將數字信號還原成模擬信號的角度看?,?量化誤差就是失真?(Distortion).?我們可以用增加采樣大小的方式來降低量化誤差?,?也就是更多的位?(bits)?來表示一個采樣信號?,?這樣可以提高精度?.
?4.?量化?(Quantization),?線性量化法?(Linear quantization)?和非線性量化法?(Nonlinear quantization)
???????所謂的量化?(Quantization)?就是將模擬信號所代表的連續范圍分成一段一段的區間?(Interval),?每一段區間我們定義一個數字化的值?.?區間的數目是跟采樣大小有關?,?舉例來說?,?有一種最簡單的量化法稱為?”?線性量化法?”(Linear quantization),?這種量化法采用等距離的間隔空間?,?架設一個訊號它的最大值是?5.0,?采樣大小為?3?位?,?則每個量化區間就時?5.0/2^3,?也就是?0.625?單位?.?另外一種相反的量化方法就是?”?非線性量化法?”(Nonlinear quantization),?這種量化法采用不同的間隔空間?.?以?”?對數量化法?”(Logarithm quantization)?為例?.?低振幅范圍的量化區間就比高振幅的范圍的區間較為接近?,?用這種量化的法產生的結果就是在低振幅時我們會得到佳好的效果?.?通常如果使用同樣的采樣大小?,?非線性量化法會比線性量化法得到更好的聲音品質?.?但是如果是要對聲音做濾波?(filtered)?或一些運算的時候?,?使用線性量化法會比較容易處理?.
?5.?聲音強度
?????????波形振幅的平方.兩個聲音強度上的差常以分貝?(db)?為單位來度量?,計算公式如下:
????20*log(A1/A2)?分貝?,?A1,A2?為兩個聲音的振幅?.
a.?如果采樣大小為?8?位?,則采樣的動態范圍為?20*log(256)?分貝?=48db;
b.?如果樣本大小為?16?位?,則采樣動態范圍為?20*log(65536)?大約是?96?分貝?,接近了人聽覺極限和痛苦極限,是再線音樂的理想范圍,?windows?同時支持8?位和?16?位的采樣大小?.
?
?6.?音頻編碼方法
??????目前已經發展了許多音頻編碼的方法用以減少存儲量或是傳輸的時間?,?以下所列為兩種較普遍的編碼方法?:
???a.PCM(Pulse code modulation);
脈沖編碼調制?,即對波形按照固定周期頻率采樣。為了保證采樣后數據質量,采樣頻率必須是樣本聲音最高頻率的兩倍,這就是?Nyquist?頻率?.
???b.ADPCM(Adaptive delta pulse modulation).
3.?RIFF?文件結構和?WAVE?文件格式?
   ?Windows?支持兩種?RIFF(Resource Interchange File Format,"?資源交互文件格式?")?格式的音頻文件:?MIDI?的?RMID?文件和波形音頻文件格式WAVE?文件,其中在計算機領域最常用的數字化聲音文件格式是后者,它是微軟專門為?Windows?系統定義的波形文件格式(?Waveform Audio?),由于其擴展名為?"*.wav"?,因而該類文件也被稱為?WAVE?文件。?為了突出重點,有的放矢,本文涉及到的聲音文件所指的就是?WAVE?文件。常見的?WAVE?語音文件主要有兩種,分別對應于單聲道(?11.025KHz?采樣率、?8Bit?的采樣值)和雙聲道(?44.1KHz?采樣率、?16Bit?的采樣值)。這里的采樣率是指聲音信號在進行?"?模→數?"?轉換過程中單位時間內采樣的次數。采樣值是指每一次采樣周期內聲音模擬信號的積分值。對于單聲道聲音文件,采樣數據為八位的短整數(short int 00H-FFH?);而對于雙聲道立體聲聲音文件,每次采樣數據為一個?16?位的整數(?int?),高八位和低八位分別代表左右兩個聲道。?WAVE?文件數據塊包含以脈沖編碼調制(?PCM?)格式表示的樣本。在進行聲音編程處理以前,首先讓我們來了解一下?RIFF?文件和?WAVE?文件格式。?
   ?RIFF?文件結構可以看作是樹狀結構,其基本構成是稱為?"?塊?"?(?Chunk?)的單元,每個塊有?"?標志符?"?、?"?數據大小?"?及?"?數據?"?所組成,塊的結構如圖?2?所示:
?
| 塊的標志符(?4BYTES?) | 
| 數據大小?(?4BYTES?) | 
| 數據 | 
????????????? ????????????????????????????????????????圖?2?
   從上圖可以看出,其中?"?標志符?"?為?4?個字符所組成的代碼,如?"RIFF"?,?"LIST"?等,指定塊的標志?ID?;數據大小用來指定塊的數據域大小,它的尺寸也為?4?個字符;數據用來描述具體的聲音信號,它可以由若干個子塊構成,一般情況下塊與塊是平行的,不能相互嵌套,但是有兩種類型的塊可以嵌套子塊,他們是?"RIFF"?或?"LIST"?標志的塊,其中?RIFF?塊的級別最高,它可以包括?LIST?塊。另外,?RIFF?塊和?LIST?塊與其他塊不同,?RIFF?塊的數據總是以一個指定文件中數據存儲格式的四個字符碼(稱為格式類型)開始,如?WAVE?文件有一個?"WAVE"?的格式類型。?LIST?塊的數據總是以一個指定列表內容的?4?個字符碼(稱為列表類型)開始,例如擴展名為?".AVI"?的視頻文件就有一個?"strl"?的列表類型。?RIFF?和?LIST?的塊結構如下:
| RIFF/LIST?標志符 | |
| 數據?1?大小 | |
| 數據?1 | 格式?/?列表類型 | 
| 數據 | |
????????????????????????????????????????????????圖?3?
   ?WAVE?文件是非常簡單的一種?RIFF?文件,它的格式類型為?"WAVE"?。?RIFF?塊包含兩個子塊,這兩個子塊的?ID?分別是?"fmt"?和?"data",?其中?"fmt"?子塊由結構?PCMWAVEFORMAT?所組成,其子塊的大小就是?sizeofof(PCMWAVEFORMAT),?數據組成就是?PCMWAVEFORMAT?結構中的數據。?WAVE?文件的結構如下圖?4?所示:
| 標志符(?RIFF?) | 
| 數據大小 | 
| 格式類型(?"WAVE"?) | 
| "fmt" | 
| Sizeof(PCMWAVEFORMAT) | 
| PCMWAVEFORMAT | 
| "data" | 
| 聲音數據大小 | 
| 聲音數據 | 
????????????????????????????????????????????????????圖?4?
   ?PCMWAVEFORMAT?結構定義如下:
| typedef struct 
 
 
 
 ? 4.?硬件抽象層?(?HAL,?Hardware Abstraction?Layer?) ?????HAL?是一個可加載的核心模塊?(HAL.dll)?,它為運行在?Windows NT?架構?(?包括?WindowsNT4.0,Windows2000,WindowsXP)?上?的硬件平臺提供低級接口?,?HAL?隱藏各種與硬件有關的細節?,例如:?I/O?接口?,中斷控制器,聲卡…這樣的話如果用戶需要訪問聲卡硬件的話只能通過該聲卡的驅動程序來實現,聲卡驅動程序再調用?HAL?中的相應例程來實現?,下圖顯示了?HAL?,聲卡驅動程序,?Waveform Audio APIs?,我們的麥克錄音程序之間的關系: | |||||||||||||||||||||||||
5.?Waveform Audio
?????Waveform Audio APIs?是?Microsoft?提供給廣大?Win32?程序員用來給自己的應用程序添加聲音支持的一套強大的?API,?它提供的功能如下:
?????????1.?打開?/?關閉?/?查詢聲音設備?;
????????2.?播放波形文件?;
????????3.?設置播放速度?;
?????????4.?播放進度控制?;
?????????5.?錄音?;
?????????6.?得到當前的播放位置?;
????????7.?調節音量?.
?
下面簡單介紹一下這套?API?提供的主要函數?:
- 打開錄音設備函數
????????????MMRESULT waveInOpen(
??????????? LPHWAVEIN phwi,?????????????????//?輸入設備句柄
????????????UINT uDeviceID,?????????????????? //?輸入設備?ID
????????????LPWAVEFORMATEX pwfx,?????? //?錄音格式指針
????????????DWORD dwCallback,??????????????//?處理?MM_WIM_***?消息的回調函數或窗??
???????????????????????????????????????????????????????//?口句柄,線程?ID
??????????? DWORD dwCallbackInstance,?
??????????? DWORD fdwOpen????????????????? //?處理消息方式的符號位
??????????);
?
- 為錄音設備準備緩存函數
??????????????MMRESULT waveInPrepareHeader(? HWAVEIN hwi,?
??????????????????????????????????????????????????????????????????LPWAVEHDR pwh,
??????????????????????????????????????????????????????????????????UINT bwh );?
?
- 給輸入設備增加一個緩存
??????????????MMRESULT waveInAddBuffer(? HWAVEIN hwi,
???????????????????????????????????????????????????????????LPWAVEHDR pwh,
???????????????????????????????????????????????????????????UINT cbwh );?
?
- 開始錄音
??????????????MMRESULT waveInStart(? HWAVEIN hwi? );?
?
- 清除緩存
??????????????MMRESULT waveInUnprepareHeader( HWAVEIN hwi,
?????????????????????????????????????????????????????????????????????LPWAVEHDR pwh,
?????????????????????????????????????????????????????????????????????UINT cbwh);?
?
- 停止錄音
??????????????MMRESULT waveInReset( HWAVEIN hwi );?
?
- 關閉錄音設備
??????????????MMRESULT waveInClose( HWAVEIN hwi );?
?
- Wave_audio?數據格式
??????????????typedef struct {
???????????????????WORD? wFormatTag; //?數據格式,一般為?WAVE_FORMAT_PCM?即
???????????????????????????????????????????????????//?脈沖編碼
?????????????????? WORD? nChannels;?????//?聲道
?????????????????? DWORD nSamplesPerSec;???//?采樣頻率
?????????????????? DWORD nAvgBytesPerSec;??//?每秒數據量
?????????????????? WORD? nBlockAlign;
?????????????????? WORD? wBitsPerSample;?????//?樣本大小
?????????????????? WORD? cbSize;
????????????} WAVEFORMATEX;?
- waveform-audio?緩存格式
?????????????typedef struct {
?????????????????? LPSTR? lpData;????????????????????//?內存指針
???????????????????DWORD? dwBufferLength;????//?長度
???????????????????DWORD? dwBytesRecorded; //?已錄音的字節長度
???????????????????DWORD? dwUser;
???????????????????DWORD? dwFlags;
???????????????????DWORD? dwLoops;??????????????//?循環次數
???????????????????struct wavehdr_tag * lpNext;
???????????????????DWORD? reserved;
?????????????} WAVEHDR;?
?
- 相關消息
?????????????MM_WIM_OPEN:?打開設備時消息,在此期間我們可以進行一些初始化工作
?????????????MM_WIM_DATA:?當緩存已滿或者停止錄音時的消息,處理這個消息可以對
?????????????????????????????????????緩存進行重新分配,實現不限長度錄音
?????????????MM_WIM_CLOSE?:關閉錄音設備時的消息。
?
?
5. Multimedia File I/O
???????Multimedia File I/O APIs?是?Microsoft?提供的另外一套強大的針對媒體文件?I/O?的?API,?我們知道許多像?MediaPlay,RealOne?這樣的多媒體程序對媒體文件的讀寫性能要求很高?,?它們幾乎要求實時的將磁盤上的媒體文件以流的形式讀入?,?但是對于一般的文件?I/O?形式如圖?1:
1.文件從磁盤上被讀入操作系統的File I/O的buffer;
????2.?然后拷貝到應用程序自己的?buffer?中?;
????3.?應用程序這時候才能讀取文件內容?.
????????上述的過程對于多媒體應用程序來說是低效的而且浪費寶貴的內存資源,如果文件和大的話勢必還要采取分段讀取等機制,?Multimedia File I/O?采取了一種直接存取機制?(?如圖?2),?使得應用程序可以直接讀取操作系統的?File I/O buffer,?大大提高了效率?.?后面我們會利用此套?API實現錄音文件的存儲?.
?
 ?
6.?麥克錄音系統簡介
???本文實現的麥克錄音系統將具備以下功能?:
??????1.?錄制用戶通過麥克風發出的聲音?;
????????????????這將利用到?Waveform APIs,?流程如下?:
?????????????????????a.?打開錄音設備?waveInOpen;
?????????????????????b.?準備?wave?數據頭?waveInPrepareHeader;
?????????????????????c.?準備數據塊?waveInAddBuffer;
?????????????????????d.?開始錄音?waveInStart;
?????????????????????e.?停止錄音?(waveInReset);
?????????????????????f.?關閉錄音設備?(waveInClose);
?????????????????????g.?當開始錄音后當?buffer?已滿時?,?將收到?MM_WIM_DATA?消息?,?處理該
????????????????????????消息可以保存已錄好數據?.
?
???????2.?根據用戶的聲音強弱動態顯示聲音波形?;
????????????這主要通過?GDI?函數來實現?.
??
??????3.?將用戶通過麥克風發出的聲音錄制成?wav?文件保存?.
????????????這將利用到?Multimedia file I/O APIs.
?????????????????a?.調用?mminoOpen?函數來打開?WAVE?文件?,?獲取?HMMIO?類型的文件句柄?;?
   ????????????b?.根據?WAVE?文件的結構?,?調用?mmioRead?、?mmioWrite?和?mmioSeek?函數實現文件的讀、寫和定位操作?;?
 ??  ???????????c?.調用?mmioClose?函數來關閉?WAVE?文件?.??
?
7.?麥克錄音系統的實現?(MicDemo)???
?
下面是該系統的界面?:
?
?namespace??perdubug??{???//??prevent?the?name-space?pollution??
?
?class??CSoundIn??
?{
?public?:????
????????????????BOOL?????__initMic();??//??get?the?best?wave?format?supported?by?your?sound?card
?????????????????????????????????????????????????????//??and?then?i?will?use?the?format?to?capture?sound.?
?
?
?????????????????void??????__closeMic();
?
????????????????
????????????????BOOL?????__openMic();??//??open?device?and?begin?to?capture?with?the?best?format(when?
????????????????????????????????????????????????????????//??invoke?__initMic?function?then?you?will?get?the?best?format
????????????????????????????????????????????????????????//??supported?by?host's?sound?card
????????????????
?????????????????//?
?
?????????????????//??if?your?want?to?capture?sound?and?export?into?a?wav?file?please?invoke?this?function
?????????????????//??to?tell?me?the?full?path?then?i?will?create?the?wav?file.
?????????????????//
?
?????????????????void??????__createOutputWaveFile(?const??TCHAR??*??lpszFileName);
????????????????
?????????????????//??if?you?invoke?any?member?function?return?error/false?please
?????????????????//??use?this?function?to?get?the?result?
?
????????????????DWORD????__getLastError();
?
?????????????????//?
?
?????????????????//??when?the?capture?buffer?is?filled?please?invoke?this?function?to?'add?buffer'(Actually
?????????????????//??you?should?create?two-circular?buffers,when?1st?buffer?is?filled?then?switch?to?2st,1st
?????????????????//??buffer?will?be?wrote?into?wav?file.
?????????????????//
?
?????????????????void??AddBuffer();
????????????????
?????????????????virtual???~?CSoundIn();
????????????????
????????????????friend?CSoundIn??&??theSoundCapture();
?private?:
?
????????????????BOOL??GetBestWaveFormat(WAVEFORMATEX??&??waveFormatEx);
????????????????
?????????????????//??because?sound?card?is?one?and?only?so?i?must?limit?the?number?of?CSoundIn?object,
?????????????????//??but?how?to?limit?the?class?object?nums?maybe?put?constructor?into?private?scope?is
?????????????????//??a?good?idea,:-)?
?
????????????????CSoundIn();??????????
?
?private?:
????????????????WAVEINCAPS????????????????????m_WaveInDevCaps;
????????????????HWAVEIN????????????????????????????m_WaveIn;
????????????????WAVEHDR???????????????????????????m_WaveHeader;
????????????????WAVEFORMATEX????????????m_WaveFormat;??
????????????????
????????????????UINT????m_WaveInSampleRate;
?????????????????int??????????m_NbMaxSamples;
????????????????UINT????m_SizeRecord;
?
????????????????DWORD???m_dwLastError;
?
?????????????????enum???{?MAX_SIZE_INPUT_BUFFER??=???1???*???2???*???1024??}?;??//??samples?*?voie?*?size_samples?
?
?
?public?:
????????????????SHORT??InputBuffer[MAX_SIZE_INPUT_BUFFER];????//??used?for?int?FFT,many?GUI?application?
???????????????????????????????????????????????????????????????????????????????????????????????????????????//??want?to?display?sound?peak?so..?
?
????????????????BOOL???m_bTerminateThread;????????????????????//???to?'kill'?waveCallback?function?
?
????????????????BOOL???m_bImportToWaveFile;
????????????????
????????????????CWaveFile???????m_waveFile;
}?;
?
}???//??end?namespace?perdubug?
?
?
對于將錄音保存在WAV文件的工作主要是由CwaveFile類來完成.下面是該類的定義:
?//?
?
?//??Encapsulates?reading?or?writing?sound?data?to?or?from?a?wave?file
?//?-----------------------------------------------------------------------------?
?
?class??CWaveFile
?{
?public?:
????WAVEFORMATEX?*??m_pwfx;?????????//??Pointer?to?WAVEFORMATEX?structure?
?
????HMMIO??????????????m_hmmio;???????????????//??MM?I/O?handle?for?the?WAVE?
?
????MMCKINFO??????m_ck;???????????????????????//??Multimedia?RIFF?chunk?
?
????MMCKINFO??????m_ckRiff;????????????????//??Use?in?opening?a?WAVE?file?
?
????DWORD?????????????m_dwSize;??????????????//??The?size?of?the?wave?file?
?
????MMIOINFO???????m_mmioinfoOut;
????DWORD?????????????m_dwFlags;
????BOOL?????????????????m_bIsReadingFromMemory;
????BYTE?*????????????????m_pbData;
????BYTE?*????????????????m_pbDataCur;
????ULONG??????????????m_ulDataSize;
????CHAR?*???????????????m_pResourceBuffer;????
?protected?:
????HRESULT?ReadMMIO();
????HRESULT?WriteMMIO(?WAVEFORMATEX??*?pwfxDest?);
?
?public?:
????CWaveFile();
?????~?CWaveFile();
?
????HRESULT?Open(?LPCTSTR?strFileName,?WAVEFORMATEX?*??pwfx,?DWORD?dwFlags?);
????HRESULT?OpenFromMemory(?BYTE?*??pbData,?ULONG?ulDataSize,?
????????????????????????????????????????????????????????WAVEFORMATEX?*??pwfx,?DWORD?dwFlags?);
????HRESULT?Close();
?
????HRESULT?Read(?BYTE?*??pBuffer,?DWORD?dwSizeToRead,?DWORD?*??pdwSizeRead?);
????HRESULT?Write(?UINT?nSizeToWrite,?BYTE?*??pbData,?UINT?*??pnSizeWrote?);
?
????DWORD???GetSize();
????HRESULT?ResetFile();
????WAVEFORMATEX?*??GetFormat()??{??return??m_pwfx;?}?;
}?;
?
?我們有了這兩個強有力的類的支持就可以開始我們的編程工作了….
????1?.用VC6?++?建立一個MFC基于對話框的工程:MicDemo;
????2?.添加我們的兩個類CSoundIn,CwaveFile;????????
????3?.當我們點擊開始(Start)按鈕的時候我們就要開始錄音了…
?
??????????void??CMicDemoDlg::OnStart()?
?????????{
????????????????m_btnStart.EnableWindow(FALSE);????????????????
?????????????????if?(theSoundCapture().__initMic())
?????????????????{
??????????????????????m_filePath.SetWindowText(_T(?"?yangchen.wav.?"?));?????????
??????????????????????theSoundCapture().__createOutputWaveFile(_T(?"?yangchen.wav?"?));?????????????????????
??????????????????????if?(?!?theSoundCapture().__openMic())
???????????????????????{
????????????????????????::MessageBox(?this?->?m_hWnd,_T(?"?Can?not?open?microphone!?"?),?_T(?"?Error?"?),MB_OK?|?MB_ICONERROR);
??????????????????????????return?;
??????????????????????}?
?
????????????????}?
?
????????????????
????????????????m_btnStop.EnableWindow(TRUE);
??????????????
???????????????//??設置定時器是為了畫波形用的???????
?
?????????????????SetTimer(?1?,??200???/*?start?slow?*/?,?NULL);
???????????}?
?
??
?????4?.在定時器的回調函數中畫波形.
????????????void??CMicDemoDlg::OnTimer(UINT?nIDEvent)?
?????????????{
?????????????????????if?(nIDEvent??==???1?)
????????????????????{???????????
?????????????????????????????????static???const???int??xCon??=???13?;
?????????????????????????????????static???const???int??yCon??=???13?;
?????????????????????????????????static???const???int??wCon??=???623?;
?????????????????????????????????static???const???int??hCon??=???80?;
????????????????????????????????
????????????????????????????????CClientDC?dc(?this?);
????????????????????????????????
????????????????????????????????CBitmap???Bitmap;
????????????????????????????????CBitmap??*??pbmOld??=??NULL;??????????????????????????????
????????????????????????????????CDC?????????dcMem;
????????????????????????????????
????????????????????????????????dcMem.CreateCompatibleDC(?&?dc);?????????????????
????????????????????????????????Bitmap.CreateCompatibleBitmap(?&?dc,wCon,hCon);
????????????????????????????????
????????????????????????????????pbmOld??=??dcMem.SelectObject(?&?Bitmap);
????????????????????????????????
????????????????????????????????dcMem.PatBlt(?0?,?0?,wCon,hCon,?WHITENESS);??????????????????????????????
????????????????????????????????dcMem.MoveTo(?0?,hCon?/?2?);
????????????????????????????????
?????????????????????????????????//?
?
?????????????????????????????????//??display?incomming?signal--key?idea!
?????????????????????????????????//
?
?????????????????????????????????for?(?int??x??=?0??;?x??<??wCon;?x?++?)???//??display?Input?
?
??????????????????????????????????{
??????????????????????????????????????????dcMem.LineTo(x,(hCon??>>???1?)??-??(theSoundCapture().InputBuffer[x]??>>???7?));
????????????????????????????????}?
?
????????????????????????????????
????????????????????????????????dc.BitBlt(xCon,yCon,wCon,hCon,?&?dcMem,??0?,??0?,?SRCCOPY);
????????????????????????????????
????????????????????????????????dcMem.SelectObject(pbmOld);
????????????????????????????????dcMem.DeleteDC();?????????????
??????????????????}?
?
???????????????????else?
?
?????????????????????????CDialog::OnTimer(nIDEvent);
????????????}?
?
???????
???????5?.點擊停止(Stop)按鈕的時候停止錄音和寫WAV文件
???????????????????void??CMicDemoDlg::OnStop()?
??????????????????{
????????????????????????m_btnStop.EnableWindow(FALSE);?
????????????????????????theSoundCapture().__closeMic();?
????????????????????????m_btnStart.EnableWindow(TRUE);
?????????????????}?
?
看完整段代碼你可能會很奇怪怎么在CmicDemoDlg中居然都沒有定義一個CSoundIn對象????呵呵,原因很簡單,因為設備的獨占性所以在一個時刻只能有一個CSoundIn對象存在(因為CSoundIn對象需要占據錄音設備),所以我們必須限制程序員生成CSoundIn對象的數量,怎么限制呢???那就是把CSoundIn的構造函數放在private區域里面:
???
??????private?:
????????????????BOOL??GetBestWaveFormat(WAVEFORMATEX??&??waveFormatEx);
????????????????
?????????????????//??because?sound?card?is?one?and?only?so?i?must?limit?the?number?of?CSoundIn?object,
?????????????????//??but?how?to?limit?the?class?object?nums?maybe?put?constructor?into?private?scope?is
?????????????????//??a?good?idea,:-)?
?
????????CSoundIn();??????????
?
這樣的話就根本無法聲明一個CSoundIn對象,不信你試一下在你的代碼中寫上:
?????CSoundIn??soundInObj;
?能編譯通過嗎???肯定是不能,那如何調用CSoundIn的成員函數呢???答案是通過一個全局函數:
?
???????????????//??global?function,:-(
???????????????//??client?can?only?through?this?function?to?use?CSoundIn?object?
?
??????????????CSoundIn??&??theSoundCapture()
???????????????{
??????????????????????static??CSoundIn?p;
??????????????????????return??p;
???????????????}?
?
?
??這時候你應該明白了為什么上面的代碼中調用CSoundIn的成員函數的時候都是用theSoundCapture來做的原因了吧.?
轉載于:https://www.cnblogs.com/huty/p/8518458.html
總結
以上是生活随笔為你收集整理的【VS开发】【智能语音处理】Windows下麦克风语音采集的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 求高手指点这两个男优的名字 ?
- 下一篇: 画一场婚礼是什么歌啊
