我正在尝试重建一个旧的节拍器应用程序,该应用程序最初是使用 C++ 中的 MFC 编写的,现在使用 C# 在 .NET 中编写。我遇到的问题之一是让计时器足够准确地“滴答”。

例如,假设简单的 BPM(每分钟节拍数)为 120,则计时器应每 0.5 秒(或 500 毫秒)滴答一次。然而,使用它作为滴答的基础并不完全准确,因为 .NET 只能保证您的计时器在经过的时间过去之前不会滴答。

目前,为了解决上面使用的相同 120 BPM 示例的问题,我将滴答声设置为 100 毫秒之类的值,并且仅在每 5 个计时器滴答声时播放点击声音。这确实大大提高了准确性,但感觉有点像黑客。

那么,获得准确报价的最佳方法是什么?我知道有比 Visual Studio 中可用的 Windows 窗体计时器更多的可用计时器,但我不太熟悉它们。

有帮助吗?

解决方案

.NET 中有三个称为“Timer”的计时器类。听起来您正在使用 Windows 窗体,但实际上您可能会发现 System.Threading.Timer 类更有用 - 但要小心,因为它会回调池线程,因此您无法直接与窗体交互回调。

另一种方法可能是 p/调用 Win32 多媒体计时器 - timeGetTime、timeSetPeriod 等。

快速谷歌发现了这个,这可能有用 http://www.codeproject.com/KB/miscctrl/lescsmultimediatimer.aspx

“多媒体”(计时器)是在这种情况下搜索的流行词。

其他提示

C++ 应用程序使用什么?您始终可以使用相同的东西,或者将计时器代码从 C++ 包装到 C++/CLI 类中。

我在开发最近的数据记录项目时遇到了这个问题。.NET 计时器( windows.forms、system.threading 和 system.timer )的问题是它们只能精确到 10 毫秒左右,我相信这是由于 .NET 内置的事件调度造成的。(我在这里谈论的是.NET 2)。这对我来说是不可接受的,所以我不得不使用多媒体计时器(你需要导入 dll )。我还为所有计时器编写了一个包装类,因此您可以根据需要使用最少的代码更改在它们之间进行切换。在这里查看我的博客文章:http://www.indigo79.net/archives/27

另一种可能性是,WPF实施dispatchertimer存在错误(毫秒和tick之间存在不匹配,导致潜在的不准确性,具体取决于确切的过程执行时间),如下所示:

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

当计时器“滴答”事件代码在下一个“滴答”发生时尚未完成执行时,计时器类可能会开始表现奇怪。解决这个问题的一种方法是在滴答事件开始时禁用计时器,然后在结束时重新启用它。

然而,这种方法不适合“滴答”代码的执行时间在滴答计时中是不可接受的错误的情况,因为计时器将在该时间内被禁用(不计数)。

如果可以选择禁用计时器,那么您还可以通过创建一个单独的线程来执行、休眠 x 毫秒、执行、休眠等来实现相同的效果...

System.Windows.Forms.Timer 精度限制为 55 毫秒...

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top