16

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

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

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

那么,获得准确刻度的最佳方法是什么?我知道有比 Visual Studio 中现成的 windows 窗体计时器更多的计时器可用,但我对它们并不熟悉。

4

6 回答 6

11

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

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

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

“多媒体”(计时器)是在此上下文中搜索的流行词。

于 2008-08-12T12:34:43.443 回答
1

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

于 2008-08-12T13:17:49.410 回答
1

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

于 2011-02-11T10:09:44.037 回答
1

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

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
于 2014-12-18T14:26:56.997 回答
0

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

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

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

于 2008-08-12T14:09:28.523 回答
0

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

于 2010-10-04T03:30:24.467 回答