【VS开发】【智能语音处理】Windows下麦克风语音采集
簡介
?? 這是我很早以前的大學(xué)畢業(yè)設(shè)計(jì),忽然間找到貼出來以紀(jì)念自己的純真年代...但是因?yàn)镃SDN不給面子所以導(dǎo)致短短的一篇文章貼了足足7次..他老提時(shí)說文章超過了64K,老大,拜托,那是算上了里面的圖片大小吧...:-(
?? 本文簡單介紹了聲卡的工作原理?,?錄音的原理以及數(shù)字音頻的基本知識并且利用?Windows?提供的?Waveform Aduio APIs?以及?Multimedia File I/O APIs?實(shí)現(xiàn)一個(gè)?Windows?環(huán)境下的麥克風(fēng)錄音以及將錄音文件保存成?.wav?文件的簡單系統(tǒng)?.
?
關(guān)鍵字
??Waveform Aduio APIs, Multimedia File I/O APIs,waveInXXX,
mmioXXX,?麥克風(fēng)?,?錄音?,?波形文件?,VC6++
?
??要深入的了解麥克風(fēng)錄音的實(shí)現(xiàn)我們必須了解聲卡的工作原理?,麥克風(fēng)錄音的原理以及了解相關(guān)的編程接口,下面我們將慢慢道來…
?
1.?聲卡的工作原理
?
聲卡的工作原理其實(shí)很簡單?,我們知道,麥克風(fēng)和喇叭所用的都是模擬信號,而電腦所能處理的都是數(shù)字信號,兩者不能混用,聲卡的作用就是實(shí)現(xiàn)兩者的轉(zhuǎn)換。從結(jié)構(gòu)上分,聲卡可分為模數(shù)轉(zhuǎn)換電路和數(shù)模轉(zhuǎn)換電路兩部分,模數(shù)轉(zhuǎn)換電路負(fù)責(zé)將麥克風(fēng)等聲音輸入設(shè)備采到的模擬聲音信號轉(zhuǎn)換為電腦能處理的數(shù)字信號,而數(shù)模轉(zhuǎn)換電路負(fù)責(zé)將電腦使用的數(shù)字聲音信號轉(zhuǎn)換為喇叭等設(shè)備能使用的模擬信號,就這么簡單?。
?
上圖就是一塊典型的聲卡?,?Mic?插口?用于連接麥克風(fēng)?,?通過它可以錄制外界的聲音
2.?數(shù)字音頻基礎(chǔ)知識
??麥克風(fēng)錄音的過程其實(shí)就是將模擬信號轉(zhuǎn)化成數(shù)字信號的過程?,?其中涉及的一些概念如下?:
?1.?采樣率?(Sampling Rate)
????采樣率指聲卡在一秒之中對聲音?(?波形?)?作記錄的次數(shù)?,?根據(jù)研究聲音播出時(shí)的質(zhì)量常常只能達(dá)到采樣率的一半?,?因此必須采取雙倍的采樣率才能將聲音標(biāo)準(zhǔn)重現(xiàn)?.?也就是只要采樣率大于原始信號頻率的兩倍以上即可減低錯(cuò)誤?,?達(dá)到和原始聲音差不多的質(zhì)量?.?人的聽力大概是?20KHZ,?所以高品質(zhì)的采樣率應(yīng)為其兩倍以上?.
????當(dāng)聲音來源為音樂時(shí)?,?因?yàn)樗鶛M跨的頻率變化極為寬廣?,?通常以?44.1KHZ?的頻率為?CD?音樂采樣率的標(biāo)準(zhǔn)?.?但是若以語言為主由于人說話的語音大概是?10KHZ,?因此加倍采樣?,?只取?22KHZ?即可?,?采樣率越高所記錄下來的音質(zhì)就越清晰?,?當(dāng)然?,?越高的采樣所記錄下的文件就越大?.
??2.?采樣位
????解析度決定了采樣的音波是否能保持原來的形狀?,?越接近原型則需解析度越高?,?若以?8?位來采樣的話其能表達(dá)的組合種類是?2?的?8?次方?,?即?256,?表示用?8?位的采樣大小能分辨出?256?個(gè)層次的聲音?,?若用?16?位來采樣?,?則能分辨的差異將高達(dá)?2?的?16?次方?,?為?65536,?其精度自然大為提高?.16?位?,8?位采樣的差別在于動態(tài)范圍的寬窄?,?動態(tài)范圍寬廣?,?音量起伏的大小變化就能夠更精細(xì)的被記錄下來?,?如此一來不論是細(xì)微的聲音或是強(qiáng)烈的動感震撼?,?都可以表現(xiàn)的淋漓盡致?,?而?CD?音質(zhì)的采樣規(guī)格正式?16?位采樣的規(guī)格?.
??3.?量化誤差?(Quantization error)
???????在采樣的過程中?,?不斷連續(xù)變化的模擬信號要用數(shù)字化的數(shù)值來表示?,?這樣的過程就會發(fā)生所謂的量化誤差?(Quantization error).?所謂的量化誤差指的是實(shí)際的信號的振幅?(smplitude)?和數(shù)字化之后所的數(shù)字之間的差異?.?如果用將數(shù)字信號還原成模擬信號的角度看?,?量化誤差就是失真?(Distortion).?我們可以用增加采樣大小的方式來降低量化誤差?,?也就是更多的位?(bits)?來表示一個(gè)采樣信號?,?這樣可以提高精度?.
?4.?量化?(Quantization),?線性量化法?(Linear quantization)?和非線性量化法?(Nonlinear quantization)
???????所謂的量化?(Quantization)?就是將模擬信號所代表的連續(xù)范圍分成一段一段的區(qū)間?(Interval),?每一段區(qū)間我們定義一個(gè)數(shù)字化的值?.?區(qū)間的數(shù)目是跟采樣大小有關(guān)?,?舉例來說?,?有一種最簡單的量化法稱為?”?線性量化法?”(Linear quantization),?這種量化法采用等距離的間隔空間?,?架設(shè)一個(gè)訊號它的最大值是?5.0,?采樣大小為?3?位?,?則每個(gè)量化區(qū)間就時(shí)?5.0/2^3,?也就是?0.625?單位?.?另外一種相反的量化方法就是?”?非線性量化法?”(Nonlinear quantization),?這種量化法采用不同的間隔空間?.?以?”?對數(shù)量化法?”(Logarithm quantization)?為例?.?低振幅范圍的量化區(qū)間就比高振幅的范圍的區(qū)間較為接近?,?用這種量化的法產(chǎn)生的結(jié)果就是在低振幅時(shí)我們會得到佳好的效果?.?通常如果使用同樣的采樣大小?,?非線性量化法會比線性量化法得到更好的聲音品質(zhì)?.?但是如果是要對聲音做濾波?(filtered)?或一些運(yùn)算的時(shí)候?,?使用線性量化法會比較容易處理?.
?5.?聲音強(qiáng)度
?????????波形振幅的平方.兩個(gè)聲音強(qiáng)度上的差常以分貝?(db)?為單位來度量?,計(jì)算公式如下:
????20*log(A1/A2)?分貝?,?A1,A2?為兩個(gè)聲音的振幅?.
a.?如果采樣大小為?8?位?,則采樣的動態(tài)范圍為?20*log(256)?分貝?=48db;
b.?如果樣本大小為?16?位?,則采樣動態(tài)范圍為?20*log(65536)?大約是?96?分貝?,接近了人聽覺極限和痛苦極限,是再線音樂的理想范圍,?windows?同時(shí)支持8?位和?16?位的采樣大小?.
?
?6.?音頻編碼方法
??????目前已經(jīng)發(fā)展了許多音頻編碼的方法用以減少存儲量或是傳輸?shù)臅r(shí)間?,?以下所列為兩種較普遍的編碼方法?:
???a.PCM(Pulse code modulation);
脈沖編碼調(diào)制?,即對波形按照固定周期頻率采樣。為了保證采樣后數(shù)據(jù)質(zhì)量,采樣頻率必須是樣本聲音最高頻率的兩倍,這就是?Nyquist?頻率?.
???b.ADPCM(Adaptive delta pulse modulation).
3.?RIFF?文件結(jié)構(gòu)和?WAVE?文件格式?
?Windows?支持兩種?RIFF(Resource Interchange File Format,"?資源交互文件格式?")?格式的音頻文件:?MIDI?的?RMID?文件和波形音頻文件格式WAVE?文件,其中在計(jì)算機(jī)領(lǐng)域最常用的數(shù)字化聲音文件格式是后者,它是微軟專門為?Windows?系統(tǒng)定義的波形文件格式(?Waveform Audio?),由于其擴(kuò)展名為?"*.wav"?,因而該類文件也被稱為?WAVE?文件。?為了突出重點(diǎn),有的放矢,本文涉及到的聲音文件所指的就是?WAVE?文件。常見的?WAVE?語音文件主要有兩種,分別對應(yīng)于單聲道(?11.025KHz?采樣率、?8Bit?的采樣值)和雙聲道(?44.1KHz?采樣率、?16Bit?的采樣值)。這里的采樣率是指聲音信號在進(jìn)行?"?模→數(shù)?"?轉(zhuǎn)換過程中單位時(shí)間內(nèi)采樣的次數(shù)。采樣值是指每一次采樣周期內(nèi)聲音模擬信號的積分值。對于單聲道聲音文件,采樣數(shù)據(jù)為八位的短整數(shù)(short int 00H-FFH?);而對于雙聲道立體聲聲音文件,每次采樣數(shù)據(jù)為一個(gè)?16?位的整數(shù)(?int?),高八位和低八位分別代表左右兩個(gè)聲道。?WAVE?文件數(shù)據(jù)塊包含以脈沖編碼調(diào)制(?PCM?)格式表示的樣本。在進(jìn)行聲音編程處理以前,首先讓我們來了解一下?RIFF?文件和?WAVE?文件格式。?
?RIFF?文件結(jié)構(gòu)可以看作是樹狀結(jié)構(gòu),其基本構(gòu)成是稱為?"?塊?"?(?Chunk?)的單元,每個(gè)塊有?"?標(biāo)志符?"?、?"?數(shù)據(jù)大小?"?及?"?數(shù)據(jù)?"?所組成,塊的結(jié)構(gòu)如圖?2?所示:
?
| 塊的標(biāo)志符(?4BYTES?) |
| 數(shù)據(jù)大小?(?4BYTES?) |
| 數(shù)據(jù) |
????????????? ????????????????????????????????????????圖?2?
從上圖可以看出,其中?"?標(biāo)志符?"?為?4?個(gè)字符所組成的代碼,如?"RIFF"?,?"LIST"?等,指定塊的標(biāo)志?ID?;數(shù)據(jù)大小用來指定塊的數(shù)據(jù)域大小,它的尺寸也為?4?個(gè)字符;數(shù)據(jù)用來描述具體的聲音信號,它可以由若干個(gè)子塊構(gòu)成,一般情況下塊與塊是平行的,不能相互嵌套,但是有兩種類型的塊可以嵌套子塊,他們是?"RIFF"?或?"LIST"?標(biāo)志的塊,其中?RIFF?塊的級別最高,它可以包括?LIST?塊。另外,?RIFF?塊和?LIST?塊與其他塊不同,?RIFF?塊的數(shù)據(jù)總是以一個(gè)指定文件中數(shù)據(jù)存儲格式的四個(gè)字符碼(稱為格式類型)開始,如?WAVE?文件有一個(gè)?"WAVE"?的格式類型。?LIST?塊的數(shù)據(jù)總是以一個(gè)指定列表內(nèi)容的?4?個(gè)字符碼(稱為列表類型)開始,例如擴(kuò)展名為?".AVI"?的視頻文件就有一個(gè)?"strl"?的列表類型。?RIFF?和?LIST?的塊結(jié)構(gòu)如下:
| RIFF/LIST?標(biāo)志符 | |
| 數(shù)據(jù)?1?大小 | |
| 數(shù)據(jù)?1 | 格式?/?列表類型 |
| 數(shù)據(jù) | |
????????????????????????????????????????????????圖?3?
?WAVE?文件是非常簡單的一種?RIFF?文件,它的格式類型為?"WAVE"?。?RIFF?塊包含兩個(gè)子塊,這兩個(gè)子塊的?ID?分別是?"fmt"?和?"data",?其中?"fmt"?子塊由結(jié)構(gòu)?PCMWAVEFORMAT?所組成,其子塊的大小就是?sizeofof(PCMWAVEFORMAT),?數(shù)據(jù)組成就是?PCMWAVEFORMAT?結(jié)構(gòu)中的數(shù)據(jù)。?WAVE?文件的結(jié)構(gòu)如下圖?4?所示:
| 標(biāo)志符(?RIFF?) |
| 數(shù)據(jù)大小 |
| 格式類型(?"WAVE"?) |
| "fmt" |
| Sizeof(PCMWAVEFORMAT) |
| PCMWAVEFORMAT |
| "data" |
| 聲音數(shù)據(jù)大小 |
| 聲音數(shù)據(jù) |
????????????????????????????????????????????????????圖?4?
?PCMWAVEFORMAT?結(jié)構(gòu)定義如下:
| typedef struct
? 4.?硬件抽象層?(?HAL,?Hardware Abstraction?Layer?) ?????HAL?是一個(gè)可加載的核心模塊?(HAL.dll)?,它為運(yùn)行在?Windows NT?架構(gòu)?(?包括?WindowsNT4.0,Windows2000,WindowsXP)?上?的硬件平臺提供低級接口?,?HAL?隱藏各種與硬件有關(guān)的細(xì)節(jié)?,例如:?I/O?接口?,中斷控制器,聲卡…這樣的話如果用戶需要訪問聲卡硬件的話只能通過該聲卡的驅(qū)動程序來實(shí)現(xiàn),聲卡驅(qū)動程序再調(diào)用?HAL?中的相應(yīng)例程來實(shí)現(xiàn)?,下圖顯示了?HAL?,聲卡驅(qū)動程序,?Waveform Audio APIs?,我們的麥克錄音程序之間的關(guān)系: | |||||||||||||||||||||||||
5.?Waveform Audio
?????Waveform Audio APIs?是?Microsoft?提供給廣大?Win32?程序員用來給自己的應(yīng)用程序添加聲音支持的一套強(qiáng)大的?API,?它提供的功能如下:
?????????1.?打開?/?關(guān)閉?/?查詢聲音設(shè)備?;
????????2.?播放波形文件?;
????????3.?設(shè)置播放速度?;
?????????4.?播放進(jìn)度控制?;
?????????5.?錄音?;
?????????6.?得到當(dāng)前的播放位置?;
????????7.?調(diào)節(jié)音量?.
?
下面簡單介紹一下這套?API?提供的主要函數(shù)?:
- 打開錄音設(shè)備函數(shù)
????????????MMRESULT waveInOpen(
??????????? LPHWAVEIN phwi,?????????????????//?輸入設(shè)備句柄
????????????UINT uDeviceID,?????????????????? //?輸入設(shè)備?ID
????????????LPWAVEFORMATEX pwfx,?????? //?錄音格式指針
????????????DWORD dwCallback,??????????????//?處理?MM_WIM_***?消息的回調(diào)函數(shù)或窗??
???????????????????????????????????????????????????????//?口句柄,線程?ID
??????????? DWORD dwCallbackInstance,?
??????????? DWORD fdwOpen????????????????? //?處理消息方式的符號位
??????????);
?
- 為錄音設(shè)備準(zhǔn)備緩存函數(shù)
??????????????MMRESULT waveInPrepareHeader(? HWAVEIN hwi,?
??????????????????????????????????????????????????????????????????LPWAVEHDR pwh,
??????????????????????????????????????????????????????????????????UINT bwh );?
?
- 給輸入設(shè)備增加一個(gè)緩存
??????????????MMRESULT waveInAddBuffer(? HWAVEIN hwi,
???????????????????????????????????????????????????????????LPWAVEHDR pwh,
???????????????????????????????????????????????????????????UINT cbwh );?
?
- 開始錄音
??????????????MMRESULT waveInStart(? HWAVEIN hwi? );?
?
- 清除緩存
??????????????MMRESULT waveInUnprepareHeader( HWAVEIN hwi,
?????????????????????????????????????????????????????????????????????LPWAVEHDR pwh,
?????????????????????????????????????????????????????????????????????UINT cbwh);?
?
- 停止錄音
??????????????MMRESULT waveInReset( HWAVEIN hwi );?
?
- 關(guān)閉錄音設(shè)備
??????????????MMRESULT waveInClose( HWAVEIN hwi );?
?
- Wave_audio?數(shù)據(jù)格式
??????????????typedef struct {
???????????????????WORD? wFormatTag; //?數(shù)據(jù)格式,一般為?WAVE_FORMAT_PCM?即
???????????????????????????????????????????????????//?脈沖編碼
?????????????????? WORD? nChannels;?????//?聲道
?????????????????? DWORD nSamplesPerSec;???//?采樣頻率
?????????????????? DWORD nAvgBytesPerSec;??//?每秒數(shù)據(jù)量
?????????????????? WORD? nBlockAlign;
?????????????????? WORD? wBitsPerSample;?????//?樣本大小
?????????????????? WORD? cbSize;
????????????} WAVEFORMATEX;?
- waveform-audio?緩存格式
?????????????typedef struct {
?????????????????? LPSTR? lpData;????????????????????//?內(nèi)存指針
???????????????????DWORD? dwBufferLength;????//?長度
???????????????????DWORD? dwBytesRecorded; //?已錄音的字節(jié)長度
???????????????????DWORD? dwUser;
???????????????????DWORD? dwFlags;
???????????????????DWORD? dwLoops;??????????????//?循環(huán)次數(shù)
???????????????????struct wavehdr_tag * lpNext;
???????????????????DWORD? reserved;
?????????????} WAVEHDR;?
?
- 相關(guān)消息
?????????????MM_WIM_OPEN:?打開設(shè)備時(shí)消息,在此期間我們可以進(jìn)行一些初始化工作
?????????????MM_WIM_DATA:?當(dāng)緩存已滿或者停止錄音時(shí)的消息,處理這個(gè)消息可以對
?????????????????????????????????????緩存進(jìn)行重新分配,實(shí)現(xiàn)不限長度錄音
?????????????MM_WIM_CLOSE?:關(guān)閉錄音設(shè)備時(shí)的消息。
?
?
5. Multimedia File I/O
???????Multimedia File I/O APIs?是?Microsoft?提供的另外一套強(qiáng)大的針對媒體文件?I/O?的?API,?我們知道許多像?MediaPlay,RealOne?這樣的多媒體程序?qū)γ襟w文件的讀寫性能要求很高?,?它們幾乎要求實(shí)時(shí)的將磁盤上的媒體文件以流的形式讀入?,?但是對于一般的文件?I/O?形式如圖?1:
1.文件從磁盤上被讀入操作系統(tǒng)的File I/O的buffer;
????2.?然后拷貝到應(yīng)用程序自己的?buffer?中?;
????3.?應(yīng)用程序這時(shí)候才能讀取文件內(nèi)容?.
????????上述的過程對于多媒體應(yīng)用程序來說是低效的而且浪費(fèi)寶貴的內(nèi)存資源,如果文件和大的話勢必還要采取分段讀取等機(jī)制,?Multimedia File I/O?采取了一種直接存取機(jī)制?(?如圖?2),?使得應(yīng)用程序可以直接讀取操作系統(tǒng)的?File I/O buffer,?大大提高了效率?.?后面我們會利用此套?API實(shí)現(xiàn)錄音文件的存儲?.
?
?
6.?麥克錄音系統(tǒng)簡介
???本文實(shí)現(xiàn)的麥克錄音系統(tǒng)將具備以下功能?:
??????1.?錄制用戶通過麥克風(fēng)發(fā)出的聲音?;
????????????????這將利用到?Waveform APIs,?流程如下?:
?????????????????????a.?打開錄音設(shè)備?waveInOpen;
?????????????????????b.?準(zhǔn)備?wave?數(shù)據(jù)頭?waveInPrepareHeader;
?????????????????????c.?準(zhǔn)備數(shù)據(jù)塊?waveInAddBuffer;
?????????????????????d.?開始錄音?waveInStart;
?????????????????????e.?停止錄音?(waveInReset);
?????????????????????f.?關(guān)閉錄音設(shè)備?(waveInClose);
?????????????????????g.?當(dāng)開始錄音后當(dāng)?buffer?已滿時(shí)?,?將收到?MM_WIM_DATA?消息?,?處理該
????????????????????????消息可以保存已錄好數(shù)據(jù)?.
?
???????2.?根據(jù)用戶的聲音強(qiáng)弱動態(tài)顯示聲音波形?;
????????????這主要通過?GDI?函數(shù)來實(shí)現(xiàn)?.
??
??????3.?將用戶通過麥克風(fēng)發(fā)出的聲音錄制成?wav?文件保存?.
????????????這將利用到?Multimedia file I/O APIs.
?????????????????a?.調(diào)用?mminoOpen?函數(shù)來打開?WAVE?文件?,?獲取?HMMIO?類型的文件句柄?;?
????????????b?.根據(jù)?WAVE?文件的結(jié)構(gòu)?,?調(diào)用?mmioRead?、?mmioWrite?和?mmioSeek?函數(shù)實(shí)現(xiàn)文件的讀、寫和定位操作?;?
?? ???????????c?.調(diào)用?mmioClose?函數(shù)來關(guān)閉?WAVE?文件?.??
?
7.?麥克錄音系統(tǒng)的實(shí)現(xiàn)?(MicDemo)???
?
下面是該系統(tǒng)的界面?:
?
?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;?}?;
}?;
?
?我們有了這兩個(gè)強(qiáng)有力的類的支持就可以開始我們的編程工作了….
????1?.用VC6?++?建立一個(gè)MFC基于對話框的工程:MicDemo;
????2?.添加我們的兩個(gè)類CSoundIn,CwaveFile;????????
????3?.當(dāng)我們點(diǎn)擊開始(Start)按鈕的時(shí)候我們就要開始錄音了…
?
??????????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);
??????????????
???????????????//??設(shè)置定時(shí)器是為了畫波形用的???????
?
?????????????????SetTimer(?1?,??200???/*?start?slow?*/?,?NULL);
???????????}?
?
??
?????4?.在定時(shí)器的回調(diào)函數(shù)中畫波形.
????????????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?.點(diǎn)擊停止(Stop)按鈕的時(shí)候停止錄音和寫WAV文件
???????????????????void??CMicDemoDlg::OnStop()?
??????????????????{
????????????????????????m_btnStop.EnableWindow(FALSE);?
????????????????????????theSoundCapture().__closeMic();?
????????????????????????m_btnStart.EnableWindow(TRUE);
?????????????????}?
?
看完整段代碼你可能會很奇怪怎么在CmicDemoDlg中居然都沒有定義一個(gè)CSoundIn對象????呵呵,原因很簡單,因?yàn)樵O(shè)備的獨(dú)占性所以在一個(gè)時(shí)刻只能有一個(gè)CSoundIn對象存在(因?yàn)镃SoundIn對象需要占據(jù)錄音設(shè)備),所以我們必須限制程序員生成CSoundIn對象的數(shù)量,怎么限制呢???那就是把CSoundIn的構(gòu)造函數(shù)放在private區(qū)域里面:
???
??????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();??????????
?
這樣的話就根本無法聲明一個(gè)CSoundIn對象,不信你試一下在你的代碼中寫上:
?????CSoundIn??soundInObj;
?能編譯通過嗎???肯定是不能,那如何調(diào)用CSoundIn的成員函數(shù)呢???答案是通過一個(gè)全局函數(shù):
?
???????????????//??global?function,:-(
???????????????//??client?can?only?through?this?function?to?use?CSoundIn?object?
?
??????????????CSoundIn??&??theSoundCapture()
???????????????{
??????????????????????static??CSoundIn?p;
??????????????????????return??p;
???????????????}?
?
?
??這時(shí)候你應(yīng)該明白了為什么上面的代碼中調(diào)用CSoundIn的成員函數(shù)的時(shí)候都是用theSoundCapture來做的原因了吧.?
轉(zhuǎn)載于:https://www.cnblogs.com/huty/p/8518458.html
總結(jié)
以上是生活随笔為你收集整理的【VS开发】【智能语音处理】Windows下麦克风语音采集的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 求高手指点这两个男优的名字 ?
- 下一篇: 简单的python服务器程序