質問

元々は C++ の MFC を使用して作成された古いメトロノーム アプリケーションを、C# を使用して .NET で作成できるように再構築しようとしています。私が直面している問題の 1 つは、タイマーを正確に「刻む」ことです。

たとえば、簡単な BPM (1 分あたりの拍数) が 120 であると仮定すると、タイマーは 0.5 秒 (または 500 ミリ秒) ごとに刻む必要があります。ただし、これを刻みの基準として使用することは、完全に正確というわけではありません。.NET は、経過時間が経過する前にタイマーが刻み込まれないことを保証するだけです。

現在、上で使用したのと同じ 120 BPM の例でこれを回避するために、ティックを 100 ミリ秒程度に設定し、タイマーの 5 ティックごとにクリック音のみを再生しています。これにより精度はかなり向上しますが、ちょっとしたハックのように感じられます。

では、正確なティックを取得する最良の方法は何でしょうか?Visual Studio ですぐに利用できる Windows フォーム タイマーよりも多くのタイマーがあることは知っていますが、それらについてはあまり詳しくありません。

役に立ちましたか?

解決

.NET には「Timer」と呼ばれる 3 つのタイマー クラスがあります。Windows フォームを使用しているように見えますが、実際には System.Threading.Timer クラスの方が便利かもしれません。ただし、このクラスはプール スレッドでコールバックするため、フォームを直接操作することはできないので注意してください。コールバック。

もう 1 つのアプローチは、Win32 マルチメディア タイマー (timeGetTime、timeSetPeriod など) を p/invoke することです。

ちょっとグーグルでこれを見つけました、役に立つかもしれません http://www.codeproject.com/KB/miscctrl/lescsmultimediatimer.aspx

「マルチメディア」(タイマー)は、この文脈で検索される流行語です。

他のヒント

C++ アプリケーションは何を使用していますか?常に同じものを使用することも、C++ のタイマー コードを C++/CLI クラスにラップすることもできます。

最近のデータログプロジェクトを開発しているときに、この問題が発生しました。.NET タイマー ( windows.forms、system.threading、および system.timer ) の問題は、.NET に組み込まれているイベント スケジューリングが原因で、精度が約 10 ミリ秒までしかないことです。(ここでは .NET 2 について話しています)。これは私には受け入れられなかったため、マルチメディア タイマーを使用する必要がありました (dll をインポートする必要があります)。また、すべてのタイマーのラッパー クラスも作成したので、必要に応じて最小限のコード変更でタイマーを切り替えることができます。ここで私のブログ投稿をチェックしてください:http://www.indigo79.net/archives/27

別の可能性は、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

次の「ティック」が発生するまでにタイマーの「ティック」イベント コードの実行が完了していない場合、タイマー クラスが異常な動作を開始する可能性があります。これに対処する 1 つの方法は、ティック イベントの開始時にタイマーを無効にし、最後に再度有効にすることです。

ただし、このアプローチは、「ティック」コードの実行時間がティックのタイミングで許容できないエラーである場合には、その時間中はタイマーが無効になる (カウントされない) ため、適切ではありません。

タイマーを無効にするオプションがある場合は、実行、x ミリ秒間のスリープ、実行、スリープなどを繰り返す別のスレッドを作成することによっても同じ効果を達成できます。

System.Windows.Forms.Timer 精度は 55 ミリ秒に制限されます...

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top