概述,計時器的設備能力,MMTIME結構的指標,Callback函式五個參數,啟動序列,停止序列,
概述
處理多媒體計時器時,可以用毫秒指定兩種不同的時間。第一個是延遲時間,第二個稱為解析度。您可以認為解析度是容錯誤差。如果指定一個延遲100毫秒,而解析度是10毫秒,則計時器的實際延遲範圍在90到110毫秒之間。
計時器的設備能力
使用計時器之前,應獲得計時器的設備能力:
timeGetDevCaps (&timecaps, uSize) ;
第一個參數是TIMECAPS型態結構的指標,第二個參數是此結構的大小。TIMECAPS結構只有兩個欄位,wPeriodMin和wPeriodMax。這是計時器裝置驅動程式所支援的最小和最大的解析度值。如果呼叫timeGetDevCaps後再查看這些值,會發現wPeriodMin是1而wPeriodMax是65535,所以此函式並不是很重要。不過,得到這些解析度值並用於其他計時器函式呼叫是個好主意。
下一步呼叫
timeBeginPeriod (uResolution) ;
來指出程式所需要的計時器解析度的最低值。該值應在TIMECAPS結構所確定的範圍之內。此呼叫允許為可能使用計時器的多個程式提供最好的計時器裝置驅動程式。呼叫timeBeginPeriod及timeEndPeriod必須成對出現,我將在後面對timeEndPeriod作簡短的描述。
現在可以真正設定一個計時器事件:
idTimer = timeSetEvent ( uDelay, uResolution, CallBackFunc, dwData, uFlag) ;
如果發生錯誤,從呼叫傳回的idTimer將是0。在呼叫的下面,將從Windows里用uDelay毫秒來呼叫CallBackFunc函式,其中允許的誤差由uResolution指定。uResolution值必須大於或等於傳遞給timeBeginPeriod的解析度。dwData是程式定義的資料,後來傳遞給CallBackFunc。最後一個參數可以是TIME_ONESHOT,也可以是TIME_PERIODIC。前者用於在uDelay毫秒數中獲得一次CallBackFunc呼叫,而後者用於每個uDelay毫秒都獲得一次CallBackFunc呼叫。
要在呼叫CallBackFunc之前終止只發生一次的計時器事件,或者暫停周期性的計時器事件,請呼叫
timeKillEvent (idTimer) ;
呼叫CallBackFunc後不必刪除只發生一次的計時器事件。在程式中用完計時器以後,請呼叫
timeEndPeriod (wResolution) ;
其中的參數與傳遞給timeBeginPeriod的相同。
另兩個函式的字首是time。函式
dwSysTime = timeGetTime () ;
傳回從Windows第一次啟動到現在的系統時間,單位是毫秒。函式
timeGetSystemTime (&mmtime, uSize) ;
MMTIME結構的指標
需要一個MMTIME結構的指標(與第一個參數一樣),以及此結構的大小(與第二個參數一樣)。雖然MMTIME結構可以在其他環境中用來得到非毫秒格式的系統時間,但此例中它都傳回毫秒時間。所以timeGetSystemTime是多餘的。
Callback函式只限於它所能做的Windows函式呼叫中。Callback函式可以呼叫PostMessage,PostMessage包含有四個計時器函式(timeSetEvent、timeKillEvent、timeGetTime和多餘的timeGetSystemTime)、兩個MIDI輸出函式(midiOutShortMsg和midiOutLongMsg)以及調試函式OutputDebugStr。
很明顯,設計多媒體計時器主要是用於MIDI序列而很少用於其他方面。當然,可以使用PostMessage來通知計時器事件的視窗訊息處理程式,而且視窗訊息處理程式可以做任何它想做的事,只是不能回應計時器callback自身的準確性。
Callback函式五個參數
Callback函式有五個參數,但只使用了其中兩個參數:從timeSetEvent傳回的計時器ID和最初作為參數傳遞給timeSetEvent的dwData值。
DRUM.C模組呼叫DRUMTIME.C中的DrumSetParams函式有很多次-建立DRUM視窗時、使用者在格線上單擊或者移動捲動列時、從磁片上載入.DRM檔案時以及清除格線時。DrumSetParams的唯一的參數是指向DRUM型態結構的指標,此結構型態在DRUMTIME.H定義。該結構以毫秒為單位儲存拍子時間、速度(通常對應於音量)、序列中的拍數以及用於儲存格線(為打擊樂器和鋼琴聲設定)的兩套47個32位元組的整數。這些32位元整數中的每一位元都對應序列的一拍。DRUM.C模組將在靜態記憶體中維護一個DRUM型態的結構,並在呼叫DrumSetParams時向它傳遞一個指標。DrumSetParams只簡單地複製此結構的內容。
啟動序列
要啟動序列,DRUM呼叫DRUMTIME中的DrumBeginSequence函式。唯一的參數就是視窗代號,其作用是通知。DrumBeginSequence打開MIDI Mapper輸出設備,如果成功,則傳送Program Change訊息來為MIDI通道0和9選擇樂器聲音(這些通道是基於0的,所以9實際指的是MIDI通道10,即打擊樂器通道。另一個通道用於鋼琴聲)。DrumBeginSequence透過呼叫timeGetDevCaps和timeBeginPeriod來繼續工作。在TIMER_RES定義的理想計時器解析度通常是5毫秒,但我定義了一個稱作minmax的巨集來計算從timeGetDevCaps傳回的限制範圍以內的解析度。
下一個呼叫是timeSetEvent,用於確定拍子時間,計算解析度、callback函式DrumTimerFunc以及TIME_ONESHOT常數。DRUMTIME用的是只發生一次的計時器,而不是周期性計時器,所以速度可以隨序列的執行而動態變化。timeSetEvent呼叫之後,計時器裝置驅動程式將在延遲時間結束以後呼叫DrumTimerFunc。
DrumTimerFunccallback是DRUMTIME.C中的函式,在DRUMTIME.C中有許多重要的操作。變數iIndex儲存序列中目前的拍子。Callback從為目前演奏的聲音傳送MIDI Note Off訊息開始。iIndex的初始值-1以防止第一次啟動序列時發生這種情況。
接下來,iIndex遞增並將其值連同使用者定義的一個WM_USER_NOTIFY訊息一起傳遞給DRUM中的視窗代號。wParam訊息參數設定為iIndex,以便在DRUM.C中,WndProc能夠移動格線底部的「跳動的小球」。
DrumTimerFunc將下列事件作為結束:把Note On訊息傳送給通道0和9的合成器上,並儲存格線值以便下一次可以關閉聲音,然後透過呼叫timeSetEvent來設定新的只發生一次的計時器事件。
停止序列
要停止序列,DRUM呼叫DrumEndSequence,其中唯一的參數可以設定為TRUE或FALSE。如果是TRUE,則DrumEndSequence按下面的程式立即結束序列:刪除所有待決的計時器事件,呼叫timeEndPeriod,向兩個MIDI通道傳送「all notes off」訊息,然後關閉MIDI輸出埠。當使用者決定終止程式時,DRUM用TRUE參數呼叫DrumEndSequence。
然而,當使用者在DRUM里的「Sequence」功能表中選擇「Stop」時,程式將用FALSE作為參數呼叫DrumEndSequence。這就允許序列在結束之前完成目前的迴圈。DrumEndSequence透過把bEndSequence整體變數設定為NULL來回應此呼叫。如果bEndSequence是TRUE,並且拍子的索引值設定為0,則DrumTimerFunc把使用者定義的WM_USER_FINISHED訊息傳送給WndProc。WndProc必須通過用TRUE作為參數呼叫DrumEndSequence來回應該訊息,以便正確地結束計時器和MIDI埠的使用