2008-08-12 82 views
14

我試圖重建一箇舊的節拍器應用程序,最初使用C++編寫的MFC使用C#編寫.NET。我遇到的其中一個問題是讓定時器足夠精確地「打勾」。例如,假設一個簡單的BPM(每分鐘跳動次數)爲120,計時器應該每隔0.5秒(或500毫秒)打勾一次。然而,使用它作爲ticks的基礎並不完全準確,因爲.NET只能保證你的timer不會在經過的時間過去之前打勾。從C#中的定時器獲取準確的刻度線

目前,爲了解決這個問題,對於上面使用的相同的120個BPM示例,我將tick設置爲100毫秒,並且只在每5個計時器刻度上播放單擊聲音。這確實提高了精確度,但是如果感覺有點像黑客攻擊。

那麼,獲得準確滴答的最佳方法是什麼?我知道有更多的定時器可用,比在Visual Studio中可用的Windows窗體計時器更多,但我對它們並不熟悉。

回答

9

.NET中有三個計時器類,稱爲「計時器」。這聽起來像你使用的Windows窗體之一,但實際上你可能會發現System.Threading.Timer類更有用 - 但要小心,因爲它回調池線程,所以你不能直接與你的表單交互回調。

的另一種方法可能是P /調用對Win32多媒體計時器 - timeGetTime,timeSetPeriod等

快速谷歌發現這一點,這可能是有用的http://www.codeproject.com/KB/miscctrl/lescsmultimediatimer.aspx

'多媒體'(定時器)是在此背景下搜索的熱門詞彙。

1

什麼是C++應用程序使用?您始終可以使用相同的東西,也可以將C++中的計時器代碼封裝到C++/CLI類中。

0

當下一個「滴答」發生時,計時器「滴答」事件代碼沒有完成執行時,定時器類可能會奇怪地開始行爲。解決這個問題的一種方法是在tick事件開始時禁用定時器,然後在最後重新啓用定時器。

但是,這種方法並不適用於「滴答」代碼的執行時間在滴答定時中不可接受的錯誤,因爲在此期間定時器將被禁用(不計數)。

如果禁止定時器是一個選項,那麼你也可以實現通過創建一個單獨的線程來執行同樣的效果,休眠x毫秒,執行,睡覺,等...

+0

但你只能確保線程睡眠至少爲x millioseconds;線程調度程序不會證明線程將以毫秒計數運行 – Wilhelm 2009-07-18 22:32:28

+0

正確。我同意你所說的關於不能確定下一次滴答的時間。我所說的一點是,當您的下一個勾號發生時,您不希望執行先前勾號中的勾號事件代碼。 – 2009-07-20 13:51:26

0

System.Windows.Forms.Timer受限於準確度爲55毫秒...

1

我在開發最近的數據記錄項目時遇到了這個問題。 .NET定時器(windows.forms,system.threading和system.timer)的問題在於,它們只能精確到10毫秒左右,這是由於事件調度內置於.NET,我相信。 (我在這裏討論.NET 2)。這對我來說是不可接受的,所以我不得不使用多媒體計時器(您需要導入dll)。我還爲所有定時器編寫了一個包裝類,因此如果需要,可以使用最少的代碼更改在它們之間切換。點擊這裏,查看我的博客文章: http://www.indigo79.net/archives/27

1

另一種可能性是,有在WPF實現DispatcherTimer 的一個bug(有毫秒之間的不匹配和蜱引起視具體過程執行時間的潛在不準確),如下證明:

http://referencesource.microsoft.com/#WindowsBase/Base/System/Windows/Threading/DispatcherTimer.cs,143

class DispatcherTimer 
{ 
    public TimeSpan Interval 
    { 
     set 
     { 
      ... 
      _interval = value; 
      // Notice below bug: ticks1 + milliseconds [Bug1] 
      _dueTimeInTicks = Environment.TickCount + (int)_interval.TotalMilliseconds; 
     } 
    } 
} 

http://referencesource.microsoft.com/#WindowsBase/Base/System/Windows/Threading/Dispatcher.cs

class Dispatcher 
{ 
    private object UpdateWin32TimerFromDispatcherThread(object unused) 
    { 
     ... 
     _dueTimeInTicks = timer._dueTimeInTicks; 
     SetWin32Timer(_dueTimeInTicks); 
    } 

    private void SetWin32Timer(int dueTimeInTicks) 
    { 
     ... 
     // Notice below bug: (ticks1 + milliseconds) - ticks2 [Bug2 - almost cancels Bug1, delta is mostly milliseconds not ticks] 
     int delta = dueTimeInTicks - Environment.TickCount; 
     SafeNativeMethods.SetTimer( 
      new HandleRef(this, _window.Value.Handle), 
      TIMERID_TIMERS, 
      delta); // <-- [Bug3 - if delta is ticks, it should be divided by TimeSpan.TicksPerMillisecond = 10000] 
    } 
} 

http://referencesource.microsoft.com/#WindowsBase/Shared/MS/Win32/SafeNativeMethodsCLR.cs,505

class SafeNativeMethodsPrivate 
{ 
    ... 
    [DllImport(ExternDll.User32, SetLastError = true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] 
    public static extern IntPtr SetTimer(HandleRef hWnd, int nIDEvent, int uElapse, NativeMethods.TimerProc lpTimerFunc); 
} 

http://msdn.microsoft.com/en-us/library/windows/desktop/ms644906%28v=vs.85%29.aspx

uElapse [in] 
Type: UINT 
The time-out value, in milliseconds. // <-- milliseconds were needed eventually