代码之家  ›  专栏  ›  技术社区  ›  jerhinesmith

从C中的计时器获取精确的滴答声#

  •  14
  • jerhinesmith  · 技术社区  · 17 年前

    我试图重建一个旧的节拍器应用程序,它最初是用C++中的MFC编写的,用C语言写在.NET中。我遇到的一个问题是让计时器精确地“滴答”一下。

    例如,假设一个简单的bpm(每分钟跳动)为120,那么计时器应该每隔.5秒(或500毫秒)计时一次。然而,使用它作为计时的基础并不完全准确,因为.NET只保证在经过的时间过去之前计时器不会计时。

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

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

    6 回复  |  直到 11 年前
        1
  •  9
  •   Will Dean    17 年前

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

    另一种方法可能是p/invoke到win32多媒体计时器-timegettime、timesetperiod等。

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

    “多媒体”(timer)是在这种上下文中搜索的热门词。

        2
  •  1
  •   Coincoin    17 年前

    C++应用程序使用的是什么?您可以总是使用相同的东西或将C++中的定时器代码封装到C++ /CLI类中。

        3
  •  1
  •   Michael Nash    14 年前

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

        4
  •  1
  •   yarony    11 年前

    另一种可能是调度程序计时器的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
    
        5
  •  0
  •   Brad Barker    17 年前

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

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

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

        6
  •  0
  •   yazanpro    14 年前

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

    推荐文章