C语言里的out函数,c语言 vc 用waveout函数写wave文件播放器
用WaveOut函數(shù)寫(xiě)wave文件播放器
要炒菜的話(huà),就得先準(zhǔn)備工具,如鍋、鏟子、爐灶等。對(duì)程序來(lái)說(shuō),就是各種函數(shù)的應(yīng)用。WaveOut函數(shù)在windowsAPI中屬于低階接口,用來(lái)播放的話(huà)需要用到下面幾個(gè):
waveOutOpen – 打開(kāi)波形輸出設(shè)備
waveOutPrepareHeader – 準(zhǔn)備播放緩沖區(qū)
waveOutUnprepareHeader – 取消播放緩沖區(qū)
waveOutWrite – 將數(shù)據(jù)寫(xiě)入波形輸出設(shè)備
waveOutReset – 波形輸出設(shè)備復(fù)位(清除正在播放的數(shù)據(jù),停止播放)
waveOutPause – 波形輸出設(shè)備暫停(暫停播放)
waveOutRestart – 波形輸出設(shè)備恢復(fù)(繼續(xù)播放)
waveOutClose – 關(guān)閉波形輸出設(shè)備
播放時(shí)使用的順序大致如下:
waveOutOpen 打開(kāi)設(shè)備
waveOutPrepareHeader 準(zhǔn)備緩沖區(qū)
waveOutWrite 寫(xiě)入波形設(shè)備
waveOutReset 波形設(shè)備復(fù)位
waveOutClose 關(guān)閉波形設(shè)備
至于暫停就更簡(jiǎn)單,播放時(shí)執(zhí)行waveOutPause時(shí)暫停播放,再執(zhí)行waveOutRestart時(shí)繼續(xù)播放。
現(xiàn)在工具已經(jīng)齊備了,下來(lái)就是準(zhǔn)備材料了。對(duì)于這個(gè)播放器來(lái)說(shuō),最重要的材料是RIFF檔案、WAVEFORMATEX和WAVEHDR這三個(gè)結(jié)構(gòu)。下面就簡(jiǎn)單介紹一下這三個(gè)結(jié)構(gòu):
RIFF全稱(chēng)為資源互換文件格式(ResourcesInterchange FileFormat),RIFF文件是windows環(huán)境下大部分多媒體文件遵循的一種文件結(jié)構(gòu),RIFF文件所包含的數(shù)據(jù)類(lèi)型由該文件的擴(kuò)展名來(lái)標(biāo)識(shí),能以RIFF文件存儲(chǔ)的數(shù)據(jù)包括:音頻視頻交錯(cuò)格式數(shù)據(jù)(.AVI) 波形格式數(shù)據(jù)(.WAV) 位圖格式數(shù)據(jù)(.RDI) MIDI格式數(shù)據(jù)(.RMI)調(diào)色板格式(.PAL)多媒體電影(.RMN)動(dòng)畫(huà)光標(biāo)(.ANI)其它RIFF文件(.BND) 。具體格式如下:
WAV文件的基本格式
內(nèi)容
變量名
大小
取值
RIFF頭
文件標(biāo)識(shí)符串
fileId
4B
“RIFF”
頭后文件長(zhǎng)度
fileLen
4B
非負(fù)整數(shù)(=文件長(zhǎng)度-8)
數(shù)據(jù)類(lèi)型標(biāo)識(shí)符
波形文件標(biāo)識(shí)符
waveId
4B
“WAVE”
格式塊
塊頭
格式塊標(biāo)識(shí)符串
chkId
4B
“fmt ”
頭后塊長(zhǎng)度
chkLen
4B
非負(fù)整數(shù)(= 16或18)
塊數(shù)據(jù)
格式標(biāo)記
wFormatTag
2B
非負(fù)短整數(shù)(PCM=1)
聲道數(shù)
wChannels
2B
非負(fù)短整數(shù)(= 1或2)
采樣率
dwSampleRate
4B
非負(fù)整數(shù)(單聲道采樣數(shù)/秒)
平均字節(jié)率
dwAvgBytesRate
4B
非負(fù)整數(shù)(字節(jié)數(shù)/秒)
數(shù)據(jù)塊對(duì)齊
wBlockAlign
2B
非負(fù)短整數(shù)(不足補(bǔ)零)
采樣位數(shù)
wBitsPerSample
2B
非負(fù)短整數(shù)(PCM時(shí)才有)
擴(kuò)展域大小
wExtSize
2B
非負(fù)短整數(shù)
可選(根據(jù)chkLen=16 or 18判斷)
擴(kuò)展域
extraInfo
extSize B
擴(kuò)展信息
數(shù)據(jù)塊
塊頭
數(shù)據(jù)塊標(biāo)識(shí)符串
chkId
4B
“data”
頭后塊長(zhǎng)度
chkLen
4B
非負(fù)整數(shù)
塊數(shù)據(jù)
波形采樣數(shù)據(jù)
x或xl、xr
chkLen B
左右聲道樣本交叉排列
樣本值為整數(shù)(整字節(jié)存儲(chǔ),不足位補(bǔ)零),
整個(gè)數(shù)據(jù)塊按blockAlign對(duì)齊
注意:波形聲音檔案以文字字串「RIFF」開(kāi)始,用來(lái)標(biāo)識(shí)這是一個(gè) RIFF 檔案。字串後面是一個(gè) 32 位元的資料塊大小,表示檔案其余部分的大小,或者是小於 8位元組的檔案大小。 資料塊以文字字串「WAVE」開(kāi)始,用來(lái)標(biāo)識(shí)這是一個(gè)波形聲音塊,後面是文字字串「fmt」——注意用空白使之成為 4 字元的字串——用來(lái)標(biāo)識(shí)包含波形聲音資料格式的子資料塊。「fmt」字串的後面是格式資訊大小,這里是 16 位元組。格式資訊是 WAVEFORMATEX 結(jié)構(gòu)的前 16 個(gè)位元組,或者,像最初定義時(shí)一樣,是包含 WAVEFORMAT 結(jié)構(gòu)的 PCMWAVEFORMAT 結(jié)構(gòu),其定義如下:
typedef struct pcmwaveformat - tag
{
WAVEFORMAT wf ; /*音頻波形格式結(jié)構(gòu)*/
WORD wBitsPerSample; /* 采樣大小 */
} PCMWAVEFORMAT;
typedef struct waveformat - tag
{
WORD wFormatTag ; /* 指定格式類(lèi)型; 默認(rèn) WAVE_FORMAT_PCM = 1; */
WORD nChannels;/* 指出波形數(shù)據(jù)的聲道數(shù); 單聲道為 1, 立體聲為 2 */
DWORD nSamplesPerSec;/* 指定采樣頻率(每秒的樣本數(shù)) */
DWORD nAvgBytesperSec;/* 指定數(shù)據(jù)傳輸?shù)膫鬏斔俾?每秒的字節(jié)數(shù)) */
WORD nBlockAlign;/* 指定塊對(duì)齊塊對(duì)齊是數(shù)據(jù)的最小單位 */
} WAVEFORMAT; /*音頻波形格式結(jié)構(gòu)*/
格式資訊的後面是文字字串「data」,然後是 32 位元的資料大小,最後是波形資料本身。 用於讀取標(biāo)記檔案的一個(gè)重要規(guī)則是忽略不準(zhǔn)備處理的資料塊。
音頻波形擴(kuò)展格式結(jié)構(gòu)WAVEFORMATEX用于打開(kāi)音頻設(shè)備,其定義如下:
typedef struct
{
WORD wFormatTag; /* 指定格式類(lèi)型; 默認(rèn) WAVE_FORMAT_PCM = 1; */
WORD nChannels; /* 指出波形數(shù)據(jù)的聲道數(shù); 單聲道為 1, 立體聲為 2 */
DWORD nSamplesPerSec; /* 指定采樣頻率(每秒的樣本數(shù)) */
DWORD nAvgBytesPerSec; /* 指定數(shù)據(jù)傳輸?shù)膫鬏斔俾?每秒的字節(jié)數(shù)) */
WORD nBlockAlign; /* 指定塊對(duì)齊塊對(duì)齊是數(shù)據(jù)的最小單位 */
WORD wBitsPerSample; /* 采樣大小 */
WORD cbSize; /* 附加信息的字節(jié)大小 */
} WAVEFORMATEX;
音頻數(shù)據(jù)塊緩存結(jié)構(gòu)WAVEHDR
其聲明如下:
type struct{
LPSTR lpData; /* 指向鎖定的數(shù)據(jù)緩沖區(qū)的指針 */
DWORD dwBufferLength; /* 數(shù)據(jù)緩沖區(qū)的大小 */
DWORD dwBytesRecorded; /* 錄音時(shí)指明緩沖區(qū)中的數(shù)據(jù)量 */
DWORD dwUser; /* 用戶(hù)數(shù)據(jù) */
DWORD dwFlag; /* 提供緩沖區(qū)信息的標(biāo)志 */
DWORD dwLoops; /* 循環(huán)播放的次數(shù) */
struct wavehdr_tag *lpNext; /* 保留 */
DWORD reserved; /* 保留 */
} WAVEHDR;
dwFlags中提供緩沖區(qū)信息的標(biāo)志。定義以下值:
WHDR_DONE被設(shè)備驅(qū)動(dòng)程序設(shè)置,用來(lái)標(biāo)識(shí)它是完成的(緩沖區(qū))并且正在返回它到應(yīng)用程序
WHDR_PREPARED由Windows設(shè)置表明,在緩沖區(qū)已準(zhǔn)備waveInPrepareHeader或waveOutPrepareHeader功能。
WHDR_BEGINLOOP這個(gè)緩沖區(qū)是在第一個(gè)循環(huán)緩沖區(qū)。這個(gè)標(biāo)志僅用于輸出緩沖器。
WHDR_ENDLOOP這個(gè)緩沖區(qū)是在一個(gè)循環(huán)中的最后一個(gè)緩沖區(qū)。這個(gè)標(biāo)志僅用于輸出緩沖器。
WHDR_INQUEUE由Windows設(shè)置為顯示緩沖區(qū)排隊(duì)播放。
現(xiàn)在材料、工具都有了,接下來(lái)就是如何炒的問(wèn)題了。windows程序設(shè)計(jì)是一種以物件為導(dǎo)向的創(chuàng)作,所謂物件就是程式與資料的組合,你所見(jiàn)到的程序界面就是一種物件。你可以先在紙上畫(huà)出程序的界面,然后再根據(jù)它逐步添加各個(gè)模塊功能。下面就是播放器的界面:
如圖有8個(gè)按鈕和一個(gè)列表框,它們代表了程序的各個(gè)功能模塊。有了界面就等于建好了房子的框架,下來(lái)只要添磚加瓦就可以了。windows程序是以消息作為驅(qū)動(dòng)的,對(duì)這些功能模塊的處理就是各種消息的處理,下面請(qǐng)看偽代碼:
//對(duì)話(huà)方塊回調(diào)函數(shù)BOOL CALLBACK DlgProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
//處理窗口消息
switch (message)
{
case WM_INITDIALOG://處理對(duì)話(huà)方塊初始化消息
//初始化代碼
return TRUE ;
//處理窗口控件消息
case WM_COMMAND:
switch (LOWORD (wParam))
{
case IDC_OPEN://處理打開(kāi)按鈕消息
//獲取文件名
//輸出音樂(lè)文件列表
//將循環(huán)標(biāo)志改為正常
//獲取wave音頻格式
//打開(kāi)波形設(shè)備if(waveOutOpen(&(params.hWaveOut), WAVE_MAPPER, &wfx, (DWORD)waveOutProc,
(DWORD)&waveFreeBlockCount, CALLBACK_FUNCTION) != MMSYSERR_NOERROR)
{
//顯示錯(cuò)誤信息
return TRUE ;
}//將當(dāng)前播放文件加亮
//開(kāi)啟音頻緩沖線(xiàn)程
return TRUE ;
//處理暫停播放消息
case IDC_PAUSE:
if//如果暫停條件為真
{
//繼續(xù)播放
}
else//如果暫停條件為假
{
//暫停播放
}
return TRUE ;
//處理上一首消息
case IDC_LAST:
//如果播放的是第一首則不處理
//發(fā)送WM_WAVEPLAY消息
return TRUE ;
//處理下一首消息
case IDC_NEXT:
//如果播放的是最后一首則不處理
//發(fā)送WM_WAVEPLAY消息
return TRUE ;
//處理正常播放消息
case IDC_NORMAL:
//將標(biāo)志置為正常播放
return TRUE ;
//處理單次循環(huán)消息
case IDC_REPLAY_ONE:
//將標(biāo)志置為單次循環(huán)
return TRUE ;
//處理全部循環(huán)消息
case IDC_REPLAY_ALL:
//將標(biāo)志置為全部循環(huán)
return TRUE ;
//處理停止消息
case IDC_STOP:
//設(shè)置停止標(biāo)志
//發(fā)送WM_WAVEPLAY消息
return TRUE ;
//處理列表控件消息
case IDC_LIST:
if(HIWORD(wParam) == LBN_DBLCLK)//如果雙擊文件
{//設(shè)置線(xiàn)程關(guān)閉條件為真
//重置波形設(shè)備
//解除所有的WAVEHDR結(jié)構(gòu)
//關(guān)閉波形設(shè)備
//如果存在打開(kāi)文件句柄則關(guān)閉它
//取得雙擊的文件序號(hào)
//獲取wave文件音頻格式結(jié)構(gòu)
//打開(kāi)波形設(shè)備//開(kāi)啟音頻緩沖線(xiàn)程
}
return TRUE ;
}
break ;
//處理自定義消息
case WM_WAVEPLAY:
//設(shè)置線(xiàn)程關(guān)閉條件為真
//重置波形設(shè)備
//解鎖所有的WAVEHDR結(jié)構(gòu)
//關(guān)閉波形設(shè)備
//如果存在文件句柄則關(guān)閉它
//如果停止條件為真則退出處理
//如果單次循環(huán)條件為真
{
//代碼
}
//如果全部循環(huán)條件為真
{
//代碼
}
//如果播放上一首條件為真
{
//代碼
}
//如果播放下一首條件為真
{
//代碼
}//如果播放的是最后一首則不處理
//獲取wave文件音頻格式結(jié)構(gòu)
//打開(kāi)波形設(shè)備
//發(fā)送列表框控件當(dāng)前選擇加亮消息
//開(kāi)啟音頻緩沖線(xiàn)程
return TRUE ;
//處理系統(tǒng)控件消息
case WM_SYSCOMMAND:
switch (wParam)
{
case SC_CLOSE://處理窗口關(guān)閉消息
if (音頻設(shè)備打開(kāi))
{
//設(shè)置線(xiàn)程關(guān)閉條件為真
//重置波形設(shè)備
//解鎖所有的WAVEHDR結(jié)構(gòu)
//釋放所有的音頻緩沖塊
//關(guān)閉波形設(shè)備
//如果文件句柄存在則關(guān)閉它
//結(jié)束對(duì)話(huà)方塊
}
else
{
//釋放所有的音頻緩沖塊
//如果文件句柄存在則關(guān)閉它
//結(jié)束對(duì)話(huà)方塊
}
return TRUE ;
}
break ;
}
return FALSE ;//返回假值
}
對(duì)于程序設(shè)計(jì)來(lái)說(shuō),最重要的就是邏輯。具體到這個(gè)播放器,就是各個(gè)模塊的邏輯以及它們之間的聯(lián)系。比如,按下打開(kāi)按鈕時(shí),彈出打開(kāi)文件對(duì)話(huà)框,然后讀取文件名,打開(kāi)設(shè)備,鎖定緩沖,再進(jìn)行播放,然后循環(huán),直到所有文件播放完畢。除了各個(gè)模塊自身的消息外,這個(gè)程序設(shè)計(jì)了一個(gè)自定義消息WM_WAVEPLAY,利用它對(duì)上一首、下一首、單次循環(huán)、全部循環(huán)、停止等按鈕消息進(jìn)行處理,這就大大縮減了代碼,使邏輯結(jié)構(gòu)更加清晰。邏輯搞清楚了,接下來(lái)就是最后一步了。將各個(gè)模塊的代碼填充完整,這個(gè)程序就基本完工了,剩下的就是調(diào)試工作了。
不過(guò)這里還有兩個(gè)問(wèn)題需要注意,一個(gè)是打開(kāi)音頻設(shè)備要用回調(diào)函數(shù)處理,另一個(gè)就是多線(xiàn)程技術(shù)。首先說(shuō)一下打開(kāi)音頻設(shè)備的問(wèn)題,請(qǐng)看一看上面的紅色代碼:
if(waveOutOpen(&(params.hWaveOut), WAVE_MAPPER, &wfx, (DWORD)waveOutProc,
(DWORD)&waveFreeBlockCount, CALLBACK_FUNCTION) != MMSYSERR_NOERROR)
{
//顯示錯(cuò)誤信息
return TRUE ;
}waveOutProc就是回調(diào)函數(shù)的名稱(chēng),waveFreeBlockCount就是傳給它的參數(shù),CALLBACK_FUNCTION指明使用回調(diào)函數(shù)處理。回調(diào)函數(shù)的名稱(chēng)可以自定義,但是格式卻必須如下:static void CALLBACK waveOutProc(HWAVEOUT hWaveOut, UINT uMsg, DWORD dwInstance, DWORD dwParam1,DWORD dwParam2 )
{
//處理音頻設(shè)備播放時(shí)的各種消息
return ;
}
為什么要在回調(diào)函數(shù)中處理設(shè)備消息?這是因?yàn)榉乐乖O(shè)備消息會(huì)干擾到程序與外界的交互消息,比如設(shè)備在播放文件時(shí),程序也許會(huì)收到用戶(hù)輸入的消息,如果同時(shí)處理設(shè)備消息,這時(shí)就有可能會(huì)導(dǎo)致故障,而用回調(diào)函數(shù)就不會(huì)出現(xiàn)這種狀況,因?yàn)樗窃诹硪痪€(xiàn)程中處理消息。
這個(gè)播放器還必須使用多線(xiàn)程技術(shù),因?yàn)椴シ盼募r(shí)需要不停讀音頻緩沖,這會(huì)導(dǎo)致界面暫時(shí)卡死(即不能與用戶(hù)進(jìn)行交互),只能等待播放完畢。怎么解決呢?用一個(gè)線(xiàn)程專(zhuān)門(mén)進(jìn)行音頻數(shù)據(jù)的緩沖與播放,而主線(xiàn)程用于各種用戶(hù)消息的處理。請(qǐng)看代碼:
#include //包含多線(xiàn)程處理頭文件
VOID bufThread(PVOID pvoid) ;//聲明音頻緩沖線(xiàn)程函數(shù)
_beginthread(bufThread,0,¶ms) ;//開(kāi)啟音頻緩沖線(xiàn)程
//定義音頻緩沖線(xiàn)程函數(shù)
VOID bufThread(PVOID pvoid)
{
DWORD readBytes ;//定義一個(gè)儲(chǔ)存讀取文件字節(jié)數(shù)的DWORD變量
char buffer[BLOCK_SIZE] = {'\0'} ; /* 定義一個(gè)臨時(shí)緩沖 */
volatile PPARAMS pparams ;//定義一個(gè)參數(shù)結(jié)構(gòu)指針
waveFreeBlockCount = BLOCK_COUNT; //設(shè)置空閑緩沖塊的總數(shù)量
waveCurrentBlock= 0;//設(shè)置當(dāng)前緩沖塊為第一塊
pparams = (PPARAMS)pvoid ;//給參數(shù)結(jié)構(gòu)指針賦值
if(pparams->bShutoff)//判斷線(xiàn)程關(guān)閉條件
return ;
//移動(dòng)文件指針到音頻數(shù)據(jù)起始地址
if(INVALID_SET_FILE_POINTER == SetFilePointer(pparams->hFile,
pparams->iCount,NULL,FILE_BEGIN))
{
MessageBox(NULL,TEXT("指針神經(jīng)病!"),
TEXT("提示"),MB_OK | MB_ICONWARNING);
return ;
}
//在循環(huán)中讀取音頻數(shù)據(jù)到臨時(shí)緩沖,再寫(xiě)入波形設(shè)備
while(!pparams->bShutoff) //判斷線(xiàn)程關(guān)閉條件
{
//讀取數(shù)據(jù)到臨時(shí)緩沖
if(!ReadFile(pparams->hFile, buffer, sizeof(buffer), &readBytes, NULL))
break ;
//如果數(shù)據(jù)為零則退出循環(huán)
if(readBytes == 0)
break;
//如果讀取的結(jié)果小于臨時(shí)緩沖,則填入0補(bǔ)充完整
if(readBytes < sizeof(buffer))
memset(buffer + readBytes, 0, sizeof(buffer) - readBytes);
//將音頻數(shù)據(jù)寫(xiě)入波形設(shè)備進(jìn)行播放
writeAudio(pparams->hWaveOut, buffer,sizeof(buffer));
}
//關(guān)閉文件
CloseHandle(pparams->hFile);
if(pparams->bShutoff)//判斷線(xiàn)程關(guān)閉條件
return ;
//如果線(xiàn)程關(guān)閉條件為假且空閑緩沖塊數(shù)量小于全部緩沖塊數(shù)量則等待
while(!pparams->bShutoff && waveFreeBlockCount < BLOCK_COUNT)
Sleep(0) ;//線(xiàn)程交出自己的時(shí)間片段給別的線(xiàn)程
if(!pparams->bShutoff)//如果線(xiàn)程關(guān)閉條件為假則發(fā)送用戶(hù)自定義消息
PostMessage(pparams->hwnd,WM_WAVEPLAY,0,0) ;
return ;
}
在建立多執(zhí)行緒的 Windows 程式時(shí),需要在「Project Settings」對(duì)話(huà)方塊中做一些修改。選擇「C/C++」頁(yè)面標(biāo)簽,然後在「Category」下拉式清單方塊中選擇「Code Generation」。在「Use Run-Time Library」下拉式清單方塊中,可以看到用於「Release」設(shè)定的「Single-Threaded」和用於 Debug 設(shè)定的「Debug Single-Threaded」。將這些分別改為「Multithreaded」和「Debug Multithreaded」。這將把編譯器旗標(biāo)改為/MT,它是編譯器在編譯多執(zhí)行緒的應(yīng)用程式所需要的。 具體地說(shuō), 編譯器將在.OBJ 檔案中插入 LIBCMT.LIB 檔案名,而不是 LIBC.LIB。連結(jié)程式使用這個(gè)名稱(chēng)與執(zhí)行期程式庫(kù)函式連結(jié)。 LIBC.LIB 和 LIBCMT.LIB 檔案包含 C 語(yǔ)言程式庫(kù)函式, 有些 C 語(yǔ)言程式庫(kù)函式包含靜態(tài)資料。例如,由於 strtok 函式可能被連續(xù)地多次呼叫,所以它在靜態(tài)記憶體中儲(chǔ)存了一個(gè)指標(biāo)。在多執(zhí)行緒程式中,每個(gè)執(zhí)行緒必須在 strtok 函式中有它自己的靜態(tài)指標(biāo)。因此,這個(gè)函式的多執(zhí)行緒版本稍微不同於單執(zhí)行緒的 strtok 函式。 同時(shí)請(qǐng)注意,必須包含表頭檔案 PROCESS.H,這個(gè)檔案定義一個(gè)名為_(kāi)beginthread 的函式,它啟動(dòng)一個(gè)新的執(zhí)行緒。只有定義了_MT 識(shí)別字,才會(huì)宣告這個(gè)函式,這是/MT 旗標(biāo)的另一個(gè)結(jié)果。下面簡(jiǎn)單介紹一下_beginthread函數(shù):
beginthreaduintptr_t _beginthread(
void( *start_address )( void * ),
unsigned stack_size,
void *arglist
);
Parameters 參數(shù):
start_address:程序執(zhí)行一個(gè)新線(xiàn)程的起始地址,即線(xiàn)程函數(shù)的名稱(chēng)。
Start address of a routine that begins execution of a new thread. For _beginthread, the calling convention is either __cdecl or __clrcall; for _beginthreadex, it is either __stdcall or __clrcall.
stack_size:新線(xiàn)程的堆棧大小或0值。
Stack size for a new thread or 0.
Arglist:傳給新線(xiàn)程的變量清單或空值。
Argument list to be passed to a new thread or NULL.
Return Value 返回值:
如果新線(xiàn)程建立成功,函數(shù)返回該線(xiàn)程的句柄;然而,如果新線(xiàn)程退出太快,_beginthread函數(shù)可能返回一個(gè)有誤的句柄。_beginthread發(fā)生錯(cuò)誤時(shí)返回1L。
在使用多線(xiàn)程時(shí),還需注意共享變量的互斥,這里利用了臨界區(qū)域的技術(shù)。這種臨界區(qū)域技術(shù)專(zhuān)用于多線(xiàn)程中共享變量的互斥使用,在任何時(shí)刻,只有一個(gè)執(zhí)行緒能擁有一個(gè)臨界區(qū)域。因此,一個(gè)執(zhí)行緒可以進(jìn)入一個(gè)臨界區(qū)域,設(shè)定一個(gè)變量,然後退出臨界區(qū)域。另一個(gè)使用該變量的執(zhí)行緒在存取變量中的項(xiàng)目之前也要先進(jìn)入該臨界區(qū)域,然後再退出臨界區(qū)域。請(qǐng)看代碼:
static CRITICAL_SECTION waveCriticalSection;//定義一個(gè)臨界區(qū)域
InitializeCriticalSection(&waveCriticalSection);//初始化臨界區(qū)域
EnterCriticalSection(&waveCriticalSection);//進(jìn)行臨界區(qū)域
(*freeBlockCounter)++;//設(shè)定變量
LeaveCriticalSection(&waveCriticalSection);//退出臨界區(qū)域
DeleteCriticalSection(&waveCriticalSection);//刪除臨界區(qū)域
注意,您可以定義多個(gè)臨界區(qū)域物件,比如 cs1 和 cs2。例如,如果一個(gè)程式有四個(gè)執(zhí)行緒,而前兩個(gè)執(zhí)行緒共用一些資料,那么它們可以使用一個(gè)臨界區(qū)域物件,而另外兩個(gè)執(zhí)行緒共用一些其他的資料,那么它們可以使用另一個(gè)臨界區(qū)域物件。 您在主執(zhí)行緒中使用臨界區(qū)域時(shí)應(yīng)該小心,如果從屬執(zhí)行緒在它自己的臨界區(qū)域中花費(fèi)了一段很長(zhǎng)的時(shí)間,那么它可能會(huì)將主執(zhí)行緒的執(zhí)行阻礙很長(zhǎng)一段時(shí)間。
至此播放器就順利地誕生了,盡管它還比較粗糙,但各項(xiàng)功能還算完備。不過(guò),請(qǐng)注意此播放器只能播放wave文件,不支持其它類(lèi)型的文件。下面就是完整代碼的附件,有需要的就請(qǐng)下載。http://download.csdn.net/detail/dnfyg_000/4976055
總結(jié)
以上是生活随笔為你收集整理的C语言里的out函数,c语言 vc 用waveout函数写wave文件播放器的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 开源游戏服务器框架NoahGameFra
- 下一篇: 程序员笔试之海康威视2021应用软件开发