日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Waveform Audio 驱动(Wavedev2)之:WAV API模拟

發布時間:2023/12/15 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Waveform Audio 驱动(Wavedev2)之:WAV API模拟 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Waveform?Audio? 驅動(Wavedev2)之:WAV?API模擬

?

Waveform?驅動對Windows?Mobile來說是一個非常重要的驅動,控制著所有有關聲音的操作,包括喇叭、耳機、麥克、聽筒等。
????要 想對驅動的整個架構和流程都非常的了解,我們必須從上層來入手,需要知道上層的API是如何調用到驅動的,其數據結構是如何封裝的。由于微軟不提供中間層 的代碼,只能只是自己去猜測。這篇文章就是去模仿WAV?API的實現方法的。順便提及下,之前幾個開發人員還討論過微軟的半開放模式和Android的 完全開源模式哪個更好。先做個總結。

完全開源優點:

1.? 添 加新功能容易:比如做Android雙卡雙待就比Windows?Mobile容易的多,之前做Windows?Mobile雙卡的項目時,那真是非常的 痛苦,微軟沒有接口,只能自己想盡一起方法往微軟原有的程序中去插入新的功能,想COM接口,Dll注入,窗口Hook等等,能用的變態方法都用上了。花 的時間的很大部分都是在尋找插入功能的方法上,而不是實現另一張卡的功能上。而Android就十分簡單了,直接在原有的代碼上增加代碼就行。

2.? 開發人員很容易了解整個架構和流程

微軟的半開放模式優點:

1.? 易 維護:?由于微軟的中間層都是以dll形式封裝好的,開發人員不能去修改,只能按照微軟的接口去做,當微軟從Windows?Mobile?5.0升級到 Windows?Mobile?6.0的時候,BSP不需要做任何修改就可以在新的系統上用,軟件也是如此。而Android的完全開源模式,開發人員會 去修改中間層,Android的版本號從1.5,1.6,2.0再到2.1,不斷的進行升級,其中間層也在改變中,添加了某些功能,優化了某些部分。像我 們公司做Android的從1.5升級到1.6就花了很長的時間。不僅驅動要修改,應用也都需要做修改。

先不談這個,回到正題。

????微軟上層的WAV?API分為waveOut和waveIn兩套,表一中,我只列了部分的wave?out?API。由于wave?In相對于wave?Out比較簡單,wave?In就不做講解了。

?

waveOutGetNumDevs ?

Retrieves?the?number?of?waveform?output?devices?present?in?the?system.

waveOutGetPitch ?

Queries?the?current?pitch?setting?of?a?waveform?output?device.

waveOutGetPlaybackRate ?

Queries?the?current?playback?rate?setting?of?a?waveform?output?device.

waveOutGetPosition ?

Retrieves?the?current?playback?position?of?the?specified?waveform?output?device.

waveOutGetProperty ?

Queries?the?value?of?a?specific?property?in?a?property?set?for?waveform?audio?output.

waveOutGetVolume ?

Queries?the?current?volume?setting?of?a?waveform?output?device.

waveOutMessage ?

Sends?messages?to?the?waveform?output?device?drivers.

waveOutOpen ?

Opens?a?specified?waveform?output?device?for?playback.

表一:WaveOut部分函數

W ave?Out?API是如何調用的驅動部分的呢?現在就來一步步的模擬來實現wave?Out?API。先看下waveOutOpen的函數參數

?

