#include<mmsystem.h>
#include<mmreg.h>
#pragma comment(lib, "winmm.lib")
由于音頻采集過程是一個持續過程,是以建議為它們各自配置設定一個線程,而使用mfc的 cwinthread 類是一個不錯的選擇,筆者就是利用cwinthread類将這兩個功能封裝成了兩個獨立的類,為以後的使用提供了很大的便利性。筆者在此為讀者提供本人寫好的一個工程,此工程為視訊語音采集的不完善版,目前實作語音本地采集與播放,vfw視訊采集與顯示(視訊不清晰),在後續章節會将vfw視訊采集進行總結,敬請期待。。。。。 工程下載下傳位址: 中選擇 videoplay.rar 下載下傳,此項目是用vs2010編譯
1、配置設定資料buffer,通過wavehdr結構體儲存,準備存儲采集到的音頻資料,此處應該根據采集頻率設定足量的buffer void crecodesound::precreateheader() { for(int i=0;i<maxrecbuffer;i++) m_rechead[i]=createwaveheader(); m_isallocated = 1; } lpwavehdr crecodesound::createwaveheader() lpwavehdr lphdr = new wavehdr; if(lphdr==null) { m_recodelog.writestring(text("\n unable to allocate the memory")); return null; } zeromemory(lphdr, sizeof(wavehdr)); char* lpbyte = new char[recbuffer];//m_waveformatex.nblockalign*soundsamples)]; if(lpbyte==null) lphdr->lpdata = lpbyte; lphdr->dwbufferlength =recbuffer; // (m_waveformatex.nblockalign*soundsamples); return lphdr; 2、初始化音頻格式結構體 waveformatex。 memset(&m_waveformatex, 0, sizeof(m_waveformatex)); m_waveformatex.wformattag = wave_format_pcm;//聲音格式為pcm m_waveformatex.nchannels 1; //采樣聲道數,對于單聲道音頻設定為1,立體聲設定為2 m_waveformatex.wbitspersample = 8;//采樣比特 8bits/次 m_waveformatex.cbsize = 0;//一般為0 m_waveformatex.nsamplespersec = 8000; //采樣率 16000 次/秒 m_waveformatex.nblockalign = 1; //一個塊的大小,采樣bit的位元組數乘以聲道數 m_waveformatex.navgbytespersec = 8000; //每秒的資料率,就是每秒能采集多少位元組的資料 3、waveinopen打開音頻輸入裝置準備開始采集 //開啟音頻采集 mmresult mmreturn = ::waveinopen( &m_hrecord, wave_mapper, &m_waveformatex, ::getcurrentthreadid(), 0, callback_thread); //error has occured while opening device if(mmreturn != mmsyserr_noerror ) //打開采集失敗 displayerror(mmreturn,"open"); return ;//false; 4、waveinprepareheader 和 waveinaddbuffer 配合将準備好的buffer提供給裝置 //将準備好的buffer提供給音頻輸入裝置 for(int i=0; i < maxrecbuffer ; i++) //準備一個bufrer給輸入裝置 mmreturn ::waveinprepareheader(m_hrecord,m_rechead[i], sizeof(wavehdr)); //發送一個buffer給指定的輸入裝置,當buffer填滿将會通知程式 = ::waveinaddbuffer(m_hrecord, m_rechead[i], sizeof(wavehdr)); 5、waveinstart正式開始采集 //開啟指定的輸入采集裝置 mmreturn = ::waveinstart(m_hrecord); if(mmreturn!=mmsyserr_noerror ) //開始采集失敗 displayerror(mmreturn,"start"); else m_isrecoding = true; 6、每當一個buffer資料填滿時,會觸發 mm_wim_data 消息,在程式中捕獲此消息,通過消息傳遞過來的 lparam,為指向資料buffer的wavehdr指針。采集到此資料時可以根據程式需要對其做相應的處理。本程式是直接将采集到的資料提供給播放線程直接播放,你也可以通過socket發送到遠端在播放,就可以網絡語音了。 void crecodesound::onsounddata(wparam wparam, lparam lparam) m_recodelog.writestring(text("\nin the onsound data")); if(m_isrecoding==false) //如果目前不在采集狀态 return ;//false; lpwavehdr lphdr = (lpwavehdr) lparam; if(lphdr->dwbytesrecorded==0 || lphdr==null) return ;//error_success; //使采集過程,知道此buffer已經沾滿,不能再填充 ::waveinunprepareheader(m_hrecord, lphdr, sizeof(wavehdr)); //将采集到的聲音發送給播放線程 ((cvideoplaydlg *)m_pdlg)->m_pplaysound->postthreadmessage(wm_playsound_playblock, lphdr->dwbytesrecorded, (lparam)lphdr->lpdata); // send recorded audio to remote host… /* if(lphdr->lpdata!=null ) ( (cvideonetdlg*) dlg )->daudio.sendaudiodata((unsigned char *)lphdr->lpdata,lphdr->dwbytesrecorded); */ if(m_isrecoding) //重新将buffer恢複到準備填充狀态 ::waveinprepareheader(m_hrecord, ::waveinaddbuffer(m_hrecord, 7、在要停止采集是使用waveinstop停止采集資料。 mmreturn = ::waveinstop(m_hrecord); 8、停止采內建功,立即waveinreset重置裝置,重置裝置将會導緻所有的采集buffer回報給程式。 if(!mmreturn) //停止采內建功,立即重置裝置,重置裝置将會導緻所有的buffer回報給程式 = false; mmreturn = ::waveinreset(m_hrecord); //重置裝置 9、延時一段時間,等待所有的資料buffer都被程式處理完成 sleep(500); //等待一段時間,使buffer回報完成 10、waveinclose關閉裝置 if(!mmreturn) //重置裝置成功,立即關閉裝置 mmreturn = ::waveinclose(m_hrecord); //關閉裝置
1、初始化音頻資料格式結構體 waveformatex //初始化音頻格式結構體 wave_format_pcm; m_waveformatex.nchannels = m_waveformatex.wbitspersample = 8; 0; m_waveformatex.nsamplespersec = 8000; = 8000 ; m_waveformatex.nblockalign = 1; 2、打開音頻輸出裝置 //打開音頻輸出裝置 ::waveoutopen( &m_hplay, wave_mapper, &m_waveformatex, 3、設定音頻輸出音量 if(mmreturn) //打開裝置失敗 displayerror(mmreturn,"playstart"); { m_isplaying dword volume = 0xffffffff; waveoutsetvolume(m_hplay, volume);//設定輸出裝置的輸出量
4、等待要輸出的資料,通過waveoutprepareheader将資料送出給裝置準備輸出,通過waveoutwrite将送出給裝置的資料輸出。 void cplaysound::onwritesounddata(wparam wparam, lparam mmresult mmresult = int length=(int) wparam; if(m_isplaying == false) return ; //false; m_playlog.writestring(text("\nplaying sound data….")); // prepare wave header for playing wavehdr *lphdr=new wavehdr; memset(lphdr,0,sizeof(wavehdr)); lphdr->lpdata=(char *)lparam; lphdr->dwbufferlength=length; //将要輸出的資料寫入buffer mmresult = ::waveoutprepareheader(m_hplay, if(mmresult) m_playlog.writestring(text("\nerror while preparing header")); //将輸出資料發送給輸出裝置 = ::waveoutwrite(m_hplay, lphdr, sizeof(wavehdr)); while writing to device")); return ;//error_success; return ;//error_success; 5、當送出給裝置的資料輸出結束,裝置會發送一條mm_wom_done消息回報給裝置,裝置應該用waveoutunprepareheader将送出給裝置輸出的資料清除。 void cplaysound::onendplaysounddata(wparam wparam, lparam lparam) lpwavehdr lphdr = (lpwavehdr) lparam; if(lphdr) ::waveoutunprepareheader(m_hplay, lphdr, sizeof(wavehdr));//音頻輸出結束,清空buffer 6、結束輸出前先用waveoutreset重置輸出裝置,重置能夠使輸出裝置全部buffer輸出結束,是以在waveoutreset後要延遲一段時間,然後調用waveoutclose關閉裝置。 void cplaysound::onstopplaying(wparam wparam, lparam mmresult mmreturn = 0; if(m_isplaying == return;// false; m_playlog.writestring(text("\n stopped playing")); ::waveoutreset(m_hplay);//重置輸出裝置,重置能夠使輸出裝置全部buffer輸出結束 if(!mmreturn) m_isplaying sleep(500); //等待所有buffer輸出完成 ::waveoutclose(m_hplay);//關閉裝置 from: