我正在尝试重建一个旧的节拍器应用程序,该应用程序最初使用C ++中的MFC编写,使用C#在.NET中编写。我遇到的一个问题是让计时器足够准确地“打勾”。
例如,假设一个简单的BPM(每分钟节拍数)为120,计时器应每隔0.5秒(或500毫秒)打勾。然而,使用它作为刻度的基础并不完全准确,因为.NET只保证计时器在经过的时间过去之前不会打勾。
目前,为了解决上面使用的相同的120 BPM示例,我将刻度设置为100毫秒,并且仅在每个第5个计时器刻度上播放点击声。这确实提高了准确性,但如果感觉有点像黑客。
那么,获得准确滴答的最佳方法是什么?我知道有更多的计时器可用于Visual Studio中随时可用的Windows窗体计时器,但我并不熟悉它们。
答案 0 :(得分:9)
.NET中有三个名为“Timer”的计时器类。听起来你正在使用Windows Forms,但实际上你可能会发现System.Threading.Timer类更有用 - 但要小心,因为它回调了池线程,所以你不能直接与你的表单交互回调。
另一种方法可能是p / invoke到Win32多媒体计时器 - timeGetTime,timeSetPeriod等。
快速谷歌发现了这个,这可能是有用的http://www.codeproject.com/KB/miscctrl/lescsmultimediatimer.aspx
'多媒体'(计时器)是在此上下文中搜索的热门词。
答案 1 :(得分:1)
使用什么C ++应用程序?您始终可以使用相同的东西或将C ++中的计时器代码包装到C ++ / CLI类中。
答案 2 :(得分:1)
开发最近的数据记录项目时遇到了这个问题。 .NET计时器(windows.forms,system.threading和system.timer)的问题在于它们只能精确到10毫秒左右,这是由于我认为.NET内置的事件调度。 (我在这里谈论.NET 2)。这对我来说是不可接受的,所以我不得不使用多媒体计时器(你需要导入dll)。我还为所有计时器编写了一个包装类,因此如果需要,您可以使用最少的代码更改在它们之间切换。在这里查看我的博客文章: http://www.indigo79.net/archives/27
答案 3 :(得分:1)
另一种可能性是DispatcherTimer的WPF实现中存在错误 (毫秒和刻度之间存在不匹配,导致潜在的不准确性,具体取决于确切的流程执行时间),如下所示:
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
答案 4 :(得分:0)
当定时器'tick'事件代码在下一次'tick'发生时没有完成执行时,定时器类可以开始表现奇怪。解决此问题的一种方法是在tick事件开始时禁用计时器,然后在结束时重新启用它。
但是,这种方法不适用于“tick”代码的执行时间不可接受的时间错误,因为在此期间计时器将被禁用(不计数)。
如果禁用定时器是一个选项,那么你也可以通过创建一个执行的单独线程,睡眠x毫秒,执行,睡眠等来实现相同的效果......
答案 5 :(得分:0)
System.Windows.Forms.Timer
的精确度限制在55毫秒......