view plain copy to clipboard print ?
  • MMRESULT?waveOutOpen(??
  • ??LPHWAVEOUT?phwo,??
  • ??UINT ?uDeviceID,??
  • ??LPWAVEFORMATEX?pwfx,??
  • ??DWORD ?dwCallback,??
  • ??DWORD ?dwInstance,??
  • ??DWORD ?fdwOpen??
  • ??
  • );??
  • MMRESULT waveOutOpen(LPHWAVEOUT phwo,UINT uDeviceID,LPWAVEFORMATEX pwfx,DWORD dwCallback,DWORD dwInstance,DWORD fdwOpen );

    其中phwo是我們要返回的WAVEOUT對象的句柄, uDeviceID 指設置的ID號,一般情況下設置為0就可以,pwfx是聲音格式的描述,dwCallback的通知,可以是回調函數,也可以是事件或者窗體消息,主要通過fdwOpen來指定其類型。具體看waveOutOpen的SDK幫助文檔。

    在WaveApi中的工作就是把waveOutOpen中的參數封裝起來,然后發到Wave驅動中想要的結構,下面是waveOutOpen的調用流程。

    1.? W ave?Api中封裝結構

    2.? 調用Wavedev2的? WAV_IOControl 函數,調用 IOCTL_WAV_MESSAGE 分支。

    3.? 調用 HandleWaveMessage WODM_OPEN 分支

    HandleWaveMessage 需要傳入兩個參數,其中一個是 PMMDRV_MESSAGE_PARAMS ,另一個是函數執行的結果pdwResult,見 HandleWaveMessage 原型和 MMDRV_MESSAGE_PARAMS 結構體定義。

    BOOL?HandleWaveMessage(PMMDRV_MESSAGE_PARAMS?pParams,?DWORD?*pdwResult)

    ?

    view plain copy to clipboard print ?
  • BOOL ?HandleWaveMessage(PMMDRV_MESSAGE_PARAMS?pParams,? DWORD ?*pdwResult)??
  • typedef ? struct ?{??
  • ????UINT ?uDeviceId;??
  • ????UINT ?uMsg;??
  • ????DWORD ?dwUser;??
  • ????DWORD ?dwParam1;??
  • ????DWORD ?dwParam2;??
  • }?MMDRV_MESSAGE_PARAMS,?*PMMDRV_MESSAGE_PARAMS;??
  • MMRESULT?waveOutMessage(??
  • ??HWAVEOUT?hwo,???
  • ??UINT ?uMsg,???
  • ??DWORD ?dw1,???
  • ??DWORD ?dw2???
  • );???
  • BOOL HandleWaveMessage(PMMDRV_MESSAGE_PARAMS pParams, DWORD *pdwResult) typedef struct {UINT uDeviceId;UINT uMsg;DWORD dwUser;DWORD dwParam1;DWORD dwParam2; } MMDRV_MESSAGE_PARAMS, *PMMDRV_MESSAGE_PARAMS; MMRESULT waveOutMessage(HWAVEOUT hwo, UINT uMsg, DWORD dw1, DWORD dw2 );

    其 中參數dwUser指向Wavedev2驅動的StreamContext對象指針,如果調用的是waveOutOpen,則dwUser做出傳出參數, 來保存StreamContext對象,否則就是作為傳入參數。waveOutMessage的uMsg會傳入驅動變成 MDRV_MESSAGE_PARAMS 中的uMsg,?同樣的dw1變dwParam1,dw2變dwParam2,所以的上層調用都是調用 waveOutMessage 這個函數實現的。

    ?

    好了,現在我們來開始顯示吧。 HWAVEOUT 要么直接指向對象,要么是對象的在數組中的索引。我們只是模擬,所以把 HWAVEOUT 直接指向對象。

    先定義一個 CWAVEOut 對象,來保存必要的數據,其他幾個參數就不做解釋了,我們看m_hWave和m_pStream,m_hWave是來保存打開Wave驅動CreateFile返回的句柄,而m_pStream是保存創建的StreamContext對象的。

    ?

    view plain copy to clipboard print ?
  • class ?CWAVEOut??
  • {??
  • public :??
  • ????MMRESULT?open();??
  • ??
  • private :??
  • ????DWORD ?m_dwCallback;??
  • ????DWORD ?m_dwInstance;??
  • ????UINT ?m_uDeviceID;??
  • ????DWORD ?m_fdwOpen;??
  • ????WAVEFORMATEX?m_wfx;??
  • ????HANDLE ?m_hWave;??
  • ????LPVOID ??m_pStream;??
  • };??
  • class CWAVEOut { public:MMRESULT open(); private:DWORD m_dwCallback;DWORD m_dwInstance;UINT m_uDeviceID;DWORD m_fdwOpen;WAVEFORMATEX m_wfx;HANDLE m_hWave;LPVOID m_pStream; };

    好,現在我們來模擬 waveOutOpen 的實現。在waveOutOpen中,只是新建一個CWAVEOut對象,然后把外部傳入的數據保存到這個對象中,最后調用open來打開音頻設備。代碼如下:

    ?

    view plain copy to clipboard print ?
  • MMRESULT?waveOutOpen(??
  • ?????????????????????LPHWAVEOUT?phwo,??
  • ?????????????????????UINT ?uDeviceID,??
  • ?????????????????????LPWAVEFORMATEX?pwfx,??
  • ?????????????????????DWORD ?dwCallback,??
  • ?????????????????????DWORD ?dwInstance,??
  • ?????????????????????DWORD ?fdwOpen??
  • ?????????????????????)??
  • {??
  • ????CWAVEOut?pWaveOut?=?new ?CWAVEOut;??
  • ????pWaveOut->m_dwCallback?=?dwCallback;??
  • ????pWaveOut->m_fdwOpen?=?fdwOpen;??
  • ????pWaveOut->m_wfx?=?*pwfx;??
  • ??????
  • ????*pwfx?=?pWaveOut;??
  • ??????
  • ????phwo?=?(LPHWAVEOUT)pWaveOut;??
  • ????return ?pWaveOut->open();??
  • }??
  • MMRESULT waveOutOpen(LPHWAVEOUT phwo,UINT uDeviceID,LPWAVEFORMATEX pwfx,DWORD dwCallback,DWORD dwInstance,DWORD fdwOpen) {CWAVEOut pWaveOut = new CWAVEOut;pWaveOut->m_dwCallback = dwCallback;pWaveOut->m_fdwOpen = fdwOpen;pWaveOut->m_wfx = *pwfx;*pwfx = pWaveOut;phwo = (LPHWAVEOUT)pWaveOut;return pWaveOut->open(); }

    O pen函數封裝 WAVEOPENDESC 作為waveOutMessage的第一個傳入參數,第二個參數是m_fdwOpen。

    ?

    view plain copy to clipboard print ?
  • MMRESULT?CWAVEOut::open()??
  • {??
  • ????MMRESULT?mmResult;??
  • ????m_hWave?=?CreateFile(L"WAV1:" ,?GENERIC_READ?|?GENERIC_WRITE,?FILE_SHARE_READ?|?FILE_SHARE_WRITE,???
  • ????????NULL,?OPEN_EXISTING,?0,?NULL);??
  • ??????
  • ????WAVEOPENDESC?waveOpenDesc;??
  • ????waveOpenDesc.hWave?=?HWAVE(this );??
  • ????waveOpenDesc.lpFormat?=?&m_wfx;??
  • ????waveOpenDesc.dwInstance?=?m_dwInstance;??
  • ????waveOpenDesc.uMappedDeviceID?=?0;??
  • ????waveOpenDesc.dwCallback?=?m_dwCallback;??
  • ??
  • ????return ?waveOutMessage((HWAVEOUT) this ,?WODM_OPEN,?&waveOpenDesc,?m_fdwOpen);??
  • }??
  • MMRESULT CWAVEOut::open() {MMRESULT mmResult;m_hWave = CreateFile(L"WAV1:", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);WAVEOPENDESC waveOpenDesc;waveOpenDesc.hWave = HWAVE(this);waveOpenDesc.lpFormat = &m_wfx;waveOpenDesc.dwInstance = m_dwInstance;waveOpenDesc.uMappedDeviceID = 0;waveOpenDesc.dwCallback = m_dwCallback;return waveOutMessage((HWAVEOUT)this, WODM_OPEN, &waveOpenDesc, m_fdwOpen); }

    waveOutMessage的工作就是只要把uMsg,dw1,dw2封裝到 MMDRV_MESSAGE_PARAMS 結構體,然后調用 DeviceIoControl 調用驅動的IO?Control。這里有一點需要注意,如果uMsg是WODM_OPEN,也就是打開音頻流的操作的時候,把 & pWaveOut -> m_pStream 作為參數傳入,因為在底層通過調用 OpenStream ,傳入指針的指針,來保存對象的。

    pDeviceContext->OpenStream((LPWAVEOPENDESC)dwParam1,?dwParam2,?(StreamContext?**)dwUser);

    ?

    ?

    ?

    view plain copy to clipboard print ?
  • MMRESULT?waveOutMessage(??
  • ????????????????????????HWAVEOUT?hwo,???
  • ????????????????????????UINT ?uMsg,???
  • ????????????????????????DWORD ?dw1,???
  • ????????????????????????DWORD ?dw2???
  • ????????????????????????)??
  • {??
  • ????CWAVEOut?*?pWaveOut?=?(CWAVEOut?*)hwo;??
  • ????MMDRV_MESSAGE_PARAMS?paramInput;??
  • ????paramInput.dwParam1?=?dw1;??
  • ????paramInput.dwParam2?=?dw2;??
  • ??????
  • ????if (WODM_OPEN?==?uMsg)??
  • ????????paramInput.dwUser?=?(DWORD )&pWaveOut->m_pStream;??
  • ????else ??
  • ????????paramInput.dwUser?=?(DWORD )pWaveOut->m_pStream;??
  • ??????
  • ????paramInput.uMsg?=?uMsg;??
  • ??
  • ????MMRESULT????dwOutput?=?0;??
  • ??????
  • ????if (!DeviceIoControl(pWaveOut->m_hWave,?IOCTL_WAV_MESSAGE,??mInput,? sizeof (paramInput),?&dwOutput,? sizeof (dwOutput),?NULL,?NULL))??
  • ????{??
  • ????????return ?MMSYSERR_ERROR;??
  • ????}??
  • ??????
  • ????return ?dwOutput;??
  • }??
  • MMRESULT waveOutMessage( HWAVEOUT hwo, UINT uMsg, DWORD dw1, DWORD dw2 ) { CWAVEOut * pWaveOut = (CWAVEOut *)hwo; MMDRV_MESSAGE_PARAMS paramInput; paramInput.dwParam1 = dw1; paramInput.dwParam2 = dw2; if(WODM_OPEN == uMsg) paramInput.dwUser = (DWORD)&pWaveOut->m_pStream; else paramInput.dwUser = (DWORD)pWaveOut->m_pStream; paramInput.uMsg = uMsg; MMRESULT dwOutput = 0; if(!DeviceIoControl(pWaveOut->m_hWave, IOCTL_WAV_MESSAGE, ?mInput, sizeof(paramInput), &dwOutput, sizeof(dwOutput), NULL, NULL)) { return MMSYSERR_ERROR; } return dwOutput; }

    我們現在模擬實現了waveOutMessage,那么其他一些函數的實現要比waveOutOpen更加的簡單。如WaveOutReset和 waveOutSetVolume ,只要調用下 waveOutMessage 就可以了。

    view plain copy to clipboard print ?
  • MMRESULT?waveOutReset(??
  • ??????????????????????HWAVEOUT?hwo???
  • ??????????????????????)??
  • {??
  • ????return ?waveOutMessage(hwo,?WODM_RESET,?0,?0);??
  • }??
  • ??
  • MMRESULT?waveOutSetVolume(??
  • ??????????????????????????HWAVEOUT?hwo,???
  • ??????????????????????????DWORD ?dwVolume???
  • ??????????????????????????)??
  • {??
  • ????return ?waveOutMessage(hwo,?WODM_SETVOLUME,?dwVolume,?0);??
  • }??
  • MMRESULT waveOutReset(HWAVEOUT hwo ) {return waveOutMessage(hwo, WODM_RESET, 0, 0); } MMRESULT waveOutSetVolume(HWAVEOUT hwo, DWORD dwVolume ) {return waveOutMessage(hwo, WODM_SETVOLUME, dwVolume, 0); }

    對于上層來說,只是簡單的進行了下封裝。當然我的封裝里面還沒有考慮到具體的一些東西,如callback函數是怎么返回的,如函數調用是hwo為空,是怎么樣的,也沒有對錯誤進行處理。

    下面是播放一個wave聲音的函數,從代碼中去解析

    ?

    ?

    view plain copy to clipboard print ?
  • MMRESULT??
  • PlayFile(LPCTSTR ?pszFilename)??
  • {?MMRESULT?mr;??
  • ??DWORD ?dwBufferSize;??
  • ??PBYTE ?pBufferBits?=?NULL;??
  • ??PWAVEFORMATEX?pwfx?=?NULL;??
  • ??DWORD ?dwSlop;??
  • ??DWORD ?dwWait;??
  • ??DWORD ?dwDuration;??
  • ??
  • ????HANDLE ?hevDone?=?CreateEvent(NULL,?FALSE,?FALSE,?NULL);??
  • ????if ?(hevDone?==?NULL)?{??
  • ????????return ?MMSYSERR_NOMEM;??
  • ????}??
  • ??
  • ????mr?=?ReadWaveFile(pszFilename,&pwfx,&dwBufferSize,&pBufferBits);??
  • ????MRCHECK(mr,?ReadWaveFile,?ERROR_READ);??
  • ??
  • ????//?Note:?Cast?to?UINT64?below?is?to?avoid?potential?DWORD?overflow?for?large?(>~4MB)?files. ??
  • ????dwDuration?=?(DWORD )((( UINT64 )dwBufferSize)?*?1000?/?pwfx->nAvgBytesPerSec);??
  • ??
  • ????HWAVEOUT?hwo;??
  • ????mr?=?waveOutOpen(&hwo,?WAVE_MAPPER,?pwfx,?(DWORD )?hevDone,?NULL,?CALLBACK_EVENT);??
  • ????MRCHECK(mr,?waveOutOpen,?ERROR_OPEN);??
  • ??
  • ????WAVEHDR?hdr;??
  • ????memset(&hdr,?0,?sizeof (hdr));??
  • ????hdr.dwBufferLength?=?dwBufferSize;??
  • ????hdr.lpData?=?(char ?*)?pBufferBits;??
  • ??
  • ????mr?=?waveOutPrepareHeader(hwo,?&hdr,?sizeof (hdr));??
  • ????MRCHECK(mr,?waveOutPrepareHeader,?ERROR_PLAY);??
  • ??
  • ????mr?=?waveOutWrite(hwo,?&hdr,?sizeof (hdr));??
  • ????MRCHECK(mr,?waveOutWrite,?ERROR_PLAY);??
  • ??
  • ????//?wait?for?play?+?1?second?slop ??
  • ????dwSlop?=?1000;??
  • ????dwWait?=?WaitForSingleObject(hevDone,?dwDuration?+?dwSlop);??
  • ????if ?(dwWait?!=?WAIT_OBJECT_0)?{??
  • ????????//?not?much?to?here,?other?than?issue?a?warning ??
  • ????????RETAILMSG(1,?(TEXT("Timeout?waiting?for?playback?to?complete/r/n" )));??
  • ????}??
  • ??
  • ????mr?=?waveOutUnprepareHeader(hwo,?&hdr,?sizeof (hdr));??
  • ????MRCHECK(mr,?waveOutUnprepareHeader,?ERROR_PLAY);??
  • ??
  • ERROR_PLAY:??
  • ????mr?=?waveOutClose(hwo);??
  • ????MRCHECK(mr,?waveOutClose,?ERROR_OPEN);??
  • ??
  • ERROR_OPEN:??
  • ????delete ?[]?pBufferBits;??
  • ????delete ?[]?pwfx;??
  • ??
  • ERROR_READ:??
  • ????CloseHandle(hevDone);??
  • ????return ?mr;??
  • }??
  • MMRESULT PlayFile(LPCTSTR pszFilename) { MMRESULT mr;DWORD dwBufferSize;PBYTE pBufferBits = NULL;PWAVEFORMATEX pwfx = NULL;DWORD dwSlop;DWORD dwWait;DWORD dwDuration;HANDLE hevDone = CreateEvent(NULL, FALSE, FALSE, NULL);if (hevDone == NULL) {return MMSYSERR_NOMEM;}mr = ReadWaveFile(pszFilename,&pwfx,&dwBufferSize,&pBufferBits);MRCHECK(mr, ReadWaveFile, ERROR_READ);// Note: Cast to UINT64 below is to avoid potential DWORD overflow for large (>~4MB) files.dwDuration = (DWORD)(((UINT64)dwBufferSize) * 1000 / pwfx->nAvgBytesPerSec);HWAVEOUT hwo;mr = waveOutOpen(&hwo, WAVE_MAPPER, pwfx, (DWORD) hevDone, NULL, CALLBACK_EVENT);MRCHECK(mr, waveOutOpen, ERROR_OPEN);WAVEHDR hdr;memset(&hdr, 0, sizeof(hdr));hdr.dwBufferLength = dwBufferSize;hdr.lpData = (char *) pBufferBits;mr = waveOutPrepareHeader(hwo, &hdr, sizeof(hdr));MRCHECK(mr, waveOutPrepareHeader, ERROR_PLAY);mr = waveOutWrite(hwo, &hdr, sizeof(hdr));MRCHECK(mr, waveOutWrite, ERROR_PLAY);// wait for play + 1 second slopdwSlop = 1000;dwWait = WaitForSingleObject(hevDone, dwDuration + dwSlop);if (dwWait != WAIT_OBJECT_0) {// not much to here, other than issue a warningRETAILMSG(1, (TEXT("Timeout waiting for playback to complete/r/n")));}mr = waveOutUnprepareHeader(hwo, &hdr, sizeof(hdr));MRCHECK(mr, waveOutUnprepareHeader, ERROR_PLAY); ERROR_PLAY:mr = waveOutClose(hwo);MRCHECK(mr, waveOutClose, ERROR_OPEN); ERROR_OPEN:delete [] pBufferBits;delete [] pwfx; ERROR_READ:CloseHandle(hevDone);return mr; }

    先調用waveOutOpen初始化音頻流。在調用waveOutPrepareHeader準備好數據頭,告訴驅動要播放多大的數據,在驅動中 waveOutPrepareHeader ? 調用 WODM_PREPARE 分支,一般情況下驅動沒有去實現 WODM_PREPARE ,直接返回 MMSYSERR_NOTSUPPORTED 。準備好Header后,調用waveOutWrite寫出buffer。

    ?

    好了,就寫到這里,如有錯誤之處,請更正。

    總結

    以上是生活随笔為你收集整理的Waveform Audio 驱动(Wavedev2)之:WAV API模拟的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。